1998-03-29 12:14:27 +04:00
|
|
|
/* Subroutines for insn-output.c for the Gmicro.
|
|
|
|
Ported by Masanobu Yuhara, Fujitsu Laboratories LTD.
|
|
|
|
(yuhara@flab.fujitsu.co.jp)
|
|
|
|
|
1998-08-16 21:35:45 +04:00
|
|
|
Copyright (C) 1990, 1991, 1997 Free Software Foundation, Inc.
|
1998-03-29 12:14:27 +04:00
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
Among other things, the copyright
|
|
|
|
notice and this notice must be preserved on all copies.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with GNU CC; see the file COPYING. If not, write to
|
|
|
|
the Free Software Foundation, 59 Temple Place - Suite 330,
|
|
|
|
Boston, MA 02111-1307, USA. */
|
|
|
|
|
|
|
|
|
|
|
|
#include "config.h"
|
1998-08-16 21:35:45 +04:00
|
|
|
#include <stdio.h>
|
1998-03-29 12:14:27 +04:00
|
|
|
#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"
|
|
|
|
|
|
|
|
extern char *rtx_name[];
|
|
|
|
|
|
|
|
mypr (s, a1, a2, a3, a4, a5)
|
|
|
|
char *s;
|
|
|
|
int a1, a2, a3, a4, a5;
|
|
|
|
{
|
|
|
|
fprintf (stderr, s, a1, a2, a3, a4, a5);
|
|
|
|
}
|
|
|
|
|
|
|
|
myprcode (i)
|
|
|
|
int i;
|
|
|
|
{
|
|
|
|
if (i < 0 || i > 90)
|
|
|
|
fprintf (stderr, "code = %d\n", i);
|
|
|
|
else
|
|
|
|
fprintf (stderr, "code = %s\n", rtx_name[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
myabort (i)
|
|
|
|
int i;
|
|
|
|
{
|
|
|
|
fprintf (stderr, "myabort");
|
|
|
|
myprcode (i);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* This is how to output an ascii string. */
|
|
|
|
/* See ASM_OUTPUT_ASCII in gmicro.h. */
|
|
|
|
output_ascii (file, p, size)
|
|
|
|
FILE *file;
|
|
|
|
char *p;
|
|
|
|
int size;
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int in_quote = 0;
|
|
|
|
register int c;
|
|
|
|
|
|
|
|
fprintf (file, "\t.sdata ");
|
|
|
|
|
|
|
|
for (i = 0; i < size; i++)
|
|
|
|
{
|
|
|
|
c = p[i];
|
|
|
|
if (c >= ' ' && c < 0x7f)
|
|
|
|
{
|
|
|
|
if (!in_quote)
|
|
|
|
{
|
|
|
|
putc ('"', file);
|
|
|
|
in_quote = 1;
|
|
|
|
}
|
|
|
|
putc (c, file);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (in_quote)
|
|
|
|
{
|
|
|
|
putc ('"', file);
|
|
|
|
in_quote = 0;
|
|
|
|
}
|
|
|
|
fprintf (file, "<%d>", c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (in_quote)
|
|
|
|
putc ('"', file);
|
|
|
|
putc ('\n', file);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* call this when GET_CODE (index) is MULT. */
|
|
|
|
print_scaled_index (file, index)
|
|
|
|
FILE *file;
|
|
|
|
register rtx index;
|
|
|
|
{
|
|
|
|
register rtx ireg;
|
|
|
|
int scale;
|
|
|
|
|
|
|
|
if (GET_CODE (XEXP (index, 0)) == REG)
|
|
|
|
{
|
|
|
|
ireg = XEXP (index, 0);
|
|
|
|
scale = INTVAL (XEXP (index, 1));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ireg = XEXP (index, 1);
|
|
|
|
scale = INTVAL (XEXP (index, 0));
|
|
|
|
}
|
|
|
|
if (scale == 1)
|
|
|
|
fprintf (file, "%s", reg_names[REGNO (ireg)]);
|
|
|
|
else
|
|
|
|
fprintf (file, "%s*%d", reg_names[REGNO (ireg)], scale);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
print_operand_address (file, addr)
|
|
|
|
FILE *file;
|
|
|
|
register rtx addr;
|
|
|
|
{
|
|
|
|
register rtx xtmp0, xtmp1, breg, ixreg;
|
|
|
|
int scale;
|
|
|
|
int needcomma = 0;
|
|
|
|
rtx offset;
|
|
|
|
|
|
|
|
fprintf (file, "@");
|
|
|
|
retry:
|
|
|
|
switch (GET_CODE (addr))
|
|
|
|
{
|
|
|
|
case MEM:
|
|
|
|
fprintf (file, "@");
|
|
|
|
addr = XEXP (addr, 0);
|
|
|
|
goto retry;
|
|
|
|
|
|
|
|
case REG:
|
|
|
|
fprintf (file, "%s", reg_names[REGNO (addr)]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MULT:
|
|
|
|
print_scaled_index (file, addr);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PRE_DEC:
|
|
|
|
fprintf (file, "-%s", reg_names[REGNO (XEXP (addr, 0))]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case POST_INC:
|
|
|
|
fprintf (file, "%s+", reg_names[REGNO (XEXP (addr, 0))]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PLUS:
|
|
|
|
xtmp0 = XEXP (addr, 0);
|
|
|
|
xtmp1 = XEXP (addr, 1);
|
|
|
|
ixreg = 0; breg = 0;
|
|
|
|
offset = 0;
|
|
|
|
if (CONSTANT_ADDRESS_P (xtmp0))
|
|
|
|
{
|
|
|
|
offset = xtmp0;
|
|
|
|
breg = xtmp1;
|
|
|
|
}
|
|
|
|
else if (CONSTANT_ADDRESS_P (xtmp1))
|
|
|
|
{
|
|
|
|
offset = xtmp1;
|
|
|
|
breg = xtmp0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
goto NOT_DISP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (REG_CODE_BASE_P (breg))
|
|
|
|
goto PRINT_MEM;
|
|
|
|
|
|
|
|
if (GET_CODE (breg) == MULT)
|
|
|
|
{
|
|
|
|
if (REG_CODE_INDEX_P (XEXP (breg, 0)))
|
|
|
|
{
|
|
|
|
ixreg = XEXP (breg, 0);
|
|
|
|
scale = INTVAL (XEXP (breg, 1));
|
|
|
|
breg = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ixreg = XEXP (breg, 1);
|
|
|
|
scale = INTVAL (XEXP (breg, 0));
|
|
|
|
breg = 0;
|
|
|
|
}
|
|
|
|
goto PRINT_MEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* GET_CODE (breg) must be PLUS here. */
|
|
|
|
xtmp0 = XEXP (breg, 0);
|
|
|
|
xtmp1 = XEXP (breg, 1);
|
|
|
|
if (REG_CODE_BASE_P (xtmp0))
|
|
|
|
{
|
|
|
|
breg = xtmp0;
|
|
|
|
xtmp0 = xtmp1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
breg = xtmp1;
|
|
|
|
/* xtmp0 = xtmp0; */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GET_CODE (xtmp0) == MULT)
|
|
|
|
{
|
|
|
|
if (REG_CODE_INDEX_P (XEXP (xtmp0, 0)))
|
|
|
|
{
|
|
|
|
ixreg = XEXP (xtmp0, 0);
|
|
|
|
scale = INTVAL (XEXP (xtmp0, 1));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ixreg = XEXP (xtmp0, 1);
|
|
|
|
scale = INTVAL (XEXP (xtmp0, 0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ixreg = xtmp0;
|
|
|
|
scale = 1;
|
|
|
|
}
|
|
|
|
goto PRINT_MEM;
|
|
|
|
|
|
|
|
NOT_DISP:
|
|
|
|
if (REG_CODE_BASE_P (xtmp0))
|
|
|
|
{
|
|
|
|
breg = xtmp0;
|
|
|
|
xtmp0 = xtmp1;
|
|
|
|
}
|
|
|
|
else if (REG_CODE_BASE_P (xtmp1))
|
|
|
|
{
|
|
|
|
breg = xtmp1;
|
|
|
|
/* xtmp0 = xtmp0; */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
goto NOT_BASE;
|
|
|
|
|
|
|
|
if (REG_CODE_INDEX_P (xtmp0))
|
|
|
|
{
|
|
|
|
ixreg = xtmp0;
|
|
|
|
scale = 1;
|
|
|
|
goto PRINT_MEM;
|
|
|
|
}
|
|
|
|
else if (CONSTANT_ADDRESS_P (xtmp0))
|
|
|
|
{
|
|
|
|
offset = xtmp0;
|
|
|
|
goto PRINT_MEM;
|
|
|
|
}
|
|
|
|
else if (GET_CODE (xtmp0) == MULT)
|
|
|
|
{
|
|
|
|
if (REG_CODE_INDEX_P (XEXP (xtmp0, 0)))
|
|
|
|
{
|
|
|
|
ixreg = XEXP (xtmp0, 0);
|
|
|
|
scale = INTVAL (XEXP (xtmp0, 1));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ixreg = XEXP (xtmp0, 1);
|
|
|
|
scale = INTVAL (XEXP (xtmp0, 0));
|
|
|
|
}
|
|
|
|
goto PRINT_MEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* GET_CODE (xtmp0) must be PLUS. */
|
|
|
|
xtmp1 = XEXP (xtmp0, 1);
|
|
|
|
xtmp0 = XEXP (xtmp0, 0);
|
|
|
|
|
|
|
|
if (CONSTANT_ADDRESS_P (xtmp0))
|
|
|
|
{
|
|
|
|
offset = xtmp0;
|
|
|
|
xtmp0 = xtmp1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
offset = xtmp1;
|
|
|
|
/* xtmp0 = xtmp0; */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (REG_CODE_INDEX_P (xtmp0))
|
|
|
|
{
|
|
|
|
ixreg = xtmp0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ /* GET_CODE (xtmp0) must be MULT. */
|
|
|
|
if (REG_CODE_INDEX_P (XEXP (xtmp0, 0)))
|
|
|
|
{
|
|
|
|
ixreg = XEXP (xtmp0, 0);
|
|
|
|
scale = INTVAL (XEXP (xtmp0, 1));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ixreg = XEXP (xtmp0, 1);
|
|
|
|
scale = INTVAL (XEXP (xtmp0, 0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
goto PRINT_MEM;
|
|
|
|
|
|
|
|
NOT_BASE:
|
|
|
|
if (GET_CODE (xtmp0) == PLUS)
|
|
|
|
{
|
|
|
|
ixreg = xtmp1;
|
|
|
|
/* xtmp0 = xtmp0; */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ixreg = xtmp0;
|
|
|
|
xtmp0 = xtmp1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (REG_CODE_INDEX_P (ixreg))
|
|
|
|
{
|
|
|
|
scale = 1;
|
|
|
|
}
|
|
|
|
else if (REG_CODE_INDEX_P (XEXP (ixreg, 0)))
|
|
|
|
{
|
|
|
|
scale = INTVAL (XEXP (ixreg, 1));
|
|
|
|
ixreg = XEXP (ixreg, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ /* was else if with no condition. OK ??? */
|
|
|
|
scale = INTVAL (XEXP (ixreg, 0));
|
|
|
|
ixreg = XEXP (ixreg, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (REG_CODE_BASE_P (XEXP (xtmp0, 0)))
|
|
|
|
{
|
|
|
|
breg = XEXP (xtmp0, 0);
|
|
|
|
offset = XEXP (xtmp0, 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
breg = XEXP (xtmp0, 1);
|
|
|
|
offset = XEXP (xtmp0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
PRINT_MEM:
|
|
|
|
if (breg == 0 && ixreg == 0)
|
|
|
|
{
|
|
|
|
output_address (offset);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (ixreg == 0 && offset == 0)
|
|
|
|
{
|
|
|
|
fprintf (file, "%s", reg_names[REGNO (breg)]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fprintf (file, "(");
|
|
|
|
if (offset != 0)
|
|
|
|
{
|
|
|
|
output_addr_const (file, offset);
|
|
|
|
needcomma = 1;
|
|
|
|
}
|
|
|
|
if (breg != 0)
|
|
|
|
{
|
|
|
|
if (needcomma)
|
|
|
|
fprintf (file, ",");
|
|
|
|
fprintf (file, "%s", reg_names[REGNO (breg)]);
|
|
|
|
needcomma = 1;
|
|
|
|
}
|
|
|
|
if (ixreg != 0)
|
|
|
|
{
|
|
|
|
if (needcomma)
|
|
|
|
fprintf (file, ",");
|
|
|
|
fprintf (file, "%s", reg_names[REGNO (ixreg)]);
|
|
|
|
if (scale != 1)
|
|
|
|
fprintf (file,"*%d", scale);
|
|
|
|
}
|
|
|
|
fprintf (file, ")");
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
output_addr_const (file, addr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Return a REG that occurs in ADDR with coefficient 1.
|
|
|
|
ADDR can be effectively incremented by incrementing REG. */
|
|
|
|
|
|
|
|
static rtx
|
|
|
|
find_addr_reg (addr)
|
|
|
|
rtx addr;
|
|
|
|
{
|
|
|
|
while (GET_CODE (addr) == PLUS)
|
|
|
|
{
|
|
|
|
if (GET_CODE (XEXP (addr, 0)) == REG)
|
|
|
|
addr = XEXP (addr, 0);
|
|
|
|
else if (GET_CODE (XEXP (addr, 1)) == REG)
|
|
|
|
addr = XEXP (addr, 1);
|
|
|
|
else if (GET_CODE (XEXP (addr, 0)) == PLUS)
|
|
|
|
addr = XEXP (addr, 0);
|
|
|
|
else if (GET_CODE (XEXP (addr, 1)) == PLUS)
|
|
|
|
addr = XEXP (addr, 1);
|
|
|
|
}
|
|
|
|
if (GET_CODE (addr) == REG)
|
|
|
|
return addr;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Return the best assembler insn template
|
|
|
|
for moving operands[1] into operands[0] as a fullword. */
|
|
|
|
|
|
|
|
static char *
|
|
|
|
singlemove_string (operands)
|
|
|
|
rtx *operands;
|
|
|
|
{
|
|
|
|
if (FPU_REG_P (operands[0]) || FPU_REG_P (operands[1]))
|
|
|
|
{
|
|
|
|
if (GREG_P (operands[0]) || GREG_P (operands[1]))
|
|
|
|
{
|
|
|
|
myabort (101); /* Not Supported yet !! */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return "fmov.s %1,%0";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return "mov.w %1,%0";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Output assembler code to perform a doubleword move insn
|
|
|
|
with operands OPERANDS. */
|
|
|
|
|
|
|
|
char *
|
|
|
|
output_move_double (operands)
|
|
|
|
rtx *operands;
|
|
|
|
{
|
|
|
|
enum
|
|
|
|
{ REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, CNSTOP, RNDOP }
|
|
|
|
optype0, optype1;
|
|
|
|
rtx latehalf[2];
|
|
|
|
rtx addreg0 = 0, addreg1 = 0;
|
|
|
|
|
|
|
|
/* First classify both operands. */
|
|
|
|
|
|
|
|
if (REG_P (operands[0]))
|
|
|
|
optype0 = REGOP;
|
|
|
|
else if (offsettable_memref_p (operands[0]))
|
|
|
|
optype0 = OFFSOP;
|
|
|
|
else if (GET_CODE (XEXP (operands[0], 0)) == POST_INC)
|
|
|
|
optype0 = POPOP;
|
|
|
|
else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
|
|
|
|
optype0 = PUSHOP;
|
|
|
|
else if (GET_CODE (operands[0]) == MEM)
|
|
|
|
optype0 = MEMOP;
|
|
|
|
else
|
|
|
|
optype0 = RNDOP;
|
|
|
|
|
|
|
|
if (REG_P (operands[1]))
|
|
|
|
optype1 = REGOP;
|
|
|
|
else if (CONSTANT_P (operands[1]))
|
|
|
|
optype1 = CNSTOP;
|
|
|
|
else if (offsettable_memref_p (operands[1]))
|
|
|
|
optype1 = OFFSOP;
|
|
|
|
else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC)
|
|
|
|
optype1 = POPOP;
|
|
|
|
else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)
|
|
|
|
optype1 = PUSHOP;
|
|
|
|
else if (GET_CODE (operands[1]) == MEM)
|
|
|
|
optype1 = MEMOP;
|
|
|
|
else
|
|
|
|
optype1 = RNDOP;
|
|
|
|
|
|
|
|
/* Check for the cases that the operand constraints are not
|
|
|
|
supposed to allow to happen. Abort if we get one,
|
|
|
|
because generating code for these cases is painful. */
|
|
|
|
|
|
|
|
if (optype0 == RNDOP || optype1 == RNDOP)
|
|
|
|
myabort (102);
|
|
|
|
|
|
|
|
/* If one operand is decrementing and one is incrementing
|
|
|
|
decrement the former register explicitly
|
|
|
|
and change that operand into ordinary indexing. */
|
|
|
|
|
|
|
|
if (optype0 == PUSHOP && optype1 == POPOP)
|
|
|
|
{
|
|
|
|
operands[0] = XEXP (XEXP (operands[0], 0), 0);
|
|
|
|
output_asm_insn ("sub.w %#8,%0", operands);
|
|
|
|
operands[0] = gen_rtx (MEM, DImode, operands[0]);
|
|
|
|
optype0 = OFFSOP;
|
|
|
|
}
|
|
|
|
if (optype0 == POPOP && optype1 == PUSHOP)
|
|
|
|
{
|
|
|
|
operands[1] = XEXP (XEXP (operands[1], 0), 0);
|
|
|
|
output_asm_insn ("sub.w %#8,%1", operands);
|
|
|
|
operands[1] = gen_rtx (MEM, DImode, operands[1]);
|
|
|
|
optype1 = OFFSOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If an operand is an unoffsettable memory ref, find a register
|
|
|
|
we can increment temporarily to make it refer to the second word. */
|
|
|
|
|
|
|
|
if (optype0 == MEMOP)
|
|
|
|
addreg0 = find_addr_reg (operands[0]);
|
|
|
|
|
|
|
|
if (optype1 == MEMOP)
|
|
|
|
addreg1 = find_addr_reg (operands[1]);
|
|
|
|
|
|
|
|
/* Ok, we can do one word at a time.
|
|
|
|
Normally we do the low-numbered word first,
|
|
|
|
but if either operand is autodecrementing then we
|
|
|
|
do the high-numbered word first.
|
|
|
|
|
|
|
|
In either case, set up in LATEHALF the operands to use
|
|
|
|
for the high-numbered word and in some cases alter the
|
|
|
|
operands in OPERANDS to be suitable for the low-numbered word. */
|
|
|
|
|
|
|
|
if (optype0 == REGOP)
|
|
|
|
latehalf[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
|
|
|
|
else if (optype0 == OFFSOP)
|
|
|
|
latehalf[0] = adj_offsettable_operand (operands[0], 4);
|
|
|
|
else
|
|
|
|
latehalf[0] = operands[0];
|
|
|
|
|
|
|
|
if (optype1 == REGOP)
|
|
|
|
latehalf[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
|
|
|
|
else if (optype1 == OFFSOP)
|
|
|
|
latehalf[1] = adj_offsettable_operand (operands[1], 4);
|
|
|
|
else if (optype1 == CNSTOP)
|
|
|
|
{
|
|
|
|
if (GET_CODE (operands[1]) == CONST_DOUBLE)
|
|
|
|
split_double (operands[1], &operands[1], &latehalf[1]);
|
|
|
|
else if (CONSTANT_P (operands[1]))
|
|
|
|
latehalf[1] = const0_rtx;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
latehalf[1] = operands[1];
|
|
|
|
|
|
|
|
/* If insn is effectively movd N(sp),-(sp) then we will do the
|
|
|
|
high word first. We should use the adjusted operand 1 (which is N+4(sp))
|
|
|
|
for the low word as well, to compensate for the first decrement of sp. */
|
|
|
|
if (optype0 == PUSHOP
|
|
|
|
&& REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM
|
|
|
|
&& reg_overlap_mentioned_p (stack_pointer_rtx, operands[1]))
|
|
|
|
operands[1] = latehalf[1];
|
|
|
|
|
|
|
|
/* If one or both operands autodecrementing,
|
|
|
|
do the two words, high-numbered first. */
|
|
|
|
|
|
|
|
/* Likewise, the first move would clobber the source of the second one,
|
|
|
|
do them in the other order. This happens only for registers;
|
|
|
|
such overlap can't happen in memory unless the user explicitly
|
|
|
|
sets it up, and that is an undefined circumstance. */
|
|
|
|
|
|
|
|
if (optype0 == PUSHOP || optype1 == PUSHOP
|
|
|
|
|| (optype0 == REGOP && optype1 == REGOP
|
|
|
|
&& REGNO (operands[0]) == REGNO (latehalf[1])))
|
|
|
|
{
|
|
|
|
/* Make any unoffsettable addresses point at high-numbered word. */
|
|
|
|
if (addreg0)
|
|
|
|
output_asm_insn ("add.w %#4,%0", &addreg0);
|
|
|
|
if (addreg1)
|
|
|
|
output_asm_insn ("add.w %#4,%0", &addreg1);
|
|
|
|
|
|
|
|
/* Do that word. */
|
|
|
|
output_asm_insn (singlemove_string (latehalf), latehalf);
|
|
|
|
|
|
|
|
/* Undo the adds we just did. */
|
|
|
|
if (addreg0)
|
|
|
|
output_asm_insn ("sub.w %#4,%0", &addreg0);
|
|
|
|
if (addreg1)
|
|
|
|
output_asm_insn ("sub.w %#4,%0", &addreg1);
|
|
|
|
|
|
|
|
/* Do low-numbered word. */
|
|
|
|
return singlemove_string (operands);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Normal case: do the two words, low-numbered first. */
|
|
|
|
|
|
|
|
output_asm_insn (singlemove_string (operands), operands);
|
|
|
|
|
|
|
|
/* Make any unoffsettable addresses point at high-numbered word. */
|
|
|
|
if (addreg0)
|
|
|
|
output_asm_insn ("add.w %#4,%0", &addreg0);
|
|
|
|
if (addreg1)
|
|
|
|
output_asm_insn ("add.w %#4,%0", &addreg1);
|
|
|
|
|
|
|
|
/* Do that word. */
|
|
|
|
output_asm_insn (singlemove_string (latehalf), latehalf);
|
|
|
|
|
|
|
|
/* Undo the adds we just did. */
|
|
|
|
if (addreg0)
|
|
|
|
output_asm_insn ("sub.w %#4,%0", &addreg0);
|
|
|
|
if (addreg1)
|
|
|
|
output_asm_insn ("sub.w %#4,%0", &addreg1);
|
|
|
|
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Move const_double to floating point register (DF) */
|
|
|
|
char *
|
|
|
|
output_move_const_double (operands)
|
|
|
|
rtx *operands;
|
|
|
|
{
|
|
|
|
int code = standard_fpu_constant_p (operands[1]);
|
|
|
|
|
|
|
|
if (FPU_REG_P (operands[0]))
|
|
|
|
{
|
|
|
|
if (code != 0)
|
|
|
|
{
|
|
|
|
static char buf[40];
|
|
|
|
|
|
|
|
sprintf (buf, "fmvr from%d,%%0.d", code);
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return "fmov %1,%0.d";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (GREG_P (operands[0]))
|
|
|
|
{
|
|
|
|
rtx xoperands[2];
|
|
|
|
xoperands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
|
1998-08-16 21:35:45 +04:00
|
|
|
xoperands[1] = GEN_INT (CONST_DOUBLE_HIGH (operands[1]));
|
1998-03-29 12:14:27 +04:00
|
|
|
output_asm_insn ("mov.w %1,%0", xoperands);
|
1998-08-16 21:35:45 +04:00
|
|
|
operands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
|
1998-03-29 12:14:27 +04:00
|
|
|
return "mov.w %1,%0";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return output_move_double (operands); /* ?????? */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
output_move_const_single (operands)
|
|
|
|
rtx *operands;
|
|
|
|
{
|
|
|
|
int code = standard_fpu_constant_p (operands[1]);
|
|
|
|
static char buf[40];
|
|
|
|
|
|
|
|
if (FPU_REG_P (operands[0]))
|
|
|
|
{
|
|
|
|
if (code != 0)
|
|
|
|
{
|
|
|
|
sprintf (buf, "fmvr from%d,%%0.s", code);
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
return "fmov.s %f1,%0";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return "mov.w %f1,%0";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Return nonzero if X, a CONST_DOUBLE, has a value that we can get
|
|
|
|
from the "fmvr" instruction of the Gmicro FPU.
|
|
|
|
The value, anded with 0xff, gives the code to use in fmovecr
|
|
|
|
to get the desired constant. */
|
|
|
|
|
|
|
|
u.i[0] = CONST_DOUBLE_LOW (x);
|
|
|
|
u.i[1] = CONST_DOUBLE_HIGH (x);
|
|
|
|
d = u.d;
|
|
|
|
|
|
|
|
if (d == 0.0) /* +0.0 */
|
|
|
|
return 0x0;
|
|
|
|
/* Note: there are various other constants available
|
|
|
|
but it is a nuisance to put in their values here. */
|
|
|
|
if (d == 1.0) /* +1.0 */
|
|
|
|
return 0x1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Stuff that looks different if it's single or double
|
|
|
|
*/
|
|
|
|
if (GET_MODE (x) == SFmode)
|
|
|
|
{
|
|
|
|
if (d == S_PI)
|
|
|
|
return 0x2;
|
|
|
|
if (d == (S_PI / 2.0))
|
|
|
|
return 0x3;
|
|
|
|
if (d == S_E)
|
|
|
|
return 0x4;
|
|
|
|
if (d == S_LOGEof2)
|
|
|
|
return 0x5;
|
|
|
|
if (d == S_LOGEof10)
|
|
|
|
return 0x6;
|
|
|
|
if (d == S_LOG10of2)
|
|
|
|
return 0x7;
|
|
|
|
if (d == S_LOG10ofE)
|
|
|
|
return 0x8;
|
|
|
|
if (d == S_LOG2ofE)
|
|
|
|
return 0x9;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (d == D_PI)
|
|
|
|
return 0x2;
|
|
|
|
if (d == (D_PI / 2.0))
|
|
|
|
return 0x3;
|
|
|
|
if (d == D_E)
|
|
|
|
return 0x4;
|
|
|
|
if (d == D_LOGEof2)
|
|
|
|
return 0x5;
|
|
|
|
if (d == D_LOGEof10)
|
|
|
|
return 0x6;
|
|
|
|
if (d == D_LOG10of2)
|
|
|
|
return 0x7;
|
|
|
|
if (d == D_LOG10ofE)
|
|
|
|
return 0x8;
|
|
|
|
if (d == D_LOG2ofE)
|
|
|
|
return 0x9;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef S_PI
|
|
|
|
#undef D_PI
|
|
|
|
#undef S_E
|
|
|
|
#undef D_E
|
|
|
|
#undef S_LOGEof2
|
|
|
|
#undef D_LOGEof2
|
|
|
|
#undef S_LOGEof10
|
|
|
|
#undef D_LOGEof10
|
|
|
|
#undef S_LOG10of2
|
|
|
|
#undef D_LOG10of2
|
|
|
|
#undef S_LOG10ofE
|
|
|
|
#undef D_LOG10ofE
|
|
|
|
#undef S_LOG2ofE
|
|
|
|
#undef D_LOG2ofE
|
|
|
|
|
|
|
|
/* dest should be operand 0 */
|
|
|
|
/* imm should be operand 1 */
|
|
|
|
|
|
|
|
extern char *sub_imm_word ();
|
|
|
|
|
|
|
|
char *
|
|
|
|
add_imm_word (imm, dest, immp)
|
|
|
|
int imm;
|
|
|
|
rtx dest, *immp;
|
|
|
|
{
|
|
|
|
int is_reg, short_ok;
|
|
|
|
|
|
|
|
|
|
|
|
if (imm < 0)
|
|
|
|
{
|
1998-08-16 21:35:45 +04:00
|
|
|
*immp = GEN_INT (-imm);
|
1998-03-29 12:14:27 +04:00
|
|
|
return sub_imm_word (-imm, dest);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (imm == 0)
|
|
|
|
return "mov:l.w #0,%0";
|
|
|
|
|
|
|
|
short_ok = short_format_ok (dest);
|
|
|
|
|
|
|
|
if (short_ok && imm <= 8)
|
|
|
|
return "add:q %1,%0.w";
|
|
|
|
|
|
|
|
if (imm < 128)
|
|
|
|
return "add:e %1,%0.w";
|
|
|
|
|
|
|
|
is_reg = (GET_CODE (dest) == REG);
|
|
|
|
|
|
|
|
if (is_reg)
|
|
|
|
return "add:l %1,%0.w";
|
|
|
|
|
|
|
|
if (short_ok)
|
|
|
|
return "add:i %1,%0.w";
|
|
|
|
|
|
|
|
return "add %1,%0.w";
|
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
sub_imm_word (imm, dest, immp)
|
|
|
|
int imm;
|
|
|
|
rtx dest, *immp;
|
|
|
|
{
|
|
|
|
int is_reg, short_ok;
|
|
|
|
|
|
|
|
if (imm < 0 && imm != 0x80000000)
|
|
|
|
{
|
1998-08-16 21:35:45 +04:00
|
|
|
*immp = GEN_INT (-imm);
|
1998-03-29 12:14:27 +04:00
|
|
|
return add_imm_word (-imm, dest);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (imm == 0)
|
|
|
|
return "mov:z.w #0,%0";
|
|
|
|
|
|
|
|
short_ok = short_format_ok (dest);
|
|
|
|
|
|
|
|
if (short_ok && imm <= 8)
|
|
|
|
return "sub:q %1,%0.w";
|
|
|
|
|
|
|
|
if (imm < 128)
|
|
|
|
return "sub:e %1,%0.w";
|
|
|
|
|
|
|
|
is_reg = (GET_CODE (dest) == REG);
|
|
|
|
|
|
|
|
if (is_reg)
|
|
|
|
return "sub:l %1,%0.w";
|
|
|
|
|
|
|
|
if (short_ok)
|
|
|
|
return "sub:i %1,%0.w";
|
|
|
|
|
|
|
|
return "sub %1,%0.w";
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
short_format_ok (x)
|
|
|
|
rtx x;
|
|
|
|
{
|
|
|
|
rtx x0, x1;
|
|
|
|
|
|
|
|
if (GET_CODE (x) == REG)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (GET_CODE (x) == MEM
|
|
|
|
&& GET_CODE (XEXP (x, 0)) == PLUS)
|
|
|
|
{
|
|
|
|
x0 = XEXP (XEXP (x, 0), 0);
|
|
|
|
x1 = XEXP (XEXP (x, 0), 1);
|
|
|
|
return ((GET_CODE (x0) == REG
|
|
|
|
&& CONSTANT_P (x1)
|
|
|
|
&& ((unsigned) (INTVAL (x1) + 0x8000) < 0x10000))
|
|
|
|
||
|
|
|
|
(GET_CODE (x1) == REG
|
|
|
|
&& CONSTANT_P (x0)
|
|
|
|
&& ((unsigned) (INTVAL (x0) + 0x8000) < 0x10000)));
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
myoutput_sp_adjust (file, op, fsize)
|
|
|
|
FILE *file;
|
|
|
|
char *op;
|
|
|
|
int fsize;
|
|
|
|
{
|
|
|
|
if (fsize == 0)
|
|
|
|
;
|
|
|
|
else if (fsize < 8)
|
|
|
|
fprintf (file, "\t%s:q #%d,sp.w\n", op, fsize);
|
|
|
|
else if (fsize < 128)
|
|
|
|
fprintf (file, "\t%s:e #%d,sp.w\n", op, fsize);
|
|
|
|
else
|
|
|
|
fprintf (file, "\t%s:l #%d,sp.w\n", op, fsize);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
mov_imm_word (imm, dest)
|
|
|
|
int imm;
|
|
|
|
rtx dest;
|
|
|
|
{
|
|
|
|
int is_reg, short_ok;
|
|
|
|
|
|
|
|
if (imm == 0)
|
|
|
|
return "mov:z.w #0,%0";
|
|
|
|
|
|
|
|
short_ok = short_format_ok (dest);
|
|
|
|
|
|
|
|
if (short_ok && imm > 0 && imm <= 8)
|
|
|
|
return "mov:q %1,%0.w";
|
|
|
|
|
|
|
|
if (-128 <= imm && imm < 128)
|
|
|
|
return "mov:e %1,%0.w";
|
|
|
|
|
|
|
|
is_reg = (GET_CODE (dest) == REG);
|
|
|
|
|
|
|
|
if (is_reg)
|
|
|
|
return "mov:l %1,%0.w";
|
|
|
|
|
|
|
|
if (short_ok)
|
|
|
|
return "mov:i %1,%0.w";
|
|
|
|
|
|
|
|
return "mov %1,%0.w";
|
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
cmp_imm_word (imm, dest)
|
|
|
|
int imm;
|
|
|
|
rtx dest;
|
|
|
|
{
|
|
|
|
int is_reg, short_ok;
|
|
|
|
|
|
|
|
if (imm == 0)
|
|
|
|
return "cmp:z.w #0,%0";
|
|
|
|
|
|
|
|
short_ok = short_format_ok (dest);
|
|
|
|
|
|
|
|
if (short_ok && imm >0 && imm <= 8)
|
|
|
|
return "cmp:q %1,%0.w";
|
|
|
|
|
|
|
|
if (-128 <= imm && imm < 128)
|
|
|
|
return "cmp:e %1,%0.w";
|
|
|
|
|
|
|
|
is_reg = (GET_CODE (dest) == REG);
|
|
|
|
|
|
|
|
if (is_reg)
|
|
|
|
return "cmp:l %1,%0.w";
|
|
|
|
|
|
|
|
if (short_ok)
|
|
|
|
return "cmp:i %1,%0.w";
|
|
|
|
|
|
|
|
return "cmp %1,%0.w";
|
|
|
|
}
|
|
|
|
|
|
|
|
char *
|
|
|
|
push_imm_word (imm)
|
|
|
|
int imm;
|
|
|
|
{
|
|
|
|
if (imm == 0)
|
|
|
|
return "mov:z.w #0,%-";
|
|
|
|
|
|
|
|
if (imm > 0 && imm <= 8)
|
|
|
|
return "mov:q %1,%-.w";
|
|
|
|
|
|
|
|
if (-128 <= imm && imm < 128)
|
|
|
|
return "mov:e %1,%-.w";
|
|
|
|
|
|
|
|
return "mov:g %1,%-.w";
|
|
|
|
|
|
|
|
/* In some cases, g-format may be better than I format.??
|
|
|
|
return "mov %1,%0.w";
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
my_signed_comp (insn)
|
|
|
|
rtx insn;
|
|
|
|
{
|
|
|
|
rtx my_insn;
|
|
|
|
|
|
|
|
my_insn = NEXT_INSN (insn);
|
|
|
|
if (GET_CODE (my_insn) != JUMP_INSN)
|
|
|
|
{
|
|
|
|
fprintf (stderr, "my_signed_comp: Not Jump_insn ");
|
|
|
|
myabort (GET_CODE (my_insn));
|
|
|
|
}
|
|
|
|
my_insn = PATTERN (my_insn);
|
|
|
|
if (GET_CODE (my_insn) != SET)
|
|
|
|
{
|
|
|
|
fprintf (stderr, "my_signed_comp: Not Set ");
|
|
|
|
myabort (GET_CODE (my_insn));
|
|
|
|
}
|
|
|
|
my_insn = SET_SRC (my_insn);
|
|
|
|
if (GET_CODE (my_insn) != IF_THEN_ELSE)
|
|
|
|
{
|
|
|
|
fprintf (stderr, "my_signed_comp: Not if_then_else ");
|
|
|
|
myabort (GET_CODE (my_insn));
|
|
|
|
}
|
|
|
|
switch (GET_CODE (XEXP (my_insn, 0)))
|
|
|
|
{
|
|
|
|
case NE:
|
|
|
|
case EQ:
|
|
|
|
case GE:
|
|
|
|
case GT:
|
|
|
|
case LE:
|
|
|
|
case LT:
|
|
|
|
return 1;
|
|
|
|
case GEU:
|
|
|
|
case GTU:
|
|
|
|
case LEU:
|
|
|
|
case LTU:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
fprintf (stderr, "my_signed_comp: Not cccc ");
|
|
|
|
myabort (GET_CODE (XEXP (my_insn, 0)));
|
|
|
|
}
|