--- a/configure +++ b/configure @@ -3602,6 +3602,9 @@ case "${target}" in ip2k-*-*) noconfigdirs="$noconfigdirs target-libstdc++-v3 ${libgcj}" ;; + ubicom32-*-*) + noconfigdirs="$noconfigdirs target-libffi" + ;; *-*-linux* | *-*-gnu* | *-*-k*bsd*-gnu | *-*-kopensolaris*-gnu) noconfigdirs="$noconfigdirs target-newlib target-libgloss" ;; --- /dev/null +++ b/gcc/config/ubicom32/constraints.md @@ -0,0 +1,149 @@ +; Constraint definitions for Ubicom32 + +; Copyright (C) 2009 Free Software Foundation, Inc. +; Contributed by Ubicom, 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 3, 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 COPYING3. If not see +; . + +(define_register_constraint "a" "ALL_ADDRESS_REGS" + "An An register.") + +(define_register_constraint "d" "DATA_REGS" + "A Dn register.") + +(define_register_constraint "h" "ACC_REGS" + "An accumulator register.") + +(define_register_constraint "l" "ACC_LO_REGS" + "An accn_lo register.") + +(define_register_constraint "Z" "FDPIC_REG" + "The FD-PIC GOT pointer: A0.") + +(define_constraint "I" + "An 8-bit signed constant value." + (and (match_code "const_int") + (match_test "(ival >= -128) && (ival <= 127)"))) + +(define_constraint "Q" + "An 8-bit signed constant value represented as unsigned." + (and (match_code "const_int") + (match_test "(ival >= 0x00) && (ival <= 0xff)"))) + +(define_constraint "R" + "An 8-bit signed constant value represented as unsigned." + (and (match_code "const_int") + (match_test "((ival >= 0x0000) && (ival <= 0x007f)) || ((ival >= 0xff80) && (ival <= 0xffff))"))) + +(define_constraint "J" + "A 7-bit unsigned constant value." + (and (match_code "const_int") + (match_test "(ival >= 0) && (ival <= 127)"))) + +(define_constraint "K" + "A 7-bit unsigned constant value shifted << 1." + (and (match_code "const_int") + (match_test "(ival >= 0) && (ival <= 254) && ((ival & 1) == 0)"))) + +(define_constraint "L" + "A 7-bit unsigned constant value shifted << 2." + (and (match_code "const_int") + (match_test "(ival >= 0) && (ival <= 508) && ((ival & 3) == 0)"))) + +(define_constraint "M" + "A 5-bit unsigned constant value." + (and (match_code "const_int") + (match_test "(ival >= 0) && (ival <= 31)"))) + +(define_constraint "N" + "A signed 16 bit constant value." + (and (match_code "const_int") + (match_test "(ival >= -32768) && (ival <= 32767)"))) + +(define_constraint "O" + "An exact bitmask of contiguous 1 bits starting at bit 0." + (and (match_code "const_int") + (match_test "exact_log2 (ival + 1) != -1"))) + +(define_constraint "P" + "A 7-bit negative constant value shifted << 2." + (and (match_code "const_int") + (match_test "(ival >= -504) && (ival <= 0) && ((ival & 3) == 0)"))) + +(define_constraint "S" + "A symbolic reference." + (match_code "symbol_ref")) + +(define_constraint "Y" + "An FD-PIC symbolic reference." + (and (match_test "TARGET_FDPIC") + (match_test "GET_CODE (op) == UNSPEC") + (ior (match_test "XINT (op, 1) == UNSPEC_FDPIC_GOT") + (match_test "XINT (op, 1) == UNSPEC_FDPIC_GOT_FUNCDESC")))) + +(define_memory_constraint "T1" + "A memory operand that can be used for .1 instruction." + (and (match_test "memory_operand (op, GET_MODE(op))") + (match_test "GET_MODE (op) == QImode"))) + +(define_memory_constraint "T2" + "A memory operand that can be used for .2 instruction." + (and (match_test "memory_operand (op, GET_MODE(op))") + (match_test "GET_MODE (op) == HImode"))) + +(define_memory_constraint "T4" + "A memory operand that can be used for .4 instruction." + (and (match_test "memory_operand (op, GET_MODE(op))") + (ior (match_test "GET_MODE (op) == SImode") + (match_test "GET_MODE (op) == DImode") + (match_test "GET_MODE (op) == SFmode")))) + +(define_memory_constraint "U1" + "An offsettable memory operand that can be used for .1 instruction." + (and (match_test "memory_operand (op, GET_MODE(op))") + (match_test "GET_MODE (op) == QImode") + (match_test "GET_CODE (XEXP (op, 0)) != POST_INC") + (match_test "GET_CODE (XEXP (op, 0)) != PRE_INC") + (match_test "GET_CODE (XEXP (op, 0)) != POST_DEC") + (match_test "GET_CODE (XEXP (op, 0)) != PRE_DEC") + (match_test "GET_CODE (XEXP (op, 0)) != POST_MODIFY") + (match_test "GET_CODE (XEXP (op, 0)) != PRE_MODIFY"))) + +(define_memory_constraint "U2" + "An offsettable memory operand that can be used for .2 instruction." + (and (match_test "memory_operand (op, GET_MODE(op))") + (match_test "GET_MODE (op) == HImode") + (match_test "GET_CODE (XEXP (op, 0)) != POST_INC") + (match_test "GET_CODE (XEXP (op, 0)) != PRE_INC") + (match_test "GET_CODE (XEXP (op, 0)) != POST_DEC") + (match_test "GET_CODE (XEXP (op, 0)) != PRE_DEC") + (match_test "GET_CODE (XEXP (op, 0)) != POST_MODIFY") + (match_test "GET_CODE (XEXP (op, 0)) != PRE_MODIFY"))) + +(define_memory_constraint "U4" + "An offsettable memory operand that can be used for .4 instruction." + (and (match_test "memory_operand (op, GET_MODE(op))") + (ior (match_test "GET_MODE (op) == SImode") + (match_test "GET_MODE (op) == DImode") + (match_test "GET_MODE (op) == SFmode")) + (match_test "GET_CODE (XEXP (op, 0)) != POST_INC") + (match_test "GET_CODE (XEXP (op, 0)) != PRE_INC") + (match_test "GET_CODE (XEXP (op, 0)) != POST_DEC") + (match_test "GET_CODE (XEXP (op, 0)) != PRE_DEC") + (match_test "GET_CODE (XEXP (op, 0)) != POST_MODIFY") + (match_test "GET_CODE (XEXP (op, 0)) != PRE_MODIFY"))) + --- /dev/null +++ b/gcc/config/ubicom32/crti.S @@ -0,0 +1,54 @@ +/* Specialized code needed to support construction and destruction of + file-scope objects in C++ and Java code, and to support exception handling. + Copyright (C) 1999 Free Software Foundation, Inc. + Contributed by Charles-Antoine Gauthier (charles.gauthier@iit.nrc.ca). + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* 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 supplies function prologues for the .init and .fini + * sections. It is linked in before crtbegin.o. + */ + .file "crti.o" + .ident "GNU C crti.o" + + .section .init + .align 2 + .globl _init + .type _init, @function +_init: + move.4 -4(sp)++, a5 +#ifdef __UBICOM32_FDPIC__ + move.4 -4(sp)++, a0 +#endif + + .section .fini + .align 2 + .globl _fini + .type _fini, @function +_fini: + move.4 -4(sp)++, a5 +#ifdef __UBICOM32_FDPIC__ + move.4 -4(sp)++, a0 +#endif --- /dev/null +++ b/gcc/config/ubicom32/crtn.S @@ -0,0 +1,47 @@ +/* Specialized code needed to support construction and destruction of + file-scope objects in C++ and Java code, and to support exception handling. + Copyright (C) 1999 Free Software Foundation, Inc. + Contributed by Charles-Antoine Gauthier (charles.gauthier@iit.nrc.ca). + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* 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 supplies function epilogues for the .init and .fini sections. + * It is linked in after all other files. + */ + + .file "crtn.o" + .ident "GNU C crtn.o" + + .section .init +#ifdef __UBICOM32_FDPIC__ + move.4 a0, (sp)4++ +#endif + ret (sp)4++ + + .section .fini +#ifdef __UBICOM32_FDPIC__ + move.4 a0, (sp)4++ +#endif + ret (sp)4++ --- /dev/null +++ b/gcc/config/ubicom32/elf.h @@ -0,0 +1,29 @@ +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "\ +%{msim:%{!shared:crt0%O%s}} \ +crti%O%s crtbegin%O%s" + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC "crtend%O%s crtn%O%s" + +#ifdef __UBICOM32_FDPIC__ +#define CRT_CALL_STATIC_FUNCTION(SECTION_OP, FUNC) \ + asm (SECTION_OP); \ + asm ("move.4 a0, 0(sp);\n\t" \ + "call a5," USER_LABEL_PREFIX #FUNC ";"); \ + asm (TEXT_SECTION_ASM_OP); +#endif + +#undef SUBTARGET_DRIVER_SELF_SPECS +#define SUBTARGET_DRIVER_SELF_SPECS \ + "%{mfdpic:-msim} " + +#define NO_IMPLICIT_EXTERN_C + +/* + * We need this to compile crtbegin/crtend. This should really be picked + * up from elfos.h but at the moment including elfos.h causes other more + * serous linker issues. + */ +#define INIT_SECTION_ASM_OP "\t.section\t.init" +#define FINI_SECTION_ASM_OP "\t.section\t.fini" --- /dev/null +++ b/gcc/config/ubicom32/linux.h @@ -0,0 +1,80 @@ +/* Definitions of target machine for Ubicom32-uclinux + + Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, + 2009 Free Software Foundation, Inc. + Contributed by Ubicom, 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 3, 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 COPYING3. If not see + . */ + +/* Don't assume anything about the header files. */ +#define NO_IMPLICIT_EXTERN_C + +#undef LIB_SPEC +#define LIB_SPEC \ + "%{pthread:-lpthread} " \ + "-lc" + +#undef LINK_GCC_C_SEQUENCE_SPEC +#define LINK_GCC_C_SEQUENCE_SPEC \ + "%{static:--start-group} %G %L %{static:--end-group} " \ + "%{!static: %G}" + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC \ + "%{!shared: %{pg|p|profile:gcrt1%O%s;pie:Scrt1%O%s;:crt1%O%s}} " \ + "crtreloc%O%s crti%O%s %{shared|pie:crtbeginS%O%s;:crtbegin%O%s}" + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC \ + "%{shared|pie:crtendS%O%s;:crtend%O%s} crtn%O%s" + +/* taken from linux.h */ +/* The GNU C++ standard library requires that these macros be defined. */ +#undef CPLUSPLUS_CPP_SPEC +#define CPLUSPLUS_CPP_SPEC "-D_GNU_SOURCE %(cpp)" + +#define TARGET_OS_CPP_BUILTINS() \ + do { \ + builtin_define_std ("__UBICOM32__"); \ + builtin_define_std ("__ubicom32__"); \ + builtin_define ("__gnu_linux__"); \ + builtin_define_std ("linux"); \ + builtin_define_std ("unix"); \ + builtin_assert ("system=linux"); \ + builtin_assert ("system=unix"); \ + builtin_assert ("system=posix"); \ + } while (0) + +#define OBJECT_FORMAT_ELF + + +#undef DRIVER_SELF_SPECS +#define DRIVER_SELF_SPECS \ + "%{!mno-fdpic:-mfdpic}" + +#undef LINK_SPEC +#define LINK_SPEC "%{mfdpic: -m elf32ubicom32fdpic -z text } %{shared} %{pie} \ + %{static:-dn -Bstatic} \ + %{shared:-G -Bdynamic} \ + %{!shared: %{!static: \ + %{rdynamic:-export-dynamic} \ + %{!dynamic-linker:-dynamic-linker /lib/ld-uClibc.so.0}} \ + %{static}} " + +/* +#define MD_UNWIND_SUPPORT "config/bfin/linux-unwind.h" +*/ --- /dev/null +++ b/gcc/config/ubicom32/predicates.md @@ -0,0 +1,327 @@ +; Predicate definitions for Ubicom32. + +; Copyright (C) 2009 Free Software Foundation, Inc. +; Contributed by Ubicom, 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 3, 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 COPYING3. If not see +; . + +(define_predicate "ubicom32_move_operand" + (match_code "const_int, const_double, const, mem, subreg, reg, lo_sum") +{ + if (CONST_INT_P (op)) + return true; + + if (GET_CODE (op) == CONST_DOUBLE) + return true; + + if (GET_CODE (op) == CONST) + return memory_address_p (mode, op); + + if (GET_MODE (op) != mode) + return false; + + if (MEM_P (op)) + return memory_address_p (mode, XEXP (op, 0)); + + if (GET_CODE (op) == SUBREG) { + op = SUBREG_REG (op); + + if (REG_P (op)) + return true; + + if (! MEM_P (op)) + return false; + + /* Paradoxical SUBREG. */ + if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (GET_MODE (op))) + return false; + + return memory_address_p (GET_MODE (op), XEXP (op, 0)); + } + + return register_operand (op, mode); +}) + +;; Returns true if OP is either a symbol reference or a sum of a +;; symbol reference and a constant. + +(define_predicate "ubicom32_symbolic_address_operand" + (match_code "symbol_ref, label_ref, const") +{ + switch (GET_CODE (op)) + { + case SYMBOL_REF: + case LABEL_REF: + return true; + + case CONST: + op = XEXP (op, 0); + return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF + || GET_CODE (XEXP (op, 0)) == LABEL_REF) + && CONST_INT_P (XEXP (op, 1))); + + default: + return false; + } +}) + +;; Return true if operand is the uClinux FD-PIC register. + +(define_predicate "ubicom32_fdpic_operand" + (match_code "reg") +{ + if (! TARGET_FDPIC) + return false; + + if (!REG_P (op)) + return false; + + if (GET_MODE (op) != mode && mode != VOIDmode) + return false; + + if (REGNO (op) != FDPIC_REGNUM && REGNO (op) < FIRST_PSEUDO_REGISTER) + return false; + + return true; +}) + +(define_predicate "ubicom32_fdpic_got_offset_operand" + (match_code "unspec") +{ + if (! TARGET_FDPIC) + return false; + + if (GET_CODE (op) != UNSPEC) + return false; + + if (XINT (op, 1) != UNSPEC_FDPIC_GOT + && XINT (op, 1) != UNSPEC_FDPIC_GOT_FUNCDESC) + return false; + + return true; +}) + +(define_predicate "ubicom32_arith_operand" + (match_code "subreg, reg, const_int, lo_sum, mem") +{ + return (ubicom32_move_operand (op, mode) + && ! ubicom32_symbolic_address_operand (op, mode) + && (! CONST_INT_P (op) + || satisfies_constraint_I (op))); +}) + +(define_predicate "ubicom32_arith_operand_dot1" + (match_code "subreg, reg, const_int, lo_sum, mem") +{ + return (ubicom32_move_operand (op, mode) + && ! ubicom32_symbolic_address_operand (op, mode) + && (! CONST_INT_P (op) + || satisfies_constraint_Q (op))); +}) + +(define_predicate "ubicom32_arith_operand_dot2" + (match_code "subreg, reg, const_int, lo_sum, mem") +{ + return (ubicom32_move_operand (op, mode) + && ! ubicom32_symbolic_address_operand (op, mode) + && (! CONST_INT_P (op) + || satisfies_constraint_R (op))); +}) + +(define_predicate "ubicom32_compare_operand" + (match_code "subreg, reg, const_int, lo_sum, mem") +{ + return (ubicom32_move_operand (op, mode) + && ! ubicom32_symbolic_address_operand (op, mode) + && (! CONST_INT_P (op) + || satisfies_constraint_N (op))); +}) + +(define_predicate "ubicom32_compare_operator" + (match_code "compare")) + +(define_predicate "ubicom32_and_or_si3_operand" + (match_code "subreg, reg, const_int, lo_sum, mem") +{ + return (ubicom32_arith_operand (op, mode) + || (CONST_INT_P (op) + && ((exact_log2 (INTVAL (op) + 1) != -1 + && exact_log2 (INTVAL (op) + 1) <= 31) + || (exact_log2 (INTVAL (op)) != -1 + && exact_log2 (INTVAL (op)) <= 31) + || (exact_log2 (~INTVAL (op)) != -1 + && exact_log2 (~INTVAL (op)) <= 31)))); +}) + +(define_predicate "ubicom32_and_or_hi3_operand" + (match_code "subreg, reg, const_int, lo_sum, mem") +{ + return (ubicom32_arith_operand (op, mode) + || (CONST_INT_P (op) + && exact_log2 (INTVAL (op) + 1) != -1 + && exact_log2 (INTVAL (op) + 1) <= 15)); +}) + +(define_predicate "ubicom32_mem_or_address_register_operand" + (match_code "subreg, reg, mem") +{ + unsigned int regno; + + if (MEM_P (op) + && memory_operand (op, mode)) + return true; + + if (REG_P (op)) + regno = REGNO (op); + else if (GET_CODE (op) == SUBREG && REG_P (SUBREG_REG (op))) + { + int offset; + if (REGNO (SUBREG_REG (op)) >= FIRST_PSEUDO_REGISTER) + offset = SUBREG_BYTE (op) / (GET_MODE_SIZE (GET_MODE (op))); + else + offset = subreg_regno_offset (REGNO (SUBREG_REG (op)), + GET_MODE (SUBREG_REG (op)), + SUBREG_BYTE (op), + GET_MODE (op)); + regno = REGNO (SUBREG_REG (op)) + offset; + } + else + return false; + + return (regno >= FIRST_PSEUDO_REGISTER + || REGNO_REG_CLASS (regno) == FDPIC_REG + || REGNO_REG_CLASS (regno) == ADDRESS_REGS); +}) + +(define_predicate "ubicom32_data_register_operand" + (match_code "subreg, reg") +{ + unsigned int regno; + + if (REG_P (op)) + regno = REGNO (op); + else if (GET_CODE (op) == SUBREG && REG_P (SUBREG_REG (op))) + { + int offset; + if (REGNO (SUBREG_REG (op)) >= FIRST_PSEUDO_REGISTER) + offset = SUBREG_BYTE (op) / (GET_MODE_SIZE (GET_MODE (op))); + else + offset = subreg_regno_offset (REGNO (SUBREG_REG (op)), + GET_MODE (SUBREG_REG (op)), + SUBREG_BYTE (op), + GET_MODE (op)); + regno = REGNO (SUBREG_REG (op)) + offset; + } + else + return false; + + return ((regno >= FIRST_PSEUDO_REGISTER + && regno != REGNO (virtual_stack_vars_rtx)) + || REGNO_REG_CLASS (regno) == DATA_REGS); +}) + +(define_predicate "ubicom32_address_register_operand" + (match_code "subreg, reg") +{ + unsigned int regno; + + if (REG_P (op)) + regno = REGNO (op); + else if (GET_CODE (op) == SUBREG && REG_P (SUBREG_REG (op))) + { + int offset; + if (REGNO (SUBREG_REG (op)) >= FIRST_PSEUDO_REGISTER) + offset = SUBREG_BYTE (op) / (GET_MODE_SIZE (GET_MODE (op))); + else + offset = subreg_regno_offset (REGNO (SUBREG_REG (op)), + GET_MODE (SUBREG_REG (op)), + SUBREG_BYTE (op), + GET_MODE (op)); + regno = REGNO (SUBREG_REG (op)) + offset; + } + else + return false; + + return (regno >= FIRST_PSEUDO_REGISTER + || REGNO_REG_CLASS (regno) == FDPIC_REG + || REGNO_REG_CLASS (regno) == ADDRESS_REGS); +}) + +(define_predicate "ubicom32_acc_lo_register_operand" + (match_code "subreg, reg") +{ + unsigned int regno; + + if (REG_P (op)) + regno = REGNO (op); + else if (GET_CODE (op) == SUBREG && REG_P (SUBREG_REG (op))) + { + int offset; + if (REGNO (SUBREG_REG (op)) >= FIRST_PSEUDO_REGISTER) + offset = SUBREG_BYTE (op) / (GET_MODE_SIZE (GET_MODE (op))); + else + offset = subreg_regno_offset (REGNO (SUBREG_REG (op)), + GET_MODE (SUBREG_REG (op)), + SUBREG_BYTE (op), + GET_MODE (op)); + regno = REGNO (SUBREG_REG (op)) + offset; + } + else + return false; + + return ((regno >= FIRST_PSEUDO_REGISTER + && regno != REGNO (virtual_stack_vars_rtx)) + || REGNO_REG_CLASS (regno) == ACC_LO_REGS); +}) + +(define_predicate "ubicom32_acc_hi_register_operand" + (match_code "subreg, reg") +{ + unsigned int regno; + + if (REG_P (op)) + regno = REGNO (op); + else if (GET_CODE (op) == SUBREG && REG_P (SUBREG_REG (op))) + { + int offset; + if (REGNO (SUBREG_REG (op)) >= FIRST_PSEUDO_REGISTER) + offset = SUBREG_BYTE (op) / (GET_MODE_SIZE (GET_MODE (op))); + else + offset = subreg_regno_offset (REGNO (SUBREG_REG (op)), + GET_MODE (SUBREG_REG (op)), + SUBREG_BYTE (op), + GET_MODE (op)); + regno = REGNO (SUBREG_REG (op)) + offset; + } + else + return false; + + return ((regno >= FIRST_PSEUDO_REGISTER + && regno != REGNO (virtual_stack_vars_rtx)) + || REGNO_REG_CLASS (regno) == ACC_REGS); +}) + +(define_predicate "ubicom32_call_address_operand" + (match_code "symbol_ref, subreg, reg") +{ + return (GET_CODE (op) == SYMBOL_REF || REG_P (op)); +}) + +(define_special_predicate "ubicom32_cc_register_operand" + (and (match_code "reg") + (match_test "REGNO (op) == CC_REGNUM"))) + --- /dev/null +++ b/gcc/config/ubicom32/t-ubicom32 @@ -0,0 +1,52 @@ +# 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 + +LIB2FUNCS_EXTRA = \ + $(srcdir)/config/udivmodsi4.c \ + $(srcdir)/config/divmod.c \ + $(srcdir)/config/udivmod.c + +# If any special flags are necessary when building libgcc2 put them here. +# +# TARGET_LIBGCC2_CFLAGS = + +# 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 + +# Commented out to speed up compiler development! +# +# MULTILIB_OPTIONS = march=ubicom32v1/march=ubicom32v2/march=ubicom32v3/march=ubicom32v4 +# MULTILIB_DIRNAMES = ubicom32v1 ubicom32v2 ubicom32v3 ubicom32v4 + +MULTILIB_OPTIONS = march=ubicom32v3/march=ubicom32v4 +MULTILIB_OPTIONS += mfdpic +MULTILIB_OPTIONS += mno-ipos-abi/mipos-abi +MULTILIB_OPTIONS += fno-leading-underscore/fleading-underscore + +# Assemble startup files. +$(T)crti.o: $(srcdir)/config/ubicom32/crti.S $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \ + -c -o $(T)crti.o -x assembler-with-cpp $(srcdir)/config/ubicom32/crti.S + +$(T)crtn.o: $(srcdir)/config/ubicom32/crtn.S $(GCC_PASSES) + $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \ + -c -o $(T)crtn.o -x assembler-with-cpp $(srcdir)/config/ubicom32/crtn.S + +# these parts are required because uClibc ldso needs them to link. +# they are not in the specfile so they will not be included automatically. +EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o crtbeginS.o crtendS.o crti.o crtn.o --- /dev/null +++ b/gcc/config/ubicom32/t-ubicom32-linux @@ -0,0 +1,35 @@ +# 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 + +LIB2FUNCS_EXTRA = \ + $(srcdir)/config/udivmodsi4.c \ + $(srcdir)/config/divmod.c \ + $(srcdir)/config/udivmod.c + +# If any special flags are necessary when building libgcc2 put them here. +# +# TARGET_LIBGCC2_CFLAGS = + +# 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 + +# We only support v3 and v4 ISAs for uClinux. + +MULTILIB_OPTIONS = march=ubicom32v3/march=ubicom32v4 + +#EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o crtbeginS.o crtendS.o --- /dev/null +++ b/gcc/config/ubicom32/t-ubicom32-uclinux @@ -0,0 +1,35 @@ +# 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 + +LIB2FUNCS_EXTRA = \ + $(srcdir)/config/udivmodsi4.c \ + $(srcdir)/config/divmod.c \ + $(srcdir)/config/udivmod.c + +# If any special flags are necessary when building libgcc2 put them here. +# +# TARGET_LIBGCC2_CFLAGS = + +# 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 + +# We only support v3 and v4 ISAs for uClinux. + +MULTILIB_OPTIONS = march=ubicom32v3/march=ubicom32v4 + +EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o # crtbeginS.o crtendS.o --- /dev/null +++ b/gcc/config/ubicom32/ubicom32-modes.def @@ -0,0 +1,30 @@ +/* Definitions of target machine for GNU compiler, Ubicom32 architecture. + Copyright (C) 2009 Free Software Foundation, Inc. + Contributed by Ubicom, 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 3, 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 COPYING3. If not see + . */ + +/* Some insns set all condition code flags, some only set the Z and N flags, and + some only set the Z flag. */ + +CC_MODE (CCW); +CC_MODE (CCWZN); +CC_MODE (CCWZ); +CC_MODE (CCS); +CC_MODE (CCSZN); +CC_MODE (CCSZ); + --- /dev/null +++ b/gcc/config/ubicom32/ubicom32-protos.h @@ -0,0 +1,84 @@ +/* Function prototypes for Ubicom IP3000. + + Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, + 2009 Free Software Foundation, Inc. + Contributed by Ubicom, Inc. + + This file is part of GNU CC. + + GNU CC is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2, or (at your option) any later + version. + + GNU CC is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License along + with GNU CC; see the file COPYING. If not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifdef RTX_CODE + +#ifdef TREE_CODE +extern void ubicom32_va_start (tree, rtx); +#endif /* TREE_CODE */ + +extern void ubicom32_print_operand (FILE *, rtx, int); +extern void ubicom32_print_operand_address (FILE *, rtx); + +extern void ubicom32_conditional_register_usage (void); +extern enum reg_class ubicom32_preferred_reload_class (rtx, enum reg_class); +extern int ubicom32_regno_ok_for_index_p (int, int); +extern void ubicom32_expand_movsi (rtx *); +extern void ubicom32_expand_addsi3 (rtx *); +extern int ubicom32_emit_mult_sequence (rtx *); +extern void ubicom32_emit_move_const_int (rtx, rtx); +extern bool ubicom32_legitimate_constant_p (rtx); +extern bool ubicom32_legitimate_address_p (enum machine_mode, rtx, int); +extern rtx ubicom32_legitimize_address (rtx, rtx, enum machine_mode); +extern rtx ubicom32_legitimize_reload_address (rtx, enum machine_mode, int, int); +extern void ubicom32_canonicalize_comparison (enum rtx_code *code, rtx *op0, rtx *op1); +extern int ubicom32_mode_dependent_address_p (rtx); +extern void ubicom32_output_cond_jump (rtx, rtx, rtx); +extern void ubicom32_expand_eh_return (rtx *); +extern void ubicom32_expand_call_fdpic (rtx *); +extern void ubicom32_expand_call_value_fdpic (rtx *); +extern enum machine_mode ubicom32_select_cc_mode (RTX_CODE, rtx, rtx); +extern rtx ubicom32_gen_compare_reg (RTX_CODE, rtx, rtx); +extern int ubicom32_shiftable_const_int (int); +#endif /* RTX_CODE */ + +#ifdef TREE_CODE +extern void init_cumulative_args (CUMULATIVE_ARGS *cum, + tree fntype, + struct rtx_def *libname, + int indirect); +extern struct rtx_def *function_arg (CUMULATIVE_ARGS *, + enum machine_mode, tree, int); +extern struct rtx_def *function_incoming_arg (CUMULATIVE_ARGS *, + enum machine_mode, + tree, int); +extern int function_arg_partial_nregs (CUMULATIVE_ARGS *, + enum machine_mode, tree, int); +extern struct rtx_def *ubicom32_va_arg (tree, tree); +extern int ubicom32_reg_parm_stack_space (tree); +#endif /* TREE_CODE */ + +extern struct rtx_def * ubicom32_builtin_saveregs (void); +extern void asm_file_start (FILE *); +extern void ubicom32_expand_prologue (void); +extern void ubicom32_expand_epilogue (void); +extern int ubicom32_initial_elimination_offset (int, int); +extern int ubicom32_regno_ok_for_base_p (int, int); +extern bool ubicom32_hard_regno_mode_ok (unsigned int, enum machine_mode); +extern int ubicom32_can_use_return_insn_p (void); +extern rtx ubicom32_return_addr_rtx (int, rtx); +extern void ubicom32_optimization_options (int, int); +extern void ubicom32_override_options (void); +extern bool ubicom32_match_cc_mode (rtx, enum machine_mode); + +extern int ubicom32_reorg_completed; + --- /dev/null +++ b/gcc/config/ubicom32/ubicom32.c @@ -0,0 +1,2881 @@ +/* Subroutines for insn-output.c for Ubicom32 + + Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, + 2009 Free Software Foundation, Inc. + Contributed by Ubicom, 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 3, 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 COPYING3. If not see + . */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "rtl.h" +#include "tree.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "real.h" +#include "insn-config.h" +#include "conditions.h" +#include "insn-flags.h" +#include "output.h" +#include "insn-attr.h" +#include "insn-codes.h" +#include "flags.h" +#include "recog.h" +#include "expr.h" +#include "function.h" +#include "obstack.h" +#include "toplev.h" +#include "tm_p.h" +#include "tm-constrs.h" +#include "basic-block.h" +#include "integrate.h" +#include "target.h" +#include "target-def.h" +#include "reload.h" +#include "df.h" +#include "langhooks.h" +#include "optabs.h" + +static tree ubicom32_handle_fndecl_attribute (tree *, tree, tree, int, bool *); +static void ubicom32_layout_frame (void); +static void ubicom32_function_prologue (FILE *, HOST_WIDE_INT); +static void ubicom32_function_epilogue (FILE *, HOST_WIDE_INT); +static bool ubicom32_rtx_costs (rtx, int, int, int *, bool speed); +static bool ubicom32_fixed_condition_code_regs (unsigned int *, + unsigned int *); +static enum machine_mode ubicom32_cc_modes_compatible (enum machine_mode, + enum machine_mode); +static int ubicom32_naked_function_p (void); +static void ubicom32_machine_dependent_reorg (void); +static bool ubicom32_assemble_integer (rtx, unsigned int, int); +static void ubicom32_asm_init_sections (void); +static int ubicom32_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,tree, + bool); +static bool ubicom32_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED, + enum machine_mode mode, const_tree type, + bool named ATTRIBUTE_UNUSED); +static bool ubicom32_callee_copies (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED, + enum machine_mode mode, const_tree type, + bool named ATTRIBUTE_UNUSED); + +static bool ubicom32_return_in_memory (const_tree type, + const_tree fntype ATTRIBUTE_UNUSED); +static bool ubicom32_is_base_reg (rtx, int); +static void ubicom32_init_builtins (void); +static rtx ubicom32_expand_builtin (tree, rtx, rtx, enum machine_mode, int); +static tree ubicom32_fold_builtin (tree, tree, bool); +static int ubicom32_get_valid_offset_mask (enum machine_mode); +static bool ubicom32_cannot_force_const_mem (rtx); + +/* Case values threshold */ +int ubicom32_case_values_threshold = 6; + +/* Nonzero if this chip supports the Ubicom32 v3 ISA. */ +int ubicom32_v3 = 1; + +/* Nonzero if this chip supports the Ubicom32 v4 ISA. */ +int ubicom32_v4 = 1; + +/* Valid attributes: + naked - don't generate function prologue/epilogue and `ret' command. */ +const struct attribute_spec ubicom32_attribute_table[] = +{ + /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */ + { "naked", 0, 0, true, false, false, ubicom32_handle_fndecl_attribute }, + { NULL, 0, 0, false, false, false, NULL } +}; + +#undef TARGET_ASM_FUNCTION_PROLOGUE +#define TARGET_ASM_FUNCTION_PROLOGUE ubicom32_function_prologue + +#undef TARGET_ASM_FUNCTION_EPILOGUE +#define TARGET_ASM_FUNCTION_EPILOGUE ubicom32_function_epilogue + +#undef TARGET_ATTRIBUTE_TABLE +#define TARGET_ATTRIBUTE_TABLE ubicom32_attribute_table + +/* All addresses cost the same amount. */ +#undef TARGET_ADDRESS_COST +#define TARGET_ADDRESS_COST hook_int_rtx_bool_0 + +#undef TARGET_RTX_COSTS +#define TARGET_RTX_COSTS ubicom32_rtx_costs + +#undef TARGET_FIXED_CONDITION_CODE_REGS +#define TARGET_FIXED_CONDITION_CODE_REGS ubicom32_fixed_condition_code_regs + +#undef TARGET_CC_MODES_COMPATIBLE +#define TARGET_CC_MODES_COMPATIBLE ubicom32_cc_modes_compatible + +#undef TARGET_MACHINE_DEPENDENT_REORG +#define TARGET_MACHINE_DEPENDENT_REORG ubicom32_machine_dependent_reorg + +#undef TARGET_ASM_INTEGER +#define TARGET_ASM_INTEGER ubicom32_assemble_integer + +#undef TARGET_ASM_INIT_SECTIONS +#define TARGET_ASM_INIT_SECTIONS ubicom32_asm_init_sections + +#undef TARGET_ARG_PARTIAL_BYTES +#define TARGET_ARG_PARTIAL_BYTES ubicom32_arg_partial_bytes + +#undef TARGET_PASS_BY_REFERENCE +#define TARGET_PASS_BY_REFERENCE ubicom32_pass_by_reference + +#undef TARGET_CALLEE_COPIES +#define TARGET_CALLEE_COPIES ubicom32_callee_copies + +#undef TARGET_RETURN_IN_MEMORY +#define TARGET_RETURN_IN_MEMORY ubicom32_return_in_memory + +#undef TARGET_INIT_BUILTINS +#define TARGET_INIT_BUILTINS ubicom32_init_builtins + +#undef TARGET_EXPAND_BUILTIN +#define TARGET_EXPAND_BUILTIN ubicom32_expand_builtin + +#undef TARGET_FOLD_BUILTIN +#define TARGET_FOLD_BUILTIN ubicom32_fold_builtin + +#undef TARGET_CANNOT_FORCE_CONST_MEM +#define TARGET_CANNOT_FORCE_CONST_MEM ubicom32_cannot_force_const_mem + +struct gcc_target targetm = TARGET_INITIALIZER; + +static char save_regs[FIRST_PSEUDO_REGISTER]; +static int nregs; +static int frame_size; +int ubicom32_stack_size = 0; /* size of allocated stack (including frame) */ +int ubicom32_can_use_calli_to_ret; + +#define STACK_UNIT_BOUNDARY (STACK_BOUNDARY / BITS_PER_UNIT) +#define ROUND_CALL_BLOCK_SIZE(BYTES) \ + (((BYTES) + (STACK_UNIT_BOUNDARY - 1)) & ~(STACK_UNIT_BOUNDARY - 1)) + +/* In case of a PRE_INC, POST_INC, PRE_DEC, POST_DEC memory reference, we + must report the mode of the memory reference from PRINT_OPERAND to + PRINT_OPERAND_ADDRESS. */ +enum machine_mode output_memory_reference_mode; + +/* Flag for some split insns from the ubicom32.md. */ +int ubicom32_reorg_completed; + +enum reg_class const ubicom32_regclass_map[FIRST_PSEUDO_REGISTER] = +{ + DATA_REGS, + DATA_REGS, + DATA_REGS, + DATA_REGS, + DATA_REGS, + DATA_REGS, + DATA_REGS, + DATA_REGS, + DATA_REGS, + DATA_REGS, + DATA_REGS, + DATA_REGS, + DATA_REGS, + DATA_REGS, + DATA_REGS, + DATA_REGS, + FDPIC_REG, + ADDRESS_REGS, + ADDRESS_REGS, + ADDRESS_REGS, + ADDRESS_REGS, + ADDRESS_REGS, + ADDRESS_REGS, + ADDRESS_REGS, + ACC_REGS, + ACC_LO_REGS, + ACC_REGS, + ACC_LO_REGS, + SOURCE3_REG, + ADDRESS_REGS, + NO_REGS, /* CC_REG must be NO_REGS */ + SPECIAL_REGS, + SPECIAL_REGS, + SPECIAL_REGS, + SPECIAL_REGS, + SPECIAL_REGS, + SPECIAL_REGS, + SPECIAL_REGS, + SPECIAL_REGS +}; + +rtx ubicom32_compare_op0; +rtx ubicom32_compare_op1; + +/* Handle command line option overrides. */ + +void +ubicom32_override_options (void) +{ + flag_pic = 0; + + if (strcmp (ubicom32_arch_name, "ubicom32v1") == 0) { + /* If we have a version 1 architecture then we want to avoid using jump + tables. */ + ubicom32_case_values_threshold = 30000; + ubicom32_v3 = 0; + ubicom32_v4 = 0; + } else if (strcmp (ubicom32_arch_name, "ubicom32v2") == 0) { + ubicom32_v3 = 0; + ubicom32_v4 = 0; + } else if (strcmp (ubicom32_arch_name, "ubicom32v3") == 0) { + ubicom32_v3 = 1; + ubicom32_v4 = 0; + } else if (strcmp (ubicom32_arch_name, "ubicom32v4") == 0) { + ubicom32_v3 = 1; + ubicom32_v4 = 1; + } + + /* There is no single unaligned SI op for PIC code. Sometimes we + need to use ".4byte" and sometimes we need to use ".picptr". + See ubicom32_assemble_integer for details. */ + if (TARGET_FDPIC) + targetm.asm_out.unaligned_op.si = 0; +} + +void +ubicom32_conditional_register_usage (void) +{ + /* If we're using the old ipOS ABI we need to make D10 through D13 + caller-clobbered. */ + if (TARGET_IPOS_ABI) + { + call_used_regs[D10_REGNUM] = 1; + call_used_regs[D11_REGNUM] = 1; + call_used_regs[D12_REGNUM] = 1; + call_used_regs[D13_REGNUM] = 1; + } +} + +/* We have some number of optimizations that don't really work for the Ubicom32 + architecture so we deal with them here. */ + +void +ubicom32_optimization_options (int level ATTRIBUTE_UNUSED, + int size ATTRIBUTE_UNUSED) +{ + /* The tree IVOPTs pass seems to do really bad things for the Ubicom32 + architecture - it tends to turn things that would happily use pre/post + increment/decrement into operations involving unecessary loop + indicies. */ + flag_ivopts = 0; + + /* We have problems where DSE at the RTL level misses partial stores + to the stack. For now we disable it to avoid this. */ + flag_dse = 0; +} + +/* Print operand X using operand code CODE to assembly language output file + FILE. */ + +void +ubicom32_print_operand (FILE *file, rtx x, int code) +{ + switch (code) + { + case 'A': + /* Identify the correct accumulator to use. */ + if (REGNO (x) == ACC0_HI_REGNUM || REGNO (x) == ACC0_LO_REGNUM) + fprintf (file, "acc0"); + else if (REGNO (x) == ACC1_HI_REGNUM || REGNO (x) == ACC1_LO_REGNUM) + fprintf (file, "acc1"); + else + abort (); + break; + + case 'b': + case 'B': + { + enum machine_mode mode; + + mode = GET_MODE (XEXP (x, 0)); + + /* These are normal and reversed branches. */ + switch (code == 'b' ? GET_CODE (x) : reverse_condition (GET_CODE (x))) + { + case NE: + fprintf (file, "ne"); + break; + + case EQ: + fprintf (file, "eq"); + break; + + case GE: + if (mode == CCSZNmode || mode == CCWZNmode) + fprintf (file, "pl"); + else + fprintf (file, "ge"); + break; + + case GT: + fprintf (file, "gt"); + break; + + case LE: + fprintf (file, "le"); + break; + + case LT: + if (mode == CCSZNmode || mode == CCWZNmode) + fprintf (file, "mi"); + else + fprintf (file, "lt"); + break; + + case GEU: + fprintf (file, "cs"); + break; + + case GTU: + fprintf (file, "hi"); + break; + + case LEU: + fprintf (file, "ls"); + break; + + case LTU: + fprintf (file, "cc"); + break; + + default: + abort (); + } + } + break; + + case 'C': + /* This is used for the operand to a call instruction; + if it's a REG, enclose it in parens, else output + the operand normally. */ + if (REG_P (x)) + { + fputc ('(', file); + ubicom32_print_operand (file, x, 0); + fputc (')', file); + } + else + ubicom32_print_operand (file, x, 0); + break; + + case 'd': + /* Bit operations we need bit numbers. */ + fprintf (file, "%d", exact_log2 (INTVAL (x))); + break; + + case 'D': + /* Bit operations we need bit numbers. */ + fprintf (file, "%d", exact_log2 (~ INTVAL (x))); + break; + + case 'E': + /* For lea, which we use to add address registers. + We don't want the '#' on a constant. */ + if (CONST_INT_P (x)) + { + fprintf (file, "%ld", INTVAL (x)); + break; + } + /* FALL THROUGH */ + + default: + switch (GET_CODE (x)) + { + case MEM: + output_memory_reference_mode = GET_MODE (x); + output_address (XEXP (x, 0)); + break; + + case PLUS: + output_address (x); + break; + + case REG: + fprintf (file, "%s", reg_names[REGNO (x)]); + break; + + case SUBREG: + fprintf (file, "%s", reg_names[subreg_regno (x)]); + break; + + /* This will only be single precision.... */ + case CONST_DOUBLE: + { + unsigned long val; + REAL_VALUE_TYPE rv; + + REAL_VALUE_FROM_CONST_DOUBLE (rv, x); + REAL_VALUE_TO_TARGET_SINGLE (rv, val); + fprintf (file, "0x%lx", val); + break; + } + + case CONST_INT: + case SYMBOL_REF: + case CONST: + case LABEL_REF: + case CODE_LABEL: + case LO_SUM: + ubicom32_print_operand_address (file, x); + break; + + case HIGH: + fprintf (file, "#%%hi("); + ubicom32_print_operand_address (file, XEXP (x, 0)); + fprintf (file, ")"); + break; + + case UNSPEC: + switch (XINT (x, 1)) + { + case UNSPEC_FDPIC_GOT: + fprintf (file, "#%%got_lo("); + ubicom32_print_operand_address (file, XVECEXP (x, 0, 0)); + fprintf (file, ")"); + break; + + case UNSPEC_FDPIC_GOT_FUNCDESC: + fprintf (file, "#%%got_funcdesc_lo("); + ubicom32_print_operand_address (file, XVECEXP (x, 0, 0)); + fprintf (file, ")"); + break; + + default: + abort (); + } + break; + + default: + abort (); + } + break; + } +} + +/* Output assembly language output for the address ADDR to FILE. */ + +void +ubicom32_print_operand_address (FILE *file, rtx addr) +{ + switch (GET_CODE (addr)) + { + case POST_INC: + ubicom32_print_operand_address (file, XEXP (addr, 0)); + fprintf (file, "%d++", GET_MODE_SIZE (output_memory_reference_mode)); + break; + + case PRE_INC: + fprintf (file, "%d", GET_MODE_SIZE (output_memory_reference_mode)); + ubicom32_print_operand_address (file, XEXP (addr, 0)); + fprintf (file, "++"); + break; + + case POST_DEC: + ubicom32_print_operand_address (file, XEXP (addr, 0)); + fprintf (file, "%d++", -GET_MODE_SIZE (output_memory_reference_mode)); + break; + + case PRE_DEC: + fprintf (file, "%d", -GET_MODE_SIZE (output_memory_reference_mode)); + ubicom32_print_operand_address (file, XEXP (addr, 0)); + fprintf (file, "++"); + break; + + case POST_MODIFY: + ubicom32_print_operand_address (file, XEXP (addr, 0)); + fprintf (file, "%ld++", INTVAL (XEXP (XEXP (addr,1), 1))); + break; + + case PRE_MODIFY: + fprintf (file, "%ld", INTVAL (XEXP (XEXP (addr,1), 1))); + ubicom32_print_operand_address (file, XEXP (addr, 0)); + fprintf (file, "++"); + break; + + case REG: + fputc ('(', file); + fprintf (file, "%s", reg_names[REGNO (addr)]); + fputc (')', file); + break; + + case PLUS: + { + rtx base = XEXP (addr, 0); + rtx index = XEXP (addr, 1); + + /* Switch around addresses of the form index * scaling + base. */ + if (! ubicom32_is_base_reg (base, 1)) + { + rtx tmp = base; + base = index; + index = tmp; + } + + if (CONST_INT_P (index)) + { + fprintf (file, "%ld", INTVAL (index)); + fputc ('(', file); + fputs (reg_names[REGNO (base)], file); + } + else if (GET_CODE (index) == MULT + || REG_P (index)) + { + if (GET_CODE (index) == MULT) + index = XEXP (index, 0); + fputc ('(', file); + fputs (reg_names[REGNO (base)], file); + fputc (',', file); + fputs (reg_names[REGNO (index)], file); + } + else + abort (); + + fputc (')', file); + break; + } + + case LO_SUM: + fprintf (file, "%%lo("); + ubicom32_print_operand (file, XEXP (addr, 1), 'L'); + fprintf (file, ")("); + ubicom32_print_operand (file, XEXP (addr, 0), 0); + fprintf (file, ")"); + break; + + case CONST_INT: + fputc ('#', file); + output_addr_const (file, addr); + break; + + default: + output_addr_const (file, addr); + break; + } +} + +/* X and Y are two things to compare using CODE. Emit the compare insn and + return the rtx for the cc reg in the proper mode. */ + +rtx +ubicom32_gen_compare_reg (enum rtx_code code, rtx x, rtx y) +{ + enum machine_mode mode = SELECT_CC_MODE (code, x, y); + rtx cc_reg; + + cc_reg = gen_rtx_REG (mode, CC_REGNUM); + + emit_insn (gen_rtx_SET (VOIDmode, cc_reg, + gen_rtx_COMPARE (mode, x, y))); + + return cc_reg; +} + +/* Given a comparison code (EQ, NE, etc.) and the first operand of a COMPARE, + return the mode to be used for the comparison. */ + +enum machine_mode +ubicom32_select_cc_mode (enum rtx_code op, rtx x, rtx y) +{ + /* Is this a short compare? */ + if (GET_MODE (x) == QImode + || GET_MODE (x) == HImode + || GET_MODE (y) == QImode + || GET_MODE (y) == HImode) + { + switch (op) + { + case EQ : + case NE : + return CCSZmode; + + case GE: + case LT: + if (y == const0_rtx) + return CCSZNmode; + + default : + return CCSmode; + } + } + + /* We have a word compare. */ + switch (op) + { + case EQ : + case NE : + return CCWZmode; + + case GE : + case LT : + if (y == const0_rtx) + return CCWZNmode; + + default : + return CCWmode; + } +} + +/* Return TRUE or FALSE depending on whether the first SET in INSN + has source and destination with matching CC modes, and that the + CC mode is at least as constrained as REQ_MODE. */ +bool +ubicom32_match_cc_mode (rtx insn, enum machine_mode req_mode) +{ + rtx set; + enum machine_mode set_mode; + + set = PATTERN (insn); + if (GET_CODE (set) == PARALLEL) + set = XVECEXP (set, 0, 0); + gcc_assert (GET_CODE (set) == SET); + gcc_assert (GET_CODE (SET_SRC (set)) == COMPARE); + + /* SET_MODE is the mode we have in the instruction. This must either + be the same or less restrictive that the required mode REQ_MODE. */ + set_mode = GET_MODE (SET_DEST (set)); + + switch (req_mode) + { + case CCSZmode: + if (set_mode != CCSZmode) + return 0; + break; + + case CCSZNmode: + if (set_mode != CCSZmode + && set_mode != CCSZNmode) + return 0; + break; + + case CCSmode: + if (set_mode != CCSmode + && set_mode != CCSZmode + && set_mode != CCSZNmode) + return 0; + break; + + case CCWZmode: + if (set_mode != CCWZmode) + return 0; + break; + + case CCWZNmode: + if (set_mode != CCWZmode + && set_mode != CCWZNmode) + return 0; + break; + + case CCWmode: + if (set_mode != CCWmode + && set_mode != CCWZmode + && set_mode != CCWZNmode) + return 0; + break; + + default: + gcc_unreachable (); + } + + return (GET_MODE (SET_SRC (set)) == set_mode); +} + +/* Replace the comparison OP0 CODE OP1 by a semantically equivalent one + that we can implement more efficiently. */ + +void +ubicom32_canonicalize_comparison (enum rtx_code *code, rtx *op0, rtx *op1) +{ + /* If we have a REG and a MEM then compare the MEM with the REG and not + the other way round. */ + if (REG_P (*op0) && MEM_P (*op1)) + { + rtx tem = *op0; + *op0 = *op1; + *op1 = tem; + *code = swap_condition (*code); + return; + } + + /* If we have a REG and a CONST_INT then we may want to reverse things + if the constant can be represented as an "I" constraint. */ + if (REG_P (*op0) && CONST_INT_P (*op1) && satisfies_constraint_I (*op1)) + { + rtx tem = *op0; + *op0 = *op1; + *op1 = tem; + *code = swap_condition (*code); + return; + } +} + +/* Return the fixed registers used for condition codes. */ + +static bool +ubicom32_fixed_condition_code_regs (unsigned int *p1, unsigned int *p2) +{ + *p1 = CC_REGNUM; + *p2 = INVALID_REGNUM; + + return true; +} + +/* If two condition code modes are compatible, return a condition code + mode which is compatible with both. Otherwise, return + VOIDmode. */ + +static enum machine_mode +ubicom32_cc_modes_compatible (enum machine_mode m1, enum machine_mode m2) +{ + if (m1 == m2) + return m1; + + if (GET_MODE_CLASS (m1) != MODE_CC || GET_MODE_CLASS (m2) != MODE_CC) + return VOIDmode; + + switch (m1) + { + case CCWmode: + if (m2 == CCWZNmode || m2 == CCWZmode) + return m1; + + return VOIDmode; + + case CCWZNmode: + if (m2 == CCWmode) + return m2; + + if (m2 == CCWZmode) + return m1; + + return VOIDmode; + + case CCWZmode: + if (m2 == CCWmode || m2 == CCWZNmode) + return m2; + + return VOIDmode; + + case CCSmode: + if (m2 == CCSZNmode || m2 == CCSZmode) + return m1; + + return VOIDmode; + + case CCSZNmode: + if (m2 == CCSmode) + return m2; + + if (m2 == CCSZmode) + return m1; + + return VOIDmode; + + case CCSZmode: + if (m2 == CCSmode || m2 == CCSZNmode) + return m2; + + return VOIDmode; + + default: + gcc_unreachable (); + } +} + +static rtx +ubicom32_legitimize_fdpic_address_symbol (rtx orig, rtx reg, rtx fdpic_reg) +{ + int unspec; + rtx got_offs; + rtx got_offs_scaled; + rtx plus_scaled; + rtx tmp; + rtx new_rtx; + + gcc_assert (reg != 0); + + if (GET_CODE (orig) == SYMBOL_REF + && SYMBOL_REF_FUNCTION_P (orig)) + unspec = UNSPEC_FDPIC_GOT_FUNCDESC; + else + unspec = UNSPEC_FDPIC_GOT; + + got_offs = gen_reg_rtx (SImode); + tmp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, orig), unspec); + emit_move_insn (got_offs, tmp); + + got_offs_scaled = gen_rtx_MULT (SImode, got_offs, GEN_INT (4)); + plus_scaled = gen_rtx_PLUS (Pmode, fdpic_reg, got_offs_scaled); + new_rtx = gen_const_mem (Pmode, plus_scaled); + emit_move_insn (reg, new_rtx); + + return reg; +} + +static rtx +ubicom32_legitimize_fdpic_address (rtx orig, rtx reg, rtx fdpic_reg) +{ + rtx addr = orig; + rtx new_rtx = orig; + + if (GET_CODE (addr) == CONST || GET_CODE (addr) == PLUS) + { + rtx base; + + if (GET_CODE (addr) == CONST) + { + addr = XEXP (addr, 0); + gcc_assert (GET_CODE (addr) == PLUS); + } + + base = ubicom32_legitimize_fdpic_address_symbol (XEXP (addr, 0), reg, fdpic_reg); + return gen_rtx_PLUS (Pmode, base, XEXP (addr, 1)); + } + + return new_rtx; +} + +/* Code generation. */ + +void +ubicom32_expand_movsi (rtx *operands) +{ + if (GET_CODE (operands[1]) == SYMBOL_REF + || (GET_CODE (operands[1]) == CONST + && GET_CODE (XEXP (operands[1], 0)) == PLUS + && GET_CODE (XEXP (XEXP (operands[1], 0), 0)) == SYMBOL_REF) + || CONSTANT_ADDRESS_P (operands[1])) + { + if (TARGET_FDPIC) + { + rtx tmp; + rtx fdpic_reg; + + gcc_assert (can_create_pseudo_p ()); + tmp = gen_reg_rtx (Pmode); + fdpic_reg = get_hard_reg_initial_val (SImode, FDPIC_REGNUM); + if (GET_CODE (operands[1]) == SYMBOL_REF + || GET_CODE (operands[1]) == LABEL_REF) + operands[1] = ubicom32_legitimize_fdpic_address_symbol (operands[1], tmp, fdpic_reg); + else + operands[1] = ubicom32_legitimize_fdpic_address (operands[1], tmp, fdpic_reg); + } + else + { + rtx tmp; + enum machine_mode mode; + + /* We want to avoid reusing operand 0 if we can because it limits + our ability to optimize later. */ + tmp = ! can_create_pseudo_p () ? operands[0] : gen_reg_rtx (Pmode); + + mode = GET_MODE (operands[0]); + emit_insn (gen_rtx_SET (VOIDmode, tmp, + gen_rtx_HIGH (mode, operands[1]))); + operands[1] = gen_rtx_LO_SUM (mode, tmp, operands[1]); + if (can_create_pseudo_p() && ! REG_P (operands[0])) + { + tmp = gen_reg_rtx (mode); + emit_insn (gen_rtx_SET (VOIDmode, tmp, operands[1])); + operands[1] = tmp; + } + } + } +} + +/* Emit code for addsi3. */ + +void +ubicom32_expand_addsi3 (rtx *operands) +{ + rtx op, clob; + + if (can_create_pseudo_p ()) + { + /* If we have a non-data reg for operand 1 then prefer that over + a CONST_INT in operand 2. */ + if (! ubicom32_data_register_operand (operands[1], GET_MODE (operands[1])) + && CONST_INT_P (operands[2])) + operands[2] = copy_to_mode_reg (SImode, operands[2]); + + if (CONST_INT_P (operands[2]) && ! satisfies_constraint_I (operands[2])) + operands[2] = copy_to_mode_reg (SImode, operands[2]); + } + + /* Emit the instruction. */ + + op = gen_rtx_SET (VOIDmode, operands[0], + gen_rtx_PLUS (SImode, operands[1], operands[2])); + + if (! can_create_pseudo_p ()) + { + /* Reload doesn't know about the flags register, and doesn't know that + it doesn't want to clobber it. We can only do this with PLUS. */ + emit_insn (op); + } + else + { + clob = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM)); + emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, op, clob))); + } +} + +/* Emit code for mulsi3. Return 1 if we have generated all the code + necessary to do the multiplication. */ + +int +ubicom32_emit_mult_sequence (rtx *operands) +{ + if (! ubicom32_v4) + { + rtx a1, a1_1, a2; + rtx b1, b1_1, b2; + rtx mac_lo_rtx; + rtx t1, t2, t3; + + /* Give up if we cannot create new pseudos. */ + if (!can_create_pseudo_p()) + return 0; + + /* Synthesize 32-bit multiplication using 16-bit operations: + + a1 = highpart (a) + a2 = lowpart (a) + + b1 = highpart (b) + b2 = lowpart (b) + + c = (a1 * b1) << 32 + (a1 * b2) << 16 + (a2 * b1) << 16 + a2 * b2 + = 0 + (a1 * b2) << 16 + (a2 * b1) << 16 + a2 * b2 + ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^ + Signed Signed Unsigned */ + + if (!ubicom32_data_register_operand (operands[1], GET_MODE (operands[1]))) + { + rtx op1; + + op1 = gen_reg_rtx (SImode); + emit_move_insn (op1, operands[1]); + operands[1] = op1; + } + + if (!ubicom32_data_register_operand (operands[2], GET_MODE (operands[2]))) + { + rtx op2; + + op2 = gen_reg_rtx (SImode); + emit_move_insn (op2, operands[2]); + operands[2] = op2; + } + + /* a1 = highpart (a) */ + a1 = gen_reg_rtx (HImode); + a1_1 = gen_reg_rtx (SImode); + emit_insn (gen_ashrsi3 (a1_1, operands[1], GEN_INT (16))); + emit_move_insn (a1, gen_lowpart (HImode, a1_1)); + + /* a2 = lowpart (a) */ + a2 = gen_reg_rtx (HImode); + emit_move_insn (a2, gen_lowpart (HImode, operands[1])); + + /* b1 = highpart (b) */ + b1 = gen_reg_rtx (HImode); + b1_1 = gen_reg_rtx (SImode); + emit_insn (gen_ashrsi3 (b1_1, operands[2], GEN_INT (16))); + emit_move_insn (b1, gen_lowpart (HImode, b1_1)); + + /* b2 = lowpart (b) */ + b2 = gen_reg_rtx (HImode); + emit_move_insn (b2, gen_lowpart (HImode, operands[2])); + + /* t1 = (a1 * b2) << 16 */ + t1 = gen_reg_rtx (SImode); + mac_lo_rtx = gen_rtx_REG (SImode, ACC0_LO_REGNUM); + emit_insn (gen_mulhisi3 (mac_lo_rtx, a1, b2)); + emit_insn (gen_ashlsi3 (t1, mac_lo_rtx, GEN_INT (16))); + + /* t2 = (a2 * b1) << 16 */ + t2 = gen_reg_rtx (SImode); + emit_insn (gen_mulhisi3 (mac_lo_rtx, a2, b1)); + emit_insn (gen_ashlsi3 (t2, mac_lo_rtx, GEN_INT (16))); + + /* mac_lo = a2 * b2 */ + emit_insn (gen_umulhisi3 (mac_lo_rtx, a2, b2)); + + /* t3 = t1 + t2 */ + t3 = gen_reg_rtx (SImode); + emit_insn (gen_addsi3 (t3, t1, t2)); + + /* c = t3 + mac_lo_rtx */ + emit_insn (gen_addsi3 (operands[0], mac_lo_rtx, t3)); + + return 1; + } + else + { + rtx acc_rtx; + + /* Give up if we cannot create new pseudos. */ + if (!can_create_pseudo_p()) + return 0; + + if (!ubicom32_data_register_operand (operands[1], GET_MODE (operands[1]))) + { + rtx op1; + + op1 = gen_reg_rtx (SImode); + emit_move_insn (op1, operands[1]); + operands[1] = op1; + } + + if (!ubicom32_data_register_operand (operands[2], GET_MODE (operands[2]))) + { + rtx op2; + + op2 = gen_reg_rtx (SImode); + emit_move_insn (op2, operands[2]); + operands[2] = op2; + } + + acc_rtx = gen_reg_rtx (DImode); + emit_insn (gen_umulsidi3 (acc_rtx, operands[1], operands[2])); + emit_move_insn (operands[0], gen_lowpart (SImode, acc_rtx)); + + return 1; + } +} + +/* Move the integer value VAL into OPERANDS[0]. */ + +void +ubicom32_emit_move_const_int (rtx dest, rtx imm) +{ + rtx xoperands[2]; + + xoperands[0] = dest; + xoperands[1] = imm; + + /* Treat mem destinations separately. Values must be explicitly sign + extended. */ + if (MEM_P (dest)) + { + rtx low_hword_mem; + rtx low_hword_addr; + + /* Emit shorter sequence for signed 7-bit quantities. */ + if (satisfies_constraint_I (imm)) + { + output_asm_insn ("move.4\t%0, %1", xoperands); + return; + } + + /* Special case for pushing constants. */ + if (GET_CODE (XEXP (dest, 0)) == PRE_DEC + && XEXP (XEXP (dest, 0), 0) == stack_pointer_rtx) + { + output_asm_insn ("movei\t-4(sp)++, #%%hi(%E1)", xoperands); + output_asm_insn ("movei\t2(sp), #%%lo(%E1)", xoperands); + return; + } + + /* See if we can add 2 to the original address. This is only + possible if the original address is of the form REG or + REG+const. */ + low_hword_addr = plus_constant (XEXP (dest, 0), 2); + if (ubicom32_legitimate_address_p (HImode, low_hword_addr, 1)) + { + low_hword_mem = gen_rtx_MEM (HImode, low_hword_addr); + MEM_COPY_ATTRIBUTES (low_hword_mem, dest); + output_asm_insn ("movei\t%0, #%%hi(%E1)", xoperands); + xoperands[0] = low_hword_mem; + output_asm_insn ("movei\t%0, #%%lo(%E1)", xoperands); + return; + } + + /* The original address is too complex. We need to use a + scratch memory by (sp) and move that to the original + destination. */ + if (! reg_mentioned_p (stack_pointer_rtx, dest)) + { + output_asm_insn ("movei\t-4(sp)++, #%%hi(%E1)", xoperands); + output_asm_insn ("movei\t2(sp), #%%lo(%E1)", xoperands); + output_asm_insn ("move.4\t%0, (sp)4++", xoperands); + return; + } + + /* Our address mentions the stack pointer so we need to + use our scratch data register here as well as scratch + memory. */ + output_asm_insn ("movei\t-4(sp)++, #%%hi(%E1)", xoperands); + output_asm_insn ("movei\t2(sp), #%%lo(%E1)", xoperands); + output_asm_insn ("move.4\td15, (sp)4++", xoperands); + output_asm_insn ("move.4\t%0, d15", xoperands); + return; + } + + /* Move into registers are zero extended by default. */ + if (! REG_P (dest)) + abort (); + + if (satisfies_constraint_N (imm)) + { + output_asm_insn ("movei\t%0, %1", xoperands); + return; + } + + if (INTVAL (xoperands[1]) >= 0xff80 + && INTVAL (xoperands[1]) < 0x10000) + { + xoperands[1] = GEN_INT (INTVAL (xoperands[1]) - 0x10000); + output_asm_insn ("move.2\t%0, %1", xoperands); + return; + } + + if ((REGNO_REG_CLASS (REGNO (xoperands[0])) == ADDRESS_REGS + || REGNO_REG_CLASS (REGNO (xoperands[0])) == FDPIC_REG) + && ((INTVAL (xoperands[1]) & 0x80000000) == 0)) + { + output_asm_insn ("moveai\t%0, #%%hi(%E1)", xoperands); + if ((INTVAL (xoperands[1]) & 0x7f) != 0) + output_asm_insn ("lea.1\t%0, %%lo(%E1)(%0)", xoperands); + return; + } + + if ((INTVAL (xoperands[1]) & 0xffff0000) == 0) + { + output_asm_insn ("movei\t%0, #%%lo(%E1)", xoperands); + output_asm_insn ("move.2\t%0, %0", xoperands); + return; + } + + /* This is very expensive. The constant is so large that we + need to use the stack to do the load. */ + output_asm_insn ("movei\t-4(sp)++, #%%hi(%E1)", xoperands); + output_asm_insn ("movei\t2(sp), #%%lo(%E1)", xoperands); + output_asm_insn ("move.4\t%0, (sp)4++", xoperands); +} + +/* Stack layout. Prologue/Epilogue. */ + +static int save_regs_size; + +static void +ubicom32_layout_frame (void) +{ + int regno; + + memset ((char *) &save_regs[0], 0, sizeof (save_regs)); + nregs = 0; + frame_size = get_frame_size (); + + if (frame_pointer_needed || df_regs_ever_live_p (FRAME_POINTER_REGNUM)) + { + save_regs[FRAME_POINTER_REGNUM] = 1; + ++nregs; + } + + if (current_function_is_leaf && ! df_regs_ever_live_p (LINK_REGNO)) + ubicom32_can_use_calli_to_ret = 1; + else + { + ubicom32_can_use_calli_to_ret = 0; + save_regs[LINK_REGNO] = 1; + ++nregs; + } + + /* Figure out which register(s) needs to be saved. */ + for (regno = 0; regno <= LAST_ADDRESS_REGNUM; regno++) + if (df_regs_ever_live_p(regno) + && ! call_used_regs[regno] + && ! fixed_regs[regno] + && ! save_regs[regno]) + { + save_regs[regno] = 1; + ++nregs; + } + + save_regs_size = 4 * nregs; +} + +static void +ubicom32_emit_add_movsi (int regno, int adj) +{ + rtx x; + rtx reg = gen_rtx_REG (SImode, regno); + + adj += 4; + if (adj > 8 * 4) + { + x = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + GEN_INT (-adj))); + RTX_FRAME_RELATED_P (x) = 1; + x = emit_move_insn (gen_rtx_MEM (SImode, stack_pointer_rtx), reg); + } + else + { + rtx addr = gen_rtx_PRE_MODIFY (Pmode, stack_pointer_rtx, + gen_rtx_PLUS (Pmode, stack_pointer_rtx, + GEN_INT (-adj))); + x = emit_move_insn (gen_rtx_MEM (SImode, addr), reg); + } + RTX_FRAME_RELATED_P (x) = 1; +} + +void +ubicom32_expand_prologue (void) +{ + rtx x; + int regno; + int outgoing_args_size = crtl->outgoing_args_size; + int adj; + + if (ubicom32_naked_function_p ()) + return; + + ubicom32_builtin_saveregs (); + + ubicom32_layout_frame (); + adj = (outgoing_args_size + get_frame_size () + save_regs_size + + crtl->args.pretend_args_size); + + if (!adj) + ; + else if (outgoing_args_size + save_regs_size < 508 + && get_frame_size () + save_regs_size > 508) + { + int i = 0; + x = gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + GEN_INT (-adj)); + x = emit_insn (x); + RTX_FRAME_RELATED_P (x) = 1; + + for (regno = LAST_ADDRESS_REGNUM; regno >= 0; --regno) + if (save_regs[regno] && regno != LINK_REGNO) + { + x = gen_rtx_MEM (SImode, + gen_rtx_PLUS (Pmode, + stack_pointer_rtx, + GEN_INT (i * 4 + outgoing_args_size))); + x = emit_move_insn (x, gen_rtx_REG (SImode, regno)); + RTX_FRAME_RELATED_P (x) = 1; + ++i; + } + if (save_regs[LINK_REGNO]) + { + x = gen_rtx_MEM (SImode, + gen_rtx_PLUS (Pmode, + stack_pointer_rtx, + GEN_INT (i * 4 + outgoing_args_size))); + x = emit_move_insn (x, gen_rtx_REG (SImode, LINK_REGNO)); + RTX_FRAME_RELATED_P (x) = 1; + } + } + else + { + int regno; + int adj = get_frame_size () + crtl->args.pretend_args_size; + int i = 0; + + if (save_regs[LINK_REGNO]) + { + ubicom32_emit_add_movsi (LINK_REGNO, adj); + ++i; + } + + for (regno = 0; regno <= LAST_ADDRESS_REGNUM; ++regno) + if (save_regs[regno] && regno != LINK_REGNO) + { + if (i) + { + rtx mem = gen_rtx_MEM (SImode, + gen_rtx_PRE_DEC (Pmode, + stack_pointer_rtx)); + x = emit_move_insn (mem, gen_rtx_REG (SImode, regno)); + RTX_FRAME_RELATED_P (x) = 1; + } + else + ubicom32_emit_add_movsi (regno, adj); + ++i; + } + + if (outgoing_args_size || (!i && adj)) + { + x = gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + GEN_INT (-outgoing_args_size - (i ? 0 : adj))); + x = emit_insn (x); + RTX_FRAME_RELATED_P (x) = 1; + } + } + + if (frame_pointer_needed) + { + int fp_adj = save_regs_size + outgoing_args_size; + x = gen_addsi3 (frame_pointer_rtx, stack_pointer_rtx, + GEN_INT (fp_adj)); + x = emit_insn (x); + RTX_FRAME_RELATED_P (x) = 1; + } +} + +void +ubicom32_expand_epilogue (void) +{ + rtx x; + int regno; + int outgoing_args_size = crtl->outgoing_args_size; + int adj; + int i; + + if (ubicom32_naked_function_p ()) + { + emit_jump_insn (gen_return_internal (gen_rtx_REG (SImode, + LINK_REGNO))); + return; + } + + if (cfun->calls_alloca) + { + x = gen_addsi3 (stack_pointer_rtx, frame_pointer_rtx, + GEN_INT (-save_regs_size)); + emit_insn (x); + outgoing_args_size = 0; + } + + if (outgoing_args_size) + { + x = gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + GEN_INT (outgoing_args_size)); + emit_insn (x); + } + + i = 0; + for (regno = LAST_ADDRESS_REGNUM; regno >= 0; --regno) + if (save_regs[regno] && regno != LINK_REGNO) + { + x = gen_rtx_MEM (SImode, gen_rtx_POST_INC (Pmode, stack_pointer_rtx)); + emit_move_insn (gen_rtx_REG (SImode, regno), x); + ++i; + } + + /* Do we have to adjust the stack after we've finished restoring regs? */ + adj = get_frame_size() + crtl->args.pretend_args_size; + if (cfun->stdarg) + adj += UBICOM32_FUNCTION_ARG_REGS * UNITS_PER_WORD; + +#if 0 + if (crtl->calls_eh_return && 0) + { + if (save_regs[LINK_REGNO]) + { + x = gen_rtx_MEM (SImode, gen_rtx_POST_INC (Pmode, stack_pointer_rtx)); + emit_move_insn (gen_rtx_REG (SImode, LINK_REGNO), x); + } + + if (adj) + { + x = gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + GEN_INT (adj)); + x = emit_insn (x); + } + + /* Perform the additional bump for __throw. */ + emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + EH_RETURN_STACKADJ_RTX)); + emit_jump_insn (gen_eh_return_internal ()); + return; + } +#endif + + if (save_regs[LINK_REGNO]) + { + if (adj >= 4 && adj <= (6 * 4)) + { + x = GEN_INT (adj + 4); + emit_jump_insn (gen_return_from_post_modify_sp (x)); + return; + } + + if (adj == 0) + { + x = gen_rtx_MEM (SImode, gen_rtx_POST_INC (Pmode, stack_pointer_rtx)); + emit_jump_insn (gen_return_internal (x)); + return; + } + + x = gen_rtx_MEM (SImode, gen_rtx_POST_INC (Pmode, stack_pointer_rtx)); + emit_move_insn (gen_rtx_REG (SImode, LINK_REGNO), x); + } + + if (adj) + { + x = gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + GEN_INT (adj)); + x = emit_insn (x); + adj = 0; + } + + /* Given that we've just done all the hard work here we may as well use + a calli to return. */ + ubicom32_can_use_calli_to_ret = 1; + emit_jump_insn (gen_return_internal (gen_rtx_REG (SImode, LINK_REGNO))); +} + +void +ubicom32_expand_call_fdpic (rtx *operands) +{ + rtx c; + rtx addr; + rtx fdpic_reg = get_hard_reg_initial_val (SImode, FDPIC_REGNUM); + + addr = XEXP (operands[0], 0); + + c = gen_call_fdpic (addr, operands[1], fdpic_reg); + emit_call_insn (c); +} + +void +ubicom32_expand_call_value_fdpic (rtx *operands) +{ + rtx c; + rtx addr; + rtx fdpic_reg = get_hard_reg_initial_val (SImode, FDPIC_REGNUM); + + addr = XEXP (operands[1], 0); + + c = gen_call_value_fdpic (operands[0], addr, operands[2], fdpic_reg); + emit_call_insn (c); +} + +void +ubicom32_expand_eh_return (rtx *operands) +{ + if (REG_P (operands[0]) + || REGNO (operands[0]) != EH_RETURN_STACKADJ_REGNO) + { + rtx sp = EH_RETURN_STACKADJ_RTX; + emit_move_insn (sp, operands[0]); + operands[0] = sp; + } + + if (REG_P (operands[1]) + || REGNO (operands[1]) != EH_RETURN_HANDLER_REGNO) + { + rtx ra = EH_RETURN_HANDLER_RTX; + emit_move_insn (ra, operands[1]); + operands[1] = ra; + } +} + +/* Compute the offsets between eliminable registers. */ + +int +ubicom32_initial_elimination_offset (int from, int to) +{ + ubicom32_layout_frame (); + if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM) + return save_regs_size + crtl->outgoing_args_size; + + if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM) + return get_frame_size ()/* + save_regs_size */; + + if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM) + return get_frame_size () + + crtl->outgoing_args_size + + save_regs_size; + + return 0; +} + +/* Return 1 if it is appropriate to emit `ret' instructions in the + body of a function. Do this only if the epilogue is simple, needing a + couple of insns. Prior to reloading, we can't tell how many registers + must be saved, so return 0 then. Return 0 if there is no frame + marker to de-allocate. + + If NON_SAVING_SETJMP is defined and true, then it is not possible + for the epilogue to be simple, so return 0. This is a special case + since NON_SAVING_SETJMP will not cause regs_ever_live to change + until final, but jump_optimize may need to know sooner if a + `return' is OK. */ + +int +ubicom32_can_use_return_insn_p (void) +{ + if (! reload_completed || frame_pointer_needed) + return 0; + + return 1; +} + +/* Attributes and CC handling. */ + +/* Handle an attribute requiring a FUNCTION_DECL; arguments as in + struct attribute_spec.handler. */ +static tree +ubicom32_handle_fndecl_attribute (tree *node, tree name, + tree args ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED, + bool *no_add_attrs) +{ + if (TREE_CODE (*node) != FUNCTION_DECL) + { + warning ("'%s' attribute only applies to functions", + IDENTIFIER_POINTER (name)); + *no_add_attrs = true; + } + + return NULL_TREE; +} + +/* 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 + + Sometimes returning a more restrictive class makes better code. For + example, on the 68000, when X is an integer constant that is in range for a + `moveq' instruction, the value of this macro is always `DATA_REGS' as long + as CLASS includes the data registers. Requiring a data register guarantees + that a `moveq' will be used. + + If X is a `const_double', by returning `NO_REGS' you can force X into a + memory constant. This is useful on certain machines where immediate + floating values cannot be loaded into certain kinds of registers. */ + +enum reg_class +ubicom32_preferred_reload_class (rtx x, enum reg_class class) +{ + /* If a symbolic constant, HIGH or a PLUS is reloaded, + it is most likely being used as an address, so + prefer ADDRESS_REGS. If 'class' is not a superset + of ADDRESS_REGS, e.g. DATA_REGS, then reject this reload. */ + if (GET_CODE (x) == PLUS + || GET_CODE (x) == HIGH + || GET_CODE (x) == LABEL_REF + || GET_CODE (x) == SYMBOL_REF + || GET_CODE (x) == CONST) + { + if (reg_class_subset_p (ALL_ADDRESS_REGS, class)) + return ALL_ADDRESS_REGS; + + return NO_REGS; + } + + return class; +} + +/* Function arguments and varargs. */ + +int +ubicom32_reg_parm_stack_space (tree fndecl) +{ + return 0; + + if (fndecl + && TYPE_ARG_TYPES (TREE_TYPE (fndecl)) != 0 + && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (TREE_TYPE (fndecl)))) + != void_type_node)) + return UBICOM32_FUNCTION_ARG_REGS * UNITS_PER_WORD; + + return 0; +} + +/* Flush the argument registers to the stack for a stdarg function; + return the new argument pointer. */ + +rtx +ubicom32_builtin_saveregs (void) +{ + int regno; + + if (! cfun->stdarg) + return 0; + + for (regno = UBICOM32_FUNCTION_ARG_REGS - 1; regno >= 0; --regno) + emit_move_insn (gen_rtx_MEM (SImode, + gen_rtx_PRE_DEC (SImode, + stack_pointer_rtx)), + gen_rtx_REG (SImode, regno)); + + return stack_pointer_rtx; +} + +void +ubicom32_va_start (tree valist, rtx nextarg) +{ + std_expand_builtin_va_start (valist, nextarg); +} + +rtx +ubicom32_va_arg (tree valist, tree type) +{ + HOST_WIDE_INT size, rsize; + tree addr, incr, tmp; + rtx addr_rtx; + int indirect = 0; + + /* Round up sizeof(type) to a word. */ + size = int_size_in_bytes (type); + rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD; + + /* Large types are passed by reference. */ + if (size > 8) + { + indirect = 1; + size = rsize = UNITS_PER_WORD; + } + + incr = valist; + addr = incr = save_expr (incr); + + /* FIXME Nat's version - is it correct? */ + tmp = fold_convert (ptr_type_node, size_int (rsize)); + tmp = build2 (PLUS_EXPR, ptr_type_node, incr, tmp); + incr = fold (tmp); + + /* FIXME Nat's version - is it correct? */ + incr = build2 (MODIFY_EXPR, ptr_type_node, valist, incr); + + TREE_SIDE_EFFECTS (incr) = 1; + expand_expr (incr, const0_rtx, VOIDmode, EXPAND_NORMAL); + + addr_rtx = expand_expr (addr, NULL, Pmode, EXPAND_NORMAL); + + if (size < UNITS_PER_WORD) + emit_insn (gen_addsi3 (addr_rtx, addr_rtx, + GEN_INT (UNITS_PER_WORD - size))); + + if (indirect) + { + addr_rtx = force_reg (Pmode, addr_rtx); + addr_rtx = gen_rtx_MEM (Pmode, addr_rtx); + set_mem_alias_set (addr_rtx, get_varargs_alias_set ()); + } + + return addr_rtx; +} + +void +init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype, rtx libname, + int indirect ATTRIBUTE_UNUSED) +{ + cum->nbytes = 0; + + if (!libname) + { + cum->stdarg = (TYPE_ARG_TYPES (fntype) != 0 + && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) + != void_type_node)); + } +} + +/* Return an RTX to represent where a value in mode MODE will be passed + to a function. If the result is 0, the argument will be pushed. */ + +rtx +function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type, + int named ATTRIBUTE_UNUSED) +{ + rtx result = 0; + int size, align; + int nregs = UBICOM32_FUNCTION_ARG_REGS; + + /* Figure out the size of the object to be passed. */ + if (mode == BLKmode) + size = int_size_in_bytes (type); + else + size = GET_MODE_SIZE (mode); + + /* Figure out the alignment of the object to be passed. */ + align = size; + + cum->nbytes = (cum->nbytes + 3) & ~3; + + /* Don't pass this arg via a register if all the argument registers + are used up. */ + if (cum->nbytes >= nregs * UNITS_PER_WORD) + return 0; + + /* Don't pass this arg via a register if it would be split between + registers and memory. */ + result = gen_rtx_REG (mode, cum->nbytes / UNITS_PER_WORD); + + return result; +} + +rtx +function_incoming_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type, + int named ATTRIBUTE_UNUSED) +{ + if (cfun->stdarg) + return 0; + + return function_arg (cum, mode, type, named); +} + + +/* 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 +ubicom32_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode, + tree type, bool named ATTRIBUTE_UNUSED) +{ + int size, diff; + + int nregs = UBICOM32_FUNCTION_ARG_REGS; + + /* round up to full word */ + cum->nbytes = (cum->nbytes + 3) & ~3; + + if (targetm.calls.pass_by_reference (cum, mode, type, named)) + return 0; + + /* number of bytes left in registers */ + diff = nregs*UNITS_PER_WORD - cum->nbytes; + + /* regs all used up */ + if (diff <= 0) + return 0; + + /* Figure out the size of the object to be passed. */ + if (mode == BLKmode) + size = int_size_in_bytes (type); + else + size = GET_MODE_SIZE (mode); + + /* enough space left in regs for size */ + if (size <= diff) + return 0; + + /* put diff bytes in regs and rest on stack */ + return diff; + +} + +static bool +ubicom32_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED, + enum machine_mode mode, const_tree type, + bool named ATTRIBUTE_UNUSED) +{ + int size; + + if (type) + size = int_size_in_bytes (type); + else + size = GET_MODE_SIZE (mode); + + return size <= 0 || size > 8; +} + +static bool +ubicom32_callee_copies (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED, + enum machine_mode mode, const_tree type, + bool named ATTRIBUTE_UNUSED) +{ + int size; + + if (type) + size = int_size_in_bytes (type); + else + size = GET_MODE_SIZE (mode); + + return size <= 0 || size > 8; +} + +static bool +ubicom32_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED) +{ + int size, mode; + + if (!type) + return true; + + size = int_size_in_bytes(type); + if (size > 8) + return true; + + mode = TYPE_MODE(type); + if (mode == BLKmode) + return true; + + return false; +} + +/* Return true if a given register number REGNO is acceptable for machine + mode MODE. */ +bool +ubicom32_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode) +{ + /* If we're not at least a v3 ISA then ACC0_HI is only 16 bits. */ + if (! ubicom32_v3) + { + if (regno == ACC0_HI_REGNUM) + return (mode == QImode || mode == HImode); + } + + /* Only the flags reg can hold CCmode. */ + if (GET_MODE_CLASS (mode) == MODE_CC) + return regno == CC_REGNUM; + + /* We restrict the choice of DImode registers to only being address, + data or accumulator regs. We also restrict them to only start on + even register numbers so we never have to worry about partial + overlaps between operands in instructions. */ + if (GET_MODE_SIZE (mode) > 4) + { + switch (REGNO_REG_CLASS (regno)) + { + case ADDRESS_REGS: + case DATA_REGS: + case ACC_REGS: + return (regno & 1) == 0; + + default: + return false; + } + } + + return true; +} + +/* The macros REG_OK_FOR..._P assume that the arg is a REG rtx + and check its validity for a certain class. + We have two alternate definitions for each of them. + The usual definition accepts all pseudo regs; the other rejects + them unless they have been allocated suitable hard regs. + The symbol REG_OK_STRICT causes the latter definition to be used. + + Most source files want to accept pseudo regs in the hope that + they will get allocated to the class that the insn wants them to be in. + Source files for reload pass need to be strict. + After reload, it makes no difference, since pseudo regs have + been eliminated by then. + + These assume that REGNO is a hard or pseudo reg number. + They give nonzero only if REGNO is a hard reg of the suitable class + or a pseudo reg currently allocated to a suitable hard reg. + Since they use reg_renumber, they are safe only once reg_renumber + has been allocated, which happens in local-alloc.c. */ + +int +ubicom32_regno_ok_for_base_p (int regno, int strict) +{ + if ((regno >= FIRST_ADDRESS_REGNUM && regno <= STACK_POINTER_REGNUM) + || (!strict + && (regno >= FIRST_PSEUDO_REGISTER + || regno == ARG_POINTER_REGNUM)) + || (strict && (reg_renumber + && reg_renumber[regno] >= FIRST_ADDRESS_REGNUM + && reg_renumber[regno] <= STACK_POINTER_REGNUM))) + return 1; + + return 0; +} + +int +ubicom32_regno_ok_for_index_p (int regno, int strict) +{ + if ((regno >= FIRST_DATA_REGNUM && regno <= LAST_DATA_REGNUM) + || (!strict && regno >= FIRST_PSEUDO_REGISTER) + || (strict && (reg_renumber + && reg_renumber[regno] >= FIRST_DATA_REGNUM + && reg_renumber[regno] <= LAST_DATA_REGNUM))) + return 1; + + return 0; +} + +/* Returns 1 if X is a valid index register. STRICT is 1 if only hard + registers should be accepted. Accept either REG or SUBREG where a + register is valid. */ + +static bool +ubicom32_is_index_reg (rtx x, int strict) +{ + if ((REG_P (x) && ubicom32_regno_ok_for_index_p (REGNO (x), strict)) + || (GET_CODE (x) == SUBREG && REG_P (SUBREG_REG (x)) + && ubicom32_regno_ok_for_index_p (REGNO (SUBREG_REG (x)), strict))) + return true; + + return false; +} + +/* Return 1 if X is a valid index for a memory address. */ + +static bool +ubicom32_is_index_expr (enum machine_mode mode, rtx x, int strict) +{ + /* Immediate index must be an unsigned 7-bit offset multiple of 1, 2 + or 4 depending on mode. */ + if (CONST_INT_P (x)) + { + switch (mode) + { + case QImode: + return satisfies_constraint_J (x); + + case HImode: + return satisfies_constraint_K (x); + + case SImode: + case SFmode: + return satisfies_constraint_L (x); + + case DImode: + return satisfies_constraint_L (x) + && satisfies_constraint_L (GEN_INT (INTVAL (x) + 4)); + + default: + return false; + } + } + + if (mode != SImode && mode != HImode && mode != QImode) + return false; + + /* Register index scaled by mode of operand: REG + REG * modesize. + Valid scaled index registers are: + + SImode (mult (dreg) 4)) + HImode (mult (dreg) 2)) + QImode (mult (dreg) 1)) */ + if (GET_CODE (x) == MULT + && ubicom32_is_index_reg (XEXP (x, 0), strict) + && CONST_INT_P (XEXP (x, 1)) + && INTVAL (XEXP (x, 1)) == (HOST_WIDE_INT)GET_MODE_SIZE (mode)) + return true; + + /* REG + REG addressing is allowed for QImode. */ + if (ubicom32_is_index_reg (x, strict) && mode == QImode) + return true; + + return false; +} + +static bool +ubicom32_is_valid_offset (enum machine_mode mode, HOST_WIDE_INT offs) +{ + if (offs < 0) + return false; + + switch (mode) + { + case QImode: + return offs <= 127; + + case HImode: + return offs <= 254; + + case SImode: + case SFmode: + return offs <= 508; + + case DImode: + return offs <= 504; + + default: + return false; + } +} + +static int +ubicom32_get_valid_offset_mask (enum machine_mode mode) +{ + switch (mode) + { + case QImode: + return 127; + + case HImode: + return 255; + + case SImode: + case SFmode: + return 511; + + case DImode: + return 255; + + default: + return 0; + } +} + +/* Returns 1 if X is a valid base register. STRICT is 1 if only hard + registers should be accepted. Accept either REG or SUBREG where a + register is valid. */ + +static bool +ubicom32_is_base_reg (rtx x, int strict) +{ + if ((REG_P (x) && ubicom32_regno_ok_for_base_p (REGNO (x), strict)) + || (GET_CODE (x) == SUBREG && REG_P (SUBREG_REG (x)) + && ubicom32_regno_ok_for_base_p (REGNO (SUBREG_REG (x)), strict))) + return true; + + return false; +} + +static bool +ubicom32_cannot_force_const_mem (rtx x ATTRIBUTE_UNUSED) +{ + return TARGET_FDPIC; +} + +/* Determine if X is a legitimate constant. */ + +bool +ubicom32_legitimate_constant_p (rtx x) +{ + /* Among its other duties, LEGITIMATE_CONSTANT_P decides whether + a constant can be entered into reg_equiv_constant[]. If we return true, + reload can create new instances of the constant whenever it likes. + + The idea is therefore to accept as many constants as possible (to give + reload more freedom) while rejecting constants that can only be created + at certain times. In particular, anything with a symbolic component will + require use of the pseudo FDPIC register, which is only available before + reload. */ + if (TARGET_FDPIC) + { + if (GET_CODE (x) == SYMBOL_REF + || (GET_CODE (x) == CONST + && GET_CODE (XEXP (x, 0)) == PLUS + && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF) + || CONSTANT_ADDRESS_P (x)) + return false; + + return true; + } + + /* For non-PIC code anything goes! */ + return true; +} + +/* Address validation. */ + +bool +ubicom32_legitimate_address_p (enum machine_mode mode, rtx x, int strict) +{ + if (TARGET_DEBUG_ADDRESS) + { + fprintf (stderr, "\n==> GO_IF_LEGITIMATE_ADDRESS%s\n", + (strict) ? " (STRICT)" : ""); + debug_rtx (x); + } + + if (CONSTANT_ADDRESS_P (x)) + return false; + + if (ubicom32_is_base_reg (x, strict)) + return true; + + if ((GET_CODE (x) == POST_INC + || GET_CODE (x) == PRE_INC + || GET_CODE (x) == POST_DEC + || GET_CODE (x) == PRE_DEC) + && REG_P (XEXP (x, 0)) + && ubicom32_is_base_reg (XEXP (x, 0), strict) + && mode != DImode) + return true; + + if ((GET_CODE (x) == PRE_MODIFY || GET_CODE (x) == POST_MODIFY) + && ubicom32_is_base_reg (XEXP (x, 0), strict) + && GET_CODE (XEXP (x, 1)) == PLUS + && rtx_equal_p (XEXP (x, 0), XEXP (XEXP (x, 1), 0)) + && CONST_INT_P (XEXP (XEXP (x, 1), 1)) + && mode != DImode) + { + HOST_WIDE_INT disp = INTVAL (XEXP (XEXP (x, 1), 1)); + switch (mode) + { + case QImode: + return disp >= -8 && disp <= 7; + + case HImode: + return disp >= -16 && disp <= 14 && ! (disp & 1); + + case SImode: + return disp >= -32 && disp <= 28 && ! (disp & 3); + + default: + return false; + } + } + + /* Accept base + index * scale. */ + if (GET_CODE (x) == PLUS + && ubicom32_is_base_reg (XEXP (x, 0), strict) + && ubicom32_is_index_expr (mode, XEXP (x, 1), strict)) + return true; + + /* Accept index * scale + base. */ + if (GET_CODE (x) == PLUS + && ubicom32_is_base_reg (XEXP (x, 1), strict) + && ubicom32_is_index_expr (mode, XEXP (x, 0), strict)) + return true; + + if (! TARGET_FDPIC) + { + /* Accept (lo_sum (reg) (symbol_ref)) that can be used as a mem+7bits + displacement operand: + + moveai a1, #%hi(SYM) + move.4 d3, %lo(SYM)(a1) */ + if (GET_CODE (x) == LO_SUM + && ubicom32_is_base_reg (XEXP (x, 0), strict) + && (GET_CODE (XEXP (x, 1)) == SYMBOL_REF + || GET_CODE (XEXP (x, 1)) == LABEL_REF /* FIXME: wrong */) + && mode != DImode) + return true; + } + + if (TARGET_DEBUG_ADDRESS) + fprintf (stderr, "\nNot a legitimate address.\n"); + + return false; +} + +rtx +ubicom32_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED, + enum machine_mode mode) +{ + if (mode == BLKmode) + return NULL_RTX; + + if (GET_CODE (x) == PLUS + && REG_P (XEXP (x, 0)) + && ! REGNO_PTR_FRAME_P (REGNO (XEXP (x, 0))) + && CONST_INT_P (XEXP (x, 1)) + && ! ubicom32_is_valid_offset (mode, INTVAL (XEXP (x, 1)))) + { + rtx base; + rtx plus; + rtx new_rtx; + HOST_WIDE_INT val = INTVAL (XEXP (x, 1)); + HOST_WIDE_INT low = val & ubicom32_get_valid_offset_mask (mode); + HOST_WIDE_INT high = val ^ low; + + if (val < 0) + return NULL_RTX; + + if (! low) + return NULL_RTX; + + /* Reload the high part into a base reg; leave the low part + in the mem directly. */ + base = XEXP (x, 0); + if (! ubicom32_is_base_reg (base, 0)) + base = copy_to_mode_reg (Pmode, base); + + plus = expand_simple_binop (Pmode, PLUS, + gen_int_mode (high, Pmode), + base, NULL, 0, OPTAB_WIDEN); + new_rtx = plus_constant (plus, low); + + return new_rtx; + } + + return NULL_RTX; +} + +/* Try a machine-dependent way of reloading an illegitimate address AD + operand. If we find one, push the reload and and return the new address. + + MODE is the mode of the enclosing MEM. OPNUM is the operand number + and TYPE is the reload type of the current reload. */ + +rtx +ubicom32_legitimize_reload_address (rtx ad, enum machine_mode mode, + int opnum, int type) +{ + /* Is this an address that we've already fixed up? If it is then + recognize it and move on. */ + if (GET_CODE (ad) == PLUS + && GET_CODE (XEXP (ad, 0)) == PLUS + && REG_P (XEXP (XEXP (ad, 0), 0)) + && CONST_INT_P (XEXP (XEXP (ad, 0), 1)) + && CONST_INT_P (XEXP (ad, 1))) + { + push_reload (XEXP (ad, 0), NULL_RTX, &XEXP (ad, 0), NULL, + BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, + opnum, (enum reload_type) type); + return ad; + } + + /* Have we got an address where the offset is simply out of range? If + yes then reload the range as a high part and smaller offset. */ + if (GET_CODE (ad) == PLUS + && REG_P (XEXP (ad, 0)) + && REGNO (XEXP (ad, 0)) < FIRST_PSEUDO_REGISTER + && REGNO_OK_FOR_BASE_P (REGNO (XEXP (ad, 0))) + && CONST_INT_P (XEXP (ad, 1)) + && ! ubicom32_is_valid_offset (mode, INTVAL (XEXP (ad, 1)))) + { + rtx temp; + rtx new_rtx; + + HOST_WIDE_INT val = INTVAL (XEXP (ad, 1)); + HOST_WIDE_INT low = val & ubicom32_get_valid_offset_mask (mode); + HOST_WIDE_INT high = val ^ low; + + /* Reload the high part into a base reg; leave the low part + in the mem directly. */ + temp = gen_rtx_PLUS (Pmode, XEXP (ad, 0), GEN_INT (high)); + new_rtx = gen_rtx_PLUS (Pmode, temp, GEN_INT (low)); + + push_reload (XEXP (new_rtx, 0), NULL_RTX, &XEXP (new_rtx, 0), NULL, + BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, + opnum, (enum reload_type) type); + return new_rtx; + } + + /* If we're presented with an pre/post inc/dec then we must force this + to be done in an address register. The register allocator should + work this out for itself but at times ends up trying to use the wrong + class. If we get the wrong class then reload will end up generating + at least 3 instructions whereas this way we can hopefully keep it to + just 2. */ + if ((GET_CODE (ad) == POST_INC + || GET_CODE (ad) == PRE_INC + || GET_CODE (ad) == POST_DEC + || GET_CODE (ad) == PRE_DEC) + && REG_P (XEXP (ad, 0)) + && REGNO (XEXP (ad, 0)) < FIRST_PSEUDO_REGISTER + && ! REGNO_OK_FOR_BASE_P (REGNO (XEXP (ad, 0)))) + { + push_reload (XEXP (ad, 0), XEXP (ad, 0), &XEXP (ad, 0), &XEXP (ad, 0), + BASE_REG_CLASS, GET_MODE (XEXP (ad, 0)), GET_MODE (XEXP (ad, 0)), 0, 0, + opnum, RELOAD_OTHER); + return ad; + } + + return NULL_RTX; +} + +/* Compute a (partial) cost for rtx X. Return true if the complete + cost has been computed, and false if subexpressions should be + scanned. In either case, *TOTAL contains the cost result. */ + +static bool +ubicom32_rtx_costs (rtx x, int code, int outer_code, int *total, + bool speed ATTRIBUTE_UNUSED) +{ + enum machine_mode mode = GET_MODE (x); + + switch (code) + { + case CONST_INT: + /* Very short constants often fold into instructions so + we pretend that they don't cost anything! This is + really important as regards zero values as otherwise + the compiler has a nasty habit of wanting to reuse + zeroes that are in regs but that tends to pessimize + the code. */ + if (satisfies_constraint_I (x)) + { + *total = 0; + return true; + } + + /* Bit clearing costs nothing */ + if (outer_code == AND + && exact_log2 (~INTVAL (x)) != -1) + { + *total = 0; + return true; + } + + /* Masking the lower set of bits costs nothing. */ + if (outer_code == AND + && exact_log2 (INTVAL (x) + 1) != -1) + { + *total = 0; + return true; + } + + /* Bit setting costs nothing. */ + if (outer_code == IOR + && exact_log2 (INTVAL (x)) != -1) + { + *total = 0; + return true; + } + + /* Larger constants that can be loaded via movei aren't too + bad. If we're just doing a set they cost nothing extra. */ + if (satisfies_constraint_N (x)) + { + if (mode == DImode) + *total = COSTS_N_INSNS (2); + else + *total = COSTS_N_INSNS (1); + return true; + } + + if (mode == DImode) + *total = COSTS_N_INSNS (5); + else + *total = COSTS_N_INSNS (3); + return true; + + case CONST_DOUBLE: + /* We don't optimize CONST_DOUBLEs well nor do we relax them well, + so their cost is very high. */ + *total = COSTS_N_INSNS (6); + return true; + + case CONST: + case SYMBOL_REF: + case MEM: + *total = 0; + return true; + + case IF_THEN_ELSE: + *total = COSTS_N_INSNS (1); + return true; + + case LABEL_REF: + case HIGH: + case LO_SUM: + case BSWAP: + case PLUS: + case MINUS: + case AND: + case IOR: + case XOR: + case ASHIFT: + case ASHIFTRT: + case LSHIFTRT: + case NEG: + case NOT: + case SIGN_EXTEND: + case ZERO_EXTEND: + case ZERO_EXTRACT: + if (outer_code == SET) + { + if (mode == DImode) + *total = COSTS_N_INSNS (2); + else + *total = COSTS_N_INSNS (1); + } + return true; + + case COMPARE: + if (outer_code == SET) + { + if (GET_MODE (XEXP (x, 0)) == DImode + || GET_MODE (XEXP (x, 1)) == DImode) + *total = COSTS_N_INSNS (2); + else + *total = COSTS_N_INSNS (1); + } + return true; + + case UMOD: + case UDIV: + case MOD: + case DIV: + if (outer_code == SET) + { + if (mode == DImode) + *total = COSTS_N_INSNS (600); + else + *total = COSTS_N_INSNS (200); + } + return true; + + case MULT: + if (outer_code == SET) + { + if (! ubicom32_v4) + { + if (mode == DImode) + *total = COSTS_N_INSNS (15); + else + *total = COSTS_N_INSNS (5); + } + else + { + if (mode == DImode) + *total = COSTS_N_INSNS (6); + else + *total = COSTS_N_INSNS (2); + } + } + return true; + + case UNSPEC: + if (XINT (x, 1) == UNSPEC_FDPIC_GOT + || XINT (x, 1) == UNSPEC_FDPIC_GOT_FUNCDESC) + *total = 0; + return true; + + default: + return false; + } +} + +/* Return 1 if ADDR can have different meanings depending on the machine + mode of the memory reference it is used for or if the address is + valid for some modes but not others. + + Autoincrement and autodecrement addresses typically have + mode-dependent effects because the amount of the increment or + decrement is the size of the operand being addressed. Some machines + have other mode-dependent addresses. Many RISC machines have no + mode-dependent addresses. + + You may assume that ADDR is a valid address for the machine. */ + +int +ubicom32_mode_dependent_address_p (rtx addr) +{ + if (GET_CODE (addr) == POST_INC + || GET_CODE (addr) == PRE_INC + || GET_CODE (addr) == POST_DEC + || GET_CODE (addr) == PRE_DEC + || GET_CODE (addr) == POST_MODIFY + || GET_CODE (addr) == PRE_MODIFY) + return 1; + + return 0; +} + +static void +ubicom32_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED) +{ + fprintf (file, "/* frame/pretend: %ld/%d save_regs: %d out_args: %d %s */\n", + get_frame_size (), crtl->args.pretend_args_size, + save_regs_size, crtl->outgoing_args_size, + current_function_is_leaf ? "leaf" : "nonleaf"); +} + +static void +ubicom32_function_epilogue (FILE *file ATTRIBUTE_UNUSED, + HOST_WIDE_INT size ATTRIBUTE_UNUSED) +{ + ubicom32_reorg_completed = 0; +} + +static void +ubicom32_machine_dependent_reorg (void) +{ +#if 0 /* Commenting out this optimization until it is fixed */ + if (optimize) + { + compute_bb_for_insn (); + + /* Do a very simple CSE pass over just the hard registers. */ + reload_cse_regs (get_insns ()); + + /* Reload_cse_regs can eliminate potentially-trapping MEMs. + Remove any EH edges associated with them. */ + if (flag_non_call_exceptions) + purge_all_dead_edges (); + } +#endif + ubicom32_reorg_completed = 1; +} + +void +ubicom32_output_cond_jump (rtx insn, rtx cond, rtx target) +{ + rtx note; + int mostly_false_jump; + rtx xoperands[2]; + rtx cc_reg; + + note = find_reg_note (insn, REG_BR_PROB, 0); + mostly_false_jump = !note || (INTVAL (XEXP (note, 0)) + <= REG_BR_PROB_BASE / 2); + + xoperands[0] = target; + xoperands[1] = cond; + cc_reg = XEXP (cond, 0); + + if (GET_MODE (cc_reg) == CCWmode + || GET_MODE (cc_reg) == CCWZmode + || GET_MODE (cc_reg) == CCWZNmode) + { + if (mostly_false_jump) + output_asm_insn ("jmp%b1.w.f\t%0", xoperands); + else + output_asm_insn ("jmp%b1.w.t\t%0", xoperands); + return; + } + + if (GET_MODE (cc_reg) == CCSmode + || GET_MODE (cc_reg) == CCSZmode + || GET_MODE (cc_reg) == CCSZNmode) + { + if (mostly_false_jump) + output_asm_insn ("jmp%b1.s.f\t%0", xoperands); + else + output_asm_insn ("jmp%b1.s.t\t%0", xoperands); + return; + } + + abort (); +} + +/* Return non-zero if FUNC is a naked function. */ + +static int +ubicom32_naked_function_p (void) +{ + return lookup_attribute ("naked", DECL_ATTRIBUTES (current_function_decl)) != NULL_TREE; +} + +/* Return an RTX indicating where the return address to the + calling function can be found. */ +rtx +ubicom32_return_addr_rtx (int count, rtx frame ATTRIBUTE_UNUSED) +{ + if (count != 0) + return NULL_RTX; + + return get_hard_reg_initial_val (Pmode, LINK_REGNO); +} + +/* + * ubicom32_readonly_data_section: This routtine handles code + * at the start of readonly data sections + */ +static void +ubicom32_readonly_data_section (const void *data ATTRIBUTE_UNUSED) +{ + static int num = 0; + if (in_section == readonly_data_section){ + fprintf (asm_out_file, "%s", DATA_SECTION_ASM_OP); + if (flag_data_sections){ + fprintf (asm_out_file, ".rodata%d", num); + fprintf (asm_out_file, ",\"a\""); + } + fprintf (asm_out_file, "\n"); + } + num++; +} + +/* + * ubicom32_text_section: not in readonly section + */ +static void +ubicom32_text_section(const void *data ATTRIBUTE_UNUSED) +{ + fprintf (asm_out_file, "%s\n", TEXT_SECTION_ASM_OP); +} + +/* + * ubicom32_data_section: not in readonly section + */ +static void +ubicom32_data_section(const void *data ATTRIBUTE_UNUSED) +{ + fprintf (asm_out_file, "%s\n", DATA_SECTION_ASM_OP); +} + +/* + * ubicom32_asm_init_sections: This routine implements special + * section handling + */ +static void +ubicom32_asm_init_sections(void) +{ + text_section = get_unnamed_section(SECTION_CODE, ubicom32_text_section, NULL); + + data_section = get_unnamed_section(SECTION_WRITE, ubicom32_data_section, NULL); + + readonly_data_section = get_unnamed_section(0, ubicom32_readonly_data_section, NULL); +} + +/* + * ubicom32_profiler: This routine would call + * mcount to support prof and gprof if mcount + * was supported. Currently, do nothing. + */ +void +ubicom32_profiler(void) +{ +} + +/* Initialise the builtin functions. Start by initialising + descriptions of different types of functions (e.g., void fn(int), + int fn(void)), and then use these to define the builtins. */ +static void +ubicom32_init_builtins (void) +{ + tree endlink; + tree short_unsigned_endlink; + tree unsigned_endlink; + tree short_unsigned_ftype_short_unsigned; + tree unsigned_ftype_unsigned; + + endlink = void_list_node; + + short_unsigned_endlink + = tree_cons (NULL_TREE, short_unsigned_type_node, endlink); + + unsigned_endlink + = tree_cons (NULL_TREE, unsigned_type_node, endlink); + + short_unsigned_ftype_short_unsigned + = build_function_type (short_unsigned_type_node, short_unsigned_endlink); + + unsigned_ftype_unsigned + = build_function_type (unsigned_type_node, unsigned_endlink); + + /* Initialise the byte swap function. */ + add_builtin_function ("__builtin_ubicom32_swapb_2", + short_unsigned_ftype_short_unsigned, + UBICOM32_BUILTIN_UBICOM32_SWAPB_2, + BUILT_IN_MD, NULL, + NULL_TREE); + + /* Initialise the byte swap function. */ + add_builtin_function ("__builtin_ubicom32_swapb_4", + unsigned_ftype_unsigned, + UBICOM32_BUILTIN_UBICOM32_SWAPB_4, + BUILT_IN_MD, NULL, + NULL_TREE); +} + +/* Given a builtin function taking 2 operands (i.e., target + source), + emit the RTL for the underlying instruction. */ +static rtx +ubicom32_expand_builtin_2op (enum insn_code icode, tree arglist, rtx target) +{ + tree arg0; + rtx op0, pat; + enum machine_mode tmode, mode0; + + /* Grab the incoming argument and emit its RTL. */ + arg0 = TREE_VALUE (arglist); + op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0); + + /* Determine the modes of the instruction operands. */ + tmode = insn_data[icode].operand[0].mode; + mode0 = insn_data[icode].operand[1].mode; + + /* Ensure that the incoming argument RTL is in a register of the + correct mode. */ + if (!(*insn_data[icode].operand[1].predicate) (op0, mode0)) + op0 = copy_to_mode_reg (mode0, op0); + + /* If there isn't a suitable target, emit a target register. */ + if (target == 0 + || GET_MODE (target) != tmode + || !(*insn_data[icode].operand[0].predicate) (target, tmode)) + target = gen_reg_rtx (tmode); + + /* Emit and return the new instruction. */ + pat = GEN_FCN (icode) (target, op0); + if (!pat) + return 0; + emit_insn (pat); + + return target; +} + +/* Expand a call to a builtin function. */ +static rtx +ubicom32_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED, + enum machine_mode mode ATTRIBUTE_UNUSED, + int ignore ATTRIBUTE_UNUSED) +{ + tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0); + tree arglist = CALL_EXPR_ARGS(exp); + int fcode = DECL_FUNCTION_CODE (fndecl); + + switch (fcode) + { + case UBICOM32_BUILTIN_UBICOM32_SWAPB_2: + return ubicom32_expand_builtin_2op (CODE_FOR_bswaphi, arglist, target); + + case UBICOM32_BUILTIN_UBICOM32_SWAPB_4: + return ubicom32_expand_builtin_2op (CODE_FOR_bswapsi, arglist, target); + + default: + gcc_unreachable(); + } + + /* Should really do something sensible here. */ + return NULL_RTX; +} + +/* Fold any constant argument for a swapb.2 instruction. */ +static tree +ubicom32_fold_builtin_ubicom32_swapb_2 (tree fndecl, tree arglist) +{ + tree arg0; + + arg0 = TREE_VALUE (arglist); + + /* Optimize constant value. */ + if (TREE_CODE (arg0) == INTEGER_CST) + { + HOST_WIDE_INT v; + HOST_WIDE_INT res; + + v = TREE_INT_CST_LOW (arg0); + res = ((v >> 8) & 0xff) + | ((v & 0xff) << 8); + + return build_int_cst (TREE_TYPE (TREE_TYPE (fndecl)), res); + } + + return NULL_TREE; +} + +/* Fold any constant argument for a swapb.4 instruction. */ +static tree +ubicom32_fold_builtin_ubicom32_swapb_4 (tree fndecl, tree arglist) +{ + tree arg0; + + arg0 = TREE_VALUE (arglist); + + /* Optimize constant value. */ + if (TREE_CODE (arg0) == INTEGER_CST) + { + unsigned HOST_WIDE_INT v; + unsigned HOST_WIDE_INT res; + + v = TREE_INT_CST_LOW (arg0); + res = ((v >> 24) & 0xff) + | (((v >> 16) & 0xff) << 8) + | (((v >> 8) & 0xff) << 16) + | ((v & 0xff) << 24); + + return build_int_cst_wide (TREE_TYPE (TREE_TYPE (fndecl)), res, 0); + } + + return NULL_TREE; +} + +/* Fold any constant arguments for builtin functions. */ +static tree +ubicom32_fold_builtin (tree fndecl, tree arglist, bool ignore ATTRIBUTE_UNUSED) +{ + switch (DECL_FUNCTION_CODE (fndecl)) + { + case UBICOM32_BUILTIN_UBICOM32_SWAPB_2: + return ubicom32_fold_builtin_ubicom32_swapb_2 (fndecl, arglist); + + case UBICOM32_BUILTIN_UBICOM32_SWAPB_4: + return ubicom32_fold_builtin_ubicom32_swapb_4 (fndecl, arglist); + + default: + return NULL; + } +} + +/* Implementation of TARGET_ASM_INTEGER. When using FD-PIC, we need to + tell the assembler to generate pointers to function descriptors in + some cases. */ +static bool +ubicom32_assemble_integer (rtx value, unsigned int size, int aligned_p) +{ + if (TARGET_FDPIC && size == UNITS_PER_WORD) + { + if (GET_CODE (value) == SYMBOL_REF + && SYMBOL_REF_FUNCTION_P (value)) + { + fputs ("\t.picptr\t%funcdesc(", asm_out_file); + output_addr_const (asm_out_file, value); + fputs (")\n", asm_out_file); + return true; + } + + if (!aligned_p) + { + /* We've set the unaligned SI op to NULL, so we always have to + handle the unaligned case here. */ + assemble_integer_with_op ("\t.4byte\t", value); + return true; + } + } + + return default_assemble_integer (value, size, aligned_p); +} + +/* If the constant I can be constructed by shifting a source-1 immediate + by a constant number of bits then return the bit count. If not + return 0. */ + +int +ubicom32_shiftable_const_int (int i) +{ + int shift = 0; + + /* Note that any constant that can be represented as an immediate to + a movei instruction is automatically ignored here in the interests + of the clarity of the output asm code. */ + if (i >= -32768 && i <= 32767) + return 0; + + /* Find the number of trailing zeroes. We could use __builtin_ctz + here but it's not obvious if this is supported on all build + compilers so we err on the side of caution. */ + if ((i & 0xffff) == 0) + { + shift += 16; + i >>= 16; + } + + if ((i & 0xff) == 0) + { + shift += 8; + i >>= 8; + } + + if ((i & 0xf) == 0) + { + shift += 4; + i >>= 4; + } + + if ((i & 0x3) == 0) + { + shift += 2; + i >>= 2; + } + + if ((i & 0x1) == 0) + { + shift += 1; + i >>= 1; + } + + if (i >= -128 && i <= 127) + return shift; + + return 0; +} + --- /dev/null +++ b/gcc/config/ubicom32/ubicom32.h @@ -0,0 +1,1564 @@ +/* Definitions of target machine for Ubicom32 + + Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, + 2009 Free Software Foundation, Inc. + Contributed by Ubicom, 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 3, 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 COPYING3. If not see + . */ + + + +#define OBJECT_FORMAT_ELF + +/* Run-time target specifications. */ + +/* Target CPU builtins. */ +#define TARGET_CPU_CPP_BUILTINS() \ + do \ + { \ + builtin_define_std ("__UBICOM32__"); \ + builtin_define_std ("__ubicom32__"); \ + \ + if (TARGET_FDPIC) \ + { \ + builtin_define ("__UBICOM32_FDPIC__"); \ + builtin_define ("__FDPIC__"); \ + } \ + } \ + while (0) + +#ifndef TARGET_DEFAULT +#define TARGET_DEFAULT 0 +#endif + +extern int ubicom32_case_values_threshold; + +/* Nonzero if this chip supports the Ubicom32 v3 ISA. */ +extern int ubicom32_v3; + +/* Nonzero if this chip supports the Ubicom32 v4 ISA. */ +extern int ubicom32_v4; + +extern int ubicom32_stack_size; + +/* Flag for whether we can use calli instead of ret in returns. */ +extern int ubicom32_can_use_calli_to_ret; + +/* This macro is a C statement to print on `stderr' a string describing the + particular machine description choice. Every machine description should + define `TARGET_VERSION'. */ +#define TARGET_VERSION fprintf (stderr, " (UBICOM32)"); + +/* We don't need a frame pointer to debug things. Doing this means + that gcc can turn on -fomit-frame-pointer when '-O' is specified. */ +#define CAN_DEBUG_WITHOUT_FP + +/* We need to handle processor-specific options. */ +#define OVERRIDE_OPTIONS ubicom32_override_options () + +#define OPTIMIZATION_OPTIONS(LEVEL, SIZE) \ + ubicom32_optimization_options (LEVEL, SIZE) + +/* For Ubicom32 the least significant bit has the lowest bit number + so we define this to be 0. */ +#define BITS_BIG_ENDIAN 0 + +/* For Ubicom32 the most significant byte in a word has the lowest + number. */ +#define BYTES_BIG_ENDIAN 1 + +/* For Ubicom32, in a multiword object, the most signifant word has the + lowest number. */ +#define WORDS_BIG_ENDIAN 1 + +/* Ubicom32 has 8 bits per byte. */ +#define BITS_PER_UNIT 8 + +/* Ubicom32 has 32 bits per word. */ +#define BITS_PER_WORD 32 + +/* Width of a word, in units (bytes). */ +#define UNITS_PER_WORD 4 + +/* Width of a pointer, in bits. */ +#define POINTER_SIZE 32 + +/* Alias for pointers. Ubicom32 is a 32-bit architecture so we use + SImode. */ +#define Pmode SImode + +/* Normal alignment required for function parameters on the stack, in + bits. */ +#define PARM_BOUNDARY 32 + +/* We need to maintain the stack on a 32-bit boundary. */ +#define STACK_BOUNDARY 32 + +/* Alignment required for a function entry point, in bits. */ +#define FUNCTION_BOUNDARY 32 + +/* Alias for the machine mode used for memory references to functions being + called, in `call' RTL expressions. We use byte-oriented addresses + here. */ +#define FUNCTION_MODE QImode + +/* Biggest alignment that any data type can require on this machine, + in bits. */ +#define BIGGEST_ALIGNMENT 32 + +/* this default to BIGGEST_ALIGNMENT unless defined */ +/* ART: What's the correct value here? Default is (((unsigned int)1<<28)*8)*/ +#undef MAX_OFILE_ALIGNMENT +#define MAX_OFILE_ALIGNMENT (128 * 8) + +/* Alignment in bits to be given to a structure bit field that follows an empty + field such as `int : 0;'. */ +#define EMPTY_FIELD_BOUNDARY 32 + +/* All structures must be a multiple of 32 bits in size. */ +#define STRUCTURE_SIZE_BOUNDARY 32 + +/* A bit-field declared as `int' forces `int' alignment for the struct. */ +#define PCC_BITFIELD_TYPE_MATTERS 1 + +/* For Ubicom32 we absolutely require that data be aligned with nominal + alignment. */ +#define STRICT_ALIGNMENT 1 + +/* Make strcpy of constants fast. */ +#define CONSTANT_ALIGNMENT(EXP, ALIGN) \ + (TREE_CODE (EXP) == STRING_CST \ + && (ALIGN) < BITS_PER_WORD ? BITS_PER_WORD : (ALIGN)) + +/* Define this macro as an expression for the alignment of a structure + (given by STRUCT as a tree node) if the alignment computed in the + usual way is COMPUTED and the alignment explicitly specified was + SPECIFIED. */ +#define DATA_ALIGNMENT(TYPE, ALIGN) \ + ((((ALIGN) < BITS_PER_WORD) \ + && (TREE_CODE (TYPE) == ARRAY_TYPE \ + || TREE_CODE (TYPE) == UNION_TYPE \ + || TREE_CODE (TYPE) == RECORD_TYPE)) ? BITS_PER_WORD : (ALIGN)) + +#define LOCAL_ALIGNMENT(TYPE,ALIGN) DATA_ALIGNMENT(TYPE,ALIGN) + +/* For Ubicom32 we default to unsigned chars. */ +#define DEFAULT_SIGNED_CHAR 0 + +/* Machine-specific data register numbers. */ +#define FIRST_DATA_REGNUM 0 +#define D10_REGNUM 10 +#define D11_REGNUM 11 +#define D12_REGNUM 12 +#define D13_REGNUM 13 +#define LAST_DATA_REGNUM 15 + +/* Machine-specific address register numbers. */ +#define FIRST_ADDRESS_REGNUM 16 +#define LAST_ADDRESS_REGNUM 22 + +/* Register numbers used for passing a function's static chain pointer. If + register windows are used, the register number as seen by the called + function is `STATIC_CHAIN_INCOMING_REGNUM', while the register number as + seen by the calling function is `STATIC_CHAIN_REGNUM'. If these registers + are the same, `STATIC_CHAIN_INCOMING_REGNUM' need not be defined. + + The static chain register need not be a fixed register. + + If the static chain is passed in memory, these macros should not be defined; + instead, the next two macros should be defined. */ +#define STATIC_CHAIN_REGNUM (FIRST_ADDRESS_REGNUM + 1) + +/* The register number of the frame pointer register, which is used to access + automatic variables in the stack frame. We generally eliminate this anyway + for Ubicom32 but we make it A6 by default. */ +#define FRAME_POINTER_REGNUM (LAST_ADDRESS_REGNUM) + +/* The register number of the stack pointer register, which is also be a + fixed register according to `FIXED_REGISTERS'. For Ubicom32 we don't + have a hardware requirement about which register this is, but by convention + we use A7. */ +#define STACK_POINTER_REGNUM (LAST_ADDRESS_REGNUM + 1) + +/* Machine-specific accumulator register numbers. */ +#define ACC0_HI_REGNUM 24 +#define ACC0_LO_REGNUM 25 +#define ACC1_HI_REGNUM 26 +#define ACC1_LO_REGNUM 27 + +/* source3 register number */ +#define SOURCE3_REGNUM 28 + +/* The register number of the arg pointer register, which is used to access the + function's argument list. On some machines, this is the same as the frame + pointer register. On some machines, the hardware determines which register + this is. On other machines, you can choose any register you wish for this + purpose. If this is not the same register as the frame pointer register, + then you must mark it as a fixed register according to `FIXED_REGISTERS', or + arrange to be able to eliminate it. */ +#define ARG_POINTER_REGNUM 29 + +/* Pseudo-reg for condition code. */ +#define CC_REGNUM 30 + +/* Interrupt set/clear registers. */ +#define INT_SET0_REGNUM 31 +#define INT_SET1_REGNUM 32 +#define INT_CLR0_REGNUM 33 +#define INT_CLR1_REGNUM 34 + +/* Scratchpad registers. */ +#define SCRATCHPAD0_REGNUM 35 +#define SCRATCHPAD1_REGNUM 36 +#define SCRATCHPAD2_REGNUM 37 +#define SCRATCHPAD3_REGNUM 38 + +/* FDPIC register. */ +#define FDPIC_REGNUM 16 + +/* Number of hardware registers known to the compiler. They receive numbers 0 + through `FIRST_PSEUDO_REGISTER-1'; thus, the first pseudo register's number + really is assigned the number `FIRST_PSEUDO_REGISTER'. */ +#define FIRST_PSEUDO_REGISTER 39 + +/* An initializer that says which registers are used for fixed purposes all + throughout the compiled code and are therefore not available for general + allocation. These would include the stack pointer, the frame pointer + (except on machines where that can be used as a general register when no + frame pointer is needed), the program counter on machines where that is + considered one of the addressable registers, and any other numbered register + with a standard use. + + This information is expressed as a sequence of numbers, separated by commas + and surrounded by braces. The Nth number is 1 if register N is fixed, 0 + otherwise. + + The table initialized from this macro, and the table initialized by the + following one, may be overridden at run time either automatically, by the + actions of the macro `CONDITIONAL_REGISTER_USAGE', or by the user with the + command options `-ffixed-REG', `-fcall-used-REG' and `-fcall-saved-REG'. */ +#define FIXED_REGISTERS \ + { \ + 0, 0, 0, 0, 0, 0, 0, 0, /* d0 - d7 */ \ + 0, 0, 0, 0, 0, 0, 0, 1, /* d8 - d15 */ \ + 0, 0, 0, 0, 0, 0, 0, 1, /* a0 - a7 */ \ + 0, 0, /* acc0 hi/lo */ \ + 0, 0, /* acc1 hi/lo */ \ + 0, /* source3 */ \ + 1, /* arg */ \ + 1, /* cc */ \ + 1, 1, /* int_set[01] */ \ + 1, 1, /* int_clr[01] */ \ + 1, 1, 1, 1 /* scratchpad[0123] */ \ + } + +/* 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. + + If a register has 0 in `CALL_USED_REGISTERS', the compiler automatically + saves it on function entry and restores it on function exit, if the register + is used within the function. */ +#define CALL_USED_REGISTERS \ + { \ + 1, 1, 1, 1, 1, 1, 1, 1, /* d0 - d7 */ \ + 1, 1, 0, 0, 0, 0, 1, 1, /* d8 - d15 */ \ + 1, 0, 0, 1, 1, 1, 0, 1, /* a0 - a7 */ \ + 1, 1, /* acc0 hi/lo */ \ + 1, 1, /* acc1 hi/lo */ \ + 1, /* source3 */ \ + 1, /* arg */ \ + 1, /* cc */ \ + 1, 1, /* int_set[01] */ \ + 1, 1, /* int_clr[01] */ \ + 1, 1, 1, 1 /* scratchpad[0123] */ \ + } + +/* How to refer to registers in assembler output. + This sequence is indexed by compiler's hard-register-number (see above). */ + +/* A C initializer containing the assembler's names for the machine registers, + each one as a C string constant. This is what translates register numbers + in the compiler into assembler language. */ +#define REGISTER_NAMES \ + { \ + "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", \ + "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", \ + "a0", "a1", "a2", "a3", "a4", "a5", "a6", "sp", \ + "acc0_hi", "acc0_lo", \ + "acc1_hi", "acc1_lo", \ + "source3", \ + "arg", \ + "cc", \ + "int_set0", "int_set1", \ + "int_clr0", "int_clr1", \ + "scratchpad0", "scratchpad1", "scratchpad2", "scratchpad3" \ + } + +#define CONDITIONAL_REGISTER_USAGE \ + ubicom32_conditional_register_usage (); + +/* Order of allocation of registers. */ + +/* If defined, an initializer for a vector of integers, containing the numbers + of hard registers in the order in which GNU CC should prefer to use them + (from most preferred to least). + + For Ubicom32 we try using caller-clobbered data registers first, then + callee-saved data registers, then caller-clobbered address registers, + then callee-saved address registers and finally everything else. + + The caller-clobbered registers are usually slightly cheaper to use because + there's no need to save/restore. */ +#define REG_ALLOC_ORDER \ + { \ + 0, 1, 2, 3, 4, /* d0 - d4 */ \ + 5, 6, 7, 8, 9, /* d5 - d9 */ \ + 14, /* d14 */ \ + 10, 11, 12, 13, /* d10 - d13 */ \ + 19, 20, 16, 21, /* a3, a4, a0, a5 */ \ + 17, 18, 22, /* a1, a2, a6 */ \ + 24, 25, /* acc0 hi/lo */ \ + 26, 27, /* acc0 hi/lo */ \ + 28 /* source3 */ \ + } + +/* C expression for the number of consecutive hard registers, starting at + register number REGNO, required to hold a value of mode MODE. */ +#define HARD_REGNO_NREGS(REGNO, MODE) \ + ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) + +/* Most registers can hold QImode, HImode and SImode values but we have to + be able to indicate any hard registers that cannot hold values with some + modes. */ +#define HARD_REGNO_MODE_OK(REGNO, MODE) \ + ubicom32_hard_regno_mode_ok(REGNO, MODE) + +/* We can rename most registers aside from the FDPIC register if we're using + FDPIC. */ +#define HARD_REGNO_RENAME_OK(from, to) (TARGET_FDPIC ? ((to) != FDPIC_REGNUM) : 1) + +/* A C expression that is nonzero if it is desirable to choose register + allocation so as to avoid move instructions between a value of mode MODE1 + and a value of mode MODE2. + + If `HARD_REGNO_MODE_OK (R, MODE1)' and `HARD_REGNO_MODE_OK (R, MODE2)' are + ever different for any R, then `MODES_TIEABLE_P (MODE1, MODE2)' must be + zero. */ +#define MODES_TIEABLE_P(MODE1, MODE2) 1 + +/* An enumeral type that must be defined with all the register class names as + enumeral values. `NO_REGS' must be first. `ALL_REGS' must be the last + register class, followed by one more enumeral value, `LIM_REG_CLASSES', + which is not a register class but rather tells how many classes there are. + + Each register class has a number, which is the value of casting the class + name to type `int'. The number serves as an index in many of the tables + described below. */ + +enum reg_class +{ + NO_REGS, + DATA_REGS, + FDPIC_REG, + ADDRESS_REGS, + ALL_ADDRESS_REGS, + ACC_LO_REGS, + ACC_REGS, + CC_REG, + DATA_ACC_REGS, + SOURCE3_REG, + SPECIAL_REGS, + GENERAL_REGS, + ALL_REGS, + LIM_REG_CLASSES +}; + +/* The number of distinct register classes. */ +#define N_REG_CLASSES (int) LIM_REG_CLASSES + +/* An initializer containing the names of the register classes as C string + constants. These names are used in writing some of the debugging dumps. */ + +#define REG_CLASS_NAMES \ +{ \ + "NO_REGS", \ + "DATA_REGS", \ + "FDPIC_REG", \ + "ADDRESS_REGS", \ + "ALL_ADDRESS_REGS", \ + "ACC_LO_REGS", \ + "ACC_REGS", \ + "CC_REG", \ + "DATA_ACC_REGS", \ + "SOURCE3_REG", \ + "SPECIAL_REGS", \ + "GENERAL_REGS", \ + "ALL_REGS", \ + "LIM_REGS" \ +} + +/* An initializer containing the contents of the register classes, as integers + which are bit masks. The Nth integer specifies the contents of class N. + The way the integer MASK is interpreted is that register R is in the class + if `MASK & (1 << R)' is 1. + + When the machine has more than 32 registers, an integer does not suffice. + Then the integers are replaced by sub-initializers, braced groupings + containing several integers. Each sub-initializer must be suitable as an + initializer for the type `HARD_REG_SET' which is defined in + `hard-reg-set.h'. */ +#define REG_CLASS_CONTENTS \ +{ \ + {0x00000000, 0x00000000}, /* No regs */ \ + {0x0000ffff, 0x00000000}, /* DATA_REGS */ \ + {0x00010000, 0x00000000}, /* FDPIC_REG */ \ + {0x20fe0000, 0x00000000}, /* ADDRESS_REGS */ \ + {0x20ff0000, 0x00000000}, /* ALL_ADDRESS_REGS */ \ + {0x0a000000, 0x00000000}, /* ACC_LO_REGS */ \ + {0x0f000000, 0x00000000}, /* ACC_REGS */ \ + {0x40000000, 0x00000000}, /* CC_REG */ \ + {0x0f00ffff, 0x00000000}, /* DATA_ACC_REGS */ \ + {0x10000000, 0x00000000}, /* SOURGE3_REG */ \ + {0x80000000, 0x0000007f}, /* SPECIAL_REGS */ \ + {0xbfffffff, 0x0000007f}, /* GENERAL_REGS */ \ + {0xbfffffff, 0x0000007f} /* ALL_REGS */ \ +} + +extern enum reg_class const ubicom32_regclass_map[FIRST_PSEUDO_REGISTER]; + +/* 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) (ubicom32_regclass_map[REGNO]) + +#define IRA_COVER_CLASSES \ +{ \ + GENERAL_REGS, \ + LIM_REG_CLASSES \ +} + +/* Ubicom32 base registers must be address registers since addresses can + only be reached via address registers. */ +#define BASE_REG_CLASS ALL_ADDRESS_REGS + +/* Ubicom32 index registers must be data registers since we cannot add + two address registers together to form an address. */ +#define INDEX_REG_CLASS DATA_REGS + +/* A C expression which is nonzero if register number NUM is suitable for use + as a base register in operand addresses. It may be either a suitable hard + register or a pseudo register that has been allocated such a hard register. */ + +#ifndef REG_OK_STRICT +#define REGNO_OK_FOR_BASE_P(regno) \ + ubicom32_regno_ok_for_base_p (regno, 0) +#else +#define REGNO_OK_FOR_BASE_P(regno) \ + ubicom32_regno_ok_for_base_p (regno, 1) +#endif + +/* A C expression which is nonzero if register number NUM is suitable for use + as an index register in operand addresses. It may be either a suitable hard + register or a pseudo register that has been allocated such a hard register. + + The difference between an index register and a base register is that the + index register may be scaled. If an address involves the sum of two + registers, neither one of them scaled, then either one may be labeled the + "base" and the other the "index"; but whichever labeling is used must fit + the machine's constraints of which registers may serve in each capacity. + The compiler will try both labelings, looking for one that is valid, and + will reload one or both registers only if neither labeling works. */ +#ifndef REG_OK_STRICT +#define REGNO_OK_FOR_INDEX_P(regno) \ + ubicom32_regno_ok_for_index_p (regno, 0) +#else +#define REGNO_OK_FOR_INDEX_P(regno) \ + ubicom32_regno_ok_for_index_p (regno, 1) +#endif + +/* Attempt to restrict the register class we need to copy value X intoto the + would-be register class CLASS. Most things are fine for Ubicom32 but we + have to restrict certain types of address loads. */ +#define PREFERRED_RELOAD_CLASS(X, CLASS) \ + ubicom32_preferred_reload_class (X, CLASS) + +/* A C expression for the maximum number of consecutive registers of + class CLASS needed to hold a value of mode MODE. For Ubicom32 this + is pretty much identical to HARD_REGNO_NREGS. */ +#define CLASS_MAX_NREGS(CLASS, MODE) \ + ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) + +/* For Ubicom32 the stack grows downwards when we push a word onto the stack + - i.e. it moves to a smaller address. */ +#define STACK_GROWS_DOWNWARD 1 + +/* Offset from the frame pointer to the first local variable slot to + be allocated. */ +#define STARTING_FRAME_OFFSET 0 + +/* Offset from the argument pointer register to the first argument's + address. */ +#define FIRST_PARM_OFFSET(FNDECL) 0 + +/* A C expression whose value is RTL representing the value of the return + address for the frame COUNT steps up from the current frame, after the + prologue. FRAMEADDR is the frame pointer of the COUNT frame, or the frame + pointer of the COUNT - 1 frame if `RETURN_ADDR_IN_PREVIOUS_FRAME' is + defined. + + The value of the expression must always be the correct address when COUNT is + zero, but may be `NULL_RTX' if there is not way to determine the return + address of other frames. */ +#define RETURN_ADDR_RTX(COUNT, FRAME) \ + ubicom32_return_addr_rtx (COUNT, FRAME) + +/* Register That Address the Stack Frame. */ + +/* We don't actually require a frame pointer in most functions with the + Ubicom32 architecture so we allow it to be eliminated. */ +#define FRAME_POINTER_REQUIRED 0 + +/* Macro that defines a table of register pairs used to eliminate unecessary + registers that point into the stack frame. + + For Ubicom32 we don't generally need an arg pointer of a frame pointer + so we allow the arg pointer to be replaced by either the frame pointer or + the stack pointer. We also allow the frame pointer to be replaced by + the stack pointer. */ +#define ELIMINABLE_REGS \ +{ \ + {ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ + {ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM}, \ + {FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM} \ +} + +/* Let the compiler know that we want to use the ELIMINABLE_REGS macro + above. */ +#define CAN_ELIMINATE(FROM, TO) 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) = ubicom32_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'. No space will be pushed onto the + stack for each call; instead, the function prologue should increase the + stack frame size by this amount. + + Defining both `PUSH_ROUNDING' and `ACCUMULATE_OUTGOING_ARGS' is not + proper. */ +#define ACCUMULATE_OUTGOING_ARGS 1 + +/* Define this macro if functions should assume that stack space has been + allocated for arguments even when their values are passed in registers. + + The value of this macro is the size, in bytes, of the area reserved for + arguments passed in registers for the function represented by FNDECL. + + This space can be allocated by the caller, or be a part of the + machine-dependent stack frame: `OUTGOING_REG_PARM_STACK_SPACE' says + which. */ +#define REG_PARM_STACK_SPACE(FNDECL) ubicom32_reg_parm_stack_space(FNDECL) + +/* 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. + + FUNDECL is a C variable whose value is a tree node that describes the + function in question. Normally it is a node of type `FUNCTION_DECL' that + describes the declaration of the function. From this it is possible to + obtain the DECL_MACHINE_ATTRIBUTES of the function. + + FUNTYPE is a C variable whose value is a tree node that describes the + function in question. Normally it is a node of type `FUNCTION_TYPE' that + describes the data type of the function. From this it is possible to obtain + the data types of the value and arguments (if known). + + When a call to a library function is being considered, FUNTYPE will contain + an identifier node for the library function. Thus, if you need to + distinguish among various library functions, you can do so by their names. + Note that "library function" in this context means a function used to + perform arithmetic, whose name is known specially in the compiler and was + not mentioned in the C code being compiled. + + STACK-SIZE is the number of bytes of arguments passed on the stack. If a + variable number of bytes is passed, it is zero, and argument popping will + always be the responsibility of the calling function. + + On the Vax, all functions always pop their arguments, so the definition of + this macro is STACK-SIZE. On the 68000, using the standard calling + convention, no functions pop their arguments, so the value of the macro is + always 0 in this case. But an alternative calling convention is available + in which functions that take a fixed number of arguments pop them but other + functions (such as `printf') pop nothing (the caller pops all). When this + convention is in use, FUNTYPE is examined to determine whether a function + takes a fixed number of arguments. */ +#define RETURN_POPS_ARGS(FUNDECL, FUNTYPE, STACK_SIZE) 0 + +/* A C expression that controls whether a function argument is passed in a + register, and which register. + + The arguments are CUM, of type CUMULATIVE_ARGS, which summarizes (in a way + defined by INIT_CUMULATIVE_ARGS and FUNCTION_ARG_ADVANCE) all of the previous + arguments so far passed in registers; MODE, the machine mode of the argument; + TYPE, the data type of the argument as a tree node or 0 if that is not known + (which happens for C support library functions); and NAMED, which is 1 for an + ordinary argument and 0 for nameless arguments that correspond to `...' in the + called function's prototype. + + The value of the expression should either be a `reg' RTX for the hard + register in which to pass the argument, or zero to pass the argument on the + stack. + + For machines like the Vax and 68000, where normally all arguments are + pushed, zero suffices as a definition. + + The usual way to make the ANSI library `stdarg.h' work on a machine where + some arguments are usually passed in registers, is to cause nameless + arguments to be passed on the stack instead. This is done by making + `FUNCTION_ARG' return 0 whenever NAMED is 0. + + You may use the macro `MUST_PASS_IN_STACK (MODE, TYPE)' in the definition of + this macro to determine if this argument is of a type that must be passed in + the stack. If `REG_PARM_STACK_SPACE' is not defined and `FUNCTION_ARG' + returns non-zero for such an argument, the compiler will abort. If + `REG_PARM_STACK_SPACE' is defined, the argument will be computed in the + stack and then loaded into a register. */ +#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \ + function_arg (&CUM, MODE, TYPE, NAMED) + +#define FUNCTION_INCOMING_ARG(CUM, MODE, TYPE, NAMED) \ + function_incoming_arg (&CUM, MODE, TYPE, NAMED) + +/* A C expression for the number of words, at the beginning of an argument, + 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. + + On some machines, certain arguments must be passed partially in registers + and partially in memory. On these machines, typically the first N words of + arguments are passed in registers, and the rest on the stack. If a + multi-word argument (a `double' or a structure) crosses that boundary, its + first few words must be passed in registers and the rest must be pushed. + This macro tells the compiler when this occurs, and how many of the words + should go in registers. + + `FUNCTION_ARG' for these arguments should return the first register to be + used by the caller for this argument; likewise `FUNCTION_INCOMING_ARG', for + the called function. */ + +/* A C expression that indicates when an argument must be passed by reference. + If nonzero for an argument, a copy of that argument is made in memory and a + pointer to the argument is passed instead of the argument itself. The + pointer is passed in whatever way is appropriate for passing a pointer to + that type. + + On machines where `REG_PARM_STACK_SPACE' is not defined, a suitable + definition of this macro might be + #define FUNCTION_ARG_PASS_BY_REFERENCE(CUM, MODE, TYPE, NAMED) \ + MUST_PASS_IN_STACK (MODE, TYPE) */ + +/* If defined, a C expression that indicates when it is the called function's + responsibility to make a copy of arguments passed by invisible reference. + Normally, the caller makes a copy and passes the address of the copy to the + routine being called. When FUNCTION_ARG_CALLEE_COPIES is defined and is + nonzero, the caller does not make a copy. Instead, it passes a pointer to + the "live" value. The called function must not modify this value. If it + can be determined that the value won't be modified, it need not make a copy; + otherwise a copy must be made. */ + +/* A C type for declaring a variable that is used as the first argument of + `FUNCTION_ARG' and other related values. For some target machines, the type + `int' suffices and can hold the number of bytes of argument so far. + + There is no need to record in `CUMULATIVE_ARGS' anything about the arguments + that have been passed on the stack. The compiler has other variables to + keep track of that. For target machines on which all arguments are passed + on the stack, there is no need to store anything in `CUMULATIVE_ARGS'; + however, the data structure must exist and should not be empty, so use + `int'. */ +struct cum_arg +{ + int nbytes; + int reg; + int stdarg; +}; +#define CUMULATIVE_ARGS struct cum_arg + +/* A C statement (sans semicolon) for initializing the variable CUM for the + state at the beginning of the argument list. The variable has type + `CUMULATIVE_ARGS'. The value of FNTYPE is the tree node for the data type + of the function which will receive the args, or 0 if the args are to a + compiler support library function. The value of INDIRECT is nonzero when + processing an indirect call, for example a call through a function pointer. + The value of INDIRECT is zero for a call to an explicitly named function, a + library function call, or when `INIT_CUMULATIVE_ARGS' is used to find + arguments for the function being compiled. + + When processing a call to a compiler support library function, LIBNAME + identifies which one. It is a `symbol_ref' rtx which contains the name of + the function, as a string. LIBNAME is 0 when an ordinary C function call is + being processed. Thus, each time this macro is called, either LIBNAME or + FNTYPE is nonzero, but never both of them at once. */ + +#define INIT_CUMULATIVE_ARGS(CUM,FNTYPE,LIBNAME,INDIRECT, NAMED_ARGS) \ + init_cumulative_args (&(CUM), FNTYPE, LIBNAME, INDIRECT); + +/* A C statement (sans semicolon) to update the summarizer variable CUM to + advance past an argument in the argument list. The values MODE, TYPE and + NAMED describe that argument. Once this is done, the variable CUM is + suitable for analyzing the *following* argument with `FUNCTION_ARG', etc. + + This macro need not do anything if the argument in question was passed on + the stack. The compiler knows how to track the amount of stack space used + for arguments without any special help. */ +#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \ + ((CUM).nbytes += ((MODE) != BLKmode \ + ? (GET_MODE_SIZE (MODE) + 3) & ~3 \ + : (int_size_in_bytes (TYPE) + 3) & ~3)) + +/* For the Ubicom32 we define the upper function argument register here. */ +#define UBICOM32_FUNCTION_ARG_REGS 10 + +/* A C expression that is nonzero if REGNO is the number of a hard register in + which function arguments are sometimes passed. This does *not* include + implicit arguments such as the static chain and the structure-value address. + On many machines, no registers can be used for this purpose since all + function arguments are pushed on the stack. */ +#define FUNCTION_ARG_REGNO_P(N) ((N) < UBICOM32_FUNCTION_ARG_REGS) + + +/* How Scalar Function Values are Returned. */ + +/* The number of the hard register that is used to return a scalar value from a + function call. */ +#define RETURN_VALUE_REGNUM 0 + +/* A C expression to create an RTX representing the place where a function + returns a value of data type VALTYPE. VALTYPE is a tree node representing a + data type. Write `TYPE_MODE (VALTYPE)' to get the machine mode used to + represent that type. On many machines, only the mode is relevant. + (Actually, on most machines, scalar values are returned in the same place + regardless of mode). + + If `PROMOTE_FUNCTION_RETURN' is defined, you must apply the same promotion + rules specified in `PROMOTE_MODE' if VALTYPE is a scalar type. + + If the precise function being called is known, FUNC is a tree node + (`FUNCTION_DECL') for it; otherwise, FUNC is a null pointer. This makes it + possible to use a different value-returning convention for specific + functions when all their calls are known. + + `FUNCTION_VALUE' is not used for return vales with aggregate data types, + because these are returned in another way. See `STRUCT_VALUE_REGNUM' and + related macros, below. */ +#define FUNCTION_VALUE(VALTYPE, FUNC) \ + gen_rtx_REG (TYPE_MODE (VALTYPE), FIRST_DATA_REGNUM) + +/* A C expression to create an RTX representing the place where a library + function returns a value of mode MODE. + + Note that "library function" in this context means a compiler support + routine, used to perform arithmetic, whose name is known specially by the + compiler and was not mentioned in the C code being compiled. + + The definition of `LIBRARY_VALUE' need not be concerned aggregate data + types, because none of the library functions returns such types. */ +#define LIBCALL_VALUE(MODE) gen_rtx_REG (MODE, FIRST_DATA_REGNUM) + +/* A C expression that is nonzero if REGNO is the number of a hard register in + which the values of called function may come back. + + A register whose use for returning values is limited to serving as the + second of a pair (for a value of type `double', say) need not be recognized + by this macro. So for most machines, this definition suffices: + + #define FUNCTION_VALUE_REGNO_P(N) ((N) == RETURN) + + If the machine has register windows, so that the caller and the called + function use different registers for the return value, this macro should + recognize only the caller's register numbers. */ +#define FUNCTION_VALUE_REGNO_P(N) ((N) == FIRST_DATA_REGNUM) + + +/* How Large Values are Returned. */ + +/* A C expression which can inhibit the returning of certain function values in + registers, based on the type of value. A nonzero value says to return the + function value in memory, just as large structures are always returned. + Here TYPE will be a C expression of type `tree', representing the data type + of the value. + + Note that values of mode `BLKmode' must be explicitly handled by this macro. + Also, the option `-fpcc-struct-return' takes effect regardless of this + macro. On most systems, it is possible to leave the macro undefined; this + causes a default definition to be used, whose value is the constant 1 for + `BLKmode' values, and 0 otherwise. + + Do not use this macro to indicate that structures and unions should always + be returned in memory. You should instead use `DEFAULT_PCC_STRUCT_RETURN' + to indicate this. */ +#define RETURN_IN_MEMORY(TYPE) \ + (int_size_in_bytes (TYPE) > 8 || TYPE_MODE (TYPE) == BLKmode) + +/* Define this macro to be 1 if all structure and union return values must be + in memory. Since this results in slower code, this should be defined only + if needed for compatibility with other compilers or with an ABI. If you + define this macro to be 0, then the conventions used for structure and union + return values are decided by the `RETURN_IN_MEMORY' macro. + + If not defined, this defaults to the value 1. */ +#define DEFAULT_PCC_STRUCT_RETURN 0 + +/* If the structure value address is not passed in a register, define + `STRUCT_VALUE' as an expression returning an RTX for the place + where the address is passed. If it returns 0, the address is + passed as an "invisible" first argument. */ +#define STRUCT_VALUE 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. + + Note that this macro's value is relevant only for functions for which frame + pointers are maintained. It is never safe to delete a final stack + adjustment in a function that has no frame pointer, and the compiler knows + this regardless of `EXIT_IGNORE_STACK'. */ +#define EXIT_IGNORE_STACK 1 + +/* A C statement or compound statement to output to FILE some assembler code to + call the profiling subroutine `mcount'. Before calling, the assembler code + must load the address of a counter variable into a register where `mcount' + expects to find the address. The name of this variable is `LP' followed by + the number LABELNO, so you would generate the name using `LP%d' in a + `fprintf'. + + The details of how the address should be passed to `mcount' are determined + by your operating system environment, not by GNU CC. To figure them out, + compile a small program for profiling using the system's installed C + compiler and look at the assembler code that results. + + This declaration must be present, but it can be an abort if profiling is + not implemented. */ + +#define FUNCTION_PROFILER(file, labelno) ubicom32_profiler(file, labelno) + +/* A C statement to output, on the stream FILE, assembler code for a block of + data that contains the constant parts of a trampoline. This code should not + include a label--the label is taken care of automatically. */ +#if 0 +#define TRAMPOLINE_TEMPLATE(FILE) \ + do { \ + fprintf (FILE, "\tadd -4,sp\n"); \ + fprintf (FILE, "\t.long 0x0004fffa\n"); \ + fprintf (FILE, "\tmov (0,sp),a0\n"); \ + fprintf (FILE, "\tadd 4,sp\n"); \ + fprintf (FILE, "\tmov (13,a0),a1\n"); \ + fprintf (FILE, "\tmov (17,a0),a0\n"); \ + fprintf (FILE, "\tjmp (a0)\n"); \ + fprintf (FILE, "\t.long 0\n"); \ + fprintf (FILE, "\t.long 0\n"); \ + } while (0) +#endif + +/* A C expression for the size in bytes of the trampoline, as an integer. */ +#define TRAMPOLINE_SIZE 0x1b + +/* Alignment required for trampolines, in bits. + + If you don't define this macro, the value of `BIGGEST_ALIGNMENT' is used for + aligning trampolines. */ +#define TRAMPOLINE_ALIGNMENT 32 + +/* A C statement to initialize the variable parts of a trampoline. ADDR is an + RTX for the address of the trampoline; FNADDR is an RTX for the address of + the nested function; STATIC_CHAIN is an RTX for the static chain value that + should be passed to the function when it is called. */ +#define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT) \ +{ \ + emit_move_insn (gen_rtx_MEM (SImode, plus_constant ((TRAMP), 0x14)), \ + (CXT)); \ + emit_move_insn (gen_rtx_MEM (SImode, plus_constant ((TRAMP), 0x18)), \ + (FNADDR)); \ +} + +/* Ubicom32 supports pre and post increment/decrement addressing. */ +#define HAVE_POST_INCREMENT 1 +#define HAVE_PRE_INCREMENT 1 +#define HAVE_POST_DECREMENT 1 +#define HAVE_PRE_DECREMENT 1 + +/* Ubicom32 supports pre and post address side-effects with constants + other than the size of the memory operand. */ +#define HAVE_PRE_MODIFY_DISP 1 +#define HAVE_POST_MODIFY_DISP 1 + +/* A C expression that is 1 if the RTX X is a constant which is a valid + address. On most machines, this can be defined as `CONSTANT_P (X)', + but a few machines are more restrictive in which constant addresses + are supported. + + `CONSTANT_P' accepts integer-values expressions whose values are not + explicitly known, such as `symbol_ref', `label_ref', and `high' + expressions and `const' arithmetic expressions, in addition to + `const_int' and `const_double' expressions. */ +#define CONSTANT_ADDRESS_P(X) \ + (GET_CODE (X) == LABEL_REF \ + || (GET_CODE (X) == CONST \ + && GET_CODE (XEXP (X, 0)) == PLUS \ + && GET_CODE (XEXP (XEXP (X, 0), 0)) == LABEL_REF)) + +/* Ubicom32 supports a maximum of 2 registers in a valid memory address. + One is always an address register while a second, optional, one may be a + data register. */ +#define MAX_REGS_PER_ADDRESS 2 + +/* A C compound statement with a conditional `goto LABEL;' executed if X (an + RTX) is a legitimate memory address on the target machine for a memory + operand of mode MODE. + + It usually pays to define several simpler macros to serve as subroutines for + this one. Otherwise it may be too complicated to understand. + + This macro must exist in two variants: a strict variant and a non-strict + one. The strict variant is used in the reload pass. It must be defined so + that any pseudo-register that has not been allocated a hard register is + considered a memory reference. In contexts where some kind of register is + required, a pseudo-register with no hard register must be rejected. + + The non-strict variant is used in other passes. It must be defined to + accept all pseudo-registers in every context where some kind of register is + required. + + Compiler source files that want to use the strict variant of this macro + define the macro `REG_OK_STRICT'. You should use an `#ifdef REG_OK_STRICT' + conditional to define the strict variant in that case and the non-strict + variant otherwise. + + Subroutines to check for acceptable registers for various purposes (one for + base registers, one for index registers, and so on) are typically among the + subroutines used to define `GO_IF_LEGITIMATE_ADDRESS'. Then only these + subroutine macros need have two variants; the higher levels of macros may be + the same whether strict or not. + + Normally, constant addresses which are the sum of a `symbol_ref' and an + integer are stored inside a `const' RTX to mark them as constant. + Therefore, there is no need to recognize such sums specifically as + legitimate addresses. Normally you would simply recognize any `const' as + legitimate. + + Usually `PRINT_OPERAND_ADDRESS' is not prepared to handle constant sums that + are not marked with `const'. It assumes that a naked `plus' indicates + indexing. If so, then you *must* reject such naked constant sums as + illegitimate addresses, so that none of them will be given to + `PRINT_OPERAND_ADDRESS'. + + On some machines, whether a symbolic address is legitimate depends on the + section that the address refers to. On these machines, define the macro + `ENCODE_SECTION_INFO' to store the information into the `symbol_ref', and + then check for it here. When you see a `const', you will have to look + inside it to find the `symbol_ref' in order to determine the section. + + The best way to modify the name string is by adding text to the beginning, + with suitable punctuation to prevent any ambiguity. Allocate the new name + in `saveable_obstack'. You will have to modify `ASM_OUTPUT_LABELREF' to + remove and decode the added text and output the name accordingly, and define + `STRIP_NAME_ENCODING' to access the original name string. + + You can check the information stored here into the `symbol_ref' in the + definitions of the macros `GO_IF_LEGITIMATE_ADDRESS' and + `PRINT_OPERAND_ADDRESS'. */ +/* On the ubicom32, the value in the address register must be + in the same memory space/segment as the effective address. + + This is problematical for reload since it does not understand + that base+index != index+base in a memory reference. */ + +#ifdef REG_OK_STRICT +#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \ + if (ubicom32_legitimate_address_p (MODE, X, 1)) goto ADDR; +#else +#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \ + if (ubicom32_legitimate_address_p (MODE, X, 0)) goto ADDR; +#endif + +/* Try machine-dependent ways of modifying an illegitimate address + to be legitimate. If we find one, return the new, valid address. + This macro is used in only one place: `memory_address' in explow.c. + + OLDX is the address as it was before break_out_memory_refs was called. + In some cases it is useful to look at this to decide what needs to be done. + + MODE and WIN are passed so that this macro can use + GO_IF_LEGITIMATE_ADDRESS. + + It is always safe for this macro to do nothing. It exists to recognize + opportunities to optimize the output. + + On RS/6000, first check for the sum of a register with a constant + integer that is out of range. If so, generate code to add the + constant with the low-order 16 bits masked to the register and force + this result into another register (this can be done with `cau'). + Then generate an address of REG+(CONST&0xffff), allowing for the + possibility of bit 16 being a one. + + Then check for the sum of a register and something not constant, try to + load the other things into a register and return the sum. */ + +#define LEGITIMIZE_ADDRESS(X, OLDX, MODE, WIN) \ +{ \ + rtx result = ubicom32_legitimize_address ((X), (OLDX), (MODE)); \ + if (result != NULL_RTX) \ + { \ + (X) = result; \ + goto WIN; \ + } \ +} + +/* Try a machine-dependent way of reloading an illegitimate address + operand. If we find one, push the reload and jump to WIN. This + macro is used in only one place: `find_reloads_address' in reload.c. */ +#define LEGITIMIZE_RELOAD_ADDRESS(AD, MODE, OPNUM, TYPE, IND, WIN) \ +{ \ + rtx new_rtx = ubicom32_legitimize_reload_address ((AD), (MODE), (OPNUM), (int)(TYPE)); \ + if (new_rtx) \ + { \ + (AD) = new_rtx; \ + goto WIN; \ + } \ +} + +/* A C statement or compound statement with a conditional `goto LABEL;' + executed if memory address X (an RTX) can have different meanings depending + on the machine mode of the memory reference it is used for or if the address + is valid for some modes but not others. + + Autoincrement and autodecrement addresses typically have mode-dependent + effects because the amount of the increment or decrement is the size of the + operand being addressed. Some machines have other mode-dependent addresses. + Many RISC machines have no mode-dependent addresses. + + You may assume that ADDR is a valid address for the machine. */ +#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR, LABEL) \ + if (ubicom32_mode_dependent_address_p (ADDR)) \ + goto LABEL; + +/* A C expression that is nonzero if X is a legitimate constant for an + immediate operand on the target machine. You can assume that X + satisfies `CONSTANT_P', so you need not check this. In fact, `1' is + a suitable definition for this macro on machines where anything + `CONSTANT_P' is valid. */ +#define LEGITIMATE_CONSTANT_P(X) \ + ubicom32_legitimate_constant_p ((X)) + +/* Moves between registers are pretty-much single instructions for + Ubicom32. We make this the default "2" that gcc likes. */ +#define REGISTER_MOVE_COST(MODE, FROM, TO) 2 + +/* This is a little bit of magic from the S390 port that wins 2% on code + size when building the Linux kernel! Unfortunately while it wins on + that size the user-space apps built using FD-PIC don't improve and the + performance is lower because we put more pressure on the caches. We may + want this back on some future CPU that has higher cache performance. */ +/* #define IRA_HARD_REGNO_ADD_COST_MULTIPLIER(regno) 0.5 */ + +/* Moves between registers and memory are more expensive than between + registers because we have caches and write buffers that slow things + down! */ +#define MEMORY_MOVE_COST(MODE, CLASS, IN) 2 + +/* A fall-through branch is very low cost but anything that changes the PC + incurs a major pipeline hazard. We don't make the full extent of this + hazard visible because we hope that multiple threads will absorb much + of the cost and so we don't want a jump being replaced with, say, 7 + instructions. */ +#define BRANCH_COST(SPEED_P, PREDICTABLE_P) \ + ((PREDICTABLE_P) ? 1 : 3) + +/* Define this macro as a C expression which is nonzero if accessing less than + a word of memory (i.e. a `char' or a `short') is no faster than accessing a + word of memory, i.e., if such access require more than one instruction or if + there is no difference in cost between byte and (aligned) word loads. + + When this macro is not defined, the compiler will access a field by finding + the smallest containing object; when it is defined, a fullword load will be + used if alignment permits. Unless bytes accesses are faster than word + accesses, using word accesses is preferable since it may eliminate + subsequent memory access if subsequent accesses occur to other fields in the + same word of the structure, but to different bytes. */ +#define SLOW_BYTE_ACCESS 0 + +/* The number of scalar move insns which should be generated instead of a + string move insn or a library call. Increasing the value will always make + code faster, but eventually incurs high cost in increased code size. + + If you don't define this, a reasonable default is used. */ +/* According to expr.c, a value of around 6 should minimize code size. */ +#define MOVE_RATIO(SPEED) 6 + +/* We're much better off calling a constant function address with the + Ubicom32 architecture because we have an opcode for doing so. Don't + let the compiler extract function addresses as common subexpressions + into an address register. */ +#define NO_FUNCTION_CSE + +#define SELECT_CC_MODE(OP, X, Y) ubicom32_select_cc_mode (OP, X, Y) + +#define REVERSIBLE_CC_MODE(MODE) 1 + +/* Canonicalize a comparison from one we don't have to one we do have. */ +#define CANONICALIZE_COMPARISON(CODE, OP0, OP1) \ + ubicom32_canonicalize_comparison (&(CODE), &(OP0), &(OP1)) + +/* Dividing the output into sections. */ + +/* A C expression whose value is a string containing the assembler operation + that should precede instructions and read-only data. Normally `".text"' is + right. */ +#define TEXT_SECTION_ASM_OP "\t.section .text" + +/* A C expression whose value is a string containing the assembler operation to + identify the following data as writable initialized data. Normally + `".data"' is right. */ +#define DATA_SECTION_ASM_OP "\t.section .data" + + +/* If defined, a C expression whose value is a string containing the + assembler operation to identify the following data as + uninitialized global data. If not defined, and neither + `ASM_OUTPUT_BSS' nor `ASM_OUTPUT_ALIGNED_BSS' are defined, + uninitialized global data will be output in the data section if + `-fno-common' is passed, otherwise `ASM_OUTPUT_COMMON' will be + used. */ +#define BSS_SECTION_ASM_OP "\t.section .bss" + +/* This is how we tell the assembler that a symbol is weak. */ + +#define ASM_WEAKEN_LABEL(FILE, NAME) \ + do \ + { \ + fputs ("\t.weak\t", (FILE)); \ + assemble_name ((FILE), (NAME)); \ + fputc ('\n', (FILE)); \ + } \ + while (0) + +/* The Overall Framework of an Assembler File. */ + +#undef SET_ASM_OP +#define SET_ASM_OP "\t.set\t" + +/* A C string constant describing how to begin a comment in the target + assembler language. The compiler assumes that the comment will end at the + end of the line. */ +#define ASM_COMMENT_START ";" + +/* 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" + +/* Like `ASM_OUTPUT_BSS' except takes the required alignment as a separate, + explicit argument. If you define this macro, it is used in place of + `ASM_OUTPUT_BSS', and gives you more flexibility in handling the required + alignment of the variable. The alignment is specified as the number of + bits. + + Try to use function `asm_output_aligned_bss' defined in file `varasm.c' when + defining this macro. */ +#define ASM_OUTPUT_ALIGNED_BSS(FILE, DECL, NAME, SIZE, ALIGN) \ + asm_output_aligned_bss ((FILE), (DECL), (NAME), (SIZE), (ALIGN)) + +/* A C expression to assign to OUTVAR (which is a variable of type `char *') a + newly allocated string made from the string NAME and the number NUMBER, with + some suitable punctuation added. Use `alloca' to get space for the string. + + The string will be used as an argument to `ASM_OUTPUT_LABELREF' to produce + an assembler label for an internal static variable whose name is NAME. + Therefore, the string must be such as to result in valid assembler code. + The argument NUMBER is different each time this macro is executed; it + prevents conflicts between similarly-named internal static variables in + different scopes. + + Ideally this string should not be a valid C identifier, to prevent any + conflict with the user's own symbols. Most assemblers allow periods or + percent signs in assembler symbols; putting at least one of these between + the name and the number will suffice. */ +#define ASM_FORMAT_PRIVATE_NAME(OUTPUT, NAME, LABELNO) \ + ((OUTPUT) = (char *) alloca (strlen ((NAME)) + 10), \ + sprintf ((OUTPUT), "%s___%d", (NAME), (LABELNO))) + +#define ASM_GENERATE_INTERNAL_LABEL(STRING, PREFIX, NUM) \ + sprintf (STRING, "*.%s%ld", PREFIX, (long)(NUM)) +/* A C statement to store into the string STRING a label whose name + is made from the string PREFIX and the number NUM. + + This string, when output subsequently by `assemble_name', should + produce the output that `(*targetm.asm_out.internal_label)' would produce + with the same PREFIX and NUM. + + If the string begins with `*', then `assemble_name' will output + the rest of the string unchanged. It is often convenient for + `ASM_GENERATE_INTERNAL_LABEL' to use `*' in this way. If the + string doesn't start with `*', then `ASM_OUTPUT_LABELREF' gets to + output the string, and may change it. (Of course, + `ASM_OUTPUT_LABELREF' is also part of your machine description, so + you should know what it does on your machine.) */ + +/* This says how to output assembler code to declare an + uninitialized external linkage data object. Under SVR4, + the linker seems to want the alignment of data objects + to depend on their types. We do exactly that here. */ + +#define COMMON_ASM_OP "\t.comm\t" + +#undef ASM_OUTPUT_COMMON +#define ASM_OUTPUT_COMMON(FILE, NAME, SIZE, ROUNDED) \ + do \ + { \ + fprintf ((FILE), "%s", COMMON_ASM_OP); \ + assemble_name ((FILE), (NAME)); \ + fprintf ((FILE), ", %u\n", (SIZE)); \ + } \ + while (0) + +/* This says how to output assembler code to declare an + uninitialized internal linkage data object. Under SVR4, + the linker seems to want the alignment of data objects + to depend on their types. We do exactly that here. */ +#define LOCAL_ASM_OP "\t.lcomm\t" + +#undef ASM_OUTPUT_LOCAL +#define ASM_OUTPUT_LOCAL(FILE, NAME, SIZE, ROUNDED) \ + do \ + { \ + fprintf ((FILE), "%s", LOCAL_ASM_OP); \ + assemble_name ((FILE), (NAME)); \ + fprintf ((FILE), ", %u\n", (SIZE)); \ + } \ + while (0) + +/* Globalizing directive for a label. */ +#define GLOBAL_ASM_OP ".global\t" + +/* Output the operand of an instruction. */ +#define PRINT_OPERAND(FILE, X, CODE) \ + ubicom32_print_operand(FILE, X, CODE) + +/* Output the address of an operand. */ +#define PRINT_OPERAND_ADDRESS(FILE, ADDR) \ + ubicom32_print_operand_address (FILE, ADDR) + +/* A C expression to output to STREAM some assembler code which will push hard + register number REGNO onto the stack. The code need not be optimal, since + this macro is used only when profiling. */ +#define ASM_OUTPUT_REG_PUSH(FILE, REGNO) + +/* A C expression to output to STREAM some assembler code which will pop hard + register number REGNO off of the stack. The code need not be optimal, since + this macro is used only when profiling. */ +#define ASM_OUTPUT_REG_POP(FILE, REGNO) + +/* 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 `ASM_OUTPUT_INTERNAL_LABEL', and they must be + printed in the same way here. For example, + + fprintf (STREAM, "\t.word L%d-L%d\n", VALUE, REL) */ +#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \ + fprintf (FILE, "\t%s .L%d-.L%d\n", ".long", 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 + `ASM_OUTPUT_INTERNAL_LABEL'. For example, + + fprintf (STREAM, "\t.word L%d\n", VALUE) */ +#define ASM_OUTPUT_ADDR_VEC_ELT(STREAM, VALUE) \ + fprintf (STREAM, "\t.word .L%d\n", VALUE) + +/* Switch into a generic section. */ +#define TARGET_ASM_NAMED_SECTION default_elf_asm_named_section + +/* Assembler Commands for Alignment. */ + +#define ASM_OUTPUT_SKIP(STREAM, N) fprintf (STREAM, "\t.skip %d,0\n", N) +/* A C statement to output to the stdio stream STREAM an assembler + instruction to advance the location counter by NBYTES bytes. + Those bytes should be zero when loaded. NBYTES will be a C + expression of type `int'. */ + +/* A C statement to output to the stdio stream STREAM an assembler command to + advance the location counter to a multiple of 2 to the POWER bytes. POWER + will be a C expression of type `int'. */ +#define ASM_OUTPUT_ALIGN(FILE, LOG) \ + if ((LOG) != 0) \ + fprintf (FILE, "\t.align %d\n", (LOG)) + +/* A C expression that returns the DBX register number for the compiler + register number REGNO. In simple cases, the value of this expression may be + REGNO itself. But sometimes there are some registers that the compiler + knows about and DBX does not, or vice versa. In such cases, some register + may need to have one number in the compiler and another for DBX. + + If two registers have consecutive numbers inside GNU CC, and they can be + used as a pair to hold a multiword value, then they *must* have consecutive + numbers after renumbering with `DBX_REGISTER_NUMBER'. Otherwise, debuggers + will be unable to access such a pair, because they expect register pairs to + be consecutive in their own numbering scheme. + + If you find yourself defining `DBX_REGISTER_NUMBER' in way that does not + preserve register pairs, then what you must do instead is redefine the + actual register numbering scheme. + + This declaration is required. */ +#define DBX_REGISTER_NUMBER(REGNO) REGNO + +/* A C expression that returns the integer offset value for an automatic + variable having address X (an RTL expression). The default computation + assumes that X is based on the frame-pointer and gives the offset from the + frame-pointer. This is required for targets that produce debugging output + for DBX or COFF-style debugging output for SDB and allow the frame-pointer + to be eliminated when the `-g' options is used. */ +#define DEBUGGER_AUTO_OFFSET(X) \ + ((GET_CODE (X) == PLUS ? INTVAL (XEXP (X, 1)) : 0) \ + + (frame_pointer_needed \ + ? 0 : -initial_elimination_offset (FRAME_POINTER_REGNUM, \ + STACK_POINTER_REGNUM))) + +/* A C expression that returns the integer offset value for an argument having + address X (an RTL expression). The nominal offset is OFFSET. */ +#define DEBUGGER_ARG_OFFSET(OFFSET, X) \ + ((GET_CODE (X) == PLUS ? OFFSET : 0) \ + + (frame_pointer_needed \ + ? 0 : -initial_elimination_offset (ARG_POINTER_REGNUM, \ + STACK_POINTER_REGNUM))) + +/* A C expression that returns the type of debugging output GNU CC produces + when the user specifies `-g' or `-ggdb'. Define this if you have arranged + for GNU CC to support more than one format of debugging output. Currently, + the allowable values are `DBX_DEBUG', `SDB_DEBUG', `DWARF_DEBUG', + `DWARF2_DEBUG', and `XCOFF_DEBUG'. + + The value of this macro only affects the default debugging output; the user + can always get a specific type of output by using `-gstabs', `-gcoff', + `-gdwarf-1', `-gdwarf-2', or `-gxcoff'. + + Defined in svr4.h. +*/ +#undef PREFERRED_DEBUGGING_TYPE +#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG + +/* Define this macro if GNU CC should produce dwarf version 2 format debugging + output in response to the `-g' option. + + To support optional call frame debugging information, you must also define + `INCOMING_RETURN_ADDR_RTX' and either set `RTX_FRAME_RELATED_P' on the + prologue insns if you use RTL for the prologue, or call `dwarf2out_def_cfa' + and `dwarf2out_reg_save' as appropriate from `FUNCTION_PROLOGUE' if you + don't. + + Defined in svr4.h. */ + +#define DWARF2_DEBUGGING_INFO 1 +/*#define DWARF2_UNWIND_INFO 1*/ +#define DWARF2_UNWIND_INFO 0 +#define INCOMING_RETURN_ADDR_RTX gen_rtx_REG (Pmode, LINK_REGNO) +#define INCOMING_FRAME_SP_OFFSET 0 +#define DWARF_FRAME_RETURN_COLUMN DWARF_FRAME_REGNUM (LINK_REGNO) +#define EH_RETURN_FIRST 9 +#define EH_RETURN_DATA_REGNO(N) ((N) < 2 ? (N) + EH_RETURN_FIRST : INVALID_REGNUM) + +/* The EH_RETURN_STACKADJ_RTX macro returns RTL which describes the + location used to store the amount to ajdust the stack. This is + usually a registers that is available from end of the function's body + to the end of the epilogue. Thus, this cannot be a register used as a + temporary by the epilogue. + + This must be an integer register. */ +#define EH_RETURN_STACKADJ_REGNO 11 +#define EH_RETURN_STACKADJ_RTX \ + gen_rtx_REG (Pmode, EH_RETURN_STACKADJ_REGNO) + +/* The EH_RETURN_HANDLER_RTX macro returns RTL which describes the + location used to store the address the processor should jump to + catch exception. This is usually a registers that is available from + end of the function's body to the end of the epilogue. Thus, this + cannot be a register used as a temporary by the epilogue. + + This must be an address register. */ +#define EH_RETURN_HANDLER_REGNO 18 +#define EH_RETURN_HANDLER_RTX \ + gen_rtx_REG (Pmode, EH_RETURN_HANDLER_REGNO) + +/* #define DWARF2_DEBUGGING_INFO */ + +/* Define this macro if GNU CC 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 have. */ +#define CASE_VECTOR_MODE Pmode + +/* Smallest number of different values for which it is best to use a + jump-table instead of a tree of conditional branches. For most Ubicom32 + targets this is quite small, but for the v1 architecture implementations + we had very little data memory and so heavily prefer the tree approach + rather than the jump tables. */ +#define CASE_VALUES_THRESHOLD ubicom32_case_values_threshold + +/* Register operations within the Ubicom32 architecture always operate on + the whole register word and not just the sub-bits required for the opcode + mode size. */ +#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 that is nonzero if on this machine the number of bits + actually used for the count of a shift operation is equal to the number of + bits needed to represent the size of the object being shifted. When this + macro is non-zero, the compiler will assume that it is safe to omit a + sign-extend, zero-extend, and certain bitwise `and' instructions that + truncates the count of a shift operation. On machines that have + instructions that act on bitfields at variable positions, which may include + `bit test' instructions, a nonzero `SHIFT_COUNT_TRUNCATED' also enables + deletion of truncations of the values that serve as arguments to bitfield + instructions. + + If both types of instructions truncate the count (for shifts) and position + (for bitfield operations), or if no variable-position bitfield instructions + exist, you should define this macro. + + However, on some machines, such as the 80386 and the 680x0, truncation only + applies to shift operations and not the (real or pretended) bitfield + operations. Define `SHIFT_COUNT_TRUNCATED' to be zero on such machines. + Instead, add patterns to the `md' file that include the implied truncation + of the shift instructions. + + You need not define this macro if it would always have the value of zero. */ +#define SHIFT_COUNT_TRUNCATED 1 + +/* 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 + +/* A C string constant that tells the GNU CC driver program options to pass + to the assembler. It can also specify how to translate options you give + to GNU CC into options for GNU CC to pass to the assembler. See the + file `sun3.h' for an example of this. + + Defined in svr4.h. */ +#undef ASM_SPEC +#define ASM_SPEC \ + "%{march=*:-m%*} %{!march=*:-mubicom32v4} %{mfdpic:-mfdpic}" + +#define LINK_SPEC "\ +%{h*} %{v:-V} \ +%{b} \ +%{mfdpic:-melf32ubicom32fdpic -z text} \ +%{static:-dn -Bstatic} \ +%{shared:-G -Bdynamic} \ +%{symbolic:-Bsymbolic} \ +%{G*} \ +%{YP,*} \ +%{Qy:} %{!Qn:-Qy}" + +#undef STARTFILE_SPEC +#undef ENDFILE_SPEC + +/* The svr4.h LIB_SPEC with -leval and --*group tacked on */ + +#undef LIB_SPEC +#define LIB_SPEC "%{!shared:%{!symbolic:--start-group -lc -leval -lgcc --end-group}}" + +#undef HAVE_GAS_SHF_MERGE +#define HAVE_GAS_SHF_MERGE 0 + +#define HANDLE_SYSV_PRAGMA 1 +#undef HANDLE_PRAGMA_PACK + +typedef void (*ubicom32_func_ptr) (void); + +/* Define builtins for selected special-purpose instructions. */ +enum ubicom32_builtins +{ + UBICOM32_BUILTIN_UBICOM32_SWAPB_2, + UBICOM32_BUILTIN_UBICOM32_SWAPB_4 +}; + +extern rtx ubicom32_compare_op0; +extern rtx ubicom32_compare_op1; + +#define TYPE_ASM_OP "\t.type\t" +#define TYPE_OPERAND_FMT "@%s" + +#ifndef ASM_DECLARE_RESULT +#define ASM_DECLARE_RESULT(FILE, RESULT) +#endif + +/* These macros generate the special .type and .size directives which + are used to set the corresponding fields of the linker symbol table + entries in an ELF object file under SVR4. These macros also output + the starting labels for the relevant functions/objects. */ + +/* Write the extra assembler code needed to declare a function properly. + Some svr4 assemblers need to also have something extra said about the + function's return value. We allow for that here. */ + +#ifndef ASM_DECLARE_FUNCTION_NAME +#define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL) \ + do \ + { \ + ASM_OUTPUT_TYPE_DIRECTIVE (FILE, NAME, "function"); \ + ASM_DECLARE_RESULT (FILE, DECL_RESULT (DECL)); \ + ASM_OUTPUT_LABEL (FILE, NAME); \ + } \ + while (0) +#endif --- /dev/null +++ b/gcc/config/ubicom32/ubicom32.md @@ -0,0 +1,3753 @@ +; GCC machine description for Ubicom32 +; +; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Free Software +; Foundation, Inc. +; Contributed by Ubicom, 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 3, 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 COPYING3. If not see +; . + +(define_constants + [(AUX_DATA_REGNO 15) + (LINK_REGNO 21) + (SP_REGNO 23) + (ACC0_HI_REGNO 24) + (ACC1_HI_REGNO 26) + (CC_REGNO 30)]) + +(define_constants + [(UNSPEC_FDPIC_GOT 0) + (UNSPEC_FDPIC_GOT_FUNCDESC 1)]) + +(define_constants + [(UNSPEC_VOLATILE_LOAD_FDPIC_FUNCDESC 0)]) + +;; Types of instructions (for scheduling purposes). + +(define_attr "type" "mul,addr,other" + (const_string "other")) + +; Define instruction scheduling characteristics. We can only issue +; one instruction per clock so we don't need to define CPU units. +; +(define_automaton "ubicom32") + +(define_cpu_unit "i_pipeline" "ubicom32"); + +; We have a 4 cycle hazard associated with address calculations which +; seems rather tricky to avoid so we go with a defensive assumption +; that almost anything can be used to generate addresses. +; +;(define_insn_reservation "ubicom32_other" 4 +; (eq_attr "type" "other") +; "i_pipeline") + +; Some moves don't generate hazards. +; +;(define_insn_reservation "ubicom32_addr" 1 +; (eq_attr "type" "addr") +; "i_pipeline") + +; We need 3 cycles between a multiply instruction and any use of the +; matching accumulator register(s). +; +(define_insn_reservation "ubicom32_mul" 4 + (eq_attr "type" "mul") + "i_pipeline") + +(define_attr "length" "" + (const_int 4)) + +(include "predicates.md") +(include "constraints.md") + +; 8-bit move with no change to the flags reg. +; +(define_insn "movqi" + [(set (match_operand:QI 0 "nonimmediate_operand" "=rm") + (match_operand:QI 1 "ubicom32_move_operand" "g"))] + "" + "move.1\\t%0, %1") + +; Combiner-generated 8-bit move with the zero flag set accordingly. +; +(define_insn "movqi_ccszn" + [(set (reg CC_REGNO) + (compare (match_operand:QI 0 "nonimmediate_operand" "rm") + (const_int 0))) + (set (match_operand:QI 1 "nonimmediate_operand" "=rm") + (match_dup 0))] + "ubicom32_match_cc_mode(insn, CCSZNmode)" + "ext.1\\t%1, %0") + +; Combine isn't very good at merging some types of operations so we +; have to make do with a peephole. It's not as effective but it's better +; than doing nothing. +; +(define_peephole2 + [(set (match_operand:QI 0 "nonimmediate_operand" "") + (match_operand:QI 1 "nonimmediate_operand" "")) + (set (match_operand 2 "ubicom32_cc_register_operand" "") + (match_operator 3 "ubicom32_compare_operator" + [(match_dup 0) + (const_int 0)]))] + "(GET_MODE (operands[2]) == CCSZNmode + || GET_MODE (operands[2]) == CCSZmode)" + [(parallel + [(set (match_dup 2) + (match_op_dup 3 + [(match_dup 1) + (const_int 0)])) + (set (match_dup 0) + (match_dup 1))])] + "") + +; Combine isn't very good at merging some types of operations so we +; have to make do with a peephole. It's not as effective but it's better +; than doing nothing. +; +(define_peephole2 + [(set (match_operand:QI 0 "nonimmediate_operand" "") + (match_operand:QI 1 "nonimmediate_operand" "")) + (set (match_operand 2 "ubicom32_cc_register_operand" "") + (match_operator 3 "ubicom32_compare_operator" + [(match_dup 1) + (const_int 0)]))] + "(GET_MODE (operands[2]) == CCSZNmode + || GET_MODE (operands[2]) == CCSZmode)" + [(parallel + [(set (match_dup 2) + (match_op_dup 3 + [(match_dup 1) + (const_int 0)])) + (set (match_dup 0) + (match_dup 1))])] + "") + +; 16-bit move with no change to the flags reg. +; +(define_insn "movhi" + [(set (match_operand:HI 0 "nonimmediate_operand" "=rm") + (match_operand:HI 1 "ubicom32_move_operand" "g"))] + "" + "* + { + if (CONST_INT_P (operands[1])) + return \"movei\\t%0, %1\"; + + return \"move.2\\t%0, %1\"; + }") + +; Combiner-generated 16-bit move with the zero flag set accordingly. +; +(define_insn "movhi_ccszn" + [(set (reg CC_REGNO) + (compare (match_operand:HI 0 "nonimmediate_operand" "rm") + (const_int 0))) + (set (match_operand:HI 1 "nonimmediate_operand" "=rm") + (match_dup 0))] + "ubicom32_match_cc_mode(insn, CCSZNmode)" + "ext.2\\t%1, %0") + +; Combine isn't very good at merging some types of operations so we +; have to make do with a peephole. It's not as effective but it's better +; than doing nothing. +; +(define_peephole2 + [(set (match_operand:HI 0 "nonimmediate_operand" "") + (match_operand:HI 1 "nonimmediate_operand" "")) + (set (match_operand 2 "ubicom32_cc_register_operand" "") + (match_operator 3 "ubicom32_compare_operator" + [(match_dup 0) + (const_int 0)]))] + "(GET_MODE (operands[2]) == CCSZNmode + || GET_MODE (operands[2]) == CCSZmode)" + [(parallel + [(set (match_dup 2) + (match_op_dup 3 + [(match_dup 1) + (const_int 0)])) + (set (match_dup 0) + (match_dup 1))])] + "") + +; Combine isn't very good at merging some types of operations so we +; have to make do with a peephole. It's not as effective but it's better +; than doing nothing. +; +(define_peephole2 + [(set (match_operand:HI 0 "nonimmediate_operand" "") + (match_operand:HI 1 "nonimmediate_operand" "")) + (set (match_operand 2 "ubicom32_cc_register_operand" "") + (match_operator 3 "ubicom32_compare_operator" + [(match_dup 1) + (const_int 0)]))] + "(GET_MODE (operands[2]) == CCSZNmode + || GET_MODE (operands[2]) == CCSZmode)" + [(parallel + [(set (match_dup 2) + (match_op_dup 3 + [(match_dup 1) + (const_int 0)])) + (set (match_dup 0) + (match_dup 1))])] + "") + +; 32-bit move with no change to the flags reg. +; +(define_expand "movsi" + [(set (match_operand:SI 0 "nonimmediate_operand" "") + (match_operand:SI 1 "general_operand" ""))] + "" + "{ + /* Convert any complexities in operand 1 into something that can just + fall into the default expander code. */ + ubicom32_expand_movsi (operands); + }") + +(define_insn "movsi_high" + [(set (match_operand:SI 0 "ubicom32_address_register_operand" "=a") + (high:SI (match_operand:SI 1 "ubicom32_symbolic_address_operand" "s")))] + "" + "moveai\\t%0, #%%hi(%E1)") + +(define_insn "movsi_lo_sum" + [(set (match_operand:SI 0 "nonimmediate_operand" "=rm") + (lo_sum:SI (match_operand:SI 1 "ubicom32_address_register_operand" "a") + (match_operand:SI 2 "immediate_operand" "s")))] + "" + "lea.1\\t%0, %%lo(%E2)(%1)") + +(define_insn "movsi_internal" + [(set (match_operand:SI 0 "nonimmediate_operand" "=rm") + (match_operand:SI 1 "ubicom32_move_operand" "rmnY"))] + "" + "* + { + if (CONST_INT_P (operands[1])) + { + ubicom32_emit_move_const_int (operands[0], operands[1]); + return \"\"; + } + + if (GET_CODE (operands[1]) == CONST_DOUBLE) + { + HOST_WIDE_INT i = CONST_DOUBLE_LOW (operands[1]); + + ubicom32_emit_move_const_int (operands[0], GEN_INT (i)); + return \"\"; + } + + if (ubicom32_address_register_operand (operands[0], VOIDmode) + && register_operand (operands[1], VOIDmode)) + { + if (ubicom32_address_register_operand (operands[1], VOIDmode)) + return \"lea.1\\t%0, 0(%1)\"; + + /* Use movea here to utilize the hazard bypass in the >= v4 ISA. */ + if (ubicom32_v4) + return \"movea\\t%0, %1\"; + + return \"move.4\\t%0, %1\"; + } + + return \"move.4\\t%0, %1\"; + }") + +; If we're not dependent on the state of the condition codes we can construct +; constants of value 2^n by using a bset. +; +(define_peephole2 + [(set (match_operand:SI 0 "nonimmediate_operand" "") + (match_operand:SI 1 "const_int_operand" ""))] + "(exact_log2 (INTVAL (operands[1])) > 14 + && peep2_regno_dead_p (0, CC_REGNO))" + [(parallel + [(set (match_dup 0) + (ior:SI (const_int 0) + (match_dup 1))) + (clobber (reg:CC CC_REGNO))])] + "") + +; If we're not dependent on the state of the condition codes we can construct +; constants of value ~(2^n) by using a bclr. +; +(define_peephole2 + [(set (match_operand:SI 0 "nonimmediate_operand" "") + (match_operand:SI 1 "const_int_operand" ""))] + "(exact_log2 (~INTVAL (operands[1])) > 14 + && peep2_regno_dead_p (0, CC_REGNO))" + [(parallel + [(set (match_dup 0) + (and:SI (const_int -1) + (match_dup 1))) + (clobber (reg:CC CC_REGNO))])] + "") + +; For 32-bit constants that have bits 0 through 24 and bit 31 set the same +; we can use swapb.4! +; +(define_peephole2 + [(set (match_operand:SI 0 "nonimmediate_operand" "") + (match_operand:SI 1 "const_int_operand" ""))] + "(ubicom32_v4 + && (INTVAL (operands[1]) & 0xffffffff) != 0xffffffff + && (INTVAL (operands[1]) & 0xffffffff) != 0 + && ((INTVAL (operands[1]) & 0x80ffffff) == 0 + || (INTVAL (operands[1]) & 0x80ffffff) == 0x80ffffff))" + [(set (match_dup 0) + (bswap:SI (match_dup 2)))] + "{ + operands[2] = GEN_INT (INTVAL (operands[1]) >> 24); + }") + +; If this is a write of a constant to memory look to see if we can usefully +; transform this into 2 smaller writes. +; +(define_peephole2 + [(set (match_operand:SI 0 "memory_operand" "") + (match_operand:SI 1 "const_int_operand" ""))] + "! satisfies_constraint_I (operands[1]) + && ubicom32_legitimate_address_p (HImode, plus_constant (XEXP (operands[0], 0), 2), 1)" + [(set (match_dup 4) (match_dup 2)) + (set (match_dup 5) (match_dup 3))] + "{ + rtx low_hword_addr; + + operands[2] = gen_highpart_mode (HImode, SImode, operands[1]); + operands[3] = gen_lowpart (HImode, operands[1]); + + operands[4] = gen_rtx_MEM (HImode, XEXP (operands[0], 0)); + MEM_COPY_ATTRIBUTES (operands[4], operands[0]); + + low_hword_addr = plus_constant (XEXP (operands[0], 0), 2); + operands[5] = gen_rtx_MEM (HImode, low_hword_addr); + MEM_COPY_ATTRIBUTES (operands[5], operands[0]); + }") + +; If we're writing memory and we've not found a better way to do this then +; try loading into a D register and then copying to memory. This will +; perform the fewest possible memory read/writes. +; +(define_peephole2 + [(match_scratch:SI 2 "d") + (set (match_operand:SI 0 "memory_operand" "") + (match_operand:SI 1 "const_int_operand" ""))] + "! satisfies_constraint_I (operands[1])" + [(set (match_dup 2) (match_dup 1)) + (set (match_dup 0) (match_dup 2))] + "") + +; If we're not dependent on the state of the condition codes we can construct +; constants of value (2^n - 1) by using an lsr.4. +; +(define_peephole2 + [(set (match_operand:SI 0 "ubicom32_data_register_operand" "") + (match_operand:SI 1 "const_int_operand" ""))] + "(exact_log2 (INTVAL (operands[1]) + 1) > 14 + && peep2_regno_dead_p (0, CC_REGNO))" + [(parallel + [(set (match_dup 0) + (lshiftrt:SI (const_int -1) + (match_dup 2))) + (clobber (reg:CC CC_REGNO))])] + "{ + operands[2] = GEN_INT (32 - exact_log2 (INTVAL (operands[1]) + 1)); + }") + +; If we're not dependent on the state of the condition codes we can construct +; constants of value (2^n - 1) by using an lsr.4. +; +(define_peephole2 + [(match_scratch:SI 2 "d") + (set (match_operand:SI 0 "nonimmediate_operand" "") + (match_operand:SI 1 "const_int_operand" ""))] + "(exact_log2 (INTVAL (operands[1]) + 1) > 14 + && peep2_regno_dead_p (0, CC_REGNO))" + [(parallel + [(set (match_dup 2) + (lshiftrt:SI (const_int -1) + (match_dup 3))) + (clobber (reg:CC CC_REGNO))]) + (set (match_dup 0) + (match_dup 2))] + "{ + operands[3] = GEN_INT (32 - exact_log2 (INTVAL (operands[1]) + 1)); + }") + +; If we're not dependent on the state of the condition codes we can construct +; some other constants by using an lsl.4 to shift 7 bits left by some +; constant. +; +(define_peephole2 + [(set (match_operand:SI 0 "ubicom32_data_register_operand" "") + (match_operand:SI 1 "const_int_operand" ""))] + "(ubicom32_shiftable_const_int (INTVAL (operands[1])) + && peep2_regno_dead_p (0, CC_REGNO))" + [(parallel + [(set (match_dup 0) + (ashift:SI (match_dup 2) + (match_dup 3))) + (clobber (reg:CC CC_REGNO))])] + "{ + int shift = ubicom32_shiftable_const_int (INTVAL (operands[1])); + operands[2] = GEN_INT (INTVAL (operands[1]) >> shift); + operands[3] = GEN_INT (shift); + }") + +; If we're not dependent on the state of the condition codes we can construct +; some other constants by using an lsl.4 to shift 7 bits left by some +; constant. +; +(define_peephole2 + [(match_scratch:SI 2 "d") + (set (match_operand:SI 0 "nonimmediate_operand" "") + (match_operand:SI 1 "const_int_operand" ""))] + "(ubicom32_shiftable_const_int (INTVAL (operands[1])) + && peep2_regno_dead_p (0, CC_REGNO))" + [(parallel + [(set (match_dup 2) + (ashift:SI (match_dup 3) + (match_dup 4))) + (clobber (reg:CC CC_REGNO))]) + (set (match_dup 0) + (match_dup 2))] + "{ + int shift = ubicom32_shiftable_const_int (INTVAL (operands[1])); + operands[3] = GEN_INT (INTVAL (operands[1]) >> shift); + operands[4] = GEN_INT (shift); + }") + +; For some 16-bit unsigned constants that have bit 15 set we can use +; swapb.2! +; +; Note that the movsi code emits the same sequence but by using a peephole2 +; we split the pattern early enough to allow instruction scheduling to +; occur. +; +(define_peephole2 + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "const_int_operand" ""))] + "(ubicom32_v4 + && (INTVAL (operands[1]) & 0xffff80ff) == 0x80ff)" + [(set (match_dup 0) + (zero_extend:SI (bswap:HI (match_dup 2))))] + "{ + HOST_WIDE_INT i = INTVAL (operands[1]) >> 8; + if (i >= 0x80) + i -= 0x100; + operands[2] = GEN_INT (i); + }") + +; In general for a 16-bit unsigned constant that has bit 15 set +; then we need a movei/move.2 pair unless we can represent it +; via just a move.2. +; +(define_peephole2 + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "const_int_operand" ""))] + "(INTVAL (operands[1]) & 0xffff8000) == 0x8000 + && (INTVAL (operands[1]) & 0xffff) < 0xff80" + [(set (match_dup 2) + (match_dup 1)) + (set (match_dup 0) + (zero_extend:SI (match_dup 2)))] + "{ + operands[2] = gen_rtx_REG (HImode, REGNO (operands[0])); + }") + +; If we're not dependent on the state of the condition codes we can construct +; 32-bit constants that have bits 16 through 31 set to arbitrary values +; and have bits 0 through 15 set to something representable as a default +; source-1 immediate - we use movei/shmrg.2 +; +(define_peephole2 + [(set (match_operand:SI 0 "ubicom32_data_register_operand" "") + (match_operand:SI 1 "const_int_operand" ""))] + "(((INTVAL (operands[1]) >= 0x8000 + && INTVAL (operands[1]) < 0xff80) + || INTVAL (operands[1]) >= 0x10000 + || INTVAL (operands[1]) < -0x8000) + && ((INTVAL (operands[1]) & 0xffff) >= 0xff80 + || (INTVAL (operands[1]) & 0xffff) < 0x80) + && peep2_regno_dead_p (0, CC_REGNO))" + [(set (match_dup 0) + (match_dup 2)) + (parallel + [(set (match_dup 0) + (ior:SI + (ashift:SI (match_dup 0) + (const_int 16)) + (zero_extend:SI + (match_dup 3)))) + (clobber (reg:CC CC_REGNO))])] + "{ + operands[2] = gen_highpart_mode (HImode, SImode, operands[1]); + operands[3] = gen_lowpart (HImode, operands[1]); + }") + +; Exactly the same as the peephole2 preceding except that this targets a +; general register instead of D register. Hopefully the later optimization +; passes will notice that the value ended up in a D register first here +; and eliminate away the other register! +; +(define_peephole2 + [(match_scratch:SI 2 "d") + (set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "const_int_operand" ""))] + "(((INTVAL (operands[1]) >= 0x8000 + && INTVAL (operands[1]) < 0xff80) + || INTVAL (operands[1]) >= 0x10000 + || INTVAL (operands[1]) < -0x8000) + && ((INTVAL (operands[1]) & 0xffff) >= 0xff80 + || (INTVAL (operands[1]) & 0xffff) < 0x80) + && peep2_regno_dead_p (0, CC_REGNO))" + [(set (match_dup 2) + (match_dup 3)) + (parallel + [(set (match_dup 2) + (ior:SI + (ashift:SI (match_dup 2) + (const_int 16)) + (zero_extend:SI + (match_dup 4)))) + (clobber (reg:CC CC_REGNO))]) + (set (match_dup 0) + (match_dup 2))] + "{ + operands[3] = gen_highpart_mode (HImode, SImode, operands[1]); + operands[4] = gen_lowpart (HImode, operands[1]); + }") + +; If we have a load of a large integer constant which does not have bit 31 +; set and we have a spare A reg then construct it with a moveai/lea.1 pair +; instead. This avoids constructing it in 3 instructions on the stack. +; +; Note that we have to be careful not to match anything that matches +; something we can do in a single instruction! There aren't many such +; constants but there are some. +; +(define_peephole2 + [(match_scratch:SI 2 "a") + (set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "const_int_operand" ""))] + "(! (INTVAL (operands[1]) & 0x80000000) + && ((INTVAL (operands[1]) >= 0x8000 + && INTVAL (operands[1]) < 0xff80) + || INTVAL (operands[1]) >= 0x10000))" + [(set (match_dup 2) + (match_dup 3)) + (set (match_dup 0) + (plus:SI (match_dup 2) + (match_dup 4)))] + "{ + HOST_WIDE_INT i = INTVAL (operands[1]); + operands[3] = GEN_INT (i & 0xffffff80); + operands[4] = GEN_INT (i & 0x7f); + }") + +; If we're not dependent on the state of the condition codes we can construct +; a 32-bit constant with a movei/movei/shmrg.2 sequence if possible. +; +(define_peephole2 + [(match_scratch:HI 2 "d") + (set (match_operand:SI 0 "ubicom32_data_register_operand" "") + (match_operand:SI 1 "const_int_operand" "")) + (match_dup 2)] + "(INTVAL (operands[1]) & 0x80000000 + && INTVAL (operands[1]) < -0x8000 + && peep2_regno_dead_p (0, CC_REGNO))" + [(set (match_dup 0) + (match_dup 3)) + (set (match_dup 2) + (match_dup 4)) + (parallel + [(set (match_dup 0) + (ior:SI + (ashift:SI (match_dup 0) + (const_int 16)) + (zero_extend:SI + (match_dup 2)))) + (clobber (reg:CC CC_REGNO))])] + "{ + operands[3] = gen_highpart_mode (HImode, SImode, operands[1]); + operands[4] = gen_lowpart (HImode, operands[1]); + }") + +; Exactly the same as the peephole2 preceding except that this targets a +; general register instead of D register. Hopefully the later optimization +; passes will notice that the value ended up in a D register first here +; and eliminate away the other register! +; +(define_peephole2 + [(match_scratch:SI 2 "d") + (match_scratch:HI 3 "d") + (set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "const_int_operand" "")) + (match_dup 3)] + "(INTVAL (operands[1]) & 0x80000000 + && INTVAL (operands[1]) < -0x8000 + && peep2_regno_dead_p (0, CC_REGNO))" + [(set (match_dup 2) + (match_dup 4)) + (set (match_dup 3) + (match_dup 5)) + (parallel + [(set (match_dup 2) + (ior:SI + (ashift:SI (match_dup 2) + (const_int 16)) + (zero_extend:SI + (match_dup 3)))) + (clobber (reg:CC CC_REGNO))]) + (set (match_dup 0) + (match_dup 2))] + "{ + operands[4] = gen_highpart_mode (HImode, SImode, operands[1]); + operands[5] = gen_lowpart (HImode, operands[1]); + }") + +(define_insn "movsi_fdpic_got_offset" + [(set (match_operand:SI 0 "ubicom32_data_register_operand" "=d") + (match_operand:SI 1 "ubicom32_fdpic_got_offset_operand" "Y"))] + "" + "movei\\t%0, %1") + +; The explicit MEM inside the UNSPEC prevents the compiler from moving +; the load before a branch after a NULL test, or before a store that +; initializes a function descriptor. + +(define_insn_and_split "load_fdpic_funcdesc" + [(set (match_operand:SI 0 "ubicom32_address_register_operand" "=a") + (unspec_volatile:SI [(mem:SI (match_operand:SI 1 "address_operand" "p"))] + UNSPEC_VOLATILE_LOAD_FDPIC_FUNCDESC))] + "" + "#" + "reload_completed" + [(set (match_dup 0) + (mem:SI (match_dup 1)))]) + +; Combiner-generated 32-bit move with the zero flag set accordingly. +; +(define_insn "movsi_ccwzn" + [(set (reg CC_REGNO) + (compare (match_operand:SI 0 "nonimmediate_operand" "rm, d") + (const_int 0))) + (set (match_operand:SI 1 "nonimmediate_operand" "=d,rm") + (match_dup 0))] + "ubicom32_match_cc_mode(insn, CCWZNmode)" + "@ + lsl.4\\t%1, %0, #0 + add.4\\t%1, #0, %0") + +; Combiner-generated 32-bit move with all flags set accordingly. +; +(define_insn "movsi_ccw" + [(set (reg CC_REGNO) + (compare (match_operand:SI 0 "ubicom32_data_register_operand" "d") + (const_int 0))) + (set (match_operand:SI 1 "nonimmediate_operand" "=rm") + (match_dup 0))] + "ubicom32_match_cc_mode(insn, CCWmode)" + "add.4\\t%1, #0, %0") + +; Combine isn't very good at merging some types of operations so we +; have to make do with a peephole. It's not as effective but it's better +; than doing nothing. +; +(define_peephole2 + [(set (match_operand:SI 0 "ubicom32_data_register_operand" "") + (match_operand:SI 1 "nonimmediate_operand" "")) + (parallel + [(set (match_operand 2 "ubicom32_cc_register_operand" "") + (match_operator 3 "ubicom32_compare_operator" + [(match_dup 0) + (const_int 0)])) + (clobber (match_operand:SI 4 "ubicom32_data_register_operand" ""))])] + "(GET_MODE (operands[2]) == CCWZNmode + || GET_MODE (operands[2]) == CCWZmode)" + [(parallel + [(set (match_dup 2) + (match_op_dup 3 + [(match_dup 1) + (const_int 0)])) + (set (match_dup 0) + (match_dup 1))])] + "") + +; Combine isn't very good at merging some types of operations so we +; have to make do with a peephole. It's not as effective but it's better +; than doing nothing. +; +(define_peephole2 + [(set (match_operand:SI 0 "nonimmediate_operand" "") + (match_operand:SI 1 "ubicom32_data_register_operand" "")) + (parallel + [(set (match_operand 2 "ubicom32_cc_register_operand" "") + (match_operator 3 "ubicom32_compare_operator" + [(match_dup 1) + (const_int 0)])) + (clobber (match_operand:SI 4 "ubicom32_data_register_operand" ""))])] + "(GET_MODE (operands[2]) == CCWZNmode + || GET_MODE (operands[2]) == CCWZmode)" + [(parallel + [(set (match_dup 2) + (match_op_dup 3 + [(match_dup 1) + (const_int 0)])) + (set (match_dup 0) + (match_dup 1))])] + "") + +; Combine isn't very good at merging some types of operations so we +; have to make do with a peephole. It's not as effective but it's better +; than doing nothing. +; +(define_peephole2 + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "nonimmediate_operand" "")) + (parallel + [(set (match_operand 2 "ubicom32_cc_register_operand" "") + (match_operator 3 "ubicom32_compare_operator" + [(match_dup 0) + (const_int 0)])) + (set (match_operand:SI 4 "ubicom32_data_register_operand" "") + (match_dup 0))])] + "(peep2_reg_dead_p (2, operands[0]) + && (GET_MODE (operands[2]) == CCWZNmode + || GET_MODE (operands[2]) == CCWZmode))" + [(parallel + [(set (match_dup 2) + (match_op_dup 3 + [(match_dup 1) + (const_int 0)])) + (set (match_dup 4) + (match_dup 1))])] + "") + +; Register renaming may make a general reg into a D reg in which case +; we may be able to simplify a compare. +; +(define_peephole2 + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "nonimmediate_operand" "")) + (parallel + [(set (match_operand 2 "ubicom32_cc_register_operand" "") + (match_operator 3 "ubicom32_compare_operator" + [(match_dup 0) + (const_int 0)])) + (clobber (match_operand:SI 4 "ubicom32_data_register_operand" ""))])] + "(peep2_reg_dead_p (2, operands[0]) + && (GET_MODE (operands[2]) == CCWZNmode + || GET_MODE (operands[2]) == CCWZmode))" + [(parallel + [(set (match_dup 2) + (match_op_dup 3 + [(match_dup 1) + (const_int 0)])) + (clobber (match_dup 4))])] + "") + +(define_insn_and_split "movdi" + [(set (match_operand:DI 0 "nonimmediate_operand" "=r,rm") + (match_operand:DI 1 "general_operand" "rmi,ri"))] + "" + "#" + "reload_completed" + [(set (match_dup 2) (match_dup 3)) + (set (match_dup 4) (match_dup 5))] + "{ + rtx dest_low; + rtx src_low; + + dest_low = gen_lowpart (SImode, operands[0]); + src_low = gen_lowpart (SImode, operands[1]); + + if (REG_P (operands[0]) + && REG_P (operands[1]) + && REGNO (operands[0]) < REGNO (operands[1])) + { + operands[2] = gen_highpart (SImode, operands[0]); + operands[3] = gen_highpart_mode (SImode, DImode, operands[1]); + operands[4] = dest_low; + operands[5] = src_low; + } + else if (reg_mentioned_p (dest_low, src_low)) + { + operands[2] = gen_highpart (SImode, operands[0]); + operands[3] = gen_highpart_mode (SImode, DImode, operands[1]); + operands[4] = dest_low; + operands[5] = src_low; + } + else + { + operands[2] = dest_low; + operands[3] = src_low; + operands[4] = gen_highpart (SImode, operands[0]); + operands[5] = gen_highpart_mode (SImode, DImode, operands[1]); + } + }" + [(set_attr "length" "8")]) + +; Combiner-generated 64-bit move with all flags set accordingly. +; +(define_insn "movdi_ccwzn" + [(set (reg CC_REGNO) + (compare (match_operand:DI 0 "nonimmediate_operand" "d, m, r") + (const_int 0))) + (set (match_operand:DI 1 "nonimmediate_operand" "=&rm,rm,!&rm") + (match_dup 0)) + (clobber (match_scratch:SI 2 "=X, d, d"))] + "ubicom32_match_cc_mode(insn, CCWZNmode)" + "* + { + operands[3] = gen_lowpart (SImode, operands[0]); + operands[4] = gen_lowpart (SImode, operands[1]); + operands[5] = gen_highpart (SImode, operands[0]); + operands[6] = gen_highpart (SImode, operands[1]); + + if (ubicom32_data_register_operand (operands[0], VOIDmode)) + return \"add.4\\t%4, #0, %3\;addc\\t%6, #0, %5\"; + + return \"movei\\t%2, #0\;add.4\\t%4, %3, %2\;addc\\t%6, %5, %2\"; + }" + [(set_attr "length" "8")]) + +(define_insn "movdi_ccw" + [(set (reg CC_REGNO) + (compare (match_operand:DI 0 "nonimmediate_operand" "d, m, r") + (const_int 0))) + (set (match_operand:DI 1 "nonimmediate_operand" "=&rm,rm,!&rm") + (match_dup 0)) + (clobber (match_scratch:SI 2 "=X, d, d"))] + "ubicom32_match_cc_mode(insn, CCWmode)" + "* + { + operands[3] = gen_lowpart (SImode, operands[0]); + operands[4] = gen_lowpart (SImode, operands[1]); + operands[5] = gen_highpart (SImode, operands[0]); + operands[6] = gen_highpart (SImode, operands[1]); + + if (ubicom32_data_register_operand (operands[0], VOIDmode)) + return \"add.4\\t%4, #0, %3\;addc\\t%6, #0, %5\"; + + return \"movei\\t%2, #0\;add.4\\t%4, %3, %2\;addc\\t%6, %5, %2\"; + }" + [(set_attr "length" "8")]) + +(define_insn "movsf" + [(set (match_operand:SF 0 "nonimmediate_operand" "=!d,*rm") + (match_operand:SF 1 "ubicom32_move_operand" "rmF,rmF"))] + "" + "* + { + if (GET_CODE (operands[1]) == CONST_DOUBLE) + { + HOST_WIDE_INT val; + REAL_VALUE_TYPE rv; + + REAL_VALUE_FROM_CONST_DOUBLE (rv, operands[1]); + REAL_VALUE_TO_TARGET_SINGLE (rv, val); + + ubicom32_emit_move_const_int (operands[0], GEN_INT (val)); + return \"\"; + } + + return \"move.4\\t%0, %1\"; + }") + +(define_insn "zero_extendqihi2" + [(set (match_operand:HI 0 "register_operand" "=r") + (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "rm")))] + "" + "move.1\\t%0, %1") + +(define_insn "zero_extendqisi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "rm")))] + "" + "move.1\\t%0, %1") + +(define_insn "zero_extendqisi2_ccwz_1" + [(set (reg CC_REGNO) + (compare + (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "rm")) + (const_int 0))) + (set (match_operand:SI 0 "ubicom32_data_register_operand" "=d") + (zero_extend:SI (match_dup 1)))] + "ubicom32_match_cc_mode(insn, CCWZmode)" + "shmrg.1\\t%0, %1, #0") + +(define_insn "zero_extendhisi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "rm")))] + "" + "move.2\\t%0, %1") + +(define_insn "zero_extendhisi2_ccwz_1" + [(set (reg CC_REGNO) + (compare + (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "rm")) + (const_int 0))) + (set (match_operand:SI 0 "ubicom32_data_register_operand" "=d") + (zero_extend:SI (match_dup 1)))] + "ubicom32_match_cc_mode(insn, CCWZmode)" + "shmrg.2\\t%0, %1, #0") + +(define_insn_and_split "zero_extendqidi2" + [(set (match_operand:DI 0 "register_operand" "=r") + (zero_extend:DI (match_operand:QI 1 "nonimmediate_operand" "rm")))] + "" + "#" + "reload_completed" + [(set (match_dup 2) + (zero_extend:SI (match_dup 1))) + (set (match_dup 3) + (const_int 0))] + "{ + operands[2] = gen_lowpart (SImode, operands[0]); + operands[3] = gen_highpart (SImode, operands[0]); + }" + [(set_attr "length" "8")]) + +(define_insn_and_split "zero_extendhidi2" + [(set (match_operand:DI 0 "register_operand" "=r") + (zero_extend:DI (match_operand:HI 1 "nonimmediate_operand" "rm")))] + "" + "#" + "reload_completed" + [(set (match_dup 2) + (zero_extend:SI (match_dup 1))) + (set (match_dup 3) + (const_int 0))] + "{ + operands[2] = gen_lowpart (SImode, operands[0]); + operands[3] = gen_highpart (SImode, operands[0]); + }" + [(set_attr "length" "8")]) + +(define_insn_and_split "zero_extendsidi2" + [(set (match_operand:DI 0 "nonimmediate_operand" "=rm") + (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "rm")))] + "" + "#" + "reload_completed" + [(set (match_dup 2) + (match_dup 1)) + (set (match_dup 3) + (const_int 0))] + "{ + operands[2] = gen_lowpart (SImode, operands[0]); + operands[3] = gen_highpart (SImode, operands[0]); + }" + [(set_attr "length" "8")]) + +(define_insn "extendqihi2" + [(set (match_operand:HI 0 "register_operand" "=r") + (sign_extend:HI (match_operand:QI 1 "nonimmediate_operand" "rm"))) + (clobber (reg:CC CC_REGNO))] + "" + "ext.1\\t%0, %1") + +(define_insn "extendqisi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "rm"))) + (clobber (reg:CC CC_REGNO))] + "" + "ext.1\\t%0, %1") + +(define_insn "extendhisi2" + [(set (match_operand:SI 0 "register_operand" "=r") + (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "rm"))) + (clobber (reg:CC CC_REGNO))] + "" + "ext.2\\t%0, %1") + +(define_insn_and_split "extendsidi2" + [(set (match_operand:DI 0 "nonimmediate_operand" "=d") + (sign_extend:DI (match_operand:SI 1 "nonimmediate_operand" "rm"))) + (clobber (reg:CC CC_REGNO))] + "" + "#" + "reload_completed" + [(set (match_dup 2) + (match_dup 1)) + (parallel + [(set (match_dup 3) + (ashiftrt:SI (match_dup 2) + (const_int 31))) + (clobber (reg:CC CC_REGNO))])] + "{ + operands[2] = gen_lowpart (SImode, operands[0]); + operands[3] = gen_highpart (SImode, operands[0]); + }" + [(set_attr "length" "8")]) + +(define_insn "bswaphi" + [(set (match_operand:HI 0 "nonimmediate_operand" "=rm") + (bswap:HI (match_operand:HI 1 "ubicom32_arith_operand" "rmI")))] + "(ubicom32_v4)" + "swapb.2\\t%0, %1"); + +(define_insn "bswaphisi" + [(set (match_operand:SI 0 "register_operand" "=r") + (zero_extend:SI + (bswap:HI (match_operand:HI 1 "ubicom32_arith_operand" "rmI"))))] + "(ubicom32_v4)" + "swapb.2\\t%0, %1"); + +(define_insn "bswapsi" + [(set (match_operand:SI 0 "nonimmediate_operand" "=rm") + (bswap:SI (match_operand:SI 1 "ubicom32_arith_operand" "rmI")))] + "(ubicom32_v4)" + "swapb.4\\t%0, %1"); + +(define_insn "tstqi_ext1" + [(set (reg CC_REGNO) + (compare (match_operand:QI 0 "nonimmediate_operand" "rm") + (const_int 0)))] + "ubicom32_match_cc_mode(insn, CCSZNmode)" + "ext.1\\t#0, %0") + +(define_expand "cmpqi" + [(set (reg CC_REGNO) + (compare (match_operand:QI 0 "ubicom32_arith_operand" "") + (match_operand:QI 1 "ubicom32_data_register_operand" "")))] + "(ubicom32_v4)" + "{ + ubicom32_compare_op0 = operands[0]; + ubicom32_compare_op1 = operands[1]; + DONE; + }") + +(define_insn "sub1_ccs" + [(set (reg CC_REGNO) + (compare (match_operand:QI 0 "ubicom32_arith_operand" "rmI") + (match_operand:QI 1 "ubicom32_data_register_operand" "d")))] + "(ubicom32_v4)" + "sub.1\\t#0, %0, %1") + +; If we're testing for equality we don't have to worry about reversing conditions. +; +(define_insn "sub1_ccsz_1" + [(set (reg:CCSZ CC_REGNO) + (compare:CCSZ (match_operand:QI 0 "nonimmediate_operand" "rm") + (match_operand:QI 1 "ubicom32_data_register_operand" "d")))] + "(ubicom32_v4)" + "sub.1\\t#0, %0, %1") + +(define_insn "sub1_ccsz_2" + [(set (reg:CCSZ CC_REGNO) + (compare:CCSZ (match_operand:QI 0 "ubicom32_data_register_operand" "d") + (match_operand:QI 1 "ubicom32_arith_operand" "rmI")))] + "(ubicom32_v4)" + "sub.1\\t#0, %1, %0") + +; When the combiner runs it doesn't have any insight into whether or not an argument +; to a compare is spilled to the stack and therefore can't swap the comparison in +; an attempt to use sub.1 more effectively. We peephole this case here. +; +(define_peephole2 + [(set (match_operand:QI 0 "register_operand" "") + (match_operand:QI 1 "ubicom32_arith_operand" "")) + (set (match_operand 2 "ubicom32_cc_register_operand" "") + (compare (match_operand:QI 3 "ubicom32_data_register_operand" "") + (match_dup 0))) + (set (pc) + (if_then_else (match_operator 4 "comparison_operator" + [(match_dup 2) + (const_int 0)]) + (label_ref (match_operand 5 "" "")) + (pc)))] + "(peep2_reg_dead_p (2, operands[0]) + && peep2_regno_dead_p (3, CC_REGNO))" + [(set (match_dup 2) + (compare (match_dup 1) + (match_dup 3))) + (set (pc) + (if_then_else (match_op_dup 6 + [(match_dup 2) + (const_int 0)]) + (label_ref (match_dup 5)) + (pc)))] + "{ + rtx cc_reg; + + cc_reg = gen_rtx_REG (GET_MODE (operands[2]), CC_REGNO); + operands[6] = gen_rtx_fmt_ee (swap_condition (GET_CODE (operands[4])), + GET_MODE (operands[4]), + cc_reg, + const0_rtx); + }") + +(define_insn "tsthi_ext2" + [(set (reg CC_REGNO) + (compare (match_operand:HI 0 "nonimmediate_operand" "rm") + (const_int 0)))] + "ubicom32_match_cc_mode(insn, CCSZNmode)" + "ext.2\\t#0, %0") + +(define_expand "cmphi" + [(set (reg CC_REGNO) + (compare (match_operand:HI 0 "ubicom32_arith_operand" "") + (match_operand:HI 1 "ubicom32_compare_operand" "")))] + "" + "{ + do + { + /* Is this a cmpi? */ + if (CONST_INT_P (operands[1])) + break; + + /* Must be a sub.2 - if necessary copy an operand into a reg. */ + if (! ubicom32_data_register_operand (operands[1], HImode)) + operands[1] = copy_to_mode_reg (HImode, operands[1]); + } + while (0); + + ubicom32_compare_op0 = operands[0]; + ubicom32_compare_op1 = operands[1]; + DONE; + }") + +(define_insn "cmpi" + [(set (reg CC_REGNO) + (compare (match_operand:HI 0 "nonimmediate_operand" "rm") + (match_operand 1 "const_int_operand" "N")))] + "" + "cmpi\\t%0, %1") + +(define_insn "sub2_ccs" + [(set (reg CC_REGNO) + (compare (match_operand:HI 0 "ubicom32_arith_operand" "rmI") + (match_operand:HI 1 "ubicom32_data_register_operand" "d")))] + "" + "sub.2\\t#0, %0, %1") + +; If we're testing for equality we don't have to worry about reversing conditions. +; +(define_insn "sub2_ccsz_1" + [(set (reg:CCSZ CC_REGNO) + (compare:CCSZ (match_operand:HI 0 "nonimmediate_operand" "rm") + (match_operand:HI 1 "ubicom32_data_register_operand" "d")))] + "" + "sub.2\\t#0, %0, %1") + +(define_insn "sub2_ccsz_2" + [(set (reg:CCSZ CC_REGNO) + (compare:CCSZ (match_operand:HI 0 "ubicom32_data_register_operand" "d") + (match_operand:HI 1 "ubicom32_arith_operand" "rmI")))] + "" + "sub.2\\t#0, %1, %0") + +; When the combiner runs it doesn't have any insight into whether or not an argument +; to a compare is spilled to the stack and therefore can't swap the comparison in +; an attempt to use sub.2 more effectively. We peephole this case here. +; +(define_peephole2 + [(set (match_operand:HI 0 "register_operand" "") + (match_operand:HI 1 "ubicom32_arith_operand" "")) + (set (match_operand 2 "ubicom32_cc_register_operand" "") + (compare (match_operand:HI 3 "ubicom32_data_register_operand" "") + (match_dup 0))) + (set (pc) + (if_then_else (match_operator 4 "comparison_operator" + [(match_dup 2) + (const_int 0)]) + (label_ref (match_operand 5 "" "")) + (pc)))] + "(peep2_reg_dead_p (2, operands[0]) + && peep2_regno_dead_p (3, CC_REGNO))" + [(set (match_dup 2) + (compare (match_dup 1) + (match_dup 3))) + (set (pc) + (if_then_else (match_op_dup 6 + [(match_dup 2) + (const_int 0)]) + (label_ref (match_dup 5)) + (pc)))] + "{ + rtx cc_reg; + + cc_reg = gen_rtx_REG (GET_MODE (operands[2]), CC_REGNO); + operands[6] = gen_rtx_fmt_ee (swap_condition (GET_CODE (operands[4])), + GET_MODE (operands[4]), + cc_reg, + const0_rtx); + }") + +(define_insn_and_split "tstsi_lsl4" + [(set (match_operand 0 "ubicom32_cc_register_operand" "=r") + (match_operator 1 "ubicom32_compare_operator" + [(match_operand:SI 2 "nonimmediate_operand" "rm") + (const_int 0)]))] + "ubicom32_match_cc_mode(insn, CCWZNmode)" + "#" + "ubicom32_match_cc_mode(insn, CCWZNmode)" + [(parallel + [(set (match_dup 0) + (match_op_dup 1 + [(match_dup 2) + (const_int 0)])) + (clobber (match_dup 3))])] + "{ + operands[3] = gen_reg_rtx (SImode); + }") + +(define_insn "tstsi_lsl4_d" + [(set (reg CC_REGNO) + (compare (match_operand:SI 0 "nonimmediate_operand" "rm") + (const_int 0))) + (clobber (match_operand:SI 1 "ubicom32_data_register_operand" "=d"))] + "ubicom32_match_cc_mode(insn, CCWZNmode)" + "lsl.4\\t%1, %0, #0") + +; Comparison for equality with -1. +; +(define_insn "cmpsi_not4_ccwz" + [(set (reg CC_REGNO) + (compare (match_operand:SI 0 "nonimmediate_operand" "rm") + (const_int -1)))] + "ubicom32_match_cc_mode(insn, CCWZmode)" + "not.4\\t#0, %0") + +(define_expand "cmpsi" + [(set (reg CC_REGNO) + (compare (match_operand:SI 0 "ubicom32_arith_operand" "") + (match_operand:SI 1 "ubicom32_compare_operand" "")))] + "" + "{ + do + { + /* Is this a cmpi? We can't take a memory address as cmpi takes + 16-bit operands. */ + if (register_operand (operands[0], SImode) + && CONST_INT_P (operands[1]) + && satisfies_constraint_N (operands[1])) + break; + + /* Must be a sub.4 - if necessary copy an operand into a reg. */ + if (! ubicom32_data_register_operand (operands[1], SImode)) + operands[1] = copy_to_mode_reg (SImode, operands[1]); + } + while (0); + + ubicom32_compare_op0 = operands[0]; + ubicom32_compare_op1 = operands[1]; + DONE; + }") + +(define_insn "cmpsi_cmpi" + [(set (reg CC_REGNO) + (compare (match_operand:SI 0 "register_operand" "r") + (match_operand 1 "const_int_operand" "N")))] + "(satisfies_constraint_N (operands[1]))" + "cmpi\\t%0, %1") + +(define_insn "cmpsi_sub4" + [(set (reg CC_REGNO) + (compare (match_operand:SI 0 "ubicom32_arith_operand" "rmI") + (match_operand:SI 1 "ubicom32_data_register_operand" "d")))] + "" + "sub.4\\t#0, %0, %1") + +; If we're testing for equality we don't have to worry about reversing conditions. +; +(define_insn "cmpsi_sub4_ccwz_1" + [(set (reg CC_REGNO) + (compare (match_operand:SI 0 "nonimmediate_operand" "rm") + (match_operand:SI 1 "ubicom32_data_register_operand" "d")))] + "ubicom32_match_cc_mode(insn, CCWZmode)" + "sub.4\\t#0, %0, %1") + +(define_insn "cmpsi_sub4_ccwz_2" + [(set (reg CC_REGNO) + (compare (match_operand:SI 0 "ubicom32_data_register_operand" "d") + (match_operand:SI 1 "nonimmediate_operand" "rm")))] + "ubicom32_match_cc_mode(insn, CCWZmode)" + "sub.4\\t#0, %1, %0") + +; When the combiner runs it doesn't have any insight into whether or not an argument +; to a compare is spilled to the stack and therefore can't swap the comparison in +; an attempt to use sub.4 more effectively. We peephole this case here. +; +(define_peephole2 + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "ubicom32_arith_operand" "")) + (set (match_operand 2 "ubicom32_cc_register_operand" "") + (compare (match_operand:SI 3 "ubicom32_data_register_operand" "") + (match_dup 0))) + (set (pc) + (if_then_else (match_operator 4 "comparison_operator" + [(match_dup 2) + (const_int 0)]) + (label_ref (match_operand 5 "" "")) + (pc)))] + "(peep2_reg_dead_p (2, operands[0]) + && peep2_regno_dead_p (3, CC_REGNO))" + [(set (match_dup 2) + (compare (match_dup 1) + (match_dup 3))) + (set (pc) + (if_then_else (match_op_dup 6 + [(match_dup 2) + (const_int 0)]) + (label_ref (match_dup 5)) + (pc)))] + "{ + rtx cc_reg; + + cc_reg = gen_rtx_REG (GET_MODE (operands[2]), CC_REGNO); + operands[6] = gen_rtx_fmt_ee (swap_condition (GET_CODE (operands[4])), + GET_MODE (operands[4]), + cc_reg, + const0_rtx); + }") + +(define_insn_and_split "tstdi_or4" + [(set (reg:CCWZ CC_REGNO) + (compare:CCWZ (match_operand:DI 0 "nonimmediate_operand" "rm") + (const_int 0)))] + "" + "#" + "" + [(parallel + [(set (reg:CCWZ CC_REGNO) + (compare:CCWZ (match_dup 0) + (const_int 0))) + (clobber (match_dup 1))])] + "{ + operands[1] = gen_reg_rtx (SImode); + }") + +(define_insn "tstdi_or4_d" + [(set (reg:CCWZ CC_REGNO) + (compare:CCWZ (match_operand:DI 0 "nonimmediate_operand" "rm") + (const_int 0))) + (clobber (match_operand:SI 1 "ubicom32_data_register_operand" "=d"))] + "" + "* + { + operands[2] = gen_lowpart (SImode, operands[0]); + operands[3] = gen_highpart_mode (SImode, DImode, operands[0]); + + if (ubicom32_data_register_operand (operands[0], GET_MODE (operands[0]))) + return \"or.4\\t#0, %2, %3\"; + + return \"move.4\\t%1, %2\;or.4\\t%1, %3, %1\"; + }" + [(set_attr "length" "8")]) + +(define_expand "cmpdi" + [(set (reg CC_REGNO) + (compare (match_operand:DI 0 "ubicom32_arith_operand" "") + (match_operand:DI 1 "ubicom32_data_register_operand" "")))] + "" + "{ + ubicom32_compare_op0 = operands[0]; + ubicom32_compare_op1 = operands[1]; + DONE; + }") + +(define_insn "cmpdi_sub4subc" + [(set (reg CC_REGNO) + (compare (match_operand:DI 0 "ubicom32_arith_operand" "rmI") + (match_operand:DI 1 "ubicom32_data_register_operand" "d")))] + "" + "* + { + operands[2] = gen_lowpart (SImode, operands[0]); + operands[3] = gen_lowpart (SImode, operands[1]); + operands[4] = gen_highpart_mode (SImode, DImode, operands[0]); + operands[5] = gen_highpart_mode (SImode, DImode, operands[1]); + + return \"sub.4\\t#0, %2, %3\;subc\\t#0, %4, %5\"; + }" + [(set_attr "length" "8")]) + +; When the combiner runs it doesn't have any insight into whether or not an argument +; to a compare is spilled to the stack and therefore can't swap the comparison in +; an attempt to use sub.4/subc more effectively. We peephole this case here. +; +(define_peephole2 + [(set (match_operand:DI 0 "register_operand" "") + (match_operand:DI 1 "ubicom32_arith_operand" "")) + (set (match_operand 2 "ubicom32_cc_register_operand" "") + (compare (match_operand:DI 3 "ubicom32_data_register_operand" "") + (match_dup 0))) + (set (pc) + (if_then_else (match_operator 4 "comparison_operator" + [(match_dup 2) + (const_int 0)]) + (label_ref (match_operand 5 "" "")) + (pc)))] + "(peep2_reg_dead_p (2, operands[0]) + && peep2_regno_dead_p (3, CC_REGNO))" + [(set (match_dup 2) + (compare (match_dup 1) + (match_dup 3))) + (set (pc) + (if_then_else (match_op_dup 6 + [(match_dup 2) + (const_int 0)]) + (label_ref (match_dup 5)) + (pc)))] + "{ + rtx cc_reg; + + cc_reg = gen_rtx_REG (GET_MODE (operands[2]), CC_REGNO); + operands[6] = gen_rtx_fmt_ee (swap_condition (GET_CODE (operands[4])), + GET_MODE (operands[4]), + cc_reg, + const0_rtx); + }") + +(define_insn "btst" + [(set (reg:CCWZ CC_REGNO) + (compare:CCWZ + (zero_extract:SI + (match_operand:SI 0 "nonimmediate_operand" "rm") + (const_int 1) + (match_operand:SI 1 "ubicom32_arith_operand" "dM")) + (const_int 0)))] + "" + "btst\\t%0, %1") + +(define_insn "bfextu_ccwz_null" + [(set (reg:CCWZ CC_REGNO) + (compare:CCWZ + (zero_extract:SI + (match_operand:SI 0 "nonimmediate_operand" "rm") + (match_operand 1 "const_int_operand" "M") + (const_int 0)) + (const_int 0))) + (clobber (match_scratch:SI 2 "=d"))] + "" + "bfextu\\t%2, %0, %1") + +(define_expand "addqi3" + [(parallel + [(set (match_operand:QI 0 "memory_operand" "") + (plus:QI (match_operand:QI 1 "nonimmediate_operand" "") + (match_operand:QI 2 "ubicom32_arith_operand" ""))) + (clobber (reg:CC CC_REGNO))])] + "(ubicom32_v4)" + "{ + if (!memory_operand (operands[0], QImode)) + FAIL; + + /* If we have a non-data reg for operand 1 then prefer that over + a CONST_INT in operand 2. */ + if (! ubicom32_data_register_operand (operands[1], GET_MODE (operands[1])) + && CONST_INT_P (operands[2])) + operands[2] = copy_to_mode_reg (QImode, operands[2]); + + if (CONST_INT_P (operands[2]) && ! satisfies_constraint_I (operands[2])) + operands[2] = copy_to_mode_reg (QImode, operands[2]); + }") + +(define_insn "addqi3_add1" + [(set (match_operand:QI 0 "memory_operand" "=m, m") + (plus:QI (match_operand:QI 1 "nonimmediate_operand" "%d,rm") + (match_operand:QI 2 "ubicom32_arith_operand" "rmI, d"))) + (clobber (reg:CC CC_REGNO))] + "(ubicom32_v4)" + "@ + add.1\\t%0, %2, %1 + add.1\\t%0, %1, %2") + +(define_insn "addqi3_add1_ccszn_null" + [(set (reg CC_REGNO) + (compare + (neg:QI (match_operand:QI 0 "nonimmediate_operand" "%d,rm")) + (match_operand:QI 1 "ubicom32_arith_operand" "rmI, d")))] + "(ubicom32_v4 + && ubicom32_match_cc_mode(insn, CCSZNmode))" + "@ + add.1\\t#0, %1, %0 + add.1\\t#0, %0, %1") + +(define_expand "addhi3" + [(parallel + [(set (match_operand:HI 0 "memory_operand" "") + (plus:HI (match_operand:HI 1 "nonimmediate_operand" "") + (match_operand:HI 2 "ubicom32_arith_operand" ""))) + (clobber (reg:CC CC_REGNO))])] + "" + "{ + if (!memory_operand (operands[0], HImode)) + FAIL; + + /* If we have a non-data reg for operand 1 then prefer that over + a CONST_INT in operand 2. */ + if (! ubicom32_data_register_operand (operands[1], GET_MODE (operands[1])) + && CONST_INT_P (operands[2])) + operands[2] = copy_to_mode_reg (HImode, operands[2]); + + if (CONST_INT_P (operands[2]) && ! satisfies_constraint_I (operands[2])) + operands[2] = copy_to_mode_reg (HImode, operands[2]); + }") + +(define_insn "addhi3_add2" + [(set (match_operand:HI 0 "memory_operand" "=m, m") + (plus:HI (match_operand:HI 1 "nonimmediate_operand" "%d,rm") + (match_operand:HI 2 "ubicom32_arith_operand" "rmI, d"))) + (clobber (reg:CC CC_REGNO))] + "" + "@ + add.2\\t%0, %2, %1 + add.2\\t%0, %1, %2") + +(define_insn "addhi3_add2_ccszn_null" + [(set (reg CC_REGNO) + (compare + (neg:HI (match_operand:HI 0 "nonimmediate_operand" "%d,rm")) + (match_operand:HI 1 "ubicom32_arith_operand" "rmI, d")))] + "ubicom32_match_cc_mode(insn, CCSZNmode)" + "@ + add.2\\t#0, %1, %0 + add.2\\t#0, %0, %1") + +(define_expand "addsi3" + [(set (match_operand:SI 0 "nonimmediate_operand" "") + (plus:SI (match_operand:SI 1 "nonimmediate_operand" "") + (match_operand:SI 2 "ubicom32_move_operand" "")))] + "" + "{ + ubicom32_expand_addsi3 (operands); + DONE; + }") + +; We start with an instruction pattern that can do all sorts of interesting +; things but we split out any uses of lea or pdec instructions because +; those instructions don't clobber the condition codes. +; +(define_insn_and_split "addsi3_1" + [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,rm,rm,rm,rm, rm,rm") + (plus:SI (match_operand:SI 1 "nonimmediate_operand" "%a, a, a, a, a, d,rm") + (match_operand:SI 2 "ubicom32_move_operand" "L, K, J, P, d,rmI, d"))) + (clobber (reg:CC CC_REGNO))] + "" + "@ + # + # + # + # + # + add.4\\t%0, %2, %1 + add.4\\t%0, %1, %2" + "(reload_completed + && ubicom32_address_register_operand (operands[1], GET_MODE (operands[1])))" + [(set (match_dup 0) + (plus:SI (match_dup 1) + (match_dup 2)))] + "" +) + +(define_insn "addsi3_1_ccwzn" + [(set (reg CC_REGNO) + (compare + (plus:SI (match_operand:SI 1 "nonimmediate_operand" "%d,rm") + (match_operand:SI 2 "ubicom32_arith_operand" "rmI, d")) + (const_int 0))) + (set (match_operand:SI 0 "nonimmediate_operand" "=rm,rm") + (plus:SI (match_dup 1) + (match_dup 2)))] + "ubicom32_match_cc_mode(insn, CCWZNmode)" + "@ + add.4\\t%0, %2, %1 + add.4\\t%0, %1, %2") + +(define_insn "addsi3_1_ccwzn_null" + [(set (reg CC_REGNO) + (compare + (neg:SI (match_operand:SI 0 "nonimmediate_operand" "%d,rm")) + (match_operand:SI 1 "ubicom32_arith_operand" "rmI, d")))] + "ubicom32_match_cc_mode(insn, CCWZNmode)" + "@ + add.4\\t#0, %1, %0 + add.4\\t#0, %0, %1") + +(define_insn_and_split "addsi3_2" + [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,rm,rm,rm,rm,rm") + (plus:SI (match_operand:SI 1 "ubicom32_address_register_operand" "%a, a, a, a, a, a") + (match_operand:SI 2 "ubicom32_move_operand" "L, K, J, P, d, n")))] + "" + "@ + lea.4\\t%0, %E2(%1) + lea.2\\t%0, %E2(%1) + lea.1\\t%0, %E2(%1) + pdec\\t%0, %n2(%1) + lea.1\\t%0, (%1,%2) + #" + "(reload_completed + && ! satisfies_constraint_L (operands[2]) + && ! satisfies_constraint_K (operands[2]) + && ! satisfies_constraint_J (operands[2]) + && ! satisfies_constraint_P (operands[2]) + && ! ubicom32_data_register_operand (operands[2], GET_MODE (operands[2])))" + [(set (reg:SI AUX_DATA_REGNO) + (match_dup 2)) + (set (match_dup 0) + (plus:SI (match_dup 1) + (reg:SI AUX_DATA_REGNO)))] + "" +) + +(define_insn "lea_2" + [(set (match_operand:SI 0 "nonimmediate_operand" "=rm") + (plus:SI (mult:SI (match_operand:SI 1 "ubicom32_data_register_operand" "d") + (const_int 2)) + (match_operand:SI 2 "ubicom32_address_register_operand" "a")))] + "" + "lea.2\\t%0, (%2,%1)") + +(define_insn "lea_4" + [(set (match_operand:SI 0 "nonimmediate_operand" "=rm") + (plus:SI (mult:SI (match_operand:SI 1 "ubicom32_data_register_operand" "d") + (const_int 4)) + (match_operand:SI 2 "ubicom32_address_register_operand" "a")))] + "" + "lea.4\\t%0, (%2,%1)") + +(define_expand "adddi3" + [(parallel + [(set (match_operand:DI 0 "nonimmediate_operand" "") + (plus:DI (match_operand:DI 1 "nonimmediate_operand" "") + (match_operand:DI 2 "ubicom32_arith_operand" ""))) + (clobber (reg:CC CC_REGNO))])] + "" + "{ + /* If we have a non-data reg for operand 1 then prefer that over + a CONST_INT in operand 2. */ + if (! ubicom32_data_register_operand (operands[1], GET_MODE (operands[1])) + && CONST_INT_P (operands[2])) + operands[2] = copy_to_mode_reg (DImode, operands[2]); + + if (CONST_INT_P (operands[2]) && ! satisfies_constraint_I (operands[2])) + operands[2] = copy_to_mode_reg (DImode, operands[2]); + }") + +; We construct a 64-bit add from 32-bit operations. Note that we use the +; & constraint to prevent overlapping registers being allocated. We do +; allow identical registers though as that won't break anything. +; +(define_insn "adddi3_add4addc" + [(set (match_operand:DI 0 "nonimmediate_operand" "=&r,&r,rm, d, m, m") + (plus:DI (match_operand:DI 1 "nonimmediate_operand" "%d,rm, 0, 0, d,rm") + (match_operand:DI 2 "ubicom32_arith_operand" "rmI, d, d,rmI,rmI, d"))) + (clobber (reg:CC CC_REGNO))] + "" + "* + { + operands[3] = gen_lowpart (SImode, operands[0]); + operands[4] = gen_lowpart (SImode, operands[1]); + operands[5] = gen_lowpart (SImode, operands[2]); + operands[6] = gen_highpart (SImode, operands[0]); + operands[7] = gen_highpart (SImode, operands[1]); + operands[8] = gen_highpart_mode (SImode, DImode, operands[2]); + + if (ubicom32_data_register_operand (operands[2], GET_MODE (operands[2]))) + return \"add.4\\t%3, %4, %5\;addc\\t%6, %7, %8\"; + + return \"add.4\\t%3, %5, %4\;addc\\t%6, %8, %7\"; + }" + [(set_attr "length" "8")]) + +(define_insn "adddi3_ccwz" + [(set (reg CC_REGNO) + (compare + (plus:DI (match_operand:DI 1 "nonimmediate_operand" "%d,rm, 0, 0, d,rm") + (match_operand:DI 2 "ubicom32_arith_operand" "rmI, d, d,rmI,rmI, d")) + (const_int 0))) + (set (match_operand:DI 0 "nonimmediate_operand" "=&r,&r,rm, d, m, m") + (plus:DI (match_dup 1) + (match_dup 2)))] + "ubicom32_match_cc_mode(insn, CCWZNmode)" + "* + { + operands[3] = gen_lowpart (SImode, operands[0]); + operands[6] = gen_highpart (SImode, operands[0]); + + if (ubicom32_data_register_operand (operands[1], GET_MODE (operands[1]))) + { + operands[4] = gen_lowpart (SImode, operands[1]); + operands[5] = gen_lowpart (SImode, operands[2]); + operands[7] = gen_highpart (SImode, operands[1]); + operands[8] = gen_highpart_mode (SImode, DImode, operands[2]); + } + else + { + operands[4] = gen_lowpart (SImode, operands[2]); + operands[5] = gen_lowpart (SImode, operands[1]); + operands[7] = gen_highpart (SImode, operands[2]); + operands[8] = gen_highpart (SImode, operands[1]); + } + + return \"add.4\\t%3, %5, %4\;addc\\t%6, %8, %7\"; + }" + [(set_attr "length" "8")]) + +(define_insn "adddi3_ccwz_null" + [(set (reg CC_REGNO) + (compare + (neg:DI (match_operand:DI 0 "nonimmediate_operand" "%d,rm")) + (match_operand:DI 1 "ubicom32_arith_operand" "rmI, d")))] + "ubicom32_match_cc_mode(insn, CCWZNmode)" + "* + { + if (ubicom32_data_register_operand (operands[0], GET_MODE (operands[0]))) + { + operands[2] = gen_lowpart (SImode, operands[0]); + operands[3] = gen_lowpart (SImode, operands[1]); + operands[4] = gen_highpart (SImode, operands[0]); + operands[5] = gen_highpart_mode (SImode, DImode, operands[1]); + } + else + { + operands[2] = gen_lowpart (SImode, operands[1]); + operands[3] = gen_lowpart (SImode, operands[0]); + operands[4] = gen_highpart (SImode, operands[1]); + operands[5] = gen_highpart (SImode, operands[0]); + } + + return \"add.4\\t#0, %3, %2\;addc\\t#0, %5, %4\"; + }" + [(set_attr "length" "8")]) + +(define_expand "subqi3" + [(parallel + [(set (match_operand:QI 0 "memory_operand" "") + (minus:QI (match_operand:QI 1 "ubicom32_arith_operand" "") + (match_operand:QI 2 "ubicom32_data_register_operand" ""))) + (clobber (reg:CC CC_REGNO))])] + "(ubicom32_v4)" + "{ + if (!memory_operand (operands[0], QImode)) + FAIL; + }") + +(define_insn "subqi3_sub1" + [(set (match_operand:QI 0 "memory_operand" "=m") + (minus:QI (match_operand:QI 1 "ubicom32_arith_operand" "rmI") + (match_operand:QI 2 "ubicom32_data_register_operand" "d"))) + (clobber (reg:CC CC_REGNO))] + "(ubicom32_v4)" + "sub.1\\t%0, %1, %2") + +(define_expand "subhi3" + [(parallel + [(set (match_operand:HI 0 "memory_operand" "") + (minus:HI (match_operand:HI 1 "ubicom32_arith_operand" "") + (match_operand:HI 2 "ubicom32_data_register_operand" ""))) + (clobber (reg:CC CC_REGNO))])] + "(ubicom32_v4)" + "{ + if (!memory_operand (operands[0], HImode)) + FAIL; + }") + +(define_insn "subhi3_sub2" + [(set (match_operand:HI 0 "memory_operand" "=m") + (minus:HI (match_operand:HI 1 "ubicom32_arith_operand" "rmI") + (match_operand:HI 2 "ubicom32_data_register_operand" "d"))) + (clobber (reg:CC CC_REGNO))] + "" + "sub.2\\t%0, %1, %2") + +(define_insn "subsi3" + [(set (match_operand:SI 0 "nonimmediate_operand" "=rm") + (minus:SI (match_operand:SI 1 "ubicom32_arith_operand" "rmI") + (match_operand:SI 2 "ubicom32_data_register_operand" "d"))) + (clobber (reg:CC CC_REGNO))] + "" + "sub.4\\t%0, %1, %2") + +(define_insn "subsi3_ccwz" + [(set (reg CC_REGNO) + (compare + (minus:SI (match_operand:SI 1 "ubicom32_arith_operand" "rmI") + (match_operand:SI 2 "ubicom32_data_register_operand" "d")) + (const_int 0))) + (set (match_operand:SI 0 "nonimmediate_operand" "=rm") + (minus:SI (match_dup 1) + (match_dup 2)))] + "ubicom32_match_cc_mode(insn, CCWZNmode)" + "sub.4\\t%0, %1, %2") + +; We construct a 64-bit add from 32-bit operations. Note that we use the +; & constraint to prevent overlapping registers being allocated. We do +; allow identical registers though as that won't break anything. +; +(define_insn "subdi3" + [(set (match_operand:DI 0 "nonimmediate_operand" "=&r,r, d, m") + (minus:DI (match_operand:DI 1 "ubicom32_arith_operand" "rmI,0,rmI,rmI") + (match_operand:DI 2 "ubicom32_data_register_operand" "d,d, 0, d"))) + (clobber (reg:CC CC_REGNO))] + "" + "* + { + operands[3] = gen_lowpart (SImode, operands[0]); + operands[4] = gen_lowpart (SImode, operands[1]); + operands[5] = gen_lowpart (SImode, operands[2]); + operands[6] = gen_highpart (SImode, operands[0]); + operands[7] = gen_highpart_mode (SImode, DImode, operands[1]); + operands[8] = gen_highpart (SImode, operands[2]); + + return \"sub.4\\t%3, %4, %5\;subc\\t%6, %7, %8\"; + }" + [(set_attr "length" "8")]) + +(define_insn "subdi3_ccwz" + [(set (reg CC_REGNO) + (compare + (minus:DI (match_operand:DI 1 "ubicom32_arith_operand" "rmI,rmI") + (match_operand:DI 2 "ubicom32_data_register_operand" "d, d")) + (const_int 0))) + (set (match_operand:DI 0 "nonimmediate_operand" "=&r, m") + (minus:DI (match_dup 1) + (match_dup 2)))] + "ubicom32_match_cc_mode(insn, CCWZNmode)" + "* + { + operands[3] = gen_lowpart (SImode, operands[0]); + operands[4] = gen_lowpart (SImode, operands[1]); + operands[5] = gen_lowpart (SImode, operands[2]); + operands[6] = gen_highpart (SImode, operands[0]); + operands[7] = gen_highpart_mode (SImode, DImode, operands[1]); + operands[8] = gen_highpart (SImode, operands[2]); + + return \"sub.4\\t%3, %4, %5\;subc\\t%6, %7, %8\"; + }" + [(set_attr "length" "8")]) + +;(define_insn "negqi2" +; [(set (match_operand:QI 0 "nonimmediate_operand" "=rm") +; (neg:QI (match_operand:QI 1 "ubicom32_data_register_operand" "d"))) +; (clobber (reg:CC CC_REGNO))] +; "(ubicom32_v4)" +; "sub.1\\t%0, #0, %1") + +;(define_insn "neghi2" +; [(set (match_operand:HI 0 "nonimmediate_operand" "=rm") +; (neg:HI (match_operand:HI 1 "ubicom32_data_register_operand" "d"))) +; (clobber (reg:CC CC_REGNO))] +; "" +; "sub.2\\t%0, #0, %1") + +(define_insn "negsi2" + [(set (match_operand:SI 0 "nonimmediate_operand" "=rm") + (neg:SI (match_operand:SI 1 "ubicom32_data_register_operand" "d"))) + (clobber (reg:CC CC_REGNO))] + "" + "sub.4\\t%0, #0, %1") + +(define_insn_and_split "negdi2" + [(set (match_operand:DI 0 "nonimmediate_operand" "=&rm") + (neg:DI (match_operand:DI 1 "ubicom32_data_register_operand" "d"))) + (clobber (reg:CC CC_REGNO))] + "" + "#" + "reload_completed" + [(parallel [(set (match_dup 0) + (minus:DI (const_int 0) + (match_dup 1))) + (clobber (reg:CC CC_REGNO))])] + "" + [(set_attr "length" "8")]) + +(define_insn "umulhisi3" + [(set (match_operand:SI 0 "ubicom32_acc_lo_register_operand" "=l, l") + (mult:SI + (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "%d,rm")) + (zero_extend:SI (match_operand:HI 2 "nonimmediate_operand" "rm, d")))) + (clobber (reg:HI ACC0_HI_REGNO)) + (clobber (reg:HI ACC1_HI_REGNO))] + "" + "@ + mulu\\t%A0, %2, %1 + mulu\\t%A0, %1, %2" + [(set_attr "type" "mul,mul")]) + +(define_insn "mulhisi3" + [(set (match_operand:SI 0 "ubicom32_acc_lo_register_operand" "=l, l") + (mult:SI + (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "%d,rm")) + (sign_extend:SI (match_operand:HI 2 "nonimmediate_operand" "rm, d")))) + (clobber (reg:HI ACC0_HI_REGNO)) + (clobber (reg:HI ACC1_HI_REGNO))] + "" + "@ + muls\\t%A0, %2, %1 + muls\\t%A0, %1, %2" + [(set_attr "type" "mul,mul")]) + +(define_expand "mulsi3" + [(set (match_operand:SI 0 "ubicom32_acc_hi_register_operand" "") + (mult:SI (match_operand:SI 1 "ubicom32_arith_operand" "") + (match_operand:SI 2 "ubicom32_arith_operand" "")))] + "" + "{ + if (ubicom32_emit_mult_sequence (operands)) + DONE; + }") + +(define_insn "umulsidi3" + [(set (match_operand:DI 0 "ubicom32_acc_hi_register_operand" "=h, h") + (mult:DI + (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "%d,rm")) + (zero_extend:DI (match_operand:SI 2 "nonimmediate_operand" "rm, d"))))] + "(ubicom32_v4)" + "@ + mulu.4\\t%A0, %2, %1 + mulu.4\\t%A0, %1, %2" + [(set_attr "type" "mul,mul")]) + +(define_peephole2 + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "nonimmediate_operand" "")) + (set (match_operand:DI 2 "ubicom32_acc_hi_register_operand" "") + (mult:DI + (zero_extend:DI (match_dup 0)) + (zero_extend:DI (match_operand:SI 3 "ubicom32_data_register_operand" ""))))] + "(peep2_reg_dead_p (2, operands[0]) + || REGNO (operands[0]) == REGNO (operands[2]) + || REGNO (operands[0]) == REGNO (operands[2]) + 1) + && ! rtx_equal_p (operands[0], operands[3])" + [(set (match_dup 2) + (mult:DI + (zero_extend:DI (match_dup 1)) + (zero_extend:DI (match_dup 3))))] + "") + +(define_peephole2 + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "nonimmediate_operand" "")) + (set (match_operand:DI 2 "ubicom32_acc_hi_register_operand" "") + (mult:DI + (zero_extend:DI (match_operand:SI 3 "ubicom32_data_register_operand" "")) + (zero_extend:DI (match_dup 0))))] + "(peep2_reg_dead_p (2, operands[0]) + || REGNO (operands[0]) == REGNO (operands[2]) + || REGNO (operands[0]) == REGNO (operands[2]) + 1) + && ! rtx_equal_p (operands[0], operands[3])" + [(set (match_dup 2) + (mult:DI + (zero_extend:DI (match_dup 1)) + (zero_extend:DI (match_dup 3))))] + "") + +(define_insn "umulsidi3_const" + [(set (match_operand:DI 0 "ubicom32_acc_hi_register_operand" "=h") + (mult:DI + (zero_extend:DI (match_operand:SI 1 "ubicom32_data_register_operand" "%d")) + (match_operand 2 "const_int_operand" "I")))] + "(ubicom32_v4 && satisfies_constraint_I (operands[2]))" + "mulu.4\\t%A0, %2, %1" + [(set_attr "type" "mul")]) + +(define_insn "mulsidi3" + [(set (match_operand:DI 0 "ubicom32_acc_hi_register_operand" "=h, h") + (mult:DI + (sign_extend:DI (match_operand:SI 1 "nonimmediate_operand" "%d,rm")) + (sign_extend:DI (match_operand:SI 2 "nonimmediate_operand" "rm, d"))))] + "(ubicom32_v4)" + "@ + muls.4\\t%A0, %2, %1 + muls.4\\t%A0, %1, %2" + [(set_attr "type" "mul,mul")]) + +(define_peephole2 + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "nonimmediate_operand" "")) + (set (match_operand:DI 2 "ubicom32_acc_hi_register_operand" "") + (mult:DI + (sign_extend:DI (match_dup 0)) + (sign_extend:DI (match_operand:SI 3 "ubicom32_data_register_operand" ""))))] + "(peep2_reg_dead_p (2, operands[0]) + || REGNO (operands[0]) == REGNO (operands[2]) + || REGNO (operands[0]) == REGNO (operands[2]) + 1) + && ! rtx_equal_p (operands[0], operands[3])" + [(set (match_dup 2) + (mult:DI + (sign_extend:DI (match_dup 1)) + (sign_extend:DI (match_dup 3))))] + "") + +(define_peephole2 + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "nonimmediate_operand" "")) + (set (match_operand:DI 2 "ubicom32_acc_hi_register_operand" "") + (mult:DI + (sign_extend:DI (match_operand:SI 3 "ubicom32_data_register_operand" "")) + (sign_extend:DI (match_dup 0))))] + "(peep2_reg_dead_p (2, operands[0]) + || REGNO (operands[0]) == REGNO (operands[2]) + || REGNO (operands[0]) == REGNO (operands[2]) + 1) + && ! rtx_equal_p (operands[0], operands[3])" + [(set (match_dup 2) + (mult:DI + (sign_extend:DI (match_dup 1)) + (sign_extend:DI (match_dup 3))))] + "") + +(define_insn "mulsidi3_const" + [(set (match_operand:DI 0 "ubicom32_acc_hi_register_operand" "=h") + (mult:DI + (sign_extend:DI (match_operand:SI 1 "ubicom32_data_register_operand" "%d")) + (match_operand 2 "const_int_operand" "I")))] + "(ubicom32_v4 && satisfies_constraint_I (operands[2]))" + "muls.4\\t%A0, %2, %1" + [(set_attr "type" "mul")]) + +(define_expand "andqi3" + [(parallel + [(set (match_operand:QI 0 "memory_operand" "") + (and:QI (match_operand:QI 1 "nonimmediate_operand" "") + (match_operand:QI 2 "ubicom32_arith_operand" ""))) + (clobber (reg:CC CC_REGNO))])] + "(ubicom32_v4)" + "{ + if (!memory_operand (operands[0], QImode)) + FAIL; + + /* If we have a non-data reg for operand 1 then prefer that over + a CONST_INT in operand 2. */ + if (! ubicom32_data_register_operand (operands[1], GET_MODE (operands[1])) + && CONST_INT_P (operands[2])) + operands[2] = copy_to_mode_reg (QImode, operands[2]); + + if (CONST_INT_P (operands[2]) && ! satisfies_constraint_I (operands[2])) + operands[2] = copy_to_mode_reg (QImode, operands[2]); + }") + +(define_insn "andqi3_and1" + [(set (match_operand:QI 0 "memory_operand" "=m, m") + (and:QI (match_operand:QI 1 "nonimmediate_operand" "%d,rm") + (match_operand:QI 2 "ubicom32_arith_operand" "rmI, d"))) + (clobber (reg:CC CC_REGNO))] + "(ubicom32_v4)" + "@ + and.1\\t%0, %2, %1 + and.1\\t%0, %1, %2") + +(define_insn "andqi3_and1_ccszn" + [(set (reg CC_REGNO) + (compare + (and:QI (match_operand:QI 1 "nonimmediate_operand" "%d,rm") + (match_operand:QI 2 "ubicom32_arith_operand" "rmI, d")) + (const_int 0))) + (set (match_operand:QI 0 "memory_operand" "=m, m") + (and:QI (match_dup 1) + (match_dup 2)))] + "(ubicom32_v4 + && ubicom32_match_cc_mode(insn, CCSZNmode))" + "@ + and.1\\t%0, %2, %1 + and.1\\t%0, %1, %2") + +(define_insn "andqi3_and1_ccszn_null" + [(set (reg CC_REGNO) + (compare + (and:QI (match_operand:QI 0 "nonimmediate_operand" "%d,rm") + (match_operand:QI 1 "ubicom32_arith_operand" "rmI, d")) + (const_int 0)))] + "(ubicom32_v4 + && ubicom32_match_cc_mode(insn, CCSZNmode))" + "@ + and.1\\t#0, %1, %0 + and.1\\t#0, %0, %1") + +(define_insn "and1_ccszn_null_1" + [(set (reg CC_REGNO) + (compare + (subreg:QI + (and:SI (match_operand:SI 0 "ubicom32_data_register_operand" "%d") + (match_operand:SI 1 "ubicom32_arith_operand" "rI")) + 3) + (const_int 0)))] + "(ubicom32_v4 + && ubicom32_match_cc_mode(insn, CCSZNmode))" + "and.1\\t#0, %1, %0") + +(define_insn "and1_ccszn_null_2" + [(set (reg CC_REGNO) + (compare + (subreg:QI + (and:SI (match_operand:SI 0 "ubicom32_data_register_operand" "d") + (subreg:SI + (match_operand:QI 1 "memory_operand" "m") + 0)) + 3) + (const_int 0)))] + "(ubicom32_v4 + && ubicom32_match_cc_mode(insn, CCSZNmode))" + "and.1\\t#0, %1, %0") + +(define_insn "and1_ccszn_null_3" + [(set (reg CC_REGNO) + (compare + (subreg:QI + (and:SI (subreg:SI + (match_operand:QI 0 "memory_operand" "m") + 0) + (match_operand:SI 1 "ubicom32_data_register_operand" "d")) + 3) + (const_int 0)))] + "(ubicom32_v4 + && ubicom32_match_cc_mode(insn, CCSZNmode))" + "and.1\\t#0, %0, %1") + +(define_expand "andhi3" + [(parallel + [(set (match_operand:HI 0 "memory_operand" "") + (and:HI (match_operand:HI 1 "nonimmediate_operand" "") + (match_operand:HI 2 "ubicom32_arith_operand" ""))) + (clobber (reg:CC CC_REGNO))])] + "" + "{ + if (!memory_operand (operands[0], HImode)) + FAIL; + + /* If we have a non-data reg for operand 1 then prefer that over + a CONST_INT in operand 2. */ + if (! ubicom32_data_register_operand (operands[1], GET_MODE (operands[1])) + && CONST_INT_P (operands[2])) + operands[2] = copy_to_mode_reg (HImode, operands[2]); + + if (CONST_INT_P (operands[2]) && ! satisfies_constraint_I (operands[2])) + operands[2] = copy_to_mode_reg (HImode, operands[2]); + }") + +(define_insn "andhi3_and2" + [(set (match_operand:HI 0 "memory_operand" "=m, m") + (and:HI (match_operand:HI 1 "nonimmediate_operand" "%d,rm") + (match_operand:HI 2 "ubicom32_arith_operand" "rmI, d"))) + (clobber (reg:CC CC_REGNO))] + "" + "@ + and.2\\t%0, %2, %1 + and.2\\t%0, %1, %2") + +(define_insn "andhi3_and2_ccszn" + [(set (reg CC_REGNO) + (compare + (and:HI (match_operand:HI 1 "nonimmediate_operand" "%d,rm") + (match_operand:HI 2 "ubicom32_arith_operand" "rmI, d")) + (const_int 0))) + (set (match_operand:HI 0 "memory_operand" "=m, m") + (and:HI (match_dup 1) + (match_dup 2)))] + "ubicom32_match_cc_mode(insn, CCSZNmode)" + "@ + and.2\\t%0, %2, %1 + and.2\\t%0, %1, %2") + +(define_insn "andhi3_and2_ccszn_null" + [(set (reg CC_REGNO) + (compare + (and:HI (match_operand:HI 0 "nonimmediate_operand" "%d,rm") + (match_operand:HI 1 "ubicom32_arith_operand" "rmI, d")) + (const_int 0)))] + "ubicom32_match_cc_mode(insn, CCSZNmode)" + "@ + and.2\\t#0, %1, %0 + and.2\\t#0, %0, %1") + +(define_insn "and2_ccszn_null_1" + [(set (reg CC_REGNO) + (compare + (subreg:HI + (and:SI (match_operand:SI 0 "ubicom32_data_register_operand" "%d") + (match_operand:SI 1 "ubicom32_arith_operand" "rI")) + 2) + (const_int 0)))] + "ubicom32_match_cc_mode(insn, CCSZNmode)" + "and.2\\t#0, %1, %0") + +(define_insn "and2_ccszn_null_2" + [(set (reg CC_REGNO) + (compare + (subreg:HI + (and:SI (match_operand:SI 0 "ubicom32_data_register_operand" "d") + (subreg:SI + (match_operand:HI 1 "memory_operand" "m") + 0)) + 2) + (const_int 0)))] + "ubicom32_match_cc_mode(insn, CCSZNmode)" + "and.2\\t#0, %1, %0") + +(define_insn "and2_ccszn_null_3" + [(set (reg CC_REGNO) + (compare + (subreg:HI + (and:SI (subreg:SI + (match_operand:HI 0 "memory_operand" "m") + 0) + (match_operand:SI 1 "ubicom32_data_register_operand" "d")) + 2) + (const_int 0)))] + "ubicom32_match_cc_mode(insn, CCSZNmode)" + "and.2\\t#0, %0, %1") + +(define_expand "andsi3" + [(parallel + [(set (match_operand:SI 0 "nonimmediate_operand" "") + (and:SI (match_operand:SI 1 "nonimmediate_operand" "") + (match_operand:SI 2 "ubicom32_and_or_si3_operand" ""))) + (clobber (reg:CC CC_REGNO))])] + "" + "{ + do + { + /* Is this a bfextu? */ + if (ubicom32_data_register_operand (operands[0], SImode) + && CONST_INT_P (operands[2]) + && exact_log2 (INTVAL (operands[2]) + 1) != -1) + break; + + /* Is this a bclr? */ + if (CONST_INT_P (operands[2]) + && exact_log2 (~INTVAL (operands[2])) != -1) + break; + + /* Must be an and.4 */ + if (!ubicom32_data_register_operand (operands[1], SImode)) + operands[1] = copy_to_mode_reg (SImode, operands[1]); + + if (!ubicom32_arith_operand (operands[2], SImode)) + operands[2] = copy_to_mode_reg (SImode, operands[2]); + } + while (0); + }") + +(define_insn "andsi3_bfextu" + [(set (match_operand:SI 0 "ubicom32_data_register_operand" "=d") + (and:SI (match_operand:SI 1 "nonimmediate_operand" "%rm") + (match_operand:SI 2 "const_int_operand" "O"))) + (clobber (reg:CC CC_REGNO))] + "(satisfies_constraint_O (operands[2]))" + "* + { + operands[3] = GEN_INT (exact_log2 (INTVAL (operands[2]) + 1)); + + return \"bfextu\\t%0, %1, %3\"; + }") + +(define_insn "andsi3_bfextu_ccwz" + [(set (reg CC_REGNO) + (compare + (and:SI (match_operand:SI 1 "nonimmediate_operand" "%rm") + (match_operand:SI 2 "const_int_operand" "O")) + (const_int 0))) + (set (match_operand:SI 0 "ubicom32_data_register_operand" "=d") + (and:SI (match_dup 1) + (match_dup 2)))] + "(satisfies_constraint_O (operands[2]) + && ubicom32_match_cc_mode(insn, CCWZmode))" + "* + { + operands[3] = GEN_INT (exact_log2 (INTVAL (operands[2]) + 1)); + + return \"bfextu\\t%0, %1, %3\"; + }") + +(define_insn "andsi3_bfextu_ccwz_null" + [(set (reg CC_REGNO) + (compare + (and:SI (match_operand:SI 0 "nonimmediate_operand" "%rm") + (match_operand:SI 1 "const_int_operand" "O")) + (const_int 0))) + (clobber (match_scratch:SI 2 "=d"))] + "(satisfies_constraint_O (operands[1]) + && ubicom32_match_cc_mode(insn, CCWZmode))" + "* + { + operands[3] = GEN_INT (exact_log2 (INTVAL (operands[1]) + 1)); + + return \"bfextu\\t%2, %0, %3\"; + }") + +(define_insn "andsi3_bclr" + [(set (match_operand:SI 0 "nonimmediate_operand" "=rm") + (and:SI (match_operand:SI 1 "ubicom32_arith_operand" "%rmI") + (match_operand:SI 2 "const_int_operand" "n"))) + (clobber (reg:CC CC_REGNO))] + "(exact_log2 (~INTVAL (operands[2])) != -1)" + "bclr\\t%0, %1, #%D2") + +(define_insn "andsi3_and4" + [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,rm") + (and:SI (match_operand:SI 1 "nonimmediate_operand" "%d,rm") + (match_operand:SI 2 "ubicom32_arith_operand" "rmI, d"))) + (clobber (reg:CC CC_REGNO))] + "" + "@ + and.4\\t%0, %2, %1 + and.4\\t%0, %1, %2") + +(define_insn "andsi3_and4_ccwzn" + [(set (reg CC_REGNO) + (compare + (and:SI (match_operand:SI 1 "nonimmediate_operand" "%d,rm") + (match_operand:SI 2 "ubicom32_arith_operand" "rmI, d")) + (const_int 0))) + (set (match_operand:SI 0 "nonimmediate_operand" "=rm,rm") + (and:SI (match_dup 1) + (match_dup 2)))] + "ubicom32_match_cc_mode(insn, CCWZNmode)" + "@ + and.4\\t%0, %2, %1 + and.4\\t%0, %1, %2") + +(define_insn "andsi3_and4_ccwzn_null" + [(set (reg CC_REGNO) + (compare + (and:SI (match_operand:SI 0 "nonimmediate_operand" "%d,rm") + (match_operand:SI 1 "ubicom32_arith_operand" "rmI, d")) + (const_int 0)))] + "ubicom32_match_cc_mode(insn, CCWZNmode)" + "@ + and.4\\t#0, %1, %0 + and.4\\t#0, %0, %1") + +(define_insn "andsi3_lsr4_ccwz_null" + [(set (reg CC_REGNO) + (compare + (and:SI (match_operand:SI 0 "nonimmediate_operand" "%rm") + (match_operand:SI 1 "const_int_operand" "n")) + (const_int 0))) + (clobber (match_scratch:SI 2 "=d"))] + "(exact_log2 ((~(INTVAL (operands[1]))) + 1) != -1 + && ubicom32_match_cc_mode(insn, CCWZmode))" + "* + { + operands[3] = GEN_INT (exact_log2 ((~(INTVAL (operands[1]))) + 1)); + + return \"lsr.4\\t%2, %0, %3\"; + }") + +; We really would like the combiner to recognize this scenario and deal with +; it but unfortunately it tries to canonicalize zero_extract ops on MEMs +; into QImode operations and we can't match them in any useful way. +; +(define_peephole2 + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "const_int_operand" "")) + (set (reg:CCWZ CC_REGNO) + (compare:CCWZ + (and:SI (match_operand:SI 2 "nonimmediate_operand" "") + (match_dup 0)) + (const_int 0)))] + "(exact_log2 (INTVAL (operands[1])) != -1 + && peep2_reg_dead_p (2, operands[0]))" + [(set (reg:CCWZ CC_REGNO) + (compare:CCWZ + (zero_extract:SI + (match_dup 2) + (const_int 1) + (match_dup 3)) + (const_int 0)))] + "{ + operands[3] = GEN_INT (exact_log2 (INTVAL (operands[1]))); + }") + +(define_expand "anddi3" + [(parallel + [(set (match_operand:DI 0 "nonimmediate_operand" "") + (and:DI (match_operand:DI 1 "nonimmediate_operand" "") + (match_operand:DI 2 "ubicom32_arith_operand" ""))) + (clobber (reg:CC CC_REGNO))])] + "" + "{ + /* If we have a non-data reg for operand 1 then prefer that over + a CONST_INT in operand 2. */ + if (! ubicom32_data_register_operand (operands[1], GET_MODE (operands[1])) + && CONST_INT_P (operands[2])) + operands[2] = copy_to_mode_reg (DImode, operands[2]); + + if (CONST_INT_P (operands[2]) && ! satisfies_constraint_I (operands[2])) + operands[2] = copy_to_mode_reg (DImode, operands[2]); + }") + +(define_insn_and_split "anddi3_and4" + [(set (match_operand:DI 0 "nonimmediate_operand" "=&r,&r, d,rm, m, m") + (and:DI (match_operand:DI 1 "nonimmediate_operand" "%d,rm, 0, 0, d,rm") + (match_operand:DI 2 "ubicom32_arith_operand" "rmI, d,rmI, d,rmI, d"))) + (clobber (reg:CC CC_REGNO))] + "" + "#" + "reload_completed" + [(parallel [(set (match_dup 3) + (and:SI (match_dup 4) + (match_dup 5))) + (clobber (reg:CC CC_REGNO))]) + (parallel [(set (match_dup 6) + (and:SI (match_dup 7) + (match_dup 8))) + (clobber (reg:CC CC_REGNO))])] + "{ + operands[3] = gen_lowpart (SImode, operands[0]); + operands[4] = gen_lowpart (SImode, operands[1]); + operands[5] = gen_lowpart (SImode, operands[2]); + operands[6] = gen_highpart (SImode, operands[0]); + operands[7] = gen_highpart (SImode, operands[1]); + operands[8] = gen_highpart_mode (SImode, DImode, operands[2]); + }" + [(set_attr "length" "8")]) + +(define_expand "iorqi3" + [(parallel + [(set (match_operand:QI 0 "memory_operand" "") + (ior:QI (match_operand:QI 1 "nonimmediate_operand" "") + (match_operand:QI 2 "ubicom32_arith_operand" ""))) + (clobber (reg:CC CC_REGNO))])] + "(ubicom32_v4)" + "{ + if (!memory_operand (operands[0], QImode)) + FAIL; + + /* If we have a non-data reg for operand 1 then prefer that over + a CONST_INT in operand 2. */ + if (! ubicom32_data_register_operand (operands[1], GET_MODE (operands[1])) + && CONST_INT_P (operands[2])) + operands[2] = copy_to_mode_reg (QImode, operands[2]); + + if (CONST_INT_P (operands[2]) && ! satisfies_constraint_I (operands[2])) + operands[2] = copy_to_mode_reg (QImode, operands[2]); + }") + +(define_insn "iorqi3_or1" + [(set (match_operand:QI 0 "memory_operand" "=m, m") + (ior:QI (match_operand:QI 1 "nonimmediate_operand" "%d,rm") + (match_operand:QI 2 "ubicom32_arith_operand" "rmI, d"))) + (clobber (reg:CC CC_REGNO))] + "(ubicom32_v4)" + "@ + or.1\\t%0, %2, %1 + or.1\\t%0, %1, %2") + +(define_expand "iorhi3" + [(parallel + [(set (match_operand:HI 0 "memory_operand" "") + (ior:HI (match_operand:HI 1 "nonimmediate_operand" "") + (match_operand:HI 2 "ubicom32_arith_operand" ""))) + (clobber (reg:CC CC_REGNO))])] + "" + "{ + if (!memory_operand (operands[0], HImode)) + FAIL; + + /* If we have a non-data reg for operand 1 then prefer that over + a CONST_INT in operand 2. */ + if (! ubicom32_data_register_operand (operands[1], GET_MODE (operands[1])) + && CONST_INT_P (operands[2])) + operands[2] = copy_to_mode_reg (HImode, operands[2]); + + if (CONST_INT_P (operands[2]) && ! satisfies_constraint_I (operands[2])) + operands[2] = copy_to_mode_reg (HImode, operands[2]); + }") + +(define_insn "iorhi3_or2" + [(set (match_operand:HI 0 "memory_operand" "=m, m") + (ior:HI (match_operand:HI 1 "nonimmediate_operand" "%d,rm") + (match_operand:HI 2 "ubicom32_arith_operand" "rmI, d"))) + (clobber (reg:CC CC_REGNO))] + "" + "@ + or.2\\t%0, %2, %1 + or.2\\t%0, %1, %2") + +(define_expand "iorsi3" + [(parallel + [(set (match_operand:SI 0 "nonimmediate_operand" "") + (ior:SI (match_operand:SI 1 "nonimmediate_operand" "") + (match_operand:SI 2 "ubicom32_and_or_si3_operand" ""))) + (clobber (reg:CC CC_REGNO))])] + "" + "{ + do + { + /* Is this a bset? */ + if (CONST_INT_P (operands[2]) + && exact_log2 (INTVAL (operands[2])) != -1) + break; + + /* Must be an or.4 */ + if (!ubicom32_data_register_operand (operands[1], SImode)) + operands[1] = copy_to_mode_reg (SImode, operands[1]); + + if (!ubicom32_arith_operand (operands[2], SImode)) + operands[2] = copy_to_mode_reg (SImode, operands[2]); + } + while (0); + }") + +(define_insn "iorsi3_bset" + [(set (match_operand:SI 0 "nonimmediate_operand" "=rm") + (ior:SI (match_operand:SI 1 "ubicom32_arith_operand" "%rmI") + (match_operand 2 "const_int_operand" "n"))) + (clobber (reg:CC CC_REGNO))] + "(exact_log2 (INTVAL (operands[2])) != -1)" + "bset\\t%0, %1, #%d2") + +(define_insn "iorsi3_or4" + [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,rm") + (ior:SI (match_operand:SI 1 "nonimmediate_operand" "%d,rm") + (match_operand:SI 2 "ubicom32_arith_operand" "rmI, d"))) + (clobber (reg:CC CC_REGNO))] + "" + "@ + or.4\\t%0, %2, %1 + or.4\\t%0, %1, %2") + +(define_insn "iorsi3_ccwzn" + [(set (reg CC_REGNO) + (compare + (ior:SI (match_operand:SI 1 "nonimmediate_operand" "%d,rm") + (match_operand:SI 2 "ubicom32_arith_operand" "rmI, d")) + (const_int 0))) + (set (match_operand:SI 0 "nonimmediate_operand" "=rm,rm") + (ior:SI (match_dup 1) + (match_dup 2)))] + "ubicom32_match_cc_mode(insn, CCWZNmode)" + "@ + or.4\\t%0, %2, %1 + or.4\\t%0, %1, %2") + +(define_insn "iorsi3_ccwzn_null" + [(set (reg CC_REGNO) + (compare + (ior:SI (match_operand:SI 0 "nonimmediate_operand" "%d,rm") + (match_operand:SI 1 "ubicom32_arith_operand" "rmI, d")) + (const_int 0)))] + "ubicom32_match_cc_mode(insn, CCWZNmode)" + "@ + or.4\\t#0, %1, %0 + or.4\\t#0, %0, %1") + +(define_expand "iordi3" + [(parallel + [(set (match_operand:DI 0 "nonimmediate_operand" "") + (ior:DI (match_operand:DI 1 "nonimmediate_operand" "") + (match_operand:DI 2 "ubicom32_arith_operand" ""))) + (clobber (reg:CC CC_REGNO))])] + "" + "{ + /* If we have a non-data reg for operand 1 then prefer that over + a CONST_INT in operand 2. */ + if (! ubicom32_data_register_operand (operands[1], GET_MODE (operands[1])) + && CONST_INT_P (operands[2])) + operands[2] = copy_to_mode_reg (DImode, operands[2]); + + if (CONST_INT_P (operands[2]) && ! satisfies_constraint_I (operands[2])) + operands[2] = copy_to_mode_reg (DImode, operands[2]); + }") + +(define_insn_and_split "iordi3_or4" + [(set (match_operand:DI 0 "nonimmediate_operand" "=&r,&r, d,rm, m, m") + (ior:DI (match_operand:DI 1 "nonimmediate_operand" "%d,rm, 0, 0, d,rm") + (match_operand:DI 2 "ubicom32_arith_operand" "rmI, d,rmI, d,rmI, d"))) + (clobber (reg:CC CC_REGNO))] + "" + "#" + "reload_completed" + [(parallel [(set (match_dup 3) + (ior:SI (match_dup 4) + (match_dup 5))) + (clobber (reg:CC CC_REGNO))]) + (parallel [(set (match_dup 6) + (ior:SI (match_dup 7) + (match_dup 8))) + (clobber (reg:CC CC_REGNO))])] + "{ + operands[3] = gen_lowpart (SImode, operands[0]); + operands[4] = gen_lowpart (SImode, operands[1]); + operands[5] = gen_lowpart (SImode, operands[2]); + operands[6] = gen_highpart (SImode, operands[0]); + operands[7] = gen_highpart (SImode, operands[1]); + operands[8] = gen_highpart_mode (SImode, DImode, operands[2]); + }" + [(set_attr "length" "8")]) + +(define_expand "xorqi3" + [(parallel + [(set (match_operand:QI 0 "memory_operand" "") + (xor:QI (match_operand:QI 1 "nonimmediate_operand" "") + (match_operand:QI 2 "ubicom32_arith_operand" ""))) + (clobber (reg:CC CC_REGNO))])] + "(ubicom32_v4)" + "{ + if (!memory_operand (operands[0], QImode)) + FAIL; + + /* If we have a non-data reg for operand 1 then prefer that over + a CONST_INT in operand 2. */ + if (! ubicom32_data_register_operand (operands[1], GET_MODE (operands[1])) + && CONST_INT_P (operands[2])) + operands[2] = copy_to_mode_reg (QImode, operands[2]); + + if (CONST_INT_P (operands[2]) && ! satisfies_constraint_I (operands[2])) + operands[2] = copy_to_mode_reg (QImode, operands[2]); + }") + +(define_insn "xorqi3_xor1" + [(set (match_operand:QI 0 "memory_operand" "=m, m") + (xor:QI (match_operand:QI 1 "nonimmediate_operand" "%d,rm") + (match_operand:QI 2 "ubicom32_arith_operand" "rmI, d"))) + (clobber (reg:CC CC_REGNO))] + "(ubicom32_v4)" + "@ + xor.1\\t%0, %2, %1 + xor.1\\t%0, %1, %2") + +(define_insn "xorqi3_xor1_ccszn" + [(set (reg CC_REGNO) + (compare + (xor:QI (match_operand:QI 1 "nonimmediate_operand" "%d,rm") + (match_operand:QI 2 "ubicom32_arith_operand" "rmI, d")) + (const_int 0))) + (set (match_operand:QI 0 "memory_operand" "=m, m") + (xor:QI (match_dup 1) + (match_dup 2)))] + "(ubicom32_v4 + && ubicom32_match_cc_mode(insn, CCSZNmode))" + "@ + xor.1\\t%0, %2, %1 + xor.1\\t%0, %1, %2") + +(define_insn "xorqi3_xor1_ccszn_null" + [(set (reg CC_REGNO) + (compare + (xor:QI (match_operand:QI 0 "nonimmediate_operand" "%d,rm") + (match_operand:QI 1 "ubicom32_arith_operand" "rmI, d")) + (const_int 0)))] + "(ubicom32_v4 + && ubicom32_match_cc_mode(insn, CCSZNmode))" + "@ + xor.1\\t#0, %1, %0 + xor.1\\t#0, %0, %1") + +(define_insn "xor1_ccszn_null_1" + [(set (reg CC_REGNO) + (compare + (subreg:QI + (xor:SI (match_operand:SI 0 "ubicom32_data_register_operand" "%d") + (match_operand:SI 1 "ubicom32_arith_operand" "rI")) + 3) + (const_int 0)))] + "(ubicom32_v4 + && ubicom32_match_cc_mode(insn, CCSZNmode))" + "xor.1\\t#0, %1, %0") + +(define_insn "xor1_ccszn_null_2" + [(set (reg CC_REGNO) + (compare + (subreg:QI + (xor:SI (match_operand:SI 0 "ubicom32_data_register_operand" "d") + (subreg:SI + (match_operand:QI 1 "memory_operand" "m") + 0)) + 3) + (const_int 0)))] + "(ubicom32_v4 + && ubicom32_match_cc_mode(insn, CCSZNmode))" + "xor.1\\t#0, %1, %0") + +(define_insn "xor1_ccwzn_null_3" + [(set (reg CC_REGNO) + (compare + (subreg:QI + (xor:SI (subreg:SI + (match_operand:QI 0 "memory_operand" "m") + 0) + (match_operand:SI 1 "ubicom32_data_register_operand" "d")) + 3) + (const_int 0)))] + "(ubicom32_v4 + && ubicom32_match_cc_mode(insn, CCSZNmode))" + "xor.1\\t#0, %0, %1") + +(define_expand "xorhi3" + [(parallel + [(set (match_operand:HI 0 "memory_operand" "") + (xor:HI (match_operand:HI 1 "nonimmediate_operand" "") + (match_operand:HI 2 "ubicom32_arith_operand" ""))) + (clobber (reg:CC CC_REGNO))])] + "" + "{ + if (!memory_operand (operands[0], HImode)) + FAIL; + + /* If we have a non-data reg for operand 1 then prefer that over + a CONST_INT in operand 2. */ + if (! ubicom32_data_register_operand (operands[1], GET_MODE (operands[1])) + && CONST_INT_P (operands[2])) + operands[2] = copy_to_mode_reg (HImode, operands[2]); + + if (CONST_INT_P (operands[2]) && ! satisfies_constraint_I (operands[2])) + operands[2] = copy_to_mode_reg (HImode, operands[2]); + }") + +(define_insn "xorhi3_xor2" + [(set (match_operand:HI 0 "memory_operand" "=m, m") + (xor:HI (match_operand:HI 1 "nonimmediate_operand" "%d,rm") + (match_operand:HI 2 "ubicom32_arith_operand" "rmI, d"))) + (clobber (reg:CC CC_REGNO))] + "" + "@ + xor.2\\t%0, %2, %1 + xor.2\\t%0, %1, %2") + +(define_insn "xorhi3_xor2_ccszn" + [(set (reg CC_REGNO) + (compare + (xor:HI (match_operand:HI 1 "nonimmediate_operand" "%d,rm") + (match_operand:HI 2 "ubicom32_arith_operand" "rmI, d")) + (const_int 0))) + (set (match_operand:HI 0 "memory_operand" "=m, m") + (xor:HI (match_dup 1) + (match_dup 2)))] + "ubicom32_match_cc_mode(insn, CCSZNmode)" + "@ + xor.2\\t%0, %2, %1 + xor.2\\t%0, %1, %2") + +(define_insn "xorhi3_xor2_ccszn_null" + [(set (reg CC_REGNO) + (compare + (xor:HI (match_operand:HI 0 "nonimmediate_operand" "%d,rm") + (match_operand:HI 1 "ubicom32_arith_operand" "rmI, d")) + (const_int 0)))] + "ubicom32_match_cc_mode(insn, CCSZNmode)" + "@ + xor.2\\t#0, %1, %0 + xor.2\\t#0, %0, %1") + +(define_insn "xor2_ccszn_null_1" + [(set (reg CC_REGNO) + (compare + (subreg:HI + (xor:SI (match_operand:SI 0 "ubicom32_data_register_operand" "%d") + (match_operand:SI 1 "ubicom32_arith_operand" "rI")) + 2) + (const_int 0)))] + "ubicom32_match_cc_mode(insn, CCSZNmode)" + "xor.2\\t#0, %1, %0") + +(define_insn "xor2_ccszn_null_2" + [(set (reg CC_REGNO) + (compare + (subreg:HI + (xor:SI (match_operand:SI 0 "ubicom32_data_register_operand" "d") + (subreg:SI + (match_operand:HI 1 "memory_operand" "m") + 0)) + 2) + (const_int 0)))] + "ubicom32_match_cc_mode(insn, CCSZNmode)" + "xor.2\\t#0, %1, %0") + +(define_insn "xor2_ccszn_null_3" + [(set (reg CC_REGNO) + (compare + (subreg:HI + (xor:SI (subreg:SI + (match_operand:HI 0 "memory_operand" "m") + 0) + (match_operand:SI 1 "ubicom32_data_register_operand" "d")) + 2) + (const_int 0)))] + "ubicom32_match_cc_mode(insn, CCSZNmode)" + "xor.2\\t#0, %0, %1") + +(define_insn "xorsi3" + [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,rm") + (xor:SI (match_operand:SI 1 "nonimmediate_operand" "%d,rm") + (match_operand:SI 2 "ubicom32_arith_operand" "rmI, d"))) + (clobber (reg:CC CC_REGNO))] + "" + "@ + xor.4\\t%0, %2, %1 + xor.4\\t%0, %1, %2") + +(define_insn "xorsi3_ccwzn" + [(set (reg CC_REGNO) + (compare + (xor:SI (match_operand:SI 1 "nonimmediate_operand" "%d,rm") + (match_operand:SI 2 "ubicom32_arith_operand" "rmI, d")) + (const_int 0))) + (set (match_operand:SI 0 "nonimmediate_operand" "=rm,rm") + (xor:SI (match_dup 1) + (match_dup 2)))] + "ubicom32_match_cc_mode(insn, CCWZNmode)" + "@ + xor.4\\t%0, %2, %1 + xor.4\\t%0, %1, %2") + +(define_insn "xorsi3_ccwzn_null" + [(set (reg CC_REGNO) + (compare + (xor:SI (match_operand:SI 0 "nonimmediate_operand" "%d,rm") + (match_operand:SI 1 "ubicom32_arith_operand" "rmI, d")) + (const_int 0)))] + "ubicom32_match_cc_mode(insn, CCWZNmode)" + "@ + xor.4\\t#0, %1, %0 + xor.4\\t#0, %0, %1") + +(define_expand "xordi3" + [(parallel + [(set (match_operand:DI 0 "nonimmediate_operand" "") + (xor:DI (match_operand:DI 1 "nonimmediate_operand" "") + (match_operand:DI 2 "ubicom32_arith_operand" ""))) + (clobber (reg:CC CC_REGNO))])] + "" + "{ + /* If we have a non-data reg for operand 1 then prefer that over + a CONST_INT in operand 2. */ + if (! ubicom32_data_register_operand (operands[1], GET_MODE (operands[1])) + && CONST_INT_P (operands[2])) + operands[2] = copy_to_mode_reg (DImode, operands[2]); + + if (CONST_INT_P (operands[2]) && ! satisfies_constraint_I (operands[2])) + operands[2] = copy_to_mode_reg (DImode, operands[2]); + }") + +(define_insn_and_split "xordi3_xor4" + [(set (match_operand:DI 0 "nonimmediate_operand" "=&r,&r, d,rm, m, m") + (xor:DI (match_operand:DI 1 "nonimmediate_operand" "%d,rm, 0, 0, d,rm") + (match_operand:DI 2 "ubicom32_arith_operand" "rmI, d,rmI, d,rmI, d"))) + (clobber (reg:CC CC_REGNO))] + "" + "#" + "reload_completed" + [(parallel [(set (match_dup 3) + (xor:SI (match_dup 4) + (match_dup 5))) + (clobber (reg:CC CC_REGNO))]) + (parallel [(set (match_dup 6) + (xor:SI (match_dup 7) + (match_dup 8))) + (clobber (reg:CC CC_REGNO))])] + "{ + operands[3] = gen_lowpart (SImode, operands[0]); + operands[4] = gen_lowpart (SImode, operands[1]); + operands[5] = gen_lowpart (SImode, operands[2]); + operands[6] = gen_highpart (SImode, operands[0]); + operands[7] = gen_highpart (SImode, operands[1]); + operands[8] = gen_highpart_mode (SImode, DImode, operands[2]); + }" + [(set_attr "length" "8")]) + +(define_insn "not2_2" + [(set (match_operand:HI 0 "memory_operand" "=m") + (subreg:HI + (not:SI (match_operand:SI 1 "ubicom32_arith_operand" "rmI")) + 2)) + (clobber (reg:CC CC_REGNO))] + "" + "not.2\\t%0, %1") + +(define_insn "one_cmplsi2" + [(set (match_operand:SI 0 "nonimmediate_operand" "=rm") + (not:SI (match_operand:SI 1 "ubicom32_arith_operand" "rmI"))) + (clobber (reg:CC CC_REGNO))] + "" + "not.4\\t%0, %1") + +(define_insn "one_cmplsi2_ccwzn" + [(set (reg CC_REGNO) + (compare + (not:SI (match_operand:SI 1 "ubicom32_arith_operand" "rmI")) + (const_int 0))) + (set (match_operand:SI 0 "nonimmediate_operand" "=rm") + (not:SI (match_dup 1)))] + "ubicom32_match_cc_mode(insn, CCWZNmode)" + "not.4\\t%0, %1") + +(define_insn "one_cmplsi2_ccwzn_null" + [(set (reg CC_REGNO) + (compare + (not:SI (match_operand:SI 0 "ubicom32_arith_operand" "rmI")) + (const_int 0)))] + "ubicom32_match_cc_mode(insn, CCWZNmode)" + "not.4\\t#0, %0") + +(define_insn_and_split "one_cmpldi2" + [(set (match_operand:DI 0 "nonimmediate_operand" "=&rm") + (not:DI (match_operand:DI 1 "nonimmediate_operand" "rmI0"))) + (clobber (reg:CC CC_REGNO))] + "" + "#" + "" + [(parallel [(set (match_dup 2) + (not:SI (match_dup 3))) + (clobber (reg:CC CC_REGNO))]) + (parallel [(set (match_dup 4) + (not:SI (match_dup 5))) + (clobber (reg:CC CC_REGNO))])] + "{ + operands[2] = gen_lowpart (SImode, operands[0]); + operands[3] = gen_lowpart (SImode, operands[1]); + operands[4] = gen_highpart (SImode, operands[0]); + operands[5] = gen_highpart (SImode, operands[1]); + }" + [(set_attr "length" "8")]) + +; Conditional jump instructions + +(define_expand "beq" + [(set (pc) + (if_then_else (eq (match_dup 1) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "{ + operands[1] = ubicom32_gen_compare_reg (EQ, ubicom32_compare_op0, + ubicom32_compare_op1); + }") + +(define_expand "bne" + [(set (pc) + (if_then_else (ne (match_dup 1) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "{ + operands[1] = ubicom32_gen_compare_reg (NE, ubicom32_compare_op0, + ubicom32_compare_op1); + }") + +(define_expand "bgt" + [(set (pc) + (if_then_else (gt (match_dup 1) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "{ + operands[1] = ubicom32_gen_compare_reg (GT, ubicom32_compare_op0, + ubicom32_compare_op1); + }") + +(define_expand "ble" + [(set (pc) + (if_then_else (le (match_dup 1) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "{ + operands[1] = ubicom32_gen_compare_reg (LE, ubicom32_compare_op0, + ubicom32_compare_op1); + }") + +(define_expand "bge" + [(set (pc) + (if_then_else (ge (match_dup 1) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "{ + operands[1] = ubicom32_gen_compare_reg (GE, ubicom32_compare_op0, + ubicom32_compare_op1); + }") + +(define_expand "blt" + [(set (pc) + (if_then_else (lt (match_dup 1) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "{ + operands[1] = ubicom32_gen_compare_reg (LT, ubicom32_compare_op0, + ubicom32_compare_op1); + }") + +(define_expand "bgtu" + [(set (pc) + (if_then_else (gtu (match_dup 1) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "{ + operands[1] = ubicom32_gen_compare_reg (GTU, ubicom32_compare_op0, + ubicom32_compare_op1); + }") + +(define_expand "bleu" + [(set (pc) + (if_then_else (leu (match_dup 1) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "{ + operands[1] = ubicom32_gen_compare_reg (LEU, ubicom32_compare_op0, + ubicom32_compare_op1); + }") + +(define_expand "bgeu" + [(set (pc) + (if_then_else (geu (match_dup 1) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "{ + operands[1] = ubicom32_gen_compare_reg (GEU, ubicom32_compare_op0, + ubicom32_compare_op1); + }") + +(define_expand "bltu" + [(set (pc) + (if_then_else (ltu (match_dup 1) + (const_int 0)) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "{ + operands[1] = ubicom32_gen_compare_reg (LTU, ubicom32_compare_op0, + ubicom32_compare_op1); + }") + +(define_insn "jcc" + [(set (pc) + (if_then_else (match_operator 1 "comparison_operator" + [(match_operand 2 "ubicom32_cc_register_operand" "") + (const_int 0)]) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "* + { + ubicom32_output_cond_jump (insn, operands[1], operands[0]); + return \"\"; + }") + +; Reverse branch - reverse our comparison condition so that we can +; branch in the opposite sense. +; +(define_insn_and_split "jcc_reverse" + [(set (pc) + (if_then_else (match_operator 1 "comparison_operator" + [(match_operand 2 "ubicom32_cc_register_operand" "") + (const_int 0)]) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "#" + "reload_completed" + [(set (pc) + (if_then_else (match_dup 3) + (label_ref (match_dup 0)) + (pc)))] + "{ + rtx cc_reg; + + cc_reg = gen_rtx_REG (GET_MODE (operands[2]), CC_REGNO); + operands[3] = gen_rtx_fmt_ee (reverse_condition (GET_CODE (operands[1])), + GET_MODE (operands[1]), + cc_reg, + const0_rtx); + }") + +(define_insn "jump" + [(set (pc) + (label_ref (match_operand 0 "" "")))] + "" + "jmpt\\t%l0") + +(define_expand "indirect_jump" + [(parallel [(set (pc) + (match_operand:SI 0 "register_operand" "")) + (clobber (match_dup 0))])] + "" + "") + +(define_insn "indirect_jump_internal" + [(set (pc) + (match_operand:SI 0 "register_operand" "a")) + (clobber (match_dup 0))] + "" + "calli\\t%0,0(%0)") + +; Program Space: The table contains instructions, typically jumps. +; CALL An,TABLE_SIZE(PC) ;An = Jump Table Base Address. +; ;An -> Here. +; LEA Ak, (An,Dn) ;Ak -> Table Entry +; JMP/CALL (Ak) + +(define_expand "tablejump" + [(parallel [(set (pc) + (match_operand:SI 0 "nonimmediate_operand" "")) + (use (label_ref (match_operand 1 "" "")))])] + "" + "") + +(define_insn "tablejump_internal" + [(set (pc) + (match_operand:SI 0 "nonimmediate_operand" "rm")) + (use (label_ref (match_operand 1 "" "")))] + "" + "ret\\t%0") + +; Call subroutine with no return value. +; +(define_expand "call" + [(call (match_operand:QI 0 "general_operand" "") + (match_operand:SI 1 "general_operand" ""))] + "" + "{ + if (TARGET_FDPIC) + { + ubicom32_expand_call_fdpic (operands); + DONE; + } + + if (! ubicom32_call_address_operand (XEXP (operands[0], 0), VOIDmode)) + XEXP (operands[0], 0) = force_reg (SImode, XEXP (operands[0], 0)); + }") + +; We expand to a simple form that doesn't clobber the link register and +; then split to a form that does. This allows the RTL optimizers that +; run before the splitter to have the opportunity to eliminate the call +; without marking A5 as being clobbered and this in turn avoids saves +; and returns in a number of cases. +; +(define_insn_and_split "call_1" + [(call (mem:QI (match_operand:SI 0 "ubicom32_call_address_operand" "a,S")) + (match_operand:SI 1 "general_operand" "g,g"))] + "! TARGET_FDPIC" + "#" + "" + [(parallel + [(call (mem:QI (match_dup 0)) + (match_dup 1)) + (clobber (reg:SI LINK_REGNO))])] + "") + +(define_insn "call_slow" + [(call (mem:QI (match_operand:SI 0 "ubicom32_call_address_operand" "a,S")) + (match_operand:SI 1 "general_operand" "g,g")) + (clobber (reg:SI LINK_REGNO))] + "(! TARGET_FDPIC && ! TARGET_FASTCALL)" + "@ + calli\\ta5, 0(%0) + moveai\\ta5, #%%hi(%C0)\;calli\\ta5, %%lo(%C0)(a5)") + +(define_insn "call_fast" + [(call (mem:QI (match_operand:SI 0 "ubicom32_call_address_operand" "a,S")) + (match_operand:SI 1 "general_operand" "g,g")) + (clobber (reg:SI LINK_REGNO))] + "(! TARGET_FDPIC && TARGET_FASTCALL)" + "@ + calli\\ta5, 0(%0) + call\\ta5, %C0") + +; We expand to a simple form that doesn't clobber the link register and +; then split to a form that does. This allows the RTL optimizers that +; run before the splitter to have the opportunity to eliminate the call +; without marking A5 as being clobbered and this in turn avoids saves +; and returns in a number of cases. +; +(define_insn_and_split "call_fdpic" + [(call (mem:QI (match_operand:SI 0 "ubicom32_call_address_operand" "a,S")) + (match_operand:SI 1 "general_operand" "g,g")) + (use (match_operand:SI 2 "ubicom32_fdpic_operand" "Z,Z"))] + "TARGET_FDPIC" + "#" + "" + [(parallel + [(call (mem:QI (match_dup 0)) + (match_dup 1)) + (use (match_dup 2)) + (clobber (reg:SI LINK_REGNO))])] + "") + +(define_insn "call_fdpic_clobber" + [(call (mem:QI (match_operand:SI 0 "ubicom32_call_address_operand" "a,S")) + (match_operand:SI 1 "general_operand" "g,g")) + (use (match_operand:SI 2 "ubicom32_fdpic_operand" "Z,Z")) + (clobber (reg:SI LINK_REGNO))] + "TARGET_FDPIC" + "@ + move.4\\ta5, 0(%0)\;move.4\\t%2, 4(%0)\;calli\\ta5, 0(a5) + call\\ta5, %C0") + +; Call subroutine, returning value in operand 0 +; (which must be a hard register). +; +(define_expand "call_value" + [(set (match_operand 0 "" "") + (call (match_operand:QI 1 "general_operand" "") + (match_operand:SI 2 "general_operand" "")))] + "" + "{ + if (TARGET_FDPIC) + { + ubicom32_expand_call_value_fdpic (operands); + DONE; + } + + if (! ubicom32_call_address_operand (XEXP (operands[1], 0), VOIDmode)) + XEXP (operands[1], 0) = force_reg (SImode, XEXP (operands[1], 0)); + }") + +; We expand to a simple form that doesn't clobber the link register and +; then split to a form that does. This allows the RTL optimizers that +; run before the splitter to have the opportunity to eliminate the call +; without marking A5 as being clobbered and this in turn avoids saves +; and returns in a number of cases. +; +(define_insn_and_split "call_value_1" + [(set (match_operand 0 "register_operand" "=r,r") + (call (mem:QI (match_operand:SI 1 "ubicom32_call_address_operand" "a,S")) + (match_operand:SI 2 "general_operand" "g,g")))] + "! TARGET_FDPIC" + "#" + "" + [(parallel + [(set (match_dup 0) + (call (mem:QI (match_dup 1)) + (match_dup 2))) + (clobber (reg:SI LINK_REGNO))])] + "") + +(define_insn "call_value_slow" + [(set (match_operand 0 "register_operand" "=r,r") + (call (mem:QI (match_operand:SI 1 "ubicom32_call_address_operand" "a,S")) + (match_operand:SI 2 "general_operand" "g,g"))) + (clobber (reg:SI LINK_REGNO))] + "(! TARGET_FDPIC && ! TARGET_FASTCALL)" + "@ + calli\\ta5, 0(%1) + moveai\\ta5, #%%hi(%C1)\;calli\\ta5, %%lo(%C1)(a5)") + +(define_insn "call_value_fast" + [(set (match_operand 0 "register_operand" "=r,r") + (call (mem:QI (match_operand:SI 1 "ubicom32_call_address_operand" "a,S")) + (match_operand:SI 2 "general_operand" "g,g"))) + (clobber (reg:SI LINK_REGNO))] + "(! TARGET_FDPIC && TARGET_FASTCALL)" + "@ + calli\\ta5, 0(%1) + call\\ta5, %C1") + +; We expand to a simple form that doesn't clobber the link register and +; then split to a form that does. This allows the RTL optimizers that +; run before the splitter to have the opportunity to eliminate the call +; without marking A5 as being clobbered and this in turn avoids saves +; and returns in a number of cases. +; +(define_insn_and_split "call_value_fdpic" + [(set (match_operand 0 "register_operand" "=r,r") + (call (mem:QI (match_operand:SI 1 "ubicom32_call_address_operand" "a,S")) + (match_operand:SI 2 "general_operand" "g,g"))) + (use (match_operand:SI 3 "ubicom32_fdpic_operand" "Z,Z"))] + "TARGET_FDPIC" + "#" + "" + [(parallel + [(set (match_dup 0) + (call (mem:QI (match_dup 1)) + (match_dup 2))) + (use (match_dup 3)) + (clobber (reg:SI LINK_REGNO))])] + "") + +(define_insn "call_value_fdpic_clobber" + [(set (match_operand 0 "register_operand" "=r,r") + (call (mem:QI (match_operand:SI 1 "ubicom32_call_address_operand" "a,S")) + (match_operand:SI 2 "general_operand" "g,g"))) + (use (match_operand:SI 3 "ubicom32_fdpic_operand" "Z,Z")) + (clobber (reg:SI LINK_REGNO))] + "TARGET_FDPIC" + "@ + move.4\\ta5, 0(%1)\;move.4\\t%3, 4(%1)\;calli\\ta5, 0(a5) + call\\ta5, %C1") + +(define_expand "untyped_call" + [(parallel [(call (match_operand 0 "" "") + (const_int 0)) + (match_operand 1 "" "") + (match_operand 2 "" "")])] + "" + "{ + int i; + + emit_call_insn (gen_call (operands[0], const0_rtx)); + + for (i = 0; i < XVECLEN (operands[2], 0); i++) + { + rtx set = XVECEXP (operands[2], 0, i); + emit_move_insn (SET_DEST (set), SET_SRC (set)); + } + DONE; + }") + +(define_insn "lsl1_1" + [(set (match_operand:SI 0 "ubicom32_data_register_operand" "=d") + (ashift:SI (subreg:SI + (match_operand:QI 1 "memory_operand" "m") + 0) + (match_operand:SI 2 "ubicom32_arith_operand" "dM"))) + (clobber (reg:CC CC_REGNO))] + "(ubicom32_v4)" + "lsl.1\\t%0, %1, %2") + +; The combiner gets rather creative about left shifts of sub-word memory +; operands because it's uncertain about whether the memory is sign or +; zero extended. It only wants zero-extended behaviour and so throws +; in an extra and operation. +; +(define_insn "lsl1_2" + [(set (match_operand:SI 0 "ubicom32_data_register_operand" "=d") + (and:SI + (ashift:SI (subreg:SI + (match_operand:QI 1 "memory_operand" "m") + 0) + (match_operand:SI 2 "const_int_operand" "M")) + (match_operand:SI 3 "const_int_operand" "n"))) + (clobber (reg:CC CC_REGNO))] + "(ubicom32_v4 + && INTVAL (operands[3]) == (0xff << INTVAL (operands[2])))" + "lsl.1\\t%0, %1, %2") + +(define_insn "lsl2_1" + [(set (match_operand:SI 0 "ubicom32_data_register_operand" "=d") + (ashift:SI (subreg:SI + (match_operand:HI 1 "memory_operand" "m") + 0) + (match_operand:SI 2 "ubicom32_arith_operand" "dM"))) + (clobber (reg:CC CC_REGNO))] + "(ubicom32_v4)" + "lsl.2\\t%0, %1, %2") + +; The combiner gets rather creative about left shifts of sub-word memory +; operands because it's uncertain about whether the memory is sign or +; zero extended. It only wants zero-extended behaviour and so throws +; in an extra and operation. +; +(define_insn "lsl2_2" + [(set (match_operand:SI 0 "ubicom32_data_register_operand" "=d") + (and:SI + (ashift:SI (subreg:SI + (match_operand:HI 1 "memory_operand" "m") + 0) + (match_operand:SI 2 "const_int_operand" "M")) + (match_operand:SI 3 "const_int_operand" "n"))) + (clobber (reg:CC CC_REGNO))] + "(ubicom32_v4 + && INTVAL (operands[3]) == (0xffff << INTVAL (operands[2])))" + "lsl.2\\t%0, %1, %2") + +(define_insn "ashlsi3" + [(set (match_operand:SI 0 "ubicom32_data_register_operand" "=d") + (ashift:SI (match_operand:SI 1 "ubicom32_arith_operand" "rmI") + (match_operand:SI 2 "ubicom32_arith_operand" "dM"))) + (clobber (reg:CC CC_REGNO))] + "" + "lsl.4\\t%0, %1, %2") + +(define_insn "lshlsi3_ccwz" + [(set (reg CC_REGNO) + (compare + (ashift:SI (match_operand:SI 1 "ubicom32_arith_operand" "rmI") + (match_operand:SI 2 "ubicom32_arith_operand" "dM")) + (const_int 0))) + (set (match_operand:SI 0 "ubicom32_data_register_operand" "=d") + (ashift:SI (match_dup 1) + (match_dup 2)))] + "ubicom32_match_cc_mode(insn, CCWZmode)" + "lsl.4\\t%0, %1, %2") + +(define_insn "lshlsi3_ccwz_null" + [(set (reg CC_REGNO) + (compare + (ashift:SI (match_operand:SI 0 "ubicom32_arith_operand" "rmI") + (match_operand:SI 1 "ubicom32_arith_operand" "dM")) + (const_int 0))) + (clobber (match_scratch:SI 2 "=d"))] + "ubicom32_match_cc_mode(insn, CCWZmode)" + "lsl.4\\t%2, %0, %1") + +; The combiner finds this canonical form for what is in essence a right +; shift. +; +(define_insn "asr1_2" + [(set (match_operand:SI 0 "ubicom32_data_register_operand" "=d") + (sign_extract:SI (match_operand:QI 1 "memory_operand" "m") + (match_operand:SI 2 "const_int_operand" "M") + (match_operand:SI 3 "const_int_operand" "M"))) + (clobber (reg:CC CC_REGNO))] + "(ubicom32_v4 + && (INTVAL (operands[2]) + INTVAL (operands[3]) == 8))" + "asr.1\\t%0, %1, %3") + +; The combiner finds this canonical form for what is in essence a right +; shift. +; +(define_insn "asr2_2" + [(set (match_operand:SI 0 "ubicom32_data_register_operand" "=d") + (sign_extract:SI (match_operand:HI 1 "memory_operand" "m") + (match_operand:SI 2 "const_int_operand" "M") + (match_operand:SI 3 "const_int_operand" "M"))) + (clobber (reg:CC CC_REGNO))] + "(ubicom32_v4 + && (INTVAL (operands[2]) + INTVAL (operands[3]) == 16))" + "asr.2\\t%0, %1, %3") + +(define_insn "ashrsi3" + [(set (match_operand:SI 0 "ubicom32_data_register_operand" "=d") + (ashiftrt:SI (match_operand:SI 1 "ubicom32_arith_operand" "rmJ") + (match_operand:SI 2 "ubicom32_arith_operand" "dM"))) + (clobber (reg:CC CC_REGNO))] + "" + "asr.4\\t%0, %1, %2") + +(define_insn "ashrsi3_ccwzn" + [(set (reg CC_REGNO) + (compare + (ashiftrt:SI (match_operand:SI 1 "ubicom32_arith_operand" "rmJ") + (match_operand:SI 2 "ubicom32_arith_operand" "dM")) + (const_int 0))) + (set (match_operand:SI 0 "ubicom32_data_register_operand" "=d") + (ashiftrt:SI (match_dup 1) + (match_dup 2)))] + "ubicom32_match_cc_mode(insn, CCWZNmode)" + "asr.4\\t%0, %1, %2") + +(define_insn "ashrsi3_ccwzn_null" + [(set (reg CC_REGNO) + (compare + (ashiftrt:SI (match_operand:SI 0 "ubicom32_arith_operand" "rmJ") + (match_operand:SI 1 "ubicom32_arith_operand" "dM")) + (const_int 0))) + (clobber (match_scratch:SI 2 "=d"))] + "ubicom32_match_cc_mode(insn, CCWZNmode)" + "asr.4\\t%2, %0, %1") + +(define_insn "lsr1_1" + [(set (match_operand:SI 0 "ubicom32_data_register_operand" "=d") + (lshiftrt:SI (subreg:SI + (match_operand:QI 1 "memory_operand" "m") + 0) + (match_operand:SI 2 "ubicom32_arith_operand" "dM"))) + (clobber (reg:CC CC_REGNO))] + "(ubicom32_v4)" + "lsr.1\\t%0, %1, %2") + +; The combiner finds this canonical form for what is in essence a right +; shift. +; +(define_insn "lsr1_2" + [(set (match_operand:SI 0 "ubicom32_data_register_operand" "=d") + (zero_extract:SI (match_operand:QI 1 "memory_operand" "m") + (match_operand:SI 2 "const_int_operand" "M") + (match_operand:SI 3 "const_int_operand" "M"))) + (clobber (reg:CC CC_REGNO))] + "(ubicom32_v4 + && (INTVAL (operands[2]) + INTVAL (operands[3]) == 8))" + "lsr.1\\t%0, %1, %3") + +(define_insn "lsr2_1" + [(set (match_operand:SI 0 "ubicom32_data_register_operand" "=d") + (lshiftrt:SI (subreg:SI + (match_operand:HI 1 "memory_operand" "m") + 0) + (match_operand:SI 2 "ubicom32_arith_operand" "dM"))) + (clobber (reg:CC CC_REGNO))] + "(ubicom32_v4)" + "lsr.2\\t%0, %1, %2") + +; The combiner finds this canonical form for what is in essence a right +; shift. +; +(define_insn "lsr2_2" + [(set (match_operand:SI 0 "ubicom32_data_register_operand" "=d") + (zero_extract:SI (match_operand:HI 1 "memory_operand" "m") + (match_operand:SI 2 "const_int_operand" "M") + (match_operand:SI 3 "const_int_operand" "M"))) + (clobber (reg:CC CC_REGNO))] + "(ubicom32_v4 + && (INTVAL (operands[2]) + INTVAL (operands[3]) == 16))" + "lsr.2\\t%0, %1, %3") + +(define_insn "lshrsi3" + [(set (match_operand:SI 0 "ubicom32_data_register_operand" "=d") + (lshiftrt:SI (match_operand:SI 1 "ubicom32_arith_operand" "rmI") + (match_operand:SI 2 "ubicom32_arith_operand" "dM"))) + (clobber (reg:CC CC_REGNO))] + "" + "lsr.4\\t%0, %1, %2") + +(define_insn "lshrsi3_ccwz" + [(set (reg CC_REGNO) + (compare + (lshiftrt:SI (match_operand:SI 1 "ubicom32_arith_operand" "rmI") + (match_operand:SI 2 "ubicom32_arith_operand" "dM")) + (const_int 0))) + (set (match_operand:SI 0 "ubicom32_data_register_operand" "=d") + (lshiftrt:SI (match_dup 1) + (match_dup 2)))] + "ubicom32_match_cc_mode(insn, CCWZmode)" + "lsr.4\\t%0, %1, %2") + +(define_insn "lshrsi3_ccwz_null" + [(set (reg CC_REGNO) + (compare + (lshiftrt:SI (match_operand:SI 0 "ubicom32_arith_operand" "rmI") + (match_operand:SI 1 "ubicom32_arith_operand" "dM")) + (const_int 0))) + (clobber (match_scratch:SI 2 "=d"))] + "ubicom32_match_cc_mode(insn, CCWZmode)" + "lsr.4\\t%2, %0, %1") + +(define_expand "prologue" + [(const_int 0)] + "" + "{ + ubicom32_expand_prologue (); + DONE; + }") + +(define_expand "epilogue" + [(return)] + "" + "{ + ubicom32_expand_epilogue (); + DONE; + }") + +(define_expand "return" + [(return)] + "" + "{ + ubicom32_expand_epilogue (); + DONE; + }") + +(define_expand "_eh_return" + [(use (match_operand:SI 0 "register_operand" "r")) + (use (match_operand:SI 1 "register_operand" "r"))] + "" + "{ + ubicom32_expand_eh_return (operands); + DONE; + }") + +; XXX - it looks almost certain that we could make return_internal use a Dn +; register too. In that instance we'd have to use a ret instruction +; rather than a calli but it might save cycles. +; +(define_insn "return_internal" + [(const_int 2) + (return) + (use (match_operand:SI 0 "ubicom32_mem_or_address_register_operand" "rm"))] + "" + "* + { + if (REG_P (operands[0]) && REGNO (operands[0]) == LINK_REGNO + && ubicom32_can_use_calli_to_ret) + return \"calli\\t%0, 0(%0)\"; + + return \"ret\\t%0\"; + }") + +(define_insn "return_from_post_modify_sp" + [(parallel + [(const_int 2) + (return) + (use (mem:SI (post_modify:SI + (reg:SI SP_REGNO) + (plus:SI (reg:SI SP_REGNO) + (match_operand:SI 0 "const_int_operand" "n")))))])] + "INTVAL (operands[0]) >= 4 && INTVAL (operands[0]) <= 7 * 4" + "ret\\t(sp)%E0++") + +;(define_insn "eh_return_internal" +; [(const_int 4) +; (return) +; (use (reg:SI 34))] +; "" +; "ret\\ta2") + +; No operation, needed in case the user uses -g but not -O. +(define_expand "nop" + [(const_int 0)] + "" + "") + +(define_insn "nop_internal" + [(const_int 0)] + "" + "nop") + +; The combiner will generate this pattern given shift and add operations. +; The canonical form that the combiner wants to use appears to be multiplies +; instead of shifts even if the compiled sources use shifts. +; +(define_insn "shmrg1_add" + [(set (match_operand:SI 0 "ubicom32_data_register_operand" "=d") + (plus:SI + (mult:SI (match_operand:SI 1 "ubicom32_data_register_operand" "d") + (const_int 256)) + (zero_extend:SI + (match_operand:QI 2 "ubicom32_arith_operand" "rmI")))) + (clobber (reg:CC CC_REGNO))] + "" + "shmrg.1\\t%0, %2, %1") + +; The combiner will generate this pattern given shift and or operations. +; +(define_insn "shmrg1_ior" + [(set (match_operand:SI 0 "ubicom32_data_register_operand" "=d") + (ior:SI + (ashift:SI (match_operand:SI 1 "ubicom32_data_register_operand" "d") + (const_int 8)) + (zero_extend:SI + (match_operand:QI 2 "ubicom32_arith_operand" "rmI")))) + (clobber (reg:CC CC_REGNO))] + "" + "shmrg.1\\t%0, %2, %1") + +; The combiner will generate this pattern given shift and add operations. +; The canonical form that the combiner wants to use appears to be multiplies +; instead of shifts even if the compiled sources use shifts. +; +(define_insn "shmrg2_add" + [(set (match_operand:SI 0 "ubicom32_data_register_operand" "=d") + (plus:SI + (mult:SI (match_operand:SI 1 "ubicom32_data_register_operand" "d") + (const_int 65536)) + (zero_extend:SI + (match_operand:HI 2 "ubicom32_arith_operand" "rmI")))) + (clobber (reg:CC CC_REGNO))] + "" + "shmrg.2\\t%0, %2, %1") + +; The combiner will generate this pattern given shift and or operations. +; +(define_insn "shmrg2_ior" + [(set (match_operand:SI 0 "ubicom32_data_register_operand" "=d") + (ior:SI + (ashift:SI (match_operand:SI 1 "ubicom32_data_register_operand" "d") + (const_int 16)) + (zero_extend:SI + (match_operand:HI 2 "ubicom32_arith_operand" "rmI")))) + (clobber (reg:CC CC_REGNO))] + "" + "shmrg.2\\t%0, %2, %1") + +; Match the case where we load a word from the stack but then discard the +; upper 16 bits. We turn this into a zero-extended load of that useful +; 16 bits direct from the stack where possible. +; + +; XXX - do these peephole2 ops actually work after the CCmode conversion? +(define_peephole2 + [(set (match_operand:SI 0 "register_operand" "") + (mem:SI (plus:SI (reg:SI SP_REGNO) + (match_operand:SI 1 "const_int_operand" "")))) + (set (match_operand:SI 2 "nonimmediate_operand" "") + (zero_extend:SI (match_operand:HI 3 "register_operand" "")))] + "(INTVAL (operands[1]) <= 252 + && REGNO (operands[3]) == REGNO (operands[0]) + && ((peep2_reg_dead_p (2, operands[0]) + && ! reg_mentioned_p (operands[0], operands[2])) + || rtx_equal_p (operands[0], operands[2])))" + [(set (match_dup 2) + (zero_extend:SI (mem:HI (plus:SI (reg:SI SP_REGNO) + (match_dup 4)))))] + "{ + operands[4] = GEN_INT (INTVAL (operands[1]) + 2); + }") + +; Match the case where we load a word from the stack but then discard the +; upper 16 bits. We turn this into a 16-bit load of that useful +; 16 bits direct from the stack where possible. +; +(define_peephole2 + [(set (match_operand:SI 0 "register_operand" "") + (mem:SI (plus:SI (reg:SI SP_REGNO) + (match_operand:SI 1 "const_int_operand" "")))) + (set (match_operand:HI 2 "nonimmediate_operand" "") + (match_operand:HI 3 "register_operand" ""))] + "(INTVAL (operands[1]) <= 252 + && REGNO (operands[3]) == REGNO (operands[0]) + && ((peep2_reg_dead_p (2, operands[0]) + && ! reg_mentioned_p (operands[0], operands[2])) + || rtx_equal_p (operands[0], operands[2])))" + [(set (match_dup 2) + (mem:HI (plus:SI (reg:SI SP_REGNO) + (match_dup 4))))] + "{ + operands[4] = GEN_INT (INTVAL (operands[1]) + 2); + }") + +; Match the case where we load a word from the stack but then discard the +; upper 24 bits. We turn this into a zero-extended load of that useful +; 8 bits direct from the stack where possible. +; +(define_peephole2 + [(set (match_operand:SI 0 "register_operand" "") + (mem:SI (plus:SI (reg:SI SP_REGNO) + (match_operand:SI 1 "const_int_operand" "")))) + (set (match_operand:SI 2 "nonimmediate_operand" "") + (zero_extend:SI (match_operand:QI 3 "register_operand" "")))] + "(INTVAL (operands[1]) <= 124 + && REGNO (operands[3]) == REGNO (operands[0]) + && ((peep2_reg_dead_p (2, operands[0]) + && ! reg_mentioned_p (operands[0], operands[2])) + || rtx_equal_p (operands[0], operands[2])))" + [(set (match_dup 2) + (zero_extend:SI (mem:QI (plus:SI (reg:SI SP_REGNO) + (match_dup 4)))))] + "{ + operands[4] = GEN_INT (INTVAL (operands[1]) + 3); + }") + +; Match the case where we load a word from the stack but then discard the +; upper 24 bits. We turn this into an 8-bit load of that useful +; 8 bits direct from the stack where possible. +; +(define_peephole2 + [(set (match_operand:SI 0 "register_operand" "") + (mem:SI (plus:SI (reg:SI SP_REGNO) + (match_operand:SI 1 "const_int_operand" "")))) + (set (match_operand:QI 2 "nonimmediate_operand" "") + (match_operand:QI 3 "register_operand" ""))] + "(INTVAL (operands[1]) <= 124 + && REGNO (operands[3]) == REGNO (operands[0]) + && ((peep2_reg_dead_p (2, operands[0]) + && ! reg_mentioned_p (operands[0], operands[2])) + || rtx_equal_p (operands[0], operands[2])))" + [(set (match_dup 2) + (mem:QI (plus:SI (reg:SI SP_REGNO) + (match_dup 4))))] + "{ + operands[4] = GEN_INT (INTVAL (operands[1]) + 3); + }") + --- /dev/null +++ b/gcc/config/ubicom32/ubicom32.opt @@ -0,0 +1,27 @@ +mdebug-address +Target RejectNegative Report Undocumented Mask(DEBUG_ADDRESS) +Debug addresses + +mdebug-context +Target RejectNegative Report Undocumented Mask(DEBUG_CONTEXT) +Debug contexts + +march= +Target Report Var(ubicom32_arch_name) Init("ubicom32v4") Joined +Specify the name of the target architecture + +mfdpic +Target Report Mask(FDPIC) +Enable Function Descriptor PIC mode + +minline-plt +Target Report Mask(INLINE_PLT) +Enable inlining of PLT in function calls + +mfastcall +Target Report Mask(FASTCALL) +Enable default fast (call) calling sequence for smaller applications + +mipos-abi +Target Report Mask(IPOS_ABI) +Enable the ipOS ABI in which D10-D13 are caller-clobbered --- /dev/null +++ b/gcc/config/ubicom32/uclinux.h @@ -0,0 +1,67 @@ +/* Definitions of target machine for Ubicom32-uclinux + + Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, + 2009 Free Software Foundation, Inc. + Contributed by Ubicom, 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 3, 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 COPYING3. If not see + . */ + +/* Don't assume anything about the header files. */ +#define NO_IMPLICIT_EXTERN_C + +#undef LIB_SPEC +#define LIB_SPEC \ + "%{pthread:-lpthread} " \ + "%{!shared:%{!symbolic: -lc}} " + + +#undef LINK_GCC_C_SEQUENCE_SPEC +#define LINK_GCC_C_SEQUENCE_SPEC \ + "%{!shared:--start-group} %G %L %{!shared:--end-group}%{shared:%G} " + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC \ + "%{!shared: crt1%O%s}" \ + " crti%O%s crtbegin%O%s" + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC "crtend%O%s crtn%O%s" + +/* This macro applies on top of OBJECT_FORMAT_ELF and indicates that + we want to support both flat and ELF output. */ +#define OBJECT_FORMAT_FLAT + +#undef DRIVER_SELF_SPECS +#define DRIVER_SELF_SPECS \ + "%{!mno-fastcall:-mfastcall}" + +/* taken from linux.h */ +/* The GNU C++ standard library requires that these macros be defined. */ +#undef CPLUSPLUS_CPP_SPEC +#define CPLUSPLUS_CPP_SPEC "-D_GNU_SOURCE %(cpp)" + +#define TARGET_OS_CPP_BUILTINS() \ + do { \ + builtin_define_std ("__UBICOM32__"); \ + builtin_define_std ("__ubicom32__"); \ + builtin_define ("__gnu_linux__"); \ + builtin_define_std ("linux"); \ + builtin_define_std ("unix"); \ + builtin_assert ("system=linux"); \ + builtin_assert ("system=unix"); \ + builtin_assert ("system=posix"); \ + } while (0) --- /dev/null +++ b/gcc/config/ubicom32/xm-ubicom32.h @@ -0,0 +1,36 @@ +/* Configuration for Ubicom's Ubicom32 architecture. + Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Free Software + Foundation, Inc. + Contributed by Ubicom Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* #defines that need visibility everywhere. */ +#define FALSE 0 +#define TRUE 1 + +/* This describes the machine the compiler is hosted on. */ +#define HOST_BITS_PER_CHAR 8 +#define HOST_BITS_PER_SHORT 16 +#define HOST_BITS_PER_INT 32 +#define HOST_BITS_PER_LONG 32 +#define HOST_BITS_PER_LONGLONG 64 + +/* Arguments to use with `exit'. */ +#define SUCCESS_EXIT_CODE 0 +#define FATAL_EXIT_CODE 33 --- a/gcc/config.gcc +++ b/gcc/config.gcc @@ -2675,6 +2675,34 @@ spu-*-elf*) c_target_objs="${c_target_objs} spu-c.o" cxx_target_objs="${cxx_target_objs} spu-c.o" ;; +ubicom32-*-elf) + xm_file=ubicom32/xm-ubicom32.h + tm_file="${tm_file} ubicom32/elf.h" # still need dbxelf.h elfos.h + tmake_file=ubicom32/t-ubicom32 + ;; +ubicom32-*-uclinux*) + xm_file=ubicom32/xm-ubicom32.h + tm_file="${tm_file} ubicom32/elf.h ubicom32/uclinux.h" # still need dbxelf.h elfos.h linux.h + tm_defines="${tm_defines} UCLIBC_DEFAULT=1" + extra_options="${extra_options} linux.opt" + tmake_file=ubicom32/t-ubicom32-uclinux + use_collect2=no + ;; +ubicom32-*-linux-uclibc) + xm_file=ubicom32/xm-ubicom32.h + tm_file="${tm_file} ubicom32/elf.h linux.h ubicom32/linux.h" # still need dbxelf.h elfos.h + tmake_file="t-slibgcc-elf-ver ubicom32/t-ubicom32-linux" + extra_parts="crtbegin.o crtbeginS.o crtend.o crtendS.o" + use_collect2=no + ;; +ubicom32-*-linux*) + xm_file=ubicom32/xm-ubicom32.h + tm_file="${tm_file} ubicom32/elf.h linux.h ubicom32/linux.h" # still need dbxelf.h elfos.h + tmake_file="t-slibgcc-elf-ver ubicom32/t-ubicom32-linux" + tm_defines="${tm_defines} UCLIBC_DEFAULT=1" + extra_parts="crtbegin.o crtbeginS.o crtend.o crtendS.o" + use_collect2=no + ;; v850e1-*-*) target_cpu_default="TARGET_CPU_v850e1" tm_file="dbxelf.h elfos.h newlib-stdint.h v850/v850.h" --- a/libgcc/config.host +++ b/libgcc/config.host @@ -563,6 +563,15 @@ sparc64-*-netbsd*) ;; spu-*-elf*) ;; +ubicom32*-*-elf*) + ;; +ubicom32*-*-uclinux*) + ;; +ubicom32*-*-linux*) + # No need to build crtbeginT.o on uClibc systems. Should probably + # be moved to the OS specific section above. + extra_parts="crtbegin.o crtbeginS.o crtend.o crtendS.o" + ;; v850e1-*-*) ;; v850e-*-*)