diff options
author | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1995-12-20 01:06:22 +0000 |
---|---|---|
committer | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1995-12-20 01:06:22 +0000 |
commit | c482518380683ee38d14024c1e362a0d681cf967 (patch) | |
tree | e69b4f6d3fee3aced20a41f3fdf543fc1c77fb5d /gnu/usr.bin/gcc/config/i860 | |
parent | 76a62188d0db49c65b696d474c855a799fd96dce (diff) |
FSF GCC version 2.7.2
Diffstat (limited to 'gnu/usr.bin/gcc/config/i860')
-rw-r--r-- | gnu/usr.bin/gcc/config/i860/bsd-gas.h | 4 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/config/i860/bsd.h | 46 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/config/i860/fx2800.h | 344 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/config/i860/i860.c | 2097 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/config/i860/i860.h | 1433 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/config/i860/i860.md | 2318 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/config/i860/mach.h | 16 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/config/i860/paragon.h | 243 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/config/i860/sysv3.h | 178 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/config/i860/sysv4.h | 212 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/config/i860/t-fx2800 | 3 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/config/i860/x-fx2800 | 7 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/config/i860/x-sysv3 | 1 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/config/i860/x-sysv4 | 44 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/config/i860/xm-fx2800.h | 12 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/config/i860/xm-i860.h | 43 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/config/i860/xm-paragon.h | 4 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/config/i860/xm-sysv3.h | 4 | ||||
-rw-r--r-- | gnu/usr.bin/gcc/config/i860/xm-sysv4.h | 4 |
19 files changed, 7013 insertions, 0 deletions
diff --git a/gnu/usr.bin/gcc/config/i860/bsd-gas.h b/gnu/usr.bin/gcc/config/i860/bsd-gas.h new file mode 100644 index 00000000000..11b400bb745 --- /dev/null +++ b/gnu/usr.bin/gcc/config/i860/bsd-gas.h @@ -0,0 +1,4 @@ +#include "i860/bsd.h" + +#undef ASCII_DATA_ASM_OP +#define ASCII_DATA_ASM_OP ".ascii" diff --git a/gnu/usr.bin/gcc/config/i860/bsd.h b/gnu/usr.bin/gcc/config/i860/bsd.h new file mode 100644 index 00000000000..4f745c50a1b --- /dev/null +++ b/gnu/usr.bin/gcc/config/i860/bsd.h @@ -0,0 +1,46 @@ +#include "i860/i860.h" + +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (i860, BSD)") + +/* BSD UN*X systems use BSD STABS debugging info. */ + +#define DBX_DEBUGGING_INFO + +#define ASCII_DATA_ASM_OP ".byte" +#define ASM_OUTPUT_ASCII(f, p, size) \ +do { register unsigned i; \ + int inside; \ + inside = FALSE; \ + for (i = 0; i < (size); i++) { \ + if (i % 64 == 0) { \ + if (i != 0) { \ + if (inside) \ + putc('"', (f)); \ + putc('\n', (f)); \ + inside = FALSE; \ + } \ + fprintf((f), "\t%s ", ASCII_DATA_ASM_OP); \ + } \ + if ((p)[i] < 32 || (p)[i] == '\\' || (p)[i] == '"' || (p)[i] >= 127) { \ + if (inside) { \ + putc('"', (f)); \ + inside = FALSE; \ + } \ + if (i % 64 != 0) \ + putc(',', (f)); \ + fprintf((f), "%d", (p)[i]); \ + } else { \ + if (!inside) { \ + if (i % 64 != 0) \ + putc(',', (f)); \ + putc('"', (f)); \ + inside = TRUE; \ + } \ + putc((p)[i], (f)); \ + } \ + } \ + if (inside) \ + putc('"', (f)); \ + putc('\n', (f)); \ +} while (0) diff --git a/gnu/usr.bin/gcc/config/i860/fx2800.h b/gnu/usr.bin/gcc/config/i860/fx2800.h new file mode 100644 index 00000000000..e295c686454 --- /dev/null +++ b/gnu/usr.bin/gcc/config/i860/fx2800.h @@ -0,0 +1,344 @@ +/* Target definitions for GNU compiler for Alliant FX/2800 + running Concentrix 2.2 + + Copyright (C) 1991 Free Software Foundation, Inc. + + Written by Howard Chu (hyc@hanauma.jpl.nasa.gov). + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#define OUTPUT_TDESC + +#include "i860/sysv4.h" + +/* The Alliant fx2800 running Concentrix 2.x is weird. This is basically + a BSD 4.3 based operating system, but it uses svr4 ELF format object + files and it somehow puts BSD stabs records into the ELF files for + symbolic debug information. The assembler is "mostly an SVR4 assembler + with some Alliant additions. We based it on the `Intel 80860 Assembly + Language Specification' from AT&T." */ + +/* This file consists of three sections. The first section establishes + definitions unique to the Alliant FX/2800. The next section reconciles + differences between Alliant and i860v4.h, and the last overrides the + remaining differences with svr4.h */ + +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (i860 Alliant)"); + +/* atexit is not present prior to Concentrix 2.2. Uncomment the following + if you're on 2.1 or older. */ + +/* #undef HAVE_ATEXIT */ + +#define I860_STRICT_ABI_PROLOGUES + +/* There is no avoiding this; -L does not exist at all (in Concentrix 2.2). */ +#define LINK_LIBGCC_SPECIAL 1 + +/* Most of the Alliant-specific definitions here are to get stab info that + Alliant's dbx can understand. */ + +#define DBX_DEBUGGING_INFO +#define DEFAULT_GDB_EXTENSIONS 0 +#define DBX_NO_XREFS +#define DBX_NO_EXTRA_TAGS + +/* Alliant dbx also needs to see the function stab before anything + else in the function. */ + +#define DBX_FUNCTION_FIRST +#define DBX_LBRAC_FIRST + +/* Alliant dbx also needs to see the end of a function somewhere. */ + +#define DBX_OUTPUT_FUNCTION_END(file,decl) \ + fprintf (file, ".stab \"\",.,0x%x,0,0\n", N_EFUN) + +/* Alliant dbx has predefined types, so they must be emitted with the + proper type numbers. The defined types are: + + Type # C, Fortran, Pascal Types + -- ------------------------ + 1 char, integer*1 + 2 short, integer*2 + 3 int, long, integer*4, integer + 4 logical*1, byte + 5 logical*2 + 6 logical*4, logical + 7 float, real*4, real + 8 double, real*8, double + 9 single complex, complex*8, complex + 10 double complex, doublecomplex + 11 character + 12 void + 13 nil + 14 boolean + 15 unsigned char, ubyte + 16 unsigned short, uword + 17 unsigned, unsigned int, unsigned long, ulong + 18 quad, logical*8 + 19 long long, integer*8 + 20 unsigned long long, uquad*8 + 21-100 reserved for future predefined types + 100 long redefine same as 3 + 101 unsigned long same as 17 + -- -------------------- + 102 First user program type + + Since long and unsigned long are int references, they must be handled + as special cases. The Alliant compiler doesn't use types 18-20, so it + sets long & unsigned long in 18 & 19, not in 100 & 101 as shown above. */ + +#define DBX_OUTPUT_STANDARD_TYPES(syms) \ +{ char *dtyps[]={"", "char", "short int", "int", "logical*1", \ + "logical*2", "logical*4", "float", "double", "complex", \ + "doublecomplex", "character", "void", "nil", "boolean", \ + "unsigned char", "short unsigned int", "unsigned int", \ + "logical*8", "long long int", "long long unsigned int",""}; \ + \ + tree decl; \ + int i; \ + \ + for (i=1;*dtyps[i];i++) \ + for (decl = syms; decl; decl = TREE_CHAIN(decl)) \ + if ((TREE_CODE (decl) == TYPE_DECL) && DECL_NAME(decl) && \ + !strcmp(IDENTIFIER_POINTER(DECL_NAME(decl)), dtyps[i])) { \ + TYPE_SYMTAB_ADDRESS (TREE_TYPE (decl)) = i; \ + typevec[i] = TYPE_DEFINED; \ + dbxout_symbol (decl, 0); \ + break; \ + } \ + \ + for (decl = syms; decl; decl = TREE_CHAIN(decl)) \ + if ((TREE_CODE (decl) == TYPE_DECL) && DECL_NAME(decl) && \ + !strcmp(IDENTIFIER_POINTER(DECL_NAME(decl)),"long int")) { \ + TYPE_SYMTAB_ADDRESS (TREE_TYPE (decl)) = i; \ + typevec[i] = TYPE_DEFINED; \ + fprintf(asmfile,".stab \"long int:t%d=3\",0,0x%x,0,0\n", \ + i++,N_LSYM); \ + TREE_ASM_WRITTEN (decl) = 1; \ + break; \ + } \ + \ + for (decl = syms; decl; decl = TREE_CHAIN(decl)) \ + if ((TREE_CODE (decl) == TYPE_DECL) && DECL_NAME(decl) && !strcmp( \ + IDENTIFIER_POINTER(DECL_NAME(decl)),"long unsigned int")) { \ + TYPE_SYMTAB_ADDRESS (TREE_TYPE (decl)) = i; \ + typevec[i] = TYPE_DEFINED; \ + fprintf(asmfile,".stab \"long unsigned int:t%d=17\",0,0x%x,0,0\n",\ + i++,N_LSYM); \ + TREE_ASM_WRITTEN (decl) = 1; \ + break; \ + } \ + next_type_number = i; }; + +/* Alliant dbx doesn't understand split names... */ + +#define DBX_CONTIN_LENGTH 0 + +/* The syntax for stabs records is also different; there is only a single + ".stab" directive instead of the 3 directives in BSD, and the order of + arguments is slightly changed. */ + +#define ASM_STABS_OP ".stab" +#define ASM_STABN_OP ".stab" +#define ASM_STABD_OP ".stab" + +#define DBX_MEMPARM_STABS_LETTER 'k' +#define DBX_REGPARM_STABS_LETTER 'r' + +#define ASM_OUTPUT_SOURCE_LINE(file,num) \ + fprintf (file, "\t.stab \"\",.,0x%x,0,%d\n", \ + N_SLINE,num) + +#if 0 /* Alliant dbx only reads first N_SO, so it + ignores the filename if dir is present. */ +#define DBX_OUTPUT_MAIN_SOURCE_DIRECTORY(file,name) \ + fprintf (file, ".stab \"%s/\",.Ltext0,0x%x,0,0\n", \ + name, N_SO) +#else +#define DBX_OUTPUT_MAIN_SOURCE_DIRECTORY(file,name) +#endif + +#define DBX_OUTPUT_MAIN_SOURCE_FILENAME(file,name) \ + fprintf (file, ".stab "); \ + output_quoted_string (file, name); \ + fprintf (file, ",.Ltext0,0x%x,0,0\n", N_SO); \ + text_section (); \ + ASM_OUTPUT_INTERNAL_LABEL (file, "Ltext", 0) + +#define DBX_OUTPUT_SOURCE_FILENAME(file,name) \ + do { fprintf (file, ".stab "); \ + output_quoted_string (file, name); \ + fprintf (file, ",.Ltext0,0x%x,0,0\n", N_SOL); \ + } while (0) + +#define DBX_OUTPUT_CONSTANT_SYMBOL(file,name,ival) \ + fprintf (file, ".stab \"%s:c=i%d\",0,0x%x,0,0\n", \ + name, ival, N_LSYM) + +#define DBX_FINISH_SYMBOL(decl) \ + int line = 0; \ + fprintf (asmfile, "\","); \ + if (current_sym_addr) \ + output_addr_const (asmfile, current_sym_addr); \ + else \ + fprintf (asmfile, "%d", current_sym_value); \ + if (decl != 0 && TREE_CODE(decl) == FUNCTION_DECL) \ + line=DECL_SOURCE_LINE (decl); \ + fprintf (asmfile, ",0x%x,%d,%d\n", current_sym_code, \ + line!=0?64:0,line) + +#define DBX_OUTPUT_CATCH(file,decl,name) \ + fprintf (file, ".stab \"%s:C1\",", \ + IDENTIFIER_POINTER (DECL_NAME (decl))); \ + assemble_name (file, name); \ + fprintf (file, ",0x%x,0,0\n", N_CATCH) + +#define DBX_OUTPUT_LBRAC(file,name) \ + if (depth > 1) { \ + fprintf (file, ".stab \"\","); \ + assemble_name (file, name); \ + fprintf (file, ",0x%x,0,%d\n", N_LBRAC, depth); } + +#define DBX_OUTPUT_RBRAC(file,name) \ + if (depth > 1) { \ + fprintf (file, ".stab \"\","); \ + assemble_name (file, name); \ + fprintf (file, ",0x%x,0,%d\n", N_RBRAC, depth); } + +#define DBX_OUTPUT_ENUM(file,type) \ + fprintf (file, "e3"); \ + CHARS(2); \ + for (tem = TYPE_VALUES (type); tem; tem = TREE_CHAIN (tem)) \ + { \ + fprintf (asmfile, "%s:%d,", \ + IDENTIFIER_POINTER (TREE_PURPOSE (tem)), \ + TREE_INT_CST_LOW (TREE_VALUE (tem))); \ + CHARS (11 + IDENTIFIER_LENGTH (TREE_PURPOSE (tem))); \ + if (TREE_CHAIN (tem) != 0) \ + CONTIN; \ + } \ + putc (';', asmfile); \ + CHARS (1); + +/* Undefine some things defined in i860.h because the native C compiler + on the FX/2800 emits code to do these operations inline. For GCC, + we will use the default implementation of these things... i.e. + generating calls to libgcc1 routines. */ + +#undef DIVSI3_LIBCALL +#undef UDIVSI3_LIBCALL +#undef REMSI3_LIBCALL +#undef UREMSI3_LIBCALL + +/* The Alliant compiler's mod function gives the wrong result after a + shift operation. This bug typically hits in hash functions. */ + +#define perform_umodsi3(a, b) a %= b; if (a == b) a=0; return a +#define perform_modsi3(a, b) a %= b; if (a == b) a=0; return a + +/* Global pointer needs to be 8 byte aligned? Link error if not... */ + +#define DATA_ALIGNMENT(dummy,align) \ + ((TREE_PUBLIC (decl) && \ + (TREE_CODE (TREE_TYPE (decl))==POINTER_TYPE)) ? 64:align) + +#undef FUNCTION_PROFILER +#define FUNCTION_PROFILER(FILE, LABELNO) \ + fprintf (FILE, "\tcall __mcount_\n\tnop\n") + +/* Overrides for i860v4.h begin here */ + +/* Provide a set of pre-definitions and pre-assertions appropriate for + the i860 running Concentrix 2.x. */ + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Di860 -Dunix -DBSD4_3 -Dalliant -Asystem(unix) -Asystem(bsd) -Acpu(i860) -Amachine(i860)" + +#undef I860_REG_PREFIX +#undef ASM_COMMENT_START +#define ASM_COMMENT_START "//" + +/* Use definitions of ASM_OUTPUT_{DOUBLE,FLOAT} as given in i860.h */ + +#undef ASM_OUTPUT_DOUBLE +#define ASM_OUTPUT_DOUBLE(FILE,VALUE) \ + fprintf(FILE, "\t.double %.20e\n", (VALUE)) +#undef ASM_OUTPUT_FLOAT +#define ASM_OUTPUT_FLOAT(FILE,VALUE) \ + fprintf(FILE, "\t.float %.12e\n", (VALUE)) + +#undef ASM_FILE_START +#define ASM_FILE_START(FILE) +#undef ASM_OUTPUT_FUNCTION_PREFIX +#define ASM_OUTPUT_FUNCTION_PREFIX(FILE,NAME) \ + fputs("\tnop\n", (FILE)); \ + current_function_original_name = (NAME) +#undef ASM_OUTPUT_PROLOGUE_SUFFIX + +/* Overrides for svr4.h begin here */ + +#undef SVR4 + +#undef SWITCH_TAKES_ARG +#undef WORD_SWITCH_TAKES_ARG + +#undef ASM_SPEC +#undef ASM_FINAL_SPEC +#undef MD_STARTFILE_PREFIX +#undef MD_EXEC_PREFIX + +/* Generate an error message if -p option is selected. Concentrix 2.x + does not support prof format profiling, only gprof is supported. */ + +#define CPP_SPEC "%{p:%e-p option not supported: use -pg instead}" + +/* Provide an appropriate LIB_SPEC. The crtend.o file provides part of the + support for getting C++ file-scope static objects constructed before + entering `main'. */ + +#undef LIB_SPEC +#define LIB_SPEC \ + "%{g*:-lg} %{!pg:-lc}%{pg:-lc_p} crtend.o%s" + +/* Tell linker to strip local symbols, since assembler may not. */ + +#undef LINK_SPEC +#define LINK_SPEC "-X" + +/* Get the correct startup file for regular or profiled code. Also + use the crtbegin.o file for C++ ... */ + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC \ + "%{!pg:crt0.o%s}%{pg:gcrt0.o%s} crtbegin.o%s" + +#undef SCCS_DIRECTIVE +#undef NO_DOLLAR_IN_LABEL +#undef TARGET_MEM_FUNCTIONS + +#undef DWARF_DEBUGGING_INFO + +#undef ASM_OUTPUT_LABELREF +#define ASM_OUTPUT_LABELREF(FILE,NAME) fprintf (FILE, "_%s", NAME) +#undef ASM_OUTPUT_EXTERNAL_LIBCALL + +#undef BSS_ASM_OP +#define BSS_ASM_OP "\t.lcomm" diff --git a/gnu/usr.bin/gcc/config/i860/i860.c b/gnu/usr.bin/gcc/config/i860/i860.c new file mode 100644 index 00000000000..dce13404783 --- /dev/null +++ b/gnu/usr.bin/gcc/config/i860/i860.c @@ -0,0 +1,2097 @@ +/* Subroutines for insn-output.c for Intel 860 + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + Derived from sparc.c. + + Written by Richard Stallman (rms@ai.mit.edu). + + Hacked substantially by Ron Guilmette (rfg@netcom.com) to cater + to the whims of the System V Release 4 assembler. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + + +#include "config.h" +#include "flags.h" +#include "rtl.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "real.h" +#include "insn-config.h" +#include "conditions.h" +#include "insn-flags.h" +#include "output.h" +#include "recog.h" +#include "insn-attr.h" + +#include <stdio.h> + +static rtx find_addr_reg (); + +#ifndef I860_REG_PREFIX +#define I860_REG_PREFIX "" +#endif + +char *i860_reg_prefix = I860_REG_PREFIX; + +/* Save information from a "cmpxx" operation until the branch is emitted. */ + +rtx i860_compare_op0, i860_compare_op1; + +/* Return non-zero if this pattern, can be evaluated safely, even if it + was not asked for. */ +int +safe_insn_src_p (op, mode) + rtx op; + enum machine_mode mode; +{ + /* Just experimenting. */ + + /* No floating point src is safe if it contains an arithmetic + operation, since that operation may trap. */ + switch (GET_CODE (op)) + { + case CONST_INT: + case LABEL_REF: + case SYMBOL_REF: + case CONST: + return 1; + + case REG: + return 1; + + case MEM: + return CONSTANT_ADDRESS_P (XEXP (op, 0)); + + /* We never need to negate or complement constants. */ + case NEG: + return (mode != SFmode && mode != DFmode); + case NOT: + case ZERO_EXTEND: + return 1; + + case EQ: + case NE: + case LT: + case GT: + case LE: + case GE: + case LTU: + case GTU: + case LEU: + case GEU: + case MINUS: + case PLUS: + return (mode != SFmode && mode != DFmode); + case AND: + case IOR: + case XOR: + case ASHIFT: + case ASHIFTRT: + case LSHIFTRT: + if ((GET_CODE (XEXP (op, 0)) == CONST_INT && ! SMALL_INT (XEXP (op, 0))) + || (GET_CODE (XEXP (op, 1)) == CONST_INT && ! SMALL_INT (XEXP (op, 1)))) + return 0; + return 1; + + default: + return 0; + } +} + +/* Return 1 if REG is clobbered in IN. + Return 2 if REG is used in IN. + Return 3 if REG is both used and clobbered in IN. + Return 0 if neither. */ + +static int +reg_clobbered_p (reg, in) + rtx reg; + rtx in; +{ + register enum rtx_code code; + + if (in == 0) + return 0; + + code = GET_CODE (in); + + if (code == SET || code == CLOBBER) + { + rtx dest = SET_DEST (in); + int set = 0; + int used = 0; + + while (GET_CODE (dest) == STRICT_LOW_PART + || GET_CODE (dest) == SUBREG + || GET_CODE (dest) == SIGN_EXTRACT + || GET_CODE (dest) == ZERO_EXTRACT) + dest = XEXP (dest, 0); + + if (dest == reg) + set = 1; + else if (GET_CODE (dest) == REG + && refers_to_regno_p (REGNO (reg), + REGNO (reg) + HARD_REGNO_NREGS (reg, GET_MODE (reg)), + SET_DEST (in), 0)) + { + set = 1; + /* Anything that sets just part of the register + is considered using as well as setting it. + But note that a straight SUBREG of a single-word value + clobbers the entire value. */ + if (dest != SET_DEST (in) + && ! (GET_CODE (SET_DEST (in)) == SUBREG + || UNITS_PER_WORD >= GET_MODE_SIZE (GET_MODE (dest)))) + used = 1; + } + + if (code == SET) + { + if (set) + used = refers_to_regno_p (REGNO (reg), + REGNO (reg) + HARD_REGNO_NREGS (reg, GET_MODE (reg)), + SET_SRC (in), 0); + else + used = refers_to_regno_p (REGNO (reg), + REGNO (reg) + HARD_REGNO_NREGS (reg, GET_MODE (reg)), + in, 0); + } + + return set + used * 2; + } + + if (refers_to_regno_p (REGNO (reg), + REGNO (reg) + HARD_REGNO_NREGS (reg, GET_MODE (reg)), + in, 0)) + return 2; + return 0; +} + +/* Return non-zero if OP can be written to without screwing up + GCC's model of what's going on. It is assumed that this operand + appears in the dest position of a SET insn in a conditional + branch's delay slot. AFTER is the label to start looking from. */ +int +operand_clobbered_before_used_after (op, after) + rtx op; + rtx after; +{ + /* Just experimenting. */ + if (GET_CODE (op) == CC0) + return 1; + if (GET_CODE (op) == REG) + { + rtx insn; + + if (op == stack_pointer_rtx) + return 0; + + /* Scan forward from the label, to see if the value of OP + is clobbered before the first use. */ + + for (insn = NEXT_INSN (after); insn; insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) == NOTE) + continue; + if (GET_CODE (insn) == INSN + || GET_CODE (insn) == JUMP_INSN + || GET_CODE (insn) == CALL_INSN) + { + switch (reg_clobbered_p (op, PATTERN (insn))) + { + default: + return 0; + case 1: + return 1; + case 0: + break; + } + } + /* If we reach another label without clobbering OP, + then we cannot safely write it here. */ + else if (GET_CODE (insn) == CODE_LABEL) + return 0; + if (GET_CODE (insn) == JUMP_INSN) + { + if (condjump_p (insn)) + return 0; + /* This is a jump insn which has already + been mangled. We can't tell what it does. */ + if (GET_CODE (PATTERN (insn)) == PARALLEL) + return 0; + if (! JUMP_LABEL (insn)) + return 0; + /* Keep following jumps. */ + insn = JUMP_LABEL (insn); + } + } + return 1; + } + + /* In both of these cases, the first insn executed + for this op will be a orh whatever%h,%?r0,%?r31, + which is tolerable. */ + if (GET_CODE (op) == MEM) + return (CONSTANT_ADDRESS_P (XEXP (op, 0))); + + return 0; +} + +/* Return non-zero if this pattern, as a source to a "SET", + is known to yield an instruction of unit size. */ +int +single_insn_src_p (op, mode) + rtx op; + enum machine_mode mode; +{ + switch (GET_CODE (op)) + { + case CONST_INT: + /* This is not always a single insn src, technically, + but output_delayed_branch knows how to deal with it. */ + return 1; + + case SYMBOL_REF: + case CONST: + /* This is not a single insn src, technically, + but output_delayed_branch knows how to deal with it. */ + return 1; + + case REG: + return 1; + + case MEM: + return 1; + + /* We never need to negate or complement constants. */ + case NEG: + return (mode != DFmode); + case NOT: + case ZERO_EXTEND: + return 1; + + case PLUS: + case MINUS: + /* Detect cases that require multiple instructions. */ + if (CONSTANT_P (XEXP (op, 1)) + && !(GET_CODE (XEXP (op, 1)) == CONST_INT + && SMALL_INT (XEXP (op, 1)))) + return 0; + case EQ: + case NE: + case LT: + case GT: + case LE: + case GE: + case LTU: + case GTU: + case LEU: + case GEU: + /* Not doing floating point, since they probably + take longer than the branch slot they might fill. */ + return (mode != SFmode && mode != DFmode); + + case AND: + if (GET_CODE (XEXP (op, 1)) == NOT) + { + rtx arg = XEXP (XEXP (op, 1), 0); + if (CONSTANT_P (arg) + && !(GET_CODE (arg) == CONST_INT + && (SMALL_INT (arg) + || INTVAL (arg) & 0xffff == 0))) + return 0; + } + case IOR: + case XOR: + /* Both small and round numbers take one instruction; + others take two. */ + if (CONSTANT_P (XEXP (op, 1)) + && !(GET_CODE (XEXP (op, 1)) == CONST_INT + && (SMALL_INT (XEXP (op, 1)) + || INTVAL (XEXP (op, 1)) & 0xffff == 0))) + return 0; + + case ASHIFT: + case ASHIFTRT: + case LSHIFTRT: + return 1; + + case SUBREG: + if (SUBREG_WORD (op) != 0) + return 0; + return single_insn_src_p (SUBREG_REG (op), mode); + + /* Not doing floating point, since they probably + take longer than the branch slot they might fill. */ + case FLOAT_EXTEND: + case FLOAT_TRUNCATE: + case FLOAT: + case FIX: + case UNSIGNED_FLOAT: + case UNSIGNED_FIX: + return 0; + + default: + return 0; + } +} + +/* Return non-zero only if OP is a register of mode MODE, + or const0_rtx. */ +int +reg_or_0_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (op == const0_rtx || register_operand (op, mode) + || op == CONST0_RTX (mode)); +} + +/* Return truth value of whether OP can be used as an operands in a three + address add/subtract insn (such as add %o1,7,%l2) of mode MODE. */ + +int +arith_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (register_operand (op, mode) + || (GET_CODE (op) == CONST_INT && SMALL_INT (op))); +} + +/* Return 1 if OP is a valid first operand for a logical insn of mode MODE. */ + +int +logic_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (register_operand (op, mode) + || (GET_CODE (op) == CONST_INT && LOGIC_INT (op))); +} + +/* Return 1 if OP is a valid first operand for a shift insn of mode MODE. */ + +int +shift_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (register_operand (op, mode) + || (GET_CODE (op) == CONST_INT)); +} + +/* Return 1 if OP is a valid first operand for either a logical insn + or an add insn of mode MODE. */ + +int +compare_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (register_operand (op, mode) + || (GET_CODE (op) == CONST_INT && SMALL_INT (op) && LOGIC_INT (op))); +} + +/* Return truth value of whether OP can be used as the 5-bit immediate + operand of a bte or btne insn. */ + +int +bte_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (register_operand (op, mode) + || (GET_CODE (op) == CONST_INT + && (unsigned) INTVAL (op) < 0x20)); +} + +/* Return 1 if OP is an indexed memory reference of mode MODE. */ + +int +indexed_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (GET_CODE (op) == MEM && GET_MODE (op) == mode + && GET_CODE (XEXP (op, 0)) == PLUS + && GET_MODE (XEXP (op, 0)) == SImode + && register_operand (XEXP (XEXP (op, 0), 0), SImode) + && register_operand (XEXP (XEXP (op, 0), 1), SImode)); +} + +/* Return 1 if OP is a suitable source operand for a load insn + with mode MODE. */ + +int +load_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (memory_operand (op, mode) || indexed_operand (op, mode)); +} + +/* Return truth value of whether OP is a integer which fits the + range constraining immediate operands in add/subtract insns. */ + +int +small_int (op, mode) + rtx op; + enum machine_mode mode; +{ + return (GET_CODE (op) == CONST_INT && SMALL_INT (op)); +} + +/* Return truth value of whether OP is a integer which fits the + range constraining immediate operands in logic insns. */ + +int +logic_int (op, mode) + rtx op; + enum machine_mode mode; +{ + return (GET_CODE (op) == CONST_INT && LOGIC_INT (op)); +} + +/* Test for a valid operand for a call instruction. + Don't allow the arg pointer register or virtual regs + since they may change into reg + const, which the patterns + can't handle yet. */ + +int +call_insn_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (GET_CODE (op) == MEM + && (CONSTANT_ADDRESS_P (XEXP (op, 0)) + || (GET_CODE (XEXP (op, 0)) == REG + && XEXP (op, 0) != arg_pointer_rtx + && !(REGNO (XEXP (op, 0)) >= FIRST_PSEUDO_REGISTER + && REGNO (XEXP (op, 0)) <= LAST_VIRTUAL_REGISTER)))) + return 1; + 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 (GET_CODE (operands[0]) == MEM) + { + if (GET_CODE (operands[1]) != MEM) + if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) + { + if (! ((cc_prev_status.flags & CC_KNOW_HI_R31) + && (cc_prev_status.flags & CC_HI_R31_ADJ) + && cc_prev_status.mdep == XEXP (operands[0], 0))) + { + CC_STATUS_INIT; + output_asm_insn ("orh %h0,%?r0,%?r31", operands); + } + cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ; + cc_status.mdep = XEXP (operands[0], 0); + return "st.l %r1,%L0(%?r31)"; + } + else + return "st.l %r1,%0"; + else + abort (); +#if 0 + { + rtx xoperands[2]; + + cc_status.flags &= ~CC_F0_IS_0; + xoperands[0] = gen_rtx (REG, SFmode, 32); + xoperands[1] = operands[1]; + output_asm_insn (singlemove_string (xoperands), xoperands); + xoperands[1] = xoperands[0]; + xoperands[0] = operands[0]; + output_asm_insn (singlemove_string (xoperands), xoperands); + return ""; + } +#endif + } + if (GET_CODE (operands[1]) == MEM) + { + if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) + { + if (! ((cc_prev_status.flags & CC_KNOW_HI_R31) + && (cc_prev_status.flags & CC_HI_R31_ADJ) + && cc_prev_status.mdep == XEXP (operands[1], 0))) + { + CC_STATUS_INIT; + output_asm_insn ("orh %h1,%?r0,%?r31", operands); + } + cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ; + cc_status.mdep = XEXP (operands[1], 0); + return "ld.l %L1(%?r31),%0"; + } + return "ld.l %m1,%0"; + } + if (GET_CODE (operands[1]) == CONST_INT) + { + if (operands[1] == const0_rtx) + return "mov %?r0,%0"; + if((INTVAL (operands[1]) & 0xffff0000) == 0) + return "or %L1,%?r0,%0"; + if((INTVAL (operands[1]) & 0xffff8000) == 0xffff8000) + return "adds %1,%?r0,%0"; + if((INTVAL (operands[1]) & 0x0000ffff) == 0) + return "orh %H1,%?r0,%0"; + } + return "mov %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; + int highest_first = 0; + int no_addreg1_decrement = 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 (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 (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) + abort (); + + /* 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 (XEXP (operands[0], 0)); + + if (optype1 == MEMOP) + addreg1 = find_addr_reg (XEXP (operands[1], 0)); + +/* ??? Perhaps in some cases move double words + if there is a spare pair of floating regs. */ + + /* 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 the first move would clobber the source of the second one, + do them in the other order. + + RMS says "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." + + but it happens on the sparc when loading parameter registers, + so I am going to define that circumstance, and make it work + as expected. */ + + if (optype0 == REGOP && optype1 == REGOP + && REGNO (operands[0]) == REGNO (latehalf[1])) + { + CC_STATUS_PARTIAL_INIT; + /* Make any unoffsettable addresses point at high-numbered word. */ + if (addreg0) + output_asm_insn ("adds 0x4,%0,%0", &addreg0); + if (addreg1) + output_asm_insn ("adds 0x4,%0,%0", &addreg1); + + /* Do that word. */ + output_asm_insn (singlemove_string (latehalf), latehalf); + + /* Undo the adds we just did. */ + if (addreg0) + output_asm_insn ("adds -0x4,%0,%0", &addreg0); + if (addreg1) + output_asm_insn ("adds -0x4,%0,%0", &addreg1); + + /* Do low-numbered word. */ + return singlemove_string (operands); + } + else if (optype0 == REGOP && optype1 != REGOP + && reg_overlap_mentioned_p (operands[0], operands[1])) + { + /* If both halves of dest are used in the src memory address, + add the two regs and put them in the low reg (operands[0]). + Then it works to load latehalf first. */ + if (reg_mentioned_p (operands[0], XEXP (operands[1], 0)) + && reg_mentioned_p (latehalf[0], XEXP (operands[1], 0))) + { + rtx xops[2]; + xops[0] = latehalf[0]; + xops[1] = operands[0]; + output_asm_insn ("adds %1,%0,%1", xops); + operands[1] = gen_rtx (MEM, DImode, operands[0]); + latehalf[1] = adj_offsettable_operand (operands[1], 4); + addreg1 = 0; + highest_first = 1; + } + /* Only one register in the dest is used in the src memory address, + and this is the first register of the dest, so we want to do + the late half first here also. */ + else if (! reg_mentioned_p (latehalf[0], XEXP (operands[1], 0))) + highest_first = 1; + /* Only one register in the dest is used in the src memory address, + and this is the second register of the dest, so we want to do + the late half last. If addreg1 is set, and addreg1 is the same + register as latehalf, then we must suppress the trailing decrement, + because it would clobber the value just loaded. */ + else if (addreg1 && reg_mentioned_p (addreg1, latehalf[0])) + no_addreg1_decrement = 1; + } + + /* Normal case: do the two words, low-numbered first. + Overlap case (highest_first set): do high-numbered word first. */ + + if (! highest_first) + output_asm_insn (singlemove_string (operands), operands); + + CC_STATUS_PARTIAL_INIT; + /* Make any unoffsettable addresses point at high-numbered word. */ + if (addreg0) + output_asm_insn ("adds 0x4,%0,%0", &addreg0); + if (addreg1) + output_asm_insn ("adds 0x4,%0,%0", &addreg1); + + /* Do that word. */ + output_asm_insn (singlemove_string (latehalf), latehalf); + + /* Undo the adds we just did. */ + if (addreg0) + output_asm_insn ("adds -0x4,%0,%0", &addreg0); + if (addreg1 && !no_addreg1_decrement) + output_asm_insn ("adds -0x4,%0,%0", &addreg1); + + if (highest_first) + output_asm_insn (singlemove_string (operands), operands); + + return ""; +} + +char * +output_fp_move_double (operands) + rtx *operands; +{ + /* If the source operand is any sort of zero, use f0 instead. */ + + if (operands[1] == CONST0_RTX (GET_MODE (operands[1]))) + operands[1] = gen_rtx (REG, DFmode, F0_REGNUM); + + if (FP_REG_P (operands[0])) + { + if (FP_REG_P (operands[1])) + return "fmov.dd %1,%0"; + if (GET_CODE (operands[1]) == REG) + { + output_asm_insn ("ixfr %1,%0", operands); + operands[0] = gen_rtx (REG, VOIDmode, REGNO (operands[0]) + 1); + operands[1] = gen_rtx (REG, VOIDmode, REGNO (operands[1]) + 1); + return "ixfr %1,%0"; + } + if (operands[1] == CONST0_RTX (DFmode)) + return "fmov.dd f0,%0"; + if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) + { + if (! ((cc_prev_status.flags & CC_KNOW_HI_R31) + && (cc_prev_status.flags & CC_HI_R31_ADJ) + && cc_prev_status.mdep == XEXP (operands[1], 0))) + { + CC_STATUS_INIT; + output_asm_insn ("orh %h1,%?r0,%?r31", operands); + } + cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ; + cc_status.mdep = XEXP (operands[1], 0); + return "fld.d %L1(%?r31),%0"; + } + return "fld.d %1,%0"; + } + else if (FP_REG_P (operands[1])) + { + if (GET_CODE (operands[0]) == REG) + { + output_asm_insn ("fxfr %1,%0", operands); + operands[0] = gen_rtx (REG, VOIDmode, REGNO (operands[0]) + 1); + operands[1] = gen_rtx (REG, VOIDmode, REGNO (operands[1]) + 1); + return "fxfr %1,%0"; + } + if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) + { + if (! ((cc_prev_status.flags & CC_KNOW_HI_R31) + && (cc_prev_status.flags & CC_HI_R31_ADJ) + && cc_prev_status.mdep == XEXP (operands[0], 0))) + { + CC_STATUS_INIT; + output_asm_insn ("orh %h0,%?r0,%?r31", operands); + } + cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ; + cc_status.mdep = XEXP (operands[0], 0); + return "fst.d %1,%L0(%?r31)"; + } + return "fst.d %1,%0"; + } + else + abort (); + /* NOTREACHED */ + return NULL; +} + +/* 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 (CONSTANT_P (XEXP (addr, 0))) + addr = XEXP (addr, 1); + else if (CONSTANT_P (XEXP (addr, 1))) + addr = XEXP (addr, 0); + else + abort (); + } + if (GET_CODE (addr) == REG) + return addr; + abort (); + /* NOTREACHED */ + return NULL; +} + +/* Return a template for a load instruction with mode MODE and + arguments from the string ARGS. + + This string is in static storage. */ + +static char * +load_opcode (mode, args, reg) + enum machine_mode mode; + char *args; + rtx reg; +{ + static char buf[30]; + char *opcode; + + switch (mode) + { + case QImode: + opcode = "ld.b"; + break; + + case HImode: + opcode = "ld.s"; + break; + + case SImode: + case SFmode: + if (FP_REG_P (reg)) + opcode = "fld.l"; + else + opcode = "ld.l"; + break; + + case DImode: + if (!FP_REG_P (reg)) + abort (); + case DFmode: + opcode = "fld.d"; + break; + + default: + abort (); + } + + sprintf (buf, "%s %s", opcode, args); + return buf; +} + +/* Return a template for a store instruction with mode MODE and + arguments from the string ARGS. + + This string is in static storage. */ + +static char * +store_opcode (mode, args, reg) + enum machine_mode mode; + char *args; + rtx reg; +{ + static char buf[30]; + char *opcode; + + switch (mode) + { + case QImode: + opcode = "st.b"; + break; + + case HImode: + opcode = "st.s"; + break; + + case SImode: + case SFmode: + if (FP_REG_P (reg)) + opcode = "fst.l"; + else + opcode = "st.l"; + break; + + case DImode: + if (!FP_REG_P (reg)) + abort (); + case DFmode: + opcode = "fst.d"; + break; + + default: + abort (); + } + + sprintf (buf, "%s %s", opcode, args); + return buf; +} + +/* Output a store-in-memory whose operands are OPERANDS[0,1]. + OPERANDS[0] is a MEM, and OPERANDS[1] is a reg or zero. + + This function returns a template for an insn. + This is in static storage. + + It may also output some insns directly. + It may alter the values of operands[0] and operands[1]. */ + +char * +output_store (operands) + rtx *operands; +{ + enum machine_mode mode = GET_MODE (operands[0]); + rtx address = XEXP (operands[0], 0); + char *string; + + cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ; + cc_status.mdep = address; + + if (! ((cc_prev_status.flags & CC_KNOW_HI_R31) + && (cc_prev_status.flags & CC_HI_R31_ADJ) + && address == cc_prev_status.mdep)) + { + CC_STATUS_INIT; + output_asm_insn ("orh %h0,%?r0,%?r31", operands); + cc_prev_status.mdep = address; + } + + /* Store zero in two parts when appropriate. */ + if (mode == DFmode && operands[1] == CONST0_RTX (DFmode)) + return store_opcode (DFmode, "%r1,%L0(%?r31)", operands[1]); + + /* Code below isn't smart enough to move a doubleword in two parts, + so use output_move_double to do that in the cases that require it. */ + if ((mode == DImode || mode == DFmode) + && ! FP_REG_P (operands[1])) + return output_move_double (operands); + + return store_opcode (mode, "%r1,%L0(%?r31)", operands[1]); +} + +/* Output a load-from-memory whose operands are OPERANDS[0,1]. + OPERANDS[0] is a reg, and OPERANDS[1] is a mem. + + This function returns a template for an insn. + This is in static storage. + + It may also output some insns directly. + It may alter the values of operands[0] and operands[1]. */ + +char * +output_load (operands) + rtx *operands; +{ + enum machine_mode mode = GET_MODE (operands[0]); + rtx address = XEXP (operands[1], 0); + + /* We don't bother trying to see if we know %hi(address). + This is because we are doing a load, and if we know the + %hi value, we probably also know that value in memory. */ + cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ; + cc_status.mdep = address; + + if (! ((cc_prev_status.flags & CC_KNOW_HI_R31) + && (cc_prev_status.flags & CC_HI_R31_ADJ) + && address == cc_prev_status.mdep + && cc_prev_status.mdep == cc_status.mdep)) + { + CC_STATUS_INIT; + output_asm_insn ("orh %h1,%?r0,%?r31", operands); + cc_prev_status.mdep = address; + } + + /* Code below isn't smart enough to move a doubleword in two parts, + so use output_move_double to do that in the cases that require it. */ + if ((mode == DImode || mode == DFmode) + && ! FP_REG_P (operands[0])) + return output_move_double (operands); + + return load_opcode (mode, "%L1(%?r31),%0", operands[0]); +} + +#if 0 +/* Load the address specified by OPERANDS[3] into the register + specified by OPERANDS[0]. + + OPERANDS[3] may be the result of a sum, hence it could either be: + + (1) CONST + (2) REG + (2) REG + CONST_INT + (3) REG + REG + CONST_INT + (4) REG + REG (special case of 3). + + Note that (3) is not a legitimate address. + All cases are handled here. */ + +void +output_load_address (operands) + rtx *operands; +{ + rtx base, offset; + + if (CONSTANT_P (operands[3])) + { + output_asm_insn ("mov %3,%0", operands); + return; + } + + if (REG_P (operands[3])) + { + if (REGNO (operands[0]) != REGNO (operands[3])) + output_asm_insn ("shl %?r0,%3,%0", operands); + return; + } + + if (GET_CODE (operands[3]) != PLUS) + abort (); + + base = XEXP (operands[3], 0); + offset = XEXP (operands[3], 1); + + if (GET_CODE (base) == CONST_INT) + { + rtx tmp = base; + base = offset; + offset = tmp; + } + + if (GET_CODE (offset) != CONST_INT) + { + /* Operand is (PLUS (REG) (REG)). */ + base = operands[3]; + offset = const0_rtx; + } + + if (REG_P (base)) + { + operands[6] = base; + operands[7] = offset; + CC_STATUS_PARTIAL_INIT; + if (SMALL_INT (offset)) + output_asm_insn ("adds %7,%6,%0", operands); + else + output_asm_insn ("mov %7,%0\n\tadds %0,%6,%0", operands); + } + else if (GET_CODE (base) == PLUS) + { + operands[6] = XEXP (base, 0); + operands[7] = XEXP (base, 1); + operands[8] = offset; + + CC_STATUS_PARTIAL_INIT; + if (SMALL_INT (offset)) + output_asm_insn ("adds %6,%7,%0\n\tadds %8,%0,%0", operands); + else + output_asm_insn ("mov %8,%0\n\tadds %0,%6,%0\n\tadds %0,%7,%0", operands); + } + else + abort (); +} +#endif + +/* Output code to place a size count SIZE in register REG. + Because block moves are pipelined, we don't include the + first element in the transfer of SIZE to REG. + For this, we subtract ALIGN. (Actually, I think it is not + right to subtract on this machine, so right now we don't.) */ + +static void +output_size_for_block_move (size, reg, align) + rtx size, reg, align; +{ + rtx xoperands[3]; + + xoperands[0] = reg; + xoperands[1] = size; + xoperands[2] = align; + +#if 1 + cc_status.flags &= ~ CC_KNOW_HI_R31; + output_asm_insn (singlemove_string (xoperands), xoperands); +#else + if (GET_CODE (size) == REG) + output_asm_insn ("sub %2,%1,%0", xoperands); + else + { + xoperands[1] + = gen_rtx (CONST_INT, VOIDmode, INTVAL (size) - INTVAL (align)); + cc_status.flags &= ~ CC_KNOW_HI_R31; + output_asm_insn ("mov %1,%0", xoperands); + } +#endif +} + +/* Emit code to perform a block move. + + OPERANDS[0] is the destination. + OPERANDS[1] is the source. + OPERANDS[2] is the size. + OPERANDS[3] is the known safe alignment. + OPERANDS[4..6] are pseudos we can safely clobber as temps. */ + +char * +output_block_move (operands) + rtx *operands; +{ + /* A vector for our computed operands. Note that load_output_address + makes use of (and can clobber) up to the 8th element of this vector. */ + rtx xoperands[10]; + rtx zoperands[10]; + static int movstrsi_label = 0; + int i, j; + rtx temp1 = operands[4]; + rtx alignrtx = operands[3]; + int align = INTVAL (alignrtx); + int chunk_size; + + xoperands[0] = operands[0]; + xoperands[1] = operands[1]; + xoperands[2] = temp1; + + /* We can't move more than four bytes at a time + because we have only one register to move them through. */ + if (align > 4) + { + align = 4; + alignrtx = gen_rtx (CONST_INT, VOIDmode, 4); + } + + /* Recognize special cases of block moves. These occur + when GNU C++ is forced to treat something as BLKmode + to keep it in memory, when its mode could be represented + with something smaller. + + We cannot do this for global variables, since we don't know + what pages they don't cross. Sigh. */ + if (GET_CODE (operands[2]) == CONST_INT + && ! CONSTANT_ADDRESS_P (operands[0]) + && ! CONSTANT_ADDRESS_P (operands[1])) + { + int size = INTVAL (operands[2]); + rtx op0 = xoperands[0]; + rtx op1 = xoperands[1]; + + if ((align & 3) == 0 && (size & 3) == 0 && (size >> 2) <= 16) + { + if (memory_address_p (SImode, plus_constant (op0, size)) + && memory_address_p (SImode, plus_constant (op1, size))) + { + cc_status.flags &= ~CC_KNOW_HI_R31; + for (i = (size>>2)-1; i >= 0; i--) + { + xoperands[0] = plus_constant (op0, i * 4); + xoperands[1] = plus_constant (op1, i * 4); + output_asm_insn ("ld.l %a1,%?r31\n\tst.l %?r31,%a0", + xoperands); + } + return ""; + } + } + else if ((align & 1) == 0 && (size & 1) == 0 && (size >> 1) <= 16) + { + if (memory_address_p (HImode, plus_constant (op0, size)) + && memory_address_p (HImode, plus_constant (op1, size))) + { + cc_status.flags &= ~CC_KNOW_HI_R31; + for (i = (size>>1)-1; i >= 0; i--) + { + xoperands[0] = plus_constant (op0, i * 2); + xoperands[1] = plus_constant (op1, i * 2); + output_asm_insn ("ld.s %a1,%?r31\n\tst.s %?r31,%a0", + xoperands); + } + return ""; + } + } + else if (size <= 16) + { + if (memory_address_p (QImode, plus_constant (op0, size)) + && memory_address_p (QImode, plus_constant (op1, size))) + { + cc_status.flags &= ~CC_KNOW_HI_R31; + for (i = size-1; i >= 0; i--) + { + xoperands[0] = plus_constant (op0, i); + xoperands[1] = plus_constant (op1, i); + output_asm_insn ("ld.b %a1,%?r31\n\tst.b %?r31,%a0", + xoperands); + } + return ""; + } + } + } + + /* Since we clobber untold things, nix the condition codes. */ + CC_STATUS_INIT; + + /* This is the size of the transfer. + Either use the register which already contains the size, + or use a free register (used by no operands). */ + output_size_for_block_move (operands[2], operands[4], alignrtx); + +#if 0 + /* Also emit code to decrement the size value by ALIGN. */ + zoperands[0] = operands[0]; + zoperands[3] = plus_constant (operands[0], align); + output_load_address (zoperands); +#endif + + /* Generate number for unique label. */ + + xoperands[3] = gen_rtx (CONST_INT, VOIDmode, movstrsi_label++); + + /* Calculate the size of the chunks we will be trying to move first. */ + +#if 0 + if ((align & 3) == 0) + chunk_size = 4; + else if ((align & 1) == 0) + chunk_size = 2; + else +#endif + chunk_size = 1; + + /* Copy the increment (negative) to a register for bla insn. */ + + xoperands[4] = gen_rtx (CONST_INT, VOIDmode, - chunk_size); + xoperands[5] = operands[5]; + output_asm_insn ("adds %4,%?r0,%5", xoperands); + + /* Predecrement the loop counter. This happens again also in the `bla' + instruction which precedes the loop, but we need to have it done + two times before we enter the loop because of the bizarre semantics + of the bla instruction. */ + + output_asm_insn ("adds %5,%2,%2", xoperands); + + /* Check for the case where the original count was less than or equal to + zero. Avoid going through the loop at all if the original count was + indeed less than or equal to zero. Note that we treat the count as + if it were a signed 32-bit quantity here, rather than an unsigned one, + even though we really shouldn't. We have to do this because of the + semantics of the `ble' instruction, which assume that the count is + a signed 32-bit value. Anyway, in practice it won't matter because + nobody is going to try to do a memcpy() of more than half of the + entire address space (i.e. 2 gigabytes) anyway. */ + + output_asm_insn ("bc .Le%3", xoperands); + + /* Make available a register which is a temporary. */ + + xoperands[6] = operands[6]; + + /* Now the actual loop. + In xoperands, elements 1 and 0 are the input and output vectors. + Element 2 is the loop index. Element 5 is the increment. */ + + output_asm_insn ("subs %1,%5,%1", xoperands); + output_asm_insn ("bla %5,%2,.Lm%3", xoperands); + output_asm_insn ("adds %0,%2,%6", xoperands); + output_asm_insn ("\n.Lm%3:", xoperands); /* Label for bla above. */ + output_asm_insn ("\n.Ls%3:", xoperands); /* Loop start label. */ + output_asm_insn ("adds %5,%6,%6", xoperands); + + /* NOTE: The code here which is supposed to handle the cases where the + sources and destinations are known to start on a 4 or 2 byte boundary + are currently broken. They fail to do anything about the overflow + bytes which might still need to be copied even after we have copied + some number of words or halfwords. Thus, for now we use the lowest + common denominator, i.e. the code which just copies some number of + totally unaligned individual bytes. (See the calculation of + chunk_size above. */ + + if (chunk_size == 4) + { + output_asm_insn ("ld.l %2(%1),%?r31", xoperands); + output_asm_insn ("bla %5,%2,.Ls%3", xoperands); + output_asm_insn ("st.l %?r31,8(%6)", xoperands); + } + else if (chunk_size == 2) + { + output_asm_insn ("ld.s %2(%1),%?r31", xoperands); + output_asm_insn ("bla %5,%2,.Ls%3", xoperands); + output_asm_insn ("st.s %?r31,4(%6)", xoperands); + } + else /* chunk_size == 1 */ + { + output_asm_insn ("ld.b %2(%1),%?r31", xoperands); + output_asm_insn ("bla %5,%2,.Ls%3", xoperands); + output_asm_insn ("st.b %?r31,2(%6)", xoperands); + } + output_asm_insn ("\n.Le%3:", xoperands); /* Here if count <= 0. */ + + return ""; +} + +/* Output a delayed branch insn with the delay insn in its + branch slot. The delayed branch insn template is in TEMPLATE, + with operands OPERANDS. The insn in its delay slot is INSN. + + As a special case, since we know that all memory transfers are via + ld/st insns, if we see a (MEM (SYMBOL_REF ...)) we divide the memory + reference around the branch as + + orh ha%x,%?r0,%?r31 + b ... + ld/st l%x(%?r31),... + + As another special case, we handle loading (SYMBOL_REF ...) and + other large constants around branches as well: + + orh h%x,%?r0,%0 + b ... + or l%x,%0,%1 + + */ + +char * +output_delayed_branch (template, operands, insn) + char *template; + rtx *operands; + rtx insn; +{ + rtx src = XVECEXP (PATTERN (insn), 0, 1); + rtx dest = XVECEXP (PATTERN (insn), 0, 0); + + /* See if we are doing some branch together with setting some register + to some 32-bit value which does (or may) have some of the high-order + 16 bits set. If so, we need to set the register in two stages. One + stage must be done before the branch, and the other one can be done + in the delay slot. */ + + if ( (GET_CODE (src) == CONST_INT + && ((unsigned) INTVAL (src) & (unsigned) 0xffff0000) != (unsigned) 0) + || (GET_CODE (src) == SYMBOL_REF) + || (GET_CODE (src) == LABEL_REF) + || (GET_CODE (src) == CONST)) + { + rtx xoperands[2]; + xoperands[0] = dest; + xoperands[1] = src; + + CC_STATUS_PARTIAL_INIT; + /* Output the `orh' insn. */ + output_asm_insn ("orh %H1,%?r0,%0", xoperands); + + /* Output the branch instruction next. */ + output_asm_insn (template, operands); + + /* Now output the `or' insn. */ + output_asm_insn ("or %L1,%0,%0", xoperands); + } + else if ((GET_CODE (src) == MEM + && CONSTANT_ADDRESS_P (XEXP (src, 0))) + || (GET_CODE (dest) == MEM + && CONSTANT_ADDRESS_P (XEXP (dest, 0)))) + { + rtx xoperands[2]; + char *split_template; + xoperands[0] = dest; + xoperands[1] = src; + + /* Output the `orh' insn. */ + if (GET_CODE (src) == MEM) + { + if (! ((cc_prev_status.flags & CC_KNOW_HI_R31) + && (cc_prev_status.flags & CC_HI_R31_ADJ) + && cc_prev_status.mdep == XEXP (operands[1], 0))) + { + CC_STATUS_INIT; + output_asm_insn ("orh %h1,%?r0,%?r31", xoperands); + } + split_template = load_opcode (GET_MODE (dest), + "%L1(%?r31),%0", dest); + } + else + { + if (! ((cc_prev_status.flags & CC_KNOW_HI_R31) + && (cc_prev_status.flags & CC_HI_R31_ADJ) + && cc_prev_status.mdep == XEXP (operands[0], 0))) + { + CC_STATUS_INIT; + output_asm_insn ("orh %h0,%?r0,%?r31", xoperands); + } + split_template = store_opcode (GET_MODE (dest), + "%r1,%L0(%?r31)", src); + } + + /* Output the branch instruction next. */ + output_asm_insn (template, operands); + + /* Now output the load or store. + No need to do a CC_STATUS_INIT, because we are branching anyway. */ + output_asm_insn (split_template, xoperands); + } + else + { + int insn_code_number; + rtx pat = gen_rtx (SET, VOIDmode, dest, src); + rtx delay_insn = gen_rtx (INSN, VOIDmode, 0, 0, 0, pat, -1, 0, 0); + int i; + + /* Output the branch instruction first. */ + output_asm_insn (template, operands); + + /* Now recognize the insn which we put in its delay slot. + We must do this after outputting the branch insn, + since operands may just be a pointer to `recog_operand'. */ + INSN_CODE (delay_insn) = insn_code_number = recog (pat, delay_insn); + if (insn_code_number == -1) + abort (); + + for (i = 0; i < insn_n_operands[insn_code_number]; i++) + { + if (GET_CODE (recog_operand[i]) == SUBREG) + recog_operand[i] = alter_subreg (recog_operand[i]); + } + + insn_extract (delay_insn); + if (! constrain_operands (insn_code_number, 1)) + fatal_insn_not_found (delay_insn); + + template = insn_template[insn_code_number]; + if (template == 0) + template = (*insn_outfun[insn_code_number]) (recog_operand, delay_insn); + output_asm_insn (template, recog_operand); + } + CC_STATUS_INIT; + return ""; +} + +/* Output a newly constructed insn DELAY_INSN. */ +char * +output_delay_insn (delay_insn) + rtx delay_insn; +{ + char *template; + int insn_code_number; + int i; + + /* Now recognize the insn which we put in its delay slot. + We must do this after outputting the branch insn, + since operands may just be a pointer to `recog_operand'. */ + insn_code_number = recog_memoized (delay_insn); + if (insn_code_number == -1) + abort (); + + /* Extract the operands of this delay insn. */ + INSN_CODE (delay_insn) = insn_code_number; + insn_extract (delay_insn); + + /* It is possible that this insn has not been properly scanned by final + yet. If this insn's operands don't appear in the peephole's + actual operands, then they won't be fixed up by final, so we + make sure they get fixed up here. -- This is a kludge. */ + for (i = 0; i < insn_n_operands[insn_code_number]; i++) + { + if (GET_CODE (recog_operand[i]) == SUBREG) + recog_operand[i] = alter_subreg (recog_operand[i]); + } + +#ifdef REGISTER_CONSTRAINTS + if (! constrain_operands (insn_code_number)) + abort (); +#endif + + cc_prev_status = cc_status; + + /* Update `cc_status' for this instruction. + The instruction's output routine may change it further. + If the output routine for a jump insn needs to depend + on the cc status, it should look at cc_prev_status. */ + + NOTICE_UPDATE_CC (PATTERN (delay_insn), delay_insn); + + /* Now get the template for what this insn would + have been, without the branch. */ + + template = insn_template[insn_code_number]; + if (template == 0) + template = (*insn_outfun[insn_code_number]) (recog_operand, delay_insn); + output_asm_insn (template, recog_operand); + return ""; +} + +/* Special routine to convert an SFmode value represented as a + CONST_DOUBLE into its equivalent unsigned long bit pattern. + We convert the value from a double precision floating-point + value to single precision first, and thence to a bit-wise + equivalent unsigned long value. This routine is used when + generating an immediate move of an SFmode value directly + into a general register because the svr4 assembler doesn't + grok floating literals in instruction operand contexts. */ + +unsigned long +sfmode_constant_to_ulong (x) + rtx x; +{ + REAL_VALUE_TYPE d; + union { float f; unsigned long i; } u2; + + if (GET_CODE (x) != CONST_DOUBLE || GET_MODE (x) != SFmode) + abort (); + +#if TARGET_FLOAT_FORMAT != HOST_FLOAT_FORMAT + error IEEE emulation needed +#endif + REAL_VALUE_FROM_CONST_DOUBLE (d, x); + u2.f = d; + return u2.i; +} + +/* This function generates the assembly code for function entry. + The macro FUNCTION_PROLOGUE in i860.h is defined to call this function. + + ASM_FILE is a stdio stream to output the code to. + SIZE is an int: how many units of temporary storage to allocate. + + Refer to the array `regs_ever_live' to determine which registers + to save; `regs_ever_live[I]' is nonzero if register number I + is ever used in the function. This macro is responsible for + knowing which registers should not be saved even if used. + + NOTE: `frame_lower_bytes' is the count of bytes which will lie + between the new `fp' value and the new `sp' value after the + prologue is done. `frame_upper_bytes' is the count of bytes + that will lie between the new `fp' and the *old* `sp' value + after the new `fp' is setup (in the prologue). The upper + part of each frame always includes at least 2 words (8 bytes) + to hold the saved frame pointer and the saved return address. + + The svr4 ABI for the i860 now requires that the values of the + stack pointer and frame pointer registers be kept aligned to + 16-byte boundaries at all times. We obey that restriction here. + + The svr4 ABI for the i860 is entirely vague when it comes to specifying + exactly where the "preserved" registers should be saved. The native + svr4 C compiler I now have doesn't help to clarify the requirements + very much because it is plainly out-of-date and non-ABI-compliant + (in at least one important way, i.e. how it generates function + epilogues). + + The native svr4 C compiler saves the "preserved" registers (i.e. + r4-r15 and f2-f7) in the lower part of a frame (i.e. at negative + offsets from the frame pointer). + + Previous versions of GCC also saved the "preserved" registers in the + "negative" part of the frame, but they saved them using positive + offsets from the (adjusted) stack pointer (after it had been adjusted + to allocate space for the new frame). That's just plain wrong + because if the current function calls alloca(), the stack pointer + will get moved, and it will be impossible to restore the registers + properly again after that. + + Both compilers handled parameter registers (i.e. r16-r27 and f8-f15) + by copying their values either into various "preserved" registers or + into stack slots in the lower part of the current frame (as seemed + appropriate, depending upon subsequent usage of these values). + + Here we want to save the preserved registers at some offset from the + frame pointer register so as to avoid any possible problems arising + from calls to alloca(). We can either save them at small positive + offsets from the frame pointer, or at small negative offsets from + the frame pointer. If we save them at small negative offsets from + the frame pointer (i.e. in the lower part of the frame) then we + must tell the rest of GCC (via STARTING_FRAME_OFFSET) exactly how + many bytes of space we plan to use in the lower part of the frame + for this purpose. Since other parts of the compiler reference the + value of STARTING_FRAME_OFFSET long before final() calls this function, + we would have to go ahead and assume the worst-case storage requirements + for saving all of the "preserved" registers (and use that number, i.e. + `80', to define STARTING_FRAME_OFFSET) if we wanted to save them in + the lower part of the frame. That could potentially be very wasteful, + and that wastefulness could really hamper people compiling for embedded + i860 targets with very tight limits on stack space. Thus, we choose + here to save the preserved registers in the upper part of the + frame, so that we can decide at the very last minute how much (or how + little) space we must allocate for this purpose. + + To satisfy the needs of the svr4 ABI "tdesc" scheme, preserved + registers must always be saved so that the saved values of registers + with higher numbers are at higher addresses. We obey that restriction + here. + + There are two somewhat different ways that you can generate prologues + here... i.e. pedantically ABI-compliant, and the "other" way. The + "other" way is more consistent with what is currently generated by the + "native" svr4 C compiler for the i860. That's important if you want + to use the current (as of 8/91) incarnation of svr4 SDB for the i860. + The SVR4 SDB for the i860 insists on having function prologues be + non-ABI-compliant! + + To get fully ABI-compliant prologues, define I860_STRICT_ABI_PROLOGUES + in the i860svr4.h file. (By default this is *not* defined). + + The differences between the ABI-compliant and non-ABI-compliant prologues + are that (a) the ABI version seems to require the use of *signed* + (rather than unsigned) adds and subtracts, and (b) the ordering of + the various steps (e.g. saving preserved registers, saving the + return address, setting up the new frame pointer value) is different. + + For strict ABI compliance, it seems to be the case that the very last + thing that is supposed to happen in the prologue is getting the frame + pointer set to its new value (but only after everything else has + already been properly setup). We do that here, but only if the symbol + I860_STRICT_ABI_PROLOGUES is defined. +*/ + +#ifndef STACK_ALIGNMENT +#define STACK_ALIGNMENT 16 +#endif + +extern char call_used_regs[]; +extern int leaf_function_p (); + +char *current_function_original_name; + +static int must_preserve_r1; +static unsigned must_preserve_bytes; + +void +function_prologue (asm_file, local_bytes) + register FILE *asm_file; + register unsigned local_bytes; +{ + register unsigned frame_lower_bytes; + register unsigned frame_upper_bytes; + register unsigned total_fsize; + register unsigned preserved_reg_bytes = 0; + register unsigned i; + register unsigned preserved_so_far = 0; + + must_preserve_r1 = (optimize < 2 || ! leaf_function_p ()); + must_preserve_bytes = 4 + (must_preserve_r1 ? 4 : 0); + + /* Count registers that need preserving. Ignore r0. It never needs + preserving. */ + + for (i = 1; i < FIRST_PSEUDO_REGISTER; i++) + { + if (regs_ever_live[i] && ! call_used_regs[i]) + preserved_reg_bytes += 4; + } + + /* Round-up the frame_lower_bytes so that it's a multiple of 16. */ + + frame_lower_bytes = (local_bytes + STACK_ALIGNMENT - 1) & -STACK_ALIGNMENT; + + /* The upper part of each frame will contain the saved fp, + the saved r1, and stack slots for all of the other "preserved" + registers that we find we will need to save & restore. */ + + frame_upper_bytes = must_preserve_bytes + preserved_reg_bytes; + + /* Round-up the frame_upper_bytes so that it's a multiple of 16. */ + + frame_upper_bytes + = (frame_upper_bytes + STACK_ALIGNMENT - 1) & -STACK_ALIGNMENT; + + total_fsize = frame_upper_bytes + frame_lower_bytes; + +#ifndef I860_STRICT_ABI_PROLOGUES + + /* There are two kinds of function prologues. + You use the "small" version if the total frame size is + small enough so that it can fit into an immediate 16-bit + value in one instruction. Otherwise, you use the "large" + version of the function prologue. */ + + if (total_fsize > 0x7fff) + { + /* Adjust the stack pointer. The ABI sez to do this using `adds', + but the native C compiler on svr4 uses `addu'. */ + + fprintf (asm_file, "\taddu -%d,%ssp,%ssp\n", + frame_upper_bytes, i860_reg_prefix, i860_reg_prefix); + + /* Save the old frame pointer. */ + + fprintf (asm_file, "\tst.l %sfp,0(%ssp)\n", + i860_reg_prefix, i860_reg_prefix); + + /* Setup the new frame pointer. The ABI sez to do this after + preserving registers (using adds), but that's not what the + native C compiler on svr4 does. */ + + fprintf (asm_file, "\taddu 0,%ssp,%sfp\n", + i860_reg_prefix, i860_reg_prefix); + + /* Get the value of frame_lower_bytes into r31. */ + + fprintf (asm_file, "\torh %d,%sr0,%sr31\n", + frame_lower_bytes >> 16, i860_reg_prefix, i860_reg_prefix); + fprintf (asm_file, "\tor %d,%sr31,%sr31\n", + frame_lower_bytes & 0xffff, i860_reg_prefix, i860_reg_prefix); + + /* Now re-adjust the stack pointer using the value in r31. + The ABI sez to do this with `subs' but SDB may prefer `subu'. */ + + fprintf (asm_file, "\tsubu %ssp,%sr31,%ssp\n", + i860_reg_prefix, i860_reg_prefix, i860_reg_prefix); + + /* Preserve registers. The ABI sez to do this before setting + up the new frame pointer, but that's not what the native + C compiler on svr4 does. */ + + for (i = 1; i < 32; i++) + if (regs_ever_live[i] && ! call_used_regs[i]) + fprintf (asm_file, "\tst.l %s%s,%d(%sfp)\n", + i860_reg_prefix, reg_names[i], + must_preserve_bytes + (4 * preserved_so_far++), + i860_reg_prefix); + + for (i = 32; i < 64; i++) + if (regs_ever_live[i] && ! call_used_regs[i]) + fprintf (asm_file, "\tfst.l %s%s,%d(%sfp)\n", + i860_reg_prefix, reg_names[i], + must_preserve_bytes + (4 * preserved_so_far++), + i860_reg_prefix); + + /* Save the return address. */ + + if (must_preserve_r1) + fprintf (asm_file, "\tst.l %sr1,4(%sfp)\n", + i860_reg_prefix, i860_reg_prefix); + } + else + { + /* Adjust the stack pointer. The ABI sez to do this using `adds', + but the native C compiler on svr4 uses `addu'. */ + + fprintf (asm_file, "\taddu -%d,%ssp,%ssp\n", + total_fsize, i860_reg_prefix, i860_reg_prefix); + + /* Save the old frame pointer. */ + + fprintf (asm_file, "\tst.l %sfp,%d(%ssp)\n", + i860_reg_prefix, frame_lower_bytes, i860_reg_prefix); + + /* Setup the new frame pointer. The ABI sez to do this after + preserving registers and after saving the return address, + (and its saz to do this using adds), but that's not what the + native C compiler on svr4 does. */ + + fprintf (asm_file, "\taddu %d,%ssp,%sfp\n", + frame_lower_bytes, i860_reg_prefix, i860_reg_prefix); + + /* Preserve registers. The ABI sez to do this before setting + up the new frame pointer, but that's not what the native + compiler on svr4 does. */ + + for (i = 1; i < 32; i++) + if (regs_ever_live[i] && ! call_used_regs[i]) + fprintf (asm_file, "\tst.l %s%s,%d(%sfp)\n", + i860_reg_prefix, reg_names[i], + must_preserve_bytes + (4 * preserved_so_far++), + i860_reg_prefix); + + for (i = 32; i < 64; i++) + if (regs_ever_live[i] && ! call_used_regs[i]) + fprintf (asm_file, "\tfst.l %s%s,%d(%sfp)\n", + i860_reg_prefix, reg_names[i], + must_preserve_bytes + (4 * preserved_so_far++), + i860_reg_prefix); + + /* Save the return address. The ABI sez to do this earlier, + and also via an offset from %sp, but the native C compiler + on svr4 does it later (i.e. now) and uses an offset from + %fp. */ + + if (must_preserve_r1) + fprintf (asm_file, "\tst.l %sr1,4(%sfp)\n", + i860_reg_prefix, i860_reg_prefix); + } + +#else /* defined(I860_STRICT_ABI_PROLOGUES) */ + + /* There are two kinds of function prologues. + You use the "small" version if the total frame size is + small enough so that it can fit into an immediate 16-bit + value in one instruction. Otherwise, you use the "large" + version of the function prologue. */ + + if (total_fsize > 0x7fff) + { + /* Adjust the stack pointer (thereby allocating a new frame). */ + + fprintf (asm_file, "\tadds -%d,%ssp,%ssp\n", + frame_upper_bytes, i860_reg_prefix, i860_reg_prefix); + + /* Save the caller's frame pointer. */ + + fprintf (asm_file, "\tst.l %sfp,0(%ssp)\n", + i860_reg_prefix, i860_reg_prefix); + + /* Save return address. */ + + if (must_preserve_r1) + fprintf (asm_file, "\tst.l %sr1,4(%ssp)\n", + i860_reg_prefix, i860_reg_prefix); + + /* Get the value of frame_lower_bytes into r31 for later use. */ + + fprintf (asm_file, "\torh %d,%sr0,%sr31\n", + frame_lower_bytes >> 16, i860_reg_prefix, i860_reg_prefix); + fprintf (asm_file, "\tor %d,%sr31,%sr31\n", + frame_lower_bytes & 0xffff, i860_reg_prefix, i860_reg_prefix); + + /* Now re-adjust the stack pointer using the value in r31. */ + + fprintf (asm_file, "\tsubs %ssp,%sr31,%ssp\n", + i860_reg_prefix, i860_reg_prefix, i860_reg_prefix); + + /* Pre-compute value to be used as the new frame pointer. */ + + fprintf (asm_file, "\tadds %ssp,%sr31,%sr31\n", + i860_reg_prefix, i860_reg_prefix, i860_reg_prefix); + + /* Preserve registers. */ + + for (i = 1; i < 32; i++) + if (regs_ever_live[i] && ! call_used_regs[i]) + fprintf (asm_file, "\tst.l %s%s,%d(%sr31)\n", + i860_reg_prefix, reg_names[i], + must_preserve_bytes + (4 * preserved_so_far++), + i860_reg_prefix); + + for (i = 32; i < 64; i++) + if (regs_ever_live[i] && ! call_used_regs[i]) + fprintf (asm_file, "\tfst.l %s%s,%d(%sr31)\n", + i860_reg_prefix, reg_names[i], + must_preserve_bytes + (4 * preserved_so_far++), + i860_reg_prefix); + + /* Actually set the new value of the frame pointer. */ + + fprintf (asm_file, "\tmov %sr31,%sfp\n", + i860_reg_prefix, i860_reg_prefix); + } + else + { + /* Adjust the stack pointer. */ + + fprintf (asm_file, "\tadds -%d,%ssp,%ssp\n", + total_fsize, i860_reg_prefix, i860_reg_prefix); + + /* Save the caller's frame pointer. */ + + fprintf (asm_file, "\tst.l %sfp,%d(%ssp)\n", + i860_reg_prefix, frame_lower_bytes, i860_reg_prefix); + + /* Save the return address. */ + + if (must_preserve_r1) + fprintf (asm_file, "\tst.l %sr1,%d(%ssp)\n", + i860_reg_prefix, frame_lower_bytes + 4, i860_reg_prefix); + + /* Preserve registers. */ + + for (i = 1; i < 32; i++) + if (regs_ever_live[i] && ! call_used_regs[i]) + fprintf (asm_file, "\tst.l %s%s,%d(%ssp)\n", + i860_reg_prefix, reg_names[i], + frame_lower_bytes + must_preserve_bytes + (4 * preserved_so_far++), + i860_reg_prefix); + + for (i = 32; i < 64; i++) + if (regs_ever_live[i] && ! call_used_regs[i]) + fprintf (asm_file, "\tfst.l %s%s,%d(%ssp)\n", + i860_reg_prefix, reg_names[i], + frame_lower_bytes + must_preserve_bytes + (4 * preserved_so_far++), + i860_reg_prefix); + + /* Setup the new frame pointer. */ + + fprintf (asm_file, "\tadds %d,%ssp,%sfp\n", + frame_lower_bytes, i860_reg_prefix, i860_reg_prefix); + } +#endif /* defined(I860_STRICT_ABI_PROLOGUES) */ + +#ifdef ASM_OUTPUT_PROLOGUE_SUFFIX + ASM_OUTPUT_PROLOGUE_SUFFIX (asm_file); +#endif /* defined(ASM_OUTPUT_PROLOGUE_SUFFIX) */ +} + +/* This function generates the assembly code for function exit. + The macro FUNCTION_EPILOGUE in i860.h is defined to call this function. + + ASM_FILE is a stdio stream to output the code to. + SIZE is an int: how many units of temporary storage to allocate. + + The function epilogue should not depend on the current stack pointer! + It should use the frame pointer only. This is mandatory because + of alloca; we also take advantage of it to omit stack adjustments + before returning. + + Note that when we go to restore the preserved register values we must + not try to address their slots by using offsets from the stack pointer. + That's because the stack pointer may have been moved during the function + execution due to a call to alloca(). Rather, we must restore all + preserved registers via offsets from the frame pointer value. + + Note also that when the current frame is being "popped" (by adjusting + the value of the stack pointer) on function exit, we must (for the + sake of alloca) set the new value of the stack pointer based upon + the current value of the frame pointer. We can't just add what we + believe to be the (static) frame size to the stack pointer because + if we did that, and alloca() had been called during this function, + we would end up returning *without* having fully deallocated all of + the space grabbed by alloca. If that happened, and a function + containing one or more alloca() calls was called over and over again, + then the stack would grow without limit! + + Finally note that the epilogues generated here are completely ABI + compliant. They go out of their way to insure that the value in + the frame pointer register is never less than the value in the stack + pointer register. It's not clear why this relationship needs to be + maintained at all times, but maintaining it only costs one extra + instruction, so what the hell. +*/ + +/* This corresponds to a version 4 TDESC structure. Lower numbered + versions successively omit the last word of the structure. We + don't try to handle version 5 here. */ + +typedef struct TDESC_flags { + int version:4; + int reg_packing:1; + int callable_block:1; + int reserved:4; + int fregs:6; /* fp regs 2-7 */ + int iregs:16; /* regs 0-15 */ +} TDESC_flags; + +typedef struct TDESC { + TDESC_flags flags; + int integer_reg_offset; /* same as must_preserve_bytes */ + int floating_point_reg_offset; + unsigned int positive_frame_size; /* same as frame_upper_bytes */ + unsigned int negative_frame_size; /* same as frame_lower_bytes */ +} TDESC; + +void +function_epilogue (asm_file, local_bytes) + register FILE *asm_file; + register unsigned local_bytes; +{ + register unsigned frame_upper_bytes; + register unsigned frame_lower_bytes; + register unsigned preserved_reg_bytes = 0; + register unsigned i; + register unsigned restored_so_far = 0; + register unsigned int_restored; + register unsigned mask; + unsigned intflags=0; + register TDESC_flags *flags = (TDESC_flags *) &intflags; + + flags->version = 4; + flags->reg_packing = 1; + flags->iregs = 8; /* old fp always gets saved */ + + /* Round-up the frame_lower_bytes so that it's a multiple of 16. */ + + frame_lower_bytes = (local_bytes + STACK_ALIGNMENT - 1) & -STACK_ALIGNMENT; + + /* Count the number of registers that were preserved in the prologue. + Ignore r0. It is never preserved. */ + + for (i = 1; i < FIRST_PSEUDO_REGISTER; i++) + { + if (regs_ever_live[i] && ! call_used_regs[i]) + preserved_reg_bytes += 4; + } + + /* The upper part of each frame will contain only saved fp, + the saved r1, and stack slots for all of the other "preserved" + registers that we find we will need to save & restore. */ + + frame_upper_bytes = must_preserve_bytes + preserved_reg_bytes; + + /* Round-up frame_upper_bytes so that t is a multiple of 16. */ + + frame_upper_bytes + = (frame_upper_bytes + STACK_ALIGNMENT - 1) & -STACK_ALIGNMENT; + + /* Restore all of the "preserved" registers that need restoring. */ + + mask = 2; + + for (i = 1; i < 32; i++, mask<<=1) + if (regs_ever_live[i] && ! call_used_regs[i]) { + fprintf (asm_file, "\tld.l %d(%sfp),%s%s\n", + must_preserve_bytes + (4 * restored_so_far++), + i860_reg_prefix, i860_reg_prefix, reg_names[i]); + if (i > 3 && i < 16) + flags->iregs |= mask; + } + + int_restored = restored_so_far; + mask = 1; + + for (i = 32; i < 64; i++) { + if (regs_ever_live[i] && ! call_used_regs[i]) { + fprintf (asm_file, "\tfld.l %d(%sfp),%s%s\n", + must_preserve_bytes + (4 * restored_so_far++), + i860_reg_prefix, i860_reg_prefix, reg_names[i]); + if (i > 33 & i < 40) + flags->fregs |= mask; + } + if (i > 33 && i < 40) + mask<<=1; + } + + /* Get the value we plan to use to restore the stack pointer into r31. */ + + fprintf (asm_file, "\tadds %d,%sfp,%sr31\n", + frame_upper_bytes, i860_reg_prefix, i860_reg_prefix); + + /* Restore the return address and the old frame pointer. */ + + if (must_preserve_r1) { + fprintf (asm_file, "\tld.l 4(%sfp),%sr1\n", + i860_reg_prefix, i860_reg_prefix); + flags->iregs |= 2; + } + + fprintf (asm_file, "\tld.l 0(%sfp),%sfp\n", + i860_reg_prefix, i860_reg_prefix); + + /* Return and restore the old stack pointer value. */ + + fprintf (asm_file, "\tbri %sr1\n\tmov %sr31,%ssp\n", + i860_reg_prefix, i860_reg_prefix, i860_reg_prefix); + +#ifdef OUTPUT_TDESC /* Output an ABI-compliant TDESC entry */ + if (! frame_lower_bytes) { + flags->version--; + if (! frame_upper_bytes) { + flags->version--; + if (restored_so_far == int_restored) /* No FP saves */ + flags->version--; + } + } + assemble_name(asm_file,current_function_original_name); + fputs(".TDESC:\n", asm_file); + fprintf(asm_file, "%s 0x%0x\n", ASM_LONG, intflags); + fprintf(asm_file, "%s %d\n", ASM_LONG, + int_restored ? must_preserve_bytes : 0); + if (flags->version > 1) { + fprintf(asm_file, "%s %d\n", ASM_LONG, + (restored_so_far == int_restored) ? 0 : must_preserve_bytes + + (4 * int_restored)); + if (flags->version > 2) { + fprintf(asm_file, "%s %d\n", ASM_LONG, frame_upper_bytes); + if (flags->version > 3) + fprintf(asm_file, "%s %d\n", ASM_LONG, frame_lower_bytes); + } + } + tdesc_section(); + fprintf(asm_file, "%s ", ASM_LONG); + assemble_name(asm_file, current_function_original_name); + fprintf(asm_file, "\n%s ", ASM_LONG); + assemble_name(asm_file, current_function_original_name); + fputs(".TDESC\n", asm_file); + text_section(); +#endif +} diff --git a/gnu/usr.bin/gcc/config/i860/i860.h b/gnu/usr.bin/gcc/config/i860/i860.h new file mode 100644 index 00000000000..d182b860da1 --- /dev/null +++ b/gnu/usr.bin/gcc/config/i860/i860.h @@ -0,0 +1,1433 @@ +/* Definitions of target machine for GNU compiler, for Intel 860. + Copyright (C) 1989, 1991, 1993, 1995 Free Software Foundation, Inc. + Hacked substantially by Ron Guilmette (rfg@segfault.us.com) to cater to + the whims of the System V Release 4 assembler. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + + +/* Note that some other tm.h files include this one and then override + many of the definitions that relate to assembler syntax. */ + + +/* Names to predefine in the preprocessor for this target machine. */ + +#define CPP_PREDEFINES "-Di860 -Dunix -Asystem(unix) -Asystem(svr4) -Acpu(i860) -Amachine(i860)" + +/* Print subsidiary information on the compiler version in use. */ +#define TARGET_VERSION fprintf (stderr, " (i860)"); + +/* Run-time compilation parameters selecting different hardware subsets + or supersets. + + On the i860, we have one: TARGET_XP. This option allows gcc to generate + additional instructions available only on the newer i860 XP (but not on + the older i860 XR). +*/ + +extern int target_flags; + +/* Nonzero if we should generate code to use the fpu. */ +#define TARGET_XP (target_flags & 1) + +/* Macro to define tables used to set the flags. + This is a list in braces of pairs in braces, + each pair being { "NAME", VALUE } + where VALUE is the bits to set or minus the bits to clear. + An empty string NAME is used to identify the default VALUE. */ + +#define TARGET_SWITCHES \ + { {"xp", 1}, \ + {"noxp", -1}, \ + {"xr", -1}, \ + { "", TARGET_DEFAULT}} + +#define TARGET_DEFAULT 0 + +/* target machine storage layout */ + +/* Define this if most significant bit is lowest numbered + in instructions that operate on numbered bit-fields. + This is a moot question on the i860 due to the lack of bit-field insns. */ +#define BITS_BIG_ENDIAN 0 + +/* Define this if most significant byte of a word is the lowest numbered. */ +/* That is not true on i860 in the mode we will use. */ +#define BYTES_BIG_ENDIAN 0 + +/* Define this if most significant word of a multiword number is the lowest + numbered. */ +/* For the i860 this goes with BYTES_BIG_ENDIAN. */ +/* NOTE: GCC probably cannot support a big-endian i860 + because GCC fundamentally assumes that the order of words + in memory as the same as the order in registers. + That's not true for the big-endian i860. + The big-endian i860 isn't important enough to + justify the trouble of changing this assumption. */ +#define WORDS_BIG_ENDIAN 0 + +/* number of bits in an addressable storage unit */ +#define BITS_PER_UNIT 8 + +/* Width in bits of a "word", which is the contents of a machine register. + Note that this is not necessarily the width of data type `int'; + if using 16-bit ints on a 68000, this would still be 32. + But on a machine with 16-bit registers, this would be 16. */ +#define BITS_PER_WORD 32 + +/* Width of a word, in units (bytes). */ +#define UNITS_PER_WORD 4 + +/* Width in bits of a pointer. + See also the macro `Pmode' defined below. */ +#define POINTER_SIZE 32 + +/* Allocation boundary (in *bits*) for storing arguments in argument list. */ +#define PARM_BOUNDARY 32 + +/* Boundary (in *bits*) on which stack pointer should be aligned. */ +#define STACK_BOUNDARY 128 + +/* Allocation boundary (in *bits*) for the code of a function. */ +#define FUNCTION_BOUNDARY 64 + +/* Alignment of field after `int : 0' in a structure. */ +#define EMPTY_FIELD_BOUNDARY 32 + +/* Every structure's size must be a multiple of this. */ +#define STRUCTURE_SIZE_BOUNDARY 8 + +/* Minimum size in bits of the largest boundary to which any + and all fundamental data types supported by the hardware + might need to be aligned. No data type wants to be aligned + rounder than this. The i860 supports 128-bit (long double) + floating point quantities, and the System V Release 4 i860 + ABI requires these to be aligned to 16-byte (128-bit) + boundaries. */ +#define BIGGEST_ALIGNMENT 128 + +/* Set this nonzero if move instructions will actually fail to work + when given unaligned data. */ +#define STRICT_ALIGNMENT 1 + +/* If bit field type is int, dont let it cross an int, + and give entire struct the alignment of an int. */ +#define PCC_BITFIELD_TYPE_MATTERS 1 + +/* Standard register usage. */ + +/* Number of actual hardware registers. + The hardware registers are assigned numbers for the compiler + from 0 to just below FIRST_PSEUDO_REGISTER. + All registers that the compiler knows about must be given numbers, + even those that are not normally considered general registers. + + i860 has 32 fullword registers and 32 floating point registers. */ + +#define FIRST_PSEUDO_REGISTER 64 + +/* 1 for registers that have pervasive standard uses + and are not available for the register allocator. + On the i860, this includes the always-0 registers + and fp, sp, arg pointer, and the return address. + Also r31, used for special purposes for constant addresses. */ +#define FIXED_REGISTERS \ + {1, 1, 1, 1, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 1, \ + 1, 1, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0} + +/* 1 for registers not available across function calls. + These must include the FIXED_REGISTERS and also any + registers that can be used without being saved. + On the i860, these are r0-r3, r16-r31, f0, f1, and f16-f31. */ +#define CALL_USED_REGISTERS \ + {1, 1, 1, 1, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 0, 0, 0, 0, 0, 0, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1, \ + 1, 1, 1, 1, 1, 1, 1, 1} + +/* Try to get a non-preserved register before trying to get one we will + have to preserve. Try to get an FP register only *after* trying to + get a general register, because it is relatively expensive to move + into or out of an FP register. */ + +#define REG_ALLOC_ORDER \ + {31, 30, 29, 28, 27, 26, 25, 24, \ + 23, 22, 21, 20, 19, 18, 17, 16, \ + 15, 14, 13, 12, 11, 10, 9, 8, \ + 7, 6, 5, 4, 3, 2, 1, 0, \ + 63, 62, 61, 60, 59, 58, 57, 56, \ + 55, 54, 53, 52, 51, 50, 49, 48, \ + 47, 46, 45, 44, 43, 42, 41, 40, \ + 39, 38, 37, 36, 35, 34, 33, 32} + +/* Return number of consecutive hard regs needed starting at reg REGNO + to hold something of mode MODE. + This is ordinarily the length in words of a value of mode MODE + but can be less for certain modes in special long registers. + + On the i860, all registers hold 32 bits worth. */ +#define HARD_REGNO_NREGS(REGNO, MODE) \ + (((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)) + +#define REGNO_MODE_ALIGNED(REGNO, MODE) \ + (((REGNO) % ((GET_MODE_UNIT_SIZE (MODE) + 3) / 4)) == 0) + +/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE. + + On the i860, we allow anything to go into any registers, but we require + any sort of value going into the FP registers to be properly aligned + (based on its size) within the FP register set. +*/ +#define HARD_REGNO_MODE_OK(REGNO, MODE) \ + (((REGNO) < 32) \ + || (MODE) == VOIDmode || (MODE) == BLKmode \ + || REGNO_MODE_ALIGNED (REGNO, MODE)) + +/* Value is 1 if it is a good idea to tie two pseudo registers + when one has mode MODE1 and one has mode MODE2. + If HARD_REGNO_MODE_OK could produce different values for MODE1 and MODE2, + for any hard reg, then this must be 0 for correct output. */ +/* I think that is not always true; alignment restrictions for doubles + should not prevent tying them with singles. So try allowing that. + On the other hand, don't let fixed and floating be tied; + this restriction is not necessary, but may make better code. */ +#define MODES_TIEABLE_P(MODE1, MODE2) \ + ((GET_MODE_CLASS (MODE1) == MODE_FLOAT \ + || GET_MODE_CLASS (MODE1) == MODE_COMPLEX_FLOAT) \ + == (GET_MODE_CLASS (MODE2) == MODE_FLOAT \ + || GET_MODE_CLASS (MODE2) == MODE_COMPLEX_FLOAT)) + +/* Specify the registers used for certain standard purposes. + The values of these macros are register numbers. */ + +/* i860 pc isn't overloaded on a register that the compiler knows about. */ +/* #define PC_REGNUM */ + +/* Register to use for pushing function arguments. */ +#define STACK_POINTER_REGNUM 2 + +/* Base register for access to local variables of the function. */ +#define FRAME_POINTER_REGNUM 3 + +/* Value should be nonzero if functions must have frame pointers. + Zero means the frame pointer need not be set up (and parms + may be accessed via the stack pointer) in functions that seem suitable. + This is computed in `reload', in reload1.c. */ +#define FRAME_POINTER_REQUIRED 1 + +/* Base register for access to arguments of the function. */ +#define ARG_POINTER_REGNUM 28 + +/* Register in which static-chain is passed to a function. */ +#define STATIC_CHAIN_REGNUM 29 + +/* Register in which address to store a structure value + is passed to a function. */ +#define STRUCT_VALUE_REGNUM 16 + +/* Register to use when a source of a floating-point zero is needed. */ +#define F0_REGNUM 32 + +/* Define the classes of registers for register constraints in the + machine description. Also define ranges of constants. + + One of the classes must always be named ALL_REGS and include all hard regs. + If there is more than one class, another class must be named NO_REGS + and contain no registers. + + The name GENERAL_REGS must be the name of a class (or an alias for + another name such as ALL_REGS). This is the class of registers + that is allowed by "g" or "r" in a register constraint. + Also, registers outside this class are allocated only when + instructions express preferences for them. + + The classes must be numbered in nondecreasing order; that is, + a larger-numbered class must never be contained completely + in a smaller-numbered class. + + For any two classes, it is very desirable that there be another + class that represents their union. */ + +/* The i860 has two kinds of registers, hence four classes. */ + +enum reg_class { NO_REGS, GENERAL_REGS, FP_REGS, ALL_REGS, LIM_REG_CLASSES }; + +#define N_REG_CLASSES (int) LIM_REG_CLASSES + +/* Give names of register classes as strings for dump file. */ + +#define REG_CLASS_NAMES \ + {"NO_REGS", "GENERAL_REGS", "FP_REGS", "ALL_REGS" } + +/* Define which registers fit in which classes. + This is an initializer for a vector of HARD_REG_SET + of length N_REG_CLASSES. */ + +#define REG_CLASS_CONTENTS \ + {{0, 0}, {0xffffffff, 0}, \ + {0, 0xffffffff}, {0xffffffff, 0xffffffff}} + +/* The same information, inverted: + Return the class number of the smallest class containing + reg number REGNO. This could be a conditional expression + or could index an array. */ + +#define REGNO_REG_CLASS(REGNO) \ + ((REGNO) >= 32 ? FP_REGS : GENERAL_REGS) + +/* The class value for index registers, and the one for base regs. */ +#define INDEX_REG_CLASS GENERAL_REGS +#define BASE_REG_CLASS GENERAL_REGS + +/* Get reg_class from a letter such as appears in the machine description. */ + +#define REG_CLASS_FROM_LETTER(C) \ + ((C) == 'f' ? FP_REGS : NO_REGS) + +/* The letters I, J, K, L and M in a register constraint string + can be used to stand for particular ranges of immediate operands. + This macro defines what the ranges are. + C is the letter, and VALUE is a constant value. + Return 1 if VALUE is in the range specified by C. + + For the i860, `I' is used for the range of constants + an add/subtract insn can actually contain. + But not including -0x8000, since we need + to negate the constant sometimes. + `J' is used for the range which is just zero (since that is R0). + `K' is used for the range allowed in bte. + `L' is used for the range allowed in logical insns. */ + +#define SMALL_INT(X) ((unsigned) (INTVAL (X) + 0x7fff) < 0xffff) + +#define LOGIC_INT(X) ((unsigned) INTVAL (X) < 0x10000) + +#define SMALL_INTVAL(X) ((unsigned) ((X) + 0x7fff) < 0xffff) + +#define LOGIC_INTVAL(X) ((unsigned) (X) < 0x10000) + +#define CONST_OK_FOR_LETTER_P(VALUE, C) \ + ((C) == 'I' ? ((unsigned) (VALUE) + 0x7fff) < 0xffff \ + : (C) == 'J' ? (VALUE) == 0 \ + : (C) == 'K' ? (unsigned) (VALUE) < 0x20 \ + : (C) == 'L' ? (unsigned) (VALUE) < 0x10000 \ + : 0) + +/* Return non-zero if the given VALUE is acceptable for the + constraint letter C. For the i860, constraint letter 'G' + permits only a floating-point zero value. */ +#define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C) \ + ((C) == 'G' && CONST_DOUBLE_LOW ((VALUE)) == 0 \ + && CONST_DOUBLE_HIGH ((VALUE)) == 0) + +/* Given an rtx X being reloaded into a reg required to be + in class CLASS, return the class of reg to actually use. + In general this is just CLASS; but on some machines + in some cases it is preferable to use a more restrictive class. + + If we are trying to put an integer constant into some register, prefer an + integer register to an FP register. If we are trying to put a + non-zero floating-point constant into some register, use an integer + register if the constant is SFmode and GENERAL_REGS is one of our options. + Otherwise, put the constant into memory. + + When reloading something smaller than a word, use a general reg + rather than an FP reg. */ + +#define PREFERRED_RELOAD_CLASS(X,CLASS) \ + ((CLASS) == ALL_REGS && GET_CODE (X) == CONST_INT ? GENERAL_REGS \ + : ((GET_MODE (X) == HImode || GET_MODE (X) == QImode) \ + && (CLASS) == ALL_REGS) \ + ? GENERAL_REGS \ + : (GET_CODE (X) == CONST_DOUBLE \ + && GET_MODE_CLASS (GET_MODE (X)) == MODE_FLOAT \ + && ! CONST_DOUBLE_OK_FOR_LETTER_P (X, 'G')) \ + ? ((CLASS) == ALL_REGS && GET_MODE (X) == SFmode ? GENERAL_REGS \ + : (CLASS) == GENERAL_REGS && GET_MODE (X) == SFmode ? (CLASS) \ + : NO_REGS) \ + : (CLASS)) + +/* Return the register class of a scratch register needed to copy IN into + a register in CLASS in MODE. If it can be done directly, NO_REGS is + returned. */ + +#define SECONDARY_INPUT_RELOAD_CLASS(CLASS,MODE,IN) \ + ((CLASS) == FP_REGS && CONSTANT_P (IN) ? GENERAL_REGS : NO_REGS) + +/* Return the maximum number of consecutive registers + needed to represent mode MODE in a register of class CLASS. */ +/* On the i860, this is the size of MODE in words. */ +#define CLASS_MAX_NREGS(CLASS, MODE) \ + ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) + +/* Stack layout; function entry, exit and calling. */ + +/* Define this if pushing a word on the stack + makes the stack pointer a smaller address. */ +#define STACK_GROWS_DOWNWARD + +/* Define this if the nominal address of the stack frame + is at the high-address end of the local variables; + that is, each additional local variable allocated + goes at a more negative offset in the frame. */ +#define FRAME_GROWS_DOWNWARD + +/* Offset within stack frame to start allocating local variables at. + If FRAME_GROWS_DOWNWARD, this is the offset to the END of the + first local allocated. Otherwise, it is the offset to the BEGINNING + of the first local allocated. */ +#define STARTING_FRAME_OFFSET 0 + +/* If we generate an insn to push BYTES bytes, + this says how many the stack pointer really advances by. + On the i860, don't define this because there are no push insns. */ +/* #define PUSH_ROUNDING(BYTES) */ + +/* Offset of first parameter from the argument pointer register value. */ +#define FIRST_PARM_OFFSET(FNDECL) 0 + +/* Value is the number of bytes of arguments automatically + popped when returning from a subroutine call. + FUNDECL is the declaration node of the function (as a tree), + FUNTYPE is the data type of the function (as a tree), + or for a library call it is an identifier node for the subroutine name. + SIZE is the number of bytes of arguments passed on the stack. */ + +#define RETURN_POPS_ARGS(FUNDECL,FUNTYPE,SIZE) 0 + +/* Define how to find the value returned by a function. + VALTYPE is the data type of the value (as a tree). + If the precise function being called is known, FUNC is its FUNCTION_DECL; + otherwise, FUNC is 0. */ + +/* On the i860, the value register depends on the mode. */ + +#define FUNCTION_VALUE(VALTYPE, FUNC) \ + gen_rtx (REG, TYPE_MODE (VALTYPE), \ + (GET_MODE_CLASS (TYPE_MODE (VALTYPE)) == MODE_FLOAT \ + ? 40 : 16)) + +/* Define how to find the value returned by a library function + assuming the value has mode MODE. */ + +#define LIBCALL_VALUE(MODE) \ + gen_rtx (REG, MODE, \ + (GET_MODE_CLASS ((MODE)) == MODE_FLOAT \ + ? 40 : 16)) + +/* 1 if N is a possible register number for a function value + as seen by the caller. */ + +#define FUNCTION_VALUE_REGNO_P(N) ((N) == 40 || (N) == 16) + +/* 1 if N is a possible register number for function argument passing. + On the i860, these are r16-r27 and f8-f15. */ + +#define FUNCTION_ARG_REGNO_P(N) \ + (((N) < 28 && (N) > 15) || ((N) < 48 && (N) >= 40)) + +/* Define a data type for recording info about an argument list + during the scan of that argument list. This data type should + hold all necessary information about the function itself + and about the args processed so far, enough to enable macros + such as FUNCTION_ARG to determine where the next arg should go. + + On the i860, we must count separately the number of general registers used + and the number of float registers used. */ + +struct cumulative_args { int ints, floats; }; +#define CUMULATIVE_ARGS struct cumulative_args + +/* Initialize a variable CUM of type CUMULATIVE_ARGS + for a call to a function whose data type is FNTYPE. + For a library call, FNTYPE is 0. + + On the i860, the general-reg offset normally starts at 0, + but starts at 4 bytes + when the function gets a structure-value-address as an + invisible first argument. */ + +#define INIT_CUMULATIVE_ARGS(CUM,FNTYPE,LIBNAME) \ + ((CUM).ints = ((FNTYPE) != 0 && aggregate_value_p (TREE_TYPE ((FNTYPE))) \ + ? 4 : 0), \ + (CUM).floats = 0) + +/* Machine-specific subroutines of the following macros. */ +#define CEILING(X,Y) (((X) + (Y) - 1) / (Y)) +#define ROUNDUP(X,Y) (CEILING ((X), (Y)) * (Y)) + +/* Update the data in CUM to advance over an argument + of mode MODE and data type TYPE. + (TYPE is null for libcalls where that information may not be available.) + Floats, and doubleword ints, are returned in f regs; + other ints, in r regs. + Aggregates, even short ones, are passed in memory. */ + +#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \ + ((TYPE) != 0 && (TREE_CODE ((TYPE)) == RECORD_TYPE \ + || TREE_CODE ((TYPE)) == UNION_TYPE) \ + ? 0 \ + : GET_MODE_CLASS ((MODE)) == MODE_FLOAT || (MODE) == DImode \ + ? ((CUM).floats = (ROUNDUP ((CUM).floats, GET_MODE_SIZE ((MODE))) \ + + ROUNDUP (GET_MODE_SIZE (MODE), 4))) \ + : GET_MODE_CLASS ((MODE)) == MODE_INT \ + ? ((CUM).ints = (ROUNDUP ((CUM).ints, GET_MODE_SIZE ((MODE))) \ + + ROUNDUP (GET_MODE_SIZE (MODE), 4))) \ + : 0) + +/* Determine where to put an argument to a function. + Value is zero to push the argument on the stack, + or a hard register in which to store the argument. + + MODE is the argument's machine mode. + TYPE is the data type of the argument (as a tree). + This is null for libcalls where that information may + not be available. + CUM is a variable of type CUMULATIVE_ARGS which gives info about + the preceding args and about the function being called. + NAMED is nonzero if this argument is a named parameter + (otherwise it is an extra parameter matching an ellipsis). */ + +/* On the i860, the first 12 words of integer arguments go in r16-r27, + and the first 8 words of floating arguments go in f8-f15. + DImode values are treated as floats. */ + +#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \ + ((TYPE) != 0 && (TREE_CODE ((TYPE)) == RECORD_TYPE \ + || TREE_CODE ((TYPE)) == UNION_TYPE) \ + ? 0 \ + : GET_MODE_CLASS ((MODE)) == MODE_FLOAT || (MODE) == DImode \ + ? (ROUNDUP ((CUM).floats, GET_MODE_SIZE ((MODE))) < 32 \ + ? gen_rtx (REG, (MODE), \ + 40+(ROUNDUP ((CUM).floats, \ + GET_MODE_SIZE ((MODE))) \ + / 4)) \ + : 0) \ + : GET_MODE_CLASS ((MODE)) == MODE_INT \ + ? (ROUNDUP ((CUM).ints, GET_MODE_SIZE ((MODE))) < 48 \ + ? gen_rtx (REG, (MODE), \ + 16+(ROUNDUP ((CUM).ints, \ + GET_MODE_SIZE ((MODE))) \ + / 4)) \ + : 0) \ + : 0) + +/* For an arg passed partly in registers and partly in memory, + this is the number of registers used. + For args passed entirely in registers or entirely in memory, zero. */ + +#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) 0 + +/* If defined, a C expression that gives the alignment boundary, in + bits, of an argument with the specified mode and type. If it is + not defined, `PARM_BOUNDARY' is used for all arguments. */ + +#define FUNCTION_ARG_BOUNDARY(MODE, TYPE) \ + (((TYPE) != 0) \ + ? ((TYPE_ALIGN(TYPE) <= PARM_BOUNDARY) \ + ? PARM_BOUNDARY \ + : TYPE_ALIGN(TYPE)) \ + : ((GET_MODE_ALIGNMENT(MODE) <= PARM_BOUNDARY) \ + ? PARM_BOUNDARY \ + : GET_MODE_ALIGNMENT(MODE))) + +/* This macro generates the assembly code for function entry. + + FILE is a stdio stream to output the code to. + SIZE is an int: how many units of temporary storage to allocate. +*/ + +#define FUNCTION_PROLOGUE(FILE, SIZE) function_prologue ((FILE), (SIZE)) + +/* Output a no-op just before the beginning of the function, + to ensure that there does not appear to be a delayed branch there. + Such a thing would confuse interrupt recovery. */ +#define ASM_OUTPUT_FUNCTION_PREFIX(FILE,NAME) \ + fprintf (FILE, "\tnop\n") + +/* Output assembler code to FILE to increment profiler label # LABELNO + for profiling a function entry. */ + +#define FUNCTION_PROFILER(FILE, LABELNO) \ + abort (); + +/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function, + the stack pointer does not matter. The value is tested only in + functions that have frame pointers. + No definition is equivalent to always zero. */ + +#define EXIT_IGNORE_STACK 1 + +/* This macro generates the assembly code for function exit. + + FILE is a stdio stream to output the code to. + SIZE is an int: how many units of temporary storage to allocate. + + The function epilogue should not depend on the current stack pointer! + It should use the frame pointer only. This is mandatory because + of alloca; we also take advantage of it to omit stack adjustments + before returning. +*/ + +#define FUNCTION_EPILOGUE(FILE, SIZE) function_epilogue ((FILE), (SIZE)) + +/* Store in the variable DEPTH the initial difference between the + frame pointer reg contents and the stack pointer reg contents, + as of the start of the function body. This depends on the layout + of the fixed parts of the stack frame and on how registers are saved. + + On the i860, FRAME_POINTER_REQUIRED is always 1, so the definition of this + macro doesn't matter. But it must be defined. */ + +#define INITIAL_FRAME_POINTER_OFFSET(DEPTH) \ + do { (DEPTH) = 0; } while (0) + +/* Output assembler code for a block containing the constant parts + of a trampoline, leaving space for the variable parts. */ + +/* On the i860, the trampoline contains five instructions: + orh #TOP_OF_FUNCTION,r0,r31 + or #BOTTOM_OF_FUNCTION,r31,r31 + orh #TOP_OF_STATIC,r0,r29 + bri r31 + or #BOTTOM_OF_STATIC,r29,r29 */ +#define TRAMPOLINE_TEMPLATE(FILE) \ +{ \ + ASM_OUTPUT_INT (FILE, gen_rtx (CONST_INT, VOIDmode, 0xec1f0000)); \ + ASM_OUTPUT_INT (FILE, gen_rtx (CONST_INT, VOIDmode, 0xe7ff0000)); \ + ASM_OUTPUT_INT (FILE, gen_rtx (CONST_INT, VOIDmode, 0xec1d0000)); \ + ASM_OUTPUT_INT (FILE, gen_rtx (CONST_INT, VOIDmode, 0x4000f800)); \ + ASM_OUTPUT_INT (FILE, gen_rtx (CONST_INT, VOIDmode, 0xe7bd0000)); \ +} + +/* Length in units of the trampoline for entering a nested function. */ + +#define TRAMPOLINE_SIZE 20 + +/* Emit RTL insns to initialize the variable parts of a trampoline. + FNADDR is an RTX for the address of the function's pure code. + CXT is an RTX for the static chain value for the function. + + Store hi function at +0, low function at +4, + hi static at +8, low static at +16 */ + +#define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT) \ +{ \ + rtx cxt = force_reg (Pmode, CXT); \ + rtx fn = force_reg (Pmode, FNADDR); \ + rtx hi_cxt = expand_shift (RSHIFT_EXPR, SImode, cxt, \ + size_int (16), 0, 0); \ + rtx hi_fn = expand_shift (RSHIFT_EXPR, SImode, fn, \ + size_int (16), 0, 0); \ + emit_move_insn (gen_rtx (MEM, HImode, plus_constant (TRAMP, 16)), \ + gen_lowpart (HImode, cxt)); \ + emit_move_insn (gen_rtx (MEM, HImode, plus_constant (TRAMP, 4)), \ + gen_lowpart (HImode, fn)); \ + emit_move_insn (gen_rtx (MEM, HImode, plus_constant (TRAMP, 8)), \ + gen_lowpart (HImode, hi_cxt)); \ + emit_move_insn (gen_rtx (MEM, HImode, plus_constant (TRAMP, 0)), \ + gen_lowpart (HImode, hi_fn)); \ +} + +/* Addressing modes, and classification of registers for them. */ + +/* #define HAVE_POST_INCREMENT */ +/* #define HAVE_POST_DECREMENT */ + +/* #define HAVE_PRE_DECREMENT */ +/* #define HAVE_PRE_INCREMENT */ + +/* Macros to check register numbers against specific register classes. */ + +/* These assume that REGNO is a hard or pseudo reg number. + They give nonzero only if REGNO is a hard reg of the suitable class + or a pseudo reg currently allocated to a suitable hard reg. + Since they use reg_renumber, they are safe only once reg_renumber + has been allocated, which happens in local-alloc.c. */ + +#define REGNO_OK_FOR_INDEX_P(REGNO) \ +((REGNO) < 32 || (unsigned) reg_renumber[REGNO] < 32) +#define REGNO_OK_FOR_BASE_P(REGNO) \ +((REGNO) < 32 || (unsigned) reg_renumber[REGNO] < 32) +#define REGNO_OK_FOR_FP_P(REGNO) \ +(((REGNO) ^ 0x20) < 32 || (unsigned) (reg_renumber[REGNO] ^ 0x20) < 32) + +/* Now macros that check whether X is a register and also, + strictly, whether it is in a specified class. + + These macros are specific to the i860, and may be used only + in code for printing assembler insns and in conditions for + define_optimization. */ + +/* 1 if X is an fp register. */ + +#define FP_REG_P(X) (REG_P (X) && REGNO_OK_FOR_FP_P (REGNO (X))) + +/* Maximum number of registers that can appear in a valid memory address. */ + +#define MAX_REGS_PER_ADDRESS 2 + +/* Recognize any constant value that is a valid address. */ + +#define CONSTANT_ADDRESS_P(X) \ + (GET_CODE (X) == LABEL_REF || GET_CODE (X) == SYMBOL_REF \ + || GET_CODE (X) == CONST_INT || GET_CODE (X) == CONST \ + || GET_CODE (X) == HIGH) + +/* Nonzero if the constant value X is a legitimate general operand. + It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE. + + On the Sparc, this is anything but a CONST_DOUBLE. + Let's try permitting CONST_DOUBLEs and see what happens. */ + +#define LEGITIMATE_CONSTANT_P(X) 1 + +/* The macros REG_OK_FOR..._P assume that the arg is a REG rtx + and check its validity for a certain class. + We have two alternate definitions for each of them. + The usual definition accepts all pseudo regs; the other rejects + them unless they have been allocated suitable hard regs. + The symbol REG_OK_STRICT causes the latter definition to be used. + + Most source files want to accept pseudo regs in the hope that + they will get allocated to the class that the insn wants them to be in. + Source files for reload pass need to be strict. + After reload, it makes no difference, since pseudo regs have + been eliminated by then. */ + +#ifndef REG_OK_STRICT + +/* Nonzero if X is a hard reg that can be used as an index + or if it is a pseudo reg. */ +#define REG_OK_FOR_INDEX_P(X) (((unsigned) REGNO (X)) - 32 >= 14) +/* Nonzero if X is a hard reg that can be used as a base reg + or if it is a pseudo reg. */ +#define REG_OK_FOR_BASE_P(X) (((unsigned) REGNO (X)) - 32 >= 14) + +#else + +/* Nonzero if X is a hard reg that can be used as an index. */ +#define REG_OK_FOR_INDEX_P(X) REGNO_OK_FOR_INDEX_P (REGNO (X)) +/* Nonzero if X is a hard reg that can be used as a base reg. */ +#define REG_OK_FOR_BASE_P(X) REGNO_OK_FOR_BASE_P (REGNO (X)) + +#endif + +/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression + that is a valid memory address for an instruction. + The MODE argument is the machine mode for the MEM expression + that wants to use this address. + + On the i860, the actual addresses must be REG+REG or REG+SMALLINT. + But we can treat a SYMBOL_REF as legitimate if it is part of this + function's constant-pool, because such addresses can actually + be output as REG+SMALLINT. + + The displacement in an address must be a multiple of the alignment. + + Try making SYMBOL_REF (and other things which are CONSTANT_ADDRESS_P) + a legitimate address, regardless. Because the only insns which can use + memory are load or store insns, the added hair in the machine description + is not that bad. It should also speed up the compiler by halving the number + of insns it must manage for each (MEM (SYMBOL_REF ...)) involved. */ + +#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \ +{ if (GET_CODE (X) == REG) \ + { if (REG_OK_FOR_BASE_P (X)) goto ADDR; } \ + else if (GET_CODE (X) == PLUS) \ + { \ + if (GET_CODE (XEXP (X, 0)) == REG \ + && REG_OK_FOR_BASE_P (XEXP (X, 0))) \ + { \ + if (GET_CODE (XEXP (X, 1)) == CONST_INT \ + && INTVAL (XEXP (X, 1)) >= -0x8000 \ + && INTVAL (XEXP (X, 1)) < 0x8000 \ + && (INTVAL (XEXP (X, 1)) & (GET_MODE_SIZE (MODE) - 1)) == 0) \ + goto ADDR; \ + } \ + else if (GET_CODE (XEXP (X, 1)) == REG \ + && REG_OK_FOR_BASE_P (XEXP (X, 1))) \ + { \ + if (GET_CODE (XEXP (X, 0)) == CONST_INT \ + && INTVAL (XEXP (X, 0)) >= -0x8000 \ + && INTVAL (XEXP (X, 0)) < 0x8000 \ + && (INTVAL (XEXP (X, 0)) & (GET_MODE_SIZE (MODE) - 1)) == 0) \ + goto ADDR; \ + } \ + } \ + else if (CONSTANT_ADDRESS_P (X)) \ + goto ADDR; \ +} + +/* Try machine-dependent ways of modifying an illegitimate address + to be legitimate. If we find one, return the new, valid address. + This macro is used in only one place: `memory_address' in explow.c. + + OLDX is the address as it was before break_out_memory_refs was called. + In some cases it is useful to look at this to decide what needs to be done. + + MODE and WIN are passed so that this macro can use + GO_IF_LEGITIMATE_ADDRESS. + + It is always safe for this macro to do nothing. It exists to recognize + opportunities to optimize the output. */ + +/* On the i860, change COMPLICATED + CONSTANT to REG+CONSTANT. + Also change a symbolic constant to a REG, + though that may not be necessary. */ + +#define LEGITIMIZE_ADDRESS(X,OLDX,MODE,WIN) \ +{ if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 0)) == MULT) \ + (X) = gen_rtx (PLUS, SImode, XEXP (X, 1), \ + force_operand (XEXP (X, 0), 0)); \ + if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == MULT) \ + (X) = gen_rtx (PLUS, SImode, XEXP (X, 0), \ + force_operand (XEXP (X, 1), 0)); \ + if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 0)) == PLUS) \ + (X) = gen_rtx (PLUS, SImode, XEXP (X, 1), \ + force_operand (XEXP (X, 0), 0)); \ + if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == PLUS) \ + (X) = gen_rtx (PLUS, SImode, XEXP (X, 0), \ + force_operand (XEXP (X, 1), 0)); \ + if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 0)) != REG \ + && GET_CODE (XEXP (X, 0)) != CONST_INT) \ + (X) = gen_rtx (PLUS, SImode, XEXP (X, 1), \ + copy_to_mode_reg (SImode, XEXP (X, 0))); \ + if (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) != REG \ + && GET_CODE (XEXP (X, 1)) != CONST_INT) \ + (X) = gen_rtx (PLUS, SImode, XEXP (X, 0), \ + copy_to_mode_reg (SImode, XEXP (X, 1))); \ + if (GET_CODE (x) == SYMBOL_REF) \ + (X) = copy_to_reg (X); \ + if (GET_CODE (x) == CONST) \ + (X) = copy_to_reg (X); \ + if (memory_address_p (MODE, X)) \ + goto WIN; } + +/* Go to LABEL if ADDR (a legitimate address expression) + has an effect that depends on the machine mode it is used for. + On the i860 this is never true. + There are some addresses that are invalid in wide modes + but valid for narrower modes, but they shouldn't affect + the places that use this macro. */ + +#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR,LABEL) + +/* Specify the machine mode that this machine uses + for the index in the tablejump instruction. */ +#define CASE_VECTOR_MODE SImode + +/* Define this if the tablejump instruction expects the table + to contain offsets from the address of the table. + Do not define this if the table should contain absolute addresses. */ +/* #define CASE_VECTOR_PC_RELATIVE */ + +/* Specify the tree operation to be used to convert reals to integers. */ +#define IMPLICIT_FIX_EXPR FIX_ROUND_EXPR + +/* This is the kind of divide that is easiest to do in the general case. */ +#define EASY_DIV_EXPR TRUNC_DIV_EXPR + +/* Must pass floats to libgcc functions as doubles. */ +#define LIBGCC_NEEDS_DOUBLE 1 + +#define DIVSI3_LIBCALL "*.div" +#define UDIVSI3_LIBCALL "*.udiv" +#define REMSI3_LIBCALL "*.rem" +#define UREMSI3_LIBCALL "*.urem" + +/* Define this as 1 if `char' should by default be signed; else as 0. */ +#define DEFAULT_SIGNED_CHAR 1 + +/* Max number of bytes we can move from memory to memory + in one reasonably fast instruction. */ +#define MOVE_MAX 16 + +/* Nonzero if access to memory by bytes is slow and undesirable. */ +#define SLOW_BYTE_ACCESS 0 + +/* Value is 1 if truncating an integer of INPREC bits to OUTPREC bits + is done just by pretending it is already truncated. */ +#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1 + +/* Value is 1 if it generates better code to perform an unsigned comparison + on the given literal integer value in the given mode when we are only + looking for an equal/non-equal result. */ +/* For the i860, if the immediate value has its high-order 27 bits zero, + then we want to engineer an unsigned comparison for EQ/NE because + such values can fit in the 5-bit immediate field of a bte or btne + instruction (which gets zero extended before comparing). For all + other immediate values on the i860, we will use signed compares + because that avoids the need for doing explicit xor's to zero_extend + the non-constant operand in cases where it was (mem:QI ...) or a + (mem:HI ...) which always gets automatically sign-extended by the + hardware upon loading. */ + +#define LITERAL_COMPARE_BETTER_UNSIGNED(intval, mode) \ + (((unsigned) (intval) & 0x1f) == (unsigned) (intval)) + +/* Specify the machine mode that pointers have. + After generation of rtl, the compiler makes no further distinction + between pointers and any other objects of this machine mode. */ +#define Pmode SImode + +/* A function address in a call instruction + is a byte address (for indexing purposes) + so give the MEM rtx a byte's mode. */ +#define FUNCTION_MODE SImode + +/* Define this if addresses of constant functions + shouldn't be put through pseudo regs where they can be cse'd. + Desirable on machines where ordinary constants are expensive + but a CALL with constant address is cheap. */ +#define NO_FUNCTION_CSE + +/* Compute the cost of computing a constant rtl expression RTX + whose rtx-code is CODE. The body of this macro is a portion + of a switch statement. If the code is computed here, + return it with a return statement. Otherwise, break from the switch. */ + +#define CONST_COSTS(RTX,CODE, OUTER_CODE) \ + case CONST_INT: \ + if (INTVAL (RTX) == 0) \ + return 0; \ + if (INTVAL (RTX) < 0x2000 && INTVAL (RTX) >= -0x2000) return 1; \ + case CONST: \ + case LABEL_REF: \ + case SYMBOL_REF: \ + return 4; \ + case CONST_DOUBLE: \ + return 6; + +/* Specify the cost of a branch insn; roughly the number of extra insns that + should be added to avoid a branch. + + Set this to 3 on the i860 since branches may often take three cycles. */ + +#define BRANCH_COST 3 + +/* Tell final.c how to eliminate redundant test instructions. */ + +/* Here we define machine-dependent flags and fields in cc_status + (see `conditions.h'). */ + +/* This holds the value sourcing h%r31. We keep this info + around so that mem/mem ops, such as increment and decrement, + etc, can be performed reasonably. */ +#define CC_STATUS_MDEP rtx + +#define CC_STATUS_MDEP_INIT (cc_status.mdep = 0) + +#define CC_NEGATED 01000 + +/* We use this macro in those places in the i860.md file where we would + normally just do a CC_STATUS_INIT (for other machines). This macro + differs from CC_STATUS_INIT in that it doesn't mess with the special + bits or fields which describe what is currently in the special r31 + scratch register, but it does clear out everything that actually + relates to the condition code bit of the i860. */ + +#define CC_STATUS_PARTIAL_INIT \ + (cc_status.flags &= (CC_KNOW_HI_R31 | CC_HI_R31_ADJ), \ + cc_status.value1 = 0, \ + cc_status.value2 = 0) + +/* Nonzero if we know the value of h%r31. */ +#define CC_KNOW_HI_R31 0100000 + +/* Nonzero if h%r31 is actually ha%something, rather than h%something. */ +#define CC_HI_R31_ADJ 0200000 + +/* Store in cc_status the expressions + that the condition codes will describe + after execution of an instruction whose pattern is EXP. + Do not alter them if the instruction would not alter the cc's. */ + +/* On the i860, only compare insns set a useful condition code. */ + +#define NOTICE_UPDATE_CC(EXP, INSN) \ +{ cc_status.flags &= (CC_KNOW_HI_R31 | CC_HI_R31_ADJ); \ + cc_status.value1 = 0; cc_status.value2 = 0; } + +/* Control the assembler format that we output. */ + +/* Assembler pseudos to introduce constants of various size. */ + +#define ASM_BYTE_OP "\t.byte" +#define ASM_SHORT "\t.short" +#define ASM_LONG "\t.long" +#define ASM_DOUBLE "\t.double" + +/* Output at beginning of assembler file. */ +/* The .file command should always begin the output. */ + +#define ASM_FILE_START(FILE) +#if 0 +#define ASM_FILE_START(FILE) \ + do { output_file_directive ((FILE), main_input_filename); \ + if (optimize) ASM_FILE_START_1 (FILE); \ + } while (0) +#endif + +#define ASM_FILE_START_1(FILE) + +/* Output to assembler file text saying following lines + may contain character constants, extra white space, comments, etc. */ + +#define ASM_APP_ON "" + +/* Output to assembler file text saying following lines + no longer contain unusual constructs. */ + +#define ASM_APP_OFF "" + +/* Output before read-only data. */ + +#define TEXT_SECTION_ASM_OP ".text" + +/* Output before writable data. */ + +#define DATA_SECTION_ASM_OP ".data" + +/* How to refer to registers in assembler output. + This sequence is indexed by compiler's hard-register-number (see above). */ + +#define REGISTER_NAMES \ +{"r0", "r1", "sp", "fp", "r4", "r5", "r6", "r7", "r8", "r9", \ + "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", \ + "r20", "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ + "r30", "r31", \ + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", \ + "f10", "f11", "f12", "f13", "f14", "f15", "f16", "f17", "f18", "f19", \ + "f20", "f21", "f22", "f23", "f24", "f25", "f26", "f27", "f28", "f29", \ + "f30", "f31" } + +/* How to renumber registers for dbx and gdb. */ + +#define DBX_REGISTER_NUMBER(REGNO) (REGNO) + +/* This is how to output the definition of a user-level label named NAME, + such as the label on a static function or variable NAME. */ + +#define ASM_OUTPUT_LABEL(FILE,NAME) \ + do { assemble_name (FILE, NAME); fputs (":\n", FILE); } while (0) + +/* This is how to output a command to make the user-level label named NAME + defined for reference from other files. */ + +#define ASM_GLOBALIZE_LABEL(FILE,NAME) \ + do { fputs (".globl ", FILE); \ + assemble_name (FILE, NAME); \ + fputs ("\n", FILE); \ + } while (0) + +/* This is how to output a reference to a user-level label named NAME. + `assemble_name' uses this. + + This definition is overridden in i860v4.h because under System V + Release 4, user-level symbols are *not* prefixed with underscores in + the generated assembly code. */ + +#define ASM_OUTPUT_LABELREF(FILE,NAME) \ + fprintf (FILE, "_%s", NAME) + +/* This is how to output an internal numbered label where + PREFIX is the class of label and NUM is the number within the class. */ + +#define ASM_OUTPUT_INTERNAL_LABEL(FILE,PREFIX,NUM) \ + fprintf (FILE, ".%s%d:\n", PREFIX, NUM) + +/* This is how to output an internal numbered label which + labels a jump table. */ + +#undef ASM_OUTPUT_CASE_LABEL +#define ASM_OUTPUT_CASE_LABEL(FILE, PREFIX, NUM, JUMPTABLE) \ +do { ASM_OUTPUT_ALIGN ((FILE), 2); \ + ASM_OUTPUT_INTERNAL_LABEL ((FILE), PREFIX, NUM); \ + } while (0) + +/* Output at the end of a jump table. */ + +#define ASM_OUTPUT_CASE_END(FILE,NUM,INSN) \ + fprintf (FILE, ".text\n") + +/* This is how to store into the string LABEL + the symbol_ref name of an internal numbered label where + PREFIX is the class of label and NUM is the number within the class. + This is suitable for output with `assemble_name'. */ + +#define ASM_GENERATE_INTERNAL_LABEL(LABEL,PREFIX,NUM) \ + sprintf (LABEL, "*.%s%d", PREFIX, NUM) + +/* This is how to output an assembler line defining a `double' constant. */ + +#define ASM_OUTPUT_DOUBLE(FILE,VALUE) \ + fprintf (FILE, "\t.double %.20e\n", (VALUE)) + +/* This is how to output an assembler line defining a `float' constant. */ + +#define ASM_OUTPUT_FLOAT(FILE,VALUE) \ + fprintf (FILE, "\t.float %.12e\n", (VALUE)) + +/* This is how to output an assembler line defining an `int' constant. */ + +#define ASM_OUTPUT_INT(FILE,VALUE) \ +( fprintf (FILE, "\t.long "), \ + output_addr_const (FILE, (VALUE)), \ + fprintf (FILE, "\n")) + +/* Likewise for `char' and `short' constants. */ + +#define ASM_OUTPUT_SHORT(FILE,VALUE) \ +( fprintf (FILE, "\t.short "), \ + output_addr_const (FILE, (VALUE)), \ + fprintf (FILE, "\n")) + +#define ASM_OUTPUT_CHAR(FILE,VALUE) \ +( fprintf (FILE, "\t.byte "), \ + output_addr_const (FILE, (VALUE)), \ + fprintf (FILE, "\n")) + +/* This is how to output an assembler line for a numeric constant byte. */ + +#define ASM_OUTPUT_BYTE(FILE,VALUE) \ + fprintf (FILE, "\t.byte 0x%x\n", (VALUE)) + +/* This is how to output code to push a register on the stack. + It need not be very fast code. */ + +#define ASM_OUTPUT_REG_PUSH(FILE,REGNO) \ + fprintf (FILE, "\taddu -16,%ssp,%ssp\n\t%sst.l %s%s,0(%ssp)\n", \ + i860_reg_prefix, i860_reg_prefix, \ + ((REGNO) < 32 ? "" : "f"), \ + i860_reg_prefix, reg_names[REGNO], \ + i860_reg_prefix) + +/* This is how to output an insn to pop a register from the stack. + It need not be very fast code. */ + +#define ASM_OUTPUT_REG_POP(FILE,REGNO) \ + fprintf (FILE, "\t%sld.l 0(%ssp),%s%s\n\taddu 16,%ssp,%ssp\n", \ + ((REGNO) < 32 ? "" : "f"), \ + i860_reg_prefix, \ + i860_reg_prefix, reg_names[REGNO], \ + i860_reg_prefix, i860_reg_prefix) + +/* This is how to output an element of a case-vector that is absolute. */ + +#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \ + fprintf (FILE, "\t.long .L%d\n", VALUE) + +/* This is how to output an element of a case-vector that is relative. + (The i860 does not use such vectors, + but we must define this macro anyway.) */ + +#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, VALUE, REL) \ + fprintf (FILE, "\t.word .L%d-.L%d\n", VALUE, REL) + +/* This is how to output an assembler line + that says to advance the location counter + to a multiple of 2**LOG bytes. */ + +#define ASM_OUTPUT_ALIGN(FILE,LOG) \ + if ((LOG) != 0) \ + fprintf (FILE, "\t.align %d\n", 1 << (LOG)) + +#define ASM_OUTPUT_SKIP(FILE,SIZE) \ + fprintf (FILE, "\t.blkb %u\n", (SIZE)) + +/* This says how to output an assembler line + to define a global common symbol. */ + +#define ASM_OUTPUT_COMMON(FILE, NAME, SIZE, ROUNDED) \ +( fputs (".comm ", (FILE)), \ + assemble_name ((FILE), (NAME)), \ + fprintf ((FILE), ",%u\n", (ROUNDED))) + +/* This says how to output an assembler line + to define a local common symbol. */ + +#define ASM_OUTPUT_LOCAL(FILE, NAME, SIZE, ROUNDED) \ +( fputs (".lcomm ", (FILE)), \ + assemble_name ((FILE), (NAME)), \ + fprintf ((FILE), ",%u\n", (ROUNDED))) + +/* Store in OUTPUT a string (made with alloca) containing + an assembler-name for a local static variable named NAME. + LABELNO is an integer which is different for each call. */ + +#define ASM_FORMAT_PRIVATE_NAME(OUTPUT, NAME, LABELNO) \ +( (OUTPUT) = (char *) alloca (strlen ((NAME)) + 10), \ + sprintf ((OUTPUT), "%s.%d", (NAME), (LABELNO))) + +/* Define the parentheses used to group arithmetic operations + in assembler code. */ + +#define ASM_OPEN_PAREN "(" +#define ASM_CLOSE_PAREN ")" + +/* Define results of standard character escape sequences. */ +#define TARGET_BELL 007 +#define TARGET_BS 010 +#define TARGET_TAB 011 +#define TARGET_NEWLINE 012 +#define TARGET_VT 013 +#define TARGET_FF 014 +#define TARGET_CR 015 + +/* Print operand X (an rtx) in assembler syntax to file FILE. + CODE is a letter or dot (`z' in `%z0') or 0 if no letter was specified. + For `%' followed by punctuation, CODE is the punctuation and X is null. + + In the following comments, the term "constant address" is used frequently. + For an exact definition of what constitutes a "constant address" see the + output_addr_const routine in final.c + + On the i860, the following target-specific special codes are recognized: + + `r' The operand can be anything, but if is is an immediate zero + value (either integer or floating point) then it will be + represented as `r0' or as `f0' (respectively). + + `m' The operand is a memory ref (to a constant address) but print + its address as a constant. + + `L' The operand is a numeric constant, a constant address, or + a memory ref to a constant address. Print the correct + notation to yield the low part of the given value or + address or the low part of the address of the referred + to memory object. + + `H' The operand is a numeric constant, a constant address, or + a memory ref to a constant address. Print the correct + notation to yield the high part of the given value or + address or the high part of the address of the referred + to memory object. + + `h' The operand is a numeric constant, a constant address, or + a memory ref to a constant address. Either print the + correct notation to yield the plain high part of the + given value or address (or the plain high part of the + address of the memory object) or else print the correct + notation to yield the "adjusted" high part of the given + address (or of the address of the referred to memory object). + + The choice of what to print depends upon whether the address + in question is relocatable or not. If it is relocatable, + print the notation to get the adjusted high part. Otherwise + just print the notation to get the plain high part. Note + that "adjusted" high parts are generally used *only* when + the next following instruction uses the low part of the + address as an offset, as in `offset(reg)'. + + `R' The operand is a floating-pointer register. Print the + name of the next following (32-bit) floating-point register. + (This is used when moving a value into just the most + significant part of a floating-point register pair.) + + `?' (takes no operand) Substitute the value of i860_reg_prefix + at this point. The value of i860_reg_prefix is typically + a null string for most i860 targets, but for System V + Release 4 the i860 assembler syntax requires that all + names of registers be prefixed with a percent-sign, so + for SVR4, the value of i860_reg_prefix is initialized to + "%" in i860.c. +*/ + +extern char *i860_reg_prefix; +extern unsigned long sfmode_constant_to_ulong (); + +#define PRINT_OPERAND_PUNCT_VALID_P(CODE) ((CODE) == '?') + +/* The following macro definition is overridden in i860v4.h + because the svr4 i860 assembler required a different syntax + for getting parts of constant/relocatable values. */ + +#define PRINT_OPERAND_PART(FILE, X, PART_CODE) \ + do { fprintf (FILE, "%s%%", PART_CODE); \ + output_address (X); \ + } while (0) + +#define OPERAND_LOW_PART "l" +#define OPERAND_HIGH_PART "h" +/* NOTE: All documentation available for the i860 sez that you must + use "ha" to get the relocated high part of a relocatable, but + reality sez different. */ +#define OPERAND_HIGH_ADJ_PART "ha" + +#define PRINT_OPERAND(FILE, X, CODE) \ +{ if ((CODE) == '?') \ + fprintf (FILE, "%s", i860_reg_prefix); \ + else if (CODE == 'R') \ + fprintf (FILE, "%s%s", i860_reg_prefix, reg_names[REGNO (X) + 1]); \ + else if (GET_CODE (X) == REG) \ + fprintf (FILE, "%s%s", i860_reg_prefix, reg_names[REGNO (X)]); \ + else if ((CODE) == 'm') \ + output_address (XEXP (X, 0)); \ + else if ((CODE) == 'L') \ + { \ + if (GET_CODE (X) == MEM) \ + PRINT_OPERAND_PART (FILE, XEXP (X, 0), OPERAND_LOW_PART); \ + else \ + PRINT_OPERAND_PART (FILE, X, OPERAND_LOW_PART); \ + } \ + else if ((CODE) == 'H') \ + { \ + if (GET_CODE (X) == MEM) \ + PRINT_OPERAND_PART (FILE, XEXP (X, 0), OPERAND_HIGH_PART); \ + else \ + PRINT_OPERAND_PART (FILE, X, OPERAND_HIGH_PART); \ + } \ + else if ((CODE) == 'h') \ + { \ + if (GET_CODE (X) == MEM) \ + PRINT_OPERAND_PART (FILE, XEXP (X, 0), OPERAND_HIGH_ADJ_PART); \ + else \ + PRINT_OPERAND_PART (FILE, X, OPERAND_HIGH_ADJ_PART); \ + } \ + else if (GET_CODE (X) == MEM) \ + output_address (XEXP (X, 0)); \ + else if ((CODE) == 'r' && (X) == const0_rtx) \ + fprintf (FILE, "%sr0", i860_reg_prefix); \ + else if ((CODE) == 'r' && (X) == CONST0_RTX (GET_MODE (X))) \ + fprintf (FILE, "%sf0", i860_reg_prefix); \ + else if (GET_CODE (X) == CONST_DOUBLE) \ + fprintf (FILE, "0x%x", sfmode_constant_to_ulong (X)); \ + else \ + output_addr_const (FILE, X); } + +/* Print a memory address as an operand to reference that memory location. */ + +#define PRINT_OPERAND_ADDRESS(FILE, ADDR) \ +{ register rtx base, index = 0; \ + int offset = 0; \ + register rtx addr = ADDR; \ + if (GET_CODE (addr) == REG) \ + { \ + fprintf (FILE, "0(%s%s)", \ + i860_reg_prefix, reg_names[REGNO (addr)]); \ + } \ + else if (GET_CODE (addr) == CONST_DOUBLE \ + && GET_MODE (addr) == SFmode) \ + fprintf (FILE, "0x%x", sfmode_constant_to_ulong (addr)); \ + else if (GET_CODE (addr) == PLUS) \ + { \ + if ((GET_CODE (XEXP (addr, 0)) == CONST_INT) \ + && (GET_CODE (XEXP (addr, 1)) == REG)) \ + fprintf (FILE, "%d(%s%s)", INTVAL (XEXP (addr, 0)), \ + i860_reg_prefix, reg_names[REGNO (XEXP (addr, 1))]);\ + else if ((GET_CODE (XEXP (addr, 1)) == CONST_INT) \ + && (GET_CODE (XEXP (addr, 0)) == REG)) \ + fprintf (FILE, "%d(%s%s)", INTVAL (XEXP (addr, 1)), \ + i860_reg_prefix, reg_names[REGNO (XEXP (addr, 0))]);\ + else if ((GET_CODE (XEXP (addr, 0)) == REG) \ + && (GET_CODE (XEXP (addr, 1)) == REG)) \ + fprintf (FILE, "%s%s(%s%s)", \ + i860_reg_prefix, reg_names[REGNO (XEXP (addr, 0))], \ + i860_reg_prefix, reg_names[REGNO (XEXP (addr, 1))]);\ + else \ + output_addr_const (FILE, addr); \ + } \ + else \ + { \ + output_addr_const (FILE, addr); \ + } \ +} + +/* The following #defines are used when compiling the routines in + libgcc1.c. Since the i860 calling conventions require single + precision floats to be passed in the floating-point registers + (rather than in the general registers) we have to build the + libgcc1.c routines in such a way that they know the actual types + of their formal arguments and the actual types of their return + values. Otherwise, gcc will generate calls to the libgcc1.c + routines, passing arguments in the floating-point registers, + but the libgcc1.c routines will expect their arguments on the + stack (where the i860 calling conventions require structs & + unions to be passed). */ + +#define FLOAT_TYPE_VALUE float +#define INTIFY(FLOATVAL) (FLOATVAL) +#define FLOATIFY(INTVAL) (INTVAL) +#define FLOAT_ARG_TYPE float + + +/* Optionally define this if you have added predicates to + `MACHINE.c'. This macro is called within an initializer of an + array of structures. The first field in the structure is the + name of a predicate and the second field is an array of rtl + codes. For each predicate, list all rtl codes that can be in + expressions matched by the predicate. The list should have a + trailing comma. Here is an example of two entries in the list + for a typical RISC machine: + + #define PREDICATE_CODES \ + {"gen_reg_rtx_operand", {SUBREG, REG}}, \ + {"reg_or_short_cint_operand", {SUBREG, REG, CONST_INT}}, + + Defining this macro does not affect the generated code (however, + incorrect definitions that omit an rtl code that may be matched + by the predicate can cause the compiler to malfunction). + Instead, it allows the table built by `genrecog' to be more + compact and efficient, thus speeding up the compiler. The most + important predicates to include in the list specified by this + macro are thoses used in the most insn patterns. */ + +#define PREDICATE_CODES \ + {"reg_or_0_operand", {REG, SUBREG, CONST_INT}}, \ + {"arith_operand", {REG, SUBREG, CONST_INT}}, \ + {"logic_operand", {REG, SUBREG, CONST_INT}}, \ + {"shift_operand", {REG, SUBREG, CONST_INT}}, \ + {"compare_operand", {REG, SUBREG, CONST_INT}}, \ + {"arith_const_operand", {CONST_INT}}, \ + {"logic_const_operand", {CONST_INT}}, \ + {"bte_operand", {REG, SUBREG, CONST_INT}}, \ + {"indexed_operand", {MEM}}, \ + {"load_operand", {MEM}}, \ + {"small_int", {CONST_INT}}, \ + {"logic_int", {CONST_INT}}, \ + {"call_insn_operand", {MEM}}, + +/* Define the information needed to generate branch insns. This is stored + from the compare operation. Note that we can't use "rtx" here since it + hasn't been defined! */ + +extern struct rtx_def *i860_compare_op0, *i860_compare_op1; + +/* Declare things which are defined in i860.c but called from + insn-output.c. */ + +extern unsigned long sfmode_constant_to_ulong (); +extern char *output_load (); +extern char *output_store (); +extern char *output_move_double (); +extern char *output_fp_move_double (); +extern char *output_block_move (); +extern char *output_delay_insn (); +extern char *output_delayed_branch (); +extern void output_load_address (); diff --git a/gnu/usr.bin/gcc/config/i860/i860.md b/gnu/usr.bin/gcc/config/i860/i860.md new file mode 100644 index 00000000000..a3df7bc4584 --- /dev/null +++ b/gnu/usr.bin/gcc/config/i860/i860.md @@ -0,0 +1,2318 @@ +;;- Machine description for Intel 860 chip for GNU C compiler +;; Copyright (C) 1989, 1990 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 2, or (at your option) +;; any later version. + +;; GNU CC is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU CC; see the file COPYING. If not, write to +;; the Free Software Foundation, 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + + +;;- See file "rtl.def" for documentation on define_insn, match_*, et. al. + +;;- cpp macro #define NOTICE_UPDATE_CC in file tm.h handles condition code +;;- updates for most instructions. + +;;- Operand classes for the register allocator: + +/* Bit-test instructions. */ + +(define_insn "" + [(set (cc0) (eq (and:SI (match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "logic_operand" "rL")) + (const_int 0)))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + return \"and %1,%0,%?r0\"; +}") + +(define_insn "" + [(set (cc0) (ne (and:SI (match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "logic_operand" "rL")) + (const_int 0)))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + cc_status.flags |= CC_NEGATED; + return \"and %1,%0,%?r0\"; +}") + +(define_insn "" + [(set (cc0) (eq (and:SI (match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "immediate_operand" "i")) + (const_int 0)))] + "GET_CODE (operands[1]) == CONST_INT && (INTVAL (operands[1]) & 0xffff) == 0" + "* +{ + CC_STATUS_PARTIAL_INIT; + return \"andh %H1,%0,%?r0\"; +}") + +(define_insn "" + [(set (cc0) (ne (and:SI (match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "immediate_operand" "i")) + (const_int 0)))] + "GET_CODE (operands[1]) == CONST_INT && (INTVAL (operands[1]) & 0xffff) == 0" + "* +{ + CC_STATUS_PARTIAL_INIT; + cc_status.flags |= CC_NEGATED; + return \"andh %H1,%0,%?r0\"; +}") + +(define_insn "" + [(set (cc0) (eq (ashiftrt:SI + (sign_extend:SI + (ashift:QI (match_operand:QI 0 "register_operand" "r") + (match_operand:QI 1 "logic_int" "n"))) + (match_operand:SI 2 "logic_int" "n")) + (const_int 0)))] + "" + "* +{ + int width = 8 - INTVAL (operands[2]); + int pos = 8 - width - INTVAL (operands[1]); + + CC_STATUS_PARTIAL_INIT; + operands[2] = gen_rtx (CONST_INT, VOIDmode, + ~((-1) << width) << pos); + return \"and %2,%0,%?r0\"; +}") + +;; ------------------------------------------------------------------------- +;; SImode signed integer comparisons +;; ------------------------------------------------------------------------- + +(define_insn "cmpeqsi" + [(set (cc0) (eq (match_operand:SI 0 "logic_operand" "r,rL") + (match_operand:SI 1 "logic_operand" "L,r")))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + if (REG_P (operands[0])) + return \"xor %1,%0,%?r0\"; + else + return \"xor %0,%1,%?r0\"; +}") + +(define_insn "cmpnesi" + [(set (cc0) (ne (match_operand:SI 0 "logic_operand" "r,rL") + (match_operand:SI 1 "logic_operand" "L,r")))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + cc_status.flags |= CC_NEGATED; + if (REG_P (operands[0])) + return \"xor %1,%0,%?r0\"; + else + return \"xor %0,%1,%?r0\"; +}") + +(define_insn "cmpltsi" + [(set (cc0) (lt (match_operand:SI 0 "arith_operand" "r,rI") + (match_operand:SI 1 "arith_operand" "I,r")))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + if (REG_P (operands[1])) + return \"subs %0,%1,%?r0\"; + else + { + cc_status.flags |= CC_REVERSED; + operands[1] = gen_rtx (CONST_INT, VOIDmode, - INTVAL (operands[1])); + return \"adds %1,%0,%?r0\"; + } +}") + +(define_insn "cmpgtsi" + [(set (cc0) (gt (match_operand:SI 0 "arith_operand" "r,rI") + (match_operand:SI 1 "arith_operand" "I,r")))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + if (REG_P (operands[0])) + return \"subs %1,%0,%?r0\"; + else + { + cc_status.flags |= CC_REVERSED; + operands[0] = gen_rtx (CONST_INT, VOIDmode, - INTVAL (operands[0])); + return \"adds %0,%1,%?r0\"; + } +}") + +(define_insn "cmplesi" + [(set (cc0) (le (match_operand:SI 0 "arith_operand" "r,rI") + (match_operand:SI 1 "arith_operand" "I,r")))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + cc_status.flags |= CC_NEGATED; + if (REG_P (operands[0])) + return \"subs %1,%0,%?r0\"; + else + { + cc_status.flags |= CC_REVERSED; + operands[0] = gen_rtx (CONST_INT, VOIDmode, - INTVAL (operands[0])); + return \"adds %0,%1,%?r0\"; + } +}") + +(define_insn "cmpgesi" + [(set (cc0) (ge (match_operand:SI 0 "arith_operand" "r,rI") + (match_operand:SI 1 "arith_operand" "I,r")))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + cc_status.flags |= CC_NEGATED; + if (REG_P (operands[1])) + return \"subs %0,%1,%?r0\"; + else + { + cc_status.flags |= CC_REVERSED; + operands[1] = gen_rtx (CONST_INT, VOIDmode, - INTVAL (operands[1])); + return \"adds %1,%0,%?r0\"; + } +}") + +;; ------------------------------------------------------------------------- +;; SImode unsigned integer comparisons +;; ------------------------------------------------------------------------- + +;; WARNING! There is a small i860 hardware limitation (bug?) which we +;; may run up against (if we are not careful) when we are trying to do +;; unsigned comparisons like (x >= 0), (x < 0), (0 <= x), and (0 > x). +;; Specifically, we must avoid using an `addu' instruction to perform +;; such comparisons because the result (in the CC bit register) will +;; come out wrong. (This fact is documented in a footnote on page 7-10 +;; of the 1991 version of the i860 Microprocessor Family Programmer's +;; Reference Manual). Note that unsigned comparisons of this sort are +;; always redundant anyway, because an unsigned quantity can never be +;; less than zero. When we see cases like this, we generate an +;; `or K,%r0,%r0' instruction instead (where K is a constant 0 or -1) +;; so as to get the CC bit register set properly for any subsequent +;; conditional jump instruction. + +(define_insn "cmpgeusi" + [(set (cc0) (geu (match_operand:SI 0 "arith_operand" "r,rI") + (match_operand:SI 1 "arith_operand" "I,r")))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + if (REG_P (operands[1])) + return \"subu %0,%1,%?r0\"; + else + { + if (INTVAL (operands[1]) == 0) + return \"or 0,%?r0,%?r0\"; + else + { + cc_status.flags |= CC_REVERSED; + operands[1] = gen_rtx (CONST_INT, VOIDmode, - INTVAL (operands[1])); + return \"addu %1,%0,%?r0\"; + } + } +}") + +(define_insn "cmpleusi" + [(set (cc0) (leu (match_operand:SI 0 "arith_operand" "r,rI") + (match_operand:SI 1 "arith_operand" "I,r")))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + if (REG_P (operands[0])) + return \"subu %1,%0,%?r0\"; + else + { + if (INTVAL (operands[0]) == 0) + return \"or 0,%?r0,%?r0\"; + else + { + cc_status.flags |= CC_REVERSED; + operands[0] = gen_rtx (CONST_INT, VOIDmode, - INTVAL (operands[0])); + return \"addu %0,%1,%?r0\"; + } + } +}") + +;; ------------------------------------------------------------------------- +;; SFmode floating-point comparisons +;; ------------------------------------------------------------------------- + +(define_insn "cmpeqsf" + [(set (cc0) (eq (match_operand:SF 0 "reg_or_0_operand" "fG") + (match_operand:SF 1 "reg_or_0_operand" "fG")))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + return \"pfeq.ss %r0,%r1,%?f0\"; +}") + +(define_insn "cmpnesf" + [(set (cc0) (ne (match_operand:SF 0 "reg_or_0_operand" "fG") + (match_operand:SF 1 "reg_or_0_operand" "fG")))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + cc_status.flags |= CC_NEGATED; + return \"pfeq.ss %r1,%r0,%?f0\"; +}") + +;; NOTE: The i860 Programmer's Reference Manual says that when we are +;; doing (A < B) or (A > B) comparisons, we have to use pfgt for these +;; in order to be IEEE compliant (in case a trap occurs during these +;; operations). Conversely, for (A <= B) or (A >= B) comparisons, we +;; must use pfle to be IEEE compliant. + +(define_insn "cmpltsf" + [(set (cc0) (lt (match_operand:SF 0 "reg_or_0_operand" "fG") + (match_operand:SF 1 "reg_or_0_operand" "fG")))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + return \"pfgt.ss %r1,%r0,%?f0\"; +}") + +(define_insn "cmpgtsf" + [(set (cc0) (gt (match_operand:SF 0 "reg_or_0_operand" "fG") + (match_operand:SF 1 "reg_or_0_operand" "fG")))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + return \"pfgt.ss %r0,%r1,%?f0\"; +}") + +;; NOTE: The pfle opcode doesn't do what you think it does. It is +;; bass-ackwards. It *clears* the CC flag if the first operand is +;; less than or equal to the second. Thus, we have to set CC_NEGATED +;; for the following two patterns. + +(define_insn "cmplesf" + [(set (cc0) (le (match_operand:SF 0 "reg_or_0_operand" "fG") + (match_operand:SF 1 "reg_or_0_operand" "fG")))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + cc_status.flags |= CC_NEGATED; + return \"pfle.ss %r0,%r1,%?f0\"; +}") + +(define_insn "cmpgesf" + [(set (cc0) (ge (match_operand:SF 0 "reg_or_0_operand" "fG") + (match_operand:SF 1 "reg_or_0_operand" "fG")))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + cc_status.flags |= CC_NEGATED; + return \"pfle.ss %r1,%r0,%?f0\"; +}") + +;; ------------------------------------------------------------------------- +;; DFmode floating-point comparisons +;; ------------------------------------------------------------------------- + +(define_insn "cmpeqdf" + [(set (cc0) (eq (match_operand:DF 0 "reg_or_0_operand" "fG") + (match_operand:DF 1 "reg_or_0_operand" "fG")))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + return \"pfeq.dd %r0,%r1,%?f0\"; +}") + +(define_insn "cmpnedf" + [(set (cc0) (ne (match_operand:DF 0 "reg_or_0_operand" "fG") + (match_operand:DF 1 "reg_or_0_operand" "fG")))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + cc_status.flags |= CC_NEGATED; + return \"pfeq.dd %r1,%r0,%?f0\"; +}") + +;; NOTE: The i860 Programmer's Reference Manual says that when we are +;; doing (A < B) or (A > B) comparisons, we have to use pfgt for these +;; in order to be IEEE compliant (in case a trap occurs during these +;; operations). Conversely, for (A <= B) or (A >= B) comparisons, we +;; must use pfle to be IEEE compliant. + +(define_insn "cmpltdf" + [(set (cc0) (lt (match_operand:DF 0 "reg_or_0_operand" "fG") + (match_operand:DF 1 "reg_or_0_operand" "fG")))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + return \"pfgt.dd %r1,%r0,%?f0\"; +}") + +(define_insn "cmpgtdf" + [(set (cc0) (gt (match_operand:DF 0 "reg_or_0_operand" "fG") + (match_operand:DF 1 "reg_or_0_operand" "fG")))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + return \"pfgt.dd %r0,%r1,%?f0\"; +}") + +;; NOTE: The pfle opcode doesn't do what you think it does. It is +;; bass-ackwards. It *clears* the CC flag if the first operand is +;; less than or equal to the second. Thus, we have to set CC_NEGATED +;; for the following two patterns. + +(define_insn "cmpledf" + [(set (cc0) (le (match_operand:DF 0 "reg_or_0_operand" "fG") + (match_operand:DF 1 "reg_or_0_operand" "fG")))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + cc_status.flags |= CC_NEGATED; + return \"pfle.dd %r0,%r1,%?f0\"; +}") + +(define_insn "cmpgedf" + [(set (cc0) (ge (match_operand:DF 0 "reg_or_0_operand" "fG") + (match_operand:DF 1 "reg_or_0_operand" "fG")))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + cc_status.flags |= CC_NEGATED; + return \"pfle.dd %r1,%r0,%?f0\"; +}") + +;; ------------------------------------------------------------------------ +;; Integer EQ/NE comparisons against constant values which will fit in the +;; 16-bit immediate field of an instruction. These are made by combining. +;; ------------------------------------------------------------------------ + +(define_insn "" + [(set (cc0) (eq (zero_extend:SI (match_operand:HI 0 "load_operand" "m")) + (match_operand:SI 1 "small_int" "I")))] + "INTVAL (operands[1]) >= 0" + "* +{ + CC_STATUS_PARTIAL_INIT; + return \"ld.s %0,%?r31\;xor %1,%?r31,%?r0\"; +}") + +(define_insn "" + [(set (cc0) (eq (match_operand:SI 0 "small_int" "I") + (zero_extend:SI (match_operand:HI 1 "load_operand" "m"))))] + "INTVAL (operands[0]) >= 0" + "* +{ + CC_STATUS_PARTIAL_INIT; + return \"ld.s %1,%?r31\;xor %0,%?r31,%?r0\"; +}") + +;; ------------------------------------------------------------------------ +;; Define the real conditional branch instructions. +;; ------------------------------------------------------------------------ + +(define_insn "cbranch" + [(set (pc) (if_then_else (eq (cc0) (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "* +{ + if ((cc_prev_status.flags & CC_NEGATED) == 0) + return \"bnc %l0\"; + else + return \"bc %l0\"; +}") + +(define_insn "flipped_cbranch" + [(set (pc) (if_then_else (ne (cc0) + (const_int 0)) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "* +{ + if ((cc_prev_status.flags & CC_NEGATED) == 0) + return \"bnc %l0\"; + else + return \"bc %l0\"; +}") + +(define_insn "inverse_cbranch" + [(set (pc) (if_then_else (eq (cc0) + (const_int 0)) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "* +{ + if ((cc_prev_status.flags & CC_NEGATED) == 0) + return \"bc %l0\"; + else + return \"bnc %l0\"; +}") + + +(define_insn "flipped_inverse_cbranch" + [(set (pc) (if_then_else (ne (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "* +{ + if ((cc_prev_status.flags & CC_NEGATED) == 0) + return \"bc %l0\"; + else + return \"bnc %l0\"; +}") + +;; Simple BTE/BTNE compare-and-branch insns made by combining. +;; Note that it is wrong to add similar patterns for QI or HImode +;; because bte/btne always compare the whole register. + +(define_insn "" + [(set (pc) + (if_then_else (eq (match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "bte_operand" "rK")) + (label_ref (match_operand 2 "" "")) + (pc)))] + "" + "bte %1,%0,%2") + +(define_insn "" + [(set (pc) + (if_then_else (ne (match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "bte_operand" "rK")) + (label_ref (match_operand 2 "" "")) + (pc)))] + "" + "btne %1,%0,%2") + +(define_insn "" + [(set (pc) + (if_then_else (eq (match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "bte_operand" "rK")) + (pc) + (label_ref (match_operand 2 "" ""))))] + "" + "btne %1,%0,%2") + +(define_insn "" + [(set (pc) + (if_then_else (ne (match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "bte_operand" "rK")) + (pc) + (label_ref (match_operand 2 "" ""))))] + "" + "bte %1,%0,%2") + +;; Load byte/halfword, zero-extend, & compare-and-branch insns. +;; These are made by combining. + +(define_insn "" + [(set (pc) + (if_then_else (eq (zero_extend:SI (match_operand:QI 0 "memory_operand" "m")) + (match_operand:SI 1 "bte_operand" "K")) + (label_ref (match_operand 2 "" "")) + (pc))) + (match_scratch:SI 3 "=r")] + "" + "ld.b %0,%3;bte %1,%3,%2") + +(define_insn "" + [(set (pc) + (if_then_else (ne (zero_extend:SI (match_operand:QI 0 "memory_operand" "m")) + (match_operand:SI 1 "bte_operand" "K")) + (label_ref (match_operand 2 "" "")) + (pc))) + (match_scratch:SI 3 "=r")] + "" + "ld.b %0,%3;btne %1,%3,%2") + +(define_insn "" + [(set (pc) + (if_then_else (eq (zero_extend:SI (match_operand:QI 0 "memory_operand" "m")) + (match_operand:SI 1 "bte_operand" "K")) + (pc) + (label_ref (match_operand 2 "" "")))) + (match_scratch:SI 3 "=r")] + "" + "ld.b %0,%3;btne %1,%3,%2") + +(define_insn "" + [(set (pc) + (if_then_else (ne (zero_extend:SI (match_operand:QI 0 "memory_operand" "m")) + (match_operand:SI 1 "bte_operand" "K")) + (pc) + (label_ref (match_operand 2 "" "")))) + (match_scratch:SI 3 "=r")] + "" + "ld.b %0,%3;bte %1,%3,%2") + +(define_insn "" + [(set (pc) + (if_then_else (eq (zero_extend:SI (match_operand:HI 0 "memory_operand" "m")) + (match_operand:SI 1 "bte_operand" "K")) + (label_ref (match_operand 2 "" "")) + (pc))) + (match_scratch:SI 3 "=r")] + "" + "ld.s %0,%3;bte %1,%3,%2") + +(define_insn "" + [(set (pc) + (if_then_else (ne (zero_extend:SI (match_operand:HI 0 "memory_operand" "m")) + (match_operand:SI 1 "bte_operand" "K")) + (label_ref (match_operand 2 "" "")) + (pc))) + (match_scratch:SI 3 "=r")] + "" + "ld.s %0,%3;btne %1,%3,%2") + +(define_insn "" + [(set (pc) + (if_then_else (eq (zero_extend:SI (match_operand:HI 0 "memory_operand" "m")) + (match_operand:SI 1 "bte_operand" "K")) + (pc) + (label_ref (match_operand 2 "" "")))) + (match_scratch:SI 3 "=r")] + "" + "ld.s %0,%3;btne %1,%3,%2") + +(define_insn "" + [(set (pc) + (if_then_else (ne (zero_extend:SI (match_operand:HI 0 "memory_operand" "m")) + (match_operand:SI 1 "bte_operand" "K")) + (pc) + (label_ref (match_operand 2 "" "")))) + (match_scratch:SI 3 "=r")] + "" + "ld.s %0,%3;bte %1,%3,%2") + + +;; Generation of conditionals. + +;; We save the compare operands in the cmpxx patterns and use then when +;; we generate the branch. + +(define_expand "cmpsi" + [(set (cc0) (compare (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "compare_operand" "")))] + "" + " +{ i860_compare_op0 = operands[0]; + i860_compare_op1 = operands[1]; + DONE; +}") + +(define_expand "cmpsf" + [(set (cc0) (compare (match_operand:SF 0 "register_operand" "") + (match_operand:SF 1 "register_operand" "")))] + "" + " +{ i860_compare_op0 = operands[0]; + i860_compare_op1 = operands[1]; + DONE; +}") + +(define_expand "cmpdf" + [(set (cc0) (compare (match_operand:DF 0 "register_operand" "") + (match_operand:DF 1 "register_operand" "")))] + "" + " +{ i860_compare_op0 = operands[0]; + i860_compare_op1 = operands[1]; + DONE; +}") + +;; These are the standard-named conditional branch patterns. +;; Detailed comments are found in the first one only. + +(define_expand "beq" + [(set (pc) + (if_then_else (eq (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + /* Emit a single-condition compare insn according to + the type of operands and the condition to be tested. */ + + if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) == MODE_INT) + emit_insn (gen_cmpeqsi (i860_compare_op0, i860_compare_op1)); + else if (GET_MODE (i860_compare_op0) == SFmode) + emit_insn (gen_cmpeqsf (i860_compare_op0, i860_compare_op1)); + else if (GET_MODE (i860_compare_op0) == DFmode) + emit_insn (gen_cmpeqdf (i860_compare_op0, i860_compare_op1)); + else + abort (); + + /* Emit branch-if-true. */ + + emit_jump_insn (gen_flipped_inverse_cbranch (operands[0])); + DONE; +}") + +(define_expand "bne" + [(set (pc) + (if_then_else (ne (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) == MODE_INT) + emit_insn (gen_cmpeqsi (i860_compare_op0, i860_compare_op1)); + else if (GET_MODE (i860_compare_op0) == SFmode) + emit_insn (gen_cmpeqsf (i860_compare_op0, i860_compare_op1)); + else if (GET_MODE (i860_compare_op0) == DFmode) + emit_insn (gen_cmpeqdf (i860_compare_op0, i860_compare_op1)); + else + abort (); + + emit_jump_insn (gen_flipped_cbranch (operands[0])); + + DONE; +}") + +(define_expand "bgt" + [(set (pc) + (if_then_else (gt (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) == MODE_INT) + emit_insn (gen_cmpgtsi (i860_compare_op0, i860_compare_op1)); + else if (GET_MODE (i860_compare_op0) == SFmode) + emit_insn (gen_cmpgtsf (i860_compare_op0, i860_compare_op1)); + else if (GET_MODE (i860_compare_op0) == DFmode) + emit_insn (gen_cmpgtdf (i860_compare_op0, i860_compare_op1)); + else + abort (); + + emit_jump_insn (gen_flipped_inverse_cbranch (operands[0])); + DONE; +}") + +(define_expand "blt" + [(set (pc) + (if_then_else (lt (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) == MODE_INT) + emit_insn (gen_cmpltsi (i860_compare_op0, i860_compare_op1)); + else if (GET_MODE (i860_compare_op0) == SFmode) + emit_insn (gen_cmpltsf (i860_compare_op0, i860_compare_op1)); + else if (GET_MODE (i860_compare_op0) == DFmode) + emit_insn (gen_cmpltdf (i860_compare_op0, i860_compare_op1)); + else + abort (); + + emit_jump_insn (gen_flipped_inverse_cbranch (operands[0])); + DONE; +}") + +(define_expand "ble" + [(set (pc) + (if_then_else (le (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) == MODE_INT) + { + emit_insn (gen_cmpgtsi (i860_compare_op0, i860_compare_op1)); + emit_jump_insn (gen_flipped_cbranch (operands[0])); + } + else + { + if (GET_MODE (i860_compare_op0) == SFmode) + emit_insn (gen_cmplesf (i860_compare_op0, i860_compare_op1)); + else if (GET_MODE (i860_compare_op0) == DFmode) + emit_insn (gen_cmpledf (i860_compare_op0, i860_compare_op1)); + else + abort (); + emit_jump_insn (gen_flipped_inverse_cbranch (operands[0])); + } + DONE; +}") + +(define_expand "bge" + [(set (pc) + (if_then_else (ge (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) == MODE_INT) + { + emit_insn (gen_cmpltsi (i860_compare_op0, i860_compare_op1)); + emit_jump_insn (gen_flipped_cbranch (operands[0])); + } + else + { + if (GET_MODE (i860_compare_op0) == SFmode) + emit_insn (gen_cmpgesf (i860_compare_op0, i860_compare_op1)); + else if (GET_MODE (i860_compare_op0) == DFmode) + emit_insn (gen_cmpgedf (i860_compare_op0, i860_compare_op1)); + else + abort (); + emit_jump_insn (gen_flipped_inverse_cbranch (operands[0])); + } + DONE; +}") + +(define_expand "bgtu" + [(set (pc) + (if_then_else (gtu (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) != MODE_INT) + abort (); + + emit_insn (gen_cmpleusi (i860_compare_op0, i860_compare_op1)); + emit_jump_insn (gen_flipped_cbranch (operands[0])); + DONE; +}") + +(define_expand "bltu" + [(set (pc) + (if_then_else (ltu (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) != MODE_INT) + abort (); + + emit_insn (gen_cmpgeusi (i860_compare_op0, i860_compare_op1)); + emit_jump_insn (gen_flipped_cbranch (operands[0])); + DONE; +}") + +(define_expand "bgeu" + [(set (pc) + (if_then_else (geu (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) != MODE_INT) + abort (); + + emit_insn (gen_cmpgeusi (i860_compare_op0, i860_compare_op1)); + emit_jump_insn (gen_flipped_inverse_cbranch (operands[0])); + DONE; +}") + +(define_expand "bleu" + [(set (pc) + (if_then_else (leu (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + " +{ + if (GET_MODE_CLASS (GET_MODE (i860_compare_op0)) != MODE_INT) + abort (); + + emit_insn (gen_cmpleusi (i860_compare_op0, i860_compare_op1)); + emit_jump_insn (gen_flipped_inverse_cbranch (operands[0])); + DONE; +}") + +;; Move instructions + +;; Note that source operands for `mov' pseudo-instructions are no longer +;; allowed (by the svr4 assembler) to be "big" things, i.e. constants that +;; won't fit in 16-bits. (This includes any sort of a relocatable address +;; also.) Thus, we must use an explicit orh/or pair of instructions if +;; the source operand is something "big". + +(define_insn "movsi" + [(set (match_operand:SI 0 "general_operand" "=r,m,f") + (match_operand:SI 1 "general_operand" "rmif,rfJ,rmfJ"))] + "" + "* +{ + if (GET_CODE (operands[0]) == MEM) + { + if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) + return output_store (operands); + if (FP_REG_P (operands[1])) + return \"fst.l %1,%0\"; + return \"st.l %r1,%0\"; + } + if (GET_CODE (operands[1]) == MEM) + { + if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) + return output_load (operands); + if (FP_REG_P (operands[0])) + return \"fld.l %1,%0\"; + return \"ld.l %1,%0\"; + } + if (FP_REG_P (operands[1]) && FP_REG_P (operands[0])) + return \"fmov.ss %1,%0\"; + if (FP_REG_P (operands[1])) + return \"fxfr %1,%0\"; + if (FP_REG_P (operands[0]) && operands[1] == const0_rtx) + return \"fmov.ss %?f0,%0\"; + if (FP_REG_P (operands[0])) + return \"ixfr %1,%0\"; + + if (GET_CODE (operands[1]) == REG) + return \"shl %?r0,%1,%0\"; + + CC_STATUS_PARTIAL_INIT; + + if (GET_CODE (operands[1]) == CONST_INT) + { + if((INTVAL (operands[1]) & 0xffff0000) == 0) + return \"or %L1,%?r0,%0\"; + if((INTVAL (operands[1]) & 0x0000ffff) == 0) + return \"orh %H1,%?r0,%0\"; + } + return \"orh %H1,%?r0,%0\;or %L1,%0,%0\"; +}") + +(define_insn "movhi" + [(set (match_operand:HI 0 "general_operand" "=r,m,!*f,!r") + (match_operand:HI 1 "general_operand" "rmi,rJ,rJ*f,*f"))] + "" + "* +{ + if (GET_CODE (operands[0]) == MEM) + { + if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) + return output_store (operands); + return \"st.s %r1,%0\"; + } + if (GET_CODE (operands[1]) == MEM) + { + if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) + return output_load (operands); + return \"ld.s %1,%0\"; + } + if (FP_REG_P (operands[1]) && FP_REG_P (operands[0])) + return \"fmov.ss %1,%0\"; + if (FP_REG_P (operands[1])) + return \"fxfr %1,%0\"; + if (FP_REG_P (operands[0]) && operands[1] == const0_rtx) + return \"fmov.ss %?f0,%0\"; + if (FP_REG_P (operands[0])) + return \"ixfr %1,%0\"; + + if (GET_CODE (operands[1]) == REG) + return \"shl %?r0,%1,%0\"; + + CC_STATUS_PARTIAL_INIT; + + return \"or %L1,%?r0,%0\"; +}") + +(define_insn "movqi" + [(set (match_operand:QI 0 "general_operand" "=r,m,!*f,!r") + (match_operand:QI 1 "general_operand" "rmi,rJ,rJ*f,*f"))] + "" + "* +{ + if (GET_CODE (operands[0]) == MEM) + { + if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) + return output_store (operands); + return \"st.b %r1,%0\"; + } + if (GET_CODE (operands[1]) == MEM) + { + if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) + return output_load (operands); + return \"ld.b %1,%0\"; + } + if (FP_REG_P (operands[1]) && FP_REG_P (operands[0])) + return \"fmov.ss %1,%0\"; + if (FP_REG_P (operands[1])) + return \"fxfr %1,%0\"; + if (FP_REG_P (operands[0]) && operands[1] == const0_rtx) + return \"fmov.ss %?f0,%0\"; + if (FP_REG_P (operands[0])) + return \"ixfr %1,%0\"; + + if (GET_CODE (operands[1]) == REG) + return \"shl %?r0,%1,%0\"; + + CC_STATUS_PARTIAL_INIT; + + return \"or %L1,%?r0,%0\"; +}") + +;; The definition of this insn does not really explain what it does, +;; but it should suffice +;; that anything generated as this insn will be recognized as one +;; and that it won't successfully combine with anything. +(define_expand "movstrsi" + [(parallel [(set (mem:BLK (match_operand:BLK 0 "general_operand" "")) + (mem:BLK (match_operand:BLK 1 "general_operand" ""))) + (use (match_operand:SI 2 "nonmemory_operand" "")) + (use (match_operand:SI 3 "immediate_operand" "")) + (clobber (match_dup 4)) + (clobber (match_dup 5)) + (clobber (match_dup 6)) + (clobber (match_dup 0)) + (clobber (match_dup 1))])] + "" + " +{ + operands[0] = copy_to_mode_reg (SImode, XEXP (operands[0], 0)); + operands[1] = copy_to_mode_reg (SImode, XEXP (operands[1], 0)); + operands[4] = gen_reg_rtx (SImode); + operands[5] = gen_reg_rtx (SImode); + operands[6] = gen_reg_rtx (SImode); +}") + +(define_insn "" + [(set (mem:BLK (match_operand:SI 0 "register_operand" "r")) + (mem:BLK (match_operand:SI 1 "register_operand" "r"))) + (use (match_operand:SI 2 "general_operand" "rn")) + (use (match_operand:SI 3 "immediate_operand" "i")) + (clobber (match_operand:SI 4 "register_operand" "=r")) + (clobber (match_operand:SI 5 "register_operand" "=r")) + (clobber (match_operand:SI 6 "register_operand" "=r")) + (clobber (match_dup 0)) + (clobber (match_dup 1))] + "" + "* return output_block_move (operands);") + +;; Floating point move insns + +;; This pattern forces (set (reg:DF ...) (const_double ...)) +;; to be reloaded by putting the constant into memory. +;; It must come before the more general movdf pattern. +(define_insn "" + [(set (match_operand:DF 0 "general_operand" "=r,f,o") + (match_operand:DF 1 "" "mG,m,G"))] + "GET_CODE (operands[1]) == CONST_DOUBLE" + "* +{ + if (FP_REG_P (operands[0]) || operands[1] == CONST0_RTX (DFmode)) + return output_fp_move_double (operands); + return output_move_double (operands); +}") + +(define_insn "movdf" + [(set (match_operand:DF 0 "general_operand" "=*rm,*r,?f,?*rm") + (match_operand:DF 1 "general_operand" "*r,m,*rfmG,f"))] + "" + "* +{ + if (GET_CODE (operands[0]) == MEM + && CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) + return output_store (operands); + if (GET_CODE (operands[1]) == MEM + && CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) + return output_load (operands); + + if (FP_REG_P (operands[0]) || FP_REG_P (operands[1])) + return output_fp_move_double (operands); + return output_move_double (operands); +}") + +(define_insn "movdi" + [(set (match_operand:DI 0 "general_operand" "=rm,r,?f,?rm") + (match_operand:DI 1 "general_operand" "r,miF,rfmG,f"))] + "" + "* +{ + if (GET_CODE (operands[0]) == MEM + && CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) + return output_store (operands); + if (GET_CODE (operands[1]) == MEM + && CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) + return output_load (operands); + + /* ??? How can we have a DFmode arg here with DImode above? */ + if (FP_REG_P (operands[0]) && operands[1] == CONST0_RTX (DFmode)) + return \"fmov.dd %?f0,%0\"; + + if (FP_REG_P (operands[0]) || FP_REG_P (operands[1])) + return output_fp_move_double (operands); + return output_move_double (operands); +}") + +;; The alternative m/r is separate from m/f +;; The first alternative is separate from the second for the same reason. +(define_insn "movsf" + [(set (match_operand:SF 0 "general_operand" "=*rf,*rf,*r,m,m") + (match_operand:SF 1 "general_operand" "*r,fmG,F,*r,f"))] + "" + "* +{ + if (GET_CODE (operands[0]) == MEM + && CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) + return output_store (operands); + if (GET_CODE (operands[1]) == MEM + && CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) + return output_load (operands); + if (FP_REG_P (operands[0])) + { + if (FP_REG_P (operands[1])) + return \"fmov.ss %1,%0\"; + if (GET_CODE (operands[1]) == REG) + return \"ixfr %1,%0\"; + if (operands[1] == CONST0_RTX (SFmode)) + return \"fmov.ss %?f0,%0\"; + if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) + { + if (! ((cc_prev_status.flags & CC_KNOW_HI_R31) + && (cc_prev_status.flags & CC_HI_R31_ADJ) + && cc_prev_status.mdep == XEXP(operands[1],0))) + { + CC_STATUS_INIT; + cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ; + cc_status.mdep = XEXP (operands[1], 0); + return \"orh %h1,%?r0,%?r31\;fld.l %L1(%?r31),%0\"; + } + return \"fld.l %L1(%?r31),%0\"; + } + return \"fld.l %1,%0\"; + } + if (FP_REG_P (operands[1]) || GET_CODE (operands[1]) == CONST_DOUBLE) + { + if (GET_CODE (operands[0]) == REG && FP_REG_P (operands[1])) + return \"fxfr %1,%0\"; + if (GET_CODE (operands[0]) == REG) + { + CC_STATUS_PARTIAL_INIT; + if (GET_CODE (operands[1]) == CONST_DOUBLE) + { + register unsigned long ul; + + ul = sfmode_constant_to_ulong (operands[1]); + if ((ul & 0x0000ffff) == 0) + return \"orh %H1,%?r0,%0\"; + if ((ul & 0xffff0000) == 0) + return \"or %L1,%?r0,%0\"; + } + return \"orh %H1,%?r0,%0\;or %L1,%0,%0\"; + } + /* Now operand 0 must be memory. + If operand 1 is CONST_DOUBLE, its value must be 0. */ + if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) + { + if (! ((cc_prev_status.flags & CC_KNOW_HI_R31) + && (cc_prev_status.flags & CC_HI_R31_ADJ) + && XEXP (operands[0], 0) == cc_prev_status.mdep)) + { + CC_STATUS_INIT; + cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ; + cc_status.mdep = XEXP (operands[0], 0); + output_asm_insn (\"orh %h0,%?r0,%?r31\", operands); + } + return \"fst.l %r1,%L0(%?r31)\"; + } + return \"fst.l %r1,%0\"; + } + if (GET_CODE (operands[0]) == MEM) + return \"st.l %r1,%0\"; + if (GET_CODE (operands[1]) == MEM) + return \"ld.l %1,%0\"; + if (operands[1] == CONST0_RTX (SFmode)) + return \"shl %?r0,%?r0,%0\"; + return \"mov %1,%0\"; +}") + +;; Special load insns for REG+REG addresses. +;; Such addresses are not "legitimate" because st rejects them. + +(define_insn "" + [(set (match_operand:DF 0 "register_operand" "=rf") + (match_operand:DF 1 "indexed_operand" "m"))] + "" + "* +{ + if (FP_REG_P (operands[0])) + return output_fp_move_double (operands); + return output_move_double (operands); +}") + +(define_insn "" + [(set (match_operand:SF 0 "register_operand" "=rf") + (match_operand:SF 1 "indexed_operand" "m"))] + "" + "* +{ + if (FP_REG_P (operands[0])) + return \"fld.l %1,%0\"; + return \"ld.l %1,%0\"; +}") + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=rf") + (match_operand:SI 1 "indexed_operand" "m"))] + "" + "* +{ + if (FP_REG_P (operands[0])) + return \"fld.l %1,%0\"; + return \"ld.l %1,%0\"; +}") + +(define_insn "" + [(set (match_operand:HI 0 "register_operand" "=r") + (match_operand:HI 1 "indexed_operand" "m"))] + "" + "ld.s %1,%0") + +(define_insn "" + [(set (match_operand:QI 0 "register_operand" "=r") + (match_operand:QI 1 "indexed_operand" "m"))] + "" + "ld.b %1,%0") + +;; Likewise for floating-point store insns. + +(define_insn "" + [(set (match_operand:DF 0 "indexed_operand" "=m") + (match_operand:DF 1 "register_operand" "f"))] + "" + "fst.d %1,%0") + +(define_insn "" + [(set (match_operand:SF 0 "indexed_operand" "=m") + (match_operand:SF 1 "register_operand" "f"))] + "" + "fst.l %1,%0") + +;;- truncation instructions +(define_insn "truncsiqi2" + [(set (match_operand:QI 0 "general_operand" "=g") + (truncate:QI + (match_operand:SI 1 "register_operand" "r")))] + "" + "* +{ + if (GET_CODE (operands[0]) == MEM) + if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) + { + if (! ((cc_prev_status.flags & CC_KNOW_HI_R31) + && (cc_prev_status.flags & CC_HI_R31_ADJ) + && XEXP (operands[0], 0) == cc_prev_status.mdep)) + { + CC_STATUS_INIT; + cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ; + cc_status.mdep = XEXP (operands[0], 0); + output_asm_insn (\"orh %h0,%?r0,%?r31\", operands); + } + return \"st.b %1,%L0(%?r31)\"; + } + else + return \"st.b %1,%0\"; + return \"shl %?r0,%1,%0\"; +}") + +(define_insn "trunchiqi2" + [(set (match_operand:QI 0 "general_operand" "=g") + (truncate:QI + (match_operand:HI 1 "register_operand" "r")))] + "" + "* +{ + if (GET_CODE (operands[0]) == MEM) + if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) + { + if (! ((cc_prev_status.flags & CC_KNOW_HI_R31) + && (cc_prev_status.flags & CC_HI_R31_ADJ) + && XEXP (operands[0], 0) == cc_prev_status.mdep)) + { + CC_STATUS_INIT; + cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ; + cc_status.mdep = XEXP (operands[0], 0); + output_asm_insn (\"orh %h0,%?r0,%?r31\", operands); + } + return \"st.b %1,%L0(%?r31)\"; + } + else + return \"st.b %1,%0\"; + return \"shl %?r0,%1,%0\"; +}") + +(define_insn "truncsihi2" + [(set (match_operand:HI 0 "general_operand" "=g") + (truncate:HI + (match_operand:SI 1 "register_operand" "r")))] + "" + "* +{ + if (GET_CODE (operands[0]) == MEM) + if (CONSTANT_ADDRESS_P (XEXP (operands[0], 0))) + { + if (! ((cc_prev_status.flags & CC_KNOW_HI_R31) + && (cc_prev_status.flags & CC_HI_R31_ADJ) + && XEXP (operands[0], 0) == cc_prev_status.mdep)) + { + CC_STATUS_INIT; + cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ; + cc_status.mdep = XEXP (operands[0], 0); + output_asm_insn (\"orh %h0,%?r0,%?r31\", operands); + } + return \"st.s %1,%L0(%?r31)\"; + } + else + return \"st.s %1,%0\"; + return \"shl %?r0,%1,%0\"; +}") + +;;- zero extension instructions + +(define_insn "zero_extendhisi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extend:SI + (match_operand:HI 1 "register_operand" "r")))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + return \"and 0xffff,%1,%0\"; +}") + +(define_insn "zero_extendqihi2" + [(set (match_operand:HI 0 "register_operand" "=r") + (zero_extend:HI + (match_operand:QI 1 "register_operand" "r")))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + return \"and 0xff,%1,%0\"; +}") + +(define_insn "zero_extendqisi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extend:SI + (match_operand:QI 1 "register_operand" "r")))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + return \"and 0xff,%1,%0\"; +}") + +;; Sign extension instructions. + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (sign_extend:SI + (match_operand:HI 1 "indexed_operand" "m")))] + "" + "ld.s %1,%0") + +(define_insn "" + [(set (match_operand:HI 0 "register_operand" "=r") + (sign_extend:HI + (match_operand:QI 1 "indexed_operand" "m")))] + "" + "ld.b %1,%0") + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (sign_extend:SI + (match_operand:QI 1 "indexed_operand" "m")))] + "" + "ld.b %1,%0") + +(define_insn "extendhisi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (sign_extend:SI + (match_operand:HI 1 "nonimmediate_operand" "mr")))] + "" + "* +{ + if (REG_P (operands[1])) + return \"shl 16,%1,%0\;shra 16,%0,%0\"; + if (GET_CODE (operands[1]) == CONST_INT) + abort (); + if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) + { + CC_STATUS_INIT; + cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ; + cc_status.mdep = XEXP (operands[1], 0); + return \"orh %h1,%?r0,%?r31\;ld.s %L1(%?r31),%0\"; + } + else + return \"ld.s %1,%0\"; +}") + +(define_insn "extendqihi2" + [(set (match_operand:HI 0 "register_operand" "=r") + (sign_extend:HI + (match_operand:QI 1 "nonimmediate_operand" "mr")))] + "" + "* +{ + if (REG_P (operands[1])) + return \"shl 24,%1,%0\;shra 24,%0,%0\"; + if (GET_CODE (operands[1]) == CONST_INT) + abort (); + if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) + { + CC_STATUS_INIT; + cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ; + cc_status.mdep = XEXP (operands[1], 0); + return \"orh %h1,%?r0,%?r31\;ld.b %L1(%?r31),%0\"; + } + else + return \"ld.b %1,%0\"; +}") + +(define_insn "extendqisi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (sign_extend:SI + (match_operand:QI 1 "nonimmediate_operand" "mr")))] + "" + "* +{ + if (REG_P (operands[1])) + return \"shl 24,%1,%0\;shra 24,%0,%0\"; + if (GET_CODE (operands[1]) == CONST_INT) + abort (); + if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) + { + CC_STATUS_INIT; + cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ; + cc_status.mdep = XEXP (operands[1], 0); + return \"orh %h1,%?r0,%?r31\;ld.b %L1(%?r31),%0\"; + } + else + return \"ld.b %1,%0\"; +}") + +;; Signed bitfield extractions come out looking like +;; (shiftrt (sign_extend (shift <Y> <C1>)) <C2>) +;; which we expand poorly as four shift insns. +;; These patterns yield two shifts: +;; (shiftrt (shift <Y> <C3>) <C4>) +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (ashiftrt:SI + (sign_extend:SI + (match_operand:QI 1 "register_operand" "r")) + (match_operand:SI 2 "logic_int" "n")))] + "INTVAL (operands[2]) < 8" + "* +{ + return \"shl 24,%1,%0\;shra 24+%2,%0,%0\"; +}") + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (ashiftrt:SI + (sign_extend:SI + (subreg:QI (ashift:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "logic_int" "n")) 0)) + (match_operand:SI 3 "logic_int" "n")))] + "INTVAL (operands[3]) < 8" + "* +{ + return \"shl 0x18+%2,%1,%0\;shra 0x18+%3,%0,%0\"; +}") + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (ashiftrt:SI + (sign_extend:SI + (ashift:QI (match_operand:QI 1 "register_operand" "r") + (match_operand:QI 2 "logic_int" "n"))) + (match_operand:SI 3 "logic_int" "n")))] + "INTVAL (operands[3]) < 8" + "* +{ + return \"shl 0x18+%2,%1,%0\;shra 0x18+%3,%0,%0\"; +}") + +;; Special patterns for optimizing bit-field instructions. + +;; First two patterns are for bitfields that came from memory +;; testing only the high bit. They work with old combiner. + +(define_insn "" + [(set (cc0) + (eq (zero_extend:SI (subreg:QI (lshiftrt:SI (match_operand:SI 0 "register_operand" "r") + (const_int 7)) 0)) + (const_int 0)))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + return \"and 128,%0,%?r0\"; +}") + +(define_insn "" + [(set (cc0) + (eq (sign_extend:SI (subreg:QI (ashiftrt:SI (match_operand:SI 0 "register_operand" "r") + (const_int 7)) 0)) + (const_int 0)))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + return \"and 128,%0,%?r0\"; +}") + +;; next two patterns are good for bitfields coming from memory +;; (via pseudo-register) or from a register, though this optimization +;; is only good for values contained wholly within the bottom 13 bits +(define_insn "" + [(set (cc0) + (eq + (and:SI (lshiftrt:SI (match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "logic_int" "n")) + (match_operand:SI 2 "logic_int" "n")) + (const_int 0)))] + "LOGIC_INTVAL (INTVAL (operands[2]) << INTVAL (operands[1]))" + "* +{ + CC_STATUS_PARTIAL_INIT; + operands[2] = gen_rtx (CONST_INT, VOIDmode, + (INTVAL (operands[2]) << INTVAL (operands[1]))); + return \"and %2,%0,%?r0\"; +}") + +(define_insn "" + [(set (cc0) + (eq + (and:SI (ashiftrt:SI (match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "logic_int" "n")) + (match_operand:SI 2 "logic_int" "n")) + (const_int 0)))] + "LOGIC_INTVAL (INTVAL (operands[2]) << INTVAL (operands[1]))" + "* +{ + CC_STATUS_PARTIAL_INIT; + operands[2] = gen_rtx (CONST_INT, VOIDmode, + (INTVAL (operands[2]) << INTVAL (operands[1]))); + return \"and %2,%0,%?r0\"; +}") + +;; Conversions between float and double. + +(define_insn "extendsfdf2" + [(set (match_operand:DF 0 "register_operand" "=f") + (float_extend:DF + (match_operand:SF 1 "register_operand" "f")))] + "" + "fmov.sd %1,%0") + +(define_insn "truncdfsf2" + [(set (match_operand:SF 0 "register_operand" "=f") + (float_truncate:SF + (match_operand:DF 1 "register_operand" "f")))] + "" + "fmov.ds %1,%0") + +;; Conversion between fixed point and floating point. +;; Note that among the fix-to-float insns +;; the ones that start with SImode come first. +;; That is so that an operand that is a CONST_INT +;; (and therefore lacks a specific machine mode). +;; will be recognized as SImode (which is always valid) +;; rather than as QImode or HImode. + +;; This pattern forces (set (reg:SF ...) (float:SF (const_int ...))) +;; to be reloaded by putting the constant into memory. +;; It must come before the more general floatsisf2 pattern. +(define_expand "floatsidf2" + [(set (match_dup 2) (match_dup 3)) + (set (match_dup 4) (xor:SI (match_operand:SI 1 "register_operand" "") + (const_int -2147483648))) + (set (match_dup 5) (match_dup 3)) + (set (subreg:SI (match_dup 5) 0) (match_dup 4)) + (set (match_operand:DF 0 "register_operand" "") + (minus:DF (match_dup 5) (match_dup 2)))] + "" + " +{ + REAL_VALUE_TYPE d; + /* 4503601774854144 is (1 << 30) * ((1 << 22) + (1 << 1)). */ + d = REAL_VALUE_ATOF (\"4503601774854144\", DFmode); + operands[2] = gen_reg_rtx (DFmode); + operands[3] = CONST_DOUBLE_FROM_REAL_VALUE (d, DFmode); + operands[4] = gen_reg_rtx (SImode); + operands[5] = gen_reg_rtx (DFmode); +}") + +;; Floating to fixed conversion. + +(define_expand "fix_truncdfsi2" + ;; This first insn produces a double-word value + ;; in which only the low word is valid. + [(set (match_dup 2) + (fix:DI (fix:DF (match_operand:DF 1 "register_operand" "f")))) + (set (match_operand:SI 0 "register_operand" "=f") + (subreg:SI (match_dup 2) 0))] + "" + " +{ + operands[2] = gen_reg_rtx (DImode); +}") + +;; Recognize the first insn generated above. +;; This RTL looks like a fix_truncdfdi2 insn, +;; but we dont call it that, because only 32 bits +;; of the result are valid. +;; This pattern will work for the intended purposes +;; as long as we do not have any fixdfdi2 or fix_truncdfdi2. +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=f") + (fix:DI (fix:DF (match_operand:DF 1 "register_operand" "f"))))] + "" + "ftrunc.dd %1,%0") + +(define_expand "fix_truncsfsi2" + ;; This first insn produces a double-word value + ;; in which only the low word is valid. + [(set (match_dup 2) + (fix:DI (fix:SF (match_operand:SF 1 "register_operand" "f")))) + (set (match_operand:SI 0 "register_operand" "=f") + (subreg:SI (match_dup 2) 0))] + "" + " +{ + operands[2] = gen_reg_rtx (DImode); +}") + +;; Recognize the first insn generated above. +;; This RTL looks like a fix_truncsfdi2 insn, +;; but we dont call it that, because only 32 bits +;; of the result are valid. +;; This pattern will work for the intended purposes +;; as long as we do not have any fixsfdi2 or fix_truncsfdi2. +(define_insn "" + [(set (match_operand:DI 0 "register_operand" "=f") + (fix:DI (fix:SF (match_operand:SF 1 "register_operand" "f"))))] + "" + "ftrunc.sd %1,%0") + +;;- arithmetic instructions + +(define_insn "addsi3" + [(set (match_operand:SI 0 "register_operand" "=r,*f") + (plus:SI (match_operand:SI 1 "nonmemory_operand" "%r,*f") + (match_operand:SI 2 "arith_operand" "rI,*f")))] + "" + "* +{ + if (which_alternative == 1) + return \"fiadd.ss %2,%1,%0\"; + CC_STATUS_PARTIAL_INIT; + return \"addu %2,%1,%0\"; +}") + +(define_insn "adddi3" + [(set (match_operand:DI 0 "register_operand" "=f") + (plus:DI (match_operand:DI 1 "register_operand" "%f") + (match_operand:DI 2 "register_operand" "f")))] + "" + "fiadd.dd %1,%2,%0") + +(define_insn "subsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r,*f") + (minus:SI (match_operand:SI 1 "register_operand" "r,I,*f") + (match_operand:SI 2 "arith_operand" "rI,r,*f")))] + "" + "* +{ + if (which_alternative == 2) + return \"fisub.ss %1,%2,%0\"; + CC_STATUS_PARTIAL_INIT; + if (REG_P (operands[2])) + return \"subu %1,%2,%0\"; + operands[2] = gen_rtx (CONST_INT, VOIDmode, - INTVAL (operands[2])); + return \"addu %2,%1,%0\"; +}") + +(define_insn "subdi3" + [(set (match_operand:DI 0 "register_operand" "=f") + (minus:DI (match_operand:DI 1 "register_operand" "f") + (match_operand:DI 2 "register_operand" "f")))] + "" + "fisub.dd %1,%2,%0") + +(define_expand "mulsi3" + [(set (subreg:SI (match_dup 4) 0) (match_operand:SI 1 "general_operand" "")) + (set (subreg:SI (match_dup 5) 0) (match_operand:SI 2 "general_operand" "")) + (clobber (match_dup 3)) + (set (subreg:SI (match_dup 3) 0) + (mult:SI (subreg:SI (match_dup 4) 0) (subreg:SI (match_dup 5) 0))) + (set (match_operand:SI 0 "register_operand" "") (subreg:SI (match_dup 3) 0))] + "" + " +{ + if (WORDS_BIG_ENDIAN) + emit_insn (gen_mulsi3_big (operands[0], operands[1], operands[2])); + else + emit_insn (gen_mulsi3_little (operands[0], operands[1], operands[2])); + DONE; +}") + +(define_expand "mulsi3_little" + [(set (subreg:SI (match_dup 4) 0) (match_operand:SI 1 "general_operand" "")) + (set (subreg:SI (match_dup 5) 0) (match_operand:SI 2 "general_operand" "")) + (clobber (match_dup 3)) + (set (subreg:SI (match_dup 3) 0) + (mult:SI (subreg:SI (match_dup 4) 0) (subreg:SI (match_dup 5) 0))) + (set (match_operand:SI 0 "register_operand" "") (subreg:SI (match_dup 3) 0))] + "! WORDS_BIG_ENDIAN" + " +{ + operands[3] = gen_reg_rtx (DImode); + operands[4] = gen_reg_rtx (DImode); + operands[5] = gen_reg_rtx (DImode); +}") + +(define_expand "mulsi3_big" + [(set (subreg:SI (match_dup 4) 1) (match_operand:SI 1 "general_operand" "")) + (set (subreg:SI (match_dup 5) 1) (match_operand:SI 2 "general_operand" "")) + (clobber (match_dup 3)) + (set (subreg:SI (match_dup 3) 1) + (mult:SI (subreg:SI (match_dup 4) 1) (subreg:SI (match_dup 5) 1))) + (set (match_operand:SI 0 "register_operand" "") (subreg:SI (match_dup 3) 1))] + "WORDS_BIG_ENDIAN" + " +{ + operands[3] = gen_reg_rtx (DImode); + operands[4] = gen_reg_rtx (DImode); + operands[5] = gen_reg_rtx (DImode); +}") + +(define_insn "" + [(set (subreg:SI (match_operand:DI 0 "register_operand" "=f") 0) + (mult:SI (subreg:SI (match_operand:DI 1 "register_operand" "f") 0) + (subreg:SI (match_operand:DI 2 "register_operand" "f") 0)))] + "! WORDS_BIG_ENDIAN" + "fmlow.dd %2,%1,%0") + +(define_insn "" + [(set (subreg:SI (match_operand:DI 0 "register_operand" "=f") 1) + (mult:SI (subreg:SI (match_operand:DI 1 "register_operand" "f") 1) + (subreg:SI (match_operand:DI 2 "register_operand" "f") 1)))] + "WORDS_BIG_ENDIAN" + "fmlow.dd %2,%1,%0") + +;;- and instructions (with compliment also) +(define_insn "andsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (and:SI (match_operand:SI 1 "nonmemory_operand" "%r") + (match_operand:SI 2 "nonmemory_operand" "rL")))] + "" + "* +{ + rtx xop[3]; + + CC_STATUS_PARTIAL_INIT; + if (REG_P (operands[2]) || LOGIC_INT (operands[2])) + return \"and %2,%1,%0\"; + if ((INTVAL (operands[2]) & 0xffff) == 0) + { + operands[2] = gen_rtx (CONST_INT, VOIDmode, + (unsigned) INTVAL (operands[2]) >> 16); + return \"andh %2,%1,%0\"; + } + xop[0] = operands[0]; + xop[1] = operands[1]; + xop[2] = gen_rtx (CONST_INT, VOIDmode, ~INTVAL (operands[2]) & 0xffff); + output_asm_insn (\"andnot %2,%1,%0\", xop); + operands[2] = gen_rtx (CONST_INT, VOIDmode, + ~(unsigned) INTVAL (operands[2]) >> 16); + return \"andnoth %2,%0,%0\"; +}") + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (and:SI (not:SI (match_operand:SI 1 "register_operand" "rn")) + (match_operand:SI 2 "register_operand" "r")))] + "" + "* +{ + rtx xop[3]; + + CC_STATUS_PARTIAL_INIT; + if (REG_P (operands[1]) || LOGIC_INT (operands[1])) + return \"andnot %1,%2,%0\"; + if ((INTVAL (operands[1]) & 0xffff) == 0) + { + operands[1] = gen_rtx (CONST_INT, VOIDmode, + (unsigned) INTVAL (operands[1]) >> 16); + return \"andnoth %1,%2,%0\"; + } + xop[0] = operands[0]; + xop[1] = gen_rtx (CONST_INT, VOIDmode, (INTVAL (operands[1]) & 0xffff)); + xop[2] = operands[2]; + output_asm_insn (\"andnot %1,%2,%0\", xop); + operands[1] = gen_rtx (CONST_INT, VOIDmode, + (unsigned) INTVAL (operands[1]) >> 16); + return \"andnoth %1,%0,%0\"; +}") + +(define_insn "iorsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (ior:SI (match_operand:SI 1 "nonmemory_operand" "%r") + (match_operand:SI 2 "nonmemory_operand" "rL")))] + "" + "* +{ + rtx xop[3]; + + CC_STATUS_PARTIAL_INIT; + if (REG_P (operands[2]) || LOGIC_INT (operands[2])) + return \"or %2,%1,%0\"; + if ((INTVAL (operands[2]) & 0xffff) == 0) + { + operands[2] = gen_rtx (CONST_INT, VOIDmode, + (unsigned) INTVAL (operands[2]) >> 16); + return \"orh %2,%1,%0\"; + } + xop[0] = operands[0]; + xop[1] = operands[1]; + xop[2] = gen_rtx (CONST_INT, VOIDmode, (INTVAL (operands[2]) & 0xffff)); + output_asm_insn (\"or %2,%1,%0\", xop); + operands[2] = gen_rtx (CONST_INT, VOIDmode, + (unsigned) INTVAL (operands[2]) >> 16); + return \"orh %2,%0,%0\"; +}") + +(define_insn "xorsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (xor:SI (match_operand:SI 1 "nonmemory_operand" "%r") + (match_operand:SI 2 "nonmemory_operand" "rL")))] + "" + "* +{ + rtx xop[3]; + + CC_STATUS_PARTIAL_INIT; + if (REG_P (operands[2]) || LOGIC_INT (operands[2])) + return \"xor %2,%1,%0\"; + if ((INTVAL (operands[2]) & 0xffff) == 0) + { + operands[2] = gen_rtx (CONST_INT, VOIDmode, + (unsigned) INTVAL (operands[2]) >> 16); + return \"xorh %2,%1,%0\"; + } + xop[0] = operands[0]; + xop[1] = operands[1]; + xop[2] = gen_rtx (CONST_INT, VOIDmode, (INTVAL (operands[2]) & 0xffff)); + output_asm_insn (\"xor %2,%1,%0\", xop); + operands[2] = gen_rtx (CONST_INT, VOIDmode, + (unsigned) INTVAL (operands[2]) >> 16); + return \"xorh %2,%0,%0\"; +}") + +;(The i860 instruction set doesn't allow an immediate second operand in +; a subtraction.) +(define_insn "negsi2" + [(set (match_operand:SI 0 "general_operand" "=r") + (neg:SI (match_operand:SI 1 "arith_operand" "r")))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + return \"subu %?r0,%1,%0\"; +}") + +(define_insn "one_cmplsi2" + [(set (match_operand:SI 0 "general_operand" "=r") + (not:SI (match_operand:SI 1 "arith_operand" "r")))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + return \"subu -1,%1,%0\"; +}") + +;; Floating point arithmetic instructions. + +(define_insn "adddf3" + [(set (match_operand:DF 0 "register_operand" "=f") + (plus:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")))] + "" + "fadd.dd %1,%2,%0") + +(define_insn "addsf3" + [(set (match_operand:SF 0 "register_operand" "=f") + (plus:SF (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f")))] + "" + "fadd.ss %1,%2,%0") + +(define_insn "subdf3" + [(set (match_operand:DF 0 "register_operand" "=f") + (minus:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")))] + "" + "fsub.dd %1,%2,%0") + +(define_insn "subsf3" + [(set (match_operand:SF 0 "register_operand" "=f") + (minus:SF (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f")))] + "" + "fsub.ss %1,%2,%0") + +(define_insn "muldf3" + [(set (match_operand:DF 0 "register_operand" "=f") + (mult:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f")))] + "" + "fmul.dd %1,%2,%0") + +(define_insn "mulsf3" + [(set (match_operand:SF 0 "register_operand" "=f") + (mult:SF (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f")))] + "" + "fmul.ss %1,%2,%0") + +(define_insn "negdf2" + [(set (match_operand:DF 0 "register_operand" "=f") + (neg:DF (match_operand:DF 1 "register_operand" "f")))] + "" + "fsub.dd %?f0,%1,%0") + +(define_insn "negsf2" + [(set (match_operand:SF 0 "register_operand" "=f") + (neg:SF (match_operand:SF 1 "register_operand" "f")))] + "" + "fsub.ss %?f0,%1,%0") + +(define_insn "divdf3" + [(set (match_operand:DF 0 "register_operand" "=&f") + (div:DF (match_operand:DF 1 "register_operand" "f") + (match_operand:DF 2 "register_operand" "f"))) + (clobber (match_scratch:DF 3 "=&f")) + (clobber (match_scratch:DF 4 "=&f"))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + if (((cc_prev_status.flags & CC_KNOW_HI_R31) == 0) + || (cc_prev_status.flags & CC_HI_R31_ADJ) + || (cc_prev_status.mdep != CONST2_RTX (SFmode))) + { + cc_status.flags |= CC_KNOW_HI_R31; + cc_status.flags &= ~CC_HI_R31_ADJ; + cc_status.mdep = CONST2_RTX (SFmode); + return \"frcp.dd %2,%3\;fmul.dd %2,%3,%0\;fmov.dd %?f0,%4\;\\\ +orh 0x4000,%?r0,%?r31\;ixfr %?r31,%R4\;fsub.dd %4,%0,%0\;\\\ +fmul.dd %3,%0,%3\;fmul.dd %2,%3,%0\;fsub.dd %4,%0,%0\;\\\ +fmul.dd %3,%0,%3\;fmul.dd %2,%3,%0\;fsub.dd %4,%0,%0\;\\\ +fmul.dd %3,%1,%3\;fmul.dd %0,%3,%0\"; + } + else + return \"frcp.dd %2,%3\;fmul.dd %2,%3,%0\;fmov.dd %?f0,%4\;\\\ +ixfr %?r31,%R4\;fsub.dd %4,%0,%0\;\\\ +fmul.dd %3,%0,%3\;fmul.dd %2,%3,%0\;fsub.dd %4,%0,%0\;\\\ +fmul.dd %3,%0,%3\;fmul.dd %2,%3,%0\;fsub.dd %4,%0,%0\;\\\ +fmul.dd %3,%1,%3\;fmul.dd %0,%3,%0\"; +}") + +(define_insn "divsf3" + [(set (match_operand:SF 0 "register_operand" "=&f") + (div:SF (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f"))) + (clobber (match_scratch:SF 3 "=&f")) + (clobber (match_scratch:SF 4 "=&f"))] + "" + "* +{ + CC_STATUS_PARTIAL_INIT; + if (((cc_prev_status.flags & CC_KNOW_HI_R31) == 0) + || (cc_prev_status.flags & CC_HI_R31_ADJ) + || (cc_prev_status.mdep != CONST2_RTX (SFmode))) + { + cc_status.flags |= CC_KNOW_HI_R31; + cc_status.flags &= ~CC_HI_R31_ADJ; + cc_status.mdep = CONST2_RTX (SFmode); + output_asm_insn (\"orh 0x4000,%?r0,%?r31\", operands); + } + return \"ixfr %?r31,%4\;frcp.ss %2,%0\;\\\ +fmul.ss %2,%0,%3\;fsub.ss %4,%3,%3\;fmul.ss %0,%3,%0\;\\\ +fmul.ss %2,%0,%3\;fsub.ss %4,%3,%3\;\\\ +fmul.ss %1,%0,%4\;fmul.ss %3,%4,%0\"; +}") + +;; Shift instructions + +;; Optimized special case of shifting. +;; Must precede the general case. + +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (ashiftrt:SI (match_operand:SI 1 "memory_operand" "m") + (const_int 24)))] + "" + "* +{ + if (CONSTANT_ADDRESS_P (XEXP (operands[1], 0))) + { + CC_STATUS_INIT; + cc_status.flags |= CC_KNOW_HI_R31 | CC_HI_R31_ADJ; + cc_status.mdep = XEXP (operands[1], 0); + return \"orh %h1,%?r0,%?r31\;ld.b %L1(%?r31),%0\"; + } + return \"ld.b %1,%0\"; +}") + + +;;- arithmetic shift instructions +(define_insn "ashlsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (ashift:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "shift_operand" "rn")))] + "" + "* +{ + return \"shl %2,%1,%0\"; +}") + +(define_insn "ashlhi3" + [(set (match_operand:HI 0 "register_operand" "=r") + (ashift:HI (match_operand:HI 1 "register_operand" "r") + (match_operand:HI 2 "shift_operand" "rn")))] + "" + "* +{ + return \"shl %2,%1,%0\"; +}") + +(define_insn "ashlqi3" + [(set (match_operand:QI 0 "register_operand" "=r") + (ashift:QI (match_operand:QI 1 "register_operand" "r") + (match_operand:QI 2 "shift_operand" "rn")))] + "" + "* +{ + return \"shl %2,%1,%0\"; +}") + +(define_insn "ashrsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (ashiftrt:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "shift_operand" "rn")))] + "" + "* +{ + return \"shra %2,%1,%0\"; +}") + +(define_insn "lshrsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (lshiftrt:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "shift_operand" "rn")))] + "" + "* +{ + return \"shr %2,%1,%0\"; +}") + +;; Unconditional and other jump instructions + +(define_insn "jump" + [(set (pc) (label_ref (match_operand 0 "" "")))] + "" + "* +{ + return \"br %l0\;nop\"; +}") + +;; Here are two simple peepholes which fill the delay slot of +;; an unconditional branch. + +(define_peephole + [(set (match_operand:SI 0 "register_operand" "=rf") + (match_operand:SI 1 "single_insn_src_p" "gfG")) + (set (pc) (label_ref (match_operand 2 "" "")))] + "" + "* return output_delayed_branch (\"br %l2\", operands, insn);") + +(define_peephole + [(set (match_operand:SI 0 "memory_operand" "=m") + (match_operand:SI 1 "reg_or_0_operand" "rfJ")) + (set (pc) (label_ref (match_operand 2 "" "")))] + "" + "* return output_delayed_branch (\"br %l2\", operands, insn);") + +(define_insn "tablejump" + [(set (pc) (match_operand:SI 0 "register_operand" "r")) + (use (label_ref (match_operand 1 "" "")))] + "" + "bri %0\;nop") + +(define_peephole + [(set (match_operand:SI 0 "memory_operand" "=m") + (match_operand:SI 1 "reg_or_0_operand" "rfJ")) + (set (pc) (match_operand:SI 2 "register_operand" "r")) + (use (label_ref (match_operand 3 "" "")))] + "" + "* return output_delayed_branch (\"bri %2\", operands, insn);") + +;;- jump to subroutine +(define_expand "call" + [(call (match_operand:SI 0 "memory_operand" "m") + (match_operand 1 "" "i"))] + ;; operand[2] is next_arg_register + "" + " +{ + /* Make sure the address is just one reg and will stay that way. */ + if (! call_insn_operand (operands[0], QImode)) + operands[0] + = change_address (operands[0], VOIDmode, + copy_to_mode_reg (Pmode, XEXP (operands[0], 0))); + if (INTVAL (operands[1]) > 0) + { + emit_move_insn (arg_pointer_rtx, stack_pointer_rtx); + emit_insn (gen_rtx (USE, VOIDmode, arg_pointer_rtx)); + } +}") + +;;- jump to subroutine +(define_insn "" + [(call (match_operand:SI 0 "call_insn_operand" "m") + (match_operand 1 "" "i"))] + ;; operand[2] is next_arg_register + "" + "* +{ + /* strip the MEM. */ + operands[0] = XEXP (operands[0], 0); + CC_STATUS_INIT; + if (GET_CODE (operands[0]) == REG) + return \"calli %0\;nop\"; + return \"call %0\;nop\"; +}") + +(define_peephole + [(set (match_operand:SI 0 "register_operand" "=rf") + (match_operand:SI 1 "single_insn_src_p" "gfG")) + (call (match_operand:SI 2 "memory_operand" "m") + (match_operand 3 "" "i"))] + ;;- Don't use operand 1 for most machines. + "! reg_mentioned_p (operands[0], operands[2])" + "* +{ + /* strip the MEM. */ + operands[2] = XEXP (operands[2], 0); + if (GET_CODE (operands[2]) == REG) + return output_delayed_branch (\"calli %2\", operands, insn); + return output_delayed_branch (\"call %2\", operands, insn); +}") + +(define_peephole + [(set (match_operand:SI 0 "memory_operand" "=m") + (match_operand:SI 1 "reg_or_0_operand" "rfJ")) + (call (match_operand:SI 2 "call_insn_operand" "m") + (match_operand 3 "" "i"))] + ;;- Don't use operand 1 for most machines. + "" + "* +{ + /* strip the MEM. */ + operands[2] = XEXP (operands[2], 0); + if (GET_CODE (operands[2]) == REG) + return output_delayed_branch (\"calli %2\", operands, insn); + return output_delayed_branch (\"call %2\", operands, insn); +}") + +(define_expand "call_value" + [(set (match_operand 0 "register_operand" "=rf") + (call (match_operand:SI 1 "memory_operand" "m") + (match_operand 2 "" "i")))] + ;; operand 3 is next_arg_register + "" + " +{ + /* Make sure the address is just one reg and will stay that way. */ + if (! call_insn_operand (operands[1], QImode)) + operands[1] + = change_address (operands[1], VOIDmode, + copy_to_mode_reg (Pmode, XEXP (operands[1], 0))); + if (INTVAL (operands[2]) > 0) + { + emit_move_insn (arg_pointer_rtx, stack_pointer_rtx); + emit_insn (gen_rtx (USE, VOIDmode, arg_pointer_rtx)); + } +}") + +(define_insn "" + [(set (match_operand 0 "register_operand" "=rf") + (call (match_operand:SI 1 "call_insn_operand" "m") + (match_operand 2 "" "i")))] + ;; operand 3 is next_arg_register + "" + "* +{ + /* strip the MEM. */ + operands[1] = XEXP (operands[1], 0); + CC_STATUS_INIT; + if (GET_CODE (operands[1]) == REG) + return \"calli %1\;nop\"; + return \"call %1\;nop\"; +}") + +(define_peephole + [(set (match_operand:SI 0 "register_operand" "=rf") + (match_operand:SI 1 "single_insn_src_p" "gfG")) + (set (match_operand 2 "" "=rf") + (call (match_operand:SI 3 "call_insn_operand" "m") + (match_operand 4 "" "i")))] + ;;- Don't use operand 4 for most machines. + "! reg_mentioned_p (operands[0], operands[3])" + "* +{ + /* strip the MEM. */ + operands[3] = XEXP (operands[3], 0); + if (GET_CODE (operands[3]) == REG) + return output_delayed_branch (\"calli %3\", operands, insn); + return output_delayed_branch (\"call %3\", operands, insn); +}") + +(define_peephole + [(set (match_operand:SI 0 "memory_operand" "=m") + (match_operand:SI 1 "reg_or_0_operand" "rJf")) + (set (match_operand 2 "" "=rf") + (call (match_operand:SI 3 "call_insn_operand" "m") + (match_operand 4 "" "i")))] + ;;- Don't use operand 4 for most machines. + "" + "* +{ + /* strip the MEM. */ + operands[3] = XEXP (operands[3], 0); + if (GET_CODE (operands[3]) == REG) + return output_delayed_branch (\"calli %3\", operands, insn); + return output_delayed_branch (\"call %3\", operands, insn); +}") + +;; Call subroutine returning any type. + +(define_expand "untyped_call" + [(parallel [(call (match_operand 0 "" "") + (const_int 0)) + (match_operand 1 "" "") + (match_operand 2 "" "")])] + "" + " +{ + int i; + + emit_call_insn (gen_call (operands[0], const0_rtx, NULL, const0_rtx)); + + for (i = 0; i < XVECLEN (operands[2], 0); i++) + { + rtx set = XVECEXP (operands[2], 0, i); + emit_move_insn (SET_DEST (set), SET_SRC (set)); + } + + /* The optimizer does not know that the call sets the function value + registers we stored in the result block. We avoid problems by + claiming that all hard registers are used and clobbered at this + point. */ + emit_insn (gen_blockage ()); + + DONE; +}") + +;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and +;; all of memory. This blocks insns from being moved across this point. + +(define_insn "blockage" + [(unspec_volatile [(const_int 0)] 0)] + "" + "") + +(define_insn "nop" + [(const_int 0)] + "" + "nop") + +(define_insn "indirect_jump" + [(set (pc) (match_operand:SI 0 "register_operand" "r"))] + "" + "bri %0") + +;; +;; A special insn that does the work to get setup just +;; before a table jump. +;; +(define_insn "" + [(set (match_operand:SI 0 "register_operand" "=r") + (mem:SI (plus:SI (match_operand:SI 1 "register_operand" "r") + (label_ref (match_operand 2 "" "")))))] + "" + "* +{ + CC_STATUS_INIT; + return \"orh %H2,%?r0,%?r31\;or %L2,%?r31,%?r31\;ld.l %?r31(%1),%0\"; +}") + +(define_peephole + [(set (match_operand:SI 0 "register_operand" "=rf") + (match_operand:SI 1 "single_insn_src_p" "gfG")) + (set (pc) (match_operand:SI 2 "register_operand" "r")) + (use (label_ref (match_operand 3 "" "")))] + "REGNO (operands[0]) != REGNO (operands[2])" + "* return output_delayed_branch (\"bri %2\", operands, insn);") diff --git a/gnu/usr.bin/gcc/config/i860/mach.h b/gnu/usr.bin/gcc/config/i860/mach.h new file mode 100644 index 00000000000..7a4f5230974 --- /dev/null +++ b/gnu/usr.bin/gcc/config/i860/mach.h @@ -0,0 +1,16 @@ +/* Configuration for an i860 running Mach as the target machine. */ + +#include "i860/i860.h" + +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (i860 Mach3.x)"); + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dunix -Di860 -DMACH -Asystem(unix) -Asystem(mach) -Acpu(i860) -Amachine(i860)" + +/* Specify extra dir to search for include files. */ +#define SYSTEM_INCLUDE_DIR "/usr/mach/include" + +/* Don't default to pcc-struct-return, because gcc is the only compiler, and + we want to retain compatibility with older gcc versions. */ +#define DEFAULT_PCC_STRUCT_RETURN 0 diff --git a/gnu/usr.bin/gcc/config/i860/paragon.h b/gnu/usr.bin/gcc/config/i860/paragon.h new file mode 100644 index 00000000000..802d4fdcb76 --- /dev/null +++ b/gnu/usr.bin/gcc/config/i860/paragon.h @@ -0,0 +1,243 @@ +/* Target definitions for GNU compiler for Intel 80860 running OSF/1AD + Copyright (C) 1991 Free Software Foundation, Inc. + + Based upon original work of Ron Guilmette (rfg@netcom.com). + Whacked into submission by Andy Pfiffer (andyp@ssd.intel.com). + Partially inspired by + Pete Beckman @ Indiana University (beckman@cs.indiana.edu) + Harry Dolan of Intel Corporation (dolan@ssd.intel.com) + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#include "i860/i860.h" +#include "svr3.h" + +/* For the sake of libgcc2.c, indicate target supports atexit. */ +#define HAVE_ATEXIT + +#undef TARGET_SWITCHES +#define TARGET_SWITCHES \ + { {"xp", 1}, \ + {"noxp", -1}, \ + {"xr", -1}, \ + {"noieee", -1}, \ + {"nx", 2}, \ + { "", TARGET_DEFAULT}} + +#undef TARGET_DEFAULT +#define TARGET_DEFAULT 1 + +/* The Intel as860 assembler does not understand .stabs, must use COFF */ +#define PREFERRED_DEBUGGING_TYPE SDB_DEBUG + +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (i860 OSF/1AD)"); + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Di860 -D__i860 -D__i860__ -D__PARAGON__ -D__OSF1__ -D_COFF -Dunix -DMACH -DCMU" + +#define CPP_SPEC "%{mnx:-D__NODE}" + +/* autoinit.o autolaunches NX applications */ +#define STARTFILE_SPEC "-ycrt0.o%s %{mnx:-yoptions/autoinit.o%s}" + +/* libic.a is the PGI intrinsic library */ +/* libpm.o and guard.o are for the performance monitoring modules (ignored) */ +/* /usr/lib/noieee contains non-IEEE compliant (but faster) math routines */ +#if HAVE_DASH_G +#define LIB_SPEC "%{mnoieee:-L/usr/lib/noieee} -L/usr/lib %{mnx:-lnx -lmach} % +{g*:-lg} -lc -lic" +#else /* HAVE_DASH_G */ +/* can't use -g for -lg; libg.a doesn't have a symbol table and ld complains */ +#define LIB_SPEC "%{mnoieee:-L/usr/lib/noieee} -L/usr/lib %{mnx:-lnx -lmach} -lc -lic" +#endif /* HAVE_DASH_G */ + +/* Get rid of definition from svr3.h. */ +#undef SIZE_TYPE + +#undef I860_REG_PREFIX + +#undef ASM_COMMENT_START +#define ASM_COMMENT_START "//" + +#undef TYPE_OPERAND_FMT +#define TYPE_OPERAND_FMT "\"%s\"" + +#define DBX_REGISTER_NUMBER(REGNO) (REGNO) + +#undef ASCII_DATA_ASM_OP +#define ASCII_DATA_ASM_OP ".byte" + +/* + * the assembler we're using doesn't grok .ident... + */ +#undef ASM_OUTPUT_IDENT +#define ASM_OUTPUT_IDENT(FILE, NAME) \ + fprintf (FILE, "//\t.ident \"%s\"\n", NAME); + +/* + * the assembler doesn't grok .double INF and the like + * but does understand .long with hex numbers, so special + * case the "symbolic" IEEE numbers. + */ +#undef ASM_OUTPUT_DOUBLE +#define ASM_OUTPUT_DOUBLE(FILE,VALUE) \ + { \ + if (REAL_VALUE_ISINF (VALUE) \ + || REAL_VALUE_ISNAN (VALUE) \ + || REAL_VALUE_MINUS_ZERO (VALUE)) \ + { \ + long t[2]; \ + REAL_VALUE_TO_TARGET_DOUBLE ((VALUE), t); \ + fprintf (FILE, "\t.long 0x%lx\n\t.long 0x%lx\n", t[0], t[1]); \ + } \ + else \ + fprintf (FILE, "\t.double %.20e\n", VALUE); \ + } + +#undef ASM_OUTPUT_FLOAT +#define ASM_OUTPUT_FLOAT(FILE,VALUE) \ + { \ + if (REAL_VALUE_ISINF (VALUE) \ + || REAL_VALUE_ISNAN (VALUE) \ + || REAL_VALUE_MINUS_ZERO (VALUE)) \ + { \ + long t; \ + REAL_VALUE_TO_TARGET_SINGLE ((VALUE), t); \ + fprintf (FILE, "\t.long 0x%lx\n", t); \ + } \ + else \ + fprintf (FILE, "\t.float %.12e\n", VALUE); \ + } + +#undef ASM_OUTPUT_ASCII +#define ASM_OUTPUT_ASCII(FILE, STR, LENGTH) \ + do \ + { \ + register unsigned char *str = (unsigned char *) (STR); \ + register unsigned char *limit = str + (LENGTH); \ + register unsigned bytes_in_chunk = 0; \ + for (; str < limit; str++) \ + { \ + register unsigned ch = *str; \ + if (ch < 32 || ch == '\\' || ch == '"' || ch >= 127) \ + { \ + if (bytes_in_chunk > 0) \ + { \ + fprintf ((FILE), "\"\n"); \ + bytes_in_chunk = 0; \ + } \ + fprintf ((FILE), "\t%s\t%d\n", ASM_BYTE_OP, ch); \ + } \ + else \ + { \ + if (bytes_in_chunk >= 60) \ + { \ + fprintf ((FILE), "\"\n"); \ + bytes_in_chunk = 0; \ + } \ + if (bytes_in_chunk == 0) \ + fprintf ((FILE), "\t%s\t\"", ASCII_DATA_ASM_OP);\ + putc (ch, (FILE)); \ + bytes_in_chunk++; \ + } \ + } \ + if (bytes_in_chunk > 0) \ + fprintf ((FILE), "\"\n"); \ + } \ + while (0) + + +/* This says how to output an assembler line + to define a local common symbol. */ + +#undef ASM_OUTPUT_LOCAL +#define ASM_OUTPUT_LOCAL(FILE, NAME, SIZE, ROUNDED) \ +( fputs (".lcomm ", (FILE)), \ + assemble_name ((FILE), (NAME)), \ + fprintf ((FILE), ",%u\n", (ROUNDED))) + +/* + * not defining ASM_STABS_OP yields .stabs in the .s file + * when using g++ -- so, I'll define it. + */ +#define ASM_STABS_OP "//.stabs" + +/* Define this macro if an argument declared as `char' or `short' in a + prototype should actually be passed as an `int'. In addition to + avoiding errors in certain cases of mismatch, it also makes for + better code on certain machines. */ +/*#define PROMOTE_PROTOTYPES*/ + +/* Define this macro if an instruction to load a value narrower + than a word from memory into a register also zero-extends the + value to the whole register. */ +/*#define BYTE_LOADS_ZERO_EXTEND*/ + +/* Define this macro as a C expression which is nonzero if + accessing less than a word of memory (i.e. a `char' or a + `short') is no faster than accessing a word of memory, i.e., if + such access require more than one instruction or if there is no + difference in cost between byte and (aligned) word loads. + + On RISC machines, it tends to generate better code to define + this as 1, since it avoids making a QI or HI mode register. */ +/* +#undef SLOW_BYTE_ACCESS +#define SLOW_BYTE_ACCESS 1 +*/ + +/* Define if shifts truncate the shift count + which implies one can omit a sign-extension or zero-extension + of a shift count. */ +#define SHIFT_COUNT_TRUNCATED 1 + + +#define FASTEST_ALIGNMENT 32 + +/* Make strings word-aligned so strcpy from constants will be faster. */ +#define CONSTANT_ALIGNMENT(EXP, ALIGN) \ + (TREE_CODE (EXP) == STRING_CST \ + && (ALIGN) < FASTEST_ALIGNMENT ? FASTEST_ALIGNMENT : (ALIGN)) + +/* Make arrays of chars word-aligned for the same reasons. */ +#define DATA_ALIGNMENT(TYPE, ALIGN) \ + (TREE_CODE (TYPE) == ARRAY_TYPE \ + && TYPE_MODE (TREE_TYPE (TYPE)) == QImode \ + && (ALIGN) < FASTEST_ALIGNMENT ? FASTEST_ALIGNMENT : (ALIGN)) + +/* + * disable a few things picked up from svr3.h + */ +#undef INIT_SECTION_ASM_OP +#undef FINI_SECTION_ASM_OP +#undef CONST_SECTION_ASM_OP +#undef CTORS_SECTION_ASM_OP +#undef DTORS_SECTION_ASM_OP +#undef DO_GLOBAL_CTORS_BODY +#undef ASM_OUTPUT_DESTRUCTOR +#undef SELECT_SECTION +#undef SELECT_RTX_SECTION +#undef READONLY_DATA_SECTION + +#define BSS_SECTION_ASM_OP ".bss" /* XXX */ +#undef EXTRA_SECTIONS +#define EXTRA_SECTIONS in_bss +#undef EXTRA_SECTION_FUNCTIONS +#define EXTRA_SECTION_FUNCTIONS \ + BSS_SECTION_FUNCTION diff --git a/gnu/usr.bin/gcc/config/i860/sysv3.h b/gnu/usr.bin/gcc/config/i860/sysv3.h new file mode 100644 index 00000000000..6a7c6a5b66e --- /dev/null +++ b/gnu/usr.bin/gcc/config/i860/sysv3.h @@ -0,0 +1,178 @@ +/* Target definitions for GNU compiler for Intel 80860 running System V.3 + Copyright (C) 1991 Free Software Foundation, Inc. + + Written by Ron Guilmette (rfg@netcom.com). + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#include "i860/i860.h" +#include "svr3.h" + +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (i860, System V Release 3)") + +/* Provide a set of pre-definitions and pre-assertions appropriate for + the i860 running svr3. */ +#define CPP_PREDEFINES "-Di860 -Dunix -D__svr3__ -Asystem(unix) -Asystem(svr3) -Acpu(i860) -Amachine(i860)" + +/* Use crt1.o as a startup file and crtn.o as a closing file. */ + +#define STARTFILE_SPEC \ + "%{pg:gcrt1.o%s}%{!pg:%{p:mcrt1.o%s}%{!p:crt1.o%s}}" + +#define LIB_SPEC "%{p:-L/usr/lib/libp}%{pg:-L/usr/lib/libp} -lc crtn.o%s" + +/* Special flags for the linker. I don't know what they do. */ + +#define LINK_SPEC "%{T*} %{z:-lm}" + +/* The prefix to be used in assembler output for all names of registers. + None is needed in V.3. */ + +#define I860_REG_PREFIX "" + +/* Delimiter that starts comments in the assembler code. */ + +#define ASM_COMMENT_START "//" + +/* Don't renumber the regusters for debugger output. */ + +#define DBX_REGISTER_NUMBER(REGNO) (REGNO) + +/* Output the special word the System V SDB wants to see just before + the first word of each function's prologue code. */ + +extern char *current_function_original_name; + +/* This special macro is used to output a magic word just before the + first word of each function. On some versions of UNIX running on + the i860, this word can be any word that looks like a NOP, however + under svr4, this neds to be an `shr r0,r0,r0' instruction in which + the normally unused low-order bits contain the length of the function + prologue code (in bytes). This is needed to make the System V SDB + debugger happy. */ + +#undef ASM_OUTPUT_FUNCTION_PREFIX +#define ASM_OUTPUT_FUNCTION_PREFIX(FILE, FNNAME) \ + do { ASM_OUTPUT_ALIGN (FILE, 2); \ + fprintf ((FILE), "\t.long\t.ep."); \ + assemble_name (FILE, FNNAME); \ + fprintf (FILE, "-"); \ + assemble_name (FILE, FNNAME); \ + fprintf (FILE, "+0xc8000000\n"); \ + current_function_original_name = (FNNAME); \ + } while (0) + +/* Output the special label that must go just after each function's + prologue code to support svr4 SDB. */ + +#define ASM_OUTPUT_PROLOGUE_SUFFIX(FILE) \ + do { fprintf (FILE, ".ep."); \ + assemble_name (FILE, current_function_original_name); \ + fprintf (FILE, ":\n"); \ + } while (0) + +/* This says how to output an assembler line + to define a local common symbol. + The difference from svr3.h is we don't limit align to 2. */ + +#undef ASM_OUTPUT_LOCAL +#define ASM_OUTPUT_LOCAL(FILE, NAME, SIZE, ROUNDED) \ + do { \ + int align = exact_log2 (ROUNDED); \ + data_section (); \ + ASM_OUTPUT_ALIGN ((FILE), align == -1 ? 2 : align); \ + ASM_OUTPUT_LABEL ((FILE), (NAME)); \ + fprintf ((FILE), "\t.set .,.+%u\n", (ROUNDED)); \ + } while (0) + +/* The routine used to output string literals. + +#define ASCII_DATA_ASM_OP ".byte" + +#define ASM_OUTPUT_ASCII(FILE, STR, LENGTH) \ + do \ + { \ + register unsigned char *str = (unsigned char *) (STR); \ + register unsigned char *limit = str + (LENGTH); \ + register unsigned bytes_in_chunk = 0; \ + for (; str < limit; str++) \ + { \ + register unsigned ch = *str; \ + if (ch < 32 || ch == '\\' || ch == '"' || ch >= 127) \ + { \ + if (bytes_in_chunk > 0) \ + { \ + fprintf ((FILE), "\"\n"); \ + bytes_in_chunk = 0; \ + } \ + fprintf ((FILE), "\t%s\t%d\n", ASM_BYTE_OP, ch); \ + } \ + else \ + { \ + if (bytes_in_chunk >= 60) \ + { \ + fprintf ((FILE), "\"\n"); \ + bytes_in_chunk = 0; \ + } \ + if (bytes_in_chunk == 0) \ + fprintf ((FILE), "\t%s\t\"", ASCII_DATA_ASM_OP); \ + putc (ch, (FILE)); \ + bytes_in_chunk++; \ + } \ + } \ + if (bytes_in_chunk > 0) \ + fprintf ((FILE), "\"\n"); \ + } \ + while (0) + + +#undef CTORS_SECTION_ASM_OP +#define CTORS_SECTION_ASM_OP ".section\t.ctors,\"x\"" +#undef DTORS_SECTION_ASM_OP +#define DTORS_SECTION_ASM_OP ".section\t.dtors,\"x\"" + +/* Add definitions to support the .tdesc section as specified in the svr4 + ABI for the i860. */ + +#define TDESC_SECTION_ASM_OP ".section\t.tdesc" + +#undef EXTRA_SECTIONS +#define EXTRA_SECTIONS in_const, in_ctors, in_dtors, in_tdesc + +#undef EXTRA_SECTION_FUNCTIONS +#define EXTRA_SECTION_FUNCTIONS \ + CONST_SECTION_FUNCTION \ + CTORS_SECTION_FUNCTION \ + DTORS_SECTION_FUNCTION \ + TDESC_SECTION_FUNCTION + +#define TDESC_SECTION_FUNCTION \ +void \ +tdesc_section () \ +{ \ + if (in_section != in_tdesc) \ + { \ + fprintf (asm_out_file, "%s\n", TDESC_SECTION_ASM_OP); \ + in_section = in_tdesc; \ + } \ +} + +/* Enable the `const' section that svr3.h defines how to use. */ +#undef USE_CONST_SECTION +#define USE_CONST_SECTION 1 diff --git a/gnu/usr.bin/gcc/config/i860/sysv4.h b/gnu/usr.bin/gcc/config/i860/sysv4.h new file mode 100644 index 00000000000..d8b0a8eafeb --- /dev/null +++ b/gnu/usr.bin/gcc/config/i860/sysv4.h @@ -0,0 +1,212 @@ +/* Target definitions for GNU compiler for Intel 80860 running System V.4 + Copyright (C) 1991 Free Software Foundation, Inc. + + Written by Ron Guilmette (rfg@netcom.com). + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#include "i860/i860.h" +#include "svr4.h" + +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (i860 System V Release 4)"); + +/* Provide a set of pre-definitions and pre-assertions appropriate for + the i860 running svr4. Note that the symbol `__svr4__' MUST BE + DEFINED! It is needed so that the va_list struct in va-i860.h + will get correctly defined for the svr4 (ABI compliant) case rather + than for the previous (svr3, svr2, ...) case. It also needs to be + defined so that the correct (svr4) version of __builtin_saveregs + will be selected when we are building gnulib2.c. + __svr4__ is our extension. */ + +#define CPP_PREDEFINES \ + "-Di860 -Dunix -DSVR4 -D__svr4__ -Asystem(unix) -Asystem(svr4) -Acpu(i860) -Amachine(i860)" + +/* The prefix to be used in assembler output for all names of registers. + This string gets prepended to all i860 register names (svr4 only). */ + +#define I860_REG_PREFIX "%" + +#define ASM_COMMENT_START "#" + +#undef TYPE_OPERAND_FMT +#define TYPE_OPERAND_FMT "\"%s\"" + +#define DBX_REGISTER_NUMBER(REGNO) (REGNO) + +/* The following macro definition overrides the one in i860.h + because the svr4 i860 assembler requires a different syntax + for getting parts of constant/relocatable values. */ + +#undef PRINT_OPERAND_PART +#define PRINT_OPERAND_PART(FILE, X, PART_CODE) \ + do { fprintf (FILE, "["); \ + output_address (X); \ + fprintf (FILE, "]@%s", PART_CODE); \ + } while (0) + +/* If the host and target formats match, output the floats as hex. */ +#if HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT +#if defined (HOST_WORDS_BIG_ENDIAN) == WORDS_BIG_ENDIAN + +/* This is how to output an assembler line defining a `double' constant. + Note that the native i860/svr4 ELF assembler can't properly handle + infinity. It generates an incorrect (non-infinity) value when given + `.double 99e9999' and it doesn't grok `inf' at all. It also mishandles + NaNs and -0.0. */ + +#undef ASM_OUTPUT_DOUBLE +#define ASM_OUTPUT_DOUBLE(FILE,VALUE) \ + { \ + if (REAL_VALUE_ISINF (VALUE) \ + || REAL_VALUE_ISNAN (VALUE) \ + || REAL_VALUE_MINUS_ZERO (VALUE)) \ + { \ + long t[2]; \ + REAL_VALUE_TO_TARGET_DOUBLE ((VALUE), t); \ + fprintf (FILE, "\t.word 0x%lx\n\t.word 0x%lx\n", t[0], t[1]); \ + } \ + else \ + fprintf (FILE, "\t.double %.20e\n", VALUE); \ + } + +/* This is how to output an assembler line defining a `float' constant. + Note that the native i860/svr4 ELF assembler can't properly handle + infinity. It actually generates an assembly time error when given + `.float 99e9999' and it doesn't grok `inf' at all. It also mishandles + NaNs and -0.0. */ + +#undef ASM_OUTPUT_FLOAT +#define ASM_OUTPUT_FLOAT(FILE,VALUE) \ + { \ + if (REAL_VALUE_ISINF (VALUE) \ + || REAL_VALUE_ISNAN (VALUE) \ + || REAL_VALUE_MINUS_ZERO (VALUE)) \ + { \ + long t; \ + REAL_VALUE_TO_TARGET_SINGLE ((VALUE), t); \ + fprintf (FILE, "\t.word 0x%lx\n", t); \ + } \ + else \ + fprintf (FILE, "\t.float %.12e\n", VALUE); \ + } + +#endif /* word order matches */ +#endif /* HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT */ + +#undef ASM_FILE_START +#define ASM_FILE_START(FILE) \ + do { output_file_directive (FILE, main_input_filename); \ + fprintf (FILE, "\t.version\t\"01.01\"\n"); \ + } while (0) + +/* Output the special word the svr4 SDB wants to see just before + the first word of each function's prologue code. */ + +extern char *current_function_original_name; + +/* This special macro is used to output a magic word just before the + first word of each function. On some versions of UNIX running on + the i860, this word can be any word that looks like a NOP, however + under svr4, this neds to be an `shr r0,r0,r0' instruction in which + the normally unused low-order bits contain the length of the function + prologue code (in bytes). This is needed to make the svr4 SDB debugger + happy. */ + +#undef ASM_OUTPUT_FUNCTION_PREFIX +#define ASM_OUTPUT_FUNCTION_PREFIX(FILE, FNNAME) \ + do { ASM_OUTPUT_ALIGN (FILE, 2); \ + fprintf ((FILE), "\t.long\t.ep."); \ + assemble_name (FILE, FNNAME); \ + fprintf (FILE, "-"); \ + assemble_name (FILE, FNNAME); \ + fprintf (FILE, "+0xc8000000\n"); \ + current_function_original_name = (FNNAME); \ + } while (0) + +/* Output the special label that must go just after each function's + prologue code to support svr4 SDB. */ + +#define ASM_OUTPUT_PROLOGUE_SUFFIX(FILE) \ + do { fprintf (FILE, ".ep."); \ + assemble_name (FILE, current_function_original_name); \ + fprintf (FILE, ":\n"); \ + } while (0) + +/* Define the pseudo-ops used to switch to the .ctors and .dtors sections. + + Note that we want to give these sections the SHF_WRITE attribute + because these sections will actually contain data (i.e. tables of + addresses of functions in the current root executable or shared library + file) and, in the case of a shared library, the relocatable addresses + will have to be properly resolved/relocated (and then written into) by + the dynamic linker when it actually attaches the given shared library + to the executing process. (Note that on SVR4, you may wish to use the + `-z text' option to the ELF linker, when building a shared library, as + an additional check that you are doing everything right. But if you do + use the `-z text' option when building a shared library, you will get + errors unless the .ctors and .dtors sections are marked as writable + via the SHF_WRITE attribute.) */ + +#undef CTORS_SECTION_ASM_OP +#define CTORS_SECTION_ASM_OP ".section\t.ctors,\"aw\"" +#undef DTORS_SECTION_ASM_OP +#define DTORS_SECTION_ASM_OP ".section\t.dtors,\"aw\"" + +/* Add definitions to support the .tdesc section as specified in the svr4 + ABI for the i860. */ + +#define TDESC_SECTION_ASM_OP ".section\t.tdesc" + +#undef EXTRA_SECTIONS +#define EXTRA_SECTIONS in_const, in_ctors, in_dtors, in_tdesc + +#undef EXTRA_SECTION_FUNCTIONS +#define EXTRA_SECTION_FUNCTIONS \ + CONST_SECTION_FUNCTION \ + CTORS_SECTION_FUNCTION \ + DTORS_SECTION_FUNCTION \ + TDESC_SECTION_FUNCTION + +#define TDESC_SECTION_FUNCTION \ +void \ +tdesc_section () \ +{ \ + if (in_section != in_tdesc) \ + { \ + fprintf (asm_out_file, "%s\n", TDESC_SECTION_ASM_OP); \ + in_section = in_tdesc; \ + } \ +} + +#ifdef OUTPUT_TDESC +#undef ASM_FILE_END +#define ASM_FILE_END(FILE) \ +do { \ + if (current_function_original_name != NULL) { \ + tdesc_section(); \ + fprintf ((FILE), "%s __ETEXT\n", ASM_LONG); \ + fprintf ((FILE), "%s 0\n", ASM_LONG); \ + text_section(); \ + fputs("__ETEXT:\n", (FILE)); \ + } \ + fprintf ((FILE), "\t.ident\t\"GCC: (GNU) %s\"\n", \ + version_string); \ + } while (0) +#endif diff --git a/gnu/usr.bin/gcc/config/i860/t-fx2800 b/gnu/usr.bin/gcc/config/i860/t-fx2800 new file mode 100644 index 00000000000..5f081735714 --- /dev/null +++ b/gnu/usr.bin/gcc/config/i860/t-fx2800 @@ -0,0 +1,3 @@ +# Use ieee rounding rules for divide and square root operations. +# /bin/cc is assumed to point to fxc version 1.3 or newer. +CCLIBFLAGS=-O -ieee -uniproc diff --git a/gnu/usr.bin/gcc/config/i860/x-fx2800 b/gnu/usr.bin/gcc/config/i860/x-fx2800 new file mode 100644 index 00000000000..cf05f8f43bd --- /dev/null +++ b/gnu/usr.bin/gcc/config/i860/x-fx2800 @@ -0,0 +1,7 @@ + +# The Alliant FX2800 is supposed to conform to the SysV R4 i860 ABI, +# but apparently the native compiler generates non ABI-compliant +# function prologues and epilogues, so we need the alloca from emacs. +# -- hyc@hanauma.jpl.nasa.gov + +ALLOCA=alloca.o diff --git a/gnu/usr.bin/gcc/config/i860/x-sysv3 b/gnu/usr.bin/gcc/config/i860/x-sysv3 new file mode 100644 index 00000000000..3375d421849 --- /dev/null +++ b/gnu/usr.bin/gcc/config/i860/x-sysv3 @@ -0,0 +1 @@ +ALLOCA=alloca.o diff --git a/gnu/usr.bin/gcc/config/i860/x-sysv4 b/gnu/usr.bin/gcc/config/i860/x-sysv4 new file mode 100644 index 00000000000..c01fb887dca --- /dev/null +++ b/gnu/usr.bin/gcc/config/i860/x-sysv4 @@ -0,0 +1,44 @@ +# The svr4 reference port for the i860 contains an alloca.o routine +# in /usr/ucblib/libucb.a, but we can't just try to get that by +# setting CLIB to /usr/ucblib/libucb.a because (unfortunately) +# there are a lot of other routines in libucb.a which are supposed +# to be the Berkeley versions of library routines normally found in +# libc.a and many of these Berkeley versions are badly broken. Thus, +# if we try to link programs with libucb.a before libc.a, those +# programs tend to crash. + +# Also, the alloca() routine supplied in early version of svr4 for +# the i860 is non-ABI compliant. It doesn't keep the stack aligned +# to a 16-byte boundary as the ABI requires. + +# More importantly however, even a fully ABI compliant alloca() routine +# would fail to work correctly with some versions of the native svr4 C +# compiler currently being distributed for the i860 (as of 1/29/92). +# The problem is that the native C compiler generates non-ABI-compliant +# function epilogues which cut back the stack (upon function exit) in +# an incorrect manner. Specifically, they cut back the stack by adding +# the nominal *static* frame size (determined statically at compile-time) +# to the stack pointer rather than setting the stack pointer based upon +# the current value of the frame pointer (as called for in the i860 ABI). +# This can cause serious trouble in cases where you repeatedly call a +# routine which itself calls alloca(). In such cases, the stack will +# grow continuously until you finally run out of swap space or exceed +# the system's process size limit. To avoid this problem (which can +# arise when a stage1 gcc is being used to build a stage2 gcc) you +# *must* link in the C language version of alloca() which is supplied +# with gcc to your stage1 version of gcc. The following definition +# forces that to happen. + +ALLOCA=alloca.o + +# We build all stages *without* shared libraries because that may make +# debugging the compiler easier (until there is a GDB which supports +# both Dwarf *and* svr4 shared libraries). + +# Note that the native C compiler for the svr4 reference port on the +# i860 recognizes a special -gg option. Using that option causes *full* +# Dwarf debugging information to be generated, whereas using only -g +# causes only limited Dwarf debugging information to be generated. +# (This is an undocumented feature of the native svr4 C compiler.) + +CCLIBFLAGS=-Bstatic -dn -gg diff --git a/gnu/usr.bin/gcc/config/i860/xm-fx2800.h b/gnu/usr.bin/gcc/config/i860/xm-fx2800.h new file mode 100644 index 00000000000..5d0870e9f58 --- /dev/null +++ b/gnu/usr.bin/gcc/config/i860/xm-fx2800.h @@ -0,0 +1,12 @@ +/* Alliant FX/2800 running Concentrix 2.x. */ + +#include "i860/xm-i860.h" + +/* + * vfprintf is not present prior to Concentrix 2.2. Unfortunately, there + * does not seem to be a cpp symbol that identifies OS revision. Undefine + * the following if running 2.1 or older. + * -- hyc@hanauma.jpl.nasa.gov + */ + +#define HAVE_VPRINTF diff --git a/gnu/usr.bin/gcc/config/i860/xm-i860.h b/gnu/usr.bin/gcc/config/i860/xm-i860.h new file mode 100644 index 00000000000..4e8488f1f10 --- /dev/null +++ b/gnu/usr.bin/gcc/config/i860/xm-i860.h @@ -0,0 +1,43 @@ +/* Configuration for GNU C-compiler for Intel i860. + Copyright (C) 1988, 1993 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 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#ifndef i860 +#define i860 +#endif + +/* #defines that need visibility everywhere. */ +#define FALSE 0 +#define TRUE 1 + +/* This describes the machine the compiler is hosted on. */ +#define HOST_BITS_PER_CHAR 8 +#define HOST_BITS_PER_SHORT 16 +#define HOST_BITS_PER_INT 32 +#define HOST_BITS_PER_LONG 32 +#define HOST_BITS_PER_LONGLONG 64 + +/* Arguments to use with `exit'. */ +#define SUCCESS_EXIT_CODE 0 +#define FATAL_EXIT_CODE 33 + +/* target machine dependencies. + tm.h is a symbolic link to the actual target specific file. */ + +#include "tm.h" diff --git a/gnu/usr.bin/gcc/config/i860/xm-paragon.h b/gnu/usr.bin/gcc/config/i860/xm-paragon.h new file mode 100644 index 00000000000..484cd13c329 --- /dev/null +++ b/gnu/usr.bin/gcc/config/i860/xm-paragon.h @@ -0,0 +1,4 @@ +/* Configuration for GCC for Intel i860 running OSF/1AD. */ + +#include "i860/xm-i860.h" +#include "xm-svr3.h" diff --git a/gnu/usr.bin/gcc/config/i860/xm-sysv3.h b/gnu/usr.bin/gcc/config/i860/xm-sysv3.h new file mode 100644 index 00000000000..ebd058c0473 --- /dev/null +++ b/gnu/usr.bin/gcc/config/i860/xm-sysv3.h @@ -0,0 +1,4 @@ +/* Configuration for GCC for Intel i860 running System V Release 3. */ + +#include "i860/xm-i860.h" +#include "xm-svr3.h" diff --git a/gnu/usr.bin/gcc/config/i860/xm-sysv4.h b/gnu/usr.bin/gcc/config/i860/xm-sysv4.h new file mode 100644 index 00000000000..3e5705ea2be --- /dev/null +++ b/gnu/usr.bin/gcc/config/i860/xm-sysv4.h @@ -0,0 +1,4 @@ +/* Configuration for GCC for Intel i860 running System V Release 4. */ + +#include "i860/xm-i860.h" +#include "xm-svr4.h" |