diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 7086dc36829..607e9790d81 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,23 @@ +2001-10-11 Hans-Peter Nilsson + + * config.gcc: Add cris-*-aout, cris-*-elf, cris-*-none, + cris-*-linux* cases. + * config/cris/cris-protos.h: New file. + * config/cris/cris.c: New file. + * config/cris/cris.h: New file. + * config/cris/cris.md: New file. + * config/cris/linux.h: New file. + * config/cris/aout.h: New file. + * config/cris/arit.c: New file. + * config/cris/cris_abi_symbol.c: New file. + * config/cris/mulsi3.asm: New file. + * config/cris/t-aout: New file. + * config/cris/t-cris: New file. + * config/cris/t-elfmulti: New file. + * config/cris/t-linux: New file. + * doc/invoke.texi: Add CRIS options. + * doc/install.texi (Specific): Add blurb for CRIS. + 2001-10-10 Hartmut Schirmer * config/float-i128.h: Make sure __STDC__VERSION__ is defined diff --git a/gcc/config.gcc b/gcc/config.gcc index 0998151fa9b..f6085953e2b 100644 --- a/gcc/config.gcc +++ b/gcc/config.gcc @@ -657,6 +657,27 @@ clipper-intergraph-clix*) extra_parts="crtbegin.o crtend.o" install_headers_dir=install-headers-cpio ;; +cris-*-aout) + tm_file="dbxelf.h cris/cris.h cris/aout.h" + gas=yes + tmake_file="cris/t-cris cris/t-aout" + ;; +cris-*-elf | cris-*-none) + tm_file="elfos.h cris/cris.h" + tmake_file="cris/t-cris cris/t-elfmulti" + gas=yes + ;; +cris-*-linux*) + tm_file="linux.h cris/cris.h cris/linux.h" + tmake_file="cris/t-cris t-slibgcc-elf-ver cris/t-linux" + extra_parts="crtbegin.o crtbeginS.o crtend.o crtendS.o" + case x${enable_threads} in + x | xyes | xpthreads | xposix) + thread_file=posix + ;; + esac + gas=yes + ;; d30v-*) float_format=i64 ;; diff --git a/gcc/config/cris/aout.h b/gcc/config/cris/aout.h new file mode 100644 index 00000000000..dc998f1b0fe --- /dev/null +++ b/gcc/config/cris/aout.h @@ -0,0 +1,422 @@ +/* Definitions for GCC. Part of the machine description for CRIS. + Copyright (C) 2001 Free Software Foundation, Inc. + Contributed by Axis Communications. Written by Hans-Peter Nilsson. + +This file is part of GCC. + +GCC 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. + +GCC 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 GCC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* After the first "Node:" comment comes all preprocessor directives and + attached declarations described in the info files, the "Using and + Porting GCC" manual (uapgcc), in the same order as found in the "Target + macros" section in the gcc-2.9x CVS edition of 2000-03-17. FIXME: Not + really, but needs an update anyway. + + There is no generic copy-of-uapgcc comment, you'll have to see uapgcc + for that. If applicable, there is a CRIS-specific comment. The order + of macro definitions follow the order in the manual. Every section in + the manual (node in the info pages) has an introductory `Node: + ' comment. If no macros are defined for a section, only + the section-comment is present. */ + +/* This file defines the macros for a.out that are not covered by cris.h. + Many macros are copied from elfos.h and should be in some generic + config/gas-aout.h. */ + +/* Node: Driver */ + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC \ + "%{melinux:crt0.o%s}\ + %{!melinux:\ + %{sim2:s2crt0.o%s}\ + %{!sim2:\ + %{sim:scrt0.o%s}\ + %{!sim:%{pg:gcrt0.o%s}\ + %{!pg:%{p:mcrt0.o%s}%{!p:crt0.o%s}}}}}" + +/* Which library to get. The only difference from the default is to get + libsc.a if -sim is given to the driver. Repeat -lc -lsysX + {X=sim,linux}, because libsysX needs (at least) errno from libc, and + then we want to resolve new unknowns in libc against libsysX, not + libnosys. Assume everything is in libc for -mlinux. */ +#undef LIB_SPEC +#define LIB_SPEC \ + "%{melinux:-lc -lsyslinux -lc -lsyslinux -lic}\ + %{!melinux:\ + %{sim*:-lc -lsyssim -lc -lsyssim}\ + %{!sim*:%{g*:-lg}\ + %{!p:%{!pg:-lc}}%{p:-lc_p}%{pg:-lc_p} -lbsp}\ + -lnosys}" + +#undef CRIS_CPP_SUBTARGET_SPEC +#define CRIS_CPP_SUBTARGET_SPEC \ + "-D__AOUT__\ + %{melinux:-D__linux__ -D__unix__ -D__elinux__ -D__uclinux__\ + %{!nostdinc:\ + %{!mbest-lib-options:%{isystem*}}\ + -isystem elinux/include%s\ + %{mbest-lib-options:%{isystem*}}}\ + %{!ansi:%{!std=*:%{!undef:-Dlinux -Dunix -Delinux -Duclinux}}}}\ + %{mbest-lib-options:\ + %{!moverride-best-lib-options:\ + %{!march=*:%{!metrax*:%{!mcpu=*:-D__tune_v8 -D__CRIS_arch_tune=8}}}}}" + +#undef CRIS_CC1_SUBTARGET_SPEC +#define CRIS_CC1_SUBTARGET_SPEC \ + "%{mbest-lib-options:\ + %{!moverride-best-lib-options:\ + %{!march=*:%{!mcpu=*:-mtune=v8}}}}" + +#undef CRIS_ASM_SUBTARGET_SPEC +#define CRIS_ASM_SUBTARGET_SPEC "--em=crisaout" + +#undef CRIS_LINK_SUBTARGET_SPEC +#define CRIS_LINK_SUBTARGET_SPEC \ + "-mcrisaout\ + %{sim2:%{!T*:-Tdata 0x4000000 -Tbss 0x8000000}}\ + %{melinux:-Ur -d\ + %{!shlib:%{!symbolic:-Bstatic}}\ + %{shlib:-Bdynamic}\ + %{symbolic:-Bdynamic}\ + %{static:-Bstatic}}\ + %{melinux-stacksize=*:-defsym __Stacksize=%*}" + +#undef CRIS_SUBTARGET_SWITCHES +#define CRIS_SUBTARGET_SWITCHES \ + {"elinux", (TARGET_MASK_SVINTO \ + + TARGET_MASK_STACK_ALIGN \ + + TARGET_MASK_CONST_ALIGN \ + + TARGET_MASK_DATA_ALIGN \ + + TARGET_MASK_ETRAX4_ADD \ + + TARGET_MASK_ALIGN_BY_32), \ + N_("Compile for the MMU-less Etrax 100-based elinux system")}, \ + /* Legacy option. */ \ + {"aout", 0, ""}, + +#undef CRIS_SUBTARGET_LONG_OPTIONS +#define CRIS_SUBTARGET_LONG_OPTIONS \ + {"elinux-stacksize=", &cris_elinux_stacksize_str, \ + N_("For elinux, request a specified stack-size for this program")}, \ + +#undef CRIS_SUBTARGET_VERSION +#define CRIS_SUBTARGET_VERSION " - a.out" + +#undef CRIS_SUBTARGET_DEFAULT +#define CRIS_SUBTARGET_DEFAULT 0 + +/* Node: Storage Layout */ + +/* We can align to 16 bits (only) with CRIS a.out. */ +#define MAX_OFILE_ALIGNMENT 16 + + +/* Node: Library Calls */ + +#define TARGET_MEM_FUNCTIONS + + +/* Node: Data Output */ + +#define ESCAPES \ +"\1\1\1\1\1\1\1\1btn\1fr\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\ +\0\0\"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\\\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\ +\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\ +\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\ +\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\ +\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1" + +/* Some svr4 assemblers have a limit on the number of characters which + can appear in the operand of a .string directive. If your assembler + has such a limitation, you should define STRING_LIMIT to reflect that + limit. Note that at least some svr4 assemblers have a limit on the + actual number of bytes in the double-quoted string, and that they + count each character in an escape sequence as one byte. Thus, an + escape sequence like \377 would count as four bytes. + + If your target assembler doesn't support the .string directive, you + should define this to zero. */ + +#define STRING_LIMIT ((unsigned) 256) + +#define STRING_ASM_OP "\t.string\t" +#define ASCII_DATA_ASM_OP "\t.ascii\t" +#define TYPE_ASM_OP "\t.type\t" +#define SIZE_ASM_OP "\t.size\t" +#define TYPE_OPERAND_FMT "@%s" + +/* The routine used to output NUL terminated strings. We use a special + version of this for most svr4 targets because doing so makes the + generated assembly code more compact (and thus faster to assemble) + as well as more readable, especially for targets like the i386 + (where the only alternative is to output character sequences as + comma separated lists of numbers). */ + +#define ASM_OUTPUT_LIMITED_STRING(FILE, STR) \ + do \ + { \ + register const unsigned char *_limited_str = \ + (const unsigned char *) (STR); \ + register unsigned ch; \ + \ + fprintf ((FILE), "%s\"", STRING_ASM_OP); \ + \ + for (; (ch = *_limited_str); _limited_str++) \ + { \ + register int escape; \ + \ + switch (escape = ESCAPES[ch]) \ + { \ + case 0: \ + putc (ch, (FILE)); \ + break; \ + case 1: \ + fprintf ((FILE), "\\%03o", ch); \ + break; \ + default: \ + putc ('\\', (FILE)); \ + putc (escape, (FILE)); \ + break; \ + } \ + } \ + \ + fprintf ((FILE), "\"\n"); \ + } \ + while (0) + +/* The routine used to output sequences of byte values. We use a special + version of this for most svr4 targets because doing so makes the + generated assembly code more compact (and thus faster to assemble) + as well as more readable. Note that if we find subparts of the + character sequence which end with NUL (and which are shorter than + STRING_LIMIT) we output those using ASM_OUTPUT_LIMITED_STRING. */ + +#undef ASM_OUTPUT_ASCII +#define ASM_OUTPUT_ASCII(FILE, STR, LENGTH) \ + do \ + { \ + register const unsigned char *_ascii_bytes = \ + (const unsigned char *) (STR); \ + register const unsigned char *limit = _ascii_bytes + (LENGTH); \ + register unsigned bytes_in_chunk = 0; \ + \ + for (; _ascii_bytes < limit; _ascii_bytes++) \ + { \ + register const unsigned char *p; \ + \ + if (bytes_in_chunk >= 60) \ + { \ + fprintf ((FILE), "\"\n"); \ + bytes_in_chunk = 0; \ + } \ + \ + for (p = _ascii_bytes; p < limit && *p != '\0'; p++) \ + continue; \ + \ + if (p < limit && (p - _ascii_bytes) <= (long)STRING_LIMIT) \ + { \ + if (bytes_in_chunk > 0) \ + { \ + fprintf ((FILE), "\"\n"); \ + bytes_in_chunk = 0; \ + } \ + \ + ASM_OUTPUT_LIMITED_STRING ((FILE), _ascii_bytes); \ + _ascii_bytes = p; \ + } \ + else \ + { \ + register int escape; \ + register unsigned ch; \ + \ + if (bytes_in_chunk == 0) \ + fprintf ((FILE), "%s\"", ASCII_DATA_ASM_OP); \ + \ + switch (escape = ESCAPES[ch = *_ascii_bytes]) \ + { \ + case 0: \ + putc (ch, (FILE)); \ + bytes_in_chunk++; \ + break; \ + case 1: \ + fprintf ((FILE), "\\%03o", ch); \ + bytes_in_chunk += 4; \ + break; \ + default: \ + putc ('\\', (FILE)); \ + putc (escape, (FILE)); \ + bytes_in_chunk += 2; \ + break; \ + } \ + } \ + } \ + \ + if (bytes_in_chunk > 0) \ + fprintf ((FILE), "\"\n"); \ + } \ + while (0) + + +/* Node: Label Output */ + +#define SET_ASM_OP "\t.set\t" + +#define ASM_OUTPUT_EXTERNAL_LIBCALL(FILE, FUN) \ + ASM_GLOBALIZE_LABEL (FILE, XSTR (FUN, 0)) + +#define ASM_WEAKEN_LABEL(FILE, NAME) \ + do \ + { \ + fputs ("\t.weak\t", (FILE)); \ + assemble_name ((FILE), (NAME)); \ + fputc ('\n', (FILE)); \ + } \ + while (0) + +#define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL) \ + do \ + { \ + fprintf (FILE, "%s", TYPE_ASM_OP); \ + assemble_name (FILE, NAME); \ + putc (',', FILE); \ + fprintf (FILE, TYPE_OPERAND_FMT, "function"); \ + putc ('\n', FILE); \ + \ + ASM_OUTPUT_LABEL(FILE, NAME); \ + } \ + while (0) + +#define ASM_DECLARE_OBJECT_NAME(FILE, NAME, DECL) \ + do \ + { \ + fprintf (FILE, "%s", TYPE_ASM_OP); \ + assemble_name (FILE, NAME); \ + putc (',', FILE); \ + fprintf (FILE, TYPE_OPERAND_FMT, "object"); \ + putc ('\n', FILE); \ + \ + size_directive_output = 0; \ + \ + if (!flag_inhibit_size_directive \ + && (DECL) && DECL_SIZE (DECL)) \ + { \ + size_directive_output = 1; \ + fprintf (FILE, "%s", SIZE_ASM_OP); \ + assemble_name (FILE, NAME); \ + putc (',', FILE); \ + fprintf (FILE, HOST_WIDE_INT_PRINT_DEC, \ + int_size_in_bytes (TREE_TYPE (DECL))); \ + fputc ('\n', FILE); \ + } \ + \ + ASM_OUTPUT_LABEL (FILE, NAME); \ + } \ + while (0) + +#define ASM_FINISH_DECLARE_OBJECT(FILE, DECL, TOP_LEVEL, AT_END)\ + do \ + { \ + const char *name = XSTR (XEXP (DECL_RTL (DECL), 0), 0); \ + \ + if (!flag_inhibit_size_directive \ + && DECL_SIZE (DECL) \ + && ! AT_END && TOP_LEVEL \ + && DECL_INITIAL (DECL) == error_mark_node \ + && !size_directive_output) \ + { \ + size_directive_output = 1; \ + fprintf (FILE, "%s", SIZE_ASM_OP); \ + assemble_name (FILE, name); \ + putc (',', FILE); \ + fprintf (FILE, HOST_WIDE_INT_PRINT_DEC, \ + int_size_in_bytes (TREE_TYPE (DECL))); \ + fputc ('\n', FILE); \ + } \ + } \ + while (0) + +#define ASM_DECLARE_FUNCTION_SIZE(FILE, FNAME, DECL) \ + do \ + { \ + if (!flag_inhibit_size_directive) \ + { \ + char label[256]; \ + static int labelno; \ + \ + labelno++; \ + \ + ASM_GENERATE_INTERNAL_LABEL (label, "Lfe", labelno); \ + ASM_OUTPUT_INTERNAL_LABEL (FILE, "Lfe", labelno); \ + \ + fprintf (FILE, "%s", SIZE_ASM_OP); \ + assemble_name (FILE, (FNAME)); \ + fprintf (FILE, ","); \ + assemble_name (FILE, label); \ + fprintf (FILE, "-"); \ + assemble_name (FILE, (FNAME)); \ + putc ('\n', FILE); \ + } \ + } \ + while (0) + + +/* Node: Alignment Output */ + +#define SKIP_ASM_OP "\t.zero\t" + +#undef ASM_OUTPUT_SKIP +#define ASM_OUTPUT_SKIP(FILE, SIZE) \ + fprintf (FILE, "%s%u\n", SKIP_ASM_OP, (SIZE)) + +/* Node: All Debuggers */ + +#define PREFERRED_DEBUGGING_TYPE DBX_DEBUG + + +/* Node: Misc */ + +#define HANDLE_SYSV_PRAGMA + +/* In theory, this one isn't necessary, but over time, external tools have + been primed on names with "." rather than "$". */ +#define NO_DOLLAR_IN_LABEL + +/* These are undocumented, but to keep a single + CRIS_ASM_OUTPUT_ALIGNED_DECL_COMMON, we set this to an asm that will + emit an error if ever output. It will not be emitted for a.out modulo + careless hacking. */ +#define COMMON_ASM_OP "\t.err\t" +#define LOCAL_ASM_OP "\t.err\t" + +#if defined(__CRIS__) && defined (__AOUT__) && defined (IN_GCC) + +#define CRIS_ABI_VERSION_SYMBOL_STRING ".$CRIS_ABI_V2" + +/* Make all a.out library functions have undefined references to the + .$CRIS_ABI_V2 symbol, so it will be picked up. Used by GDB. GDB has + a bug with reading a.out symbols; it does not see the GNU weak + extensions, so we can't have .$CRIS_ABI_V2 weak. Weak. */ +__asm__ (".set .$abi_referer," CRIS_ABI_VERSION_SYMBOL_STRING); +#endif + +/* + * Local variables: + * eval: (c-set-style "gnu") + * indent-tabs-mode: t + * End: + */ diff --git a/gcc/config/cris/arit.c b/gcc/config/cris/arit.c new file mode 100644 index 00000000000..91f4e0560e9 --- /dev/null +++ b/gcc/config/cris/arit.c @@ -0,0 +1,302 @@ +/* Signed and unsigned multiplication and division and modulus for CRIS. + Contributed by Axis Communications. + Written by Hans-Peter Nilsson , c:a 1992. + + Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC 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. + +In addition to the permissions in the GNU General Public License, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file with other programs, and to distribute +those programs without any restriction coming from the use of this +file. (The General Public License restrictions do apply in other +respects; for example, they cover modification of the file, and +distribution when not linked into another program.) + +This file 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 this program; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + + As a special exception, if you link this library with files, some of + which are compiled with GCC, this library does not by itself cause + the resulting object or executable to be covered by the GNU General + Public License. + This exception does not however invalidate any other reasons why + the executable file or object might be covered by the GNU General + Public License. */ + + +/* Note that we provide prototypes for all "const" functions, to attach + the const attribute. This is necessary in 2.7.2 - adding the + attribute to the function *definition* is a syntax error. + This did not work with e.g. 2.1; back then, the return type had to + be "const". */ + +#include "config.h" + +#if defined (__CRIS_arch_version) && __CRIS_arch_version >= 3 +#define LZ(v) __extension__ \ + ({ int tmp_; __asm__ ("lz %1,%0" : "=r" (tmp_) : "r" (v)); tmp_; }) +#endif + + +#if defined (L_udivsi3) || defined (L_divsi3) || defined (L_umodsi3) \ + || defined (L_modsi3) +/* Result type of divmod worker function. */ +struct quot_rem + { + long quot; + long rem; + }; + +/* This is the worker function for div and mod. It is inlined into the + respective library function. */ +static __inline__ struct quot_rem +do_31div (unsigned long a, unsigned long b) __attribute__ ((__const__)); + +static __inline__ struct quot_rem +do_31div (unsigned long a, unsigned long b) +{ + /* Adjust operands and result if a is 31 bits. */ + long extra = 0; + int quot_digits = 0; + + if (b == 0) + { + struct quot_rem ret; + ret.quot = 0xffffffff; + ret.rem = 0xffffffff; + return ret; + } + + if (a < b) + return (struct quot_rem) { 0, a }; + +#ifdef LZ + if (b <= a) + { + quot_digits = LZ (b) - LZ (a); + quot_digits += (a >= (b << quot_digits)); + b <<= quot_digits; + } +#else + while (b <= a) + { + b <<= 1; + quot_digits++; + } +#endif + + /* Is a 31 bits? Note that bit 31 is handled by the caller. */ + if (a & 0x40000000) + { + /* Then make b:s highest bit max 0x40000000, because it must have + been 0x80000000 to be 1 bit higher than a. */ + b >>= 1; + + /* Adjust a to be maximum 0x3fffffff, i.e. two upper bits zero. */ + if (a >= b) + { + a -= b; + extra = 1 << (quot_digits - 1); + } + else + { + a -= b >> 1; + + /* Remember that we adjusted a by subtracting b * 2 ** Something. */ + extra = 1 << quot_digits; + } + + /* The number of quotient digits will be one less, because + we just adjusted b. */ + quot_digits--; + } + + /* Now do the division part. */ + + /* Subtract b and add ones to the right when a >= b + i.e. "a - (b - 1) == (a - b) + 1". */ + b--; + +#define DS __asm__ ("dstep %2,%0" : "=r" (a) : "0" (a), "r" (b)) + + switch (quot_digits) + { + case 32: DS; case 31: DS; case 30: DS; case 29: DS; + case 28: DS; case 27: DS; case 26: DS; case 25: DS; + case 24: DS; case 23: DS; case 22: DS; case 21: DS; + case 20: DS; case 19: DS; case 18: DS; case 17: DS; + case 16: DS; case 15: DS; case 14: DS; case 13: DS; + case 12: DS; case 11: DS; case 10: DS; case 9: DS; + case 8: DS; case 7: DS; case 6: DS; case 5: DS; + case 4: DS; case 3: DS; case 2: DS; case 1: DS; + case 0:; + } + + { + struct quot_rem ret; + ret.quot = (a & ((1 << quot_digits) - 1)) + extra; + ret.rem = a >> quot_digits; + return ret; + } +} + +/* Note that unsigned and signed division both build when L_divsi3, but + the unsigned variant is then inlined, as with do_31div above. */ +#if defined (L_udivsi3) || defined (L_divsi3) +#ifndef L_udivsi3 +static __inline__ +#endif +unsigned long +__Udiv (unsigned long a, unsigned long b) __attribute__ ((__const__)); + +#ifndef L_udivsi3 +static __inline__ +#endif +unsigned long +__Udiv (unsigned long a, unsigned long b) +{ + long extra = 0; + + /* Adjust operands and result, if a and/or b is 32 bits. */ + /* Effectively: b & 0x80000000. */ + if ((long) b < 0) + return a >= b; + + /* Effectively: a & 0x80000000. */ + if ((long) a < 0) + { + int tmp = 0; + + if (b == 0) + return 0xffffffff; +#ifdef LZ + tmp = LZ (b); +#else + for (tmp = 31; (((long) b & (1 << tmp)) == 0); tmp--) + ; + + tmp = 31 - tmp; +#endif + + if ((b << tmp) > a) + { + extra = 1 << (tmp-1); + a -= b << (tmp - 1); + } + else + { + extra = 1 << tmp; + a -= b << tmp; + } + } + + return do_31div (a, b).quot+extra; +} + + +#ifdef L_divsi3 +long +__Div (long a, long b) __attribute__ ((__const__)); + +long +__Div (long a, long b) +{ + long sign; + long result; + + /* Do *not* call do_31div since abs (-2147483648) == 2147483648 + <=> abs (-0x80000000) == 0x80000000 + which is still 32 bits. */ + + sign = a ^ b; + result = __Udiv (abs (a), abs (b)); + + return (sign < 0) ? -result : result; +} +#endif /* L_divsi3 */ +#endif /* L_udivsi3 || L_divsi3 */ + + +/* Note that unsigned and signed modulus both build when L_modsi3, but + then the unsigned variant is inlined, as with do_31div above. */ +#if defined (L_umodsi3) || defined (L_modsi3) +#ifndef L_umodsi3 +static __inline__ +#endif +unsigned long +__Umod (unsigned long a, unsigned long b) __attribute__ ((__const__)); + +#ifndef L_umodsi3 +static __inline__ +#endif +unsigned long +__Umod (unsigned long a, unsigned long b) +{ + /* Adjust operands and result if a and/or b is 32 bits. */ + if ((long) b < 0) + return a >= b ? a - b : a; + + if ((long) a < 0) + { + int tmp = 0; + + if (b == 0) + return a; +#ifdef LZ + tmp = LZ (b); +#else + for (tmp = 31; (((long) b & (1 << tmp)) == 0); tmp--) + ; + tmp = 31 - tmp; +#endif + + if ((b << tmp) > a) + { + a -= b << (tmp - 1); + } + else + { + a -= b << tmp; + } + } + + return do_31div (a, b).rem; +} + +#ifdef L_modsi3 +long +__Mod (long a, long b) __attribute__ ((__const__)); + +long +__Mod (long a, long b) +{ + long result; + + result = __Umod (abs (a), abs (b)); + + return (a < 0) ? -result : result; +} +#endif /* L_modsi3 */ +#endif /* L_umodsi3 || L_modsi3 */ +#endif /* L_udivsi3 || L_divsi3 || L_umodsi3 || L_modsi3 */ + +/* + * Local variables: + * eval: (c-set-style "gnu") + * indent-tabs-mode: t + * End: + */ diff --git a/gcc/config/cris/cris-protos.h b/gcc/config/cris/cris-protos.h new file mode 100644 index 00000000000..e13c94fb36d --- /dev/null +++ b/gcc/config/cris/cris-protos.h @@ -0,0 +1,75 @@ +/* Definitions for GCC. Part of the machine description for CRIS. + Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + Contributed by Axis Communications. + +This file is part of GCC. + +GCC 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. + +GCC 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 GCC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Prototypes for the CRIS port. */ + +#if defined(FILE) || defined(stdin) || defined(stdout) || defined(getc) || defined(putc) +#define STDIO_INCLUDED +#endif + +extern void cris_conditional_register_usage PARAMS ((void)); +extern int cris_simple_epilogue PARAMS ((void)); +#ifdef RTX_CODE +extern const char *cris_op_str PARAMS ((rtx)); +extern int cris_eligible_for_epilogue_delay PARAMS ((rtx)); +extern void cris_notice_update_cc PARAMS ((rtx, rtx)); +extern int cris_address_cost PARAMS ((rtx)); +extern void cris_print_operand PARAMS ((FILE *, rtx, int)); +extern void cris_print_operand_address PARAMS ((FILE *, rtx)); +extern int cris_side_effect_mode_ok PARAMS ((enum rtx_code, rtx *, int, int, + int, int, int)); +extern rtx cris_return_addr_rtx PARAMS ((int, rtx)); +extern rtx cris_split_movdx PARAMS ((rtx *)); +extern int cris_legitimate_pic_operand PARAMS ((rtx)); +extern int cris_gotless_symbol PARAMS ((rtx)); +extern int cris_got_symbol PARAMS ((rtx)); +extern int cris_symbol PARAMS ((rtx)); +extern void cris_output_addr_const PARAMS ((FILE *, rtx)); +extern int cris_cfun_uses_pic_table PARAMS ((void)); +extern void cris_target_asm_named_section + PARAMS ((const char *, unsigned int)); + +# ifdef TREE_CODE +extern rtx cris_expand_builtin_va_arg PARAMS ((tree, tree)); +extern void cris_encode_section_info PARAMS ((tree)); +# endif +#endif /* RTX_CODE */ + +#ifdef STDIO_INCLUDED +# ifdef TREE_CODE +extern void cris_asm_output_mi_thunk PARAMS ((FILE *, tree, int, tree)); +# endif +#endif + +#ifdef GCC_C_PRAGMA_H +extern void cris_pragma_expand_mul PARAMS ((cpp_reader *)); +#endif + +/* Need one that returns an int; usable in expressions. */ +extern int cris_fatal PARAMS ((char *)); + +extern void cris_override_options PARAMS ((void)); + +extern int cris_initial_elimination_offset PARAMS ((int, int)); + +extern void cris_init_expanders PARAMS ((void)); + +extern int cris_delay_slots_for_epilogue PARAMS ((void)); diff --git a/gcc/config/cris/cris.c b/gcc/config/cris/cris.c new file mode 100644 index 00000000000..b87a2ac4e83 --- /dev/null +++ b/gcc/config/cris/cris.c @@ -0,0 +1,3043 @@ +/* Definitions for GCC. Part of the machine description for CRIS. + Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + Contributed by Axis Communications. Written by Hans-Peter Nilsson. + +This file is part of GCC. + +GCC 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. + +GCC 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 GCC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#include "config.h" +#include "system.h" +#include "rtl.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "real.h" +#include "insn-config.h" +#include "conditions.h" +#include "output.h" +#include "insn-attr.h" +#include "flags.h" +#include "tree.h" +#include "expr.h" +#include "except.h" +#include "function.h" +#include "toplev.h" +#include "recog.h" +#include "tm_p.h" +#include "debug.h" +#include "target.h" +#include "target-def.h" + +/* Usable when we have an amount to add or subtract, and want the + optimal size of the insn. */ +#define ADDITIVE_SIZE_MODIFIER(size) \ + ((size) <= 63 ? "q" : (size) <= 255 ? "u.b" : (size) <= 65535 ? "u.w" : ".d") + +#define ASSERT_PLT_UNSPEC(x) \ + do \ + { \ + if (XEXP (x, 1) != NULL_RTX \ + || (GET_CODE (XVECEXP (x, 0, 0)) != SYMBOL_REF \ + && GET_CODE (XVECEXP (x, 0, 0)) != LABEL_REF)) \ + abort (); \ + } while (0) + +/* Per-function machine data. */ +struct machine_function + { + int needs_return_address_on_stack; + }; + +/* Fix for reg_overlap_mentioned_p. */ +static int cris_reg_overlap_mentioned_p PARAMS ((rtx, rtx)); + +/* This little fix suppresses the 'u' or 's' when '%e' in assembly + pattern. */ +static char cris_output_insn_is_bound = 0; + +/* This one suppresses printing out the "rPIC+" in + "rPIC+sym:GOTOFF+offset" when doing PIC. For a PLT symbol, it + suppresses outputting it as [rPIC+sym:GOTPLT] and outputs similarly + just the "sym:GOTOFF" part. */ +static int cris_pic_sympart_only = 0; + +static void +cris_print_base PARAMS ((rtx, FILE *)); + +static void +cris_print_index PARAMS ((rtx, FILE *)); + +static void +cris_init_machine_status PARAMS ((struct function *)); + +static int +cris_initial_frame_pointer_offset PARAMS ((void)); + +static int +saved_regs_mentioned PARAMS ((rtx)); + +static void cris_target_asm_function_prologue + PARAMS ((FILE *, HOST_WIDE_INT)); +static void cris_target_asm_function_epilogue + PARAMS ((FILE *, HOST_WIDE_INT)); + +/* The function cris_target_asm_function_epilogue puts the last insn to + output here. Used in delay_slots_for_epilogue and function_epilogue. */ +static char save_last[80]; + +/* This is the argument from the "-max-stack-stackframe=" option. */ +const char *cris_max_stackframe_str; + +/* This is the argument from the "-march=" option. */ +const char *cris_cpu_str; + +/* This is the argument from the "-mtune=" option. */ +const char *cris_tune_str; + +/* This is the argument from the "-melinux-stacksize=" option. */ +const char *cris_elinux_stacksize_str; + +/* This is the parsed result of the "-max-stack-stackframe=" option. If + it (still) is zero, then there was no such option given. */ +int cris_max_stackframe = 0; + +/* This is the parsed result of the "-march=" option, if given. */ +int cris_cpu_version = CRIS_DEFAULT_CPU_VERSION; + +#undef TARGET_ASM_FUNCTION_PROLOGUE +#define TARGET_ASM_FUNCTION_PROLOGUE cris_target_asm_function_prologue + +#undef TARGET_ASM_FUNCTION_EPILOGUE +#define TARGET_ASM_FUNCTION_EPILOGUE cris_target_asm_function_epilogue + +struct gcc_target targetm = TARGET_INITIALIZER; + +/* Predicate functions. */ + +/* This checks a part of an address, the one that is not a plain register + for an addressing mode using BDAP. + Allowed operands is either: + a) a register + b) a CONST operand (but not a symbol when generating PIC) + c) a [r] or [r+] in SImode, or sign-extend from HI or QI. */ + +int +cris_bdap_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + register enum rtx_code code = GET_CODE (op); + + if (mode != SImode && (mode != VOIDmode || GET_MODE (op) != VOIDmode)) + return 0; + + /* Just return whether this is a simple register or constant. */ + if (register_operand (op, mode) + || (CONSTANT_P (op) && !(flag_pic && cris_symbol (op)))) + return 1; + + /* Is it a [r] or possibly a [r+]? */ + if (code == MEM) + { + rtx tem = XEXP (op, 0); + + if (mode == SImode + && (register_operand (tem, SImode) + || (GET_CODE (tem) == POST_INC + && register_operand (XEXP (tem, 0), SImode)))) + return 1; + else + return 0; + } + + /* Perhaps a sign-extended mem: [r].(b|w) or [r+].(b|w)? */ + if (code == SIGN_EXTEND) + { + rtx tem = XEXP (op, 0); + + if (GET_CODE (tem) != MEM) + return 0; + + tem = XEXP (tem, 0); + if (mode == SImode + && (register_operand (tem, SImode) + || (GET_CODE (tem) == POST_INC + && register_operand (XEXP (tem, 0), SImode)))) + return 1; + else + return 0; + } + + return 0; +} + +/* This is similar to cris_bdap_operand: + It checks a part of an address, the one that is not a plain register + for an addressing mode using BDAP *or* BIAP. + Allowed operands is either: + a) a register + b) a CONST operand (but not a symbol when generating PIC) + c) a mult of (1, 2 or 4) and a register + d) a [r] or [r+] in SImode, or sign-extend from HI or QI. */ + +int +cris_bdap_biap_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + register enum rtx_code code = GET_CODE (op); + rtx reg; + rtx val; + + /* Check for bdap operand. */ + if (cris_bdap_operand (op, mode)) + return 1; + + if (mode != SImode && (mode != VOIDmode || GET_MODE (op) != VOIDmode)) + return 0; + + /* Check that we're looking at a BIAP operand. */ + if (code != MULT) + return 0; + + /* Canonicalize register and multiplicand. */ + if (GET_CODE (XEXP (op, 0)) == CONST_INT) + { + val = XEXP (op, 0); + reg = XEXP (op, 1); + } + else + { + val = XEXP (op, 1); + reg = XEXP (op, 0); + } + + /* Check that the operands are correct after canonicalization. */ + if (! register_operand (reg, SImode) || GET_CODE (val) != CONST_INT) + return 0; + + /* Check that the multiplicand has a valid value. */ + if ((code == MULT + && (INTVAL (val) == 1 || INTVAL (val) == 2 || INTVAL (val) == 4))) + return 1; + + return 0; +} + +/* Check if MODE is same as mode for X, and X is PLUS, MINUS, IOR or + AND or UMIN. */ + +int +cris_orthogonal_operator (x, mode) + rtx x; + enum machine_mode mode; +{ + enum rtx_code code = GET_CODE (x); + + if (mode == VOIDmode) + mode = GET_MODE (x); + + return (GET_MODE (x) == mode + && (code == PLUS || code == MINUS + || code == IOR || code == AND || code == UMIN)); +} + +/* Check if MODE is same as mode for X, and X is PLUS, IOR or AND or + UMIN. */ + +int +cris_commutative_orth_op (x, mode) + rtx x; + enum machine_mode mode; +{ + enum rtx_code code = GET_CODE (x); + + if (mode == VOIDmode) + mode = GET_MODE (x); + + return (GET_MODE (x) == mode && + (code == PLUS + || code == IOR || code == AND || code == UMIN)); +} + +/* Check if MODE is same as mode for X, and X is PLUS or MINUS or UMIN. */ + +int +cris_operand_extend_operator (x, mode) + rtx x; + enum machine_mode mode; +{ + enum rtx_code code = GET_CODE (x); + + if (mode == VOIDmode) + mode = GET_MODE (x); + + return (GET_MODE (x) == mode + && (code == PLUS || code == MINUS || code == UMIN)); +} + +/* Check to see if MODE is same as mode for X, and X is SIGN_EXTEND or + ZERO_EXTEND. */ + +int +cris_extend_operator (x, mode) + rtx x; + enum machine_mode mode; +{ + enum rtx_code code = GET_CODE (x); + + if (mode == VOIDmode) + mode = GET_MODE (x); + + return + (GET_MODE (x) == mode && (code == SIGN_EXTEND || code == ZERO_EXTEND)); +} + +/* Check to see if MODE is same as mode for X, and X is PLUS or BOUND. */ + +int +cris_plus_or_bound_operator (x, mode) + rtx x; + enum machine_mode mode; +{ + enum rtx_code code = GET_CODE (x); + + if (mode == VOIDmode) + mode = GET_MODE (x); + + return + (GET_MODE (x) == mode && (code == UMIN || code == PLUS)); +} + +/* Since with -fPIC, not all symbols are valid PIC symbols or indeed + general_operands, we have to have a predicate that matches it for the + "movsi" expander. */ + +int +cris_general_operand_or_symbol (op, mode) + rtx op; + enum machine_mode mode; +{ + return general_operand (op, mode) + || (CONSTANT_P (op) && cris_symbol (op)); +} + +/* Since a PIC symbol without a GOT entry is not a general_operand, we + have to have a predicate that matches it. We use this in the expanded + "movsi" anonymous pattern for PIC symbols. */ + +int +cris_general_operand_or_gotless_symbol (op, mode) + rtx op; + enum machine_mode mode; +{ + return general_operand (op, mode) + || (CONSTANT_P (op) && cris_gotless_symbol (op)); +} + +/* Since a PLT symbol is not a general_operand, we have to have a + predicate that matches it when we need it. We use this in the expanded + "call" and "call_value" anonymous patterns. */ + +int +cris_general_operand_or_plt_symbol (op, mode) + rtx op; + enum machine_mode mode; +{ + return general_operand (op, mode) + || (GET_CODE (op) == CONST + && GET_CODE (XEXP (op, 0)) == UNSPEC + && !TARGET_AVOID_GOTPLT); +} + +/* This matches a (MEM (general_operand)) or + (MEM (cris_general_operand_or_symbol)). The second one isn't a valid + memory_operand, so we need this predicate to recognize call + destinations before we change them to a PLT operand (by wrapping in + UNSPEC 0). */ + +int +cris_mem_call_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + rtx xmem; + + if (GET_CODE (op) != MEM) + return 0; + + if (memory_operand (op, mode)) + return 1; + + xmem = XEXP (op, 0); + + return cris_general_operand_or_symbol (xmem, GET_MODE (op)); +} + +/* The CONDITIONAL_REGISTER_USAGE worker. */ + +void +cris_conditional_register_usage () +{ + /* FIXME: This isn't nice. We should be able to use that register for + something else if the PIC table isn't needed. */ + if (flag_pic) + fixed_regs[PIC_OFFSET_TABLE_REGNUM] + = call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1; +} + +/* Return current_function_uses_pic_offset_table. For use in cris.md, + since some generated files do not include function.h. */ + +int +cris_cfun_uses_pic_table () +{ + return current_function_uses_pic_offset_table; +} + +/* Given an rtx, return the text string corresponding to the CODE of X. + Intended for use in the assembly language output section of a + define_insn. */ + +const char * +cris_op_str (x) + rtx x; +{ + cris_output_insn_is_bound = 0; + switch (GET_CODE (x)) + { + case PLUS: + return "add"; + break; + + case MINUS: + return "sub"; + break; + + case MULT: + return "mul"; + break; + + case DIV: + return "div"; + break; + + case AND: + return "and"; + break; + + case IOR: + return "or"; + break; + + case XOR: + return "xor"; + break; + + case NOT: + return "not"; + break; + + case ASHIFT: + return "lsl"; + break; + + case LSHIFTRT: + return "lsr"; + break; + + case ASHIFTRT: + return "asr"; + break; + + case UMIN: + /* Used to control the sign/zero-extend character for the 'e' modifier. + BOUND has none. */ + cris_output_insn_is_bound = 1; + return "bound"; + break; + + default: + return "Unknown operator"; + break; + } +} + +/* Print an index part of an address to file. */ + +static void +cris_print_index (index, file) + rtx index; + FILE * file; +{ + rtx inner = XEXP (index, 0); + + /* Make the index "additive" unless we'll output a negative number, in + which case the sign character is free (as in free beer). */ + if (GET_CODE (index) != CONST_INT || INTVAL (index) >= 0) + putc ('+', file); + + if (REG_P (index)) + fprintf (file, "$%s.b", reg_names[REGNO (index)]); + else if (CONSTANT_P (index)) + cris_output_addr_const (file, index); + else if (GET_CODE (index) == MULT) + { + fprintf (file, "$%s.", + reg_names[REGNO (XEXP (index, 0))]); + + putc (INTVAL (XEXP (index, 1)) == 2 ? 'w' : 'd', file); + } + else if (GET_CODE (index) == SIGN_EXTEND && + GET_CODE (inner) == MEM) + { + rtx inner_inner = XEXP (inner, 0); + + if (GET_CODE (inner_inner) == POST_INC) + { + fprintf (file, "[$%s+].", + reg_names[REGNO (XEXP (inner_inner, 0))]); + putc (GET_MODE (inner) == HImode ? 'w' : 'b', file); + } + else + { + fprintf (file, "[$%s].", reg_names[REGNO (inner_inner)]); + + putc (GET_MODE (inner) == HImode ? 'w' : 'b', file); + } + } + else if (GET_CODE (index) == MEM) + { + if (GET_CODE (inner) == POST_INC) + fprintf (file, "[$%s+].d", reg_names[REGNO (XEXP (inner, 0))]); + else + fprintf (file, "[$%s].d", reg_names[REGNO (inner)]); + } + else + fatal_insn ("Unexpected index-type in cris_print_index", index); +} + +/* Print a base rtx of an address to file. */ + +static void +cris_print_base (base, file) + rtx base; + FILE *file; +{ + if (REG_P (base)) + fprintf (file, "$%s", reg_names[REGNO (base)]); + else if (GET_CODE (base) == POST_INC) + fprintf (file, "$%s+", reg_names[REGNO (XEXP (base, 0))]); + else + fatal_insn ("Unexpected base-type in cris_print_base", base); +} + +/* Usable as a guard in expressions. */ + +int +cris_fatal (arg) + char *arg; +{ + internal_error (arg); + + /* We'll never get here; this is just to appease compilers. */ + return 0; +} + +/* Textual function prologue. */ + +static void +cris_target_asm_function_prologue (file, size) + FILE *file; + HOST_WIDE_INT size; +{ + int regno; + + /* Shorten the used name for readability. */ + int cfoa_size = current_function_outgoing_args_size; + int last_movem_reg = -1; + int doing_dwarf = dwarf2out_do_frame (); + int framesize; + int faked_args_size = 0; + int cfa_write_offset = 0; + char *cfa_label = NULL; + int return_address_on_stack + = regs_ever_live[CRIS_SRP_REGNUM] + || cfun->machine->needs_return_address_on_stack != 0; + + /* Don't do anything if no prologues or epilogues are wanted. */ + if (!TARGET_PROLOGUE_EPILOGUE) + return; + + if (size < 0) + abort (); + + /* Align the size to what's best for the CPU model. */ + if (TARGET_STACK_ALIGN) + size = TARGET_ALIGN_BY_32 ? (size + 3) & ~3 : (size + 1) & ~1; + + if (current_function_pretend_args_size) + { + int pretend = current_function_pretend_args_size; + for (regno = CRIS_FIRST_ARG_REG + CRIS_MAX_ARGS_IN_REGS - 1; + pretend > 0; + regno--, pretend -= 4) + { + fprintf (file, "\tpush $%s\n", reg_names[regno]); + faked_args_size += 4; + } + } + + framesize = faked_args_size; + + if (doing_dwarf) + { + /* FIXME: Slightly redundant calculation, as we do the same in + pieces below. This offset must be the total adjustment of the + stack-pointer. We can then def_cfa call at the end of this + function with the current implementation of execute_cfa_insn, but + that wouldn't really be clean. */ + + int cfa_offset + = faked_args_size + + (return_address_on_stack ? 4 : 0) + + (frame_pointer_needed ? 4 : 0); + + int cfa_reg; + + if (frame_pointer_needed) + cfa_reg = FRAME_POINTER_REGNUM; + else + { + cfa_reg = STACK_POINTER_REGNUM; + cfa_offset += cris_initial_frame_pointer_offset (); + } + + cfa_label = dwarf2out_cfi_label (); + dwarf2out_def_cfa (cfa_label, cfa_reg, cfa_offset); + + cfa_write_offset = - faked_args_size - 4; + } + + /* Save SRP if not a leaf function. */ + if (return_address_on_stack) + { + fprintf (file, "\tPush $srp\n"); + framesize += 4; + + if (doing_dwarf) + { + dwarf2out_return_save (cfa_label, cfa_write_offset); + cfa_write_offset -= 4; + } + } + + /* Set up frame pointer if needed. */ + if (frame_pointer_needed) + { + fprintf (file, "\tpush $%s\n\tmove.d $sp,$%s\n", + reg_names[FRAME_POINTER_REGNUM], + reg_names[FRAME_POINTER_REGNUM]); + framesize += 4; + + if (doing_dwarf) + { + dwarf2out_reg_save (cfa_label, FRAME_POINTER_REGNUM, + cfa_write_offset); + cfa_write_offset -= 4; + } + } + + /* Local vars are located above saved regs. */ + cfa_write_offset -= size; + + /* Get a contiguous sequence of registers, starting with r0, that need + to be saved. */ + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + { + if ((((regs_ever_live[regno] + && !call_used_regs[regno]) + || (regno == PIC_OFFSET_TABLE_REGNUM + && (current_function_uses_pic_offset_table + /* It is saved anyway, if there would be a gap. */ + || (flag_pic + && regs_ever_live[regno + 1] + && !call_used_regs[regno + 1])))) + && (regno != FRAME_POINTER_REGNUM || !frame_pointer_needed) + && regno != CRIS_SRP_REGNUM) + || (current_function_calls_eh_return + && (regno == EH_RETURN_DATA_REGNO (0) + || regno == EH_RETURN_DATA_REGNO (1) + || regno == EH_RETURN_DATA_REGNO (2) + || regno == EH_RETURN_DATA_REGNO (3)))) + { + /* Check if movem may be used for registers so far. */ + if (regno == last_movem_reg + 1) + /* Yes, update next expected register. */ + last_movem_reg++; + else + { + /* We cannot use movem for all registers. We have to flush + any movem:ed registers we got so far. */ + if (last_movem_reg != -1) + { + /* It is a win to use a side-effect assignment for + 64 <= size <= 128. But side-effect on movem was + not usable for CRIS v0..3. Also only do it if + side-effects insns are allowed. */ + if ((last_movem_reg + 1) * 4 + size >= 64 + && (last_movem_reg + 1) * 4 + size <= 128 + && cris_cpu_version >= CRIS_CPU_SVINTO + && TARGET_SIDE_EFFECT_PREFIXES) + fprintf (file, "\tmovem $%s,[$sp=$sp-%d]\n", + reg_names[last_movem_reg], + (last_movem_reg + 1) * 4 + size); + else + { + /* Avoid printing multiple subsequent sub:s for sp. */ + fprintf (file, "\tsub%s %d,$sp\n", + ADDITIVE_SIZE_MODIFIER ((last_movem_reg + 1) + * 4 + size), + (last_movem_reg + 1) * 4 + size); + + fprintf (file, "\tmovem $%s,[$sp]\n", + reg_names[last_movem_reg]); + } + + framesize += (last_movem_reg + 1) * 4 + size; + + if (TARGET_PDEBUG) + fprintf (file, "; frame %d, #regs %d, bytes %d args %d\n", + size, + last_movem_reg + 1, + (last_movem_reg + 1) * 4, + current_function_args_size); + + last_movem_reg = -1; + size = 0; + } + else if (size > 0) + { + /* Local vars on stack, but there are no movem:s. + Just allocate space. */ + fprintf (file, "\tSub%s %d,$sp\n", + ADDITIVE_SIZE_MODIFIER (size), + size); + framesize += size; + size = 0; + } + + fprintf (file, "\tPush $%s\n", reg_names[regno]); + framesize += 4; + } + + if (doing_dwarf) + { + /* Registers are stored lowest numbered at highest address, + which matches the loop order; we just need to update the + write-offset. */ + dwarf2out_reg_save (cfa_label, regno, cfa_write_offset); + cfa_write_offset -= 4; + } + } + } + + /* Check after, if we can movem all registers. This is the normal + case. */ + if (last_movem_reg != -1) + { + /* Side-effect assignment on movem was not supported for CRIS v0..3, + and don't do it if we're asked not to. + + The movem is already accounted for, for unwind. */ + + if ((last_movem_reg + 1) * 4 + size >= 64 + && (last_movem_reg + 1) * 4 + size <= 128 + && cris_cpu_version >= CRIS_CPU_SVINTO + && TARGET_SIDE_EFFECT_PREFIXES) + fprintf (file, "\tmovem $%s,[$sp=$sp-%d]\n", + reg_names[last_movem_reg], + (last_movem_reg+1) * 4 + size); + else + { + /* Avoid printing multiple subsequent sub:s for sp. FIXME: + Clean up the conditional expression. */ + fprintf (file, "\tsub%s %d,$sp\n", + ADDITIVE_SIZE_MODIFIER ((last_movem_reg + 1) * 4 + size), + (last_movem_reg + 1) * 4 + size); + /* To be compatible with v0..v3 means we do not use an assignment + addressing mode with movem. We normally don't need that + anyway. It would only be slightly more efficient for 64..128 + bytes frame size. */ + fprintf (file, "\tmovem $%s,[$sp]\n", reg_names[last_movem_reg]); + } + + framesize += (last_movem_reg + 1) * 4 + size; + + if (TARGET_PDEBUG) + fprintf (file, "; frame %d, #regs %d, bytes %d args %d\n", + size, + last_movem_reg + 1, + (last_movem_reg + 1) * 4, + current_function_args_size); + + /* We have to put outgoing argument space after regs. */ + if (cfoa_size) + { + /* This does not need to be accounted for, for unwind. */ + + fprintf (file, "\tSub%s %d,$sp\n", + ADDITIVE_SIZE_MODIFIER (cfoa_size), + cfoa_size); + framesize += cfoa_size; + } + } + else if ((size + cfoa_size) > 0) + { + /* This does not need to be accounted for, for unwind. */ + + /* Local vars on stack, and we could not use movem. Add a sub here. */ + fprintf (file, "\tSub%s %d,$sp\n", + ADDITIVE_SIZE_MODIFIER (size + cfoa_size), + cfoa_size + size); + framesize += size + cfoa_size; + } + + /* Set up the PIC register. */ + if (current_function_uses_pic_offset_table) + asm_fprintf (file, "\tmove.d $pc,$%s\n\tsub.d .:GOTOFF,$%s\n", + reg_names[PIC_OFFSET_TABLE_REGNUM], + reg_names[PIC_OFFSET_TABLE_REGNUM]); + + if (TARGET_PDEBUG) + fprintf (file, + "; parm #%d @ %d; frame %d, FP-SP is %d; leaf: %s%s; fp %s, outg: %d arg %d\n", + CRIS_MAX_ARGS_IN_REGS + 1, FIRST_PARM_OFFSET (0), + get_frame_size (), + cris_initial_frame_pointer_offset (), + leaf_function_p () ? "yes" : "no", + return_address_on_stack ? "no" :"yes", + frame_pointer_needed ? "yes" : "no", + cfoa_size, current_function_args_size); + + if (cris_max_stackframe && framesize > cris_max_stackframe) + warning ("Stackframe too big: %d bytes", framesize); +} + +/* Return nonzero if there are regs mentioned in the insn that are not all + in the call_used regs. This is part of the decision whether an insn + can be put in the epilogue. */ + +static int +saved_regs_mentioned (x) + rtx x; +{ + int i; + const char *fmt; + RTX_CODE code; + + /* Mainly stolen from refers_to_regno_p in rtlanal.c. */ + + code = GET_CODE (x); + + switch (code) + { + case REG: + i = REGNO (x); + return !call_used_regs[i]; + + case SUBREG: + /* If this is a SUBREG of a hard reg, we can see exactly which + registers are being modified. Otherwise, handle normally. */ + i = REGNO (SUBREG_REG (x)); + return !call_used_regs[i]; + + default: + ; + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + if (saved_regs_mentioned (XEXP (x, i))) + return 1; + } + else if (fmt[i] == 'E') + { + int j; + for (j = XVECLEN (x, i) - 1; j >=0; j--) + if (saved_regs_mentioned (XEXP (x, i))) + return 1; + } + } + + return 0; +} + +/* Figure out if the insn may be put in the epilogue. */ + +int +cris_eligible_for_epilogue_delay (insn) + rtx insn; +{ + /* First of all, it must be as slottable as for a delayed branch insn. */ + if (get_attr_slottable (insn) != SLOTTABLE_YES) + return 0; + + /* It must not refer to the stack pointer (may be valid for some cases + that I can't think of). */ + if (reg_mentioned_p (stack_pointer_rtx, PATTERN (insn))) + return 0; + + /* The frame pointer will be restored in the epilogue, before the + "ret", so it can't be referred to. */ + if (frame_pointer_needed + && reg_mentioned_p (frame_pointer_rtx, PATTERN (insn))) + return 0; + + /* All saved regs are restored before the delayed insn. + This means that we cannot have any instructions that mention the + registers that are restored by the epilogue. */ + if (saved_regs_mentioned (PATTERN (insn))) + return 0; + + /* It seems to be ok. */ + return 1; +} + +/* Return the number of delay-slots in the epilogue: return 1 if it + contains "ret", else 0. */ + +int +cris_delay_slots_for_epilogue () +{ + /* Check if we use a return insn, which we only do for leaf functions. + Else there is no slot to fill. */ + if (regs_ever_live[CRIS_SRP_REGNUM] + || cfun->machine->needs_return_address_on_stack != 0) + return 0; + + /* By calling function_epilogue with the same parameters as from gcc + we can get info about if the epilogue can fill the delay-slot by itself. + If it is filled from the epilogue, then the corresponding string + is in save_last. + This depends on that the "size" argument to function_epilogue + always is get_frame_size. + FIXME: Kludgy. At least make it a separate function that is not + misnamed or abuses the stream parameter. */ + cris_target_asm_function_epilogue (NULL, get_frame_size ()); + + if (*save_last) + return 1; + return 0; +} + +/* Textual function epilogue. When file is NULL, it serves doubly as + a test for whether the epilogue can fill any "ret" delay-slots by + itself by storing the delay insn in save_last. */ + +static void +cris_target_asm_function_epilogue (file, size) + FILE *file; + HOST_WIDE_INT size; +{ + int regno; + int last_movem_reg = -1; + rtx insn = get_last_insn (); + int argspace_offset = current_function_outgoing_args_size; + int pretend = current_function_pretend_args_size; + int return_address_on_stack + = regs_ever_live[CRIS_SRP_REGNUM] + || cfun->machine->needs_return_address_on_stack != 0; + + save_last[0] = 0; + + if (file && !TARGET_PROLOGUE_EPILOGUE) + return; + + if (TARGET_PDEBUG && file) + fprintf (file, ";;\n"); + + /* Align byte count of stack frame. */ + if (TARGET_STACK_ALIGN) + size = TARGET_ALIGN_BY_32 ? (size + 3) & ~3 : (size + 1) & ~1; + + /* If the last insn was a BARRIER, we don't have to write any code, + then all returns were covered by "return" insns. */ + if (GET_CODE (insn) == NOTE) + insn = prev_nonnote_insn (insn); + if (insn + && (GET_CODE (insn) == BARRIER + /* We must make sure that the insn really is a "return" and + not a conditional branch. Try to match the return exactly, + and if it doesn't match, assume it is a conditional branch + (and output an epilogue). */ + || (GET_CODE (insn) == JUMP_INSN + && GET_CODE (PATTERN (insn)) == RETURN))) + { + if (TARGET_PDEBUG && file) + fprintf (file, ";;;;;\n"); + return; + } + + /* Check how many saved regs we can movem. They start at r0 and must + be contiguous. */ + for (regno = 0; + regno < FIRST_PSEUDO_REGISTER; + regno++) + if ((((regs_ever_live[regno] + && !call_used_regs[regno]) + || (regno == PIC_OFFSET_TABLE_REGNUM + && (current_function_uses_pic_offset_table + /* It is saved anyway, if there would be a gap. */ + || (flag_pic + && regs_ever_live[regno + 1] + && !call_used_regs[regno + 1])))) + && (regno != FRAME_POINTER_REGNUM || !frame_pointer_needed) + && regno != CRIS_SRP_REGNUM) + || (current_function_calls_eh_return + && (regno == EH_RETURN_DATA_REGNO (0) + || regno == EH_RETURN_DATA_REGNO (1) + || regno == EH_RETURN_DATA_REGNO (2) + || regno == EH_RETURN_DATA_REGNO (3)))) + + { + if (regno == last_movem_reg + 1) + last_movem_reg++; + else + break; + } + + for (regno = FIRST_PSEUDO_REGISTER - 1; + regno > last_movem_reg; + regno--) + if ((((regs_ever_live[regno] + && !call_used_regs[regno]) + || (regno == PIC_OFFSET_TABLE_REGNUM + && (current_function_uses_pic_offset_table + /* It is saved anyway, if there would be a gap. */ + || (flag_pic + && regs_ever_live[regno + 1] + && !call_used_regs[regno + 1])))) + && (regno != FRAME_POINTER_REGNUM || !frame_pointer_needed) + && regno != CRIS_SRP_REGNUM) + || (current_function_calls_eh_return + && (regno == EH_RETURN_DATA_REGNO (0) + || regno == EH_RETURN_DATA_REGNO (1) + || regno == EH_RETURN_DATA_REGNO (2) + || regno == EH_RETURN_DATA_REGNO (3)))) + { + if (argspace_offset) + { + /* There is an area for outgoing parameters located before + the saved registers. We have to adjust for that. */ + if (file) + fprintf (file, "\tAdd%s %d,$sp\n", + ADDITIVE_SIZE_MODIFIER (argspace_offset), + argspace_offset); + + /* Make sure we only do this once. */ + argspace_offset = 0; + } + + /* Flush previous non-movem:ed registers. */ + if (*save_last && file) + fprintf (file, save_last); + sprintf (save_last, "\tPop $%s\n"); + } + + if (last_movem_reg != -1) + { + if (argspace_offset) + { + /* Adjust for the outgoing parameters area, if that's not + handled yet. */ + if (*save_last && file) + { + fprintf (file, save_last); + *save_last = 0; + } + + if (file) + fprintf (file, "\tAdd%s %d,$sp\n", + ADDITIVE_SIZE_MODIFIER (argspace_offset), + argspace_offset); + argspace_offset = 0; + } + /* Flush previous non-movem:ed registers. */ + else if (*save_last && file) + fprintf (file, save_last); + sprintf (save_last, "\tmovem [$sp+],$%s\n", reg_names[last_movem_reg]); + } + + /* Restore frame pointer if necessary. */ + if (frame_pointer_needed) + { + if (*save_last && file) + fprintf (file, save_last); + + if (file) + fprintf (file, "\tmove.d $%s,$sp\n", + reg_names[FRAME_POINTER_REGNUM]); + sprintf (save_last, "\tPop $%s\n", + reg_names[FRAME_POINTER_REGNUM]); + } + else + { + /* If there was no frame-pointer to restore sp from, we must + explicitly deallocate local variables. */ + + /* Handle space for outgoing parameters that hasn't been handled + yet. */ + size += argspace_offset; + + if (size) + { + if (*save_last && file) + fprintf (file, save_last); + + sprintf (save_last, "\tadd%s %d,$sp\n", + ADDITIVE_SIZE_MODIFIER (size), size); + } + + /* If the size was not in the range for a "quick", we must flush + it here. */ + if (size > 63) + { + if (file) + fprintf (file, save_last); + *save_last = 0; + } + } + + /* If this function has no pushed register parameters + (stdargs/varargs), and if it is not a leaf function, then we can + just jump-return here. */ + if (return_address_on_stack && pretend == 0) + { + if (*save_last && file) + fprintf (file, save_last); + *save_last = 0; + + if (file) + { + if (current_function_calls_eh_return) + { + /* The installed EH-return address is in *this* frame, so we + need to pop it before we return. */ + fprintf (file, "\tpop $srp\n", reg_names[CRIS_STACKADJ_REG]); + fprintf (file, "\tret\n"); + fprintf (file, "\tadd.d $%s,$sp\n", reg_names[CRIS_STACKADJ_REG]); + } + else + fprintf (file, "\tJump [$sp+]\n"); + + /* Do a sanity check to avoid generating invalid code. */ + if (current_function_epilogue_delay_list) + internal_error ("Allocated but unused delay list in epilogue"); + } + return; + } + + /* Rather than add current_function_calls_eh_return conditions + everywhere in the following code (and not be able to test it + thoroughly), assert the assumption that all usage of + __builtin_eh_return are handled above. */ + if (current_function_calls_eh_return) + internal_error ("Unexpected function type needing stack adjustment for\ + __builtin_eh_return"); + + /* If we pushed some register parameters, then adjust the stack for + them. */ + if (pretend) + { + /* Since srp is stored on the way, we need to restore it first. */ + if (return_address_on_stack) + { + if (*save_last && file) + fprintf (file, save_last); + *save_last = 0; + + if (file) + fprintf (file, "\tpop $srp\n"); + } + + if (*save_last && file) + fprintf (file, save_last); + + sprintf (save_last, "\tadd%s %d,$sp\n", + ADDITIVE_SIZE_MODIFIER (pretend), pretend); + } + + /* Here's where we have a delay-slot we need to fill. */ + if (file && current_function_epilogue_delay_list) + { + /* If gcc has allocated an insn for the epilogue delay slot, but + things were arranged so we now thought we could do it + ourselves, don't forget to flush that insn. */ + if (*save_last) + fprintf (file, save_last); + + fprintf (file, "\tRet\n"); + + /* Output the delay-slot-insn the mandated way. */ + final_scan_insn (XEXP (current_function_epilogue_delay_list, 0), + file, 1, -2, 1); + } + else if (file) + { + fprintf (file, "\tRet\n"); + + /* If the GCC did not do it, we have to use whatever insn we have, + or a nop. */ + if (*save_last) + fprintf (file, save_last); + else + fprintf (file, "\tnOp\n"); + } +} + +/* The PRINT_OPERAND worker. */ + +void +cris_print_operand (file, x, code) + FILE *file; + rtx x; + int code; +{ + rtx operand = x; + + /* Size-strings corresponding to MULT expressions. */ + static const char *mults[] = { "BAD:0", ".b", ".w", "BAD:3", ".d" }; + + /* New code entries should just be added to the switch below. If + handling is finished, just return. If handling was just a + modification of the operand, the modified operand should be put in + "operand", and then do a break to let default handling + (zero-modifier) output the operand. */ + + switch (code) + { + case 'b': + /* Print the unsigned supplied integer as if it was signed + and < 0, i.e print 255 or 65535 as -1, 254, 65534 as -2, etc. */ + if (GET_CODE (x) != CONST_INT + || ! CONST_OK_FOR_LETTER_P (INTVAL (x), 'O')) + fatal_insn ("Internal: Invalid operand with 'b'", x); + fprintf (file, "%d", INTVAL (x)| (INTVAL (x) <= 255 ? ~255 : ~65535)); + return; + + case 'x': + /* Print assembler code for operator. */ + fprintf (file, "%s", cris_op_str (operand)); + return; + + case 'v': + /* Print the operand without the PIC register. */ + if (! flag_pic || ! cris_gotless_symbol (x)) + fatal_insn ("Internal: Invalid operand with 'v'", x); + cris_pic_sympart_only++; + cris_output_addr_const (file, x); + cris_pic_sympart_only--; + return; + + case 'P': + /* Print the PIC register. Applied to a GOT-less PIC symbol for + sanity. */ + if (! flag_pic || ! cris_gotless_symbol (x)) + fatal_insn ("Internal: Invalid operand with 'P'", x); + fprintf (file, "$%s", reg_names [PIC_OFFSET_TABLE_REGNUM]); + return; + + case 'p': + /* Adjust a power of two to its log2. */ + if (GET_CODE (x) != CONST_INT || exact_log2 (INTVAL (x)) < 0 ) + fatal_insn ("Internal: Invalid operand with 'p'", x); + fprintf (file, "%d", exact_log2 (INTVAL (x))); + return; + + case 's': + /* For an integer, print 'b' or 'w' if <= 255 or <= 65535 + respectively. This modifier also terminates the inhibiting + effects of the 'x' modifier. */ + cris_output_insn_is_bound = 0; + if (GET_MODE (x) == VOIDmode && GET_CODE (x) == CONST_INT) + { + if (INTVAL (x) >= 0) + { + if (INTVAL (x) <= 255) + putc ('b', file); + else if (INTVAL (x) <= 65535) + putc ('w', file); + else + putc ('d', file); + } + else + putc ('d', file); + return; + } + + /* For a non-integer, print the size of the operand. */ + putc ((GET_MODE (x) == SImode || GET_MODE (x) == SFmode) + ? 'd' : GET_MODE (x) == HImode ? 'w' + : GET_MODE (x) == QImode ? 'b' + /* If none of the above, emit an erroneous size letter. */ + : 'X', + file); + return; + + case 'z': + /* Const_int: print b for -127 <= x <= 255, + w for -32768 <= x <= 65535, else abort. */ + if (GET_CODE (x) != CONST_INT + || INTVAL (x) < -32768 || INTVAL (x) > 65535) + fatal_insn ("Internal: Invalid operand with 'z'", x); + putc (INTVAL (x) >= -128 && INTVAL (x) <= 255 ? 'b' : 'w', file); + return; + + case '#': + /* Output a 'nop' if there's nothing for the delay slot. + This method stolen from the sparc files. */ + if (dbr_sequence_length () == 0) + fputs ("\n\tnop", file); + return; + + case 'H': + /* Print high (most significant) part of something. */ + switch (GET_CODE (operand)) + { + case CONST_INT: + /* Sign-extension from a normal int to a long long. */ + fprintf (file, INTVAL (operand) < 0 ? "-1" : "0"); + return; + + case CONST_DOUBLE: + /* High part of a long long constant. */ + if (GET_MODE (operand) == VOIDmode) + { + fprintf (file, "0x%x", CONST_DOUBLE_HIGH (x)); + return; + } + else + fatal_insn ("Internal: Invalid operand with 'H'", x); + + case REG: + /* Print reg + 1. Check that there's not an attempt to print + high-parts of registers like stack-pointer or higher. */ + if (REGNO (operand) > STACK_POINTER_REGNUM - 2) + internal_error ("Internal: Bad register: %d", REGNO (operand)); + fprintf (file, "$%s", reg_names[REGNO (operand) + 1]); + return; + + case MEM: + /* Adjust memory address to high part. */ + { + rtx adj_mem = operand; + int size + = GET_MODE_BITSIZE (GET_MODE (operand)) / BITS_PER_UNIT; + + /* Adjust so we can use two SImode in DImode. + Calling adj_offsettable_operand will make sure it is an + offsettable address. Don't do this for a postincrement + though; it should remain as it was. */ + if (GET_CODE (XEXP (adj_mem, 0)) != POST_INC) + adj_mem + = adjust_address (adj_mem, GET_MODE (adj_mem), size / 2); + + output_address (XEXP (adj_mem, 0)); + return; + } + + default: + fatal_insn ("Internal: Invalid operand for 'H'", x); + } + + case 'L': + /* Strip the MEM expression. */ + operand = XEXP (operand, 0); + break; + + case 'e': + /* Print 's' if operand is SIGN_EXTEND or 'u' if ZERO_EXTEND unless + cris_output_insn_is_bound is nonzero. */ + if (GET_CODE (operand) != SIGN_EXTEND + && GET_CODE (operand) != ZERO_EXTEND + && GET_CODE (operand) != CONST_INT) + fatal_insn ("Internal: Invalid operand with 'e'", x); + + if (cris_output_insn_is_bound) + { + cris_output_insn_is_bound = 0; + return; + } + + putc (GET_CODE (operand) == SIGN_EXTEND + || (GET_CODE (operand) == CONST_INT && INTVAL (operand) < 0) + ? 's' : 'u', file); + return; + + case 'm': + /* Print the size letter of the inner element. We can do it by + calling ourselves with the 's' modifier. */ + if (GET_CODE (operand) != SIGN_EXTEND && GET_CODE (operand) != ZERO_EXTEND) + fatal_insn ("Internal: Invalid operand with 'm'", x); + cris_print_operand (file, XEXP (operand, 0), 's'); + return; + + case 'M': + /* Print the least significant part of operand. */ + if (GET_CODE (operand) == CONST_DOUBLE) + { + fprintf (file, "0x%x", CONST_DOUBLE_LOW (x)); + return; + } + /* If not a CONST_DOUBLE, the least significant part equals the + normal part, so handle it normally. */ + break; + + case 'A': + /* When emitting an add for the high part of a DImode constant, we + want to use addq for 0 and adds.w for -1. */ + if (GET_CODE (operand) != CONST_INT) + fatal_insn ("Internal: Invalid operand with 'A' output modifier", x); + fprintf (file, INTVAL (operand) < 0 ? "adds.w" : "addq"); + return; + + case 'D': + /* When emitting an sub for the high part of a DImode constant, we + want to use subq for 0 and subs.w for -1. */ + if (GET_CODE (operand) != CONST_INT) + fatal_insn ("Internal: Invalid operand with 'D' output modifier", x); + fprintf (file, INTVAL (operand) < 0 ? "subs.w" : "subq"); + return; + + case 'S': + /* Print the operand as the index-part of an address. + Easiest way out is to use cris_print_index. */ + cris_print_index (operand, file); + return; + + case 'T': + /* Print the size letter for an operand to a MULT, which must be a + const_int with a suitable value. */ + if (GET_CODE (operand) != CONST_INT || INTVAL (operand) > 4) + fatal_insn ("Internal: Invalid operand with 'T'", x); + + fprintf (file, "%s", mults[INTVAL (operand)]); + return; + + case 0: + /* No code, print as usual. */ + break; + + default: + { +#define BADFORMAT "Internal: Invalid operand for '%c'" + char s[sizeof BADFORMAT]; + sprintf (s, BADFORMAT, code); + fatal_insn (s, x); + } + } + + /* Print an operand as without a modifier letter. */ + switch (GET_CODE (operand)) + { + case REG: + if (REGNO (operand) > 15) + internal_error ("Internal: Bad register: %d", REGNO (operand)); + fprintf (file, "$%s", reg_names[REGNO (operand)]); + return; + + case MEM: + output_address (XEXP (operand, 0)); + return; + + case CONST_DOUBLE: + if (GET_MODE (operand) == VOIDmode) + /* A long long constant. */ + output_addr_const (file, operand); + else + { + /* Only single precision is allowed as plain operands the + moment. FIXME: REAL_VALUE_FROM_CONST_DOUBLE isn't + documented. */ + REAL_VALUE_TYPE r; + long l; + + /* FIXME: Perhaps check overflow of the "single". */ + REAL_VALUE_FROM_CONST_DOUBLE (r, operand); + REAL_VALUE_TO_TARGET_SINGLE (r, l); + + fprintf (file, "0x%lx", l); + } + return; + + case UNSPEC: + ASSERT_PLT_UNSPEC (operand); + /* Fall through. */ + + case CONST: + cris_output_addr_const (file, operand); + return; + + case MULT: + case ASHIFT: + { + /* For a (MULT (reg X) const_int) we output "rX.S". */ + int i = GET_CODE (XEXP (operand, 1)) == CONST_INT + ? INTVAL (XEXP (operand, 1)) : INTVAL (XEXP (operand, 0)); + rtx reg = GET_CODE (XEXP (operand, 1)) == CONST_INT + ? XEXP (operand, 0) : XEXP (operand, 1); + + if (GET_CODE (reg) != REG + || (GET_CODE (XEXP (operand, 0)) != CONST_INT + && GET_CODE (XEXP (operand, 1)) != CONST_INT)) + fatal_insn ("Can't print operand", x); + + cris_print_base (reg, file); + fprintf (file, ".%c", + i == 0 || (i == 1 && GET_CODE (operand) == MULT) ? 'b' + : i == 4 ? 'd' + : (i == 2 && GET_CODE (operand) == MULT) || i == 1 ? 'w' + : 'd'); + return; + } + + default: + /* No need to handle all strange variants, let output_addr_const + do it for us. */ + if (CONSTANT_P (operand)) + { + cris_output_addr_const (file, operand); + return; + } + + fatal_insn ("Internal: Cannot decode operand", x); + } +} + +/* The PRINT_OPERAND_ADDRESS worker. */ + +void +cris_print_operand_address (file, x) + FILE *file; + rtx x; +{ + /* All these were inside MEM:s so output indirection characters. */ + putc ('[', file); + + if (CONSTANT_ADDRESS_P (x)) + cris_output_addr_const (file, x); + else if (BASE_OR_AUTOINCR_P (x)) + cris_print_base (x, file); + else if (GET_CODE (x) == PLUS) + { + rtx x1, x2; + + x1 = XEXP (x, 0); + x2 = XEXP (x, 1); + if (BASE_P (x1)) + { + cris_print_base (x1, file); + cris_print_index (x2, file); + } + else if (BASE_P (x2)) + { + cris_print_base (x2, file); + cris_print_index (x1, file); + } + else + fatal_insn ("Internal: This is not a recognized address", x); + } + else if (GET_CODE (x) == MEM) + { + /* A DIP. Output more indirection characters. */ + putc ('[', file); + cris_print_base (XEXP (x, 0), file); + putc (']', file); + } + else + fatal_insn ("Internal: This is not a recognized address", x); + + putc (']', file); +} + +/* The RETURN_ADDR_RTX worker. + We mark that the return address is used, either by EH or + __builtin_return_address, for use by the function prologue and + epilogue. FIXME: This isn't optimal; we just use the mark in the + prologue and epilogue to say that the return address is to be stored + in the stack frame. We could return SRP for leaf-functions and use the + initial-value machinery. */ + +rtx +cris_return_addr_rtx (count, frameaddr) + int count; + rtx frameaddr ATTRIBUTE_UNUSED; +{ + cfun->machine->needs_return_address_on_stack = 1; + + /* The return-address is stored just above the saved frame-pointer (if + present). Apparently we can't eliminate from the frame-pointer in + that direction, so use the incoming args (maybe pretended) pointer. */ + return count == 0 + ? gen_rtx_MEM (Pmode, plus_constant (virtual_incoming_args_rtx, -4)) + : NULL_RTX; +} + +/* This used to be the INITIAL_FRAME_POINTER_OFFSET worker; now only + handles FP -> SP elimination offset. */ + +static int +cris_initial_frame_pointer_offset () +{ + int regno; + + /* Initial offset is 0 if we dont have a frame pointer. */ + int offs = 0; + + /* And 4 for each register pushed. */ + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if ((((regs_ever_live[regno] + && !call_used_regs[regno]) + || (regno == PIC_OFFSET_TABLE_REGNUM + && (current_function_uses_pic_offset_table + /* It is saved anyway, if there would be a gap. */ + || (flag_pic + && regs_ever_live[regno + 1] + && !call_used_regs[regno + 1])))) + && (regno != FRAME_POINTER_REGNUM || !frame_pointer_needed) + && regno != CRIS_SRP_REGNUM) + || (current_function_calls_eh_return + && (regno == EH_RETURN_DATA_REGNO (0) + || regno == EH_RETURN_DATA_REGNO (1) + || regno == EH_RETURN_DATA_REGNO (2) + || regno == EH_RETURN_DATA_REGNO (3)))) + offs += 4; + + /* And then, last, we add the locals allocated. */ + offs += get_frame_size (); + + /* And more; the accumulated args size. */ + offs += current_function_outgoing_args_size; + + /* Then round it off, in case we use aligned stack. */ + if (TARGET_STACK_ALIGN) + offs = TARGET_ALIGN_BY_32 ? (offs + 3) & ~3 : (offs + 1) & ~1; + + return offs; +} + +/* The INITIAL_ELIMINATION_OFFSET worker. + Calculate the difference between imaginary registers such as frame + pointer and the stack pointer. Used to eliminate the frame pointer + and imaginary arg pointer. */ + +int +cris_initial_elimination_offset (fromreg, toreg) + int fromreg; + int toreg; +{ + int fp_sp_offset + = cris_initial_frame_pointer_offset (); + + /* We should be able to use regs_ever_live and related prologue + information here, or alpha should not as well. */ + int return_address_on_stack + = regs_ever_live[CRIS_SRP_REGNUM] + || cfun->machine->needs_return_address_on_stack != 0; + + /* Here we act as if the frame-pointer is needed. */ + int ap_fp_offset = 4 + (return_address_on_stack ? 4 : 0); + + if (fromreg == ARG_POINTER_REGNUM + && toreg == FRAME_POINTER_REGNUM) + return ap_fp_offset; + + /* Between the frame pointer and the stack are only "normal" stack + variables and saved registers. */ + if (fromreg == FRAME_POINTER_REGNUM + && toreg == STACK_POINTER_REGNUM) + return fp_sp_offset; + + /* We need to balance out the frame pointer here. */ + if (fromreg == ARG_POINTER_REGNUM + && toreg == STACK_POINTER_REGNUM) + return ap_fp_offset + fp_sp_offset - 4; + + abort (); +} + +/* This function looks into the pattern to see how this insn affects + condition codes. + + Used when to eliminate test insns before a condition-code user, + such as a "scc" insn or a conditional branch. This includes + checking if the entities that cc was updated by, are changed by the + operation. + + Currently a jumble of the old peek-inside-the-insn and the newer + check-cc-attribute methods. */ + +void +cris_notice_update_cc (exp, insn) + rtx exp; + rtx insn; +{ + /* Check if user specified "-mcc-init" as a bug-workaround. FIXME: + TARGET_CCINIT does not work; we must set CC_REVERSED as below. + Several test-cases will otherwise fail, for example + gcc.c-torture/execute/20000217-1.c -O0 and -O1. */ + if (TARGET_CCINIT) + { + CC_STATUS_INIT; + return; + } + + /* Slowly, we're converting to using attributes to control the setting + of condition-code status. */ + switch (get_attr_cc (insn)) + { + case CC_NONE: + /* Even if it is "none", a setting may clobber a previous + cc-value, so check. */ + if (GET_CODE (exp) == SET) + { + if (cc_status.value1 + && cris_reg_overlap_mentioned_p (SET_DEST (exp), + cc_status.value1)) + cc_status.value1 = 0; + + if (cc_status.value2 + && cris_reg_overlap_mentioned_p (SET_DEST (exp), + cc_status.value2)) + cc_status.value2 = 0; + } + return; + + case CC_CLOBBER: + CC_STATUS_INIT; + break; + + case CC_NORMAL: + /* Which means, for: + (set (cc0) (...)): + CC is (...). + + (set (reg) (...)): + CC is (reg) and (...) - unless (...) is 0, then CC does not change. + CC_NO_OVERFLOW unless (...) is reg or mem. + + (set (mem) (...)): + CC does not change. + + (set (pc) (...)): + CC does not change. + + (parallel + (set (reg1) (mem (bdap/biap))) + (set (reg2) (bdap/biap))): + CC is (reg1) and (mem (reg2)) + + (parallel + (set (mem (bdap/biap)) (reg1)) [or 0] + (set (reg2) (bdap/biap))): + CC does not change. + + (where reg and mem includes strict_low_parts variants thereof) + + For all others, assume CC is clobbered. + Note that we do not have to care about setting CC_NO_OVERFLOW, + since the overflow flag is set to 0 (i.e. right) for + instructions where it does not have any sane sense, but where + other flags have meanings. (This includes shifts; the carry is + not set by them). + + Note that there are other parallel constructs we could match, + but we don't do that yet. */ + + if (GET_CODE (exp) == SET) + { + /* FIXME: Check when this happens. It looks like we should + actually do a CC_STATUS_INIT here to be safe. */ + if (SET_DEST (exp) == pc_rtx) + return; + + /* Record CC0 changes, so we do not have to output multiple + test insns. */ + if (SET_DEST (exp) == cc0_rtx) + { + cc_status.value1 = SET_SRC (exp); + cc_status.value2 = 0; + + /* Handle flags for the special btstq on one bit. */ + if (GET_CODE (SET_SRC (exp)) == ZERO_EXTRACT + && XEXP (SET_SRC (exp), 1) == const1_rtx) + { + if (GET_CODE (XEXP (SET_SRC (exp), 0)) == CONST_INT) + /* Using cmpq. */ + cc_status.flags = CC_INVERTED; + else + /* A one-bit btstq. */ + cc_status.flags = CC_Z_IN_NOT_N; + } + else + cc_status.flags = 0; + + if (GET_CODE (SET_SRC (exp)) == COMPARE) + { + if (!REG_P (XEXP (SET_SRC (exp), 0)) + && XEXP (SET_SRC (exp), 1) != const0_rtx) + /* For some reason gcc will not canonicalize compare + operations, reversing the sign by itself if + operands are in wrong order. */ + /* (But NOT inverted; eq is still eq.) */ + cc_status.flags = CC_REVERSED; + + /* This seems to be overlooked by gcc. FIXME: Check again. + FIXME: Is it really safe? */ + cc_status.value2 + = gen_rtx_MINUS (GET_MODE (SET_SRC (exp)), + XEXP (SET_SRC (exp), 0), + XEXP (SET_SRC (exp), 1)); + } + return; + } + else if (REG_P (SET_DEST (exp)) + || (GET_CODE (SET_DEST (exp)) == STRICT_LOW_PART + && REG_P (XEXP (SET_DEST (exp), 0)))) + { + /* A register is set; normally CC is set to show that no + test insn is needed. Catch the exceptions. */ + + /* If not to cc0, then no "set"s in non-natural mode give + ok cc0... */ + if (GET_MODE_SIZE (GET_MODE (SET_DEST (exp))) > UNITS_PER_WORD + || GET_MODE_CLASS (GET_MODE (SET_DEST (exp))) == MODE_FLOAT) + { + /* ... except add:s and sub:s in DImode. */ + if (GET_MODE (SET_DEST (exp)) == DImode + && (GET_CODE (SET_SRC (exp)) == PLUS + || GET_CODE (SET_SRC (exp)) == MINUS)) + { + cc_status.flags = 0; + cc_status.value1 = SET_DEST (exp); + cc_status.value2 = SET_SRC (exp); + + if (cris_reg_overlap_mentioned_p (cc_status.value1, + cc_status.value2)) + cc_status.value2 = 0; + + /* Add and sub may set V, which gets us + unoptimizable results in "gt" and "le" condition + codes. */ + cc_status.flags |= CC_NO_OVERFLOW; + + return; + } + } + else if (SET_SRC (exp) == const0_rtx) + { + /* There's no CC0 change when clearing a register or + memory. Just check for overlap. */ + if ((cc_status.value1 + && cris_reg_overlap_mentioned_p (SET_DEST (exp), + cc_status.value1))) + cc_status.value1 = 0; + + if ((cc_status.value2 + && cris_reg_overlap_mentioned_p (SET_DEST (exp), + cc_status.value2))) + cc_status.value2 = 0; + + return; + } + else + { + cc_status.flags = 0; + cc_status.value1 = SET_DEST (exp); + cc_status.value2 = SET_SRC (exp); + + if (cris_reg_overlap_mentioned_p (cc_status.value1, + cc_status.value2)) + cc_status.value2 = 0; + + /* Some operations may set V, which gets us + unoptimizable results in "gt" and "le" condition + codes. */ + if (GET_CODE (SET_SRC (exp)) == PLUS + || GET_CODE (SET_SRC (exp)) == MINUS + || GET_CODE (SET_SRC (exp)) == NEG) + cc_status.flags |= CC_NO_OVERFLOW; + + return; + } + } + else if (GET_CODE (SET_DEST (exp)) == MEM + || (GET_CODE (SET_DEST (exp)) == STRICT_LOW_PART + && GET_CODE (XEXP (SET_DEST (exp), 0)) == MEM)) + { + /* When SET to MEM, then CC is not changed (except for + overlap). */ + if ((cc_status.value1 + && cris_reg_overlap_mentioned_p (SET_DEST (exp), + cc_status.value1))) + cc_status.value1 = 0; + + if ((cc_status.value2 + && cris_reg_overlap_mentioned_p (SET_DEST (exp), + cc_status.value2))) + cc_status.value2 = 0; + + return; + } + } + else if (GET_CODE (exp) == PARALLEL) + { + if (GET_CODE (XVECEXP (exp, 0, 0)) == SET + && GET_CODE (XVECEXP (exp, 0, 1)) == SET + && REG_P (XEXP (XVECEXP (exp, 0, 1), 0))) + { + if (REG_P (XEXP (XVECEXP (exp, 0, 0), 0)) + && GET_CODE (XEXP (XVECEXP (exp, 0, 0), 1)) == MEM) + { + /* For "move.S [rx=ry+o],rz", say CC reflects + value1=rz and value2=[rx] */ + cc_status.value1 = XEXP (XVECEXP (exp, 0, 0), 0); + cc_status.value2 + = gen_rtx_MEM (GET_MODE (XEXP (XVECEXP (exp, 0, 0), 0)), + XEXP (XVECEXP (exp, 0, 1), 0)); + cc_status.flags = 0; + + /* Huh? A side-effect cannot change the destination + register. */ + if (cris_reg_overlap_mentioned_p (cc_status.value1, + cc_status.value2)) + internal_error ("Internal: sideeffect-insn affecting main effect"); + return; + } + else if ((REG_P (XEXP (XVECEXP (exp, 0, 0), 1)) + || XEXP (XVECEXP (exp, 0, 0), 1) == const0_rtx) + && GET_CODE (XEXP (XVECEXP (exp, 0, 0), 0)) == MEM) + { + /* For "move.S rz,[rx=ry+o]" and "clear.S [rx=ry+o]", + say flags are not changed, except for overlap. */ + if ((cc_status.value1 + && cris_reg_overlap_mentioned_p (XEXP + (XVECEXP + (exp, 0, 0), 0), + cc_status.value1)) + || (cc_status.value2 + && cris_reg_overlap_mentioned_p (XEXP + (XVECEXP + (exp, 0, 1), 0), + cc_status.value2))) + CC_STATUS_INIT; + return; + } + } + } + break; + + default: + /* Unknown cc_attr value. */ + abort (); + } + + CC_STATUS_INIT; +} + +/* Return != 0 if the return sequence for the current function is short, + like "ret" or "jump [sp+]". Prior to reloading, we can't tell how + many registers must be saved, so return 0 then. */ + +int +cris_simple_epilogue () +{ + int regno; + int reglimit = STACK_POINTER_REGNUM; + int lastreg = -1; + + if (! reload_completed + || frame_pointer_needed + || get_frame_size () != 0 + || current_function_pretend_args_size + || current_function_args_size + || current_function_outgoing_args_size + || current_function_calls_eh_return + + /* If we're not supposed to emit prologue and epilogue, we must + not emit return-type instructions. */ + || !TARGET_PROLOGUE_EPILOGUE) + return 0; + + /* We allow a "movem [sp+],rN" to sit in front if the "jump [sp+]" or + in the delay-slot of the "ret". */ + for (regno = 0; regno < reglimit; regno++) + if ((regs_ever_live[regno] && ! call_used_regs[regno]) + || (regno == PIC_OFFSET_TABLE_REGNUM + && (current_function_uses_pic_offset_table + /* It is saved anyway, if there would be a gap. */ + || (flag_pic + && regs_ever_live[regno + 1] + && !call_used_regs[regno + 1])))) + { + if (lastreg != regno - 1) + return 0; + lastreg = regno; + } + + return 1; +} + +/* The ADDRESS_COST worker. */ + +int +cris_address_cost (x) + rtx x; +{ + /* The metric to use for the cost-macros is unclear. + The metric used here is (the number of cycles needed) / 2, + where we consider equal a cycle for a word of code and a cycle to + read memory. */ + + /* The cheapest addressing modes get 0, since nothing extra is needed. */ + if (BASE_OR_AUTOINCR_P (x)) + return 0; + + /* An indirect mem must be a DIP. This means two bytes extra for code, + and 4 bytes extra for memory read, i.e. (2 + 4) / 2. */ + if (GET_CODE (x) == MEM) + return (2 + 4) / 2; + + /* Assume (2 + 4) / 2 for a single constant; a dword, since it needs + an extra DIP prefix and 4 bytes of constant in most cases. + For PIC and a symbol with a GOT entry, we double the cost since we + add a [rPIC+...] offset. A GOT-less symbol uses a BDAP prefix + equivalent to the DIP prefix for non-PIC, hence the same cost. */ + if (CONSTANT_P (x)) + return flag_pic && cris_got_symbol (x) ? 2 * (2 + 4) / 2 : (2 + 4) / 2; + + /* Handle BIAP and BDAP prefixes. */ + if (GET_CODE (x) == PLUS) + { + rtx tem1 = XEXP (x, 0); + rtx tem2 = XEXP (x, 1); + + /* A BIAP is 2 extra bytes for the prefix insn, nothing more. We + recognize the typical MULT which is always in tem1 because of + insn canonicalization. */ + if ((GET_CODE (tem1) == MULT && BIAP_INDEX_P (tem1)) + || REG_P (tem1)) + return 2 / 2; + + /* A BDAP (quick) is 2 extra bytes. Any constant operand to the + PLUS is always found in tem2. */ + if (GET_CODE (tem2) == CONST_INT + && INTVAL (tem2) < 128 && INTVAL (tem2) >= -128) + return 2 / 2; + + /* A BDAP -32768 .. 32767 is like BDAP quick, but with 2 extra + bytes. */ + if (GET_CODE (tem2) == CONST_INT + && CONST_OK_FOR_LETTER_P (INTVAL (tem2), 'L')) + return (2 + 2) / 2; + + /* A BDAP with some other constant is 2 bytes extra. */ + if (CONSTANT_P (tem2)) + return (2 + 2 + 2) / 2; + + /* BDAP with something indirect should have a higher cost than + BIAP with register. FIXME: Should it cost like a MEM or more? */ + /* Don't need to check it, it's the only one left. + FIXME: There was a REG test missing, perhaps there are others. + Think more. */ + return (2 + 2 + 2) / 2; + } + + /* What else? Return a high cost. It matters only for valid + addressing modes. */ + return 10; +} + +/* Check various objections to the side-effect. Used in the test-part + of an anonymous insn describing an insn with a possible side-effect. + Returns nonzero if the implied side-effect is ok. + + code : PLUS or MULT + ops : An array of rtx:es. lreg, rreg, rval, + The variables multop and other_op are indexes into this, + or -1 if they are not applicable. + lreg : The register that gets assigned in the side-effect. + rreg : One register in the side-effect expression + rval : The other register, or an int. + multop : An integer to multiply rval with. + other_op : One of the entities of the main effect, + whose mode we must consider. */ + +int +cris_side_effect_mode_ok (code, ops, lreg, rreg, rval, multop, other_op) + enum rtx_code code; + rtx *ops; + int lreg, rreg, rval, multop, other_op; +{ + /* Find what value to multiply with, for rx =ry + rz * n. */ + int mult = multop < 0 ? 1 : INTVAL (ops[multop]); + + rtx reg_rtx = ops[rreg]; + rtx val_rtx = ops[rval]; + + /* The operands may be swapped. Canonicalize them in reg_rtx and + val_rtx, where reg_rtx always is a reg (for this constraint to + match). */ + if (! BASE_P (reg_rtx)) + reg_rtx = val_rtx, val_rtx = ops[rreg]; + + /* Don't forget to check that reg_rtx really is a reg. If it isn't, + we have no business. */ + if (! BASE_P (reg_rtx)) + return 0; + + /* Don't do this when -mno-split. */ + if (!TARGET_SIDE_EFFECT_PREFIXES) + return 0; + + /* The mult expression may be hidden in lreg. FIXME: Add more + commentary about that. */ + if (GET_CODE (val_rtx) == MULT) + { + mult = INTVAL (XEXP (val_rtx, 1)); + val_rtx = XEXP (val_rtx, 0); + code = MULT; + } + + /* First check the "other operand". */ + if (other_op >= 0) + { + if (GET_MODE_SIZE (GET_MODE (ops[other_op])) > UNITS_PER_WORD) + return 0; + + /* Check if the lvalue register is the same as the "other + operand". If so, the result is undefined and we shouldn't do + this. FIXME: Check again. */ + if ((BASE_P (ops[lreg]) + && BASE_P (ops[other_op]) + && REGNO (ops[lreg]) == REGNO (ops[other_op])) + || rtx_equal_p (ops[other_op], ops[lreg])) + return 0; + } + + /* Do not accept frame_pointer_rtx as any operand. */ + if (ops[lreg] == frame_pointer_rtx || ops[rreg] == frame_pointer_rtx + || ops[rval] == frame_pointer_rtx + || (other_op >= 0 && ops[other_op] == frame_pointer_rtx)) + return 0; + + if (code == PLUS + && ! BASE_P (val_rtx)) + { + + /* Do not allow rx = rx + n if a normal add or sub with same size + would do. */ + if (rtx_equal_p (ops[lreg], reg_rtx) + && GET_CODE (val_rtx) == CONST_INT + && (INTVAL (val_rtx) <= 63 && INTVAL (val_rtx) >= -63)) + return 0; + + /* Check allowed cases, like [r(+)?].[bwd] and const. + A symbol is not allowed with PIC. */ + if (CONSTANT_P (val_rtx)) + return flag_pic == 0 || cris_symbol (val_rtx) == 0; + + if (GET_CODE (val_rtx) == MEM + && BASE_OR_AUTOINCR_P (XEXP (val_rtx, 0))) + return 1; + + if (GET_CODE (val_rtx) == SIGN_EXTEND + && GET_CODE (XEXP (val_rtx, 0)) == MEM + && BASE_OR_AUTOINCR_P (XEXP (XEXP (val_rtx, 0), 0))) + return 1; + + /* If we got here, it's not a valid addressing mode. */ + return 0; + } + else if (code == MULT + || (code == PLUS && BASE_P (val_rtx))) + { + /* Do not allow rx = rx + ry.S, since it doesn't give better code. */ + if (rtx_equal_p (ops[lreg], reg_rtx) + || (mult == 1 && rtx_equal_p (ops[lreg], val_rtx))) + return 0; + + /* Do not allow bad multiply-values. */ + if (mult != 1 && mult != 2 && mult != 4) + return 0; + + /* Only allow r + ... */ + if (! BASE_P (reg_rtx)) + return 0; + + /* If we got here, all seems ok. + (All checks need to be done above). */ + return 1; + } + + /* If we get here, the caller got its initial tests wrong. */ + internal_error ("Internal: cris_side_effect_mode_ok with bad operands"); +} + +/* The function reg_overlap_mentioned_p in CVS (still as of 2001-05-16) + does not handle the case where the IN operand is strict_low_part; it + does handle it for X. Test-case in Axis-20010516. This function takes + care of that for THIS port. FIXME: strict_low_part is going away + anyway. */ + +static int +cris_reg_overlap_mentioned_p (x, in) + rtx x, in; +{ + /* The function reg_overlap_mentioned now handles when X is + strict_low_part, but not when IN is a STRICT_LOW_PART. */ + if (GET_CODE (in) == STRICT_LOW_PART) + in = XEXP (in, 0); + + return reg_overlap_mentioned_p (x, in); +} + +/* The TARGET_ASM_NAMED_SECTION worker. + We just dispatch to the functions for ELF and a.out. */ + +void +cris_target_asm_named_section (name, flags) + const char *name; + unsigned int flags; +{ + if (! TARGET_ELF) + default_no_named_section (name, flags); + else + default_elf_asm_named_section (name, flags); +} + +/* The LEGITIMATE_PIC_OPERAND_P worker. */ + +int +cris_legitimate_pic_operand (x) + rtx x; +{ + /* The PIC representation of a symbol with a GOT entry will be (for + example; relocations differ): + sym => [rPIC+sym:GOT] + and for a GOT-less symbol it will be (for example, relocation differ): + sym => rPIC+sym:GOTOFF + so only a symbol with a GOT is by itself a valid operand, and it + can't be a sum of a symbol and an offset. */ + return ! cris_symbol (x) || cris_got_symbol (x); +} + +/* Return non-zero if there's a SYMBOL_REF or LABEL_REF hiding inside this + CONSTANT_P. */ + +int +cris_symbol (x) + rtx x; +{ + switch (GET_CODE (x)) + { + case SYMBOL_REF: + case LABEL_REF: + return 1; + + case UNSPEC: + /* A PLT reference. */ + ASSERT_PLT_UNSPEC (x); + return 1; + + case CONST: + return cris_symbol (XEXP (x, 0)); + + case PLUS: + case MINUS: + return cris_symbol (XEXP (x, 0)) || cris_symbol (XEXP (x, 1)); + + case CONST_INT: + case CONST_DOUBLE: + case CONSTANT_P_RTX: + return 0; + + default: + fatal_insn ("Unrecognized supposed constant", x); + } + + return 1; +} + +/* Return non-zero if there's a SYMBOL_REF or LABEL_REF hiding inside this + CONSTANT_P, and the symbol does not need a GOT entry. Also set + current_function_uses_pic_offset_table if we're generating PIC and ever + see something that would need one. */ + +int +cris_gotless_symbol (x) + rtx x; +{ + switch (GET_CODE (x)) + { + case UNSPEC: + ASSERT_PLT_UNSPEC (x); + return 1; + + case SYMBOL_REF: + if (flag_pic && cfun != NULL) + current_function_uses_pic_offset_table = 1; + return SYMBOL_REF_FLAG (x); + + case LABEL_REF: + /* We don't set current_function_uses_pic_offset_table for + LABEL_REF:s in here, since they are almost always originating + from some branch. The only time it does not come from a label is + when GCC does something like __builtin_setjmp. Then we get the + LABEL_REF from the movsi expander, so we mark it there as a + special case. */ + return 1; + + case CONST: + return cris_gotless_symbol (XEXP (x, 0)); + + case PLUS: + case MINUS: + { + int x0 = cris_gotless_symbol (XEXP (x, 0)) != 0; + int x1 = cris_gotless_symbol (XEXP (x, 1)) != 0; + + /* One and only one of them must be a local symbol. Neither must + be some other, more general kind of symbol. */ + return + (x0 ^ x1) + && ! (x0 == 0 && cris_symbol (XEXP (x, 0))) + && ! (x1 == 0 && cris_symbol (XEXP (x, 1))); + } + + case CONST_INT: + case CONST_DOUBLE: + case CONSTANT_P_RTX: + return 0; + + default: + fatal_insn ("Unrecognized supposed constant", x); + } + + return 1; +} + +/* Return non-zero if there's a SYMBOL_REF or LABEL_REF hiding inside this + CONSTANT_P, and the symbol needs a GOT entry. */ + +int +cris_got_symbol (x) + rtx x; +{ + switch (GET_CODE (x)) + { + case UNSPEC: + ASSERT_PLT_UNSPEC (x); + return 0; + + case SYMBOL_REF: + if (flag_pic && cfun != NULL) + current_function_uses_pic_offset_table = 1; + return ! SYMBOL_REF_FLAG (x); + + case CONST: + return cris_got_symbol (XEXP (x, 0)); + + case LABEL_REF: + /* A LABEL_REF is never visible as a symbol outside the local + function. */ + case PLUS: + case MINUS: + /* Nope, can't access the GOT for "symbol + offset". */ + return 0; + + case CONST_INT: + case CONST_DOUBLE: + case CONSTANT_P_RTX: + return 0; + + default: + fatal_insn ("Unrecognized supposed constant in cris_global_pic_symbol", + x); + } + + return 1; +} + +/* The OVERRIDE_OPTIONS worker. + As is the norm, this also parses -mfoo=bar type parameters. */ + +void +cris_override_options () +{ + if (cris_max_stackframe_str) + { + cris_max_stackframe = atoi (cris_max_stackframe_str); + + /* Do some sanity checking. */ + if (cris_max_stackframe < 0 || cris_max_stackframe > 0x20000000) + internal_error ("-max-stackframe=%d is not usable, not between 0 and %d", + cris_max_stackframe, 0x20000000); + } + + /* Let "-metrax4" and "-metrax100" change the cpu version. */ + if (TARGET_SVINTO && cris_cpu_version < CRIS_CPU_SVINTO) + cris_cpu_version = CRIS_CPU_SVINTO; + else if (TARGET_ETRAX4_ADD && cris_cpu_version < CRIS_CPU_ETRAX4) + cris_cpu_version = CRIS_CPU_ETRAX4; + + /* Parse -march=... and its synonym, the deprecated -mcpu=... */ + if (cris_cpu_str) + { + cris_cpu_version + = (*cris_cpu_str == 'v' ? atoi (cris_cpu_str + 1) : -1); + + if (strcmp ("etrax4", cris_cpu_str) == 0) + cris_cpu_version = 3; + + if (strcmp ("svinto", cris_cpu_str) == 0 + || strcmp ("etrax100", cris_cpu_str) == 0) + cris_cpu_version = 8; + + if (strcmp ("ng", cris_cpu_str) == 0 + || strcmp ("etrax100lx", cris_cpu_str) == 0) + cris_cpu_version = 10; + + if (cris_cpu_version < 0 || cris_cpu_version > 10) + error ("Unknown CRIS version specification in -march= or -mcpu= : %s", + cris_cpu_str); + + /* Set the target flags. */ + if (cris_cpu_version >= CRIS_CPU_ETRAX4) + target_flags |= TARGET_MASK_ETRAX4_ADD; + + /* If this is Svinto or higher, align for 32 bit accesses. */ + if (cris_cpu_version >= CRIS_CPU_SVINTO) + target_flags + |= (TARGET_MASK_SVINTO | TARGET_MASK_ALIGN_BY_32 + | TARGET_MASK_STACK_ALIGN | TARGET_MASK_CONST_ALIGN + | TARGET_MASK_DATA_ALIGN); + + /* Note that we do not add new flags when it can be completely + described with a macro that uses -mcpu=X. So + TARGET_HAS_MUL_INSNS is (cris_cpu_version >= CRIS_CPU_NG). */ + } + + if (cris_tune_str) + { + int cris_tune + = (*cris_tune_str == 'v' ? atoi (cris_tune_str + 1) : -1); + + if (strcmp ("etrax4", cris_tune_str) == 0) + cris_tune = 3; + + if (strcmp ("svinto", cris_tune_str) == 0 + || strcmp ("etrax100", cris_tune_str) == 0) + cris_tune = 8; + + if (strcmp ("ng", cris_tune_str) == 0 + || strcmp ("etrax100lx", cris_tune_str) == 0) + cris_tune = 10; + + if (cris_tune < 0 || cris_tune > 10) + error ("Unknown CRIS cpu version specification in -mtune= : %s", + cris_tune_str); + + if (cris_tune >= CRIS_CPU_SVINTO) + /* We have currently nothing more to tune than alignment for + memory accesses. */ + target_flags + |= (TARGET_MASK_STACK_ALIGN | TARGET_MASK_CONST_ALIGN + | TARGET_MASK_DATA_ALIGN | TARGET_MASK_ALIGN_BY_32); + } + + if (flag_pic) + { + /* Use error rather than warning, so invalid use is easily + detectable. Still change to the values we expect, to avoid + further errors. */ + if (! TARGET_LINUX) + { + error ("-fPIC not supported in this configuration"); + flag_pic = 0; + } + + /* Turn off function CSE. We need to have the addresses reach the + call expanders to get PLT-marked, as they could otherwise be + compared against zero directly or indirectly. After visiting the + call expanders they will then be cse:ed, as the call expanders + force_reg the addresses, effectively forcing flag_no_function_cse + to 0. */ + flag_no_function_cse = 1; + } + + if ((write_symbols == DWARF_DEBUG + || write_symbols == DWARF2_DEBUG) && ! TARGET_ELF) + { + warning ("Specified -g option is invalid with -maout and -melinux"); + write_symbols = DBX_DEBUG; + } + + /* Set the per-function-data initializer. */ + init_machine_status = cris_init_machine_status; +} + +/* The ASM_OUTPUT_MI_THUNK worker. */ + +void +cris_asm_output_mi_thunk (stream, thunkdecl, delta, funcdecl) + FILE *stream; + tree thunkdecl ATTRIBUTE_UNUSED; + int delta; + tree funcdecl; +{ + if (delta > 0) + asm_fprintf (stream, "\tadd%s %d,$%s\n", + ADDITIVE_SIZE_MODIFIER (delta), delta, + reg_names[CRIS_FIRST_ARG_REG]); + else if (delta < 0) + asm_fprintf (stream, "\tsub%s %d,$%s\n", + ADDITIVE_SIZE_MODIFIER (-delta), -delta, + reg_names[CRIS_FIRST_ARG_REG]); + + if (flag_pic) + { + const char *name = XSTR (XEXP (DECL_RTL (funcdecl), 0), 0); + + STRIP_NAME_ENCODING (name, name); + fprintf (stream, "add.d "); + assemble_name (stream, name); + fprintf (stream, "%s,$pc\n", CRIS_PLT_PCOFFSET_SUFFIX); + } + else + { + fprintf (stream, "jump "); + assemble_name (stream, XSTR (XEXP (DECL_RTL (funcdecl), 0), 0)); + fprintf (stream, "\n"); + } +} + +/* The EXPAND_BUILTIN_VA_ARG worker. This is modified from the + "standard" implementation of va_arg: read the value from the current + address and increment by the size of one or two registers. The + important difference for CRIS is that if the type is + pass-by-reference, then perform an indirection. */ + +rtx +cris_expand_builtin_va_arg (valist, type) + tree valist; + tree type; +{ + tree addr_tree, t; + rtx addr; + enum machine_mode mode = TYPE_MODE (type); + int passed_size; + + /* Get AP. */ + addr_tree = valist; + + /* Check if the type is passed by value or by reference. */ + if (MUST_PASS_IN_STACK (mode, type) + || CRIS_FUNCTION_ARG_SIZE (mode, type) > 8) + { + tree type_ptr = build_pointer_type (type); + addr_tree = build1 (INDIRECT_REF, type_ptr, addr_tree); + passed_size = 4; + } + else + passed_size = (CRIS_FUNCTION_ARG_SIZE (mode, type) > 4) ? 8 : 4; + + addr = expand_expr (addr_tree, NULL_RTX, Pmode, EXPAND_NORMAL); + addr = copy_to_reg (addr); + + /* Compute new value for AP. */ + t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, + build (PLUS_EXPR, TREE_TYPE (valist), valist, + build_int_2 (passed_size, 0))); + TREE_SIDE_EFFECTS (t) = 1; + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); + + return addr; +} + +/* The INIT_EXPANDERS worker sets the per-function-data initializer and + mark functions. */ + +void +cris_init_expanders () +{ + /* Nothing here at the moment. */ +} + +/* Zero initialization is OK for all current fields. */ + +static void +cris_init_machine_status (p) + struct function *p; +{ + p->machine = xcalloc (1, sizeof (struct machine_function)); +} + +/* Split a 2 word move (DI or presumably DF) into component parts. + Originally a copy of gen_split_move_double in m32r.c. */ + +rtx +cris_split_movdx (operands) + rtx *operands; +{ + enum machine_mode mode = GET_MODE (operands[0]); + rtx dest = operands[0]; + rtx src = operands[1]; + rtx val; + + /* We might have (SUBREG (MEM)) here, so just get rid of the + subregs to make this code simpler. It is safe to call + alter_subreg any time after reload. */ + if (GET_CODE (dest) == SUBREG) + dest = alter_subreg (dest); + if (GET_CODE (src) == SUBREG) + src = alter_subreg (src); + + start_sequence (); + if (GET_CODE (dest) == REG) + { + int dregno = REGNO (dest); + + /* Reg-to-reg copy. */ + if (GET_CODE (src) == REG) + { + int sregno = REGNO (src); + + int reverse = (dregno == sregno + 1); + + /* We normally copy the low-numbered register first. However, if + the first register operand 0 is the same as the second register of + operand 1, we must copy in the opposite order. */ + emit_insn (gen_rtx_SET (VOIDmode, + operand_subword (dest, reverse, TRUE, mode), + operand_subword (src, reverse, TRUE, mode))); + + emit_insn (gen_rtx_SET (VOIDmode, + operand_subword (dest, !reverse, TRUE, mode), + operand_subword (src, !reverse, TRUE, mode))); + } + /* Constant-to-reg copy. */ + else if (GET_CODE (src) == CONST_INT || GET_CODE (src) == CONST_DOUBLE) + { + rtx words[2]; + split_double (src, &words[0], &words[1]); + emit_insn (gen_rtx_SET (VOIDmode, + operand_subword (dest, 0, TRUE, mode), + words[0])); + + emit_insn (gen_rtx_SET (VOIDmode, + operand_subword (dest, 1, TRUE, mode), + words[1])); + } + /* Mem-to-reg copy. */ + else if (GET_CODE (src) == MEM) + { + /* If the high-address word is used in the address, we must load it + last. Otherwise, load it first. */ + rtx addr = XEXP (src, 0); + int reverse + = (refers_to_regno_p (dregno, dregno + 1, addr, NULL) != 0); + + /* The original code imples that we can't do + move.x [rN+],rM move.x [rN],rM+1 + when rN is dead, because of REG_NOTES damage. That is + consistent with what I've seen, so don't try it. + + We have two different cases here; if the addr is POST_INC, + just pass it through, otherwise add constants. */ + + if (GET_CODE (addr) == POST_INC) + { + emit_insn (gen_rtx_SET (VOIDmode, + operand_subword (dest, 0, TRUE, mode), + change_address (src, SImode, addr))); + emit_insn (gen_rtx_SET (VOIDmode, + operand_subword (dest, 1, TRUE, mode), + change_address (src, SImode, addr))); + } + else + { + /* Make sure we don't get any other addresses with + embedded postincrements. They should be stopped in + GO_IF_LEGITIMATE_ADDRESS, but we're here for your + safety. */ + if (side_effects_p (addr)) + fatal_insn ("Unexpected side-effects in address", addr); + + emit_insn (gen_rtx_SET + (VOIDmode, + operand_subword (dest, reverse, TRUE, mode), + change_address + (src, SImode, + plus_constant (addr, + reverse * UNITS_PER_WORD)))); + emit_insn (gen_rtx_SET + (VOIDmode, + operand_subword (dest, ! reverse, TRUE, mode), + change_address + (src, SImode, + plus_constant (addr, + (! reverse) * + UNITS_PER_WORD)))); + } + } + else + abort (); + } + /* Reg-to-mem copy or clear mem. */ + else if (GET_CODE (dest) == MEM + && (GET_CODE (src) == REG + || src == const0_rtx + || src == CONST0_RTX (DFmode))) + { + rtx addr = XEXP (dest, 0); + + if (GET_CODE (addr) == POST_INC) + { + emit_insn (gen_rtx_SET (VOIDmode, + change_address (dest, SImode, addr), + operand_subword (src, 0, TRUE, mode))); + emit_insn (gen_rtx_SET (VOIDmode, + change_address (dest, SImode, addr), + operand_subword (src, 1, TRUE, mode))); + } + else + { + /* Make sure we don't get any other addresses with embedded + postincrements. They should be stopped in + GO_IF_LEGITIMATE_ADDRESS, but we're here for your safety. */ + if (side_effects_p (addr)) + fatal_insn ("Unexpected side-effects in address", addr); + + emit_insn (gen_rtx_SET + (VOIDmode, + change_address (dest, SImode, addr), + operand_subword (src, 0, TRUE, mode))); + + emit_insn (gen_rtx_SET + (VOIDmode, + change_address (dest, SImode, + plus_constant (addr, + UNITS_PER_WORD)), + operand_subword (src, 1, TRUE, mode))); + } + } + + else + abort (); + + val = gen_sequence (); + end_sequence (); + return val; +} + +/* This is in essence a copy of output_addr_const altered to output + symbolic operands as PIC. + + FIXME: Add hooks similar to ASM_OUTPUT_SYMBOL_REF to get this effect in + the "real" output_addr_const. All we need is one for LABEL_REF (and + one for CODE_LABEL?). */ + +void +cris_output_addr_const (file, x) + FILE *file; + rtx x; +{ + int is_plt = 0; + +restart: + switch (GET_CODE (x)) + { + case UNSPEC: + ASSERT_PLT_UNSPEC (x); + x = XVECEXP (x, 0, 0); + is_plt = 1; + + /* Fall through. */ + case SYMBOL_REF: + if (flag_pic) + { + const char *origstr = XSTR (x, 0); + const char *str; + + STRIP_NAME_ENCODING (str, origstr); + + if (is_plt) + { + if (cris_pic_sympart_only) + { + assemble_name (file, str); + fprintf (file, ":PLTG"); + } + else + { + if (TARGET_AVOID_GOTPLT) + /* We shouldn't get here. */ + abort (); + + fprintf (file, "[$%s+", reg_names [PIC_OFFSET_TABLE_REGNUM]); + assemble_name (file, XSTR (x, 0)); + + if (flag_pic == 1) + fprintf (file, ":GOTPLT16]"); + else + fprintf (file, ":GOTPLT]"); + } + } + else if (cris_gotless_symbol (x)) + { + if (! cris_pic_sympart_only) + fprintf (file, "$%s+", reg_names [PIC_OFFSET_TABLE_REGNUM]); + assemble_name (file, str); + fprintf (file, ":GOTOFF"); + } + else if (cris_got_symbol (x)) + { + if (cris_pic_sympart_only) + abort (); + fprintf (file, "[$%s+", reg_names [PIC_OFFSET_TABLE_REGNUM]); + assemble_name (file, XSTR (x, 0)); + + if (flag_pic == 1) + fprintf (file, ":GOT16]"); + else + fprintf (file, ":GOT]"); + } + else + fatal_insn ("Unexpected PIC symbol", x); + + /* Sanity check. */ + if (! current_function_uses_pic_offset_table) + internal_error ("Emitting PIC operand, but PIC register isn't set up"); + } + else + assemble_name (file, XSTR (x, 0)); + break; + + case LABEL_REF: + /* If we get one of those here, it should be dressed as PIC. Branch + labels are normally output with the 'l' specifier, which means it + will go directly to output_asm_label and not end up here. */ + if (GET_CODE (XEXP (x, 0)) != CODE_LABEL + && (GET_CODE (XEXP (x, 0)) != NOTE + || NOTE_LINE_NUMBER (XEXP (x, 0)) != NOTE_INSN_DELETED_LABEL)) + fatal_insn ("Unexpected address expression", x); + + if (flag_pic) + { + if (cris_gotless_symbol (x)) + { + if (! cris_pic_sympart_only) + fprintf (file, "$%s+", reg_names [PIC_OFFSET_TABLE_REGNUM]); + cris_output_addr_const (file, XEXP (x, 0)); + + fprintf (file, ":GOTOFF"); + } + else + /* Labels are never marked as global symbols. */ + fatal_insn ("Unexpected PIC symbol", x); + + /* Sanity check. */ + if (! current_function_uses_pic_offset_table) + internal_error ("Emitting PIC operand, but PIC register isn't set up"); + break; + } + + output_addr_const (file, x); + break; + + case NOTE: + if (NOTE_LINE_NUMBER (x) != NOTE_INSN_DELETED_LABEL) + fatal_insn ("Unexpected NOTE as addr_const:", x); + case CODE_LABEL: + case CONST_INT: + case CONST_DOUBLE: + case ZERO_EXTEND: + case SIGN_EXTEND: + output_addr_const (file, x); + break; + + case CONST: + /* This used to output parentheses around the expression, + but that does not work on the 386 (either ATT or BSD assembler). */ + cris_output_addr_const (file, XEXP (x, 0)); + break; + + case PLUS: + /* Some assemblers need integer constants to appear last (eg masm). */ + if (GET_CODE (XEXP (x, 0)) == CONST_INT) + { + cris_output_addr_const (file, XEXP (x, 1)); + if (INTVAL (XEXP (x, 0)) >= 0) + fprintf (file, "+"); + output_addr_const (file, XEXP (x, 0)); + } + else + { + cris_output_addr_const (file, XEXP (x, 0)); + if (GET_CODE (XEXP (x, 1)) != CONST_INT + || INTVAL (XEXP (x, 1)) >= 0) + fprintf (file, "+"); + cris_output_addr_const (file, XEXP (x, 1)); + } + break; + + case MINUS: + /* Avoid outputting things like x-x or x+5-x, + since some assemblers can't handle that. */ + x = simplify_subtraction (x); + if (GET_CODE (x) != MINUS) + goto restart; + + cris_output_addr_const (file, XEXP (x, 0)); + fprintf (file, "-"); + if ((GET_CODE (XEXP (x, 1)) == CONST_INT + && INTVAL (XEXP (x, 1)) < 0) + || GET_CODE (XEXP (x, 1)) != CONST_INT) + { + fprintf (file, "%s", targetm.asm_out.open_paren); + cris_output_addr_const (file, XEXP (x, 1)); + fprintf (file, "%s", targetm.asm_out.close_paren); + } + else + output_addr_const (file, XEXP (x, 1)); + break; + + default: + fatal_insn ("Unexpected address expression", x); + } +} + +/* The ENCODE_SECTION_INFO worker. Code-in whether we can get away + without a GOT entry (needed for externally visible objects but not for + functions) into SYMBOL_REF_FLAG and add the PLT suffix for global + functions. */ + +void +cris_encode_section_info (exp) + tree exp; +{ + if (flag_pic) + { + if (DECL_P (exp)) + { + if (TREE_CODE (exp) == FUNCTION_DECL + && (TREE_PUBLIC (exp) || DECL_WEAK (exp))) + SYMBOL_REF_FLAG (XEXP (DECL_RTL (exp), 0)) = 0; + else + SYMBOL_REF_FLAG (XEXP (DECL_RTL (exp), 0)) + = ! TREE_PUBLIC (exp) && ! DECL_WEAK (exp); + } + else + /* Others are local entities. */ + SYMBOL_REF_FLAG (XEXP (TREE_CST_RTL (exp), 0)) = 1; + } +} + +#if 0 +/* Various small functions to replace macros. Only called from a + debugger. They might collide with gcc functions or system functions, + so only emit them when '#if 1' above. */ + +enum rtx_code Get_code PARAMS ((rtx)); + +enum rtx_code +Get_code (x) + rtx x; +{ + return GET_CODE (x); +} + +const char *Get_mode PARAMS ((rtx)); + +const char * +Get_mode (x) + rtx x; +{ + return GET_MODE_NAME (GET_MODE (x)); +} + +rtx Xexp PARAMS ((rtx, int)); + +rtx +Xexp (x, n) + rtx x; + int n; +{ + return XEXP (x, n); +} + +rtx Xvecexp PARAMS ((rtx, int, int)); + +rtx +Xvecexp (x, n, m) + rtx x; + int n; +{ + return XVECEXP (x, n, m); +} + +int Get_rtx_len PARAMS ((rtx)); + +int +Get_rtx_len (x) + rtx x; +{ + return GET_RTX_LENGTH (GET_CODE (x)); +} + +/* Use upper-case to distinguish from local variables that are sometimes + called next_insn and prev_insn. */ + +rtx Next_insn PARAMS ((rtx)); + +rtx +Next_insn (insn) + rtx insn; +{ + return NEXT_INSN (insn); +} + +rtx Prev_insn PARAMS ((rtx)); + +rtx +Prev_insn (insn) + rtx insn; +{ + return PREV_INSN (insn); +} +#endif + +/* + * Local variables: + * eval: (c-set-style "gnu") + * indent-tabs-mode: t + * End: + */ diff --git a/gcc/config/cris/cris.h b/gcc/config/cris/cris.h new file mode 100644 index 00000000000..ccba9ae9427 --- /dev/null +++ b/gcc/config/cris/cris.h @@ -0,0 +1,1937 @@ +/* Definitions for GCC. Part of the machine description for CRIS. + Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + Contributed by Axis Communications. Written by Hans-Peter Nilsson. + +This file is part of GCC. + +GCC 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. + +GCC 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 GCC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* After the first "Node:" comment comes all preprocessor directives and + attached declarations described in the info files, the "Using and + Porting GCC" manual (uapgcc), in the same order as found in the "Target + macros" section in the gcc-2.9x CVS edition of 2000-03-17. FIXME: Not + really, but needs an update anyway. + + There is no generic copy-of-uapgcc comment, you'll have to see uapgcc + for that. If applicable, there is a CRIS-specific comment. The order + of macro definitions follow the order in the manual. Every section in + the manual (node in the info pages) has an introductory `Node: + ' comment. If no macros are defined for a section, only + the section-comment is present. */ + +/* Note that other header files (e.g. config/elfos.h, config/linux.h, + config/cris/linux.h and config/cris/aout.h) are responsible for lots of + settings not repeated below. This file contains general CRIS + definitions and definitions for the cris-*-elf subtarget. */ + +/* Replacement for REG_P since it does not match SUBREGs. Happens for + testcase Axis-20000320 with gcc-2.9x. */ +#define REG_S_P(x) \ + (REG_P (x) || (GET_CODE (x) == SUBREG && REG_P (XEXP (x, 0)))) + +/* Last register in main register bank r0..r15. */ +#define CRIS_LAST_GENERAL_REGISTER 15 + +/* Descriptions of registers used for arguments. */ +#define CRIS_FIRST_ARG_REG 10 +#define CRIS_MAX_ARGS_IN_REGS 4 + +/* Other convenience definitions. */ +#define CRIS_PC_REGNUM 15 +#define CRIS_SRP_REGNUM 16 + +/* Most of the time, we need the index into the register-names array. + When passing debug-info, we need the real register number. */ +#define CRIS_CANONICAL_SRP_REGNUM (16 + 11) +#define CRIS_CANONICAL_MOF_REGNUM (16 + 7) + +/* When generating PIC, these suffixes are added to the names of non-local + functions when being output. Contrary to other ports, we have offsets + relative to the GOT, not the PC. We might implement PC-relative PLT + semantics later for the general case; they are used in some cases right + now, such as MI thunks. */ +#define CRIS_GOTPLT_SUFFIX ":GOTPLT" +#define CRIS_PLT_GOTOFFSET_SUFFIX ":PLTG" +#define CRIS_PLT_PCOFFSET_SUFFIX ":PLT" + +#define CRIS_FUNCTION_ARG_SIZE(MODE, TYPE) \ + ((MODE) != BLKmode ? GET_MODE_SIZE (MODE) \ + : (unsigned) int_size_in_bytes (TYPE)) + +/* Check for max allowed stackframe. A "const char *" to be parsed. */ +extern const char *cris_max_stackframe_str; + +/* Which CPU version this is. A "const char *" to be parsed. */ +extern const char *cris_cpu_str; + +/* Which CPU version this is. The parsed and adjusted cris_cpu_str. */ +extern int cris_cpu_version; + +/* Which CPU version to tune for. A "const char *" to be parsed. */ +extern const char *cris_tune_str; + +/* The argument to "-melinux-stacksize=". We don't parse it currently; + it's just passed on to the linker. We might want to do something + here someday. */ +extern const char *cris_elinux_stacksize_str; + +/* Changing the order used to be necessary to put the fourth __make_dp + argument (a DImode parameter) in registers, to fit with the libfunc + parameter passing scheme used for intrinsic functions. FIXME: Check + performance and maybe remove definition from TARGET_LIBGCC2_CFLAGS now + that it isn't strictly necessary. We used to do this through + TARGET_LIBGCC2_CFLAGS, but that became increasingly difficult as the + parenthesis (that needed quoting) travels through several layers of + make and shell invocations. */ +#ifdef IN_LIBGCC2 +#define __make_dp(a,b,c,d) __cris_make_dp(d,a,b,c) +#endif + + +/* Node: Driver */ + +/* When using make with defaults.mak for Sun this will handily remove + any "-target sun*" switches. */ +/* We need to override any previous definitions (linux.h) */ +#undef WORD_SWITCH_TAKES_ARG +#define WORD_SWITCH_TAKES_ARG(STR) \ + (DEFAULT_WORD_SWITCH_TAKES_ARG (STR) \ + || !strcmp (STR, "target")) + +/* Also provide canonical vN definitions when user specifies an alias. + Note that -melf overrides -maout. */ + +/* The `-$' is here mostly due to the integrated preprocessor not + handling the builtin expansion of "#define __REGISTER_PREFIX__ $" + gracefully. This is slightly redundant although not incorrect. + We're quite alone defining REGISTER_PREFIX as "$" so it's unlikely + someone will fight for us. This year in the mountains. + Note that for -melinux and -mlinux, command-line -isystem options are + emitted both before and after the synthesized one. We can't remove all + of them: a %{= CRIS_CPU_NG) + +#define TARGET_OPTIONS \ + {{"cpu=", &cris_cpu_str, ""}, \ + {"arch=", &cris_cpu_str, \ + N_("Generate code for the specified chip or CPU version")}, \ + {"tune=", &cris_tune_str, \ + N_("Tune alignment for the specified chip or CPU version")}, \ + {"max-stackframe=", &cris_max_stackframe_str, \ + N_("Warn when a stackframe is larger than the specified size")}, \ + CRIS_SUBTARGET_LONG_OPTIONS \ + {"ax-stackframe=", &cris_max_stackframe_str, ""}} + +#define CRIS_SUBTARGET_LONG_OPTIONS + +/* Print subsidiary information on the compiler version in use. + Do not use VD.D syntax (D=digit), since this will cause confusion + with the base gcc version among users, when we ask which version of + gcc-cris they are using. Please use some flavor of "R" for + the version (no need for major.minor versions, I believe). */ +#define TARGET_VERSION \ + fprintf (stderr, " [Axis CRIS release R36a%s]", CRIS_SUBTARGET_VERSION) + +/* For the cris-*-elf subtarget. */ +#define CRIS_SUBTARGET_VERSION " - generic ELF" + +#define OVERRIDE_OPTIONS cris_override_options () + +/* The following gives optimal code for gcc-2.7.2, but *may* be subject + to change. Omitting flag_force_addr gives .1-.7% faster code for gcc + *only*, but 1.3% larger code. On ipps it gives 5.3-10.6% slower + code(!) and 0.3% larger code. For products, images gets .1-1.8% + larger. Do not set strict aliasing from optimization options. */ +#define OPTIMIZATION_OPTIONS(OPTIMIZE, SIZE) \ + do \ + { \ + if ((OPTIMIZE) >= 2 || (SIZE)) \ + { \ + flag_force_addr = \ + flag_omit_frame_pointer = 1; \ + } \ + flag_strict_aliasing = 0; \ + } \ + while (0) + + +/* Node: Storage Layout */ + +#define BITS_BIG_ENDIAN 0 + +#define BYTES_BIG_ENDIAN 0 + +/* WORDS_BIG_ENDIAN is not defined in the hardware, but for consistency, + we use little-endianness, and we may also be able to use + post-increment on DImode indirect. */ +#define WORDS_BIG_ENDIAN 0 + +#define BITS_PER_UNIT 8 + +#define BITS_PER_WORD 32 + +#define UNITS_PER_WORD 4 + +#define POINTER_SIZE 32 + +/* A combination of defining PROMOTE_MODE, PROMOTE_FUNCTION_ARGS, + PROMOTE_FOR_CALL_ONLY and *not* defining PROMOTE_PROTOTYPES gives the + best code size and speed for gcc, ipps and products in gcc-2.7.2. */ +#define CRIS_PROMOTED_MODE(MODE, UNSIGNEDP, TYPE) \ + (GET_MODE_CLASS (MODE) == MODE_INT && GET_MODE_SIZE (MODE) < 4) \ + ? SImode : MODE + +#define PROMOTE_MODE(MODE, UNSIGNEDP, TYPE) \ + (MODE) = CRIS_PROMOTED_MODE (MODE, UNSIGNEDP, TYPE) + +#define PROMOTE_FUNCTION_ARGS + +/* Defining PROMOTE_FUNCTION_RETURN in gcc-2.7.2 uncovers bug 981110 (even + if defining FUNCTION_VALUE with MODE as PROMOTED_MODE ;-) + + FIXME: Report this when cris.h is part of GCC, so others can easily + see the problem. Maybe check other systems that define + PROMOTE_FUNCTION_RETURN. */ +#define PROMOTE_FOR_CALL_ONLY + +/* We will be using prototype promotion, so they will be 32 bit. */ +#define PARM_BOUNDARY 32 + +/* Stack boundary is guided by -mstack-align, -mno-stack-align, + -malign. + Old comment: (2.1: still valid in 2.7.2?) + Note that to make this macro affect the alignment of stack + locals, a fix was required, and special precautions when handling + the stack pointer in various other macros (FUNCTION_PROLOGUE et al) + were required. See file "function.c". If you would just define + this macro, it would only affect the builtin alloca and variable + local data (non-ANSI, non-K&R, Gnu C extension). */ +#define STACK_BOUNDARY \ + (TARGET_STACK_ALIGN ? (TARGET_ALIGN_BY_32 ? 32 : 16) : 8) + +#define FUNCTION_BOUNDARY 16 + +/* Do not change BIGGEST_ALIGNMENT (when optimizing), as it will affect + strange places, at least in 2.1. */ +#define BIGGEST_ALIGNMENT 8 + +/* If -m16bit, -m16-bit, -malign or -mdata-align, + align everything to 16 bit. */ +#define DATA_ALIGNMENT(TYPE, BASIC_ALIGN) \ + (TARGET_DATA_ALIGN \ + ? (TARGET_ALIGN_BY_32 \ + ? (BASIC_ALIGN < 32 ? 32 : BASIC_ALIGN) \ + : (BASIC_ALIGN < 16 ? 16 : BASIC_ALIGN)) : BASIC_ALIGN) + +/* Note that CONSTANT_ALIGNMENT has the effect of making gcc believe that + ALL references to constant stuff (in code segment, like strings) has + this alignment. That is a rather rushed assumption. Luckily we do not + care about the "alignment" operand to builtin memcpy (only place where + it counts), so it doesn't affect any bad spots. */ +#define CONSTANT_ALIGNMENT(CONSTANT, BASIC_ALIGN) \ + (TARGET_CONST_ALIGN \ + ? (TARGET_ALIGN_BY_32 \ + ? (BASIC_ALIGN < 32 ? 32 : BASIC_ALIGN) \ + : (BASIC_ALIGN < 16 ? 16 : BASIC_ALIGN)) : BASIC_ALIGN) + +/* FIXME: Define LOCAL_ALIGNMENT for word and dword or arrays and + structures (if -mstack-align=), and check that it is good. */ + +#define EMPTY_FIELD_BOUNDARY 8 + +#define STRUCTURE_SIZE_BOUNDARY 8 + +#define STRICT_ALIGNMENT 0 + +/* Remove any previous definition (elfos.h). + ??? If it wasn't for all the other stuff that affects layout of + structures and bit-fields, this could presumably cause incompatibility + with other GNU/Linux ports (i.e. elfos.h users). */ +#undef PCC_BITFIELD_TYPE_MATTERS + +/* This is only used for non-scalars. Strange stuff happens to structs + (FIXME: What?) if we use anything larger than largest actually used + datum size, so lets make it 32. The type "long long" will still work + as usual. We can still have DImode insns, but they will only be used + for scalar data (i.e. long long). */ +#define MAX_FIXED_MODE_SIZE 32 + + +/* Node: Type Layout */ + +/* Note that DOUBLE_TYPE_SIZE is not defined anymore, since the default + value gives a 64-bit double, which is what we now use. */ + +/* For compatibility and historical reasons, a char should be signed. */ +#define DEFAULT_SIGNED_CHAR 1 + +/* No DEFAULT_SHORT_ENUMS, please. */ + +/* Note that WCHAR_TYPE_SIZE is used in cexp.y, + where TARGET_SHORT is not available. */ +#undef WCHAR_TYPE +#define WCHAR_TYPE "long int" + +#undef WCHAR_TYPE_SIZE +#define WCHAR_TYPE_SIZE 32 + + +/* Node: Register Basics */ + +/* We count all 16 non-special registers, SRP and a faked argument + pointer register. */ +#define FIRST_PSEUDO_REGISTER (16 + 1 + 1) + +/* For CRIS, these are r15 (pc) and r14 (sp). Register r8 is used as a + frame-pointer, but is not fixed. SRP is not included in general + registers and will not be used automatically. All other special + registers are fixed at the moment. The faked argument pointer register + is fixed too. */ +#define FIXED_REGISTERS \ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1} + +/* Register r9 is used for structure-address, r10-r13 for parameters, + r10- for return values. */ +#define CALL_USED_REGISTERS \ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1} + +#define CONDITIONAL_REGISTER_USAGE cris_conditional_register_usage () + + +/* Node: Allocation Order */ + +/* We need this on CRIS, because call-used regs should be used first, + (so we dont need to push). Else start using registers from r0 and up. + This preference is mainly because if we put call-used-regs from r0 + and up, then we can't use movem to push the rest, (which have to be + saved if we use them, and movem has to start with r0). + Change here if you change which registers to use as call registers. + + The actual need to explicitly prefer call-used registers improved the + situation a lot for 2.1, but might not actually be needed anymore. + Still, this order reflects what GCC should find out by itself, so it + probably does not hurt. + + Order of preference: Call-used-regs first, then r0 and up, last fp & + sp & pc as fillers. + Call-used regs in opposite order, so they will cause less conflict if + a function has few args (<= 3) and it wants a scratch reg. + Use struct-return address first, since very few functions use + structure return values so it is likely to be available. */ +#define REG_ALLOC_ORDER \ + {9, 13, 12, 11, 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 14, 15, 16, 17} + + +/* Node: Values in Registers */ + +/* The VOIDmode test is so we can omit mode on anonymous insns. FIXME: + Still needed in 2.9x, at least for Axis-20000319. */ +#define HARD_REGNO_NREGS(REGNO, MODE) \ + (MODE == VOIDmode \ + ? 1 : ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)) + +/* CRIS permits all registers to hold all modes. */ +#define HARD_REGNO_MODE_OK(REGNO, MODE) 1 + +#define MODES_TIEABLE_P(MODE1, MODE2) 1 + + +/* Node: Leaf Functions */ +/* (no definitions) */ + +/* Node: Stack Registers */ +/* (no definitions) */ + + +/* Node: Register Classes */ + +/* CRIS has only one kind of registers, so NO_REGS and ALL_REGS + are the only classes. FIXME: It actually makes sense to have another + class for special registers, and yet another class for the + multiply-overflow register in v10; then a class for the return + register also makes sense. */ +enum reg_class {NO_REGS, ALL_REGS, LIM_REG_CLASSES}; + +#define N_REG_CLASSES (int) LIM_REG_CLASSES + +#define REG_CLASS_NAMES {"NO_REGS", "ALL_REGS"} + +#define GENERAL_REGS ALL_REGS + +/* Count in the faked argument register in GENERAL_REGS. Keep out SRP. */ +#define REG_CLASS_CONTENTS {{0}, {0x2ffff}} + +#define REGNO_REG_CLASS(REGNO) GENERAL_REGS + +#define BASE_REG_CLASS GENERAL_REGS + +#define INDEX_REG_CLASS GENERAL_REGS + +/* Get reg_class from a letter such as appears in the machine + description. No letters are used, since 'r' is used for any + register. */ +#define REG_CLASS_FROM_LETTER(C) NO_REGS + +/* Since it uses reg_renumber, it is safe only once reg_renumber + has been allocated, which happens in local-alloc.c. */ +#define REGNO_OK_FOR_BASE_P(REGNO) \ + ((REGNO) <= CRIS_LAST_GENERAL_REGISTER \ + || (REGNO) == ARG_POINTER_REGNUM \ + || (unsigned) reg_renumber[REGNO] <= CRIS_LAST_GENERAL_REGISTER \ + || (unsigned) reg_renumber[REGNO] == ARG_POINTER_REGNUM) + +/* See REGNO_OK_FOR_BASE_P. */ +#define REGNO_OK_FOR_INDEX_P(REGNO) REGNO_OK_FOR_BASE_P(REGNO) + +/* It seems like gcc (2.7.2 and 2.9x of 2000-03-22) may send "NO_REGS" as + the class for a constant (testcase: __Mul in arit.c). To avoid forcing + out a constant into the constant pool, we will trap this case and + return something a bit more sane. FIXME: Check if this is a bug. */ +#define PREFERRED_RELOAD_CLASS(X, CLASS) \ + ((CLASS) == NO_REGS ? GENERAL_REGS : (CLASS)) + +/* For CRIS, this is always the size of MODE in words, + since all registers are the same size. To use omitted modes in + patterns with reload constraints, you must say the widest size + which is allowed for VOIDmode. + FIXME: Does that still apply for gcc-2.9x? Keep poisoned until such + patterns are added back. News: 2001-03-16: Happens as early as the + underscore-test. */ +#define CLASS_MAX_NREGS(CLASS, MODE) \ + ((MODE) == VOIDmode \ + ? 1 /* + cris_fatal ("CLASS_MAX_NREGS with VOIDmode") */ \ + : ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)) + +/* We are now out of letters; we could use ten more. This forces us to + use C-code in the 'md' file. FIXME: Use some EXTRA_CONSTRAINTS. */ +#define CONST_OK_FOR_LETTER_P(VALUE, C) \ + ( \ + /* MOVEQ, CMPQ, ANDQ, ORQ. */ \ + (C) == 'I' ? (VALUE) >= -32 && (VALUE) <= 31 : \ + /* ADDQ, SUBQ. */ \ + (C) == 'J' ? (VALUE) >= 0 && (VALUE) <= 63 : \ + /* ASRQ, BTSTQ, LSRQ, LSLQ. */ \ + (C) == 'K' ? (VALUE) >= 0 && (VALUE) <= 31 : \ + /* A 16-bit signed number. */ \ + (C) == 'L' ? (VALUE) >= -32768 && (VALUE) <= 32767 : \ + /* The constant 0 for CLEAR. */ \ + (C) == 'M' ? (VALUE) == 0 : \ + /* A negative ADDQ or SUBQ. */ \ + (C) == 'N' ? (VALUE) >= -63 && (VALUE) < 0 : \ + /* Quickened ints, QI and HI. */ \ + (C) == 'O' ? (VALUE) >= 0 && (VALUE) <= 65535 \ + && ((VALUE) >= (65535-31) \ + || ((VALUE) >= (255-31) \ + && (VALUE) <= 255 )) : \ + /* A 16-bit number signed *or* unsigned. */ \ + (C) == 'P' ? (VALUE) >= -32768 && (VALUE) <= 65535 : \ + 0) + +/* It is really simple to make up a 0.0; it is the same as int-0 in + IEEE754. */ +#define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C) \ + ((C) == 'G' && ((VALUE) == CONST0_RTX (DFmode) \ + || (VALUE) == CONST0_RTX (SFmode))) + +/* We need this on cris to distinguish delay-slottable addressing modes. */ +#define EXTRA_CONSTRAINT(X, C) \ + ( \ + /* Slottable address mode? */ \ + (C) == 'Q' ? EXTRA_CONSTRAINT_Q (X) : \ + /* Operand to BDAP or BIAP? */ \ + (C) == 'R' ? EXTRA_CONSTRAINT_R (X) : \ + /* A local PIC symbol? */ \ + (C) == 'S' ? EXTRA_CONSTRAINT_S (X) : \ + /* A three-address addressing-mode? */ \ + (C) == 'T' ? EXTRA_CONSTRAINT_T (X) : \ + /* A global PIC symbol? */ \ + (C) == 'U' ? EXTRA_CONSTRAINT_U (X) : \ + 0) + +#define EXTRA_CONSTRAINT_Q(X) \ + ( \ + /* Slottable addressing modes: \ + A register? FIXME: Unnecessary. */ \ + (BASE_P (X) && REGNO (X) != CRIS_PC_REGNUM) \ + /* Indirect register: [reg]? */ \ + || (GET_CODE (X) == MEM && BASE_P (XEXP (X, 0)) \ + && REGNO (XEXP (X, 0)) != CRIS_PC_REGNUM) \ + ) + +#define EXTRA_CONSTRAINT_R(X) \ + ( \ + /* An operand to BDAP or BIAP: \ + A BIAP; r.S? */ \ + BIAP_INDEX_P (X) \ + /* A [reg] or (int) [reg], maybe with post-increment. */ \ + || BDAP_INDEX_P (X) \ + || CONSTANT_INDEX_P (X) \ + ) + +/* FIXME: Bug below: We can't have XEXP (X, 0)) both be MEM and a + CONSTANT_P. Parens don't match indentation. */ + +#define EXTRA_CONSTRAINT_T(X) \ + ( \ + /* Three-address-operands. All are indirect-memory: */ \ + GET_CODE (X) == MEM \ + && ((GET_CODE (XEXP (X, 0)) == MEM \ + /* Double indirect: [[reg]] or [[reg+]]? */ \ + && (BASE_OR_AUTOINCR_P (XEXP (XEXP (X, 0), 0)))) \ + /* Just an explicite indirect reference: [const]? */ \ + || CONSTANT_P (XEXP (X, 0)) \ + /* Something that is indexed; [...+...]? */ \ + || (GET_CODE (XEXP (X, 0)) == PLUS \ + /* A BDAP constant: [reg+(8|16|32)bit offset]? */ \ + && ((BASE_P (XEXP (XEXP (X, 0), 0)) \ + && CONSTANT_INDEX_P (XEXP (XEXP (X, 0), 1))) \ + /* Swap arguments to the above. FIXME: gcc-2.9x? */ \ + || (BASE_P (XEXP (XEXP (X, 0), 1)) \ + && CONSTANT_INDEX_P (XEXP (XEXP (X, 0), 0))) \ + /* A BDAP register: [reg+[reg(+)].S]? */ \ + || (BASE_P (XEXP (XEXP (X, 0), 0)) \ + && BDAP_INDEX_P(XEXP(XEXP(X, 0), 1))) \ + /* Same, but with swapped arguments. */ \ + || (BASE_P (XEXP (XEXP (X, 0), 1)) \ + && BDAP_INDEX_P (XEXP (XEXP (X, 0), 0))) \ + /* A BIAP: [reg+reg.S]. */ \ + || (BASE_P (XEXP (XEXP (X, 0), 0)) \ + && BIAP_INDEX_P (XEXP (XEXP (X, 0), 1))) \ + /* Same, but with swapped arguments. */ \ + || (BASE_P (XEXP (XEXP (X, 0), 1)) \ + && BIAP_INDEX_P (XEXP (XEXP (X, 0), 0)))))) \ + ) + +#define EXTRA_CONSTRAINT_S(X) \ + (flag_pic && CONSTANT_P (X) && cris_gotless_symbol (X)) + +#define EXTRA_CONSTRAINT_U(X) \ + (flag_pic && CONSTANT_P (X) && cris_got_symbol (X)) + + +/* Node: Frame Layout */ + +#define STACK_GROWS_DOWNWARD +#define FRAME_GROWS_DOWNWARD + +/* It seems to be indicated in the code (at least 2.1) that this is + better a constant, and best 0. */ +#define STARTING_FRAME_OFFSET 0 + +#define FIRST_PARM_OFFSET(FNDECL) 0 + +#define RETURN_ADDR_RTX(COUNT, FRAMEADDR) \ + cris_return_addr_rtx (COUNT, FRAMEADDR) + +#define INCOMING_RETURN_ADDR_RTX gen_rtx (REG, Pmode, CRIS_SRP_REGNUM) + +/* FIXME: Any __builtin_eh_return callers must not return anything and + there must not be collisions with incoming parameters. Luckily the + number of __builtin_eh_return callers is limited. For now return + parameter registers in reverse order and hope for the best. */ +#define EH_RETURN_DATA_REGNO(N) \ + (((N) >= 0 && (N) < 4) ? (CRIS_FIRST_ARG_REG + 3 - (N)) : INVALID_REGNUM) + +/* Store the stack adjustment in the structure-return-address register. */ +#define CRIS_STACKADJ_REG STRUCT_VALUE_REGNUM +#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (SImode, CRIS_STACKADJ_REG) + +#define EH_RETURN_HANDLER_RTX \ + cris_return_addr_rtx (0, NULL) + +#define INIT_EXPANDERS cris_init_expanders () + +/* FIXME: Move this to right node (it's not documented properly yet). */ +#define DWARF_FRAME_RETURN_COLUMN DWARF_FRAME_REGNUM (CRIS_SRP_REGNUM) + +/* FIXME: Move this to right node (it's not documented properly yet). + FIXME: Check what alignment we can assume regarding + TARGET_STACK_ALIGN and TARGET_ALIGN_BY_32. */ +#define DWARF_CIE_DATA_ALIGNMENT -1 + +/* If we would ever need an exact mapping between canonical register + number and dwarf frame register, we would either need to include all + registers in the gcc decription (with some marked fixed of course), or + an inverse mapping from dwarf register to gcc register. There is one + need in dwarf2out.c:expand_builtin_init_dwarf_reg_sizes. Right now, I + don't see that we need exact correspondence between DWARF *frame* + registers and DBX_REGISTER_NUMBER, so map them onto GCC registers. */ +#define DWARF_FRAME_REGNUM(REG) (REG) + +/* Node: Stack Checking */ +/* (no definitions) FIXME: Check. */ + +/* Node: Frame Registers */ + +#define STACK_POINTER_REGNUM 14 + +/* Register used for frame pointer. This is also the last of the saved + registers, when a frame pointer is not used. */ +#define FRAME_POINTER_REGNUM 8 + +/* Faked register, is always eliminated. We need it to eliminate + allocating stack slots for the return address and the frame pointer. */ +#define ARG_POINTER_REGNUM 17 + +#define STATIC_CHAIN_REGNUM 7 + + +/* Node: Elimination */ + +/* Really only needed if the stack frame has variable length (alloca + or variable sized local arguments (GNU C extension). */ +#define FRAME_POINTER_REQUIRED 0 + +#define ELIMINABLE_REGS \ + {{ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ + {ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM}, \ + {FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}} + +/* We need not worry about when the frame-pointer is required for other + reasons. */ +#define CAN_ELIMINATE(FROM, TO) 1 + +#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ + (OFFSET) = cris_initial_elimination_offset (FROM, TO) + + +/* Node: Stack Arguments */ + +/* Since many parameters take up one register each in any case, + PROMOTE_PROTOTYPES would seem like a good idea, but measurements + indicate that a combination using PROMOTE_MODE is better. */ + +#define ACCUMULATE_OUTGOING_ARGS 1 + +#define RETURN_POPS_ARGS(FUNDECL, FUNTYPE, STACKSIZE) 0 + + +/* Node: Register Arguments */ + +/* The void_type_node is sent as a "closing" call. We have to stop it + since it's invalid to FUNCTION_ARG_PASS_BY_REFERENCE (or was invalid at + some time). */ +#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \ + ((CUM).regs < CRIS_MAX_ARGS_IN_REGS \ + && (TYPE) != void_type_node \ + && ! FUNCTION_ARG_PASS_BY_REFERENCE (CUM, MODE, TYPE, NAMED) \ + ? gen_rtx (REG, MODE, (CRIS_FIRST_ARG_REG) + (CUM).regs) \ + : NULL_RTX) + +/* The differences between this and the previous, is that this one checks + that an argument is named, since incoming stdarg/varargs arguments are + pushed onto the stack, and we don't have to check against the "closing" + void_type_node TYPE parameter. */ +#define FUNCTION_INCOMING_ARG(CUM, MODE, TYPE, NAMED) \ + (((NAMED) && (CUM).regs < CRIS_MAX_ARGS_IN_REGS \ + && ! FUNCTION_ARG_PASS_BY_REFERENCE (CUM, MODE, TYPE, NAMED)) \ + ? gen_rtx (REG, MODE, CRIS_FIRST_ARG_REG + (CUM).regs) \ + : NULL_RTX) + +#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) \ + (((CUM).regs == (CRIS_MAX_ARGS_IN_REGS - 1) \ + && !MUST_PASS_IN_STACK (MODE, TYPE) \ + && CRIS_FUNCTION_ARG_SIZE (MODE, TYPE) > 4 \ + && CRIS_FUNCTION_ARG_SIZE (MODE, TYPE) <= 8) \ + ? 1 : 0) + +/* Structs may be passed by value, but they must not be more than 8 + bytes long. */ +#define FUNCTION_ARG_PASS_BY_REFERENCE(CUM, MODE, TYPE, NAMED) \ + (MUST_PASS_IN_STACK (MODE, TYPE) \ + || CRIS_FUNCTION_ARG_SIZE (MODE, TYPE) > 8) \ + +/* Contrary to what you'd believe, defining FUNCTION_ARG_CALLEE_COPIES + seems like a (small total) loss, at least for gcc-2.7.2 compiling and + running gcc-2.1 (small win in size, small loss running -- 100.1%), + and similarly for size for products (.1 .. .3% bloat, sometimes win). + Due to the empirical likeliness of making slower code, it is not + defined. */ + +/* This no longer *needs* to be a structure; but keeping it as such should + not hurt (and hacking the ABI is simpler). */ +#define CUMULATIVE_ARGS struct cum_args +struct cum_args {int regs;}; + +/* The regs member is an integer, the number of arguments got into + registers so far, and lib is nonzero if init_cumulative_args was + found to generate a call to a library function. */ +#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT) \ + ((CUM).regs = 0) + +#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \ + ((CUM).regs \ + = (FUNCTION_ARG_PASS_BY_REFERENCE(CUM, MODE, TYPE, NAMED) \ + ? (CRIS_MAX_ARGS_IN_REGS) + 1 \ + : ((CUM).regs \ + + (3 + (CRIS_FUNCTION_ARG_SIZE (MODE, TYPE))) / 4))) + +#define FUNCTION_ARG_REGNO_P(REGNO) \ + ((REGNO) >= CRIS_FIRST_ARG_REG \ + && (REGNO) < CRIS_FIRST_ARG_REG + (CRIS_MAX_ARGS_IN_REGS)) + + +/* Node: Scalar Return */ + +/* Let's assume all functions return in r[CRIS_FIRST_ARG_REG] for the + time being. */ +#define FUNCTION_VALUE(VALTYPE, FUNC) \ + gen_rtx (REG, TYPE_MODE (VALTYPE), CRIS_FIRST_ARG_REG) + +#define LIBCALL_VALUE(MODE) gen_rtx (REG, MODE, CRIS_FIRST_ARG_REG) + +#define FUNCTION_VALUE_REGNO_P(N) ((N) == CRIS_FIRST_ARG_REG) + + +/* Node: Aggregate Return */ + +#if 0 +/* FIXME: Let's try this some time, so we return structures in registers. + We would cast the result of int_size_in_bytes to unsigned, so we will + get a huge number for "structures" of variable size (-1). */ +#define RETURN_IN_MEMORY(TYPE) \ + ((unsigned) int_size_in_bytes (TYPE) > CRIS_MAX_ARGS_IN_REGS * UNITS_PER_WORD) +#endif + +#define STRUCT_VALUE_REGNUM ((CRIS_FIRST_ARG_REG) - 1) + + +/* Node: Caller Saves */ +/* (no definitions) */ + +/* Node: Function entry */ + +/* See cris.c for TARGET_ASM_FUNCTION_PROLOGUE and + TARGET_ASM_FUNCTION_EPILOGUE. */ + +/* If the epilogue uses the "ret" insn, we need to fill the + delay slot. */ +#define DELAY_SLOTS_FOR_EPILOGUE cris_delay_slots_for_epilogue () + +#define ELIGIBLE_FOR_EPILOGUE_DELAY(INSN, N) \ + cris_eligible_for_epilogue_delay (INSN) + +#define ASM_OUTPUT_MI_THUNK(FILE, THUNK_FNDECL, DELTA, FUNCTION) \ + cris_asm_output_mi_thunk(FILE, THUNK_FNDECL, DELTA, FUNCTION) + + +/* Node: Profiling */ + +#define FUNCTION_PROFILER(FILE, LABELNO) \ + error ("No FUNCTION_PROFILER for CRIS") + +/* No profiling for the time being. */ +#define FUNCTION_BLOCK_PROFILER(FILE, LABELNO) \ + error ("No FUNCTION_BLOCK_PROFILER for CRIS") + +/* No profiling for the time being. */ +#define BLOCK_PROFILER(FILE, BLOCKNO) \ + error ("No BLOCK_PROFILER for CRIS") + +/* FIXME: Some of the undefined macros might be mandatory. If so, fix + documentation. */ + + +/* Node: Varargs */ + +/* We save the register number of the first anonymous argument in + first_vararg_reg, and take care of this in the function prologue. + This behaviour is used by at least one more port (the ARM?), but + may be unsafe when compiling nested functions. (With varargs? Hairy.) + Note that nested-functions is a GNU C extension. + + FIXME: We can actually put the size in PRETEND and deduce the number + of registers from it in the prologue and epilogue. */ +#define SETUP_INCOMING_VARARGS(ARGSSF, MODE, TYPE, PRETEND, SECOND) \ + do \ + { \ + if ((ARGSSF).regs < (CRIS_MAX_ARGS_IN_REGS)) \ + (PRETEND) = ((CRIS_MAX_ARGS_IN_REGS) - (ARGSSF).regs) * 4; \ + if (TARGET_PDEBUG) \ + { \ + fprintf (asm_out_file, \ + "\n; VA:: %s: %d args before, anon @ #%d, %dtime\n", \ + current_function_varargs ? "OLD" : "ANSI", \ + (ARGSSF).regs, PRETEND, SECOND); \ + } \ + } \ + while (0) + +/* FIXME: This and other EXPAND_BUILTIN_VA_... target macros are not + documented, although used by several targets. */ +#define EXPAND_BUILTIN_VA_ARG(VALIST, TYPE) \ + cris_expand_builtin_va_arg (VALIST, TYPE) + + +/* Node: Trampolines */ + +/* This looks too complicated, and it is. I assigned r7 to be the + static chain register, but it is call-saved, so we have to save it, + and come back to restore it after the call, so we have to save srp... + Anyway, trampolines are rare enough that we can cope with this + somewhat lack of elegance. + (Do not be tempted to "straighten up" whitespace in the asms; the + assembler #NO_APP state mandates strict spacing). */ +#define TRAMPOLINE_TEMPLATE(FILE) \ + do \ + { \ + fprintf (FILE, "\tmove.d $%s,[$pc+20]\n", \ + reg_names[STATIC_CHAIN_REGNUM]); \ + fprintf (FILE, "\tmove $srp,[$pc+22]\n"); \ + fprintf (FILE, "\tmove.d 0,$%s\n", \ + reg_names[STATIC_CHAIN_REGNUM]); \ + fprintf (FILE, "\tjsr 0\n"); \ + fprintf (FILE, "\tmove.d 0,$%s\n", \ + reg_names[STATIC_CHAIN_REGNUM]); \ + fprintf (FILE, "\tjump 0\n"); \ + } \ + while (0) + +#define TRAMPOLINE_SIZE 32 + +/* CRIS wants instructions on word-boundary. + Note that due to a bug (reported) in 2.7.2 and earlier, this is + actually treated as alignment in _bytes_, not _bits_. (Obviously + this is not fatal, only a slight waste of stack space). */ +#define TRAMPOLINE_ALIGNMENT 16 + +#define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT) \ + do \ + { \ + emit_move_insn (gen_rtx (MEM, SImode, \ + plus_constant (TRAMP, 10)), \ + CXT); \ + emit_move_insn (gen_rtx (MEM, SImode, \ + plus_constant (TRAMP, 16)), \ + FNADDR); \ + } \ + while (0) + +/* Note that there is no need to do anything with the cache for sake of + a trampoline. */ + + +/* Node: Library Calls */ + +#define MULSI3_LIBCALL "__Mul" +#define DIVSI3_LIBCALL "__Div" +#define UDIVSI3_LIBCALL "__Udiv" +#define MODSI3_LIBCALL "__Mod" +#define UMODSI3_LIBCALL "__Umod" + +/* If you change this, you have to check whatever libraries and systems + that use it. */ +#define TARGET_EDOM 33 + + +/* Node: Addressing Modes */ + +#define HAVE_POST_INCREMENT 1 + +#define CONSTANT_ADDRESS_P(X) CONSTANT_P (X) + +#define MAX_REGS_PER_ADDRESS 2 + +/* There are helper macros defined here which are used only in + GO_IF_LEGITIMATE_ADDRESS. + + Note that you *have to* reject invalid addressing modes for mode + MODE, even if it is legal for normal addressing modes. You cannot + rely on the constraints to do this work. They can only be used to + doublecheck your intentions. One example is that you HAVE TO reject + (mem:DI (plus:SI (reg:SI x) (reg:SI y))) because for some reason + this cannot be reloaded. (Which of course you can argue that gcc + should have done.) FIXME: Strange. Check. */ + +/* No symbol can be used as an index (or more correct, as a base) together + with a register with PIC; the PIC register must be there. */ +#define CONSTANT_INDEX_P(X) \ + (CONSTANT_P (X) && !(flag_pic && cris_symbol (X))) + +/* True if X is a valid base register. */ +#define BASE_P(X) \ + (REG_P (X) && REG_OK_FOR_BASE_P (X)) + +/* True if X is a valid base register with or without autoincrement. */ +#define BASE_OR_AUTOINCR_P(X) \ + (BASE_P (X) || (GET_CODE (X) == POST_INC && BASE_P (XEXP (X, 0)))) + +/* True if X is a valid (register) index for BDAP, i.e. [Rs].S or [Rs+].S. */ +#define BDAP_INDEX_P(X) \ + ((GET_CODE (X) == MEM && GET_MODE (X) == SImode \ + && BASE_OR_AUTOINCR_P (XEXP (X, 0))) \ + || (GET_CODE (X) == SIGN_EXTEND \ + && GET_CODE (XEXP (X, 0)) == MEM \ + && (GET_MODE (XEXP (X, 0)) == HImode \ + || GET_MODE (XEXP (X, 0)) == QImode) \ + && BASE_OR_AUTOINCR_P (XEXP (XEXP (X, 0), 0)))) + +/* True if X is a valid (register) index for BIAP, i.e. Rd.m. */ +#define BIAP_INDEX_P(X) \ + ((BASE_P (X) && REG_OK_FOR_INDEX_P (X)) \ + || (GET_CODE (X) == MULT \ + && BASE_P (XEXP (X, 0)) \ + && REG_OK_FOR_INDEX_P (XEXP (X, 0)) \ + && GET_CODE (XEXP (X, 1)) == CONST_INT \ + && (INTVAL (XEXP (X, 1)) == 2 \ + || INTVAL (XEXP (X, 1)) == 4))) + +/* True if X is an address that doesn't need a prefix i.e. [Rs] or [Rs+]. */ +#define SIMPLE_ADDRESS_P(X) \ + (BASE_P (X) \ + || (GET_CODE (X) == POST_INC \ + && BASE_P (XEXP (X, 0)))) + +/* A PIC operand looks like a normal symbol here. At output we dress it + in "[rPIC+symbol:GOT]" (global symbol) or "rPIC+symbol:GOTOFF" (local + symbol) so we exclude all addressing modes where we can't replace a + plain "symbol" with that. A global PIC symbol does not fit anywhere + here (but is thankfully a general_operand in itself). A local PIC + symbol is valid for the plain "symbol + offset" case. */ +#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \ + { \ + rtx x1, x2; \ + if (SIMPLE_ADDRESS_P (X)) \ + goto ADDR; \ + if (CONSTANT_P (X) \ + && (! flag_pic \ + || cris_gotless_symbol (X) \ + || ! cris_symbol (X))) \ + goto ADDR; \ + /* Indexed? */ \ + if (GET_CODE (X) == PLUS) \ + { \ + x1 = XEXP (X, 0); \ + x2 = XEXP (X, 1); \ + /* BDAP o, Rd. */ \ + if ((BASE_P (x1) && CONSTANT_INDEX_P (x2)) \ + || (BASE_P (x2) && CONSTANT_INDEX_P (x1)) \ + /* BDAP Rs[+], Rd. */ \ + || (GET_MODE_SIZE (MODE) <= UNITS_PER_WORD \ + && ((BASE_P (x1) && BDAP_INDEX_P (x2)) \ + || (BASE_P (x2) && BDAP_INDEX_P (x1)) \ + /* BIAP.m Rs, Rd */ \ + || (BASE_P (x1) && BIAP_INDEX_P (x2)) \ + || (BASE_P (x2) && BIAP_INDEX_P (x1))))) \ + goto ADDR; \ + } \ + else if (GET_CODE (X) == MEM) \ + { \ + /* DIP (Rs). Reject [[reg+]] and [[reg]] for \ + DImode (long long). */ \ + if (GET_MODE_SIZE (MODE) <= UNITS_PER_WORD \ + && (BASE_P (XEXP (X, 0)) \ + || BASE_OR_AUTOINCR_P (XEXP (X, 0)))) \ + goto ADDR; \ + } \ + } + +#ifndef REG_OK_STRICT + /* Nonzero if X is a hard reg that can be used as a base reg + or if it is a pseudo reg. */ +# define REG_OK_FOR_BASE_P(X) \ + (REGNO (X) <= CRIS_LAST_GENERAL_REGISTER \ + || REGNO (X) == ARG_POINTER_REGNUM \ + || REGNO (X) >= FIRST_PSEUDO_REGISTER) +#else + /* 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 + +#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) REG_OK_FOR_BASE_P (X) +#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)) +#endif + +/* For now, don't do anything. GCC does a good job most often. + + Maybe we could do something about gcc:s misbehaviour when it + recalculates frame offsets for local variables, from fp+offs to + sp+offs. The resulting address expression gets screwed up + sometimes, but I'm not sure that it may be fixed here, since it is + already split up in several instructions (Is this still true?). + FIXME: Check and adjust for gcc-2.9x. */ +#define LEGITIMIZE_ADDRESS(X, OLDX, MODE, WIN) {} + +/* Functionality import from EGCS. + Kludge to solve Axis-990219: Work around imperfection in + reload_load_address1: + (plus (sign_extend (mem:qi (reg))) (reg)) + should be reloaded as (plus (reg) (reg)), not + (plus (sign_extend (reg)) (reg)). + There are no checks that reload_load_address_1 "reloads" + addresses correctly, so invalidness is not caught or + corrected. + When the right thing happens, the "something_reloaded" kludge can + be removed. The right thing does not appear to happen for + EGCS CVS as of this date (above). */ + +#define LEGITIMIZE_RELOAD_ADDRESS(X, MODE, OPNUM, TYPE, IND_LEVELS, WIN) \ + do \ + { \ + if (GET_CODE (X) == PLUS \ + && REG_P (XEXP (X, 1)) \ + && GET_CODE (XEXP (X, 0)) == SIGN_EXTEND \ + && GET_CODE (XEXP (XEXP (X, 0), 0)) == MEM \ + && (GET_MODE (XEXP (XEXP (X, 0), 0)) == HImode \ + || GET_MODE (XEXP (XEXP (X, 0), 0)) == QImode) \ + && (REG_P (XEXP (XEXP (XEXP (X, 0), 0), 0)) \ + || (GET_CODE (XEXP (XEXP (XEXP (X, 0), 0), 0)) \ + == POST_INC \ + && REG_P (XEXP (XEXP (XEXP (XEXP (X, 0), 0), 0), \ + 0))))) \ + { \ + int something_reloaded = 0; \ + \ + if (REGNO (XEXP (X, 1)) >= FIRST_PSEUDO_REGISTER) \ + { \ + /* Second reg is pseudo, reload it. */ \ + push_reload (XEXP (X, 1), NULL_RTX, &XEXP (X, 1), \ + NULL, \ + GENERAL_REGS, GET_MODE (X), VOIDmode, 0, 0, \ + OPNUM, TYPE); \ + something_reloaded = 1; \ + } \ + \ + if (REG_P (XEXP (XEXP (X, 0), 0)) \ + && (REGNO (XEXP (XEXP (X, 0), 0)) \ + >= FIRST_PSEUDO_REGISTER)) \ + { \ + /* First one is a pseudo - reload that. */ \ + push_reload (XEXP (XEXP (X, 0), 0), NULL_RTX, \ + &XEXP (XEXP (X, 0), 0), NULL, \ + GENERAL_REGS, \ + GET_MODE (X), VOIDmode, 0, 0, OPNUM, TYPE); \ + something_reloaded = 1; \ + } \ + \ + if (! something_reloaded \ + || (GET_CODE (XEXP (XEXP (X, 0), 0)) == POST_INC \ + && (REGNO (XEXP (XEXP (XEXP (X, 0), 0), 0)) \ + >= FIRST_PSEUDO_REGISTER))) \ + /* Reload the sign_extend. Happens if neither reg is a \ + pseudo, or the first one was inside post_increment. */ \ + push_reload (XEXP (X, 0), NULL_RTX, &XEXP (X, 0), NULL, \ + GENERAL_REGS, GET_MODE (X), VOIDmode, 0, 0, \ + OPNUM, TYPE); \ + goto WIN; \ + } \ + } \ + while (0) + +/* In CRIS, only the postincrement address mode depends thus, + since the increment depends on the size of the operand. */ +#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR, LABEL) \ + do \ + { \ + if (GET_CODE (ADDR) == POST_INC) \ + goto LABEL; \ + } \ + while (0) + +#define LEGITIMATE_CONSTANT_P(X) 1 + + +/* Node: Condition Code */ + +#define NOTICE_UPDATE_CC(EXP, INSN) cris_notice_update_cc (EXP, INSN) + +/* FIXME: Maybe define CANONICALIZE_COMPARISON later, when playing with + optimizations. It is needed; currently we do this with instruction + patterns and NOTICE_UPDATE_CC. */ + + +/* Node: Costs */ + +#define CONST_COSTS(RTX, CODE, OUTER_CODE) \ + case CONST_INT: \ + if (INTVAL (RTX) == 0) \ + return 0; \ + if (INTVAL (RTX) < 32 && INTVAL (RTX) >= -32) \ + return 1; \ + /* Eight or 16 bits are a word and cycle more expensive. */ \ + if (INTVAL (RTX) <= 32767 && INTVAL (RTX) >= -32768) \ + return 2; \ + /* A 32 bit constant (or very seldom, unsigned 16 bits) costs \ + another word. FIXME: This isn't linear to 16 bits. */ \ + return 4; \ + case LABEL_REF: \ + return 6; \ + case CONST: \ + case SYMBOL_REF: \ + /* For PIC, we need a prefix (if it isn't already there), \ + and the PIC register. For a global PIC symbol, we also need a \ + read of the GOT. */ \ + return \ + flag_pic ? (cris_got_symbol (RTX) ? (2 + 4 + 6) : (2 + 6)) : 6; \ + case CONST_DOUBLE: \ + if (RTX != CONST0_RTX (GET_MODE (RTX) == VOIDmode ? DImode \ + : GET_MODE (RTX))) \ + return 12; \ + /* Make 0.0 cheap, else test-insns will not be used. */ \ + return 0; + +#define RTX_COSTS(X, CODE, OUTER_CODE) \ + case MULT: \ + /* Identify values that are no powers of two. Powers of 2 are \ + taken care of already and those values should not be \ + changed. */ \ + if (GET_CODE (XEXP (X, 1)) != CONST_INT \ + || exact_log2 (INTVAL (XEXP (X, 1)) < 0)) \ + { \ + /* If we have a multiply insn, then the cost is between \ + 1 and 2 "fast" instructions. */ \ + if (TARGET_HAS_MUL_INSNS) \ + return COSTS_N_INSNS (1) + COSTS_N_INSNS (1) /2; \ + \ + /* Estimate as 4 + 4 * #ofbits. */ \ + return COSTS_N_INSNS (132); \ + } \ + break; \ + case UDIV: \ + case MOD: \ + case UMOD: \ + case DIV: \ + if (GET_CODE (XEXP (X, 1)) != CONST_INT \ + || exact_log2 (INTVAL (XEXP (X, 1)) < 0)) \ + /* Estimate this as 4 + 8 * #of bits. */ \ + return COSTS_N_INSNS (260); \ + \ + case AND: \ + if (GET_CODE (XEXP (X, 1)) == CONST_INT \ + /* Two constants may actually happen before optimization. */ \ + && GET_CODE (XEXP (X, 0)) != CONST_INT \ + && !CONST_OK_FOR_LETTER_P (INTVAL (XEXP (X, 1)), 'I')) \ + return \ + rtx_cost (XEXP (X, 0), OUTER_CODE) + 2 \ + + 2 * GET_MODE_NUNITS (GET_MODE (XEXP (X, 0))); \ + \ + case ZERO_EXTEND: case SIGN_EXTEND: \ + /* Same as move. If embedded in other insn, cost is 0. */ \ + return rtx_cost (XEXP (X, 0), OUTER_CODE); + +#define ADDRESS_COST(X) cris_address_cost (X) + +/* FIXME: Need to define REGISTER_MOVE_COST when more register classes are + introduced. */ + +/* This isn't strictly correct for v0..3 in buswidth-8bit mode, but + should suffice. */ +#define MEMORY_MOVE_COST(M, CLASS, IN) \ + (((M) == QImode) ? 4 : ((M) == HImode) ? 4 : 6) + +/* Regardless of the presence of delay slots, the default value of 1 for + BRANCH_COST is the best in the range (1, 2, 3), tested with gcc-2.7.2 + with testcases ipps and gcc, giving smallest and fastest code. */ + +#define SLOW_BYTE_ACCESS 0 + +/* This is the threshold *below* which inline move sequences of + word-length sizes will be emitted. The "9" will translate to + (9 - 1) * 4 = 32 bytes maximum moved, but using 16 instructions + (8 instruction sequences) or less. */ +#define MOVE_RATIO 9 + + +/* Node: Sections */ + +#define TEXT_SECTION_ASM_OP "\t.text" + +#define DATA_SECTION_ASM_OP "\t.data" + +#define FORCE_EH_FRAME_INFO_IN_DATA_SECTION (! TARGET_ELF) + +/* The jump table is immediately connected to the preceding insn. */ +#define JUMP_TABLES_IN_TEXT_SECTION 1 + +/* We need to code in PIC-specific flags into SYMBOL_REF_FLAG. */ + +#define ENCODE_SECTION_INFO(EXP) cris_encode_section_info (EXP) + +/* We pull a little trick to register the _fini function with atexit, + after (presumably) registering the eh frame info, since we don't handle + _fini (a.k.a. ___fini_start) in crt0 or have a crti for "pure" ELF. */ +#ifdef CRT_BEGIN +#define FORCE_INIT_SECTION_ALIGN \ + do \ + { \ + extern void __fini__start (void); \ + atexit (__fini__start); \ + } \ + while (0) +#endif + +/* Node: PIC */ + +#define PIC_OFFSET_TABLE_REGNUM 0 + +#define LEGITIMATE_PIC_OPERAND_P(X) cris_legitimate_pic_operand (X) + + +/* Node: File Framework */ + +/* NO_APP *only at file start* means faster assembly. + It also means comments are not allowed. + In some cases comments will be output for debugging purposes. + Make sure they are allowed then. */ +/* Override previous definitions (elfos.h). */ +#undef ASM_FILE_START +#define ASM_FILE_START(STREAM) \ + do \ + { \ + if (TARGET_PDEBUG || flag_print_asm_name) \ + fprintf ((STREAM), "#APP\n"); \ + else \ + fprintf ((STREAM), "#NO_APP\n"); \ + if (TARGET_ELF) \ + output_file_directive ((STREAM), main_input_filename); \ + } \ + while (0) + +/* Override previous definitions (elfos.h). */ +#undef ASM_FILE_END +#define ASM_FILE_END(STREAM) + +/* We don't want an .ident for gcc. To avoid that but still support + #ident, we override ASM_OUTPUT_IDENT and, since the gcc .ident is its + only use besides ASM_OUTPUT_IDENT, undef IDENT_ASM_OP from elfos.h. */ +#undef IDENT_ASM_OP +#undef ASM_OUTPUT_IDENT +#define ASM_OUTPUT_IDENT(FILE, NAME) \ + fprintf (FILE, "%s\"%s\"\n", "\t.ident\t", NAME); + +#define ASM_APP_ON "#APP\n" + +#define ASM_APP_OFF "#NO_APP\n" + + +/* Node: Data Output */ + +/* We must use REAL_VALUE_TO_TARGET_SINGLE and + REAL_VALUE_TO_TARGET_LONG_DOUBLE. It seems real.h cannot support when + target-double is target-single is 32bit-single. */ +#define ASM_OUTPUT_LONG_DOUBLE(FILE, VALUE) \ + do \ + { \ + long l[2]; \ + REAL_VALUE_TO_TARGET_LONG_DOUBLE (VALUE, l); \ + fprintf (FILE, "\t.dword 0x%lx\n", l[0]); \ + fprintf (FILE, "\t.dword 0x%lx\n", l[1]); \ + } \ + while (0) + +/* FIXME: The manual says "array of long:s", but + REAL_VALUE_TO_TARGET_SINGLE actually writes a long. */ +#define ASM_OUTPUT_FLOAT(FILE, VALUE) \ + do \ + { \ + long l; \ + REAL_VALUE_TO_TARGET_SINGLE (VALUE, l); \ + fprintf (FILE, "\t.dword 0x%lx\n", l); \ + } \ + while (0) + +/* This is what is used by gcc for 64-bit floats, + not the "long double" one. */ +#define ASM_OUTPUT_DOUBLE(FILE, VALUE) \ + ASM_OUTPUT_LONG_DOUBLE (FILE, VALUE) + + +/* This is a kludge for a.out+ELF support: For non-ELF prioritized + [cd]tors, globalize the function so collect2 can collect it. This is + due to short-sightedness guided by defined (ASM_OUTPUT_SECTION_NAME) + && defined (ASM_OUTPUT_CONSTRUCTOR). */ + +#define ASM_OUTPUT_INT(FILE, VALUE) \ + do \ + { \ + fprintf (FILE, "\t.dword "); \ + output_addr_const (FILE, (VALUE)); \ + fprintf (FILE, "\n"); \ + } \ + while (0) + +#define ASM_OUTPUT_SHORT(FILE, VALUE) \ + do \ + { \ + fprintf (FILE, "\t.word "); \ + output_addr_const (FILE, (VALUE)); \ + fprintf (FILE, "\n"); \ + } \ + while (0) + +#define ASM_OUTPUT_CHAR(FILE, VALUE) \ + do \ + { \ + fprintf (FILE, "\t.byte "); \ + output_addr_const (FILE, (VALUE)); \ + fprintf (FILE, "\n"); \ + } \ + while (0) + +#define ASM_OUTPUT_BYTE(FILE, VALUE) \ + fprintf (FILE, "\t.byte 0x%x\n", (VALUE)) + +#define IS_ASM_LOGICAL_LINE_SEPARATOR(C) (C) == '@' + +/* FIXME: These are undocumented. */ +/* We need to define these, since the 2byte, 4byte, 8byte op:s are only + available in ELF. These "normal" pseudos do not have any alignment + constraints or side-effects. */ +#undef UNALIGNED_SHORT_ASM_OP +#define UNALIGNED_SHORT_ASM_OP "\t.word\t" + +#undef INT_ASM_OP +#define INT_ASM_OP "\t.dword\t" + +#undef UNALIGNED_INT_ASM_OP +#define UNALIGNED_INT_ASM_OP "\t.dword\t" + +#undef UNALIGNED_DOUBLE_INT_ASM_OP +#define UNALIGNED_DOUBLE_INT_ASM_OP "\t.quad\t" + +/* Node: Uninitialized Data */ + +/* Remember to round off odd values if we want data alignment, + since we cannot do that with an .align directive. + + Using .comm causes the space not to be reserved in .bss, but by + tricks with the symbol type. Not good if other tools than binutils + are used on the object files. Since ".global ... .lcomm ..." works, we + use that. Use .._ALIGNED_COMMON, since gcc whines when we only have + ..._COMMON, and we prefer to whine outselves; BIGGEST_ALIGNMENT is not + the one to check. This done for a.out only. */ +/* FIXME: I suspect a bug in gcc with alignment. Do not warn until + investigated; it mucks up the testsuite results. */ +#define CRIS_ASM_OUTPUT_ALIGNED_DECL_COMMON(FILE, DECL, NAME, SIZE, ALIGN, LOCAL) \ + do \ + { \ + int align_ = (ALIGN) / BITS_PER_UNIT; \ + if (TARGET_DATA_ALIGN && TARGET_ALIGN_BY_32 && align_ < 4) \ + align_ = 4; \ + else if (TARGET_DATA_ALIGN && align_ < 2) \ + align_ = 2; \ + /* FIXME: Do we need this? */ \ + else if (align_ < 1) \ + align_ = 1; \ + \ + if (TARGET_ELF) \ + { \ + if (LOCAL) \ + { \ + fprintf ((FILE), "%s", LOCAL_ASM_OP); \ + assemble_name ((FILE), (NAME)); \ + fprintf ((FILE), "\n"); \ + } \ + fprintf ((FILE), "%s", COMMON_ASM_OP); \ + assemble_name ((FILE), (NAME)); \ + fprintf ((FILE), ",%u,%u\n", (SIZE), align_); \ + } \ + else \ + { \ + /* We can't tell a one-only or weak COMM from a "global \ + COMM" so just make all non-locals weak. */ \ + if (! (LOCAL)) \ + ASM_WEAKEN_LABEL (FILE, NAME); \ + fputs ("\t.lcomm ", (FILE)); \ + assemble_name ((FILE), (NAME)); \ + fprintf ((FILE), ",%u\n", \ + ((SIZE) + (align_ - 1)) & ~(align_ - 1)); \ + } \ + } \ + while (0) + +#define ASM_OUTPUT_ALIGNED_DECL_COMMON(FILE, DECL, NAME, SIZE, ALIGN) \ + CRIS_ASM_OUTPUT_ALIGNED_DECL_COMMON(FILE, DECL, NAME, SIZE, ALIGN, 0) + +#undef ASM_OUTPUT_ALIGNED_DECL_LOCAL +#define ASM_OUTPUT_ALIGNED_DECL_LOCAL(FILE, DECL, NAME, SIZE, ALIGN) \ + CRIS_ASM_OUTPUT_ALIGNED_DECL_COMMON(FILE, DECL, NAME, SIZE, ALIGN, 1) + +/* FIXME: define ASM_OUTPUT_SHARED_COMMON and emit an error when it is + used with -melinux and a.out. */ + +/* Node: Label Output */ + +#define ASM_OUTPUT_LABEL(FILE, NAME) \ + do \ + { \ + assemble_name (FILE, NAME); \ + fputs (":\n", FILE); \ + } \ + while (0) + +#define ASM_GLOBALIZE_LABEL(FILE, NAME) \ + do \ + { \ + fputs ("\t.global ", FILE); \ + assemble_name (FILE, NAME); \ + fputs ("\n", FILE); \ + } \ + while (0) + +#define SUPPORTS_WEAK 1 + +/* FIXME: This macro isn't documented, but this would probably be an + appropriate location. It's only used in crtstuff.c, else we'd have to + handle (to #undef or ignore it) in a.out. */ +#define HAVE_GAS_HIDDEN 1 + +#undef ASM_OUTPUT_INTERNAL_LABEL +#define ASM_OUTPUT_INTERNAL_LABEL(FILE, PREFIX, NUM) \ + do \ + { \ + asm_fprintf (FILE, "%L%s%d:\n", PREFIX, NUM); \ + } \ + while (0) + +/* Remove any previous definition (elfos.h). */ +#undef ASM_GENERATE_INTERNAL_LABEL +#define ASM_GENERATE_INTERNAL_LABEL(LABEL, PREFIX, NUM) \ + sprintf (LABEL, "*%s%s%ld", LOCAL_LABEL_PREFIX, PREFIX, (long) NUM) + +#define ASM_FORMAT_PRIVATE_NAME(OUTPUT, NAME, LABELNO) \ + do \ + { \ + (OUTPUT) = (char *) alloca (strlen ((NAME)) + 10); \ + sprintf ((OUTPUT), "%s.%d", (NAME), (LABELNO)); \ + } \ + while (0) + + +/* Node: Initialization */ +/* (no definitions) */ + +/* Node: Macros for Initialization */ + +/* We don't want to use "strip" for anything linked with "-melinux" + "-shlib", seen by the linker as "-Ur -d -Bdynamic" in combination. */ +#define SET_STRIPPABLE_EXECUTABLE(DS, ARGC, ARGV) \ + do \ + { \ + int i; \ + int flags = 0; \ + for (i = (ARGC) - 1; i > 0; i--) \ + { \ + if (strcmp ((ARGV)[i], "-Ur") == 0) \ + flags |= 1; \ + else if (strcmp ((ARGV)[i], "-d") == 0) \ + flags |= 2; \ + else if (strcmp ((ARGV)[i], "-Bdynamic") == 0) \ + flags |= 4; \ + \ + if (flags == 7) \ + break; \ + } \ + \ + (DS) = (flags != 7); \ + } \ + while (0) + + +/* Node: Instruction Output */ + +#define REGISTER_NAMES \ + {"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", \ + "r9", "r10", "r11", "r12", "r13", "sp", "pc", "srp", "faked_ap"} + +#define ADDITIONAL_REGISTER_NAMES \ + {{"r14", 14}, {"r15", 15}} + +#define PRINT_OPERAND(FILE, X, CODE) \ + cris_print_operand (FILE, X, CODE) + +/* For delay-slot handling. */ +#define PRINT_OPERAND_PUNCT_VALID_P(CODE) (CODE == '#') + +#define PRINT_OPERAND_ADDRESS(FILE, ADDR) \ + cris_print_operand_address (FILE, ADDR) + +/* Output an empty line to illustrate the presence of the delay slot. */ +#define DBR_OUTPUT_SEQEND(FILE) \ + fprintf (FILE, "\n") + +#define LOCAL_LABEL_PREFIX (TARGET_ELF ? "." : "") + +/* cppinit.c initializes a const array from this, so it must be constant, + can't have it different based on options. Luckily, the prefix is + always allowed, so let's have it on all GCC-generated code. Note that + we have this verbatim everywhere in the back-end, not using %R or %s or + such. */ +#define REGISTER_PREFIX "$" + +/* Remove any previous definition (elfos.h). */ +/* We use -fno-leading-underscore to remove it, when necessary. */ +#undef USER_LABEL_PREFIX +#define USER_LABEL_PREFIX "_" + +#define ASM_OUTPUT_REG_PUSH(FILE, REGNO) \ + fprintf (FILE, "\tpush $%s\n", reg_names[REGNO]) + +#define ASM_OUTPUT_REG_POP(FILE, REGNO) \ + fprintf (FILE, "\tpop $%s\n", reg_names[REGNO]) + + +/* Node: Dispatch Tables */ + +#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \ + asm_fprintf (FILE, "\t.word %LL%d-%LL%d\n", VALUE, REL) + +#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \ + asm_fprintf (FILE, "\t.dword %LL%d\n", VALUE) + +/* Defined to also emit an .align in elfos.h. We don't want that. */ +#undef ASM_OUTPUT_CASE_LABEL + +/* Since the "bound" insn loads the comparison value if the compared< + value (register) is out of bounds (0..comparison value-1), we need + to output another case to catch it. + The way to find it is to look for the label_ref at the else-arm inside + the expanded casesi core-insn. + FIXME: Check this construct when changing to new version of gcc. */ +#define ASM_OUTPUT_CASE_END(STREAM, NUM, TABLE) \ + do \ + { \ + asm_fprintf (STREAM, "\t.word %LL%d-%LL%d%s\n", \ + CODE_LABEL_NUMBER \ + (XEXP (XEXP (XEXP \ + (XVECEXP \ + (PATTERN (PREV_INSN (PREV_INSN \ + (TABLE))), \ + 0, 0), 1), 2), 0)), \ + NUM, \ + (TARGET_PDEBUG ? "; default" : "")); \ + } \ + while (0) + + +/* Node: Exception Region Output */ +/* (no definitions) */ +/* FIXME: Fill in with our own optimized layout. */ + +/* Node: Alignment Output */ + +#define ASM_OUTPUT_ALIGN(FILE, LOG) \ + fprintf (FILE, "\t.align %d\n", (LOG)) + + +/* Node: All Debuggers */ + +#define DBX_REGISTER_NUMBER(REGNO) \ + ((REGNO) == CRIS_SRP_REGNUM ? CRIS_CANONICAL_SRP_REGNUM : (REGNO)) + +/* FIXME: Investigate DEBUGGER_AUTO_OFFSET, DEBUGGER_ARG_OFFSET. */ + + +/* Node: DBX Options */ + +/* Is this correct? Check later. */ +#define DBX_NO_XREFS + +#define DBX_CONTIN_LENGTH 0 + +/* FIXME: Is this needed when we have 0 DBX_CONTIN_LENGTH? */ +#define DBX_CONTIN_CHAR '?' + + +/* Node: DBX Hooks */ +/* (no definitions) */ + +/* Node: File names and DBX */ +/* (no definitions) */ + + +/* Node: SDB and DWARF */ + +#define DWARF_LINE_MIN_INSTR_LENGTH 2 + + +/* Node: Cross-compilation */ +#define REAL_ARITHMETIC + + +/* Node: Misc */ + +/* FIXME: Check this one more time. */ +#define PREDICATE_CODES \ + {"cris_orthogonal_operator", \ + {PLUS, MINUS, IOR, AND, UMIN}}, \ + {"cris_commutative_orth_op", \ + {PLUS, IOR, AND, UMIN}}, \ + {"cris_operand_extend_operator", \ + {PLUS, MINUS, UMIN}}, \ + {"cris_extend_operator", \ + {ZERO_EXTEND, SIGN_EXTEND}}, \ + {"cris_plus_or_bound_operator", \ + {PLUS, UMIN}}, \ + {"cris_bdap_operand", \ + {SUBREG, REG, LABEL_REF, SYMBOL_REF, MEM, CONST_INT, \ + CONST_DOUBLE, CONST, SIGN_EXTEND}}, \ + {"cris_bdap_biap_operand", \ + {SUBREG, REG, LABEL_REF, SYMBOL_REF, MEM, CONST_INT, \ + CONST_DOUBLE, CONST, SIGN_EXTEND, MULT}}, \ + {"cris_general_operand_or_gotless_symbol", \ + {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF, \ + LABEL_REF, SUBREG, REG, MEM}}, \ + {"cris_general_operand_or_symbol", \ + {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF, \ + LABEL_REF, SUBREG, REG, MEM}}, \ + {"cris_general_operand_or_plt_symbol", \ + {CONST_INT, CONST_DOUBLE, CONST, SYMBOL_REF, \ + LABEL_REF, SUBREG, REG, MEM}}, \ + {"cris_mem_call_operand", \ + {MEM}}, + +/* A combination of the bound (umin) insn together with a + sign-extended add via the table to PC seems optimal. + If the table overflows, the assembler will take care of it. + Theoretically, in extreme cases (uncertain if they occur), an error + will be emitted, so FIXME: Check how large case-tables are emitted, + possible add an option to emit SImode case-tables. */ +#define CASE_VECTOR_MODE HImode + +#define CASE_VECTOR_PC_RELATIVE 1 + +/* FIXME: Investigate CASE_VECTOR_SHORTEN_MODE to make sure HImode is not + used when broken-.word could possibly fail (plus test-case). */ + +#define IMPLICIT_FIX_EXPR FIX_ROUND_EXPR + +#define FIXUNS_TRUNC_LIKE_FIX_TRUNC + +#define EASY_DIV_EXPR TRUNC_DIV_EXPR + +/* This is the number of bytes that can be moved in one + reasonably fast instruction sequence. For CRIS, this is two + instructions: mem => reg, reg => mem. */ +#define MOVE_MAX 4 + +/* Maybe SHIFT_COUNT_TRUNCATED is safe to define? FIXME: Check later. */ + +#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1 + +#define STORE_FLAG_VALUE 1 + +#define Pmode SImode + +#define FUNCTION_MODE QImode + +#define NO_IMPLICIT_EXTERN_C + +/* + * Local variables: + * eval: (c-set-style "gnu") + * indent-tabs-mode: t + * End: + */ diff --git a/gcc/config/cris/cris.md b/gcc/config/cris/cris.md new file mode 100644 index 00000000000..3df8aaabef3 --- /dev/null +++ b/gcc/config/cris/cris.md @@ -0,0 +1,5096 @@ +;; GCC machine description for CRIS cpu cores. +;; Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. +;; Contributed by Axis Communications. + +;; This file is part of GCC. +;; +;; GCC 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. +;; +;; GCC 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 GCC; see the file COPYING. If not, write to +;; the Free Software Foundation, 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;; The original PO technology requires these to be ordered by speed, +;; so that assigner will pick the fastest. + +;; See files "md.texi" and "rtl.def" for documentation on define_insn, +;; match_*, et. al. +;; +;; The function cris_notice_update_cc in cris.c handles condition code +;; updates for most instructions, helped by the "cc" attribute. + +;; There are several instructions that are orthogonal in size, and seems +;; they could be matched by a single pattern without a specified size +;; for the operand that is orthogonal. However, this did not work on +;; gcc-2.7.2 (and problably not on gcc-2.8.1), relating to that when a +;; constant is substituted into an operand, the actual mode must be +;; deduced from the pattern. There is reasonable hope that that has been +;; fixed in egcs post 1.1.1, so FIXME: try again. + +;; You will notice that three-operand alternatives ("=r", "r", "!To") +;; are marked with a "!" constraint modifier to avoid being reloaded +;; into. This is because gcc would otherwise prefer to use the constant +;; pool and its offsettable address instead of reloading to an +;; ("=r", "0", "i") alternative. Also, the constant-pool support was not +;; only suboptimal but also buggy in 2.7.2, ??? maybe only in 2.6.3. + +;; All insns that look like (set (...) (plus (...) (reg:SI 8))) +;; get problems when reloading r8 (frame pointer) to r14 + offs (stack +;; pointer). Thus the instructions that get into trouble have specific +;; checks against matching frame_pointer_rtx. +;; ??? But it should be re-checked for gcc > 2.7.2 +;; FIXME: This changed some time ago (from 2000-03-16) for gcc-2.9x. + +;; FIXME: When PIC, all [rX=rY+S] could be enabled to match +;; [rX=gotless_symbol]. +;; The movsi for a gotless symbol could be split (post reload). + +;; UNSPEC Usage: +;; 0 PLT reference from call expansion: operand 0 is the address, +;; the mode is VOIDmode. Always wrapped in CONST. + +;; We need an attribute to define whether an instruction can be put in +;; a branch-delay slot or not, and whether it has a delay slot. +;; +;; Branches and return instructions have a delay slot, and cannot +;; themselves be put in a delay slot. This has changed *for short +;; branches only* between architecture variants, but the possible win +;; is presumed negligible compared to the added complexity of the machine +;; description: one would have to add always-correct infrastructure to +;; distinguish short branches. +;; +;; Whether an instruction can be put in a delay slot depends on the +;; instruction (all short instructions except jumps and branches) +;; and the addressing mode (must not be prefixed or referring to pc). +;; In short, any "slottable" instruction must be 16 bit and not refer +;; to pc, or alter it. +;; +;; The possible values are "yes", "no" and "has_slot". Yes/no means if +;; the insn is slottable or not. Has_slot means that the insn is a +;; return insn or branch insn (which are not considered slottable since +;; that is generally true). Having the semmingly illogical value +;; "has_slot" means we do not have to add another attribute just to say +;; that an insn has a delay-slot, since it also infers that it is not +;; slottable. Better names for the attribute were found to be longer and +;; not add readability to the machine description. +;; +;; The default that is defined here for this attribute is "no", not +;; slottable, not having a delay-slot, so there's no need to worry about +;; it being wrong for non-branch and return instructions. +;; The default could depend on the kind of insn and the addressing +;; mode, but that would need more attributes and hairier, more error +;; prone code. +;; +;; There is an extra constraint, 'Q', which recognizes indirect reg, +;; except when the reg is pc. The constraints 'Q' and '>' together match +;; all possible memory operands that are slottable. +;; For other operands, you need to check if it has a valid "slottable" +;; quick-immediate operand, where the particular signedness-variation +;; may match the constraints 'I' or 'J'.), and include it in the +;; constraint pattern for the slottable pattern. An alternative using +;; only "r" constraints is most often slottable. + +(define_attr "slottable" "no,yes,has_slot" (const_string "no")) + +;; We also need attributes to sanely determine the condition code +;; state. See cris_notice_update_cc for how this is used. + +(define_attr "cc" "none,clobber,normal" (const_string "normal")) + +;; A branch or return has one delay-slot. The instruction in the +;; delay-slot is always executed, independent of whether the branch is +;; taken or not. Note that besides setting "slottable" to "has_slot", +;; there also has to be a "%#" at the end of a "delayed" instruction +;; output pattern (for "jump" this means "ba %l0%#"), so print_operand can +;; catch it and print a "nop" if necessary. This method was stolen from +;; sparc.md. + +(define_delay (eq_attr "slottable" "has_slot") + [(eq_attr "slottable" "yes") (nil) (nil)]) + +;; Test insns. + +;; DImode +;; +;; Allow register and offsettable mem operands only; post-increment is +;; not worth the trouble. + +(define_insn "tstdi" + [(set (cc0) + (match_operand:DI 0 "nonimmediate_operand" "r,o"))] + "" + "test.d %M0\;ax\;test.d %H0") + +;; No test insns with side-effect on the mem addressing. +;; +;; See note on cmp-insns with side-effects (or lack of them) + +;; Normal named test patterns from SI on. +;; FIXME: Seems they should change to be in order smallest..largest. + +(define_insn "tstsi" + [(set (cc0) + (match_operand:SI 0 "nonimmediate_operand" "r,Q>,m"))] + "" + "test.d %0" + [(set_attr "slottable" "yes,yes,no")]) + +(define_insn "tsthi" + [(set (cc0) + (match_operand:HI 0 "nonimmediate_operand" "r,Q>,m"))] + "" + "test.w %0" + [(set_attr "slottable" "yes,yes,no")]) + +(define_insn "tstqi" + [(set (cc0) + (match_operand:QI 0 "nonimmediate_operand" "r,Q>,m"))] + "" + "test.b %0" + [(set_attr "slottable" "yes,yes,no")]) + +;; It seems that the position of the sign-bit and the fact that 0.0 is +;; all 0-bits would make "tstsf" a straight-forward implementation; +;; either "test.d" it for positive/negative or "btstq 30,r" it for +;; zeroness. +;; +;; FIXME: Do that some time; check next_cc0_user to determine if +;; zero or negative is tested for. + +;; Compare insns. + +;; We could optimize the sizes of the immediate operands for various +;; cases, but that is not worth it because of the very little usage of +;; DImode for anything else but a structure/block-mode. Just do the +;; obvious stuff for the straight-forward constraint letters. + +(define_insn "cmpdi" + [(set (cc0) + (compare (match_operand:DI 0 "nonimmediate_operand" "r,r,r,r,r,r,o") + (match_operand:DI 1 "general_operand" "K,I,P,n,r,o,r")))] + "" + "@ + cmpq %1,%M0\;ax\;cmpq 0,%H0 + cmpq %1,%M0\;ax\;cmpq -1,%H0 + cmp%e1.%z1 %1,%M0\;ax\;cmpq %H1,%H0 + cmp.d %M1,%M0\;ax\;cmp.d %H1,%H0 + cmp.d %M1,%M0\;ax\;cmp.d %H1,%H0 + cmp.d %M1,%M0\;ax\;cmp.d %H1,%H0 + cmp.d %M0,%M1\;ax\;cmp.d %H0,%H1") + +;; Note that compare insns with side effect addressing mode (e.g.): +;; +;; cmp.S [rx=ry+i],rz; +;; cmp.S [%3=%1+%2],%0 +;; +;; are *not* usable for gcc since the reloader *does not accept* +;; cc0-changing insns with side-effects other than setting the condition +;; codes. The reason is that the reload stage *may* cause another insn to +;; be output after the main instruction, in turn invalidating cc0 for the +;; insn using the test. (This does not apply to the CRIS case, since a +;; reload for output -- move to memory -- does not change the condition +;; code. Unfortunately we have no way to describe that at the moment. I +;; think code would improve being in the order of one percent faster. + +;; We have cmps and cmpu (compare reg w. sign/zero extended mem). +;; These are mostly useful for compares in SImode, using 8 or 16-bit +;; constants, but sometimes gcc will find its way to use it for other +;; (memory) operands. Avoid side-effect patterns, though (see above). +;; +;; FIXME: These could have an anonymous mode for operand 1. + +;; QImode + +(define_insn "*cmp_extsi" + [(set (cc0) + (compare + (match_operand:SI 0 "register_operand" "r,r") + (match_operator:SI 2 "cris_extend_operator" + [(match_operand:QI 1 "memory_operand" "Q>,m")])))] + "" + "cmp%e2.%s1 %1,%0" + [(set_attr "slottable" "yes,no")]) + +;; HImode +(define_insn "*cmp_exthi" + [(set (cc0) + (compare + (match_operand:SI 0 "register_operand" "r,r") + (match_operator:SI 2 "cris_extend_operator" + [(match_operand:HI 1 "memory_operand" "Q>,m")])))] + "" + "cmp%e2.%s1 %1,%0" + [(set_attr "slottable" "yes,no")]) + +;; Swap operands; it seems the canonical look (if any) is not enforced. +;; +;; FIXME: Investigate that. +;; FIXME: These could have an anonymous mode for operand 1. + +;; QImode + +(define_insn "*cmp_swapextqi" + [(set (cc0) + (compare + (match_operator:SI 2 "cris_extend_operator" + [(match_operand:QI 0 "memory_operand" "Q>,m")]) + (match_operand:SI 1 "register_operand" "r,r")))] + "" + "cmp%e2.%s0 %0,%1" ; The function cris_notice_update_cc knows about + ; swapped operands to compares. + [(set_attr "slottable" "yes,no")]) + +;; HImode + +(define_insn "*cmp_swapexthi" + [(set (cc0) + (compare + (match_operator:SI 2 "cris_extend_operator" + [(match_operand:HI 0 "memory_operand" "Q>,m")]) + (match_operand:SI 1 "register_operand" "r,r")))] + "" + "cmp%e2.%s0 %0,%1" ; The function cris_notice_update_cc knows about + ; swapped operands to compares. + [(set_attr "slottable" "yes,no")]) + +;; The "normal" compare patterns, from SI on. + +(define_insn "cmpsi" + [(set (cc0) + (compare + (match_operand:SI 0 "nonimmediate_operand" "r,r,r,r,Q>,Q>,r,r,m,m") + (match_operand:SI 1 "general_operand" "I,r,Q>,M,M,r,P,g,M,r")))] + "" + "@ + cmpq %1,%0 + cmp.d %1,%0 + cmp.d %1,%0 + test.d %0 + test.d %0 + cmp.d %0,%1 + cmp%e1.%z1 %1,%0 + cmp.d %1,%0 + test.d %0 + cmp.d %0,%1" + [(set_attr "slottable" "yes,yes,yes,yes,yes,yes,no,no,no,no")]) + +(define_insn "cmphi" + [(set (cc0) + (compare (match_operand:HI 0 "nonimmediate_operand" "r,r,Q>,Q>,r,m,m") + (match_operand:HI 1 "general_operand" "r,Q>,M,r,g,M,r")))] + "" + "@ + cmp.w %1,%0 + cmp.w %1,%0 + test.w %0 + cmp.w %0,%1 + cmp.w %1,%0 + test.w %0 + cmp.w %0,%1" + [(set_attr "slottable" "yes,yes,yes,yes,no,no,no")]) + +(define_insn "cmpqi" + [(set (cc0) + (compare + (match_operand:QI 0 "nonimmediate_operand" "r,r,r,Q>,Q>,r,m,m") + (match_operand:QI 1 "general_operand" "r,Q>,M,M,r,g,M,r")))] + "" + "@ + cmp.b %1,%0 + cmp.b %1,%0 + test.b %0 + test.b %0 + cmp.b %0,%1 + cmp.b %1,%0 + test.b %0 + cmp.b %0,%1" + [(set_attr "slottable" "yes,yes,yes,yes,yes,no,no,no")]) + +;; Pattern matching the BTST insn. +;; It is useful for "if (i & val)" constructs, where val is an exact +;; power of 2, or if val + 1 is a power of two, where we check for a bunch +;; of zeros starting at bit 0). + +;; SImode. This mode is the only one needed, since gcc automatically +;; extends subregs for lower-size modes. FIXME: Add test-case. +(define_insn "*btst" + [(set (cc0) + (zero_extract + (match_operand:SI 0 "nonmemory_operand" "r,r,r,r,r,r,n") + (match_operand:SI 1 "const_int_operand" "K,n,K,n,K,n,n") + (match_operand:SI 2 "nonmemory_operand" "M,M,K,n,r,r,r")))] + ;; Either it is a single bit, or consecutive ones starting at 0. + "GET_CODE (operands[1]) == CONST_INT + && (operands[1] == const1_rtx || operands[2] == const0_rtx) + && (REG_S_P (operands[0]) + || (operands[1] == const1_rtx + && REG_S_P (operands[2]) + && GET_CODE (operands[0]) == CONST_INT + && exact_log2 (INTVAL (operands[0])) >= 0))" + +;; The last "&&" condition above should be caught by some kind of +;; canonicalization in gcc, but we can easily help with it here. +;; It results from expressions of the type +;; "power_of_2_value & (1 << y)". +;; +;; Since there may be codes with tests in on bits (in constant position) +;; beyond the size of a word, handle that by assuming those bits are 0. +;; GCC should handle that, but it's a matter of easily-added belts while +;; having suspenders. + + "@ + btstq (%1-1),%0 + test.d %0 + btstq %2,%0 + clearf nz + btst %2,%0 + clearf nz + cmpq %p0,%2" + [(set_attr "slottable" "yes")]) + +;; Move insns. + +;; The whole mandatory movdi family is here; expander, "anonymous" +;; recognizer and splitter. We're forced to have a movdi pattern, +;; although GCC should be able to split it up itself. Normally it can, +;; but if other insns have DI operands (as is the case here), reload +;; must be able to generate or match a movdi. many testcases fail at +;; -O3 or -fssa if we don't have this. FIXME: Fix GCC... See +;; . +;; However, a patch from Richard Kenner (similar to the cause of +;; discussion at the URL above), indicates otherwise. See +;; . +;; The truth has IMO is not been decided yet, so check from time to +;; time by disabling the movdi patterns. + +(define_expand "movdi" + [(set (match_operand:DI 0 "nonimmediate_operand" "") + (match_operand:DI 1 "general_operand" ""))] + "" + " +{ + if (GET_CODE (operands[0]) == MEM && operands[1] != const0_rtx) + operands[1] = copy_to_mode_reg (DImode, operands[1]); + + /* Some other ports (as of 2001-09-10 for example mcore and romp) also + prefer to split up constants early, like this. The testcase in + gcc.c-torture/execute/961213-1.c shows that CSE2 gets confused by the + resulting subreg sets when using the construct from mcore (as of FSF + CVS, version -r 1.5), and it believe that the high part (the last one + emitted) is the final value. This construct from romp seems more + robust, especially considering the head comments from + emit_no_conflict_block. */ + if ((GET_CODE (operands[1]) == CONST_INT + || GET_CODE (operands[1]) == CONST_DOUBLE) + && ! reload_completed + && ! reload_in_progress) + { + rtx insns; + rtx op0 = operands[0]; + rtx op1 = operands[1]; + + start_sequence (); + emit_move_insn (operand_subword (op0, 0, 1, DImode), + operand_subword (op1, 0, 1, DImode)); + emit_move_insn (operand_subword (op0, 1, 1, DImode), + operand_subword (op1, 1, 1, DImode)); + insns = get_insns (); + end_sequence (); + + emit_no_conflict_block (insns, op0, op1, 0, op1); + DONE; + } +}") + +(define_insn "*movdi_insn" + [(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,m") + (match_operand:DI 1 "general_operand" "r,g,rM"))] + "register_operand (operands[0], DImode) + || register_operand (operands[1], DImode) + || operands[1] == const0_rtx" + "#") + +(define_split + [(set (match_operand:DI 0 "nonimmediate_operand" "") + (match_operand:DI 1 "general_operand" ""))] + "reload_completed" + [(match_dup 2)] + "operands[2] = cris_split_movdx (operands);") + +;; Side-effect patterns for move.S1 [rx=ry+rx.S2],rw +;; and move.S1 [rx=ry+i],rz +;; Then movs.S1 and movu.S1 for both modes. +;; +;; move.S1 [rx=ry+rz.S],rw avoiding when rx is ry, or rw is rx +;; FIXME: These could have anonymous mode for operand 0. + +;; QImode + +(define_insn "*mov_sideqi_biap" + [(set (match_operand:QI 0 "register_operand" "=r,r") + (mem:QI (plus:SI + (mult:SI (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "const_int_operand" "n,n")) + (match_operand:SI 3 "register_operand" "r,r")))) + (set (match_operand:SI 4 "register_operand" "=*3,r") + (plus:SI (mult:SI (match_dup 1) + (match_dup 2)) + (match_dup 3)))] + "cris_side_effect_mode_ok (MULT, operands, 4, 3, 1, 2, 0)" + "@ + # + move.%s0 [%4=%3+%1%T2],%0") + +;; HImode + +(define_insn "*mov_sidehi_biap" + [(set (match_operand:HI 0 "register_operand" "=r,r") + (mem:HI (plus:SI + (mult:SI (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "const_int_operand" "n,n")) + (match_operand:SI 3 "register_operand" "r,r")))) + (set (match_operand:SI 4 "register_operand" "=*3,r") + (plus:SI (mult:SI (match_dup 1) + (match_dup 2)) + (match_dup 3)))] + "cris_side_effect_mode_ok (MULT, operands, 4, 3, 1, 2, 0)" + "@ + # + move.%s0 [%4=%3+%1%T2],%0") + +;; SImode + +(define_insn "*mov_sidesi_biap" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (mem:SI (plus:SI + (mult:SI (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "const_int_operand" "n,n")) + (match_operand:SI 3 "register_operand" "r,r")))) + (set (match_operand:SI 4 "register_operand" "=*3,r") + (plus:SI (mult:SI (match_dup 1) + (match_dup 2)) + (match_dup 3)))] + "cris_side_effect_mode_ok (MULT, operands, 4, 3, 1, 2, 0)" + "@ + # + move.%s0 [%4=%3+%1%T2],%0") + +;; move.S1 [rx=ry+i],rz +;; avoiding move.S1 [ry=ry+i],rz +;; and move.S1 [rz=ry+i],rz +;; Note that "i" is allowed to be a register. +;; FIXME: These could have anonymous mode for operand 0. + +;; QImode + +(define_insn "*mov_sideqi" + [(set (match_operand:QI 0 "register_operand" "=r,r,r") + (mem:QI + (plus:SI (match_operand:SI 1 "cris_bdap_operand" "%r,r,r") + (match_operand:SI 2 "cris_bdap_operand" "r>Ri,r,>Ri")))) + (set (match_operand:SI 3 "register_operand" "=*1,r,r") + (plus:SI (match_dup 1) + (match_dup 2)))] + "cris_side_effect_mode_ok (PLUS, operands, 3, 1, 2, -1, 0)" + "* +{ + if (which_alternative == 0 + && (GET_CODE (operands[2]) != CONST_INT + || INTVAL (operands[2]) > 127 + || INTVAL (operands[2]) < -128 + || CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'N') + || CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J'))) + return \"#\"; + return \"move.%s0 [%3=%1%S2],%0\"; +}") + +;; HImode + +(define_insn "*mov_sidehi" + [(set (match_operand:HI 0 "register_operand" "=r,r,r") + (mem:HI + (plus:SI (match_operand:SI 1 "cris_bdap_operand" "%r,r,r") + (match_operand:SI 2 "cris_bdap_operand" "r>Ri,r,>Ri")))) + (set (match_operand:SI 3 "register_operand" "=*1,r,r") + (plus:SI (match_dup 1) + (match_dup 2)))] + "cris_side_effect_mode_ok (PLUS, operands, 3, 1, 2, -1, 0)" + "* +{ + if (which_alternative == 0 + && (GET_CODE (operands[2]) != CONST_INT + || INTVAL (operands[2]) > 127 + || INTVAL (operands[2]) < -128 + || CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'N') + || CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J'))) + return \"#\"; + return \"move.%s0 [%3=%1%S2],%0\"; +}") + +;; SImode + +(define_insn "*mov_sidesi" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (mem:SI + (plus:SI (match_operand:SI 1 "cris_bdap_operand" "%r,r,r") + (match_operand:SI 2 "cris_bdap_operand" "r>Ri,r,>Ri")))) + (set (match_operand:SI 3 "register_operand" "=*1,r,r") + (plus:SI (match_dup 1) + (match_dup 2)))] + "cris_side_effect_mode_ok (PLUS, operands, 3, 1, 2, -1, 0)" + "* +{ + if (which_alternative == 0 + && (GET_CODE (operands[2]) != CONST_INT + || INTVAL (operands[2]) > 127 + || INTVAL (operands[2]) < -128 + || CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'N') + || CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J'))) + return \"#\"; + return \"move.%s0 [%3=%1%S2],%0\"; +}") + +;; Other way around; move to memory. + +;; For all side-effect patterns, it seems to be the case that the +;; predicate isn't consulted after combine. For sake of stability, we +;; recognize and split the cases where dangerous register combinations are +;; spotted: where a register is set in the side-effect, and used in the +;; main insn. We don't handle the case where the set in the main insn +;; overlaps the set in the side-effect; that would be too big a bug to +;; paper over. We handle just the case where the set in the side-effect +;; overlaps the input operand of the main insn (i.e. just moves to memory). + +;; +;; move.s rx,[ry=rx+rw.S] +;; FIXME: These could have anonymous mode for operand 3. + +;; QImode + +(define_insn "*mov_sideqi_biap_mem" + [(set (mem:QI (plus:SI + (mult:SI (match_operand:SI 0 "register_operand" "r,r,r") + (match_operand:SI 1 "const_int_operand" "n,n,n")) + (match_operand:SI 2 "register_operand" "r,r,r"))) + (match_operand:QI 3 "register_operand" "r,r,r")) + (set (match_operand:SI 4 "register_operand" "=*2,!*3,r") + (plus:SI (mult:SI (match_dup 0) + (match_dup 1)) + (match_dup 2)))] + "cris_side_effect_mode_ok (MULT, operands, 4, 2, 0, 1, 3)" + "@ + # + # + move.%s3 %3,[%4=%2+%0%T1]") + +;; HImode + +(define_insn "*mov_sidehi_biap_mem" + [(set (mem:HI (plus:SI + (mult:SI (match_operand:SI 0 "register_operand" "r,r,r") + (match_operand:SI 1 "const_int_operand" "n,n,n")) + (match_operand:SI 2 "register_operand" "r,r,r"))) + (match_operand:HI 3 "register_operand" "r,r,r")) + (set (match_operand:SI 4 "register_operand" "=*2,!*3,r") + (plus:SI (mult:SI (match_dup 0) + (match_dup 1)) + (match_dup 2)))] + "cris_side_effect_mode_ok (MULT, operands, 4, 2, 0, 1, 3)" + "@ + # + # + move.%s3 %3,[%4=%2+%0%T1]") + +;; SImode + +(define_insn "*mov_sidesi_biap_mem" + [(set (mem:SI (plus:SI + (mult:SI (match_operand:SI 0 "register_operand" "r,r,r") + (match_operand:SI 1 "const_int_operand" "n,n,n")) + (match_operand:SI 2 "register_operand" "r,r,r"))) + (match_operand:SI 3 "register_operand" "r,r,r")) + (set (match_operand:SI 4 "register_operand" "=*2,!*3,r") + (plus:SI (mult:SI (match_dup 0) + (match_dup 1)) + (match_dup 2)))] + "cris_side_effect_mode_ok (MULT, operands, 4, 2, 0, 1, 3)" + "@ + # + # + move.%s3 %3,[%4=%2+%0%T1]") + +;; Split for the case above where the predicate isn't honored; only the +;; constraint, and we end up with the set in the side-effect gets the same +;; register as the input register. Arguably a GCC bug, but we'll spot it +;; rarely enough that we need to catch it ourselves to be safe. + +(define_split + [(parallel + [(set (mem (plus:SI + (mult:SI (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "const_int_operand" "")) + (match_operand:SI 2 "register_operand" ""))) + (match_operand 3 "register_operand" "")) + (set (match_operand:SI 4 "register_operand" "") + (plus:SI (mult:SI (match_dup 0) + (match_dup 1)) + (match_dup 2)))])] + "reload_completed && reg_overlap_mentioned_p (operands[4], operands[3])" + [(set (match_dup 5) (match_dup 3)) + (set (match_dup 4) (match_dup 2)) + (set (match_dup 4) + (plus:SI (mult:SI (match_dup 0) + (match_dup 1)) + (match_dup 4)))] + "operands[5] + = gen_rtx_MEM (GET_MODE (operands[3]), + gen_rtx_PLUS (SImode, + gen_rtx_MULT (SImode, + operands[0], operands[1]), + operands[2]));") + +;; move.s rx,[ry=rz+i] +;; FIXME: These could have anonymous mode for operand 2. + +;; QImode + +(define_insn "*mov_sideqi_mem" + [(set (mem:QI + (plus:SI (match_operand:SI 0 "cris_bdap_operand" "%r,r,r,r") + (match_operand:SI 1 "cris_bdap_operand" "r>Ri,r>Ri,r,>Ri"))) + (match_operand:QI 2 "register_operand" "r,r,r,r")) + (set (match_operand:SI 3 "register_operand" "=*0,!*2,r,r") + (plus:SI (match_dup 0) + (match_dup 1)))] + "cris_side_effect_mode_ok (PLUS, operands, 3, 0, 1, -1, 2)" + "* +{ + if (which_alternative == 0 + && (GET_CODE (operands[1]) != CONST_INT + || INTVAL (operands[1]) > 127 + || INTVAL (operands[1]) < -128 + || CONST_OK_FOR_LETTER_P (INTVAL (operands[1]), 'N') + || CONST_OK_FOR_LETTER_P (INTVAL (operands[1]), 'J'))) + return \"#\"; + if (which_alternative == 1) + return \"#\"; + return \"move.%s2 %2,[%3=%0%S1]\"; +}") + +;; HImode + +(define_insn "*mov_sidehi_mem" + [(set (mem:HI + (plus:SI (match_operand:SI 0 "cris_bdap_operand" "%r,r,r,r") + (match_operand:SI 1 "cris_bdap_operand" "r>Ri,r>Ri,r,>Ri"))) + (match_operand:HI 2 "register_operand" "r,r,r,r")) + (set (match_operand:SI 3 "register_operand" "=*0,!*2,r,r") + (plus:SI (match_dup 0) + (match_dup 1)))] + "cris_side_effect_mode_ok (PLUS, operands, 3, 0, 1, -1, 2)" + "* +{ + if (which_alternative == 0 + && (GET_CODE (operands[1]) != CONST_INT + || INTVAL (operands[1]) > 127 + || INTVAL (operands[1]) < -128 + || CONST_OK_FOR_LETTER_P (INTVAL (operands[1]), 'N') + || CONST_OK_FOR_LETTER_P (INTVAL (operands[1]), 'J'))) + return \"#\"; + if (which_alternative == 1) + return \"#\"; + return \"move.%s2 %2,[%3=%0%S1]\"; +}") + +;; SImode + +(define_insn "*mov_sidesi_mem" + [(set (mem:SI + (plus:SI (match_operand:SI 0 "cris_bdap_operand" "%r,r,r,r") + (match_operand:SI 1 "cris_bdap_operand" "r>Ri,r>Ri,r,>Ri"))) + (match_operand:SI 2 "register_operand" "r,r,r,r")) + (set (match_operand:SI 3 "register_operand" "=*0,!*2,r,r") + (plus:SI (match_dup 0) + (match_dup 1)))] + "cris_side_effect_mode_ok (PLUS, operands, 3, 0, 1, -1, 2)" + "* +{ + if (which_alternative == 0 + && (GET_CODE (operands[1]) != CONST_INT + || INTVAL (operands[1]) > 127 + || INTVAL (operands[1]) < -128 + || CONST_OK_FOR_LETTER_P (INTVAL (operands[1]), 'N') + || CONST_OK_FOR_LETTER_P (INTVAL (operands[1]), 'J'))) + return \"#\"; + if (which_alternative == 1) + return \"#\"; + return \"move.%s2 %2,[%3=%0%S1]\"; +}") + +;; Like the biap case, a split where the set in the side-effect gets the +;; same register as the input register to the main insn due to gcc not +;; always checking the predicate. + +(define_split + [(parallel + [(set (mem (plus:SI + (match_operand:SI 0 "cris_bdap_operand" "") + (match_operand:SI 1 "cris_bdap_operand" ""))) + (match_operand:SI 2 "register_operand" "")) + (set (match_operand:SI 3 "register_operand" "") + (plus:SI (match_dup 0) (match_dup 1)))])] + "reload_completed && reg_overlap_mentioned_p (operands[3], operands[2])" + [(set (match_dup 4) (match_dup 2)) + (set (match_dup 3) (match_dup 0)) + (set (match_dup 3) (plus:SI (match_dup 3) (match_dup 1)))] + "operands[4] + = gen_rtx_MEM (GET_MODE (operands[2]), + gen_rtx_PLUS (SImode, operands[0], operands[1]));") + +;; Clear memory side-effect patterns. It is hard to get to the mode if +;; the MEM was anonymous, so there will be one for each mode. + +;; clear.d [ry=rx+rw.s2] + +(define_insn "*clear_sidesi_biap" + [(set (mem:SI (plus:SI + (mult:SI (match_operand:SI 0 "register_operand" "r,r") + (match_operand:SI 1 "const_int_operand" "n,n")) + (match_operand:SI 2 "register_operand" "r,r"))) + (const_int 0)) + (set (match_operand:SI 3 "register_operand" "=*2,r") + (plus:SI (mult:SI (match_dup 0) + (match_dup 1)) + (match_dup 2)))] + "cris_side_effect_mode_ok (MULT, operands, 3, 2, 0, 1, -1)" + "@ + # + clear.d [%3=%2+%0%T1]") + +;; clear.d [ry=rz+i] + +(define_insn "*clear_sidesi" + [(set (mem:SI + (plus:SI (match_operand:SI 0 "cris_bdap_operand" "%r,r,r") + (match_operand:SI 1 "cris_bdap_operand" "r>Ri,r,>Ri"))) + (const_int 0)) + (set (match_operand:SI 2 "register_operand" "=*0,r,r") + (plus:SI (match_dup 0) + (match_dup 1)))] + "cris_side_effect_mode_ok (PLUS, operands, 2, 0, 1, -1, -1)" + "* +{ + if (which_alternative == 0 + && (GET_CODE (operands[1]) != CONST_INT + || INTVAL (operands[1]) > 127 + || INTVAL (operands[1]) < -128 + || CONST_OK_FOR_LETTER_P (INTVAL (operands[1]), 'N') + || CONST_OK_FOR_LETTER_P (INTVAL (operands[1]), 'J'))) + return \"#\"; + return \"clear.d [%2=%0%S1]\"; +}") + +;; clear.w [ry=rx+rw.s2] + +(define_insn "*clear_sidehi_biap" + [(set (mem:HI (plus:SI + (mult:SI (match_operand:SI 0 "register_operand" "r,r") + (match_operand:SI 1 "const_int_operand" "n,n")) + (match_operand:SI 2 "register_operand" "r,r"))) + (const_int 0)) + (set (match_operand:SI 3 "register_operand" "=*2,r") + (plus:SI (mult:SI (match_dup 0) + (match_dup 1)) + (match_dup 2)))] + "cris_side_effect_mode_ok (MULT, operands, 3, 2, 0, 1, -1)" + "@ + # + clear.w [%3=%2+%0%T1]") + +;; clear.w [ry=rz+i] + +(define_insn "*clear_sidehi" + [(set (mem:HI + (plus:SI (match_operand:SI 0 "cris_bdap_operand" "%r,r,r") + (match_operand:SI 1 "cris_bdap_operand" "r>Ri,r,>Ri"))) + (const_int 0)) + (set (match_operand:SI 2 "register_operand" "=*0,r,r") + (plus:SI (match_dup 0) + (match_dup 1)))] + "cris_side_effect_mode_ok (PLUS, operands, 2, 0, 1, -1, -1)" + "* +{ + if (which_alternative == 0 + && (GET_CODE (operands[1]) != CONST_INT + || INTVAL (operands[1]) > 127 + || INTVAL (operands[1]) < -128 + || CONST_OK_FOR_LETTER_P (INTVAL (operands[1]), 'N') + || CONST_OK_FOR_LETTER_P (INTVAL (operands[1]), 'J'))) + return \"#\"; + return \"clear.w [%2=%0%S1]\"; +}") + +;; clear.b [ry=rx+rw.s2] + +(define_insn "*clear_sideqi_biap" + [(set (mem:QI (plus:SI + (mult:SI (match_operand:SI 0 "register_operand" "r,r") + (match_operand:SI 1 "const_int_operand" "n,n")) + (match_operand:SI 2 "register_operand" "r,r"))) + (const_int 0)) + (set (match_operand:SI 3 "register_operand" "=*2,r") + (plus:SI (mult:SI (match_dup 0) + (match_dup 1)) + (match_dup 2)))] + "cris_side_effect_mode_ok (MULT, operands, 3, 2, 0, 1, -1)" + "@ + # + clear.b [%3=%2+%0%T1]") + +;; clear.b [ry=rz+i] + +(define_insn "*clear_sideqi" + [(set (mem:QI + (plus:SI (match_operand:SI 0 "cris_bdap_operand" "%r,r,r") + (match_operand:SI 1 "cris_bdap_operand" "r>Ri,r,>Ri"))) + (const_int 0)) + (set (match_operand:SI 2 "register_operand" "=*0,r,r") + (plus:SI (match_dup 0) + (match_dup 1)))] + "cris_side_effect_mode_ok (PLUS, operands, 2, 0, 1, -1, -1)" + "* +{ + if (which_alternative == 0 + && (GET_CODE (operands[1]) != CONST_INT + || INTVAL (operands[1]) > 127 + || INTVAL (operands[1]) < -128 + || CONST_OK_FOR_LETTER_P (INTVAL (operands[1]), 'N') + || CONST_OK_FOR_LETTER_P (INTVAL (operands[1]), 'J'))) + return \"#\"; + return \"clear.b [%2=%0%S1]\"; +}") + +;; To appease test-case gcc.c-torture/execute/920501-2.c (and others) at +;; -O0, we need a movdi as a temporary measure. Here's how things fail: +;; A cmpdi RTX needs reloading (global): +;; (insn 185 326 186 (set (cc0) +;; (compare (mem/f:DI (reg/v:SI 22) 0) +;; (const_int 1 [0x1]))) 4 {cmpdi} (nil) +;; (nil)) +;; Now, reg 22 is reloaded for input address, and the mem is also moved +;; out of the instruction (into a register), since one of the operands +;; must be a register. Reg 22 is reloaded (into reg 10), and the mem is +;; moved out and synthesized in SImode parts (reg 9, reg 10 - should be ok +;; wrt. overlap). The bad things happen with the synthesis in +;; emit_move_insn_1; the location where to substitute reg 10 is lost into +;; two new RTX:es, both still having reg 22. Later on, the left-over reg +;; 22 is recognized to have an equivalent in memory which is substituted +;; straight in, and we end up with an unrecognizable insn: +;; (insn 325 324 326 (set (reg:SI 9 r9) +;; (mem/f:SI (mem:SI (plus:SI (reg:SI 8 r8) +;; (const_int -84 [0xffffffac])) 0) 0)) -1 (nil) +;; (nil)) +;; which is the first part of the reloaded synthesized "movdi". +;; The right thing would be to add equivalent replacement locations for +;; insn with pseudos that need more reloading. The question is where. + +;; Normal move patterns from SI on. + +(define_expand "movsi" + [(set + (match_operand:SI 0 "nonimmediate_operand" "") + (match_operand:SI 1 "cris_general_operand_or_symbol" ""))] + "" + " +{ + /* If the output goes to a MEM, make sure we have zero or a register as + input. */ + if (GET_CODE (operands[0]) == MEM + && ! REG_S_P (operands[1]) + && operands[1] != const0_rtx + && ! no_new_pseudos) + operands[1] = force_reg (SImode, operands[1]); + + /* If we're generating PIC and have an incoming symbol, validize it to a + general operand or something that will match a special pattern. + + FIXME: Do we *have* to recognize anything that would normally be a + valid symbol? Can we exclude global PIC addresses with an added + offset? */ + if (flag_pic + && CONSTANT_ADDRESS_P (operands[1]) + && cris_symbol (operands[1])) + { + /* We must have a register as destination for what we're about to + do, and for the patterns we generate. */ + if (! REG_S_P (operands[0])) + { + if (no_new_pseudos) + abort (); + operands[1] = force_reg (SImode, operands[1]); + } + else + { + /* Mark a needed PIC setup for a LABEL_REF:s coming in here: + they are so rare not-being-branch-targets that we don't mark + a function as needing PIC setup just because we have + inspected LABEL_REF:s as operands. It is only in + __builtin_setjmp and such that we can get a LABEL_REF + assigned to a register. */ + if (GET_CODE (operands[1]) == LABEL_REF) + current_function_uses_pic_offset_table = 1; + + /* We don't have to do anything for global PIC operands; they + look just like ``[rPIC+sym]''. */ + if (! cris_got_symbol (operands[1]) + /* We don't do anything for local PIC operands; we match + that with a special alternative. */ + && ! cris_gotless_symbol (operands[1])) + { + /* We get here when we have to change something that would + be recognizable if it wasn't PIC. A ``sym'' is ok for + PIC symbols both with and without a GOT entry. And ``sym + + offset'' is ok for local symbols, so the only thing it + could be, is a global symbol with an offset. Check and + abort if not. */ + rtx sym = get_related_value (operands[1]); + HOST_WIDE_INT offs = get_integer_term (operands[1]); + + if (sym == NULL_RTX || offs == 0) + abort (); + emit_move_insn (operands[0], sym); + if (expand_binop (SImode, add_optab, operands[0], + GEN_INT (offs), operands[0], 0, + OPTAB_LIB_WIDEN) != operands[0]) + abort (); + DONE; + } + } + } +}") + +(define_insn "*movsi_internal" + [(set + (match_operand:SI 0 "nonimmediate_operand" "=r,r,r,Q>,r,Q>,g,r,r,r,g") + (match_operand:SI 1 + ;; FIXME: We want to put S last, but apparently g matches S. + ;; It's a bug: an S is not a general_operand and shouldn't match g. + "cris_general_operand_or_gotless_symbol" "r,Q>,M,M,I,r,M,n,!S,g,r"))] + "" + "* +{ + /* Better to have c-switch here; it is worth it to optimize the size of + move insns. The alternative would be to try to find more constraint + letters. FIXME: Check again. It seems this could shrink a bit. */ + switch (which_alternative) + { + case 0: + case 1: + case 5: + case 9: + case 10: + return \"move.d %1,%0\"; + + case 2: + case 3: + case 6: + return \"clear.d %0\"; + + /* Constants -32..31 except 0. */ + case 4: + return \"moveq %1,%0\"; + + /* We can win a little on constants -32768..-33, 32..65535. */ + case 7: + if (INTVAL (operands[1]) > 0 && INTVAL (operands[1]) < 65536) + { + if (INTVAL (operands[1]) < 256) + return \"movu.b %1,%0\"; + return \"movu.w %1,%0\"; + } + else if (INTVAL (operands[1]) >= -32768 && INTVAL (operands[1]) < 32768) + { + if (INTVAL (operands[1]) >= -128 && INTVAL (operands[1]) < 128) + return \"movs.b %1,%0\"; + return \"movs.w %1,%0\"; + } + return \"move.d %1,%0\"; + + case 8: + /* FIXME: Try and split this into pieces GCC makes better code of, + than this multi-insn pattern. Synopsis: wrap the GOT-relative + symbol into an unspec, and when PIC, recognize the unspec + everywhere a symbol is normally recognized. (The PIC register + should be recognized by GCC as pic_offset_table_rtx when needed + and similar for PC.) Each component can then be optimized with + the rest of the code; it should be possible to have a constant + term added on an unspec. Don't forget to add a REG_EQUAL (or + is it REG_EQUIV) note to the destination. It might not be + worth it. Measure. + + Note that the 'v' modifier makes PLT references be output as + sym:PLT rather than [rPIC+sym:GOTPLT]. */ + return \"move.d %v1,%0\;add.d %P1,%0\"; + + default: + return \"BOGUS: %1 to %0\"; + } +}" + [(set_attr "slottable" "yes,yes,yes,yes,yes,yes,no,no,no,no,no")]) + +;; Extend operations with side-effect from mem to register, using +;; MOVS/MOVU. These are from mem to register only. +;; +;; [rx=ry+rz.S] +;; +;; QImode to HImode +;; +;; FIXME: Can we omit extend to HImode, since GCC should truncate for +;; HImode by itself? Perhaps use only anonymous modes? + +(define_insn "*ext_sideqihi_biap" + [(set (match_operand:HI 0 "register_operand" "=r,r") + (match_operator:HI + 5 "cris_extend_operator" + [(mem:QI (plus:SI + (mult:SI (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "const_int_operand" "n,n")) + (match_operand:SI 3 "register_operand" "r,r")))])) + (set (match_operand:SI 4 "register_operand" "=*3,r") + (plus:SI (mult:SI (match_dup 1) + (match_dup 2)) + (match_dup 3)))] + "cris_side_effect_mode_ok (MULT, operands, 4, 3, 1, 2, 0)" + "@ + # + mov%e5.%m5 [%4=%3+%1%T2],%0") + +;; QImode to SImode + +(define_insn "*ext_sideqisi_biap" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (match_operator:SI + 5 "cris_extend_operator" + [(mem:QI (plus:SI + (mult:SI (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "const_int_operand" "n,n")) + (match_operand:SI 3 "register_operand" "r,r")))])) + (set (match_operand:SI 4 "register_operand" "=*3,r") + (plus:SI (mult:SI (match_dup 1) + (match_dup 2)) + (match_dup 3)))] + "cris_side_effect_mode_ok (MULT, operands, 4, 3, 1, 2, 0)" + "@ + # + mov%e5.%m5 [%4=%3+%1%T2],%0") + +;; HImode to SImode + +(define_insn "*ext_sidehisi_biap" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (match_operator:SI + 5 "cris_extend_operator" + [(mem:HI (plus:SI + (mult:SI (match_operand:SI 1 "register_operand" "r,r") + (match_operand:SI 2 "const_int_operand" "n,n")) + (match_operand:SI 3 "register_operand" "r,r")))])) + (set (match_operand:SI 4 "register_operand" "=*3,r") + (plus:SI (mult:SI (match_dup 1) + (match_dup 2)) + (match_dup 3)))] + "cris_side_effect_mode_ok (MULT, operands, 4, 3, 1, 2, 0)" + "@ + # + mov%e5.%m5 [%4=%3+%1%T2],%0") + +;; Same but [rx=ry+i] + +;; QImode to HImode + +(define_insn "*ext_sideqihi" + [(set (match_operand:HI 0 "register_operand" "=r,r,r") + (match_operator:HI + 4 "cris_extend_operator" + [(mem:QI (plus:SI + (match_operand:SI 1 "cris_bdap_operand" "%r,r,r") + (match_operand:SI 2 "cris_bdap_operand" "r>Ri,r,>Ri")))])) + (set (match_operand:SI 3 "register_operand" "=*1,r,r") + (plus:SI (match_dup 1) + (match_dup 2)))] + "cris_side_effect_mode_ok (PLUS, operands, 3, 1, 2, -1, 0)" + "* +{ + if (which_alternative == 0 + && (GET_CODE (operands[2]) != CONST_INT + || INTVAL (operands[2]) > 127 + || INTVAL (operands[2]) < -128 + || CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'N') + || CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J'))) + return \"#\"; + return \"mov%e4.%m4 [%3=%1%S2],%0\"; +}") + +;; QImode to SImode + +(define_insn "*ext_sideqisi" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (match_operator:SI + 4 "cris_extend_operator" + [(mem:QI (plus:SI + (match_operand:SI 1 "cris_bdap_operand" "%r,r,r") + (match_operand:SI 2 "cris_bdap_operand" "r>Ri,r,>Ri")))])) + (set (match_operand:SI 3 "register_operand" "=*1,r,r") + (plus:SI (match_dup 1) + (match_dup 2)))] + "cris_side_effect_mode_ok (PLUS, operands, 3, 1, 2, -1, 0)" + "* +{ + if (which_alternative == 0 + && (GET_CODE (operands[2]) != CONST_INT + || INTVAL (operands[2]) > 127 + || INTVAL (operands[2]) < -128 + || CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'N') + || CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J'))) + return \"#\"; + return \"mov%e4.%m4 [%3=%1%S2],%0\"; +}") + +;; HImode to SImode + +(define_insn "*ext_sidehisi" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (match_operator:SI + 4 "cris_extend_operator" + [(mem:HI (plus:SI + (match_operand:SI 1 "cris_bdap_operand" "%r,r,r") + (match_operand:SI 2 "cris_bdap_operand" "r>Ri,r,>Ri")))])) + (set (match_operand:SI 3 "register_operand" "=*1,r,r") + (plus:SI (match_dup 1) + (match_dup 2)))] + "cris_side_effect_mode_ok (PLUS, operands, 3, 1, 2, -1, 0)" + "* +{ + if (which_alternative == 0 + && (GET_CODE (operands[2]) != CONST_INT + || INTVAL (operands[2]) > 127 + || INTVAL (operands[2]) < -128 + || CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'N') + || CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J'))) + return \"#\"; + return \"mov%e4.%m4 [%3=%1%S2],%0\"; +}") + +;; FIXME: See movsi. + +(define_insn "movhi" + [(set + (match_operand:HI 0 "nonimmediate_operand" "=r,r,r,Q>,r,Q>,r,r,r,g,g,r") + (match_operand:HI 1 "general_operand" "r,Q>,M,M,I,r,L,O,n,M,r,g"))] + "" + "* +{ + switch (which_alternative) + { + case 0: + case 1: + case 5: + case 10: + case 11: + return \"move.w %1,%0\"; + case 2: + case 3: + case 9: + return \"clear.w %0\"; + case 4: + return \"moveq %1,%0\"; + case 6: + case 8: + if (INTVAL (operands[1]) < 256 && INTVAL (operands[1]) >= -128) + { + if (INTVAL (operands[1]) > 0) + return \"movu.b %1,%0\"; + return \"movs.b %1,%0\"; + } + return \"move.w %1,%0\"; + case 7: + return \"movEq %b1,%0\"; + default: + return \"BOGUS: %1 to %0\"; + } +}" + [(set_attr "slottable" "yes,yes,yes,yes,yes,yes,no,yes,no,no,no,no") + (set (attr "cc") + (if_then_else (eq_attr "alternative" "7") + (const_string "clobber") + (const_string "normal")))]) + +(define_insn "movstricthi" + [(set + (strict_low_part + (match_operand:HI 0 "nonimmediate_operand" "+r,r,r,Q>,Q>,g,r,g")) + (match_operand:HI 1 "general_operand" "r,Q>,M,M,r,M,g,r"))] + "" + "@ + move.w %1,%0 + move.w %1,%0 + clear.w %0 + clear.w %0 + move.w %1,%0 + clear.w %0 + move.w %1,%0 + move.w %1,%0" + [(set_attr "slottable" "yes,yes,yes,yes,yes,no,no,no")]) + +(define_insn "movqi" + [(set (match_operand:QI 0 "nonimmediate_operand" "=r,Q>,r,r,Q>,r,g,g,r,r") + (match_operand:QI 1 "general_operand" "r,r,Q>,M,M,I,M,r,O,g"))] + "" + "@ + move.b %1,%0 + move.b %1,%0 + move.b %1,%0 + clear.b %0 + clear.b %0 + moveq %1,%0 + clear.b %0 + move.b %1,%0 + moveq %b1,%0 + move.b %1,%0" + [(set_attr "slottable" "yes,yes,yes,yes,yes,yes,no,no,yes,no") + (set (attr "cc") + (if_then_else (eq_attr "alternative" "8") + (const_string "clobber") + (const_string "normal")))]) + +(define_insn "movstrictqi" + [(set (strict_low_part + (match_operand:QI 0 "nonimmediate_operand" "+r,Q>,r,r,Q>,g,g,r")) + (match_operand:QI 1 "general_operand" "r,r,Q>,M,M,M,r,g"))] + "" + "@ + move.b %1,%0 + move.b %1,%0 + move.b %1,%0 + clear.b %0 + clear.b %0 + clear.b %0 + move.b %1,%0 + move.b %1,%0" + [(set_attr "slottable" "yes,yes,yes,yes,yes,no,no,no")]) + +;; The valid "quick" bit-patterns are, except for 0.0, denormalized +;; values REALLY close to 0, and some NaN:s (I think; their exponent is +;; all ones); the worthwhile one is "0.0". +;; It will use clear, so we know ALL types of immediate 0 never change cc. + +(define_insn "movsf" + [(set (match_operand:SF 0 "nonimmediate_operand" "=r,Q>,r,r,Q>,g,g,r") + (match_operand:SF 1 "general_operand" "r,r,Q>,G,G,G,r,g"))] + "" + "@ + move.d %1,%0 + move.d %1,%0 + move.d %1,%0 + clear.d %0 + clear.d %0 + clear.d %0 + move.d %1,%0 + move.d %1,%0" + [(set_attr "slottable" "yes,yes,yes,yes,yes,no,no,no")]) + + +;; Sign- and zero-extend insns with standard names. +;; Those for integer source operand are ordered with the widest source +;; type first. + +;; Sign-extend. + +(define_insn "extendsidi2" + [(set (match_operand:DI 0 "register_operand" "=r") + (sign_extend:DI (match_operand:SI 1 "general_operand" "g")))] + "" + "move.d %1,%M0\;smi %H0\;neg.d %H0,%H0") + +(define_insn "extendhidi2" + [(set (match_operand:DI 0 "register_operand" "=r") + (sign_extend:DI (match_operand:HI 1 "general_operand" "g")))] + "" + "movs.w %1,%M0\;smi %H0\;neg.d %H0,%H0") + +(define_insn "extendhisi2" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (sign_extend:SI (match_operand:HI 1 "general_operand" "r,Q>,g")))] + "" + "movs.w %1,%0" + [(set_attr "slottable" "yes,yes,no")]) + +(define_insn "extendqidi2" + [(set (match_operand:DI 0 "register_operand" "=r") + (sign_extend:DI (match_operand:QI 1 "general_operand" "g")))] + "" + "movs.b %1,%M0\;smi %H0\;neg.d %H0,%H0") + +(define_insn "extendqisi2" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (sign_extend:SI (match_operand:QI 1 "general_operand" "r,Q>,g")))] + "" + "movs.b %1,%0" + [(set_attr "slottable" "yes,yes,no")]) + +;; To do a byte->word exension, extend to dword, exept that the top half +;; of the register will be clobbered. FIXME: Perhaps this is not needed. + +(define_insn "extendqihi2" + [(set (match_operand:HI 0 "register_operand" "=r,r,r") + (sign_extend:HI (match_operand:QI 1 "general_operand" "r,Q>,g")))] + "" + "movs.b %1,%0" + [(set_attr "slottable" "yes,yes,no")]) + + +;; Zero-extend. The DImode ones are synthesized by gcc, so we don't +;; specify them here. + +(define_insn "zero_extendhisi2" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (zero_extend:SI + (match_operand:HI 1 "nonimmediate_operand" "r,Q>,m")))] + "" + "movu.w %1,%0" + [(set_attr "slottable" "yes,yes,no")]) + +(define_insn "zero_extendqisi2" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (zero_extend:SI + (match_operand:QI 1 "nonimmediate_operand" "r,Q>,m")))] + "" + "movu.b %1,%0" + [(set_attr "slottable" "yes,yes,no")]) + +;; Same comment as sign-extend QImode to HImode above applies. + +(define_insn "zero_extendqihi2" + [(set (match_operand:HI 0 "register_operand" "=r,r,r") + (zero_extend:HI + (match_operand:QI 1 "nonimmediate_operand" "r,Q>,m")))] + "" + "movu.b %1,%0" + [(set_attr "slottable" "yes,yes,no")]) + +;; All kinds of arithmetic and logical instructions. +;; +;; First, anonymous patterns to match addressing modes with +;; side-effects. +;; +;; op.S [rx=ry+I],rz; (add, sub, or, and, bound). +;; +;; [rx=ry+rz.S] +;; FIXME: These could have anonymous mode for operand 0. + +;; QImode + +(define_insn "*op_sideqi_biap" + [(set (match_operand:QI 0 "register_operand" "=r,r") + (match_operator:QI + 6 "cris_orthogonal_operator" + [(match_operand:QI 1 "register_operand" "0,0") + (mem:QI (plus:SI + (mult:SI (match_operand:SI 2 "register_operand" "r,r") + (match_operand:SI 3 "const_int_operand" "n,n")) + (match_operand:SI 4 "register_operand" "r,r")))])) + (set (match_operand:SI 5 "register_operand" "=*4,r") + (plus:SI (mult:SI (match_dup 2) + (match_dup 3)) + (match_dup 4)))] + "cris_side_effect_mode_ok (MULT, operands, 5, 4, 2, 3, 0)" + "@ + # + %x6.%s0 [%5=%4+%2%T3],%0") + +;; HImode + +(define_insn "*op_sidehi_biap" + [(set (match_operand:HI 0 "register_operand" "=r,r") + (match_operator:HI + 6 "cris_orthogonal_operator" + [(match_operand:HI 1 "register_operand" "0,0") + (mem:HI (plus:SI + (mult:SI (match_operand:SI 2 "register_operand" "r,r") + (match_operand:SI 3 "const_int_operand" "n,n")) + (match_operand:SI 4 "register_operand" "r,r")))])) + (set (match_operand:SI 5 "register_operand" "=*4,r") + (plus:SI (mult:SI (match_dup 2) + (match_dup 3)) + (match_dup 4)))] + "cris_side_effect_mode_ok (MULT, operands, 5, 4, 2, 3, 0)" + "@ + # + %x6.%s0 [%5=%4+%2%T3],%0") + +;; SImode + +(define_insn "*op_sidesi_biap" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (match_operator:SI + 6 "cris_orthogonal_operator" + [(match_operand:SI 1 "register_operand" "0,0") + (mem:SI (plus:SI + (mult:SI (match_operand:SI 2 "register_operand" "r,r") + (match_operand:SI 3 "const_int_operand" "n,n")) + (match_operand:SI 4 "register_operand" "r,r")))])) + (set (match_operand:SI 5 "register_operand" "=*4,r") + (plus:SI (mult:SI (match_dup 2) + (match_dup 3)) + (match_dup 4)))] + "cris_side_effect_mode_ok (MULT, operands, 5, 4, 2, 3, 0)" + "@ + # + %x6.%s0 [%5=%4+%2%T3],%0") + +;; [rx=ry+i] ([%4=%2+%3]) +;; FIXME: These could have anonymous mode for operand 0. + +;; QImode + +(define_insn "*op_sideqi" + [(set (match_operand:QI 0 "register_operand" "=r,r,r") + (match_operator:QI + 5 "cris_orthogonal_operator" + [(match_operand:QI 1 "register_operand" "0,0,0") + (mem:QI (plus:SI + (match_operand:SI 2 "cris_bdap_operand" "%r,r,r") + (match_operand:SI 3 "cris_bdap_operand" "r>Ri,r,>Ri")))])) + (set (match_operand:SI 4 "register_operand" "=*2,r,r") + (plus:SI (match_dup 2) + (match_dup 3)))] + "cris_side_effect_mode_ok (PLUS, operands, 4, 2, 3, -1, 0)" + "* +{ + if (which_alternative == 0 + && (GET_CODE (operands[3]) != CONST_INT + || INTVAL (operands[3]) > 127 + || INTVAL (operands[3]) < -128 + || CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'N') + || CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'J'))) + return \"#\"; + return \"%x5.%s0 [%4=%2%S3],%0\"; +}") + +;; HImode + +(define_insn "*op_sidehi" + [(set (match_operand:HI 0 "register_operand" "=r,r,r") + (match_operator:HI + 5 "cris_orthogonal_operator" + [(match_operand:HI 1 "register_operand" "0,0,0") + (mem:HI (plus:SI + (match_operand:SI 2 "cris_bdap_operand" "%r,r,r") + (match_operand:SI 3 "cris_bdap_operand" "r>Ri,r,>Ri")))])) + (set (match_operand:SI 4 "register_operand" "=*2,r,r") + (plus:SI (match_dup 2) + (match_dup 3)))] + "cris_side_effect_mode_ok (PLUS, operands, 4, 2, 3, -1, 0)" + "* +{ + if (which_alternative == 0 + && (GET_CODE (operands[3]) != CONST_INT + || INTVAL (operands[3]) > 127 + || INTVAL (operands[3]) < -128 + || CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'N') + || CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'J'))) + return \"#\"; + return \"%x5.%s0 [%4=%2%S3],%0\"; +}") + +;; SImode + +(define_insn "*op_sidesi" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (match_operator:SI + 5 "cris_orthogonal_operator" + [(match_operand:SI 1 "register_operand" "0,0,0") + (mem:SI (plus:SI + (match_operand:SI 2 "cris_bdap_operand" "%r,r,r") + (match_operand:SI 3 "cris_bdap_operand" "r>Ri,r,>Ri")))])) + (set (match_operand:SI 4 "register_operand" "=*2,r,r") + (plus:SI (match_dup 2) + (match_dup 3)))] + "cris_side_effect_mode_ok (PLUS, operands, 4, 2, 3, -1, 0)" + "* +{ + if (which_alternative == 0 + && (GET_CODE (operands[3]) != CONST_INT + || INTVAL (operands[3]) > 127 + || INTVAL (operands[3]) < -128 + || CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'N') + || CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'J'))) + return \"#\"; + return \"%x5.%s0 [%4=%2%S3],%0\"; +}") + +;; To match all cases for commutative operations we may have to have the +;; following pattern for add, or & and. I do not know really, but it does +;; not break anything. +;; +;; FIXME: This really ought to be checked. +;; +;; op.S [rx=ry+I],rz; +;; +;; [rx=ry+rz.S] +;; FIXME: These could have anonymous mode for operand 0. + +;; QImode + +(define_insn "*op_swap_sideqi_biap" + [(set (match_operand:QI 0 "register_operand" "=r,r") + (match_operator:QI + 6 "cris_commutative_orth_op" + [(mem:QI (plus:SI + (mult:SI (match_operand:SI 2 "register_operand" "r,r") + (match_operand:SI 3 "const_int_operand" "n,n")) + (match_operand:SI 4 "register_operand" "r,r"))) + (match_operand:QI 1 "register_operand" "0,0")])) + (set (match_operand:SI 5 "register_operand" "=*4,r") + (plus:SI (mult:SI (match_dup 2) + (match_dup 3)) + (match_dup 4)))] + "cris_side_effect_mode_ok (MULT, operands, 5, 4, 2, 3, 0)" + "@ + # + %x6.%s0 [%5=%4+%2%T3],%0") + +;; HImode + +(define_insn "*op_swap_sidehi_biap" + [(set (match_operand:HI 0 "register_operand" "=r,r") + (match_operator:HI + 6 "cris_commutative_orth_op" + [(mem:HI (plus:SI + (mult:SI (match_operand:SI 2 "register_operand" "r,r") + (match_operand:SI 3 "const_int_operand" "n,n")) + (match_operand:SI 4 "register_operand" "r,r"))) + (match_operand:HI 1 "register_operand" "0,0")])) + (set (match_operand:SI 5 "register_operand" "=*4,r") + (plus:SI (mult:SI (match_dup 2) + (match_dup 3)) + (match_dup 4)))] + "cris_side_effect_mode_ok (MULT, operands, 5, 4, 2, 3, 0)" + "@ + # + %x6.%s0 [%5=%4+%2%T3],%0") + +;; SImode + +(define_insn "*op_swap_sidesi_biap" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (match_operator:SI + 6 "cris_commutative_orth_op" + [(mem:SI (plus:SI + (mult:SI (match_operand:SI 2 "register_operand" "r,r") + (match_operand:SI 3 "const_int_operand" "n,n")) + (match_operand:SI 4 "register_operand" "r,r"))) + (match_operand:SI 1 "register_operand" "0,0")])) + (set (match_operand:SI 5 "register_operand" "=*4,r") + (plus:SI (mult:SI (match_dup 2) + (match_dup 3)) + (match_dup 4)))] + "cris_side_effect_mode_ok (MULT, operands, 5, 4, 2, 3, 0)" + "@ + # + %x6.%s0 [%5=%4+%2%T3],%0") + +;; [rx=ry+i] ([%4=%2+%3]) +;; FIXME: These could have anonymous mode for operand 0. + +;; QImode + +(define_insn "*op_swap_sideqi" + [(set (match_operand:QI 0 "register_operand" "=r,r,r") + (match_operator:QI + 5 "cris_commutative_orth_op" + [(mem:QI + (plus:SI (match_operand:SI 2 "cris_bdap_operand" "%r,r,r") + (match_operand:SI 3 "cris_bdap_operand" "r>Ri,r,>Ri"))) + (match_operand:QI 1 "register_operand" "0,0,0")])) + (set (match_operand:SI 4 "register_operand" "=*2,r,r") + (plus:SI (match_dup 2) + (match_dup 3)))] + "cris_side_effect_mode_ok (PLUS, operands, 4, 2, 3, -1, 0)" + "* +{ + if (which_alternative == 0 + && (GET_CODE (operands[3]) != CONST_INT + || INTVAL (operands[3]) > 127 + || INTVAL (operands[3]) < -128 + || CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'N') + || CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'J'))) + return \"#\"; + return \"%x5.%s0 [%4=%2%S3],%0\"; +}") + +;; HImode + +(define_insn "*op_swap_sidehi" + [(set (match_operand:HI 0 "register_operand" "=r,r,r") + (match_operator:HI + 5 "cris_commutative_orth_op" + [(mem:HI + (plus:SI (match_operand:SI 2 "cris_bdap_operand" "%r,r,r") + (match_operand:SI 3 "cris_bdap_operand" "r>Ri,r,>Ri"))) + (match_operand:HI 1 "register_operand" "0,0,0")])) + (set (match_operand:SI 4 "register_operand" "=*2,r,r") + (plus:SI (match_dup 2) + (match_dup 3)))] + "cris_side_effect_mode_ok (PLUS, operands, 4, 2, 3, -1, 0)" + "* +{ + if (which_alternative == 0 + && (GET_CODE (operands[3]) != CONST_INT + || INTVAL (operands[3]) > 127 + || INTVAL (operands[3]) < -128 + || CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'N') + || CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'J'))) + return \"#\"; + return \"%x5.%s0 [%4=%2%S3],%0\"; +}") + +;; SImode + +(define_insn "*op_swap_sidesi" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (match_operator:SI + 5 "cris_commutative_orth_op" + [(mem:SI + (plus:SI (match_operand:SI 2 "cris_bdap_operand" "%r,r,r") + (match_operand:SI 3 "cris_bdap_operand" "r>Ri,r,>Ri"))) + (match_operand:SI 1 "register_operand" "0,0,0")])) + (set (match_operand:SI 4 "register_operand" "=*2,r,r") + (plus:SI (match_dup 2) + (match_dup 3)))] + "cris_side_effect_mode_ok (PLUS, operands, 4, 2, 3, -1, 0)" + "* +{ + if (which_alternative == 0 + && (GET_CODE (operands[3]) != CONST_INT + || INTVAL (operands[3]) > 127 + || INTVAL (operands[3]) < -128 + || CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'N') + || CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'J'))) + return \"#\"; + return \"%x5.%s0 [%4=%2%S3],%0\"; +}") + +;; Add operations, standard names. + +;; Note that for the 'P' constraint, the high part can be -1 or 0. We +;; output the insn through the 'A' output modifier as "adds.w" and "addq", +;; respectively. +(define_insn "adddi3" + [(set (match_operand:DI 0 "register_operand" "=r,r,r,&r,&r") + (plus:DI (match_operand:DI 1 "register_operand" "%0,0,0,0,r") + (match_operand:DI 2 "general_operand" "J,N,P,g,!To")))] + "" + "@ + addq %2,%M0\;ax\;addq 0,%H0 + subq %n2,%M0\;ax\;subq 0,%H0 + add%e2.%z2 %2,%M0\;ax\;%A2 %H2,%H0 + add.d %M2,%M0\;ax\;add.d %H2,%H0 + add.d %M2,%M1,%M0\;ax\;add.d %H2,%H1,%H0") + +(define_insn "addsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r,r,r,r") + (plus:SI + (match_operand:SI 1 "register_operand" "%0,0,0,0,0,0,r,r") + (match_operand:SI 2 "general_operand" "r,Q>,J,N,n,g,!To,0")))] + +;; The last constraint is due to that after reload, the '%' is not +;; honored, and canonicalization doesn't care about keeping the same +;; register as in destination. This will happen after insn splitting. +;; gcc <= 2.7.2. FIXME: Check for gcc-2.9x + + "" + "* +{ + switch (which_alternative) + { + case 0: + case 1: + return \"add.d %2,%0\"; + case 2: + return \"addq %2,%0\"; + case 3: + return \"subq %n2,%0\"; + case 4: + /* 'Known value', but not in -63..63. + Check if addu/subu may be used. */ + if (INTVAL (operands[2]) > 0) + { + if (INTVAL (operands[2]) < 256) + return \"addu.b %2,%0\"; + if (INTVAL (operands[2]) < 65536) + return \"addu.w %2,%0\"; + } + else + { + if (INTVAL (operands[2]) >= -255) + return \"subu.b %n2,%0\"; + if (INTVAL (operands[2]) >= -65535) + return \"subu.w %n2,%0\"; + } + return \"add.d %2,%0\"; + case 6: + return \"add.d %2,%1,%0\"; + case 5: + return \"add.d %2,%0\"; + case 7: + return \"add.d %1,%0\"; + default: + return \"BOGUS addsi %2+%1 to %0\"; + } +}" + [(set_attr "slottable" "yes,yes,yes,yes,no,no,no,yes")]) + +(define_insn "addhi3" + [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r,r") + (plus:HI (match_operand:HI 1 "register_operand" "%0,0,0,0,0,r") + (match_operand:HI 2 "general_operand" "r,Q>,J,N,g,!To")))] + "" + "@ + add.w %2,%0 + add.w %2,%0 + addq %2,%0 + subq %n2,%0 + add.w %2,%0 + add.w %2,%1,%0" + [(set_attr "slottable" "yes,yes,yes,yes,no,no") + (set_attr "cc" "normal,normal,clobber,clobber,normal,normal")]) + +(define_insn "addqi3" + [(set (match_operand:QI 0 "register_operand" "=r,r,r,r,r,r,r") + (plus:QI (match_operand:QI 1 "register_operand" "%0,0,0,0,0,0,r") + (match_operand:QI 2 "general_operand" "r,Q>,J,N,O,g,!To")))] + "" + "@ + add.b %2,%0 + add.b %2,%0 + addq %2,%0 + subq %n2,%0 + subQ -%b2,%0 + add.b %2,%0 + add.b %2,%1,%0" + [(set_attr "slottable" "yes,yes,yes,yes,yes,no,no") + (set_attr "cc" "normal,normal,clobber,clobber,clobber,normal,normal")]) + +;; Subtract. +;; +;; Note that because of insn canonicalization these will *seldom* but +;; rarely be used with a known constant as an operand. + +;; Note that for the 'P' constraint, the high part can be -1 or 0. We +;; output the insn through the 'D' output modifier as "subs.w" and "subq", +;; respectively. +(define_insn "subdi3" + [(set (match_operand:DI 0 "register_operand" "=r,r,r,&r,&r") + (minus:DI (match_operand:DI 1 "register_operand" "0,0,0,0,r") + (match_operand:DI 2 "general_operand" "J,N,P,g,!To")))] + "" + "@ + subq %2,%M0\;ax\;subq 0,%H0 + addq %n2,%M0\;ax\;addq 0,%H0 + sub%e2.%z2 %2,%M0\;ax\;%D2 %H2,%H0 + sub.d %M2,%M0\;ax\;sub.d %H2,%H0 + sub.d %M2,%M1,%M0\;ax\;sub.d %H2,%H1,%H0") + +(define_insn "subsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r,r,r,r") + (minus:SI + (match_operand:SI 1 "register_operand" "0,0,0,0,0,0,0,r") + (match_operand:SI 2 "general_operand" "r,Q>,J,N,P,n,g,!To")))] + "" + +;; This does not do the optimal: "addu.w 65535,r0" when %2 is negative. +;; But then again, %2 should not be negative. + + "@ + sub.d %2,%0 + sub.d %2,%0 + subq %2,%0 + addq %n2,%0 + sub%e2.%z2 %2,%0 + sub.d %2,%0 + sub.d %2,%0 + sub.d %2,%1,%0" + [(set_attr "slottable" "yes,yes,yes,yes,no,no,no,no")]) + +(define_insn "subhi3" + [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r,r") + (minus:HI (match_operand:HI 1 "register_operand" "0,0,0,0,0,r") + (match_operand:HI 2 "general_operand" "r,Q>,J,N,g,!To")))] + "" + "@ + sub.w %2,%0 + sub.w %2,%0 + subq %2,%0 + addq %n2,%0 + sub.w %2,%0 + sub.w %2,%1,%0" + [(set_attr "slottable" "yes,yes,yes,yes,no,no") + (set_attr "cc" "normal,normal,clobber,clobber,normal,normal")]) + +(define_insn "subqi3" + [(set (match_operand:QI 0 "register_operand" "=r,r,r,r,r,r") + (minus:QI (match_operand:QI 1 "register_operand" "0,0,0,0,0,r") + (match_operand:QI 2 "general_operand" "r,Q>,J,N,g,!To")))] + "" + "@ + sub.b %2,%0 + sub.b %2,%0 + subq %2,%0 + addq %2,%0 + sub.b %2,%0 + sub.b %2,%1,%0" + [(set_attr "slottable" "yes,yes,yes,yes,no,no") + (set_attr "cc" "normal,normal,clobber,clobber,normal,normal")]) + +;; CRIS has some add/sub-with-sign/zero-extend instructions. +;; Although these perform sign/zero-extension to SImode, they are +;; equally applicable for the HImode case. +;; FIXME: Check; GCC should handle the widening. +;; Note that these must be located after the normal add/sub patterns, +;; so not to get constants into any less specific operands. +;; +;; Extend with add/sub and side-effect. +;; +;; ADDS/SUBS/ADDU/SUBU and BOUND, which needs a check for zero_extend +;; +;; adds/subs/addu/subu bound [rx=ry+rz.S] +;; FIXME: These could have anonymous mode for operand 0. + +;; QImode to HImode +;; FIXME: GCC should widen. + +(define_insn "*extopqihi_side_biap" + [(set (match_operand:HI 0 "register_operand" "=r,r") + (match_operator:HI + 6 "cris_operand_extend_operator" + [(match_operand:HI 1 "register_operand" "0,0") + (match_operator:HI + 7 "cris_extend_operator" + [(mem:QI (plus:SI + (mult:SI (match_operand:SI 2 "register_operand" "r,r") + (match_operand:SI 3 "const_int_operand" "n,n")) + (match_operand:SI 4 "register_operand" "r,r")))])])) + (set (match_operand:SI 5 "register_operand" "=*4,r") + (plus:SI (mult:SI (match_dup 2) + (match_dup 3)) + (match_dup 4)))] + "(GET_CODE (operands[5]) != UMIN || GET_CODE (operands[7]) == ZERO_EXTEND) + && cris_side_effect_mode_ok (MULT, operands, 5, 4, 2, 3, 0)" + "@ + # + %x6%e7.%m7 [%5=%4+%2%T3],%0") + +;; QImode to SImode + +(define_insn "*extopqisi_side_biap" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (match_operator:SI + 6 "cris_operand_extend_operator" + [(match_operand:SI 1 "register_operand" "0,0") + (match_operator:SI + 7 "cris_extend_operator" + [(mem:QI (plus:SI + (mult:SI (match_operand:SI 2 "register_operand" "r,r") + (match_operand:SI 3 "const_int_operand" "n,n")) + (match_operand:SI 4 "register_operand" "r,r")))])])) + (set (match_operand:SI 5 "register_operand" "=*4,r") + (plus:SI (mult:SI (match_dup 2) + (match_dup 3)) + (match_dup 4)))] + "(GET_CODE (operands[5]) != UMIN || GET_CODE (operands[7]) == ZERO_EXTEND) + && cris_side_effect_mode_ok (MULT, operands, 5, 4, 2, 3, 0)" + "@ + # + %x6%e7.%m7 [%5=%4+%2%T3],%0") + +;; HImode to SImode + +(define_insn "*extophisi_side_biap" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (match_operator:SI + 6 "cris_operand_extend_operator" + [(match_operand:SI 1 "register_operand" "0,0") + (match_operator:SI + 7 "cris_extend_operator" + [(mem:HI (plus:SI + (mult:SI (match_operand:SI 2 "register_operand" "r,r") + (match_operand:SI 3 "const_int_operand" "n,n")) + (match_operand:SI 4 "register_operand" "r,r")))])])) + (set (match_operand:SI 5 "register_operand" "=*4,r") + (plus:SI (mult:SI (match_dup 2) + (match_dup 3)) + (match_dup 4)))] + "(GET_CODE (operands[5]) != UMIN || GET_CODE (operands[7]) == ZERO_EXTEND) + && cris_side_effect_mode_ok (MULT, operands, 5, 4, 2, 3, 0)" + "@ + # + %x6%e7.%m7 [%5=%4+%2%T3],%0") + + +;; [rx=ry+i] +;; FIXME: These could have anonymous mode for operand 0. + +;; QImode to HImode + +(define_insn "*extopqihi_side" + [(set (match_operand:HI 0 "register_operand" "=r,r,r") + (match_operator:HI + 5 "cris_operand_extend_operator" + [(match_operand:HI 1 "register_operand" "0,0,0") + (match_operator:HI + 6 "cris_extend_operator" + [(mem:QI + (plus:SI (match_operand:SI 2 "cris_bdap_operand" "%r,r,r") + (match_operand:SI 3 "cris_bdap_operand" "r>Ri,r,>Ri") + ))])])) + (set (match_operand:SI 4 "register_operand" "=*2,r,r") + (plus:SI (match_dup 2) + (match_dup 3)))] + "(GET_CODE (operands[5]) != UMIN || GET_CODE (operands[6]) == ZERO_EXTEND) + && cris_side_effect_mode_ok (PLUS, operands, 4, 2, 3, -1, 0)" + "* +{ + if (which_alternative == 0 + && (GET_CODE (operands[3]) != CONST_INT + || INTVAL (operands[3]) > 127 + || INTVAL (operands[3]) < -128 + || CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'N') + || CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'J'))) + return \"#\"; + return \"%x5%e6.%m6 [%4=%2%S3],%0\"; +}") + +;; QImode to SImode + +(define_insn "*extopqisi_side" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (match_operator:SI + 5 "cris_operand_extend_operator" + [(match_operand:SI 1 "register_operand" "0,0,0") + (match_operator:SI + 6 "cris_extend_operator" + [(mem:QI + (plus:SI (match_operand:SI 2 "cris_bdap_operand" "%r,r,r") + (match_operand:SI 3 "cris_bdap_operand" "r>Ri,r,>Ri") + ))])])) + (set (match_operand:SI 4 "register_operand" "=*2,r,r") + (plus:SI (match_dup 2) + (match_dup 3)))] + + "(GET_CODE (operands[5]) != UMIN || GET_CODE (operands[6]) == ZERO_EXTEND) + && cris_side_effect_mode_ok (PLUS, operands, 4, 2, 3, -1, 0)" + "* +{ + if (which_alternative == 0 + && (GET_CODE (operands[3]) != CONST_INT + || INTVAL (operands[3]) > 127 + || INTVAL (operands[3]) < -128 + || CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'N') + || CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'J'))) + return \"#\"; + return \"%x5%e6.%m6 [%4=%2%S3],%0\"; +}") + +;; HImode to SImode + +(define_insn "*extophisi_side" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (match_operator:SI + 5 "cris_operand_extend_operator" + [(match_operand:SI 1 "register_operand" "0,0,0") + (match_operator:SI + 6 "cris_extend_operator" + [(mem:HI + (plus:SI (match_operand:SI 2 "cris_bdap_operand" "%r,r,r") + (match_operand:SI 3 "cris_bdap_operand" "r>Ri,r,>Ri") + ))])])) + (set (match_operand:SI 4 "register_operand" "=*2,r,r") + (plus:SI (match_dup 2) + (match_dup 3)))] + "(GET_CODE (operands[5]) != UMIN || GET_CODE (operands[6]) == ZERO_EXTEND) + && cris_side_effect_mode_ok (PLUS, operands, 4, 2, 3, -1, 0)" + "* +{ + if (which_alternative == 0 + && (GET_CODE (operands[3]) != CONST_INT + || INTVAL (operands[3]) > 127 + || INTVAL (operands[3]) < -128 + || CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'N') + || CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'J'))) + return \"#\"; + return \"%x5%e6.%m6 [%4=%2%S3],%0\"; +}") + + +;; As with op.S we may have to add special pattern to match commuted +;; operands to adds/addu and bound +;; +;; adds/addu/bound [rx=ry+rz.S] + +;; QImode to HImode +;; FIXME: GCC should widen. +;; FIXME: These could have anonymous mode for operand 0. + +(define_insn "*extopqihi_swap_side_biap" + [(set (match_operand:HI 0 "register_operand" "=r,r") + (match_operator:HI + 7 "cris_plus_or_bound_operator" + [(match_operator:HI + 6 "cris_extend_operator" + [(mem:QI (plus:SI + (mult:SI (match_operand:SI 2 "register_operand" "r,r") + (match_operand:SI 3 "const_int_operand" "n,n")) + (match_operand:SI 4 "register_operand" "r,r")))]) + (match_operand:HI 1 "register_operand" "0,0")])) + (set (match_operand:SI 5 "register_operand" "=*4,r") + (plus:SI (mult:SI (match_dup 2) + (match_dup 3)) + (match_dup 4)))] + "(GET_CODE (operands[6]) != UMIN || GET_CODE (operands[6]) == ZERO_EXTEND) + && cris_side_effect_mode_ok (MULT, operands, 5, 4, 2, 3, 0)" + "@ + # + %x7%e6.%m6 [%5=%4+%2%T3],%0") + +;; QImode to SImode + +(define_insn "*extopqisi_swap_side_biap" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (match_operator:SI + 7 "cris_plus_or_bound_operator" + [(match_operator:SI + 6 "cris_extend_operator" + [(mem:QI (plus:SI + (mult:SI (match_operand:SI 2 "register_operand" "r,r") + (match_operand:SI 3 "const_int_operand" "n,n")) + (match_operand:SI 4 "register_operand" "r,r")))]) + (match_operand:SI 1 "register_operand" "0,0")])) + (set (match_operand:SI 5 "register_operand" "=*4,r") + (plus:SI (mult:SI (match_dup 2) + (match_dup 3)) + (match_dup 4)))] + "(GET_CODE (operands[6]) != UMIN || GET_CODE (operands[6]) == ZERO_EXTEND) + && cris_side_effect_mode_ok (MULT, operands, 5, 4, 2, 3, 0)" + "@ + # + %x7%e6.%m6 [%5=%4+%2%T3],%0") + +;; HImode to SImode +(define_insn "*extophisi_swap_side_biap" + [(set (match_operand:SI 0 "register_operand" "=r,r") + (match_operator:SI + 7 "cris_plus_or_bound_operator" + [(match_operator:SI + 6 "cris_extend_operator" + [(mem:HI (plus:SI + (mult:SI (match_operand:SI 2 "register_operand" "r,r") + (match_operand:SI 3 "const_int_operand" "n,n")) + (match_operand:SI 4 "register_operand" "r,r")))]) + (match_operand:SI 1 "register_operand" "0,0")])) + (set (match_operand:SI 5 "register_operand" "=*4,r") + (plus:SI (mult:SI (match_dup 2) + (match_dup 3)) + (match_dup 4)))] + "(GET_CODE (operands[6]) != UMIN || GET_CODE (operands[6]) == ZERO_EXTEND) + && cris_side_effect_mode_ok (MULT, operands, 5, 4, 2, 3, 0)" + "@ + # + %x7%e6.%m6 [%5=%4+%2%T3],%0") + +;; [rx=ry+i] +;; FIXME: These could have anonymous mode for operand 0. +;; FIXME: GCC should widen. + +;; QImode to HImode + +(define_insn "*extopqihi_swap_side" + [(set (match_operand:HI 0 "register_operand" "=r,r,r") + (match_operator:HI + 6 "cris_plus_or_bound_operator" + [(match_operator:HI + 5 "cris_extend_operator" + [(mem:QI (plus:SI + (match_operand:SI 2 "cris_bdap_operand" "%r,r,r") + (match_operand:SI 3 "cris_bdap_operand" "r>Ri,r,>Ri")))]) + (match_operand:HI 1 "register_operand" "0,0,0")])) + (set (match_operand:SI 4 "register_operand" "=*2,r,r") + (plus:SI (match_dup 2) + (match_dup 3)))] + "(GET_CODE (operands[6]) != UMIN || GET_CODE (operands[5]) == ZERO_EXTEND) + && cris_side_effect_mode_ok (PLUS, operands, 4, 2, 3, -1, 0)" + "* +{ + if (which_alternative == 0 + && (GET_CODE (operands[3]) != CONST_INT + || INTVAL (operands[3]) > 127 + || INTVAL (operands[3]) < -128 + || CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'N') + || CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'J'))) + return \"#\"; + return \"%x6%e5.%m5 [%4=%2%S3],%0\"; +}") + +;; QImode to SImode + +(define_insn "*extopqisi_swap_side" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (match_operator:SI + 6 "cris_plus_or_bound_operator" + [(match_operator:SI + 5 "cris_extend_operator" + [(mem:QI (plus:SI + (match_operand:SI 2 "cris_bdap_operand" "%r,r,r") + (match_operand:SI 3 "cris_bdap_operand" "r>Ri,r,>Ri")))]) + (match_operand:SI 1 "register_operand" "0,0,0")])) + (set (match_operand:SI 4 "register_operand" "=*2,r,r") + (plus:SI (match_dup 2) + (match_dup 3)))] + "(GET_CODE (operands[6]) != UMIN || GET_CODE (operands[5]) == ZERO_EXTEND) + && cris_side_effect_mode_ok (PLUS, operands, 4, 2, 3, -1, 0)" + "* +{ + if (which_alternative == 0 + && (GET_CODE (operands[3]) != CONST_INT + || INTVAL (operands[3]) > 127 + || INTVAL (operands[3]) < -128 + || CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'N') + || CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'J'))) + return \"#\"; + return \"%x6%e5.%m5 [%4=%2%S3],%0\"; +}") + +;; HImode to SImode + +(define_insn "*extophisi_swap_side" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (match_operator:SI + 6 "cris_plus_or_bound_operator" + [(match_operator:SI + 5 "cris_extend_operator" + [(mem:HI (plus:SI + (match_operand:SI 2 "cris_bdap_operand" "%r,r,r") + (match_operand:SI 3 "cris_bdap_operand" "r>Ri,r,>Ri")))]) + (match_operand:SI 1 "register_operand" "0,0,0")])) + (set (match_operand:SI 4 "register_operand" "=*2,r,r") + (plus:SI (match_dup 2) + (match_dup 3)))] + "(GET_CODE (operands[6]) != UMIN || GET_CODE (operands[5]) == ZERO_EXTEND) + && cris_side_effect_mode_ok (PLUS, operands, 4, 2, 3, -1, 0)" + "* +{ + if (which_alternative == 0 + && (GET_CODE (operands[3]) != CONST_INT + || INTVAL (operands[3]) > 127 + || INTVAL (operands[3]) < -128 + || CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'N') + || CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'J'))) + return \"#\"; + return \"%x6%e5.%m5 [%4=%2%S3],%0\"; +}") + +;; Extend versions (zero/sign) of normal add/sub (no side-effects). +;; FIXME: These could have anonymous mode for operand 0. + +;; QImode to HImode +;; FIXME: GCC should widen. + +(define_insn "*extopqihi" + [(set (match_operand:HI 0 "register_operand" "=r,r,r,r") + (match_operator:HI + 3 "cris_operand_extend_operator" + [(match_operand:HI 1 "register_operand" "0,0,0,r") + (match_operator:HI + 4 "cris_extend_operator" + [(match_operand:QI 2 "nonimmediate_operand" "r,Q>,m,!To")])]))] + "(GET_CODE (operands[3]) != UMIN || GET_CODE (operands[4]) == ZERO_EXTEND) + && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD + && (operands[1] != frame_pointer_rtx || GET_CODE (operands[3]) != PLUS)" + "@ + %x3%e4.%m4 %2,%0 + %x3%e4.%m4 %2,%0 + %x3%e4.%m4 %2,%0 + %x3%e4.%m4 %2,%1,%0" + [(set_attr "slottable" "yes,yes,no,no") + (set_attr "cc" "clobber")]) + +;; QImode to SImode + +(define_insn "*extopqisi" + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") + (match_operator:SI + 3 "cris_operand_extend_operator" + [(match_operand:SI 1 "register_operand" "0,0,0,r") + (match_operator:SI + 4 "cris_extend_operator" + [(match_operand:QI 2 "nonimmediate_operand" "r,Q>,m,!To")])]))] + "(GET_CODE (operands[3]) != UMIN || GET_CODE (operands[4]) == ZERO_EXTEND) + && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD + && (operands[1] != frame_pointer_rtx || GET_CODE (operands[3]) != PLUS)" + "@ + %x3%e4.%m4 %2,%0 + %x3%e4.%m4 %2,%0 + %x3%e4.%m4 %2,%0 + %x3%e4.%m4 %2,%1,%0" + [(set_attr "slottable" "yes,yes,no,no")]) + +;; HImode to SImode + +(define_insn "*extophisi" + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") + (match_operator:SI + 3 "cris_operand_extend_operator" + [(match_operand:SI 1 "register_operand" "0,0,0,r") + (match_operator:SI + 4 "cris_extend_operator" + [(match_operand:HI 2 "nonimmediate_operand" "r,Q>,m,!To")])]))] + "(GET_CODE (operands[3]) != UMIN || GET_CODE (operands[4]) == ZERO_EXTEND) + && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD + && (operands[1] != frame_pointer_rtx || GET_CODE (operands[3]) != PLUS)" + "@ + %x3%e4.%m4 %2,%0 + %x3%e4.%m4 %2,%0 + %x3%e4.%m4 %2,%0 + %x3%e4.%m4 %2,%1,%0" + [(set_attr "slottable" "yes,yes,no,no")]) + + +;; As with the side-effect patterns, may have to have swapped operands for add. +;; FIXME: *should* be redundant to gcc. + +;; QImode to HImode + +(define_insn "*extopqihi_swap" + [(set (match_operand:HI 0 "register_operand" "=r,r,r,r") + (match_operator:HI + 4 "cris_plus_or_bound_operator" + [(match_operator:HI + 3 "cris_extend_operator" + [(match_operand:QI 2 "nonimmediate_operand" "r,Q>,m,!To")]) + (match_operand:HI 1 "register_operand" "0,0,0,r")]))] + "(GET_CODE (operands[3]) != UMIN || GET_CODE (operands[4]) == ZERO_EXTEND) + && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD + && operands[1] != frame_pointer_rtx" + "@ + %x4%e3.%m3 %2,%0 + %x4%e3.%m3 %2,%0 + %x4%e3.%m3 %2,%0 + %x4%e3.%m3 %2,%1,%0" + [(set_attr "slottable" "yes,yes,no,no") + (set_attr "cc" "clobber")]) + +;; QImode to SImode + +(define_insn "*extopqisi_swap" + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") + (match_operator:SI + 4 "cris_plus_or_bound_operator" + [(match_operator:SI + 3 "cris_extend_operator" + [(match_operand:QI 2 "nonimmediate_operand" "r,Q>,m,!To")]) + (match_operand:SI 1 "register_operand" "0,0,0,r")]))] + "(GET_CODE (operands[3]) != UMIN || GET_CODE (operands[4]) == ZERO_EXTEND) + && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD + && operands[1] != frame_pointer_rtx" + "@ + %x4%e3.%m3 %2,%0 + %x4%e3.%m3 %2,%0 + %x4%e3.%m3 %2,%0 + %x4%e3.%m3 %2,%1,%0" + [(set_attr "slottable" "yes,yes,no,no")]) + +;; HImode to SImode + +(define_insn "*extophisi_swap" + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") + (match_operator:SI + 4 "cris_plus_or_bound_operator" + [(match_operator:SI + 3 "cris_extend_operator" + [(match_operand:HI 2 "nonimmediate_operand" "r,Q>,m,!To")]) + (match_operand:SI 1 "register_operand" "0,0,0,r")]))] + "(GET_CODE (operands[3]) != UMIN || GET_CODE (operands[4]) == ZERO_EXTEND) + && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD + && operands[1] != frame_pointer_rtx" + "@ + %x4%e3.%m3 %2,%0 + %x4%e3.%m3 %2,%0 + %x4%e3.%m3 %2,%0 + %x4%e3.%m3 %2,%1,%0" + [(set_attr "slottable" "yes,yes,no,no")]) + +;; This is the special case when we use what corresponds to the +;; instruction above in "casesi". Do *not* change it to use the generic +;; pattern and "REG 15" as pc; I did that and it led to madness and +;; maintenance problems: Instead of (as imagined) recognizing and removing +;; or replacing this pattern with something simpler, other variant +;; patterns were recognized or combined, including some prefix variants +;; where the value in pc is not that of the next instruction (which means +;; this instruction actually *is* special and *should* be marked as such). +;; When switching from the "generic pattern match" approach to this simpler +;; approach, there were insignificant differences in gcc, ipps and +;; product code, somehow due to scratching reload behind the ear or +;; something. Testcase "gcc" looked .01% slower and 4 bytes bigger; +;; product code became .001% smaller but "looked better". The testcase +;; "ipps" was just different at register allocation). +;; +;; Assumptions in the jump optimizer forces us to use IF_THEN_ELSE in this +;; pattern with the default-label as the else, with the "if" being +;; index-is-less-than the max number of cases plus one. The default-label +;; is attached to the end of the case-table at time of output. + +(define_insn "*casesi_adds_w" + [(set (pc) + (if_then_else + (ltu (match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "const_int_operand" "n")) + (plus:SI (sign_extend:SI + (mem:HI + (plus:SI (mult:SI (match_dup 0) (const_int 2)) + (pc)))) + (pc)) + (label_ref (match_operand 2 "" "")))) + (use (label_ref (match_operand 3 "" "")))] + + "operands[0] != frame_pointer_rtx" + + "adds.w [$pc+%0.w],$pc" + [(set_attr "cc" "clobber")]) + +;; Multiply instructions. + +;; Sometimes powers of 2 (which are normally canonicalized to a +;; left-shift) appear here, as a result of address reloading. +;; As a special, for values 3 and 5, we can match with an addi, so add those. +;; +;; FIXME: This may be unnecessary now. +;; Explicitly named for convenience of having a gen_... function. + +(define_insn "addi_mul" + [(set (match_operand:SI 0 "register_operand" "=r") + (mult:SI + (match_operand:SI 1 "register_operand" "%0") + (match_operand:SI 2 "const_int_operand" "n")))] + "operands[0] != frame_pointer_rtx + && operands[1] != frame_pointer_rtx + && GET_CODE (operands[2]) == CONST_INT + && (INTVAL (operands[2]) == 2 + || INTVAL (operands[2]) == 4 || INTVAL (operands[2]) == 3 + || INTVAL (operands[2]) == 5)" + "* +{ + if (INTVAL (operands[2]) == 2) + return \"lslq 1,%0\"; + else if (INTVAL (operands[2]) == 4) + return \"lslq 2,%0\"; + else if (INTVAL (operands[2]) == 3) + return \"addi %0.w,%0\"; + else if (INTVAL (operands[2]) == 5) + return \"addi %0.d,%0\"; + return \"BAD: adr_mulsi: %0=%1*%2\"; +}" +[(set_attr "slottable" "yes") + ;; No flags are changed if this insn is "addi", but it does not seem + ;; worth the trouble to distinguish that to the lslq cases. + (set_attr "cc" "clobber")]) + +;; The addi insn as it is normally used. + +(define_insn "*addi" + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI + (mult:SI (match_operand:SI 2 "register_operand" "r") + (match_operand:SI 3 "const_int_operand" "n")) + (match_operand:SI 1 "register_operand" "0")))] + "operands[0] != frame_pointer_rtx + && operands[1] != frame_pointer_rtx + && GET_CODE (operands[3]) == CONST_INT + && (INTVAL (operands[3]) == 1 + || INTVAL (operands[3]) == 2 || INTVAL (operands[3]) == 4)" + "addi %2%T3,%0" + [(set_attr "slottable" "yes") + (set_attr "cc" "none")]) + +;; The mstep instruction. Probably not useful by itself; it's to +;; non-linear wrt. the other insns. We used to expand to it, so at least +;; it's correct. + +(define_insn "mstep_shift" + [(set (match_operand:SI 0 "register_operand" "=r") + (if_then_else:SI + (lt:SI (cc0) (const_int 0)) + (plus:SI (ashift:SI (match_operand:SI 1 "register_operand" "0") + (const_int 1)) + (match_operand:SI 2 "register_operand" "r")) + (ashift:SI (match_operand:SI 3 "register_operand" "0") + (const_int 1))))] + "" + "mstep %2,%0" + [(set_attr "slottable" "yes")]) + +;; When illegitimate addresses are legitimized, sometimes gcc forgets +;; to canonicalize the multiplications. +;; +;; FIXME: Check gcc > 2.7.2, remove and possibly fix in gcc. + +(define_insn "mstep_mul" + [(set (match_operand:SI 0 "register_operand" "=r") + (if_then_else:SI + (lt:SI (cc0) (const_int 0)) + (plus:SI (mult:SI (match_operand:SI 1 "register_operand" "0") + (const_int 2)) + (match_operand:SI 2 "register_operand" "r")) + (mult:SI (match_operand:SI 3 "register_operand" "0") + (const_int 2))))] + "operands[0] != frame_pointer_rtx + && operands[1] != frame_pointer_rtx + && operands[2] != frame_pointer_rtx + && operands[3] != frame_pointer_rtx" + "mstep %2,%0" + [(set_attr "slottable" "yes")]) + +(define_insn "umulhisi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (mult:SI + (zero_extend:SI (match_operand:HI 1 "register_operand" "0")) + (zero_extend:SI (match_operand:HI 2 "register_operand" "r"))))] + "TARGET_HAS_MUL_INSNS" + "mulu.w %2,%0" + [(set_attr "slottable" "yes") + ;; Just N unusable here, but let's be safe. + (set_attr "cc" "clobber")]) + +(define_insn "umulqihi3" + [(set (match_operand:HI 0 "register_operand" "=r") + (mult:HI + (zero_extend:HI (match_operand:QI 1 "register_operand" "0")) + (zero_extend:HI (match_operand:QI 2 "register_operand" "r"))))] + "TARGET_HAS_MUL_INSNS" + "mulu.b %2,%0" + [(set_attr "slottable" "yes") + ;; Not exactly sure, but let's be safe. + (set_attr "cc" "clobber")]) + +;; Note that gcc does not make use of such a thing as umulqisi3. It gets +;; confused and will erroneously use it instead of umulhisi3, failing (at +;; least) gcc.c-torture/execute/arith-rand.c at all optimization levels. +;; Inspection of optab code shows that there must be only one widening +;; multiplication per mode widened to. + +(define_insn "mulsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (mult:SI (match_operand:SI 1 "register_operand" "0") + (match_operand:SI 2 "register_operand" "r")))] + "TARGET_HAS_MUL_INSNS" + "muls.d %2,%0" + [(set_attr "slottable" "yes") + ;; Just N unusable here, but let's be safe. + (set_attr "cc" "clobber")]) + +;; A few multiply variations. + +;; This really extends to SImode, so cc should be considered clobbered. + +(define_insn "mulqihi3" + [(set (match_operand:HI 0 "register_operand" "=r") + (mult:HI + (sign_extend:HI (match_operand:QI 1 "register_operand" "0")) + (sign_extend:HI (match_operand:QI 2 "register_operand" "r"))))] + "TARGET_HAS_MUL_INSNS" + "muls.b %2,%0" + [(set_attr "slottable" "yes") + (set_attr "cc" "clobber")]) + +(define_insn "mulhisi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (mult:SI + (sign_extend:SI (match_operand:HI 1 "register_operand" "0")) + (sign_extend:SI (match_operand:HI 2 "register_operand" "r"))))] + "TARGET_HAS_MUL_INSNS" + "muls.w %2,%0" + [(set_attr "slottable" "yes") + ;; Just N unusable here, but let's be safe. + (set_attr "cc" "clobber")]) + +;; When needed, we can get the high 32 bits from the overflow +;; register. We don't care to split and optimize these. +;; +;; Note that cc0 is still valid after the move-from-overflow-register +;; insn; no special precaution need to be taken in cris_notice_update_cc. + +(define_insn "mulsidi3" + [(set (match_operand:DI 0 "register_operand" "=r") + (mult:DI + (sign_extend:DI (match_operand:SI 1 "register_operand" "0")) + (sign_extend:DI (match_operand:SI 2 "register_operand" "r"))))] + "TARGET_HAS_MUL_INSNS" + "muls.d %2,%M0\;move $mof,%H0") + +(define_insn "umulsidi3" + [(set (match_operand:DI 0 "register_operand" "=r") + (mult:DI + (zero_extend:DI (match_operand:SI 1 "register_operand" "0")) + (zero_extend:DI (match_operand:SI 2 "register_operand" "r"))))] + "TARGET_HAS_MUL_INSNS" + "mulu.d %2,%M0\;move $mof,%H0") + +;; This pattern would probably not be needed if we add "mof" in its own +;; register class (and open a can of worms about /not/ pairing it with a +;; "normal" register). Having multiple register classes here, and +;; applicable to the v10 variant only, seems worse than having these two +;; patterns with multi-insn contents for now (may change; having a free +;; call-clobbered register is worth some trouble). + +(define_insn "smulsi3_highpart" + [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,m") + (truncate:SI + (lshiftrt:DI + (mult:DI + (sign_extend:DI (match_operand:SI 1 "register_operand" "%0,r,r")) + (sign_extend:DI (match_operand:SI 2 "register_operand" "r,r,r"))) + (const_int 32)))) + (clobber (match_scratch:SI 3 "=X,1,1"))] + "TARGET_HAS_MUL_INSNS" + "muls.d %2,%1\;move $mof,%0" + [(set_attr "cc" "clobber")]) + +(define_insn "umulsi3_highpart" + [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,m") + (truncate:SI + (lshiftrt:DI + (mult:DI + (zero_extend:DI (match_operand:SI 1 "register_operand" "%0,r,r")) + (zero_extend:DI (match_operand:SI 2 "register_operand" "r,r,r"))) + (const_int 32)))) + (clobber (match_scratch:SI 3 "=X,1,1"))] + "TARGET_HAS_MUL_INSNS" + "mulu.d %2,%1\;move $mof,%0" + [(set_attr "cc" "clobber")]) + +;; Divide and modulus instructions. CRIS only has a step instruction. + +(define_insn "dstep_shift" + [(set (match_operand:SI 0 "register_operand" "=r") + (if_then_else:SI + (geu:SI (ashift:SI (match_operand:SI 1 "register_operand" "0") + (const_int 1)) + (match_operand:SI 2 "register_operand" "r")) + (minus:SI (ashift:SI (match_operand:SI 3 "register_operand" "0") + (const_int 1)) + (match_operand:SI 4 "register_operand" "2")) + (ashift:SI (match_operand:SI 5 "register_operand" "0") + (const_int 1))))] + "" + "dstep %2,%0" + [(set_attr "slottable" "yes")]) + +;; Here's a variant with mult instead of ashift. +;; +;; FIXME: This should be investigated. Which one matches through combination? + +(define_insn "dstep_mul" + [(set (match_operand:SI 0 "register_operand" "=r") + (if_then_else:SI + (geu:SI (mult:SI (match_operand:SI 1 "register_operand" "0") + (const_int 2)) + (match_operand:SI 2 "register_operand" "r")) + (minus:SI (mult:SI (match_operand:SI 3 "register_operand" "0") + (const_int 2)) + (match_operand:SI 4 "register_operand" "2")) + (mult:SI (match_operand:SI 5 "register_operand" "0") + (const_int 2))))] + "operands[0] != frame_pointer_rtx + && operands[1] != frame_pointer_rtx + && operands[2] != frame_pointer_rtx + && operands[3] != frame_pointer_rtx" + "dstep %2,%0" + [(set_attr "slottable" "yes")]) + +;; Logical operators. + +;; Bitwise "and". + +;; There is no use in defining "anddi3", because gcc can expand this by +;; itself, and make reasonable code without interference. + +;; If the first operand is memory or a register and is the same as the +;; second operand, and the third operand is -256 or -65536, we can use +;; CLEAR instead. Or, if the first operand is a register, and the third +;; operand is 255 or 65535, we can zero_extend. +;; GCC isnt smart enough to recognize these cases (yet), and they seem +;; to be common enough to be worthwhile. +;; FIXME: This should be made obsolete. + +(define_expand "andsi3" + [(set (match_operand:SI 0 "nonimmediate_operand" "") + (and:SI (match_operand:SI 1 "nonimmediate_operand" "") + (match_operand:SI 2 "general_operand" "")))] + "" + " +{ + if (! (GET_CODE (operands[2]) == CONST_INT + && (((INTVAL (operands[2]) == -256 + || INTVAL (operands[2]) == -65536) + && rtx_equal_p (operands[1], operands[0])) + || ((INTVAL (operands[2]) == 255 + || INTVAL (operands[2]) == 65535) + && REG_P (operands[0]))))) + { + /* Make intermediate steps if operand0 is not a register or + operand1 is not a register, and hope that the reload pass will + make something useful out of it. Note that the operands are + *not* canonicalized. For the moment, I chicken out on this, + because all or most ports do not describe 'and' with + canonicalized operands, and I seem to remember magic in reload, + checking that operand1 has constraint '%0', in which case + operand0 and operand1 must have similar predicates. + FIXME: Investigate. */ + rtx reg0 = REG_P (operands[0]) ? operands[0] : gen_reg_rtx (SImode); + rtx reg1 = operands[1]; + + if (! REG_P (reg1)) + { + emit_move_insn (reg0, reg1); + reg1 = reg0; + } + + emit_insn (gen_rtx_SET (SImode, reg0, + gen_rtx_AND (SImode, reg1, operands[2]))); + + /* Make sure we get the right *final* destination. */ + if (! REG_P (operands[0])) + emit_move_insn (operands[0], reg0); + + DONE; + } +}") + +;; Some special cases of andsi3. + +(define_insn "*andsi_movu" + [(set (match_operand:SI 0 "register_operand" "=r,r,r") + (and:SI (match_operand:SI 1 "nonimmediate_operand" "%r,Q>,m") + (match_operand:SI 2 "const_int_operand" "n,n,n")))] + "INTVAL (operands[2]) == 255 || INTVAL (operands[2]) == 65535" + "movu.%z2 %1,%0" + [(set_attr "slottable" "yes,yes,no")]) + +(define_insn "*andsi_clear" + [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,Q>,Q>,m,m") + (and:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,0,0,0,0") + (match_operand:SI 2 "const_int_operand" "P,n,P,n,P,n")))] + "INTVAL (operands[2]) == -65536 || INTVAL (operands[2]) == -256" + "@ + cLear.b %0 + cLear.w %0 + cLear.b %0 + cLear.w %0 + cLear.b %0 + cLear.w %0" + [(set_attr "slottable" "yes,yes,yes,yes,no,no") + (set_attr "cc" "none")]) + +;; This is a catch-all pattern, taking care of everything that was not +;; matched in the insns above. +;; +;; Sidenote: the tightening from "nonimmediate_operand" to +;; "register_operand" for operand 1 actually increased the register +;; pressure (worse code). That will hopefully change with an +;; improved reload pass. + +(define_insn "*expanded_andsi" + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r") + (and:SI (match_operand:SI 1 "register_operand" "%0,0,0,0,r") + (match_operand:SI 2 "general_operand" "I,r,Q>,g,!To")))] + "" + "@ + andq %2,%0 + and.d %2,%0 + and.d %2,%0 + and.d %2,%0 + and.d %2,%1,%0" + [(set_attr "slottable" "yes,yes,yes,no,no")]) + +;; For both QI and HI we may use the quick patterns. This results in +;; useless condition codes, but that is used rarely enough for it to +;; normally be a win (could check ahead for use of cc0, but seems to be +;; more pain than win). + +;; FIXME: See note for andsi3 + +(define_expand "andhi3" + [(set (match_operand:HI 0 "nonimmediate_operand" "") + (and:HI (match_operand:HI 1 "nonimmediate_operand" "") + (match_operand:HI 2 "general_operand" "")))] + "" + " +{ + if (! (GET_CODE (operands[2]) == CONST_INT + && (((INTVAL (operands[2]) == -256 + || INTVAL (operands[2]) == 65280) + && rtx_equal_p (operands[1], operands[0])) + || (INTVAL (operands[2]) == 255 + && REG_P (operands[0]))))) + { + /* See comment for andsi3. */ + rtx reg0 = REG_P (operands[0]) ? operands[0] : gen_reg_rtx (HImode); + rtx reg1 = operands[1]; + + if (! REG_P (reg1)) + { + emit_move_insn (reg0, reg1); + reg1 = reg0; + } + + emit_insn (gen_rtx_SET (HImode, reg0, + gen_rtx_AND (HImode, reg1, operands[2]))); + + /* Make sure we get the right destination. */ + if (! REG_P (operands[0])) + emit_move_insn (operands[0], reg0); + + DONE; + } +}") + +;; Some fast andhi3 special cases. + +(define_insn "*andhi_movu" + [(set (match_operand:HI 0 "register_operand" "=r,r,r") + (and:HI (match_operand:HI 1 "nonimmediate_operand" "r,Q>,m") + (const_int 255)))] + "" + "mOvu.b %1,%0" + [(set_attr "slottable" "yes,yes,no")]) + +(define_insn "*andhi_clear_signed" + [(set (match_operand:HI 0 "nonimmediate_operand" "=r,Q>,m") + (and:HI (match_operand:HI 1 "nonimmediate_operand" "0,0,0") + (const_int -256)))] + "" + "cLear.b %0" + [(set_attr "slottable" "yes,yes,no") + (set_attr "cc" "none")]) + +;; FIXME: Either this or the pattern above should be redundant. +(define_insn "*andhi_clear_unsigned" + [(set (match_operand:HI 0 "nonimmediate_operand" "=r,Q>,m") + (and:HI (match_operand:HI 1 "nonimmediate_operand" "0,0,0") + (const_int 65280)))] + "" + "cLear.b %0" + [(set_attr "slottable" "yes,yes,no") + (set_attr "cc" "none")]) + +;; Catch-all andhi3 pattern. + +(define_insn "*expanded_andhi" + [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r,r,r") + (and:HI (match_operand:HI 1 "register_operand" "%0,0,0,0,0,0,r") + (match_operand:HI 2 "general_operand" "I,r,Q>,L,O,g,!To")))] + +;; Sidenote: the tightening from "general_operand" to +;; "register_operand" for operand 1 actually increased the register +;; pressure (worse code). That will hopefully change with an +;; improved reload pass. + + "" + "@ + andq %2,%0 + and.w %2,%0 + and.w %2,%0 + and.w %2,%0 + anDq %b2,%0 + and.w %2,%0 + and.w %2,%1,%0" + [(set_attr "slottable" "yes,yes,yes,no,yes,no,no") + (set_attr "cc" "clobber,normal,normal,normal,clobber,normal,normal")]) + +;; A strict_low_part pattern. + +(define_insn "*andhi_lowpart" + [(set (strict_low_part + (match_operand:HI 0 "register_operand" "=r,r,r,r,r,r")) + (and:HI (match_operand:HI 1 "register_operand" "%0,0,0,0,0,r") + (match_operand:HI 2 "general_operand" "r,Q>,L,O,g,!To")))] + "" + "@ + and.w %2,%0 + and.w %2,%0 + and.w %2,%0 + anDq %b2,%0 + and.w %2,%0 + and.w %2,%1,%0" + [(set_attr "slottable" "yes,yes,no,yes,no,no") + (set_attr "cc" "normal,normal,normal,clobber,normal,normal")]) + +(define_insn "andqi3" + [(set (match_operand:QI 0 "register_operand" "=r,r,r,r,r,r") + (and:QI (match_operand:QI 1 "register_operand" "%0,0,0,0,0,r") + (match_operand:QI 2 "general_operand" "I,r,Q>,O,g,!To")))] + "" + "@ + andq %2,%0 + and.b %2,%0 + and.b %2,%0 + andQ %b2,%0 + and.b %2,%0 + and.b %2,%1,%0" + [(set_attr "slottable" "yes,yes,yes,yes,no,no") + (set_attr "cc" "clobber,normal,normal,clobber,normal,normal")]) + +(define_insn "*andqi_lowpart" + [(set (strict_low_part + (match_operand:QI 0 "register_operand" "=r,r,r,r,r")) + (and:QI (match_operand:QI 1 "register_operand" "%0,0,0,0,r") + (match_operand:QI 2 "general_operand" "r,Q>,O,g,!To")))] + "" + "@ + and.b %2,%0 + and.b %2,%0 + andQ %b2,%0 + and.b %2,%0 + and.b %2,%1,%0" + [(set_attr "slottable" "yes,yes,yes,no,no") + (set_attr "cc" "normal,normal,clobber,normal,normal")]) + +;; Bitwise or. + +;; Same comment as anddi3 applies here - no need for such a pattern. + +;; It seems there's no need to jump through hoops to get good code such as +;; with andsi3. + +(define_insn "iorsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r,r") + (ior:SI (match_operand:SI 1 "register_operand" "%0,0,0,0,0,r") + (match_operand:SI 2 "general_operand" "I,r,Q>,n,g,!To")))] + "" + "@ + orq %2,%0 + or.d %2,%0 + or.d %2,%0 + oR.%s2 %2,%0 + or.d %2,%0 + or.d %2,%1,%0" + [(set_attr "slottable" "yes,yes,yes,no,no,no") + (set_attr "cc" "normal,normal,normal,clobber,normal,normal")]) + +(define_insn "iorhi3" + [(set (match_operand:HI 0 "register_operand" "=r,r,r,r,r,r,r") + (ior:HI (match_operand:HI 1 "register_operand" "%0,0,0,0,0,0,r") + (match_operand:HI 2 "general_operand" "I,r,Q>,L,O,g,!To")))] + "" + "@ + orq %2,%0 + or.w %2,%0 + or.w %2,%0 + or.w %2,%0 + oRq %b2,%0 + or.w %2,%0 + or.w %2,%1,%0" + [(set_attr "slottable" "yes,yes,yes,no,yes,no,no") + (set_attr "cc" "clobber,normal,normal,normal,clobber,normal,normal")]) + +(define_insn "iorqi3" + [(set (match_operand:QI 0 "register_operand" "=r,r,r,r,r,r") + (ior:QI (match_operand:QI 1 "register_operand" "%0,0,0,0,0,r") + (match_operand:QI 2 "general_operand" "I,r,Q>,O,g,!To")))] + "" + "@ + orq %2,%0 + or.b %2,%0 + or.b %2,%0 + orQ %b2,%0 + or.b %2,%0 + or.b %2,%1,%0" + [(set_attr "slottable" "yes,yes,yes,yes,no,no") + (set_attr "cc" "clobber,normal,normal,clobber,normal,normal")]) + +;; Exclusive-or + +;; See comment about "anddi3" for xordi3 - no need for such a pattern. + +(define_insn "xorsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (xor:SI (match_operand:SI 1 "register_operand" "%0") + (match_operand:SI 2 "register_operand" "r")))] + "" + "xor %2,%0" + [(set_attr "slottable" "yes")]) + +(define_insn "xorhi3" + [(set (match_operand:HI 0 "register_operand" "=r") + (xor:HI (match_operand:HI 1 "register_operand" "%0") + (match_operand:HI 2 "register_operand" "r")))] + "" + "xor %2,%0" + [(set_attr "slottable" "yes") + (set_attr "cc" "clobber")]) + +(define_insn "xorqi3" + [(set (match_operand:QI 0 "register_operand" "=r") + (xor:QI (match_operand:QI 1 "register_operand" "%0") + (match_operand:QI 2 "register_operand" "r")))] + "" + "xor %2,%0" + [(set_attr "slottable" "yes") + (set_attr "cc" "clobber")]) + +;; Negation insns. + +;; Questionable use, here mostly as a (slightly usable) define_expand +;; example. + +(define_expand "negsf2" + [(set (match_dup 2) + (match_dup 3)) + (parallel [(set (match_operand:SF 0 "register_operand" "=r") + (neg:SF (match_operand:SF 1 + "register_operand" "0"))) + (use (match_dup 2))])] + "" + " +{ + operands[2] = gen_reg_rtx (SImode); + operands[3] = GEN_INT (1 << 31); +}") + +(define_insn "*expanded_negsf2" + [(set (match_operand:SF 0 "register_operand" "=r") + (neg:SF (match_operand:SF 1 "register_operand" "0"))) + (use (match_operand:SI 2 "register_operand" "r"))] + "" + "xor %2,%0" + [(set_attr "slottable" "yes")]) + +;; No "negdi2" although we could make one up that may be faster than +;; the one in libgcc. + +(define_insn "negsi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (neg:SI (match_operand:SI 1 "register_operand" "r")))] + "" + "neg.d %1,%0" + [(set_attr "slottable" "yes")]) + +(define_insn "neghi2" + [(set (match_operand:HI 0 "register_operand" "=r") + (neg:HI (match_operand:HI 1 "register_operand" "r")))] + "" + "neg.w %1,%0" + [(set_attr "slottable" "yes")]) + +(define_insn "negqi2" + [(set (match_operand:QI 0 "register_operand" "=r") + (neg:QI (match_operand:QI 1 "register_operand" "r")))] + "" + "neg.b %1,%0" + [(set_attr "slottable" "yes")]) + +;; One-complements. + +;; See comment on anddi3 - no need for a DImode pattern. + +(define_insn "one_cmplsi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (not:SI (match_operand:SI 1 "register_operand" "0")))] + "" + "not %0" + [(set_attr "slottable" "yes")]) + +(define_insn "one_cmplhi2" + [(set (match_operand:HI 0 "register_operand" "=r") + (not:HI (match_operand:HI 1 "register_operand" "0")))] + "" + "not %0" + [(set_attr "slottable" "yes") + (set_attr "cc" "clobber")]) + +(define_insn "one_cmplqi2" + [(set (match_operand:QI 0 "register_operand" "=r") + (not:QI (match_operand:QI 1 "register_operand" "0")))] + "" + "not %0" + [(set_attr "slottable" "yes") + (set_attr "cc" "clobber")]) + +;; Arithmetic shift right. + +(define_insn "ashrsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (ashiftrt:SI (match_operand:SI 1 "register_operand" "0") + (match_operand:SI 2 "nonmemory_operand" "Kr")))] + "" + "* +{ + if (REG_S_P (operands[2])) + return \"asr.d %2,%0\"; + + return \"asrq %2,%0\"; +}" + [(set_attr "slottable" "yes")]) + +;; Since gcc gets lost, and forgets to zero-extend the source (or mask +;; the destination) when it changes shifts of lower modes into SImode, +;; it is better to make these expands an anonymous patterns instead of +;; the more correct define_insns. This occurs when gcc thinks that is +;; is better to widen to SImode and use immediate shift count. + +;; FIXME: Is this legacy or still true for gcc >= 2.7.2? + +(define_expand "ashrhi3" + [(set (match_dup 3) + (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "rm"))) + (set (match_dup 4) + (zero_extend:SI (match_operand:HI 2 "nonimmediate_operand" "rm"))) + (set (match_dup 5) (ashiftrt:SI (match_dup 3) (match_dup 4))) + (set (match_operand:HI 0 "general_operand" "=g") + (subreg:HI (match_dup 5) 0))] + "" + " +{ + int i; + + for (i = 3; i < 6; i++) + operands[i] = gen_reg_rtx (SImode); +}") + +(define_insn "*expanded_ashrhi" + [(set (match_operand:HI 0 "register_operand" "=r") + (ashiftrt:HI (match_operand:HI 1 "register_operand" "0") + (match_operand:HI 2 "register_operand" "r")))] + "" + "asr.w %2,%0" + [(set_attr "slottable" "yes")]) + +(define_insn "*ashrhi_lowpart" + [(set (strict_low_part (match_operand:HI 0 "register_operand" "+r")) + (ashiftrt:HI (match_dup 0) + (match_operand:HI 1 "register_operand" "r")))] + "" + "asr.w %1,%0" + [(set_attr "slottable" "yes")]) + +;; Same comment goes as for "ashrhi3". + +(define_expand "ashrqi3" + [(set (match_dup 3) + (sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "g"))) + (set (match_dup 4) + (zero_extend:SI (match_operand:QI 2 "nonimmediate_operand" "g"))) + (set (match_dup 5) (ashiftrt:SI (match_dup 3) (match_dup 4))) + (set (match_operand:QI 0 "general_operand" "=g") + (subreg:QI (match_dup 5) 0))] + "" + " +{ + int i; + + for (i = 3; i < 6; i++) + operands[i] = gen_reg_rtx (SImode); +}") + +(define_insn "*expanded_ashrqi" + [(set (match_operand:QI 0 "register_operand" "=r") + (ashiftrt:QI (match_operand:QI 1 "register_operand" "0") + (match_operand:QI 2 "register_operand" "r")))] + "" + "asr.b %2,%0" + [(set_attr "slottable" "yes")]) + +;; A strict_low_part matcher. + +(define_insn "*ashrqi_lowpart" + [(set (strict_low_part (match_operand:QI 0 "register_operand" "+r")) + (ashiftrt:QI (match_dup 0) + (match_operand:QI 1 "register_operand" "r")))] + "" + "asr.b %1,%0" + [(set_attr "slottable" "yes")]) + +;; Logical shift right. + +(define_insn "lshrsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (lshiftrt:SI (match_operand:SI 1 "register_operand" "0") + (match_operand:SI 2 "nonmemory_operand" "Kr")))] + "" + "* +{ + if (REG_S_P (operands[2])) + return \"lsr.d %2,%0\"; + + return \"lsrq %2,%0\"; +}" + [(set_attr "slottable" "yes")]) + +;; Same comments as for ashrhi3. + +(define_expand "lshrhi3" + [(set (match_dup 3) + (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "g"))) + (set (match_dup 4) + (zero_extend:SI (match_operand:HI 2 "nonimmediate_operand" "g"))) + (set (match_dup 5) (lshiftrt:SI (match_dup 3) (match_dup 4))) + (set (match_operand:HI 0 "general_operand" "=g") + (subreg:HI (match_dup 5) 0))] + "" + " +{ + int i; + + for (i = 3; i < 6; i++) + operands[i] = gen_reg_rtx (SImode); +}") + +(define_insn "*expanded_lshrhi" + [(set (match_operand:HI 0 "register_operand" "=r") + (lshiftrt:HI (match_operand:HI 1 "register_operand" "0") + (match_operand:HI 2 "register_operand" "r")))] + "" + "lsr.w %2,%0" + [(set_attr "slottable" "yes")]) + +;; A strict_low_part matcher. + +(define_insn "*lshrhi_lowpart" + [(set (strict_low_part (match_operand:HI 0 "register_operand" "+r")) + (lshiftrt:HI (match_dup 0) + (match_operand:HI 1 "register_operand" "r")))] + "" + "lsr.w %1,%0" + [(set_attr "slottable" "yes")]) + +;; Same comments as for ashrhi3. + +(define_expand "lshrqi3" + [(set (match_dup 3) + (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "g"))) + (set (match_dup 4) + (zero_extend:SI (match_operand:QI 2 "nonimmediate_operand" "g"))) + (set (match_dup 5) (lshiftrt:SI (match_dup 3) (match_dup 4))) + (set (match_operand:QI 0 "general_operand" "=g") + (subreg:QI (match_dup 5) 0))] + "" + " +{ + int i; + + for (i = 3; i < 6; i++) + operands[i] = gen_reg_rtx (SImode); +}") + +(define_insn "*expanded_lshrqi" + [(set (match_operand:QI 0 "register_operand" "=r") + (lshiftrt:QI (match_operand:QI 1 "register_operand" "0") + (match_operand:QI 2 "register_operand" "r")))] + "" + "lsr.b %2,%0" + [(set_attr "slottable" "yes")]) + +;; A strict_low_part matcher. + +(define_insn "*lshrqi_lowpart" + [(set (strict_low_part (match_operand:QI 0 "register_operand" "+r")) + (lshiftrt:QI (match_dup 0) + (match_operand:QI 1 "register_operand" "r")))] + "" + "lsr.b %1,%0" + [(set_attr "slottable" "yes")]) + +;; Arithmetic/logical shift left. + +(define_insn "ashlsi3" + [(set (match_operand:SI 0 "register_operand" "=r") + (ashift:SI (match_operand:SI 1 "register_operand" "0") + (match_operand:SI 2 "nonmemory_operand" "Kr")))] + "" + "* +{ + if (REG_S_P (operands[2])) + return \"lsl.d %2,%0\"; + + return \"lslq %2,%0\"; +}" + [(set_attr "slottable" "yes")]) + +;; For narrower modes than SI, we can use lslq although it makes cc +;; unusable. The win is that we do not have to reload the shift-count +;; into a register. + +(define_insn "ashlhi3" + [(set (match_operand:HI 0 "register_operand" "=r,r") + (ashift:HI (match_operand:HI 1 "register_operand" "0,0") + (match_operand:HI 2 "nonmemory_operand" "r,K")))] + "" + "* +{ + return + (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) > 15) + ? \"moveq 0,%0\" + : (CONSTANT_P (operands[2]) + ? \"lslq %2,%0\" : \"lsl.w %2,%0\"); +}" + [(set_attr "slottable" "yes") + (set_attr "cc" "normal,clobber")]) + +;; A strict_low_part matcher. + +(define_insn "*ashlhi_lowpart" + [(set (strict_low_part (match_operand:HI 0 "register_operand" "+r")) + (ashift:HI (match_dup 0) + (match_operand:HI 1 "register_operand" "r")))] + "" + "lsl.w %1,%0" + [(set_attr "slottable" "yes")]) + +(define_insn "ashlqi3" + [(set (match_operand:QI 0 "register_operand" "=r,r") + (ashift:QI (match_operand:QI 1 "register_operand" "0,0") + (match_operand:QI 2 "nonmemory_operand" "r,K")))] + "" + "* +{ + return + (GET_CODE (operands[2]) == CONST_INT + && INTVAL (operands[2]) > 7) + ? \"moveq 0,%0\" + : (CONSTANT_P (operands[2]) + ? \"lslq %2,%0\" : \"lsl.b %2,%0\"); +}" + [(set_attr "slottable" "yes") + (set_attr "cc" "normal,clobber")]) + +;; A strict_low_part matcher. + +(define_insn "*ashlqi_lowpart" + [(set (strict_low_part (match_operand:QI 0 "register_operand" "+r")) + (ashift:QI (match_dup 0) + (match_operand:QI 1 "register_operand" "r")))] + "" + "lsl.b %1,%0" + [(set_attr "slottable" "yes")]) + +;; Various strange insns that gcc likes. + +;; Fortunately, it is simple to construct an abssf (although it may not +;; be very much used in practice). + +(define_insn "abssf2" + [(set (match_operand:SF 0 "register_operand" "=r") + (abs:SF (match_operand:SF 1 "register_operand" "0")))] + "" + "lslq 1,%0\;lsrq 1,%0") + +(define_insn "abssi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (abs:SI (match_operand:SI 1 "register_operand" "r")))] + "" + "abs %1,%0" + [(set_attr "slottable" "yes")]) + +;; FIXME: GCC should be able to do these expansions itself. + +(define_expand "abshi2" + [(set (match_dup 2) + (sign_extend:SI (match_operand:HI 1 "general_operand" "g"))) + (set (match_dup 3) (abs:SI (match_dup 2))) + (set (match_operand:HI 0 "register_operand" "=r") + (subreg:HI (match_dup 3) 0))] + "" + "operands[2] = gen_reg_rtx (SImode); operands[3] = gen_reg_rtx (SImode);") + +(define_expand "absqi2" + [(set (match_dup 2) + (sign_extend:SI (match_operand:QI 1 "general_operand" "g"))) + (set (match_dup 3) (abs:SI (match_dup 2))) + (set (match_operand:QI 0 "register_operand" "=r") + (subreg:QI (match_dup 3) 0))] + "" + "operands[2] = gen_reg_rtx (SImode); operands[3] = gen_reg_rtx (SImode);") + +;; Bound-insn. Defined to be the same as an unsigned minimum, which is an +;; operation supported by gcc. Used in casesi, but used now and then in +;; normal code too. + +(define_insn "uminsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") + (umin:SI (match_operand:SI 1 "register_operand" "%0,0,0,r") + (match_operand:SI 2 "general_operand" "r,Q>,g,!STo")))] + "" + "* +{ + if (GET_CODE (operands[2]) == CONST_INT) + { + if (INTVAL (operands[2]) < 256) + return \"bound.b %2,%0\"; + + if (INTVAL (operands[2]) < 65536) + return \"bound.w %2,%0\"; + } + else if (which_alternative == 3) + return \"bound.d %2,%1,%0\"; + + return \"bound.d %2,%0\"; +}" + [(set_attr "slottable" "yes,yes,no,no")]) + +;; Jump and branch insns. + +(define_insn "jump" + [(set (pc) + (label_ref (match_operand 0 "" "")))] + "" + "ba %l0%#" + [(set_attr "slottable" "has_slot")]) + +;; Testcase gcc.c-torture/compile/991213-3.c fails if we allow a constant +;; here, since the insn is not recognized as an indirect jump by +;; jmp_uses_reg_or_mem used by computed_jump_p. Perhaps it is a kludge to +;; change from general_operand to nonimmediate_operand (at least the docs +;; should be changed), but then again the pattern is called indirect_jump. +(define_insn "indirect_jump" + [(set (pc) (match_operand:SI 0 "nonimmediate_operand" "rm"))] + "" + "jump %0") + +;; Return insn. Used whenever the epilogue is very simple; if it is only +;; a single ret or jump [sp+] or a contiguous sequence of movem:able saved +;; registers. No allocated stack space is allowed. +;; Note that for this pattern, although named, it is ok to check the +;; context of the insn in the test, not only compiler switches. + +(define_insn "return" + [(return)] + "cris_simple_epilogue ()" + "* +{ + int i; + + /* Just needs to hold a 'movem [sp+],rN'. */ + char rd[sizeof (\"movem [$sp+],$r99\")]; + + *rd = 0; + + /* Start from the last call-saved register. We know that we have a + simple epilogue, so we just have to find the last register in the + movem sequence. */ + for (i = 8; i >= 0; i--) + if (regs_ever_live[i] + || (i == PIC_OFFSET_TABLE_REGNUM + && current_function_uses_pic_offset_table)) + break; + + if (i >= 0) + sprintf (rd, \"movem [$sp+],$%s\", reg_names [i]); + + if (regs_ever_live[CRIS_SRP_REGNUM]) + { + if (*rd) + output_asm_insn (rd, operands); + return \"jump [$sp+]\"; + } + + if (*rd) + { + output_asm_insn (\"reT\", operands); + output_asm_insn (rd, operands); + return \"\"; + } + + return \"ret%#\"; +}" + [(set (attr "slottable") + (if_then_else + (ne (symbol_ref "regs_ever_live[CRIS_SRP_REGNUM]") (const_int 0)) + (const_string "no") ; If jump then not slottable. + (if_then_else + (ne (symbol_ref + "(regs_ever_live[0] + || (flag_pic != 0 && regs_ever_live[1]) + || (PIC_OFFSET_TABLE_REGNUM == 0 + && cris_cfun_uses_pic_table ()))") + (const_int 0)) + (const_string "no") ; ret+movem [sp+],rx: slot already filled. + (const_string "has_slot")))) ; If ret then need to fill a slot. + (set_attr "cc" "none")]) + +;; Conditional branches. + +;; We suffer from the same overflow-bit-gets-in-the-way problem as +;; e.g. m68k, so we have to check if overflow bit is set on all "signed" +;; conditions. + +(define_insn "beq" + [(set (pc) + (if_then_else (eq (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "beq %l0%#" + [(set_attr "slottable" "has_slot")]) + +(define_insn "bne" + [(set (pc) + (if_then_else (ne (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "bne %l0%#" + [(set_attr "slottable" "has_slot")]) + +(define_insn "bgt" + [(set (pc) + (if_then_else (gt (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "* +{ + return + (cc_prev_status.flags & CC_NO_OVERFLOW) + ? 0 : \"bgt %l0%#\"; +}" + [(set_attr "slottable" "has_slot")]) + +(define_insn "bgtu" + [(set (pc) + (if_then_else (gtu (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "bhi %l0%#" + [(set_attr "slottable" "has_slot")]) + +(define_insn "blt" + [(set (pc) + (if_then_else (lt (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "* +{ + return + (cc_prev_status.flags & CC_NO_OVERFLOW) + ? \"bmi %l0%#\" : \"blt %l0%#\"; +}" + [(set_attr "slottable" "has_slot")]) + +(define_insn "bltu" + [(set (pc) + (if_then_else (ltu (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "blo %l0%#" + [(set_attr "slottable" "has_slot")]) + +(define_insn "bge" + [(set (pc) + (if_then_else (ge (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "* +{ + return + (cc_prev_status.flags & CC_NO_OVERFLOW) + ? \"bpl %l0%#\" : \"bge %l0%#\"; +}" + [(set_attr "slottable" "has_slot")]) + +(define_insn "bgeu" + [(set (pc) + (if_then_else (geu (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "bhs %l0%#" + [(set_attr "slottable" "has_slot")]) + +(define_insn "ble" + [(set (pc) + (if_then_else (le (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "* +{ + return + (cc_prev_status.flags & CC_NO_OVERFLOW) + ? 0 : \"ble %l0%#\"; +}" + [(set_attr "slottable" "has_slot")]) + +(define_insn "bleu" + [(set (pc) + (if_then_else (leu (cc0) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "bls %l0%#" + [(set_attr "slottable" "has_slot")]) + +;; Reversed anonymous patterns to the ones above, as mandated. + +(define_insn "*beq_reversed" + [(set (pc) + (if_then_else (eq (cc0) + (const_int 0)) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "bne %l0%#" + [(set_attr "slottable" "has_slot")]) + +(define_insn "*bne_reversed" + [(set (pc) + (if_then_else (ne (cc0) + (const_int 0)) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "beq %l0%#" + [(set_attr "slottable" "has_slot")]) + +(define_insn "*bgt_reversed" + [(set (pc) + (if_then_else (gt (cc0) + (const_int 0)) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "* +{ + return + (cc_prev_status.flags & CC_NO_OVERFLOW) + ? 0 : \"ble %l0%#\"; +}" + [(set_attr "slottable" "has_slot")]) + +(define_insn "*bgtu_reversed" + [(set (pc) + (if_then_else (gtu (cc0) + (const_int 0)) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "bls %l0%#" + [(set_attr "slottable" "has_slot")]) + +(define_insn "*blt_reversed" + [(set (pc) + (if_then_else (lt (cc0) + (const_int 0)) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "* +{ + return + (cc_prev_status.flags & CC_NO_OVERFLOW) + ? \"bpl %l0%#\" : \"bge %l0%#\"; +}" + [(set_attr "slottable" "has_slot")]) + +(define_insn "*bltu_reversed" + [(set (pc) + (if_then_else (ltu (cc0) + (const_int 0)) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "bhs %l0%#" + [(set_attr "slottable" "has_slot")]) + +(define_insn "*bge_reversed" + [(set (pc) + (if_then_else (ge (cc0) + (const_int 0)) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "* +{ + return + (cc_prev_status.flags & CC_NO_OVERFLOW) + ? \"bmi %l0%#\" : \"blt %l0%#\"; +}" + [(set_attr "slottable" "has_slot")]) + +(define_insn "*bgeu_reversed" + [(set (pc) + (if_then_else (geu (cc0) + (const_int 0)) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "blo %l0%#" + [(set_attr "slottable" "has_slot")]) + +(define_insn "*ble_reversed" + [(set (pc) + (if_then_else (le (cc0) + (const_int 0)) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "* +{ + return + (cc_prev_status.flags & CC_NO_OVERFLOW) + ? 0 : \"bgt %l0%#\"; +}" + [(set_attr "slottable" "has_slot")]) + +(define_insn "*bleu_reversed" + [(set (pc) + (if_then_else (leu (cc0) + (const_int 0)) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "bhi %l0%#" + [(set_attr "slottable" "has_slot")]) + +;; Set on condition: sCC. + +;; Like bCC, we have to check the overflow bit for +;; signed conditions. + +(define_insn "sgeu" + [(set (match_operand:SI 0 "register_operand" "=r") + (geu:SI (cc0) (const_int 0)))] + "" + "shs %0" + [(set_attr "slottable" "yes") + (set_attr "cc" "none")]) + +(define_insn "sltu" + [(set (match_operand:SI 0 "register_operand" "=r") + (ltu:SI (cc0) (const_int 0)))] + "" + "slo %0" + [(set_attr "slottable" "yes") + (set_attr "cc" "none")]) + +(define_insn "seq" + [(set (match_operand:SI 0 "register_operand" "=r") + (eq:SI (cc0) (const_int 0)))] + "" + "seq %0" + [(set_attr "slottable" "yes") + (set_attr "cc" "none")]) + +(define_insn "sge" + [(set (match_operand:SI 0 "register_operand" "=r") + (ge:SI (cc0) (const_int 0)))] + "" + "* +{ + return + (cc_prev_status.flags & CC_NO_OVERFLOW) + ? \"spl %0\" : \"sge %0\"; +}" + [(set_attr "slottable" "yes") + (set_attr "cc" "none")]) + +(define_insn "sgt" + [(set (match_operand:SI 0 "register_operand" "=r") + (gt:SI (cc0) (const_int 0)))] + "" + "* +{ + return + (cc_prev_status.flags & CC_NO_OVERFLOW) + ? 0 : \"sgt %0\"; +}" + [(set_attr "slottable" "yes") + (set_attr "cc" "none")]) + +(define_insn "sgtu" + [(set (match_operand:SI 0 "register_operand" "=r") + (gtu:SI (cc0) (const_int 0)))] + "" + "shi %0" + [(set_attr "slottable" "yes") + (set_attr "cc" "none")]) + +(define_insn "sle" + [(set (match_operand:SI 0 "register_operand" "=r") + (le:SI (cc0) (const_int 0)))] + "" + "* +{ + return + (cc_prev_status.flags & CC_NO_OVERFLOW) + ? 0 : \"sle %0\"; +}" + [(set_attr "slottable" "yes") + (set_attr "cc" "none")]) + +(define_insn "sleu" + [(set (match_operand:SI 0 "register_operand" "=r") + (leu:SI (cc0) (const_int 0)))] + "" + "sls %0" + [(set_attr "slottable" "yes")]) + +(define_insn "slt" + [(set (match_operand:SI 0 "register_operand" "=r") + (lt:SI (cc0) (const_int 0)))] + "" + "* +{ + return + (cc_prev_status.flags & CC_NO_OVERFLOW) + ? \"smi %0\" : \"slt %0\"; +}" + [(set_attr "slottable" "yes") + (set_attr "cc" "none")]) + +(define_insn "sne" + [(set (match_operand:SI 0 "register_operand" "=r") + (ne:SI (cc0) (const_int 0)))] + "" + "sne %0" + [(set_attr "slottable" "yes") + (set_attr "cc" "none")]) + +;; Call insns. + +;; We need to make these patterns "expand", since the real operand is +;; hidden in a (mem:QI ) inside operand[0] (call_value: operand[1]), +;; and cannot be checked if it were a "normal" pattern. +;; Note that "call" and "call_value" are *always* called with a +;; mem-operand for operand 0 and 1 respective. What happens for combined +;; instructions is a different issue. + +(define_expand "call" + [(parallel [(call (match_operand:QI 0 "cris_mem_call_operand" "") + (match_operand 1 "general_operand" "")) + ;; 16 is the srp (can't use the symbolic name here) + (clobber (reg:SI 16))])] + "" + " +{ + rtx op0; + + if (GET_CODE (operands[0]) != MEM) + abort (); + + if (flag_pic) + { + op0 = XEXP (operands[0], 0); + + /* It might be that code can be generated that jumps to 0 (or to a + specific address). Don't abort on that. At least there's a + test-case. */ + if (CONSTANT_ADDRESS_P (op0) && GET_CODE (op0) != CONST_INT) + { + if (no_new_pseudos) + abort (); + + /* For local symbols (non-PLT), get the plain symbol reference + into a register. For symbols that can be PLT, make them PLT. */ + if (cris_gotless_symbol (op0) || GET_CODE (op0) != SYMBOL_REF) + op0 = force_reg (Pmode, op0); + else if (cris_symbol (op0)) + /* FIXME: Would hanging a REG_EQUIV/EQUAL on that register + for the symbol cause bad recombinatorial effects? */ + op0 = force_reg (Pmode, + gen_rtx_CONST + (VOIDmode, + gen_rtx_UNSPEC (VOIDmode, + gen_rtvec (1, op0), 0))); + else + abort (); + + operands[0] = gen_rtx_MEM (GET_MODE (operands[0]), op0); + } + } +}") + +;; Accept *anything* as operand 1. Accept operands for operand 0 in +;; order of preference (Q includes r, but r is shorter, faster) + +(define_insn "*expanded_call" + [(call (mem:QI (match_operand:SI + 0 "cris_general_operand_or_plt_symbol" "r,Q>,g,S")) + (match_operand 1 "" "")) + (clobber (reg:SI 16))] ;; 16 is the srp (can't use symbolic name) + "! TARGET_AVOID_GOTPLT" + "jsr %0") + +;; Same as above, since can't afford wasting a constraint letter to mean +;; "S unless TARGET_AVOID_GOTPLT". +(define_insn "*expanded_call_no_gotplt" + [(call (mem:QI (match_operand:SI + 0 "cris_general_operand_or_plt_symbol" "r,Q>,g")) + (match_operand 1 "" "")) + (clobber (reg:SI 16))] ;; 16 is the srp (can't use symbolic name) + "TARGET_AVOID_GOTPLT" + "jsr %0") + +(define_expand "call_value" + [(parallel [(set (match_operand 0 "" "") + (call (match_operand:QI 1 "cris_mem_call_operand" "") + (match_operand 2 "" ""))) + ;; 16 is the srp (can't use symbolic name) + (clobber (reg:SI 16))])] + "" + " +{ + rtx op1; + + if (GET_CODE (operands[1]) != MEM) + abort (); + + if (flag_pic) + { + op1 = XEXP (operands[1], 0); + + /* It might be that code can be generated that jumps to 0 (or to a + specific address). Don't abort on that. At least there's a + test-case. */ + if (CONSTANT_ADDRESS_P (op1) && GET_CODE (op1) != CONST_INT) + { + if (no_new_pseudos) + abort (); + + if (cris_gotless_symbol (op1)) + op1 = force_reg (Pmode, op1); + else if (cris_symbol (op1)) + /* FIXME: Would hanging a REG_EQUIV/EQUAL on that register + for the symbol cause bad recombinatorial effects? */ + op1 = force_reg (Pmode, + gen_rtx_CONST + (VOIDmode, + gen_rtx_UNSPEC (VOIDmode, + gen_rtvec (1, op1), 0))); + else + abort (); + + operands[1] = gen_rtx_MEM (GET_MODE (operands[1]), op1); + } + } +}") + +;; Accept *anything* as operand 2. The validity other than "general" of +;; operand 0 will be checked elsewhere. Accept operands for operand 1 in +;; order of preference (Q includes r, but r is shorter, faster). +;; We also accept a PLT symbol. We output it as [rPIC+sym:GOTPLT] rather +;; than requiring getting rPIC + sym:PLT into a register. + +(define_insn "*expanded_call_value" + [(set (match_operand 0 "nonimmediate_operand" "=g,g,g,g") + (call (mem:QI (match_operand:SI + 1 "cris_general_operand_or_plt_symbol" "r,Q>,g,S")) + (match_operand 2 "" ""))) + (clobber (reg:SI 16))] + "! TARGET_AVOID_GOTPLT" + "Jsr %1" + [(set_attr "cc" "clobber")]) + +;; Same as above, since can't afford wasting a constraint letter to mean +;; "S unless TARGET_AVOID_GOTPLT". +(define_insn "*expanded_call_value_no_gotplt" + [(set (match_operand 0 "nonimmediate_operand" "=g,g,g") + (call (mem:QI (match_operand:SI + 1 "cris_general_operand_or_plt_symbol" "r,Q>,g")) + (match_operand 2 "" ""))) + (clobber (reg:SI 16))] + "TARGET_AVOID_GOTPLT" + "Jsr %1" + [(set_attr "cc" "clobber")]) + +;; Used in debugging. No use for the direct pattern; unfilled +;; delayed-branches are taken care of by other means. + +(define_insn "nop" + [(const_int 0)] + "" + "nop" + [(set_attr "cc" "none")]) + +;; We expand on casesi so we can use "bound" and "add offset fetched from +;; a table to pc" (adds.w [pc+%0.w],pc). + +;; Note: if you change the "parallel" (or add anything after it) in +;; this expansion, you must change the macro ASM_OUTPUT_CASE_END +;; accordingly, to add the default case at the end of the jump-table. + +(define_expand "casesi" + [(set (match_dup 5) (match_operand:SI 0 "general_operand" "")) + (set (match_dup 6) + (minus:SI (match_dup 5) + (match_operand:SI 1 "const_int_operand" "n"))) + (set (match_dup 7) + (umin:SI (match_dup 6) + (match_operand:SI 2 "const_int_operand" "n"))) + (parallel + [(set (pc) + (if_then_else + (ltu (match_dup 7) (match_dup 2)) + (plus:SI (sign_extend:SI + (mem:HI + (plus:SI (mult:SI (match_dup 7) (const_int 2)) + (pc)))) + (pc)) + (label_ref (match_operand 4 "" "")))) + (use (label_ref (match_operand 3 "" "")))])] + "" + " +{ + operands[2] = plus_constant (operands[2], 1); + operands[5] = gen_reg_rtx (SImode); + operands[6] = gen_reg_rtx (SImode); + operands[7] = gen_reg_rtx (SImode); +}") + +;; Split-patterns. Some of them have modes unspecified. This +;; should always be ok; if for no other reason sparc.md has it as +;; well. +;; +;; When register_operand is specified for an operand, we can get a +;; subreg as well (Axis-990331), so don't just assume that REG_P is true +;; for a register_operand and that REGNO can be used as is. It is best to +;; guard with REG_P, unless it is worth it to adjust for the subreg case. + +;; op [rx + 0],ry,rz +;; The index to rx is optimized into zero, and gone. + +;; First, recognize bound [rx],ry,rz; where [rx] is zero-extended, +;; and add/sub [rx],ry,rz, with zero or sign-extend on [rx]. +;; Split this into: +;; move ry,rz +;; op [rx],rz +;; Lose if rz=ry or rx=rz. +;; Call this op-extend-split + +(define_split + [(set (match_operand 0 "register_operand" "") + (match_operator + 4 "cris_operand_extend_operator" + [(match_operand 1 "register_operand" "") + (match_operator + 3 "cris_extend_operator" + [(match_operand 2 "memory_operand" "")])]))] + "REG_P (operands[0]) + && REG_P (operands[1]) + && REGNO (operands[1]) != REGNO (operands[0]) + && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD + && REG_P (XEXP (operands[2], 0)) + && REGNO (XEXP (operands[2], 0)) != REGNO (operands[0])" + [(set (match_dup 0) + (match_dup 1)) + (set (match_dup 0) + (match_op_dup + 4 [(match_dup 0) + (match_op_dup 3 [(match_dup 2)])]))] + "") + +;; As op-extend-split, but recognize and split op [rz],ry,rz into +;; ext [rz],rz +;; op ry,rz +;; Do this for plus or bound only, being commutative operations, since we +;; have swapped the operands. +;; Call this op-extend-split-rx=rz + +(define_split + [(set (match_operand 0 "register_operand" "") + (match_operator + 4 "cris_plus_or_bound_operator" + [(match_operand 1 "register_operand" "") + (match_operator + 3 "cris_extend_operator" + [(match_operand 2 "memory_operand" "")])]))] + "REG_P (operands[0]) + && REG_P (operands[1]) + && REGNO (operands[1]) != REGNO (operands[0]) + && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD + && REG_P (XEXP (operands[2], 0)) + && REGNO (XEXP (operands[2], 0)) == REGNO (operands[0])" + [(set (match_dup 0) + (match_op_dup 3 [(match_dup 2)])) + (set (match_dup 0) + (match_op_dup + 4 [(match_dup 0) + (match_dup 1)]))] + "") + +;; As the op-extend-split, but swapped operands, and only for +;; plus or bound, being the commutative extend-operators. FIXME: Why is +;; this needed? Is it? +;; Call this op-extend-split-swapped + +(define_split + [(set (match_operand 0 "register_operand" "") + (match_operator + 4 "cris_plus_or_bound_operator" + [(match_operator + 3 "cris_extend_operator" + [(match_operand 2 "memory_operand" "")]) + (match_operand 1 "register_operand" "")]))] + "REG_P (operands[0]) + && REG_P (operands[1]) + && REGNO (operands[1]) != REGNO (operands[0]) + && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD + && REG_P (XEXP (operands[2], 0)) + && REGNO (XEXP (operands[2], 0)) != REGNO (operands[0])" + [(set (match_dup 0) + (match_dup 1)) + (set (match_dup 0) + (match_op_dup + 4 [(match_dup 0) + (match_op_dup 3 [(match_dup 2)])]))] + "") + +;; As op-extend-split-rx=rz, but swapped operands, only for plus or +;; bound. Call this op-extend-split-swapped-rx=rz. + +(define_split + [(set (match_operand 0 "register_operand" "") + (match_operator + 4 "cris_plus_or_bound_operator" + [(match_operator + 3 "cris_extend_operator" + [(match_operand 2 "memory_operand" "")]) + (match_operand 1 "register_operand" "")]))] + "REG_P (operands[0]) + && REG_P (operands[1]) + && REGNO (operands[1]) != REGNO (operands[0]) + && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD + && REG_P (XEXP (operands[2], 0)) + && REGNO (XEXP (operands[2], 0)) == REGNO (operands[0])" + [(set (match_dup 0) + (match_op_dup 3 [(match_dup 2)])) + (set (match_dup 0) + (match_op_dup + 4 [(match_dup 0) + (match_dup 1)]))] + "") + +;; As op-extend-split, but the mem operand is not extended. +;; +;; op [rx],ry,rz changed into +;; move ry,rz +;; op [rx],rz +;; lose if ry=rz or rx=rz +;; Call this op-extend. + +(define_split + [(set (match_operand 0 "register_operand" "") + (match_operator + 3 "cris_orthogonal_operator" + [(match_operand 1 "register_operand" "") + (match_operand 2 "memory_operand" "")]))] + "REG_P (operands[0]) + && REG_P (operands[1]) + && REGNO (operands[1]) != REGNO (operands[0]) + && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD + && REG_P (XEXP (operands[2], 0)) + && REGNO (XEXP (operands[2], 0)) != REGNO (operands[0])" + [(set (match_dup 0) + (match_dup 1)) + (set (match_dup 0) + (match_op_dup + 3 [(match_dup 0) + (match_dup 2)]))] + "") + +;; As op-extend-split-rx=rz, non-extended. +;; Call this op-split-rx=rz + +(define_split + [(set (match_operand 0 "register_operand" "") + (match_operator + 3 "cris_commutative_orth_op" + [(match_operand 2 "memory_operand" "") + (match_operand 1 "register_operand" "")]))] + "REG_P (operands[0]) + && REG_P (operands[1]) + && REGNO (operands[1]) != REGNO (operands[0]) + && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD + && REG_P (XEXP (operands[2], 0)) + && REGNO (XEXP (operands[2], 0)) != REGNO (operands[0])" + [(set (match_dup 0) + (match_dup 1)) + (set (match_dup 0) + (match_op_dup + 3 [(match_dup 0) + (match_dup 2)]))] + "") + +;; As op-extend-split-swapped, nonextended. +;; Call this op-split-swapped. + +(define_split + [(set (match_operand 0 "register_operand" "") + (match_operator + 3 "cris_commutative_orth_op" + [(match_operand 1 "register_operand" "") + (match_operand 2 "memory_operand" "")]))] + "REG_P (operands[0]) && REG_P (operands[1]) + && REGNO (operands[1]) != REGNO (operands[0]) + && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD + && REG_P (XEXP (operands[2], 0)) + && REGNO (XEXP (operands[2], 0)) == REGNO (operands[0])" + [(set (match_dup 0) + (match_dup 2)) + (set (match_dup 0) + (match_op_dup + 3 [(match_dup 0) + (match_dup 1)]))] + "") + +;; As op-extend-split-swapped-rx=rz, non-extended. +;; Call this op-split-swapped-rx=rz. + +(define_split + [(set (match_operand 0 "register_operand" "") + (match_operator + 3 "cris_orthogonal_operator" + [(match_operand 2 "memory_operand" "") + (match_operand 1 "register_operand" "")]))] + "REG_P (operands[0]) && REG_P (operands[1]) + && REGNO (operands[1]) != REGNO (operands[0]) + && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD + && REG_P (XEXP (operands[2], 0)) + && REGNO (XEXP (operands[2], 0)) == REGNO (operands[0])" + [(set (match_dup 0) + (match_dup 2)) + (set (match_dup 0) + (match_op_dup + 3 [(match_dup 0) + (match_dup 1)]))] + "") + +;; Splits for all cases in side-effect insns where (possibly after reload +;; and register allocation) rx and ry in [rx=ry+i] are equal. + +;; move.S1 [rx=rx+rz.S2],ry + +(define_split + [(parallel + [(set (match_operand 0 "register_operand" "") + (mem (plus:SI + (mult:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "const_int_operand" "")) + (match_operand:SI 3 "register_operand" "")))) + (set (match_operand:SI 4 "register_operand" "") + (plus:SI (mult:SI (match_dup 1) + (match_dup 2)) + (match_dup 3)))])] + "REG_P (operands[3]) && REG_P (operands[4]) + && REGNO (operands[3]) == REGNO (operands[4])" + [(set (match_dup 4) (plus:SI (mult:SI (match_dup 1) (match_dup 2)) + (match_dup 3))) + (set (match_dup 0) (match_dup 5))] + "operands[5] = gen_rtx_MEM (GET_MODE (operands[0]), operands[3]);") + +;; move.S1 [rx=rx+i],ry + +(define_split + [(parallel + [(set (match_operand 0 "register_operand" "") + (mem + (plus:SI (match_operand:SI 1 "cris_bdap_operand" "") + (match_operand:SI 2 "cris_bdap_operand" "")))) + (set (match_operand:SI 3 "register_operand" "") + (plus:SI (match_dup 1) + (match_dup 2)))])] + "(rtx_equal_p (operands[3], operands[1]) + || rtx_equal_p (operands[3], operands[2]))" + [(set (match_dup 3) (plus:SI (match_dup 1) (match_dup 2))) + (set (match_dup 0) (match_dup 4))] + "operands[4] = gen_rtx_MEM (GET_MODE (operands[0]), operands[3]);") + +;; move.S1 ry,[rx=rx+rz.S2] + +(define_split + [(parallel + [(set (mem (plus:SI + (mult:SI (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "const_int_operand" "")) + (match_operand:SI 2 "register_operand" ""))) + (match_operand 3 "register_operand" "")) + (set (match_operand:SI 4 "register_operand" "") + (plus:SI (mult:SI (match_dup 0) + (match_dup 1)) + (match_dup 2)))])] + "REG_P (operands[2]) && REG_P (operands[4]) + && REGNO (operands[4]) == REGNO (operands[2])" + [(set (match_dup 4) (plus:SI (mult:SI (match_dup 0) (match_dup 1)) + (match_dup 2))) + (set (match_dup 5) (match_dup 3))] + "operands[5] = gen_rtx_MEM (GET_MODE (operands[3]), operands[4]);") + +;; move.S1 ry,[rx=rx+i] + +(define_split + [(parallel + [(set (mem + (plus:SI (match_operand:SI 0 "cris_bdap_operand" "") + (match_operand:SI 1 "cris_bdap_operand" ""))) + (match_operand 2 "register_operand" "")) + (set (match_operand:SI 3 "register_operand" "") + (plus:SI (match_dup 0) + (match_dup 1)))])] + "(rtx_equal_p (operands[3], operands[0]) + || rtx_equal_p (operands[3], operands[1]))" + [(set (match_dup 3) (plus:SI (match_dup 0) (match_dup 1))) + (set (match_dup 5) (match_dup 2))] + "operands[5] = gen_rtx_MEM (GET_MODE (operands[2]), operands[3]);") + +;; clear.d ry,[rx=rx+rz.S2] + +(define_split + [(parallel + [(set (mem:SI (plus:SI + (mult:SI (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "const_int_operand" "")) + (match_operand:SI 2 "register_operand" ""))) + (const_int 0)) + (set (match_operand:SI 3 "register_operand" "") + (plus:SI (mult:SI (match_dup 0) + (match_dup 1)) + (match_dup 2)))])] + "REG_P (operands[2]) && REG_P (operands[3]) + && REGNO (operands[3]) == REGNO (operands[2])" + [(set (match_dup 3) (plus:SI (mult:SI (match_dup 0) (match_dup 1)) + (match_dup 2))) + (set (mem:SI (match_dup 3)) (const_int 0))] + "") + +;; clear.w ry,[rx=rx+rz.S2] + +(define_split + [(parallel + [(set (mem:HI (plus:SI + (mult:SI (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "const_int_operand" "")) + (match_operand:SI 2 "register_operand" ""))) + (const_int 0)) + (set (match_operand:SI 3 "register_operand" "") + (plus:SI (mult:SI (match_dup 0) + (match_dup 1)) + (match_dup 2)))])] + "REG_P (operands[2]) && REG_P (operands[3]) + && REGNO (operands[3]) == REGNO (operands[2])" + [(set (match_dup 3) (plus:SI (mult:SI (match_dup 0) (match_dup 1)) + (match_dup 2))) + (set (mem:HI (match_dup 3)) (const_int 0))] + "") + +;; clear.b ry,[rx=rx+rz.S2] + +(define_split + [(parallel + [(set (mem:QI (plus:SI + (mult:SI (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "const_int_operand" "")) + (match_operand:SI 2 "register_operand" ""))) + (const_int 0)) + (set (match_operand:SI 3 "register_operand" "") + (plus:SI (mult:SI (match_dup 0) + (match_dup 1)) + (match_dup 2)))])] + "REG_P (operands[2]) && REG_P (operands[3]) + && REGNO (operands[3]) == REGNO (operands[2])" + [(set (match_dup 3) (plus:SI (mult:SI (match_dup 0) (match_dup 1)) + (match_dup 2))) + (set (mem:QI (match_dup 3)) (const_int 0))] + "") + +;; clear.d ry,[rx=rx+i] + +(define_split + [(parallel + [(set (mem:SI + (plus:SI (match_operand:SI 0 "cris_bdap_operand" "") + (match_operand:SI 1 "cris_bdap_operand" ""))) + (const_int 0)) + (set (match_operand:SI 2 "register_operand" "") + (plus:SI (match_dup 0) + (match_dup 1)))])] + "(rtx_equal_p (operands[0], operands[2]) + || rtx_equal_p (operands[2], operands[1]))" + [(set (match_dup 2) (plus:SI (match_dup 0) (match_dup 1))) + (set (mem:SI (match_dup 2)) (const_int 0))] + "") + +;; clear.w ry,[rx=rx+i] + +(define_split + [(parallel + [(set (mem:HI + (plus:SI (match_operand:SI 0 "cris_bdap_operand" "") + (match_operand:SI 1 "cris_bdap_operand" ""))) + (const_int 0)) + (set (match_operand:SI 2 "register_operand" "") + (plus:SI (match_dup 0) + (match_dup 1)))])] + "(rtx_equal_p (operands[0], operands[2]) + || rtx_equal_p (operands[2], operands[1]))" + [(set (match_dup 2) (plus:SI (match_dup 0) (match_dup 1))) + (set (mem:HI (match_dup 2)) (const_int 0))] + "") + +;; clear.b ry,[rx=rx+i] + +(define_split + [(parallel + [(set (mem:QI + (plus:SI (match_operand:SI 0 "cris_bdap_operand" "") + (match_operand:SI 1 "cris_bdap_operand" ""))) + (const_int 0)) + (set (match_operand:SI 2 "register_operand" "") + (plus:SI (match_dup 0) + (match_dup 1)))])] + "(rtx_equal_p (operands[0], operands[2]) + || rtx_equal_p (operands[2], operands[1]))" + [(set (match_dup 2) (plus:SI (match_dup 0) (match_dup 1))) + (set (mem:QI (match_dup 2)) (const_int 0))] + "") + +;; mov(s|u).S1 [rx=rx+rz.S2],ry + +(define_split + [(parallel + [(set (match_operand 0 "register_operand" "") + (match_operator + 5 "cris_extend_operator" + [(mem (plus:SI + (mult:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "const_int_operand" "")) + (match_operand:SI 3 "register_operand" "")))])) + (set (match_operand:SI 4 "register_operand" "") + (plus:SI (mult:SI (match_dup 1) + (match_dup 2)) + (match_dup 3)))])] + "REG_P (operands[3]) + && REG_P (operands[4]) + && REGNO (operands[3]) == REGNO (operands[4])" + [(set (match_dup 4) (plus:SI (mult:SI (match_dup 1) (match_dup 2)) + (match_dup 3))) + (set (match_dup 0) (match_op_dup 5 [(match_dup 6)]))] + "operands[6] = gen_rtx_MEM (GET_MODE (XEXP (operands[5],0)), + operands[4]);") + +;; mov(s|u).S1 [rx=rx+i],ry + +(define_split + [(parallel + [(set (match_operand 0 "register_operand" "") + (match_operator + 4 "cris_extend_operator" + [(mem (plus:SI + (match_operand:SI 1 "cris_bdap_operand" "") + (match_operand:SI 2 "cris_bdap_operand" "")))])) + (set (match_operand:SI 3 "register_operand" "") + (plus:SI (match_dup 1) + (match_dup 2)))])] + "(rtx_equal_p (operands[1], operands[3]) + || rtx_equal_p (operands[2], operands[3]))" + [(set (match_dup 3) (plus:SI (match_dup 1) (match_dup 2))) + (set (match_dup 0) (match_op_dup 4 [(match_dup 5)]))] + "operands[5] = gen_rtx_MEM (GET_MODE (XEXP (operands[4], 0)), + operands[3]);") + +;; op.S1 [rx=rx+i],ry + +(define_split + [(parallel + [(set (match_operand 0 "register_operand" "") + (match_operator + 5 "cris_orthogonal_operator" + [(match_operand 1 "register_operand" "") + (mem (plus:SI + (match_operand:SI 2 "cris_bdap_operand" "") + (match_operand:SI 3 "cris_bdap_operand" "")))])) + (set (match_operand:SI 4 "register_operand" "") + (plus:SI (match_dup 2) + (match_dup 3)))])] + "(rtx_equal_p (operands[4], operands[2]) + || rtx_equal_p (operands[4], operands[3]))" + [(set (match_dup 4) (plus:SI (match_dup 2) (match_dup 3))) + (set (match_dup 0) (match_op_dup 5 [(match_dup 1) (match_dup 6)]))] + "operands[6] = gen_rtx_MEM (GET_MODE (operands[0]), operands[4]);") + +;; op.S1 [rx=rx+rz.S2],ry + +(define_split + [(parallel + [(set (match_operand 0 "register_operand" "") + (match_operator + 6 "cris_orthogonal_operator" + [(match_operand 1 "register_operand" "") + (mem (plus:SI + (mult:SI (match_operand:SI 2 "register_operand" "") + (match_operand:SI 3 "const_int_operand" "")) + (match_operand:SI 4 "register_operand" "")))])) + (set (match_operand:SI 5 "register_operand" "") + (plus:SI (mult:SI (match_dup 2) + (match_dup 3)) + (match_dup 4)))])] + "REG_P (operands[4]) + && REG_P (operands[5]) + && REGNO (operands[5]) == REGNO (operands[4])" + [(set (match_dup 5) (plus:SI (mult:SI (match_dup 2) (match_dup 3)) + (match_dup 4))) + (set (match_dup 0) (match_op_dup 6 [(match_dup 1) (match_dup 7)]))] + "operands[7] = gen_rtx_MEM (GET_MODE (operands[0]), operands[5]);") + +;; op.S1 [rx=rx+rz.S2],ry (swapped) + +(define_split + [(parallel + [(set (match_operand 0 "register_operand" "") + (match_operator + 6 "cris_commutative_orth_op" + [(mem (plus:SI + (mult:SI (match_operand:SI 2 "register_operand" "") + (match_operand:SI 3 "const_int_operand" "")) + (match_operand:SI 4 "register_operand" ""))) + (match_operand 1 "register_operand" "")])) + (set (match_operand:SI 5 "register_operand" "") + (plus:SI (mult:SI (match_dup 2) + (match_dup 3)) + (match_dup 4)))])] + "REG_P (operands[4]) + && REG_P (operands[5]) + && REGNO (operands[5]) == REGNO (operands[4])" + [(set (match_dup 5) (plus:SI (mult:SI (match_dup 2) (match_dup 3)) + (match_dup 4))) + (set (match_dup 0) (match_op_dup 6 [(match_dup 7) (match_dup 1)]))] + "operands[7] = gen_rtx_MEM (GET_MODE (operands[0]), operands[5]);") + +;; op.S1 [rx=rx+i],ry (swapped) + +(define_split + [(parallel + [(set (match_operand 0 "register_operand" "") + (match_operator + 5 "cris_commutative_orth_op" + [(mem + (plus:SI (match_operand:SI 2 "cris_bdap_operand" "") + (match_operand:SI 3 "cris_bdap_operand" ""))) + (match_operand 1 "register_operand" "")])) + (set (match_operand:SI 4 "register_operand" "") + (plus:SI (match_dup 2) + (match_dup 3)))])] + "(rtx_equal_p (operands[4], operands[2]) + || rtx_equal_p (operands[4], operands[3]))" + [(set (match_dup 4) (plus:SI (match_dup 2) (match_dup 3))) + (set (match_dup 0) (match_op_dup 5 [(match_dup 6) (match_dup 1)]))] + "operands[6] = gen_rtx_MEM (GET_MODE (operands[0]), operands[4]);") + +;; op(s|u).S1 [rx=rx+rz.S2],ry + +(define_split + [(parallel + [(set (match_operand 0 "register_operand" "") + (match_operator + 6 "cris_operand_extend_operator" + [(match_operand 1 "register_operand" "") + (match_operator + 7 "cris_extend_operator" + [(mem (plus:SI + (mult:SI (match_operand:SI 2 "register_operand" "") + (match_operand:SI 3 "const_int_operand" "")) + (match_operand:SI 4 "register_operand" "")))])])) + (set (match_operand:SI 5 "register_operand" "") + (plus:SI (mult:SI (match_dup 2) + (match_dup 3)) + (match_dup 4)))])] + "REG_P (operands[4]) + && REG_P (operands[5]) + && REGNO (operands[5]) == REGNO (operands[4])" + [(set (match_dup 5) (plus:SI (mult:SI (match_dup 2) (match_dup 3)) + (match_dup 4))) + (set (match_dup 0) (match_op_dup 6 [(match_dup 1) (match_dup 8)]))] + "operands[8] = gen_rtx (GET_CODE (operands[7]), GET_MODE (operands[7]), + gen_rtx_MEM (GET_MODE (XEXP (operands[7], 0)), + operands[5]));") + +;; op(s|u).S1 [rx=rx+i],ry + +(define_split + [(parallel + [(set (match_operand 0 "register_operand" "") + (match_operator + 5 "cris_operand_extend_operator" + [(match_operand 1 "register_operand" "") + (match_operator + 6 "cris_extend_operator" + [(mem + (plus:SI (match_operand:SI 2 "cris_bdap_operand" "") + (match_operand:SI 3 "cris_bdap_operand" "") + ))])])) + (set (match_operand:SI 4 "register_operand" "") + (plus:SI (match_dup 2) + (match_dup 3)))])] + "(rtx_equal_p (operands[4], operands[2]) + || rtx_equal_p (operands[4], operands[3]))" + [(set (match_dup 4) (plus:SI (match_dup 2) (match_dup 3))) + (set (match_dup 0) (match_op_dup 5 [(match_dup 1) (match_dup 7)]))] + "operands[7] = gen_rtx (GET_CODE (operands[6]), GET_MODE (operands[6]), + gen_rtx_MEM (GET_MODE (XEXP (operands[6], 0)), + operands[4]));") + +;; op(s|u).S1 [rx=rx+rz.S2],ry (swapped, plus or bound) + +(define_split + [(parallel + [(set (match_operand 0 "register_operand" "") + (match_operator + 7 "cris_plus_or_bound_operator" + [(match_operator + 6 "cris_extend_operator" + [(mem (plus:SI + (mult:SI (match_operand:SI 2 "register_operand" "") + (match_operand:SI 3 "const_int_operand" "")) + (match_operand:SI 4 "register_operand" "")))]) + (match_operand 1 "register_operand" "")])) + (set (match_operand:SI 5 "register_operand" "") + (plus:SI (mult:SI (match_dup 2) + (match_dup 3)) + (match_dup 4)))])] + "REG_P (operands[4]) && REG_P (operands[5]) + && REGNO (operands[5]) == REGNO (operands[4])" + [(set (match_dup 5) (plus:SI (mult:SI (match_dup 2) (match_dup 3)) + (match_dup 4))) + (set (match_dup 0) (match_op_dup 6 [(match_dup 8) (match_dup 1)]))] + "operands[8] = gen_rtx (GET_CODE (operands[6]), GET_MODE (operands[6]), + gen_rtx_MEM (GET_MODE (XEXP (operands[6], 0)), + operands[5]));") + +;; op(s|u).S1 [rx=rx+i],ry (swapped, plus or bound) + +(define_split + [(parallel + [(set (match_operand 0 "register_operand" "") + (match_operator + 6 "cris_plus_or_bound_operator" + [(match_operator + 5 "cris_extend_operator" + [(mem (plus:SI + (match_operand:SI 2 "cris_bdap_operand" "") + (match_operand:SI 3 "cris_bdap_operand" "")))]) + (match_operand 1 "register_operand" "")])) + (set (match_operand:SI 4 "register_operand" "") + (plus:SI (match_dup 2) + (match_dup 3)))])] + "(rtx_equal_p (operands[4], operands[2]) + || rtx_equal_p (operands[4], operands[3]))" + [(set (match_dup 4) (plus:SI (match_dup 2) (match_dup 3))) + (set (match_dup 0) (match_op_dup 6 [(match_dup 7) (match_dup 1)]))] + "operands[7] = gen_rtx (GET_CODE (operands[5]), GET_MODE (operands[5]), + gen_rtx_MEM (GET_MODE (XEXP (operands[5], 0)), + operands[4]));") + +;; Splits for addressing prefixes that have no side-effects, so we can +;; fill a delay slot. Never split if we lose something, though. + +;; If we have a +;; move [indirect_ref],rx +;; where indirect ref = {const, [r+], [r]}, it costs as much as +;; move indirect_ref,rx +;; move [rx],rx +;; Take care not to allow indirect_ref = register. + +;; We're not allowed to generate copies of registers with different mode +;; until after reload; copying pseudos upsets reload. CVS as of +;; 2001-08-24, unwind-dw2-fde.c, _Unwind_Find_FDE ICE in +;; cselib_invalidate_regno. + +(define_split + [(set (match_operand 0 "register_operand" "") + (match_operand 1 "indirect_operand" ""))] + "reload_completed + && REG_P (operands[0]) + && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD + && (GET_CODE (XEXP (operands[1], 0)) == MEM + || CONSTANT_P (XEXP (operands[1], 0)))" + [(set (match_dup 2) (match_dup 4)) + (set (match_dup 0) (match_dup 3))] + "operands[2] = gen_rtx_REG (Pmode, REGNO (operands[0])); + operands[3] = gen_rtx_MEM (GET_MODE (operands[0]), operands[2]); + operands[4] = XEXP (operands[1], 0);") + +;; As the above, but MOVS and MOVU. + +(define_split + [(set (match_operand 0 "register_operand" "") + (match_operator + 4 "cris_extend_operator" + [(match_operand 1 "indirect_operand" "")]))] + "reload_completed + && REG_P (operands[0]) + && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD + && (GET_CODE (XEXP (operands[1], 0)) == MEM + || CONSTANT_P (XEXP (operands[1], 0)))" + [(set (match_dup 2) (match_dup 5)) + (set (match_dup 0) (match_op_dup 4 [(match_dup 3)]))] + "operands[2] = gen_rtx_REG (Pmode, REGNO (operands[0])); + operands[3] = gen_rtx_MEM (GET_MODE (XEXP (operands[4], 0)), operands[2]); + operands[5] = XEXP (operands[1], 0);") + +;; Various peephole optimizations. +;; +;; Watch out: when you exchange one set of instructions for another, the +;; condition codes setting must be the same, or you have to CC_INIT or +;; whatever is appropriate, in the pattern before you emit the +;; assembly text. This is best done here, not in cris_notice_update_cc, +;; to keep changes local to their cause. +;; +;; Do not add patterns that you do not know will be matched. +;; Please also add a self-contained test-case. + +;; We have trouble with and:s and shifts. Maybe something is broken in +;; gcc? Or it could just be that bitfield insn expansion is a bit +;; suboptimal when not having extzv insns. + +(define_peephole + [(set (match_operand 0 "register_operand" "=r") + (ashiftrt:SI (match_dup 0) + (match_operand:SI 1 "const_int_operand" "n"))) + (set (match_dup 0) + (and:SI (match_dup 0) + (match_operand 2 "const_int_operand" "n")))] + "INTVAL (operands[2]) > 31 + && INTVAL (operands[2]) < 255 + && INTVAL (operands[1]) > 23" + +;; The m flag should be ignored, because this will be a *byte* "and" +;; operation. + + "* +{ + cc_status.flags |= CC_NOT_NEGATIVE; + + return \"lsrq %1,%0\;and.b %2,%0\"; +}") + +(define_peephole + [(set (match_operand 0 "register_operand" "=r") + (ashiftrt:SI (match_dup 0) + (match_operand:SI 1 "const_int_operand" "n"))) + (set (match_dup 0) + (and:SI (match_dup 0) + (match_operand 2 "const_int_operand" "n")))] + "INTVAL (operands[2]) > 31 + && INTVAL (operands[2]) < 65535 + && INTVAL (operands[2]) != 255 + && INTVAL (operands[1]) > 15" + +;; The m flag should be ignored, because this will be a *word* "and" +;; operation. + + "* +{ + cc_status.flags |= CC_NOT_NEGATIVE; + + return \"lsrq %1,%0\;and.w %2,%0\"; +}") + +(define_peephole + [(set (match_operand 0 "register_operand" "=r") + (lshiftrt:SI (match_dup 0) + (match_operand:SI 1 "const_int_operand" "n"))) + (set (match_dup 0) + (and:SI (match_dup 0) + (match_operand 2 "const_int_operand" "n")))] + "INTVAL (operands[2]) > 31 + && INTVAL (operands[2]) < 255 + && INTVAL (operands[1]) > 23" + +;; The m flag should be ignored, because this will be a *byte* "and" +;; operation. + + "* +{ + cc_status.flags |= CC_NOT_NEGATIVE; + + return \"lsrq %1,%0\;and.b %2,%0\"; +}") + +(define_peephole + [(set (match_operand 0 "register_operand" "=r") + (lshiftrt:SI (match_dup 0) + (match_operand:SI 1 "const_int_operand" "n"))) + (set (match_dup 0) + (and:SI (match_dup 0) + (match_operand 2 "const_int_operand" "n")))] + "INTVAL (operands[2]) > 31 && INTVAL (operands[2]) < 65535 + && INTVAL (operands[2]) != 255 + && INTVAL (operands[1]) > 15" + +;; The m flag should be ignored, because this will be a *word* "and" +;; operation. + + "* +{ + cc_status.flags |= CC_NOT_NEGATIVE; + + return \"lsrq %1,%0\;and.w %2,%0\"; +}") + + +;; Change +;; add.d n,rx +;; move [rx],ry +;; into +;; move [rx=rx+n],ry +;; when -128 <= n <= 127. +;; This will reduce the size of the assembler code for n = [-128..127], +;; and speed up accordingly. + +(define_peephole + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (match_operand:SI 1 "register_operand" "0") + (match_operand:SI 2 "const_int_operand" "n"))) + (set (match_operand 3 "register_operand" "=r") + (mem (match_dup 0)))] + "GET_MODE (operands[3]) != DImode + && REGNO (operands[3]) != REGNO (operands[0]) + && (BASE_P (operands[1]) || BASE_P (operands[2])) + && ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J') + && ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'N') + && (INTVAL (operands[2]) >= -128 && INTVAL (operands[2]) < 128)" + "move.%s3 [%0=%1%S2],%3") + +;; Vice versa: move ry,[rx=rx+n] + +(define_peephole + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (match_operand:SI 1 "register_operand" "0") + (match_operand:SI 2 "const_int_operand" "n"))) + (set (mem (match_dup 0)) + (match_operand 3 "register_operand" "=r"))] + "GET_MODE (operands[3]) != DImode + && REGNO (operands[3]) != REGNO (operands[0]) + && (BASE_P (operands[1]) || BASE_P (operands[2])) + && ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J') + && ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'N') + && (INTVAL (operands[2]) >= -128 && INTVAL (operands[2]) < 128)" + "move.%s3 %3,[%0=%1%S2]" + [(set_attr "cc" "none")]) + +;; As above, change: +;; add.d n,rx +;; op.d [rx],ry +;; into: +;; op.d [rx=rx+n],ry +;; Saves when n = [-128..127]. +;; +;; Splitting and joining combinations for side-effect modes are slightly +;; out of hand. They probably will not save the time they take typing in, +;; not to mention the bugs that creep in. FIXME: Get rid of as many of +;; the splits and peepholes as possible. + +(define_peephole + [(set (match_operand:SI 0 "register_operand" "=r") + (plus:SI (match_operand:SI 1 "register_operand" "0") + (match_operand:SI 2 "const_int_operand" "n"))) + (set (match_operand 3 "register_operand" "=r") + (match_operator 4 "cris_orthogonal_operator" + [(match_dup 3) + (mem (match_dup 0))]))] + "GET_MODE (operands[3]) != DImode + && REGNO (operands[0]) != REGNO (operands[3]) + && ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J') + && ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'N') + && INTVAL (operands[2]) >= -128 + && INTVAL (operands[2]) <= 127" + "%x4.%s3 [%0=%1%S2],%3") + +;; Sometimes, for some reason the pattern +;; move x,rx +;; add y,rx +;; move [rx],rz +;; will occur. Solve this, and likewise for to-memory. + +(define_peephole + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") + (match_operand:SI 1 "cris_bdap_biap_operand" "r,>Ri,r,>Ri")) + (set (match_dup 0) + (plus:SI (match_operand:SI 2 "cris_bdap_biap_operand" "0,0,r>Ri,r") + (match_operand:SI 3 "cris_bdap_biap_operand" "r>Ri,r,0,0"))) + (set (match_operand 4 "register_operand" "=r,r,r,r") + (mem (match_dup 0)))] + "(rtx_equal_p (operands[2], operands[0]) + || rtx_equal_p (operands[3], operands[0])) + && cris_side_effect_mode_ok (PLUS, operands, 0, + (REG_S_P (operands[1]) + ? 1 + : (rtx_equal_p (operands[2], operands[0]) + ? 3 : 2)), + (! REG_S_P (operands[1]) + ? 1 + : (rtx_equal_p (operands[2], operands[0]) + ? 3 : 2)), + -1, 4)" + "@ + move.%s4 [%0=%1%S3],%4 + move.%s4 [%0=%3%S1],%4 + move.%s4 [%0=%1%S2],%4 + move.%s4 [%0=%2%S1],%4") + +;; As above but to memory. + +(define_peephole + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") + (match_operand:SI 1 "cris_bdap_biap_operand" "r,>Ri,r,>Ri")) + (set (match_dup 0) + (plus:SI (match_operand:SI 2 "cris_bdap_biap_operand" "0,0,r>Ri,r") + (match_operand:SI 3 "cris_bdap_biap_operand" "r>Ri,r,0,0"))) + (set (mem (match_dup 0)) + (match_operand 4 "register_operand" "=r,r,r,r"))] + "(rtx_equal_p (operands[2], operands[0]) + || rtx_equal_p (operands[3], operands[0])) + && cris_side_effect_mode_ok (PLUS, operands, 0, + (REG_S_P (operands[1]) + ? 1 + : (rtx_equal_p (operands[2], operands[0]) + ? 3 : 2)), + (! REG_S_P (operands[1]) + ? 1 + : (rtx_equal_p (operands[2], operands[0]) + ? 3 : 2)), + -1, 4)" + "@ + move.%s4 %4,[%0=%1%S3] + move.%s4 %4,[%0=%3%S1] + move.%s4 %4,[%0=%1%S2] + move.%s4 %4,[%0=%2%S1]" + [(set_attr "cc" "none")]) + + +;; As the move from-memory above, but with an operation. + +(define_peephole + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") + (match_operand:SI 1 "cris_bdap_biap_operand" "r,>Ri,r,>Ri")) + (set (match_dup 0) + (plus:SI (match_operand:SI 2 "cris_bdap_biap_operand" "0,0,r>Ri,r") + (match_operand:SI 3 "cris_bdap_biap_operand" "r>Ri,r,0,0"))) + (set (match_operand 4 "register_operand" "=r,r,r,r") + (match_operator 5 "cris_orthogonal_operator" + [(match_dup 3) + (mem (match_dup 0))]))] + "(rtx_equal_p (operands[2], operands[0]) + || rtx_equal_p (operands[3], operands[0])) + && cris_side_effect_mode_ok (PLUS, operands, 0, + (REG_S_P (operands[1]) + ? 1 + : (rtx_equal_p (operands[2], operands[0]) + ? 3 : 2)), + (! REG_S_P (operands[1]) + ? 1 + : (rtx_equal_p (operands[2], operands[0]) + ? 3 : 2)), + -1, 4)" + "@ + %x5.%s4 [%0=%1%S3],%4 + %x5.%s4 [%0=%3%S1],%4 + %x5.%s4 [%0=%1%S2],%4 + %x5.%s4 [%0=%2%S1],%4") + +;; Same, but with swapped operands (and commutative operation). + +(define_peephole + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") + (match_operand:SI 1 "cris_bdap_biap_operand" "r,>Ri,r,>Ri")) + (set (match_dup 0) + (plus:SI (match_operand:SI 2 "cris_bdap_biap_operand" "0,0,r>Ri,r") + (match_operand:SI 3 "cris_bdap_biap_operand" "r>Ri,r,0,0"))) + (set (match_operand 4 "register_operand" "=r,r,r,r") + (match_operator 5 "cris_commutative_orth_op" + [(mem (match_dup 0)) + (match_dup 3)]))] + "(rtx_equal_p (operands[2], operands[0]) + || rtx_equal_p (operands[3], operands[0])) + && cris_side_effect_mode_ok (PLUS, operands, 0, + (REG_S_P (operands[1]) + ? 1 + : (rtx_equal_p (operands[2], operands[0]) + ? 3 : 2)), + (! REG_S_P (operands[1]) + ? 1 + : (rtx_equal_p (operands[2], operands[0]) + ? 3 : 2)), + -1, 4)" + "@ + %x5.%s4 [%0=%1%S3],%4 + %x5.%s4 [%0=%3%S1],%4 + %x5.%s4 [%0=%1%S2],%4 + %x5.%s4 [%0=%2%S1],%4") + +;; Another spotted bad code: +;; move rx,ry +;; move [ry],ry + +(define_peephole + [(set (match_operand:SI 0 "register_operand" "=r") + (match_operand:SI 1 "register_operand" "r")) + (set (match_operand 2 "register_operand" "=r") + (mem (match_dup 0)))] + "REGNO (operands[0]) == REGNO (operands[2]) + && GET_MODE_SIZE (GET_MODE (operands[2])) <= UNITS_PER_WORD" + "move.%s2 [%1],%0" + [(set_attr "slottable" "yes")]) + +;; And a simple variant with extended operand. + +(define_peephole + [(set (match_operand:SI 0 "register_operand" "=r") + (match_operand:SI 1 "register_operand" "r")) + (set (match_operand 2 "register_operand" "=r") + (match_operator 3 "cris_extend_operator" [(mem (match_dup 0))]))] + "REGNO (operands[0]) == REGNO (operands[2]) + && GET_MODE_SIZE (GET_MODE (operands[2])) <= UNITS_PER_WORD" + "mov%e3.%m3 [%1],%0" + [(set_attr "slottable" "yes")]) + +;; Here are all peepholes that have a saved testcase. +;; Do not add new peepholes without testcases. + +;; peep-1: +;; move.d [r10+16],r9 +;; and.d r12,r9 +;; change to +;; and.d [r10+16],r12,r9 +;; With generalization of the operation, the size and the addressing mode. +;; This seems to be the result of a quirk in register allocation +;; missing the three-operand cases when having different predicates. +;; Maybe that it matters that it is a commutative operation. +;; This pattern helps that situation, but there's still the increased +;; register pressure. +;; Note that adding the noncommutative variant did not show any matches +;; in ipps and cc1, so it's not here. + +(define_peephole + [(set (match_operand 0 "register_operand" "=r,r,r,r") + (mem (plus:SI + (match_operand:SI 1 "cris_bdap_biap_operand" "r,r>Ri,r,r>Ri") + (match_operand:SI 2 "cris_bdap_biap_operand" "r>Ri,r,r>Ri,r")))) + (set (match_dup 0) + (match_operator 5 "cris_commutative_orth_op" + [(match_operand 3 "register_operand" "0,0,r,r") + (match_operand 4 "register_operand" "r,r,0,0")]))] + "(rtx_equal_p (operands[3], operands[0]) + || rtx_equal_p (operands[4], operands[0])) + && ! rtx_equal_p (operands[3], operands[4]) + && (REG_S_P (operands[1]) || REG_S_P (operands[2])) + && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD" + "@ + %x5.%s0 [%1%S2],%4,%0 + %x5.%s0 [%2%S1],%4,%0 + %x5.%s0 [%1%S2],%3,%0 + %x5.%s0 [%2%S1],%3,%0") + +;; peep-2: +;; I cannot tell GCC (2.1, 2.7.2) how to correctly reload an instruction +;; that looks like +;; and.b some_byte,const,reg_32 +;; where reg_32 is the destination of the "three-address" code optimally. +;; It should be: +;; movu.b some_byte,reg_32 +;; and.b const,reg_32 +;; but is turns into: +;; move.b some_byte,reg_32 +;; and.d const,reg_32 +;; Fix it here. + +(define_peephole + [(set (match_operand:SI 0 "register_operand" "=r") + (match_operand:SI 1 "nonimmediate_operand" "rm")) + (set (match_operand:SI 2 "register_operand" "=0") + (and:SI (match_dup 0) + (match_operand:SI 3 "const_int_operand" "n")))] + + ;; Since the size of the memory access could be made different here, + ;; don't do this for a mem-volatile access. + + "REGNO (operands[2]) == REGNO (operands[0]) + && INTVAL (operands[3]) <= 65535 && INTVAL (operands[3]) >= 0 + && ! CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'I') + && (GET_CODE (operands[1]) != MEM || ! MEM_VOLATILE_P (operands[1]))" + "* +{ + if (CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'O')) + return \"movu.%z3 %1,%0\;andq %b3,%0\"; + + cc_status.flags |= CC_NOT_NEGATIVE; + + return \"movu.%z3 %1,%0\;and.%z3 %3,%0\"; +}") + +;; peep-3 + +(define_peephole + [(set (match_operand 0 "register_operand" "=r") + (match_operand 1 "nonimmediate_operand" "rm")) + (set (match_operand:SI 2 "register_operand" "=r") + (and:SI (subreg:SI (match_dup 0) 0) + (match_operand 3 "const_int_operand" "n")))] + + ;; Since the size of the memory access could be made different here, + ;; don't do this for a mem-volatile access. + + "REGNO (operands[0]) == REGNO (operands[2]) + && INTVAL (operands[3]) > 0 + && INTVAL (operands[3]) <= 65535 + && ! CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'I') + && (GET_CODE (operands[1]) != MEM || ! MEM_VOLATILE_P (operands[1]))" + "* +{ + if (CONST_OK_FOR_LETTER_P (INTVAL (operands[3]), 'O')) + return \"movu.%z3 %1,%0\;andq %b3,%0\"; + + cc_status.flags |= CC_NOT_NEGATIVE; + + return \"movu.%z3 %1,%0\;and.%z3 %3,%0\"; +}") + +;; Local variables: +;; mode:emacs-lisp +;; comment-start: ";; " +;; eval: (set-syntax-table (copy-sequence (syntax-table))) +;; eval: (modify-syntax-entry ?[ "(]") +;; eval: (modify-syntax-entry ?] ")[") +;; eval: (modify-syntax-entry ?{ "(}") +;; eval: (modify-syntax-entry ?} "){") +;; eval: (setq indent-tabs-mode t) +;; End: diff --git a/gcc/config/cris/cris_abi_symbol.c b/gcc/config/cris/cris_abi_symbol.c new file mode 100644 index 00000000000..a30d2052f53 --- /dev/null +++ b/gcc/config/cris/cris_abi_symbol.c @@ -0,0 +1,56 @@ +/* Define symbol to recognize CRIS ABI version 2, for a.out use. + Contributed by Axis Communications. + Written by Hans-Peter Nilsson , c:a 1992. + + Copyright (C) 2000, 2001 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC 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. + +In addition to the permissions in the GNU General Public License, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file with other programs, and to distribute +those programs without any restriction coming from the use of this +file. (The General Public License restrictions do apply in other +respects; for example, they cover modification of the file, and +distribution when not linked into another program.) + +This file 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 this program; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + + As a special exception, if you link this library with files, some of + which are compiled with GCC, this library does not by itself cause + the resulting object or executable to be covered by the GNU General + Public License. + This exception does not however invalidate any other reasons why + the executable file or object might be covered by the GNU General + Public License. */ + +#include "config.h" + +#ifdef __AOUT__ + +/* ELF support was not released before the ABI was changed, so we + restrict this awkwardness to a.out. This symbol is for gdb to + recognize, so it can debug both old and new programs successfully. */ +__asm__ (".global " CRIS_ABI_VERSION_SYMBOL_STRING); +__asm__ (".set " CRIS_ABI_VERSION_SYMBOL_STRING ",0"); + +#else /* not __AOUT__ */ + +/* The file must not be empty (declaration/definition-wise) according to + ISO, IIRC. */ +extern int _Dummy; + +#endif /* not __AOUT__ */ diff --git a/gcc/config/cris/linux.h b/gcc/config/cris/linux.h new file mode 100644 index 00000000000..b02e19e2a41 --- /dev/null +++ b/gcc/config/cris/linux.h @@ -0,0 +1,134 @@ +/* Definitions for GCC. Part of the machine description for CRIS. + Copyright (C) 2001 Free Software Foundation, Inc. + Contributed by Axis Communications. Written by Hans-Peter Nilsson. + +This file is part of GCC. + +GCC 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. + +GCC 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 GCC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + + +/* After the first "Node:" comment comes all preprocessor directives and + attached declarations described in the info files, the "Using and + Porting GCC" manual (uapgcc), in the same order as found in the "Target + macros" section in the gcc-2.9x CVS edition of 2000-03-17. FIXME: Not + really, but needs an update anyway. + + There is no generic copy-of-uapgcc comment, you'll have to see uapgcc + for that. If applicable, there is a CRIS-specific comment. The order + of macro definitions follow the order in the manual. Every section in + the manual (node in the info pages) has an introductory `Node: + ' comment. If no macros are defined for a section, only + the section-comment is present. */ + +/* This file defines the macros for cris-axis-linux-gnu that are not + covered by cris.h, elfos.h and (config/)linux.h. */ + + +/* Node: Instruction Output */ + +#undef USER_LABEL_PREFIX +#define USER_LABEL_PREFIX "" + +/* Node: Driver */ +/* These macros are CRIS-specific, but used in target driver macros. */ + +#undef CRIS_CPP_SUBTARGET_SPEC +#define CRIS_CPP_SUBTARGET_SPEC \ + "-D__linux__ -D__unix__ -D__ELF__\ + %{pthread:-D_REENTRANT}\ + %{fPIC|fpic: -D__PIC__ -D__pic__}\ + %{!fleading-underscore:-fno-leading-underscore -D__NO_UNDERSCORES__}\ + %{!march=*:%{!cpu=*:-D__arch_v10 -D__CRIS_arch_version=10}}\ + %{!ansi:%{!std=*:%{!undef:-Dlinux -Dunix}\ + -Asystem(unix) -Asystem(posix) -Acpu(cris) -Amachine(cris)}}" + +#undef CRIS_CC1_SUBTARGET_SPEC +#define CRIS_CC1_SUBTARGET_SPEC \ + "%{!march=*:%{!cpu=*:-march=v10}}" + +#undef CRIS_ASM_SUBTARGET_SPEC +#define CRIS_ASM_SUBTARGET_SPEC \ + "--em=criself\ + %{!fleading-underscore:--no-underscore}\ + %{fPIC|fpic: --pic}" + +/* Provide a legacy -mlinux option. */ +#undef CRIS_SUBTARGET_SWITCHES +#define CRIS_SUBTARGET_SWITCHES \ + {"linux", 0, ""}, \ + {"gotplt", -TARGET_MASK_AVOID_GOTPLT, ""}, \ + {"no-gotplt", TARGET_MASK_AVOID_GOTPLT, \ + N_("Together with -fpic and -fPIC, do not use GOTPLT references")}, + +#undef CRIS_SUBTARGET_DEFAULT +#define CRIS_SUBTARGET_DEFAULT \ + (TARGET_MASK_SVINTO \ + + TARGET_MASK_ETRAX4_ADD \ + + TARGET_MASK_ALIGN_BY_32 \ + + TARGET_MASK_ELF \ + + TARGET_MASK_LINUX) + +#undef CRIS_DEFAULT_CPU_VERSION +#define CRIS_DEFAULT_CPU_VERSION CRIS_CPU_NG + +#undef CRIS_SUBTARGET_VERSION +#define CRIS_SUBTARGET_VERSION " - cris-axis-linux-gnu" + + +/* Redefine what was in svr4.h. Include order madness makes it less + useful to include (config/)linux.h after cris.h. (config/)linux.h + includes svr4.h which undef:s lots of supposedly arch-specific macros + carefully defined by cris.h. */ +#undef LIB_SPEC +#define LIB_SPEC "%{!shared:%{!symbolic:-lc}}" + +/* We need an -rpath-link to ld.so.1, and presumably to each directory + specified with -B. */ +#undef CRIS_LINK_SUBTARGET_SPEC +#define CRIS_LINK_SUBTARGET_SPEC \ + "-mcrislinux\ + -rpath-link include/asm/../..%s\ + %{shared} %{static}\ + %{symbolic:-Bdynamic} %{shlib:-Bdynamic} %{static:-Bstatic}\ + %{!shared:%{!static:%{rdynamic:-export-dynamic}}}\ + %{O2|O3: --gc-sections}" + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC \ + "%{!shared:\ + %{pg:gcrt1.o%s}\ + %{!pg:\ + %{p:gcrt1.o%s}\ + %{!p:\ + %{profile:gcrt1.o%s}\ + %{!profile:crt1.o%s}}}}\ + crti.o%s\ + %{!shared:crtbegin.o%s}\ + %{shared:crtbeginS.o%s}" + + +/* Node: Sections */ + +/* GNU/Linux has crti and crtn and does not need the + FORCE_INIT_SECTION_ALIGN trick in cris.h. */ +#undef FORCE_INIT_SECTION_ALIGN + +/* + * Local variables: + * eval: (c-set-style "gnu") + * indent-tabs-mode: t + * End: + */ diff --git a/gcc/config/cris/mulsi3.asm b/gcc/config/cris/mulsi3.asm new file mode 100644 index 00000000000..3c482e7f2a9 --- /dev/null +++ b/gcc/config/cris/mulsi3.asm @@ -0,0 +1,227 @@ +;; This code used to be expanded through interesting expansions in +;; the machine description, compiled from this code: +;; +;; #ifdef L_mulsi3 +;; long __Mul (unsigned long a, unsigned long b) __attribute__ ((__const__)); +;; +;; /* This must be compiled with the -mexpand-mul flag, to synthesize the +;; multiplication from the mstep instructions. The check for +;; smaller-size multiplication pays off in the order of .5-10%; +;; estimated median 1%, depending on application. +;; FIXME: It can be further optimized if we go to assembler code, as +;; gcc 2.7.2 adds a few unnecessary instructions and does not put the +;; basic blocks in optimal order. */ +;; long +;; __Mul (unsigned long a, unsigned long b) +;; { +;; #if defined (__CRIS_arch_version) && __CRIS_arch_version >= 10 +;; /* In case other code is compiled without -march=v10, they will +;; contain calls to __Mul, regardless of flags at link-time. The +;; "else"-code below will work, but is unnecessarily slow. This +;; sometimes cuts a few minutes off from simulation time by just +;; returning a "mulu.d". */ +;; return a * b; +;; #else +;; unsigned long min; +;; +;; /* Get minimum via the bound insn. */ +;; min = a < b ? a : b; +;; +;; /* Can we omit computation of the high part? */ +;; if (min > 65535) +;; /* No. Perform full multiplication. */ +;; return a * b; +;; else +;; { +;; /* Check if both operands are within 16 bits. */ +;; unsigned long max; +;; +;; /* Get maximum, by knowing the minimum. +;; This will partition a and b into max and min. +;; This is not currently something GCC understands, +;; so do this trick by asm. */ +;; __asm__ ("xor %1,%0\n\txor %2,%0" +;; : "=r" (max) +;; : "r" (b), "r" (a), "0" (min)); +;; +;; if (max > 65535) +;; /* Make GCC understand that only the low part of "min" will be +;; used. */ +;; return max * (unsigned short) min; +;; else +;; /* Only the low parts of both operands are necessary. */ +;; return ((unsigned short) max) * (unsigned short) min; +;; } +;; #endif /* not __CRIS_arch_version >= 10 */ +;; } +;; #endif /* L_mulsi3 */ +;; +;; That approach was abandoned since the caveats outweighted the +;; benefits. The expand-multiplication machinery is also removed, so you +;; can't do this anymore. +;; +;; For doubters of there being any benefits, some where: insensitivity to: +;; - ABI changes (mostly for experimentation) +;; - assembler syntax differences (mostly debug format). +;; - insn scheduling issues. +;; Most ABI experiments will presumably happen with arches with mul insns, +;; so that argument doesn't really hold anymore, and it's unlikely there +;; being new arch variants needing insn scheduling and not having mul +;; insns. + +;; ELF and a.out have different syntax for local labels: the "wrong" +;; one may not be omitted from the object. +#undef L +#ifdef __AOUT__ +# define L(x) x +#else +# define L(x) .x +#endif + + .global ___Mul + .type ___Mul,@function +___Mul: +#if defined (__CRIS_arch_version) && __CRIS_arch_version >= 10 + ret + mulu.d $r11,$r10 +#else + move.d $r10,$r12 + move.d $r11,$r9 + bound.d $r12,$r9 + cmpu.w 65535,$r9 + bls L(L3) + move.d $r12,$r13 + + movu.w $r11,$r9 + lslq 16,$r13 + mstep $r9,$r13 + mstep $r9,$r13 + mstep $r9,$r13 + mstep $r9,$r13 + mstep $r9,$r13 + mstep $r9,$r13 + mstep $r9,$r13 + mstep $r9,$r13 + mstep $r9,$r13 + mstep $r9,$r13 + mstep $r9,$r13 + mstep $r9,$r13 + mstep $r9,$r13 + mstep $r9,$r13 + mstep $r9,$r13 + mstep $r9,$r13 + clear.w $r10 + test.d $r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + movu.w $r12,$r12 + move.d $r11,$r9 + clear.w $r9 + test.d $r9 + mstep $r12,$r9 + mstep $r12,$r9 + mstep $r12,$r9 + mstep $r12,$r9 + mstep $r12,$r9 + mstep $r12,$r9 + mstep $r12,$r9 + mstep $r12,$r9 + mstep $r12,$r9 + mstep $r12,$r9 + mstep $r12,$r9 + mstep $r12,$r9 + mstep $r12,$r9 + mstep $r12,$r9 + mstep $r12,$r9 + mstep $r12,$r9 + add.w $r9,$r10 + lslq 16,$r10 + ret + add.d $r13,$r10 + +L(L3): + move.d $r9,$r10 + xor $r11,$r10 + xor $r12,$r10 + cmpu.w 65535,$r10 + bls L(L5) + movu.w $r9,$r13 + + movu.w $r13,$r13 + move.d $r10,$r9 + lslq 16,$r9 + mstep $r13,$r9 + mstep $r13,$r9 + mstep $r13,$r9 + mstep $r13,$r9 + mstep $r13,$r9 + mstep $r13,$r9 + mstep $r13,$r9 + mstep $r13,$r9 + mstep $r13,$r9 + mstep $r13,$r9 + mstep $r13,$r9 + mstep $r13,$r9 + mstep $r13,$r9 + mstep $r13,$r9 + mstep $r13,$r9 + mstep $r13,$r9 + clear.w $r10 + test.d $r10 + mstep $r13,$r10 + mstep $r13,$r10 + mstep $r13,$r10 + mstep $r13,$r10 + mstep $r13,$r10 + mstep $r13,$r10 + mstep $r13,$r10 + mstep $r13,$r10 + mstep $r13,$r10 + mstep $r13,$r10 + mstep $r13,$r10 + mstep $r13,$r10 + mstep $r13,$r10 + mstep $r13,$r10 + mstep $r13,$r10 + mstep $r13,$r10 + lslq 16,$r10 + ret + add.d $r9,$r10 + +L(L5): + movu.w $r9,$r9 + lslq 16,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + mstep $r9,$r10 + ret + mstep $r9,$r10 +#endif +L(Lfe1): + .size ___Mul,L(Lfe1)-___Mul diff --git a/gcc/config/cris/t-aout b/gcc/config/cris/t-aout new file mode 100644 index 00000000000..a21052e42f0 --- /dev/null +++ b/gcc/config/cris/t-aout @@ -0,0 +1,12 @@ +LIB2FUNCS_STATIC_EXTRA = \ + tmpabi_symbol.c $(srcdir)/config/cris/mulsi3.asm + +MULTILIB_OPTIONS = melinux +MULTILIB_DIRNAMES = elinux +MULTILIB_EXTRA_OPTS = mbest-lib-options + +INSTALL_LIBGCC = install-multilib +LIBGCC = stmp-multilib + +tmpabi_symbol.c: $(srcdir)/config/cris/cris_abi_symbol.c + cp $(srcdir)/config/cris/cris_abi_symbol.c $@ diff --git a/gcc/config/cris/t-cris b/gcc/config/cris/t-cris new file mode 100644 index 00000000000..5e7edf4d6d2 --- /dev/null +++ b/gcc/config/cris/t-cris @@ -0,0 +1,43 @@ +# +# t-cris +# +# The Makefile fragment to include when compiling gcc et al for CRIS. +# +# +# The makefile macros etc. are included in the order found in the +# section "Target Fragment" in the gcc info-files (or the paper copy) of +# "Using and Porting GCC" +# +# Don't run fixproto +STMP_FIXPROTO = + +LIB2FUNCS_EXTRA = _udivsi3.c _divsi3.c _umodsi3.c _modsi3.c +CRIS_LIB1CSRC = $(srcdir)/config/cris/arit.c + +FPBIT = tmplibgcc_fp_bit.c +DPBIT = dp-bit.c + +dp-bit.c: $(srcdir)/config/fp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' > dp-bit.c + cat $(srcdir)/config/fp-bit.c >> dp-bit.c + +# Use another name to avoid confusing SUN make, if support for +# it is reinstated elsewhere. Prefixed with "tmplibgcc" means +# "make clean" will wipe it. We define a few L_ thingies +# because we can't select them individually through FPBIT_FUNCS; +# see above. +tmplibgcc_fp_bit.c: $(srcdir)/config/fp-bit.c + echo '#define FLOAT_BIT_ORDER_MISMATCH' > $@ + echo '#define FLOAT' >> $@ + cat $(srcdir)/config/fp-bit.c >> $@ + +# The fixed-point arithmetic code is in one file, arit.c, +# similar to libgcc2.c (or the old libgcc1.c). We need to +# "split it up" with one file per define. +$(LIB2FUNCS_EXTRA): $(CRIS_LIB1CSRC) + name=`echo $@ | sed -e 's,.*/,,' | sed -e 's,.c$$,,'`; \ + echo "#define L$$name" > tmp-$@ \ + && echo '#include "$<"' >> tmp-$@ \ + && mv -f tmp-$@ $@ + +TARGET_LIBGCC2_CFLAGS = -Dinhibit_libc diff --git a/gcc/config/cris/t-elfmulti b/gcc/config/cris/t-elfmulti new file mode 100644 index 00000000000..f4e4fcef64e --- /dev/null +++ b/gcc/config/cris/t-elfmulti @@ -0,0 +1,16 @@ +LIB2FUNCS_STATIC_EXTRA = $(srcdir)/config/cris/mulsi3.asm +EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o +MULTILIB_OPTIONS = march=v10 +MULTILIB_DIRNAMES = v10 +MULTILIB_MATCHES = \ + march?v10=mcpu?etrax100lx \ + march?v10=mcpu?ng \ + march?v10=march?etrax100lx \ + march?v10=march?ng \ + march?v10=march?v11 \ + march?v10=mcpu?v11 \ + march?v10=mcpu?v10 +MULTILIB_EXTRA_OPTS = mbest-lib-options +INSTALL_LIBGCC = install-multilib +LIBGCC = stmp-multilib +CRTSTUFF_T_CFLAGS = $(LIBGCC2_CFLAGS) -moverride-best-lib-options diff --git a/gcc/config/cris/t-linux b/gcc/config/cris/t-linux new file mode 100644 index 00000000000..43c3acd2ebb --- /dev/null +++ b/gcc/config/cris/t-linux @@ -0,0 +1,6 @@ +TARGET_LIBGCC2_CFLAGS += -fPIC +CRTSTUFF_T_CFLAGS_S = $(TARGET_LIBGCC2_CFLAGS) + +# Override t-slibgcc-elf-ver to export some libgcc symbols with +# the symbol versions that glibc used. +SHLIB_MAPFILES += $(srcdir)/config/libgcc-glibc.ver diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi index 90cc3011cac..b4fbbae4c07 100644 --- a/gcc/doc/install.texi +++ b/gcc/doc/install.texi @@ -1783,6 +1783,45 @@ can also be obtained from: @uref{http://www.elec.canterbury.ac.nz/c4x/,,http://www.elec.canterbury.ac.nz/c4x/} @end itemize +@html +

+
+@end html +@heading @anchor{cris}CRIS + +CRIS is the CPU architecture in Axis Communications ETRAX system-on-a-chip +series. These are used in embedded applications. + +@ifnothtml +@xref{CRIS Options,, CRIS Options, gcc, Using and Porting the GNU Compiler +Collection (GCC)}, +@end ifnothtml +@ifhtml +See ``CRIS Options'' in the main manual +@end ifhtml +for a list of CRIS-specific options. + +There are a few different CRIS targets: +@table @code +@item cris-axis-aout +Old target. Includes a multilib for the @samp{elinux} a.out-based +target. No multilibs for newer architecture variants. +@item cris-axis-elf +Mainly for monolithic embedded systems. Includes a multilib for the +@samp{v10} core used in @samp{ETRAX 100 LX}. +@item cris-axis-linux-gnu +A GNU/Linux port for the CRIS architecture, currently targeting +@samp{ETRAX 100 LX} by default. +@end table + +For @code{cris-axis-aout} and @code{cris-axis-elf} you need binutils 2.11 +or newer. For @code{cris-axis-linux-gnu} you need binutils 2.12 or newer. + +Pre-packaged tools can be obtained from +@uref{ftp://ftp.axis.com/pub/axis/tools/cris/compiler-kit/}. More +information about this platform is available at +@uref{http://developer.axis.com/}. + @html


diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 0588c1a5b8f..cab5005f752 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -588,6 +588,15 @@ in the following sections. -msmall-exec -mno-small-exec -mmvcle -mno-mvcle @gol -m64 -m31 -mdebug -mno-debug} +@emph{CRIS Options} +@gccoptlist{ +-mcpu=@var{cpu} -march=@var{cpu} -mtune=@var{cpu} @gol +-mmax-stack-frame=@var{n} -melinux-stacksize=@var{n} @gol +-metrax4 -metrax100 -mpdebug -mcc-init -mno-side-effects @gol +-mstack-align -mdata-align -mconst-align @gol +-m32-bit -m16-bit -m8-bit -mno-prologue-epilogue -mno-gotplt @gol +-melf -maout -melinux -mlinux -sim -sim2} + @item Code Generation Options @xref{Code Gen Options,,Options for Code Generation Conventions}. @gccoptlist{ @@ -5096,6 +5105,7 @@ that macro, which enables you to change the defaults. * IA-64 Options:: * D30V Options:: * S/390 and zSeries Options:: +* CRIS Options:: @end menu @node M680x0 Options @@ -9464,6 +9474,143 @@ The default is to not print debug information. @end table +@node CRIS Options +@subsection CRIS Options +@cindex CRIS Options + +These options are defined specifically for the CRIS ports. + +@table @gcctabopt +@item -march=@var{architecture-type} +@itemx -mcpu=@var{architecture-type} +@opindex march +@opindex mcpu +Generate code for the specified architecture. The choices for +@var{architecture-type} are @samp{v3}, @samp{v8} and @samp{v10} for +respectively ETRAX@w{ }4, ETRAX@w{ }100, and ETRAX@w{ }100@w{ }LX. +Default is @samp{v0} except for cris-axis-linux-gnu, where the default is +@samp{v10}. + +@item -mtune=@var{architecture-type} +@opindex mtune +Tune to @var{architecture-type} everything applicable about the generated +code, except for the ABI and the set of available instructions. The +choices for @var{architecture-type} are the same as for +@option{-march=@var{architecture-type}}. + +@item -mmax-stack-frame=@var{n} +@opindex mmax-stack-frame +Warn when the stack frame of a function exceeds @var{n} bytes. + +@item -melinux-stacksize=@var{n} +@opindex melinux-stacksize +Only available with the @samp{cris-axis-aout} target. Arranges for +indications in the program to the kernel loader that the stack of the +program should be set to @var{n} bytes. + +@item -metrax4 +@itemx -metrax100 +@opindex metrax4 +@opindex metrax100 +The options @option{-metrax4} and @option{-metrax100} are synonyms for +@option{-march=v3} and @option{-march=v8} respectively. + +@item -mpdebug +@opindex mpdebug +Enable CRIS-specific verbose debug-related information in the assembly +code. This option also has the effect to turn off the @samp{#NO_APP} +formatted-code indicator to the assembler at the beginning of the +assembly file. + +@item -mcc-init +@opindex mcc-init +Do not use condition-code results from previous instruction; always emit +compare and test instructions before use of condition codes. + +@item -mno-side-effects +@opindex mno-side-effects +Do not emit instructions with side-effects in addressing modes other than +post-increment. + +@item -mstack-align +@itemx -mno-stack-align +@itemx -mdata-align +@itemx -mno-data-align +@itemx -mconst-align +@itemx -mno-const-align +@opindex mstack-align +@opindex mno-stack-align +@opindex mdata-align +@opindex mno-data-align +@opindex mconst-align +@opindex mno-const-align +These options (no-options) arranges (eliminate arrangements) for the +stack-frame, individual data and constants to be aligned for the maximum +single data access size for the chosen CPU model. The default is to +arrange for 32-bit alignment. ABI details such as structure layout are +not affected by these options. + +@item -m32-bit +@itemx -m16-bit +@itemx -m8-bit +@opindex m32-bit +@opindex m16-bit +@opindex m8-bit +Similar to the stack- data- and const-align options above, these options +arrange for stack-frame, writable data and constants to all be 32-bit, +16-bit or 8-bit aligned. The default is 32-bit alignment. + +@item -mno-prologue-epilogue +@itemx -mprologue-epilogue +@opindex mno-prologue-epilogue +@opindex mprologue-epilogue +With @option{-mno-prologue-epilogue}, the normal function prologue and +epilogue that sets up the stack-frame are omitted and no return +instructions or return sequences are generated in the code. Use this +option only together with visual inspection of the compiled code: no +warnings or errors are generated when call-saved registers must be saved, +or storage for local variable needs to be allocated. + +@item -mno-gotplt +@itemx -mgotplt +@opindex mno-gotplt +@opindex mgotplt +With @option{-fpic} and @option{-fPIC}, don't generate (do generate) +instruction sequences that load addresses for functions from the PLT part +of the GOT rather than (traditional on other architectures) calls to the +PLT. The default is @option{-mgotplt}. + +@item -maout +@opindex maout +Legacy no-op option only recognized with the cris-axis-aout target. + +@item -melf +@opindex melf +Legacy no-op option only recognized with the cris-axis-elf and +cris-axis-linux-gnu targets. + +@item -melinux +@opindex melinux +Only recognized with the cris-axis-aout target, where it selects a +GNU/linux-like multilib, include files and instruction set for +@option{-march=v8}. + +@item -mlinux +@opindex mlinux +Legacy no-op option only recognized with the cris-axis-linux-gnu target. + +@item -sim +@opindex sim +This option, recognized for the cris-axis-aout and cris-axis-elf arranges +to link with input-output functions from a simulator library. Code, +initialized data and zero-initialized data are allocated consecutively. + +@item -sim2 +@opindex sim2 +Like @option{-sim}, but pass linker options to locate initialized data at +0x40000000 and zero-initialized data at 0x80000000. +@end table + @node Code Gen Options @section Options for Code Generation Conventions