/* $Id: saturn.c,v 1.1.1.10 1995/07/11 22:17:31 alex Exp $ */
/* Subroutines for insn-output.c for GNU compiler. Saturn version.
This port, done by Alex T. Ramos <ramos@engr.latech.edu> in 1994,
is the first 4-bit port of GNU CC.
Copyright (C) 1987, 1992 Free Software Foundation, Inc
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 1, 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 "config.h"
#include "rtl.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "insn-flags.h"
#include "output.h"
#include "real.h"
#include "flags.h"
/* Under BSDi, the standard assert() is broken */
#define ASSERT(CONDITION) if(!(CONDITION)) abort();
/* RCS identification info */
char *saturn_md_version = "";
char *saturn_c_version =
"$Id: saturn.c,v 1.1.1.10 1995/07/11 22:17:31 alex Exp $";
/* Internal gcc variables */
extern char *reg_names[];
extern int optimize;
/* Exported global variables */
int parm_alloc_order[N_REG_PARMS] = PARM_ALLOC_ORDER;
rtx cmp_op0 = 0, cmp_op1 = 0;
/* Exported functions */
int
branch_target(x)
rtx x;
{
return 1;
}
int
arithmetic_register(x)
rtx x;
{
if(REG_P(x) && REGNO(x)>=REG_A && REGNO(x)<=REG_D)
return 1;
else
return 0;
}
/* Return true if OP is a CONST_INT equal to 1.
This is an acceptable operand for inc or dec instructions. */
int
immediate_one_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return (GET_CODE (op) == CONST_INT
&& abs (INTVAL (op)) == 1);
}
/* Return true if OP is a CONST_INT, with OP <= 16.
This is an acceptable operand for constant add instructions. */
int
immediate_nib_p1_operand (op, mode)
rtx op;
enum machine_mode mode;
{
return (GET_CODE (op) == CONST_INT
&& INTVAL (op) != 0
&& abs (INTVAL (op)) <= 16);
}
int
function_arg_regno_p(regno)
int regno;
{
int i;
for (i=0; i<N_REG_PARMS; ++i) {
if (regno == parm_alloc_order[i])
return 1;
}
return 0;
}
char *
mode_fieldspec(x)
rtx x;
{
switch (GET_MODE (x))
{
case QImode:
return 0;
case HImode:
return "b";
case SImode:
return 0;
case PDImode:
return "a";
case DImode:
return "wp";
case TImode:
return "w";
default:
abort();
}
}
char *
reg_p_for_mode(x)
rtx x;
{
switch (GET_MODE (x))
{
case QImode:
return "0";
case HImode:
return "1";
case SImode:
return "3";
case PDImode:
return "4";
case DImode:
return "7";
case TImode:
return "15";
default:
abort();
}
}
char *
mode_spec (x)
rtx x;
{
switch (GET_MODE (x))
{
case QImode:
return "qi";
case HImode:
return "hi";
case SImode:
return "si";
case PDImode:
return "pdi";
case DImode:
return "di";
case TImode:
return "ti";
default:
abort();
}
}
void
output_lib_cmp (insn, x, y)
rtx insn, x, y;
{
rtx compare = next_cc0_user (insn);
enum rtx_code code = GET_CODE (XEXP (XEXP (PATTERN (compare), 1), 0));
enum machine_mode mode = GET_MODE (x);
char *libcall;
rtx l[1];
switch (mode)
{
case DFmode:
switch (code)
{
case EQ:
libcall = "__eqdf2";
break;
case NE:
libcall = "__nedf2";
break;
case GE:
libcall = "__gedf2";
break;
case GT:
libcall = "__gtdf2";
break;
case LE:
libcall = "__ledf2";
break;
case LT:
libcall = "__ltdf2";
break;
default:
abort ();
}
break;
default:
abort ();
}
l[0] = gen_rtx (SYMBOL_REF, Pmode, libcall);
assemble_external_libcall (l[0]);
output_asm_insn ("jsr %0", l);
}
char *
cmp_jmp (op, label, reversed)
rtx op;
rtx label;
int reversed;
{
enum cmp_type { gt, eq, ge, lt, ne, le };
enum cmp_type cmp_rev[] = { le, ne, lt, ge, eq, gt };
static char *cmp_str[] = { "gt", "eq", "ge", "lt", "ne", "le" };
static char temp[256];
enum rtx_code code = GET_CODE (op);
enum machine_mode mode = GET_MODE (XEXP (op, 0));
static int jid = 0;
int type, is_signed;
rtx x[3];
*temp = '\0';
if (GET_MODE_CLASS (GET_MODE (cmp_op0)) == MODE_FLOAT) {
sprintf (temp, "bc%s\t.J%d\n\tjmp %%0\n.J%d",
reversed ? "s" : "c", jid, jid);
jid++;
goto done;
}
switch(code) {
case GT: type = gt; is_signed = 1; break;
case LT: type = lt; is_signed = 1; break;
case GE: type = ge; is_signed = 1; break;
case LE: type = le; is_signed = 1; break;
case GEU: type = ge; is_signed = 0; break;
case GTU: type = gt; is_signed = 0; break;
case LEU: type = le; is_signed = 0; break;
case LTU: type = lt; is_signed = 0; break;
/* Sign doesn't matter in equality test, so treat as unsigned. */
case EQ: type = eq; is_signed = 0; break;
case NE: type = ne; is_signed = 0; break;
default: abort();
}
if(!is_signed) {
char *fs = mode_fieldspec (cmp_op0);
if (fs) {
sprintf (temp, "b%s.%s %%1,%%2,.J%d\n\tjmp %%0\n.J%d",
reversed ? cmp_str[type] : cmp_str[cmp_rev[type]],
fs, jid, jid);
}
else {
fs = reg_p_for_mode (cmp_op0);
sprintf (temp, "move.1 #%s,p\n\t"
"b%s.wp %%1,%%2,.J%d\n\t"
"move.1 #7,p\n\t"
"jmp %%0\n"
".J%d\n\t"
"move.1 #7,p",
fs, reversed ? cmp_str[type] : cmp_str[cmp_rev[type]],
jid, jid);
}
jid++;
goto done;
}
/* Signed comparison */
if(!cmp_op1) { /* against zero */
int msn = GET_MODE_SIZE (GET_MODE (cmp_op0)) - 1;
int lose = jid++;
int lose1 = jid++;
int win = jid++;
int carry_wins;
static char *restore_clean = "\tlsr.p #1,%1";
static char *restore_full = "\tnot.p %1\n\tlsr.p #1,%1\n\tnot.p %1";
if (msn != 7)
sprintf(temp, "move.1 #%d,p\n\t", msn);
if (reversed) {
reversed = 0;
type = cmp_rev[type];
}
if (type == gt || type == le) {
sprintf(temp+strlen(temp), "beq.wp %%1,0,.J%d\n\t",
(type==le) ? win : lose1);
}
carry_wins = (type == lt || type == le);
sprintf(temp+strlen(temp), "add.p %%1,%%1\n\t"
"bc%c .J%d\n",
carry_wins ? 'c' : 's', lose);
sprintf(temp+strlen(temp), ".J%d\n", win);
if(carry_wins)
strcat(temp, restore_full);
else
strcat(temp, restore_clean);
if(msn != 7)
sprintf(temp+strlen(temp), "\n\tmove.1 #7,p");
sprintf(temp+strlen(temp), "\n\tjmp %%0\n"
".J%d\n", lose);
if(carry_wins)
strcat(temp, restore_clean);
else
strcat(temp, restore_full);
sprintf(temp+strlen(temp), "\n.J%d", lose1);
if(msn != 7)
sprintf(temp+strlen(temp), "\n\tmove.1 #7,p");
}
else { /* sign-compare 2 registers */
int lose = jid++;
int msn = GET_MODE_SIZE (GET_MODE (cmp_op0)) - 1;
/* here, reversed is not */
char* operator = reversed ? cmp_str[type] : cmp_str[cmp_rev[type]];
if(msn != 7)
sprintf(temp, "move.1 #%d,p\n\t", msn);
sprintf(temp+strlen(temp), "add.p #8,%%1\n\t"
"add.p #8,%%2\n\t"
"b%s.wp %%1,%%2,.J%d\n\t",
operator, lose);
sprintf(temp+strlen(temp), "sub.p #8,%%1\n\t"
"sub.p #8,%%2\n\t");
if(msn != 7)
sprintf(temp+strlen(temp), "move.1 #7,p\n\t");
sprintf(temp+strlen(temp), "jmp %%0\n"
".J%d\n\t", lose);
sprintf(temp+strlen(temp), "sub.p #8,%%1\n\t"
"sub.p #8,%%2");
if(msn != 7)
sprintf(temp+strlen(temp), "\n\tmove.1 #7,p");
}
done:
x[0]=label;
x[1]=cmp_op0;
x[2]=cmp_op1 ? cmp_op1 : const0_rtx;
output_asm_insn (temp, x);
return "";
}
char *saturn_add_unroll = "64"; /* -munroll-adds-above-64 */
char *
expand_inline_add_const (reg, cons)
rtx reg;
int cons;
{
char template[256];
int unroll;
if (1 != sscanf (saturn_add_unroll, "%d", &unroll)) {
fatal ("-munroll-adds-above-XXX requires an integer argument");
}
if (abs(cons) > unroll) {
sprintf (template, "push\n\tmove.1 #0,p\n\tmove.5 #$%x,c\n\t"
"exg.a a,%%0\n\tadd.a c,a\n\texg.a a,%%0\n\t"
"move.1 #7,p\n\tpop", cons & 0xfffff);
output_asm_insn (template, ®);
return "";
}
if (cons>16) {
expand_inline_add_const (reg,16);
expand_inline_add_const (reg,cons-16);
return "";
}
else
if (cons < -16) {
expand_inline_add_const (reg,-16);
expand_inline_add_const (reg,cons+16);
return "";
}
else
if (cons == 0) {
return "";
}
sprintf (template, "%s.a #%d,%%0", cons>0 ? "add" : "sub", abs(cons));
output_asm_insn (template, ®);
return "";
}
char *
expand_inline_add (reg, add)
rtx reg, add;
{
char template [256];
rtx op[2];
switch (GET_CODE (add))
{
case CONST:
expand_inline_add (reg, XEXP (add, 0));
break;
case CONST_INT:
expand_inline_add_const (reg, INTVAL (add));
break;
case LABEL_REF:
case SYMBOL_REF:
sprintf (template, "push\n\tmove.1 #0,p\n\tmove.ao #%%1,c\n\t"
"exg.a a,%%0\n\tadd.a c,a\n\texg.a a,%%0\n\t"
"move.1 #7,p\n\tpop");
op[0] = reg;
op[1] = add;
output_asm_insn (template, op);
break;
case PLUS:
expand_inline_add (reg, XEXP (add, 0));
expand_inline_add (reg, XEXP (add, 1));
break;
default:
abort ();
}
return "";
}
void
saturn_print_operand_address (file, addr)
FILE *file;
register rtx addr;
{
switch (GET_CODE(addr)) {
case REG:
fprintf (file, "(%s)", reg_names[REGNO (addr)]);
break;
case SYMBOL_REF:
assemble_name (file, XSTR(addr,0));
break;
case PLUS:
warning ("compiling without optimization not supported");
fprintf (file, "(%s+%d)", reg_names[REGNO (XEXP(addr,0))],
INTVAL (XEXP (addr,1)));
break;
default:
abort();
}
}
/* Print an instruction operand X on file FILE.
CODE is the code from the %-spec that requested printing this operand;
if `%z3' was used to print operand 3, then CODE is 'z'. */
void
saturn_print_operand (file, x, code)
FILE *file;
rtx x;
char code;
{
if(code == 'm') {
/*
* print out the next available asm mode for the machine mode
* of this operand. This is for instructions that don't care
* about bits outside the used width.
*/
if(GET_MODE(x) == SImode)
fputs("a", file);
else
fputs(mode_fieldspec(x), file);
}
else
switch (GET_CODE (x))
{
case REG:
fprintf (file, "%s", reg_names[REGNO (x)]);
break;
case MEM:
output_address (XEXP (x, 0));
break;
case LABEL_REF:
saturn_print_operand (file, XEXP (x, 0), code);
break;
case CONST:
saturn_print_operand (file, XEXP (x, 0), code);
break;
case CONST_INT:
switch(code) {
case 'P':
if(CONSTANT_P(x))
fputc('#', file);
fprintf (file, "%d", INTVAL(x));
break;
case 'n':
fprintf (file, "%d", -INTVAL(x));
break;
case 'L':
fprintf (file, "$%x00000000", INTVAL(x) & 0xFFFFFFFFU);
break;
case 'A':
fprintf (file, "$%05x", INTVAL(x) & 0xfffff);
break;
case 'B':
fprintf (file, "$%08x", INTVAL(x) & 0xffffffff);
break;
case 'C':
fprintf (file, "$%04x", INTVAL(x) & 0xffff);
break;
case 'D':
fprintf (file, "$%02x", INTVAL(x) & 0xff);
break;
default:
fprintf (file, "%d", INTVAL(x));
break;
}
break;
case SYMBOL_REF:
assemble_name (file, XSTR(x,0));
break;
case CODE_LABEL:
{
char label[32];
ASM_GENERATE_INTERNAL_LABEL (label, "L", CODE_LABEL_NUMBER(x));
assemble_name (file, label);
}
break;
case PLUS:
saturn_print_operand (file, XEXP (x, 0), code);
fprintf (file, "+");
saturn_print_operand (file, XEXP (x, 1), code);
break;
default:
abort();
}
}
void
saturn_output_addr_const (file, x, size)
FILE *file;
rtx x;
int size;
{
char buf[256];
restart:
switch (GET_CODE (x))
{
case SYMBOL_REF:
assemble_name (file, XSTR (x, 0));
break;
case LABEL_REF:
ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (XEXP (x, 0)));
assemble_name (file, buf);
break;
case CODE_LABEL:
ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (x));
assemble_name (file, buf);
break;
case CONST_INT:
switch (GET_MODE (x))
{
case VOIDmode:
switch (size)
{
case 1:
goto size_for_qi;
case 2:
goto size_for_hi;
case 4:
goto size_for_si;
case 5:
goto size_for_pdi;
case 8:
goto size_for_di;
case 16:
goto size_for_ti;
default:
output_operand_lossage ("unknown size const_int operand");
break;
}
break;
size_for_qi:
case QImode:
if (INTVAL (x) < 0)
fprintf (file, "$%x", INTVAL (x) & 0xf);
else
fprintf (file, "%d", INTVAL (x));
break;
size_for_hi:
case HImode:
if (INTVAL (x) < 0)
fprintf (file, "$%x", INTVAL (x) & 0xff);
else
fprintf (file, "%d", INTVAL (x));
break;
size_for_si:
case SImode:
if (INTVAL (x) < 0)
fprintf (file, "$%x", INTVAL (x) & 0xffff);
else
fprintf (file, "%d", INTVAL (x));
break;
size_for_pdi:
case PDImode:
if (INTVAL (x) < 0)
fprintf (file, "$%x", INTVAL (x) & 0xfffff);
else
fprintf (file, "%d", INTVAL (x));
break;
size_for_di:
case DImode:
if (INTVAL (x) < 0)
fprintf (file,
#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT
"$%x",
#else
"$%lx",
#endif
INTVAL (x));
else
fprintf (file,
#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT
"%d",
#else
"%ld",
#endif
INTVAL (x));
break;
default:
output_operand_lossage ("unknown mode in const_int operand");
break;
}
break;
case CONST:
saturn_output_addr_const (file, XEXP (x, 0), size);
break;
size_for_ti:
case CONST_DOUBLE:
if (GET_MODE (x) == VOIDmode)
{
if (CONST_DOUBLE_HIGH (x))
fprintf (file,
#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT
"$%x%08x",
#else
"$%lx%08lx",
#endif
CONST_DOUBLE_HIGH (x), CONST_DOUBLE_LOW (x));
else if (CONST_DOUBLE_LOW (x) < 0)
fprintf (file,
#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT
"$%x",
#else
"$%lx",
#endif
CONST_DOUBLE_LOW (x));
else
fprintf (file,
#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT
"%d",
#else
"%ld",
#endif
CONST_DOUBLE_LOW (x));
}
else
output_operand_lossage ("floating constant misused");
break;
case PLUS:
if (GET_CODE (XEXP (x, 0)) == CONST_INT)
{
saturn_output_addr_const (file, XEXP (x, 1), size);
if (INTVAL (XEXP (x, 0)) >= 0)
fprintf (file, "+");
saturn_output_addr_const (file, XEXP (x, 0), size);
}
else
{
saturn_output_addr_const (file, XEXP (x, 0), size);
if (INTVAL (XEXP (x, 1)) >= 0)
fprintf (file, "+");
saturn_output_addr_const (file, XEXP (x, 1), size);
}
break;
case MINUS:
x = simplify_subtraction (x);
if (GET_CODE (x) != MINUS)
goto restart;
saturn_output_addr_const (file, XEXP (x, 0), size);
fprintf (file, "-");
if (GET_CODE (XEXP (x, 1)) == CONST_INT
&& INTVAL (XEXP (x, 1)) < 0)
{
fprintf (file, ASM_OPEN_PAREN);
saturn_output_addr_const (file, XEXP (x, 1), size);
fprintf (file, ASM_CLOSE_PAREN);
}
else
saturn_output_addr_const (file, XEXP (x, 1), size);
break;
case ZERO_EXTEND:
case SIGN_EXTEND:
saturn_output_addr_const (file, XEXP (x, 0), size);
break;
default:
output_operand_lossage ("invalid operand expression");
break;
}
}
enum reg_class
secondary_reload_class (class, mode, x)
enum reg_class class;
enum machine_mode mode;
rtx x;
{
ASSERT(!REG_P(x) || REGNO(x)>=0);
ASSERT(class>=0 && class<LIM_REG_CLASSES);
ASSERT(class != NO_REGS);
switch (GET_CODE(x)) {
case REG:
case SUBREG:
switch (REGNO(x)) {
/* only D and pseudos require C; all others can be moved through A */
case REG_C:
return NO_REGS;
case REG_A:
/* need REG_C if this class might contain REG_D */
return
TEST_HARD_REG_BIT(reg_class_contents[class], REG_D) ?
CREG : NO_REGS;
case REG_D0: case REG_D1:
case REG_B: case REG_R0:
case REG_R1: case REG_R2:
case REG_R3: case REG_R4:
/* for these regs, CLASS is more important */
break;
case REG_D:
case REG_HS:
default: /* pseudos fall here */
return class==CREG ? NO_REGS : CREG;
}
break;
case SYMBOL_REF:
case LABEL_REF:
case CONST:
case CONST_INT: {
switch (class) {
case AREG: case CREG: case ACREG:
# if 0
/* this did not work too well. */
if ( abs(INTVAL (x)) <= 0xFFFFF) {
return DnREG;
}
# endif
case DnREG: case PTR_REGS:
return NO_REGS;
case DREG: case GENERAL_REGS:
return CREG;
case RnREG: case BREG: case T_REGS:
return ACREG;
default:
abort();
}
}
case MEM:
break;
default:
/* unexpected X value */
abort ();
}
switch (class) {
case GENERAL_REGS:
case DREG:
return CREG;
case AREG: case CREG: case ACREG:
return NO_REGS;
case PTR_REGS:
case DnREG:
case RnREG:
case BREG: case T_REGS:
return ACREG;
default:
abort();
}
}
enum reg_class
preferred_reload_class (x,class)
rtx x;
enum reg_class class;
{
return class;
}
#define MAGIC 10 /* make sure we notice here when new classes are added */
static int class_move_cost[N_REG_CLASSES-1][N_REG_CLASSES-1] = {
/* a b c d q D R - hs D t g X */
/* a */ { 2, 2, 2, 4, 4, 2, 2, 4, 9, 2, 2, 4, 4 },
/* b */ { 2, 2, 2, 4, 4, 4, 4, 4, 9, 4, 2, 4, 4 },
/* c */ { 2, 2, 2, 2, 2, 2, 2, 2, 9, 2, 2, 2, 2 },
/* d */ { 4, 4, 2, 2, 4, 4, 4, 4, 9, 4, 4, 4, 4 },
/* q */ { 4, 4, 2, 4, 4, 4, 4, 4, 9, 4, 4, 4, 4 },
/* D */ { 2, 4, 2, 4, 4, 4, 4, 4, 9, 4, 4, 4, 4 },
/* R */ { 2, 4, 2, 4, 4, 4, 4, 4, 9, 4, 4, 4, 4 },
/* - */ { 4, 4, 2, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4 },
/* - */ { 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 },
/* D */ { 2, 4, 2, 4, 4, 4, 4, 4, 9, 4, 4, 4, 4 },
/* t */ { 2, 2, 2, 4, 4, 4, 4, 4, 9, 4, 4, 4, 4 },
/* g */ { 4, 4, 2, 4, 4, 4, 4, 4, 9, 4, 4, 4, 4 },
/* X */ { 4, 4, 2, 4, 4, 4, 4, 4, 9, 4, 4, 4, MAGIC }
};
int register_move_cost(from, to)
int from, to;
{
ASSERT(class_move_cost[ALL_REGS-1][ALL_REGS-1] == MAGIC);
if (from==0 || to==0) {
return 8; /* this case occurs; meaning unknown */
}
return class_move_cost[from-1][to-1];
}
void
function_prologue(file, local_storage_size)
FILE *file;
int local_storage_size;
{
int i;
fprintf (file, "\t;; regs used: ");
for (i=0; i<FIRST_PSEUDO_REGISTER; ++i) {
if (regs_ever_live[i]) {
fprintf (file, " %s", reg_names[i]);
}
}
fprintf (file, "\n\tmove.a #%d,d0\n", local_storage_size + 10);
fputs ("\tjsr ___prologue\n", file);
}
void
function_epilogue(file, local_storage_size)
FILE *file;
int local_storage_size;
{
fputs ("\tjmp ___epilogue\n", file);
}
rtx
find_add_insn(rtx insn, rtx stop)
{
while (insn && insn!=stop) {
if (GET_CODE(insn)==INSN &&
GET_CODE(PATTERN(insn))==SET &&
REG_P(SET_DEST(PATTERN(insn))) &&
REGNO(SET_DEST(PATTERN(insn)))==FRAME_POINTER_REGNUM &&
GET_CODE(SET_SRC(PATTERN(insn)))==PLUS) {
return insn;
}
insn = NEXT_INSN(insn);
}
return NULL;
}
rtx
find_float_cmp_insn (rtx insn, rtx stop)
{
rtvec rtv;
rtx comp;
while (insn && insn != stop)
{
if (GET_CODE (insn) == INSN &&
GET_CODE (PATTERN (insn)) == PARALLEL &&
(rtv = XVEC (PATTERN (insn), 0)) &&
GET_CODE (RTVEC_ELT (rtv, 0)) == SET &&
(comp = XEXP (RTVEC_ELT (rtv, 0), 1)) &&
GET_CODE (comp) == COMPARE &&
GET_CODE (XEXP (comp, 0)) == REG &&
GET_MODE_CLASS (GET_MODE (XEXP (comp, 0))) == MODE_FLOAT)
{
return insn;
}
insn = NEXT_INSN (insn);
}
return NULL;
}
int
frame_pointer_used_between_p (insn, stop)
rtx insn, stop;
{
if (reg_used_between_p(frame_pointer_rtx,insn,stop)) {
return 1;
}
if (find_float_cmp_insn (insn, stop)) {
return 1;
}
while ( (insn = NEXT_INSN(insn)) && insn != stop) {
if (GET_CODE (insn) == CALL_INSN)
return 1;
if (GET_CODE (insn) == CODE_LABEL)
return 1;
}
return 0;
}
void
optimize_adjacent_addition()
{
int bb;
rtx insn;
extern int n_basic_blocks;
extern rtx *basic_block_head, *basic_block_end;
for (bb=0; bb<n_basic_blocks; ++bb) {
insn = basic_block_head[bb];
while (insn = find_add_insn(insn, basic_block_end[bb])) {
rtx second = find_add_insn(NEXT_INSN(insn), basic_block_end[bb]);
if (second && !frame_pointer_used_between_p(insn, second)) {
XEXP(SET_SRC(PATTERN(insn)),1) = gen_rtx
(CONST_INT,
VOIDmode,
INTVAL(XEXP(SET_SRC(PATTERN(insn)),1))
+ INTVAL(XEXP(SET_SRC(PATTERN(second)),1)));
delete_insn(second);
}
insn = NEXT_INSN(insn);
}
}
}
void
fake_indexed_addressing(insn)
rtx insn;
{
int xop;
rtx subreg, trunc, mem, sub;
rtx base_reg, index, set[2];
do {
if (GET_CODE (insn) != INSN || GET_CODE (PATTERN (insn)) != SET)
continue;
for (xop=0;
(mem = XEXP (PATTERN(insn), xop)),
xop<=1 && GET_CODE (mem) != MEM;
++xop) {
/* empty loop */
}
if (xop<2 && GET_CODE (XEXP (mem,0)) == PLUS)
{
base_reg = XEXP (XEXP (mem, 0), 0);
index = XEXP (XEXP (mem, 0), 1);
emit_insn_before (gen_addpdi3 (base_reg, base_reg, index), insn);
index = gen_rtx (CONST_INT, VOIDmode, -INTVAL(index));
emit_insn_after (gen_addpdi3 (base_reg, base_reg, index), insn);
set[xop] = gen_rtx (MEM, GET_MODE(mem), base_reg);
set[!xop] = XEXP (PATTERN(insn), !xop);
emit_insn_after (gen_rtx (SET, VOIDmode, set[0], set[1]), insn);
delete_insn (insn);
}
else { /* xop == 2 (no MEM found) */
trunc = subreg = XEXP (PATTERN(insn), 1);
if (GET_CODE (subreg) == SUBREG
&& GET_CODE (mem = XEXP (subreg, 0)) == MEM
&& GET_CODE (XEXP (mem,0)) == PLUS)
{
base_reg = XEXP (XEXP (mem, 0), 0);
index = XEXP (XEXP (mem, 0), 1);
emit_insn_before (gen_addpdi3 (base_reg, base_reg, index),
insn);
index = gen_rtx (CONST_INT, VOIDmode, -INTVAL(index));
emit_insn_after (gen_addpdi3 (base_reg, base_reg, index),
insn);
sub = gen_rtx (MEM, GET_MODE(mem), base_reg);
set[0] = XEXP (PATTERN (insn), 0);
set[1] = gen_rtx (SUBREG, GET_MODE(subreg), sub, XINT (subreg,
1));
emit_insn_after (gen_rtx (SET, VOIDmode, set[0], set[1]),
insn);
delete_insn (insn);
}
else
if (GET_CODE (trunc) == TRUNCATE
&& GET_MODE (trunc) == PDImode
&& GET_CODE (mem = XEXP (trunc, 0)) == MEM
&& GET_CODE (XEXP (mem,0)) == PLUS)
{
base_reg = XEXP (XEXP (mem, 0), 0);
index = XEXP (XEXP (mem, 0), 1);
emit_insn_before (gen_addpdi3 (base_reg, base_reg, index),
insn);
index = gen_rtx (CONST_INT, VOIDmode, -INTVAL(index));
emit_insn_after (gen_addpdi3 (base_reg, base_reg, index),
insn);
sub = gen_rtx (MEM, GET_MODE(mem), base_reg);
set[0] = XEXP (PATTERN (insn), 0);
set[1] = gen_rtx (TRUNCATE, GET_MODE(trunc), sub);
emit_insn_after (gen_rtx (SET, VOIDmode, set[0], set[1]),
insn);
delete_insn (insn);
}
}
}
while(insn = NEXT_INSN(insn));
if (optimize) {
optimize_adjacent_addition();
}
}
char *
output_shift_insn (operands, type, sense)
rtx operands[3];
char type, sense;
{
int n;
char tem[200];
char libcall[20];
rtx l;
char *fs = mode_fieldspec (operands[0]);
char *p = 0;
if (!fs)
{
p = reg_p_for_mode (operands[0]);
fs = "wp";
}
if (GET_CODE (operands[2]) == CONST_INT)
{
n = INTVAL (operands[2]);
if (n <= 0)
return "";
if (p)
{
sprintf (tem, "move.1 #%s,p", p);
output_asm_insn (tem, operands);
}
while (n % 4)
{
n--;
sprintf (tem, "ls%c.%s #1,%%0", sense, fs);
output_asm_insn (tem, operands);
}
while (n)
{
n -= 4;
sprintf (tem, "ls%c.%s #4,%%0", sense, fs);
output_asm_insn (tem, operands);
}
if (p)
{
sprintf (tem, "move.1 #7,p", p);
output_asm_insn (tem, operands);
}
}
else
{
sprintf (libcall, "__%csh%c%s2", type, sense, mode_spec (operands[0]));
l = gen_rtx (SYMBOL_REF, Pmode, libcall);
assemble_external_libcall (l);
sprintf (tem, "jsr %%1 ; %s-shift '%%0' for '%%2' bits",
(sense == 'r') ? "right" : "left");
operands[1] = l;
output_asm_insn (tem, operands);
}
return "";
}
/*
* Floating point emulation for the HP48.
*/
#include "tree.h"
#define DOMAIN 1
#define SING 2
#define OVERFLOW 3
#define UNDERFLOW 4
#define TLOSS 5
#define PLOSS 6
#define INVALID 7
#define NMSGS 8
static char *errmsg[NMSGS] =
{
"unknown",
"domain",
"singularity",
"overflow",
"underflow",
"total loss of precision",
"partial loss of precision",
"invalid operation"
};
int merror = 0;
extern int merror;
struct saturn_ereal {
char sign;
unsigned long long mhi;
unsigned long long mlo;
char esign;
unsigned short exp;
};
void
saturn_real_error (name, code)
char *name;
int code;
{
char errstr[80];
/* Display string passed by calling function, which is supposed to be
the name of the function in which the error occurred.
Display error message defined by the code argument. */
if ((code < 0) || (code >= NMSGS))
code = 0;
sprintf (errstr, " %s %s error", name, errmsg[code]);
if (extra_warnings)
warning (errstr);
merror = code + 1;
}
void
saturn_real_clear (r)
REAL_VALUE_TYPE *r;
{
r->sign = 1;
r->mant = 0;
r->esign = 1;
r->exp = 0;
}
void
saturn_real_copy (src, dst)
REAL_VALUE_TYPE *src;
REAL_VALUE_TYPE *dst;
{
bcopy ((char *)src, (char *)dst, sizeof (REAL_VALUE_TYPE));
}
REAL_VALUE_TYPE
saturn_real_normalize (e, mode)
struct saturn_ereal e;
enum machine_mode mode;
{
REAL_VALUE_TYPE x;
unsigned long long t;
long exp;
long max_exp;
if (e.mhi == 0 && e.mlo == 0)
{
exp = 0;
goto pack;
}
exp = e.esign * e.exp;
while (e.mhi < 100000000000000ULL)
{
/* scale left */
t = e.mlo / 1000000000000000ULL;
e.mlo = e.mlo % 1000000000000000ULL;
e.mlo *= 10;
e.mhi *= 10;
e.mhi += t;
exp -= 1;
}
do
{
while (e.mhi >= 1000000000000000ULL)
{
/* scale right */
e.mlo /= 10;
t = e.mhi % 10;
e.mlo += t * 1000000000000000ULL;
e.mhi /= 10;
exp += 1;
}
switch (mode)
{
case SFmode:
case DFmode:
t = e.mhi % 1000;
t /= 100;
if (t >= 5)
e.mhi += 1000;
e.mhi -= e.mhi % 1000;
break;
default:
t = e.mlo / 1000000000000000ULL;
if (t >= 5)
e.mhi += 1;
break;
}
}
while (e.mhi >= 1000000000000000ULL);
switch (mode)
{
case SFmode:
case DFmode:
max_exp = 499;
break;
default:
max_exp = 49999;
break;
}
if (abs (exp) > max_exp)
{
if (exp < 0)
{
saturn_real_error ("real_normalize", UNDERFLOW);
e.mhi = 100000000000000ULL;
exp = -max_exp;
}
else
{
saturn_real_error ("real_normalize", OVERFLOW);
switch (mode)
{
case SFmode:
case DFmode:
e.mhi = 999999999999000ULL;
break;
default:
e.mhi = 999999999999999ULL;
break;
}
exp = max_exp;
}
}
pack:
x.sign = e.sign;
x.mant = e.mhi;
if (exp < 0)
{
x.esign = -1;
x.exp = -exp;
}
else
{
x.esign = 1;
x.exp = exp;
}
return x;
}
REAL_VALUE_TYPE
saturn_real_add (d1, d2)
REAL_VALUE_TYPE d1;
REAL_VALUE_TYPE d2;
{
struct saturn_ereal e, tmp;
unsigned long long f;
int i, c, t;
int sign, shift;
long e1, e2;
e1 = d1.esign * d1.exp;
e2 = d2.esign * d2.exp;
if (e1 < e2)
{
saturn_real_copy (&d1, &tmp);
saturn_real_copy (&d2, &d1);
saturn_real_copy (&tmp, &d2);
}
e.exp = d1.exp;
e.esign = d1.esign;
shift = e.exp - d2.exp;
if (shift > 17)
{
e.sign = d1.sign;
e.mhi = d1.mant;
e.mlo = 0;
}
else
{
/* perform addition */
sign = 0;
if (d1.sign != d2.sign)
sign = 1;
e.sign = d1.sign;
f = 1;
for (i = shift + 1; i < 16; i++)
f *= 10;
c = 0;
e.mlo = 0;
for (i = 0; i < shift; i++)
{
if (sign)
t = c - (d2.mant % 10);
else
t = c + (d2.mant % 10);
c = t / 10;
t = t % 10;
e.mlo += t * f;
d2.mant /= 10;
f *= 10;
}
f = 1;
e.mhi = 0;
for (i = 0; i < 15; i++)
{
t = d1.mant % 10;
if (sign)
t += c - (d2.mant % 10);
else
t += c + (d2.mant % 10);
c = t / 10;
t = t % 10;
e.mhi += t * f;
d1.mant /= 10;
d2.mant /= 10;
f *= 10;
}
if (c != 0)
{
c = c % 10;
if (sign)
e.sign = -e.sign;
e.mhi += c * f;
}
}
/* normalize */
return saturn_real_normalize (e, VOIDmode);
}
REAL_VALUE_TYPE
saturn_real_sub (d1, d2)
REAL_VALUE_TYPE d1;
REAL_VALUE_TYPE d2;
{
d2 = saturn_real_negate (d2);
return saturn_real_add (d1, d2);
}
REAL_VALUE_TYPE
saturn_real_mul (d1, d2)
REAL_VALUE_TYPE d1;
REAL_VALUE_TYPE d2;
{
struct saturn_ereal e;
long exp;
unsigned long h1, l1;
unsigned long h2, l2;
unsigned long long t1, t2;
exp = d1.esign * d1.exp + d2.esign * d2.exp + 2;
if (exp < 0)
{
e.esign = -1;
e.exp = -exp;
}
else
{
e.esign = 1;
e.exp = exp;
}
e.sign = d1.sign * d2.sign;
h1 = (unsigned long)(d1.mant / 100000000ULL);
l1 = (unsigned long)(d1.mant % 100000000ULL);
h2 = (unsigned long)(d2.mant / 100000000ULL);
l2 = (unsigned long)(d2.mant % 100000000ULL);
e.mhi = (unsigned long long)h1 * (unsigned long long)h2;
e.mlo = (unsigned long long)l1 * (unsigned long long)l2;
t1 = (unsigned long long)l1 * (unsigned long long)h2;
e.mhi += t1 / 100000000ULL;
e.mlo += (t1 % 100000000ULL) * 100000000ULL;
if (e.mlo > 10000000000000000ULL)
{
e.mhi += e.mlo / 10000000000000000ULL;
e.mlo = e.mlo % 10000000000000000ULL;
}
t2 = (unsigned long long)h1 * (unsigned long long)l2;
e.mhi += t2 / 100000000ULL;
e.mlo += (t2 % 100000000ULL) * 100000000ULL;
if (e.mlo > 10000000000000000ULL)
{
e.mhi += e.mlo / 10000000000000000ULL;
e.mlo = e.mlo % 10000000000000000ULL;
}
return saturn_real_normalize (e, VOIDmode);
}
REAL_VALUE_TYPE
saturn_real_div (d1, d2)
REAL_VALUE_TYPE d1;
REAL_VALUE_TYPE d2;
{
struct saturn_ereal e;
int i, t;
long exp;
e.sign = d1.sign * d2.sign;
if (d1.mant == 0)
{
saturn_real_error ("real_div", SING);
e.mhi = 999999999999999ULL;
e.mlo = 0;
e.esign = 1;
e.exp = 49999;
return saturn_real_normalize (e);
}
exp = d1.esign * d1.exp - d2.esign * d2.exp + 14;
if (exp < 0)
{
e.esign = -1;
e.exp = -exp;
}
else
{
e.esign = 1;
e.exp = exp;
}
e.mhi = d1.mant / d2.mant;
d1.mant -= e.mhi * d2.mant;
e.mlo = 0;
for (i = 0; i < 16; i++)
{
e.mlo *= 10;
d1.mant *= 10;
t = d1.mant / d2.mant;
e.mlo += t;
d1.mant -= t * d2.mant;
}
return saturn_real_normalize (e, VOIDmode);
}
REAL_VALUE_TYPE
saturn_real_from_ulong (unsigned HOST_WIDE_INT l)
{
REAL_VALUE_TYPE x;
int i;
unsigned char data[15];
for (i = 0; i < 16; i++)
data[i] = 0;
for (i = 0; i < 15; i++)
{
data[i] = l % 10;
l /= 10;
if (l == 0)
break;
}
x.sign = 1;
x.esign = 1;
x.exp = i;
x.mant = 0;
for (i = x.exp; i >= 0; i--)
{
x.mant *= 10;
x.mant += data[i];
}
for (i = x.exp + 1; i < 15; i++)
x.mant *= 10;
return x;
}
void
saturn_real_to_int (lo, hi, x)
HOST_WIDE_INT *lo;
HOST_WIDE_INT *hi;
REAL_VALUE_TYPE x;
{
/* UNFINISHED */
*lo = 0;
*hi = 0;
}
void
saturn_real_from_int (x, lo, hi)
REAL_VALUE_TYPE *x;
HOST_WIDE_INT lo;
HOST_WIDE_INT hi;
{
REAL_VALUE_TYPE one, df, dg;
int sign;
saturn_real_clear (&one);
one.mant = 100000000000000ULL;
sign = 0;
if (hi < 0)
{
sign = 1;
hi = ~hi;
if (lo)
lo = -lo;
else
hi += 1;
}
df = saturn_real_ldexp (one, HOST_BITS_PER_WIDE_INT);
dg = saturn_real_from_ulong ((unsigned HOST_WIDE_INT)hi);
dg = saturn_real_mul (dg, df);
df = saturn_real_from_ulong ((unsigned HOST_WIDE_INT)lo);
dg = saturn_real_add (dg, df);
if (sign)
dg = saturn_real_negate (dg);
saturn_real_copy (&dg, x);
}
void
saturn_real_from_uint (x, lo, hi)
REAL_VALUE_TYPE *x;
unsigned HOST_WIDE_INT lo;
unsigned HOST_WIDE_INT hi;
{
REAL_VALUE_TYPE one, df, dg;
saturn_real_clear (&one);
one.mant = 1000000000000000ULL;
df = saturn_real_ldexp (one, HOST_BITS_PER_WIDE_INT);
dg = saturn_real_from_ulong (hi);
dg = saturn_real_mul (dg, df);
df = saturn_real_from_ulong (lo);
dg = saturn_real_add (dg, df);
saturn_real_copy (&dg, x);
}
REAL_VALUE_TYPE
saturn_real_from_float (f)
HOST_WIDE_INT f;
{
REAL_VALUE_TYPE r;
/* UNFINISHED */
saturn_real_clear (&r);
return r;
}
REAL_VALUE_TYPE
saturn_real_from_double (d)
HOST_WIDE_INT *d;
{
REAL_VALUE_TYPE r;
/* UNFINISHED */
saturn_real_clear (&r);
return r;
}
long
saturn_real_to_float (r)
REAL_VALUE_TYPE r;
{
/* UNFINISHED */
return 0;
}
void
saturn_real_to_double (REAL_VALUE_TYPE r, long *d)
{
/* UNFINISHED */
d[0] = 0;
d[1] = 0;
}
REAL_VALUE_TYPE
saturn_real_negate (x)
REAL_VALUE_TYPE x;
{
x.sign = -x.sign;
return x;
}
REAL_VALUE_TYPE
saturn_real_truncate (mode, x)
enum machine_mode mode;
REAL_VALUE_TYPE x;
{
struct saturn_ereal e;
switch (mode)
{
case SFmode:
case DFmode:
e.sign = x.sign;
e.mhi = x.mant;
e.mlo = 0;
e.esign = x.esign;
e.exp = x.exp;
return saturn_real_normalize (e, mode);
default:
return x;
}
}
REAL_VALUE_TYPE
saturn_real_ldexp (x, n)
REAL_VALUE_TYPE x;
int n;
{
REAL_VALUE_TYPE r;
saturn_real_clear (&r);
while (n)
{
if (n % 2)
r = saturn_real_add (r, x);
x = saturn_real_add (x, x);
n /= 2;
}
return r;
}
int
saturn_real_cmp (x, y)
REAL_VALUE_TYPE x;
REAL_VALUE_TYPE y;
{
int i;
int msign;
if (x.sign != y.sign)
{
if (x.mant != 0 || y.mant != 0)
return x.sign;
return 0;
}
/* Same sign. */
if (x.sign == 0)
msign = 1;
else
msign = -1;
/* Compare the exponents. */
if (x.esign > y.esign)
return msign;
else if (x.esign < x.esign)
return -msign;
else if (x.exp > y.exp)
return msign;
else if (x.exp < y.exp)
return -msign;
/* Compare the mantissa. */
if (x.mant > y.mant)
return msign;
else if (x.mant < y.mant)
return -msign;
return 0;
}
#define INVALID_EXP (-7)
#define chrtoi(c) ((int)((c) & 0x7f) - (int)'0')
REAL_VALUE_TYPE
saturn_real_atof (ss, mode)
char *ss;
enum machine_mode mode;
{
REAL_VALUE_TYPE r;
struct saturn_ereal er;
long exp_exp = 0;
long neg_exp;
long e;
int imp_exp = INVALID_EXP;
int sign = 1;
int exp_sign = 1;
int lz_exp = 0;
int ptr;
int mptr = 0;
int exparse = 0;
int nonzero = 0;
int i;
char *s;
unsigned char mantissa[31];
for (i = 0; i < 31; i++)
mantissa[i] = 0;
s = ss;
while (*s == ' ') /* skip leading spaces */
++s;
ptr = -1;
while (s[++ptr])
{
switch (s[ptr])
{
case 'E':
case 'e':
if (exparse)
goto error;
exparse = 1;
if (imp_exp == INVALID_EXP)
imp_exp = ptr - 1;
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
nonzero = 1;
case '0':
if (exparse)
exp_exp = exp_exp * 10 + chrtoi (s[ptr]);
else if (s[ptr] != '0' || nonzero)
{
if (mptr < 31)
mantissa[mptr++] = chrtoi (s[ptr]);
else if (imp_exp == INVALID_EXP)
imp_exp = ptr - 1;
}
else
--lz_exp;
break;
case '-':
if (exparse)
exp_sign = -1;
else
{
sign *= -1;
++s;
--ptr;
}
break;
case '+':
++s;
--ptr;
break;
case '.':
imp_exp = ptr - 1;
break;
default:
error:
saturn_real_error ("real_atof", DOMAIN);
saturn_real_clear (&r);
return r;
}
}
if (imp_exp == INVALID_EXP)
imp_exp = ptr - 1;
er.sign = sign;
e = imp_exp + exp_sign * exp_exp + lz_exp;
if (e < 0)
{
er.esign = -1;
er.exp = -e;
}
else
{
er.esign = 1;
er.exp = e;
}
er.mhi = 0;
for (i = 0; i < 15; i++)
{
er.mhi *= 10;
er.mhi += mantissa[i];
}
er.mlo = 0;
for (i = 0; i < 16; i++)
{
er.mlo *= 10;
er.mlo += mantissa[15 + i];
}
return saturn_real_normalize (er, mode);
}
extern void print_real PROTO ((REAL_VALUE_TYPE));
void
print_real (r)
REAL_VALUE_TYPE r;
{
char *s, buf[100];
int i;
unsigned char data[15];
s = buf;
if (r.sign < 0)
*s++ = '-';
for (i = 0; i < 15; i++)
{
data[14 - i] = r.mant % 10;
r.mant /= 10;
}
*s++ = data[0] + '0';
*s++ = '.';
for (i = 1; i < 15; i++)
*s++ = data[i] + '0';
*s++ = 'E';
if (r.esign < 0)
*s++ = '-';
for (i = 0; i < 5; i++)
{
data[i] = r.exp % 10;
r.exp /= 10;
}
for (i = 4; i >= 0; i--)
*s++ = data[i] + '0';
*s = '\0';
fprintf (stderr, "%s\n", buf);
}
void
saturn_real_to_decimal (r, s)
REAL_VALUE_TYPE r;
char *s;
{
int i;
unsigned char data[15];
if (r.sign < 0)
*s++ = '-';
for (i = 0; i < 15; i++)
{
data[14 - i] = r.mant % 10;
r.mant /= 10;
}
*s++ = data[0] + '0';
*s++ = '.';
for (i = 1; i < 15; i++)
*s++ = data[i] + '0';
s--;
while (*s == '0')
s--;
if (*s != '.')
s++;
if (r.exp > 0)
{
*s++ = 'E';
if (r.esign < 0)
*s++ = '-';
for (i = 0; i < 5; i++)
{
data[i] = r.exp % 10;
r.exp /= 10;
if (r.exp == 0)
break;
}
for ( ; i >= 0; i--)
*s++ = data[i] + '0';
}
*s = '\0';
}
void
saturn_real_arith (value, icode, d1, d2)
REAL_VALUE_TYPE *value;
int icode;
REAL_VALUE_TYPE *d1;
REAL_VALUE_TYPE *d2;
{
int code = (enum tree_code) icode;
switch (code)
{
case PLUS_EXPR: /* d1 + d2 */
*value = saturn_real_add (*d1, *d2);
break;
case MINUS_EXPR: /* d1 - d2 */
*value = saturn_real_sub (*d1, *d2);
break;
case MULT_EXPR: /* d1 * d2 */
*value = saturn_real_mul (*d1, *d2);
break;
case RDIV_EXPR: /* d1 / d2 */
*value = saturn_real_div (*d1, *d2);
break;
case MIN_EXPR: /* min (d1, d2) */
if (saturn_real_cmp (*d1, *d2) < 0)
saturn_real_copy (d1, value);
else
saturn_real_copy (d2, value);
break;
case MAX_EXPR: /* max (d1, d2) */
if (saturn_real_cmp (*d1, *d2) > 0)
saturn_real_copy (d1, value);
else
saturn_real_copy (d2, value);
break;
default:
saturn_real_clear (value);
break;
}
}
int
saturn_significand_size (mode)
enum machine_mode mode;
{
switch (mode)
{
case SFmode:
case DFmode:
return 39;
case TFmode:
return 49;
default:
abort ();
}
}
void saturn_output_ascii(file, string, len)
FILE *file;
char *string;
int len;
{
size_t n = len;
char *p = string;
fputs("\ttextr \"", file);
while(n--) {
if(isprint(*p) && *p!='\\' && *p!='"')
fputc(*p, file);
else
fprintf(file, "\\%03o", *p);
++p;
}
fputs("\"\n", file);
}
syntax highlighted by Code2HTML, v. 0.9.1