+2005-08-22 Aldy Hernandez <aldyh@redhat.com>
+
+ * doc/invoke.texi (Option Summary): Add ms1 options.
+ * doc/extend.texi: Document interrupt handler attribute for ms1.
+ * doc/md.texi: Document ms1 constraints.
+ * config.gcc: Add ms1-*-elf.
+ * config/ms1/ms1.h: New.
+ * config/ms1/ms1.c: New.
+ * config/ms1/ms1.md: New.
+ * config/ms1/ms1-protos.h: New.
+ * config/ms1/ABI.txt: New.
+ * config/ms1/crti.asm: New.
+ * config/ms1/crtn.asm: New.
+ * config/ms1/lib2extra-funcs.c: New.
+ * config/ms1/t-ms1: New.
+ * config/ms1/ms1.opt: New.
+
2005-08-22 Ira Rosen <irar@il.ibm.com>
* config/rs6000/altivec.md (xorv4sf3): New.
use_collect2=no
use_fixproto=yes
;;
+ms1-*-elf)
+ tm_file="dbxelf.h elfos.h svr4.h ${tm_file}"
+ tmake_file="${tmake_file} ms1/t-ms1"
+ ;;
+ns32k-*-netbsdelf*)
+ echo "GCC does not yet support the ${target} target"; exit 1
+ ;;
+ns32k-*-netbsd*)
+ tm_file="${tm_file} netbsd.h netbsd-aout.h ns32k/netbsd.h"
+ # On NetBSD, the headers are already okay, except for math.h.
+ tmake_file="t-netbsd ns32k/t-ns32k"
+ extra_parts=""
+ use_collect2=yes
+ ;;
pdp11-*-bsd)
tm_file="${tm_file} pdp11/2bsd.h"
use_fixproto=yes
--- /dev/null
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved.
+
+--------------------------------------------------------------------------
+
+ MS1 ABI
+ =========
+
+Sizes and alignments
+--------------------
+
+ Type Size (bytes) Alignment (bytes)
+
+ char 1 1
+ short 2 2
+ int 4 4
+ unsigned 4 4
+ long 4 4
+ long long 8 8
+ float 4 4
+ double 8 8
+ pointers 4 4
+
+* alignment within aggregates (structs and unions) is as above, with
+ padding added if needed
+* aggregates have alignment equal to that of their most aligned
+ member
+* aggregates have sizes which are a multiple of their alignment
+
+
+Floating point
+--------------
+
+All emulated using IEEE floating point conventions.
+
+Registers
+----------------
+
+r0 always zero
+r1 argument register 1
+r2 argument register 2
+r3 argument register 3
+r4 argument register 4
+r5 callee must save
+r6 callee must save
+r7 call clobbers
+r8 call clobbers
+r9 call clobbers
+r10 call clobbers
+r11 function return value
+r12 frame pointer
+r13 stack pointer
+r14 linkage pointer
+r15 interrupt pointer
+
+Stack alignment 8 bytes
+
+Structures passed <= 32 bits as values, else as pointers
+
+The MS1 Stack
+---------------
+
+Space is allocated as needed in the stack frame for the following at compile
+time:
+
+* Outgoing parameters beyond the fourth
+
+* All automatic arrays, automatic data aggregates, automatic
+ scalars which must be addressable, and automatic scalars for
+ which there is no room in registers
+
+* Compiler-generated temporary values (typically when there are
+ too many for the compiler to keep them all in registers)
+
+Space can be allocated dynamically (at runtime) in the stack frame for the
+following:
+
+* Memory allocated using the alloca() function of the C library
+
+Addressable automatic variables on the stack are addressed with positive
+offsets relative to r12; dynamically allocated space is addressed with positive
+offsets from the pointer returned by alloca().
+
+Stack Frame
+-----------
+
+ +-----------------------+
+ | Parameter Word 1 |
+ +-----------------------+ <-sp
+ | Previous FP |
+ +-----------------------+
+ | Return address |
+ +-----------------------+
+ | Saved Registers |
+ +-----------------------+
+ | ... |
+ +-----------------------+
+ | Local Variables |
+ +-----------------------+ <-fp
+ | Alloca |
+ +-----------------------+
+ | ... |
+ +-----------------------+
+ | Parameter Word 2 |
+ +-----------------------+
+ | Parameter Word 1 |
+ +-----------------------+ <-sp
+
+
+Parameter Assignment to Registers
+---------------------------------
+
+Consider the parameters in a function call as ordered from left (first
+parameter) to right. GR contains the number of the next available
+general-purpose register. STARG is the address of the next available stack
+parameter word.
+
+INITIALIZE:
+ Set GR=r1 and STARG to point to parameter word 1.
+
+SCAN:
+ If there are no more parameters, terminate.
+ Otherwise, select one of the following depending on the type
+ of the next parameter:
+
+ SIMPLE ARG:
+
+ A SIMPLE ARG is one of the following:
+
+ * One of the simple integer types which will fit into a
+ general-purpose register,
+ * A pointer to an object of any type,
+ * A struct or union small enough to fit in a register (<= 32 bits)
+ * A larger struct or union, which shall be treated as a
+ pointer to the object or to a copy of the object.
+ (See below for when copies are made.)
+
+ If GR > r4, go to STACK. Otherwise, load the parameter value into
+ general-purpose register GR and advance GR to the next general-purpose
+ register. Values shorter than the register size are sign-extended or
+ zero-extended depending on whether they are signed or unsigned. Then
+ go to SCAN.
+
+ DOUBLE or LONG LONG
+
+ If GR > r3, go to STACK. Otherwise, if GR is odd, advance GR to the
+ next register. Load the 64-bit long long or double value into register
+ pair GR and GR+1. Advance GR to GR+2 and go to SCAN.
+
+ STACK:
+
+ Parameters not otherwise handled above are passed in the parameter
+ words of the caller's stack frame. SIMPLE ARGs, as defined above, are
+ considered to have size and alignment equal to the size of a
+ general-purpose register, with simple argument types shorter than this
+ sign- or zero-extended to this width. Round STARG up to a multiple of
+ the alignment requirement of the parameter and copy the argument
+ byte-for-byte into STARG, STARG+1, ... STARG+size-1. Set STARG to
+ STARG+size and go to SCAN.
+
+
+Structure passing
+-----------------
+
+As noted above, code which passes structures and unions by value is implemented
+specially. (In this section, "struct" will refer to structs and unions
+inclusively.) Structs small enough to fit in a register are passed by value in
+a single register or in a stack frame slot the size of a register. Structs
+containing a single double or long long component are passed by value in two
+registers or in a stack frame slot the size of two registers. Other structs
+are handled by passing the address of the structure. In this case, a copy of
+the structure will be made if necessary in order to preserve the pass-by-value
+semantics.
+
+Copies of large structs are made under the following rules:
+
+ ANSI mode K&R Mode
+ --------- --------
+Normal param Callee copies if needed Caller copies
+Varargs (...) param Caller copies Caller copies
+
+In the case of normal (non-varargs) large-struct parameters in ANSI mode, the
+callee is responsible for producing the same effect as if a copy of the
+structure were passed, preserving the pass-by-value semantics. This may be
+accomplished by having the callee make a copy, but in some cases the callee may
+be able to determine that a copy is not necessary in order to produce the same
+results. In such cases, the callee may choose to avoid making a copy of the
+parameter.
+
+
+Varargs handling
+----------------
+
+No special changes are needed for handling varargs parameters other than the
+caller knowing that a copy is needed on struct parameters larger than a
+register (see above).
+
+The varargs macros set up a register save area for the general-purpose
+registers to be saved. Because the save area lies between the caller and
+callee stack frames, the saved register parameters are contiguous with
+parameters passed on the stack. A pointer advances from the register save area
+into the caller's stack frame.
+
+
+Function return values
+----------------------
+
+ Type Register
+ ---- --------
+ int r11
+ short r11
+ long r11
+ long long stack
+ float r11
+ double stack
+
--- /dev/null
+# crti.asm for ms1
+#
+# Copyright (C) 2005 Free Software Foundation, Inc.
+#
+# This file 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 GCC; see the file COPYING. If not, write to the Free
+# Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# As a special exception, if you link this library with files
+# compiled with GCC to produce an executable, this does not cause
+# the resulting executable to be covered by the GNU General Public License.
+# This exception does not however invalidate any other reasons why
+# the executable file might be covered by the GNU General Public License.
+#
+
+# This file just make a stack frame for the contents of the .fini and
+# .init sections. Users may put any desired instructions in those
+# sections.
+
+ .file "crti.asm"
+
+ .section ".init"
+ .global _init
+ .type _init,#function
+ .align 4
+_init:
+ subi sp, sp, #4
+ stw r14, sp, #0
+ or r0, r0, r0
+ or r0, r0, r0
+ or r0, r0, r0
+ or r0, r0, r0
+ or r0, r0, r0
+ or r0, r0, r0
+ or r0, r0, r0
+ or r0, r0, r0
+
+ .section ".fini"
+ .global _fini
+ .type _fini,#function
+ .align 4
+_fini:
+ subi sp, sp, #4
+ stw r14, sp, #0
+ or r0, r0, r0
+ or r0, r0, r0
+ or r0, r0, r0
+ or r0, r0, r0
+ or r0, r0, r0
+ or r0, r0, r0
+ or r0, r0, r0
+ or r0, r0, r0
--- /dev/null
+# crtn.asm for ms1
+
+# Copyright (C) 2005 Free Software Foundation, Inc.
+#
+# This file 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 GCC; see the file COPYING. If not, write to the Free
+# Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# As a special exception, if you link this library with files
+# compiled with GCC to produce an executable, this does not cause
+# the resulting executable to be covered by the GNU General Public License.
+# This exception does not however invalidate any other reasons why
+# the executable file might be covered by the GNU General Public License.
+#
+
+# This file just makes sure that the .fini and .init sections do in
+# fact return. Users may put any desired instructions in those sections.
+# This file is the last thing linked into any executable.
+
+ .file "crtn.asm"
+
+ .section ".init"
+ .align 4
+ ldw r14, sp, #0
+ addi sp, sp, #4
+ jal r0, r14
+ or r0, r0, r0
+
+ .section ".fini"
+ .align 4
+
+ ldw r14, sp, #0
+ addi sp, sp, #4
+ jal r0, r14
+ or r0, r0, r0
--- /dev/null
+/* Copyright (C) 2005 Free Software Foundation,
+
+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 into combinations with other programs,
+and to distribute those combinations 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 a combine
+executable.)
+
+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, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA. */
+
+#define BITS_PER_UNIT 8
+
+typedef int HItype __attribute__ ((mode (HI)));
+typedef unsigned int UHItype __attribute__ ((mode (HI)));
+
+typedef int SItype __attribute__ ((mode (SI)));
+typedef unsigned int USItype __attribute__ ((mode (SI)));
+
+typedef int word_type __attribute__ ((mode (__word__)));
+
+struct SIstruct {HItype low, high;};
+
+typedef union
+{
+ struct SIstruct s;
+ SItype ll;
+} SIunion;
+
+SItype
+__lshrsi3 (SItype u, word_type b)
+{
+ SIunion w;
+ word_type bm;
+ SIunion uu;
+
+ if (b == 0)
+ return u;
+
+ uu.ll = u;
+
+ bm = (sizeof (HItype) * BITS_PER_UNIT) - b;
+ if (bm <= 0)
+ {
+ w.s.high = 0;
+ w.s.low = (UHItype)uu.s.high >> -bm;
+ }
+ else
+ {
+ UHItype carries = (UHItype)uu.s.high << bm;
+ w.s.high = (UHItype)uu.s.high >> b;
+ w.s.low = ((UHItype)uu.s.low >> b) | carries;
+ }
+
+ return w.ll;
+}
+
+SItype
+__ashlsi3 (SItype u, word_type b)
+{
+ SIunion w;
+ word_type bm;
+ SIunion uu;
+
+ if (b == 0)
+ return u;
+
+ uu.ll = u;
+
+ bm = (sizeof (HItype) * BITS_PER_UNIT) - b;
+ if (bm <= 0)
+ {
+ w.s.low = 0;
+ w.s.high = (UHItype)uu.s.low << -bm;
+ }
+ else
+ {
+ UHItype carries = (UHItype)uu.s.low >> bm;
+ w.s.low = (UHItype)uu.s.low << b;
+ w.s.high = ((UHItype)uu.s.high << b) | carries;
+ }
+
+ return w.ll;
+}
+
+SItype
+__ashrsi3 (SItype u, word_type b)
+{
+ SIunion w;
+ word_type bm;
+ SIunion uu;
+
+ if (b == 0)
+ return u;
+
+ uu.ll = u;
+
+ bm = (sizeof (HItype) * BITS_PER_UNIT) - b;
+ if (bm <= 0)
+ {
+ /* w.s.high = 1..1 or 0..0 */
+ w.s.high = uu.s.high >> (sizeof (HItype) * BITS_PER_UNIT - 1);
+ w.s.low = uu.s.high >> -bm;
+ }
+ else
+ {
+ UHItype carries = (UHItype)uu.s.high << bm;
+ w.s.high = uu.s.high >> b;
+ w.s.low = ((UHItype)uu.s.low >> b) | carries;
+ }
+
+ return w.ll;
+}
+
+USItype
+__mulsi3 (USItype a, USItype b)
+{
+ USItype c = 0;
+
+ while (a != 0)
+ {
+ if (a & 1)
+ c += b;
+ a >>= 1;
+ b <<= 1;
+ }
+
+ return c;
+}
+
+USItype
+udivmodsi4(USItype num, USItype den, word_type modwanted)
+{
+ USItype bit = 1;
+ USItype res = 0;
+
+ while (den < num && bit && !(den & (1L<<31)))
+ {
+ den <<=1;
+ bit <<=1;
+ }
+ while (bit)
+ {
+ if (num >= den)
+ {
+ num -= den;
+ res |= bit;
+ }
+ bit >>=1;
+ den >>=1;
+ }
+ if (modwanted) return num;
+ return res;
+}
+
+SItype
+__divsi3 (SItype a, SItype b)
+{
+ word_type neg = 0;
+ SItype res;
+
+ if (a < 0)
+ {
+ a = -a;
+ neg = !neg;
+ }
+
+ if (b < 0)
+ {
+ b = -b;
+ neg = !neg;
+ }
+
+ res = udivmodsi4 (a, b, 0);
+
+ if (neg)
+ res = -res;
+
+ return res;
+}
+
+SItype
+__modsi3 (SItype a, SItype b)
+{
+ word_type neg = 0;
+ SItype res;
+
+ if (a < 0)
+ {
+ a = -a;
+ neg = 1;
+ }
+
+ if (b < 0)
+ b = -b;
+
+ res = udivmodsi4 (a, b, 1);
+
+ if (neg)
+ res = -res;
+
+ return res;
+}
+
+SItype
+__udivsi3 (SItype a, SItype b)
+{
+ return udivmodsi4 (a, b, 0);
+}
+
+SItype
+__umodsi3 (SItype a, SItype b)
+{
+ return udivmodsi4 (a, b, 1);
+}
--- /dev/null
+/* Prototypes for exported functions defined in ms1.c
+ Copyright (C) 2005 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.
+
+ 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, 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+extern void ms1_init_expanders (void);
+extern void ms1_expand_prologue (void);
+extern void ms1_expand_epilogue (enum epilogue_type);
+extern unsigned ms1_compute_frame_size (int);
+extern void ms1_override_options (void);
+extern int ms1_initial_elimination_offset (int, int);
+extern const char * ms1_asm_output_opcode (FILE *, const char *);
+extern int ms1_epilogue_uses (int);
+
+#ifdef TREE_CODE
+extern const char * ms1_cannot_inline_p (tree);
+extern int ms1_function_arg_boundary (enum machine_mode, tree);
+extern void ms1_function_arg_advance (CUMULATIVE_ARGS *, enum machine_mode, tree, int);
+#endif
+
+#ifdef RTX_CODE
+extern void ms1_expand_eh_return (rtx *);
+extern void ms1_emit_eh_epilogue (rtx *);
+extern void ms1_print_operand (FILE *, rtx, int);
+extern void ms1_print_operand_address (FILE *, rtx);
+extern int ms1_check_split (rtx, enum machine_mode);
+extern int ms1_reg_ok_for_base_p (rtx, int);
+extern int ms1_legitimate_address_p (enum machine_mode, rtx, int);
+/* Predicates for machine description. */
+extern int uns_arith_operand (rtx, enum machine_mode);
+extern int arith_operand (rtx, enum machine_mode);
+extern int reg_or_0_operand (rtx, enum machine_mode);
+extern int big_const_operand (rtx, enum machine_mode);
+extern int single_const_operand (rtx, enum machine_mode);
+extern void ms1_emit_cbranch (enum rtx_code, rtx, rtx, rtx);
+extern void ms1_set_memflags (rtx);
+extern rtx ms1_return_addr_rtx (int);
+extern void ms1_split_words (enum machine_mode, enum machine_mode, rtx *);
+extern void ms1_final_prescan_insn (rtx, rtx *, int);
+#endif
+
+#ifdef TREE_CODE
+#ifdef RTX_CODE
+extern void ms1_init_cumulative_args (CUMULATIVE_ARGS *, tree, rtx, tree, int);
+extern rtx ms1_function_arg (const CUMULATIVE_ARGS *, enum machine_mode, tree, int, int);
+extern void ms1_va_start (tree, rtx);
+extern enum reg_class ms1_secondary_reload_class (enum reg_class, enum machine_mode, rtx);
+extern rtx ms1_function_value (tree, enum machine_mode, tree);
+#endif
+#endif
--- /dev/null
+/* Target definitions for the MorphoRISC1
+ Copyright (C) 2005 Free Software Foundation, Inc.
+ Contributed by Red Hat, 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.
+
+ 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, 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "rtl.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "real.h"
+#include "insn-config.h"
+#include "conditions.h"
+#include "insn-attr.h"
+#include "recog.h"
+#include "toplev.h"
+#include "output.h"
+#include "integrate.h"
+#include "tree.h"
+#include "function.h"
+#include "expr.h"
+#include "optabs.h"
+#include "libfuncs.h"
+#include "flags.h"
+#include "tm_p.h"
+#include "ggc.h"
+#include "insn-flags.h"
+#include "obstack.h"
+#include "except.h"
+#include "target.h"
+#include "target-def.h"
+
+/* Frame pointer register mask. */
+#define FP_MASK (1 << (GPR_FP))
+
+/* Link register mask. */
+#define LINK_MASK (1 << (GPR_LINK))
+
+/* First GPR. */
+#define MS1_INT_ARG_FIRST 1
+
+/* Given a SIZE in bytes, advance to the next word. */
+#define ROUND_ADVANCE(SIZE) (((SIZE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
+
+/* A C structure for machine-specific, per-function data.
+ This is added to the cfun structure. */
+struct machine_function GTY(())
+{
+ /* Flags if __builtin_return_address (n) with n >= 1 was used. */
+ int ra_needs_full_frame;
+ struct rtx_def * eh_stack_adjust;
+ int interrupt_handler;
+};
+
+/* Define the information needed to generate branch and scc insns.
+ This is stored from the compare operation. */
+struct rtx_def * ms1_compare_op0;
+struct rtx_def * ms1_compare_op1;
+
+/* Current frame information calculated by compute_frame_size. */
+struct ms1_frame_info current_frame_info;
+
+/* Zero structure to initialize current_frame_info. */
+struct ms1_frame_info zero_frame_info;
+
+/* ms1 doesn't have unsigned compares need a library call for this. */
+struct rtx_def * ms1_ucmpsi3_libcall;
+
+\f
+static rtx
+ms1_struct_value_rtx (tree fndecl ATTRIBUTE_UNUSED,
+ int incoming ATTRIBUTE_UNUSED)
+{
+ return gen_rtx_REG (Pmode, RETVAL_REGNUM);
+}
+
+/* Implement RETURN_ADDR_RTX. */
+rtx
+ms1_return_addr_rtx (int count)
+{
+ if (count != 0)
+ return NULL_RTX;
+
+ return get_hard_reg_initial_val (Pmode, GPR_LINK);
+}
+
+/* The following variable value indicates the number of nops required
+ between the current instruction and the next instruction to avoid
+ any pipeline hazards. */
+static int ms1_nops_required = 0;
+static const char * ms1_nop_reasons = "";
+
+/* Implement ASM_OUTPUT_OPCODE. */
+const char *
+ms1_asm_output_opcode (FILE *f ATTRIBUTE_UNUSED, const char *ptr)
+{
+ if (ms1_nops_required)
+ fprintf (f, ";# need %d nops because of %s\n\t",
+ ms1_nops_required, ms1_nop_reasons);
+
+ while (ms1_nops_required)
+ {
+ fprintf (f, "or r0, r0, r0\n\t");
+ -- ms1_nops_required;
+ }
+
+ return ptr;
+}
+
+/* Return TRUE if INSN is a memory load. */
+static bool
+ms1_memory_load (rtx insn)
+{
+ return ((GET_CODE (insn) == SET) && (GET_CODE (XEXP (insn,1)) == MEM));
+}
+
+/* Given an insn, return whether it's a memory operation or a branch
+ operation, otherwise return TYPE_ARITH. */
+static enum attr_type
+ms1_get_attr_type (rtx complete_insn)
+{
+ rtx insn = PATTERN (complete_insn);
+
+ if ((GET_CODE (insn) == SET)
+ && ((GET_CODE (XEXP (insn, 0)) == MEM)
+ || (GET_CODE (XEXP (insn, 1)) == MEM)))
+ return TYPE_MEM;
+
+ else if (((GET_CODE (insn) == SET) && (XEXP (insn, 0) == pc_rtx))
+ || (GET_CODE (complete_insn) == JUMP_INSN)
+ || (GET_CODE (complete_insn) == CALL_INSN))
+ return TYPE_BRANCH;
+
+ else
+ return TYPE_ARITH;
+}
+
+/* A helper routine for insn_dependent_p called through note_stores. */
+
+static void
+insn_dependent_p_1 (rtx x, rtx pat ATTRIBUTE_UNUSED, void *data)
+{
+ rtx * pinsn = (rtx *) data;
+
+ if (*pinsn && reg_mentioned_p (x, *pinsn))
+ *pinsn = NULL_RTX;
+}
+
+/* Return true if anything in insn X is (anti,output,true)
+ dependent on anything in insn Y. */
+
+static bool
+insn_dependent_p (rtx x, rtx y)
+{
+ rtx tmp;
+
+ if (! INSN_P (x) || ! INSN_P (y))
+ return 0;
+
+ tmp = PATTERN (y);
+ note_stores (PATTERN (x), insn_dependent_p_1, &tmp);
+ if (tmp == NULL_RTX)
+ return true;
+
+ tmp = PATTERN (x);
+ note_stores (PATTERN (y), insn_dependent_p_1, &tmp);
+ return (tmp == NULL_RTX);
+}
+
+
+/* Return true if anything in insn X is true dependent on anything in
+ insn Y. */
+static bool
+insn_true_dependent_p (rtx x, rtx y)
+{
+ rtx tmp;
+
+ if (! INSN_P (x) || ! INSN_P (y))
+ return 0;
+
+ tmp = PATTERN (y);
+ note_stores (PATTERN (x), insn_dependent_p_1, &tmp);
+ return (tmp == NULL_RTX);
+}
+
+/* The following determines the number of nops that need to be
+ inserted between the previous instructions and current instruction
+ to avoid pipeline hazards on the ms1 processor. Remember that
+ the function is not called for asm insns. */
+
+void
+ms1_final_prescan_insn (rtx insn,
+ rtx * opvec ATTRIBUTE_UNUSED,
+ int noperands ATTRIBUTE_UNUSED)
+{
+ rtx prev_i;
+
+ ms1_nops_required = 0;
+ ms1_nop_reasons = "";
+
+ /* Only worry about real instructions. */
+ if (! INSN_P (insn))
+ return;
+
+ /* Find the previous real instructions. */
+ prev_i = PREV_INSN (insn);
+ while (prev_i != NULL
+ && (! INSN_P (prev_i)
+ || GET_CODE (PATTERN (prev_i)) == USE
+ || GET_CODE (PATTERN (prev_i)) == CLOBBER))
+ prev_i = PREV_INSN (prev_i);
+
+ /* If there isn't one then there is nothing that we need do. */
+ if (prev_i == NULL || ! INSN_P (prev_i))
+ return;
+
+ /* Delayed branch slots already taken care of by delay branch scheduling. */
+ if (ms1_get_attr_type (prev_i) == TYPE_BRANCH)
+ return;
+
+ switch (ms1_get_attr_type (insn))
+ {
+ case TYPE_MEM:
+ /* Avoid consecutive memory operation. */
+ if (ms1_get_attr_type (prev_i) == TYPE_MEM
+ && ms1_cpu == PROCESSOR_MS1_64_001)
+ {
+ ms1_nops_required = 1;
+ ms1_nop_reasons = "consecutive mem ops";
+ }
+ /* Drop through. */
+
+ case TYPE_ARITH:
+ case TYPE_COMPLEX:
+ /* One cycle of delay is required between load
+ and the dependent arithmetic instruction. */
+ if (ms1_memory_load (PATTERN (prev_i))
+ && insn_true_dependent_p (prev_i, insn))
+ {
+ ms1_nops_required = 1;
+ ms1_nop_reasons = "load->arith dependency delay";
+ }
+ break;
+
+ case TYPE_BRANCH:
+ if (insn_dependent_p (prev_i, insn))
+ {
+ if (ms1_get_attr_type (prev_i) == TYPE_ARITH
+ && ms1_cpu == PROCESSOR_MS1_64_001)
+ {
+ /* One cycle of delay between arith
+ instructions and branch dependent on arith. */
+ ms1_nops_required = 1;
+ ms1_nop_reasons = "arith->branch dependency delay";
+ }
+ else if (ms1_memory_load (PATTERN (prev_i)))
+ {
+ /* Two cycles of delay are required
+ between load and dependent branch. */
+ if (ms1_cpu == PROCESSOR_MS1_64_001)
+ ms1_nops_required = 2;
+ else
+ ms1_nops_required = 1;
+ ms1_nop_reasons = "load->branch dependency delay";
+ }
+ }
+ break;
+
+ default:
+ fatal_insn ("ms1_final_prescan_insn, invalid insn #1", insn);
+ break;
+ }
+}
+
+/* Print debugging information for a frame. */
+static void
+ms1_debug_stack (struct ms1_frame_info * info)
+{
+ int regno;
+
+ if (!info)
+ {
+ error ("info pointer NULL");
+ gcc_unreachable ();
+ }
+
+ fprintf (stderr, "\nStack information for function %s:\n",
+ ((current_function_decl && DECL_NAME (current_function_decl))
+ ? IDENTIFIER_POINTER (DECL_NAME (current_function_decl))
+ : "<unknown>"));
+
+ fprintf (stderr, "\ttotal_size = %d\n", info->total_size);
+ fprintf (stderr, "\tpretend_size = %d\n", info->pretend_size);
+ fprintf (stderr, "\targs_size = %d\n", info->args_size);
+ fprintf (stderr, "\textra_size = %d\n", info->extra_size);
+ fprintf (stderr, "\treg_size = %d\n", info->reg_size);
+ fprintf (stderr, "\tvar_size = %d\n", info->var_size);
+ fprintf (stderr, "\tframe_size = %d\n", info->frame_size);
+ fprintf (stderr, "\treg_mask = 0x%x\n", info->reg_mask);
+ fprintf (stderr, "\tsave_fp = %d\n", info->save_fp);
+ fprintf (stderr, "\tsave_lr = %d\n", info->save_lr);
+ fprintf (stderr, "\tinitialized = %d\n", info->initialized);
+ fprintf (stderr, "\tsaved registers =");
+
+ /* Print out reg_mask in a more readable format. */
+ for (regno = GPR_R0; regno <= GPR_LAST; regno++)
+ if ( (1 << regno) & info->reg_mask)
+ fprintf (stderr, " %s", reg_names[regno]);
+
+ putc ('\n', stderr);
+ fflush (stderr);
+}
+
+/* Print a memory address as an operand to reference that memory location. */
+
+static void
+ms1_print_operand_simple_address (FILE * file, rtx addr)
+{
+ if (!addr)
+ error ("PRINT_OPERAND_ADDRESS, null pointer");
+
+ else
+ switch (GET_CODE (addr))
+ {
+ case REG:
+ fprintf (file, "%s, #0", reg_names [REGNO (addr)]);
+ break;
+
+ case PLUS:
+ {
+ rtx reg = 0;
+ rtx offset = 0;
+ rtx arg0 = XEXP (addr, 0);
+ rtx arg1 = XEXP (addr, 1);
+
+ if (GET_CODE (arg0) == REG)
+ {
+ reg = arg0;
+ offset = arg1;
+ if (GET_CODE (offset) == REG)
+ fatal_insn ("PRINT_OPERAND_ADDRESS, 2 regs", addr);
+ }
+
+ else if (GET_CODE (arg1) == REG)
+ reg = arg1, offset = arg0;
+ else if (CONSTANT_P (arg0) && CONSTANT_P (arg1))
+ {
+ fprintf (file, "%s, #", reg_names [GPR_R0]);
+ output_addr_const (file, addr);
+ break;
+ }
+ fprintf (file, "%s, #", reg_names [REGNO (reg)]);
+ output_addr_const (file, offset);
+ break;
+ }
+
+ case LABEL_REF:
+ case SYMBOL_REF:
+ case CONST_INT:
+ case CONST:
+ output_addr_const (file, addr);
+ break;
+
+ default:
+ fatal_insn ("PRINT_OPERAND_ADDRESS, invalid insn #1", addr);
+ break;
+ }
+}
+
+/* Implement PRINT_OPERAND_ADDRESS. */
+void
+ms1_print_operand_address (FILE * file, rtx addr)
+{
+ if (GET_CODE (addr) == AND
+ && GET_CODE (XEXP (addr, 1)) == CONST_INT
+ && INTVAL (XEXP (addr, 1)) == -3)
+ ms1_print_operand_simple_address (file, XEXP (addr, 0));
+ else
+ ms1_print_operand_simple_address (file, addr);
+}
+
+/* Implement PRINT_OPERAND. */
+void
+ms1_print_operand (FILE * file, rtx x, int code)
+{
+ switch (code)
+ {
+ case '#':
+ /* Output a nop if there's nothing for the delay slot. */
+ if (dbr_sequence_length () == 0)
+ fputs ("\n\tor r0, r0, r0", file);
+ return;
+
+ case 'H':
+ fprintf(file, "#%%hi16(");
+ output_addr_const (file, x);
+ fprintf(file, ")");
+ return;
+
+ case 'L':
+ fprintf(file, "#%%lo16(");
+ output_addr_const (file, x);
+ fprintf(file, ")");
+ return;
+
+ case 'N':
+ fprintf(file, "#%ld", ~INTVAL (x));
+ return;
+
+ case 'z':
+ if (GET_CODE (x) == CONST_INT && INTVAL (x) == 0)
+ {
+ fputs (reg_names[GPR_R0], file);
+ return;
+ }
+
+ case 0:
+ /* Handled below. */
+ break;
+
+ default:
+ /* output_operand_lossage ("ms1_print_operand: unknown code"); */
+ fprintf (file, "unknown code");
+ return;
+ }
+
+ switch (GET_CODE (x))
+ {
+ case REG:
+ fputs (reg_names [REGNO (x)], file);
+ break;
+
+ case CONST:
+ case CONST_INT:
+ fprintf(file, "#%ld", INTVAL (x));
+ break;
+
+ case MEM:
+ ms1_print_operand_address(file, XEXP (x,0));
+ break;
+
+ case LABEL_REF:
+ case SYMBOL_REF:
+ output_addr_const (file, x);
+ break;
+
+ default:
+ fprintf(file, "Uknown code: %d", GET_CODE (x));
+ break;
+ }
+
+ return;
+}
+
+/* Implement INIT_CUMULATIVE_ARGS. */
+void
+ms1_init_cumulative_args (CUMULATIVE_ARGS * cum, tree fntype, rtx libname,
+ tree fndecl ATTRIBUTE_UNUSED, int incoming)
+{
+ *cum = 0;
+
+ if (TARGET_DEBUG_ARG)
+ {
+ fprintf (stderr, "\nms1_init_cumulative_args:");
+
+ if (incoming)
+ fputs (" incoming", stderr);
+
+ if (fntype)
+ {
+ tree ret_type = TREE_TYPE (fntype);
+ fprintf (stderr, " return = %s,",
+ tree_code_name[ (int)TREE_CODE (ret_type) ]);
+ }
+
+ if (libname && GET_CODE (libname) == SYMBOL_REF)
+ fprintf (stderr, " libname = %s", XSTR (libname, 0));
+
+ if (cfun->returns_struct)
+ fprintf (stderr, " return-struct");
+
+ putc ('\n', stderr);
+ }
+}
+
+/* Compute the slot number to pass an argument in.
+ Returns the slot number or -1 if passing on the stack.
+
+ CUM is a variable of type CUMULATIVE_ARGS which gives info about
+ the preceding args and about the function being called.
+ MODE is the argument's machine mode.
+ TYPE is the data type of the argument (as a tree).
+ This is null for libcalls where that information may
+ not be available.
+ NAMED is nonzero if this argument is a named parameter
+ (otherwise it is an extra parameter matching an ellipsis).
+ INCOMING_P is zero for FUNCTION_ARG, nonzero for FUNCTION_INCOMING_ARG.
+ *PREGNO records the register number to use if scalar type. */
+
+static int
+ms1_function_arg_slotno (const CUMULATIVE_ARGS * cum,
+ enum machine_mode mode,
+ tree type,
+ int named ATTRIBUTE_UNUSED,
+ int incoming_p ATTRIBUTE_UNUSED,
+ int * pregno)
+{
+ int regbase = MS1_INT_ARG_FIRST;
+ int slotno = * cum;
+
+ if (mode == VOIDmode || targetm.calls.must_pass_in_stack (mode, type))
+ return -1;
+
+ if (slotno >= MS1_NUM_ARG_REGS)
+ return -1;
+
+ * pregno = regbase + slotno;
+
+ return slotno;
+}
+
+/* Implement FUNCTION_ARG. */
+rtx
+ms1_function_arg (const CUMULATIVE_ARGS * cum,
+ enum machine_mode mode,
+ tree type,
+ int named,
+ int incoming_p)
+{
+ int slotno, regno;
+ rtx reg;
+
+ slotno = ms1_function_arg_slotno (cum, mode, type, named, incoming_p,
+ & regno);
+
+ if (slotno == -1)
+ reg = NULL_RTX;
+ else
+ reg = gen_rtx_REG (mode, regno);
+
+ return reg;
+}
+
+/* Implement FUNCTION_ARG_ADVANCE. */
+void
+ms1_function_arg_advance (CUMULATIVE_ARGS * cum,
+ enum machine_mode mode,
+ tree type ATTRIBUTE_UNUSED,
+ int named)
+{
+ int slotno, regno;
+
+ /* We pass 0 for incoming_p here, it doesn't matter. */
+ slotno = ms1_function_arg_slotno (cum, mode, type, named, 0, ®no);
+
+ * cum += (mode != BLKmode
+ ? ROUND_ADVANCE (GET_MODE_SIZE (mode))
+ : ROUND_ADVANCE (int_size_in_bytes (type)));
+
+ if (TARGET_DEBUG_ARG)
+ fprintf (stderr,
+ "ms1_function_arg_advance: words = %2d, mode = %4s, named = %d, size = %3d\n",
+ *cum, GET_MODE_NAME (mode), named,
+ (*cum) * UNITS_PER_WORD);
+}
+
+/* Implement hook TARGET_ARG_PARTIAL_BYTES.
+
+ Returns the number of bytes at the beginning of an argument that
+ must be put in registers. The value must be zero for arguments
+ that are passed entirely in registers or that are entirely pushed
+ on the stack. */
+static int
+ms1_arg_partial_bytes (CUMULATIVE_ARGS * pcum,
+ enum machine_mode mode,
+ tree type,
+ bool named ATTRIBUTE_UNUSED)
+{
+ int cum = * pcum;
+ int words;
+
+ if (mode == BLKmode)
+ words = ((int_size_in_bytes (type) + UNITS_PER_WORD - 1)
+ / UNITS_PER_WORD);
+ else
+ words = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+
+ if (! targetm.calls.pass_by_reference (& cum, mode, type, named)
+ && cum < MS1_NUM_ARG_REGS
+ && (cum + words) > MS1_NUM_ARG_REGS)
+ {
+ int bytes = (MS1_NUM_ARG_REGS - cum) * UNITS_PER_WORD;
+
+ if (TARGET_DEBUG)
+ fprintf (stderr, "function_arg_partial_nregs = %d\n", bytes);
+ return bytes;
+ }
+
+ return 0;
+}
+
+
+/* Implement TARGET_PASS_BY_REFERENCE hook. */
+static bool
+ms1_pass_by_reference (CUMULATIVE_ARGS * cum ATTRIBUTE_UNUSED,
+ enum machine_mode mode ATTRIBUTE_UNUSED,
+ tree type,
+ bool named ATTRIBUTE_UNUSED)
+{
+ return (type && int_size_in_bytes (type) > 4 * UNITS_PER_WORD);
+}
+
+/* Implement FUNCTION_ARG_BOUNDARY. */
+int
+ms1_function_arg_boundary (enum machine_mode mode ATTRIBUTE_UNUSED,
+ tree type ATTRIBUTE_UNUSED)
+{
+ return BITS_PER_WORD;
+}
+
+/* Implement REG_OK_FOR_BASE_P. */
+int
+ms1_reg_ok_for_base_p (rtx x, int strict)
+{
+ if (strict)
+ return (((unsigned) REGNO (x)) < FIRST_PSEUDO_REGISTER);
+ return 1;
+}
+
+/* Helper function of ms1_legitimate_address_p. Return true if XINSN
+ is a simple address, otherwise false. */
+static bool
+ms1_legitimate_simple_address_p (enum machine_mode mode ATTRIBUTE_UNUSED,
+ rtx xinsn,
+ int strict)
+{
+ if (TARGET_DEBUG)
+ {
+ fprintf (stderr, "\n========== GO_IF_LEGITIMATE_ADDRESS, %sstrict\n",
+ strict ? "" : "not ");
+ debug_rtx (xinsn);
+ }
+
+ if (GET_CODE (xinsn) == REG && ms1_reg_ok_for_base_p (xinsn, strict))
+ return true;
+
+ if (GET_CODE (xinsn) == PLUS
+ && GET_CODE (XEXP (xinsn, 0)) == REG
+ && ms1_reg_ok_for_base_p (XEXP (xinsn, 0), strict)
+ && GET_CODE (XEXP (xinsn, 1)) == CONST_INT
+ && SMALL_INT (XEXP (xinsn, 1)))
+ return true;
+
+ return false;
+}
+
+
+/* Helper function of GO_IF_LEGITIMATE_ADDRESS. Return non-zero if
+ XINSN is a legitimate address on MS1. */
+int
+ms1_legitimate_address_p (enum machine_mode mode,
+ rtx xinsn,
+ int strict)
+{
+ if (ms1_legitimate_simple_address_p (mode, xinsn, strict))
+ return 1;
+
+ if ((mode) == SImode
+ && GET_CODE (xinsn) == AND
+ && GET_CODE (XEXP (xinsn, 1)) == CONST_INT
+ && INTVAL (XEXP (xinsn, 1)) == -3)
+ return ms1_legitimate_simple_address_p (mode, XEXP (xinsn, 0), strict);
+ else
+ return 0;
+}
+
+/* Return truth value of whether OP can be used as an operands where a
+ register or 16 bit unsigned integer is needed. */
+
+int
+uns_arith_operand (rtx op, enum machine_mode mode)
+{
+ if (GET_CODE (op) == CONST_INT && SMALL_INT_UNSIGNED (op))
+ return 1;
+
+ return register_operand (op, mode);
+}
+
+/* Return truth value of whether OP can be used as an operands where a
+ 16 bit integer is needed. */
+
+int
+arith_operand (rtx op, enum machine_mode mode)
+{
+ if (GET_CODE (op) == CONST_INT && SMALL_INT (op))
+ return 1;
+
+ return register_operand (op, mode);
+}
+
+/* Return truth value of whether OP is a register or the constant 0. */
+
+int
+reg_or_0_operand (rtx op, enum machine_mode mode)
+{
+ switch (GET_CODE (op))
+ {
+ case CONST_INT:
+ return INTVAL (op) == 0;
+
+ case REG:
+ case SUBREG:
+ return register_operand (op, mode);
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* Return truth value of whether OP is a constant that requires two
+ loads to put in a register. */
+
+int
+big_const_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_LETTER_P (INTVAL (op), 'M'))
+ return 1;
+
+ return 0;
+}
+
+/* Return truth value of whether OP is a constant that require only
+ one load to put in a register. */
+
+int
+single_const_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ if (big_const_operand (op, mode)
+ || GET_CODE (op) == CONST
+ || GET_CODE (op) == LABEL_REF
+ || GET_CODE (op) == SYMBOL_REF)
+ return 0;
+
+ return 1;
+}
+
+/* True if the current function is an interrupt handler
+ (either via #pragma or an attribute specification). */
+int interrupt_handler;
+enum processor_type ms1_cpu;
+
+static struct machine_function *
+ms1_init_machine_status (void)
+{
+ struct machine_function *f;
+
+ f = ggc_alloc_cleared (sizeof (struct machine_function));
+
+ return f;
+}
+
+/* Implement OVERRIDE_OPTIONS. */
+void
+ms1_override_options (void)
+{
+ if (ms1_cpu_string != NULL)
+ {
+ if (!strcasecmp (ms1_cpu_string, "MS1-64-001"))
+ ms1_cpu = PROCESSOR_MS1_64_001;
+ else if (!strcasecmp (ms1_cpu_string, "MS1-16-002"))
+ ms1_cpu = PROCESSOR_MS1_16_002;
+ else if (!strcasecmp (ms1_cpu_string, "MS1-16-003"))
+ {
+ ms1_cpu = PROCESSOR_MS1_16_003;
+ target_flags |= MASK_MUL;
+ }
+ else
+ error ("bad value (%s) for -march= switch", ms1_cpu_string);
+ }
+ else
+ ms1_cpu = PROCESSOR_MS1_64_001;
+
+ if (flag_exceptions)
+ {
+ flag_omit_frame_pointer = 0;
+ flag_gcse = 0;
+ }
+
+ init_machine_status = ms1_init_machine_status;
+}
+
+/* Do what is necessary for `va_start'. We look at the current function
+ to determine if stdarg or varargs is used and return the address of the
+ first unnamed parameter. */
+
+static rtx
+ms1_builtin_saveregs (void)
+{
+ int first_reg = 0;
+ rtx address;
+ int regno;
+
+ for (regno = first_reg; regno < MS1_NUM_ARG_REGS; regno ++)
+ emit_move_insn (gen_rtx_MEM (word_mode,
+ gen_rtx_PLUS (Pmode,
+ gen_rtx_REG (SImode, ARG_POINTER_REGNUM),
+ GEN_INT (UNITS_PER_WORD * regno))),
+ gen_rtx_REG (word_mode,
+ MS1_INT_ARG_FIRST + regno));
+
+ address = gen_rtx_PLUS (Pmode,
+ gen_rtx_REG (SImode, ARG_POINTER_REGNUM),
+ GEN_INT (UNITS_PER_WORD * first_reg));
+ return address;
+}
+
+/* Implement `va_start'. */
+
+void
+ms1_va_start (tree valist, rtx nextarg)
+{
+ ms1_builtin_saveregs ();
+ std_expand_builtin_va_start (valist, nextarg);
+}
+
+/* Returns the number of bytes offset between the frame pointer and the stack
+ pointer for the current function. SIZE is the number of bytes of space
+ needed for local variables. */
+
+unsigned int
+ms1_compute_frame_size (int size)
+{
+ int regno;
+ unsigned int total_size;
+ unsigned int var_size;
+ unsigned int args_size;
+ unsigned int pretend_size;
+ unsigned int extra_size;
+ unsigned int reg_size;
+ unsigned int frame_size;
+ unsigned int reg_mask;
+
+ var_size = size;
+ args_size = current_function_outgoing_args_size;
+ pretend_size = current_function_pretend_args_size;
+ extra_size = FIRST_PARM_OFFSET (0);
+ total_size = extra_size + pretend_size + args_size + var_size;
+ reg_size = 0;
+ reg_mask = 0;
+
+ /* Calculate space needed for registers. */
+ for (regno = GPR_R0; regno <= GPR_LAST; regno++)
+ {
+ if (MUST_SAVE_REGISTER (regno))
+ {
+ reg_size += UNITS_PER_WORD;
+ reg_mask |= 1 << regno;
+ }
+ }
+
+ current_frame_info.save_fp = (regs_ever_live [GPR_FP]
+ || frame_pointer_needed
+ || interrupt_handler);
+ current_frame_info.save_lr = (regs_ever_live [GPR_LINK]
+ || profile_flag
+ || interrupt_handler);
+
+ reg_size += (current_frame_info.save_fp + current_frame_info.save_lr)
+ * UNITS_PER_WORD;
+ total_size += reg_size;
+ total_size = ((total_size + 3) & ~3);
+
+ frame_size = total_size;
+
+ /* Save computed information. */
+ current_frame_info.pretend_size = pretend_size;
+ current_frame_info.var_size = var_size;
+ current_frame_info.args_size = args_size;
+ current_frame_info.reg_size = reg_size;
+ current_frame_info.frame_size = args_size + var_size;
+ current_frame_info.total_size = total_size;
+ current_frame_info.extra_size = extra_size;
+ current_frame_info.reg_mask = reg_mask;
+ current_frame_info.initialized = reload_completed;
+
+ return total_size;
+}
+
+/* Emit code to save REG in stack offset pointed to by MEM.
+ STACK_OFFSET is the offset from the SP where the save will happen.
+ This function sets the REG_FRAME_RELATED_EXPR note accordingly. */
+static void
+ms1_emit_save_restore (enum save_direction direction,
+ rtx reg,
+ rtx mem,
+ int stack_offset)
+{
+ if (direction == FROM_PROCESSOR_TO_MEM)
+ {
+ rtx insn;
+
+ insn = emit_move_insn (mem, reg);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ REG_NOTES (insn)
+ = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
+ gen_rtx_SET (VOIDmode,
+ gen_rtx_MEM
+ (SImode,
+ gen_rtx_PLUS (SImode,
+ stack_pointer_rtx,
+ GEN_INT (stack_offset))),
+ reg),
+ REG_NOTES (insn));
+ }
+ else
+ emit_move_insn (reg, mem);
+}
+
+
+/* Emit code to save the frame pointer in the prologue and restore
+ frame pointer in epilogue. */
+
+static void
+ms1_emit_save_fp (enum save_direction direction,
+ struct ms1_frame_info info)
+{
+ rtx base_reg;
+ int reg_mask = info.reg_mask & ~(FP_MASK | LINK_MASK);
+ int offset = info.total_size;
+ int stack_offset = info.total_size;
+
+ /* If there is nothing to save, get out now. */
+ if (! info.save_fp && ! info.save_lr && ! reg_mask)
+ return;
+
+ /* If offset doesn't fit in a 15-bit signed integer,
+ uses a scratch registers to get a smaller offset. */
+ if (CONST_OK_FOR_LETTER_P(offset, 'O'))
+ base_reg = stack_pointer_rtx;
+ else
+ {
+ /* Use the scratch register R9 that holds old stack pointer. */
+ base_reg = gen_rtx_REG (SImode, GPR_R9);
+ offset = 0;
+ }
+
+ if (info.save_fp)
+ {
+ offset -= UNITS_PER_WORD;
+ stack_offset -= UNITS_PER_WORD;
+ ms1_emit_save_restore (direction, gen_rtx_REG (SImode, GPR_FP),
+ gen_rtx_MEM (SImode,
+ gen_rtx_PLUS (SImode, base_reg, GEN_INT (offset))),
+ stack_offset);
+ }
+}
+
+/* Emit code to save registers in the prologue and restore register
+ in epilogue. */
+
+static void
+ms1_emit_save_regs (enum save_direction direction,
+ struct ms1_frame_info info)
+{
+ rtx base_reg;
+ int regno;
+ int reg_mask = info.reg_mask & ~(FP_MASK | LINK_MASK);
+ int offset = info.total_size;
+ int stack_offset = info.total_size;
+
+ /* If there is nothing to save, get out now. */
+ if (! info.save_fp && ! info.save_lr && ! reg_mask)
+ return;
+
+ /* If offset doesn't fit in a 15-bit signed integer,
+ uses a scratch registers to get a smaller offset. */
+ if (CONST_OK_FOR_LETTER_P(offset, 'O'))
+ base_reg = stack_pointer_rtx;
+ else
+ {
+ /* Use the scratch register R9 that holds old stack pointer. */
+ base_reg = gen_rtx_REG (SImode, GPR_R9);
+ offset = 0;
+ }
+
+ if (info.save_fp)
+ {
+ /* This just records the space for it, the actual move generated in
+ ms1_emit_save_fp (). */
+ offset -= UNITS_PER_WORD;
+ stack_offset -= UNITS_PER_WORD;
+ }
+
+ if (info.save_lr)
+ {
+ offset -= UNITS_PER_WORD;
+ stack_offset -= UNITS_PER_WORD;
+ ms1_emit_save_restore (direction, gen_rtx_REG (SImode, GPR_LINK),
+ gen_rtx_MEM (SImode,
+ gen_rtx_PLUS (SImode, base_reg, GEN_INT (offset))),
+ stack_offset);
+ }
+
+ /* Save any needed call-saved regs. */
+ for (regno = GPR_R0; regno <= GPR_LAST; regno++)
+ {
+ if ((reg_mask & (1 << regno)) != 0)
+ {
+ offset -= UNITS_PER_WORD;
+ stack_offset -= UNITS_PER_WORD;
+ ms1_emit_save_restore (direction, gen_rtx_REG (SImode, regno),
+ gen_rtx_MEM (SImode,
+ gen_rtx_PLUS (SImode, base_reg, GEN_INT (offset))),
+ stack_offset);
+ }
+ }
+}
+
+/* Return true if FUNC is a function with the 'interrupt' attribute. */
+static bool
+ms1_interrupt_function_p (tree func)
+{
+ tree a;
+
+ if (TREE_CODE (func) != FUNCTION_DECL)
+ return false;
+
+ a = lookup_attribute ("interrupt", DECL_ATTRIBUTES (func));
+ return a != NULL_TREE;
+}
+
+/* Generate prologue code. */
+void
+ms1_expand_prologue (void)
+{
+ rtx size_rtx, insn;
+ unsigned int frame_size;
+
+ if (ms1_interrupt_function_p (current_function_decl))
+ {
+ interrupt_handler = 1;
+ if (cfun->machine)
+ cfun->machine->interrupt_handler = 1;
+ }
+
+ ms1_compute_frame_size (get_frame_size ());
+
+ if (TARGET_DEBUG_STACK)
+ ms1_debug_stack (¤t_frame_info);
+
+ /* Compute size of stack adjustment. */
+ frame_size = current_frame_info.total_size;
+
+ /* If offset doesn't fit in a 15-bit signed integer,
+ uses a scratch registers to get a smaller offset. */
+ if (CONST_OK_FOR_LETTER_P(frame_size, 'O'))
+ size_rtx = GEN_INT (frame_size);
+ else
+ {
+ /* We do not have any scratch registers. */
+ gcc_assert (!interrupt_handler);
+
+ size_rtx = gen_rtx_REG (SImode, GPR_R9);
+ insn = emit_move_insn (size_rtx, GEN_INT (frame_size & 0xffff0000));
+ insn = emit_insn (gen_iorsi3 (size_rtx, size_rtx,
+ GEN_INT (frame_size & 0x0000ffff)));
+ }
+
+ /* Allocate stack for this frame. */
+ /* Make stack adjustment and use scratch register if constant too
+ large to fit as immediate. */
+ if (frame_size)
+ {
+ insn = emit_insn (gen_subsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ size_rtx));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ REG_NOTES (insn)
+ = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
+ gen_rtx_SET (VOIDmode,
+ stack_pointer_rtx,
+ gen_rtx_MINUS (SImode,
+ stack_pointer_rtx,
+ GEN_INT (frame_size))),
+ REG_NOTES (insn));
+ }
+
+ /* Set R9 to point to old sp if required for access to register save area. */
+ if ( current_frame_info.reg_size != 0
+ && !CONST_OK_FOR_LETTER_P (frame_size, 'O'))
+ emit_insn (gen_addsi3 (size_rtx, size_rtx, stack_pointer_rtx));
+
+ /* Save the frame pointer. */
+ ms1_emit_save_fp (FROM_PROCESSOR_TO_MEM, current_frame_info);
+
+ /* Now put the frame pointer into the frame pointer register. */
+ if (frame_pointer_needed)
+ {
+ insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+
+ /* Save the registers. */
+ ms1_emit_save_regs (FROM_PROCESSOR_TO_MEM, current_frame_info);
+
+ /* If we are profiling, make sure no instructions are scheduled before
+ the call to mcount. */
+ if (profile_flag)
+ emit_insn (gen_blockage ());
+}
+
+/* Implement EPILOGUE_USES. */
+int
+ms1_epilogue_uses (int regno)
+{
+ if (cfun->machine && cfun->machine->interrupt_handler && reload_completed)
+ return 1;
+ return regno == GPR_LINK;
+}
+
+/* Generate epilogue. EH_MODE is NORMAL_EPILOGUE when generating a
+ function epilogue, or EH_EPILOGUE when generating an EH
+ epilogue. */
+void
+ms1_expand_epilogue (enum epilogue_type eh_mode)
+{
+ rtx size_rtx, insn;
+ unsigned frame_size;
+
+ ms1_compute_frame_size (get_frame_size ());
+
+ if (TARGET_DEBUG_STACK)
+ ms1_debug_stack (& current_frame_info);
+
+ /* Compute size of stack adjustment. */
+ frame_size = current_frame_info.total_size;
+
+ /* If offset doesn't fit in a 15-bit signed integer,
+ uses a scratch registers to get a smaller offset. */
+ if (CONST_OK_FOR_LETTER_P(frame_size, 'O'))
+ size_rtx = GEN_INT (frame_size);
+ else
+ {
+ /* We do not have any scratch registers. */
+ gcc_assert (!interrupt_handler);
+
+ size_rtx = gen_rtx_REG (SImode, GPR_R9);
+ insn = emit_move_insn (size_rtx, GEN_INT (frame_size & 0xffff0000));
+ insn = emit_insn (gen_iorsi3 (size_rtx, size_rtx,
+ GEN_INT (frame_size & 0x0000ffff)));
+ /* Set R9 to point to old sp if required for access to register
+ save area. */
+ emit_insn (gen_addsi3 (size_rtx, size_rtx, stack_pointer_rtx));
+ }
+
+ /* Restore sp if there was some possible change to it. */
+ if (frame_pointer_needed)
+ insn = emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
+
+ /* Restore the registers. */
+ ms1_emit_save_fp (FROM_MEM_TO_PROCESSOR, current_frame_info);
+ ms1_emit_save_regs (FROM_MEM_TO_PROCESSOR, current_frame_info);
+
+ /* Make stack adjustment and use scratch register if constant too
+ large to fit as immediate. */
+ if (frame_size)
+ {
+ if (CONST_OK_FOR_LETTER_P(frame_size, 'O'))
+ /* Can handle this with simple add. */
+ insn = emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ size_rtx));
+ else
+ /* Scratch reg R9 has the old sp value. */
+ insn = emit_move_insn (stack_pointer_rtx,
+ gen_rtx_REG (SImode, GPR_R9));
+
+ REG_NOTES (insn)
+ = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
+ gen_rtx_SET (VOIDmode,
+ stack_pointer_rtx,
+ gen_rtx_PLUS (SImode,
+ stack_pointer_rtx,
+ GEN_INT (frame_size))),
+ REG_NOTES (insn));
+ }
+
+ if (cfun->machine && cfun->machine->eh_stack_adjust != NULL_RTX)
+ /* Perform the additional bump for __throw. */
+ emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ cfun->machine->eh_stack_adjust));
+
+ /* Generate the approriate return. */
+ if (eh_mode == EH_EPILOGUE)
+ {
+ emit_jump_insn (gen_eh_return_internal ());
+ emit_barrier ();
+ }
+ else if (interrupt_handler)
+ emit_jump_insn (gen_return_interrupt_internal ());
+ else
+ emit_jump_insn (gen_return_internal ());
+
+ /* Reset state info for each function. */
+ interrupt_handler = 0;
+ current_frame_info = zero_frame_info;
+ if (cfun->machine)
+ cfun->machine->eh_stack_adjust = NULL_RTX;
+}
+
+
+/* Generate code for the "eh_return" pattern. */
+void
+ms1_expand_eh_return (rtx * operands)
+{
+ if (GET_CODE (operands[0]) != REG
+ || REGNO (operands[0]) != EH_RETURN_STACKADJ_REGNO)
+ {
+ rtx sp = EH_RETURN_STACKADJ_RTX;
+
+ emit_move_insn (sp, operands[0]);
+ operands[0] = sp;
+ }
+
+ emit_insn (gen_eh_epilogue (operands[0]));
+}
+
+/* Generate code for the "eh_epilogue" pattern. */
+void
+ms1_emit_eh_epilogue (rtx * operands ATTRIBUTE_UNUSED)
+{
+ cfun->machine->eh_stack_adjust = EH_RETURN_STACKADJ_RTX; /* operands[0]; */
+ ms1_expand_epilogue (EH_EPILOGUE);
+}
+
+/* Handle an "interrupt" attribute. */
+static tree
+ms1_handle_interrupt_attribute (tree * node,
+ tree name,
+ tree args ATTRIBUTE_UNUSED,
+ int flags ATTRIBUTE_UNUSED,
+ bool * no_add_attrs)
+{
+ if (TREE_CODE (*node) != FUNCTION_DECL)
+ {
+ warning (OPT_Wattributes,
+ "%qs attribute only applies to functions",
+ IDENTIFIER_POINTER (name));
+ *no_add_attrs = true;
+ }
+
+ return NULL_TREE;
+}
+
+/* Table of machine attributes. */
+const struct attribute_spec ms1_attribute_table[] =
+{
+ /* name, min, max, decl?, type?, func?, handler */
+ { "interrupt", 0, 0, false, false, false, ms1_handle_interrupt_attribute },
+ { NULL, 0, 0, false, false, false, NULL }
+};
+
+/* Implement INITIAL_ELIMINATION_OFFSET. */
+int
+ms1_initial_elimination_offset (int from, int to)
+{
+ ms1_compute_frame_size (get_frame_size ());
+
+ if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
+ return 0;
+
+ else if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
+ return current_frame_info.total_size;
+
+ else if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
+ return current_frame_info.total_size;
+
+ else
+ gcc_unreachable ();
+}
+
+/* Generate a compare for CODE. Return a brand-new rtx that
+ represents the result of the compare. */
+
+static rtx
+ms1_generate_compare (enum rtx_code code, rtx op0, rtx op1)
+{
+ rtx scratch0, scratch1, const_scratch;
+
+ switch (code)
+ {
+ case GTU:
+ case LTU:
+ case GEU:
+ case LEU:
+ /* Need to adjust ranges for faking unsigned compares. */
+ scratch0 = gen_reg_rtx (SImode);
+ scratch1 = gen_reg_rtx (SImode);
+ const_scratch = force_reg (SImode, GEN_INT(MS1_MIN_INT));
+ emit_insn (gen_addsi3 (scratch0, const_scratch, op0));
+ emit_insn (gen_addsi3 (scratch1, const_scratch, op1));
+ break;
+ default:
+ scratch0 = op0;
+ scratch1 = op1;
+ break;
+ }
+
+ /* Adjust compare operator to fake unsigned compares. */
+ switch (code)
+ {
+ case GTU:
+ code = GT; break;
+ case LTU:
+ code = LT; break;
+ case GEU:
+ code = GE; break;
+ case LEU:
+ code = LE; break;
+ default:
+ /* do nothing */
+ break;
+ }
+
+ /* Generate the actual compare. */
+ return gen_rtx_fmt_ee (code, VOIDmode, scratch0, scratch1);
+}
+
+/* Emit a branch of kind CODE to location LOC. */
+
+void
+ms1_emit_cbranch (enum rtx_code code, rtx loc, rtx op0, rtx op1)
+{
+ rtx condition_rtx, loc_ref;
+
+ if (! reg_or_0_operand (op0, SImode))
+ op0 = copy_to_mode_reg (SImode, op0);
+
+ if (! reg_or_0_operand (op1, SImode))
+ op1 = copy_to_mode_reg (SImode, op1);
+
+ condition_rtx = ms1_generate_compare (code, op0, op1);
+ loc_ref = gen_rtx_LABEL_REF (VOIDmode, loc);
+ emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
+ gen_rtx_IF_THEN_ELSE (VOIDmode, condition_rtx,
+ loc_ref, pc_rtx)));
+}
+
+/* Subfunction of the following function. Update the flags of any MEM
+ found in part of X. */
+
+static void
+ms1_set_memflags_1 (rtx x, int in_struct_p, int volatile_p)
+{
+ int i;
+
+ switch (GET_CODE (x))
+ {
+ case SEQUENCE:
+ case PARALLEL:
+ for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
+ ms1_set_memflags_1 (XVECEXP (x, 0, i), in_struct_p, volatile_p);
+ break;
+
+ case INSN:
+ ms1_set_memflags_1 (PATTERN (x), in_struct_p, volatile_p);
+ break;
+
+ case SET:
+ ms1_set_memflags_1 (SET_DEST (x), in_struct_p, volatile_p);
+ ms1_set_memflags_1 (SET_SRC (x), in_struct_p, volatile_p);
+ break;
+
+ case MEM:
+ MEM_IN_STRUCT_P (x) = in_struct_p;
+ MEM_VOLATILE_P (x) = volatile_p;
+ /* Sadly, we cannot use alias sets because the extra aliasing
+ produced by the AND interferes. Given that two-byte quantities
+ are the only thing we would be able to differentiate anyway,
+ there does not seem to be any point in convoluting the early
+ out of the alias check. */
+ /* set_mem_alias_set (x, alias_set); */
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* Look for any MEMs in the current sequence of insns and set the
+ in-struct, unchanging, and volatile flags from the flags in REF.
+ If REF is not a MEM, don't do anything. */
+
+void
+ms1_set_memflags (rtx ref)
+{
+ rtx insn;
+ int in_struct_p, volatile_p;
+
+ if (GET_CODE (ref) != MEM)
+ return;
+
+ in_struct_p = MEM_IN_STRUCT_P (ref);
+ volatile_p = MEM_VOLATILE_P (ref);
+
+ /* This is only called from ms1.md, after having had something
+ generated from one of the insn patterns. So if everything is
+ zero, the pattern is already up-to-date. */
+ if (! in_struct_p && ! volatile_p)
+ return;
+
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ ms1_set_memflags_1 (insn, in_struct_p, volatile_p);
+}
+
+/* Implement SECONDARY_RELOAD_CLASS. */
+enum reg_class
+ms1_secondary_reload_class (enum reg_class class ATTRIBUTE_UNUSED,
+ enum machine_mode mode,
+ rtx x)
+{
+ if ((mode == QImode && (!TARGET_BYTE_ACCESS)) || mode == HImode)
+ {
+ if (GET_CODE (x) == MEM
+ || (GET_CODE (x) == REG && true_regnum (x) == -1)
+ || (GET_CODE (x) == SUBREG
+ && (GET_CODE (SUBREG_REG (x)) == MEM
+ || (GET_CODE (SUBREG_REG (x)) == REG
+ && true_regnum (SUBREG_REG (x)) == -1))))
+ return GENERAL_REGS;
+ }
+
+ return NO_REGS;
+}
+
+/* Handle FUNCTION_VALUE, FUNCTION_OUTGOING_VALUE, and LIBCALL_VALUE
+ macros. */
+rtx
+ms1_function_value (tree valtype, enum machine_mode mode, tree func_decl ATTRIBUTE_UNUSED)
+{
+ if ((mode) == DImode || (mode) == DFmode)
+ return gen_rtx_MEM (mode, gen_rtx_REG (mode, RETURN_VALUE_REGNUM));
+
+ if (valtype)
+ mode = TYPE_MODE (valtype);
+
+ return gen_rtx_REG (mode, RETURN_VALUE_REGNUM);
+}
+
+/* Split a move into two smaller pieces.
+ MODE indicates the reduced mode. OPERANDS[0] is the original destination
+ OPERANDS[1] is the original src. The new destinations are
+ OPERANDS[2] and OPERANDS[4], while the new sources are OPERANDS[3]
+ and OPERANDS[5]. */
+
+void
+ms1_split_words (enum machine_mode nmode,
+ enum machine_mode omode,
+ rtx *operands)
+{
+ rtx dl,dh; /* src/dest pieces. */
+ rtx sl,sh;
+ int move_high_first = 0; /* Assume no overlap. */
+
+ switch (GET_CODE (operands[0])) /* Dest. */
+ {
+ case SUBREG:
+ case REG:
+ if ((GET_CODE (operands[1]) == REG
+ || GET_CODE (operands[1]) == SUBREG)
+ && true_regnum (operands[0]) <= true_regnum (operands[1]))
+ move_high_first = 1;
+
+ if (GET_CODE (operands[0]) == SUBREG)
+ {
+ dl = gen_rtx_SUBREG (nmode, SUBREG_REG (operands[0]),
+ SUBREG_BYTE (operands[0]) + GET_MODE_SIZE (nmode));
+ dh = gen_rtx_SUBREG (nmode, SUBREG_REG (operands[0]), SUBREG_BYTE (operands[0]));
+ }
+ else if (GET_CODE (operands[0]) == REG && ! IS_PSEUDO_P (operands[0]))
+ {
+ int r = REGNO (operands[0]);
+ dh = gen_rtx_REG (nmode, r);
+ dl = gen_rtx_REG (nmode, r + HARD_REGNO_NREGS (r, nmode));
+ }
+ else
+ {
+ dh = gen_rtx_SUBREG (nmode, operands[0], 0);
+ dl = gen_rtx_SUBREG (nmode, operands[0], GET_MODE_SIZE (nmode));
+ }
+ break;
+
+ case MEM:
+ switch (GET_CODE (XEXP (operands[0], 0)))
+ {
+ case POST_INC:
+ case POST_DEC:
+ gcc_unreachable ();
+ default:
+ dl = operand_subword (operands[0],
+ GET_MODE_SIZE (nmode)/UNITS_PER_WORD,
+ 0, omode);
+ dh = operand_subword (operands[0], 0, 0, omode);
+ }
+ break;
+ default:
+ gcc_unreachable ();
+ }
+
+ switch (GET_CODE (operands[1]))
+ {
+ case REG:
+ if (! IS_PSEUDO_P (operands[1]))
+ {
+ int r = REGNO (operands[1]);
+
+ sh = gen_rtx_REG (nmode, r);
+ sl = gen_rtx_REG (nmode, r + HARD_REGNO_NREGS (r, nmode));
+ }
+ else
+ {
+ sh = gen_rtx_SUBREG (nmode, operands[1], 0);
+ sl = gen_rtx_SUBREG (nmode, operands[1], GET_MODE_SIZE (nmode));
+ }
+ break;
+
+ case CONST_DOUBLE:
+ if (operands[1] == const0_rtx)
+ sh = sl = const0_rtx;
+ else
+ split_double (operands[1], & sh, & sl);
+ break;
+
+ case CONST_INT:
+ if (operands[1] == const0_rtx)
+ sh = sl = const0_rtx;
+ else
+ {
+ int vl, vh;
+
+ switch (nmode)
+ {
+ default:
+ gcc_unreachable ();
+ }
+
+ sl = GEN_INT (vl);
+ sh = GEN_INT (vh);
+ }
+ break;
+
+ case SUBREG:
+ sl = gen_rtx_SUBREG (nmode,
+ SUBREG_REG (operands[1]),
+ SUBREG_BYTE (operands[1]) + GET_MODE_SIZE (nmode));
+ sh = gen_rtx_SUBREG (nmode,
+ SUBREG_REG (operands[1]),
+ SUBREG_BYTE (operands[1]));
+ break;
+
+ case MEM:
+ switch (GET_CODE (XEXP (operands[1], 0)))
+ {
+ case POST_DEC:
+ case POST_INC:
+ gcc_unreachable ();
+ break;
+ default:
+ sl = operand_subword (operands[1],
+ GET_MODE_SIZE (nmode)/UNITS_PER_WORD,
+ 0, omode);
+ sh = operand_subword (operands[1], 0, 0, omode);
+
+ /* Check if the DF load is going to clobber the register
+ used for the address, and if so make sure that is going
+ to be the second move. */
+ if (GET_CODE (dl) == REG
+ && true_regnum (dl)
+ == true_regnum (XEXP (XEXP (sl, 0 ), 0)))
+ move_high_first = 1;
+ }
+ break;
+ default:
+ gcc_unreachable ();
+ }
+
+ if (move_high_first)
+ {
+ operands[2] = dh;
+ operands[3] = sh;
+ operands[4] = dl;
+ operands[5] = sl;
+ }
+ else
+ {
+ operands[2] = dl;
+ operands[3] = sl;
+ operands[4] = dh;
+ operands[5] = sh;
+ }
+ return;
+}
+
+/* Implement TARGET_MUST_PASS_IN_STACK hook. */
+static bool
+ms1_pass_in_stack (enum machine_mode mode ATTRIBUTE_UNUSED, tree type)
+{
+ return (((type) != 0
+ && (TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
+ || TREE_ADDRESSABLE (type))));
+}
+\f
+/* Initialize the GCC target structure. */
+const struct attribute_spec ms1_attribute_table[];
+
+#undef TARGET_ATTRIBUTE_TABLE
+#define TARGET_ATTRIBUTE_TABLE ms1_attribute_table
+#undef TARGET_STRUCT_VALUE_RTX
+#define TARGET_STRUCT_VALUE_RTX ms1_struct_value_rtx
+#undef TARGET_PROMOTE_PROTOTYPES
+#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true
+#undef TARGET_PASS_BY_REFERENCE
+#define TARGET_PASS_BY_REFERENCE ms1_pass_by_reference
+#undef TARGET_MUST_PASS_IN_STACK
+#define TARGET_MUST_PASS_IN_STACK ms1_pass_in_stack
+#undef TARGET_ARG_PARTIAL_BYTES
+#define TARGET_ARG_PARTIAL_BYTES ms1_arg_partial_bytes
+
+struct gcc_target targetm = TARGET_INITIALIZER;
+\f
+#include "gt-ms1.h"
--- /dev/null
+/* Target Definitions for MorphoRISC1
+ Copyright (C) 2005 Free Software Foundation, Inc.
+ Contributed by Red Hat, 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.
+
+ 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, 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+extern struct rtx_def * ms1_ucmpsi3_libcall;
+
+enum processor_type
+{
+ PROCESSOR_MS1_64_001,
+ PROCESSOR_MS1_16_002,
+ PROCESSOR_MS1_16_003
+};
+
+enum epilogue_type
+{
+ EH_EPILOGUE,
+ NORMAL_EPILOGUE
+};
+
+extern enum processor_type ms1_cpu;
+
+\f
+/* A C string constant that tells the GCC driver program options to pass to
+ the assembler. */
+#undef ASM_SPEC
+#define ASM_SPEC "%{march=ms1-16-002: -march=ms1-16-002} %{march=ms1-16-003: -march=ms1-16-003} %{!march=*: -march=ms1-16-002}"
+
+/* A string to pass to at the end of the comman given to the linker. */
+#undef LIB_SPEC
+#define LIB_SPEC "--start-group -lc -lsim --end-group \
+%{msim: ; \
+march=ms1-64-001:-T 64-001.ld%s; \
+march=MS1-64-001:-T 64-001.ld%s; \
+march=ms1-16-002:-T 16-002.ld%s; \
+march=MS1-16-002:-T 16-002.ld%s; \
+march=ms1-16-003:-T 16-003.ld%s; \
+march=MS1-16-003:-T 16-003.ld%s}"
+
+/* A string to pass at the very beginning of the command given to the
+ linker. */
+#undef STARTFILE_SPEC
+#define STARTFILE_SPEC "%{msim:crt0.o%s;\
+march=ms1-64-001:%{!mno-crt0:crt0-64-001.o%s} startup-64-001.o%s; \
+march=MS1-64-001:%{!mno-crt0:crt0-64-001.o%s} startup-64-001.o%s; \
+march=ms1-16-002:%{!mno-crt0:crt0-16-002.o%s} startup-16-002.o%s; \
+march=MS1-16-002:%{!mno-crt0:crt0-16-002.o%s} startup-16-002.o%s; \
+march=ms1-16-003:%{!mno-crt0:crt0-16-003.o%s} startup-16-003.o%s; \
+march=MS1-16-003:%{!mno-crt0:crt0-16-003.o%s} startup-16-003.o%s} \
+crti.o%s crtbegin.o%s"
+
+/* A string to pass at the end of the command given to the linker. */
+#undef ENDFILE_SPEC
+#define ENDFILE_SPEC "%{msim:exit.o%s; \
+march=ms1-64-001:exit-64-001.o%s; \
+march=MS1-64-001:exit-64-001.o%s; \
+march=ms1-16-002:exit-16-002.o%s; \
+march=MS1-16-002:exit-16-002.o%s; \
+march=ms1-16-003:exit-16-003.o%s; \
+march=MS1-16-003:exit-16-003.o%s} \
+ crtend.o%s crtn.o%s"
+\f
+/* Run-time target specifications. */
+
+#define TARGET_CPU_CPP_BUILTINS() \
+ do \
+ { \
+ builtin_define_std ("ms1"); \
+ builtin_assert ("machine=ms1"); \
+ } \
+ while (0)
+
+#define TARGET_VERSION fprintf (stderr, " (ms1)");
+
+#define OVERRIDE_OPTIONS ms1_override_options ()
+
+#define CAN_DEBUG_WITHOUT_FP 1
+
+\f
+/* Storage Layout. */
+
+#define BITS_BIG_ENDIAN 0
+
+#define BYTES_BIG_ENDIAN 1
+
+#define WORDS_BIG_ENDIAN 1
+
+#define UNITS_PER_WORD 4
+
+/* A macro to update MODE and UNSIGNEDP when an object whose type is TYPE and
+ which has the specified mode and signedness is to be stored in a register.
+ This macro is only called when TYPE is a scalar type.
+
+ On most RISC machines, which only have operations that operate on a full
+ register, define this macro to set M to `word_mode' if M is an integer mode
+ narrower than `BITS_PER_WORD'. In most cases, only integer modes should be
+ widened because wider-precision floating-point operations are usually more
+ expensive than their narrower counterparts.
+
+ For most machines, the macro definition does not change UNSIGNEDP. However,
+ some machines, have instructions that preferentially handle either signed or
+ unsigned quantities of certain modes. For example, on the DEC Alpha, 32-bit
+ loads from memory and 32-bit add instructions sign-extend the result to 64
+ bits. On such machines, set UNSIGNEDP according to which kind of extension
+ is more efficient.
+
+ Do not define this macro if it would never modify MODE. */
+#define PROMOTE_MODE(MODE,UNSIGNEDP,TYPE) \
+ do \
+ { \
+ if (GET_MODE_CLASS (MODE) == MODE_INT \
+ && GET_MODE_SIZE (MODE) < 4) \
+ (MODE) = SImode; \
+ } \
+ while (0)
+
+/* Normal alignment required for function parameters on the stack, in bits.
+ All stack parameters receive at least this much alignment regardless of data
+ type. On most machines, this is the same as the size of an integer. */
+#define PARM_BOUNDARY 32
+
+/* Define this macro to the minimum alignment enforced by hardware for
+ the stack pointer on this machine. The definition is a C
+ expression for the desired alignment (measured in bits). This
+ value is used as a default if PREFERRED_STACK_BOUNDARY is not
+ defined. On most machines, this should be the same as
+ PARM_BOUNDARY. */
+#define STACK_BOUNDARY 32
+
+/* Alignment required for a function entry point, in bits. */
+#define FUNCTION_BOUNDARY 32
+
+/* Biggest alignment that any data type can require on this machine,
+ in bits. */
+#define BIGGEST_ALIGNMENT 32
+
+/* If defined, a C expression to compute the alignment for a variable
+ in the static store. TYPE is the data type, and ALIGN is the
+ alignment that the object would ordinarily have. The value of this
+ macro is used instead of that alignment to align the object.
+
+ If this macro is not defined, then ALIGN is used. */
+#define DATA_ALIGNMENT(TYPE, ALIGN) \
+ (TREE_CODE (TYPE) == ARRAY_TYPE \
+ && TYPE_MODE (TREE_TYPE (TYPE)) == QImode \
+ && (ALIGN) < BITS_PER_WORD ? BITS_PER_WORD : (ALIGN))
+
+/* If defined, a C expression to compute the alignment given to a constant that
+ is being placed in memory. CONSTANT is the constant and ALIGN is the
+ alignment that the object would ordinarily have. The value of this macro is
+ used instead of that alignment to align the object.
+
+ If this macro is not defined, then ALIGN is used.
+
+ The typical use of this macro is to increase alignment for string constants
+ to be word aligned so that `strcpy' calls that copy constants can be done
+ inline. */
+#define CONSTANT_ALIGNMENT(EXP, ALIGN) \
+ (TREE_CODE (EXP) == STRING_CST \
+ && (ALIGN) < BITS_PER_WORD ? BITS_PER_WORD : (ALIGN))
+
+/* Number of bits which any structure or union's size must be a multiple of.
+ Each structure or union's size is rounded up to a multiple of this.
+
+ If you do not define this macro, the default is the same as `BITS_PER_UNIT'. */
+#define STRUCTURE_SIZE_BOUNDARY 32
+
+/* Define this macro to be the value 1 if instructions will fail to work if
+ given data not on the nominal alignment. If instructions will merely go
+ slower in that case, define this macro as 0. */
+#define STRICT_ALIGNMENT 1
+
+/* Define this if you wish to imitate the way many other C compilers handle
+ alignment of bitfields and the structures that contain them. */
+#define PCC_BITFIELD_TYPE_MATTERS 1
+\f
+/* Layout of Source Language Data Types. */
+
+#define INT_TYPE_SIZE 32
+
+#define SHORT_TYPE_SIZE 16
+
+#define LONG_TYPE_SIZE 32
+
+#define LONG_LONG_TYPE_SIZE 64
+
+#define CHAR_TYPE_SIZE 8
+
+#define FLOAT_TYPE_SIZE 32
+
+#define DOUBLE_TYPE_SIZE 64
+
+#define LONG_DOUBLE_TYPE_SIZE 64
+
+#define DEFAULT_SIGNED_CHAR 1
+\f
+/* Register Basics. */
+
+/* General purpose registers. */
+#define GPR_FIRST 0 /* First gpr */
+#define GPR_LAST 15 /* Last possible gpr */
+
+#define GPR_R0 0 /* Always 0 */
+#define GPR_R7 7 /* Used as a scratch register */
+#define GPR_R8 8 /* Used as a scratch register */
+#define GPR_R9 9 /* Used as a scratch register */
+#define GPR_R10 10 /* Used as a scratch register */
+#define GPR_R11 11 /* Used as a scratch register */
+#define GPR_FP 12 /* Frame pointer */
+#define GPR_SP 13 /* Stack pointer */
+#define GPR_LINK 14 /* Saved return address as
+ seen by the caller */
+#define GPR_INTERRUPT_LINK 15 /* hold return addres for interrupts */
+
+/* Argument register that is eliminated in favor of the frame and/or stack
+ pointer. Also add register to point to where the return address is
+ stored. */
+#define SPECIAL_REG_FIRST (GPR_LAST + 1)
+#define SPECIAL_REG_LAST (SPECIAL_REG_FIRST)
+#define ARG_POINTER_REGNUM (SPECIAL_REG_FIRST + 0)
+#define SPECIAL_REG_P(R) ((R) == SPECIAL_REG_FIRST)
+
+/* The first/last register that can contain the arguments to a function. */
+#define FIRST_ARG_REGNUM 1
+#define LAST_ARG_REGNUM 4
+
+/* The register used to hold functions return value */
+#define RETVAL_REGNUM 11
+
+#define FIRST_PSEUDO_REGISTER (GPR_FIRST + 17)
+
+#define IS_PSEUDO_P(R) (REGNO (R) >= FIRST_PSEUDO_REGISTER)
+
+/* R0 always has the value 0
+ R10 static link
+ R12 FP pointer to active frame
+ R13 SP pointer to top of stack
+ R14 RA return address
+ R15 IRA interrupt return address. */
+#define FIXED_REGISTERS { 1, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 1, 1, 1, 1, \
+ 1 \
+ }
+
+/* Like `FIXED_REGISTERS' but has 1 for each register that is clobbered (in
+ general) by function calls as well as for fixed registers. This macro
+ therefore identifies the registers that are not available for general
+ allocation of values that must live across function calls. */
+#define CALL_USED_REGISTERS { 1, 1, 1, 1, 1, 0, 0, 1, \
+ 1, 1, 1, 1, 1, 1, 1, 1, \
+ 1 \
+ }
+
+\f
+/* How Values Fit in Registers. */
+
+#define HARD_REGNO_NREGS(REGNO, MODE) \
+ ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
+
+#define HARD_REGNO_MODE_OK(REGNO, MODE) 1
+
+/* A C expression that is nonzero if a value of mode MODE1 is
+ accessible in mode MODE2 without copying. */
+#define MODES_TIEABLE_P(MODE1, MODE2) 1
+\f
+/* Register Classes. */
+
+enum reg_class
+{
+ NO_REGS,
+ ALL_REGS,
+ LIM_REG_CLASSES
+};
+
+#define GENERAL_REGS ALL_REGS
+
+#define N_REG_CLASSES ((int) LIM_REG_CLASSES)
+
+#define REG_CLASS_NAMES {"NO_REGS", "ALL_REGS" }
+
+#define REG_CLASS_CONTENTS \
+ { \
+ { 0x0, 0x0 }, \
+ { (((1 << (GPR_LAST + 1)) - 1) & ~(1 << GPR_FIRST)), 0x0 }, \
+ }
+
+/* A C expression whose value is a register class containing hard register
+ REGNO. In general there is more than one such class; choose a class which
+ is "minimal", meaning that no smaller class also contains the register. */
+#define REGNO_REG_CLASS(REGNO) GENERAL_REGS
+
+#define BASE_REG_CLASS GENERAL_REGS
+
+#define INDEX_REG_CLASS NO_REGS
+
+#define REG_CLASS_FROM_LETTER(CHAR) NO_REGS
+
+#define REGNO_OK_FOR_BASE_P(NUM) 1
+
+#define REGNO_OK_FOR_INDEX_P(NUM) 1
+
+/* A C expression that places additional restrictions on the register class to
+ use when it is necessary to copy value X into a register in class CLASS.
+ The value is a register class; perhaps CLASS, or perhaps another, smaller
+ class. On many machines, the following definition is safe:
+
+ #define PREFERRED_RELOAD_CLASS(X,CLASS) CLASS
+*/
+#define PREFERRED_RELOAD_CLASS(X, CLASS) (CLASS)
+
+#define SECONDARY_RELOAD_CLASS(CLASS,MODE,X) \
+ ms1_secondary_reload_class((CLASS), (MODE), (X))
+
+/* A C expression for the maximum number of consecutive registers of
+ class CLASS needed to hold a value of mode MODE. */
+#define CLASS_MAX_NREGS(CLASS, MODE) \
+ ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
+
+/* For MorphoRISC1:
+
+ `I' is used for the range of constants an arithmetic insn can
+ actually contain (16 bits signed integers).
+
+ `J' is used for the range which is just zero (ie, $r0).
+
+ `K' is used for the range of constants a logical insn can actually
+ contain (16 bit zero-extended integers).
+
+ `L' is used for the range of constants that be loaded with lui
+ (ie, the bottom 16 bits are zero).
+
+ `M' is used for the range of constants that take two words to load
+ (ie, not matched by `I', `K', and `L').
+
+ `N' is used for negative 16 bit constants other than -65536.
+
+ `O' is a 15 bit signed integer.
+
+ `P' is used for positive 16 bit constants. */
+
+#define SMALL_INT(X) ((unsigned HOST_WIDE_INT) (INTVAL (X) + 0x8000) < 0x10000)
+#define SMALL_INT_UNSIGNED(X) ((unsigned HOST_WIDE_INT) (INTVAL (X)) < 0x10000)
+
+/* A C expression that defines the machine-dependent operand
+ constraint letters that specify particular ranges of integer
+ values. If C is one of those letters, the expression should check
+ that VALUE, an integer, is in the appropriate range and return 1 if
+ so, 0 otherwise. If C is not one of those letters, the value
+ should be 0 regardless of VALUE. */
+#define CONST_OK_FOR_LETTER_P(VALUE, C) \
+ ((C) == 'I' ? ((unsigned HOST_WIDE_INT) ((VALUE) + 0x8000) < 0x10000) \
+ : (C) == 'J' ? ((VALUE) == 0) \
+ : (C) == 'K' ? ((unsigned HOST_WIDE_INT) (VALUE) < 0x10000) \
+ : (C) == 'L' ? (((VALUE) & 0x0000ffff) == 0 \
+ && (((VALUE) & ~2147483647) == 0 \
+ || ((VALUE) & ~2147483647) == ~2147483647)) \
+ : (C) == 'M' ? ((((VALUE) & ~0x0000ffff) != 0) \
+ && (((VALUE) & ~0x0000ffff) != ~0x0000ffff) \
+ && (((VALUE) & 0x0000ffff) != 0 \
+ || (((VALUE) & ~2147483647) != 0 \
+ && ((VALUE) & ~2147483647) != ~2147483647))) \
+ : (C) == 'N' ? ((unsigned HOST_WIDE_INT) ((VALUE) + 0xffff) < 0xffff) \
+ : (C) == 'O' ? ((unsigned HOST_WIDE_INT) ((VALUE) + 0x4000) < 0x8000) \
+ : (C) == 'P' ? ((VALUE) != 0 && (((VALUE) & ~0x0000ffff) == 0)) \
+ : 0)
+
+/* A C expression that defines the machine-dependent operand constraint letters
+ (`G', `H') that specify particular ranges of `const_double' values. */
+#define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C) 0
+
+/* Most negative value represent on ms1 */
+#define MS1_MIN_INT 0x80000000
+\f
+/* Basic Stack Layout. */
+
+enum save_direction
+{
+ FROM_PROCESSOR_TO_MEM,
+ FROM_MEM_TO_PROCESSOR
+};
+
+/* Tell prologue and epilogue if register REGNO should be saved / restored.
+ The return address and frame pointer are treated separately.
+ Don't consider them here. */
+#define MUST_SAVE_REGISTER(regno) \
+ ( (regno) != GPR_LINK \
+ && (regno) != GPR_FP \
+ && (regno) != GPR_SP \
+ && (regno) != GPR_R0 \
+ && (( regs_ever_live [regno] && ! call_used_regs [regno] ) \
+ /* Save ira register in an interrupt handler. */ \
+ || (interrupt_handler && (regno) == GPR_INTERRUPT_LINK) \
+ /* Save any register used in an interrupt handler. */ \
+ || (interrupt_handler && regs_ever_live [regno]) \
+ /* Save call clobbered registers in non-leaf interrupt \
+ handlers. */ \
+ || (interrupt_handler && call_used_regs[regno] \
+ && !current_function_is_leaf) \
+ ||(current_function_calls_eh_return \
+ && (regno == GPR_R7 || regno == GPR_R8)) \
+ ) \
+ )
+
+#define STACK_GROWS_DOWNWARD 1
+
+/* Offset from the frame pointer to the first local variable slot to be
+ allocated.
+
+ If `FRAME_GROWS_DOWNWARD', find the next slot's offset by
+ subtracting the first slot's length from `STARTING_FRAME_OFFSET'.
+ Otherwise, it is found by adding the length of the first slot to
+ the value `STARTING_FRAME_OFFSET'. */
+#define STARTING_FRAME_OFFSET current_function_outgoing_args_size
+
+/* Offset from the argument pointer register to the first argument's address.
+ On some machines it may depend on the data type of the function.
+
+ If `ARGS_GROW_DOWNWARD', this is the offset to the location above the first
+ argument's address. */
+#define FIRST_PARM_OFFSET(FUNDECL) 0
+
+#define RETURN_ADDR_RTX(COUNT, FRAMEADDR) \
+ ms1_return_addr_rtx (COUNT)
+
+/* A C expression whose value is RTL representing the location of the incoming
+ return address at the beginning of any function, before the prologue. This
+ RTL is either a `REG', indicating that the return value is saved in `REG',
+ or a `MEM' representing a location in the stack.
+
+ You only need to define this macro if you want to support call frame
+ debugging information like that provided by DWARF 2. */
+#define INCOMING_RETURN_ADDR_RTX gen_rtx_REG (SImode, GPR_LINK)
+
+/* A C expression whose value is an integer giving the offset, in bytes, from
+ the value of the stack pointer register to the top of the stack frame at the
+ beginning of any function, before the prologue. The top of the frame is
+ defined to be the value of the stack pointer in the previous frame, just
+ before the call instruction.
+
+ You only need to define this macro if you want to support call frame
+ debugging information like that provided by DWARF 2. */
+#define INCOMING_FRAME_SP_OFFSET 0
+
+#define STACK_POINTER_REGNUM GPR_SP
+
+#define FRAME_POINTER_REGNUM GPR_FP
+
+/* The register number of the arg pointer register, which is used to
+ access the function's argument list. */
+#define ARG_POINTER_REGNUM (SPECIAL_REG_FIRST + 0)
+
+/* Register numbers used for passing a function's static chain pointer. */
+#define STATIC_CHAIN_REGNUM 10
+
+/* A C expression which is nonzero if a function must have and use a frame
+ pointer. */
+#define FRAME_POINTER_REQUIRED 0
+
+/* Structure to be filled in by compute_frame_size with register
+ save masks, and offsets for the current function. */
+
+struct ms1_frame_info
+{
+ unsigned int total_size; /* # Bytes that the entire frame takes up. */
+ unsigned int pretend_size; /* # Bytes we push and pretend caller did. */
+ unsigned int args_size; /* # Bytes that outgoing arguments take up. */
+ unsigned int extra_size;
+ unsigned int reg_size; /* # Bytes needed to store regs. */
+ unsigned int var_size; /* # Bytes that variables take up. */
+ unsigned int frame_size; /* # Bytes in current frame. */
+ unsigned int reg_mask; /* Mask of saved registers. */
+ unsigned int save_fp; /* Nonzero if frame pointer must be saved. */
+ unsigned int save_lr; /* Nonzero if return pointer must be saved. */
+ int initialized; /* Nonzero if frame size already calculated. */
+};
+
+extern struct ms1_frame_info current_frame_info;
+
+/* If defined, this macro specifies a table of register pairs used to eliminate
+ unneeded registers that point into the stack frame. */
+#define ELIMINABLE_REGS \
+{ \
+ {ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
+ {ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM}, \
+ {FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM} \
+}
+
+/* A C expression that returns non-zero if the compiler is allowed to try to
+ replace register number FROM with register number TO. */
+#define CAN_ELIMINATE(FROM, TO) \
+ ((FROM) == ARG_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM \
+ ? ! frame_pointer_needed \
+ : 1)
+
+/* This macro is similar to `INITIAL_FRAME_POINTER_OFFSET'. It
+ specifies the initial difference between the specified pair of
+ registers. This macro must be defined if `ELIMINABLE_REGS' is
+ defined. */
+#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
+ (OFFSET) = ms1_initial_elimination_offset (FROM, TO)
+
+/* If defined, the maximum amount of space required for outgoing
+ arguments will be computed and placed into the variable
+ `current_function_outgoing_args_size'. */
+#define ACCUMULATE_OUTGOING_ARGS 1
+
+/* Define this if it is the responsibility of the caller to
+ allocate the area reserved for arguments passed in registers. */
+#define OUTGOING_REG_PARM_STACK_SPACE
+
+/* The number of register assigned to holding function arguments. */
+#define MS1_NUM_ARG_REGS 4
+
+/* Define this if it is the responsibility of the caller to allocate
+ the area reserved for arguments passed in registers. */
+#define REG_PARM_STACK_SPACE(FNDECL) (MS1_NUM_ARG_REGS * UNITS_PER_WORD)
+
+/* Define this macro if `REG_PARM_STACK_SPACE' is defined, but the stack
+ parameters don't skip the area specified by it. */
+#define STACK_PARMS_IN_REG_PARM_AREA
+
+/* A C expression that should indicate the number of bytes of its own
+ arguments that a function pops on returning, or 0 if the function
+ pops no arguments and the caller must therefore pop them all after
+ the function returns. */
+#define RETURN_POPS_ARGS(FUNDECL, FUNTYPE, STACK_SIZE) 0
+
+#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \
+ ms1_function_arg (& (CUM), (MODE), (TYPE), (NAMED), FALSE)
+
+#define CUMULATIVE_ARGS int
+
+#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, FNDECL, N_NAMED_ARGS) \
+ ms1_init_cumulative_args (& (CUM), FNTYPE, LIBNAME, FNDECL, FALSE)
+
+#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \
+ ms1_function_arg_advance (&CUM, MODE, TYPE, NAMED)
+
+#define FUNCTION_ARG_BOUNDARY(MODE, TYPE) \
+ ms1_function_arg_boundary (MODE, TYPE)
+
+#define FUNCTION_ARG_REGNO_P(REGNO) \
+ ((REGNO) >= FIRST_ARG_REGNUM && ((REGNO) <= LAST_ARG_REGNUM))
+
+#define RETURN_VALUE_REGNUM RETVAL_REGNUM
+
+#define FUNCTION_VALUE(VALTYPE, FUNC) \
+ ms1_function_value (VALTYPE, TYPE_MODE(VALTYPE), FUNC)
+
+#define LIBCALL_VALUE(MODE) \
+ ms1_function_value (NULL_TREE, MODE, NULL_TREE)
+
+#define FUNCTION_VALUE_REGNO_P(REGNO) ((REGNO) == RETURN_VALUE_REGNUM)
+
+/* A C expression which can inhibit the returning of certain function
+ values in registers, based on the type of value. */
+#define RETURN_IN_MEMORY(TYPE) (int_size_in_bytes (TYPE) > UNITS_PER_WORD)
+
+/* Define this macro to be 1 if all structure and union return values must be
+ in memory. */
+#define DEFAULT_PCC_STRUCT_RETURN 0
+
+/* Define this macro as a C expression that is nonzero if the return
+ instruction or the function epilogue ignores the value of the stack
+ pointer; in other words, if it is safe to delete an instruction to
+ adjust the stack pointer before a return from the function. */
+#define EXIT_IGNORE_STACK 1
+
+#define EPILOGUE_USES(REGNO) ms1_epilogue_uses(REGNO)
+
+/* Define this macro if the function epilogue contains delay slots to which
+ instructions from the rest of the function can be "moved". */
+#define DELAY_SLOTS_FOR_EPILOGUE 1
+
+/* A C expression that returns 1 if INSN can be placed in delay slot number N
+ of the epilogue. */
+#define ELIGIBLE_FOR_EPILOGUE_DELAY(INSN, N) 0
+
+#define FUNCTION_PROFILER(FILE, LABELNO) gcc_unreachable ()
+
+#define EXPAND_BUILTIN_VA_START(VALIST, NEXTARG) \
+ ms1_va_start (VALIST, NEXTARG)
+
+/* Trampolines are not implemented. */
+#define TRAMPOLINE_SIZE 0
+
+#define INITIALIZE_TRAMPOLINE(ADDR, FNADDR, STATIC_CHAIN)
+
+/* ?? What is this -- aldyh ?? */
+#define UCMPSI3_LIBCALL "__ucmpsi3"
+
+/* Addressing Modes. */
+
+/* A C expression that is 1 if the RTX X is a constant which is a valid
+ address. */
+#define CONSTANT_ADDRESS_P(X) CONSTANT_P (X)
+
+/* A number, the maximum number of registers that can appear in a valid memory
+ address. Note that it is up to you to specify a value equal to the maximum
+ number that `GO_IF_LEGITIMATE_ADDRESS' would ever accept. */
+#define MAX_REGS_PER_ADDRESS 1
+
+#ifdef REG_OK_STRICT
+#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \
+{ \
+ if (ms1_legitimate_address_p (MODE, X, 1)) \
+ goto ADDR; \
+}
+#else
+#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \
+{ \
+ if (ms1_legitimate_address_p (MODE, X, 0)) \
+ goto ADDR; \
+}
+#endif
+
+#ifdef REG_OK_STRICT
+#define REG_OK_FOR_BASE_P(X) ms1_reg_ok_for_base_p (X, 1)
+#else
+#define REG_OK_FOR_BASE_P(X) ms1_reg_ok_for_base_p (X, 0)
+#endif
+
+#define REG_OK_FOR_INDEX_P(X) REG_OK_FOR_BASE_P (X)
+
+#define LEGITIMIZE_ADDRESS(X, OLDX, MODE, WIN) {}
+
+#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR, LABEL)
+
+#define LEGITIMATE_CONSTANT_P(X) 1
+
+/* A C expression for the cost of moving data of mode M between a register and
+ memory. A value of 2 is the default; this cost is relative to those in
+ `REGISTER_MOVE_COST'.
+
+ If moving between registers and memory is more expensive than between two
+ registers, you should define this macro to express the relative cost. */
+#define MEMORY_MOVE_COST(M,C,I) 10
+
+/* Define this macro as a C expression which is nonzero if accessing less than
+ a word of memory (i.e. a `char' or a `short') is no faster than accessing a
+ word of memory. */
+#define SLOW_BYTE_ACCESS 1
+
+#define SLOW_UNALIGNED_ACCESS(MODE, ALIGN) 1
+
+#define TEXT_SECTION_ASM_OP ".text"
+
+#define DATA_SECTION_ASM_OP ".data"
+
+#define BSS_SECTION_ASM_OP "\t.section\t.bss"
+
+/* A C string constant for text to be output before each `asm' statement or
+ group of consecutive ones. Normally this is `"#APP"', which is a comment
+ that has no effect on most assemblers but tells the GNU assembler that it
+ must check the lines that follow for all valid assembler constructs. */
+#define ASM_APP_ON "#APP\n"
+
+/* A C string constant for text to be output after each `asm' statement or
+ group of consecutive ones. Normally this is `"#NO_APP"', which tells the
+ GNU assembler to resume making the time-saving assumptions that are valid
+ for ordinary compiler output. */
+#define ASM_APP_OFF "#NO_APP\n"
+
+/* This is how to output an assembler line defining a `char' constant. */
+#define ASM_OUTPUT_CHAR(FILE, VALUE) \
+ do \
+ { \
+ fprintf (FILE, "\t.byte\t"); \
+ output_addr_const (FILE, (VALUE)); \
+ fprintf (FILE, "\n"); \
+ } \
+ while (0)
+
+/* This is how to output an assembler line defining a `short' constant. */
+#define ASM_OUTPUT_SHORT(FILE, VALUE) \
+ do \
+ { \
+ fprintf (FILE, "\t.hword\t"); \
+ output_addr_const (FILE, (VALUE)); \
+ fprintf (FILE, "\n"); \
+ } \
+ while (0)
+
+/* This is how to output an assembler line defining an `int' constant.
+ We also handle symbol output here. */
+#define ASM_OUTPUT_INT(FILE, VALUE) \
+ do \
+ { \
+ fprintf (FILE, "\t.word\t"); \
+ output_addr_const (FILE, (VALUE)); \
+ fprintf (FILE, "\n"); \
+ } \
+ while (0)
+
+/* A C statement to output to the stdio stream STREAM an assembler instruction
+ to assemble a single byte containing the number VALUE.
+
+ This declaration must be present. */
+#define ASM_OUTPUT_BYTE(STREAM, VALUE) \
+ fprintf (STREAM, "\t%s\t0x%x\n", ASM_BYTE_OP, (VALUE))
+
+/* Globalizing directive for a label. */
+#define GLOBAL_ASM_OP "\t.globl "
+
+#define REGISTER_NAMES \
+{ "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", \
+ "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15", \
+ "ap" }
+
+/* If defined, a C initializer for an array of structures containing a name and
+ a register number. This macro defines additional names for hard registers,
+ thus allowing the `asm' option in declarations to refer to registers using
+ alternate names. */
+#define ADDITIONAL_REGISTER_NAMES \
+{ { "FP", 12}, {"SP", 13}, {"RA", 14}, {"IRA", 15} }
+
+/* Define this macro if you are using an unusual assembler that requires
+ different names for the machine instructions.
+
+ The definition is a C statement or statements which output an assembler
+ instruction opcode to the stdio stream STREAM. The macro-operand PTR is a
+ variable of type `char *' which points to the opcode name in its "internal"
+ form--the form that is written in the machine description. The definition
+ should output the opcode name to STREAM, performing any translation you
+ desire, and increment the variable PTR to point at the end of the opcode so
+ that it will not be output twice. */
+#define ASM_OUTPUT_OPCODE(STREAM, PTR) \
+ (PTR) = ms1_asm_output_opcode (STREAM, PTR)
+
+#define FINAL_PRESCAN_INSN(INSN, OPVEC, NOPERANDS) \
+ ms1_final_prescan_insn (INSN, OPVEC, NOPERANDS)
+
+#define PRINT_OPERAND(STREAM, X, CODE) ms1_print_operand (STREAM, X, CODE)
+
+/* A C expression which evaluates to true if CODE is a valid punctuation
+ character for use in the `PRINT_OPERAND' macro. */
+/* #: Print nop for delay slot. */
+#define PRINT_OPERAND_PUNCT_VALID_P(CODE) ((CODE) == '#')
+
+#define PRINT_OPERAND_ADDRESS(STREAM, X) ms1_print_operand_address (STREAM, X)
+
+/* If defined, C string expressions to be used for the `%R', `%L', `%U', and
+ `%I' options of `asm_fprintf' (see `final.c'). These are useful when a
+ single `md' file must support multiple assembler formats. In that case, the
+ various `tm.h' files can define these macros differently.
+
+ USER_LABEL_PREFIX is defined in svr4.h. */
+#define REGISTER_PREFIX "%"
+#define LOCAL_LABEL_PREFIX "."
+#define USER_LABEL_PREFIX ""
+#define IMMEDIATE_PREFIX ""
+
+/* This macro should be provided on machines where the addresses in a dispatch
+ table are relative to the table's own address.
+
+ The definition should be a C statement to output to the stdio stream STREAM
+ an assembler pseudo-instruction to generate a difference between two labels.
+ VALUE and REL are the numbers of two internal labels. The definitions of
+ these labels are output using `targetm.asm_out.internal_label', and they
+ must be printed in the same way here. */
+#define ASM_OUTPUT_ADDR_DIFF_ELT(STREAM, BODY, VALUE, REL) \
+fprintf (STREAM, "\t.word .L%d-.L%d\n", VALUE, REL)
+
+/* This macro should be provided on machines where the addresses in a dispatch
+ table are absolute.
+
+ The definition should be a C statement to output to the stdio stream STREAM
+ an assembler pseudo-instruction to generate a reference to a label. VALUE
+ is the number of an internal label whose definition is output using
+ `targetm.asm_out.internal_label'. */
+#define ASM_OUTPUT_ADDR_VEC_ELT(STREAM, VALUE) \
+fprintf (STREAM, "\t.word .L%d\n", VALUE)
+
+#define DWARF_FRAME_RETURN_COLUMN DWARF_FRAME_REGNUM (GPR_LINK)
+
+#define EH_RETURN_DATA_REGNO(N) \
+ ((N) == 0 ? GPR_R7 : (N) == 1 ? GPR_R8 : INVALID_REGNUM)
+
+#define EH_RETURN_STACKADJ_REGNO GPR_R11
+#define EH_RETURN_STACKADJ_RTX \
+ gen_rtx_REG (SImode, EH_RETURN_STACKADJ_REGNO)
+#define EH_RETURN_HANDLER_REGNO GPR_R10
+#define EH_RETURN_HANDLER_RTX \
+ gen_rtx_REG (SImode, EH_RETURN_HANDLER_REGNO)
+\f
+#define ASM_OUTPUT_ALIGN(STREAM, POWER) \
+ fprintf ((STREAM), "\t.p2align %d\n", (POWER))
+
+#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG
+
+#ifndef DWARF2_DEBUGGING_INFO
+#define DWARF2_DEBUGGING_INFO
+#endif
+
+/* Define this macro if GCC should produce dwarf version 2-style
+ line numbers. This usually requires extending the assembler to
+ support them, and #defining DWARF2_LINE_MIN_INSN_LENGTH in the
+ assembler configuration header files. */
+#define DWARF2_ASM_LINE_DEBUG_INFO 1
+
+/* An alias for a machine mode name. This is the machine mode that
+ elements of a jump-table should have. */
+#define CASE_VECTOR_MODE SImode
+
+/* Define this macro if operations between registers with integral
+ mode smaller than a word are always performed on the entire
+ register. Most RISC machines have this property and most CISC
+ machines do not. */
+#define WORD_REGISTER_OPERATIONS
+
+/* The maximum number of bytes that a single instruction can move quickly from
+ memory to memory. */
+#define MOVE_MAX 4
+
+/* A C expression which is nonzero if on this machine it is safe to "convert"
+ an integer of INPREC bits to one of OUTPREC bits (where OUTPREC is smaller
+ than INPREC) by merely operating on it as if it had only OUTPREC bits.
+
+ On many machines, this expression can be 1.
+
+ When `TRULY_NOOP_TRUNCATION' returns 1 for a pair of sizes for modes for
+ which `MODES_TIEABLE_P' is 0, suboptimal code can result. If this is the
+ case, making `TRULY_NOOP_TRUNCATION' return 0 in such cases may improve
+ things. */
+#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1
+
+#define Pmode SImode
+
+/* An alias for the machine mode used for memory references to functions being
+ called, in `call' RTL expressions. On most machines this should be
+ `QImode'. */
+#define FUNCTION_MODE QImode
+
+#define HANDLE_SYSV_PRAGMA 1
+
+/* Indicate how many instructions can be issued at the same time. */
+#define ISSUE_RATE 1
+
+/* Define the information needed to generate branch and scc insns. This is
+ stored from the compare operation. Note that we can't use "rtx" here
+ since it hasn't been defined! */
+
+extern struct rtx_def * ms1_compare_op0;
+extern struct rtx_def * ms1_compare_op1;
+
--- /dev/null
+;; Machine description for MorphoRISC1
+;; Copyright (C) 2005 Free Software Foundation, Inc.
+;; Contributed by Red Hat, 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.
+
+;; 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, 51 Franklin Street, Fifth Floor, Boston, MA
+;; 02110-1301, USA.
+\f
+;; Attributes
+(define_attr "type" "branch,mem,io,arith,complex,unknown"
+ (const_string "unknown") )
+
+;; If the attribute takes numeric values, no `enum' type will be defined and
+;; the function to obtain the attribute's value will return `int'.
+
+(define_attr "length" "" (const_int 4))
+
+\f
+;; DFA scheduler.
+(define_automaton "other")
+(define_cpu_unit "decode_unit" "other")
+(define_cpu_unit "memory_unit" "other")
+(define_cpu_unit "branch_unit" "other")
+
+(define_insn_reservation "mem_access" 2
+ (eq_attr "type" "mem")
+ "decode_unit+memory_unit*2")
+
+(define_insn_reservation "io_access" 2
+ (eq_attr "type" "io")
+ "decode_unit+memory_unit*2")
+
+(define_insn_reservation "branch_access" 2
+ (eq_attr "type" "branch")
+ "decode_unit+branch_unit*2")
+
+(define_insn_reservation "arith_access" 1
+ (eq_attr "type" "arith")
+ "decode_unit")
+
+(define_bypass 2 "arith_access" "branch_access")
+(define_bypass 3 "mem_access" "branch_access")
+(define_bypass 3 "io_access" "branch_access")
+
+\f
+;; Delay Slots
+
+;; The ms1 does not allow branches in the delay slot.
+;; The ms1 does not allow back to back memory or io instruction.
+;; The compiler does not know what the type of instruction is at
+;; the destination of the branch. Thus, only type that will be acceptable
+;; (safe) is the arith type.
+
+(define_delay (eq_attr "type" "branch")
+ [(eq_attr "type" "arith") (nil) (nil)])
+
+\f
+;; Issue 64382
+;; This pattern implements the decrement and branch non-zero instruction
+;; which can be used by gcc loop optimizer under certain conditions.
+;; For an example of it being used try compiling the gcc test case
+;; gcc.c-torture/execute/921213-1.c with optimisations enabled.
+
+;; XXX - FIXME - TARGET_MUL is used as a condition since it is set when the
+;; target is the MS1-16-003, which is the only Morpho CPU which currently
+;; implements this instruction. Stricly speaking we ought to define a
+;; new command line switch to enable/disable the DBNZ instruction or else
+;; change this pattern so that it explicitly checks for an MS1-16-003
+;; architecture.
+
+(define_insn "decrement_and_branch_until_zero"
+ [(parallel [(set (pc)
+ (if_then_else
+ (ne (match_operand:SI 0 "register_operand" "+r") (const_int 0))
+ (label_ref (match_operand 1 "" ""))
+ (pc)))
+ (set (match_dup 0)
+ (plus:SI (match_dup 0) (const_int -1)))
+ ])
+ ]
+ "TARGET_MUL"
+ "dbnz\t%0, %l1%#"
+ [(set_attr "length" "4")]
+)
+
+;; This peephole is defined in the vain hope that it might actually trigger one
+;; day, although I have yet to find a test case that matches it. The normal
+;; problem is that GCC likes to move the loading of the constant value -1 out
+;; of the loop, so it is not here to be matched.
+
+(define_peephole2
+ [(set (match_operand:SI 0 "register_operand" "")
+ (plus:SI (match_dup 0) (const_int -1)))
+ (set (match_operand:SI 1 "register_operand" "")
+ (const_int -1))
+ (set (pc) (if_then_else
+ (ne (match_dup 0) (match_dup 1))
+ (label_ref (match_operand 2 "" ""))
+ (pc)))
+ ]
+ "TARGET_MUL"
+ [(parallel [(set (pc)
+ (if_then_else
+ (ne (match_dup 0) (const_int 0))
+ (label_ref (match_dup 2))
+ (pc)))
+ (set (match_dup 0)
+ (plus:SI (match_dup 0) (const_int -1)))
+ ])
+ ]
+ ""
+)
+\f
+;; Moves
+
+(define_expand "loadqi"
+ [
+ ;; compute shift
+ (set (match_operand:SI 2 "register_operand" "")
+ (and:SI (match_dup 1) (const_int 3)))
+ (set (match_dup 2) (xor:SI (match_dup 2) (const_int 3)))
+ (set (match_dup 2 ) (ashift:SI (match_dup 2) (const_int 3)))
+
+ ;; get word that contains byte
+ (set (match_operand:SI 0 "register_operand" "")
+ (mem:SI (and:SI (match_operand:SI 1 "register_operand" "")
+ (const_int -3))))
+
+ ;; align byte
+ (set (match_dup 0) (ashiftrt:SI (match_dup 0) (match_dup 2)))
+ ]
+ ""
+ "")
+
+
+;; storeqi
+;; operand 0 byte value to store
+;; operand 1 address
+;; operand 2 temp, word containing byte
+;; operand 3 temp, shift count
+;; operand 4 temp, mask, aligned and masked byte
+;; operand 5 (unused)
+(define_expand "storeqi"
+ [
+ ;; compute shift
+ (set (match_operand:SI 3 "register_operand" "")
+ (and:SI (match_operand:SI 1 "register_operand" "") (const_int 3)))
+ (set (match_dup 3) (xor:SI (match_dup 3) (const_int 3)))
+ (set (match_dup 3) (ashift:SI (match_dup 3) (const_int 3)))
+
+ ;; get word that contains byte
+ (set (match_operand:SI 2 "register_operand" "")
+ (mem:SI (and:SI (match_dup 1) (const_int -3))))
+
+ ;; generate mask
+ (set (match_operand:SI 4 "register_operand" "") (const_int 255))
+ (set (match_dup 4) (ashift:SI (match_dup 4) (match_dup 3)))
+ (set (match_dup 4) (not:SI (match_dup 4)))
+
+ ;; clear appropriate bits
+ (set (match_dup 2) (and:SI (match_dup 2) (match_dup 4)))
+
+ ;; align byte
+ (set (match_dup 4)
+ (and:SI (match_operand:SI 0 "register_operand" "") (const_int 255)))
+ (set (match_dup 4) (ashift:SI (match_dup 4) (match_dup 3)))
+
+ ;; combine
+ (set (match_dup 2) (ior:SI (match_dup 4) (match_dup 2)))
+ ;; store updated word
+ (set (mem:SI (and:SI (match_dup 1) (const_int -3))) (match_dup 2))
+ ]
+ ""
+ "")
+
+
+(define_expand "movqi"
+ [(set (match_operand:QI 0 "general_operand" "")
+ (match_operand:QI 1 "general_operand" ""))]
+ ""
+ "
+{
+ if (!reload_in_progress
+ && !reload_completed
+ && GET_CODE (operands[0]) == MEM
+ && GET_CODE (operands[1]) == MEM)
+ operands[1] = copy_to_mode_reg (QImode, operands[1]);
+
+ if ( (! TARGET_BYTE_ACCESS) && GET_CODE (operands[0]) == MEM)
+ {
+ rtx scratch1 = gen_reg_rtx (SImode);
+ rtx scratch2 = gen_reg_rtx (SImode);
+ rtx scratch3 = gen_reg_rtx (SImode);
+ rtx data = operands[1];
+ rtx address = XEXP (operands[0], 0);
+ rtx seq;
+
+ if ( GET_CODE (data) != REG )
+ data = copy_to_mode_reg (QImode, data);
+
+ if ( GET_CODE (address) != REG )
+ address = copy_to_mode_reg (SImode, address);
+
+ start_sequence ();
+ emit_insn (gen_storeqi (gen_lowpart (SImode, data), address,
+ scratch1, scratch2, scratch3));
+ ms1_set_memflags (operands[0]);
+ seq = get_insns ();
+ end_sequence ();
+ emit_insn (seq);
+ DONE;
+ }
+
+ if ( (! TARGET_BYTE_ACCESS) && GET_CODE (operands[1]) == MEM)
+ {
+ rtx scratch1 = gen_reg_rtx (SImode);
+ rtx data = operands[0];
+ rtx address = XEXP (operands[1], 0);
+ rtx seq;
+
+ if ( GET_CODE (address) != REG )
+ address = copy_to_mode_reg (SImode, address);
+
+ start_sequence ();
+ emit_insn (gen_loadqi (gen_lowpart (SImode, data), address, scratch1));
+ ms1_set_memflags (operands[1]);
+ seq = get_insns ();
+ end_sequence ();
+ emit_insn (seq);
+ DONE;
+ }
+
+ /* If the load is a psuedo register in a stack slot, some simplification
+ can be made because the loads are aligned */
+ if ( (! TARGET_BYTE_ACCESS)
+ && (reload_in_progress && GET_CODE (operands[1]) == SUBREG
+ && GET_CODE (SUBREG_REG (operands[1])) == REG
+ && REGNO (SUBREG_REG (operands[1])) >= FIRST_PSEUDO_REGISTER))
+ {
+ rtx data = operands[0];
+ rtx address = XEXP (operands[1], 0);
+ rtx seq;
+
+ start_sequence ();
+ emit_insn (gen_movsi (gen_lowpart (SImode, data), address));
+ ms1_set_memflags (operands[1]);
+ seq = get_insns ();
+ end_sequence ();
+ emit_insn (seq);
+ DONE;
+ }
+}")
+
+(define_insn "*movqi_internal"
+ [(set (match_operand:QI 0 "nonimmediate_operand" "=r,r,m,r")
+ (match_operand:QI 1 "general_operand" "r,m,r,I"))]
+ "TARGET_BYTE_ACCESS
+ && (!memory_operand (operands[0], QImode)
+ || !memory_operand (operands[1], QImode))"
+ "@
+ or %0, %1, %1
+ ldb %0, %1
+ stb %1, %0
+ addi %0, r0, %1"
+ [(set_attr "length" "4,4,4,4")
+ (set_attr "type" "arith,mem,mem,arith")])
+
+(define_insn "*movqi_internal_nobyte"
+ [(set (match_operand:QI 0 "register_operand" "=r,r")
+ (match_operand:QI 1 "arith_operand" "r,I"))]
+ "!TARGET_BYTE_ACCESS
+ && (!memory_operand (operands[0], QImode)
+ || !memory_operand (operands[1], QImode))"
+ "@
+ or %0, %1, %1
+ addi %0, r0, %1"
+ [(set_attr "length" "4,4")
+ (set_attr "type" "arith,arith")])
+
+
+;; The MorphoRISC does not have 16-bit loads and stores.
+;; These operations must be synthesized. Note that the code
+;; for loadhi and storehi assumes that the least significant bits
+;; is ignored.
+
+;; loadhi
+;; operand 0 location of result
+;; operand 1 memory address
+;; operand 2 temp
+(define_expand "loadhi"
+ [
+ ;; compute shift
+ (set (match_operand:SI 2 "register_operand" "")
+ (and:SI (match_dup 1) (const_int 2)))
+ (set (match_dup 2) (xor:SI (match_dup 2) (const_int 2)))
+ (set (match_dup 2 ) (ashift:SI (match_dup 2) (const_int 3)))
+
+ ;; get word that contains the 16-bits
+ (set (match_operand:SI 0 "register_operand" "")
+ (mem:SI (and:SI (match_operand:SI 1 "register_operand" "")
+ (const_int -3))))
+
+ ;; align 16-bit value
+ (set (match_dup 0) (ashiftrt:SI (match_dup 0) (match_dup 2)))
+ ]
+ ""
+ "")
+
+;; storehi
+;; operand 0 byte value to store
+;; operand 1 address
+;; operand 2 temp, word containing byte
+;; operand 3 temp, shift count
+;; operand 4 temp, mask, aligned and masked byte
+;; operand 5 (unused)
+(define_expand "storehi"
+ [
+ ;; compute shift
+ (set (match_operand:SI 3 "register_operand" "")
+ (and:SI (match_operand:SI 1 "register_operand" "") (const_int 2)))
+ (set (match_dup 3) (xor:SI (match_dup 3) (const_int 2)))
+ (set (match_dup 3) (ashift:SI (match_dup 3) (const_int 3)))
+
+ ;; get word that contains the 16-bits
+ (set (match_operand:SI 2 "register_operand" "")
+ (mem:SI (and:SI (match_dup 1) (const_int -3))))
+
+ ;; generate mask
+ (set (match_operand:SI 4 "register_operand" "") (const_int 65535))
+ (set (match_dup 4) (ashift:SI (match_dup 4) (match_dup 3)))
+ (set (match_dup 4) (not:SI (match_dup 4)))
+
+ ;; clear appropriate bits
+ (set (match_dup 2) (and:SI (match_dup 2) (match_dup 4)))
+
+ ;; align 16-bit value
+ (set (match_dup 4)
+ (and:SI (match_operand:SI 0 "register_operand" "") (const_int 65535)))
+ (set (match_dup 4) (ashift:SI (match_dup 4) (match_dup 3)))
+
+ ;; combine
+ (set (match_dup 2) (ior:SI (match_dup 4) (match_dup 2)))
+ ;; store updated word
+ (set (mem:SI (and:SI (match_dup 1) (const_int -3))) (match_dup 2))
+ ]
+ ""
+ "")
+
+
+(define_expand "movhi"
+ [(set (match_operand:HI 0 "general_operand" "")
+ (match_operand:HI 1 "general_operand" ""))]
+ ""
+ "
+{
+ if (!reload_in_progress
+ && !reload_completed
+ && GET_CODE (operands[0]) == MEM
+ && GET_CODE (operands[1]) == MEM)
+ operands[1] = copy_to_mode_reg (HImode, operands[1]);
+
+ if ( GET_CODE (operands[0]) == MEM)
+ {
+ rtx scratch1 = gen_reg_rtx (SImode);
+ rtx scratch2 = gen_reg_rtx (SImode);
+ rtx scratch3 = gen_reg_rtx (SImode);
+ rtx data = operands[1];
+ rtx address = XEXP (operands[0], 0);
+ rtx seq;
+
+ if (GET_CODE (data) != REG)
+ data = copy_to_mode_reg (HImode, data);
+
+ if (GET_CODE (address) != REG)
+ address = copy_to_mode_reg (SImode, address);
+
+ start_sequence ();
+ emit_insn (gen_storehi (gen_lowpart (SImode, data), address,
+ scratch1, scratch2, scratch3));
+ ms1_set_memflags (operands[0]);
+ seq = get_insns ();
+ end_sequence ();
+ emit_insn (seq);
+ DONE;
+ }
+
+ if ( GET_CODE (operands[1]) == MEM)
+ {
+ rtx scratch1 = gen_reg_rtx (SImode);
+ rtx data = operands[0];
+ rtx address = XEXP (operands[1], 0);
+ rtx seq;
+
+ if (GET_CODE (address) != REG)
+ address = copy_to_mode_reg (SImode, address);
+
+ start_sequence ();
+ emit_insn (gen_loadhi (gen_lowpart (SImode, data), address,
+ scratch1));
+ ms1_set_memflags (operands[1]);
+ seq = get_insns ();
+ end_sequence ();
+ emit_insn (seq);
+ DONE;
+ }
+
+ /* If the load is a psuedo register in a stack slot, some simplification
+ can be made because the loads are aligned */
+ if ( (reload_in_progress && GET_CODE (operands[1]) == SUBREG
+ && GET_CODE (SUBREG_REG (operands[1])) == REG
+ && REGNO (SUBREG_REG (operands[1])) >= FIRST_PSEUDO_REGISTER))
+ {
+ rtx data = operands[0];
+ rtx address = XEXP (operands[1], 0);
+ rtx seq;
+
+ start_sequence ();
+ emit_insn (gen_movsi (gen_lowpart (SImode, data), address));
+ ms1_set_memflags (operands[1]);
+ seq = get_insns ();
+ end_sequence ();
+ emit_insn (seq);
+ DONE;
+ }
+}")
+
+(define_insn "*movhi_internal"
+ [(set (match_operand:HI 0 "register_operand" "=r,r")
+ (match_operand:HI 1 "arith_operand" "r,I"))]
+ "!memory_operand (operands[0], HImode) || !memory_operand (operands[1], HImode)"
+ "@
+ or %0, %1, %1
+ addi %0, r0, %1"
+ [(set_attr "length" "4,4")
+ (set_attr "type" "arith,arith")])
+
+(define_expand "movsi"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "")
+ (match_operand:SI 1 "general_operand" ""))]
+ ""
+ "
+{
+ if (!reload_in_progress && !reload_completed
+ && !register_operand (operands[0], SImode)
+ && !register_operand (operands[1], SImode))
+ operands[1] = copy_to_mode_reg (SImode, operands[1]);
+
+ /* Take care of constants that don't fit in single instruction */
+ if ( (reload_in_progress || reload_completed)
+ && !single_const_operand (operands[1], SImode))
+ {
+ emit_insn (gen_movsi_high (operands[0], operands[1]));
+ emit_insn (gen_movsi_lo_sum (operands[0], operands[0], operands[1]));
+ DONE;
+ }
+
+}")
+
+(define_insn "movsi_high"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (high:SI (match_operand:SI 1 "general_operand" "i")))]
+ ""
+ "*
+{
+ return \"ldui\\t%0, %H1\";
+}"
+ [(set_attr "length" "4")
+ (set_attr "type" "arith")])
+
+
+(define_insn "movsi_lo_sum"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (lo_sum:SI (match_operand:SI 1 "register_operand" "r")
+ (match_operand:SI 2 "general_operand" "i")))]
+ ""
+ "*
+{
+ return \"addui\\t%0, %1, %L2\";
+}"
+ [(set_attr "length" "4")
+ (set_attr "type" "arith")])
+
+/* Take care of constants that don't fit in single instruction */
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (match_operand:SI 1 "general_operand" ""))]
+ "(reload_in_progress || reload_completed)
+ && !single_const_operand (operands[1], SImode)"
+
+ [(set (match_dup 0 )
+ (high:SI (match_dup 1)))
+ (set (match_dup 0 )
+ (lo_sum:SI (match_dup 0)
+ (match_dup 1)))]
+)
+
+
+;; The last pattern in movsi (with two instructions)
+;; is really handled by the emit_insn's in movsi
+;; and the define_split above. This provides additional
+;; instructions to fill delay slots.
+
+;; Note - it is best to only have one movsi pattern and to handle
+;; all the various contingencies by the use of alternatives. This
+;; allows reload the greatest amount of flexability (since reload will
+;; only choose amoungst alternatives for a selected insn, it will not
+;; replace the insn with another one).
+(define_insn "*movsi_internal"
+ [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,m,r,r,r,r,r")
+ (match_operand:SI 1 "general_operand" "r,m,r,I,P,L,N,i"))]
+ "(!memory_operand (operands[0], SImode) || !memory_operand (operands[1], SImode))
+ && !((reload_in_progress || reload_completed)
+ && !single_const_operand (operands[1], SImode))"
+ "@
+ or %0, %1, %1
+ ldw %0, %1
+ stw %1, %0
+ addi %0, r0, %1
+ addui %0, r0, %1
+ ldui %0, %H1
+ nori %0, r0, %N1
+ ldui %0, %H1\;addui %0, %0, %L1"
+ [(set_attr "length" "4,4,4,4,4,4,4,8")
+ (set_attr "type" "arith,mem,mem,arith,arith,arith,arith,complex")]
+)
+
+;; Floating Point Moves
+;;
+;; Note - Patterns for SF mode moves are compulsory, but
+;; patterns for DF are optional, as GCC can synthesize them.
+
+(define_expand "movsf"
+ [(set (match_operand:SF 0 "general_operand" "")
+ (match_operand:SF 1 "general_operand" ""))]
+ ""
+ "
+{
+ if (!reload_in_progress
+ && !reload_completed
+ && GET_CODE (operands[0]) == MEM
+ && (GET_CODE (operands[1]) == MEM
+ || GET_CODE (operands[1]) == CONST_DOUBLE))
+ operands[1] = copy_to_mode_reg (SFmode, operands[1]);
+
+ /* Take care of reg <- SF constant */
+ if ( const_double_operand (operands[1], GET_MODE (operands[1]) ) )
+ {
+ emit_insn (gen_movsf_high (operands[0], operands[1]));
+ emit_insn (gen_movsf_lo_sum (operands[0], operands[0], operands[1]));
+ DONE;
+ }
+}")
+
+(define_insn "movsf_lo_sum"
+ [(set (match_operand:SF 0 "register_operand" "=r")
+ (lo_sum:SF (match_operand:SF 1 "register_operand" "r")
+ (match_operand:SF 2 "const_double_operand" "")))]
+ ""
+ "*
+{
+ REAL_VALUE_TYPE r;
+ long i;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (r, operands[2]);
+ REAL_VALUE_TO_TARGET_SINGLE (r, i);
+ operands[2] = GEN_INT (i);
+ return \"addui\\t%0, %1, %L2\";
+}"
+ [(set_attr "length" "4")
+ (set_attr "type" "arith")])
+
+(define_insn "movsf_high"
+ [(set (match_operand:SF 0 "register_operand" "=r")
+ (high:SF (match_operand:SF 1 "const_double_operand" "")))]
+ ""
+ "*
+{
+ REAL_VALUE_TYPE r;
+ long i;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]);
+ REAL_VALUE_TO_TARGET_SINGLE (r, i);
+ operands[1] = GEN_INT (i);
+ return \"ldui\\t%0, %H1\";
+}"
+ [(set_attr "length" "4")
+ (set_attr "type" "arith")])
+
+
+(define_insn "*movsf_internal"
+ [(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,m")
+ (match_operand:SF 1 "nonimmediate_operand" "r,m,r"))]
+ "!memory_operand (operands[0], SFmode) || !memory_operand (operands[1], SFmode)"
+ "@
+ or %0, %1, %1
+ ldw %0, %1
+ stw %1, %0"
+ [(set_attr "length" "4,4,4")
+ (set_attr "type" "arith,mem,mem")]
+)
+
+(define_expand "movdf"
+ [(set (match_operand:DF 0 "general_operand" "")
+ (match_operand:DF 1 "general_operand" ""))]
+ ""
+ "
+{
+ /* One of the ops has to be in a register or 0 */
+ if (!register_operand (operand0, DFmode)
+ && !reg_or_0_operand (operand1, DFmode))
+ operands[1] = copy_to_mode_reg (DFmode, operand1);
+}")
+
+(define_insn_and_split "*movdf_internal"
+ [(set (match_operand:DF 0 "nonimmediate_operand" "=r,o")
+ (match_operand:DF 1 "general_operand" "rim,r"))]
+ "! (memory_operand (operands[0], DFmode)
+ && memory_operand (operands[1], DFmode))"
+ "#"
+
+ "(reload_completed || reload_in_progress)"
+
+ [(set (match_dup 2) (match_dup 3))
+ (set (match_dup 4) (match_dup 5))
+ ]
+
+ "{
+ /* figure out what precisely to put into operands 2, 3, 4, and 5 */
+ ms1_split_words (SImode, DFmode, operands);
+ }"
+)
+
+\f
+;; Reloads
+
+;; Like `movM', but used when a scratch register is required to move between
+;; operand 0 and operand 1. Operand 2 describes the scratch register. See the
+;; discussion of the `SECONDARY_RELOAD_CLASS' macro.
+
+(define_expand "reload_inqi"
+ [(set (match_operand:QI 0 "register_operand" "=r")
+ (match_operand:QI 1 "memory_operand" "m"))
+ (clobber (match_operand:DI 2 "register_operand" "=&r"))]
+ "! TARGET_BYTE_ACCESS"
+ "
+{
+ rtx scratch1 = gen_rtx_REG (SImode, REGNO (operands[2]));
+ rtx scratch2 = gen_rtx_REG (SImode, REGNO (operands[2])+1);
+ rtx data = operands[0];
+ rtx address = XEXP (operands[1], 0);
+ rtx swap, seq;
+
+ /* It is possible that the registers we got for scratch1
+ might coincide with that of operands[0]. gen_loadqi
+ requires operand0 and operand2 to be different registers.
+ The following statement ensure that is always the case. */
+ if (REGNO(operands[0]) == REGNO(scratch1))
+ {
+ swap = scratch1;
+ scratch1 = scratch2;
+ scratch2 = swap;
+ }
+
+ /* need to make sure address is already in register */
+ if ( GET_CODE (address) != REG )
+ address = force_operand (address, scratch2);
+
+ start_sequence ();
+ emit_insn (gen_loadqi (gen_lowpart (SImode, data), address, scratch1));
+ ms1_set_memflags (operands[1]);
+ seq = get_insns ();
+ end_sequence ();
+ emit_insn (seq);
+ DONE;
+}")
+
+(define_expand "reload_outqi"
+ [(set (match_operand:QI 0 "memory_operand" "=m")
+ (match_operand:QI 1 "register_operand" "r"))
+ (clobber (match_operand:TI 2 "register_operand" "=&r"))]
+ "! TARGET_BYTE_ACCESS"
+ "
+{
+ rtx scratch1 = gen_rtx_REG (SImode, REGNO (operands[2]));
+ rtx scratch2 = gen_rtx_REG (SImode, REGNO (operands[2])+1);
+ rtx scratch3 = gen_rtx_REG (SImode, REGNO (operands[2])+2);
+ rtx scratch4 = gen_rtx_REG (SImode, REGNO (operands[2])+3);
+ rtx data = operands[1];
+ rtx address = XEXP (operands[0], 0);
+ rtx seq;
+
+ /* need to make sure address is already in register */
+ if ( GET_CODE (address) != REG )
+ address = force_operand (address, scratch4);
+
+ start_sequence ();
+ emit_insn (gen_storeqi (gen_lowpart (SImode, data), address,
+ scratch1, scratch2, scratch3));
+ ms1_set_memflags (operands[0]);
+ seq = get_insns ();
+ end_sequence ();
+ emit_insn (seq);
+ DONE;
+}")
+
+(define_expand "reload_inhi"
+ [(set (match_operand:HI 0 "register_operand" "=r")
+ (match_operand:HI 1 "memory_operand" "m"))
+ (clobber (match_operand:DI 2 "register_operand" "=&r"))]
+ ""
+ "
+{
+ rtx scratch1 = gen_rtx_REG (SImode, REGNO (operands[2]));
+ rtx scratch2 = gen_rtx_REG (SImode, REGNO (operands[2])+1);
+ rtx data = operands[0];
+ rtx address = XEXP (operands[1], 0);
+ rtx swap, seq;
+
+ /* It is possible that the registers we got for scratch1
+ might coincide with that of operands[0]. gen_loadqi
+ requires operand0 and operand2 to be different registers.
+ The following statement ensure that is always the case. */
+ if (REGNO(operands[0]) == REGNO(scratch1))
+ {
+ swap = scratch1;
+ scratch1 = scratch2;
+ scratch2 = swap;
+ }
+
+ /* need to make sure address is already in register */
+ if ( GET_CODE (address) != REG )
+ address = force_operand (address, scratch2);
+
+ start_sequence ();
+ emit_insn (gen_loadhi (gen_lowpart (SImode, data), address,
+ scratch1));
+ ms1_set_memflags (operands[1]);
+ seq = get_insns ();
+ end_sequence ();
+ emit_insn (seq);
+ DONE;
+}")
+
+(define_expand "reload_outhi"
+ [(set (match_operand:HI 0 "memory_operand" "=m")
+ (match_operand:HI 1 "register_operand" "r"))
+ (clobber (match_operand:TI 2 "register_operand" "=&r"))]
+ ""
+ "
+{
+ rtx scratch1 = gen_rtx_REG (SImode, REGNO (operands[2]));
+ rtx scratch2 = gen_rtx_REG (SImode, REGNO (operands[2])+1);
+ rtx scratch3 = gen_rtx_REG (SImode, REGNO (operands[2])+2);
+ rtx scratch4 = gen_rtx_REG (SImode, REGNO (operands[2])+3);
+ rtx data = operands[1];
+ rtx address = XEXP (operands[0], 0);
+ rtx seq;
+
+ /* need to make sure address is already in register */
+ if ( GET_CODE (address) != REG )
+ address = force_operand (address, scratch4);
+
+ start_sequence ();
+ emit_insn (gen_storehi (gen_lowpart (SImode, data), address,
+ scratch1, scratch2, scratch3));
+ ms1_set_memflags (operands[0]);
+ seq = get_insns ();
+ end_sequence ();
+ emit_insn (seq);
+ DONE;
+}")
+
+\f
+;; 32 bit Integer arithmetic
+
+;; Addition
+(define_insn "addsi3"
+ [(set (match_operand:SI 0 "register_operand" "=r,r")
+ (plus:SI (match_operand:SI 1 "register_operand" "%r,r")
+ (match_operand:SI 2 "arith_operand" "r,I")))]
+ ""
+ "@
+ add %0, %1, %2
+ addi %0, %1, %2"
+ [(set_attr "length" "4,4")
+ (set_attr "type" "arith,arith")])
+
+;; Subtraction
+(define_insn "subsi3"
+ [(set (match_operand:SI 0 "register_operand" "=r,r")
+ (minus:SI (match_operand:SI 1 "reg_or_0_operand" "rJ,rJ")
+ (match_operand:SI 2 "arith_operand" "rJ,I")))]
+ ""
+ "@
+ sub %0, %z1, %z2
+ subi %0, %z1, %2"
+ [(set_attr "length" "4,4")
+ (set_attr "type" "arith,arith")])
+
+;; Negation
+(define_insn "negsi2"
+ [(set (match_operand:SI 0 "register_operand" "=r,r")
+ (neg:SI (match_operand:SI 1 "arith_operand" "r,I")))]
+ ""
+ "@
+ sub %0, r0, %1
+ subi %0, r0, %1"
+ [(set_attr "length" "4,4")
+ (set_attr "type" "arith,arith")])
+
+\f
+;; 32 bit Integer Shifts and Rotates
+
+;; Arithmetic Shift Left
+(define_insn "ashlsi3"
+ [(set (match_operand:SI 0 "register_operand" "=r,r")
+ (ashift:SI (match_operand:SI 1 "register_operand" "r,r")
+ (match_operand:SI 2 "arith_operand" "r,K")))]
+ ""
+ "@
+ lsl %0, %1, %2
+ lsli %0, %1, %2"
+ [(set_attr "length" "4,4")
+ (set_attr "type" "arith,arith")])
+
+;; Arithmetic Shift Right
+(define_insn "ashrsi3"
+ [(set (match_operand:SI 0 "register_operand" "=r,r")
+ (ashiftrt:SI (match_operand:SI 1 "register_operand" "r,r")
+ (match_operand:SI 2 "uns_arith_operand" "r,K")))]
+ ""
+ "@
+ asr %0, %1, %2
+ asri %0, %1, %2"
+ [(set_attr "length" "4,4")
+ (set_attr "type" "arith,arith")])
+
+;; Logical Shift Right
+(define_insn "lshrsi3"
+ [(set (match_operand:SI 0 "register_operand" "=r,r")
+ (lshiftrt:SI (match_operand:SI 1 "register_operand" "r,r")
+ (match_operand:SI 2 "uns_arith_operand" "r,K")))]
+ ""
+ "@
+ lsr %0, %1, %2
+ lsri %0, %1, %2"
+ [(set_attr "length" "4,4")
+ (set_attr "type" "arith,arith")])
+
+\f
+;; 32 Bit Integer Logical operations
+
+;; Logical AND, 32 bit integers
+(define_insn "andsi3"
+ [(set (match_operand:SI 0 "register_operand" "=r,r")
+ (and:SI (match_operand:SI 1 "register_operand" "%r,r")
+ (match_operand:SI 2 "uns_arith_operand" "r,K")))]
+ ""
+ "@
+ and %0, %1, %2
+ andi %0, %1, %2"
+ [(set_attr "length" "4,4")
+ (set_attr "type" "arith,arith")])
+
+;; Inclusive OR, 32 bit integers
+(define_insn "iorsi3"
+ [(set (match_operand:SI 0 "register_operand" "=r,r")
+ (ior:SI (match_operand:SI 1 "register_operand" "%r,r")
+ (match_operand:SI 2 "uns_arith_operand" "r,K")))]
+ ""
+ "@
+ or %0, %1, %2
+ ori %0, %1, %2"
+ [(set_attr "length" "4,4")
+ (set_attr "type" "arith,arith")])
+
+;; Exclusive OR, 32 bit integers
+(define_insn "xorsi3"
+ [(set (match_operand:SI 0 "register_operand" "=r,r")
+ (xor:SI (match_operand:SI 1 "register_operand" "%r,r")
+ (match_operand:SI 2 "uns_arith_operand" "r,K")))]
+ ""
+ "@
+ xor %0, %1, %2
+ xori %0, %1, %2"
+ [(set_attr "length" "4,4")
+ (set_attr "type" "arith,arith")])
+
+
+;; One's complement, 32 bit integers
+(define_insn "one_cmplsi2"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (not:SI (match_operand:SI 1 "register_operand" "r")))]
+ ""
+ "nor %0, %1, %1"
+ [(set_attr "length" "4")
+ (set_attr "type" "arith")])
+
+\f
+;; Multiply
+
+(define_insn "mulhisi3"
+ [(set (match_operand:SI 0 "register_operand" "=r,r")
+ (mult:SI (sign_extend:SI (match_operand:HI 1 "register_operand" "%r,r"))
+ (sign_extend:SI (match_operand:HI 2 "arith_operand" "r,I"))))]
+ "TARGET_MUL"
+ "@
+ mul %0, %1, %2
+ muli %0, %1, %2"
+ [(set_attr "length" "4,4")
+ (set_attr "type" "arith,arith")])
+
+\f
+;; Comparisons
+
+;; Note, we store the operands in the comparison insns, and use them later
+;; when generating the branch or scc operation.
+
+;; First the routines called by the machine independent part of the compiler
+(define_expand "cmpsi"
+ [(set (cc0)
+ (compare (match_operand:SI 0 "register_operand" "")
+ (match_operand:SI 1 "arith_operand" "")))]
+ ""
+ "
+{
+ ms1_compare_op0 = operands[0];
+ ms1_compare_op1 = operands[1];
+ DONE;
+}")
+
+\f
+;; Branches
+
+(define_expand "beq"
+ [(use (match_operand 0 "" ""))]
+ ""
+ "
+{ ms1_emit_cbranch (EQ, operands[0],
+ ms1_compare_op0, ms1_compare_op1);
+ DONE;
+}")
+
+(define_expand "bne"
+ [(use (match_operand 0 "" ""))]
+ ""
+ "
+{ ms1_emit_cbranch (NE, operands[0],
+ ms1_compare_op0, ms1_compare_op1);
+ DONE;
+}")
+
+(define_expand "bge"
+ [(use (match_operand 0 "" ""))]
+ ""
+ "
+{ ms1_emit_cbranch (GE, operands[0],
+ ms1_compare_op0, ms1_compare_op1);
+ DONE;
+}")
+
+(define_expand "bgt"
+ [(use (match_operand 0 "" ""))]
+ ""
+ "
+{ ms1_emit_cbranch (GT, operands[0],
+ ms1_compare_op0, ms1_compare_op1);
+ DONE;
+}")
+
+(define_expand "ble"
+ [(use (match_operand 0 "" ""))]
+ ""
+ "
+{ ms1_emit_cbranch (LE, operands[0],
+ ms1_compare_op0, ms1_compare_op1);
+ DONE;
+}")
+
+(define_expand "blt"
+ [(use (match_operand 0 "" ""))]
+ ""
+ "
+{ ms1_emit_cbranch (LT, operands[0],
+ ms1_compare_op0, ms1_compare_op1);
+ DONE;
+}")
+
+(define_expand "bgeu"
+ [(use (match_operand 0 "" ""))]
+ ""
+ "
+{ ms1_emit_cbranch (GEU, operands[0],
+ ms1_compare_op0, ms1_compare_op1);
+ DONE;
+}")
+
+(define_expand "bgtu"
+ [(use (match_operand 0 "" ""))]
+ ""
+ "
+{ ms1_emit_cbranch (GTU, operands[0],
+ ms1_compare_op0, ms1_compare_op1);
+ DONE;
+}")
+
+(define_expand "bleu"
+ [(use (match_operand 0 "" ""))]
+ ""
+ "
+{ ms1_emit_cbranch (LEU, operands[0],
+ ms1_compare_op0, ms1_compare_op1);
+ DONE;
+}")
+
+(define_expand "bltu"
+ [(use (match_operand 0 "" ""))]
+ ""
+ "
+{ ms1_emit_cbranch (LTU, operands[0],
+ ms1_compare_op0, ms1_compare_op1);
+ DONE;
+}")
+
+(define_expand "bunge"
+ [(use (match_operand 0 "" ""))]
+ ""
+ "
+{ ms1_emit_cbranch (GEU, operands[0],
+ ms1_compare_op0, ms1_compare_op1);
+ DONE;
+}")
+
+(define_expand "bungt"
+ [(use (match_operand 0 "" ""))]
+ ""
+ "
+{
+ ms1_emit_cbranch (GTU, operands[0],
+ ms1_compare_op0, ms1_compare_op1);
+ DONE;
+}")
+
+(define_expand "bunle"
+ [(use (match_operand 0 "" ""))]
+ ""
+ "
+{
+ ms1_emit_cbranch (LEU, operands[0],
+ ms1_compare_op0, ms1_compare_op1);
+ DONE;
+}")
+
+(define_expand "bunlt"
+ [(use (match_operand 0 "" ""))]
+ ""
+ "
+{
+ ms1_emit_cbranch (LTU, operands[0],
+ ms1_compare_op0, ms1_compare_op1);
+ DONE;
+}")
+
+(define_insn "*beq_true"
+ [(set (pc)
+ (if_then_else (eq (match_operand:SI 0 "reg_or_0_operand" "rJ")
+ (match_operand:SI 1 "reg_or_0_operand" "rJ"))
+ (label_ref (match_operand 2 "" ""))
+ (pc)))]
+ ""
+ "breq %z0, %z1, %l2%#"
+ [(set_attr "length" "4")
+ (set_attr "type" "branch")])
+
+(define_insn "*beq_false"
+ [(set (pc)
+ (if_then_else (eq (match_operand:SI 0 "reg_or_0_operand" "rJ")
+ (match_operand:SI 1 "reg_or_0_operand" "rJ"))
+ (pc)
+ (label_ref (match_operand 2 "" ""))))]
+ ""
+ "brne %z0, %z1, %l2%#"
+ [(set_attr "length" "4")
+ (set_attr "type" "branch")])
+
+
+(define_insn "*bne_true"
+ [(set (pc)
+ (if_then_else (ne (match_operand:SI 0 "reg_or_0_operand" "rJ")
+ (match_operand:SI 1 "reg_or_0_operand" "rJ"))
+ (label_ref (match_operand 2 "" ""))
+ (pc)))]
+ ""
+ "brne %z0, %z1, %l2%#"
+ [(set_attr "length" "4")
+ (set_attr "type" "branch")])
+
+(define_insn "*bne_false"
+ [(set (pc)
+ (if_then_else (ne (match_operand:SI 0 "reg_or_0_operand" "rJ")
+ (match_operand:SI 1 "reg_or_0_operand" "rJ"))
+ (pc)
+ (label_ref (match_operand 2 "" ""))))]
+ ""
+ "breq %z0, %z1, %l2%#"
+ [(set_attr "length" "4")
+ (set_attr "type" "branch")])
+
+(define_insn "*blt_true"
+ [(set (pc)
+ (if_then_else (lt (match_operand:SI 0 "reg_or_0_operand" "rJ")
+ (match_operand:SI 1 "reg_or_0_operand" "rJ"))
+ (label_ref (match_operand 2 "" ""))
+ (pc)))]
+ ""
+ "brlt %z0, %z1, %l2%#"
+ [(set_attr "length" "4")
+ (set_attr "type" "branch")])
+
+(define_insn "*blt_false"
+ [(set (pc)
+ (if_then_else (lt (match_operand:SI 0 "reg_or_0_operand" "rJ")
+ (match_operand:SI 1 "reg_or_0_operand" "rJ"))
+ (pc)
+ (label_ref (match_operand 2 "" ""))))]
+ ""
+ "brle %z1, %z0,%l2%#"
+ [(set_attr "length" "4")
+ (set_attr "type" "branch")])
+
+(define_insn "*ble_true"
+ [(set (pc)
+ (if_then_else (le (match_operand:SI 0 "reg_or_0_operand" "rJ")
+ (match_operand:SI 1 "reg_or_0_operand" "rJ"))
+ (label_ref (match_operand 2 "" ""))
+ (pc)))]
+ ""
+ "brle %z0, %z1, %l2%#"
+ [(set_attr "length" "4")
+ (set_attr "type" "branch")])
+
+(define_insn "*ble_false"
+ [(set (pc)
+ (if_then_else (le (match_operand:SI 0 "reg_or_0_operand" "rJ")
+ (match_operand:SI 1 "reg_or_0_operand" "rJ"))
+ (pc)
+ (label_ref (match_operand 2 "" ""))))]
+ ""
+ "brlt %z1, %z0,%l2%#"
+ [(set_attr "length" "4")
+ (set_attr "type" "branch")])
+
+(define_insn "*bgt_true"
+ [(set (pc)
+ (if_then_else (gt (match_operand:SI 0 "reg_or_0_operand" "rJ")
+ (match_operand:SI 1 "reg_or_0_operand" "rJ"))
+ (label_ref (match_operand 2 "" ""))
+ (pc)))]
+ ""
+ "brlt %z1, %z0, %l2%#"
+ [(set_attr "length" "4")
+ (set_attr "type" "branch")])
+
+(define_insn "*bgt_false"
+ [(set (pc)
+ (if_then_else (gt (match_operand:SI 0 "reg_or_0_operand" "rJ")
+ (match_operand:SI 1 "reg_or_0_operand" "rJ"))
+ (pc)
+ (label_ref (match_operand 2 "" ""))))]
+ ""
+ "brle %z0, %z1, %l2%#"
+ [(set_attr "length" "4")
+ (set_attr "type" "branch")])
+
+(define_insn "*bge_true"
+ [(set (pc)
+ (if_then_else (ge (match_operand:SI 0 "reg_or_0_operand" "rJ")
+ (match_operand:SI 1 "reg_or_0_operand" "rJ"))
+ (label_ref (match_operand 2 "" ""))
+ (pc)))]
+ ""
+ "brle %z1, %z0,%l2%#"
+ [(set_attr "length" "4")
+ (set_attr "type" "branch")])
+
+(define_insn "*bge_false"
+ [(set (pc)
+ (if_then_else (ge (match_operand:SI 0 "reg_or_0_operand" "rJ")
+ (match_operand:SI 1 "reg_or_0_operand" "rJ"))
+ (pc)
+ (label_ref (match_operand 2 "" ""))))]
+ ""
+ "brlt %z0, %z1, %l2%#"
+ [(set_attr "length" "4")
+ (set_attr "type" "branch")])
+
+;; No unsigned operators on Morpho ms1. All the unsigned operations are
+;; converted to the signed operations above.
+
+\f
+;; Set flag operations
+
+;; "seq", "sne", "slt", "sle", "sgt", "sge", "sltu", "sleu",
+;; "sgtu", and "sgeu" don't exist as regular instruction on the
+;; ms1, so these are not defined
+
+;; Call and branch instructions
+
+(define_expand "call"
+ [(parallel [(call (mem:SI (match_operand:SI 0 "register_operand" ""))
+ (match_operand 1 "" ""))
+ (clobber (reg:SI 14))])]
+ ""
+ "
+{
+ operands[0] = force_reg (SImode, XEXP (operands[0], 0));
+}")
+
+(define_insn "call_internal"
+ [(call (mem:SI (match_operand 0 "register_operand" "r"))
+ (match_operand 1 "" ""))
+ ;; possibly add a clobber of the reg that gets the return address
+ (clobber (reg:SI 14))]
+ ""
+ "jal r14, %0%#"
+ [(set_attr "length" "4")
+ (set_attr "type" "branch")])
+
+(define_expand "call_value"
+ [(parallel [(set (match_operand 0 "register_operand" "")
+ (call (mem:SI (match_operand:SI 1 "register_operand" ""))
+ (match_operand 2 "general_operand" "")))
+ (clobber (reg:SI 14))])]
+ ""
+ "
+{
+ operands[1] = force_reg (SImode, XEXP (operands[1], 0));
+}")
+
+
+(define_insn "call_value_internal"
+ [(set (match_operand 0 "register_operand" "=r")
+ (call (mem:SI (match_operand 1 "register_operand" "r"))
+ (match_operand 2 "" "")))
+ ;; possibly add a clobber of the reg that gets the return address
+ (clobber (reg:SI 14))]
+ ""
+ "jal r14, %1%#"
+ [(set_attr "length" "4")
+ (set_attr "type" "branch")])
+
+;; Subroutine return
+(define_insn "return_internal"
+ [(const_int 2)
+ (return)
+ (use (reg:SI 14))]
+ ""
+ "jal r0, r14%#"
+ [(set_attr "length" "4")
+ (set_attr "type" "branch")])
+
+;; Interrupt return
+(define_insn "return_interrupt_internal"
+ [(const_int 3)
+ (return)
+ (use (reg:SI 15))]
+ ""
+ "reti r15%#"
+ [(set_attr "length" "4")
+ (set_attr "type" "branch")])
+
+;; Subroutine return
+(define_insn "eh_return_internal"
+ [(return)
+ (use (reg:SI 7))
+ (use (reg:SI 8))
+ (use (reg:SI 11))
+ (use (reg:SI 10))]
+ ""
+ "jal r0, r11%#"
+ [(set_attr "length" "4")
+ (set_attr "type" "branch")])
+
+
+;; Normal unconditional jump
+(define_insn "jump"
+ [(set (pc) (label_ref (match_operand 0 "" "")))]
+ ""
+ "jmp %l0%#"
+ [(set_attr "length" "4")
+ (set_attr "type" "branch")])
+
+;; Indirect jump through a register
+(define_insn "indirect_jump"
+ [(set (pc) (match_operand 0 "register_operand" "r"))]
+ ""
+ "jal r0,%0%#"
+ [(set_attr "length" "4")
+ (set_attr "type" "branch")])
+
+(define_insn "tablejump"
+ [(set (pc) (match_operand:SI 0 "register_operand" "r"))
+ (use (label_ref (match_operand 1 "" "")))]
+ ""
+ "jal r0, %0%#"
+ [(set_attr "length" "4")
+ (set_attr "type" "branch")])
+
+\f
+(define_expand "prologue"
+ [(const_int 1)]
+ ""
+ "
+{
+ ms1_expand_prologue ();
+ DONE;
+}")
+
+(define_expand "epilogue"
+ [(const_int 2)]
+ ""
+ "
+{
+ ms1_expand_epilogue (NORMAL_EPILOGUE);
+ DONE;
+}")
+
+
+(define_expand "eh_return"
+ [(use (match_operand:SI 0 "register_operand" "r"))]
+ ""
+ "
+{
+ ms1_expand_eh_return (operands);
+ DONE;
+}")
+
+
+(define_insn_and_split "eh_epilogue"
+ [(unspec [(match_operand 0 "register_operand" "r")] 6)]
+ ""
+ "#"
+ "reload_completed"
+ [(const_int 1)]
+ "ms1_emit_eh_epilogue (operands); DONE;"
+)
+\f
+;; No operation, needed in case the user uses -g but not -O.
+(define_insn "nop"
+ [(const_int 0)]
+ ""
+ "or r0,r0,r0"
+ [(set_attr "length" "4")
+ (set_attr "type" "arith")])
+
+;; ::::::::::::::::::::
+;; ::
+;; :: UNSPEC_VOLATILE usage
+;; ::
+;; ::::::::::::::::::::
+;;
+;; 0 blockage
+;; 1 Enable interrupts
+;; 2 Disable interrupts
+;;
+
+;; Pseudo instruction that prevents the scheduler from moving code above this
+;; point.
+(define_insn "blockage"
+ [(unspec_volatile [(const_int 0)] 0)]
+ ""
+ ""
+ [(set_attr "length" "0")])
+
+;; Trap instruction to allow usage of the __builtin_trap function
+(define_insn "trap"
+ [(trap_if (const_int 1) (const_int 0))
+ (clobber (reg:SI 14))]
+ ""
+ "si r14%#"
+ [(set_attr "length" "4")
+ (set_attr "type" "branch")])
+
+(define_expand "conditional_trap"
+ [(trap_if (match_operator 0 "comparison_operator"
+ [(match_dup 2)
+ (match_dup 3)])
+ (match_operand 1 "const_int_operand" ""))]
+ ""
+ "
+{
+ operands[2] = ms1_compare_op0;
+ operands[3] = ms1_compare_op1;
+}")
+
+;; Templates to control handling of interrupts
+
+;; Enable interrupts template
+(define_insn "ei"
+ [(unspec_volatile [(const_int 0)] 1)]
+ ""
+ "ei"
+ [(set_attr "length" "4")])
+
+;; Enable interrupts template
+(define_insn "di"
+ [(unspec_volatile [(const_int 0)] 2)]
+ ""
+ "di"
+ [(set_attr "length" "4")])
--- /dev/null
+; Options for the ms1 port of the compiler
+;
+; Copyright (C) 2005 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.
+;
+; 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, 51 Franklin Street, Fifth Floor, Boston, MA
+; 02110-1301, USA.
+
+mmul
+Target Report Mask(MUL)
+Generate multiply instructions
+
+mbacc
+Target Report Mask(BYTE_ACCESS)
+Use byte loads and stores when generating code.
+
+msim
+Target RejectNegative
+Use simulator runtime
+
+mno-crt0
+Target RejectNegative
+Do not include crt0.o in the startup files
+
+mdebug-arg
+Target RejectNegative Mask(DEBUG_ARG)
+Internal debug switch
+
+mdebug-addr
+Target RejectNegative Mask(DEBUG_ADDR)
+Internal debug switch
+
+mdebug-stack
+Target RejectNegative Mask(DEBUG_STACK)
+Internal debug switch
+
+mdebug-loc
+Target RejectNegative Mask(DEBUG_LOC)
+Internal debug switch
+
+mdebug
+Target RejectNegative Mask(DEBUG)
+Internal debug switch
+
+march=
+Target RejectNegative Joined Var(ms1_cpu_string)
+Specify CPU for code generation purposes
--- /dev/null
+# Copyright (C) 2005 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.
+#
+# 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, 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+# Name of assembly file containing libgcc1 functions.
+# This entry must be present, but it can be empty if the target does
+# not need any assembler functions to support its code generation.
+CROSS_LIBGCC1 =
+#
+# Alternatively if assembler functions *are* needed then define the
+# entries below:
+# CROSS_LIBGCC1 = libgcc1-asm.a
+# LIB1ASMSRC = ms1/lib1funcs.asm
+# LIB1ASMFUNCS = _udivsi3 etc...
+
+LIB2FUNCS_EXTRA = $(srcdir)/config/ms1/lib2extra-funcs.c
+
+# If any special flags are necessary when building libgcc2 put them here.
+#
+# TARGET_LIBGCC2_CFLAGS =
+
+EXTRA_PARTS = crtbegin.o crtend.o crti.o crtn.o
+
+# We want fine grained libraries, so use the new code to build the
+# floating point emulation libraries.
+FPBIT = fp-bit.c
+DPBIT = dp-bit.c
+
+fp-bit.c: $(srcdir)/config/fp-bit.c
+ echo '#define FLOAT' > fp-bit.c
+ cat $(srcdir)/config/fp-bit.c >> fp-bit.c
+
+dp-bit.c: $(srcdir)/config/fp-bit.c
+ cat $(srcdir)/config/fp-bit.c > dp-bit.c
+
+# Assemble startup files.
+crti.o: $(srcdir)/config/ms1/crti.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) -c -o crti.o -x assembler $(srcdir)/config/ms1/crti.asm
+
+crtn.o: $(srcdir)/config/ms1/crtn.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) -c -o crtn.o -x assembler $(srcdir)/config/ms1/crtn.asm
+
+# Enable the following if multilibs are needed.
+# See gcc/genmultilib, gcc/gcc.texi and gcc/tm.texi for a
+# description of the options and their values.
+#
+# MULTILIB_OPTIONS =
+# MULTILIB_DIRNAMES =
+# MULTILIB_MATCHES =
+# MULTILIB_EXCEPTIONS =
+# MULTILIB_EXTRA_OPTS =
+#
+# LIBGCC = stmp-multilib
+# INSTALL_LIBGCC = install-multilib
+
@item interrupt
@cindex interrupt handler functions
-Use this attribute on the ARM, AVR, C4x, CRX, M32C, M32R/D and Xstormy16
+Use this attribute on the ARM, AVR, C4x, CRX, M32C, M32R/D, MS1, and Xstormy16
ports to indicate that the specified function is an interrupt handler.
The compiler will generate function entry and exit sequences suitable
for use in an interrupt handler when this attribute is present.
-mam33-2 -mno-am33-2 @gol
-mno-crt0 -mrelax}
+@emph{MS1 Options}
+@gccoptlist{-mno-crt0 -mmul -mbacc -msim @gol
+-march=@var{cpu-type} }
+
@emph{PDP-11 Options}
@gccoptlist{-mfpu -msoft-float -mac0 -mno-ac0 -m40 -m45 -m10 @gol
-mbcopy -mbcopy-builtin -mint32 -mno-int16 @gol
* MIPS Options::
* MMIX Options::
* MN10300 Options::
+* MS1 Options::
* PDP-11 Options::
* PowerPC Options::
* RS/6000 and PowerPC Options::
This option makes symbolic debugging impossible.
@end table
+@node MS1 Options
+@subsection MS1 Options
+@cindex MS1 options
+
+These @option{-m} options are defined for Morpho MS1 architectures:
+
+@table @gcctabopt
+
+@item -march=@var{cpu-type}
+@opindex march
+Generate code that will run on @var{cpu-type}, which is the name of a system
+representing a certain processor type. Possible values for
+@var{cpu-type} are @samp{MS1-64-001}, @samp{MS1-16-002}, and
+@samp{MS1-16-003}.
+
+When this option is not used, the default is @option{-march=MS1-16-003}.
+
+@item -mmul
+@opindex mmul
+Generate multiply instructions.
+
+@item -mno-mul
+@opindex mno-mul
+Do not generate multiply instructions.
+
+@item -mbacc
+@opindex mbacc
+Use byte loads and stores when generating code.
+
+@item -mno-bacc
+@opindex mno-bacc
+Do not use byte loads and stores when generating code.
+
+@item -msim
+@opindex msim
+Use simulator runtime
+
+@item -mno-crt0
+@opindex mno-crt0
+Do not link in the C run-time initialization object file
+@file{crti.o}. Other run-time initialisation and termination files
+such as @file{startup.o} and @file{exit.o} are still included on the
+linker command line.
+
+@end table
+
@node PDP-11 Options
@subsection PDP-11 Options
@cindex PDP-11 Options
System V Release 4 small data area reference
@end table
+@item MorphoRISC family---@file{ms1.h}
+@table @code
+@item I
+Constant for an arithmetic insn (16-bit signed integer).
+
+@item J
+The constant 0.
+
+@item K
+Constant for a logical insn (16-bit zero-extended integer).
+
+@item L
+A constant that can be loaded with @code{lui} (i.e.@: the bottom 16
+bits are zero).
+
+@item M
+A constant that takes two words to load (i.e.@: not matched by
+@code{I}, @code{K}, or @code{L}).
+
+@item N
+Negative 16-bit constants other than -65536.
+
+@item O
+A 15-bit signed integer constant.
+
+@item P
+A positive 16-bit constant.
+@end table
+
@item Intel 386---@file{i386.h}
@table @code
@item q