#include "config.h"
#include "system.h"
#include "rtl.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "output.h"
#include "insn-attr.h"
#include "flags.h"
#include "recog.h"
#include "obstack.h"
#include "tree.h"
#include "function.h"
#include "expr.h"
#include "ggc.h"
#include "toplev.h"
#include "tm_p.h"
#include "target.h"
#include "target-def.h"
#define min(A,B) ((A) < (B) ? (A) : (B))
#define max(A,B) ((A) > (B) ? (A) : (B))
static int unsigned_comparisons_p PARAMS ((rtx));
static void output_loadsave_fpregs PARAMS ((FILE *, enum rtx_code, rtx));
static void output_fpops PARAMS ((FILE *));
static void init_fpops PARAMS ((void));
static int memory_offset_in_range_p PARAMS ((rtx, enum machine_mode, int, int));
static unsigned int hash_rtx PARAMS ((rtx));
static void romp_output_function_prologue PARAMS ((FILE *, HOST_WIDE_INT));
static void romp_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));
static void romp_select_rtx_section PARAMS ((enum machine_mode, rtx,
unsigned HOST_WIDE_INT));
static void romp_encode_section_info PARAMS ((tree, int));
#undef TARGET_ASM_FUNCTION_PROLOGUE
#define TARGET_ASM_FUNCTION_PROLOGUE romp_output_function_prologue
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE romp_output_function_epilogue
#undef TARGET_ASM_SELECT_RTX_SECTION
#define TARGET_ASM_SELECT_RTX_SECTION romp_select_rtx_section
#undef TARGET_ENCODE_SECTION_INFO
#define TARGET_ENCODE_SECTION_INFO romp_encode_section_info
struct gcc_target targetm = TARGET_INITIALIZER;
int
next_insn_tests_no_unsigned (insn)
rtx insn;
{
register rtx next = next_cc0_user (insn);
if (next == 0)
{
if (find_reg_note (insn, REG_UNUSED, cc0_rtx))
return 1;
else
abort ();
}
return ((GET_CODE (next) == JUMP_INSN
|| GET_CODE (next) == INSN
|| GET_CODE (next) == CALL_INSN)
&& ! unsigned_comparisons_p (PATTERN (next)));
}
static int
unsigned_comparisons_p (x)
rtx x;
{
register const char *fmt;
register int len, i;
register enum rtx_code code = GET_CODE (x);
switch (code)
{
case REG:
case PC:
case CC0:
case CONST_INT:
case CONST_DOUBLE:
case CONST:
case LABEL_REF:
case SYMBOL_REF:
return 0;
case LTU:
case GTU:
case LEU:
case GEU:
return (XEXP (x, 0) == cc0_rtx || XEXP (x, 1) == cc0_rtx);
default:
break;
}
len = GET_RTX_LENGTH (code);
fmt = GET_RTX_FORMAT (code);
for (i = 0; i < len; i++)
{
if (fmt[i] == 'e')
{
if (unsigned_comparisons_p (XEXP (x, i)))
return 1;
}
else if (fmt[i] == 'E')
{
register int j;
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
if (unsigned_comparisons_p (XVECEXP (x, i, j)))
return 1;
}
}
return 0;
}
void
update_cc (body, insn)
rtx body ATTRIBUTE_UNUSED;
rtx insn;
{
switch (get_attr_cc (insn))
{
case CC_NONE:
break;
case CC_CHANGE0:
if (cc_status.value1 != 0
&& reg_overlap_mentioned_p (recog_data.operand[0], cc_status.value1))
cc_status.value1 = 0;
if (cc_status.value2 != 0
&& reg_overlap_mentioned_p (recog_data.operand[0], cc_status.value2))
cc_status.value2 = 0;
break;
case CC_COPY1TO0:
if (cc_status.value1 != 0
&& reg_overlap_mentioned_p (recog_data.operand[0], cc_status.value1))
cc_status.value1 = 0;
if (cc_status.value2 != 0
&& reg_overlap_mentioned_p (recog_data.operand[0], cc_status.value2))
cc_status.value2 = 0;
if (cc_status.value1 != 0
&& rtx_equal_p (cc_status.value1, recog_data.operand[1]))
cc_status.value2 = recog_data.operand[0];
if (cc_status.value2 != 0
&& rtx_equal_p (cc_status.value2, recog_data.operand[1]))
cc_status.value1 = recog_data.operand[0];
break;
case CC_CLOBBER:
CC_STATUS_INIT;
break;
case CC_SETS:
CC_STATUS_INIT;
cc_status.flags |= CC_NO_OVERFLOW;
cc_status.value1 = recog_data.operand[0];
break;
case CC_COMPARE:
{
register rtx p = PATTERN (insn);
CC_STATUS_INIT;
if (GET_CODE (p) == PARALLEL)
p = XVECEXP (p, 0, 0);
cc_status.value1 = SET_SRC (p);
if (GET_CODE (SET_SRC (p)) == REG)
cc_status.flags |= CC_NO_OVERFLOW;
if (! next_insn_tests_no_unsigned (insn))
cc_status.flags |= CC_UNSIGNED;
}
break;
case CC_TBIT:
CC_STATUS_INIT;
cc_status.flags = CC_IN_TB | CC_NOT_NEGATIVE;
break;
default:
abort ();
}
}
int
restore_compare_p (op)
rtx op;
{
enum rtx_code code = GET_CODE (op);
return (((code == GEU || code == LEU || code == GTU || code == LTU)
&& ! (cc_status.flags & CC_UNSIGNED))
|| ((code == GE || code == LE || code == GT || code == LT)
&& (cc_status.flags & CC_UNSIGNED)));
}
const char *
output_in_line_mul ()
{
static char insns[200];
int i;
strcpy (insns, "s %0,%0\n");
strcat (insns, "\tmts r10,%1\n");
for (i = 0; i < 16; i++)
strcat (insns, "\tm %0,%2\n");
strcat (insns, "\tmfs r10,%0");
return insns;
}
static int
memory_offset_in_range_p (op, mode, low, high)
register rtx op;
enum machine_mode mode;
int low, high;
{
int offset = 0;
if (! memory_operand (op, mode))
return 0;
while (GET_CODE (op) == SUBREG)
{
offset += SUBREG_BYTE (op);
op = SUBREG_REG (op);
}
if (GET_CODE (op) != MEM)
abort ();
mode = GET_MODE (op);
op = XEXP (op, 0);
if (GET_CODE (op) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (op))
offset = get_pool_offset (op) + 12;
else if (GET_CODE (op) == PLUS)
{
if (GET_CODE (XEXP (op, 1)) != CONST_INT
|| ! register_operand (XEXP (op, 0), Pmode))
return 0;
offset += INTVAL (XEXP (op, 1));
}
else if (! register_operand (op, Pmode))
return 0;
return (offset >= low && offset <= high
&& (offset % GET_MODE_SIZE (mode) == 0));
}
int
zero_memory_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return memory_offset_in_range_p (op, mode, 0, 0);
}
int
short_memory_operand (op, mode)
rtx op;
enum machine_mode mode;
{
if (mode == VOIDmode)
mode = GET_MODE (op);
return memory_offset_in_range_p (op, mode, 0,
15 * min (UNITS_PER_WORD,
GET_MODE_SIZE (mode)));
}
int
symbolic_memory_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
if (! memory_operand (op, mode))
return 0;
while (GET_CODE (op) == SUBREG)
op = SUBREG_REG (op);
if (GET_CODE (op) != MEM)
abort ();
op = XEXP (op, 0);
if (constant_pool_address_operand (op, VOIDmode))
return 0;
else
return romp_symbolic_operand (op, Pmode)
|| (GET_CODE (op) == PLUS && register_operand (XEXP (op, 0), Pmode)
&& romp_symbolic_operand (XEXP (op, 1), Pmode));
}
int
current_function_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
if (GET_CODE (op) != MEM || GET_CODE (XEXP (op, 0)) != SYMBOL_REF
|| ! CONSTANT_POOL_ADDRESS_P (XEXP (op, 0)))
return 0;
op = get_pool_constant (XEXP (op, 0));
return (GET_CODE (op) == SYMBOL_REF
&& ! strcmp (current_function_name, XSTR (op, 0)));
}
int
null_epilogue ()
{
return (reload_completed
&& first_reg_to_save () == 16
&& ! romp_pushes_stack ());
}
int
constant_pool_address_operand (op, mode)
rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
return ((GET_CODE (op) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (op))
|| (GET_CODE (op) == CONST && GET_CODE (XEXP (op, 0)) == PLUS
&& GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT
&& GET_CODE (XEXP (XEXP (op, 0), 0)) == SYMBOL_REF
&& CONSTANT_POOL_ADDRESS_P (XEXP (XEXP (op, 0), 0))));
}
int
romp_symbolic_operand (op, mode)
register rtx op;
enum machine_mode mode ATTRIBUTE_UNUSED;
{
switch (GET_CODE (op))
{
case SYMBOL_REF:
case LABEL_REF:
return ! op->integrated;
case CONST:
op = XEXP (op, 0);
return (GET_CODE (XEXP (op, 0)) == SYMBOL_REF
|| GET_CODE (XEXP (op, 0)) == LABEL_REF)
&& GET_CODE (XEXP (op, 1)) == CONST_INT;
default:
return 0;
}
}
int
constant_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
switch (GET_CODE (op))
{
case LABEL_REF:
case SYMBOL_REF:
case PLUS:
case CONST:
return romp_symbolic_operand (op,mode);
case CONST_INT:
return (unsigned int) (INTVAL (op) + 0x8000) < 0x10000
|| (INTVAL (op) & 0xffff) == 0 || (INTVAL (op) & 0xffff0000) == 0;
default:
return 0;
}
}
int
reg_or_cint_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) == CONST_INT)
return constant_operand (op, mode);
return register_operand (op, mode);
}
int
reg_or_any_cint_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
return GET_CODE (op) == CONST_INT || register_operand (op, mode);
}
int
reg_or_D_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) == CONST_INT)
return (unsigned) (INTVAL (op) + 0x8000) < 0x10000;
return register_operand (op, mode);
}
int
reg_or_add_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
return reg_or_D_operand (op, mode) || romp_symbolic_operand (op, mode)
|| (GET_CODE (op) == CONST_INT && (INTVAL (op) & 0xffff) == 0);
}
int
reg_or_and_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
if (reg_or_cint_operand (op, mode))
return 1;
if (GET_CODE (op) != CONST_INT)
return 0;
return (INTVAL (op) & 0xffff) == 0xffff
|| (INTVAL (op) & 0xffff0000) == 0xffff0000;
}
int
reg_or_mem_operand (op, mode)
register rtx op;
register enum machine_mode mode;
{
return register_operand (op, mode) || memory_operand (op, mode);
}
int
reg_or_nonsymb_mem_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
if (register_operand (op, mode))
return 1;
if (memory_operand (op, mode) && ! symbolic_memory_operand (op, mode))
return 1;
return 0;
}
int
romp_operand (op, mode)
register rtx op;
enum machine_mode mode;
{
if (GET_CODE (op) == CONST_INT)
return constant_operand (op, mode);
return general_operand (op, mode);
}
int
reg_0_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return ((mode == VOIDmode || mode == GET_MODE (op))
&& GET_CODE (op) == REG && REGNO (op) == 0);
}
int
reg_15_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return ((mode == VOIDmode || mode == GET_MODE (op))
&& GET_CODE (op) == REG && REGNO (op) == 15);
}
int
float_binary (op, mode)
register rtx op;
enum machine_mode mode;
{
if (mode != VOIDmode && mode != GET_MODE (op))
return 0;
if (GET_MODE (op) != SFmode && GET_MODE (op) != DFmode)
return 0;
switch (GET_CODE (op))
{
case PLUS:
case MINUS:
case MULT:
case DIV:
return GET_MODE (XEXP (op, 0)) == GET_MODE (op)
&& GET_MODE (XEXP (op, 1)) == GET_MODE (op);
default:
return 0;
}
}
int
float_unary (op, mode)
register rtx op;
enum machine_mode mode;
{
if (mode != VOIDmode && mode != GET_MODE (op))
return 0;
if (GET_MODE (op) != SFmode && GET_MODE (op) != DFmode)
return 0;
return (GET_CODE (op) == NEG || GET_CODE (op) == ABS)
&& GET_MODE (XEXP (op, 0)) == GET_MODE (op);
}
int
float_conversion (op, mode)
register rtx op;
enum machine_mode mode;
{
if (mode != VOIDmode && mode != GET_MODE (op))
return 0;
switch (GET_CODE (op))
{
case FLOAT_TRUNCATE:
return GET_MODE (op) == SFmode && GET_MODE (XEXP (op, 0)) == DFmode;
case FLOAT_EXTEND:
return GET_MODE (op) == DFmode && GET_MODE (XEXP (op, 0)) == SFmode;
case FLOAT:
return ((GET_MODE (XEXP (op, 0)) == SImode
|| GET_CODE (XEXP (op, 0)) == CONST_INT)
&& (GET_MODE (op) == SFmode || GET_MODE (op) == DFmode));
case FIX:
return ((GET_MODE (op) == SImode
|| GET_CODE (XEXP (op, 0)) == CONST_INT)
&& (GET_MODE (XEXP (op, 0)) == SFmode
|| GET_MODE (XEXP (op, 0)) == DFmode));
default:
return 0;
}
}
void
print_operand (file, x, code)
FILE *file;
rtx x;
int code;
{
int i;
switch (code)
{
case 'B':
if (GET_CODE (x) != CONST_INT)
output_operand_lossage ("invalid %%B value");
fprintf (file, "%d", INTVAL (x) / 8);
break;
case 'L':
if (GET_CODE (x) != CONST_INT)
output_operand_lossage ("invalid %%L value");
fprintf (file, "%d", INTVAL (x) & 0xffff);
break;
case 's':
if (GET_CODE (x) != CONST_INT)
output_operand_lossage ("invalid %%s value");
if (INTVAL (x) >= 16)
fprintf (file, "16");
break;
case 'S':
if (GET_CODE (x) != CONST_INT)
output_operand_lossage ("invalid %%S value");
fprintf (file, "%d", INTVAL (x) & 15);
break;
case 'b':
if (GET_CODE (x) != CONST_INT)
output_operand_lossage ("invalid %%b value");
if ((i = exact_log2 (INTVAL (x))) >= 0)
fprintf (file, "%d", (31 - i) % 16);
else if ((i = exact_log2 (~ INTVAL (x))) >= 0)
fprintf (file, "%d", (31 - i) % 16);
else
output_operand_lossage ("invalid %%b value");
break;
case 'h':
if (GET_CODE (x) != CONST_INT)
output_operand_lossage ("invalid %%h value");
if ((INTVAL (x) & 0xffff0000) == 0)
fprintf (file, "l");
else if ((INTVAL (x) & 0xffff) == 0)
fprintf (file, "u");
else
output_operand_lossage ("invalid %%h value");
break;
case 'H':
if (GET_CODE (x) != CONST_INT)
output_operand_lossage ("invalid %%H value");
if ((INTVAL (x) & 0xffff0000) == 0)
fprintf (file, "%d", INTVAL (x) & 0xffff);
else if ((INTVAL (x) & 0xffff) == 0)
fprintf (file, "%d", (INTVAL (x) >> 16) & 0xffff);
else
output_operand_lossage ("invalid %%H value");
break;
case 'z':
if (GET_CODE (x) != CONST_INT)
output_operand_lossage ("invalid %%z value");
if ((INTVAL (x) & 0xffff0000) == 0)
fprintf (file, "lz");
else if ((INTVAL (x) & 0xffff0000) == 0xffff0000)
fprintf (file, "lo");
else if ((INTVAL (x) & 0xffff) == 0)
fprintf (file, "uz");
else if ((INTVAL (x) & 0xffff) == 0xffff)
fprintf (file, "uo");
else
output_operand_lossage ("invalid %%z value");
break;
case 'Z':
if (GET_CODE (x) != CONST_INT)
output_operand_lossage ("invalid %%Z value");
if ((INTVAL (x) & 0xffff0000) == 0
|| (INTVAL (x) & 0xffff0000) == 0xffff0000)
fprintf (file, "%d", INTVAL (x) & 0xffff);
else if ((INTVAL (x) & 0xffff) == 0 || (INTVAL (x) & 0xffff) == 0xffff)
fprintf (file, "%d", (INTVAL (x) >> 16) & 0xffff);
else
output_operand_lossage ("invalid %%Z value");
break;
case 'k':
if (GET_CODE (x) != CONST_INT)
output_operand_lossage ("invalid %%k value");
if ((INTVAL (x) & 0xffff0000) == 0
|| (INTVAL (x) & 0xffff0000) == 0xffff0000)
fprintf (file, "l");
else if ((INTVAL (x) & 0xffff) == 0
|| (INTVAL (x) & 0xffff) == 0xffff)
fprintf (file, "u");
else
output_operand_lossage ("invalid %%k value");
break;
case 't':
if (GET_CODE (x) != CONST_INT)
output_operand_lossage ("invalid %%k value");
if (INTVAL (x) < 16)
fprintf (file, "u");
else
fprintf (file, "l");
break;
case 'M':
if (short_memory_operand (x, VOIDmode))
fprintf (file, "s");
break;
case 'N':
if (zero_memory_operand (x, VOIDmode))
fprintf (file, "s");
break;
case 'O':
if (GET_CODE (x) == REG)
fprintf (file, "%s", reg_names[REGNO (x) + 1]);
else if (GET_CODE (x) == MEM)
print_operand (file, gen_rtx_MEM (GET_MODE (x),
plus_constant (XEXP (x, 0), 4)), 0);
else
abort ();
break;
case 'C':
if (! constant_pool_address_operand (x, VOIDmode))
abort ();
if (GET_CODE (x) == SYMBOL_REF)
fprintf (file, "%d", get_pool_offset (x) + 12);
else
fprintf (file, "%d",
(get_pool_offset (XEXP (XEXP (x, 0), 0)) + 12
+ INTVAL (XEXP (XEXP (x, 0), 1))));
break;
case 'j':
switch (GET_CODE (x))
{
case EQ:
if (cc_status.flags & CC_IN_TB)
fprintf (file, "ntb");
else
fprintf (file, "eq");
break;
case NE:
if (cc_status.flags & CC_IN_TB)
fprintf (file, "tb");
else
fprintf (file, "ne");
break;
case GT:
case GTU:
fprintf (file, "h");
break;
case LT:
case LTU:
fprintf (file, "l");
break;
case GE:
case GEU:
fprintf (file, "he");
break;
case LE:
case LEU:
fprintf (file, "le");
break;
default:
output_operand_lossage ("invalid %%j value");
}
break;
case 'J':
switch (GET_CODE (x))
{
case EQ:
if (cc_status.flags & CC_IN_TB)
fprintf (file, "tb");
else
fprintf (file, "ne");
break;
case NE:
if (cc_status.flags & CC_IN_TB)
fprintf (file, "ntb");
else
fprintf (file, "eq");
break;
case GT:
case GTU:
fprintf (file, "le");
break;
case LT:
case LTU:
fprintf (file, "he");
break;
case GE:
case GEU:
fprintf (file, "l");
break;
case LE:
case LEU:
fprintf (file, "h");
break;
default:
output_operand_lossage ("invalid %%j value");
}
break;
case '.':
break;
case '#':
if (dbr_sequence_length ())
fprintf (file, "x");
break;
case 0:
if (GET_CODE (x) == REG)
fprintf (file, "%s", reg_names[REGNO (x)]);
else if (GET_CODE (x) == MEM)
{
if (GET_CODE (XEXP (x, 0)) == SYMBOL_REF
&& current_function_operand (x, Pmode))
fprintf (file, "r14");
else
output_address (XEXP (x, 0));
}
else
output_addr_const (file, x);
break;
default:
output_operand_lossage ("invalid %%xn code");
}
}
int
first_reg_to_save()
{
int first_reg;
for (first_reg = 6; first_reg <= 15; first_reg++)
if (regs_ever_live[first_reg])
break;
if (first_reg > 14 && romp_using_r14 ())
first_reg = 14;
return first_reg;
}
int
romp_sa_size ()
{
int size;
int i;
size = 4 + 4 + 1 + (16 - first_reg_to_save ());
for (i = 2 + (TARGET_FP_REGS != 0); i <= 7; i++)
if (regs_ever_live[i + 17])
{
size += 18;
break;
}
return size * 4;
}
int
romp_makes_calls ()
{
rtx insn;
for (insn = get_insns (); insn; insn = next_insn (insn))
{
if (GET_CODE (insn) == CALL_INSN)
return 1;
else if (GET_CODE (insn) == INSN)
{
rtx body = PATTERN (insn);
if (GET_CODE (body) != USE && GET_CODE (body) != CLOBBER
&& GET_CODE (body) != ADDR_VEC
&& GET_CODE (body) != ADDR_DIFF_VEC
&& get_attr_type (insn) == TYPE_FP)
return 1;
}
}
return 0;
}
int
romp_using_r14 ()
{
return (write_symbols != NO_DEBUG || current_function_profile
|| get_pool_size () != 0 || romp_makes_calls ());
}
int
romp_pushes_stack ()
{
return (frame_pointer_needed || write_symbols != NO_DEBUG
|| (romp_sa_size () + get_frame_size ()) > 100
|| romp_makes_calls ());
}
static void
romp_output_function_prologue (file, size)
FILE *file;
HOST_WIDE_INT size;
{
int first_reg;
int reg_save_offset;
HOST_WIDE_INT fp_save = size + current_function_outgoing_args_size;
init_fpops ();
size += romp_sa_size () + current_function_outgoing_args_size;
first_reg = first_reg_to_save();
reg_save_offset = - (4 + 4 + 1 + (16 - first_reg)) * 4;
if (first_reg == 15)
fprintf (file, "\tst r15,%d(r1)\n", reg_save_offset);
else if (first_reg < 16)
fprintf (file, "\tstm r%d,%d(r1)\n", first_reg, reg_save_offset);
if (romp_using_r14 ())
fprintf (file, "\tcas r14,r0,r0\n");
if (frame_pointer_needed)
fprintf (file, "\tcal r13,-%d(r1)\n", romp_sa_size () + 64);
if (romp_pushes_stack ())
{
if (size >= 32768)
{
if (size >= 65536)
{
fprintf (file, "\tcau r0,%d(r0)\n", size >> 16);
fprintf (file, "\toil r0,r0,%d\n", size & 0xffff);
}
else
fprintf (file, "\tcal16 r0,%d(r0)\n", size);
fprintf (file, "\ts r1,r0\n");
}
else
fprintf (file, "\tcal r1,-%d(r1)\n", size);
}
output_loadsave_fpregs (file, USE,
plus_constant (stack_pointer_rtx, fp_save));
}
void
output_encoded_offset (file, reg_offset)
FILE *file;
unsigned reg_offset;
{
reg_offset = (reg_offset + 3) / 4;
if (reg_offset < (1 << 6))
fprintf (file, "\t.byte %d\n", reg_offset);
else if (reg_offset < (1 << (6 + 8)))
fprintf (file, "\t.short %d\n", (1 << (6 + 8)) + reg_offset);
else if (reg_offset < (1 << (6 + 8 + 8)))
{
fprintf (file, "\t.byte %d\n", (2 << 6) + (reg_offset >> ( 6+ 8)));
fprintf (file, "\t.short %d\n", reg_offset % (1 << (6 + 8)));
}
else
{
fprintf (file, "\t.short %d", (3 << (6 + 8)) + (reg_offset >> (6 + 8)));
fprintf (file, "\t.short %d\n", reg_offset % (1 << (6 + 8)));
}
}
static void
romp_output_function_epilogue (file, size)
FILE *file;
HOST_WIDE_INT size;
{
int first_reg = first_reg_to_save();
int pushes_stack = romp_pushes_stack ();
int reg_save_offset = - ((16 - first_reg) + 1 + 4 + 4) * 4;
HOST_WIDE_INT total_size = (size + romp_sa_size ()
+ current_function_outgoing_args_size);
HOST_WIDE_INT fp_save = size + current_function_outgoing_args_size;
int long_frame = total_size >= 32768;
rtx insn = get_last_insn ();
int write_code = 1;
int nargs = 0;
tree argptr;
for (argptr = DECL_ARGUMENTS (current_function_decl);
argptr; argptr = TREE_CHAIN (argptr))
{
int this_size = int_size_in_bytes (TREE_TYPE (argptr));
if (this_size > 0)
nargs += (this_size + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
}
if (GET_CODE (insn) == NOTE)
insn = prev_nonnote_insn (insn);
if (insn && GET_CODE (insn) == BARRIER)
write_code = 0;
if (write_code)
output_loadsave_fpregs (file, CLOBBER,
plus_constant (gen_rtx_REG (Pmode, 1), fp_save));
if (pushes_stack && ! long_frame)
reg_save_offset += total_size;
else if (long_frame && write_code)
fprintf (file, "\tcal r1,%d(r13)\n", romp_sa_size () + 64);
if (first_reg == 15 && write_code)
fprintf (file, "\tl r15,%d(r1)\n", reg_save_offset);
else if (first_reg < 16 && write_code)
fprintf (file, "\tlm r%d,%d(r1)\n", first_reg, reg_save_offset);
if (first_reg == 16) first_reg = 0;
if (pushes_stack)
{
if (write_code)
{
if (long_frame)
fprintf (file, "\tbr r15\n");
else
fprintf (file, "\tbrx r15\n\tcal r1,%d(r1)\n", total_size);
}
fprintf (file, "\t.long 0x%x\n", 0xdf07df08 + first_reg * 0x10);
if (nargs > 15) nargs = 15;
fprintf (file, "\t.byte 0x%x1\n", nargs);
output_encoded_offset (file, total_size);
}
else
{
if (write_code)
fprintf (file, "\tbr r15\n");
fprintf (file, "\t.long 0xdf02df00\n");
}
output_fpops (file);
}
struct symref_hashent {
rtx symref;
struct symref_hashent *next;
};
#define SYMHASHSIZE 151
#define HASHBITS 65535
static struct symref_hashent *symref_hash_table[SYMHASHSIZE];
rtx
get_symref (name)
register const char *name;
{
register const char *sp = name;
unsigned int hash = 0;
struct symref_hashent *p, **last_p;
while (*sp)
hash = (hash << 4) + *sp++;
hash = (hash & HASHBITS) % SYMHASHSIZE;
for (last_p = &symref_hash_table[hash], p = *last_p;
p; last_p = &p->next, p = *last_p)
if (strcmp (name, XSTR (p->symref, 0)) == 0)
break;
if (p == 0)
{
p = *last_p = (struct symref_hashent *)
xmalloc (sizeof (struct symref_hashent));
p->symref = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name));
p->next = 0;
}
return p->symref;
}
int
check_precision (opmode, op1, op2)
enum machine_mode opmode;
rtx op1, op2;
{
if (opmode == SFmode)
return 1;
if (GET_CODE (op1) != FLOAT && GET_CODE (op1) != FLOAT_EXTEND)
return 1;
if (op2 && GET_CODE (op2) != FLOAT && GET_CODE (op2) != FLOAT_EXTEND)
return 1;
return 0;
}
struct fp_op {
struct fp_op *next_same_hash;
struct fp_op *next_in_mem;
int mem_offset;
short size;
short noperands;
rtx ops[3];
enum rtx_code opcode;
};
#define FP_HASH_SIZE 101
static struct fp_op *fp_hash_table[FP_HASH_SIZE];
static struct fp_op *first_fpop;
static struct fp_op *last_fpop_in_mem;
static int subr_number = 0;
int data_offset;
static unsigned int
hash_rtx (x)
register rtx x;
{
register unsigned int hash = (((int) GET_CODE (x) << 10)
+ ((int) GET_MODE (x) << 20));
register int i;
register const char *fmt = GET_RTX_FORMAT (GET_CODE (x));
for (i = 0; i < GET_RTX_LENGTH (GET_CODE (x)); i++)
if (fmt[i] == 'e')
hash += hash_rtx (XEXP (x, i));
else if (fmt[i] == 'u')
hash += (unsigned HOST_WIDE_INT) XEXP (x, i);
else if (fmt[i] == 'i')
hash += XINT (x, i);
else if (fmt[i] == 's')
hash += (unsigned HOST_WIDE_INT) XSTR (x, i);
return hash;
}
const char *
output_fpop (code, op0, op1, op2, insn)
enum rtx_code code;
rtx op0, op1, op2;
rtx insn ATTRIBUTE_UNUSED;
{
static char outbuf[40];
unsigned int hash, hash0, hash1, hash2;
int size, i;
register struct fp_op *fpop, *last_fpop;
int dyadic = (op2 != 0);
enum machine_mode opmode;
int noperands;
rtx tem;
unsigned int tem_hash;
int fr0_avail = 0;
hash0 = op0 ? hash_rtx (op0) : 0;
hash1 = op1 ? hash_rtx (op1) : 0;
hash2 = op2 ? hash_rtx (op2) : 0;
if (hash0 > hash1 && code == EQ)
{
tem = op0; op0 = op1; op1 = tem;
tem_hash = hash0; hash0 = hash1; hash1 = tem_hash;
}
else if (hash1 > hash2 && (code == PLUS || code == MULT))
{
tem = op1; op1 = op2; op2 = tem;
tem_hash = hash1; hash1 = hash2; hash2 = tem_hash;
}
if (op2 && (code == PLUS || code == MULT)
&& (rtx_equal_p (op0, op2)
|| (GET_CODE (op0) == REG && GET_CODE (op2) == REG
&& REGNO (op0) == REGNO (op2))))
{
tem = op1; op1 = op2; op2 = tem;
tem_hash = hash1; hash1 = hash2; hash2 = tem_hash;
}
if (op1 && rtx_equal_p (op0, op1)
&& code != EQ && code != GE && code != SET
&& ((GET_MODE (op1) != SFmode && GET_MODE (op1) != SImode)
|| GET_CODE (op0) != REG || FP_REGNO_P (REGNO (op0))))
{
op1 = op2;
op2 = 0;
}
noperands = 1 + (op1 != 0) + (op2 != 0);
hash = ((int) code << 13) + (hash0 << 2) + (hash1 << 1) + hash2;
hash %= FP_HASH_SIZE;
for (fpop = fp_hash_table[hash], last_fpop = 0;
fpop;
last_fpop = fpop, fpop = fpop->next_same_hash)
if (fpop->opcode == code && noperands == fpop->noperands
&& (op0 == 0 || rtx_equal_p (op0, fpop->ops[0]))
&& (op1 == 0 || rtx_equal_p (op1, fpop->ops[1]))
&& (op2 == 0 || rtx_equal_p (op2, fpop->ops[2])))
goto win;
fpop = (struct fp_op *) xmalloc (sizeof (struct fp_op));
fpop->mem_offset = data_offset;
fpop->opcode = code;
fpop->noperands = noperands;
fpop->ops[0] = op0;
fpop->ops[1] = op1;
fpop->ops[2] = op2;
size = 24;
if (op0 && (GET_CODE (op0) != REG
|| ! FP_REGNO_P (REGNO (op0)) || REGNO (op0) == 23))
size += 32;
if (op1 && (GET_CODE (op1) != REG
|| ! FP_REGNO_P (REGNO (op1)) || REGNO (op1) == 23))
size += 32;
if (op2 && (GET_CODE (op2) != REG
|| ! FP_REGNO_P (REGNO (op2)) || REGNO (op2) == 23))
size += 32;
if ((op0 && GET_MODE (op0) == DFmode)
|| (op1 && GET_MODE (op1) == DFmode)
|| (op2 && GET_MODE (op2) == DFmode))
opmode = DFmode;
else
opmode = SFmode;
if (op0 && GET_MODE (op0) != opmode)
size += 12;
if (op1 && GET_MODE (op1) != opmode)
size += 12;
if (op2 && GET_MODE (op2) != opmode)
size += 12;
if (op2 && GET_MODE (op0) != GET_MODE (op2))
size += 12;
if (code == EQ)
size += 32;
else if (code == GE)
size += 64;
else if (code == USE || code == CLOBBER)
{
size = 36 - 24;
for (i = 0; i <= 7; i++)
if (INTVAL (op0) & (1 << (7-i)))
size += 24 + 8 * (i == 7);
}
size +=16;
i = 0;
if (op0 && GET_CODE (op0) == REG && FP_REGNO_P (REGNO (op0)))
i++;
if (op1 && GET_CODE (op1) == REG && FP_REGNO_P (REGNO (op1)))
i++;
if (op2 && GET_CODE (op2) == REG && FP_REGNO_P (REGNO (op2)))
i++;
if ((op0 == 0 || GET_CODE (op0) != REG || REGNO(op0) != 17)
&& (op1 == 0 || GET_CODE (op1) != REG || REGNO(op1) != 17)
&& (op2 == 0 || GET_CODE (op2) != REG || REGNO(op2) != 17))
fr0_avail = 1;
if (dyadic)
{
if (i == 0)
size += fr0_avail ? 64 : 112;
else if (fpop->noperands == 2 && i == 1)
size += fr0_avail ? 0 : 64;
else if (fpop->noperands == 3)
{
if (GET_CODE (op0) == REG && FP_REGNO_P (REGNO (op0))
&& GET_CODE (op2) == REG && FP_REGNO_P (REGNO (op2)))
{
if (REGNO (op0) == REGNO (op2))
#if 1
abort ();
#else
size += fr0_avail ? 0 : 64;
#endif
}
else
{
i = 0;
if (GET_CODE (op0) == REG && FP_REGNO_P (REGNO (op0)))
i++;
if (GET_CODE (op2) == REG && FP_REGNO_P (REGNO (op2)))
i++;
if (i == 0)
size += fr0_avail ? 64 : 112;
else if (i == 1)
size += fr0_avail ? 0 : 64;
}
}
}
else if (code != USE && code != CLOBBER
&& (GET_CODE (op0) != REG || ! FP_REGNO_P (REGNO (op0))))
size += 64;
if (! TARGET_FULL_FP_BLOCKS)
{
size = 12;
if (code == USE || code == CLOBBER)
size += 2;
else
{
if (op0) size += 2;
if (op1) size += 2;
if (op2) size += 2;
}
if (size % UNITS_PER_WORD)
size += 2;
if (code != USE && code != CLOBBER && op0 && GET_CODE (op0) != REG)
size += 4;
if (op1 && GET_CODE (op1) != REG)
size += 4;
if (op2 && GET_CODE (op2) != REG)
size += 4;
if (code != USE && code != CLOBBER &&
op0 && GET_CODE (op0) == CONST_DOUBLE && GET_MODE (op0) == DFmode)
size += 4;
if (op1 && GET_CODE (op1) == CONST_DOUBLE && GET_MODE (op1) == DFmode)
size += 4;
if (op2 && GET_CODE (op2) == CONST_DOUBLE && GET_MODE (op2) == DFmode)
size += 4;
}
fpop->size = size;
data_offset += size / UNITS_PER_WORD;
fpop->next_in_mem = 0;
fpop->next_same_hash = 0;
if (last_fpop_in_mem)
last_fpop_in_mem->next_in_mem = fpop;
else
first_fpop = fpop;
last_fpop_in_mem = fpop;
if (last_fpop)
last_fpop->next_same_hash = fpop;
else
fp_hash_table[hash] = fpop;
win:
if (fpop->mem_offset < 32768 / UNITS_PER_WORD)
sprintf (outbuf, "cal r15,%d(r14)\n\tbalr%s r15,r15",
fpop->mem_offset * UNITS_PER_WORD,
dbr_sequence_length () ? "x" : "");
else
sprintf (outbuf, "get r15,$L%dF%d\n\tbalr%s r15,r15",
subr_number, fpop->mem_offset * UNITS_PER_WORD,
dbr_sequence_length () ? "x" : "");
return outbuf;
}
static void
output_loadsave_fpregs (file, code, addr)
FILE *file;
enum rtx_code code;
rtx addr;
{
register int i;
register int mask = 0;
for (i = 2 + (TARGET_FP_REGS != 0); i <= 7; i++)
if (regs_ever_live[i + 17])
mask |= 1 << (7 - i);
if (mask)
fprintf (file, "\t%s\n",
output_fpop (code, GEN_INT (mask), gen_rtx_MEM (Pmode, addr),
0, const0_rtx));
}
static void
output_fpops (file)
FILE *file;
{
register struct fp_op *fpop;
register int size_so_far;
register int i;
rtx immed[3];
if (first_fpop == 0)
return;
data_section ();
ASM_OUTPUT_ALIGN (file, 2);
for (fpop = first_fpop; fpop; fpop = fpop->next_in_mem)
{
if (fpop->mem_offset < 32768 / UNITS_PER_WORD)
fprintf (file, "# data area offset = %d\n",
fpop->mem_offset * UNITS_PER_WORD);
else
fprintf (file, "L%dF%d:\n",
subr_number, fpop->mem_offset * UNITS_PER_WORD);
fprintf (file, "\tcas r0,r15,r0\n");
fprintf (file, "\t.long FPGLUE\n");
switch (fpop->opcode)
{
case USE:
fprintf (file, "\t.byte 0x1d\t# STOREM\n");
break;
case CLOBBER:
fprintf (file, "\t.byte 0x0f\t# LOADM\n");
break;
case ABS:
fprintf (file, "\t.byte 0x00\t# ABS\n");
break;
case PLUS:
fprintf (file, "\t.byte 0x02\t# ADD\n");
break;
case EQ:
fprintf (file, "\t.byte 0x07\t# CMP\n");
break;
case GE:
fprintf (file, "\t.byte 0x08\t# CMPT\n");
break;
case DIV:
fprintf (file, "\t.byte 0x0c\t# DIV\n");
break;
case SET:
fprintf (file, "\t.byte 0x14\t# MOVE\n");
break;
case MULT:
fprintf (file, "\t.byte 0x15\t# MUL\n");
break;
case NEG:
fprintf (file, "\t.byte 0x16\t# NEG\n");
break;
case SQRT:
fprintf (file, "\t.byte 0x1c\t# SQRT\n");
break;
case MINUS:
fprintf (file, "\t.byte 0x1e\t# SUB\n");
break;
default:
abort ();
}
fprintf (file, "\t.byte %d\n", fpop->noperands);
fprintf (file, "\t.short 0x8001\n");
if ((fpop->ops[0] == 0
|| GET_CODE (fpop->ops[0]) != REG || REGNO(fpop->ops[0]) != 17)
&& (fpop->ops[1] == 0 || GET_CODE (fpop->ops[1]) != REG
|| REGNO(fpop->ops[1]) != 17)
&& (fpop->ops[2] == 0 || GET_CODE (fpop->ops[2]) != REG
|| REGNO(fpop->ops[2]) != 17))
fprintf (file, "\t.byte %d, 0x80\n", fpop->size);
else
fprintf (file, "\t.byte %d, 0\n", fpop->size);
size_so_far = 12;
for (i = 0; i < fpop->noperands; i++)
{
register int type;
register int opbyte;
register const char *desc0;
char desc1[50];
immed[i] = 0;
switch (GET_MODE (fpop->ops[i]))
{
case SImode:
case VOIDmode:
desc0 = "int";
type = 0;
break;
case SFmode:
desc0 = "float";
type = 2;
break;
case DFmode:
desc0 = "double";
type = 3;
break;
default:
abort ();
}
switch (GET_CODE (fpop->ops[i]))
{
case REG:
strcpy(desc1, reg_names[REGNO (fpop->ops[i])]);
if (FP_REGNO_P (REGNO (fpop->ops[i])))
{
type += 0x10;
opbyte = REGNO (fpop->ops[i]) - 17;
}
else
{
type += 0x00;
opbyte = REGNO (fpop->ops[i]);
if (type == 3)
opbyte = (opbyte << 4) + opbyte + 1;
}
break;
case MEM:
type += 0x30;
if (GET_CODE (XEXP (fpop->ops[i], 0)) == PLUS)
{
immed[i] = XEXP (XEXP (fpop->ops[i], 0), 1);
opbyte = REGNO (XEXP (XEXP (fpop->ops[i], 0), 0));
if (GET_CODE (immed[i]) == CONST_INT)
sprintf (desc1, "%d(%s)", INTVAL (immed[i]),
reg_names[opbyte]);
else
sprintf (desc1, "<memory> (%s)", reg_names[opbyte]);
}
else if (GET_CODE (XEXP (fpop->ops[i], 0)) == REG)
{
opbyte = REGNO (XEXP (fpop->ops[i], 0));
immed[i] = const0_rtx;
sprintf (desc1, "(%s)", reg_names[opbyte]);
}
else
{
immed[i] = XEXP (fpop->ops[i], 0);
opbyte = 0;
sprintf(desc1, "<memory>");
}
break;
case CONST_INT:
case CONST_DOUBLE:
case CONST:
case SYMBOL_REF:
case LABEL_REF:
type += 0x20;
opbyte = 0;
immed[i] = fpop->ops[i];
desc1[0] = '$';
desc1[1] = '\0';
break;
default:
abort ();
}
if (i == 0 && (fpop->opcode == USE || fpop->opcode == CLOBBER))
type = 0xff, opbyte = INTVAL (fpop->ops[0]), immed[i] = 0;
fprintf (file, "\t.byte 0x%x,0x%x # (%s) %s\n",
type, opbyte, desc0, desc1);
size_so_far += 2;
}
if (size_so_far % UNITS_PER_WORD)
{
fprintf (file, "\t.space 2\n");
size_so_far += 2;
}
for (i = 0; i < fpop->noperands; i++)
if (immed[i])
switch (GET_MODE (immed[i]))
{
case SImode:
case VOIDmode:
size_so_far += 4;
fprintf (file, "\t.long ");
output_addr_const (file, immed[i]);
fprintf (file, "\n");
break;
case DFmode:
size_so_far += 4;
case SFmode:
size_so_far += 4;
if (GET_CODE (immed[i]) == CONST_DOUBLE)
{
REAL_VALUE_TYPE r;
REAL_VALUE_FROM_CONST_DOUBLE (r, immed[i]);
assemble_real (r, GET_MODE (immed[i]),
GET_MODE_ALIGNMENT (GET_MODE (immed[i])));
}
else
abort ();
break;
default:
abort ();
}
if (size_so_far != fpop->size)
{
if (TARGET_FULL_FP_BLOCKS)
fprintf (file, "\t.space %d\n", fpop->size - size_so_far);
else
abort ();
}
}
subr_number++;
text_section ();
}
static void
init_fpops()
{
register int i;
first_fpop = last_fpop_in_mem = 0;
for (i = 0; i < FP_HASH_SIZE; i++)
fp_hash_table[i] = 0;
}
int
romp_debugger_auto_correction(offset)
int offset;
{
int fp_to_sp;
INITIAL_ELIMINATION_OFFSET (FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM,
fp_to_sp);
return -(offset+fp_to_sp+4);
}
int
romp_debugger_arg_correction (offset)
int offset;
{
int fp_to_argp;
INITIAL_ELIMINATION_OFFSET (ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM,
fp_to_argp);
return (offset - fp_to_argp);
}
void
romp_initialize_trampoline (tramp, fnaddr, cxt)
rtx tramp, fnaddr, cxt;
{
rtx addr, temp, val;
temp = expand_simple_binop (SImode, PLUS, tramp, GEN_INT (4),
0, 1, OPTAB_LIB_WIDEN);
emit_move_insn (gen_rtx_MEM (SImode, memory_address (SImode, tramp)), temp);
val = force_reg (SImode, cxt);
addr = memory_address (HImode, plus_constant (tramp, 10));
emit_move_insn (gen_rtx_MEM (HImode, addr), gen_lowpart (HImode, val));
temp = expand_shift (RSHIFT_EXPR, SImode, val, build_int_2 (16, 0), 0, 1);
addr = memory_address (HImode, plus_constant (tramp, 6));
emit_move_insn (gen_rtx_MEM (HImode, addr), gen_lowpart (HImode, temp));
val = force_reg (SImode, fnaddr);
addr = memory_address (HImode, plus_constant (tramp, 24));
emit_move_insn (gen_rtx_MEM (HImode, addr), gen_lowpart (HImode, val));
temp = expand_shift (RSHIFT_EXPR, SImode, val, build_int_2 (16, 0), 0, 1);
addr = memory_address (HImode, plus_constant (tramp, 20));
emit_move_insn (gen_rtx_MEM (HImode, addr), gen_lowpart (HImode, temp));
}
static void
romp_select_rtx_section (mode, x, align)
enum machine_mode mode ATTRIBUTE_UNUSED;
rtx x ATTRIBUTE_UNUSED;
unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED;
{
data section ();
}
static void
romp_encode_section_info (decl, first)
tree decl;
int first ATTRIBUTE_UNUSED;
{
if (TREE_CODE (TREE_TYPE (decl)) == FUNCTION_TYPE)
SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1;
}