diff options
Diffstat (limited to 'gcc/config/ns32k')
-rwxr-xr-x | gcc/config/ns32k/encore.h | 196 | ||||
-rwxr-xr-x | gcc/config/ns32k/genix.h | 167 | ||||
-rwxr-xr-x | gcc/config/ns32k/merlin.h | 231 | ||||
-rwxr-xr-x | gcc/config/ns32k/netbsd.h | 113 | ||||
-rwxr-xr-x | gcc/config/ns32k/ns32k.c | 1196 | ||||
-rwxr-xr-x | gcc/config/ns32k/ns32k.h | 1722 | ||||
-rwxr-xr-x | gcc/config/ns32k/ns32k.md | 3058 | ||||
-rwxr-xr-x | gcc/config/ns32k/pc532-mach.h | 30 | ||||
-rwxr-xr-x | gcc/config/ns32k/pc532-min.h | 41 | ||||
-rwxr-xr-x | gcc/config/ns32k/pc532.h | 73 | ||||
-rwxr-xr-x | gcc/config/ns32k/sequent.h | 77 | ||||
-rwxr-xr-x | gcc/config/ns32k/tek6000.h | 237 | ||||
-rwxr-xr-x | gcc/config/ns32k/tek6100.h | 7 | ||||
-rwxr-xr-x | gcc/config/ns32k/tek6200.h | 7 | ||||
-rwxr-xr-x | gcc/config/ns32k/x-genix | 6 | ||||
-rwxr-xr-x | gcc/config/ns32k/xm-genix.h | 5 | ||||
-rwxr-xr-x | gcc/config/ns32k/xm-netbsd.h | 8 | ||||
-rwxr-xr-x | gcc/config/ns32k/xm-ns32k.h | 42 | ||||
-rwxr-xr-x | gcc/config/ns32k/xm-pc532-min.h | 7 |
19 files changed, 7223 insertions, 0 deletions
diff --git a/gcc/config/ns32k/encore.h b/gcc/config/ns32k/encore.h new file mode 100755 index 0000000..31e2894 --- /dev/null +++ b/gcc/config/ns32k/encore.h @@ -0,0 +1,196 @@ +/* Definitions of target machine for GNU compiler. ENCORE NS32000 version. + Copyright (C) 1988, 1993 Free Software Foundation, Inc. + Adapted by Robert Brown (brown@harvard.harvard.edu) from the Sequent + version by Michael Tiemann (tiemann@mcc.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. */ + + +#define EXTERNAL_PREFIX '?' +#define IMMEDIATE_PREFIX '$' + +#include "ns32k/ns32k.h" + +#define SDB_DEBUGGING_INFO +#undef DBX_REGISTER_NUMBER +#define DBX_REGISTER_NUMBER(REGNO) (REGNO) + +/* Cause long-jump assembler to be used, + since otherwise some files fail to be assembled right. */ +#define ASM_SPEC "-j" + +#undef ASM_FILE_START +#undef ASM_GENERATE_INTERNAL_LABEL +#undef ASM_OUTPUT_ADDR_DIFF_ELT +#undef ASM_OUTPUT_ALIGN +#undef ASM_OUTPUT_ASCII +#undef ASM_OUTPUT_DOUBLE +#undef ASM_OUTPUT_INT +#undef ASM_OUTPUT_INTERNAL_LABEL +#undef ASM_OUTPUT_LOCAL +#undef CPP_PREDEFINES +#undef FUNCTION_BOUNDARY +#undef PRINT_OPERAND +#undef PRINT_OPERAND_ADDRESS +#undef TARGET_VERSION +#undef FUNCTION_PROFILER +#undef ASM_OUTPUT_LABELREF_AS_INT + +#define TARGET_DEFAULT 9 /* 32332 with 32081. */ +#define TARGET_VERSION fprintf (stderr, " (32000, Encore syntax)"); +/* Note Encore does not standardly do -Dencore. */ +/* budd: should have a -ns32332 (or -apc) switch! but no harm for now */ +#define CPP_PREDEFINES "-Dns32000 -Dn16 -Dns16000 -Dns32332 -Dunix -Asystem(unix) -Acpu(ns32k) -Amachine(ns32k)" + +/* Ignore certain cpp directives used in header files on sysV. */ +#define SCCS_DIRECTIVE + +/* Output #ident as a .ident. */ +#define ASM_OUTPUT_IDENT(FILE, NAME) fprintf (FILE, "\t.ident \"%s\"\n", NAME); + +/* The .file command should always begin the output. */ +#define ASM_FILE_START(FILE) \ +output_file_directive ((FILE), main_input_filename) + +#define FUNCTION_BOUNDARY 128 /* speed optimization */ + +/* + * The Encore assembler uses ".align 2" to align on 2-byte boundaries. + */ + +#define ASM_OUTPUT_ALIGN(FILE,LOG) \ + fprintf (FILE, "\t.align %d\n", 1 << (LOG)) + +/* The Encore assembler doesn't seem to accept the usual second argument + and warns that .align may not work in the text section if optimization + is on. */ +#undef LABEL_ALIGN_AFTER_BARRIER +#define LABEL_ALIGN_AFTER_BARRIER(LABEL) 0 + +/* + * Internal labels are prefixed with a period. + */ + +#define ASM_GENERATE_INTERNAL_LABEL(LABEL,PREFIX,NUM) \ + sprintf (LABEL, "*.%s%d", PREFIX, NUM) +#define ASM_OUTPUT_INTERNAL_LABEL(FILE,PREFIX,NUM) \ + fprintf (FILE, ".%s%d:\n", PREFIX, NUM) +#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \ + fprintf (FILE, "\t.double .L%d-.LI%d\n", VALUE, REL) + +/* + * Different syntax for integer constants, double constants, and + * uninitialized locals. + */ + +#define ASM_OUTPUT_INT(FILE,VALUE) \ +( fprintf (FILE, "\t.double "), \ + output_addr_const (FILE, (VALUE)), \ + fprintf (FILE, "\n")) + +#define ASM_OUTPUT_LABELREF_AS_INT(STREAM, NAME) \ +do { \ + fprintf (STREAM, "\t.double\t"); \ + ASM_OUTPUT_LABELREF (STREAM, NAME); \ + fprintf (STREAM, "\n"); \ +} while (0) + + +#define ASM_OUTPUT_DOUBLE(FILE,VALUE) \ + fprintf (FILE, "\t.long 0f%.20e\n", (VALUE)) + +#define ASM_OUTPUT_LOCAL(FILE, NAME, SIZE, ROUNDED) \ +( fputs ("\t.bss ", (FILE)), \ + assemble_name ((FILE), (NAME)), \ + fprintf ((FILE), ",%u,%u\n", (SIZE), (ROUNDED))) + + /* + * Encore assembler can't handle huge string constants like the one in + * gcc.c. If the default routine in varasm.c were more conservative, this + * code could be eliminated. It starts a new .ascii directive every 40 + * characters. + */ + +#define ASM_OUTPUT_ASCII(file, p, size) \ +do { \ + int i; \ + for (i = 0; i < (size); i++) \ + { \ + register int c = (p)[i]; \ + if ((i / 40) * 40 == i) \ + if (i == 0) \ + fprintf ((file), "\t.ascii \""); \ + else \ + fprintf ((file), "\"\n\t.ascii \""); \ + if (c == '\"' || c == '\\') \ + putc ('\\', (file)); \ + if (c >= ' ' && c < 0177) \ + putc (c, (file)); \ + else \ + { \ + fprintf ((file), "\\%o", c); \ + if (i < (size) - 1 \ + && (p)[i + 1] >= '0' && (p)[i + 1] <= '9')\ + fprintf ((file), "\"\n\t.ascii \""); \ + } \ + } \ + fprintf ((file), "\"\n"); \ +} while (0) + +/* Modify syntax of jsr instructions. */ +#define CALL_MEMREF_IMPLICIT + +#define NO_ABSOLUTE_PREFIX_IF_SYMBOLIC + +#define PRINT_OPERAND(FILE, X, CODE) print_operand(FILE, X, CODE) + +#define PRINT_OPERAND_ADDRESS(FILE, ADDR) print_operand_address(FILE, ADDR) + +/* Change the way in which data is allocated and initialized on the + encore so that both private and shared data are supported. Shared data + that is initialized must be contained in the ".shrdata" section + of the program. This is accomplished by defining the SHARED_SECTION_ASM_OP + macro. Share data that is simply allocated, and not initialized must + be prefixed with the ".shrcomm" or ".shrbss" pseudo op, for common or + local data respectively. This is accomplished by redefining the + ASM_OUTPUT_COMMON and ASM_OUTPUT_LOCAL macros. */ + +/* Assembler pseudo-op for shared data segment. */ + +#define SHARED_SECTION_ASM_OP ".shrdata" + +/* This says how to output an assembler line + to define a shared common symbol. */ + +#define ASM_OUTPUT_SHARED_COMMON(FILE, NAME, SIZE, ROUNDED) \ +( fputs (".shrcomm ", (FILE)), \ + assemble_name ((FILE), (NAME)), \ + fprintf ((FILE), ",%d\n", (ROUNDED))) + +/* This says how to output an assembler line + to define a shared local symbol. */ + +#define ASM_OUTPUT_SHARED_LOCAL(FILE, NAME, SIZE, ROUNDED) \ +( fputs ("\t.shrbss ", (FILE)), \ + assemble_name ((FILE), (NAME)), \ + fprintf ((FILE), ",%d,%d\n", (SIZE), (ROUNDED))) + +#define FUNCTION_PROFILER(FILE, LABELNO) \ + fprintf (FILE, "\taddr .LP%d,r0\n\tjsr mcount\n", (LABELNO)) + +#define ENCORE_ASM diff --git a/gcc/config/ns32k/genix.h b/gcc/config/ns32k/genix.h new file mode 100755 index 0000000..ac84cfc --- /dev/null +++ b/gcc/config/ns32k/genix.h @@ -0,0 +1,167 @@ +/* Definitions of target machine for GNU compiler. Genix ns32000 version. + Copyright (C) 1987, 1988, 1994 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. */ + +#include "ns32k/encore.h" + +/* We don't want the one Encore needs. */ +#undef ASM_SPEC + +/* The following defines override ones in ns32k.h and prevent any attempts + to explicitly or implicitly make references to the SB register in the GCC + generated code. It is necessary to avoid such references under Genix V.3.1 + because this OS doesn't even save/restore the SB on context switches! */ + +#define IS_OK_REG_FOR_BASE_P(X) \ + ( (GET_CODE (X) == REG) && REG_OK_FOR_BASE_P (X) ) + +#undef INDIRECTABLE_1_ADDRESS_P +#define INDIRECTABLE_1_ADDRESS_P(X) \ + (CONSTANT_ADDRESS_NO_LABEL_P (X) \ + || IS_OK_REG_FOR_BASE_P (X) \ + || (GET_CODE (X) == PLUS \ + && IS_OK_REG_FOR_BASE_P (XEXP (X, 0)) \ + && CONSTANT_ADDRESS_P (XEXP (X, 1)) ) ) + +/* Note that for double indirects, only FP, SP, and SB are allowed + as the inner-most base register. But we are avoiding use of SB. */ + +#undef MEM_REG +#define MEM_REG(X) \ + ( (GET_CODE (X) == REG) \ + && ( (REGNO (X) == FRAME_POINTER_REGNUM) \ + || (REGNO (X) == STACK_POINTER_REGNUM) ) ) + +#undef INDIRECTABLE_2_ADDRESS_P +#define INDIRECTABLE_2_ADDRESS_P(X) \ + (GET_CODE (X) == MEM \ + && (((xfoo0 = XEXP (X, 0), MEM_REG (xfoo0)) \ + || (GET_CODE (xfoo0) == PLUS \ + && MEM_REG (XEXP (xfoo0, 0)) \ + && CONSTANT_ADDRESS_NO_LABEL_P (XEXP (xfoo0, 1)))) \ + || CONSTANT_ADDRESS_NO_LABEL_P (xfoo0))) + +/* Go to ADDR if X is a valid address not using indexing. + (This much is the easy part.) */ +#undef GO_IF_NONINDEXED_ADDRESS +#define GO_IF_NONINDEXED_ADDRESS(X, ADDR) \ +{ register rtx xfoob = (X); \ + if (GET_CODE (xfoob) == REG) goto ADDR; \ + if (INDIRECTABLE_1_ADDRESS_P(X)) goto ADDR; \ + if (CONSTANT_P(X)) goto ADDR; \ + if (INDIRECTABLE_2_ADDRESS_P (X)) goto ADDR; \ + if (GET_CODE (X) == PLUS) \ + if (CONSTANT_ADDRESS_NO_LABEL_P (XEXP (X, 1))) \ + if (INDIRECTABLE_2_ADDRESS_P (XEXP (X, 0))) \ + goto ADDR; \ +} + +/* A bug in the GNX 3.X assembler causes references to external symbols to + be mishandled if the symbol is also used as the name of a function-local + variable or as the name of a struct or union field. The problem only + appears when you are also using the -g option so that SDB debugging + directives are also being produced by GCC. In such cases, the assembler + gets the external entity confused with the local entity and addressing + havoc ensues. The solution is to get GCC to produce .global directives + for all external entities which are actually referenced within the current + source file. The following macro does this. */ + +#define ASM_OUTPUT_EXTERNAL(FILE, DECL, NAME) \ + ASM_GLOBALIZE_LABEL(FILE,NAME); + +/* Genix wants 0l instead of 0f. */ + +#undef ASM_OUTPUT_DOUBLE +#define ASM_OUTPUT_DOUBLE(FILE,VALUE) \ + fprintf (FILE, "\t.long 0l%.20e\n", (VALUE)) + +/* A bug in the GNX 3.X linker prevents symbol-table entries with a storage- + class field of C_EFCN (-1) from being accepted. */ + +#ifdef PUT_SDB_EPILOGUE_END +#undef PUT_SDB_EPILOGUE_END +#endif +#define PUT_SDB_EPILOGUE_END(NAME) + +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (32000, National syntax)"); + +/* Same as the encore definition except + * Different syntax for double constants. + * Don't output `?' before external regs. + * Output `(sb)' in certain indirect refs. */ + +#error this has not been updated since version 1. +#error it is certainly wrong. + +#undef PRINT_OPERAND +#define PRINT_OPERAND(FILE, X, CODE) \ +{ if (CODE == '$') putc ('$', FILE); \ + else if (CODE == '?'); \ + else if (GET_CODE (X) == REG) \ + fprintf (FILE, "%s", reg_names[REGNO (X)]); \ + else if (GET_CODE (X) == MEM) \ + { \ + rtx xfoo; \ + xfoo = XEXP (X, 0); \ + switch (GET_CODE (xfoo)) \ + { \ + case MEM: \ + if (GET_CODE (XEXP (xfoo, 0)) == REG) \ + if (REGNO (XEXP (xfoo, 0)) == STACK_POINTER_REGNUM) \ + fprintf (FILE, "0(0(sp))"); \ + else fprintf (FILE, "0(0(%s))", \ + reg_names[REGNO (XEXP (xfoo, 0))]); \ + else \ + { \ + extern int paren_base_reg_printed; \ + fprintf (FILE, "0("); \ + paren_base_reg_printed = 0; \ + output_address (xfoo); \ + if (!paren_base_reg_printed) \ + fprintf (FILE, "(sb)"); \ + putc (')', FILE); \ + } \ + break; \ + case REG: \ + fprintf (FILE, "0(%s)", reg_names[REGNO (xfoo)]); \ + break; \ + case PRE_DEC: \ + case POST_INC: \ + fprintf (FILE, "tos"); \ + break; \ + case CONST_INT: \ + fprintf (FILE, "@%d", INTVAL (xfoo)); \ + break; \ + default: \ + output_address (xfoo); \ + break; \ + } \ + } \ + else if (GET_CODE (X) == CONST_DOUBLE && GET_MODE (X) != VOIDmode) \ + if (GET_MODE (X) == DFmode) \ + { union { double d; int i[2]; } u; \ + u.i[0] = CONST_DOUBLE_LOW (X); u.i[1] = CONST_DOUBLE_HIGH (X); \ + fprintf (FILE, "$0l%.20e", u.d); } \ + else { union { double d; int i[2]; } u; \ + u.i[0] = CONST_DOUBLE_LOW (X); u.i[1] = CONST_DOUBLE_HIGH (X); \ + fprintf (FILE, "$0f%.20e", u.d); } \ + else if (GET_CODE (X) == CONST) \ + output_addr_const (FILE, X); \ + else { putc ('$', FILE); output_addr_const (FILE, X); }} diff --git a/gcc/config/ns32k/merlin.h b/gcc/config/ns32k/merlin.h new file mode 100755 index 0000000..d385395 --- /dev/null +++ b/gcc/config/ns32k/merlin.h @@ -0,0 +1,231 @@ +/* Definitions of target machine for GNU compiler. MERLIN NS32000 version. + Copyright (C) 1990, 1994 Free Software Foundation, Inc. + By Mark Mason (mason@reed.bitnet, pyramid!unify!mason@uunet.uu.net). + +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. */ + +/* Two flags to control how addresses are printed in assembler insns. */ + +#define SEQUENT_ADDRESS_BUG 1 +#define SEQUENT_BASE_REGS + +#include "ns32k/ns32k.h" + +/* This is BSD, so it wants DBX format. */ +#define DBX_DEBUGGING_INFO + +/* Sequent has some changes in the format of DBX symbols. */ +#define DBX_NO_XREFS 1 + +/* Don't split DBX symbols into continuations. */ +#define DBX_CONTIN_LENGTH 0 + +#define TARGET_DEFAULT 1 + +/* Print subsidiary information on the compiler version in use. */ +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (32000, UTek syntax)"); + +/* These control the C++ compiler somehow. */ +#define FASCIST_ASSEMBLER +#define USE_COLLECT + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES \ + "-Dns32000 -Dns32k -Dns16000 -Dmerlin -Dunix -DUtek -Dbsd \ + -Asystem(unix) -Asystem(bsd) -Acpu(ns32k) -Amachine(ns32k)" + +/* This is how to align the code that follows an unconditional branch. + Don't define it, since it confuses the assembler (we hear). */ + +#undef LABEL_ALIGN_AFTER_BARRIER + +/* Assembler pseudo-op for shared data segment. */ +#define SHARED_SECTION_ASM_OP ".shdata" + +/* %$ means print the prefix for an immediate operand. */ + +#ifdef UTEK_ASM +#undef PRINT_OPERAND +#define PRINT_OPERAND(FILE, X, CODE) \ +{ if (CODE == '$') putc('$', FILE); \ + else if (CODE == '?'); \ + else if (GET_CODE (X) == CONST_INT) \ + fprintf(FILE, "$%d", INTVAL(X)); \ + else if (GET_CODE (X) == REG) \ + fprintf (FILE, "%s", reg_names[REGNO (X)]); \ + else if (GET_CODE (X) == MEM) \ + { \ + rtx xfoo; \ + xfoo = XEXP (X, 0); \ + switch (GET_CODE (xfoo)) \ + { \ + case MEM: \ + if (GET_CODE (XEXP (xfoo, 0)) == REG) \ + if (REGNO (XEXP (xfoo, 0)) == STACK_POINTER_REGNUM) \ + fprintf (FILE, "0(0(sp))"); \ + else fprintf (FILE, "0(0(%s))", \ + reg_names[REGNO (XEXP (xfoo, 0))]); \ + else \ + { \ + if (GET_CODE (XEXP (xfoo, 0)) == SYMBOL_REF \ + || GET_CODE (XEXP (xfoo, 0)) == CONST) \ + { \ + fprintf(FILE, "0("); \ + output_address(xfoo); \ + fprintf(FILE, "(sb))"); \ + } \ + else \ + { \ + fprintf (FILE, "0("); \ + output_address (xfoo); \ + putc (')', FILE); \ + } \ + } \ + break; \ + case REG: \ + fprintf (FILE, "0(%s)", reg_names[REGNO (xfoo)]); \ + break; \ + case PRE_DEC: \ + case POST_INC: \ + fprintf (FILE, "tos"); \ + break; \ + case CONST_INT: \ + fprintf (FILE, "$%d", INTVAL (xfoo)); \ + break; \ + default: \ + output_address (xfoo); \ + break; \ + } \ + } \ + else if (GET_CODE (X) == CONST_DOUBLE && GET_MODE (X) != VOIDmode) \ + if (GET_MODE (X) == DFmode) \ + { union { double d; int i[2]; } u; \ + u.i[0] = CONST_DOUBLE_LOW (X); u.i[1] = CONST_DOUBLE_HIGH (X); \ + fprintf (FILE, "$0d%.20e", u.d); } \ + else { union { double d; int i[2]; } u; \ + u.i[0] = CONST_DOUBLE_LOW (X); u.i[1] = CONST_DOUBLE_HIGH (X); \ + fprintf (FILE, "$0f%.20e", u.d); } \ + else output_addr_const (FILE, X); } + +#undef FUNCTION_PROLOGUE + +/* This differs from the one in ns32k.h in printing a bitmask + rather than a register list in the enter or save instruction. */ + +#define FUNCTION_PROLOGUE(FILE, SIZE) \ +{ register int regno, g_regs_used = 0; \ + int used_regs_buf[8], *bufp = used_regs_buf; \ + int used_fregs_buf[8], *fbufp = used_fregs_buf; \ + extern char call_used_regs[]; \ + MAIN_FUNCTION_PROLOGUE; \ + for (regno = 0; regno < 8; regno++) \ + if (regs_ever_live[regno] \ + && ! call_used_regs[regno]) \ + { \ + *bufp++ = regno; g_regs_used++; \ + } \ + *bufp = -1; \ + for (; regno < 16; regno++) \ + if (regs_ever_live[regno] && !call_used_regs[regno]) { \ + *fbufp++ = regno; \ + } \ + *fbufp = -1; \ + bufp = used_regs_buf; \ + if (frame_pointer_needed) \ + fprintf (FILE, "\tenter "); \ + else if (g_regs_used) \ + fprintf (FILE, "\tsave "); \ + if (frame_pointer_needed || g_regs_used) \ + { \ + char mask = 0; \ + while (*bufp >= 0) \ + mask |= 1 << *bufp++; \ + fprintf (FILE, "$0x%x", (int) mask & 0xff); \ + } \ + if (frame_pointer_needed) \ + fprintf (FILE, ",%d\n", SIZE); \ + else if (g_regs_used) \ + fprintf (FILE, "\n"); \ + fbufp = used_fregs_buf; \ + while (*fbufp >= 0) \ + { \ + if ((*fbufp & 1) || (fbufp[0] != fbufp[1] - 1)) \ + fprintf (FILE, "\tmovf f%d,tos\n", *fbufp++ - 8); \ + else \ + { \ + fprintf (FILE, "\tmovl f%d,tos\n", fbufp[0] - 8); \ + fbufp += 2; \ + } \ + } \ +} + +#undef FUNCTION_EPILOGUE + +/* This differs from the one in ns32k.h in printing a bitmask + rather than a register list in the exit or restore instruction. */ + +#define FUNCTION_EPILOGUE(FILE, SIZE) \ +{ register int regno, g_regs_used = 0, f_regs_used = 0; \ + int used_regs_buf[8], *bufp = used_regs_buf; \ + int used_fregs_buf[8], *fbufp = used_fregs_buf; \ + extern char call_used_regs[]; \ + *fbufp++ = -2; \ + for (regno = 8; regno < 16; regno++) \ + if (regs_ever_live[regno] && !call_used_regs[regno]) { \ + *fbufp++ = regno; f_regs_used++; \ + } \ + fbufp--; \ + for (regno = 0; regno < 8; regno++) \ + if (regs_ever_live[regno] \ + && ! call_used_regs[regno]) \ + { \ + *bufp++ = regno; g_regs_used++; \ + } \ + while (fbufp > used_fregs_buf) \ + { \ + if ((*fbufp & 1) && fbufp[0] == fbufp[-1] + 1) \ + { \ + fprintf (FILE, "\tmovl tos,f%d\n", fbufp[-1] - 8); \ + fbufp -= 2; \ + } \ + else fprintf (FILE, "\tmovf tos,f%d\n", *fbufp-- - 8); \ + } \ + if (frame_pointer_needed) \ + fprintf (FILE, "\texit "); \ + else if (g_regs_used) \ + fprintf (FILE, "\trestore "); \ + if (g_regs_used || frame_pointer_needed) \ + { \ + char mask = 0; \ + \ + while (bufp > used_regs_buf) \ + { \ + /* Utek assembler takes care of reversing this */ \ + mask |= 1 << *--bufp; \ + } \ + fprintf (FILE, "$0x%x\n", (int) mask & 0xff); \ + } \ + if (current_function_pops_args) \ + fprintf (FILE, "\tret %d\n", current_function_pops_args); \ + else fprintf (FILE, "\tret 0\n"); } + +#endif /* UTEK_ASM */ + +#undef PRINT_OPERAND_ADDRESS +#define PRINT_OPERAND_ADDRESS(FILE, ADDR) print_operand_address(FILE, ADDR) diff --git a/gcc/config/ns32k/netbsd.h b/gcc/config/ns32k/netbsd.h new file mode 100755 index 0000000..bc86e31 --- /dev/null +++ b/gcc/config/ns32k/netbsd.h @@ -0,0 +1,113 @@ +/* Configuration for a ns32532 running NetBSD as the target machine. + Copyright (C) 1988, 1994, 1995, 1996, 1998 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. + +*/ + +#include <ns32k/ns32k.h> + +/* Compile for the floating point unit & 32532 by default; + Don't assume SB is zero; + Don't use bitfield instructions; + FPU is 32381; */ + +#define TARGET_DEFAULT (1 + 24 + 32 + 64 + 256) + +/* 32-bit alignment for efficiency */ + +#undef POINTER_BOUNDARY +#define POINTER_BOUNDARY 32 + +/* 32-bit alignment for efficiency */ + +#undef FUNCTION_BOUNDARY +#define FUNCTION_BOUNDARY 32 + +/* 32532 spec says it can handle any alignment. Rumor from tm-ns32k.h + tells this might not be actually true (but it's for 32032, perhaps + National has fixed the bug for 32532). You might have to change this + if the bug still exists. */ + +#undef STRICT_ALIGNMENT +#define STRICT_ALIGNMENT 0 + +/* Use pc relative addressing whenever possible, + it's more efficient than absolute (ns32k.c) + You have to fix a bug in gas 1.38.1 to make this work with gas, + patch available from jkp@cs.hut.fi. + (NetBSD's gas version has this patch already applied) */ + +#define PC_RELATIVE + +/* Operand of bsr or jsr should be just the address. */ + +#define CALL_MEMREF_IMPLICIT + +/* movd insns may have floating point constant operands. */ + +#define MOVD_FLOAT_OK + +/* Get generic NetBSD definitions. */ +#include <netbsd.h> + +/* Names to predefine in the preprocessor for this target machine. */ + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dns32k -Dns32000 -Dns32532 -D__NetBSD__ -Dpc532 -D__ns32k__ -D__KPRINTF_ATTRIBUTE__ -Asystem(unix) -Asystem(NetBSD) -Acpu(ns32k) -Amachine(ns32k)" + +/* Make gcc agree with <machine/ansi.h> */ + +#undef SIZE_TYPE +#define SIZE_TYPE "unsigned int" + +#undef PTRDIFF_TYPE +#define PTRDIFF_TYPE "int" + +#undef WCHAR_TYPE +#define WCHAR_TYPE "int" + +#undef WCHAR_UNSIGNED +#define WCHAR_UNSIGNED 0 + +#undef WCHAR_TYPE_SIZE +#define WCHAR_TYPE_SIZE 32 + +/* This is BSD, so it wants DBX format. */ + +#define DBX_DEBUGGING_INFO + +/* Do not break .stabs pseudos into continuations. */ + +#define DBX_CONTIN_LENGTH 0 + +/* This is the char to use for continuation (in case we need to turn + continuation back on). */ + +#define DBX_CONTIN_CHAR '?' + +/* Don't default to pcc-struct-return, because gcc is the only compiler, and + we want to retain compatibility with older gcc versions. */ + +#undef PCC_STATIC_STRUCT_RETURN +#define DEFAULT_PCC_STRUCT_RETURN 0 + +/* Until they use ELF or something that handles dwarf2 unwinds + and initialization stuff better. */ +#define DWARF2_UNWIND_INFO 0 + diff --git a/gcc/config/ns32k/ns32k.c b/gcc/config/ns32k/ns32k.c new file mode 100755 index 0000000..af89e59 --- /dev/null +++ b/gcc/config/ns32k/ns32k.c @@ -0,0 +1,1196 @@ +/* Subroutines for assembler code output on the NS32000. + Copyright (C) 1988, 1994, 1995, 1996, 1997 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. */ + +/* Some output-actions in ns32k.md need these. */ +#include "config.h" +#include "system.h" +#include "rtl.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "real.h" +#include "insn-config.h" +#include "conditions.h" +#include "insn-flags.h" +#include "output.h" +#include "insn-attr.h" +#include "tree.h" +#include "expr.h" +#include "flags.h" + +#ifdef OSF_OS +int ns32k_num_files = 0; +#endif + +/* This duplicates reg_class_contens in reg_class.c, but maybe that isn't + initialized in time. Also this is more convenient as an array of ints. + We know that HARD_REG_SET fits in an unsigned int */ + +unsigned int ns32k_reg_class_contents[N_REG_CLASSES] = REG_CLASS_CONTENTS; + +enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] = +{ + GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, + GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, + FLOAT_REG0, LONG_FLOAT_REG0, FLOAT_REGS, FLOAT_REGS, + FLOAT_REGS, FLOAT_REGS, FLOAT_REGS, FLOAT_REGS, + FP_REGS, FP_REGS, FP_REGS, FP_REGS, + FP_REGS, FP_REGS, FP_REGS, FP_REGS, + FRAME_POINTER_REG, STACK_POINTER_REG +}; + +char *ns32k_out_reg_names[] = OUTPUT_REGISTER_NAMES; + +void +trace (s, s1, s2) + char *s, *s1, *s2; +{ + fprintf (stderr, s, s1, s2); +} + + +/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE. */ +int +hard_regno_mode_ok (regno, mode) + int regno; + enum machine_mode mode; +{ + int size = GET_MODE_UNIT_SIZE(mode); + + if (FLOAT_MODE_P(mode)) + { + if (size == UNITS_PER_WORD && regno < L1_REGNUM) + return 1; + if (size == UNITS_PER_WORD * 2 + && (((regno & 1) == 0 && regno < FRAME_POINTER_REGNUM))) + return 1; + return 0; + } + if (size == UNITS_PER_WORD * 2 + && (regno & 1) == 0 && regno < F0_REGNUM) + return 1; + if (size <= UNITS_PER_WORD + && (regno < F0_REGNUM || regno == FRAME_POINTER_REGNUM + || regno == STACK_POINTER_REGNUM)) + return 1; + return 0; +} + +int register_move_cost(CLASS1, CLASS2) + enum reg_class CLASS1; + enum reg_class CLASS2; +{ + if (CLASS1 == NO_REGS || CLASS2 == NO_REGS) + return 2; + if((SUBSET_P(CLASS1, FP_REGS) && !SUBSET_P(CLASS2, FP_REGS)) + || (!SUBSET_P(CLASS1, FP_REGS) && SUBSET_P(CLASS2, FP_REGS))) + return 8; + if (((CLASS1) == STACK_POINTER_REG && !SUBSET_P(CLASS2,GENERAL_REGS)) + || ((CLASS2) == STACK_POINTER_REG && !SUBSET_P(CLASS1,GENERAL_REGS))) + return 6; + if (((CLASS1) == FRAME_POINTER_REG && !SUBSET_P(CLASS2,GENERAL_REGS)) + || ((CLASS2) == FRAME_POINTER_REG && !SUBSET_P(CLASS1,GENERAL_REGS))) + return 6; + return 2; +} + +#if 0 +/* We made the insn definitions copy from floating point to general + registers via the stack. */ +int secondary_memory_needed(CLASS1, CLASS2, M) + enum reg_class CLASS1; + enum reg_class CLASS2; + enum machine_mode M; +{ + int ret = ((SUBSET_P(CLASS1, FP_REGS) && !SUBSET_P(CLASS2, FP_REGS)) + || (!SUBSET_P(CLASS1, FP_REGS) && SUBSET_P(CLASS2, FP_REGS))); + return ret; +} +#endif + + +/* ADDRESS_COST calls this. This function is not optimal + for the 32032 & 32332, but it probably is better than + the default. */ + +int +calc_address_cost (operand) + rtx operand; +{ + int i; + int cost = 0; + + if (GET_CODE (operand) == MEM) + cost += 3; + if (GET_CODE (operand) == MULT) + cost += 2; +#if 0 + if (GET_CODE (operand) == REG) + cost += 1; /* not really, but the documentation + says different amount of registers + shouldn't return the same costs */ +#endif + switch (GET_CODE (operand)) + { + case REG: + case CONST: + case CONST_INT: + case CONST_DOUBLE: + case SYMBOL_REF: + case LABEL_REF: + case POST_DEC: + case PRE_DEC: + break; + case MEM: + cost += calc_address_cost (XEXP (operand, 0)); + break; + case MULT: + case PLUS: + for (i = 0; i < GET_RTX_LENGTH (GET_CODE (operand)); i++) + { + cost += calc_address_cost (XEXP (operand, i)); + } + default: + break; + } + return cost; +} + +/* Return the register class of a scratch register needed to copy IN into + or out of a register in CLASS in MODE. If it can be done directly, + NO_REGS is returned. */ + +enum reg_class +secondary_reload_class (class, mode, in) + enum reg_class class; + enum machine_mode mode; + rtx in; +{ + int regno = true_regnum (in); + + if (regno >= FIRST_PSEUDO_REGISTER) + regno = -1; + + if ((class == FRAME_POINTER_REG && regno == STACK_POINTER_REGNUM) + || ( class == STACK_POINTER_REG && regno == FRAME_POINTER_REGNUM)) + return GENERAL_REGS; + else + return NO_REGS; +} + +/* Generate the rtx that comes from an address expression in the md file */ +/* The expression to be build is BASE[INDEX:SCALE]. To recognize this, + scale must be converted from an exponent (from ASHIFT) to a + multiplier (for MULT). */ +static rtx +gen_indexed_expr (base, index, scale) + rtx base, index, scale; +{ + rtx addr; + + /* This generates an invalid addressing mode, if BASE is + fp or sp. This is handled by PRINT_OPERAND_ADDRESS. */ + if (GET_CODE (base) != REG && GET_CODE (base) != CONST_INT) + base = gen_rtx (MEM, SImode, base); + addr = gen_rtx (MULT, SImode, index, + GEN_INT (1 << INTVAL (scale))); + addr = gen_rtx (PLUS, SImode, base, addr); + return addr; +} + +/* Return 1 if OP is a valid operand of mode MODE. This + predicate rejects operands which do not have a mode + (such as CONST_INT which are VOIDmode). */ +int +reg_or_mem_operand (op, mode) + register rtx op; + enum machine_mode mode; +{ + return (GET_MODE (op) == mode + && (GET_CODE (op) == REG + || GET_CODE (op) == SUBREG + || GET_CODE (op) == MEM)); +} + + +/* Split one or more DImode RTL references into pairs of SImode + references. The RTL can be REG, offsettable MEM, integer constant, or + CONST_DOUBLE. "operands" is a pointer to an array of DImode RTL to + split and "num" is its length. lo_half and hi_half are output arrays + that parallel "operands". */ + +void +split_di (operands, num, lo_half, hi_half) + rtx operands[]; + int num; + rtx lo_half[], hi_half[]; +{ + while (num--) + { + if (GET_CODE (operands[num]) == REG) + { + lo_half[num] = gen_rtx (REG, SImode, REGNO (operands[num])); + hi_half[num] = gen_rtx (REG, SImode, REGNO (operands[num]) + 1); + } + else if (CONSTANT_P (operands[num])) + { + split_double (operands[num], &lo_half[num], &hi_half[num]); + } + else if (offsettable_memref_p (operands[num])) + { + lo_half[num] = operands[num]; + hi_half[num] = adj_offsettable_operand (operands[num], 4); + } + else + abort(); + } +} + +/* 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[1]) == CONST_INT + && INTVAL (operands[1]) <= 7 + && INTVAL (operands[1]) >= -8) + return "movqd %1,%0"; + return "movd %1,%0"; +} + +char * +output_move_double (operands) + rtx *operands; +{ + enum anon1 { REGOP, OFFSOP, PUSHOP, CNSTOP, RNDOP } optype0, optype1; + rtx latehalf[2]; + + /* First classify both operands. */ + + if (REG_P (operands[0])) + optype0 = REGOP; + else if (offsettable_memref_p (operands[0])) + optype0 = OFFSOP; + else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC) + optype0 = PUSHOP; + else + optype0 = RNDOP; + + if (REG_P (operands[1])) + optype1 = REGOP; + else if (CONSTANT_P (operands[1]) + || GET_CODE (operands[1]) == CONST_DOUBLE) + optype1 = CNSTOP; + else if (offsettable_memref_p (operands[1])) + optype1 = OFFSOP; + else if (GET_CODE (XEXP (operands[1], 0)) == PRE_DEC) + optype1 = PUSHOP; + 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 (); + + /* 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) + split_double (operands[1], &operands[1], &latehalf[1]); + else + latehalf[1] = operands[1]; + + /* If insn is effectively movd N(sp),tos then we will do the + high word first. We should use the adjusted operand 1 (which is N+4(sp)) + for the low word as well, to compensate for the first decrement of sp. + Given this, it doesn't matter which half we do "first". */ + if (optype0 == PUSHOP + && REGNO (XEXP (XEXP (operands[0], 0), 0)) == STACK_POINTER_REGNUM + && reg_overlap_mentioned_p (stack_pointer_rtx, operands[1])) + operands[1] = latehalf[1]; + + /* If one or both operands autodecrementing, + do the two words, high-numbered first. */ + else if (optype0 == PUSHOP || optype1 == PUSHOP) + { + output_asm_insn (singlemove_string (latehalf), latehalf); + return singlemove_string (operands); + } + + /* If the first move would clobber the source of the second one, + do them in the other order. */ + + /* Overlapping registers. */ + if (optype0 == REGOP && optype1 == REGOP + && REGNO (operands[0]) == REGNO (latehalf[1])) + { + /* Do that word. */ + output_asm_insn (singlemove_string (latehalf), latehalf); + /* Do low-numbered word. */ + return singlemove_string (operands); + } + /* Loading into a register which overlaps a register used in the address. */ + else if (optype0 == REGOP && optype1 != REGOP + && reg_overlap_mentioned_p (operands[0], operands[1])) + { + if (reg_mentioned_p (operands[0], XEXP (operands[1], 0)) + && reg_mentioned_p (latehalf[0], XEXP (operands[1], 0))) + { + /* If both halves of dest are used in the src memory address, + load the destination address into the low reg (operands[0]). + Then it works to load latehalf first. */ + rtx xops[2]; + xops[0] = XEXP (operands[1], 0); + xops[1] = operands[0]; + output_asm_insn ("addr %a0,%1", xops); + operands[1] = gen_rtx (MEM, DImode, operands[0]); + latehalf[1] = adj_offsettable_operand (operands[1], 4); + /* The first half has the overlap, Do the late half first. */ + output_asm_insn (singlemove_string (latehalf), latehalf); + /* Then clobber. */ + return singlemove_string (operands); + } + if (reg_mentioned_p (operands[0], XEXP (operands[1], 0))) + { + /* The first half has the overlap, Do the late half first. */ + output_asm_insn (singlemove_string (latehalf), latehalf); + /* Then clobber. */ + return singlemove_string (operands); + } + } + + /* Normal case. Do the two words, low-numbered first. */ + + output_asm_insn (singlemove_string (operands), operands); + + operands[0] = latehalf[0]; + operands[1] = latehalf[1]; + return singlemove_string (operands); +} + + +#define MAX_UNALIGNED_COPY (32) +/* Expand string/block move operations. + + operands[0] is the pointer to the destination. + operands[1] is the pointer to the source. + operands[2] is the number of bytes to move. + operands[3] is the alignment. */ + +static void +move_tail(operands, bytes, offset) + rtx operands[]; + int bytes; + int offset; +{ + if (bytes & 2) + { + rtx src, dest; + dest = change_address(operands[0], HImode, + plus_constant(XEXP(operands[0], 0), offset)); + src = change_address(operands[1], HImode, + plus_constant(XEXP(operands[1], 0), offset)); + emit_move_insn(dest, src); + offset += 2; + } + if (bytes & 1) + { + rtx src, dest; + dest = change_address(operands[0], QImode, + plus_constant(XEXP(operands[0], 0), offset)); + src = change_address(operands[1], QImode, + plus_constant(XEXP(operands[1], 0), offset)); + emit_move_insn(dest, src); + } +} + +void +expand_block_move (operands) + rtx operands[]; +{ + rtx bytes_rtx = operands[2]; + rtx align_rtx = operands[3]; + int constp = (GET_CODE (bytes_rtx) == CONST_INT); + int bytes = (constp ? INTVAL (bytes_rtx) : 0); + int align = INTVAL (align_rtx); + rtx src_reg = gen_rtx(REG, Pmode, 1); + rtx dest_reg = gen_rtx(REG, Pmode, 2); + rtx count_reg = gen_rtx(REG, SImode, 0); + rtx insn; + + if (constp && bytes <= 0) + return; + + if (constp && bytes < 20) + { + int words = bytes >> 2; + if (words) + if (words < 3 || flag_unroll_loops) + { + int offset = 0; + for (; words; words--, offset += 4) + { + rtx src, dest; + dest = change_address(operands[0], SImode, + plus_constant(XEXP(operands[0], 0), offset)); + src = change_address(operands[1], SImode, + plus_constant(XEXP(operands[1], 0), offset)); + emit_move_insn(dest, src); + } + } + else + { + /* Use movmd. It is slower than multiple movd's but more + compact. It is also slower than movsd for large copies + but causes less registers reloading so is better than movsd + for small copies. */ + rtx src, dest; + dest = copy_addr_to_reg (XEXP(operands[0], 0)); + src = copy_addr_to_reg (XEXP(operands[1], 0)); + + emit_insn(gen_movstrsi2(dest, src, GEN_INT(words))); + } + move_tail(operands, bytes & 3, bytes & ~3); + return; + } + + if (align > UNITS_PER_WORD) + align = UNITS_PER_WORD; + + /* Move the address into scratch registers. */ + emit_insn(gen_rtx(CLOBBER, VOIDmode, dest_reg)); + emit_move_insn(dest_reg, XEXP (operands[0], 0)); + emit_insn(gen_rtx(CLOBBER, VOIDmode, src_reg)); + emit_move_insn(src_reg, XEXP (operands[1], 0)); + emit_insn(gen_rtx(CLOBBER, VOIDmode, count_reg)); + + if (constp && (align == UNITS_PER_WORD || bytes < MAX_UNALIGNED_COPY)) + { + rtx bytes_reg; + + /* constant no of bytes and aligned or small enough copy to not bother + * aligning. Emit insns to copy by words. + */ + if (bytes >> 2) + { + emit_move_insn(count_reg, GEN_INT(bytes >> 2)); + emit_insn(gen_movstrsi1 (GEN_INT(4))); + } + /* insns to copy rest */ + move_tail(operands, bytes & 3, bytes & ~3); + } + else if (align == UNITS_PER_WORD) + { + /* insns to copy by words */ + emit_insn(gen_lshrsi3 (count_reg, bytes_rtx, GEN_INT(2))); + emit_insn(gen_movstrsi1 (GEN_INT(4))); + /* insns to copy rest */ + emit_insn(gen_andsi3 (count_reg, bytes_rtx, GEN_INT(3))); + emit_insn(gen_movstrsi1 (const1_rtx)); + } + else + { + /* Not aligned and we may have a lot to copy so it is worth + * aligning. + */ + rtx aligned_label = gen_label_rtx (); + rtx bytes_reg; + + bytes_reg = copy_to_mode_reg(SImode, bytes_rtx); + if (!constp) + { + /* Emit insns to test and skip over the alignment if it is + * not worth it. This doubles as a test to ensure that the alignment + * operation can't copy too many bytes + */ + emit_insn(gen_cmpsi (bytes_reg, GEN_INT(MAX_UNALIGNED_COPY))); + emit_jump_insn (gen_blt (aligned_label)); + } + + /* Emit insns to do alignment at run time */ + emit_insn(gen_negsi2 (count_reg, src_reg)); + emit_insn(gen_andsi3 (count_reg, count_reg, GEN_INT(3))); + emit_insn(gen_subsi3 (bytes_reg, bytes_reg, count_reg)); + emit_insn(gen_movstrsi1 (const1_rtx)); + if (!constp) + emit_label (aligned_label); + + /* insns to copy by words */ + emit_insn (gen_lshrsi3 (count_reg, bytes_reg, GEN_INT(2))); + emit_insn(gen_movstrsi1 (GEN_INT(4))); + + /* insns to copy rest */ + emit_insn (gen_andsi3 (count_reg, bytes_reg, GEN_INT(3))); + emit_insn(gen_movstrsi1 (const1_rtx)); + } +} + + +/* Returns 1 if OP contains a global symbol reference */ + +int +global_symbolic_reference_mentioned_p (op, f) + rtx op; + int f; +{ + register char *fmt; + register int i; + + if (GET_CODE (op) == SYMBOL_REF) + { + if (! SYMBOL_REF_FLAG (op)) + return 1; + else + return 0; + } + else if (f && GET_CODE (op) != CONST) + return 0; + + fmt = GET_RTX_FORMAT (GET_CODE (op)); + for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--) + { + if (fmt[i] == 'E') + { + register int j; + + for (j = XVECLEN (op, i) - 1; j >= 0; j--) + if (global_symbolic_reference_mentioned_p (XVECEXP (op, i, j), 0)) + return 1; + } + else if (fmt[i] == 'e' + && global_symbolic_reference_mentioned_p (XEXP (op, i), 0)) + return 1; + } + + return 0; +} + + +/* Returns 1 if OP contains a symbol reference */ + +int +symbolic_reference_mentioned_p (op) + rtx op; +{ + register char *fmt; + register int i; + + if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF) + return 1; + + fmt = GET_RTX_FORMAT (GET_CODE (op)); + for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--) + { + if (fmt[i] == 'E') + { + register int j; + + for (j = XVECLEN (op, i) - 1; j >= 0; j--) + if (symbolic_reference_mentioned_p (XVECEXP (op, i, j))) + return 1; + } + else if (fmt[i] == 'e' && symbolic_reference_mentioned_p (XEXP (op, i))) + return 1; + } + + return 0; +} + +/* Return nonzero if IDENTIFIER with arguments ARGS is a valid machine specific + attribute for DECL. The attributes in ATTRIBUTES have previously been + assigned to DECL. */ + +int +ns32k_valid_decl_attribute_p (decl, attributes, identifier, args) + tree decl; + tree attributes; + tree identifier; + tree args; +{ + return 0; +} + +/* Return nonzero if IDENTIFIER with arguments ARGS is a valid machine specific + attribute for TYPE. The attributes in ATTRIBUTES have previously been + assigned to TYPE. */ + +int +ns32k_valid_type_attribute_p (type, attributes, identifier, args) + tree type; + tree attributes; + tree identifier; + tree args; +{ + if (TREE_CODE (type) != FUNCTION_TYPE + && TREE_CODE (type) != FIELD_DECL + && TREE_CODE (type) != TYPE_DECL) + return 0; + + /* Stdcall attribute says callee is responsible for popping arguments + if they are not variable. */ + if (is_attribute_p ("stdcall", identifier)) + return (args == NULL_TREE); + + /* Cdecl attribute says the callee is a normal C declaration */ + if (is_attribute_p ("cdecl", identifier)) + return (args == NULL_TREE); + + return 0; +} + +/* Return 0 if the attributes for two types are incompatible, 1 if they + are compatible, and 2 if they are nearly compatible (which causes a + warning to be generated). */ + +int +ns32k_comp_type_attributes (type1, type2) + tree type1; + tree type2; +{ + return 1; +} + + +/* 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. + + On the ns32k, the RET insn may be used to pop them if the number + of args is fixed, but if the number is variable then the caller + must pop them all. RET can't be used for library calls now + because the library is compiled with the Unix compiler. + Use of RET is a selectable option, since it is incompatible with + standard Unix calling sequences. If the option is not selected, + the caller must always pop the args. + + The attribute stdcall is equivalent to RET on a per module basis. */ + +int +ns32k_return_pops_args (fundecl, funtype, size) + tree fundecl; + tree funtype; + int size; +{ + int rtd = TARGET_RTD; + + if (TREE_CODE (funtype) == IDENTIFIER_NODE) + return rtd ? size : 0; + + /* Cdecl functions override -mrtd, and never pop the stack */ + if (lookup_attribute ("cdecl", TYPE_ATTRIBUTES (funtype))) + return 0; + + /* Stdcall functions will pop the stack if not variable args */ + if (lookup_attribute ("stdcall", TYPE_ATTRIBUTES (funtype))) + rtd = 1; + + if (rtd) + { + if (TYPE_ARG_TYPES (funtype) == NULL_TREE + || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (funtype))) == void_type_node)) + return size; + } + + return 0; +} + +/* PRINT_OPERAND is defined to call this function, + which is easier to debug than putting all the code in + a macro definition in ns32k.h. */ + +/* XXX time 12% of cpu time is in fprintf for non optimizing */ +void +print_operand (file, x, code) + FILE *file; + rtx x; + char code; +{ + if (code == '$') + PUT_IMMEDIATE_PREFIX (file); + else if (code == '?') + PUT_EXTERNAL_PREFIX (file); + else if (GET_CODE (x) == REG) + fprintf (file, "%s", ns32k_out_reg_names[REGNO (x)]); + else if (GET_CODE (x) == MEM) + { + rtx tmp = XEXP (x, 0); + output_address (XEXP (x, 0)); + } + else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) != VOIDmode) + { + if (GET_MODE (x) == DFmode) + { + union { double d; int i[2]; } u; + u.i[0] = CONST_DOUBLE_LOW (x); u.i[1] = CONST_DOUBLE_HIGH (x); + PUT_IMMEDIATE_PREFIX(file); +#ifdef SEQUENT_ASM + /* Sequent likes its floating point constants as integers */ + fprintf (file, "0Dx%08x%08x", u.i[1], u.i[0]); +#else +#ifdef ENCORE_ASM + fprintf (file, "0f%.20e", u.d); +#else + fprintf (file, "0d%.20e", u.d); +#endif +#endif + } + else + { + union { double d; int i[2]; } u; + u.i[0] = CONST_DOUBLE_LOW (x); u.i[1] = CONST_DOUBLE_HIGH (x); + PUT_IMMEDIATE_PREFIX (file); +#ifdef SEQUENT_ASM + /* We have no way of winning if we can't get the bits + for a sequent floating point number. */ +#if HOST_FLOAT_FORMAT != TARGET_FLOAT_FORMAT + abort (); +#endif + { + union { float f; long l; } uu; + uu.f = u.d; + fprintf (file, "0Fx%08x", uu.l); + } +#else + fprintf (file, "0f%.20e", u.d); +#endif + } + } + else + { + if (flag_pic + && GET_CODE (x) == CONST + && symbolic_reference_mentioned_p (x)) + { + fprintf(stderr, "illegal constant for pic-mode: \n"); + print_rtl(stderr, x); + fprintf(stderr, "\nGET_CODE (x) == %d, CONST == %d, symbolic_reference_mentioned_p (x) == %d\n", + GET_CODE (x), CONST, symbolic_reference_mentioned_p(x)); + abort (); + } + else if (flag_pic + && (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)) + { + output_addr_const (file, x); + fprintf (file, "(sb)"); + } + else + { +#ifdef NO_IMMEDIATE_PREFIX_IF_SYMBOLIC + if (GET_CODE (x) == CONST_INT) +#endif + PUT_IMMEDIATE_PREFIX (file); + output_addr_const (file, x); + } + } +} + +/* PRINT_OPERAND_ADDRESS is defined to call this function, + which is easier to debug than putting all the code in + a macro definition in ns32k.h . */ + +/* Completely rewritten to get this to work with Gas for PC532 Mach. + This function didn't work and I just wasn't able (nor very willing) to + figure out how it worked. + 90-11-25 Tatu Yl|nen <ylo@cs.hut.fi> */ + +void +print_operand_address (file, addr) + register FILE *file; + register rtx addr; +{ + static char scales[] = { 'b', 'w', 'd', 0, 'q', }; + rtx offset, base, indexexp, tmp; + int scale; + extern int flag_pic; + + if (GET_CODE (addr) == PRE_DEC || GET_CODE (addr) == POST_DEC) + { + fprintf (file, "tos"); + return; + } + + offset = NULL; + base = NULL; + indexexp = NULL; + while (addr != NULL) + { + if (GET_CODE (addr) == PLUS) + { + if (GET_CODE (XEXP (addr, 0)) == PLUS) + { + tmp = XEXP (addr, 1); + addr = XEXP (addr, 0); + } + else + { + tmp = XEXP (addr,0); + addr = XEXP (addr,1); + } + } + else + { + tmp = addr; + addr = NULL; + } + switch (GET_CODE (tmp)) + { + case PLUS: + abort (); + case MEM: + if (base) + { + indexexp = base; + base = tmp; + } + else + base = tmp; + break; + case REG: + if (REGNO (tmp) < F0_REGNUM) + if (base) + { + indexexp = tmp; + } + else + base = tmp; + else + if (base) + { + indexexp = base; + base = tmp; + } + else + base = tmp; + break; + case MULT: + indexexp = tmp; + break; + case SYMBOL_REF: + if (flag_pic && ! CONSTANT_POOL_ADDRESS_P (tmp) + && ! SYMBOL_REF_FLAG (tmp)) + { + if (base) + { + if (indexexp) + abort (); + indexexp = base; + } + base = tmp; + break; + } + case CONST: + if (flag_pic && GET_CODE (tmp) == CONST) + { + rtx sym, off, tmp1; + tmp1 = XEXP (tmp,0); + if (GET_CODE (tmp1) != PLUS) + abort (); + + sym = XEXP (tmp1,0); + if (GET_CODE (sym) != SYMBOL_REF) + { + off = sym; + sym = XEXP (tmp1,1); + } + else + off = XEXP (tmp1,1); + if (GET_CODE (sym) == SYMBOL_REF) + { + if (GET_CODE (off) != CONST_INT) + abort (); + + if (CONSTANT_POOL_ADDRESS_P (sym) + || SYMBOL_REF_FLAG (sym)) + { + SYMBOL_REF_FLAG (tmp) = 1; + } + else + { + if (base) + { + if (indexexp) + abort (); + + indexexp = base; + } + + if (offset != 0) + abort (); + + base = sym; + offset = off; + break; + } + } + } + case CONST_INT: + case LABEL_REF: + if (offset) + offset = gen_rtx (PLUS, SImode, tmp, offset); + else + offset = tmp; + break; + default: + abort (); + } + } + if (! offset) + offset = const0_rtx; + + if (base +#ifndef INDEX_RATHER_THAN_BASE + && (flag_pic || TARGET_HIMEM) + && GET_CODE (base) != SYMBOL_REF + && GET_CODE (offset) != CONST_INT +#else + /* This is a re-implementation of the SEQUENT_ADDRESS_BUG fix. */ +#endif + && !indexexp && GET_CODE (base) == REG + && REG_OK_FOR_INDEX_P (base)) + { + indexexp = base; + base = NULL; + } + + /* now, offset, base and indexexp are set */ +#ifndef BASE_REG_NEEDED + if (! base) + { +#if defined (PC_RELATIVE) || defined (NO_ABSOLUTE_PREFIX_IF_SYMBOLIC) + if (GET_CODE (offset) == CONST_INT) +#endif + PUT_ABSOLUTE_PREFIX (file); + } +#endif + + output_addr_const (file, offset); + if (base) /* base can be (REG ...) or (MEM ...) */ + switch (GET_CODE (base)) + { + /* now we must output base. Possible alternatives are: + (rN) (REG ...) + (sp) (REG ...) + (fp) (REG ...) + (pc) (REG ...) used for SYMBOL_REF and LABEL_REF, output + (disp(fp)) (MEM ...) just before possible [rX:y] + (disp(sp)) (MEM ...) + (disp(sb)) (MEM ...) + */ + case REG: + fprintf (file, "(%s)", ns32k_out_reg_names[REGNO (base)]); + break; + case SYMBOL_REF: + if (! flag_pic) + abort (); + + fprintf (file, "("); + output_addr_const (file, base); + fprintf (file, "(sb))"); + break; + case MEM: + addr = XEXP(base,0); + base = NULL; + offset = NULL; + while (addr != NULL) + { + if (GET_CODE (addr) == PLUS) + { + if (GET_CODE (XEXP (addr, 0)) == PLUS) + { + tmp = XEXP (addr, 1); + addr = XEXP (addr, 0); + } + else + { + tmp = XEXP (addr, 0); + addr = XEXP (addr, 1); + } + } + else + { + tmp = addr; + addr = NULL; + } + switch (GET_CODE (tmp)) + { + case REG: + base = tmp; + break; + case CONST: + case CONST_INT: + case SYMBOL_REF: + case LABEL_REF: + if (offset) + offset = gen_rtx (PLUS, SImode, tmp, offset); + else + offset = tmp; + break; + default: + abort (); + } + } + if (! offset) + offset = const0_rtx; + fprintf (file, "("); + output_addr_const (file, offset); + if (base) + fprintf (file, "(%s)", ns32k_out_reg_names[REGNO (base)]); + else if (TARGET_SB) + fprintf (file, "(sb)"); + else + abort (); + fprintf (file, ")"); + break; + default: + abort (); + } +#ifdef PC_RELATIVE + else if (GET_CODE (offset) != CONST_INT) + fprintf (file, "(pc)"); +#ifdef BASE_REG_NEEDED + else if (TARGET_SB) + fprintf (file, "(sb)"); + else + abort (); +#endif +#endif /* PC_RELATIVE */ + + /* now print index if we have one */ + if (indexexp) + { + if (GET_CODE (indexexp) == MULT) + { + scale = INTVAL (XEXP (indexexp, 1)) >> 1; + indexexp = XEXP (indexexp, 0); + } + else + scale = 0; + if (GET_CODE (indexexp) != REG || REGNO (indexexp) >= F0_REGNUM) + abort (); + +#ifdef UTEK_ASM + fprintf (file, "[%c`%s]", + scales[scale], + ns32k_out_reg_names[REGNO (indexexp)]); +#else + fprintf (file, "[%s:%c]", + ns32k_out_reg_names[REGNO (indexexp)], + scales[scale]); +#endif + } +} + +/* National 32032 shifting is so bad that we can get + better performance in many common cases by using other + techniques. */ +char * +output_shift_insn (operands) + rtx *operands; +{ + if (GET_CODE (operands[2]) == CONST_INT + && INTVAL (operands[2]) > 0 + && INTVAL (operands[2]) <= 3) + if (GET_CODE (operands[0]) == REG) + { + if (GET_CODE (operands[1]) == REG) + { + if (REGNO (operands[0]) == REGNO (operands[1])) + { + if (operands[2] == const1_rtx) + return "addd %0,%0"; + else if (INTVAL (operands[2]) == 2) + return "addd %0,%0\n\taddd %0,%0"; + } + if (operands[2] == const1_rtx) + return "movd %1,%0\n\taddd %0,%0"; + + operands[1] = gen_indexed_expr (const0_rtx, operands[1], operands[2]); + return "addr %a1,%0"; + } + if (operands[2] == const1_rtx) + return "movd %1,%0\n\taddd %0,%0"; + } + else if (GET_CODE (operands[1]) == REG) + { + operands[1] = gen_indexed_expr (const0_rtx, operands[1], operands[2]); + return "addr %a1,%0"; + } + else if (INTVAL (operands[2]) == 1 + && GET_CODE (operands[1]) == MEM + && rtx_equal_p (operands [0], operands[1])) + { + rtx temp = XEXP (operands[1], 0); + + if (GET_CODE (temp) == REG + || (GET_CODE (temp) == PLUS + && GET_CODE (XEXP (temp, 0)) == REG + && GET_CODE (XEXP (temp, 1)) == CONST_INT)) + return "addd %0,%0"; + } + else return "ashd %2,%0"; + return "ashd %2,%0"; +} + +char * +output_move_dconst (n, s) + int n; + char *s; +{ + static char r[32]; + + if (n > -9 && n < 8) + strcpy (r, "movqd "); + else if (n > 0 && n < 256) + strcpy (r, "movzbd "); + else if (n > 0 && n < 65536) + strcpy (r, "movzwd "); + else if (n < 0 && n > -129) + strcpy (r, "movxbd "); + else if (n < 0 && n > -32769) + strcpy (r, "movxwd "); + else + strcpy (r, "movd "); + strcat (r, s); + return r; +} diff --git a/gcc/config/ns32k/ns32k.h b/gcc/config/ns32k/ns32k.h new file mode 100755 index 0000000..d409ac2 --- /dev/null +++ b/gcc/config/ns32k/ns32k.h @@ -0,0 +1,1722 @@ +/* Definitions of target machine for GNU compiler. NS32000 version. + Copyright (C) 1988, 93, 94, 95, 96, 1997 Free Software Foundation, Inc. + Contributed by Michael Tiemann (tiemann@cygnus.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. */ + + +/* 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 "-Dns32000 -Dunix -Asystem(unix) -Acpu(ns32k) -Amachine(ns32k)" + +/* Print subsidiary information on the compiler version in use. */ +#define TARGET_VERSION fprintf (stderr, " (32000, GAS syntax)"); + + +/* ABSOLUTE PREFIX, IMMEDIATE_PREFIX and EXTERNAL_PREFIX can be defined + to cover most NS32k addressing syntax variations. This way we don't + need to redefine long macros in all the tm.h files for just slight + variations in assembler syntax. */ + +#ifndef ABSOLUTE_PREFIX +#define ABSOLUTE_PREFIX '@' +#endif + +#if defined(IMMEDIATE_PREFIX) && IMMEDIATE_PREFIX +#define PUT_IMMEDIATE_PREFIX(FILE) putc(IMMEDIATE_PREFIX, FILE) +#else +#define PUT_IMMEDIATE_PREFIX(FILE) +#endif +#if defined(ABSOLUTE_PREFIX) && ABSOLUTE_PREFIX +#define PUT_ABSOLUTE_PREFIX(FILE) putc(ABSOLUTE_PREFIX, FILE) +#else +#define PUT_ABSOLUTE_PREFIX(FILE) +#endif +#if defined(EXTERNAL_PREFIX) && EXTERNAL_PREFIX +#define PUT_EXTERNAL_PREFIX(FILE) putc(EXTERNAL_PREFIX, FILE) +#else +#define PUT_EXTERNAL_PREFIX(FILE) +#endif + +/* Run-time compilation parameters selecting different hardware subsets. */ + +extern int target_flags; + +/* Macros used in the machine description to test the flags. */ + +/* Compile 32081 insns for floating point (not library calls). */ +#define TARGET_32081 (target_flags & 1) +#define TARGET_32381 (target_flags & 256) + +/* The use of multiply-add instructions is optional because it can + * cause an abort due to being unable to find a spill register. The + * main problem is that the multiply-add instructions require f0 and + * f0 is not available for spilling because it is "explicitly + * mentioned" in the rtl for function return values. This can be fixed + * by defining SMALL_REGISTER_CLASSES, but that causes worse code for + * the (more common) integer case. We really need better reload code. + */ + +#define TARGET_MULT_ADD (target_flags & 512) + +/* Compile using rtd insn calling sequence. + This will not work unless you use prototypes at least + for all functions that can take varying numbers of args. */ +#define TARGET_RTD (target_flags & 2) + +/* Compile passing first two args in regs 0 and 1. */ +#define TARGET_REGPARM (target_flags & 4) + +/* Options to select type of CPU, for better optimization. + The output is correct for any kind of 32000 regardless of these options. */ +#define TARGET_32532 (target_flags & 8) +#define TARGET_32332 (target_flags & 16) + +/* Ok to use the static base register (and presume it's 0) */ +#define TARGET_SB ((target_flags & 32) == 0) +#define TARGET_HIMEM (target_flags & 128) + +/* Compile using bitfield insns. */ +#define TARGET_BITFIELD ((target_flags & 64) == 0) + +/* 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 \ + { { "32081", 1}, \ + { "soft-float", -257}, \ + { "rtd", 2}, \ + { "nortd", -2}, \ + { "regparm", 4}, \ + { "noregparm", -4}, \ + { "32532", 24}, \ + { "32332", -8}, \ + { "32332", 16}, \ + { "32032", -24}, \ + { "sb", -32}, \ + { "nosb", 32}, \ + { "bitfield", -64}, \ + { "nobitfield", 64}, \ + { "himem", 128}, \ + { "nohimem", -128}, \ + { "32381", 256}, \ + { "mult-add", 512}, \ + { "nomult-add", -512}, \ + { "", TARGET_DEFAULT}} + +/* TARGET_DEFAULT is defined in encore.h, pc532.h, etc. */ + +/* When we are generating PIC, the sb is used as a pointer + to the GOT. 32381 is a superset of 32081 */ + +#define OVERRIDE_OPTIONS \ +{ \ + if (flag_pic || TARGET_HIMEM) target_flags |= 32; \ + if (TARGET_32381) target_flags |= 1; \ + else target_flags &= ~512; \ +} + +/* Zero or more C statements that may conditionally modify two + variables `fixed_regs' and `call_used_regs' (both of type `char + []') after they have been initialized from the two preceding + macros. + + This is necessary in case the fixed or call-clobbered registers + depend on target flags. + + You need not define this macro if it has no work to do. + + If the usage of an entire class of registers depends on the target + flags, you may indicate this to GCC by using this macro to modify + `fixed_regs' and `call_used_regs' to 1 for each of the registers in + the classes which should not be used by GCC. Also define the macro + `REG_CLASS_FROM_LETTER' to return `NO_REGS' if it is called with a + letter for a class that shouldn't be used. + + (However, if this class is not included in `GENERAL_REGS' and all + of the insn patterns whose constraints permit this class are + controlled by target switches, then GCC will automatically avoid + using these registers when the target switches are opposed to + them.) */ + +#define CONDITIONAL_REGISTER_USAGE \ +do \ + { \ + if (!TARGET_32081) \ + { \ + int regno; \ + \ + for (regno = F0_REGNUM; regno <= F0_REGNUM + 8; regno++) \ + fixed_regs[regno] = call_used_regs[regno] = 1; \ + } \ + if (!TARGET_32381) \ + { \ + int regno; \ + \ + for (regno = L1_REGNUM; regno <= L1_REGNUM + 8; regno++) \ + fixed_regs[regno] = call_used_regs[regno] = 1; \ + } \ + } \ +while (0) + + +/* target machine storage layout */ + +/* Define this if most significant bit is lowest numbered + in instructions that operate on numbered bit-fields. + This is not true on the ns32k. */ +#define BITS_BIG_ENDIAN 0 + +/* Define this if most significant byte of a word is the lowest numbered. */ +/* That is not true on the ns32k. */ +#define BYTES_BIG_ENDIAN 0 + +/* Define this if most significant word of a multiword number is lowest + numbered. This is not true on the ns32k. */ +#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 32000, 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 32 + +/* Allocation boundary (in *bits*) for the code of a function. */ +#define FUNCTION_BOUNDARY 16 + +/* 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 + +/* No data type wants to be aligned rounder than this. */ +#define BIGGEST_ALIGNMENT 32 + +/* Set this nonzero if move instructions will actually fail to work + when given unaligned data. National claims that the NS32032 + works without strict alignment, but rumor has it that operands + crossing a page boundary cause unpredictable results. */ +#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. */ +/* Required on the 386 since it doesn't have a full set of bitfield insns. + (There is no signed extv insn.) */ +#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. */ +#define FIRST_PSEUDO_REGISTER 26 + +/* 1 for registers that have pervasive standard uses + and are not available for the register allocator. + On the ns32k, these are the FP, SP, (SB and PC are not included here). */ +#define FIXED_REGISTERS {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 for registers not available across function calls. + These must include the FIXED_REGISTERS and also any + registers that can be used without being saved. + The latter must include the registers where values are returned + and the register where structure-value addresses are passed. + Aside from that, you can include as many other registers as you like. */ +#define CALL_USED_REGISTERS {1, 1, 1, 0, 0, 0, 0, 0, \ + 1, 1, 1, 1, 0, 0, 0, 0, \ + 1, 1, 0, 0, 0, 0, 0, 0, \ + 1, 1} + +/* 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", "r2", "r3", "r4", "r5", "r6", "r7", \ + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", \ + "l1", "l1h","l3", "l3h","l5", "l5h","l7", "l7h", \ + "fp", "sp"} + + +#define ADDITIONAL_REGISTER_NAMES \ +{{"l0", 8}, {"l2", 10}, {"l4", 12}, {"l6", 14}} + +/* l0-7 are not recognized by the assembler. These are the names to use, + * but we don't want ambiguous names in REGISTER_NAMES + */ +#define OUTPUT_REGISTER_NAMES \ +{"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \ + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", \ + "f1", "l1h","f3", "l3h","f5", "l5h","f7", "f7h", \ + "fp", "sp"} + +#define REG_ALLOC_ORDER \ +{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 10, 11, 18, 12, 13, 20, 14, 15, 22, 24, 25, 17, 19, 23} + +/* How to renumber registers for dbx and gdb. + NS32000 may need more change in the numeration. XXX */ + +#define DBX_REGISTER_NUMBER(REGNO) \ + ((REGNO) < L1_REGNUM? (REGNO) \ + : (REGNO) < FRAME_POINTER_REGNUM? (REGNO) - L1_REGNUM + 22 \ + : (REGNO) == FRAME_POINTER_REGNUM? 17 \ + : 16) + + + + +#define R0_REGNUM 0 +#define F0_REGNUM 8 +#define L1_REGNUM 16 + +/* Specify the registers used for certain standard purposes. + The values of these macros are register numbers. */ + +/* NS32000 pc is not overloaded on a register. */ +/* #define PC_REGNUM */ + +/* Register to use for pushing function arguments. */ +#define STACK_POINTER_REGNUM 25 + +/* Base register for access to local variables of the function. */ +#define FRAME_POINTER_REGNUM 24 + + +/* 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 ns32k, all registers are 32 bits long except for the 32381 "long" + registers but we treat those as pairs */ +#define LONG_FP_REGS_P(REGNO) ((REGNO) >= L1_REGNUM && (REGNO) < L1_REGNUM + 8) +#define HARD_REGNO_NREGS(REGNO, MODE) \ + ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) + +/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE. */ +#define HARD_REGNO_MODE_OK(REGNO, MODE) hard_regno_mode_ok (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. + + Early documentation says SI and DI are not tieable if some reg can + be OK for SI but not for DI. However other ports (mips, i860, mvs + and tahoe) don't meet the above criterion. Evidently the real + requirement is somewhat laxer. Documentation was changed for gcc + 2.8 but was not picked up by egcs (at least egcs 1.0). Having all + integer modes tieable definitely generates faster code. */ + +#define MODES_TIEABLE_P(MODE1, MODE2) \ + ((FLOAT_MODE_P(MODE1) && FLOAT_MODE_P(MODE2) \ + && (GET_MODE_UNIT_SIZE(MODE1) == GET_MODE_UNIT_SIZE(MODE2))) \ + || (!FLOAT_MODE_P(MODE1) && !FLOAT_MODE_P(MODE2))) + +/* 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 0 + +/* Base register for access to arguments of the function. */ +#define ARG_POINTER_REGNUM 24 + +/* Register in which static-chain is passed to a function. */ +#define STATIC_CHAIN_REGNUM 1 + +/* Register in which address to store a structure value + is passed to a function. */ +#define STRUCT_VALUE_REGNUM 2 + +/* 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. */ + +enum reg_class +{ NO_REGS, GENERAL_REGS, FLOAT_REG0, LONG_FLOAT_REG0, FLOAT_REGS, + FP_REGS, GEN_AND_FP_REGS, FRAME_POINTER_REG, STACK_POINTER_REG, + GEN_AND_MEM_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", "FLOAT_REG0", "LONG_FLOAT_REG0", "FLOAT_REGS", \ + "FP_REGS", "GEN_AND_FP_REGS", "FRAME_POINTER_REG", "STACK_POINTER_REG", \ + "GEN_AND_MEM_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, 0x00ff, 0x100, 0x300, 0xff00, \ + 0xffff00, 0xffffff, 0x1000000, 0x2000000, \ + 0x30000ff, 0x3ffffff } + +#define SUBSET_P(CLASS1, CLASS2) \ + ((ns32k_reg_class_contents[CLASS1] & ~ns32k_reg_class_contents[CLASS2]) \ + == 0) + +/* 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) (regclass_map[REGNO]) + +/* The class value for index registers, and the one for base regs. */ + +#define INDEX_REG_CLASS GENERAL_REGS +#define BASE_REG_CLASS GEN_AND_MEM_REGS + +/* Get reg_class from a letter such as appears in the machine description. */ + +#define REG_CLASS_FROM_LETTER(C) \ + ((C) == 'u' ? FLOAT_REG0 \ + : (C) == 'v' ? LONG_FLOAT_REG0 \ + : (C) == 'f' ? FLOAT_REGS \ + : (C) == 'l' ? FP_REGS \ + : (C) == 'x' ? FRAME_POINTER_REG \ + : (C) == 'y' ? STACK_POINTER_REG \ + : 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. + + On the ns32k, these letters are used as follows: + + I : Matches integers which are valid shift amounts for scaled indexing. + These are 0, 1, 2, 3 for byte, word, double, and quadword. + Used for matching arithmetic shifts only on 32032 & 32332. + J : Matches integers which fit a "quick" operand. + K : Matches integers 0 to 7 (for inss and exts instructions). + */ + +#define CONST_OK_FOR_LETTER_P(VALUE, C) \ + ((VALUE) < 8 && (VALUE) + 8 >= 0 ? \ + ((C) == 'I' ? (!TARGET_32532 && 0 <= (VALUE) && (VALUE) <= 3) : \ + (C) == 'J' ? (VALUE) <= 7 : \ + (C) == 'K' ? 0 <= (VALUE) : 0) : 0) + +/* Similar, but for floating constants, and defining letters G and H. + Here VALUE is the CONST_DOUBLE rtx itself. */ + +#define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C) 1 + +/* 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. */ + +/* We return GENERAL_REGS instead of GEN_AND_MEM_REGS. + The latter offers no real additional possibilities + and can cause spurious secondary reloading. */ + +#define PREFERRED_RELOAD_CLASS(X,CLASS) \ + ((CLASS) == GEN_AND_MEM_REGS ? GENERAL_REGS : (CLASS)) + +/* Return the maximum number of consecutive registers + needed to represent mode MODE in a register of class CLASS. */ +/* On the 32000, 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 + +/* A C expression whose value is RTL representing the location of the + incoming return address at the beginning of any function, before + the prologue. This RTL is either a `REG', indicating that the + return value is saved in `REG', or a `MEM' representing a location + in the stack. + + You only need to define this macro if you want to support call + frame debugging information like that provided by DWARF 2. + + Before the prologue, RA is at 0(sp). */ + +#define INCOMING_RETURN_ADDR_RTX \ + gen_rtx (MEM, VOIDmode, gen_rtx (REG, VOIDmode, STACK_POINTER_REGNUM)) + +/* A C expression whose value is RTL representing the value of the + return address for the frame COUNT steps up from the current frame, + after the prologue. FRAMEADDR is the frame pointer of the COUNT + frame, or the frame pointer of the COUNT - 1 frame if + `RETURN_ADDR_IN_PREVIOUS_FRAME' is defined. + + After the prologue, RA is at 4(fp) in the current frame. */ + +#define RETURN_ADDR_RTX(COUNT, FRAME) \ + (gen_rtx (MEM, Pmode, gen_rtx (PLUS, Pmode, (FRAME), GEN_INT(4)))) + +/* A C expression whose value is an integer giving the offset, in + bytes, from the value of the stack pointer register to the top of + the stack frame at the beginning of any function, before the + prologue. The top of the frame is defined to be the value of the + stack pointer in the previous frame, just before the call + instruction. + + You only need to define this macro if you want to support call + frame debugging information like that provided by DWARF 2. */ + +#define INCOMING_FRAME_SP_OFFSET 4 + +/* Offset of the CFA from the argument pointer register value. */ +#define ARG_POINTER_CFA_OFFSET 8 + +/* If we generate an insn to push BYTES bytes, + this says how many the stack pointer really advances by. + On the 32000, sp@- in a byte insn really pushes a BYTE. */ +#define PUSH_ROUNDING(BYTES) (BYTES) + +/* Offset of first parameter from the argument pointer register value. */ +#define FIRST_PARM_OFFSET(FNDECL) 8 + +/* Value is the number of byte 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. + + On the 32000, the RET insn may be used to pop them if the number + of args is fixed, but if the number is variable then the caller + must pop them all. RET can't be used for library calls now + because the library is compiled with the Unix compiler. + Use of RET is a selectable option, since it is incompatible with + standard Unix calling sequences. If the option is not selected, + the caller must always pop the args. + + The attribute stdcall is equivalent to RTD on a per module basis. */ + +#define RETURN_POPS_ARGS(FUNDECL,FUNTYPE,SIZE) \ + (ns32k_return_pops_args (FUNDECL, FUNTYPE, SIZE)) + +/* 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 32000 the return value is in R0, + or perhaps in F0 if there is fp support. */ + +#define FUNCTION_VALUE(VALTYPE, FUNC) LIBCALL_VALUE(TYPE_MODE (VALTYPE)) + +/* Define how to find the value returned by a library function + assuming the value has mode MODE. */ + +/* On the 32000 the return value is in R0, + or perhaps F0 is there is fp support. */ + +#define LIBCALL_VALUE(MODE) \ + gen_rtx (REG, MODE, \ + FLOAT_MODE_P(MODE) && TARGET_32081 ? F0_REGNUM: R0_REGNUM) + +/* Define this if PCC uses the nonreentrant convention for returning + structure and union values. */ + +#define PCC_STATIC_STRUCT_RETURN + +/* 1 if N is a possible register number for a function value. + On the 32000, R0 and F0 are the only registers thus used. */ + +#define FUNCTION_VALUE_REGNO_P(N) (((N) & ~8) == 0) + +/* 1 if N is a possible register number for function argument passing. + On the 32000, no registers are used in this way. */ + +#define FUNCTION_ARG_REGNO_P(N) 0 + +/* 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 ns32k, this is a single integer, which is a number of bytes + of arguments scanned so far. */ + +#define CUMULATIVE_ARGS int + +/* 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 ns32k, the offset starts at 0. */ + +#define INIT_CUMULATIVE_ARGS(CUM,FNTYPE,LIBNAME,INDIRECT) \ + ((CUM) = 0) + +/* 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.) */ + +#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \ + ((CUM) += ((MODE) != BLKmode \ + ? (GET_MODE_SIZE (MODE) + 3) & ~3 \ + : (int_size_in_bytes (TYPE) + 3) & ~3)) + +/* Define where to put the arguments 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 32000 all args are pushed, except if -mregparm is specified + then the first two words of arguments are passed in r0, r1. + *NOTE* -mregparm does not work. + It exists only to test register calling conventions. */ + +#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \ +((TARGET_REGPARM && (CUM) < 8) ? gen_rtx (REG, (MODE), (CUM) / 4) : 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) \ +((TARGET_REGPARM && (CUM) < 8 \ + && 8 < ((CUM) + ((MODE) == BLKmode \ + ? int_size_in_bytes (TYPE) \ + : GET_MODE_SIZE (MODE)))) \ + ? 2 - (CUM) / 4 : 0) + +#ifndef MAIN_FUNCTION_PROLOGUE +#define MAIN_FUNCTION_PROLOGUE +#endif + +/* + * The function prologue for the ns32k is fairly simple. + * If a frame pointer is needed (decided in reload.c ?) then + * we need assembler of the form + * + * # Save the oldframe pointer, set the new frame pointer, make space + * # on the stack and save any general purpose registers necessary + * + * enter [<general purpose regs to save>], <local stack space> + * + * movf fn, tos # Save any floating point registers necessary + * . + * . + * + * If a frame pointer is not needed we need assembler of the form + * + * # Make space on the stack + * + * adjspd <local stack space + 4> + * + * # Save any general purpose registers necessary + * + * save [<general purpose regs to save>] + * + * movf fn, tos # Save any floating point registers necessary + * . + * . + */ +#if defined(IMMEDIATE_PREFIX) && IMMEDIATE_PREFIX +#define ADJSP(FILE, n) \ + fprintf (FILE, "\tadjspd %c%d\n", IMMEDIATE_PREFIX, (n)) +#else +#define ADJSP(FILE, n) \ + fprintf (FILE, "\tadjspd %d\n", (n)) +#endif + +#define FUNCTION_PROLOGUE(FILE, SIZE) \ +{ register int regno, g_regs_used = 0; \ + int used_regs_buf[8], *bufp = used_regs_buf; \ + int used_fregs_buf[17], *fbufp = used_fregs_buf; \ + extern char call_used_regs[]; \ + extern int current_function_uses_pic_offset_table, flag_pic; \ + MAIN_FUNCTION_PROLOGUE; \ + for (regno = R0_REGNUM; regno < F0_REGNUM; regno++) \ + if (regs_ever_live[regno] \ + && ! call_used_regs[regno]) \ + { \ + *bufp++ = regno; g_regs_used++; \ + } \ + *bufp = -1; \ + for (; regno < FRAME_POINTER_REGNUM; regno++) \ + if (regs_ever_live[regno] && !call_used_regs[regno]) \ + { \ + *fbufp++ = regno; \ + } \ + *fbufp = -1; \ + bufp = used_regs_buf; \ + if (frame_pointer_needed) \ + fprintf (FILE, "\tenter ["); \ + else \ + { \ + if (SIZE) \ + ADJSP (FILE, SIZE + 4); \ + if (g_regs_used && g_regs_used > 4) \ + fprintf (FILE, "\tsave ["); \ + else \ + { \ + while (*bufp >= 0) \ + fprintf (FILE, "\tmovd r%d,tos\n", *bufp++); \ + g_regs_used = 0; \ + } \ + } \ + while (*bufp >= 0) \ + { \ + fprintf (FILE, "r%d", *bufp++); \ + if (*bufp >= 0) \ + fputc (',', FILE); \ + } \ + if (frame_pointer_needed) \ + fprintf (FILE, "],%d\n", SIZE); \ + else if (g_regs_used) \ + fprintf (FILE, "]\n"); \ + fbufp = used_fregs_buf; \ + while (*fbufp >= 0) \ + { \ + if ((*fbufp & 1) || (fbufp[0] != fbufp[1] - 1)) \ + fprintf (FILE, "\tmovf %s,tos\n", ns32k_out_reg_names[*fbufp++]); \ + else \ + { \ + fprintf (FILE, "\tmovl %s,tos\n", \ + ns32k_out_reg_names[fbufp[0]]); \ + fbufp += 2; \ + } \ + } \ + if (flag_pic && current_function_uses_pic_offset_table) \ + { \ + fprintf (FILE, "\tsprd sb,tos\n"); \ + if (TARGET_REGPARM) \ + { \ + fprintf (FILE, "\taddr __GLOBAL_OFFSET_TABLE_(pc),tos\n"); \ + fprintf (FILE, "\tlprd sb,tos\n"); \ + } \ + else \ + { \ + fprintf (FILE, "\taddr __GLOBAL_OFFSET_TABLE_(pc),r0\n"); \ + fprintf (FILE, "\tlprd sb,r0\n"); \ + } \ + } \ +} + +/* Output assembler code to FILE to increment profiler label # LABELNO + for profiling a function entry. + + THIS DEFINITION FOR THE 32000 IS A GUESS. IT HAS NOT BEEN TESTED. */ + +#define FUNCTION_PROFILER(FILE, LABELNO) \ + fprintf (FILE, "\taddr LP%d,r0\n\tbsr mcount\n", (LABELNO)) + +/* 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. + + We use 0, because using 1 requires hair in FUNCTION_EPILOGUE + that is worse than the stack adjust we could save. */ + +/* #define EXIT_IGNORE_STACK 1 */ + +/* This macro generates the assembly code for function exit, + on machines that need it. If FUNCTION_EPILOGUE is not defined + then individual return instructions are generated for each + return statement. Args are same as for FUNCTION_PROLOGUE. + + The function epilogue should not depend on the current stack pointer, + if EXIT_IGNORE_STACK is nonzero. That doesn't apply here. + + If a frame pointer is needed (decided in reload.c ?) then + we need assembler of the form + + movf tos, fn # Restore any saved floating point registers + . + . + + # Restore any saved general purpose registers, restore the stack + # pointer from the frame pointer, restore the old frame pointer. + exit [<general purpose regs to save>] + + If a frame pointer is not needed we need assembler of the form + # Restore any general purpose registers saved + + movf tos, fn # Restore any saved floating point registers + . + . + . + restore [<general purpose regs to save>] + + # reclaim space allocated on stack + + adjspd <-(local stack space + 4)> */ + + +#define FUNCTION_EPILOGUE(FILE, SIZE) \ +{ register int regno, g_regs_used = 0, f_regs_used = 0; \ + int used_regs_buf[8], *bufp = used_regs_buf; \ + int used_fregs_buf[17], *fbufp = used_fregs_buf; \ + extern char call_used_regs[]; \ + extern int current_function_uses_pic_offset_table, flag_pic; \ + if (flag_pic && current_function_uses_pic_offset_table) \ + fprintf (FILE, "\tlprd sb,tos\n"); \ + *fbufp++ = -2; \ + for (regno = F0_REGNUM; regno < FRAME_POINTER_REGNUM; regno++) \ + if (regs_ever_live[regno] && !call_used_regs[regno]) \ + { \ + *fbufp++ = regno; f_regs_used++; \ + } \ + fbufp--; \ + for (regno = 0; regno < F0_REGNUM; regno++) \ + if (regs_ever_live[regno] \ + && ! call_used_regs[regno]) \ + { \ + *bufp++ = regno; g_regs_used++; \ + } \ + while (fbufp > used_fregs_buf) \ + { \ + if ((*fbufp & 1) && fbufp[0] == fbufp[-1] + 1) \ + { \ + fprintf (FILE, "\tmovl tos,%s\n", \ + ns32k_out_reg_names[fbufp[-1]]); \ + fbufp -= 2; \ + } \ + else fprintf (FILE, "\tmovf tos,%s\n", ns32k_out_reg_names[*fbufp--]); \ + } \ + if (frame_pointer_needed) \ + fprintf (FILE, "\texit ["); \ + else \ + { \ + if (g_regs_used && g_regs_used > 4) \ + fprintf (FILE, "\trestore ["); \ + else \ + { \ + while (bufp > used_regs_buf) \ + fprintf (FILE, "\tmovd tos,r%d\n", *--bufp); \ + g_regs_used = 0; \ + } \ + } \ + while (bufp > used_regs_buf) \ + { \ + fprintf (FILE, "r%d", *--bufp); \ + if (bufp > used_regs_buf) \ + fputc (',', FILE); \ + } \ + if (g_regs_used || frame_pointer_needed) \ + fprintf (FILE, "]\n"); \ + if (SIZE && !frame_pointer_needed) \ + ADJSP (FILE, -(SIZE + 4)); \ + if (current_function_pops_args) \ + fprintf (FILE, "\tret %d\n", current_function_pops_args); \ + else fprintf (FILE, "\tret 0\n"); } + +/* 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. */ + +#define INITIAL_FRAME_POINTER_OFFSET(DEPTH) \ +{ \ + int regno; \ + int offset = -4; \ + extern int current_function_uses_pic_offset_table, flag_pic; \ + for (regno = 0; regno < L1_REGNUM; regno++) \ + if (regs_ever_live[regno] && ! call_used_regs[regno]) \ + offset += 4; \ + for (; regno < FRAME_POINTER_REGNUM; regno++) \ + if (regs_ever_live[regno] && ! call_used_regs[regno]) \ + offset += 8; \ + if (flag_pic && current_function_uses_pic_offset_table) \ + offset += 4; \ + (DEPTH) = (offset + get_frame_size () \ + + (get_frame_size () == 0 ? 0 : 4)); \ +} + + +/* Output assembler code for a block containing the constant parts + of a trampoline, leaving space for the variable parts. */ + +/* On the 32k, the trampoline looks like this: + addr 0(pc),r2 + jump @__trampoline + .int STATIC + .int FUNCTION +Doing trampolines with a library assist function is easier than figuring +out how to do stores to memory in reverse byte order (the way immediate +operands on the 32k are stored). */ + +#define TRAMPOLINE_TEMPLATE(FILE) \ +{ \ + fprintf (FILE, "\taddr 0(pc),r2\n" ); \ + fprintf (FILE, "\tjump " ); \ + PUT_ABSOLUTE_PREFIX (FILE); \ + fprintf (FILE, "__trampoline\n" ); \ + ASM_OUTPUT_INT (FILE, const0_rtx); \ + ASM_OUTPUT_INT (FILE, const0_rtx); \ +} + +/* 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. */ + +#define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT) \ +{ \ + emit_move_insn (gen_rtx (MEM, SImode, plus_constant (TRAMP, 12)), CXT); \ + emit_move_insn (gen_rtx (MEM, SImode, plus_constant (TRAMP, 16)), FNADDR); \ +} + +/* This is the library routine that is used + to transfer control from the trampoline + to the actual nested function. */ + +/* The function name __transfer_from_trampoline is not actually used. + The function definition just permits use of "asm with operands" + (though the operand list is empty). */ +#define TRANSFER_FROM_TRAMPOLINE \ +void \ +__transfer_from_trampoline () \ +{ \ + asm (".globl __trampoline"); \ + asm ("__trampoline:"); \ + asm ("movd 16(r2),tos"); \ + asm ("movd 12(r2),r1"); \ + asm ("ret 0"); \ +} + +/* Addressing modes, and classification of registers for them. */ + +/* #define HAVE_POST_INCREMENT 0 */ +/* #define HAVE_POST_DECREMENT 0 */ + +/* #define HAVE_PRE_DECREMENT 0 */ +/* #define HAVE_PRE_INCREMENT 0 */ + +/* 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. */ + +/* note that FP and SP cannot be used as an index. What about PC? */ +#define REGNO_OK_FOR_INDEX_P(REGNO) \ +((REGNO) < F0_REGNUM || (unsigned)reg_renumber[REGNO] < F0_REGNUM) +#define REGNO_OK_FOR_BASE_P(REGNO) \ +((REGNO) < F0_REGNUM || (unsigned)reg_renumber[REGNO] < F0_REGNUM \ + || (REGNO) == FRAME_POINTER_REGNUM || (REGNO) == STACK_POINTER_REGNUM) + +#define FP_REG_P(X) \ + (GET_CODE (X) == REG && REGNO (X) >= F0_REGNUM && REGNO (X) < FRAME_POINTER_REGNUM) + +/* 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. + This might not work on future ns32k processors as negative + displacements are not officially allowed but a mode reserved + to National. This works on processors up to 32532, though, + and we don't expect any new ones in the series ;-( */ + +#define CONSTANT_ADDRESS_P(X) \ + (GET_CODE (X) == LABEL_REF || GET_CODE (X) == SYMBOL_REF \ + || GET_CODE (X) == CONST \ + || (GET_CODE (X) == CONST_INT \ + && NS32K_DISPLACEMENT_P (INTVAL (X)))) + +#define CONSTANT_ADDRESS_NO_LABEL_P(X) \ + (GET_CODE (X) == CONST_INT \ + && NS32K_DISPLACEMENT_P (INTVAL (X))) + +/* Return the register class of a scratch register needed to copy IN into + or out of a register in CLASS in MODE. If it can be done directly, + NO_REGS is returned. */ + +#define SECONDARY_RELOAD_CLASS(CLASS,MODE,IN) \ + secondary_reload_class (CLASS, MODE, IN) + +/* Certain machines have the property that some registers cannot be + copied to some other registers without using memory. Define this + macro on those machines to be a C expression that is non-zero if + objects of mode M in registers of CLASS1 can only be copied to + registers of class CLASS2 by storing a register of CLASS1 into + memory and loading that memory location into a register of CLASS2. + + On the ns32k, floating point regs can only be loaded through memory + + The movdf and movsf insns in ns32k.md copy between general and + floating registers using the stack. In principle, we could get + better code not allowing that case in the constraints and defining + SECONDARY_MEMORY_NEEDED in practice, though the stack slots used + are not available for optimization. */ + +#if 0 +#define SECONDARY_MEMORY_NEEDED(CLASS1, CLASS2, M) \ + secondary_memory_needed(CLASS1, CLASS2, M) +#endif + +/* SMALL_REGISTER_CLASSES is true only if we have said we are using the + * multiply-add instructions. + */ +#define SMALL_REGISTER_CLASSES (target_flags & 512) + +/* A C expression whose value is nonzero if pseudos that have been + assigned to registers of class CLASS would likely be spilled + because registers of CLASS are needed for spill registers. + + The default definition won't do because class LONG_FLOAT_REG0 has two + registers which are always acessed as a pair */ + +#define CLASS_LIKELY_SPILLED_P(CLASS) \ + (reg_class_size[(int) (CLASS)] == 1 || (CLASS) == LONG_FLOAT_REG0) + + +/* Nonzero if the constant value X is a legitimate general operand. + It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE. */ + +#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) \ + (REGNO (X) < F0_REGNUM || REGNO (X) >= FIRST_PSEUDO_REGISTER) +/* Nonzero if X is a hard reg that can be used as a base reg + of if it is a pseudo reg. */ +#define REG_OK_FOR_BASE_P(X) (REGNO (X) < F0_REGNUM || REGNO (X) >= FRAME_POINTER_REGNUM) +/* Nonzero if X is a floating point reg or a pseudo reg. */ + +#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. + + The other macros defined here are used only in GO_IF_LEGITIMATE_ADDRESS. */ + +/* 1 if X is an address that we could indirect through. */ +/***** NOTE ***** There is a bug in the Sequent assembler which fails + to fixup addressing information for symbols used as offsets + from registers which are not FP or SP (or SB or PC). This + makes _x(fp) valid, while _x(r0) is invalid. */ + +#define INDIRECTABLE_1_ADDRESS_P(X) \ + (CONSTANT_ADDRESS_P (X) \ + || (GET_CODE (X) == REG && REG_OK_FOR_BASE_P (X)) \ + || (GET_CODE (X) == PLUS \ + && GET_CODE (XEXP (X, 0)) == REG \ + && REG_OK_FOR_BASE_P (XEXP (X, 0)) \ + && ((flag_pic || TARGET_HIMEM) ? \ + CONSTANT_ADDRESS_NO_LABEL_P (XEXP (X, 1)) \ + : \ + CONSTANT_ADDRESS_P (XEXP (X, 1))) \ + && (GET_CODE (X) != CONST_INT || NS32K_DISPLACEMENT_P (INTVAL (X))))) + +/* 1 if integer I will fit in a 4 byte displacement field. + Strictly speaking, we can't be sure that a symbol will fit this range. + But, in practice, it always will. */ + +/* idall@eleceng.adelaide.edu.au says that the 32016 and 32032 + can handle the full range of displacements--it is only the addresses + that have a limited range. So the following was deleted: + (((i) <= 16777215 && (i) >= -16777216) + || ((TARGET_32532 || TARGET_32332) && ...)) */ +#define NS32K_DISPLACEMENT_P(i) \ + ((i) < (1 << 29) && (i) >= - (1 << 29)) + +/* Check for frame pointer or stack pointer. */ +#define MEM_REG(X) \ + (GET_CODE (X) == REG && (REGNO (X) == FRAME_POINTER_REGNUM \ + || REGNO(X) == STACK_POINTER_REGNUM)) + +/* A memory ref whose address is the FP or SP, with optional integer offset, + or (on certain machines) a constant address. */ +#define INDIRECTABLE_2_ADDRESS_P(X) \ + (GET_CODE (X) == MEM \ + && (((xfoo0 = XEXP (X, 0), MEM_REG (xfoo0)) \ + || (GET_CODE (xfoo0) == PLUS \ + && MEM_REG (XEXP (xfoo0, 0)) \ + && CONSTANT_ADDRESS_NO_LABEL_P (XEXP (xfoo0, 1)))) \ + || (TARGET_SB && CONSTANT_ADDRESS_P (xfoo0)))) + +/* Go to ADDR if X is a valid address not using indexing. + (This much is the easy part.) */ +#define GO_IF_NONINDEXED_ADDRESS(X, ADDR) \ +{ register rtx xfoob = (X); \ + if (INDIRECTABLE_1_ADDRESS_P (X)) goto ADDR; \ + if (INDIRECTABLE_2_ADDRESS_P (X)) goto ADDR; \ + if (GET_CODE (X) == PLUS) \ + if (CONSTANT_ADDRESS_NO_LABEL_P (XEXP (X, 1))) \ + if (INDIRECTABLE_2_ADDRESS_P (XEXP (X, 0))) \ + goto ADDR; \ +} + +/* Go to ADDR if X is a valid address not using indexing. + (This much is the easy part.) */ +#define GO_IF_INDEXING(X, MODE, ADDR) \ +{ register rtx xfoob = (X); \ + if (GET_CODE (xfoob) == PLUS && INDEX_TERM_P (XEXP (xfoob, 0), MODE)) \ + GO_IF_INDEXABLE_ADDRESS (XEXP (xfoob, 1), ADDR); \ + if (GET_CODE (xfoob) == PLUS && INDEX_TERM_P (XEXP (xfoob, 1), MODE)) \ + GO_IF_INDEXABLE_ADDRESS (XEXP (xfoob, 0), ADDR); } \ + +#define GO_IF_INDEXABLE_ADDRESS(X, ADDR) \ +{ if (GET_CODE (X) == REG && REG_OK_FOR_BASE_P (X)) goto ADDR; \ + if (INDIRECTABLE_2_ADDRESS_P (X)) goto ADDR; \ + if (INDIRECTABLE_1_ADDRESS_P (X)) goto ADDR; \ +} + +/* 1 if PROD is either a reg times size of mode MODE + or just a reg, if MODE is just one byte. Actually, on the ns32k, + since the index mode is independent of the operand size, + we can match more stuff... + + This macro's expansion uses the temporary variables xfoo0, xfoo1 + and xfoo2 that must be declared in the surrounding context. */ +#define INDEX_TERM_P(PROD, MODE) \ +((GET_CODE (PROD) == REG && REG_OK_FOR_INDEX_P (PROD)) \ + || (GET_CODE (PROD) == MULT \ + && (xfoo0 = XEXP (PROD, 0), xfoo1 = XEXP (PROD, 1), \ + (GET_CODE (xfoo1) == CONST_INT \ + && GET_CODE (xfoo0) == REG \ + && FITS_INDEX_RANGE (INTVAL (xfoo1)) \ + && REG_OK_FOR_INDEX_P (xfoo0))))) + +#define FITS_INDEX_RANGE(X) \ + ((xfoo2 = (unsigned)(X)-1), \ + ((xfoo2 < 4 && xfoo2 != 2) || xfoo2 == 7)) + +/* Note that xfoo0, xfoo1, xfoo2 are used in some of the submacros above. */ +#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \ +{ register rtx xfooy, xfoo0, xfoo1; \ + unsigned xfoo2; \ + extern int current_function_uses_pic_offset_table, flag_pic; \ + xfooy = X; \ + if (flag_pic && ! current_function_uses_pic_offset_table \ + && global_symbolic_reference_mentioned_p (X, 1)) \ + current_function_uses_pic_offset_table = 1; \ + GO_IF_NONINDEXED_ADDRESS (xfooy, ADDR); \ + if (GET_CODE (xfooy) == PLUS) \ + { \ + if (CONSTANT_ADDRESS_NO_LABEL_P (XEXP (xfooy, 1)) \ + && GET_CODE (XEXP (xfooy, 0)) == PLUS) \ + xfooy = XEXP (xfooy, 0); \ + else if (CONSTANT_ADDRESS_NO_LABEL_P (XEXP (xfooy, 0)) \ + && GET_CODE (XEXP (xfooy, 1)) == PLUS) \ + xfooy = XEXP (xfooy, 1); \ + GO_IF_INDEXING (xfooy, MODE, ADDR); \ + } \ + else if (INDEX_TERM_P (xfooy, MODE)) \ + goto ADDR; \ + else if (GET_CODE (xfooy) == PRE_DEC) \ + if (REGNO (XEXP (xfooy, 0)) == STACK_POINTER_REGNUM) goto ADDR; \ + else abort (); \ +} + +/* 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. + + For the ns32k, we do nothing */ + +#define LEGITIMIZE_ADDRESS(X,OLDX,MODE,WIN) {} + +/* Nonzero if the constant value X is a legitimate general operand + when generating PIC code. It is given that flag_pic is on and + that X satisfies CONSTANT_P or is a CONST_DOUBLE. */ + +extern int current_function_uses_pic_offset_table, flag_pic; +#define LEGITIMATE_PIC_OPERAND_P(X) \ + (((! current_function_uses_pic_offset_table \ + && symbolic_reference_mentioned_p (X))? \ + (current_function_uses_pic_offset_table = 1):0 \ + ), (! SYMBOLIC_CONST (X) \ + || GET_CODE (X) == SYMBOL_REF || GET_CODE (X) == LABEL_REF)) + +#define SYMBOLIC_CONST(X) \ +(GET_CODE (X) == SYMBOL_REF \ + || GET_CODE (X) == LABEL_REF \ + || (GET_CODE (X) == CONST && symbolic_reference_mentioned_p (X))) + +/* Define this macro if references to a symbol must be treated + differently depending on something about the variable or + function named by the symbol (such as what section it is in). + + On the ns32k, if using PIC, mark a SYMBOL_REF for a non-global + symbol or a code symbol. These symbols are referenced via pc + and not via sb. */ + +#define ENCODE_SECTION_INFO(DECL) \ +do \ + { \ + extern int flag_pic; \ + if (flag_pic) \ + { \ + rtx rtl = (TREE_CODE_CLASS (TREE_CODE (DECL)) != 'd' \ + ? TREE_CST_RTL (DECL) : DECL_RTL (DECL)); \ + SYMBOL_REF_FLAG (XEXP (rtl, 0)) \ + = (TREE_CODE_CLASS (TREE_CODE (DECL)) != 'd' \ + || ! TREE_PUBLIC (DECL)); \ + } \ + } \ +while (0) + +/* Go to LABEL if ADDR (a legitimate address expression) + has an effect that depends on the machine mode it is used for. + On the ns32k, only predecrement and postincrement address depend thus + (the amount of decrement or increment being the length of the operand). */ + +#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR,LABEL) \ + { if (GET_CODE (ADDR) == POST_INC || GET_CODE (ADDR) == PRE_DEC) \ + goto LABEL;} + +/* If defined, a C expression whose value is nonzero if IDENTIFIER + with arguments ARGS is a valid machine specific attribute for DECL. + The attributes in ATTRIBUTES have previously been assigned to DECL. */ + +#define VALID_MACHINE_DECL_ATTRIBUTE(DECL, ATTRIBUTES, NAME, ARGS) \ + (ns32k_valid_decl_attribute_p (DECL, ATTRIBUTES, NAME, ARGS)) + +/* If defined, a C expression whose value is nonzero if IDENTIFIER + with arguments ARGS is a valid machine specific attribute for TYPE. + The attributes in ATTRIBUTES have previously been assigned to TYPE. */ + +#define VALID_MACHINE_TYPE_ATTRIBUTE(TYPE, ATTRIBUTES, NAME, ARGS) \ + (ns32k_valid_type_attribute_p (TYPE, ATTRIBUTES, NAME, ARGS)) + +/* If defined, a C expression whose value is zero if the attributes on + TYPE1 and TYPE2 are incompatible, one if they are compatible, and + two if they are nearly compatible (which causes a warning to be + generated). */ + +#define COMP_TYPE_ATTRIBUTES(TYPE1, TYPE2) \ + (ns32k_comp_type_attributes (TYPE1, TYPE2)) + +/* If defined, a C statement that assigns default attributes to newly + defined TYPE. */ + +/* #define SET_DEFAULT_TYPE_ATTRIBUTES (TYPE) */ + +/* Specify the machine mode that this machine uses + for the index in the tablejump instruction. + HI mode is more efficient but the range is not wide enough for + all programs. */ +#define CASE_VECTOR_MODE SImode + +/* Define as C expression which evaluates to nonzero 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 1 + +/* 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 + +/* 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 4 + +/* The number of scalar move insns which should be generated instead + of a string move insn or a library call. + + We have a smart movstrsi insn */ +#define MOVE_RATIO 0 + +/* Define this if zero-extension is slow (more than one real instruction). */ +/* #define SLOW_ZERO_EXTEND */ + +/* Nonzero if access to memory by bytes is slow and undesirable. */ +#define SLOW_BYTE_ACCESS 0 + +/* 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 */ + +/* 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 + +/* We assume that the store-condition-codes instructions store 0 for false + and some other value for true. This is the value stored for true. */ + +#define STORE_FLAG_VALUE 1 + +/* 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 QImode + +/* Compute the cost of address ADDRESS. */ + +#define ADDRESS_COST(RTX) calc_address_cost (RTX) + +/* 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) <= 7 && INTVAL (RTX) >= -8) return 0; \ + if (INTVAL (RTX) < 0x2000 && INTVAL (RTX) >= -0x2000) \ + return 1; \ + case CONST: \ + case LABEL_REF: \ + case SYMBOL_REF: \ + return 3; \ + case CONST_DOUBLE: \ + return 5; + +/* Tell final.c how to eliminate redundant test instructions. */ + +/* Here we define machine-dependent flags and fields in cc_status + (see `conditions.h'). */ + +/* This bit means that what ought to be in the Z bit + should be tested in the F bit. */ +#define CC_Z_IN_F 04000 + +/* This bit means that what ought to be in the Z bit + is complemented in the F bit. */ +#define CC_Z_IN_NOT_F 010000 + +/* 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. */ + +#define NOTICE_UPDATE_CC(EXP, INSN) \ +{ if (GET_CODE (EXP) == SET) \ + { if (GET_CODE (SET_DEST (EXP)) == CC0) \ + { cc_status.flags = 0; \ + cc_status.value1 = SET_DEST (EXP); \ + cc_status.value2 = SET_SRC (EXP); \ + } \ + else if (GET_CODE (SET_SRC (EXP)) == CALL) \ + { CC_STATUS_INIT; } \ + else if (GET_CODE (SET_DEST (EXP)) == REG) \ + { if (cc_status.value1 \ + && reg_overlap_mentioned_p (SET_DEST (EXP), cc_status.value1)) \ + cc_status.value1 = 0; \ + if (cc_status.value2 \ + && reg_overlap_mentioned_p (SET_DEST (EXP), cc_status.value2)) \ + cc_status.value2 = 0; \ + } \ + else if (GET_CODE (SET_DEST (EXP)) == MEM) \ + { CC_STATUS_INIT; } \ + } \ + else if (GET_CODE (EXP) == PARALLEL \ + && GET_CODE (XVECEXP (EXP, 0, 0)) == SET) \ + { if (GET_CODE (SET_DEST (XVECEXP (EXP, 0, 0))) == CC0) \ + { cc_status.flags = 0; \ + cc_status.value1 = SET_DEST (XVECEXP (EXP, 0, 0)); \ + cc_status.value2 = SET_SRC (XVECEXP (EXP, 0, 0)); \ + } \ + else if (GET_CODE (SET_DEST (XVECEXP (EXP, 0, 0))) == REG) \ + { if (cc_status.value1 \ + && reg_overlap_mentioned_p (SET_DEST (XVECEXP (EXP, 0, 0)), cc_status.value1)) \ + cc_status.value1 = 0; \ + if (cc_status.value2 \ + && reg_overlap_mentioned_p (SET_DEST (XVECEXP (EXP, 0, 0)), cc_status.value2)) \ + cc_status.value2 = 0; \ + } \ + else if (GET_CODE (SET_DEST (XVECEXP (EXP, 0, 0))) == MEM) \ + { CC_STATUS_INIT; } \ + } \ + else if (GET_CODE (EXP) == CALL) \ + { /* all bets are off */ CC_STATUS_INIT; } \ + else { /* nothing happens? CC_STATUS_INIT; */} \ + if (cc_status.value1 && GET_CODE (cc_status.value1) == REG \ + && cc_status.value2 \ + && reg_overlap_mentioned_p (cc_status.value1, cc_status.value2)) \ + abort (); \ +} + +/* Describe the costs of the following register moves which are discouraged: + 1.) Moves between the Floating point registers and the frame pointer and stack pointer + 2.) Moves between the stack pointer and the frame pointer + 3.) Moves between the floating point and general registers + + These all involve two memory references. This is worse than a memory + to memory move (default cost 4) + */ + +#define REGISTER_MOVE_COST(CLASS1, CLASS2) register_move_cost(CLASS1, CLASS2) + +#define OUTPUT_JUMP(NORMAL, NO_OV) \ +{ if (cc_status.flags & CC_NO_OVERFLOW) \ + return NO_OV; \ + return NORMAL; } + +/* Dividing the output into sections */ + +/* Output before read-only data. */ + +#define TEXT_SECTION_ASM_OP ".text" + +/* Output before writable data. */ + +#define DATA_SECTION_ASM_OP ".data" + +/* Define the output Assembly Language */ + +/* Output at beginning of assembler file. */ + +#define ASM_FILE_START(FILE) fprintf (FILE, "#NO_APP\n"); + +/* Output to assembler file text saying following lines + may contain character constants, extra white space, comments, etc. */ + +#define ASM_APP_ON "#APP\n" + +/* Output to assembler file text saying following lines + no longer contain unusual constructs. */ + +#define ASM_APP_OFF "#NO_APP\n" + +/* Output of Data */ + +/* This is how to output an assembler line defining a `double' constant. */ + +#define ASM_OUTPUT_DOUBLE(FILE,VALUE) \ + fprintf (FILE, "\t.double 0d%.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 0f%.20e\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.word "), \ + 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 an assembler line defining an external/static + address which is not in tree format (for collect.c). */ + +/* The prefix to add to user-visible assembler symbols. */ +#define USER_LABEL_PREFIX "_" + +/* This is how to output an insn to push a register on the stack. + It need not be very fast code. */ + +#define ASM_OUTPUT_REG_PUSH(FILE,REGNO) \ + fprintf (FILE, "\tmovd %s,tos\n", reg_names[REGNO]) + +/* 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, "\tmovd tos,%s\n", reg_names[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. */ + +#ifndef COLLECT +#define ASM_OUTPUT_LABEL(FILE,NAME) \ + do { assemble_name (FILE, NAME); fputs (":\n", FILE); } while (0) +#else +#define ASM_OUTPUT_LABEL(STREAM,NAME) \ +do { \ + fprintf (STREAM, "%s:\n", NAME); \ +} while (0) +#endif + +/* This is how to output a command to make the user-level label named NAME + defined for reference from other files. */ + +#ifndef COLLECT +#define ASM_GLOBALIZE_LABEL(FILE,NAME) \ + do { fputs (".globl ", FILE); assemble_name (FILE, NAME); fputs ("\n", FILE);} while (0) +#else +#define ASM_GLOBALIZE_LABEL(STREAM,NAME) \ +do { \ + fprintf (STREAM, "\t.globl\t%s\n", NAME); \ +} while (0) +#endif + +/* This is how to output a reference to a user-level label named NAME. + `assemble_name' uses this. */ + +#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 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 align the code that follows an unconditional branch. */ + +#define LABEL_ALIGN_AFTER_BARRIER(LABEL) (2) + +/* This is how to output an element of a case-vector that is absolute. + (The ns32k does not use such vectors, + but we must define this macro anyway.) */ + +#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. */ +/* ** Notice that the second element is LI format! */ +#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \ + fprintf (FILE, "\t.long L%d-LI%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) \ + fprintf (FILE, "\t.align %d\n", (LOG)) + +#define ASM_OUTPUT_SKIP(FILE,SIZE) \ + fprintf (FILE, "\t.space %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 an instruction operand X on file FILE. + CODE is the code from the %-spec that requested printing this operand; + if `%z3' was used to print operand 3, then CODE is 'z'. */ + +/* %$ means print the prefix for an immediate operand. */ + +#define PRINT_OPERAND_PUNCT_VALID_P(CODE) \ + ((CODE) == '$' || (CODE) == '?') + +#define PRINT_OPERAND(FILE, X, CODE) print_operand(FILE, X, CODE) + +/* Print a memory operand whose address is X, on file FILE. */ + +#define PRINT_OPERAND_ADDRESS(FILE, ADDR) print_operand_address(FILE, ADDR) + +/* Prototypes for functions in ns32k.c */ + +/* Prototypes would be nice, but for now it causes too many problems. + This file gets included in places where the types (such as "rtx" + and enum machine_mode) are not defined. */ +#define NS32K_PROTO(ARGS) () + +int hard_regno_mode_ok NS32K_PROTO((int regno, enum machine_mode mode)); +int register_move_cost NS32K_PROTO((enum reg_class CLASS1, enum reg_class CLASS2)); +int calc_address_cost NS32K_PROTO((rtx operand)); +enum reg_class secondary_reload_class NS32K_PROTO((enum reg_class class, + enum machine_mode mode, rtx in)); +int reg_or_mem_operand NS32K_PROTO((register rtx op, enum machine_mode mode)); + +void split_di NS32K_PROTO((rtx operands[], int num, rtx lo_half[], hi_half[])); + +void expand_block_move NS32K_PROTO((rtx operands[])); +int global_symbolic_reference_mentioned_p NS32K_PROTO((rtx op, int f)); +int ns32k_comp_type_attributes NS32K_PROTO((tree type1, tree type2)); +int ns32k_return_pops_args NS32K_PROTO((tree fundecl, tree funtype, int size)); +int ns32k_valid_decl_attribute_p NS32K_PROTO((tree decl, tree attributes, + tree identifier, tree args)); +int ns32k_valid_type_attribute_p NS32K_PROTO((tree decl, tree attributes, + tree identifier, tree args)); +void print_operand NS32K_PROTO((FILE *file, rtx x, char code)); +void print_operand_address NS32K_PROTO((register FILE *file, register rtx addr)); +char *output_move_dconst NS32K_PROTO((int n, char *s)); +char *output_move_double NS32K_PROTO((rtx *operands)); +char *output_shift_insn NS32K_PROTO((rtx *operands)); + +extern unsigned int ns32k_reg_class_contents[N_REG_CLASSES]; +extern char *ns32k_out_reg_names[]; +extern enum reg_class regclass_map[]; /* smalled class containing REGNO */ + +/* +Local variables: +version-control: t +End: +*/ diff --git a/gcc/config/ns32k/ns32k.md b/gcc/config/ns32k/ns32k.md new file mode 100755 index 0000000..161b74e --- /dev/null +++ b/gcc/config/ns32k/ns32k.md @@ -0,0 +1,3058 @@ +;;- Machine description for GNU compiler, ns32000 Version +;; Copyright (C) 1988, 1994, 1996 Free Software Foundation, Inc. +;; Contributed by Michael Tiemann (tiemann@cygnus.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. + + +; BUGS: +;; Insert no-op between an insn with memory read-write operands +;; following by a scale-indexing operation. +;; The Sequent assembler does not allow addresses to be used +;; except in insns which explicitly compute an effective address. +;; I.e., one cannot say "cmpd _p,@_x" +;; Implement unsigned multiplication?? + +;;- Instruction patterns. When multiple patterns apply, +;;- the first one in the file is chosen. +;;- +;;- 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. + +;; We don't want to allow a constant operand for test insns because +;; (set (cc0) (const_int foo)) has no mode information. Such insns will +;; be folded while optimizing anyway. +;; +;; In order for pic mode to work we cannot generate, for example +;; +;; addd _x+5,r1 +;; +;; instead we must force gcc to generate something like +;; +;; addr 5(_x(sb)),r0 +;; addd r0,r1 +;; +;; This was done through operand constraints (using "rmn" in place of "g"), +;; but with the proper definition of LEGITIMATE_PIC_OPERAND (ns32k.h) +;; this is unnecessary. +;; +(define_insn "tstsi" + [(set (cc0) + (match_operand:SI 0 "nonimmediate_operand" "rm"))] + "" + "* +{ cc_status.flags |= CC_REVERSED; + operands[1] = const0_rtx; + return \"cmpqd %1,%0\"; }") + +(define_insn "tsthi" + [(set (cc0) + (match_operand:HI 0 "nonimmediate_operand" "g"))] + "" + "* +{ cc_status.flags |= CC_REVERSED; + operands[1] = const0_rtx; + return \"cmpqw %1,%0\"; }") + +(define_insn "tstqi" + [(set (cc0) + (match_operand:QI 0 "nonimmediate_operand" "g"))] + "" + "* +{ cc_status.flags |= CC_REVERSED; + operands[1] = const0_rtx; + return \"cmpqb %1,%0\"; }") + +(define_insn "tstdf" + [(set (cc0) + (match_operand:DF 0 "general_operand" "lmF"))] + "TARGET_32081" + "* +{ cc_status.flags |= CC_REVERSED; + operands[1] = CONST0_RTX (DFmode); + return \"cmpl %1,%0\"; }") + +(define_insn "tstsf" + [(set (cc0) + (match_operand:SF 0 "general_operand" "fmF"))] + "TARGET_32081" + "* +{ cc_status.flags |= CC_REVERSED; + operands[1] = CONST0_RTX (SFmode); + return \"cmpf %1,%0\"; }") + +;; See note 1 +(define_insn "cmpsi" + [(set (cc0) + (compare (match_operand:SI 0 "general_operand" "g") + (match_operand:SI 1 "general_operand" "g")))] + "" + "* +{ + if (GET_CODE (operands[1]) == CONST_INT) + { + int i = INTVAL (operands[1]); + if (i <= 7 && i >= -8) + { + cc_status.flags |= CC_REVERSED; + return \"cmpqd %1,%0\"; + } + } + cc_status.flags &= ~CC_REVERSED; + if (GET_CODE (operands[0]) == CONST_INT) + { + int i = INTVAL (operands[0]); + if (i <= 7 && i >= -8) + return \"cmpqd %0,%1\"; + } + return \"cmpd %0,%1\"; +}") + +(define_insn "cmphi" + [(set (cc0) + (compare (match_operand:HI 0 "general_operand" "g") + (match_operand:HI 1 "general_operand" "g")))] + "" + "* +{ + if (GET_CODE (operands[1]) == CONST_INT) + { + short i = INTVAL (operands[1]); + if (i <= 7 && i >= -8) + { + cc_status.flags |= CC_REVERSED; + if (INTVAL (operands[1]) > 7) + operands[1] = GEN_INT (i); + return \"cmpqw %1,%0\"; + } + } + cc_status.flags &= ~CC_REVERSED; + if (GET_CODE (operands[0]) == CONST_INT) + { + short i = INTVAL (operands[0]); + if (i <= 7 && i >= -8) + { + if (INTVAL (operands[0]) > 7) + operands[0] = GEN_INT (i); + return \"cmpqw %0,%1\"; + } + } + return \"cmpw %0,%1\"; +}") + +(define_insn "cmpqi" + [(set (cc0) + (compare (match_operand:QI 0 "general_operand" "g") + (match_operand:QI 1 "general_operand" "g")))] + "" + "* +{ + if (GET_CODE (operands[1]) == CONST_INT) + { + char i = INTVAL (operands[1]); + if (i <= 7 && i >= -8) + { + cc_status.flags |= CC_REVERSED; + if (INTVAL (operands[1]) > 7) + operands[1] = GEN_INT (i); + return \"cmpqb %1,%0\"; + } + } + cc_status.flags &= ~CC_REVERSED; + if (GET_CODE (operands[0]) == CONST_INT) + { + char i = INTVAL (operands[0]); + if (i <= 7 && i >= -8) + { + if (INTVAL (operands[0]) > 7) + operands[0] = GEN_INT (i); + return \"cmpqb %0,%1\"; + } + } + return \"cmpb %0,%1\"; +}") + +(define_insn "cmpdf" + [(set (cc0) + (compare (match_operand:DF 0 "general_operand" "lmF") + (match_operand:DF 1 "general_operand" "lmF")))] + "TARGET_32081" + "cmpl %0,%1") + +(define_insn "cmpsf" + [(set (cc0) + (compare (match_operand:SF 0 "general_operand" "fmF") + (match_operand:SF 1 "general_operand" "fmF")))] + "TARGET_32081" + "cmpf %0,%1") + +;; movdf and movsf copy between general and floating registers using +;; the stack. In principle, we could get better code not allowing +;; that case in the constraints and defining SECONDARY_MEMORY_NEEDED +;; in practice, though the stack slots used are not available for +;; optimization. +(define_insn "movdf" + [(set (match_operand:DF 0 "general_operand" "=lg<") + (match_operand:DF 1 "general_operand" "lFg"))] + "" + "* +{ + if (FP_REG_P (operands[0])) + { + if (FP_REG_P (operands[1]) || GET_CODE (operands[1]) == CONST_DOUBLE) + return \"movl %1,%0\"; + if (REG_P (operands[1])) + { + rtx xoperands[2]; + xoperands[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1); + output_asm_insn (\"movd %1,tos\", xoperands); + output_asm_insn (\"movd %1,tos\", operands); + return \"movl tos,%0\"; + } + return \"movl %1,%0\"; + } + else if (FP_REG_P (operands[1])) + { + if (REG_P (operands[0])) + { + output_asm_insn (\"movl %1,tos\;movd tos,%0\", operands); + operands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); + return \"movd tos,%0\"; + } + else + return \"movl %1,%0\"; + } + return output_move_double (operands); +}") + +(define_insn "movsf" + [(set (match_operand:SF 0 "general_operand" "=fg<") + (match_operand:SF 1 "general_operand" "fFg"))] + "" + "* +{ + if (FP_REG_P (operands[0])) + { + if (GET_CODE (operands[1]) == REG && REGNO (operands[1]) < F0_REGNUM) + return \"movd %1,tos\;movf tos,%0\"; + else + return \"movf %1,%0\"; + } + else if (FP_REG_P (operands[1])) + { + if (REG_P (operands[0])) + return \"movf %1,tos\;movd tos,%0\"; + return \"movf %1,%0\"; + } +#if 0 /* Someone suggested this for the Sequent. Is it needed? */ + else if (GET_CODE (operands[1]) == CONST_DOUBLE) + return \"movf %1,%0\"; +#endif +/* There was a #if 0 around this, but that was erroneous + for many machines -- rms. */ +#ifndef MOVD_FLOAT_OK + /* GAS understands floating constants in ordinary movd instructions + but other assemblers might object. */ + else if (GET_CODE (operands[1]) == CONST_DOUBLE) + { + union {int i[2]; float f; double d;} convrt; + convrt.i[0] = CONST_DOUBLE_LOW (operands[1]); + convrt.i[1] = CONST_DOUBLE_HIGH (operands[1]); + convrt.f = convrt.d; + + /* Is there a better machine-independent way to do this? */ + operands[1] = GEN_INT (convrt.i[0]); + return \"movd %1,%0\"; + } +#endif + else return \"movd %1,%0\"; +}") + +(define_insn "" + [(set (match_operand:TI 0 "memory_operand" "=m") + (match_operand:TI 1 "memory_operand" "m"))] + "" + "movmd %1,%0,4") + +(define_insn "movdi" + [(set (match_operand:DI 0 "general_operand" "=g<,*f,g") + (match_operand:DI 1 "general_operand" "gF,g,*f"))] + "" + "* +{ + if (FP_REG_P (operands[0])) + { + if (FP_REG_P (operands[1]) || GET_CODE (operands[1]) == CONST_DOUBLE) + return \"movl %1,%0\"; + if (REG_P (operands[1])) + { + rtx xoperands[2]; + xoperands[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1); + output_asm_insn (\"movd %1,tos\", xoperands); + output_asm_insn (\"movd %1,tos\", operands); + return \"movl tos,%0\"; + } + return \"movl %1,%0\"; + } + else if (FP_REG_P (operands[1])) + { + if (REG_P (operands[0])) + { + output_asm_insn (\"movl %1,tos\;movd tos,%0\", operands); + operands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1); + return \"movd tos,%0\"; + } + else + return \"movl %1,%0\"; + } + return output_move_double (operands); +}") + +;; This special case must precede movsi. +(define_insn "" + [(set (reg:SI 25) + (match_operand:SI 0 "general_operand" "g"))] + "" + "lprd sp,%0") + +(define_insn "movsi" + [(set (match_operand:SI 0 "general_operand" "=g<,g<,*f,g,x") + (match_operand:SI 1 "general_operand" "g,?xy,g,*f,rmn"))] + "" + "* +{ + extern int flag_pic; \ + + if (FP_REG_P (operands[0])) + { + if (GET_CODE (operands[1]) == REG && REGNO (operands[1]) < F0_REGNUM) + return \"movd %1,tos\;movf tos,%0\"; + else + return \"movf %1,%0\"; + } + else if (FP_REG_P (operands[1])) + { + if (REG_P (operands[0])) + return \"movf %1,tos\;movd tos,%0\"; + return \"movf %1,%0\"; + } + if (GET_CODE (operands[0]) == REG + && REGNO (operands[0]) == FRAME_POINTER_REGNUM) + return \"lprd fp,%1\"; + if (GET_CODE (operands[1]) == CONST_DOUBLE) + operands[1] + = GEN_INT (CONST_DOUBLE_LOW (operands[1])); + if (GET_CODE (operands[1]) == CONST_INT) + { + int i = INTVAL (operands[1]); + if (! TARGET_32532) + { + if (i <= 7 && i >= -8) + return \"movqd %1,%0\"; + if (NS32K_DISPLACEMENT_P (i)) +#if defined (GNX_V3) || defined (UTEK_ASM) + return \"addr %c1,%0\"; +#else + return \"addr @%c1,%0\"; +#endif + return \"movd %1,%0\"; + } + else + return output_move_dconst(i, \"%1,%0\"); + } + else if (GET_CODE (operands[1]) == CONST && ! flag_pic) + { + /* Must contain symbols so we don`t know how big it is. In + * that case addr might lead to overflow. For PIC symbolic + * address loads always have to be done with addr. + */ + return \"movd %1,%0\"; + } + else if (GET_CODE (operands[1]) == REG) + { + if (REGNO (operands[1]) < F0_REGNUM) + return \"movd %1,%0\"; + else if (REGNO (operands[1]) == FRAME_POINTER_REGNUM) + { + if (GET_CODE(operands[0]) == REG) + return \"sprd fp,%0\"; + else + return \"addr 0(fp),%0\" ; + } + else if (REGNO (operands[1]) == STACK_POINTER_REGNUM) + { + if (GET_CODE(operands[0]) == REG) + return \"sprd sp,%0\"; + else + return \"addr 0(sp),%0\" ; + } + else abort (); + } + else if (GET_CODE (operands[1]) == MEM) + return \"movd %1,%0\"; + + /* Check if this effective address can be + calculated faster by pulling it apart. */ + if (REG_P (operands[0]) + && GET_CODE (operands[1]) == MULT + && GET_CODE (XEXP (operands[1], 1)) == CONST_INT + && (INTVAL (XEXP (operands[1], 1)) == 2 + || INTVAL (XEXP (operands[1], 1)) == 4)) + { + rtx xoperands[3]; + xoperands[0] = operands[0]; + xoperands[1] = XEXP (operands[1], 0); + xoperands[2] = GEN_INT (INTVAL (XEXP (operands[1], 1)) >> 1); + return output_shift_insn (xoperands); + } + return \"addr %a1,%0\"; +}") + +(define_insn "movhi" + [(set (match_operand:HI 0 "general_operand" "=g<,*f,g") + (match_operand:HI 1 "general_operand" "g,g,*f"))] + "" + "* +{ + if (GET_CODE (operands[1]) == CONST_INT) + { + short i = INTVAL (operands[1]); + if (i <= 7 && i >= -8) + { + if (INTVAL (operands[1]) > 7) + operands[1] = + GEN_INT (i); + return \"movqw %1,%0\"; + } + return \"movw %1,%0\"; + } + else if (FP_REG_P (operands[0])) + { + if (GET_CODE (operands[1]) == REG && REGNO (operands[1]) < F0_REGNUM) + return \"movwf %1,tos\;movf tos,%0\"; + else + return \"movwf %1,%0\"; + } + else if (FP_REG_P (operands[1])) + { + if (REG_P (operands[0])) + return \"movf %1,tos\;movd tos,%0\"; + return \"movf %1,%0\"; + } + else + return \"movw %1,%0\"; +}") + +(define_insn "movstricthi" + [(set (strict_low_part (match_operand:HI 0 "general_operand" "+r")) + (match_operand:HI 1 "general_operand" "g"))] + "" + "* +{ + if (GET_CODE (operands[1]) == CONST_INT + && INTVAL(operands[1]) <= 7 && INTVAL(operands[1]) >= -8) + return \"movqw %1,%0\"; + return \"movw %1,%0\"; +}") + +(define_insn "movqi" + [(set (match_operand:QI 0 "general_operand" "=g<,*f,g") + (match_operand:QI 1 "general_operand" "g,g,*f"))] + "" + "* +{ if (GET_CODE (operands[1]) == CONST_INT) + { + char char_val = (char)INTVAL (operands[1]); + if (char_val <= 7 && char_val >= -8) + { + if (INTVAL (operands[1]) > 7) + operands[1] = + GEN_INT (char_val); + return \"movqb %1,%0\"; + } + return \"movb %1,%0\"; + } + else if (FP_REG_P (operands[0])) + { + if (GET_CODE (operands[1]) == REG && REGNO (operands[1]) < F0_REGNUM) + return \"movbf %1,tos\;movf tos,%0\"; + else + return \"movbf %1,%0\"; + } + else if (FP_REG_P (operands[1])) + { + if (REG_P (operands[0])) + return \"movf %1,tos\;movd tos,%0\"; + return \"movf %1,%0\"; + } + else + return \"movb %1,%0\"; +}") + +(define_insn "movstrictqi" + [(set (strict_low_part (match_operand:QI 0 "general_operand" "+r")) + (match_operand:QI 1 "general_operand" "g"))] + "" + "* +{ + if (GET_CODE (operands[1]) == CONST_INT + && INTVAL(operands[1]) < 8 && INTVAL(operands[1]) > -9) + return \"movqb %1,%0\"; + return \"movb %1,%0\"; +}") + +;; Block moves +;; Argument 0 is the destination +;; Argument 1 is the source +;; Argument 2 is the length +;; Argument 3 is the alignment +;; +;; Strategy: Use define_expand to +;; either emit insns directly if it can be done simply or +;; emit rtl to match movstrsi1 which has extra scratch registers +;; which can be used to generate more complex code. + +(define_expand "movstrsi" + [(parallel [(set (match_operand:BLK 0 "general_operand" "") + (match_operand:BLK 1 "general_operand" "")) + (use (match_operand:SI 2 "general_operand" "")) + (use (match_operand:SI 3 "const_int_operand" ""))])] + "" + " +{ + if (operands[0]) /* avoid unused code messages */ + { + expand_block_move (operands); + DONE; + } +}") + +;; Special Registers: +;; r0 count +;; r1 from +;; r2 to +;; r3 match + + +(define_insn "movstrsi1" + [(set (mem:BLK (reg:SI 2)) + (mem:BLK (reg:SI 1))) + (use (reg:SI 0)) + (set (reg:SI 2) (plus:SI (reg:SI 2) (mult:SI (reg:SI 0) (match_operand:SI 0 "const_int_operand" "")))) + (set (reg:SI 1) (plus:SI (reg:SI 1) (mult:SI (reg:SI 0) (match_dup 0)))) + (set (reg:SI 0) (const_int 0))] + "" + "* + { + int align = INTVAL(operands[0]); + if (align == 4) + return \"movsd\"; + else + return \"movsb\"; + }") + +(define_insn "movstrsi2" + [(set (mem:BLK (match_operand:SI 0 "address_operand" "g")) + (mem:BLK (match_operand:SI 1 "address_operand" "g"))) + (use (match_operand 2 "immediate_operand" "i"))] + "" + "movmd %a1,%a0,%2") + + +;; Extension and truncation insns. +;; Those for integer source operand +;; are ordered widest source type first. + +(define_insn "truncsiqi2" + [(set (match_operand:QI 0 "general_operand" "=g<") + (truncate:QI (match_operand:SI 1 "nonimmediate_operand" "g")))] + "" + "movb %1,%0") + +(define_insn "truncsihi2" + [(set (match_operand:HI 0 "general_operand" "=g<") + (truncate:HI (match_operand:SI 1 "nonimmediate_operand" "g")))] + "" + "movw %1,%0") + +(define_insn "trunchiqi2" + [(set (match_operand:QI 0 "general_operand" "=g<") + (truncate:QI (match_operand:HI 1 "nonimmediate_operand" "g")))] + "" + "movb %1,%0") + +(define_insn "extendhisi2" + [(set (match_operand:SI 0 "general_operand" "=g<") + (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "g")))] + "" + "movxwd %1,%0") + +(define_insn "extendqihi2" + [(set (match_operand:HI 0 "general_operand" "=g<") + (sign_extend:HI (match_operand:QI 1 "nonimmediate_operand" "g")))] + "" + "movxbw %1,%0") + +(define_insn "extendqisi2" + [(set (match_operand:SI 0 "general_operand" "=g<") + (sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "g")))] + "" + "movxbd %1,%0") + +(define_insn "extendsfdf2" + [(set (match_operand:DF 0 "general_operand" "=lm<") + (float_extend:DF (match_operand:SF 1 "general_operand" "fmF")))] + "TARGET_32081" + "movfl %1,%0") + +(define_insn "truncdfsf2" + [(set (match_operand:SF 0 "general_operand" "=fm<") + (float_truncate:SF (match_operand:DF 1 "general_operand" "lmF")))] + "TARGET_32081" + "movlf %1,%0") + +(define_insn "zero_extendhisi2" + [(set (match_operand:SI 0 "general_operand" "=g<") + (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "g")))] + "" + "movzwd %1,%0") + +(define_insn "zero_extendqihi2" + [(set (match_operand:HI 0 "general_operand" "=g<") + (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "g")))] + "" + "movzbw %1,%0") + +(define_insn "zero_extendqisi2" + [(set (match_operand:SI 0 "general_operand" "=g<") + (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "g")))] + "" + "movzbd %1,%0") + +;; Fix-to-float conversion insns. +;; Note that 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. + +;; Rumor has it that the National part does not correctly convert +;; constant ints to floats. This conversion is therefore disabled. +;; A register must be used to perform the conversion. + +(define_insn "floatsisf2" + [(set (match_operand:SF 0 "general_operand" "=fm<") + (float:SF (match_operand:SI 1 "general_operand" "rm")))] + "TARGET_32081" + "movdf %1,%0") + +(define_insn "floatsidf2" + [(set (match_operand:DF 0 "general_operand" "=lm<") + (float:DF (match_operand:SI 1 "general_operand" "rm")))] + "TARGET_32081" + "movdl %1,%0") + +(define_insn "floathisf2" + [(set (match_operand:SF 0 "general_operand" "=fm<") + (float:SF (match_operand:HI 1 "general_operand" "rm")))] + "TARGET_32081" + "movwf %1,%0") + +(define_insn "floathidf2" + [(set (match_operand:DF 0 "general_operand" "=lm<") + (float:DF (match_operand:HI 1 "general_operand" "rm")))] + "TARGET_32081" + "movwl %1,%0") + +(define_insn "floatqisf2" + [(set (match_operand:SF 0 "general_operand" "=fm<") + (float:SF (match_operand:QI 1 "general_operand" "rm")))] + "TARGET_32081" + "movbf %1,%0") + +; Some assemblers warn that this insn doesn't work. +; Maybe they know something we don't. +;(define_insn "floatqidf2" +; [(set (match_operand:DF 0 "general_operand" "=lm<") +; (float:DF (match_operand:QI 1 "general_operand" "rm")))] +; "TARGET_32081" +; "movbl %1,%0") + +;; Float-to-fix conversion insns. +;; The sequent compiler always generates "trunc" insns. + +(define_insn "fixsfqi2" + [(set (match_operand:QI 0 "general_operand" "=g<") + (fix:QI (fix:SF (match_operand:SF 1 "general_operand" "fm"))))] + "TARGET_32081" + "truncfb %1,%0") + +(define_insn "fixsfhi2" + [(set (match_operand:HI 0 "general_operand" "=g<") + (fix:HI (fix:SF (match_operand:SF 1 "general_operand" "fm"))))] + "TARGET_32081" + "truncfw %1,%0") + +(define_insn "fixsfsi2" + [(set (match_operand:SI 0 "general_operand" "=g<") + (fix:SI (fix:SF (match_operand:SF 1 "general_operand" "fm"))))] + "TARGET_32081" + "truncfd %1,%0") + +(define_insn "fixdfqi2" + [(set (match_operand:QI 0 "general_operand" "=g<") + (fix:QI (fix:DF (match_operand:DF 1 "general_operand" "lm"))))] + "TARGET_32081" + "trunclb %1,%0") + +(define_insn "fixdfhi2" + [(set (match_operand:HI 0 "general_operand" "=g<") + (fix:HI (fix:DF (match_operand:DF 1 "general_operand" "lm"))))] + "TARGET_32081" + "trunclw %1,%0") + +(define_insn "fixdfsi2" + [(set (match_operand:SI 0 "general_operand" "=g<") + (fix:SI (fix:DF (match_operand:DF 1 "general_operand" "lm"))))] + "TARGET_32081" + "truncld %1,%0") + +;; Unsigned + +(define_insn "fixunssfqi2" + [(set (match_operand:QI 0 "general_operand" "=g<") + (unsigned_fix:QI (fix:SF (match_operand:SF 1 "general_operand" "fm"))))] + "TARGET_32081" + "truncfb %1,%0") + +(define_insn "fixunssfhi2" + [(set (match_operand:HI 0 "general_operand" "=g<") + (unsigned_fix:HI (fix:SF (match_operand:SF 1 "general_operand" "fm"))))] + "TARGET_32081" + "truncfw %1,%0") + +(define_insn "fixunssfsi2" + [(set (match_operand:SI 0 "general_operand" "=g<") + (unsigned_fix:SI (fix:SF (match_operand:SF 1 "general_operand" "fm"))))] + "TARGET_32081" + "truncfd %1,%0") + +(define_insn "fixunsdfqi2" + [(set (match_operand:QI 0 "general_operand" "=g<") + (unsigned_fix:QI (fix:DF (match_operand:DF 1 "general_operand" "lm"))))] + "TARGET_32081" + "trunclb %1,%0") + +(define_insn "fixunsdfhi2" + [(set (match_operand:HI 0 "general_operand" "=g<") + (unsigned_fix:HI (fix:DF (match_operand:DF 1 "general_operand" "lm"))))] + "TARGET_32081" + "trunclw %1,%0") + +(define_insn "fixunsdfsi2" + [(set (match_operand:SI 0 "general_operand" "=g<") + (unsigned_fix:SI (fix:DF (match_operand:DF 1 "general_operand" "lm"))))] + "TARGET_32081" + "truncld %1,%0") + +;;; These are not yet used by GCC +(define_insn "fix_truncsfqi2" + [(set (match_operand:QI 0 "general_operand" "=g<") + (fix:QI (match_operand:SF 1 "general_operand" "fm")))] + "TARGET_32081" + "truncfb %1,%0") + +(define_insn "fix_truncsfhi2" + [(set (match_operand:HI 0 "general_operand" "=g<") + (fix:HI (match_operand:SF 1 "general_operand" "fm")))] + "TARGET_32081" + "truncfw %1,%0") + +(define_insn "fix_truncsfsi2" + [(set (match_operand:SI 0 "general_operand" "=g<") + (fix:SI (match_operand:SF 1 "general_operand" "fm")))] + "TARGET_32081" + "truncfd %1,%0") + +(define_insn "fix_truncdfqi2" + [(set (match_operand:QI 0 "general_operand" "=g<") + (fix:QI (match_operand:DF 1 "general_operand" "lm")))] + "TARGET_32081" + "trunclb %1,%0") + +(define_insn "fix_truncdfhi2" + [(set (match_operand:HI 0 "general_operand" "=g<") + (fix:HI (match_operand:DF 1 "general_operand" "lm")))] + "TARGET_32081" + "trunclw %1,%0") + +(define_insn "fix_truncdfsi2" + [(set (match_operand:SI 0 "general_operand" "=g<") + (fix:SI (match_operand:DF 1 "general_operand" "lm")))] + "TARGET_32081" + "truncld %1,%0") + +;; Multiply-add instructions +(define_insn "" + [(set (match_operand:DF 0 "general_operand" "=v,v") + (plus:DF (mult:DF (match_operand:DF 1 "general_operand" "%lmF,0") + (match_operand:DF 2 "general_operand" "lmF,lmF")) + (match_operand:DF 3 "general_operand" "0,lmF")))] + "TARGET_MULT_ADD" + "@ + dotl %1,%2 + polyl %2,%3") + +(define_insn "" + [(set (match_operand:SF 0 "general_operand" "=u,u") + (plus:SF (mult:SF (match_operand:SF 1 "general_operand" "%fmF,0") + (match_operand:SF 2 "general_operand" "fmF,fmF")) + (match_operand:SF 3 "general_operand" "0,fmF")))] + "TARGET_MULT_ADD" + "@ + dotf %1,%2 + polyf %2,%3") + + +;; Multiply-sub instructions +(define_insn "" + [(set (match_operand:DF 0 "general_operand" "=v") + (minus:DF (mult:DF (match_operand:DF 1 "general_operand" "%lmF") + (match_operand:DF 2 "general_operand" "lmF")) + (match_operand:DF 3 "general_operand" "0")))] + "TARGET_MULT_ADD" + "@ + negl %0,%0\;dotl %1,%2") + +(define_insn "" + [(set (match_operand:SF 0 "general_operand" "=u") + (minus:SF (mult:SF (match_operand:SF 1 "general_operand" "%fmF") + (match_operand:SF 2 "general_operand" "fmF")) + (match_operand:SF 3 "general_operand" "0")))] + "TARGET_MULT_ADD" + "@ + negf %0,%0\;dotf %1,%2") + +;;- All kinds of add instructions. + +(define_insn "adddf3" + [(set (match_operand:DF 0 "general_operand" "=lm") + (plus:DF (match_operand:DF 1 "general_operand" "%0") + (match_operand:DF 2 "general_operand" "lmF")))] + "TARGET_32081" + "addl %2,%0") + + +(define_insn "addsf3" + [(set (match_operand:SF 0 "general_operand" "=fm") + (plus:SF (match_operand:SF 1 "general_operand" "%0") + (match_operand:SF 2 "general_operand" "fmF")))] + "TARGET_32081" + "addf %2,%0") + +(define_insn "" + [(set (reg:SI 25) + (plus:SI (reg:SI 25) + (match_operand:SI 0 "immediate_operand" "i")))] + "GET_CODE (operands[0]) == CONST_INT" + "* +{ +#ifndef SEQUENT_ADJUST_STACK + if (TARGET_32532) + if (INTVAL (operands[0]) == 8) + return \"cmpd tos,tos\"; + if (TARGET_32532 || TARGET_32332) + if (INTVAL (operands[0]) == 4) + return \"cmpqd %$0,tos\"; +#endif + if (! TARGET_32532) + { + if (INTVAL (operands[0]) < 64 && INTVAL (operands[0]) > -64) + return \"adjspb %n0\"; + else if (INTVAL (operands[0]) < 8192 && INTVAL (operands[0]) >= -8192) + return \"adjspw %n0\"; + } + return \"adjspd %n0\"; +}") + +(define_insn "" + [(set (match_operand:SI 0 "general_operand" "=g<") + (plus:SI (reg:SI 24) + (match_operand:SI 1 "immediate_operand" "i")))] + "GET_CODE (operands[1]) == CONST_INT" + "addr %c1(fp),%0") + +(define_insn "" + [(set (match_operand:SI 0 "general_operand" "=g<") + (plus:SI (reg:SI 25) + (match_operand:SI 1 "immediate_operand" "i")))] + "GET_CODE (operands[1]) == CONST_INT" + "addr %c1(sp),%0") + +(define_insn "adddi3" + [(set (match_operand:DI 0 "general_operand" "=ro") + (plus:DI (match_operand:DI 1 "general_operand" "%0") + (match_operand:DI 2 "general_operand" "ron")))] + "" + "* +{ + rtx low[3], high[3], xops[4]; + split_di (operands, 3, low, high); + xops[0] = low[0]; + xops[1] = high[0]; + xops[2] = low[2]; + xops[3] = high[2]; + + if (GET_CODE (xops[2]) == CONST_INT) + { + int i = INTVAL (xops[2]); + + if (i <= 7 && i >= -8) + { + if (i == 0) + { + i = INTVAL (xops[3]); + if (i <= 7 && i >= -8) + output_asm_insn (\"addqd %3,%1\", xops); + else + output_asm_insn (\"addd %3,%1\", xops); + } + else + { + output_asm_insn (\"addqd %2,%0\", xops); + output_asm_insn (\"addcd %3,%1\", xops); + } + return \"\"; + } + } + output_asm_insn (\"addd %2,%0\", xops); + output_asm_insn (\"addcd %3,%1\", xops); + return \"\"; +}") + +;; See Note 1 +(define_insn "addsi3" + [(set (match_operand:SI 0 "general_operand" "=g,=g&<") + (plus:SI (match_operand:SI 1 "general_operand" "%0,r") + (match_operand:SI 2 "general_operand" "g,i")))] + "" + "* +{ + if (which_alternative == 1) + { + if (GET_CODE (operands[2]) == CONST_INT) + { + int i = INTVAL (operands[2]); + if (NS32K_DISPLACEMENT_P (i)) + return \"addr %c2(%1),%0\"; + else + return \"movd %1,%0\;addd %2,%0\"; + } + else + { + if (flag_pic) + return \"addr %a2[%1:b],%0\"; + else + return \"addr %c2(%1),%0\"; + } + } + else if (GET_CODE (operands[2]) == CONST_INT) + { + int i = INTVAL (operands[2]); + + if (i <= 7 && i >= -8) + return \"addqd %2,%0\"; + else if (! TARGET_32532 && GET_CODE (operands[0]) == REG + && NS32K_DISPLACEMENT_P (i)) + return \"addr %c2(%0),%0\"; + } + return \"addd %2,%0\"; +}") + +(define_insn "addhi3" + [(set (match_operand:HI 0 "general_operand" "=g") + (plus:HI (match_operand:HI 1 "general_operand" "%0") + (match_operand:HI 2 "general_operand" "g")))] + "" + "* +{ if (GET_CODE (operands[2]) == CONST_INT) + { + int i = INTVAL (operands[2]); + if (i <= 7 && i >= -8) + return \"addqw %2,%0\"; + } + return \"addw %2,%0\"; +}") + +(define_insn "" + [(set (strict_low_part (match_operand:HI 0 "general_operand" "=r")) + (plus:HI (match_operand:HI 1 "general_operand" "0") + (match_operand:HI 2 "general_operand" "g")))] + "" + "* +{ + if (GET_CODE (operands[1]) == CONST_INT + && INTVAL (operands[1]) >-9 && INTVAL(operands[1]) < 8) + return \"addqw %2,%0\"; + return \"addw %2,%0\"; +}") + +(define_insn "addqi3" + [(set (match_operand:QI 0 "general_operand" "=g") + (plus:QI (match_operand:QI 1 "general_operand" "%0") + (match_operand:QI 2 "general_operand" "g")))] + "" + "* +{ if (GET_CODE (operands[2]) == CONST_INT) + { + int i = INTVAL (operands[2]); + if (i <= 7 && i >= -8) + return \"addqb %2,%0\"; + } + return \"addb %2,%0\"; +}") + +(define_insn "" + [(set (strict_low_part (match_operand:QI 0 "general_operand" "=r")) + (plus:QI (match_operand:QI 1 "general_operand" "0") + (match_operand:QI 2 "general_operand" "g")))] + "" + "* +{ + if (GET_CODE (operands[1]) == CONST_INT + && INTVAL (operands[1]) >-9 && INTVAL(operands[1]) < 8) + return \"addqb %2,%0\"; + return \"addb %2,%0\"; +}") + +;;- All kinds of subtract instructions. + +(define_insn "subdf3" + [(set (match_operand:DF 0 "general_operand" "=lm") + (minus:DF (match_operand:DF 1 "general_operand" "0") + (match_operand:DF 2 "general_operand" "lmF")))] + "TARGET_32081" + "subl %2,%0") + +(define_insn "subsf3" + [(set (match_operand:SF 0 "general_operand" "=fm") + (minus:SF (match_operand:SF 1 "general_operand" "0") + (match_operand:SF 2 "general_operand" "fmF")))] + "TARGET_32081" + "subf %2,%0") + +(define_insn "" + [(set (reg:SI 25) + (minus:SI (reg:SI 25) + (match_operand:SI 0 "immediate_operand" "i")))] + "GET_CODE (operands[0]) == CONST_INT" + "* +{ + if (! TARGET_32532 && GET_CODE(operands[0]) == CONST_INT + && INTVAL(operands[0]) < 64 && INTVAL(operands[0]) > -64) + return \"adjspb %0\"; + return \"adjspd %0\"; +}") + +(define_insn "subdi3" + [(set (match_operand:DI 0 "general_operand" "=ro") + (minus:DI (match_operand:DI 1 "general_operand" "0") + (match_operand:DI 2 "general_operand" "ron")))] + "" + "* +{ + rtx low[3], high[3], xops[4]; + split_di (operands, 3, low, high); + xops[0] = low[0]; + xops[1] = high[0]; + xops[2] = low[2]; + xops[3] = high[2]; + + if (GET_CODE (xops[2]) == CONST_INT) + { + int i = INTVAL (xops[2]); + + if (i <= 8 && i >= -7) + { + if (i == 0) + { + i = INTVAL (xops[3]); + if (i <= 8 && i >= -7) + output_asm_insn (\"addqd %n3,%1\", xops); + else + output_asm_insn (\"subd %3,%1\", xops); + } + else + { + output_asm_insn (\"addqd %n2,%0\", xops); + output_asm_insn (\"subcd %3,%1\", xops); + } + return \"\"; + } + } + output_asm_insn (\"subd %2,%0\", xops); + output_asm_insn (\"subcd %3,%1\", xops); + return \"\"; +}") + +(define_insn "subsi3" + [(set (match_operand:SI 0 "general_operand" "=g") + (minus:SI (match_operand:SI 1 "general_operand" "0") + (match_operand:SI 2 "general_operand" "g")))] + "" + "* +{ if (GET_CODE (operands[2]) == CONST_INT) + { + int i = INTVAL (operands[2]); + + if (i <= 8 && i >= -7) + return \"addqd %n2,%0\"; + } + return \"subd %2,%0\"; +}") + +(define_insn "subhi3" + [(set (match_operand:HI 0 "general_operand" "=g") + (minus:HI (match_operand:HI 1 "general_operand" "0") + (match_operand:HI 2 "general_operand" "g")))] + "" + "* +{ if (GET_CODE (operands[2]) == CONST_INT) + { + int i = INTVAL (operands[2]); + + if (i <= 8 && i >= -7) + return \"addqw %n2,%0\"; + } + return \"subw %2,%0\"; +}") + +(define_insn "" + [(set (strict_low_part (match_operand:HI 0 "general_operand" "=r")) + (minus:HI (match_operand:HI 1 "general_operand" "0") + (match_operand:HI 2 "general_operand" "g")))] + "" + "* +{ + if (GET_CODE (operands[1]) == CONST_INT + && INTVAL (operands[1]) >-8 && INTVAL(operands[1]) < 9) + return \"addqw %n2,%0\"; + return \"subw %2,%0\"; +}") + +(define_insn "subqi3" + [(set (match_operand:QI 0 "general_operand" "=g") + (minus:QI (match_operand:QI 1 "general_operand" "0") + (match_operand:QI 2 "general_operand" "g")))] + "" + "* +{ if (GET_CODE (operands[2]) == CONST_INT) + { + int i = INTVAL (operands[2]); + + if (i <= 8 && i >= -7) + return \"addqb %n2,%0\"; + } + return \"subb %2,%0\"; +}") + +(define_insn "" + [(set (strict_low_part (match_operand:QI 0 "general_operand" "=r")) + (minus:QI (match_operand:QI 1 "general_operand" "0") + (match_operand:QI 2 "general_operand" "g")))] + "" + "* +{ + if (GET_CODE (operands[1]) == CONST_INT + && INTVAL (operands[1]) >-8 && INTVAL(operands[1]) < 9) + return \"addqb %n2,%0\"; + return \"subb %2,%0\"; +}") + +;;- Multiply instructions. + +(define_insn "muldf3" + [(set (match_operand:DF 0 "general_operand" "=lm") + (mult:DF (match_operand:DF 1 "general_operand" "%0") + (match_operand:DF 2 "general_operand" "lmF")))] + "TARGET_32081" + "mull %2,%0") + +(define_insn "mulsf3" + [(set (match_operand:SF 0 "general_operand" "=fm") + (mult:SF (match_operand:SF 1 "general_operand" "%0") + (match_operand:SF 2 "general_operand" "fmF")))] + "TARGET_32081" + "mulf %2,%0") + +;; See note 1 +(define_insn "mulsi3" + [(set (match_operand:SI 0 "general_operand" "=g") + (mult:SI (match_operand:SI 1 "general_operand" "%0") + (match_operand:SI 2 "general_operand" "g")))] + "" + "muld %2,%0") + +(define_insn "mulhi3" + [(set (match_operand:HI 0 "general_operand" "=g") + (mult:HI (match_operand:HI 1 "general_operand" "%0") + (match_operand:HI 2 "general_operand" "g")))] + "" + "mulw %2,%0") + +(define_insn "mulqi3" + [(set (match_operand:QI 0 "general_operand" "=g") + (mult:QI (match_operand:QI 1 "general_operand" "%0") + (match_operand:QI 2 "general_operand" "g")))] + "" + "mulb %2,%0") + +(define_insn "umulsidi3" + [(set (match_operand:DI 0 "general_operand" "=g") + (mult:DI (zero_extend:DI + (match_operand:SI 1 "nonimmediate_operand" "0")) + (zero_extend:DI + (match_operand:SI 2 "nonimmediate_operand" "g"))))] + "" + "meid %2,%0") + +;; divmod insns: We can only do the unsigned case. +(define_expand "udivmodsi4" + [(parallel + [(set (match_operand:SI 0 "reg_or_mem_operand" "") + (udiv:SI (match_operand:SI 1 "general_operand" "") + (match_operand:SI 2 "general_operand" ""))) + (set (match_operand:SI 3 "reg_or_mem_operand" "") + (umod:SI (match_dup 1) (match_dup 2)))])] + "" + " +{ + rtx temp = gen_reg_rtx(DImode); + rtx insn, first, last; + first = emit_move_insn(gen_lowpart(SImode, temp), operands[1]); + emit_move_insn(gen_highpart(SImode, temp), const0_rtx); + emit_insn(gen_udivmoddisi4_internal(temp, temp, operands[2])); + last = emit_move_insn(temp, temp); + { + rtx divdi, moddi, divsi, modsi; + divsi = gen_rtx (UDIV, SImode, operands[1], operands[2]); + modsi = gen_rtx (UMOD, SImode, operands[1], operands[2]); + divdi = gen_rtx (ZERO_EXTEND, DImode, divsi); + moddi = gen_rtx (ZERO_EXTEND, DImode, modsi); + REG_NOTES (first) = gen_rtx (INSN_LIST, REG_LIBCALL, last, + REG_NOTES (first)); + REG_NOTES (last) = gen_rtx (INSN_LIST, REG_RETVAL, first, + gen_rtx (EXPR_LIST, REG_EQUAL, + gen_rtx (IOR, DImode, moddi, + gen_rtx (ASHIFT, DImode, divdi, GEN_INT(32))), + REG_NOTES (last))); + } + + insn = emit_move_insn(operands[0], gen_highpart(SImode, temp)); + insn = emit_move_insn(operands[3], gen_lowpart(SImode, temp)); + DONE; +}") + +;; If we try and describe what this does, we have to zero-expand an +;; operand, which prevents it being a constant (VOIDmode) (see udivmoddisi4 +;; below. This udivmoddisi4_internal never matches anything and is only +;; ever used when explicitly emitted by a define_expand. +(define_insn "udivmoddisi4_internal" + [(set (match_operand:DI 0 "reg_or_mem_operand" "=rm") + (unspec:SI [(match_operand:DI 1 "reg_or_mem_operand" "0") + (match_operand:SI 2 "general_operand" "g")] 0))] + "" + "deid %2,%0") + +;; Retain this insn which *does* have a pattern indicating what it does, +;; just in case the compiler is smart enough to recognize a substitution. +(define_insn "udivmoddisi4" + [(set (subreg:SI (match_operand:DI 0 "register_operand" "=rm") 1) + (truncate:SI (udiv:DI (match_operand:DI 1 "reg_or_mem_operand" "0") + (zero_extend:DI (match_operand:SI 2 "nonimmediate_operand" "g"))))) + (set (subreg:SI (match_operand:DI 3 "register_operand" "=0") 0) + (truncate:SI (umod:DI (match_dup 1) (zero_extend:DI (match_dup 2)))))] + "" + "deid %2,%0") + +;; Part word variants. These seem to never be used at the moment (gcc +;; 2.7.2.2). The code generation prefers to zero extend hi's and qi's +;; and use signed div and mod. Keep these insns incase that changes. +;; divmod should have an advantage when both div and mod are needed. However, +;; divmod uses two registers, so maybe the compiler knows best. + +(define_expand "udivmodhi4" + [(parallel + [(set (match_operand:HI 0 "reg_or_mem_operand" "") + (udiv:HI (match_operand:HI 1 "general_operand" "") + (match_operand:HI 2 "general_operand" ""))) + (set (match_operand:HI 3 "reg_or_mem_operand" "") + (umod:HI (match_dup 1) (match_dup 2)))])] + "" + " +{ + rtx temp = gen_reg_rtx(DImode); + rtx insn, first, last; + first = emit_move_insn(gen_lowpart(HImode, temp), operands[1]); + emit_move_insn(gen_highpart (HImode, temp), const0_rtx); + operands[2] = force_reg(HImode, operands[2]); + emit_insn(gen_udivmoddihi4_internal(temp, temp, operands[2])); + last = emit_move_insn(temp, temp); + { + rtx divdi, moddi, divhi, modhi; + divhi = gen_rtx (UDIV, HImode, operands[1], operands[2]); + modhi = gen_rtx (UMOD, HImode, operands[1], operands[2]); + divdi = gen_rtx (ZERO_EXTEND, DImode, divhi); + moddi = gen_rtx (ZERO_EXTEND, DImode, modhi); + REG_NOTES (first) = gen_rtx (INSN_LIST, REG_LIBCALL, last, + REG_NOTES (first)); + REG_NOTES (last) = gen_rtx (INSN_LIST, REG_RETVAL, first, + gen_rtx (EXPR_LIST, REG_EQUAL, + gen_rtx(IOR, DImode, moddi, + gen_rtx(ASHIFT, DImode, divdi, GEN_INT(32))), + REG_NOTES (last))); + } + + insn = emit_move_insn(operands[0], gen_highpart(HImode, temp)); + insn = emit_move_insn(operands[3], gen_lowpart(HImode, temp)); + DONE; +}") + +;; deiw wants two hi's in seperate registers or else they can be adjacent +;; in memory. DI mode will ensure two registers are available, but if we +;; want to allow memory as an operand we would need SI mode. There is no +;; way to do this, so just restrict operand 0 and 1 to be in registers. +(define_insn "udivmoddihi4_internal" + [(set (match_operand:DI 0 "register_operand" "=r") + (unspec:HI [(match_operand:DI 1 "register_operand" "0") + (match_operand:HI 2 "general_operand" "g")] 0))] + "" + "deiw %2,%0") + +(define_insn "udivmoddihi4" + [(set (subreg:HI (match_operand:DI 0 "register_operand" "=r") 1) + (truncate:HI (udiv:DI (match_operand:DI 1 "reg_or_mem_operand" "0") + (zero_extend:DI (match_operand:HI 2 "nonimmediate_operand" "g"))))) + (set (subreg:HI (match_operand:DI 3 "register_operand" "=0") 0) + (truncate:HI (umod:DI (match_dup 1) (zero_extend:DI (match_dup 2)))))] + "" + "deiw %2,%0") + +(define_expand "udivmodqi4" + [(parallel + [(set (match_operand:QI 0 "reg_or_mem_operand" "") + (udiv:QI (match_operand:QI 1 "general_operand" "") + (match_operand:QI 2 "general_operand" ""))) + (set (match_operand:QI 3 "reg_or_mem_operand" "") + (umod:QI (match_dup 1) (match_dup 2)))])] + "" + " +{ + rtx temp = gen_reg_rtx(DImode); + rtx insn, first, last; + first = emit_move_insn(gen_lowpart(QImode, temp), operands[1]); + emit_move_insn(gen_highpart(QImode, temp), const0_rtx); + operands[2] = force_reg(QImode, operands[2]); + emit_insn(gen_udivmoddiqi4_internal(temp, temp, operands[2])); + last = emit_move_insn(temp, temp); + { + rtx divdi, moddi, divqi, modqi; + divqi = gen_rtx (UDIV, QImode, operands[1], operands[2]); + modqi = gen_rtx (UMOD, QImode, operands[1], operands[2]); + divdi = gen_rtx (ZERO_EXTEND, DImode, divqi); + moddi = gen_rtx (ZERO_EXTEND, DImode, modqi); + REG_NOTES (first) = gen_rtx (INSN_LIST, REG_LIBCALL, last, + REG_NOTES (first)); + REG_NOTES (last) = gen_rtx (INSN_LIST, REG_RETVAL, first, + gen_rtx (EXPR_LIST, REG_EQUAL, + gen_rtx(IOR, DImode, moddi, + gen_rtx(ASHIFT, DImode, divdi, GEN_INT(32))), + REG_NOTES (last))); + } + + insn = emit_move_insn(operands[0], gen_highpart(QImode, temp)); + insn = emit_move_insn(operands[3], gen_lowpart(QImode, temp)); + DONE; +}") + +;; deib wants two qi's in seperate registers or else they can be adjacent +;; in memory. DI mode will ensure two registers are available, but if we +;; want to allow memory as an operand we would need HI mode. There is no +;; way to do this, so just restrict operand 0 and 1 to be in registers. +(define_insn "udivmoddiqi4_internal" + [(set (match_operand:DI 0 "register_operand" "=r") + (unspec:QI [(match_operand:DI 1 "reg_or_mem_operand" "0") + (match_operand:QI 2 "general_operand" "g")] 0))] + "" + "deib %2,%0") + +(define_insn "udivmoddiqi4" + [(set (subreg:QI (match_operand:DI 0 "register_operand" "=r") 1) + (truncate:QI (udiv:DI (match_operand:DI 1 "reg_or_mem_operand" "0") + (zero_extend:DI (match_operand:QI 2 "nonimmediate_operand" "g"))))) + (set (subreg:QI (match_operand:DI 3 "register_operand" "=0") 0) + (truncate:QI (umod:DI (match_dup 1) (zero_extend:DI (match_dup 2)))))] + "" + "deib %2,%0") + +;;- Divide instructions. + +(define_insn "divdf3" + [(set (match_operand:DF 0 "general_operand" "=lm") + (div:DF (match_operand:DF 1 "general_operand" "0") + (match_operand:DF 2 "general_operand" "lmF")))] + "TARGET_32081" + "divl %2,%0") + +(define_insn "divsf3" + [(set (match_operand:SF 0 "general_operand" "=fm") + (div:SF (match_operand:SF 1 "general_operand" "0") + (match_operand:SF 2 "general_operand" "fmF")))] + "TARGET_32081" + "divf %2,%0") + +;; See note 1 +(define_insn "divsi3" + [(set (match_operand:SI 0 "general_operand" "=g") + (div:SI (match_operand:SI 1 "general_operand" "0") + (match_operand:SI 2 "general_operand" "g")))] + "" + "quod %2,%0") + +(define_insn "divhi3" + [(set (match_operand:HI 0 "general_operand" "=g") + (div:HI (match_operand:HI 1 "general_operand" "0") + (match_operand:HI 2 "general_operand" "g")))] + "" + "quow %2,%0") + +(define_insn "divqi3" + [(set (match_operand:QI 0 "general_operand" "=g") + (div:QI (match_operand:QI 1 "general_operand" "0") + (match_operand:QI 2 "general_operand" "g")))] + "" + "quob %2,%0") + +;; Remainder instructions. + +;; See note 1 +(define_insn "modsi3" + [(set (match_operand:SI 0 "general_operand" "=g") + (mod:SI (match_operand:SI 1 "general_operand" "0") + (match_operand:SI 2 "general_operand" "g")))] + "" + "remd %2,%0") + +(define_insn "modhi3" + [(set (match_operand:HI 0 "general_operand" "=g") + (mod:HI (match_operand:HI 1 "general_operand" "0") + (match_operand:HI 2 "general_operand" "g")))] + "" + "remw %2,%0") + +(define_insn "modqi3" + [(set (match_operand:QI 0 "general_operand" "=g") + (mod:QI (match_operand:QI 1 "general_operand" "0") + (match_operand:QI 2 "general_operand" "g")))] + "" + "remb %2,%0") + + +;;- Logical Instructions: AND + +;; See note 1 +(define_insn "andsi3" + [(set (match_operand:SI 0 "general_operand" "=g") + (and:SI (match_operand:SI 1 "general_operand" "%0") + (match_operand:SI 2 "general_operand" "g")))] + "" + "* +{ + if (GET_CODE (operands[2]) == CONST_INT) + { + if ((INTVAL (operands[2]) | 0xff) == 0xffffffff) + { + if (INTVAL (operands[2]) == 0xffffff00) + return \"movqb %$0,%0\"; + else + { + operands[2] = GEN_INT (INTVAL (operands[2]) & 0xff); + return \"andb %2,%0\"; + } + } + if ((INTVAL (operands[2]) | 0xffff) == 0xffffffff) + { + if (INTVAL (operands[2]) == 0xffff0000) + return \"movqw %$0,%0\"; + else + { + operands[2] = GEN_INT (INTVAL (operands[2]) & 0xffff); + return \"andw %2,%0\"; + } + } + } + return \"andd %2,%0\"; +}") + +(define_insn "andhi3" + [(set (match_operand:HI 0 "general_operand" "=g") + (and:HI (match_operand:HI 1 "general_operand" "%0") + (match_operand:HI 2 "general_operand" "g")))] + "" + "* +{ + if (GET_CODE (operands[2]) == CONST_INT + && (INTVAL (operands[2]) | 0xff) == 0xffffffff) + { + if (INTVAL (operands[2]) == 0xffffff00) + return \"movqb %$0,%0\"; + else + { + operands[2] = GEN_INT (INTVAL (operands[2]) & 0xff); + return \"andb %2,%0\"; + } + } + return \"andw %2,%0\"; +}") + +(define_insn "andqi3" + [(set (match_operand:QI 0 "general_operand" "=g") + (and:QI (match_operand:QI 1 "general_operand" "%0") + (match_operand:QI 2 "general_operand" "g")))] + "" + "andb %2,%0") + +;; See note 1 +(define_insn "" + [(set (match_operand:SI 0 "general_operand" "=g") + (and:SI (not:SI (match_operand:SI 1 "general_operand" "g")) + (match_operand:SI 2 "general_operand" "0")))] + "" + "bicd %1,%0") + +(define_insn "" + [(set (match_operand:HI 0 "general_operand" "=g") + (and:HI (not:HI (match_operand:HI 1 "general_operand" "g")) + (match_operand:HI 2 "general_operand" "0")))] + "" + "bicw %1,%0") + +(define_insn "" + [(set (match_operand:QI 0 "general_operand" "=g") + (and:QI (not:QI (match_operand:QI 1 "general_operand" "g")) + (match_operand:QI 2 "general_operand" "0")))] + "" + "bicb %1,%0") + +;;- Bit set instructions. + +;; See note 1 +(define_insn "iorsi3" + [(set (match_operand:SI 0 "general_operand" "=g") + (ior:SI (match_operand:SI 1 "general_operand" "%0") + (match_operand:SI 2 "general_operand" "g")))] + "" + "* +{ + if (GET_CODE (operands[2]) == CONST_INT) { + if ((INTVAL (operands[2]) & 0xffffff00) == 0) + return \"orb %2,%0\"; + if ((INTVAL (operands[2]) & 0xffff0000) == 0) + return \"orw %2,%0\"; + } + return \"ord %2,%0\"; +}") + +(define_insn "iorhi3" + [(set (match_operand:HI 0 "general_operand" "=g") + (ior:HI (match_operand:HI 1 "general_operand" "%0") + (match_operand:HI 2 "general_operand" "g")))] + "" + "* +{ + if (GET_CODE(operands[2]) == CONST_INT && + (INTVAL(operands[2]) & 0xffffff00) == 0) + return \"orb %2,%0\"; + return \"orw %2,%0\"; +}") + +(define_insn "iorqi3" + [(set (match_operand:QI 0 "general_operand" "=g") + (ior:QI (match_operand:QI 1 "general_operand" "%0") + (match_operand:QI 2 "general_operand" "g")))] + "" + "orb %2,%0") + +;;- xor instructions. + +;; See note 1 +(define_insn "xorsi3" + [(set (match_operand:SI 0 "general_operand" "=g") + (xor:SI (match_operand:SI 1 "general_operand" "%0") + (match_operand:SI 2 "general_operand" "g")))] + "" + "* +{ + if (GET_CODE (operands[2]) == CONST_INT) { + if ((INTVAL (operands[2]) & 0xffffff00) == 0) + return \"xorb %2,%0\"; + if ((INTVAL (operands[2]) & 0xffff0000) == 0) + return \"xorw %2,%0\"; + } + return \"xord %2,%0\"; +}") + +(define_insn "xorhi3" + [(set (match_operand:HI 0 "general_operand" "=g") + (xor:HI (match_operand:HI 1 "general_operand" "%0") + (match_operand:HI 2 "general_operand" "g")))] + "" + "* +{ + if (GET_CODE(operands[2]) == CONST_INT && + (INTVAL(operands[2]) & 0xffffff00) == 0) + return \"xorb %2,%0\"; + return \"xorw %2,%0\"; +}") + +(define_insn "xorqi3" + [(set (match_operand:QI 0 "general_operand" "=g") + (xor:QI (match_operand:QI 1 "general_operand" "%0") + (match_operand:QI 2 "general_operand" "g")))] + "" + "xorb %2,%0") + +(define_insn "negdf2" + [(set (match_operand:DF 0 "general_operand" "=lm<") + (neg:DF (match_operand:DF 1 "general_operand" "lmF")))] + "TARGET_32081" + "negl %1,%0") + +(define_insn "negsf2" + [(set (match_operand:SF 0 "general_operand" "=fm<") + (neg:SF (match_operand:SF 1 "general_operand" "fmF")))] + "TARGET_32081" + "negf %1,%0") + +(define_insn "negdi2" + [(set (match_operand:DI 0 "general_operand" "=ro") + (neg:DI (match_operand:DI 1 "general_operand" "ro")))] + "" + "* +{ + rtx low[2], high[2], xops[4]; + split_di (operands, 2, low, high); + xops[0] = low[0]; + xops[1] = high[0]; + xops[2] = low[1]; + xops[3] = high[1]; + + if (rtx_equal_p (operands[0], operands[1])) + { + output_asm_insn (\"negd %3,%1\", xops); + output_asm_insn (\"negd %2,%0\", xops); + output_asm_insn (\"subcd %$0,%1\", xops); + } + else + { + output_asm_insn (\"negd %2,%0\", xops); + output_asm_insn (\"movqd %$0,%1\", xops); + output_asm_insn (\"subcd %3,%1\", xops); + } + return \"\"; +}") + +;; See note 1 +(define_insn "negsi2" + [(set (match_operand:SI 0 "general_operand" "=g<") + (neg:SI (match_operand:SI 1 "general_operand" "g")))] + "" + "negd %1,%0") + +(define_insn "neghi2" + [(set (match_operand:HI 0 "general_operand" "=g<") + (neg:HI (match_operand:HI 1 "general_operand" "g")))] + "" + "negw %1,%0") + +(define_insn "negqi2" + [(set (match_operand:QI 0 "general_operand" "=g<") + (neg:QI (match_operand:QI 1 "general_operand" "g")))] + "" + "negb %1,%0") + +;; See note 1 +(define_insn "one_cmplsi2" + [(set (match_operand:SI 0 "general_operand" "=g<") + (not:SI (match_operand:SI 1 "general_operand" "g")))] + "" + "comd %1,%0") + +(define_insn "one_cmplhi2" + [(set (match_operand:HI 0 "general_operand" "=g<") + (not:HI (match_operand:HI 1 "general_operand" "g")))] + "" + "comw %1,%0") + +(define_insn "one_cmplqi2" + [(set (match_operand:QI 0 "general_operand" "=g<") + (not:QI (match_operand:QI 1 "general_operand" "g")))] + "" + "comb %1,%0") + +;; arithmetic left and right shift operations +;; on the 32532 we will always use lshd for arithmetic left shifts, +;; because it is three times faster. Broken programs which +;; use negative shift counts are probably broken differently +;; than elsewhere. + +;; alternative 0 never matches on the 32532 +;; See note 1 +(define_insn "ashlsi3" + [(set (match_operand:SI 0 "general_operand" "=g,g") + (ashift:SI (match_operand:SI 1 "general_operand" "r,0") + (match_operand:SI 2 "general_operand" "I,g")))] + "" + "* +{ if (TARGET_32532) + return \"lshd %2,%0\"; + else + return output_shift_insn (operands); +}") + +(define_insn "ashlhi3" + [(set (match_operand:HI 0 "general_operand" "=g") + (ashift:HI (match_operand:HI 1 "general_operand" "0") + (match_operand:SI 2 "general_operand" "g")))] + "" + "* +{ if (GET_CODE (operands[2]) == CONST_INT) + { + if (INTVAL (operands[2]) == 1) + return \"addw %0,%0\"; + else if (! TARGET_32532 && INTVAL (operands[2]) == 2) + return \"addw %0,%0\;addw %0,%0\"; + } + if (TARGET_32532) + return \"lshw %2,%0\"; + else + return \"ashw %2,%0\"; +}") + +(define_insn "ashlqi3" + [(set (match_operand:QI 0 "general_operand" "=g") + (ashift:QI (match_operand:QI 1 "general_operand" "0") + (match_operand:SI 2 "general_operand" "g")))] + "" + "* +{ if (GET_CODE (operands[2]) == CONST_INT) + { + if (INTVAL (operands[2]) == 1) + return \"addb %0,%0\"; + else if (! TARGET_32532 && INTVAL (operands[2]) == 2) + return \"addb %0,%0\;addb %0,%0\"; + } + if (TARGET_32532) + return \"lshb %2,%0\"; + else + return \"ashb %2,%0\"; +}") + +;; Arithmetic right shift on the 32k works by negating the shift count. +(define_expand "ashrsi3" + [(set (match_operand:SI 0 "general_operand" "=g") + (ashiftrt:SI (match_operand:SI 1 "general_operand" "g") + (match_operand:SI 2 "general_operand" "g")))] + "" + " +{ + if (GET_CODE (operands[2]) != CONST_INT) + operands[2] = gen_rtx (NEG, SImode, negate_rtx (SImode, operands[2])); +}") + +(define_insn "" + [(set (match_operand:SI 0 "general_operand" "=g") + (ashiftrt:SI (match_operand:SI 1 "general_operand" "0") + (match_operand:SI 2 "immediate_operand" "i")))] + "" + "ashd %n2,%0") + +(define_insn "" + [(set (match_operand:SI 0 "general_operand" "=g") + (ashiftrt:SI (match_operand:SI 1 "general_operand" "0") + (neg:SI (match_operand:SI 2 "general_operand" "r"))))] + "" + "ashd %2,%0") + +(define_expand "ashrhi3" + [(set (match_operand:HI 0 "general_operand" "=g") + (ashiftrt:HI (match_operand:HI 1 "general_operand" "g") + (match_operand:SI 2 "general_operand" "g")))] + "" + " +{ + if (GET_CODE (operands[2]) != CONST_INT) + operands[2] = gen_rtx (NEG, SImode, negate_rtx (SImode, operands[2])); +}") + +(define_insn "" + [(set (match_operand:HI 0 "general_operand" "=g") + (ashiftrt:HI (match_operand:HI 1 "general_operand" "0") + (match_operand:SI 2 "immediate_operand" "i")))] + "" + "ashw %n2,%0") + +(define_insn "" + [(set (match_operand:HI 0 "general_operand" "=g") + (ashiftrt:HI (match_operand:HI 1 "general_operand" "0") + (neg:SI (match_operand:SI 2 "general_operand" "r"))))] + "" + "ashw %2,%0") + +(define_expand "ashrqi3" + [(set (match_operand:QI 0 "general_operand" "=g") + (ashiftrt:QI (match_operand:QI 1 "general_operand" "g") + (match_operand:SI 2 "general_operand" "g")))] + "" + " +{ + if (GET_CODE (operands[2]) != CONST_INT) + operands[2] = gen_rtx (NEG, SImode, negate_rtx (SImode, operands[2])); +}") + +(define_insn "" + [(set (match_operand:QI 0 "general_operand" "=g") + (ashiftrt:QI (match_operand:QI 1 "general_operand" "0") + (match_operand:SI 2 "immediate_operand" "i")))] + "" + "ashb %n2,%0") + +(define_insn "" + [(set (match_operand:QI 0 "general_operand" "=g") + (ashiftrt:QI (match_operand:QI 1 "general_operand" "0") + (neg:SI (match_operand:SI 2 "general_operand" "r"))))] + "" + "ashb %2,%0") + +;; logical shift instructions + +;; Logical right shift on the 32k works by negating the shift count. +(define_expand "lshrsi3" + [(set (match_operand:SI 0 "general_operand" "=g") + (lshiftrt:SI (match_operand:SI 1 "general_operand" "g") + (match_operand:SI 2 "general_operand" "g")))] + "" + " +{ + if (GET_CODE (operands[2]) != CONST_INT) + operands[2] = gen_rtx (NEG, SImode, negate_rtx (SImode, operands[2])); +}") + +(define_insn "" + [(set (match_operand:SI 0 "general_operand" "=g") + (lshiftrt:SI (match_operand:SI 1 "general_operand" "0") + (match_operand:SI 2 "immediate_operand" "i")))] + "" + "lshd %n2,%0") + +(define_insn "" + [(set (match_operand:SI 0 "general_operand" "=g") + (lshiftrt:SI (match_operand:SI 1 "general_operand" "0") + (neg:SI (match_operand:SI 2 "general_operand" "r"))))] + "" + "lshd %2,%0") + +(define_expand "lshrhi3" + [(set (match_operand:HI 0 "general_operand" "=g") + (lshiftrt:HI (match_operand:HI 1 "general_operand" "g") + (match_operand:SI 2 "general_operand" "g")))] + "" + " +{ + if (GET_CODE (operands[2]) != CONST_INT) + operands[2] = gen_rtx (NEG, SImode, negate_rtx (SImode, operands[2])); +}") + +(define_insn "" + [(set (match_operand:HI 0 "general_operand" "=g") + (lshiftrt:HI (match_operand:HI 1 "general_operand" "0") + (match_operand:SI 2 "immediate_operand" "i")))] + "" + "lshw %n2,%0") + +(define_insn "" + [(set (match_operand:HI 0 "general_operand" "=g") + (lshiftrt:HI (match_operand:HI 1 "general_operand" "0") + (neg:SI (match_operand:SI 2 "general_operand" "r"))))] + "" + "lshw %2,%0") + +(define_expand "lshrqi3" + [(set (match_operand:QI 0 "general_operand" "=g") + (lshiftrt:QI (match_operand:QI 1 "general_operand" "g") + (match_operand:SI 2 "general_operand" "g")))] + "" + " +{ + if (GET_CODE (operands[2]) != CONST_INT) + operands[2] = gen_rtx (NEG, SImode, negate_rtx (SImode, operands[2])); +}") + +(define_insn "" + [(set (match_operand:QI 0 "general_operand" "=g") + (lshiftrt:QI (match_operand:QI 1 "general_operand" "0") + (match_operand:SI 2 "immediate_operand" "i")))] + "" + "lshb %n2,%0") + +(define_insn "" + [(set (match_operand:QI 0 "general_operand" "=g") + (lshiftrt:QI (match_operand:QI 1 "general_operand" "0") + (neg:SI (match_operand:SI 2 "general_operand" "r"))))] + "" + "lshb %2,%0") + +;; Rotate instructions + +;; See note 1 +(define_insn "rotlsi3" + [(set (match_operand:SI 0 "general_operand" "=g") + (rotate:SI (match_operand:SI 1 "general_operand" "0") + (match_operand:SI 2 "general_operand" "g")))] + "" + "rotd %2,%0") + +(define_insn "rotlhi3" + [(set (match_operand:HI 0 "general_operand" "=g") + (rotate:HI (match_operand:HI 1 "general_operand" "0") + (match_operand:SI 2 "general_operand" "g")))] + "" + "rotw %2,%0") + +(define_insn "rotlqi3" + [(set (match_operand:QI 0 "general_operand" "=g") + (rotate:QI (match_operand:QI 1 "general_operand" "0") + (match_operand:SI 2 "general_operand" "g")))] + "" + "rotb %2,%0") + +;; Right rotate on the 32k works by negating the shift count. +(define_expand "rotrsi3" + [(set (match_operand:SI 0 "general_operand" "=g") + (rotatert:SI (match_operand:SI 1 "general_operand" "g") + (match_operand:SI 2 "general_operand" "g")))] + "" + " +{ + if (GET_CODE (operands[2]) != CONST_INT) + operands[2] = gen_rtx (NEG, SImode, negate_rtx (SImode, operands[2])); +}") + +(define_insn "" + [(set (match_operand:SI 0 "general_operand" "=g") + (rotatert:SI (match_operand:SI 1 "general_operand" "0") + (match_operand:SI 2 "immediate_operand" "i")))] + "" + "rotd %n2,%0") + +(define_insn "" + [(set (match_operand:SI 0 "general_operand" "=g") + (rotatert:SI (match_operand:SI 1 "general_operand" "0") + (neg:SI (match_operand:SI 2 "general_operand" "r"))))] + "" + "rotd %2,%0") + +(define_expand "rotrhi3" + [(set (match_operand:HI 0 "general_operand" "=g") + (rotatert:HI (match_operand:HI 1 "general_operand" "g") + (match_operand:SI 2 "general_operand" "g")))] + "" + " +{ + if (GET_CODE (operands[2]) != CONST_INT) + operands[2] = gen_rtx (NEG, SImode, negate_rtx (SImode, operands[2])); +}") + +(define_insn "" + [(set (match_operand:HI 0 "general_operand" "=g") + (rotatert:HI (match_operand:HI 1 "general_operand" "0") + (match_operand:SI 2 "immediate_operand" "i")))] + "" + "rotw %n2,%0") + +(define_insn "" + [(set (match_operand:HI 0 "general_operand" "=g") + (rotatert:HI (match_operand:HI 1 "general_operand" "0") + (neg:SI (match_operand:SI 2 "general_operand" "r"))))] + "" + "rotw %2,%0") + +(define_expand "rotrqi3" + [(set (match_operand:QI 0 "general_operand" "=g") + (rotatert:QI (match_operand:QI 1 "general_operand" "g") + (match_operand:SI 2 "general_operand" "g")))] + "" + " +{ + if (GET_CODE (operands[2]) != CONST_INT) + operands[2] = gen_rtx (NEG, SImode, negate_rtx (SImode, operands[2])); +}") + +(define_insn "" + [(set (match_operand:QI 0 "general_operand" "=g") + (rotatert:QI (match_operand:QI 1 "general_operand" "0") + (match_operand:SI 2 "immediate_operand" "i")))] + "" + "rotb %n2,%0") + +(define_insn "" + [(set (match_operand:QI 0 "general_operand" "=g") + (rotatert:QI (match_operand:QI 1 "general_operand" "0") + (neg:SI (match_operand:SI 2 "general_operand" "r"))))] + "" + "rotb %2,%0") + +;;- load or push effective address +;; These come after the move, add, and multiply patterns +;; because we don't want pushl $1 turned into pushad 1. + +(define_insn "" + [(set (match_operand:SI 0 "general_operand" "=g<") + (match_operand:QI 1 "address_operand" "p"))] + "" + "* +{ + if (REG_P (operands[0]) + && GET_CODE (operands[1]) == MULT + && GET_CODE (XEXP (operands[1], 1)) == CONST_INT + && (INTVAL (XEXP (operands[1], 1)) == 2 + || INTVAL (XEXP (operands[1], 1)) == 4)) + { + rtx xoperands[3]; + xoperands[0] = operands[0]; + xoperands[1] = XEXP (operands[1], 0); + xoperands[2] = GEN_INT (INTVAL (XEXP (operands[1], 1)) >> 1); + return output_shift_insn (xoperands); + } + return \"addr %a1,%0\"; +}") + +;;; Index insns. These are about the same speed as multiply-add counterparts. +;;; but slower then using power-of-2 shifts if we can use them +; +;;; See note 1 +;(define_insn "" +; [(set (match_operand:SI 0 "register_operand" "=r") +; (plus:SI (match_operand:SI 1 "general_operand" "g") +; (mult:SI (match_operand:SI 2 "register_operand" "0") +; (plus:SI (match_operand:SI 3 "general_operand" "g") (const_int 1)))))] +; "GET_CODE (operands[3]) != CONST_INT || INTVAL (operands[3]) > 8" +; "indexd %0,%3,%1") +; +;(define_insn "" +; [(set (match_operand:SI 0 "register_operand" "=r") +; (plus:SI (mult:SI (match_operand:SI 1 "register_operand" "0") +; (plus:SI (match_operand:SI 2 "general_operand" "g") (const_int 1))) +; (match_operand:SI 3 "general_operand" "g")))] +; "GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) > 8" +; "indexd %0,%2,%3") + +;; Set, Clear, and Invert bit + +;; See note 1 +(define_insn "" + [(set (zero_extract:SI (match_operand:SI 0 "general_operand" "+g") + (const_int 1) + (match_operand:SI 1 "general_operand" "g")) + (const_int 1))] + "" + "sbitd %1,%0") + +;; See note 1 +(define_insn "" + [(set (zero_extract:SI (match_operand:SI 0 "general_operand" "+g") + (const_int 1) + (match_operand:SI 1 "general_operand" "g")) + (const_int 0))] + "" + "cbitd %1,%0") + +;; See note 1 +(define_insn "" + [(set (match_operand:SI 0 "general_operand" "+g") + (xor:SI (ashift:SI (const_int 1) + (match_operand:SI 1 "general_operand" "g")) + (match_dup 0)))] + "" + "ibitd %1,%0") + +;; See note 1 +(define_insn "" + [(set (match_operand:QI 0 "general_operand" "=g") + (xor:QI (subreg:QI + (ashift:SI (const_int 1) + (match_operand:QI 1 "general_operand" "g")) 0) + (match_dup 0)))] + "" + "ibitb %1,%0") + +;; Recognize jbs and jbc instructions. + +(define_insn "" + [(set (cc0) + (zero_extract (match_operand:SI 0 "general_operand" "rm") + (const_int 1) + (match_operand:SI 1 "general_operand" "g")))] + "" + "* +{ cc_status.flags = CC_Z_IN_F; + return \"tbitd %1,%0\"; +}") + +;; extract(base, width, offset) +;; Signed bitfield extraction is not supported in hardware on the +;; NS 32032. It is therefore better to let GCC figure out a +;; good strategy for generating the proper instruction sequence +;; and represent it as rtl. + +;; Optimize the case of extracting a byte or word from a register. +;; Otherwise we must load a register with the offset of the +;; chunk we want, and perform an extract insn (each of which +;; is very expensive). Since we use the stack to do our bit-twiddling +;; we cannot use it for a destination. Perhaps things are fast +;; enough on the 32532 that such hacks are not needed. + +(define_insn "" + [(set (match_operand:SI 0 "general_operand" "=ro") + (zero_extract:SI (match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "const_int_operand" "i") + (match_operand:SI 3 "const_int_operand" "i")))] + "(INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 16) + && (INTVAL (operands[3]) == 8 || INTVAL (operands[3]) == 16 || INTVAL (operands[3]) == 24)" + "* +{ + output_asm_insn (\"movd %1,tos\", operands); + if (INTVAL (operands[2]) == 16) + { + if (INTVAL (operands[3]) == 8) + output_asm_insn (\"movzwd 1(sp),%0\", operands); + else + output_asm_insn (\"movzwd 2(sp),%0\", operands); + } + else + { + if (INTVAL (operands[3]) == 8) + output_asm_insn (\"movzbd 1(sp),%0\", operands); + else if (INTVAL (operands[3]) == 16) + output_asm_insn (\"movzbd 2(sp),%0\", operands); + else + output_asm_insn (\"movzbd 3(sp),%0\", operands); + } + if (TARGET_32532 || TARGET_32332) + return \"cmpqd %$0,tos\"; + else + return \"adjspb %$-4\"; +}") + +;; The exts/ext instructions have the problem that they always access +;; 32 bits even if the bitfield is smaller. For example the instruction +;; extsd 7(r1),r0,2,5 +;; would read not only at address 7(r1) but also at 8(r1) to 10(r1). +;; If these addresses are in a different (unmapped) page a memory fault +;; is the result. +;; +;; Timing considerations: +;; movd 0(r1),r0 3 bytes +;; lshd -26,r0 4 +;; andd 0x1f,r0 5 +;; takes about 13 cycles on the 532 while +;; extsd 7(r1),r0,2,5 5 bytes +;; takes about 21 cycles. +;; +;; The inss/ins instructions suffer from the same problem. +;; +;; A machine specific option (-mbitfield/-mnobitfield) is used +;; to allow/disallow the use of these instructions. + +(define_insn "" + [(set (match_operand:SI 0 "general_operand" "=g<") + (zero_extract:SI (match_operand:SI 1 "register_operand" "g") + (match_operand:SI 2 "const_int_operand" "i") + (match_operand:SI 3 "general_operand" "rK")))] + "TARGET_BITFIELD" + "* +{ if (GET_CODE (operands[3]) == CONST_INT) + return \"extsd %1,%0,%3,%2\"; + else return \"extd %3,%1,%0,%2\"; +}") + +(define_insn "extzv" + [(set (match_operand:SI 0 "general_operand" "=g<") + (zero_extract:SI (match_operand:QI 1 "general_operand" "g") + (match_operand:SI 2 "const_int_operand" "i") + (match_operand:SI 3 "general_operand" "rK")))] + "TARGET_BITFIELD" + "* +{ if (GET_CODE (operands[3]) == CONST_INT) + return \"extsd %1,%0,%3,%2\"; + else return \"extd %3,%1,%0,%2\"; +}") + +(define_insn "" + [(set (zero_extract:SI (match_operand:SI 0 "memory_operand" "+o") + (match_operand:SI 1 "const_int_operand" "i") + (match_operand:SI 2 "general_operand" "rn")) + (match_operand:SI 3 "general_operand" "rm"))] + "TARGET_BITFIELD" + "* +{ if (GET_CODE (operands[2]) == CONST_INT) + { + if (INTVAL (operands[2]) >= 8) + { + operands[0] = adj_offsettable_operand (operands[0], + INTVAL (operands[2]) / 8); + operands[2] = GEN_INT (INTVAL (operands[2]) % 8); + } + if (INTVAL (operands[1]) <= 8) + return \"inssb %3,%0,%2,%1\"; + else if (INTVAL (operands[1]) <= 16) + return \"inssw %3,%0,%2,%1\"; + else + return \"inssd %3,%0,%2,%1\"; + } + return \"insd %2,%3,%0,%1\"; +}") + +(define_insn "" + [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+r") + (match_operand:SI 1 "const_int_operand" "i") + (match_operand:SI 2 "general_operand" "rK")) + (match_operand:SI 3 "general_operand" "rm"))] + "TARGET_BITFIELD" + "* +{ if (GET_CODE (operands[2]) == CONST_INT) + if (INTVAL (operands[1]) <= 8) + return \"inssb %3,%0,%2,%1\"; + else if (INTVAL (operands[1]) <= 16) + return \"inssw %3,%0,%2,%1\"; + else + return \"inssd %3,%0,%2,%1\"; + return \"insd %2,%3,%0,%1\"; +}") + +(define_insn "insv" + [(set (zero_extract:SI (match_operand:QI 0 "general_operand" "+g") + (match_operand:SI 1 "const_int_operand" "i") + (match_operand:SI 2 "general_operand" "rK")) + (match_operand:SI 3 "general_operand" "rm"))] + "TARGET_BITFIELD" + "* +{ if (GET_CODE (operands[2]) == CONST_INT) + if (INTVAL (operands[1]) <= 8) + return \"inssb %3,%0,%2,%1\"; + else if (INTVAL (operands[1]) <= 16) + return \"inssw %3,%0,%2,%1\"; + else + return \"inssd %3,%0,%2,%1\"; + return \"insd %2,%3,%0,%1\"; +}") + + +(define_insn "jump" + [(set (pc) + (label_ref (match_operand 0 "" "")))] + "" + "br %l0") + +(define_insn "beq" + [(set (pc) + (if_then_else (eq (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "* +{ if (cc_prev_status.flags & CC_Z_IN_F) + return \"bfc %l0\"; + else if (cc_prev_status.flags & CC_Z_IN_NOT_F) + return \"bfs %l0\"; + else return \"beq %l0\"; +}") + +(define_insn "bne" + [(set (pc) + (if_then_else (ne (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "* +{ if (cc_prev_status.flags & CC_Z_IN_F) + return \"bfs %l0\"; + else if (cc_prev_status.flags & CC_Z_IN_NOT_F) + return \"bfc %l0\"; + else return \"bne %l0\"; +}") + +(define_insn "bgt" + [(set (pc) + (if_then_else (gt (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "bgt %l0") + +(define_insn "bgtu" + [(set (pc) + (if_then_else (gtu (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "bhi %l0") + +(define_insn "blt" + [(set (pc) + (if_then_else (lt (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "blt %l0") + +(define_insn "bltu" + [(set (pc) + (if_then_else (ltu (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "blo %l0") + +(define_insn "bge" + [(set (pc) + (if_then_else (ge (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "bge %l0") + +(define_insn "bgeu" + [(set (pc) + (if_then_else (geu (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "bhs %l0") + +(define_insn "ble" + [(set (pc) + (if_then_else (le (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "ble %l0") + +(define_insn "bleu" + [(set (pc) + (if_then_else (leu (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "bls %l0") + +(define_insn "" + [(set (pc) + (if_then_else (eq (cc0) + (const_int 0)) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "* +{ if (cc_prev_status.flags & CC_Z_IN_F) + return \"bfs %l0\"; + else if (cc_prev_status.flags & CC_Z_IN_NOT_F) + return \"bfc %l0\"; + else return \"bne %l0\"; +}") + +(define_insn "" + [(set (pc) + (if_then_else (ne (cc0) + (const_int 0)) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "* +{ if (cc_prev_status.flags & CC_Z_IN_F) + return \"bfc %l0\"; + else if (cc_prev_status.flags & CC_Z_IN_NOT_F) + return \"bfs %l0\"; + else return \"beq %l0\"; +}") + +(define_insn "" + [(set (pc) + (if_then_else (gt (cc0) + (const_int 0)) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "ble %l0") + +(define_insn "" + [(set (pc) + (if_then_else (gtu (cc0) + (const_int 0)) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "bls %l0") + +(define_insn "" + [(set (pc) + (if_then_else (lt (cc0) + (const_int 0)) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "bge %l0") + +(define_insn "" + [(set (pc) + (if_then_else (ltu (cc0) + (const_int 0)) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "bhs %l0") + +(define_insn "" + [(set (pc) + (if_then_else (ge (cc0) + (const_int 0)) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "blt %l0") + +(define_insn "" + [(set (pc) + (if_then_else (geu (cc0) + (const_int 0)) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "blo %l0") + +(define_insn "" + [(set (pc) + (if_then_else (le (cc0) + (const_int 0)) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "bgt %l0") + +(define_insn "" + [(set (pc) + (if_then_else (leu (cc0) + (const_int 0)) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "bhi %l0") + +;; Subtract-and-jump and Add-and-jump insns. +;; These can actually be used for adding numbers in the range -8 to 7 + +(define_insn "" + [(set (pc) + (if_then_else + (ne (match_operand:SI 0 "general_operand" "+g") + (match_operand:SI 1 "const_int_operand" "i")) + (label_ref (match_operand 2 "" "")) + (pc))) + (set (match_dup 0) + (minus:SI (match_dup 0) + (match_dup 1)))] + "INTVAL (operands[1]) > -8 && INTVAL (operands[1]) <= 8" + "acbd %n1,%0,%l2") + +(define_insn "" + [(set (pc) + (if_then_else + (ne (match_operand:SI 0 "general_operand" "+g") + (match_operand:SI 1 "const_int_operand" "i")) + (label_ref (match_operand 2 "" "")) + (pc))) + (set (match_dup 0) + (plus:SI (match_dup 0) + (match_operand:SI 3 "const_int_operand" "i")))] + "INTVAL (operands[1]) == - INTVAL (operands[3]) + && INTVAL (operands[3]) >= -8 && INTVAL (operands[3]) < 8" + "acbd %3,%0,%l2") + +(define_insn "call" + [(call (match_operand:QI 0 "memory_operand" "m") + (match_operand:QI 1 "general_operand" "g"))] + "" + "* +{ +#ifndef JSR_ALWAYS + if (GET_CODE (operands[0]) == MEM) + { + rtx temp = XEXP (operands[0], 0); + if (CONSTANT_ADDRESS_P (temp)) + { +#ifdef ENCORE_ASM + return \"bsr %?%0\"; +#else +#ifdef CALL_MEMREF_IMPLICIT + operands[0] = temp; + return \"bsr %0\"; +#else +#ifdef GNX_V3 + return \"bsr %0\"; +#else + return \"bsr %?%a0\"; +#endif +#endif +#endif + } + if (GET_CODE (XEXP (operands[0], 0)) == REG) +#if defined (GNX_V3) || defined (CALL_MEMREF_IMPLICIT) + return \"jsr %0\"; +#else + return \"jsr %a0\"; +#endif + } +#endif /* not JSR_ALWAYS */ + return \"jsr %0\"; +}") + +(define_insn "call_value" + [(set (match_operand 0 "" "=rf") + (call (match_operand:QI 1 "memory_operand" "m") + (match_operand:QI 2 "general_operand" "g")))] + "" + "* +{ +#ifndef JSR_ALWAYS + if (GET_CODE (operands[1]) == MEM) + { + rtx temp = XEXP (operands[1], 0); + if (CONSTANT_ADDRESS_P (temp)) + { +#ifdef ENCORE_ASM + return \"bsr %?%1\"; +#else +#ifdef CALL_MEMREF_IMPLICIT + operands[1] = temp; + return \"bsr %1\"; +#else +#ifdef GNX_V3 + return \"bsr %1\"; +#else + return \"bsr %?%a1\"; +#endif +#endif +#endif + } + if (GET_CODE (XEXP (operands[1], 0)) == REG) +#if defined (GNX_V3) || defined (CALL_MEMREF_IMPLICIT) + return \"jsr %1\"; +#else + return \"jsr %a1\"; +#endif + } +#endif /* not JSR_ALWAYS */ + return \"jsr %1\"; +}") + +;; 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 "return" + [(return)] + "0" + "ret 0") + +(define_insn "abssf2" + [(set (match_operand:SF 0 "general_operand" "=fm<") + (abs:SF (match_operand:SF 1 "general_operand" "fmF")))] + "TARGET_32081" + "absf %1,%0") + +(define_insn "absdf2" + [(set (match_operand:DF 0 "general_operand" "=lm<") + (abs:DF (match_operand:DF 1 "general_operand" "lmF")))] + "TARGET_32081" + "absl %1,%0") + +;; See note 1 +(define_insn "abssi2" + [(set (match_operand:SI 0 "general_operand" "=g<") + (abs:SI (match_operand:SI 1 "general_operand" "g")))] + "" + "absd %1,%0") + +(define_insn "abshi2" + [(set (match_operand:HI 0 "general_operand" "=g<") + (abs:HI (match_operand:HI 1 "general_operand" "g")))] + "" + "absw %1,%0") + +(define_insn "absqi2" + [(set (match_operand:QI 0 "general_operand" "=g<") + (abs:QI (match_operand:QI 1 "general_operand" "g")))] + "" + "absb %1,%0") + +(define_insn "nop" + [(const_int 0)] + "" + "nop") + +(define_insn "indirect_jump" + [(set (pc) (match_operand:SI 0 "register_operand" "r"))] + "" + "jump %0") + +(define_insn "tablejump" + [(set (pc) + (plus:SI (pc) (match_operand:SI 0 "general_operand" "g"))) + (use (label_ref (match_operand 1 "" "")))] + "" + "* +{ + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"LI\", + CODE_LABEL_NUMBER (operands[1])); + return \"cased %0\"; +}") + +;; Scondi instructions +(define_insn "seq" + [(set (match_operand:SI 0 "general_operand" "=g<") + (eq:SI (cc0) (const_int 0)))] + "" + "* +{ if (cc_prev_status.flags & CC_Z_IN_F) + return \"sfcd %0\"; + else if (cc_prev_status.flags & CC_Z_IN_NOT_F) + return \"sfsd %0\"; + else return \"seqd %0\"; +}") + +(define_insn "" + [(set (match_operand:HI 0 "general_operand" "=g<") + (eq:HI (cc0) (const_int 0)))] + "" + "* +{ if (cc_prev_status.flags & CC_Z_IN_F) + return \"sfcw %0\"; + else if (cc_prev_status.flags & CC_Z_IN_NOT_F) + return \"sfsw %0\"; + else return \"seqw %0\"; +}") + +(define_insn "" + [(set (match_operand:QI 0 "general_operand" "=g<") + (eq:QI (cc0) (const_int 0)))] + "" + "* +{ if (cc_prev_status.flags & CC_Z_IN_F) + return \"sfcb %0\"; + else if (cc_prev_status.flags & CC_Z_IN_NOT_F) + return \"sfsb %0\"; + else return \"seqb %0\"; +}") + +(define_insn "sne" + [(set (match_operand:SI 0 "general_operand" "=g<") + (ne:SI (cc0) (const_int 0)))] + "" + "* +{ if (cc_prev_status.flags & CC_Z_IN_F) + return \"sfsd %0\"; + else if (cc_prev_status.flags & CC_Z_IN_NOT_F) + return \"sfcd %0\"; + else return \"sned %0\"; +}") + +(define_insn "" + [(set (match_operand:HI 0 "general_operand" "=g<") + (ne:HI (cc0) (const_int 0)))] + "" + "* +{ if (cc_prev_status.flags & CC_Z_IN_F) + return \"sfsw %0\"; + else if (cc_prev_status.flags & CC_Z_IN_NOT_F) + return \"sfcw %0\"; + else return \"snew %0\"; +}") + +(define_insn "" + [(set (match_operand:QI 0 "general_operand" "=g<") + (ne:QI (cc0) (const_int 0)))] + "" + "* +{ if (cc_prev_status.flags & CC_Z_IN_F) + return \"sfsb %0\"; + else if (cc_prev_status.flags & CC_Z_IN_NOT_F) + return \"sfcb %0\"; + else return \"sneb %0\"; +}") + +(define_insn "sgt" + [(set (match_operand:SI 0 "general_operand" "=g<") + (gt:SI (cc0) (const_int 0)))] + "" + "sgtd %0") + +(define_insn "" + [(set (match_operand:HI 0 "general_operand" "=g<") + (gt:HI (cc0) (const_int 0)))] + "" + "sgtw %0") + +(define_insn "" + [(set (match_operand:QI 0 "general_operand" "=g<") + (gt:QI (cc0) (const_int 0)))] + "" + "sgtb %0") + +(define_insn "sgtu" + [(set (match_operand:SI 0 "general_operand" "=g<") + (gtu:SI (cc0) (const_int 0)))] + "" + "shid %0") + +(define_insn "" + [(set (match_operand:HI 0 "general_operand" "=g<") + (gtu:HI (cc0) (const_int 0)))] + "" + "shiw %0") + +(define_insn "" + [(set (match_operand:QI 0 "general_operand" "=g<") + (gtu:QI (cc0) (const_int 0)))] + "" + "shib %0") + +(define_insn "slt" + [(set (match_operand:SI 0 "general_operand" "=g<") + (lt:SI (cc0) (const_int 0)))] + "" + "sltd %0") + +(define_insn "" + [(set (match_operand:HI 0 "general_operand" "=g<") + (lt:HI (cc0) (const_int 0)))] + "" + "sltw %0") + +(define_insn "" + [(set (match_operand:QI 0 "general_operand" "=g<") + (lt:QI (cc0) (const_int 0)))] + "" + "sltb %0") + +(define_insn "sltu" + [(set (match_operand:SI 0 "general_operand" "=g<") + (ltu:SI (cc0) (const_int 0)))] + "" + "slod %0") + +(define_insn "" + [(set (match_operand:HI 0 "general_operand" "=g<") + (ltu:HI (cc0) (const_int 0)))] + "" + "slow %0") + +(define_insn "" + [(set (match_operand:QI 0 "general_operand" "=g<") + (ltu:QI (cc0) (const_int 0)))] + "" + "slob %0") + +(define_insn "sge" + [(set (match_operand:SI 0 "general_operand" "=g<") + (ge:SI (cc0) (const_int 0)))] + "" + "sged %0") + +(define_insn "" + [(set (match_operand:HI 0 "general_operand" "=g<") + (ge:HI (cc0) (const_int 0)))] + "" + "sgew %0") + +(define_insn "" + [(set (match_operand:QI 0 "general_operand" "=g<") + (ge:QI (cc0) (const_int 0)))] + "" + "sgeb %0") + +(define_insn "sgeu" + [(set (match_operand:SI 0 "general_operand" "=g<") + (geu:SI (cc0) (const_int 0)))] + "" + "shsd %0") + +(define_insn "" + [(set (match_operand:HI 0 "general_operand" "=g<") + (geu:HI (cc0) (const_int 0)))] + "" + "shsw %0") + +(define_insn "" + [(set (match_operand:QI 0 "general_operand" "=g<") + (geu:QI (cc0) (const_int 0)))] + "" + "shsb %0") + +(define_insn "sle" + [(set (match_operand:SI 0 "general_operand" "=g<") + (le:SI (cc0) (const_int 0)))] + "" + "sled %0") + +(define_insn "" + [(set (match_operand:HI 0 "general_operand" "=g<") + (le:HI (cc0) (const_int 0)))] + "" + "slew %0") + +(define_insn "" + [(set (match_operand:QI 0 "general_operand" "=g<") + (le:QI (cc0) (const_int 0)))] + "" + "sleb %0") + +(define_insn "sleu" + [(set (match_operand:SI 0 "general_operand" "=g<") + (leu:SI (cc0) (const_int 0)))] + "" + "slsd %0") + +(define_insn "" + [(set (match_operand:HI 0 "general_operand" "=g<") + (leu:HI (cc0) (const_int 0)))] + "" + "slsw %0") + +(define_insn "" + [(set (match_operand:QI 0 "general_operand" "=g<") + (leu:QI (cc0) (const_int 0)))] + "" + "slsb %0") + +;; ffs instructions + +(define_insn "" + [(set (match_operand:SI 0 "general_operand" "ro") + (minus:SI + (plus:SI (ffs:SI (zero_extract:SI + (match_operand:SI 1 "general_operand" "g") + (minus:SI (const_int 32) (match_dup 0)) + (match_dup 0))) + (match_dup 0)) + (const_int 1)))] + "" + "ffsd %1,%0; bfc 1f; addqd %$-1,%0; 1:") + +(define_expand "ffssi2" + [(set (match_operand:SI 0 "general_operand" "=g") (const_int 0)) + (set (match_dup 0) + (minus:SI + (plus:SI (ffs:SI (zero_extract:SI + (match_operand:SI 1 "general_operand" "g") + (minus:SI (const_int 32) (match_dup 0)) + (match_dup 0))) + (match_dup 0)) + (const_int 1))) + (set (match_dup 0) + (plus:SI (match_dup 0) + (const_int 1)))] + "" + "operands[1] = make_safe_from(operands[1], operands[0]);") + +;; Speed up stack adjust followed by a HI fixedpoint push. + +(define_peephole + [(set (reg:SI 25) (plus:SI (reg:SI 25) (const_int -2))) + (set (match_operand:HI 0 "push_operand" "=m") + (match_operand:HI 1 "general_operand" "g"))] + "! reg_mentioned_p (stack_pointer_rtx, operands[1])" + "* +{ + if (GET_CODE (operands[1]) == CONST_INT) + output_asm_insn (output_move_dconst (INTVAL (operands[1]), \"%1,tos\"), + operands); + else + output_asm_insn (\"movzwd %1,tos\", operands); + return \"\"; +}") + +;; Speed up stack adjust followed by a zero_extend:HI(QI) fixedpoint push. + +(define_peephole + [(set (reg:SI 25) (plus:SI (reg:SI 25) (const_int -2))) + (set (match_operand:HI 0 "push_operand" "=m") + (zero_extend:HI (match_operand:QI 1 "general_operand" "g")))] + "! reg_mentioned_p (stack_pointer_rtx, operands[1])" + "* +{ + if (GET_CODE (operands[1]) == CONST_INT) + output_asm_insn (output_move_dconst (INTVAL (operands[1]), \"%1,tos\"), + operands); + else + output_asm_insn (\"movzbd %1,tos\", operands); + return \"\"; +}") + +;; Speed up stack adjust followed by a sign_extend:HI(QI) fixedpoint push. + +(define_peephole + [(set (reg:SI 25) (plus:SI (reg:SI 25) (const_int -2))) + (set (match_operand:HI 0 "push_operand" "=m") + (sign_extend:HI (match_operand:QI 1 "general_operand" "g")))] + "! reg_mentioned_p (stack_pointer_rtx, operands[1])" + "* +{ + if (GET_CODE (operands[1]) == CONST_INT) + output_asm_insn (output_move_dconst (INTVAL (operands[1]), \"%1,tos\"), + operands); + else + output_asm_insn (\"movxbd %1,tos\", operands); + return \"\"; +}") + +;; Speed up stack adjust followed by a QI fixedpoint push. + +(define_peephole + [(set (reg:SI 25) (plus:SI (reg:SI 25) (const_int -3))) + (set (match_operand:QI 0 "push_operand" "=m") + (match_operand:QI 1 "general_operand" "g"))] + "! reg_mentioned_p (stack_pointer_rtx, operands[1])" + "* +{ + if (GET_CODE (operands[1]) == CONST_INT) + output_asm_insn (output_move_dconst (INTVAL (operands[1]), \"%1,tos\"), + operands); + else + output_asm_insn (\"movzbd %1,tos\", operands); + return \"\"; +}") + +;; Speed up stack adjust followed by a SI fixedpoint push. + +(define_peephole + [(set (reg:SI 25) (plus:SI (reg:SI 25) (const_int 4))) + (set (match_operand:SI 0 "push_operand" "=m") + (match_operand:SI 1 "general_operand" "g"))] + "! reg_mentioned_p (stack_pointer_rtx, operands[1])" + "* +{ + if (GET_CODE (operands[1]) == CONST_INT) + output_asm_insn (output_move_dconst (INTVAL (operands[1]), \"%1,0(sp)\"), + operands); + else if (GET_CODE (operands[1]) != REG + && GET_CODE (operands[1]) != MEM + && address_operand (operands[1], SImode)) + output_asm_insn (\"addr %a1,0(sp)\", operands); + else + output_asm_insn (\"movd %1,0(sp)\", operands); + return \"\"; +}") + +;; Speed up stack adjust followed by two fullword fixedpoint pushes. + +(define_peephole + [(set (reg:SI 25) (plus:SI (reg:SI 25) (const_int 8))) + (set (match_operand:SI 0 "push_operand" "=m") + (match_operand:SI 1 "general_operand" "g")) + (set (match_operand:SI 2 "push_operand" "=m") + (match_operand:SI 3 "general_operand" "g"))] + "! reg_mentioned_p (stack_pointer_rtx, operands[1]) + && ! reg_mentioned_p (stack_pointer_rtx, operands[3])" + "* +{ + if (GET_CODE (operands[1]) == CONST_INT) + output_asm_insn (output_move_dconst (INTVAL (operands[1]), \"%1,4(sp)\"), + operands); + else if (GET_CODE (operands[1]) != REG + && GET_CODE (operands[1]) != MEM + && address_operand (operands[1], SImode)) + output_asm_insn (\"addr %a1,4(sp)\", operands); + else + output_asm_insn (\"movd %1,4(sp)\", operands); + + if (GET_CODE (operands[3]) == CONST_INT) + output_asm_insn (output_move_dconst (INTVAL (operands[3]), \"%3,0(sp)\"), + operands); + else if (GET_CODE (operands[3]) != REG + && GET_CODE (operands[3]) != MEM + && address_operand (operands[3], SImode)) + output_asm_insn (\"addr %a3,0(sp)\", operands); + else + output_asm_insn (\"movd %3,0(sp)\", operands); + return \"\"; +}") diff --git a/gcc/config/ns32k/pc532-mach.h b/gcc/config/ns32k/pc532-mach.h new file mode 100755 index 0000000..0aeabad --- /dev/null +++ b/gcc/config/ns32k/pc532-mach.h @@ -0,0 +1,30 @@ +/* Definitions of target machine for GNU compiler. + PC532 with National 32532, running Mach 3.0. + Copyright (C) 1992, 1994 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. */ + +#include "ns32k/pc532.h" + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dns32532 -DPC532 -DMACH=1 -Asystem(unix) -Asystem(mach) -Acpu(ns32k) -Amachine(ns32k)" + +/* There's a bug in the setjmp implementation that strikes + if the caller of setjmp doesn't have a frame pointer. */ +#undef FRAME_POINTER_REQUIRED +#define FRAME_POINTER_REQUIRED current_function_calls_setjmp diff --git a/gcc/config/ns32k/pc532-min.h b/gcc/config/ns32k/pc532-min.h new file mode 100755 index 0000000..9d3d5de --- /dev/null +++ b/gcc/config/ns32k/pc532-min.h @@ -0,0 +1,41 @@ +/* Definitions of target machine for GNU compiler. + PC532 with National 32532, running Minix. + Works with pc532 Minix 1.5hybrid. + Copyright (C) 1990 Free Software Foundation, Inc. + + Derived from SEQUENT NS32000, written originally + by Bruce Culbertson <culberts@hplabs.hp.com>, + hacked for easier fit in gcc by Jyrki Kuoppala <jkp@cs.hut.fi>. + +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 "ns32k/pc532.h" + +/* Minix has crtso.o instead of crt0.o */ +#undef STARTFILE_SPEC +#define STARTFILE_SPEC \ + "%{pg:gcrtso.o%s}%{!pg:%{p:mcrtso.o%s}%{!p:crtso.o%s}}" + +/* our setjmp doesn't save registers, so we must tell gcc to save + call-saved-regs in a function calling setjmp */ + +#define NON_SAVING_SETJMP (current_function_calls_setjmp) + +/* at least with estdio there's no _cleanup() but we have atexit() */ + +#define HAVE_ATEXIT diff --git a/gcc/config/ns32k/pc532.h b/gcc/config/ns32k/pc532.h new file mode 100755 index 0000000..d98bf43 --- /dev/null +++ b/gcc/config/ns32k/pc532.h @@ -0,0 +1,73 @@ +/* Definitions of target machine for GNU compiler. + PC532 with National 32532. + Copyright (C) 1990, 1994 Free Software Foundation, Inc. + Contributed by Jukka Virtanen <jtv@hut.fi>, Jyrki Kuoppala <jkp@cs.hut.fi>, + Tatu Yl|nen <ylo@ngs.fi>, Johannes Helander <jvh@cs.hut.fi>. + +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 "ns32k/ns32k.h" + +/* Compile for the floating point unit & 32532 by default; + also presume SB is zero and no bitfield instructions */ + +#define TARGET_DEFAULT (1 + 24 + 64) + +/* Write DBX debugging info for gdb to read */ + +#define DBX_DEBUGGING_INFO + +/* Use the re-entrant and potentially faster method */ + +#undef PCC_STATIC_STRUCT_RETURN + +/* 32-bit alignment for efficiency */ +#undef POINTER_BOUNDARY +#define POINTER_BOUNDARY 32 + +/* 32-bit alignment for efficiency */ +#undef FUNCTION_BOUNDARY +#define FUNCTION_BOUNDARY 32 + +/* 32532 spec says it can handle any alignment. Rumor from tm-ns32k.h + tells this might not be actually true (but it's for 32032, perhaps + National has fixed the bug for 32532). You might have to change this + if the bug still exists. */ + +#undef STRICT_ALIGNMENT +#define STRICT_ALIGNMENT 0 + +/* Maybe someone needs to know which processor we're running on */ + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dns32000 -Dns32532 -Dpc532 -Dunix -Asystem(unix) -Acpu(ns32k) -Amachine(ns32k)" + +/* Use pc relative addressing whenever possible, + it's more efficient than absolute (ns32k.c) + You have to fix a bug in gas 1.38.1 to make this work with gas, + patch available from jkp@cs.hut.fi. */ + +#define PC_RELATIVE + +/* Operand of bsr or jsr should be just the address. */ + +#define CALL_MEMREF_IMPLICIT + +/* movd insns may have floating point constant operands. */ + +#define MOVD_FLOAT_OK diff --git a/gcc/config/ns32k/sequent.h b/gcc/config/ns32k/sequent.h new file mode 100755 index 0000000..1e8c353 --- /dev/null +++ b/gcc/config/ns32k/sequent.h @@ -0,0 +1,77 @@ +/* Definitions of target machine for GNU compiler. SEQUENT NS32000 version. + Copyright (C) 1987 Free Software Foundation, Inc. + Contributed by Michael Tiemann (tiemann@mcc.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 "ns32k/ns32k.h" + +/* This is BSD, so it wants DBX format. */ +#define DBX_DEBUGGING_INFO + +/* Sequent has some changes in the format of DBX symbols. */ +#define DBX_NO_XREFS 1 + +/* Don't split DBX symbols into continuations. */ +#define DBX_CONTIN_LENGTH 0 + +#define TARGET_DEFAULT 9 /* 32332 with 32081 (guessing). */ + +/* Print subsidiary information on the compiler version in use. */ +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (32000, Sequent syntax)"); + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES "-Dns32000 -Dsequent -Dunix -Asystem(unix) -Asystem(bsd) -Acpu(ns32k) -Amachine(ns32k)" + +/* Link with libg.a when debugging, for dbx's sake. */ + +#define LIB_SPEC "%{g:-lg} %{!p:%{!pg:-lc}}%{p:-lc_p}%{pg:-lc_p} " + +/* gcc should find libgcc.a itself, not ask linker to do so. */ + +#define LINK_LIBGCC_SPECIAL + +/* GCC must match what sys/types.h uses for size_t. */ + +#define SIZE_TYPE "int" + +/* This is how to align the code that follows an unconditional branch. + Don't define it, since it confuses the assembler (we hear). */ + +#undef LABEL_ALIGN_AFTER_BARRIER + +/* Assembler pseudo-op for shared data segment. */ +#define SHARED_SECTION_ASM_OP ".shdata" + +/* Control how stack adjust insns are output. */ +#define SEQUENT_ADJUST_STACK + +#define NO_ABSOLUTE_PREFIX_IF_SYMBOLIC + +#define IMMEDIATE_PREFIX 0 + +#define SEQUENT_ASM + +/* Operand of bsr or jsr should be just the address. */ + +#define CALL_MEMREF_IMPLICIT + +/* Output a reg as an index rather than a base if we have the choice. */ + +#define INDEX_RATHER_THAN_BASE diff --git a/gcc/config/ns32k/tek6000.h b/gcc/config/ns32k/tek6000.h new file mode 100755 index 0000000..01c88e3 --- /dev/null +++ b/gcc/config/ns32k/tek6000.h @@ -0,0 +1,237 @@ +/* Definitions of target machine for GNU compiler. + Generic Tektronix 6000 series NS32000 version. + See ns32k/tek6100.h and ns32k/tek6200.h, which include this file. + Copyright (C) 1990 Free Software Foundation, Inc. + Created by Snoopy (sopwith.uucp!snoopy). + Based on work by Mark Mason (mason@reed.bitnet, + pyramid!unify!mason@uunet.uu.net) and Keith Packard. + +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. */ + +/* Generate syntax for the UTek assembler. */ +#ifndef UTEK_ASM +#define UTEK_ASM +#endif + +/* Two flags to control how addresses are printed in assembler insns. */ + +/* The way PUT_ABSOLUTE_PREFIX in ns32k.h works, setting it to 0 will + * turn it off. Define ABSOLUTE_PREFIX before including ns32k.h. + */ +#define ABSOLUTE_PREFIX 0 +#define IMMEDIATE_PREFIX '$' + +#include "ns32k/ns32k.h" + +/* Define these after ns32k.c so we will notice if gcc tries to + * output external mode addressing. UTek's as and ld do not support + * external mode addressing, according to Daryl McDaniel (illian.uucp!darylm). + * Hopefully the UTek assembler will complain if gcc feeds it this stuff. + * They don't seem to do anything, I think that gcc is not actually + * trying to generate external mode operands. + */ +#undef PUT_EXTERNAL_PREFIX +#define PUT_EXTERNAL_PREFIX(arg) fprintf(arg, " Should not be using external mode under UTek. ") +#define EXTERNAL_PREFIX '%' + +/* Used in ns32k.c to control syntax. */ +#define NO_ABSOLUTE_PREFIX_IF_SYMBOLIC +#define NO_IMMEDIATE_PREFIX_IF_SYMBOLIC + +/* Used in ns32k.md to specify syntax of bsr/jsr operand. */ +#define CALL_MEMREF_IMPLICIT + +/* #define PC_RELATIVE */ /* Seems to break things. */ +#define BASE_REG_NEEDED /* Seems to fix problem where external mode + * syntax was being generated. + */ + +/* ------------ Debugging Support ----------------------------- */ + +/* The sdb support does not yet work with UTek. Need to teach gcc + * how to create sdb type stabs as well as dbx style stabs. + */ +#define DBX_DEBUGGING_INFO +/* #define SDB_DEBUGGING_INFO */ + +/* Act the same as the UTek complier: -g for dbx, -go for sdb. + * This is used in toplev.c. + */ +#define PREFERRED_DEBUGGING_TYPE DBX_DEBUG + +#define CC1_SPEC "{go:-gcoff}" +#define CC1PLUS_SPEC "{go:-gcoff}" + +/* Sequent has some changes in the format of DBX symbols. */ +#define DBX_NO_XREFS 1 + +/* Don't split DBX symbols into continuations. */ +#define DBX_CONTIN_LENGTH 0 + +/* ------------------------------------------- */ + +#define TARGET_DEFAULT 1 + +/* These control the C++ compiler somehow. */ +#define FASCIST_ASSEMBLER +#define USE_COLLECT + +/* Print subsidiary information on the compiler version in use. */ +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (ns32k, UTek syntax)"); + +/* The tek6100.h and tek6200.h files add stratos or merlin respectively. */ + +#define CPP_PREDEFINES_Tek6000 \ + "-Dns16000 -Dns32000 -Dns32k -Dns32016 -DUTek -DUTEK -Dbsd -DBSD \ + -Asystem(unix) -Asystem(bsd) -Acpu(ns32k) -Amachine(ns32k)" +#undef CPP_PREDEFINES +#define CPP_PREDEFINES CPP_PREDEFINES_Tek6000 + +/* This is how to align the code that follows an unconditional branch. + Don't define it, since it confuses the assembler (we hear). */ + +#undef LABEL_ALIGN_AFTER_BARRIER + +/* Assembler pseudo-op for shared data segment. */ +#define SHARED_SECTION_ASM_OP ".shdata" + +#ifdef UTEK_ASM +#undef FUNCTION_PROLOGUE + +/* This differs from the one in ns32k.h in printing a bitmask + rather than a register list in the enter or save instruction. */ + +#define FUNCTION_PROLOGUE(FILE, SIZE) \ +{ register int regno, g_regs_used = 0; \ + int used_regs_buf[8], *bufp = used_regs_buf; \ + int used_fregs_buf[8], *fbufp = used_fregs_buf; \ + extern char call_used_regs[]; \ + MAIN_FUNCTION_PROLOGUE; \ + for (regno = 0; regno < 8; regno++) \ + if (regs_ever_live[regno] \ + && ! call_used_regs[regno]) \ + { \ + *bufp++ = regno; g_regs_used++; \ + } \ + *bufp = -1; \ + for (; regno < 16; regno++) \ + if (regs_ever_live[regno] && !call_used_regs[regno]) { \ + *fbufp++ = regno; \ + } \ + *fbufp = -1; \ + bufp = used_regs_buf; \ + if (frame_pointer_needed) \ + fprintf (FILE, "\tenter "); \ + else if (g_regs_used) \ + fprintf (FILE, "\tsave "); \ + if (frame_pointer_needed || g_regs_used) \ + { \ + char mask = 0; \ + while (*bufp >= 0) \ + mask |= 1 << *bufp++; \ + fprintf (FILE, "$0x%x", (int) mask & 0xff); \ + } \ + if (frame_pointer_needed) \ + fprintf (FILE, ",$%d\n", SIZE); \ + else if (g_regs_used) \ + fprintf (FILE, "\n"); \ + fbufp = used_fregs_buf; \ + while (*fbufp >= 0) \ + { \ + if ((*fbufp & 1) || (fbufp[0] != fbufp[1] - 1)) \ + fprintf (FILE, "\tmovf f%d,tos\n", *fbufp++ - 8); \ + else \ + { \ + fprintf (FILE, "\tmovl f%d,tos\n", fbufp[0] - 8); \ + fbufp += 2; \ + } \ + } \ +} + +#undef FUNCTION_EPILOGUE + +/* This differs from the one in ns32k.h in printing a bitmask + rather than a register list in the exit or restore instruction. */ + +#define FUNCTION_EPILOGUE(FILE, SIZE) \ +{ register int regno, g_regs_used = 0, f_regs_used = 0; \ + int used_regs_buf[8], *bufp = used_regs_buf; \ + int used_fregs_buf[8], *fbufp = used_fregs_buf; \ + extern char call_used_regs[]; \ + *fbufp++ = -2; \ + for (regno = 8; regno < 16; regno++) \ + if (regs_ever_live[regno] && !call_used_regs[regno]) { \ + *fbufp++ = regno; f_regs_used++; \ + } \ + fbufp--; \ + for (regno = 0; regno < 8; regno++) \ + if (regs_ever_live[regno] \ + && ! call_used_regs[regno]) \ + { \ + *bufp++ = regno; g_regs_used++; \ + } \ + while (fbufp > used_fregs_buf) \ + { \ + if ((*fbufp & 1) && fbufp[0] == fbufp[-1] + 1) \ + { \ + fprintf (FILE, "\tmovl tos,f%d\n", fbufp[-1] - 8); \ + fbufp -= 2; \ + } \ + else fprintf (FILE, "\tmovf tos,f%d\n", *fbufp-- - 8); \ + } \ + if (frame_pointer_needed) \ + fprintf (FILE, "\texit "); \ + else if (g_regs_used) \ + fprintf (FILE, "\trestore "); \ + if (g_regs_used || frame_pointer_needed) \ + { \ + char mask = 0; \ + \ + while (bufp > used_regs_buf) \ + { \ + /* Utek assembler takes care of reversing this */ \ + mask |= 1 << *--bufp; \ + } \ + fprintf (FILE, "$0x%x\n", (int) mask & 0xff); \ + } \ + if (current_function_pops_args) \ + fprintf (FILE, "\tret $%d\n", current_function_pops_args); \ + else fprintf (FILE, "\tret $0\n"); } + +/* UTek assembler needs "ret $0", not "ret 0". */ +#undef TRANSFER_FROM_TRAMPOLINE +#define TRANSFER_FROM_TRAMPOLINE \ +void \ +__transfer_from_trampoline () \ +{ \ + asm ("___trampoline:"); \ + asm ("movd 16(r2),tos"); \ + asm ("movd 12(r2),r2"); \ + asm ("ret $0"); \ +} + +#endif /* UTEK_ASM */ + +#undef PRINT_OPERAND_ADDRESS +#define PRINT_OPERAND_ADDRESS(FILE, ADDR) print_operand_address(FILE, ADDR) + +/* The UTek library supplies bcopy() and friends, not memcpy(). */ +#ifdef TARGET_MEM_FUNCTIONS +#undef TARGET_MEM_FUNCTIONS +#endif diff --git a/gcc/config/ns32k/tek6100.h b/gcc/config/ns32k/tek6100.h new file mode 100755 index 0000000..e5b385f --- /dev/null +++ b/gcc/config/ns32k/tek6100.h @@ -0,0 +1,7 @@ +#include "ns32k/tek6000.h" + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES \ + "-Dns32000 -Dns32k -Dns16000 -Dns32016 -DUTek -DUTEK -Dbsd -DBSD -Dstratos \ + -Asystem(unix) -Asystem(bsd) -Acpu(ns32k) -Amachine(ns32k)" + diff --git a/gcc/config/ns32k/tek6200.h b/gcc/config/ns32k/tek6200.h new file mode 100755 index 0000000..c03f255 --- /dev/null +++ b/gcc/config/ns32k/tek6200.h @@ -0,0 +1,7 @@ +#include "ns32k/tek6000.h" + +#undef CPP_PREDEFINES +#define CPP_PREDEFINES \ + "-Dns32000 -Dns32k -Dns16000 -Dns32016 -DUTek -DUTEK -Dbsd -DBSD -Dmerlin \ + -Asystem(unix) -Asystem(bsd) -Acpu(ns32k) -Amachine(ns32k)" + diff --git a/gcc/config/ns32k/x-genix b/gcc/config/ns32k/x-genix new file mode 100755 index 0000000..0598df8 --- /dev/null +++ b/gcc/config/ns32k/x-genix @@ -0,0 +1,6 @@ +# Makefile modifications for compilation on Genix. +ALLOCA=alloca.o +MALLOC = malloc.o + +# You must get malloc.c and getpagesize.h from GNU Emacs. + diff --git a/gcc/config/ns32k/xm-genix.h b/gcc/config/ns32k/xm-genix.h new file mode 100755 index 0000000..15ff46f --- /dev/null +++ b/gcc/config/ns32k/xm-genix.h @@ -0,0 +1,5 @@ +/* Config file for ns32k running system V. */ + +#include "ns32k/xm-ns32k.h" + +#define USG diff --git a/gcc/config/ns32k/xm-netbsd.h b/gcc/config/ns32k/xm-netbsd.h new file mode 100755 index 0000000..9d344dc --- /dev/null +++ b/gcc/config/ns32k/xm-netbsd.h @@ -0,0 +1,8 @@ +/* Configuration for GCC for ns32k running NetBSD as host. */ + +#include <ns32k/xm-ns32k.h> + +/* ns32k/xm-ns32k.h defines these macros, but we don't need them */ +#undef memcmp +#undef memcpy +#undef memset diff --git a/gcc/config/ns32k/xm-ns32k.h b/gcc/config/ns32k/xm-ns32k.h new file mode 100755 index 0000000..a5ef337 --- /dev/null +++ b/gcc/config/ns32k/xm-ns32k.h @@ -0,0 +1,42 @@ +/* Configuration for GNU C-compiler for Vax. + Copyright (C) 1987, 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. */ + +/* #defines that need visibility everywhere. */ +#define FALSE 0 +#define TRUE 1 + +/* target machine dependencies. + tm.h is a symbolic link to the actual target specific file. */ +#include "tm.h" + +/* 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 + +#define memcpy(src,dst,len) bcopy ((dst),(src),(len)) +#define memset gcc_memset +#define memcmp(left,right,len) bcmp ((left),(right),(len)) diff --git a/gcc/config/ns32k/xm-pc532-min.h b/gcc/config/ns32k/xm-pc532-min.h new file mode 100755 index 0000000..7097d7e --- /dev/null +++ b/gcc/config/ns32k/xm-pc532-min.h @@ -0,0 +1,7 @@ +#ifndef HZ +#define HZ 60 +#endif + +#ifndef MAXPATHLEN +#define MAXPATHLEN 1024 +#endif |