summaryrefslogtreecommitdiff
path: root/gcc/config/sh
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/sh')
-rw-r--r--gcc/config/sh/constraints.md265
-rw-r--r--gcc/config/sh/crt1.asm1369
-rw-r--r--gcc/config/sh/crti.asm125
-rw-r--r--gcc/config/sh/crtn.asm77
-rw-r--r--gcc/config/sh/divcost-analysis88
-rw-r--r--gcc/config/sh/divtab-sh4-300.c77
-rw-r--r--gcc/config/sh/divtab-sh4.c85
-rw-r--r--gcc/config/sh/divtab.c200
-rw-r--r--gcc/config/sh/elf.h90
-rw-r--r--gcc/config/sh/embed-elf.h36
-rw-r--r--gcc/config/sh/lib1funcs-4-300.asm936
-rw-r--r--gcc/config/sh/lib1funcs-Os-4-200.asm322
-rw-r--r--gcc/config/sh/lib1funcs.asm3933
-rw-r--r--gcc/config/sh/lib1funcs.h76
-rw-r--r--gcc/config/sh/libgcc-excl.ver8
-rw-r--r--gcc/config/sh/libgcc-glibc.ver48
-rw-r--r--gcc/config/sh/linux-atomic.asm223
-rw-r--r--gcc/config/sh/linux-unwind.h256
-rw-r--r--gcc/config/sh/linux.h137
-rw-r--r--gcc/config/sh/little.h21
-rw-r--r--gcc/config/sh/netbsd-elf.h117
-rw-r--r--gcc/config/sh/newlib.h25
-rw-r--r--gcc/config/sh/predicates.md833
-rw-r--r--gcc/config/sh/rtems.h26
-rw-r--r--gcc/config/sh/rtemself.h26
-rw-r--r--gcc/config/sh/sh-c.c68
-rw-r--r--gcc/config/sh/sh-modes.def34
-rw-r--r--gcc/config/sh/sh-protos.h186
-rw-r--r--gcc/config/sh/sh-symbian.h42
-rw-r--r--gcc/config/sh/sh.c12610
-rw-r--r--gcc/config/sh/sh.h2511
-rw-r--r--gcc/config/sh/sh.md13490
-rw-r--r--gcc/config/sh/sh.opt338
-rw-r--r--gcc/config/sh/sh1.md85
-rw-r--r--gcc/config/sh/sh4-300.md287
-rw-r--r--gcc/config/sh/sh4.md486
-rw-r--r--gcc/config/sh/sh4a.md236
-rw-r--r--gcc/config/sh/sh64.h26
-rw-r--r--gcc/config/sh/shmedia.h30
-rw-r--r--gcc/config/sh/shmedia.md94
-rw-r--r--gcc/config/sh/sshmedia.h78
-rw-r--r--gcc/config/sh/superh.h107
-rw-r--r--gcc/config/sh/superh.opt10
-rw-r--r--gcc/config/sh/symbian-base.c244
-rw-r--r--gcc/config/sh/symbian-c.c181
-rw-r--r--gcc/config/sh/symbian-cxx.c662
-rw-r--r--gcc/config/sh/symbian-post.h88
-rw-r--r--gcc/config/sh/symbian-pre.h40
-rw-r--r--gcc/config/sh/t-elf10
-rw-r--r--gcc/config/sh/t-linux8
-rw-r--r--gcc/config/sh/t-linux641
-rw-r--r--gcc/config/sh/t-netbsd31
-rw-r--r--gcc/config/sh/t-netbsd-sh5-641
-rw-r--r--gcc/config/sh/t-rtems7
-rw-r--r--gcc/config/sh/t-sh166
-rw-r--r--gcc/config/sh/t-sh6429
-rw-r--r--gcc/config/sh/t-superh33
-rw-r--r--gcc/config/sh/t-symbian81
-rw-r--r--gcc/config/sh/t-vxworks9
-rw-r--r--gcc/config/sh/ushmedia.h1087
-rw-r--r--gcc/config/sh/vxworks.h69
61 files changed, 42864 insertions, 0 deletions
diff --git a/gcc/config/sh/constraints.md b/gcc/config/sh/constraints.md
new file mode 100644
index 000000000..6b0e5d27c
--- /dev/null
+++ b/gcc/config/sh/constraints.md
@@ -0,0 +1,265 @@
+;; Constraint definitions for Renesas / SuperH SH.
+;; Copyright (C) 2007, 2008 Free Software Foundation, Inc.
+;;
+;; This file is part of GCC.
+;;
+;; GCC is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 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
+;; <http://www.gnu.org/licenses/>.
+
+;; Overview of uppercase letter constraints:
+;; Bxx: miscellaneous constraints
+;; Bsc: SCRATCH - for the scratch register in movsi_ie in the
+;; fldi0 / fldi0 cases
+;; Cxx: Constants other than only CONST_INT
+;; Css: signed 16-bit constant, literal or symbolic
+;; Csu: unsigned 16-bit constant, literal or symbolic
+;; Csy: label or symbol
+;; Cpg: non-explicit constants that can be directly loaded into a general
+;; purpose register in PIC code. like 's' except we don't allow
+;; PIC_ADDR_P
+;; IJKLMNOP: CONT_INT constants
+;; Ixx: signed xx bit
+;; J16: 0xffffffff00000000 | 0x00000000ffffffff
+;; Kxx: unsigned xx bit
+;; M: 1
+;; N: 0
+;; P27: 1 | 2 | 8 | 16
+;; Pso: 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128
+;; Psz: ~1 | ~2 | ~4 | ~8 | ~16 | ~32 | ~64 | ~128
+;; Q: pc relative load operand
+;; Rxx: reserved for exotic register classes.
+;; Sxx: extra memory (storage) constraints
+;; Sua: unaligned memory operations
+;; W: vector
+;; Z: zero in any mode
+;;
+;; unused CONST_INT constraint letters: LO
+;; unused EXTRA_CONSTRAINT letters: D T U Y
+
+;; Register constraints
+(define_register_constraint "a" "ALL_REGS"
+ "@internal")
+
+(define_register_constraint "b" "TARGET_REGS"
+ "Branch target registers.")
+
+(define_register_constraint "c" "FPSCR_REGS"
+ "Floating-point status register.")
+
+(define_register_constraint "d" "DF_REGS"
+ "Double precision floating-point register.")
+
+(define_register_constraint "e" "TARGET_FMOVD ? NO_REGS : FP_REGS"
+ "Floating-point register.")
+
+(define_register_constraint "f" "FP_REGS"
+ "Floating-point register.")
+
+(define_register_constraint "k" "SIBCALL_REGS"
+ "@internal")
+
+(define_register_constraint "l" "PR_REGS"
+ "PR register.")
+
+(define_register_constraint "t" "T_REGS"
+ "T register.")
+
+(define_register_constraint "w" "FP0_REGS"
+ "Floating-point register 0.")
+
+(define_register_constraint "x" "MAC_REGS"
+ "MACH and MACL registers.")
+
+(define_register_constraint "y" "FPUL_REGS"
+ "FPUL register.")
+
+(define_register_constraint "z" "R0_REGS"
+ "R0 register.")
+
+;; Integer constraints
+(define_constraint "I06"
+ "A signed 6-bit constant, as used in SHmedia beqi, bnei and xori."
+ (and (match_code "const_int")
+ (match_test "ival >= -32 && ival <= 31")))
+
+(define_constraint "I08"
+ "A signed 8-bit constant, as used in add, sub, etc."
+ (and (match_code "const_int")
+ (match_test "ival >= -128 && ival <= 127")))
+
+(define_constraint "I10"
+ "A signed 10-bit constant, as used in in SHmedia andi, ori."
+ (and (match_code "const_int")
+ (match_test "ival >= -512 && ival <= 511")))
+
+(define_constraint "I16"
+ "A signed 16-bit constant, as used in SHmedia movi."
+ (and (match_code "const_int")
+ (match_test "ival >= -32768 && ival <= 32767")))
+
+(define_constraint "I20"
+ "A signed 20-bit constant, as used in SH2A movi20."
+ (and (match_code "const_int")
+ (match_test "ival >= -524288 && ival <= 524287")
+ (match_test "TARGET_SH2A")))
+
+(define_constraint "I28"
+ "A signed 28-bit constant, as used in SH2A movi20s."
+ (and (match_code "const_int")
+ (match_test "ival >= -134217728 && ival <= 134217727")
+ (match_test "(ival & 255) == 0")
+ (match_test "TARGET_SH2A")))
+(define_constraint "J16"
+ "0xffffffff00000000 or 0x00000000ffffffff."
+ (and (match_code "const_int")
+ (match_test "CONST_OK_FOR_J16 (ival)")))
+
+(define_constraint "K03"
+ "An unsigned 3-bit constant, as used in SH2A bclr, bset, etc."
+ (and (match_code "const_int")
+ (match_test "ival >= 0 && ival <= 7")))
+
+(define_constraint "K08"
+ "An unsigned 8-bit constant, as used in and, or, etc."
+ (and (match_code "const_int")
+ (match_test "ival >= 0 && ival <= 255")))
+
+(define_constraint "K12"
+ "An unsigned 8-bit constant, as used in SH2A 12-bit display."
+ (and (match_code "const_int")
+ (match_test "ival >= 0 && ival <= 4095")))
+
+(define_constraint "K16"
+ "An unsigned 16-bit constant, as used in SHmedia shori."
+ (and (match_code "const_int")
+ (match_test "ival >= 0 && ival <= 65535")))
+
+(define_constraint "P27"
+ "A constant for shift operand 1,2,8 or 16."
+ (and (match_code "const_int")
+ (match_test "ival == 1 || ival == 2 || ival == 8 || ival == 16")))
+
+(define_constraint "M"
+ "Integer constant 1."
+ (and (match_code "const_int")
+ (match_test "ival == 1")))
+
+(define_constraint "N"
+ "Integer constant 0."
+ (and (match_code "const_int")
+ (match_test "ival == 0")))
+
+;; Floating-point constraints
+(define_constraint "G"
+ "Double constant 0."
+ (and (match_code "const_double")
+ (match_test "fp_zero_operand (op) && fldi_ok ()")))
+
+(define_constraint "H"
+ "Double constant 1."
+ (and (match_code "const_double")
+ (match_test "fp_one_operand (op) && fldi_ok ()")))
+
+;; Extra constraints
+(define_constraint "Q"
+ "A pc relative load operand."
+ (and (match_code "mem")
+ (match_test "IS_PC_RELATIVE_LOAD_ADDR_P (XEXP (op, 0))")))
+
+(define_constraint "Bsc"
+ "Constraint for selecting FLDI0 or FLDI1 instruction. If the clobber
+ operand is not SCRATCH (i.e. REG) then R0 is probably being used,
+ hence mova is being used, hence do not select this pattern."
+ (match_code "scratch"))
+
+(define_constraint "Css"
+ "A signed 16-bit constant, literal or symbolic."
+ (and (match_code "const")
+ (match_test "GET_CODE (XEXP (op, 0)) == UNSPEC")
+ (match_test "XINT (XEXP (op, 0), 1) == UNSPEC_EXTRACT_S16")))
+
+(define_constraint "Csu"
+ "An unsigned 16-bit constant, literal or symbolic."
+ (and (match_code "const")
+ (match_test "GET_CODE (XEXP (op, 0)) == UNSPEC")
+ (match_test "XINT (XEXP (op, 0), 1) == UNSPEC_EXTRACT_U16")))
+
+(define_constraint "Csy"
+ "A label or a symbol."
+ (ior (match_test "NON_PIC_REFERENCE_P (op)")
+ (match_test "PIC_ADDR_P (op)")))
+
+(define_constraint "Z"
+ "A zero in any shape or form."
+ (match_test "op == CONST0_RTX (GET_MODE (op))"))
+
+(define_constraint "W"
+ "Any vector constant we can handle."
+ (and (match_code "const_vector")
+ (ior (match_test "sh_rep_vec (op, VOIDmode)")
+ (match_test "HOST_BITS_PER_WIDE_INT >= 64
+ ? sh_const_vec (op, VOIDmode)
+ : sh_1el_vec (op, VOIDmode)"))))
+
+(define_constraint "Cpg"
+ "A non-explicit constant that can be loaded directly into a general
+ purpose register. This is like 's' except we don't allow
+ PIC_ADDR_P."
+ (match_test "IS_NON_EXPLICIT_CONSTANT_P (op)"))
+
+(define_constraint "Pso"
+ "Integer constant with a single bit set in its lower 8-bit."
+ (and (match_code "const_int")
+ (ior (match_test "ival == 1")
+ (match_test "ival == 2")
+ (match_test "ival == 4")
+ (match_test "ival == 8")
+ (match_test "ival == 16")
+ (match_test "ival == 32")
+ (match_test "ival == 64")
+ (match_test "ival == 128"))))
+
+(define_constraint "Psz"
+ "Integer constant with a single zero bit in the lower 8-bit."
+ (and (match_code "const_int")
+ (ior (match_test "~ival == 1")
+ (match_test "~ival == 2")
+ (match_test "~ival == 4")
+ (match_test "~ival == 8")
+ (match_test "~ival == 16")
+ (match_test "~ival == 32")
+ (match_test "~ival == 64")
+ (match_test "~ival == 128"))))
+
+(define_memory_constraint "Sr0"
+ "@internal"
+ (and (match_test "memory_operand (op, GET_MODE (op))")
+ (match_test "!refers_to_regno_p (R0_REG, R0_REG + 1, op, (rtx *) 0)")))
+
+(define_memory_constraint "Sua"
+ "@internal"
+ (and (match_test "memory_operand (op, GET_MODE (op))")
+ (match_test "GET_CODE (XEXP (op, 0)) != PLUS")))
+
+(define_memory_constraint "Sbv"
+ "A memory reference, as used in SH2A bclr.b, bset.b, etc."
+ (and (match_test "MEM_P (op) && GET_MODE (op) == QImode")
+ (match_test "REG_P (XEXP (op, 0))")))
+
+(define_memory_constraint "Sbw"
+ "A memory reference, as used in SH2A bclr.b, bset.b, etc."
+ (and (match_test "MEM_P (op) && GET_MODE (op) == QImode")
+ (match_test "GET_CODE (XEXP (op, 0)) == PLUS")
+ (match_test "REG_P (XEXP (XEXP (op, 0), 0))")
+ (match_test "satisfies_constraint_K12 (XEXP (XEXP (op, 0), 1))")))
diff --git a/gcc/config/sh/crt1.asm b/gcc/config/sh/crt1.asm
new file mode 100644
index 000000000..e2857904f
--- /dev/null
+++ b/gcc/config/sh/crt1.asm
@@ -0,0 +1,1369 @@
+/* Copyright (C) 2000, 2001, 2003, 2004, 2005, 2006, 2009
+ Free Software Foundation, Inc.
+ This file was pretty much copied from newlib.
+
+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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+
+#ifdef MMU_SUPPORT
+ /* Section used for exception/timer interrupt stack area */
+ .section .data.vbr.stack,"aw"
+ .align 4
+ .global __ST_VBR
+__ST_VBR:
+ .zero 1024 * 2 /* ; 2k for VBR handlers */
+/* Label at the highest stack address where the stack grows from */
+__timer_stack:
+#endif /* MMU_SUPPORT */
+
+ /* ;----------------------------------------
+ Normal newlib crt1.asm */
+
+#ifdef __SH5__
+ .section .data,"aw"
+ .global ___data
+___data:
+
+ .section .rodata,"a"
+ .global ___rodata
+___rodata:
+
+#define ICCR_BASE 0x01600000
+#define OCCR_BASE 0x01e00000
+#define MMUIR_BASE 0x00000000
+#define MMUDR_BASE 0x00800000
+
+#define PTE_ENABLED 1
+#define PTE_DISABLED 0
+
+#define PTE_SHARED (1 << 1)
+#define PTE_NOT_SHARED 0
+
+#define PTE_CB_UNCACHEABLE 0
+#define PTE_CB_DEVICE 1
+#define PTE_CB_CACHEABLE_WB 2
+#define PTE_CB_CACHEABLE_WT 3
+
+#define PTE_SZ_4KB (0 << 3)
+#define PTE_SZ_64KB (1 << 3)
+#define PTE_SZ_1MB (2 << 3)
+#define PTE_SZ_512MB (3 << 3)
+
+#define PTE_PRR (1 << 6)
+#define PTE_PRX (1 << 7)
+#define PTE_PRW (1 << 8)
+#define PTE_PRU (1 << 9)
+
+#define SR_MMU_BIT 31
+#define SR_BL_BIT 28
+
+#define ALIGN_4KB (0xfff)
+#define ALIGN_1MB (0xfffff)
+#define ALIGN_512MB (0x1fffffff)
+
+#define DYNACON_BASE 0x0f000000
+#define DM_CB_DLINK_BASE 0x0c000000
+#define DM_DB_DLINK_BASE 0x0b000000
+
+#define FEMI_AREA_0 0x00000000
+#define FEMI_AREA_1 0x04000000
+#define FEMI_AREA_2 0x05000000
+#define FEMI_AREA_3 0x06000000
+#define FEMI_AREA_4 0x07000000
+#define FEMI_CB 0x08000000
+
+#define EMI_BASE 0X80000000
+
+#define DMA_BASE 0X0e000000
+
+#define CPU_BASE 0X0d000000
+
+#define PERIPH_BASE 0X09000000
+#define DMAC_BASE 0x0e000000
+#define INTC_BASE 0x0a000000
+#define CPRC_BASE 0x0a010000
+#define TMU_BASE 0x0a020000
+#define SCIF_BASE 0x0a030000
+#define RTC_BASE 0x0a040000
+
+
+
+#define LOAD_CONST32(val, reg) \
+ movi ((val) >> 16) & 65535, reg; \
+ shori (val) & 65535, reg
+
+#define LOAD_PTEH_VAL(sym, align, bits, scratch_reg, reg) \
+ LOAD_ADDR (sym, reg); \
+ LOAD_CONST32 ((align), scratch_reg); \
+ andc reg, scratch_reg, reg; \
+ LOAD_CONST32 ((bits), scratch_reg); \
+ or reg, scratch_reg, reg
+
+#define LOAD_PTEL_VAL(sym, align, bits, scratch_reg, reg) \
+ LOAD_ADDR (sym, reg); \
+ LOAD_CONST32 ((align), scratch_reg); \
+ andc reg, scratch_reg, reg; \
+ LOAD_CONST32 ((bits), scratch_reg); \
+ or reg, scratch_reg, reg
+
+#define SET_PTE(pte_addr_reg, pteh_val_reg, ptel_val_reg) \
+ putcfg pte_addr_reg, 0, r63; \
+ putcfg pte_addr_reg, 1, ptel_val_reg; \
+ putcfg pte_addr_reg, 0, pteh_val_reg
+
+#if __SH5__ == 64
+ .section .text,"ax"
+#define LOAD_ADDR(sym, reg) \
+ movi (sym >> 48) & 65535, reg; \
+ shori (sym >> 32) & 65535, reg; \
+ shori (sym >> 16) & 65535, reg; \
+ shori sym & 65535, reg
+#else
+ .mode SHmedia
+ .section .text..SHmedia32,"ax"
+#define LOAD_ADDR(sym, reg) \
+ movi (sym >> 16) & 65535, reg; \
+ shori sym & 65535, reg
+#endif
+ .global start
+start:
+ LOAD_ADDR (_stack, r15)
+
+#ifdef MMU_SUPPORT
+ ! Set up the VM using the MMU and caches
+
+ ! .vm_ep is first instruction to execute
+ ! after VM initialization
+ pt/l .vm_ep, tr1
+
+ ! Configure instruction cache (ICCR)
+ movi 3, r2
+ movi 0, r3
+ LOAD_ADDR (ICCR_BASE, r1)
+ putcfg r1, 0, r2
+ putcfg r1, 1, r3
+
+ ! movi 7, r2 ! write through
+ ! Configure operand cache (OCCR)
+ LOAD_ADDR (OCCR_BASE, r1)
+ putcfg r1, 0, r2
+ putcfg r1, 1, r3
+
+ ! Disable all PTE translations
+ LOAD_ADDR (MMUIR_BASE, r1)
+ LOAD_ADDR (MMUDR_BASE, r2)
+ movi 64, r3
+ pt/l .disable_ptes_loop, tr0
+.disable_ptes_loop:
+ putcfg r1, 0, r63
+ putcfg r2, 0, r63
+ addi r1, 16, r1
+ addi r2, 16, r2
+ addi r3, -1, r3
+ bgt r3, r63, tr0
+
+ LOAD_ADDR (MMUIR_BASE, r1)
+
+ ! FEMI instruction mappings
+ ! Area 0 - 1Mb cacheable at 0x00000000
+ ! Area 1 - None
+ ! Area 2 - 1Mb cacheable at 0x05000000
+ ! - 1Mb cacheable at 0x05100000
+ ! Area 3 - None
+ ! Area 4 - None
+
+ ! Map a 1Mb page for instructions at 0x00000000
+ LOAD_PTEH_VAL (FEMI_AREA_0, ALIGN_1MB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL (FEMI_AREA_0, ALIGN_1MB, PTE_CB_CACHEABLE_WB | PTE_SZ_1MB | PTE_PRX | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+
+ ! Map a 1Mb page for instructions at 0x05000000
+ addi r1, 16, r1
+ LOAD_PTEH_VAL (FEMI_AREA_2, ALIGN_1MB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL (FEMI_AREA_2, ALIGN_1MB, PTE_CB_CACHEABLE_WB | PTE_SZ_1MB | PTE_PRX | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+
+ ! Map a 1Mb page for instructions at 0x05100000
+ addi r1, 16, r1
+ LOAD_PTEH_VAL ((FEMI_AREA_2+0x100000), ALIGN_1MB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL ((FEMI_AREA_2+0x100000), ALIGN_1MB, PTE_CB_CACHEABLE_WB | PTE_SZ_1MB | PTE_PRX | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+
+ ! Map a 512M page for instructions at EMI base
+ addi r1, 16, r1
+ LOAD_PTEH_VAL (EMI_BASE, ALIGN_512MB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL (EMI_BASE, ALIGN_512MB, PTE_CB_CACHEABLE_WB | PTE_SZ_512MB | PTE_PRX | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+
+ ! Map a 4K page for instructions at DM_DB_DLINK_BASE
+ addi r1, 16, r1
+ LOAD_PTEH_VAL (DM_DB_DLINK_BASE, ALIGN_4KB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL (DM_DB_DLINK_BASE, ALIGN_4KB, PTE_CB_CACHEABLE_WB | PTE_SZ_4KB | PTE_PRX | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+
+ LOAD_ADDR (MMUDR_BASE, r1)
+
+ ! FEMI data mappings
+ ! Area 0 - 1Mb cacheable at 0x00000000
+ ! Area 1 - 1Mb device at 0x04000000
+ ! Area 2 - 1Mb cacheable at 0x05000000
+ ! - 1Mb cacheable at 0x05100000
+ ! Area 3 - None
+ ! Area 4 - None
+ ! CB - 1Mb device at 0x08000000
+
+ ! Map a 1Mb page for data at 0x00000000
+ LOAD_PTEH_VAL (FEMI_AREA_0, ALIGN_1MB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL (FEMI_AREA_0, ALIGN_1MB, PTE_CB_CACHEABLE_WB | PTE_SZ_1MB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+
+ ! Map a 1Mb page for data at 0x04000000
+ addi r1, 16, r1
+ LOAD_PTEH_VAL (FEMI_AREA_1, ALIGN_1MB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL (FEMI_AREA_1, ALIGN_1MB, PTE_CB_DEVICE | PTE_SZ_1MB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+
+ ! Map a 1Mb page for data at 0x05000000
+ addi r1, 16, r1
+ LOAD_PTEH_VAL (FEMI_AREA_2, ALIGN_1MB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL (FEMI_AREA_2, ALIGN_1MB, PTE_CB_CACHEABLE_WB | PTE_SZ_1MB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+
+ ! Map a 1Mb page for data at 0x05100000
+ addi r1, 16, r1
+ LOAD_PTEH_VAL ((FEMI_AREA_2+0x100000), ALIGN_1MB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL ((FEMI_AREA_2+0x100000), ALIGN_1MB, PTE_CB_CACHEABLE_WB | PTE_SZ_1MB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+
+ ! Map a 4K page for registers at 0x08000000
+ addi r1, 16, r1
+ LOAD_PTEH_VAL (FEMI_CB, ALIGN_4KB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL (FEMI_CB, ALIGN_4KB, PTE_CB_DEVICE | PTE_SZ_4KB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+
+ ! Map a 512M page for data at EMI
+ addi r1, 16, r1
+ LOAD_PTEH_VAL (EMI_BASE, ALIGN_512MB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL (EMI_BASE, ALIGN_512MB, PTE_CB_CACHEABLE_WB | PTE_SZ_512MB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+
+ ! Map a 4K page for DYNACON at DYNACON_BASE
+ addi r1, 16, r1
+ LOAD_PTEH_VAL (DYNACON_BASE, ALIGN_4KB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL (DYNACON_BASE, ALIGN_4KB, PTE_CB_DEVICE | PTE_SZ_4KB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+
+ ! Map a 4K page for instructions at DM_DB_DLINK_BASE
+ addi r1, 16, r1
+ LOAD_PTEH_VAL (DM_DB_DLINK_BASE, ALIGN_4KB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL (DM_DB_DLINK_BASE, ALIGN_4KB, PTE_CB_CACHEABLE_WB | PTE_SZ_4KB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+
+ ! Map a 4K page for data at DM_DB_DLINK_BASE+0x1000
+ addi r1, 16, r1
+ LOAD_PTEH_VAL ((DM_DB_DLINK_BASE+0x1000), ALIGN_4KB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL ((DM_DB_DLINK_BASE+0x1000), ALIGN_4KB, PTE_CB_UNCACHEABLE | PTE_SZ_4KB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+
+ ! Map a 4K page for stack DM_DB_DLINK_BASE+0x2000
+ addi r1, 16, r1
+ LOAD_PTEH_VAL ((DM_DB_DLINK_BASE+0x2000), ALIGN_4KB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL ((DM_DB_DLINK_BASE+0x2000), ALIGN_4KB, PTE_CB_CACHEABLE_WB | PTE_SZ_4KB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+
+ ! Map a 1M page for DM_CB_BASE2 at DM_CB_DLINK
+ ! 0x0c000000 - 0x0c0fffff
+ addi r1, 16, r1
+ LOAD_PTEH_VAL (DM_CB_DLINK_BASE, ALIGN_1MB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL (DM_CB_DLINK_BASE, ALIGN_1MB, PTE_CB_DEVICE | PTE_SZ_1MB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+
+ ! Map a 1M page for DM_CB_BASE2 at DM_CB_DLINK
+ ! 0x0c100000 - 0x0c1fffff
+ addi r1, 16, r1
+ LOAD_PTEH_VAL ((DM_CB_DLINK_BASE+0x100000), ALIGN_1MB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL ((DM_CB_DLINK_BASE+0x100000), ALIGN_1MB, PTE_CB_DEVICE | PTE_SZ_1MB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+
+ ! Map a 1M page for DM_CB_BASE2 at DM_CB_DLINK
+ ! 0x0c200000 - 0x0c2fffff
+ addi r1, 16, r1
+ LOAD_PTEH_VAL ((DM_CB_DLINK_BASE+0x200000), ALIGN_1MB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL ((DM_CB_DLINK_BASE+0x200000), ALIGN_1MB, PTE_CB_DEVICE | PTE_SZ_1MB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+
+ ! Map a 1M page for DM_CB_BASE2 at DM_CB_DLINK
+ ! 0x0c400000 - 0x0c4fffff
+ addi r1, 16, r1
+ LOAD_PTEH_VAL ((DM_CB_DLINK_BASE+0x400000), ALIGN_1MB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL ((DM_CB_DLINK_BASE+0x400000), ALIGN_1MB, PTE_CB_DEVICE | PTE_SZ_1MB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+
+ ! Map a 1M page for DM_CB_BASE2 at DM_CB_DLINK
+ ! 0x0c800000 - 0x0c8fffff
+ addi r1, 16, r1
+ LOAD_PTEH_VAL ((DM_CB_DLINK_BASE+0x800000), ALIGN_1MB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL ((DM_CB_DLINK_BASE+0x800000), ALIGN_1MB, PTE_CB_DEVICE | PTE_SZ_1MB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+
+ ! Map a 4K page for DMA control registers
+ addi r1, 16, r1
+ LOAD_PTEH_VAL (DMA_BASE, ALIGN_4KB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL (DMA_BASE, ALIGN_4KB, PTE_CB_DEVICE | PTE_SZ_4KB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+
+ ! Map lots of 4K pages for peripherals
+
+ ! /* peripheral */
+ addi r1, 16, r1
+ LOAD_PTEH_VAL (PERIPH_BASE, ALIGN_4KB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL (PERIPH_BASE, ALIGN_4KB, PTE_CB_DEVICE | PTE_SZ_4KB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+ ! /* dmac */
+ addi r1, 16, r1
+ LOAD_PTEH_VAL (DMAC_BASE, ALIGN_4KB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL (DMAC_BASE, ALIGN_4KB, PTE_CB_DEVICE | PTE_SZ_4KB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+ ! /* intc */
+ addi r1, 16, r1
+ LOAD_PTEH_VAL (INTC_BASE, ALIGN_4KB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL (INTC_BASE, ALIGN_4KB, PTE_CB_DEVICE | PTE_SZ_4KB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+ ! /* rtc */
+ addi r1, 16, r1
+ LOAD_PTEH_VAL (RTC_BASE, ALIGN_4KB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL (RTC_BASE, ALIGN_4KB, PTE_CB_DEVICE | PTE_SZ_4KB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+ ! /* dmac */
+ addi r1, 16, r1
+ LOAD_PTEH_VAL (TMU_BASE, ALIGN_4KB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL (TMU_BASE, ALIGN_4KB, PTE_CB_DEVICE | PTE_SZ_4KB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+ ! /* scif */
+ addi r1, 16, r1
+ LOAD_PTEH_VAL (SCIF_BASE, ALIGN_4KB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL (SCIF_BASE, ALIGN_4KB, PTE_CB_DEVICE | PTE_SZ_4KB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+ ! /* cprc */
+ addi r1, 16, r1
+ LOAD_PTEH_VAL (CPRC_BASE, ALIGN_4KB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL (CPRC_BASE, ALIGN_4KB, PTE_CB_DEVICE | PTE_SZ_4KB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+
+ ! Map CPU WPC registers
+ addi r1, 16, r1
+ LOAD_PTEH_VAL (CPU_BASE, ALIGN_1MB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL (CPU_BASE, ALIGN_1MB, PTE_CB_DEVICE | PTE_SZ_1MB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+ addi r1, 16, r1
+
+ LOAD_PTEH_VAL ((CPU_BASE+0x100000), ALIGN_1MB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL ((CPU_BASE+0x100000), ALIGN_1MB, PTE_CB_DEVICE | PTE_SZ_1MB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+
+ addi r1, 16, r1
+ LOAD_PTEH_VAL ((CPU_BASE+0x200000), ALIGN_1MB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL ((CPU_BASE+0x200000), ALIGN_1MB, PTE_CB_DEVICE | PTE_SZ_1MB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+
+ addi r1, 16, r1
+ LOAD_PTEH_VAL ((CPU_BASE+0x400000), ALIGN_1MB, PTE_ENABLED | PTE_NOT_SHARED, r25, r2)
+ LOAD_PTEL_VAL ((CPU_BASE+0x400000), ALIGN_1MB, PTE_CB_DEVICE | PTE_SZ_1MB | PTE_PRR | PTE_PRW | PTE_PRU, r25, r3)
+ SET_PTE (r1, r2, r3)
+
+ ! Switch over to virtual addressing and enabled cache
+ getcon sr, r1
+ movi 1, r2
+ shlli r2, SR_BL_BIT, r2
+ or r1, r2, r1
+ putcon r1, ssr
+ getcon sr, r1
+ movi 1, r2
+ shlli r2, SR_MMU_BIT, r2
+ or r1, r2, r1
+ putcon r1, ssr
+ gettr tr1, r1
+ putcon r1, spc
+ synco
+ rte
+
+ ! VM entry point. From now on, we are in VM mode.
+.vm_ep:
+
+ ! Install the trap handler, by seeding vbr with the
+ ! correct value, and by assigning sr.bl = 0.
+
+ LOAD_ADDR (vbr_start, r1)
+ putcon r1, vbr
+ movi ~(1<<28), r1
+ getcon sr, r2
+ and r1, r2, r2
+ putcon r2, sr
+#endif /* MMU_SUPPORT */
+
+ pt/l .Lzero_bss_loop, tr0
+ pt/l _init, tr5
+ pt/l ___setup_argv_and_call_main, tr6
+ pt/l _exit, tr7
+
+ ! zero out bss
+ LOAD_ADDR (_edata, r0)
+ LOAD_ADDR (_end, r1)
+.Lzero_bss_loop:
+ stx.q r0, r63, r63
+ addi r0, 8, r0
+ bgt/l r1, r0, tr0
+
+ LOAD_ADDR (___data, r26)
+ LOAD_ADDR (___rodata, r27)
+
+#ifdef __SH_FPU_ANY__
+ getcon sr, r0
+ ! enable the FP unit, by resetting SR.FD
+ ! also zero out SR.FR, SR.SZ and SR.PR, as mandated by the ABI
+ movi 0, r1
+ shori 0xf000, r1
+ andc r0, r1, r0
+ putcon r0, sr
+#if __SH5__ == 32
+ pt/l ___set_fpscr, tr0
+ movi 0, r4
+ blink tr0, r18
+#endif
+#endif
+
+ ! arrange for exit to call fini
+ pt/l _atexit, tr1
+ LOAD_ADDR (_fini, r2)
+ blink tr1, r18
+
+ ! call init
+ blink tr5, r18
+
+ ! call the mainline
+ blink tr6, r18
+
+ ! call exit
+ blink tr7, r18
+ ! We should never return from _exit but in case we do we would enter the
+ ! the following tight loop. This avoids executing any data that might follow.
+limbo:
+ pt/l limbo, tr0
+ blink tr0, r63
+
+#ifdef MMU_SUPPORT
+ ! All these traps are handled in the same place.
+ .balign 256
+vbr_start:
+ pt/l handler, tr0 ! tr0 trashed.
+ blink tr0, r63
+ .balign 256
+vbr_100:
+ pt/l handler, tr0 ! tr0 trashed.
+ blink tr0, r63
+vbr_100_end:
+ .balign 256
+vbr_200:
+ pt/l handler, tr0 ! tr0 trashed.
+ blink tr0, r63
+ .balign 256
+vbr_300:
+ pt/l handler, tr0 ! tr0 trashed.
+ blink tr0, r63
+ .balign 256
+vbr_400: ! Should be at vbr+0x400
+handler:
+ /* If the trap handler is there call it */
+ LOAD_ADDR (__superh_trap_handler, r2)
+ pta chandler,tr2
+ beq r2, r63, tr2 /* If zero, ie not present branch around to chandler */
+ /* Now call the trap handler with as much of the context unchanged as possible.
+ Move trapping address into R18 to make it look like the trap point */
+ getcon spc, r18
+ pt/l __superh_trap_handler, tr0
+ blink tr0, r7
+chandler:
+ getcon spc, r62
+ getcon expevt, r2
+ pt/l _exit, tr0
+ blink tr0, r63
+
+ /* Simulated trap handler */
+ .section .text..SHmedia32,"ax"
+gcc2_compiled.:
+ .section .debug_abbrev
+.Ldebug_abbrev0:
+ .section .text..SHmedia32
+.Ltext0:
+ .section .debug_info
+.Ldebug_info0:
+ .section .debug_line
+.Ldebug_line0:
+ .section .text..SHmedia32,"ax"
+ .align 5
+ .global __superh_trap_handler
+ .type __superh_trap_handler,@function
+__superh_trap_handler:
+.LFB1:
+ ptabs r18, tr0
+ addi.l r15, -8, r15
+ st.l r15, 4, r14
+ addi.l r15, -8, r15
+ add.l r15, r63, r14
+ st.l r14, 0, r2
+ ptabs r7, tr0
+ addi.l r14, 8, r14
+ add.l r14, r63, r15
+ ld.l r15, 4, r14
+ addi.l r15, 8, r15
+ blink tr0, r63
+.LFE1:
+.Lfe1:
+ .size __superh_trap_handler,.Lfe1-__superh_trap_handler
+
+ .section .text..SHmedia32
+.Letext0:
+
+ .section .debug_info
+ .ualong 0xa7
+ .uaword 0x2
+ .ualong .Ldebug_abbrev0
+ .byte 0x4
+ .byte 0x1
+ .ualong .Ldebug_line0
+ .ualong .Letext0
+ .ualong .Ltext0
+ .string "trap_handler.c"
+
+ .string "xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+
+ .string "GNU C 2.97-sh5-010522"
+
+ .byte 0x1
+ .byte 0x2
+ .ualong 0x9a
+ .byte 0x1
+ .string "_superh_trap_handler"
+
+ .byte 0x1
+ .byte 0x2
+ .byte 0x1
+ .ualong .LFB1
+ .ualong .LFE1
+ .byte 0x1
+ .byte 0x5e
+ .byte 0x3
+ .string "trap_reason"
+
+ .byte 0x1
+ .byte 0x1
+ .ualong 0x9a
+ .byte 0x2
+ .byte 0x91
+ .byte 0x0
+ .byte 0x0
+ .byte 0x4
+ .string "unsigned int"
+
+ .byte 0x4
+ .byte 0x7
+ .byte 0x0
+
+ .section .debug_abbrev
+ .byte 0x1
+ .byte 0x11
+ .byte 0x1
+ .byte 0x10
+ .byte 0x6
+ .byte 0x12
+ .byte 0x1
+ .byte 0x11
+ .byte 0x1
+ .byte 0x3
+ .byte 0x8
+ .byte 0x1b
+ .byte 0x8
+ .byte 0x25
+ .byte 0x8
+ .byte 0x13
+ .byte 0xb
+ .byte 0,0
+ .byte 0x2
+ .byte 0x2e
+ .byte 0x1
+ .byte 0x1
+ .byte 0x13
+ .byte 0x3f
+ .byte 0xc
+ .byte 0x3
+ .byte 0x8
+ .byte 0x3a
+ .byte 0xb
+ .byte 0x3b
+ .byte 0xb
+ .byte 0x27
+ .byte 0xc
+ .byte 0x11
+ .byte 0x1
+ .byte 0x12
+ .byte 0x1
+ .byte 0x40
+ .byte 0xa
+ .byte 0,0
+ .byte 0x3
+ .byte 0x5
+ .byte 0x0
+ .byte 0x3
+ .byte 0x8
+ .byte 0x3a
+ .byte 0xb
+ .byte 0x3b
+ .byte 0xb
+ .byte 0x49
+ .byte 0x13
+ .byte 0x2
+ .byte 0xa
+ .byte 0,0
+ .byte 0x4
+ .byte 0x24
+ .byte 0x0
+ .byte 0x3
+ .byte 0x8
+ .byte 0xb
+ .byte 0xb
+ .byte 0x3e
+ .byte 0xb
+ .byte 0,0
+ .byte 0
+
+ .section .debug_pubnames
+ .ualong 0x27
+ .uaword 0x2
+ .ualong .Ldebug_info0
+ .ualong 0xab
+ .ualong 0x5b
+ .string "_superh_trap_handler"
+
+ .ualong 0x0
+
+ .section .debug_aranges
+ .ualong 0x1c
+ .uaword 0x2
+ .ualong .Ldebug_info0
+ .byte 0x4
+ .byte 0x0
+ .uaword 0x0,0
+ .ualong .Ltext0
+ .ualong .Letext0-.Ltext0
+ .ualong 0x0
+ .ualong 0x0
+ .ident "GCC: (GNU) 2.97-sh5-010522"
+#endif /* MMU_SUPPORT */
+#else /* ! __SH5__ */
+
+ ! make a place to keep any previous value of the vbr register
+ ! this will only have a value if it has been set by redboot (for example)
+ .section .bss
+old_vbr:
+ .long 0
+#ifdef PROFILE
+profiling_enabled:
+ .long 0
+#endif
+
+
+ .section .text
+ .global start
+ .import ___rtos_profiler_start_timer
+ .weak ___rtos_profiler_start_timer
+start:
+ mov.l stack_k,r15
+
+#if defined (__SH3__) || (defined (__SH_FPU_ANY__) && ! defined (__SH2A__)) || defined (__SH4_NOFPU__)
+#define VBR_SETUP
+ ! before zeroing the bss ...
+ ! if the vbr is already set to vbr_start then the program has been restarted
+ ! (i.e. it is not the first time the program has been run since reset)
+ ! reset the vbr to its old value before old_vbr (in bss) is wiped
+ ! this ensures that the later code does not create a circular vbr chain
+ stc vbr, r1
+ mov.l vbr_start_k, r2
+ cmp/eq r1, r2
+ bf 0f
+ ! reset the old vbr value
+ mov.l old_vbr_k, r1
+ mov.l @r1, r2
+ ldc r2, vbr
+0:
+#endif /* VBR_SETUP */
+
+ ! zero out bss
+ mov.l edata_k,r0
+ mov.l end_k,r1
+ mov #0,r2
+start_l:
+ mov.l r2,@r0
+ add #4,r0
+ cmp/ge r0,r1
+ bt start_l
+
+#if defined (__SH_FPU_ANY__)
+ mov.l set_fpscr_k, r1
+ mov #4,r4
+ jsr @r1
+ shll16 r4 ! Set DN bit (flush denormal inputs to zero)
+ lds r3,fpscr ! Switch to default precision
+#endif /* defined (__SH_FPU_ANY__) */
+
+#ifdef VBR_SETUP
+ ! save the existing contents of the vbr
+ ! there will only be a prior value when using something like redboot
+ ! otherwise it will be zero
+ stc vbr, r1
+ mov.l old_vbr_k, r2
+ mov.l r1, @r2
+ ! setup vbr
+ mov.l vbr_start_k, r1
+ ldc r1,vbr
+#endif /* VBR_SETUP */
+
+ ! if an rtos is exporting a timer start fn,
+ ! then pick up an SR which does not enable ints
+ ! (the rtos will take care of this)
+ mov.l rtos_start_fn, r0
+ mov.l sr_initial_bare, r1
+ tst r0, r0
+ bt set_sr
+
+ mov.l sr_initial_rtos, r1
+
+set_sr:
+ ! Set status register (sr)
+ ldc r1, sr
+
+ ! arrange for exit to call fini
+ mov.l atexit_k,r0
+ mov.l fini_k,r4
+ jsr @r0
+ nop
+
+#ifdef PROFILE
+ ! arrange for exit to call _mcleanup (via stop_profiling)
+ mova stop_profiling,r0
+ mov.l atexit_k,r1
+ jsr @r1
+ mov r0, r4
+
+ ! Call profiler startup code
+ mov.l monstartup_k, r0
+ mov.l start_k, r4
+ mov.l etext_k, r5
+ jsr @r0
+ nop
+
+ ! enable profiling trap
+ ! until now any trap 33s will have been ignored
+ ! This means that all library functions called before this point
+ ! (directly or indirectly) may have the profiling trap at the start.
+ ! Therefore, only mcount itself may not have the extra header.
+ mov.l profiling_enabled_k2, r0
+ mov #1, r1
+ mov.l r1, @r0
+#endif /* PROFILE */
+
+ ! call init
+ mov.l init_k,r0
+ jsr @r0
+ nop
+
+ ! call the mainline
+ mov.l main_k,r0
+ jsr @r0
+ nop
+
+ ! call exit
+ mov r0,r4
+ mov.l exit_k,r0
+ jsr @r0
+ nop
+
+ .balign 4
+#ifdef PROFILE
+stop_profiling:
+ # stop mcount counting
+ mov.l profiling_enabled_k2, r0
+ mov #0, r1
+ mov.l r1, @r0
+
+ # call mcleanup
+ mov.l mcleanup_k, r0
+ jmp @r0
+ nop
+
+ .balign 4
+mcleanup_k:
+ .long __mcleanup
+monstartup_k:
+ .long ___monstartup
+profiling_enabled_k2:
+ .long profiling_enabled
+start_k:
+ .long _start
+etext_k:
+ .long __etext
+#endif /* PROFILE */
+
+ .align 2
+#if defined (__SH_FPU_ANY__)
+set_fpscr_k:
+ .long ___set_fpscr
+#endif /* defined (__SH_FPU_ANY__) */
+
+stack_k:
+ .long _stack
+edata_k:
+ .long _edata
+end_k:
+ .long _end
+main_k:
+ .long ___setup_argv_and_call_main
+exit_k:
+ .long _exit
+atexit_k:
+ .long _atexit
+init_k:
+ .long _init
+fini_k:
+ .long _fini
+#ifdef VBR_SETUP
+old_vbr_k:
+ .long old_vbr
+vbr_start_k:
+ .long vbr_start
+#endif /* VBR_SETUP */
+
+sr_initial_rtos:
+ ! Privileged mode RB 1 BL 0. Keep BL 0 to allow default trap handlers to work.
+ ! Whether profiling or not, keep interrupts masked,
+ ! the RTOS will enable these if required.
+ .long 0x600000f1
+
+rtos_start_fn:
+ .long ___rtos_profiler_start_timer
+
+#ifdef PROFILE
+sr_initial_bare:
+ ! Privileged mode RB 1 BL 0. Keep BL 0 to allow default trap handlers to work.
+ ! For bare machine, we need to enable interrupts to get profiling working
+ .long 0x60000001
+#else
+
+sr_initial_bare:
+ ! Privileged mode RB 1 BL 0. Keep BL 0 to allow default trap handlers to work.
+ ! Keep interrupts disabled - the application will enable as required.
+ .long 0x600000f1
+#endif
+
+ ! supplied for backward compatibility only, in case of linking
+ ! code whose main() was compiled with an older version of GCC.
+ .global ___main
+___main:
+ rts
+ nop
+#ifdef VBR_SETUP
+! Exception handlers
+ .section .text.vbr, "ax"
+vbr_start:
+
+ .org 0x100
+vbr_100:
+#ifdef PROFILE
+ ! Note on register usage.
+ ! we use r0..r3 as scratch in this code. If we are here due to a trapa for profiling
+ ! then this is OK as we are just before executing any function code.
+ ! The other r4..r7 we save explicityl on the stack
+ ! Remaining registers are saved by normal ABI conventions and we assert we do not
+ ! use floating point registers.
+ mov.l expevt_k1, r1
+ mov.l @r1, r1
+ mov.l event_mask, r0
+ and r0,r1
+ mov.l trapcode_k, r2
+ cmp/eq r1,r2
+ bt 1f
+ bra handler_100 ! if not a trapa, go to default handler
+ nop
+1:
+ mov.l trapa_k, r0
+ mov.l @r0, r0
+ shlr2 r0 ! trapa code is shifted by 2.
+ cmp/eq #33, r0
+ bt 2f
+ bra handler_100
+ nop
+2:
+
+ ! If here then it looks like we have trap #33
+ ! Now we need to call mcount with the following convention
+ ! Save and restore r4..r7
+ mov.l r4,@-r15
+ mov.l r5,@-r15
+ mov.l r6,@-r15
+ mov.l r7,@-r15
+ sts.l pr,@-r15
+
+ ! r4 is frompc.
+ ! r5 is selfpc
+ ! r0 is the branch back address.
+ ! The code sequence emitted by gcc for the profiling trap is
+ ! .align 2
+ ! trapa #33
+ ! .align 2
+ ! .long lab Where lab is planted by the compiler. This is the address
+ ! of a datum that needs to be incremented.
+ sts pr, r4 ! frompc
+ stc spc, r5 ! selfpc
+ mov #2, r2
+ not r2, r2 ! pattern to align to 4
+ and r2, r5 ! r5 now has aligned address
+! add #4, r5 ! r5 now has address of address
+ mov r5, r2 ! Remember it.
+! mov.l @r5, r5 ! r5 has value of lable (lab in above example)
+ add #8, r2
+ ldc r2, spc ! our return address avoiding address word
+
+ ! only call mcount if profiling is enabled
+ mov.l profiling_enabled_k, r0
+ mov.l @r0, r0
+ cmp/eq #0, r0
+ bt 3f
+ ! call mcount
+ mov.l mcount_k, r2
+ jsr @r2
+ nop
+3:
+ lds.l @r15+,pr
+ mov.l @r15+,r7
+ mov.l @r15+,r6
+ mov.l @r15+,r5
+ mov.l @r15+,r4
+ rte
+ nop
+ .balign 4
+event_mask:
+ .long 0xfff
+trapcode_k:
+ .long 0x160
+expevt_k1:
+ .long 0xff000024 ! Address of expevt
+trapa_k:
+ .long 0xff000020
+mcount_k:
+ .long __call_mcount
+profiling_enabled_k:
+ .long profiling_enabled
+#endif
+ ! Non profiling case.
+handler_100:
+ mov.l 2f, r0 ! load the old vbr setting (if any)
+ mov.l @r0, r0
+ cmp/eq #0, r0
+ bf 1f
+ ! no previous vbr - jump to own generic handler
+ bra handler
+ nop
+1: ! there was a previous handler - chain them
+ add #0x7f, r0 ! 0x7f
+ add #0x7f, r0 ! 0xfe
+ add #0x2, r0 ! add 0x100 without corrupting another register
+ jmp @r0
+ nop
+ .balign 4
+2:
+ .long old_vbr
+
+ .org 0x400
+vbr_400: ! Should be at vbr+0x400
+ mov.l 2f, r0 ! load the old vbr setting (if any)
+ mov.l @r0, r0
+ cmp/eq #0, r0
+ ! no previous vbr - jump to own generic handler
+ bt handler
+ ! there was a previous handler - chain them
+ rotcr r0
+ rotcr r0
+ add #0x7f, r0 ! 0x1fc
+ add #0x7f, r0 ! 0x3f8
+ add #0x02, r0 ! 0x400
+ rotcl r0
+ rotcl r0 ! Add 0x400 without corrupting another register
+ jmp @r0
+ nop
+ .balign 4
+2:
+ .long old_vbr
+handler:
+ /* If the trap handler is there call it */
+ mov.l superh_trap_handler_k, r0
+ cmp/eq #0, r0 ! True if zero.
+ bf 3f
+ bra chandler
+ nop
+3:
+ ! Here handler available, call it.
+ /* Now call the trap handler with as much of the context unchanged as possible.
+ Move trapping address into PR to make it look like the trap point */
+ stc spc, r1
+ lds r1, pr
+ mov.l expevt_k, r4
+ mov.l @r4, r4 ! r4 is value of expevt, first parameter.
+ mov r1, r5 ! Remember trapping pc.
+ mov r1, r6 ! Remember trapping pc.
+ mov.l chandler_k, r1
+ mov.l superh_trap_handler_k, r2
+ ! jmp to trap handler to avoid disturbing pr.
+ jmp @r2
+ nop
+
+ .org 0x600
+vbr_600:
+#ifdef PROFILE
+ ! Should be at vbr+0x600
+ ! Now we are in the land of interrupts so need to save more state.
+ ! Save register state
+ mov.l interrupt_stack_k, r15 ! r15 has been saved to sgr.
+ mov.l r0,@-r15
+ mov.l r1,@-r15
+ mov.l r2,@-r15
+ mov.l r3,@-r15
+ mov.l r4,@-r15
+ mov.l r5,@-r15
+ mov.l r6,@-r15
+ mov.l r7,@-r15
+ sts.l pr,@-r15
+ sts.l mach,@-r15
+ sts.l macl,@-r15
+#if defined(__SH_FPU_ANY__)
+ ! Save fpul and fpscr, save fr0-fr7 in 64 bit mode
+ ! and set the pervading precision for the timer_handler
+ mov #0,r0
+ sts.l fpul,@-r15
+ sts.l fpscr,@-r15
+ lds r0,fpscr ! Clear fpscr
+ fmov fr0,@-r15
+ fmov fr1,@-r15
+ fmov fr2,@-r15
+ fmov fr3,@-r15
+ mov.l pervading_precision_k,r0
+ fmov fr4,@-r15
+ fmov fr5,@-r15
+ mov.l @r0,r0
+ fmov fr6,@-r15
+ fmov fr7,@-r15
+ lds r0,fpscr
+#endif /* __SH_FPU_ANY__ */
+ ! Pass interrupted pc to timer_handler as first parameter (r4).
+ stc spc, r4
+ mov.l timer_handler_k, r0
+ jsr @r0
+ nop
+#if defined(__SH_FPU_ANY__)
+ mov #0,r0
+ lds r0,fpscr ! Clear the fpscr
+ fmov @r15+,fr7
+ fmov @r15+,fr6
+ fmov @r15+,fr5
+ fmov @r15+,fr4
+ fmov @r15+,fr3
+ fmov @r15+,fr2
+ fmov @r15+,fr1
+ fmov @r15+,fr0
+ lds.l @r15+,fpscr
+ lds.l @r15+,fpul
+#endif /* __SH_FPU_ANY__ */
+ lds.l @r15+,macl
+ lds.l @r15+,mach
+ lds.l @r15+,pr
+ mov.l @r15+,r7
+ mov.l @r15+,r6
+ mov.l @r15+,r5
+ mov.l @r15+,r4
+ mov.l @r15+,r3
+ mov.l @r15+,r2
+ mov.l @r15+,r1
+ mov.l @r15+,r0
+ stc sgr, r15 ! Restore r15, destroyed by this sequence.
+ rte
+ nop
+#if defined(__SH_FPU_ANY__)
+ .balign 4
+pervading_precision_k:
+#define CONCAT1(A,B) A##B
+#define CONCAT(A,B) CONCAT1(A,B)
+ .long CONCAT(__USER_LABEL_PREFIX__,__fpscr_values)+4
+#endif
+#else
+ mov.l 2f, r0 ! Load the old vbr setting (if any).
+ mov.l @r0, r0
+ cmp/eq #0, r0
+ ! no previous vbr - jump to own handler
+ bt chandler
+ ! there was a previous handler - chain them
+ rotcr r0
+ rotcr r0
+ add #0x7f, r0 ! 0x1fc
+ add #0x7f, r0 ! 0x3f8
+ add #0x7f, r0 ! 0x5f4
+ add #0x03, r0 ! 0x600
+ rotcl r0
+ rotcl r0 ! Add 0x600 without corrupting another register
+ jmp @r0
+ nop
+ .balign 4
+2:
+ .long old_vbr
+#endif /* PROFILE code */
+chandler:
+ mov.l expevt_k, r4
+ mov.l @r4, r4 ! r4 is value of expevt hence making this the return code
+ mov.l handler_exit_k,r0
+ jsr @r0
+ nop
+ ! We should never return from _exit but in case we do we would enter the
+ ! the following tight loop
+limbo:
+ bra limbo
+ nop
+ .balign 4
+#ifdef PROFILE
+interrupt_stack_k:
+ .long __timer_stack ! The high end of the stack
+timer_handler_k:
+ .long __profil_counter
+#endif
+expevt_k:
+ .long 0xff000024 ! Address of expevt
+chandler_k:
+ .long chandler
+superh_trap_handler_k:
+ .long __superh_trap_handler
+handler_exit_k:
+ .long _exit
+ .align 2
+! Simulated compile of trap handler.
+ .section .debug_abbrev,"",@progbits
+.Ldebug_abbrev0:
+ .section .debug_info,"",@progbits
+.Ldebug_info0:
+ .section .debug_line,"",@progbits
+.Ldebug_line0:
+ .text
+.Ltext0:
+ .align 5
+ .type __superh_trap_handler,@function
+__superh_trap_handler:
+.LFB1:
+ mov.l r14,@-r15
+.LCFI0:
+ add #-4,r15
+.LCFI1:
+ mov r15,r14
+.LCFI2:
+ mov.l r4,@r14
+ lds r1, pr
+ add #4,r14
+ mov r14,r15
+ mov.l @r15+,r14
+ rts
+ nop
+.LFE1:
+.Lfe1:
+ .size __superh_trap_handler,.Lfe1-__superh_trap_handler
+ .section .debug_frame,"",@progbits
+.Lframe0:
+ .ualong .LECIE0-.LSCIE0
+.LSCIE0:
+ .ualong 0xffffffff
+ .byte 0x1
+ .string ""
+ .uleb128 0x1
+ .sleb128 -4
+ .byte 0x11
+ .byte 0xc
+ .uleb128 0xf
+ .uleb128 0x0
+ .align 2
+.LECIE0:
+.LSFDE0:
+ .ualong .LEFDE0-.LASFDE0
+.LASFDE0:
+ .ualong .Lframe0
+ .ualong .LFB1
+ .ualong .LFE1-.LFB1
+ .byte 0x4
+ .ualong .LCFI0-.LFB1
+ .byte 0xe
+ .uleb128 0x4
+ .byte 0x4
+ .ualong .LCFI1-.LCFI0
+ .byte 0xe
+ .uleb128 0x8
+ .byte 0x8e
+ .uleb128 0x1
+ .byte 0x4
+ .ualong .LCFI2-.LCFI1
+ .byte 0xd
+ .uleb128 0xe
+ .align 2
+.LEFDE0:
+ .text
+.Letext0:
+ .section .debug_info
+ .ualong 0xb3
+ .uaword 0x2
+ .ualong .Ldebug_abbrev0
+ .byte 0x4
+ .uleb128 0x1
+ .ualong .Ldebug_line0
+ .ualong .Letext0
+ .ualong .Ltext0
+ .string "trap_handler.c"
+ .string "xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ .string "GNU C 3.2 20020529 (experimental)"
+ .byte 0x1
+ .uleb128 0x2
+ .ualong 0xa6
+ .byte 0x1
+ .string "_superh_trap_handler"
+ .byte 0x1
+ .byte 0x2
+ .byte 0x1
+ .ualong .LFB1
+ .ualong .LFE1
+ .byte 0x1
+ .byte 0x5e
+ .uleb128 0x3
+ .string "trap_reason"
+ .byte 0x1
+ .byte 0x1
+ .ualong 0xa6
+ .byte 0x2
+ .byte 0x91
+ .sleb128 0
+ .byte 0x0
+ .uleb128 0x4
+ .string "unsigned int"
+ .byte 0x4
+ .byte 0x7
+ .byte 0x0
+ .section .debug_abbrev
+ .uleb128 0x1
+ .uleb128 0x11
+ .byte 0x1
+ .uleb128 0x10
+ .uleb128 0x6
+ .uleb128 0x12
+ .uleb128 0x1
+ .uleb128 0x11
+ .uleb128 0x1
+ .uleb128 0x3
+ .uleb128 0x8
+ .uleb128 0x1b
+ .uleb128 0x8
+ .uleb128 0x25
+ .uleb128 0x8
+ .uleb128 0x13
+ .uleb128 0xb
+ .byte 0x0
+ .byte 0x0
+ .uleb128 0x2
+ .uleb128 0x2e
+ .byte 0x1
+ .uleb128 0x1
+ .uleb128 0x13
+ .uleb128 0x3f
+ .uleb128 0xc
+ .uleb128 0x3
+ .uleb128 0x8
+ .uleb128 0x3a
+ .uleb128 0xb
+ .uleb128 0x3b
+ .uleb128 0xb
+ .uleb128 0x27
+ .uleb128 0xc
+ .uleb128 0x11
+ .uleb128 0x1
+ .uleb128 0x12
+ .uleb128 0x1
+ .uleb128 0x40
+ .uleb128 0xa
+ .byte 0x0
+ .byte 0x0
+ .uleb128 0x3
+ .uleb128 0x5
+ .byte 0x0
+ .uleb128 0x3
+ .uleb128 0x8
+ .uleb128 0x3a
+ .uleb128 0xb
+ .uleb128 0x3b
+ .uleb128 0xb
+ .uleb128 0x49
+ .uleb128 0x13
+ .uleb128 0x2
+ .uleb128 0xa
+ .byte 0x0
+ .byte 0x0
+ .uleb128 0x4
+ .uleb128 0x24
+ .byte 0x0
+ .uleb128 0x3
+ .uleb128 0x8
+ .uleb128 0xb
+ .uleb128 0xb
+ .uleb128 0x3e
+ .uleb128 0xb
+ .byte 0x0
+ .byte 0x0
+ .byte 0x0
+ .section .debug_pubnames,"",@progbits
+ .ualong 0x27
+ .uaword 0x2
+ .ualong .Ldebug_info0
+ .ualong 0xb7
+ .ualong 0x67
+ .string "_superh_trap_handler"
+ .ualong 0x0
+ .section .debug_aranges,"",@progbits
+ .ualong 0x1c
+ .uaword 0x2
+ .ualong .Ldebug_info0
+ .byte 0x4
+ .byte 0x0
+ .uaword 0x0
+ .uaword 0x0
+ .ualong .Ltext0
+ .ualong .Letext0-.Ltext0
+ .ualong 0x0
+ .ualong 0x0
+#endif /* VBR_SETUP */
+#endif /* ! __SH5__ */
diff --git a/gcc/config/sh/crti.asm b/gcc/config/sh/crti.asm
new file mode 100644
index 000000000..ef5cd719d
--- /dev/null
+++ b/gcc/config/sh/crti.asm
@@ -0,0 +1,125 @@
+/* Copyright (C) 2000, 2001, 2009 Free Software Foundation, Inc.
+ This file was adapted from glibc sources.
+
+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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+
+/* The code in sections .init and .fini is supposed to be a single
+ regular function. The function in .init is called directly from
+ start in crt1.asm. The function in .fini is atexit()ed in crt1.asm
+ too.
+
+ crti.asm contributes the prologue of a function to these sections,
+ and crtn.asm comes up the epilogue. STARTFILE_SPEC should list
+ crti.o before any other object files that might add code to .init
+ or .fini sections, and ENDFILE_SPEC should list crtn.o after any
+ such object files. */
+
+ .section .init
+/* The alignment below can't be smaller, otherwise the mova below
+ breaks. Yes, we might align just the label, but then we'd be
+ exchanging an alignment here for one there, since the code fragment
+ below ensures 4-byte alignment on __ELF__. */
+#ifdef __ELF__
+ .p2align 2
+#else
+ .p2align 1
+#endif
+ .global _init
+_init:
+#if __SHMEDIA__
+ addi r15, -16, r15
+ st.q r15, 8, r14
+ st.q r15, 0, r18
+ add r15, r63, r14
+#elif __SH5__ && ! __SHMEDIA__
+ mov r15,r0
+ add #-8,r15
+ mov.l r14,@-r0
+ sts.l pr,@-r0
+ mov r15,r14
+ nop
+#else
+#ifdef __ELF__
+ mov.l r12,@-r15
+ mova 0f,r0
+ mov.l 0f,r12
+#endif
+ mov.l r14,@-r15
+#ifdef __ELF__
+ add r0,r12
+#endif
+ sts.l pr,@-r15
+#ifdef __ELF__
+ bra 1f
+#endif
+ mov r15,r14
+#ifdef __ELF__
+0: .long _GLOBAL_OFFSET_TABLE_
+1:
+#endif
+#endif /* __SHMEDIA__ */
+
+ .section .fini
+/* The alignment below can't be smaller, otherwise the mova below
+ breaks. Yes, we might align just the label, but then we'd be
+ exchanging an alignment here for one there, since the code fragment
+ below ensures 4-byte alignment on __ELF__. */
+#ifdef __ELF__
+ .p2align 2
+#else
+ .p2align 1
+#endif
+ .global _fini
+_fini:
+#if __SHMEDIA__
+ addi r15, -16, r15
+ st.q r15, 8, r14
+ st.q r15, 0, r18
+ add r15, r63, r14
+#elif __SH5__ && ! __SHMEDIA__
+ mov r15,r0
+ add #-8,r15
+ mov.l r14,@-r0
+ sts.l pr,@-r0
+ mov r15,r14
+ nop
+#else
+#ifdef __ELF__
+ mov.l r12,@-r15
+ mova 0f,r0
+ mov.l 0f,r12
+#endif
+ mov.l r14,@-r15
+#ifdef __ELF__
+ add r0,r12
+#endif
+ sts.l pr,@-r15
+#ifdef __ELF__
+ bra 1f
+#endif
+ mov r15,r14
+#ifdef __ELF__
+0: .long _GLOBAL_OFFSET_TABLE_
+1:
+#endif
+#endif /* __SHMEDIA__ */
diff --git a/gcc/config/sh/crtn.asm b/gcc/config/sh/crtn.asm
new file mode 100644
index 000000000..670d90f7b
--- /dev/null
+++ b/gcc/config/sh/crtn.asm
@@ -0,0 +1,77 @@
+/* Copyright (C) 2000, 2001, 2009 Free Software Foundation, Inc.
+ This file was adapted from glibc sources.
+
+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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+/* See an explanation about .init and .fini in crti.asm. */
+
+ .section .init
+#if __SHMEDIA__
+ add r14, r63, r15
+ ld.q r15, 0, r18
+ ptabs r18, tr0
+ ld.q r15, 8, r14
+ addi r15, 16, r15
+ blink tr0, r63
+#elif __SH5__ && ! __SHMEDIA__
+ mov r14,r15
+ lds.l @r14+,pr
+ mov.l @r14,r14
+ rts
+ add #8,r15
+#else
+ mov r14,r15
+ lds.l @r15+,pr
+ mov.l @r15+,r14
+ rts
+#ifdef __ELF__
+ mov.l @r15+,r12
+#else
+ nop
+#endif
+#endif /* __SHMEDIA__ */
+
+ .section .fini
+#if __SHMEDIA__
+ add r14, r63, r15
+ ld.q r15, 0, r18
+ ptabs r18, tr0
+ ld.q r15, 8, r14
+ addi r15, 16, r15
+ blink tr0, r63
+#elif __SH5__ && ! __SHMEDIA__
+ mov r14,r15
+ lds.l @r14+,pr
+ mov.l @r14,r14
+ rts
+ add #8,r15
+#else
+ mov r14,r15
+ lds.l @r15+,pr
+ mov.l @r15+,r14
+ rts
+#ifdef __ELF__
+ mov.l @r15+,r12
+#else
+ nop
+#endif
+#endif /* __SHMEDIA__ */
diff --git a/gcc/config/sh/divcost-analysis b/gcc/config/sh/divcost-analysis
new file mode 100644
index 000000000..d55bb6621
--- /dev/null
+++ b/gcc/config/sh/divcost-analysis
@@ -0,0 +1,88 @@
+Analysis of cycle costs for SH4:
+
+-> udiv_le128: 5
+-> udiv_ge64k: 6
+-> udiv udiv_25: 10
+-> pos_divisor: 3
+-> pos_result linear: 5
+-> pos_result - -: 5
+-> div_le128: 7
+-> div_ge64k: 9
+sdivsi3 -> udiv_25 13
+udiv25 -> div_ge64k_end: 15
+div_ge64k_end -> rts: 13
+div_le128 -> div_le128_2: 2, r1 latency 3
+udiv_le128 -> div_le128_2: 2, r1 latency 3
+(u)div_le128 -> div_by_1: 9
+(u)div_le128 -> rts: 17
+div_by_1(_neg) -> rts: 4
+div_ge64k -> div_r8: 2
+div_ge64k -> div_ge64k_2: 3
+udiv_ge64k -> udiv_r8: 3
+udiv_ge64k -> div_ge64k_2: 3 + LS
+(u)div_ge64k -> div_ge64k_end: 13
+div_r8 -> div_r8_2: 2
+udiv_r8 -> div_r8_2: 2 + LS
+(u)div_r8 -> rts: 21
+
+-> - + neg_result: 5
+-> + - neg_result: 5
+-> div_le128_neg: 7
+-> div_ge64k_neg: 9
+-> div_r8_neg: 11
+-> <64k div_ge64k_neg_end: 28
+-> >=64k div_ge64k_neg_end: 22
+div_ge64k_neg_end ft -> rts: 14
+div_r8_neg_end -> rts: 4
+div_r8_neg -> div_r8_neg_end: 18
+div_le128_neg -> div_by_1_neg: 4
+div_le128_neg -> rts 18
+
+ sh4-200 absolute divisor range:
+ 1 [2..128] [129..64K) [64K..|dividend|/256] >=64K,>|dividend/256|
+udiv 18 22 38 32 30
+sdiv pos: 20 24 41 35 32
+sdiv neg: 15 25 42 36 33
+
+ sh4-300 absolute divisor range:
+ 8 bit 16 bit 24 bit > 24 bit
+udiv 15 35 28 25
+sdiv 14 36 34 31
+
+
+fp-based:
+
+unsigned: 42 + 3 + 3 (lingering ftrc latency + sts fpul,rx) at caller's site
+signed: 33 + 3 + 3 (lingering ftrc latency + sts fpul,rx) at caller's site
+
+call-div1: divisor range:
+ [1..64K) >= 64K
+unsigned: 63 58
+signed: 76 76
+
+SFUNC_STATIC call overhead:
+mov.l 0f,r1
+bsrf r1
+
+SFUNC_GOT call overhead - current:
+mov.l 0f,r1
+mova 0f,r0
+mov.l 1f,r2
+add r1,r0
+mov.l @(r0,r2),r0
+jmp @r0
+; 3 cycles worse than SFUNC_STATIC
+
+SFUNC_GOT call overhead - improved assembler:
+mov.l 0f,r1
+mova 0f,r0
+mov.l @(r0,r1),r0
+jmp @r0
+; 2 cycles worse than SFUNC_STATIC
+
+
+Copyright (C) 2006, 2007 Free Software Foundation, Inc.
+
+Copying and distribution of this file, with or without modification,
+are permitted in any medium without royalty provided the copyright
+notice and this notice are preserved.
diff --git a/gcc/config/sh/divtab-sh4-300.c b/gcc/config/sh/divtab-sh4-300.c
new file mode 100644
index 000000000..c8a65cfbc
--- /dev/null
+++ b/gcc/config/sh/divtab-sh4-300.c
@@ -0,0 +1,77 @@
+/* Copyright (C) 2004, 2006, 2009 Free Software Foundation, Inc.
+
+This file is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3, or (at your option) any
+later version.
+
+This file is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+
+/* Calculate division table for ST40-300 integer division
+ Contributed by Joern Rennecke
+ joern.rennecke@st.com */
+
+#include <stdio.h>
+#include <math.h>
+
+int
+main ()
+{
+ int i, j;
+ double q, r, err, max_err = 0, max_s_err = 0;
+
+ puts("/* This table has been generated by divtab-sh4.c. */");
+ puts ("\t.balign 4");
+ for (i = -128; i < 128; i++)
+ {
+ int n = 0;
+ if (i == 0)
+ {
+ /* output some dummy number for 1/0. */
+ puts ("LOCAL(div_table_clz):\n\t.byte\t0");
+ continue;
+ }
+ for (j = i < 0 ? -i : i; j < 128; j += j)
+ n++;
+ printf ("\t.byte\t%d\n", n - 7);
+ }
+ puts("\
+/* 1/-128 .. 1/127, normalized. There is an implicit leading 1 in bit 32,\n\
+ or in bit 33 for powers of two. */\n\
+ .balign 4");
+ for (i = -128; i < 128; i++)
+ {
+ if (i == 0)
+ {
+ puts ("LOCAL(div_table_inv):\n\t.long\t0x0");
+ continue;
+ }
+ j = i < 0 ? -i : i;
+ while (j < 64)
+ j += j;
+ q = 4.*(1<<30)*128/j;
+ r = ceil (q);
+ printf ("\t.long\t0x%X\n", (unsigned) r);
+ err = r - q;
+ if (err > max_err)
+ max_err = err;
+ err = err * j / 128;
+ if (err > max_s_err)
+ max_s_err = err;
+ }
+ printf ("\t/* maximum error: %f scaled: %f*/\n", max_err, max_s_err);
+ exit (0);
+}
diff --git a/gcc/config/sh/divtab-sh4.c b/gcc/config/sh/divtab-sh4.c
new file mode 100644
index 000000000..758508130
--- /dev/null
+++ b/gcc/config/sh/divtab-sh4.c
@@ -0,0 +1,85 @@
+/* Copyright (C) 2004, 2009 Free Software Foundation, Inc.
+
+This file is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3, or (at your option) any
+later version.
+
+This file is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+/* Calculate division table for SH2..4 integer division
+ Contributed by Joern Rernnecke
+ joern.rennecke@superh.com */
+
+#include <stdio.h>
+#include <math.h>
+
+int
+main ()
+{
+ int i, j;
+ double q, r, err, max_err = 0, max_s_err = 0;
+
+ puts("/* This table has been generated by divtab-sh4.c. */");
+ puts ("\t.balign 4");
+ puts ("LOCAL(div_table_clz):");
+ /* output some dummy number for 1/0. */
+ printf ("\t.byte\t%d\n", 0);
+ for (i = 1; i <= 128; i++)
+ {
+ int n = 0;
+ if (i == 128)
+ puts ("\
+/* Lookup table translating positive divisor to index into table of\n\
+ normalized inverse. N.B. the '0' entry is also the last entry of the\n\
+ previous table, and causes an unaligned access for division by zero. */\n\
+LOCAL(div_table_ix):");
+ for (j = i; j <= 128; j += j)
+ n++;
+ printf ("\t.byte\t%d\n", n - 7);
+ }
+ for (i = 1; i <= 128; i++)
+ {
+ j = i < 0 ? -i : i;
+ while (j < 128)
+ j += j;
+ printf ("\t.byte\t%d\n", j * 2 - 96*4);
+ }
+ puts("\
+/* 1/64 .. 1/127, normalized. There is an implicit leading 1 in bit 32. */\n\
+ .balign 4\n\
+LOCAL(zero_l):");
+ for (i = 64; i < 128; i++)
+ {
+ if (i == 96)
+ puts ("LOCAL(div_table):");
+ q = 4.*(1<<30)*128/i;
+ r = ceil (q);
+ /* The value for 64 is actually differently scaled that it would
+ appear from this calculation. The implicit part is %01, not 10.
+ Still, since the value in the table is 0 either way, this
+ doesn't matter here. Still, the 1/64 entry is effectively a 1/128
+ entry. */
+ printf ("\t.long\t0x%X\n", (unsigned) r);
+ err = r - q;
+ if (err > max_err)
+ max_err = err;
+ err = err * i / 128;
+ if (err > max_s_err)
+ max_s_err = err;
+ }
+ printf ("\t/* maximum error: %f scaled: %f*/\n", max_err, max_s_err);
+ exit (0);
+}
diff --git a/gcc/config/sh/divtab.c b/gcc/config/sh/divtab.c
new file mode 100644
index 000000000..f8db2f508
--- /dev/null
+++ b/gcc/config/sh/divtab.c
@@ -0,0 +1,200 @@
+/* Copyright (C) 2003, 2009 Free Software Foundation, Inc.
+
+This file is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3, or (at your option) any
+later version.
+
+This file is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+
+/* Calculate division table for SH5Media integer division
+ Contributed by Joern Rennecke
+ joern.rennecke@superh.com */
+
+#include <stdio.h>
+#include <math.h>
+
+#define BITS 5
+#define N_ENTRIES (1 << BITS)
+#define CUTOFF_BITS 20
+
+#define BIAS (-330)
+
+double max_defect = 0.;
+double max_defect_x;
+
+double min_defect = 1e9;
+double min_defect_x;
+
+double max_defect2 = 0.;
+double max_defect2_x;
+
+double min_defect2 = 0.;
+double min_defect2_x;
+
+double min_defect3 = 01e9;
+double min_defect3_x;
+int min_defect3_val;
+
+double max_defect3 = 0.;
+double max_defect3_x;
+int max_defect3_val;
+
+static double note_defect3 (int val, double d2, double y2d, double x)
+{
+ int cutoff_val = val >> CUTOFF_BITS;
+ double cutoff;
+ double defect;
+
+ if (val < 0)
+ cutoff_val++;
+ cutoff = (cutoff_val * (1<<CUTOFF_BITS) - val) * y2d;
+ defect = cutoff + val * d2;
+ if (val < 0)
+ defect = - defect;
+ if (defect > max_defect3)
+ {
+ max_defect3 = defect;
+ max_defect3_x = x;
+ max_defect3_val = val;
+ }
+ if (defect < min_defect3)
+ {
+ min_defect3 = defect;
+ min_defect3_x = x;
+ min_defect3_val = val;
+ }
+}
+
+/* This function assumes 32-bit integers. */
+static double
+calc_defect (double x, int constant, int factor)
+{
+ double y0 = (constant - (int) floor ((x * factor * 64.))) / 16384.;
+ double y1 = 2 * y0 -y0 * y0 * (x + BIAS / (1.*(1LL<<30)));
+ double y2d0, y2d;
+ int y2d1;
+ double d, d2;
+
+ y1 = floor (y1 * (1024 * 1024 * 1024)) / (1024 * 1024 * 1024);
+ d = y1 - 1 / x;
+ if (d > max_defect)
+ {
+ max_defect = d;
+ max_defect_x = x;
+ }
+ if (d < min_defect)
+ {
+ min_defect = d;
+ min_defect_x = x;
+ }
+ y2d0 = floor (y1 * x * (1LL << 60-16));
+ y2d1 = (int) (long long) y2d0;
+ y2d = - floor ((y1 - y0 / (1<<30-14)) * y2d1) / (1LL<<44);
+ d2 = y1 + y2d - 1/x;
+ if (d2 > max_defect2)
+ {
+ max_defect2 = d2;
+ max_defect2_x = x;
+ }
+ if (d2 < min_defect2)
+ {
+ min_defect2 = d2;
+ min_defect2_x = x;
+ }
+ /* zero times anything is trivially zero. */
+ note_defect3 ((1 << CUTOFF_BITS) - 1, d2, y2d, x);
+ note_defect3 (1 << CUTOFF_BITS, d2, y2d, x);
+ note_defect3 ((1U << 31) - (1 << CUTOFF_BITS), d2, y2d, x);
+ note_defect3 ((1U << 31) - 1, d2, y2d, x);
+ note_defect3 (-1, d2, y2d, x);
+ note_defect3 (-(1 << CUTOFF_BITS), d2, y2d, x);
+ note_defect3 ((1U << 31) - (1 << CUTOFF_BITS) + 1, d2, y2d, x);
+ note_defect3 (-(1U << 31), d2, y2d, x);
+ return d;
+}
+
+int
+main ()
+{
+ int i;
+ unsigned char factors[N_ENTRIES];
+ short constants[N_ENTRIES];
+ int steps = N_ENTRIES / 2;
+ double step = 1. / steps;
+ double eps30 = 1. / (1024 * 1024 * 1024);
+
+ for (i = 0; i < N_ENTRIES; i++)
+ {
+ double x_low = (i < steps ? 1. : -3.) + i * step;
+ double x_high = x_low + step - eps30;
+ double x_med;
+ int factor, constant;
+ double low_defect, med_defect, high_defect, max_defect;
+
+ factor = (1./x_low- 1./x_high) / step * 256. + 0.5;
+ if (factor == 256)
+ factor = 255;
+ factors[i] = factor;
+ /* Use minimum of error function for x_med. */
+ x_med = sqrt (256./factor);
+ if (x_low < 0)
+ x_med = - x_med;
+ low_defect = 1. / x_low + x_low * factor / 256.;
+ high_defect = 1. / x_high + x_high * factor / 256.;
+ med_defect = 1. / x_med + x_med * factor / 256.;
+ max_defect
+ = ((low_defect > high_defect) ^ (x_med < 0)) ? low_defect : high_defect;
+ constant = (med_defect + max_defect) * 0.5 * 16384. + 0.5;
+ if (constant < -32768 || constant > 32767)
+ abort ();
+ constants[i] = constant;
+ calc_defect (x_low, constant, factor);
+ calc_defect (x_med, constant, factor);
+ calc_defect (x_high, constant, factor);
+ }
+ printf ("/* This table has been generated by divtab.c .\n");
+ printf ("Defects for bias %d:\n", BIAS);
+ printf (" Max defect: %e at %e\n", max_defect, max_defect_x);
+ printf (" Min defect: %e at %e\n", min_defect, min_defect_x);
+ printf (" Max 2nd step defect: %e at %e\n", max_defect2, max_defect2_x);
+ printf (" Min 2nd step defect: %e at %e\n", min_defect2, min_defect2_x);
+ printf (" Max div defect: %e at %d:%e\n", max_defect3, max_defect3_val, max_defect3_x);
+ printf (" Min div defect: %e at %d:%e\n", min_defect3, min_defect3_val, min_defect3_x);
+ printf (" Defect at 1: %e\n",
+ calc_defect (1., constants[0], factors[0]));
+ printf (" Defect at -2: %e */\n",
+ calc_defect (-2., constants[steps], factors[steps]));
+ printf ("\t.section\t.rodata\n");
+ printf ("\t.balign 2\n");
+ printf ("/* negative division constants */\n");
+ for (i = steps; i < 2 * steps; i++)
+ printf ("\t.word\t%d\n", constants[i]);
+ printf ("/* negative division factors */\n");
+ for (i = steps; i < 2*steps; i++)
+ printf ("\t.byte\t%d\n", factors[i]);
+ printf ("\t.skip %d\n", steps);
+ printf ("\t.global GLOBAL(div_table):\n");
+ printf ("GLOBAL(div_table):\n");
+ printf ("\t.skip %d\n", steps);
+ printf ("/* positive division factors */\n");
+ for (i = 0; i < steps; i++)
+ printf ("\t.byte\t%d\n", factors[i]);
+ printf ("/* positive division constants */\n");
+ for (i = 0; i < steps; i++)
+ printf ("\t.word\t%d\n", constants[i]);
+ exit (0);
+}
diff --git a/gcc/config/sh/elf.h b/gcc/config/sh/elf.h
new file mode 100644
index 000000000..336743cc8
--- /dev/null
+++ b/gcc/config/sh/elf.h
@@ -0,0 +1,90 @@
+/* Definitions of target machine for gcc for Renesas / SuperH SH using ELF.
+ Copyright (C) 1996, 1997, 2000, 2001, 2002, 2004, 2005, 2007, 2010
+ Free Software Foundation, Inc.
+ Contributed by Ian Lance Taylor <ian@cygnus.com>.
+
+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
+<http://www.gnu.org/licenses/>. */
+
+/* Let sh.c know this is ELF. */
+#undef TARGET_ELF
+#define TARGET_ELF 1
+
+/* Generate DWARF2 debugging information and make it the default */
+#define DWARF2_DEBUGGING_INFO 1
+
+#undef PREFERRED_DEBUGGING_TYPE
+#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG
+
+/* use a more compact format for line information */
+#define DWARF2_ASM_LINE_DEBUG_INFO 1
+
+#undef WCHAR_TYPE
+/* #define WCHAR_TYPE (TARGET_SH5 ? "int" : "long int") */
+#define WCHAR_TYPE SH_ELF_WCHAR_TYPE
+
+#undef WCHAR_TYPE_SIZE
+#define WCHAR_TYPE_SIZE 32
+
+
+/* The prefix to add to user-visible assembler symbols. */
+
+#undef LOCAL_LABEL_PREFIX
+#define LOCAL_LABEL_PREFIX "."
+
+#undef SIZE_TYPE
+#define SIZE_TYPE (TARGET_SH5 ? "long unsigned int" : "unsigned int")
+
+#undef PTRDIFF_TYPE
+#define PTRDIFF_TYPE (TARGET_SH5 ? "long int" : "int")
+
+/* Pass -ml and -mrelax to the assembler and linker. */
+#undef ASM_SPEC
+#define ASM_SPEC SH_ASM_SPEC
+
+#undef LINK_SPEC
+#define LINK_SPEC SH_LINK_SPEC
+#undef LINK_EMUL_PREFIX
+#if TARGET_ENDIAN_DEFAULT == MASK_LITTLE_ENDIAN
+#define LINK_EMUL_PREFIX "sh%{!mb:l}elf"
+#else
+#define LINK_EMUL_PREFIX "sh%{ml:l}elf"
+#endif
+
+#define DBX_REGISTER_NUMBER(REGNO) SH_DBX_REGISTER_NUMBER (REGNO)
+
+#undef ASM_GENERATE_INTERNAL_LABEL
+#define ASM_GENERATE_INTERNAL_LABEL(STRING, PREFIX, NUM) \
+ sprintf ((STRING), "*%s%s%ld", LOCAL_LABEL_PREFIX, (PREFIX), (long)(NUM))
+
+#define DBX_LINES_FUNCTION_RELATIVE 1
+#define DBX_OUTPUT_NULL_N_SO_AT_MAIN_SOURCE_FILE_END
+
+#undef STARTFILE_SPEC
+#define STARTFILE_SPEC \
+ "%{!shared: crt1.o%s} crti.o%s \
+ %{!shared:crtbegin.o%s} %{shared:crtbeginS.o%s}"
+
+#undef ENDFILE_SPEC
+#define ENDFILE_SPEC \
+ "%{!shared:crtend.o%s} %{shared:crtendS.o%s} crtn.o%s"
+
+#undef LIB_SPEC
+#define LIB_SPEC "-lc"
+
+/* ASM_OUTPUT_CASE_LABEL is defined in elfos.h. With it,
+ a redundant .align was generated. */
+#undef ASM_OUTPUT_CASE_LABEL
diff --git a/gcc/config/sh/embed-elf.h b/gcc/config/sh/embed-elf.h
new file mode 100644
index 000000000..a9f6d9438
--- /dev/null
+++ b/gcc/config/sh/embed-elf.h
@@ -0,0 +1,36 @@
+/* Definitions of target machine for GNU compiler for Renesas / SuperH SH
+ non-Linux embedded targets.
+ Copyright (C) 2002, 2003, 2007, 2010, 2011 Free Software Foundation, Inc.
+ Contributed by J"orn Rennecke <joern.rennecke@superh.com>
+
+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
+<http://www.gnu.org/licenses/>. */
+
+#undef USER_LABEL_PREFIX
+#define USER_LABEL_PREFIX "_"
+
+/* While the speed-optimized implementations of udivsi3_i4i / sdivsi3_i4i
+ in libgcc are not available for SH2, the space-optimized ones in
+ libgcc-Os-4-200 are. Thus, when not optimizing for space, link
+ libgcc-Os-4-200 after libgcc, so that -mdiv=call-table works for -m2. */
+#define LIBGCC_SPEC "%{!shared: \
+ %{m4-100*:-lic_invalidate_array_4-100} \
+ %{m4-200*:-lic_invalidate_array_4-200} \
+ %{m4-300*|m4-340:-lic_invalidate_array_4a %{!Os: -lgcc-4-300}} \
+ %{m4a*:-lic_invalidate_array_4a}} \
+ %{Os: -lgcc-Os-4-200} \
+ -lgcc \
+ %{!Os: -lgcc-Os-4-200}"
diff --git a/gcc/config/sh/lib1funcs-4-300.asm b/gcc/config/sh/lib1funcs-4-300.asm
new file mode 100644
index 000000000..b131877f1
--- /dev/null
+++ b/gcc/config/sh/lib1funcs-4-300.asm
@@ -0,0 +1,936 @@
+/* Copyright (C) 2004, 2006, 2009 Free Software Foundation, Inc.
+
+This file is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3, or (at your option) any
+later version.
+
+This file is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+
+/* libgcc routines for the STMicroelectronics ST40-300 CPU.
+ Contributed by J"orn Rennecke joern.rennecke@st.com. */
+
+#include "lib1funcs.h"
+
+#if !__SHMEDIA__
+#ifdef L_div_table
+#if defined (__SH3__) || defined (__SH3E__) || defined (__SH4__) || defined (__SH4_SINGLE__) || defined (__SH4_SINGLE_ONLY__) || defined (__SH4_NOFPU__)
+/* This code used shld, thus is not suitable for SH1 / SH2. */
+
+/* Signed / unsigned division without use of FPU, optimized for SH4-300.
+ Uses a lookup table for divisors in the range -128 .. +127, and
+ div1 with case distinction for larger divisors in three more ranges.
+ The code is lumped together with the table to allow the use of mova. */
+#ifdef __LITTLE_ENDIAN__
+#define L_LSB 0
+#define L_LSWMSB 1
+#define L_MSWLSB 2
+#else
+#define L_LSB 3
+#define L_LSWMSB 2
+#define L_MSWLSB 1
+#endif
+
+ .global GLOBAL(udivsi3_i4i)
+ .global GLOBAL(sdivsi3_i4i)
+ FUNC(GLOBAL(udivsi3_i4i))
+ FUNC(GLOBAL(sdivsi3_i4i))
+
+ .balign 4
+LOCAL(div_ge8m): ! 10 cycles up to here
+ rotcr r1 ! signed shift must use original sign from r4
+ div0s r5,r4
+ mov #24,r7
+ shld r7,r6
+ shad r0,r1
+ rotcl r6
+ div1 r5,r1
+ swap.w r5,r0 ! detect -0x80000000 : 0x800000
+ rotcl r6
+ swap.w r4,r7
+ div1 r5,r1
+ swap.b r7,r7
+ rotcl r6
+ or r7,r0
+ div1 r5,r1
+ swap.w r0,r7
+ rotcl r6
+ or r7,r0
+ div1 r5,r1
+ add #-0x80,r0
+ rotcl r6
+ extu.w r0,r0
+ div1 r5,r1
+ neg r0,r0
+ rotcl r6
+ swap.w r0,r0
+ div1 r5,r1
+ mov.l @r15+,r7
+ and r6,r0
+ rotcl r6
+ div1 r5,r1
+ shll2 r0
+ rotcl r6
+ exts.b r0,r0
+ div1 r5,r1
+ swap.w r0,r0
+ exts.w r0,r1
+ exts.b r6,r0
+ mov.l @r15+,r6
+ rotcl r0
+ rts
+ sub r1,r0
+ ! 31 cycles up to here
+
+ .balign 4
+LOCAL(udiv_ge64k): ! 3 cycles up to here
+ mov r4,r0
+ shlr8 r0
+ div0u
+ cmp/hi r0,r5
+ bt LOCAL(udiv_r8)
+ mov.l r5,@-r15
+ shll8 r5
+ ! 7 cycles up to here
+ .rept 8
+ div1 r5,r0
+ .endr
+ extu.b r4,r1 ! 15 cycles up to here
+ extu.b r0,r6
+ xor r1,r0
+ xor r6,r0
+ swap.b r6,r6
+ .rept 8
+ div1 r5,r0
+ .endr ! 25 cycles up to here
+ extu.b r0,r0
+ mov.l @r15+,r5
+ or r6,r0
+ mov.l @r15+,r6
+ rts
+ rotcl r0 ! 28 cycles up to here
+
+ .balign 4
+LOCAL(udiv_r8): ! 6 cycles up to here
+ mov.l r4,@-r15
+ shll16 r4
+ shll8 r4
+ !
+ shll r4
+ mov r0,r1
+ div1 r5,r1
+ mov r4,r0
+ rotcl r0
+ mov.l @r15+,r4
+ div1 r5,r1
+ ! 12 cycles up to here
+ .rept 6
+ rotcl r0; div1 r5,r1
+ .endr
+ mov.l @r15+,r6 ! 24 cycles up to here
+ rts
+ rotcl r0
+
+ .balign 4
+LOCAL(div_ge32k): ! 6 cycles up to here
+ mov.l r7,@-r15
+ swap.w r5,r6
+ exts.b r6,r7
+ exts.w r6,r6
+ cmp/eq r6,r7
+ extu.b r1,r6
+ bf/s LOCAL(div_ge8m)
+ cmp/hi r1,r4 ! copy sign bit of r4 into T
+ rotcr r1 ! signed shift must use original sign from r4
+ div0s r5,r4
+ shad r0,r1
+ shll8 r5
+ div1 r5,r1
+ mov r5,r7 ! detect r4 == 0x80000000 && r5 == 0x8000(00)
+ div1 r5,r1
+ shlr8 r7
+ div1 r5,r1
+ swap.w r4,r0
+ div1 r5,r1
+ swap.b r0,r0
+ div1 r5,r1
+ or r0,r7
+ div1 r5,r1
+ add #-80,r7
+ div1 r5,r1
+ swap.w r7,r0
+ div1 r5,r1
+ or r0,r7
+ extu.b r1,r0
+ xor r6,r1
+ xor r0,r1
+ exts.b r0,r0
+ div1 r5,r1
+ extu.w r7,r7
+ div1 r5,r1
+ neg r7,r7 ! upper 16 bit of r7 == 0 if r4 == 0x80000000 && r5 == 0x8000
+ div1 r5,r1
+ and r0,r7
+ div1 r5,r1
+ swap.w r7,r7 ! 26 cycles up to here.
+ div1 r5,r1
+ shll8 r0
+ div1 r5,r1
+ exts.w r7,r7
+ div1 r5,r1
+ add r0,r0
+ div1 r5,r1
+ sub r7,r0
+ extu.b r1,r1
+ mov.l @r15+,r7
+ rotcl r1
+ mov.l @r15+,r6
+ add r1,r0
+ mov #-8,r1
+ rts
+ shad r1,r5 ! 34 cycles up to here
+
+ .balign 4
+GLOBAL(udivsi3_i4i):
+ mov.l r6,@-r15
+ extu.w r5,r6
+ cmp/eq r5,r6
+ mov #0x7f,r0
+ bf LOCAL(udiv_ge64k)
+ cmp/hi r0,r5
+ bf LOCAL(udiv_le128)
+ mov r4,r1
+ shlr8 r1
+ div0u
+ shlr r1
+ shll16 r6
+ div1 r6,r1
+ extu.b r4,r0 ! 7 cycles up to here
+ .rept 8
+ div1 r6,r1
+ .endr ! 15 cycles up to here
+ xor r1,r0 ! xor dividend with result lsb
+ .rept 6
+ div1 r6,r1
+ .endr
+ mov.l r7,@-r15 ! 21 cycles up to here
+ div1 r6,r1
+ extu.b r0,r7
+ div1 r6,r1
+ shll8 r7
+ extu.w r1,r0
+ xor r7,r1 ! replace lsb of result with lsb of dividend
+ div1 r6,r1
+ mov #0,r7
+ div1 r6,r1
+ !
+ div1 r6,r1
+ bra LOCAL(div_end)
+ div1 r6,r1 ! 28 cycles up to here
+
+ /* This is link-compatible with a GLOBAL(sdivsi3) call,
+ but we effectively clobber only r1, macl and mach */
+ /* Because negative quotients are calculated as one's complements,
+ -0x80000000 divided by the smallest positive number of a number
+ range (0x80, 0x8000, 0x800000) causes saturation in the one's
+ complement representation, and we have to suppress the
+ one's -> two's complement adjustment. Since positive numbers
+ don't get such an adjustment, it's OK to also compute one's -> two's
+ complement adjustment suppression for a dividend of 0. */
+ .balign 4
+GLOBAL(sdivsi3_i4i):
+ mov.l r6,@-r15
+ exts.b r5,r6
+ cmp/eq r5,r6
+ mov #-1,r1
+ bt/s LOCAL(div_le128)
+ cmp/pz r4
+ addc r4,r1
+ exts.w r5,r6
+ cmp/eq r5,r6
+ mov #-7,r0
+ bf/s LOCAL(div_ge32k)
+ cmp/hi r1,r4 ! copy sign bit of r4 into T
+ rotcr r1
+ shll16 r6 ! 7 cycles up to here
+ shad r0,r1
+ div0s r5,r4
+ div1 r6,r1
+ mov.l r7,@-r15
+ div1 r6,r1
+ mov r4,r0 ! re-compute adjusted dividend
+ div1 r6,r1
+ mov #-31,r7
+ div1 r6,r1
+ shad r7,r0
+ div1 r6,r1
+ add r4,r0 ! adjusted dividend
+ div1 r6,r1
+ mov.l r8,@-r15
+ div1 r6,r1
+ swap.w r4,r8 ! detect special case r4 = 0x80000000, r5 = 0x80
+ div1 r6,r1
+ swap.b r8,r8
+ xor r1,r0 ! xor dividend with result lsb
+ div1 r6,r1
+ div1 r6,r1
+ or r5,r8
+ div1 r6,r1
+ add #-0x80,r8 ! r8 is 0 iff there is a match
+ div1 r6,r1
+ swap.w r8,r7 ! or upper 16 bits...
+ div1 r6,r1
+ or r7,r8 !...into lower 16 bits
+ div1 r6,r1
+ extu.w r8,r8
+ div1 r6,r1
+ extu.b r0,r7
+ div1 r6,r1
+ shll8 r7
+ exts.w r1,r0
+ xor r7,r1 ! replace lsb of result with lsb of dividend
+ div1 r6,r1
+ neg r8,r8 ! upper 16 bits of r8 are now 0xffff iff we want end adjm.
+ div1 r6,r1
+ and r0,r8
+ div1 r6,r1
+ swap.w r8,r7
+ div1 r6,r1
+ mov.l @r15+,r8 ! 58 insns, 29 cycles up to here
+LOCAL(div_end):
+ div1 r6,r1
+ shll8 r0
+ div1 r6,r1
+ exts.w r7,r7
+ div1 r6,r1
+ add r0,r0
+ div1 r6,r1
+ sub r7,r0
+ extu.b r1,r1
+ mov.l @r15+,r7
+ rotcl r1
+ mov.l @r15+,r6
+ rts
+ add r1,r0
+
+ .balign 4
+LOCAL(udiv_le128): ! 4 cycles up to here (or 7 for mispredict)
+ mova LOCAL(div_table_inv),r0
+ shll2 r6
+ mov.l @(r0,r6),r1
+ mova LOCAL(div_table_clz),r0
+ lds r4,mach
+ !
+ !
+ !
+ tst r1,r1
+ !
+ bt 0f
+ dmulu.l r1,r4
+0: mov.b @(r0,r5),r1
+ clrt
+ !
+ !
+ sts mach,r0
+ addc r4,r0
+ rotcr r0
+ mov.l @r15+,r6
+ rts
+ shld r1,r0
+
+ .balign 4
+LOCAL(div_le128): ! 3 cycles up to here (or 6 for mispredict)
+ mova LOCAL(div_table_inv),r0
+ shll2 r6
+ mov.l @(r0,r6),r1
+ mova LOCAL(div_table_clz),r0
+ neg r4,r6
+ bf 0f
+ mov r4,r6
+0: lds r6,mach
+ tst r1,r1
+ bt 0f
+ dmulu.l r1,r6
+0: div0s r4,r5
+ mov.b @(r0,r5),r1
+ bt/s LOCAL(le128_neg)
+ clrt
+ !
+ sts mach,r0
+ addc r6,r0
+ rotcr r0
+ mov.l @r15+,r6
+ rts
+ shld r1,r0
+
+/* Could trap divide by zero for the cost of one cycle more mispredict penalty:
+...
+ dmulu.l r1,r6
+0: div0s r4,r5
+ bt/s LOCAL(le128_neg)
+ tst r5,r5
+ bt LOCAL(div_by_zero)
+ mov.b @(r0,r5),r1
+ sts mach,r0
+ addc r6,r0
+...
+LOCAL(div_by_zero):
+ trapa #
+ .balign 4
+LOCAL(le128_neg):
+ bt LOCAL(div_by_zero)
+ mov.b @(r0,r5),r1
+ sts mach,r0
+ addc r6,r0
+... */
+
+ .balign 4
+LOCAL(le128_neg):
+ sts mach,r0
+ addc r6,r0
+ rotcr r0
+ mov.l @r15+,r6
+ shad r1,r0
+ rts
+ neg r0,r0
+ ENDFUNC(GLOBAL(udivsi3_i4i))
+ ENDFUNC(GLOBAL(sdivsi3_i4i))
+
+/* This table has been generated by divtab-sh4.c. */
+ .balign 4
+ .byte -7
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -3
+ .byte -3
+ .byte -3
+ .byte -3
+ .byte -3
+ .byte -3
+ .byte -3
+ .byte -3
+ .byte -2
+ .byte -2
+ .byte -2
+ .byte -2
+ .byte -1
+ .byte -1
+ .byte 0
+LOCAL(div_table_clz):
+ .byte 0
+ .byte 0
+ .byte -1
+ .byte -1
+ .byte -2
+ .byte -2
+ .byte -2
+ .byte -2
+ .byte -3
+ .byte -3
+ .byte -3
+ .byte -3
+ .byte -3
+ .byte -3
+ .byte -3
+ .byte -3
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+/* 1/-128 .. 1/127, normalized. There is an implicit leading 1 in bit 32,
+ or in bit 33 for powers of two. */
+ .balign 4
+ .long 0x0
+ .long 0x2040811
+ .long 0x4104105
+ .long 0x624DD30
+ .long 0x8421085
+ .long 0xA6810A7
+ .long 0xC9714FC
+ .long 0xECF56BF
+ .long 0x11111112
+ .long 0x135C8114
+ .long 0x15B1E5F8
+ .long 0x18118119
+ .long 0x1A7B9612
+ .long 0x1CF06ADB
+ .long 0x1F7047DD
+ .long 0x21FB7813
+ .long 0x24924925
+ .long 0x27350B89
+ .long 0x29E4129F
+ .long 0x2C9FB4D9
+ .long 0x2F684BDB
+ .long 0x323E34A3
+ .long 0x3521CFB3
+ .long 0x38138139
+ .long 0x3B13B13C
+ .long 0x3E22CBCF
+ .long 0x41414142
+ .long 0x446F8657
+ .long 0x47AE147B
+ .long 0x4AFD6A06
+ .long 0x4E5E0A73
+ .long 0x51D07EAF
+ .long 0x55555556
+ .long 0x58ED2309
+ .long 0x5C9882BA
+ .long 0x60581606
+ .long 0x642C8591
+ .long 0x68168169
+ .long 0x6C16C16D
+ .long 0x702E05C1
+ .long 0x745D1746
+ .long 0x78A4C818
+ .long 0x7D05F418
+ .long 0x81818182
+ .long 0x86186187
+ .long 0x8ACB90F7
+ .long 0x8F9C18FA
+ .long 0x948B0FCE
+ .long 0x9999999A
+ .long 0x9EC8E952
+ .long 0xA41A41A5
+ .long 0xA98EF607
+ .long 0xAF286BCB
+ .long 0xB4E81B4F
+ .long 0xBACF914D
+ .long 0xC0E07039
+ .long 0xC71C71C8
+ .long 0xCD856891
+ .long 0xD41D41D5
+ .long 0xDAE6076C
+ .long 0xE1E1E1E2
+ .long 0xE9131AC0
+ .long 0xF07C1F08
+ .long 0xF81F81F9
+ .long 0x0
+ .long 0x4104105
+ .long 0x8421085
+ .long 0xC9714FC
+ .long 0x11111112
+ .long 0x15B1E5F8
+ .long 0x1A7B9612
+ .long 0x1F7047DD
+ .long 0x24924925
+ .long 0x29E4129F
+ .long 0x2F684BDB
+ .long 0x3521CFB3
+ .long 0x3B13B13C
+ .long 0x41414142
+ .long 0x47AE147B
+ .long 0x4E5E0A73
+ .long 0x55555556
+ .long 0x5C9882BA
+ .long 0x642C8591
+ .long 0x6C16C16D
+ .long 0x745D1746
+ .long 0x7D05F418
+ .long 0x86186187
+ .long 0x8F9C18FA
+ .long 0x9999999A
+ .long 0xA41A41A5
+ .long 0xAF286BCB
+ .long 0xBACF914D
+ .long 0xC71C71C8
+ .long 0xD41D41D5
+ .long 0xE1E1E1E2
+ .long 0xF07C1F08
+ .long 0x0
+ .long 0x8421085
+ .long 0x11111112
+ .long 0x1A7B9612
+ .long 0x24924925
+ .long 0x2F684BDB
+ .long 0x3B13B13C
+ .long 0x47AE147B
+ .long 0x55555556
+ .long 0x642C8591
+ .long 0x745D1746
+ .long 0x86186187
+ .long 0x9999999A
+ .long 0xAF286BCB
+ .long 0xC71C71C8
+ .long 0xE1E1E1E2
+ .long 0x0
+ .long 0x11111112
+ .long 0x24924925
+ .long 0x3B13B13C
+ .long 0x55555556
+ .long 0x745D1746
+ .long 0x9999999A
+ .long 0xC71C71C8
+ .long 0x0
+ .long 0x24924925
+ .long 0x55555556
+ .long 0x9999999A
+ .long 0x0
+ .long 0x55555556
+ .long 0x0
+ .long 0x0
+LOCAL(div_table_inv):
+ .long 0x0
+ .long 0x0
+ .long 0x0
+ .long 0x55555556
+ .long 0x0
+ .long 0x9999999A
+ .long 0x55555556
+ .long 0x24924925
+ .long 0x0
+ .long 0xC71C71C8
+ .long 0x9999999A
+ .long 0x745D1746
+ .long 0x55555556
+ .long 0x3B13B13C
+ .long 0x24924925
+ .long 0x11111112
+ .long 0x0
+ .long 0xE1E1E1E2
+ .long 0xC71C71C8
+ .long 0xAF286BCB
+ .long 0x9999999A
+ .long 0x86186187
+ .long 0x745D1746
+ .long 0x642C8591
+ .long 0x55555556
+ .long 0x47AE147B
+ .long 0x3B13B13C
+ .long 0x2F684BDB
+ .long 0x24924925
+ .long 0x1A7B9612
+ .long 0x11111112
+ .long 0x8421085
+ .long 0x0
+ .long 0xF07C1F08
+ .long 0xE1E1E1E2
+ .long 0xD41D41D5
+ .long 0xC71C71C8
+ .long 0xBACF914D
+ .long 0xAF286BCB
+ .long 0xA41A41A5
+ .long 0x9999999A
+ .long 0x8F9C18FA
+ .long 0x86186187
+ .long 0x7D05F418
+ .long 0x745D1746
+ .long 0x6C16C16D
+ .long 0x642C8591
+ .long 0x5C9882BA
+ .long 0x55555556
+ .long 0x4E5E0A73
+ .long 0x47AE147B
+ .long 0x41414142
+ .long 0x3B13B13C
+ .long 0x3521CFB3
+ .long 0x2F684BDB
+ .long 0x29E4129F
+ .long 0x24924925
+ .long 0x1F7047DD
+ .long 0x1A7B9612
+ .long 0x15B1E5F8
+ .long 0x11111112
+ .long 0xC9714FC
+ .long 0x8421085
+ .long 0x4104105
+ .long 0x0
+ .long 0xF81F81F9
+ .long 0xF07C1F08
+ .long 0xE9131AC0
+ .long 0xE1E1E1E2
+ .long 0xDAE6076C
+ .long 0xD41D41D5
+ .long 0xCD856891
+ .long 0xC71C71C8
+ .long 0xC0E07039
+ .long 0xBACF914D
+ .long 0xB4E81B4F
+ .long 0xAF286BCB
+ .long 0xA98EF607
+ .long 0xA41A41A5
+ .long 0x9EC8E952
+ .long 0x9999999A
+ .long 0x948B0FCE
+ .long 0x8F9C18FA
+ .long 0x8ACB90F7
+ .long 0x86186187
+ .long 0x81818182
+ .long 0x7D05F418
+ .long 0x78A4C818
+ .long 0x745D1746
+ .long 0x702E05C1
+ .long 0x6C16C16D
+ .long 0x68168169
+ .long 0x642C8591
+ .long 0x60581606
+ .long 0x5C9882BA
+ .long 0x58ED2309
+ .long 0x55555556
+ .long 0x51D07EAF
+ .long 0x4E5E0A73
+ .long 0x4AFD6A06
+ .long 0x47AE147B
+ .long 0x446F8657
+ .long 0x41414142
+ .long 0x3E22CBCF
+ .long 0x3B13B13C
+ .long 0x38138139
+ .long 0x3521CFB3
+ .long 0x323E34A3
+ .long 0x2F684BDB
+ .long 0x2C9FB4D9
+ .long 0x29E4129F
+ .long 0x27350B89
+ .long 0x24924925
+ .long 0x21FB7813
+ .long 0x1F7047DD
+ .long 0x1CF06ADB
+ .long 0x1A7B9612
+ .long 0x18118119
+ .long 0x15B1E5F8
+ .long 0x135C8114
+ .long 0x11111112
+ .long 0xECF56BF
+ .long 0xC9714FC
+ .long 0xA6810A7
+ .long 0x8421085
+ .long 0x624DD30
+ .long 0x4104105
+ .long 0x2040811
+ /* maximum error: 0.987342 scaled: 0.921875*/
+
+#endif /* SH3 / SH4 */
+
+#endif /* L_div_table */
+#endif /* !__SHMEDIA__ */
diff --git a/gcc/config/sh/lib1funcs-Os-4-200.asm b/gcc/config/sh/lib1funcs-Os-4-200.asm
new file mode 100644
index 000000000..aae57ccd3
--- /dev/null
+++ b/gcc/config/sh/lib1funcs-Os-4-200.asm
@@ -0,0 +1,322 @@
+/* Copyright (C) 2006, 2009 Free Software Foundation, Inc.
+
+This file is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3, or (at your option) any
+later version.
+
+This file is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+/* Moderately Space-optimized libgcc routines for the Renesas SH /
+ STMicroelectronics ST40 CPUs.
+ Contributed by J"orn Rennecke joern.rennecke@st.com. */
+
+#include "lib1funcs.h"
+
+#if !__SHMEDIA__
+#ifdef L_udivsi3_i4i
+
+/* 88 bytes; sh4-200 cycle counts:
+ divisor >= 2G: 11 cycles
+ dividend < 2G: 48 cycles
+ dividend >= 2G: divisor != 1: 54 cycles
+ dividend >= 2G, divisor == 1: 22 cycles */
+#if defined (__SH_FPU_DOUBLE__) || defined (__SH4_SINGLE_ONLY__)
+!! args in r4 and r5, result in r0, clobber r1
+
+ .global GLOBAL(udivsi3_i4i)
+ FUNC(GLOBAL(udivsi3_i4i))
+GLOBAL(udivsi3_i4i):
+ mova L1,r0
+ cmp/pz r5
+ sts fpscr,r1
+ lds.l @r0+,fpscr
+ sts.l fpul,@-r15
+ bf LOCAL(huge_divisor)
+ mov.l r1,@-r15
+ lds r4,fpul
+ cmp/pz r4
+#ifdef FMOVD_WORKS
+ fmov.d dr0,@-r15
+ float fpul,dr0
+ fmov.d dr2,@-r15
+ bt LOCAL(dividend_adjusted)
+ mov #1,r1
+ fmov.d @r0,dr2
+ cmp/eq r1,r5
+ bt LOCAL(div_by_1)
+ fadd dr2,dr0
+LOCAL(dividend_adjusted):
+ lds r5,fpul
+ float fpul,dr2
+ fdiv dr2,dr0
+LOCAL(div_by_1):
+ fmov.d @r15+,dr2
+ ftrc dr0,fpul
+ fmov.d @r15+,dr0
+#else /* !FMOVD_WORKS */
+ fmov.s DR01,@-r15
+ mov #1,r1
+ fmov.s DR00,@-r15
+ float fpul,dr0
+ fmov.s DR21,@-r15
+ bt/s LOCAL(dividend_adjusted)
+ fmov.s DR20,@-r15
+ cmp/eq r1,r5
+ bt LOCAL(div_by_1)
+ fmov.s @r0+,DR20
+ fmov.s @r0,DR21
+ fadd dr2,dr0
+LOCAL(dividend_adjusted):
+ lds r5,fpul
+ float fpul,dr2
+ fdiv dr2,dr0
+LOCAL(div_by_1):
+ fmov.s @r15+,DR20
+ fmov.s @r15+,DR21
+ ftrc dr0,fpul
+ fmov.s @r15+,DR00
+ fmov.s @r15+,DR01
+#endif /* !FMOVD_WORKS */
+ lds.l @r15+,fpscr
+ sts fpul,r0
+ rts
+ lds.l @r15+,fpul
+
+#ifdef FMOVD_WORKS
+ .p2align 3 ! make double below 8 byte aligned.
+#endif
+LOCAL(huge_divisor):
+ lds r1,fpscr
+ add #4,r15
+ cmp/hs r5,r4
+ rts
+ movt r0
+
+ .p2align 2
+L1:
+#ifndef FMOVD_WORKS
+ .long 0x80000
+#else
+ .long 0x180000
+#endif
+ .double 4294967296
+
+ ENDFUNC(GLOBAL(udivsi3_i4i))
+#elif !defined (__sh1__) /* !__SH_FPU_DOUBLE__ */
+
+#if 0
+/* With 36 bytes, the following would probably be the most compact
+ implementation, but with 139 cycles on an sh4-200, it is extremely slow. */
+GLOBAL(udivsi3_i4i):
+ mov.l r2,@-r15
+ mov #0,r1
+ div0u
+ mov r1,r2
+ mov.l r3,@-r15
+ mov r1,r3
+ sett
+ mov r4,r0
+LOCAL(loop):
+ rotcr r2
+ ;
+ bt/s LOCAL(end)
+ cmp/gt r2,r3
+ rotcl r0
+ bra LOCAL(loop)
+ div1 r5,r1
+LOCAL(end):
+ rotcl r0
+ mov.l @r15+,r3
+ rts
+ mov.l @r15+,r2
+#endif /* 0 */
+
+/* Size: 186 bytes jointly for udivsi3_i4i and sdivsi3_i4i
+ sh4-200 run times:
+ udiv small divisor: 55 cycles
+ udiv large divisor: 52 cycles
+ sdiv small divisor, positive result: 59 cycles
+ sdiv large divisor, positive result: 56 cycles
+ sdiv small divisor, negative result: 65 cycles (*)
+ sdiv large divisor, negative result: 62 cycles (*)
+ (*): r2 is restored in the rts delay slot and has a lingering latency
+ of two more cycles. */
+ .balign 4
+ .global GLOBAL(udivsi3_i4i)
+ FUNC(GLOBAL(udivsi3_i4i))
+ FUNC(GLOBAL(sdivsi3_i4i))
+GLOBAL(udivsi3_i4i):
+ sts pr,r1
+ mov.l r4,@-r15
+ extu.w r5,r0
+ cmp/eq r5,r0
+ swap.w r4,r0
+ shlr16 r4
+ bf/s LOCAL(large_divisor)
+ div0u
+ mov.l r5,@-r15
+ shll16 r5
+LOCAL(sdiv_small_divisor):
+ div1 r5,r4
+ bsr LOCAL(div6)
+ div1 r5,r4
+ div1 r5,r4
+ bsr LOCAL(div6)
+ div1 r5,r4
+ xtrct r4,r0
+ xtrct r0,r4
+ bsr LOCAL(div7)
+ swap.w r4,r4
+ div1 r5,r4
+ bsr LOCAL(div7)
+ div1 r5,r4
+ xtrct r4,r0
+ mov.l @r15+,r5
+ swap.w r0,r0
+ mov.l @r15+,r4
+ jmp @r1
+ rotcl r0
+LOCAL(div7):
+ div1 r5,r4
+LOCAL(div6):
+ div1 r5,r4; div1 r5,r4; div1 r5,r4
+ div1 r5,r4; div1 r5,r4; rts; div1 r5,r4
+
+LOCAL(divx3):
+ rotcl r0
+ div1 r5,r4
+ rotcl r0
+ div1 r5,r4
+ rotcl r0
+ rts
+ div1 r5,r4
+
+LOCAL(large_divisor):
+ mov.l r5,@-r15
+LOCAL(sdiv_large_divisor):
+ xor r4,r0
+ .rept 4
+ rotcl r0
+ bsr LOCAL(divx3)
+ div1 r5,r4
+ .endr
+ mov.l @r15+,r5
+ mov.l @r15+,r4
+ jmp @r1
+ rotcl r0
+ ENDFUNC(GLOBAL(udivsi3_i4i))
+
+ .global GLOBAL(sdivsi3_i4i)
+GLOBAL(sdivsi3_i4i):
+ mov.l r4,@-r15
+ cmp/pz r5
+ mov.l r5,@-r15
+ bt/s LOCAL(pos_divisor)
+ cmp/pz r4
+ neg r5,r5
+ extu.w r5,r0
+ bt/s LOCAL(neg_result)
+ cmp/eq r5,r0
+ neg r4,r4
+LOCAL(pos_result):
+ swap.w r4,r0
+ bra LOCAL(sdiv_check_divisor)
+ sts pr,r1
+LOCAL(pos_divisor):
+ extu.w r5,r0
+ bt/s LOCAL(pos_result)
+ cmp/eq r5,r0
+ neg r4,r4
+LOCAL(neg_result):
+ mova LOCAL(negate_result),r0
+ ;
+ mov r0,r1
+ swap.w r4,r0
+ lds r2,macl
+ sts pr,r2
+LOCAL(sdiv_check_divisor):
+ shlr16 r4
+ bf/s LOCAL(sdiv_large_divisor)
+ div0u
+ bra LOCAL(sdiv_small_divisor)
+ shll16 r5
+ .balign 4
+LOCAL(negate_result):
+ neg r0,r0
+ jmp @r2
+ sts macl,r2
+ ENDFUNC(GLOBAL(sdivsi3_i4i))
+#endif /* !__SH_FPU_DOUBLE__ */
+#endif /* L_udivsi3_i4i */
+
+#ifdef L_sdivsi3_i4i
+#if defined (__SH_FPU_DOUBLE__) || defined (__SH4_SINGLE_ONLY__)
+/* 48 bytes, 45 cycles on sh4-200 */
+!! args in r4 and r5, result in r0, clobber r1
+
+ .global GLOBAL(sdivsi3_i4i)
+ FUNC(GLOBAL(sdivsi3_i4i))
+GLOBAL(sdivsi3_i4i):
+ sts.l fpscr,@-r15
+ sts fpul,r1
+ mova L1,r0
+ lds.l @r0+,fpscr
+ lds r4,fpul
+#ifdef FMOVD_WORKS
+ fmov.d dr0,@-r15
+ float fpul,dr0
+ lds r5,fpul
+ fmov.d dr2,@-r15
+#else
+ fmov.s DR01,@-r15
+ fmov.s DR00,@-r15
+ float fpul,dr0
+ lds r5,fpul
+ fmov.s DR21,@-r15
+ fmov.s DR20,@-r15
+#endif
+ float fpul,dr2
+ fdiv dr2,dr0
+#ifdef FMOVD_WORKS
+ fmov.d @r15+,dr2
+#else
+ fmov.s @r15+,DR20
+ fmov.s @r15+,DR21
+#endif
+ ftrc dr0,fpul
+#ifdef FMOVD_WORKS
+ fmov.d @r15+,dr0
+#else
+ fmov.s @r15+,DR00
+ fmov.s @r15+,DR01
+#endif
+ lds.l @r15+,fpscr
+ sts fpul,r0
+ rts
+ lds r1,fpul
+
+ .p2align 2
+L1:
+#ifndef FMOVD_WORKS
+ .long 0x80000
+#else
+ .long 0x180000
+#endif
+
+ ENDFUNC(GLOBAL(sdivsi3_i4i))
+#endif /* __SH_FPU_DOUBLE__ */
+#endif /* L_sdivsi3_i4i */
+#endif /* !__SHMEDIA__ */
diff --git a/gcc/config/sh/lib1funcs.asm b/gcc/config/sh/lib1funcs.asm
new file mode 100644
index 000000000..2f0ca16cd
--- /dev/null
+++ b/gcc/config/sh/lib1funcs.asm
@@ -0,0 +1,3933 @@
+/* Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+ 2004, 2005, 2006, 2009
+ Free Software Foundation, Inc.
+
+This file is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3, or (at your option) any
+later version.
+
+This file is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+
+!! libgcc routines for the Renesas / SuperH SH CPUs.
+!! Contributed by Steve Chamberlain.
+!! sac@cygnus.com
+
+!! ashiftrt_r4_x, ___ashrsi3, ___ashlsi3, ___lshrsi3 routines
+!! recoded in assembly by Toshiyasu Morita
+!! tm@netcom.com
+
+#if defined(__ELF__) && defined(__linux__)
+.section .note.GNU-stack,"",%progbits
+.previous
+#endif
+
+/* SH2 optimizations for ___ashrsi3, ___ashlsi3, ___lshrsi3 and
+ ELF local label prefixes by J"orn Rennecke
+ amylaar@cygnus.com */
+
+#include "lib1funcs.h"
+
+/* t-vxworks needs to build both PIC and non-PIC versions of libgcc,
+ so it is more convenient to define NO_FPSCR_VALUES here than to
+ define it on the command line. */
+#if defined __vxworks && defined __PIC__
+#define NO_FPSCR_VALUES
+#endif
+
+#if ! __SH5__
+#ifdef L_ashiftrt
+ .global GLOBAL(ashiftrt_r4_0)
+ .global GLOBAL(ashiftrt_r4_1)
+ .global GLOBAL(ashiftrt_r4_2)
+ .global GLOBAL(ashiftrt_r4_3)
+ .global GLOBAL(ashiftrt_r4_4)
+ .global GLOBAL(ashiftrt_r4_5)
+ .global GLOBAL(ashiftrt_r4_6)
+ .global GLOBAL(ashiftrt_r4_7)
+ .global GLOBAL(ashiftrt_r4_8)
+ .global GLOBAL(ashiftrt_r4_9)
+ .global GLOBAL(ashiftrt_r4_10)
+ .global GLOBAL(ashiftrt_r4_11)
+ .global GLOBAL(ashiftrt_r4_12)
+ .global GLOBAL(ashiftrt_r4_13)
+ .global GLOBAL(ashiftrt_r4_14)
+ .global GLOBAL(ashiftrt_r4_15)
+ .global GLOBAL(ashiftrt_r4_16)
+ .global GLOBAL(ashiftrt_r4_17)
+ .global GLOBAL(ashiftrt_r4_18)
+ .global GLOBAL(ashiftrt_r4_19)
+ .global GLOBAL(ashiftrt_r4_20)
+ .global GLOBAL(ashiftrt_r4_21)
+ .global GLOBAL(ashiftrt_r4_22)
+ .global GLOBAL(ashiftrt_r4_23)
+ .global GLOBAL(ashiftrt_r4_24)
+ .global GLOBAL(ashiftrt_r4_25)
+ .global GLOBAL(ashiftrt_r4_26)
+ .global GLOBAL(ashiftrt_r4_27)
+ .global GLOBAL(ashiftrt_r4_28)
+ .global GLOBAL(ashiftrt_r4_29)
+ .global GLOBAL(ashiftrt_r4_30)
+ .global GLOBAL(ashiftrt_r4_31)
+ .global GLOBAL(ashiftrt_r4_32)
+
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_0))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_1))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_2))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_3))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_4))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_5))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_6))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_7))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_8))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_9))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_10))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_11))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_12))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_13))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_14))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_15))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_16))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_17))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_18))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_19))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_20))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_21))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_22))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_23))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_24))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_25))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_26))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_27))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_28))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_29))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_30))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_31))
+ HIDDEN_FUNC(GLOBAL(ashiftrt_r4_32))
+
+ .align 1
+GLOBAL(ashiftrt_r4_32):
+GLOBAL(ashiftrt_r4_31):
+ rotcl r4
+ rts
+ subc r4,r4
+
+GLOBAL(ashiftrt_r4_30):
+ shar r4
+GLOBAL(ashiftrt_r4_29):
+ shar r4
+GLOBAL(ashiftrt_r4_28):
+ shar r4
+GLOBAL(ashiftrt_r4_27):
+ shar r4
+GLOBAL(ashiftrt_r4_26):
+ shar r4
+GLOBAL(ashiftrt_r4_25):
+ shar r4
+GLOBAL(ashiftrt_r4_24):
+ shlr16 r4
+ shlr8 r4
+ rts
+ exts.b r4,r4
+
+GLOBAL(ashiftrt_r4_23):
+ shar r4
+GLOBAL(ashiftrt_r4_22):
+ shar r4
+GLOBAL(ashiftrt_r4_21):
+ shar r4
+GLOBAL(ashiftrt_r4_20):
+ shar r4
+GLOBAL(ashiftrt_r4_19):
+ shar r4
+GLOBAL(ashiftrt_r4_18):
+ shar r4
+GLOBAL(ashiftrt_r4_17):
+ shar r4
+GLOBAL(ashiftrt_r4_16):
+ shlr16 r4
+ rts
+ exts.w r4,r4
+
+GLOBAL(ashiftrt_r4_15):
+ shar r4
+GLOBAL(ashiftrt_r4_14):
+ shar r4
+GLOBAL(ashiftrt_r4_13):
+ shar r4
+GLOBAL(ashiftrt_r4_12):
+ shar r4
+GLOBAL(ashiftrt_r4_11):
+ shar r4
+GLOBAL(ashiftrt_r4_10):
+ shar r4
+GLOBAL(ashiftrt_r4_9):
+ shar r4
+GLOBAL(ashiftrt_r4_8):
+ shar r4
+GLOBAL(ashiftrt_r4_7):
+ shar r4
+GLOBAL(ashiftrt_r4_6):
+ shar r4
+GLOBAL(ashiftrt_r4_5):
+ shar r4
+GLOBAL(ashiftrt_r4_4):
+ shar r4
+GLOBAL(ashiftrt_r4_3):
+ shar r4
+GLOBAL(ashiftrt_r4_2):
+ shar r4
+GLOBAL(ashiftrt_r4_1):
+ rts
+ shar r4
+
+GLOBAL(ashiftrt_r4_0):
+ rts
+ nop
+
+ ENDFUNC(GLOBAL(ashiftrt_r4_0))
+ ENDFUNC(GLOBAL(ashiftrt_r4_1))
+ ENDFUNC(GLOBAL(ashiftrt_r4_2))
+ ENDFUNC(GLOBAL(ashiftrt_r4_3))
+ ENDFUNC(GLOBAL(ashiftrt_r4_4))
+ ENDFUNC(GLOBAL(ashiftrt_r4_5))
+ ENDFUNC(GLOBAL(ashiftrt_r4_6))
+ ENDFUNC(GLOBAL(ashiftrt_r4_7))
+ ENDFUNC(GLOBAL(ashiftrt_r4_8))
+ ENDFUNC(GLOBAL(ashiftrt_r4_9))
+ ENDFUNC(GLOBAL(ashiftrt_r4_10))
+ ENDFUNC(GLOBAL(ashiftrt_r4_11))
+ ENDFUNC(GLOBAL(ashiftrt_r4_12))
+ ENDFUNC(GLOBAL(ashiftrt_r4_13))
+ ENDFUNC(GLOBAL(ashiftrt_r4_14))
+ ENDFUNC(GLOBAL(ashiftrt_r4_15))
+ ENDFUNC(GLOBAL(ashiftrt_r4_16))
+ ENDFUNC(GLOBAL(ashiftrt_r4_17))
+ ENDFUNC(GLOBAL(ashiftrt_r4_18))
+ ENDFUNC(GLOBAL(ashiftrt_r4_19))
+ ENDFUNC(GLOBAL(ashiftrt_r4_20))
+ ENDFUNC(GLOBAL(ashiftrt_r4_21))
+ ENDFUNC(GLOBAL(ashiftrt_r4_22))
+ ENDFUNC(GLOBAL(ashiftrt_r4_23))
+ ENDFUNC(GLOBAL(ashiftrt_r4_24))
+ ENDFUNC(GLOBAL(ashiftrt_r4_25))
+ ENDFUNC(GLOBAL(ashiftrt_r4_26))
+ ENDFUNC(GLOBAL(ashiftrt_r4_27))
+ ENDFUNC(GLOBAL(ashiftrt_r4_28))
+ ENDFUNC(GLOBAL(ashiftrt_r4_29))
+ ENDFUNC(GLOBAL(ashiftrt_r4_30))
+ ENDFUNC(GLOBAL(ashiftrt_r4_31))
+ ENDFUNC(GLOBAL(ashiftrt_r4_32))
+#endif
+
+#ifdef L_ashiftrt_n
+
+!
+! GLOBAL(ashrsi3)
+!
+! Entry:
+!
+! r4: Value to shift
+! r5: Shifts
+!
+! Exit:
+!
+! r0: Result
+!
+! Destroys:
+!
+! (none)
+!
+
+ .global GLOBAL(ashrsi3)
+ HIDDEN_FUNC(GLOBAL(ashrsi3))
+ .align 2
+GLOBAL(ashrsi3):
+ mov #31,r0
+ and r0,r5
+ mova LOCAL(ashrsi3_table),r0
+ mov.b @(r0,r5),r5
+#ifdef __sh1__
+ add r5,r0
+ jmp @r0
+#else
+ braf r5
+#endif
+ mov r4,r0
+
+ .align 2
+LOCAL(ashrsi3_table):
+ .byte LOCAL(ashrsi3_0)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_1)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_2)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_3)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_4)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_5)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_6)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_7)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_8)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_9)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_10)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_11)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_12)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_13)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_14)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_15)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_16)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_17)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_18)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_19)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_20)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_21)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_22)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_23)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_24)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_25)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_26)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_27)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_28)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_29)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_30)-LOCAL(ashrsi3_table)
+ .byte LOCAL(ashrsi3_31)-LOCAL(ashrsi3_table)
+
+LOCAL(ashrsi3_31):
+ rotcl r0
+ rts
+ subc r0,r0
+
+LOCAL(ashrsi3_30):
+ shar r0
+LOCAL(ashrsi3_29):
+ shar r0
+LOCAL(ashrsi3_28):
+ shar r0
+LOCAL(ashrsi3_27):
+ shar r0
+LOCAL(ashrsi3_26):
+ shar r0
+LOCAL(ashrsi3_25):
+ shar r0
+LOCAL(ashrsi3_24):
+ shlr16 r0
+ shlr8 r0
+ rts
+ exts.b r0,r0
+
+LOCAL(ashrsi3_23):
+ shar r0
+LOCAL(ashrsi3_22):
+ shar r0
+LOCAL(ashrsi3_21):
+ shar r0
+LOCAL(ashrsi3_20):
+ shar r0
+LOCAL(ashrsi3_19):
+ shar r0
+LOCAL(ashrsi3_18):
+ shar r0
+LOCAL(ashrsi3_17):
+ shar r0
+LOCAL(ashrsi3_16):
+ shlr16 r0
+ rts
+ exts.w r0,r0
+
+LOCAL(ashrsi3_15):
+ shar r0
+LOCAL(ashrsi3_14):
+ shar r0
+LOCAL(ashrsi3_13):
+ shar r0
+LOCAL(ashrsi3_12):
+ shar r0
+LOCAL(ashrsi3_11):
+ shar r0
+LOCAL(ashrsi3_10):
+ shar r0
+LOCAL(ashrsi3_9):
+ shar r0
+LOCAL(ashrsi3_8):
+ shar r0
+LOCAL(ashrsi3_7):
+ shar r0
+LOCAL(ashrsi3_6):
+ shar r0
+LOCAL(ashrsi3_5):
+ shar r0
+LOCAL(ashrsi3_4):
+ shar r0
+LOCAL(ashrsi3_3):
+ shar r0
+LOCAL(ashrsi3_2):
+ shar r0
+LOCAL(ashrsi3_1):
+ rts
+ shar r0
+
+LOCAL(ashrsi3_0):
+ rts
+ nop
+
+ ENDFUNC(GLOBAL(ashrsi3))
+#endif
+
+#ifdef L_ashiftlt
+
+!
+! GLOBAL(ashlsi3)
+!
+! Entry:
+!
+! r4: Value to shift
+! r5: Shifts
+!
+! Exit:
+!
+! r0: Result
+!
+! Destroys:
+!
+! (none)
+!
+ .global GLOBAL(ashlsi3)
+ HIDDEN_FUNC(GLOBAL(ashlsi3))
+ .align 2
+GLOBAL(ashlsi3):
+ mov #31,r0
+ and r0,r5
+ mova LOCAL(ashlsi3_table),r0
+ mov.b @(r0,r5),r5
+#ifdef __sh1__
+ add r5,r0
+ jmp @r0
+#else
+ braf r5
+#endif
+ mov r4,r0
+
+ .align 2
+LOCAL(ashlsi3_table):
+ .byte LOCAL(ashlsi3_0)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_1)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_2)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_3)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_4)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_5)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_6)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_7)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_8)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_9)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_10)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_11)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_12)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_13)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_14)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_15)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_16)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_17)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_18)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_19)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_20)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_21)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_22)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_23)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_24)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_25)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_26)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_27)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_28)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_29)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_30)-LOCAL(ashlsi3_table)
+ .byte LOCAL(ashlsi3_31)-LOCAL(ashlsi3_table)
+
+LOCAL(ashlsi3_6):
+ shll2 r0
+LOCAL(ashlsi3_4):
+ shll2 r0
+LOCAL(ashlsi3_2):
+ rts
+ shll2 r0
+
+LOCAL(ashlsi3_7):
+ shll2 r0
+LOCAL(ashlsi3_5):
+ shll2 r0
+LOCAL(ashlsi3_3):
+ shll2 r0
+LOCAL(ashlsi3_1):
+ rts
+ shll r0
+
+LOCAL(ashlsi3_14):
+ shll2 r0
+LOCAL(ashlsi3_12):
+ shll2 r0
+LOCAL(ashlsi3_10):
+ shll2 r0
+LOCAL(ashlsi3_8):
+ rts
+ shll8 r0
+
+LOCAL(ashlsi3_15):
+ shll2 r0
+LOCAL(ashlsi3_13):
+ shll2 r0
+LOCAL(ashlsi3_11):
+ shll2 r0
+LOCAL(ashlsi3_9):
+ shll8 r0
+ rts
+ shll r0
+
+LOCAL(ashlsi3_22):
+ shll2 r0
+LOCAL(ashlsi3_20):
+ shll2 r0
+LOCAL(ashlsi3_18):
+ shll2 r0
+LOCAL(ashlsi3_16):
+ rts
+ shll16 r0
+
+LOCAL(ashlsi3_23):
+ shll2 r0
+LOCAL(ashlsi3_21):
+ shll2 r0
+LOCAL(ashlsi3_19):
+ shll2 r0
+LOCAL(ashlsi3_17):
+ shll16 r0
+ rts
+ shll r0
+
+LOCAL(ashlsi3_30):
+ shll2 r0
+LOCAL(ashlsi3_28):
+ shll2 r0
+LOCAL(ashlsi3_26):
+ shll2 r0
+LOCAL(ashlsi3_24):
+ shll16 r0
+ rts
+ shll8 r0
+
+LOCAL(ashlsi3_31):
+ shll2 r0
+LOCAL(ashlsi3_29):
+ shll2 r0
+LOCAL(ashlsi3_27):
+ shll2 r0
+LOCAL(ashlsi3_25):
+ shll16 r0
+ shll8 r0
+ rts
+ shll r0
+
+LOCAL(ashlsi3_0):
+ rts
+ nop
+
+ ENDFUNC(GLOBAL(ashlsi3))
+#endif
+
+#ifdef L_lshiftrt
+
+!
+! GLOBAL(lshrsi3)
+!
+! Entry:
+!
+! r4: Value to shift
+! r5: Shifts
+!
+! Exit:
+!
+! r0: Result
+!
+! Destroys:
+!
+! (none)
+!
+ .global GLOBAL(lshrsi3)
+ HIDDEN_FUNC(GLOBAL(lshrsi3))
+ .align 2
+GLOBAL(lshrsi3):
+ mov #31,r0
+ and r0,r5
+ mova LOCAL(lshrsi3_table),r0
+ mov.b @(r0,r5),r5
+#ifdef __sh1__
+ add r5,r0
+ jmp @r0
+#else
+ braf r5
+#endif
+ mov r4,r0
+
+ .align 2
+LOCAL(lshrsi3_table):
+ .byte LOCAL(lshrsi3_0)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_1)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_2)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_3)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_4)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_5)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_6)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_7)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_8)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_9)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_10)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_11)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_12)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_13)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_14)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_15)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_16)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_17)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_18)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_19)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_20)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_21)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_22)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_23)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_24)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_25)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_26)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_27)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_28)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_29)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_30)-LOCAL(lshrsi3_table)
+ .byte LOCAL(lshrsi3_31)-LOCAL(lshrsi3_table)
+
+LOCAL(lshrsi3_6):
+ shlr2 r0
+LOCAL(lshrsi3_4):
+ shlr2 r0
+LOCAL(lshrsi3_2):
+ rts
+ shlr2 r0
+
+LOCAL(lshrsi3_7):
+ shlr2 r0
+LOCAL(lshrsi3_5):
+ shlr2 r0
+LOCAL(lshrsi3_3):
+ shlr2 r0
+LOCAL(lshrsi3_1):
+ rts
+ shlr r0
+
+LOCAL(lshrsi3_14):
+ shlr2 r0
+LOCAL(lshrsi3_12):
+ shlr2 r0
+LOCAL(lshrsi3_10):
+ shlr2 r0
+LOCAL(lshrsi3_8):
+ rts
+ shlr8 r0
+
+LOCAL(lshrsi3_15):
+ shlr2 r0
+LOCAL(lshrsi3_13):
+ shlr2 r0
+LOCAL(lshrsi3_11):
+ shlr2 r0
+LOCAL(lshrsi3_9):
+ shlr8 r0
+ rts
+ shlr r0
+
+LOCAL(lshrsi3_22):
+ shlr2 r0
+LOCAL(lshrsi3_20):
+ shlr2 r0
+LOCAL(lshrsi3_18):
+ shlr2 r0
+LOCAL(lshrsi3_16):
+ rts
+ shlr16 r0
+
+LOCAL(lshrsi3_23):
+ shlr2 r0
+LOCAL(lshrsi3_21):
+ shlr2 r0
+LOCAL(lshrsi3_19):
+ shlr2 r0
+LOCAL(lshrsi3_17):
+ shlr16 r0
+ rts
+ shlr r0
+
+LOCAL(lshrsi3_30):
+ shlr2 r0
+LOCAL(lshrsi3_28):
+ shlr2 r0
+LOCAL(lshrsi3_26):
+ shlr2 r0
+LOCAL(lshrsi3_24):
+ shlr16 r0
+ rts
+ shlr8 r0
+
+LOCAL(lshrsi3_31):
+ shlr2 r0
+LOCAL(lshrsi3_29):
+ shlr2 r0
+LOCAL(lshrsi3_27):
+ shlr2 r0
+LOCAL(lshrsi3_25):
+ shlr16 r0
+ shlr8 r0
+ rts
+ shlr r0
+
+LOCAL(lshrsi3_0):
+ rts
+ nop
+
+ ENDFUNC(GLOBAL(lshrsi3))
+#endif
+
+#ifdef L_movmem
+ .text
+ .balign 4
+ .global GLOBAL(movmem)
+ HIDDEN_FUNC(GLOBAL(movmem))
+ HIDDEN_ALIAS(movstr,movmem)
+ /* This would be a lot simpler if r6 contained the byte count
+ minus 64, and we wouldn't be called here for a byte count of 64. */
+GLOBAL(movmem):
+ sts.l pr,@-r15
+ shll2 r6
+ bsr GLOBAL(movmemSI52+2)
+ mov.l @(48,r5),r0
+ .balign 4
+LOCAL(movmem_loop): /* Reached with rts */
+ mov.l @(60,r5),r0
+ add #-64,r6
+ mov.l r0,@(60,r4)
+ tst r6,r6
+ mov.l @(56,r5),r0
+ bt LOCAL(movmem_done)
+ mov.l r0,@(56,r4)
+ cmp/pl r6
+ mov.l @(52,r5),r0
+ add #64,r5
+ mov.l r0,@(52,r4)
+ add #64,r4
+ bt GLOBAL(movmemSI52)
+! done all the large groups, do the remainder
+! jump to movmem+
+ mova GLOBAL(movmemSI4)+4,r0
+ add r6,r0
+ jmp @r0
+LOCAL(movmem_done): ! share slot insn, works out aligned.
+ lds.l @r15+,pr
+ mov.l r0,@(56,r4)
+ mov.l @(52,r5),r0
+ rts
+ mov.l r0,@(52,r4)
+ .balign 4
+! ??? We need aliases movstr* for movmem* for the older libraries. These
+! aliases will be removed at the some point in the future.
+ .global GLOBAL(movmemSI64)
+ HIDDEN_FUNC(GLOBAL(movmemSI64))
+ HIDDEN_ALIAS(movstrSI64,movmemSI64)
+GLOBAL(movmemSI64):
+ mov.l @(60,r5),r0
+ mov.l r0,@(60,r4)
+ .global GLOBAL(movmemSI60)
+ HIDDEN_FUNC(GLOBAL(movmemSI60))
+ HIDDEN_ALIAS(movstrSI60,movmemSI60)
+GLOBAL(movmemSI60):
+ mov.l @(56,r5),r0
+ mov.l r0,@(56,r4)
+ .global GLOBAL(movmemSI56)
+ HIDDEN_FUNC(GLOBAL(movmemSI56))
+ HIDDEN_ALIAS(movstrSI56,movmemSI56)
+GLOBAL(movmemSI56):
+ mov.l @(52,r5),r0
+ mov.l r0,@(52,r4)
+ .global GLOBAL(movmemSI52)
+ HIDDEN_FUNC(GLOBAL(movmemSI52))
+ HIDDEN_ALIAS(movstrSI52,movmemSI52)
+GLOBAL(movmemSI52):
+ mov.l @(48,r5),r0
+ mov.l r0,@(48,r4)
+ .global GLOBAL(movmemSI48)
+ HIDDEN_FUNC(GLOBAL(movmemSI48))
+ HIDDEN_ALIAS(movstrSI48,movmemSI48)
+GLOBAL(movmemSI48):
+ mov.l @(44,r5),r0
+ mov.l r0,@(44,r4)
+ .global GLOBAL(movmemSI44)
+ HIDDEN_FUNC(GLOBAL(movmemSI44))
+ HIDDEN_ALIAS(movstrSI44,movmemSI44)
+GLOBAL(movmemSI44):
+ mov.l @(40,r5),r0
+ mov.l r0,@(40,r4)
+ .global GLOBAL(movmemSI40)
+ HIDDEN_FUNC(GLOBAL(movmemSI40))
+ HIDDEN_ALIAS(movstrSI40,movmemSI40)
+GLOBAL(movmemSI40):
+ mov.l @(36,r5),r0
+ mov.l r0,@(36,r4)
+ .global GLOBAL(movmemSI36)
+ HIDDEN_FUNC(GLOBAL(movmemSI36))
+ HIDDEN_ALIAS(movstrSI36,movmemSI36)
+GLOBAL(movmemSI36):
+ mov.l @(32,r5),r0
+ mov.l r0,@(32,r4)
+ .global GLOBAL(movmemSI32)
+ HIDDEN_FUNC(GLOBAL(movmemSI32))
+ HIDDEN_ALIAS(movstrSI32,movmemSI32)
+GLOBAL(movmemSI32):
+ mov.l @(28,r5),r0
+ mov.l r0,@(28,r4)
+ .global GLOBAL(movmemSI28)
+ HIDDEN_FUNC(GLOBAL(movmemSI28))
+ HIDDEN_ALIAS(movstrSI28,movmemSI28)
+GLOBAL(movmemSI28):
+ mov.l @(24,r5),r0
+ mov.l r0,@(24,r4)
+ .global GLOBAL(movmemSI24)
+ HIDDEN_FUNC(GLOBAL(movmemSI24))
+ HIDDEN_ALIAS(movstrSI24,movmemSI24)
+GLOBAL(movmemSI24):
+ mov.l @(20,r5),r0
+ mov.l r0,@(20,r4)
+ .global GLOBAL(movmemSI20)
+ HIDDEN_FUNC(GLOBAL(movmemSI20))
+ HIDDEN_ALIAS(movstrSI20,movmemSI20)
+GLOBAL(movmemSI20):
+ mov.l @(16,r5),r0
+ mov.l r0,@(16,r4)
+ .global GLOBAL(movmemSI16)
+ HIDDEN_FUNC(GLOBAL(movmemSI16))
+ HIDDEN_ALIAS(movstrSI16,movmemSI16)
+GLOBAL(movmemSI16):
+ mov.l @(12,r5),r0
+ mov.l r0,@(12,r4)
+ .global GLOBAL(movmemSI12)
+ HIDDEN_FUNC(GLOBAL(movmemSI12))
+ HIDDEN_ALIAS(movstrSI12,movmemSI12)
+GLOBAL(movmemSI12):
+ mov.l @(8,r5),r0
+ mov.l r0,@(8,r4)
+ .global GLOBAL(movmemSI8)
+ HIDDEN_FUNC(GLOBAL(movmemSI8))
+ HIDDEN_ALIAS(movstrSI8,movmemSI8)
+GLOBAL(movmemSI8):
+ mov.l @(4,r5),r0
+ mov.l r0,@(4,r4)
+ .global GLOBAL(movmemSI4)
+ HIDDEN_FUNC(GLOBAL(movmemSI4))
+ HIDDEN_ALIAS(movstrSI4,movmemSI4)
+GLOBAL(movmemSI4):
+ mov.l @(0,r5),r0
+ rts
+ mov.l r0,@(0,r4)
+
+ ENDFUNC(GLOBAL(movmemSI64))
+ ENDFUNC(GLOBAL(movmemSI60))
+ ENDFUNC(GLOBAL(movmemSI56))
+ ENDFUNC(GLOBAL(movmemSI52))
+ ENDFUNC(GLOBAL(movmemSI48))
+ ENDFUNC(GLOBAL(movmemSI44))
+ ENDFUNC(GLOBAL(movmemSI40))
+ ENDFUNC(GLOBAL(movmemSI36))
+ ENDFUNC(GLOBAL(movmemSI32))
+ ENDFUNC(GLOBAL(movmemSI28))
+ ENDFUNC(GLOBAL(movmemSI24))
+ ENDFUNC(GLOBAL(movmemSI20))
+ ENDFUNC(GLOBAL(movmemSI16))
+ ENDFUNC(GLOBAL(movmemSI12))
+ ENDFUNC(GLOBAL(movmemSI8))
+ ENDFUNC(GLOBAL(movmemSI4))
+ ENDFUNC(GLOBAL(movmem))
+#endif
+
+#ifdef L_movmem_i4
+ .text
+ .global GLOBAL(movmem_i4_even)
+ .global GLOBAL(movmem_i4_odd)
+ .global GLOBAL(movmemSI12_i4)
+
+ HIDDEN_FUNC(GLOBAL(movmem_i4_even))
+ HIDDEN_FUNC(GLOBAL(movmem_i4_odd))
+ HIDDEN_FUNC(GLOBAL(movmemSI12_i4))
+
+ HIDDEN_ALIAS(movstr_i4_even,movmem_i4_even)
+ HIDDEN_ALIAS(movstr_i4_odd,movmem_i4_odd)
+ HIDDEN_ALIAS(movstrSI12_i4,movmemSI12_i4)
+
+ .p2align 5
+L_movmem_2mod4_end:
+ mov.l r0,@(16,r4)
+ rts
+ mov.l r1,@(20,r4)
+
+ .p2align 2
+
+GLOBAL(movmem_i4_even):
+ mov.l @r5+,r0
+ bra L_movmem_start_even
+ mov.l @r5+,r1
+
+GLOBAL(movmem_i4_odd):
+ mov.l @r5+,r1
+ add #-4,r4
+ mov.l @r5+,r2
+ mov.l @r5+,r3
+ mov.l r1,@(4,r4)
+ mov.l r2,@(8,r4)
+
+L_movmem_loop:
+ mov.l r3,@(12,r4)
+ dt r6
+ mov.l @r5+,r0
+ bt/s L_movmem_2mod4_end
+ mov.l @r5+,r1
+ add #16,r4
+L_movmem_start_even:
+ mov.l @r5+,r2
+ mov.l @r5+,r3
+ mov.l r0,@r4
+ dt r6
+ mov.l r1,@(4,r4)
+ bf/s L_movmem_loop
+ mov.l r2,@(8,r4)
+ rts
+ mov.l r3,@(12,r4)
+
+ ENDFUNC(GLOBAL(movmem_i4_even))
+ ENDFUNC(GLOBAL(movmem_i4_odd))
+
+ .p2align 4
+GLOBAL(movmemSI12_i4):
+ mov.l @r5,r0
+ mov.l @(4,r5),r1
+ mov.l @(8,r5),r2
+ mov.l r0,@r4
+ mov.l r1,@(4,r4)
+ rts
+ mov.l r2,@(8,r4)
+
+ ENDFUNC(GLOBAL(movmemSI12_i4))
+#endif
+
+#ifdef L_mulsi3
+
+
+ .global GLOBAL(mulsi3)
+ HIDDEN_FUNC(GLOBAL(mulsi3))
+
+! r4 = aabb
+! r5 = ccdd
+! r0 = aabb*ccdd via partial products
+!
+! if aa == 0 and cc = 0
+! r0 = bb*dd
+!
+! else
+! aa = bb*dd + (aa*dd*65536) + (cc*bb*65536)
+!
+
+GLOBAL(mulsi3):
+ mulu.w r4,r5 ! multiply the lsws macl=bb*dd
+ mov r5,r3 ! r3 = ccdd
+ swap.w r4,r2 ! r2 = bbaa
+ xtrct r2,r3 ! r3 = aacc
+ tst r3,r3 ! msws zero ?
+ bf hiset
+ rts ! yes - then we have the answer
+ sts macl,r0
+
+hiset: sts macl,r0 ! r0 = bb*dd
+ mulu.w r2,r5 ! brewing macl = aa*dd
+ sts macl,r1
+ mulu.w r3,r4 ! brewing macl = cc*bb
+ sts macl,r2
+ add r1,r2
+ shll16 r2
+ rts
+ add r2,r0
+
+ ENDFUNC(GLOBAL(mulsi3))
+#endif
+#endif /* ! __SH5__ */
+#ifdef L_sdivsi3_i4
+ .title "SH DIVIDE"
+!! 4 byte integer Divide code for the Renesas SH
+#ifdef __SH4__
+!! args in r4 and r5, result in fpul, clobber dr0, dr2
+
+ .global GLOBAL(sdivsi3_i4)
+ HIDDEN_FUNC(GLOBAL(sdivsi3_i4))
+GLOBAL(sdivsi3_i4):
+ lds r4,fpul
+ float fpul,dr0
+ lds r5,fpul
+ float fpul,dr2
+ fdiv dr2,dr0
+ rts
+ ftrc dr0,fpul
+
+ ENDFUNC(GLOBAL(sdivsi3_i4))
+#elif defined(__SH4_SINGLE__) || defined(__SH4_SINGLE_ONLY__) || (defined (__SH5__) && ! defined __SH4_NOFPU__)
+!! args in r4 and r5, result in fpul, clobber r2, dr0, dr2
+
+#if ! __SH5__ || __SH5__ == 32
+#if __SH5__
+ .mode SHcompact
+#endif
+ .global GLOBAL(sdivsi3_i4)
+ HIDDEN_FUNC(GLOBAL(sdivsi3_i4))
+GLOBAL(sdivsi3_i4):
+ sts.l fpscr,@-r15
+ mov #8,r2
+ swap.w r2,r2
+ lds r2,fpscr
+ lds r4,fpul
+ float fpul,dr0
+ lds r5,fpul
+ float fpul,dr2
+ fdiv dr2,dr0
+ ftrc dr0,fpul
+ rts
+ lds.l @r15+,fpscr
+
+ ENDFUNC(GLOBAL(sdivsi3_i4))
+#endif /* ! __SH5__ || __SH5__ == 32 */
+#endif /* ! __SH4__ */
+#endif
+
+#ifdef L_sdivsi3
+/* __SH4_SINGLE_ONLY__ keeps this part for link compatibility with
+ sh2e/sh3e code. */
+#if (! defined(__SH4__) && ! defined (__SH4_SINGLE__)) || defined (__linux__)
+!!
+!! Steve Chamberlain
+!! sac@cygnus.com
+!!
+!!
+
+!! args in r4 and r5, result in r0 clobber r1, r2, r3, and t bit
+
+ .global GLOBAL(sdivsi3)
+#if __SHMEDIA__
+#if __SH5__ == 32
+ .section .text..SHmedia32,"ax"
+#else
+ .text
+#endif
+ .align 2
+#if 0
+/* The assembly code that follows is a hand-optimized version of the C
+ code that follows. Note that the registers that are modified are
+ exactly those listed as clobbered in the patterns divsi3_i1 and
+ divsi3_i1_media.
+
+int __sdivsi3 (i, j)
+ int i, j;
+{
+ register unsigned long long r18 asm ("r18");
+ register unsigned long long r19 asm ("r19");
+ register unsigned long long r0 asm ("r0") = 0;
+ register unsigned long long r1 asm ("r1") = 1;
+ register int r2 asm ("r2") = i >> 31;
+ register int r3 asm ("r3") = j >> 31;
+
+ r2 = r2 ? r2 : r1;
+ r3 = r3 ? r3 : r1;
+ r18 = i * r2;
+ r19 = j * r3;
+ r2 *= r3;
+
+ r19 <<= 31;
+ r1 <<= 31;
+ do
+ if (r18 >= r19)
+ r0 |= r1, r18 -= r19;
+ while (r19 >>= 1, r1 >>= 1);
+
+ return r2 * (int)r0;
+}
+*/
+GLOBAL(sdivsi3):
+ pt/l LOCAL(sdivsi3_dontadd), tr2
+ pt/l LOCAL(sdivsi3_loop), tr1
+ ptabs/l r18, tr0
+ movi 0, r0
+ movi 1, r1
+ shari.l r4, 31, r2
+ shari.l r5, 31, r3
+ cmveq r2, r1, r2
+ cmveq r3, r1, r3
+ muls.l r4, r2, r18
+ muls.l r5, r3, r19
+ muls.l r2, r3, r2
+ shlli r19, 31, r19
+ shlli r1, 31, r1
+LOCAL(sdivsi3_loop):
+ bgtu r19, r18, tr2
+ or r0, r1, r0
+ sub r18, r19, r18
+LOCAL(sdivsi3_dontadd):
+ shlri r1, 1, r1
+ shlri r19, 1, r19
+ bnei r1, 0, tr1
+ muls.l r0, r2, r0
+ add.l r0, r63, r0
+ blink tr0, r63
+#elif 0 /* ! 0 */
+ // inputs: r4,r5
+ // clobbered: r1,r2,r3,r18,r19,r20,r21,r25,tr0
+ // result in r0
+GLOBAL(sdivsi3):
+ // can create absolute value without extra latency,
+ // but dependent on proper sign extension of inputs:
+ // shari.l r5,31,r2
+ // xor r5,r2,r20
+ // sub r20,r2,r20 // r20 is now absolute value of r5, zero-extended.
+ shari.l r5,31,r2
+ ori r2,1,r2
+ muls.l r5,r2,r20 // r20 is now absolute value of r5, zero-extended.
+ movi 0xffffffffffffbb0c,r19 // shift count eqiv 76
+ shari.l r4,31,r3
+ nsb r20,r0
+ shlld r20,r0,r25
+ shlri r25,48,r25
+ sub r19,r25,r1
+ mmulfx.w r1,r1,r2
+ mshflo.w r1,r63,r1
+ // If r4 was to be used in-place instead of r21, could use this sequence
+ // to compute absolute:
+ // sub r63,r4,r19 // compute absolute value of r4
+ // shlri r4,32,r3 // into lower 32 bit of r4, keeping
+ // mcmv r19,r3,r4 // the sign in the upper 32 bits intact.
+ ori r3,1,r3
+ mmulfx.w r25,r2,r2
+ sub r19,r0,r0
+ muls.l r4,r3,r21
+ msub.w r1,r2,r2
+ addi r2,-2,r1
+ mulu.l r21,r1,r19
+ mmulfx.w r2,r2,r2
+ shlli r1,15,r1
+ shlrd r19,r0,r19
+ mulu.l r19,r20,r3
+ mmacnfx.wl r25,r2,r1
+ ptabs r18,tr0
+ sub r21,r3,r25
+
+ mulu.l r25,r1,r2
+ addi r0,14,r0
+ xor r4,r5,r18
+ shlrd r2,r0,r2
+ mulu.l r2,r20,r3
+ add r19,r2,r19
+ shari.l r18,31,r18
+ sub r25,r3,r25
+
+ mulu.l r25,r1,r2
+ sub r25,r20,r25
+ add r19,r18,r19
+ shlrd r2,r0,r2
+ mulu.l r2,r20,r3
+ addi r25,1,r25
+ add r19,r2,r19
+
+ cmpgt r25,r3,r25
+ add.l r19,r25,r0
+ xor r0,r18,r0
+ blink tr0,r63
+#else /* ! 0 && ! 0 */
+
+ // inputs: r4,r5
+ // clobbered: r1,r18,r19,r20,r21,r25,tr0
+ // result in r0
+ HIDDEN_FUNC(GLOBAL(sdivsi3_2))
+#ifndef __pic__
+ FUNC(GLOBAL(sdivsi3))
+GLOBAL(sdivsi3): /* this is the shcompact entry point */
+ // The special SHmedia entry point sdivsi3_1 prevents accidental linking
+ // with the SHcompact implementation, which clobbers tr1 / tr2.
+ .global GLOBAL(sdivsi3_1)
+GLOBAL(sdivsi3_1):
+ .global GLOBAL(div_table_internal)
+ movi (GLOBAL(div_table_internal) >> 16) & 65535, r20
+ shori GLOBAL(div_table_internal) & 65535, r20
+#endif
+ .global GLOBAL(sdivsi3_2)
+ // div_table in r20
+ // clobbered: r1,r18,r19,r21,r25,tr0
+GLOBAL(sdivsi3_2):
+ nsb r5, r1
+ shlld r5, r1, r25 // normalize; [-2 ..1, 1..2) in s2.62
+ shari r25, 58, r21 // extract 5(6) bit index (s2.4 with hole -1..1)
+ ldx.ub r20, r21, r19 // u0.8
+ shari r25, 32, r25 // normalize to s2.30
+ shlli r21, 1, r21
+ muls.l r25, r19, r19 // s2.38
+ ldx.w r20, r21, r21 // s2.14
+ ptabs r18, tr0
+ shari r19, 24, r19 // truncate to s2.14
+ sub r21, r19, r19 // some 11 bit inverse in s1.14
+ muls.l r19, r19, r21 // u0.28
+ sub r63, r1, r1
+ addi r1, 92, r1
+ muls.l r25, r21, r18 // s2.58
+ shlli r19, 45, r19 // multiply by two and convert to s2.58
+ /* bubble */
+ sub r19, r18, r18
+ shari r18, 28, r18 // some 22 bit inverse in s1.30
+ muls.l r18, r25, r0 // s2.60
+ muls.l r18, r4, r25 // s32.30
+ /* bubble */
+ shari r0, 16, r19 // s-16.44
+ muls.l r19, r18, r19 // s-16.74
+ shari r25, 63, r0
+ shari r4, 14, r18 // s19.-14
+ shari r19, 30, r19 // s-16.44
+ muls.l r19, r18, r19 // s15.30
+ xor r21, r0, r21 // You could also use the constant 1 << 27.
+ add r21, r25, r21
+ sub r21, r19, r21
+ shard r21, r1, r21
+ sub r21, r0, r0
+ blink tr0, r63
+#ifndef __pic__
+ ENDFUNC(GLOBAL(sdivsi3))
+#endif
+ ENDFUNC(GLOBAL(sdivsi3_2))
+#endif
+#elif defined __SHMEDIA__
+/* m5compact-nofpu */
+ // clobbered: r18,r19,r20,r21,r25,tr0,tr1,tr2
+ .mode SHmedia
+ .section .text..SHmedia32,"ax"
+ .align 2
+ FUNC(GLOBAL(sdivsi3))
+GLOBAL(sdivsi3):
+ pt/l LOCAL(sdivsi3_dontsub), tr0
+ pt/l LOCAL(sdivsi3_loop), tr1
+ ptabs/l r18,tr2
+ shari.l r4,31,r18
+ shari.l r5,31,r19
+ xor r4,r18,r20
+ xor r5,r19,r21
+ sub.l r20,r18,r20
+ sub.l r21,r19,r21
+ xor r18,r19,r19
+ shlli r21,32,r25
+ addi r25,-1,r21
+ addz.l r20,r63,r20
+LOCAL(sdivsi3_loop):
+ shlli r20,1,r20
+ bgeu/u r21,r20,tr0
+ sub r20,r21,r20
+LOCAL(sdivsi3_dontsub):
+ addi.l r25,-1,r25
+ bnei r25,-32,tr1
+ xor r20,r19,r20
+ sub.l r20,r19,r0
+ blink tr2,r63
+ ENDFUNC(GLOBAL(sdivsi3))
+#else /* ! __SHMEDIA__ */
+ FUNC(GLOBAL(sdivsi3))
+GLOBAL(sdivsi3):
+ mov r4,r1
+ mov r5,r0
+
+ tst r0,r0
+ bt div0
+ mov #0,r2
+ div0s r2,r1
+ subc r3,r3
+ subc r2,r1
+ div0s r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ div1 r0,r3
+ rotcl r1
+ addc r2,r1
+ rts
+ mov r1,r0
+
+
+div0: rts
+ mov #0,r0
+
+ ENDFUNC(GLOBAL(sdivsi3))
+#endif /* ! __SHMEDIA__ */
+#endif /* ! __SH4__ */
+#endif
+#ifdef L_udivsi3_i4
+
+ .title "SH DIVIDE"
+!! 4 byte integer Divide code for the Renesas SH
+#ifdef __SH4__
+!! args in r4 and r5, result in fpul, clobber r0, r1, r4, r5, dr0, dr2, dr4,
+!! and t bit
+
+ .global GLOBAL(udivsi3_i4)
+ HIDDEN_FUNC(GLOBAL(udivsi3_i4))
+GLOBAL(udivsi3_i4):
+ mov #1,r1
+ cmp/hi r1,r5
+ bf trivial
+ rotr r1
+ xor r1,r4
+ lds r4,fpul
+ mova L1,r0
+#ifdef FMOVD_WORKS
+ fmov.d @r0+,dr4
+#else
+ fmov.s @r0+,DR40
+ fmov.s @r0,DR41
+#endif
+ float fpul,dr0
+ xor r1,r5
+ lds r5,fpul
+ float fpul,dr2
+ fadd dr4,dr0
+ fadd dr4,dr2
+ fdiv dr2,dr0
+ rts
+ ftrc dr0,fpul
+
+trivial:
+ rts
+ lds r4,fpul
+
+ .align 2
+#ifdef FMOVD_WORKS
+ .align 3 ! make double below 8 byte aligned.
+#endif
+L1:
+ .double 2147483648
+
+ ENDFUNC(GLOBAL(udivsi3_i4))
+#elif defined (__SH5__) && ! defined (__SH4_NOFPU__)
+#if ! __SH5__ || __SH5__ == 32
+!! args in r4 and r5, result in fpul, clobber r20, r21, dr0, fr33
+ .mode SHmedia
+ .global GLOBAL(udivsi3_i4)
+ HIDDEN_FUNC(GLOBAL(udivsi3_i4))
+GLOBAL(udivsi3_i4):
+ addz.l r4,r63,r20
+ addz.l r5,r63,r21
+ fmov.qd r20,dr0
+ fmov.qd r21,dr32
+ ptabs r18,tr0
+ float.qd dr0,dr0
+ float.qd dr32,dr32
+ fdiv.d dr0,dr32,dr0
+ ftrc.dq dr0,dr32
+ fmov.s fr33,fr32
+ blink tr0,r63
+
+ ENDFUNC(GLOBAL(udivsi3_i4))
+#endif /* ! __SH5__ || __SH5__ == 32 */
+#elif defined(__SH4_SINGLE__) || defined(__SH4_SINGLE_ONLY__)
+!! args in r4 and r5, result in fpul, clobber r0, r1, r4, r5, dr0, dr2, dr4
+
+ .global GLOBAL(udivsi3_i4)
+ HIDDEN_FUNC(GLOBAL(udivsi3_i4))
+GLOBAL(udivsi3_i4):
+ mov #1,r1
+ cmp/hi r1,r5
+ bf trivial
+ sts.l fpscr,@-r15
+ mova L1,r0
+ lds.l @r0+,fpscr
+ rotr r1
+ xor r1,r4
+ lds r4,fpul
+#ifdef FMOVD_WORKS
+ fmov.d @r0+,dr4
+#else
+ fmov.s @r0+,DR40
+ fmov.s @r0,DR41
+#endif
+ float fpul,dr0
+ xor r1,r5
+ lds r5,fpul
+ float fpul,dr2
+ fadd dr4,dr0
+ fadd dr4,dr2
+ fdiv dr2,dr0
+ ftrc dr0,fpul
+ rts
+ lds.l @r15+,fpscr
+
+#ifdef FMOVD_WORKS
+ .align 3 ! make double below 8 byte aligned.
+#endif
+trivial:
+ rts
+ lds r4,fpul
+
+ .align 2
+L1:
+#ifndef FMOVD_WORKS
+ .long 0x80000
+#else
+ .long 0x180000
+#endif
+ .double 2147483648
+
+ ENDFUNC(GLOBAL(udivsi3_i4))
+#endif /* ! __SH4__ */
+#endif
+
+#ifdef L_udivsi3
+/* __SH4_SINGLE_ONLY__ keeps this part for link compatibility with
+ sh2e/sh3e code. */
+#if (! defined(__SH4__) && ! defined (__SH4_SINGLE__)) || defined (__linux__)
+
+!! args in r4 and r5, result in r0, clobbers r4, pr, and t bit
+ .global GLOBAL(udivsi3)
+ HIDDEN_FUNC(GLOBAL(udivsi3))
+
+#if __SHMEDIA__
+#if __SH5__ == 32
+ .section .text..SHmedia32,"ax"
+#else
+ .text
+#endif
+ .align 2
+#if 0
+/* The assembly code that follows is a hand-optimized version of the C
+ code that follows. Note that the registers that are modified are
+ exactly those listed as clobbered in the patterns udivsi3_i1 and
+ udivsi3_i1_media.
+
+unsigned
+__udivsi3 (i, j)
+ unsigned i, j;
+{
+ register unsigned long long r0 asm ("r0") = 0;
+ register unsigned long long r18 asm ("r18") = 1;
+ register unsigned long long r4 asm ("r4") = i;
+ register unsigned long long r19 asm ("r19") = j;
+
+ r19 <<= 31;
+ r18 <<= 31;
+ do
+ if (r4 >= r19)
+ r0 |= r18, r4 -= r19;
+ while (r19 >>= 1, r18 >>= 1);
+
+ return r0;
+}
+*/
+GLOBAL(udivsi3):
+ pt/l LOCAL(udivsi3_dontadd), tr2
+ pt/l LOCAL(udivsi3_loop), tr1
+ ptabs/l r18, tr0
+ movi 0, r0
+ movi 1, r18
+ addz.l r5, r63, r19
+ addz.l r4, r63, r4
+ shlli r19, 31, r19
+ shlli r18, 31, r18
+LOCAL(udivsi3_loop):
+ bgtu r19, r4, tr2
+ or r0, r18, r0
+ sub r4, r19, r4
+LOCAL(udivsi3_dontadd):
+ shlri r18, 1, r18
+ shlri r19, 1, r19
+ bnei r18, 0, tr1
+ blink tr0, r63
+#else
+GLOBAL(udivsi3):
+ // inputs: r4,r5
+ // clobbered: r18,r19,r20,r21,r22,r25,tr0
+ // result in r0.
+ addz.l r5,r63,r22
+ nsb r22,r0
+ shlld r22,r0,r25
+ shlri r25,48,r25
+ movi 0xffffffffffffbb0c,r20 // shift count eqiv 76
+ sub r20,r25,r21
+ mmulfx.w r21,r21,r19
+ mshflo.w r21,r63,r21
+ ptabs r18,tr0
+ mmulfx.w r25,r19,r19
+ sub r20,r0,r0
+ /* bubble */
+ msub.w r21,r19,r19
+ addi r19,-2,r21 /* It would be nice for scheduling to do this add to r21
+ before the msub.w, but we need a different value for
+ r19 to keep errors under control. */
+ mulu.l r4,r21,r18
+ mmulfx.w r19,r19,r19
+ shlli r21,15,r21
+ shlrd r18,r0,r18
+ mulu.l r18,r22,r20
+ mmacnfx.wl r25,r19,r21
+ /* bubble */
+ sub r4,r20,r25
+
+ mulu.l r25,r21,r19
+ addi r0,14,r0
+ /* bubble */
+ shlrd r19,r0,r19
+ mulu.l r19,r22,r20
+ add r18,r19,r18
+ /* bubble */
+ sub.l r25,r20,r25
+
+ mulu.l r25,r21,r19
+ addz.l r25,r63,r25
+ sub r25,r22,r25
+ shlrd r19,r0,r19
+ mulu.l r19,r22,r20
+ addi r25,1,r25
+ add r18,r19,r18
+
+ cmpgt r25,r20,r25
+ add.l r18,r25,r0
+ blink tr0,r63
+#endif
+#elif defined (__SHMEDIA__)
+/* m5compact-nofpu - more emphasis on code size than on speed, but don't
+ ignore speed altogether - div1 needs 9 cycles, subc 7 and rotcl 4.
+ So use a short shmedia loop. */
+ // clobbered: r20,r21,r25,tr0,tr1,tr2
+ .mode SHmedia
+ .section .text..SHmedia32,"ax"
+ .align 2
+GLOBAL(udivsi3):
+ pt/l LOCAL(udivsi3_dontsub), tr0
+ pt/l LOCAL(udivsi3_loop), tr1
+ ptabs/l r18,tr2
+ shlli r5,32,r25
+ addi r25,-1,r21
+ addz.l r4,r63,r20
+LOCAL(udivsi3_loop):
+ shlli r20,1,r20
+ bgeu/u r21,r20,tr0
+ sub r20,r21,r20
+LOCAL(udivsi3_dontsub):
+ addi.l r25,-1,r25
+ bnei r25,-32,tr1
+ add.l r20,r63,r0
+ blink tr2,r63
+#else /* ! defined (__SHMEDIA__) */
+LOCAL(div8):
+ div1 r5,r4
+LOCAL(div7):
+ div1 r5,r4; div1 r5,r4; div1 r5,r4
+ div1 r5,r4; div1 r5,r4; div1 r5,r4; rts; div1 r5,r4
+
+LOCAL(divx4):
+ div1 r5,r4; rotcl r0
+ div1 r5,r4; rotcl r0
+ div1 r5,r4; rotcl r0
+ rts; div1 r5,r4
+
+GLOBAL(udivsi3):
+ sts.l pr,@-r15
+ extu.w r5,r0
+ cmp/eq r5,r0
+#ifdef __sh1__
+ bf LOCAL(large_divisor)
+#else
+ bf/s LOCAL(large_divisor)
+#endif
+ div0u
+ swap.w r4,r0
+ shlr16 r4
+ bsr LOCAL(div8)
+ shll16 r5
+ bsr LOCAL(div7)
+ div1 r5,r4
+ xtrct r4,r0
+ xtrct r0,r4
+ bsr LOCAL(div8)
+ swap.w r4,r4
+ bsr LOCAL(div7)
+ div1 r5,r4
+ lds.l @r15+,pr
+ xtrct r4,r0
+ swap.w r0,r0
+ rotcl r0
+ rts
+ shlr16 r5
+
+LOCAL(large_divisor):
+#ifdef __sh1__
+ div0u
+#endif
+ mov #0,r0
+ xtrct r4,r0
+ xtrct r0,r4
+ bsr LOCAL(divx4)
+ rotcl r0
+ bsr LOCAL(divx4)
+ rotcl r0
+ bsr LOCAL(divx4)
+ rotcl r0
+ bsr LOCAL(divx4)
+ rotcl r0
+ lds.l @r15+,pr
+ rts
+ rotcl r0
+
+ ENDFUNC(GLOBAL(udivsi3))
+#endif /* ! __SHMEDIA__ */
+#endif /* __SH4__ */
+#endif /* L_udivsi3 */
+
+#ifdef L_udivdi3
+#ifdef __SHMEDIA__
+ .mode SHmedia
+ .section .text..SHmedia32,"ax"
+ .align 2
+ .global GLOBAL(udivdi3)
+ FUNC(GLOBAL(udivdi3))
+GLOBAL(udivdi3):
+ HIDDEN_ALIAS(udivdi3_internal,udivdi3)
+ shlri r3,1,r4
+ nsb r4,r22
+ shlld r3,r22,r6
+ shlri r6,49,r5
+ movi 0xffffffffffffbaf1,r21 /* .l shift count 17. */
+ sub r21,r5,r1
+ mmulfx.w r1,r1,r4
+ mshflo.w r1,r63,r1
+ sub r63,r22,r20 // r63 == 64 % 64
+ mmulfx.w r5,r4,r4
+ pta LOCAL(large_divisor),tr0
+ addi r20,32,r9
+ msub.w r1,r4,r1
+ madd.w r1,r1,r1
+ mmulfx.w r1,r1,r4
+ shlri r6,32,r7
+ bgt/u r9,r63,tr0 // large_divisor
+ mmulfx.w r5,r4,r4
+ shlri r2,32+14,r19
+ addi r22,-31,r0
+ msub.w r1,r4,r1
+
+ mulu.l r1,r7,r4
+ addi r1,-3,r5
+ mulu.l r5,r19,r5
+ sub r63,r4,r4 // Negate to make sure r1 ends up <= 1/r2
+ shlri r4,2,r4 /* chop off leading %0000000000000000 001.00000000000 - or, as
+ the case may be, %0000000000000000 000.11111111111, still */
+ muls.l r1,r4,r4 /* leaving at least one sign bit. */
+ mulu.l r5,r3,r8
+ mshalds.l r1,r21,r1
+ shari r4,26,r4
+ shlld r8,r0,r8
+ add r1,r4,r1 // 31 bit unsigned reciprocal now in r1 (msb equiv. 0.5)
+ sub r2,r8,r2
+ /* Can do second step of 64 : 32 div now, using r1 and the rest in r2. */
+
+ shlri r2,22,r21
+ mulu.l r21,r1,r21
+ shlld r5,r0,r8
+ addi r20,30-22,r0
+ shlrd r21,r0,r21
+ mulu.l r21,r3,r5
+ add r8,r21,r8
+ mcmpgt.l r21,r63,r21 // See Note 1
+ addi r20,30,r0
+ mshfhi.l r63,r21,r21
+ sub r2,r5,r2
+ andc r2,r21,r2
+
+ /* small divisor: need a third divide step */
+ mulu.l r2,r1,r7
+ ptabs r18,tr0
+ addi r2,1,r2
+ shlrd r7,r0,r7
+ mulu.l r7,r3,r5
+ add r8,r7,r8
+ sub r2,r3,r2
+ cmpgt r2,r5,r5
+ add r8,r5,r2
+ /* could test r3 here to check for divide by zero. */
+ blink tr0,r63
+
+LOCAL(large_divisor):
+ mmulfx.w r5,r4,r4
+ shlrd r2,r9,r25
+ shlri r25,32,r8
+ msub.w r1,r4,r1
+
+ mulu.l r1,r7,r4
+ addi r1,-3,r5
+ mulu.l r5,r8,r5
+ sub r63,r4,r4 // Negate to make sure r1 ends up <= 1/r2
+ shlri r4,2,r4 /* chop off leading %0000000000000000 001.00000000000 - or, as
+ the case may be, %0000000000000000 000.11111111111, still */
+ muls.l r1,r4,r4 /* leaving at least one sign bit. */
+ shlri r5,14-1,r8
+ mulu.l r8,r7,r5
+ mshalds.l r1,r21,r1
+ shari r4,26,r4
+ add r1,r4,r1 // 31 bit unsigned reciprocal now in r1 (msb equiv. 0.5)
+ sub r25,r5,r25
+ /* Can do second step of 64 : 32 div now, using r1 and the rest in r25. */
+
+ shlri r25,22,r21
+ mulu.l r21,r1,r21
+ pta LOCAL(no_lo_adj),tr0
+ addi r22,32,r0
+ shlri r21,40,r21
+ mulu.l r21,r7,r5
+ add r8,r21,r8
+ shlld r2,r0,r2
+ sub r25,r5,r25
+ bgtu/u r7,r25,tr0 // no_lo_adj
+ addi r8,1,r8
+ sub r25,r7,r25
+LOCAL(no_lo_adj):
+ mextr4 r2,r25,r2
+
+ /* large_divisor: only needs a few adjustments. */
+ mulu.l r8,r6,r5
+ ptabs r18,tr0
+ /* bubble */
+ cmpgtu r5,r2,r5
+ sub r8,r5,r2
+ blink tr0,r63
+ ENDFUNC(GLOBAL(udivdi3))
+/* Note 1: To shift the result of the second divide stage so that the result
+ always fits into 32 bits, yet we still reduce the rest sufficiently
+ would require a lot of instructions to do the shifts just right. Using
+ the full 64 bit shift result to multiply with the divisor would require
+ four extra instructions for the upper 32 bits (shift / mulu / shift / sub).
+ Fortunately, if the upper 32 bits of the shift result are nonzero, we
+ know that the rest after taking this partial result into account will
+ fit into 32 bits. So we just clear the upper 32 bits of the rest if the
+ upper 32 bits of the partial result are nonzero. */
+#endif /* __SHMEDIA__ */
+#endif /* L_udivdi3 */
+
+#ifdef L_divdi3
+#ifdef __SHMEDIA__
+ .mode SHmedia
+ .section .text..SHmedia32,"ax"
+ .align 2
+ .global GLOBAL(divdi3)
+ FUNC(GLOBAL(divdi3))
+GLOBAL(divdi3):
+ pta GLOBAL(udivdi3_internal),tr0
+ shari r2,63,r22
+ shari r3,63,r23
+ xor r2,r22,r2
+ xor r3,r23,r3
+ sub r2,r22,r2
+ sub r3,r23,r3
+ beq/u r22,r23,tr0
+ ptabs r18,tr1
+ blink tr0,r18
+ sub r63,r2,r2
+ blink tr1,r63
+ ENDFUNC(GLOBAL(divdi3))
+#endif /* __SHMEDIA__ */
+#endif /* L_divdi3 */
+
+#ifdef L_umoddi3
+#ifdef __SHMEDIA__
+ .mode SHmedia
+ .section .text..SHmedia32,"ax"
+ .align 2
+ .global GLOBAL(umoddi3)
+ FUNC(GLOBAL(umoddi3))
+GLOBAL(umoddi3):
+ HIDDEN_ALIAS(umoddi3_internal,umoddi3)
+ shlri r3,1,r4
+ nsb r4,r22
+ shlld r3,r22,r6
+ shlri r6,49,r5
+ movi 0xffffffffffffbaf1,r21 /* .l shift count 17. */
+ sub r21,r5,r1
+ mmulfx.w r1,r1,r4
+ mshflo.w r1,r63,r1
+ sub r63,r22,r20 // r63 == 64 % 64
+ mmulfx.w r5,r4,r4
+ pta LOCAL(large_divisor),tr0
+ addi r20,32,r9
+ msub.w r1,r4,r1
+ madd.w r1,r1,r1
+ mmulfx.w r1,r1,r4
+ shlri r6,32,r7
+ bgt/u r9,r63,tr0 // large_divisor
+ mmulfx.w r5,r4,r4
+ shlri r2,32+14,r19
+ addi r22,-31,r0
+ msub.w r1,r4,r1
+
+ mulu.l r1,r7,r4
+ addi r1,-3,r5
+ mulu.l r5,r19,r5
+ sub r63,r4,r4 // Negate to make sure r1 ends up <= 1/r2
+ shlri r4,2,r4 /* chop off leading %0000000000000000 001.00000000000 - or, as
+ the case may be, %0000000000000000 000.11111111111, still */
+ muls.l r1,r4,r4 /* leaving at least one sign bit. */
+ mulu.l r5,r3,r5
+ mshalds.l r1,r21,r1
+ shari r4,26,r4
+ shlld r5,r0,r5
+ add r1,r4,r1 // 31 bit unsigned reciprocal now in r1 (msb equiv. 0.5)
+ sub r2,r5,r2
+ /* Can do second step of 64 : 32 div now, using r1 and the rest in r2. */
+
+ shlri r2,22,r21
+ mulu.l r21,r1,r21
+ addi r20,30-22,r0
+ /* bubble */ /* could test r3 here to check for divide by zero. */
+ shlrd r21,r0,r21
+ mulu.l r21,r3,r5
+ mcmpgt.l r21,r63,r21 // See Note 1
+ addi r20,30,r0
+ mshfhi.l r63,r21,r21
+ sub r2,r5,r2
+ andc r2,r21,r2
+
+ /* small divisor: need a third divide step */
+ mulu.l r2,r1,r7
+ ptabs r18,tr0
+ sub r2,r3,r8 /* re-use r8 here for rest - r3 */
+ shlrd r7,r0,r7
+ mulu.l r7,r3,r5
+ /* bubble */
+ addi r8,1,r7
+ cmpgt r7,r5,r7
+ cmvne r7,r8,r2
+ sub r2,r5,r2
+ blink tr0,r63
+
+LOCAL(large_divisor):
+ mmulfx.w r5,r4,r4
+ shlrd r2,r9,r25
+ shlri r25,32,r8
+ msub.w r1,r4,r1
+
+ mulu.l r1,r7,r4
+ addi r1,-3,r5
+ mulu.l r5,r8,r5
+ sub r63,r4,r4 // Negate to make sure r1 ends up <= 1/r2
+ shlri r4,2,r4 /* chop off leading %0000000000000000 001.00000000000 - or, as
+ the case may be, %0000000000000000 000.11111111111, still */
+ muls.l r1,r4,r4 /* leaving at least one sign bit. */
+ shlri r5,14-1,r8
+ mulu.l r8,r7,r5
+ mshalds.l r1,r21,r1
+ shari r4,26,r4
+ add r1,r4,r1 // 31 bit unsigned reciprocal now in r1 (msb equiv. 0.5)
+ sub r25,r5,r25
+ /* Can do second step of 64 : 32 div now, using r1 and the rest in r25. */
+
+ shlri r25,22,r21
+ mulu.l r21,r1,r21
+ pta LOCAL(no_lo_adj),tr0
+ addi r22,32,r0
+ shlri r21,40,r21
+ mulu.l r21,r7,r5
+ add r8,r21,r8
+ shlld r2,r0,r2
+ sub r25,r5,r25
+ bgtu/u r7,r25,tr0 // no_lo_adj
+ addi r8,1,r8
+ sub r25,r7,r25
+LOCAL(no_lo_adj):
+ mextr4 r2,r25,r2
+
+ /* large_divisor: only needs a few adjustments. */
+ mulu.l r8,r6,r5
+ ptabs r18,tr0
+ add r2,r6,r7
+ cmpgtu r5,r2,r8
+ cmvne r8,r7,r2
+ sub r2,r5,r2
+ shlrd r2,r22,r2
+ blink tr0,r63
+ ENDFUNC(GLOBAL(umoddi3))
+/* Note 1: To shift the result of the second divide stage so that the result
+ always fits into 32 bits, yet we still reduce the rest sufficiently
+ would require a lot of instructions to do the shifts just right. Using
+ the full 64 bit shift result to multiply with the divisor would require
+ four extra instructions for the upper 32 bits (shift / mulu / shift / sub).
+ Fortunately, if the upper 32 bits of the shift result are nonzero, we
+ know that the rest after taking this partial result into account will
+ fit into 32 bits. So we just clear the upper 32 bits of the rest if the
+ upper 32 bits of the partial result are nonzero. */
+#endif /* __SHMEDIA__ */
+#endif /* L_umoddi3 */
+
+#ifdef L_moddi3
+#ifdef __SHMEDIA__
+ .mode SHmedia
+ .section .text..SHmedia32,"ax"
+ .align 2
+ .global GLOBAL(moddi3)
+ FUNC(GLOBAL(moddi3))
+GLOBAL(moddi3):
+ pta GLOBAL(umoddi3_internal),tr0
+ shari r2,63,r22
+ shari r3,63,r23
+ xor r2,r22,r2
+ xor r3,r23,r3
+ sub r2,r22,r2
+ sub r3,r23,r3
+ beq/u r22,r63,tr0
+ ptabs r18,tr1
+ blink tr0,r18
+ sub r63,r2,r2
+ blink tr1,r63
+ ENDFUNC(GLOBAL(moddi3))
+#endif /* __SHMEDIA__ */
+#endif /* L_moddi3 */
+
+#ifdef L_set_fpscr
+#if !defined (__SH2A_NOFPU__)
+#if defined (__SH2E__) || defined (__SH2A__) || defined (__SH3E__) || defined(__SH4_SINGLE__) || defined(__SH4__) || defined(__SH4_SINGLE_ONLY__) || __SH5__ == 32
+#ifdef __SH5__
+ .mode SHcompact
+#endif
+ .global GLOBAL(set_fpscr)
+ HIDDEN_FUNC(GLOBAL(set_fpscr))
+GLOBAL(set_fpscr):
+ lds r4,fpscr
+#ifdef __PIC__
+ mov.l r12,@-r15
+#ifdef __vxworks
+ mov.l LOCAL(set_fpscr_L0_base),r12
+ mov.l LOCAL(set_fpscr_L0_index),r0
+ mov.l @r12,r12
+ mov.l @(r0,r12),r12
+#else
+ mova LOCAL(set_fpscr_L0),r0
+ mov.l LOCAL(set_fpscr_L0),r12
+ add r0,r12
+#endif
+ mov.l LOCAL(set_fpscr_L1),r0
+ mov.l @(r0,r12),r1
+ mov.l @r15+,r12
+#else
+ mov.l LOCAL(set_fpscr_L1),r1
+#endif
+ swap.w r4,r0
+ or #24,r0
+#ifndef FMOVD_WORKS
+ xor #16,r0
+#endif
+#if defined(__SH4__) || defined (__SH2A_DOUBLE__)
+ swap.w r0,r3
+ mov.l r3,@(4,r1)
+#else /* defined (__SH2E__) || defined(__SH3E__) || defined(__SH4_SINGLE*__) */
+ swap.w r0,r2
+ mov.l r2,@r1
+#endif
+#ifndef FMOVD_WORKS
+ xor #8,r0
+#else
+ xor #24,r0
+#endif
+#if defined(__SH4__) || defined (__SH2A_DOUBLE__)
+ swap.w r0,r2
+ rts
+ mov.l r2,@r1
+#else /* defined(__SH2E__) || defined(__SH3E__) || defined(__SH4_SINGLE*__) */
+ swap.w r0,r3
+ rts
+ mov.l r3,@(4,r1)
+#endif
+ .align 2
+#ifdef __PIC__
+#ifdef __vxworks
+LOCAL(set_fpscr_L0_base):
+ .long ___GOTT_BASE__
+LOCAL(set_fpscr_L0_index):
+ .long ___GOTT_INDEX__
+#else
+LOCAL(set_fpscr_L0):
+ .long _GLOBAL_OFFSET_TABLE_
+#endif
+LOCAL(set_fpscr_L1):
+ .long GLOBAL(fpscr_values@GOT)
+#else
+LOCAL(set_fpscr_L1):
+ .long GLOBAL(fpscr_values)
+#endif
+
+ ENDFUNC(GLOBAL(set_fpscr))
+#ifndef NO_FPSCR_VALUES
+#ifdef __ELF__
+ .comm GLOBAL(fpscr_values),8,4
+#else
+ .comm GLOBAL(fpscr_values),8
+#endif /* ELF */
+#endif /* NO_FPSCR_VALUES */
+#endif /* SH2E / SH3E / SH4 */
+#endif /* __SH2A_NOFPU__ */
+#endif /* L_set_fpscr */
+#ifdef L_ic_invalidate
+#if __SH5__ == 32
+ .mode SHmedia
+ .section .text..SHmedia32,"ax"
+ .align 2
+ .global GLOBAL(init_trampoline)
+ HIDDEN_FUNC(GLOBAL(init_trampoline))
+GLOBAL(init_trampoline):
+ st.l r0,8,r2
+#ifdef __LITTLE_ENDIAN__
+ movi 9,r20
+ shori 0x402b,r20
+ shori 0xd101,r20
+ shori 0xd002,r20
+#else
+ movi 0xffffffffffffd002,r20
+ shori 0xd101,r20
+ shori 0x402b,r20
+ shori 9,r20
+#endif
+ st.q r0,0,r20
+ st.l r0,12,r3
+ ENDFUNC(GLOBAL(init_trampoline))
+ .global GLOBAL(ic_invalidate)
+ HIDDEN_FUNC(GLOBAL(ic_invalidate))
+GLOBAL(ic_invalidate):
+ ocbwb r0,0
+ synco
+ icbi r0, 0
+ ptabs r18, tr0
+ synci
+ blink tr0, r63
+ ENDFUNC(GLOBAL(ic_invalidate))
+#elif defined(__SH4A__)
+ .global GLOBAL(ic_invalidate)
+ HIDDEN_FUNC(GLOBAL(ic_invalidate))
+GLOBAL(ic_invalidate):
+ ocbwb @r4
+ synco
+ icbi @r4
+ rts
+ nop
+ ENDFUNC(GLOBAL(ic_invalidate))
+#elif defined(__SH4_SINGLE__) || defined(__SH4__) || defined(__SH4_SINGLE_ONLY__) || (defined(__SH4_NOFPU__) && !defined(__SH5__))
+ /* For system code, we use ic_invalidate_line_i, but user code
+ needs a different mechanism. A kernel call is generally not
+ available, and it would also be slow. Different SH4 variants use
+ different sizes and associativities of the Icache. We use a small
+ bit of dispatch code that can be put hidden in every shared object,
+ which calls the actual processor-specific invalidation code in a
+ separate module.
+ Or if you have operating system support, the OS could mmap the
+ procesor-specific code from a single page, since it is highly
+ repetitive. */
+ .global GLOBAL(ic_invalidate)
+ HIDDEN_FUNC(GLOBAL(ic_invalidate))
+GLOBAL(ic_invalidate):
+#ifdef __pic__
+#ifdef __vxworks
+ mov.l 1f,r1
+ mov.l 2f,r0
+ mov.l @r1,r1
+ mov.l 0f,r2
+ mov.l @(r0,r1),r0
+#else
+ mov.l 1f,r1
+ mova 1f,r0
+ mov.l 0f,r2
+ add r1,r0
+#endif
+ mov.l @(r0,r2),r1
+#else
+ mov.l 0f,r1
+#endif
+ ocbwb @r4
+ mov.l @(8,r1),r0
+ sub r1,r4
+ and r4,r0
+ add r1,r0
+ jmp @r0
+ mov.l @(4,r1),r0
+ .align 2
+#ifndef __pic__
+0: .long GLOBAL(ic_invalidate_array)
+#else /* __pic__ */
+ .global GLOBAL(ic_invalidate_array)
+0: .long GLOBAL(ic_invalidate_array)@GOT
+#ifdef __vxworks
+1: .long ___GOTT_BASE__
+2: .long ___GOTT_INDEX__
+#else
+1: .long _GLOBAL_OFFSET_TABLE_
+#endif
+ ENDFUNC(GLOBAL(ic_invalidate))
+#endif /* __pic__ */
+#endif /* SH4 */
+#endif /* L_ic_invalidate */
+
+#ifdef L_ic_invalidate_array
+#if defined(__SH4A__) || (defined (__FORCE_SH4A__) && (defined(__SH4_SINGLE__) || defined(__SH4__) || defined(__SH4_SINGLE_ONLY__) || (defined(__SH4_NOFPU__) && !defined(__SH5__))))
+ .global GLOBAL(ic_invalidate_array)
+ /* This is needed when an SH4 dso with trampolines is used on SH4A. */
+ .global GLOBAL(ic_invalidate_array)
+ FUNC(GLOBAL(ic_invalidate_array))
+GLOBAL(ic_invalidate_array):
+ add r1,r4
+ synco
+ icbi @r4
+ rts
+ nop
+ .align 2
+ .long 0
+ ENDFUNC(GLOBAL(ic_invalidate_array))
+#elif defined(__SH4_SINGLE__) || defined(__SH4__) || defined(__SH4_SINGLE_ONLY__) || (defined(__SH4_NOFPU__) && !defined(__SH5__))
+ .global GLOBAL(ic_invalidate_array)
+ .p2align 5
+ FUNC(GLOBAL(ic_invalidate_array))
+/* This must be aligned to the beginning of a cache line. */
+GLOBAL(ic_invalidate_array):
+#ifndef WAYS
+#define WAYS 4
+#define WAY_SIZE 0x4000
+#endif
+#if WAYS == 1
+ .rept WAY_SIZE * WAYS / 32
+ rts
+ nop
+ .rept 7
+ .long WAY_SIZE - 32
+ .endr
+ .endr
+#elif WAYS <= 6
+ .rept WAY_SIZE * WAYS / 32
+ braf r0
+ add #-8,r0
+ .long WAY_SIZE + 8
+ .long WAY_SIZE - 32
+ .rept WAYS-2
+ braf r0
+ nop
+ .endr
+ .rept 7 - WAYS
+ rts
+ nop
+ .endr
+ .endr
+#else /* WAYS > 6 */
+ /* This variant needs two different pages for mmap-ing. */
+ .rept WAYS-1
+ .rept WAY_SIZE / 32
+ braf r0
+ nop
+ .long WAY_SIZE
+ .rept 6
+ .long WAY_SIZE - 32
+ .endr
+ .endr
+ .endr
+ .rept WAY_SIZE / 32
+ rts
+ .rept 15
+ nop
+ .endr
+ .endr
+#endif /* WAYS */
+ ENDFUNC(GLOBAL(ic_invalidate_array))
+#endif /* SH4 */
+#endif /* L_ic_invalidate_array */
+
+#if defined (__SH5__) && __SH5__ == 32
+#ifdef L_shcompact_call_trampoline
+ .section .rodata
+ .align 1
+LOCAL(ct_main_table):
+.word LOCAL(ct_r2_fp) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r2_ld) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r2_pop) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r3_fp) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r3_ld) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r3_pop) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r4_fp) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r4_ld) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r4_pop) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r5_fp) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r5_ld) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r5_pop) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r6_fph) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r6_fpl) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r6_ld) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r6_pop) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r7_fph) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r7_fpl) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r7_ld) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r7_pop) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r8_fph) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r8_fpl) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r8_ld) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r8_pop) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r9_fph) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r9_fpl) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r9_ld) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r9_pop) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_pop_seq) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_pop_seq) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_r9_pop) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_ret_wide) - datalabel LOCAL(ct_main_label)
+.word LOCAL(ct_call_func) - datalabel LOCAL(ct_main_label)
+ .mode SHmedia
+ .section .text..SHmedia32, "ax"
+ .align 2
+
+ /* This function loads 64-bit general-purpose registers from the
+ stack, from a memory address contained in them or from an FP
+ register, according to a cookie passed in r1. Its execution
+ time is linear on the number of registers that actually have
+ to be copied. See sh.h for details on the actual bit pattern.
+
+ The function to be called is passed in r0. If a 32-bit return
+ value is expected, the actual function will be tail-called,
+ otherwise the return address will be stored in r10 (that the
+ caller should expect to be clobbered) and the return value
+ will be expanded into r2/r3 upon return. */
+
+ .global GLOBAL(GCC_shcompact_call_trampoline)
+ FUNC(GLOBAL(GCC_shcompact_call_trampoline))
+GLOBAL(GCC_shcompact_call_trampoline):
+ ptabs/l r0, tr0 /* Prepare to call the actual function. */
+ movi ((datalabel LOCAL(ct_main_table) - 31 * 2) >> 16) & 65535, r0
+ pt/l LOCAL(ct_loop), tr1
+ addz.l r1, r63, r1
+ shori ((datalabel LOCAL(ct_main_table) - 31 * 2)) & 65535, r0
+LOCAL(ct_loop):
+ nsb r1, r28
+ shlli r28, 1, r29
+ ldx.w r0, r29, r30
+LOCAL(ct_main_label):
+ ptrel/l r30, tr2
+ blink tr2, r63
+LOCAL(ct_r2_fp): /* Copy r2 from an FP register. */
+ /* It must be dr0, so just do it. */
+ fmov.dq dr0, r2
+ movi 7, r30
+ shlli r30, 29, r31
+ andc r1, r31, r1
+ blink tr1, r63
+LOCAL(ct_r3_fp): /* Copy r3 from an FP register. */
+ /* It is either dr0 or dr2. */
+ movi 7, r30
+ shlri r1, 26, r32
+ shlli r30, 26, r31
+ andc r1, r31, r1
+ fmov.dq dr0, r3
+ beqi/l r32, 4, tr1
+ fmov.dq dr2, r3
+ blink tr1, r63
+LOCAL(ct_r4_fp): /* Copy r4 from an FP register. */
+ shlri r1, 23 - 3, r34
+ andi r34, 3 << 3, r33
+ addi r33, LOCAL(ct_r4_fp_copy) - datalabel LOCAL(ct_r4_fp_base), r32
+LOCAL(ct_r4_fp_base):
+ ptrel/l r32, tr2
+ movi 7, r30
+ shlli r30, 23, r31
+ andc r1, r31, r1
+ blink tr2, r63
+LOCAL(ct_r4_fp_copy):
+ fmov.dq dr0, r4
+ blink tr1, r63
+ fmov.dq dr2, r4
+ blink tr1, r63
+ fmov.dq dr4, r4
+ blink tr1, r63
+LOCAL(ct_r5_fp): /* Copy r5 from an FP register. */
+ shlri r1, 20 - 3, r34
+ andi r34, 3 << 3, r33
+ addi r33, LOCAL(ct_r5_fp_copy) - datalabel LOCAL(ct_r5_fp_base), r32
+LOCAL(ct_r5_fp_base):
+ ptrel/l r32, tr2
+ movi 7, r30
+ shlli r30, 20, r31
+ andc r1, r31, r1
+ blink tr2, r63
+LOCAL(ct_r5_fp_copy):
+ fmov.dq dr0, r5
+ blink tr1, r63
+ fmov.dq dr2, r5
+ blink tr1, r63
+ fmov.dq dr4, r5
+ blink tr1, r63
+ fmov.dq dr6, r5
+ blink tr1, r63
+LOCAL(ct_r6_fph): /* Copy r6 from a high FP register. */
+ /* It must be dr8. */
+ fmov.dq dr8, r6
+ movi 15, r30
+ shlli r30, 16, r31
+ andc r1, r31, r1
+ blink tr1, r63
+LOCAL(ct_r6_fpl): /* Copy r6 from a low FP register. */
+ shlri r1, 16 - 3, r34
+ andi r34, 3 << 3, r33
+ addi r33, LOCAL(ct_r6_fp_copy) - datalabel LOCAL(ct_r6_fp_base), r32
+LOCAL(ct_r6_fp_base):
+ ptrel/l r32, tr2
+ movi 7, r30
+ shlli r30, 16, r31
+ andc r1, r31, r1
+ blink tr2, r63
+LOCAL(ct_r6_fp_copy):
+ fmov.dq dr0, r6
+ blink tr1, r63
+ fmov.dq dr2, r6
+ blink tr1, r63
+ fmov.dq dr4, r6
+ blink tr1, r63
+ fmov.dq dr6, r6
+ blink tr1, r63
+LOCAL(ct_r7_fph): /* Copy r7 from a high FP register. */
+ /* It is either dr8 or dr10. */
+ movi 15 << 12, r31
+ shlri r1, 12, r32
+ andc r1, r31, r1
+ fmov.dq dr8, r7
+ beqi/l r32, 8, tr1
+ fmov.dq dr10, r7
+ blink tr1, r63
+LOCAL(ct_r7_fpl): /* Copy r7 from a low FP register. */
+ shlri r1, 12 - 3, r34
+ andi r34, 3 << 3, r33
+ addi r33, LOCAL(ct_r7_fp_copy) - datalabel LOCAL(ct_r7_fp_base), r32
+LOCAL(ct_r7_fp_base):
+ ptrel/l r32, tr2
+ movi 7 << 12, r31
+ andc r1, r31, r1
+ blink tr2, r63
+LOCAL(ct_r7_fp_copy):
+ fmov.dq dr0, r7
+ blink tr1, r63
+ fmov.dq dr2, r7
+ blink tr1, r63
+ fmov.dq dr4, r7
+ blink tr1, r63
+ fmov.dq dr6, r7
+ blink tr1, r63
+LOCAL(ct_r8_fph): /* Copy r8 from a high FP register. */
+ /* It is either dr8 or dr10. */
+ movi 15 << 8, r31
+ andi r1, 1 << 8, r32
+ andc r1, r31, r1
+ fmov.dq dr8, r8
+ beq/l r32, r63, tr1
+ fmov.dq dr10, r8
+ blink tr1, r63
+LOCAL(ct_r8_fpl): /* Copy r8 from a low FP register. */
+ shlri r1, 8 - 3, r34
+ andi r34, 3 << 3, r33
+ addi r33, LOCAL(ct_r8_fp_copy) - datalabel LOCAL(ct_r8_fp_base), r32
+LOCAL(ct_r8_fp_base):
+ ptrel/l r32, tr2
+ movi 7 << 8, r31
+ andc r1, r31, r1
+ blink tr2, r63
+LOCAL(ct_r8_fp_copy):
+ fmov.dq dr0, r8
+ blink tr1, r63
+ fmov.dq dr2, r8
+ blink tr1, r63
+ fmov.dq dr4, r8
+ blink tr1, r63
+ fmov.dq dr6, r8
+ blink tr1, r63
+LOCAL(ct_r9_fph): /* Copy r9 from a high FP register. */
+ /* It is either dr8 or dr10. */
+ movi 15 << 4, r31
+ andi r1, 1 << 4, r32
+ andc r1, r31, r1
+ fmov.dq dr8, r9
+ beq/l r32, r63, tr1
+ fmov.dq dr10, r9
+ blink tr1, r63
+LOCAL(ct_r9_fpl): /* Copy r9 from a low FP register. */
+ shlri r1, 4 - 3, r34
+ andi r34, 3 << 3, r33
+ addi r33, LOCAL(ct_r9_fp_copy) - datalabel LOCAL(ct_r9_fp_base), r32
+LOCAL(ct_r9_fp_base):
+ ptrel/l r32, tr2
+ movi 7 << 4, r31
+ andc r1, r31, r1
+ blink tr2, r63
+LOCAL(ct_r9_fp_copy):
+ fmov.dq dr0, r9
+ blink tr1, r63
+ fmov.dq dr2, r9
+ blink tr1, r63
+ fmov.dq dr4, r9
+ blink tr1, r63
+ fmov.dq dr6, r9
+ blink tr1, r63
+LOCAL(ct_r2_ld): /* Copy r2 from a memory address. */
+ pt/l LOCAL(ct_r2_load), tr2
+ movi 3, r30
+ shlli r30, 29, r31
+ and r1, r31, r32
+ andc r1, r31, r1
+ beq/l r31, r32, tr2
+ addi.l r2, 8, r3
+ ldx.q r2, r63, r2
+ /* Fall through. */
+LOCAL(ct_r3_ld): /* Copy r3 from a memory address. */
+ pt/l LOCAL(ct_r3_load), tr2
+ movi 3, r30
+ shlli r30, 26, r31
+ and r1, r31, r32
+ andc r1, r31, r1
+ beq/l r31, r32, tr2
+ addi.l r3, 8, r4
+ ldx.q r3, r63, r3
+LOCAL(ct_r4_ld): /* Copy r4 from a memory address. */
+ pt/l LOCAL(ct_r4_load), tr2
+ movi 3, r30
+ shlli r30, 23, r31
+ and r1, r31, r32
+ andc r1, r31, r1
+ beq/l r31, r32, tr2
+ addi.l r4, 8, r5
+ ldx.q r4, r63, r4
+LOCAL(ct_r5_ld): /* Copy r5 from a memory address. */
+ pt/l LOCAL(ct_r5_load), tr2
+ movi 3, r30
+ shlli r30, 20, r31
+ and r1, r31, r32
+ andc r1, r31, r1
+ beq/l r31, r32, tr2
+ addi.l r5, 8, r6
+ ldx.q r5, r63, r5
+LOCAL(ct_r6_ld): /* Copy r6 from a memory address. */
+ pt/l LOCAL(ct_r6_load), tr2
+ movi 3 << 16, r31
+ and r1, r31, r32
+ andc r1, r31, r1
+ beq/l r31, r32, tr2
+ addi.l r6, 8, r7
+ ldx.q r6, r63, r6
+LOCAL(ct_r7_ld): /* Copy r7 from a memory address. */
+ pt/l LOCAL(ct_r7_load), tr2
+ movi 3 << 12, r31
+ and r1, r31, r32
+ andc r1, r31, r1
+ beq/l r31, r32, tr2
+ addi.l r7, 8, r8
+ ldx.q r7, r63, r7
+LOCAL(ct_r8_ld): /* Copy r8 from a memory address. */
+ pt/l LOCAL(ct_r8_load), tr2
+ movi 3 << 8, r31
+ and r1, r31, r32
+ andc r1, r31, r1
+ beq/l r31, r32, tr2
+ addi.l r8, 8, r9
+ ldx.q r8, r63, r8
+LOCAL(ct_r9_ld): /* Copy r9 from a memory address. */
+ pt/l LOCAL(ct_check_tramp), tr2
+ ldx.q r9, r63, r9
+ blink tr2, r63
+LOCAL(ct_r2_load):
+ ldx.q r2, r63, r2
+ blink tr1, r63
+LOCAL(ct_r3_load):
+ ldx.q r3, r63, r3
+ blink tr1, r63
+LOCAL(ct_r4_load):
+ ldx.q r4, r63, r4
+ blink tr1, r63
+LOCAL(ct_r5_load):
+ ldx.q r5, r63, r5
+ blink tr1, r63
+LOCAL(ct_r6_load):
+ ldx.q r6, r63, r6
+ blink tr1, r63
+LOCAL(ct_r7_load):
+ ldx.q r7, r63, r7
+ blink tr1, r63
+LOCAL(ct_r8_load):
+ ldx.q r8, r63, r8
+ blink tr1, r63
+LOCAL(ct_r2_pop): /* Pop r2 from the stack. */
+ movi 1, r30
+ ldx.q r15, r63, r2
+ shlli r30, 29, r31
+ addi.l r15, 8, r15
+ andc r1, r31, r1
+ blink tr1, r63
+LOCAL(ct_r3_pop): /* Pop r3 from the stack. */
+ movi 1, r30
+ ldx.q r15, r63, r3
+ shlli r30, 26, r31
+ addi.l r15, 8, r15
+ andc r1, r31, r1
+ blink tr1, r63
+LOCAL(ct_r4_pop): /* Pop r4 from the stack. */
+ movi 1, r30
+ ldx.q r15, r63, r4
+ shlli r30, 23, r31
+ addi.l r15, 8, r15
+ andc r1, r31, r1
+ blink tr1, r63
+LOCAL(ct_r5_pop): /* Pop r5 from the stack. */
+ movi 1, r30
+ ldx.q r15, r63, r5
+ shlli r30, 20, r31
+ addi.l r15, 8, r15
+ andc r1, r31, r1
+ blink tr1, r63
+LOCAL(ct_r6_pop): /* Pop r6 from the stack. */
+ movi 1, r30
+ ldx.q r15, r63, r6
+ shlli r30, 16, r31
+ addi.l r15, 8, r15
+ andc r1, r31, r1
+ blink tr1, r63
+LOCAL(ct_r7_pop): /* Pop r7 from the stack. */
+ ldx.q r15, r63, r7
+ movi 1 << 12, r31
+ addi.l r15, 8, r15
+ andc r1, r31, r1
+ blink tr1, r63
+LOCAL(ct_r8_pop): /* Pop r8 from the stack. */
+ ldx.q r15, r63, r8
+ movi 1 << 8, r31
+ addi.l r15, 8, r15
+ andc r1, r31, r1
+ blink tr1, r63
+LOCAL(ct_pop_seq): /* Pop a sequence of registers off the stack. */
+ andi r1, 7 << 1, r30
+ movi (LOCAL(ct_end_of_pop_seq) >> 16) & 65535, r32
+ shlli r30, 2, r31
+ shori LOCAL(ct_end_of_pop_seq) & 65535, r32
+ sub.l r32, r31, r33
+ ptabs/l r33, tr2
+ blink tr2, r63
+LOCAL(ct_start_of_pop_seq): /* Beginning of pop sequence. */
+ ldx.q r15, r63, r3
+ addi.l r15, 8, r15
+ ldx.q r15, r63, r4
+ addi.l r15, 8, r15
+ ldx.q r15, r63, r5
+ addi.l r15, 8, r15
+ ldx.q r15, r63, r6
+ addi.l r15, 8, r15
+ ldx.q r15, r63, r7
+ addi.l r15, 8, r15
+ ldx.q r15, r63, r8
+ addi.l r15, 8, r15
+LOCAL(ct_r9_pop): /* Pop r9 from the stack. */
+ ldx.q r15, r63, r9
+ addi.l r15, 8, r15
+LOCAL(ct_end_of_pop_seq): /* Label used to compute first pop instruction. */
+LOCAL(ct_check_tramp): /* Check whether we need a trampoline. */
+ pt/u LOCAL(ct_ret_wide), tr2
+ andi r1, 1, r1
+ bne/u r1, r63, tr2
+LOCAL(ct_call_func): /* Just branch to the function. */
+ blink tr0, r63
+LOCAL(ct_ret_wide): /* Call the function, so that we can unpack its
+ 64-bit return value. */
+ add.l r18, r63, r10
+ blink tr0, r18
+ ptabs r10, tr0
+#if __LITTLE_ENDIAN__
+ shari r2, 32, r3
+ add.l r2, r63, r2
+#else
+ add.l r2, r63, r3
+ shari r2, 32, r2
+#endif
+ blink tr0, r63
+
+ ENDFUNC(GLOBAL(GCC_shcompact_call_trampoline))
+#endif /* L_shcompact_call_trampoline */
+
+#ifdef L_shcompact_return_trampoline
+ /* This function does the converse of the code in `ret_wide'
+ above. It is tail-called by SHcompact functions returning
+ 64-bit non-floating-point values, to pack the 32-bit values in
+ r2 and r3 into r2. */
+
+ .mode SHmedia
+ .section .text..SHmedia32, "ax"
+ .align 2
+ .global GLOBAL(GCC_shcompact_return_trampoline)
+ HIDDEN_FUNC(GLOBAL(GCC_shcompact_return_trampoline))
+GLOBAL(GCC_shcompact_return_trampoline):
+ ptabs/l r18, tr0
+#if __LITTLE_ENDIAN__
+ addz.l r2, r63, r2
+ shlli r3, 32, r3
+#else
+ addz.l r3, r63, r3
+ shlli r2, 32, r2
+#endif
+ or r3, r2, r2
+ blink tr0, r63
+
+ ENDFUNC(GLOBAL(GCC_shcompact_return_trampoline))
+#endif /* L_shcompact_return_trampoline */
+
+#ifdef L_shcompact_incoming_args
+ .section .rodata
+ .align 1
+LOCAL(ia_main_table):
+.word 1 /* Invalid, just loop */
+.word LOCAL(ia_r2_ld) - datalabel LOCAL(ia_main_label)
+.word LOCAL(ia_r2_push) - datalabel LOCAL(ia_main_label)
+.word 1 /* Invalid, just loop */
+.word LOCAL(ia_r3_ld) - datalabel LOCAL(ia_main_label)
+.word LOCAL(ia_r3_push) - datalabel LOCAL(ia_main_label)
+.word 1 /* Invalid, just loop */
+.word LOCAL(ia_r4_ld) - datalabel LOCAL(ia_main_label)
+.word LOCAL(ia_r4_push) - datalabel LOCAL(ia_main_label)
+.word 1 /* Invalid, just loop */
+.word LOCAL(ia_r5_ld) - datalabel LOCAL(ia_main_label)
+.word LOCAL(ia_r5_push) - datalabel LOCAL(ia_main_label)
+.word 1 /* Invalid, just loop */
+.word 1 /* Invalid, just loop */
+.word LOCAL(ia_r6_ld) - datalabel LOCAL(ia_main_label)
+.word LOCAL(ia_r6_push) - datalabel LOCAL(ia_main_label)
+.word 1 /* Invalid, just loop */
+.word 1 /* Invalid, just loop */
+.word LOCAL(ia_r7_ld) - datalabel LOCAL(ia_main_label)
+.word LOCAL(ia_r7_push) - datalabel LOCAL(ia_main_label)
+.word 1 /* Invalid, just loop */
+.word 1 /* Invalid, just loop */
+.word LOCAL(ia_r8_ld) - datalabel LOCAL(ia_main_label)
+.word LOCAL(ia_r8_push) - datalabel LOCAL(ia_main_label)
+.word 1 /* Invalid, just loop */
+.word 1 /* Invalid, just loop */
+.word LOCAL(ia_r9_ld) - datalabel LOCAL(ia_main_label)
+.word LOCAL(ia_r9_push) - datalabel LOCAL(ia_main_label)
+.word LOCAL(ia_push_seq) - datalabel LOCAL(ia_main_label)
+.word LOCAL(ia_push_seq) - datalabel LOCAL(ia_main_label)
+.word LOCAL(ia_r9_push) - datalabel LOCAL(ia_main_label)
+.word LOCAL(ia_return) - datalabel LOCAL(ia_main_label)
+.word LOCAL(ia_return) - datalabel LOCAL(ia_main_label)
+ .mode SHmedia
+ .section .text..SHmedia32, "ax"
+ .align 2
+
+ /* This function stores 64-bit general-purpose registers back in
+ the stack, and loads the address in which each register
+ was stored into itself. The lower 32 bits of r17 hold the address
+ to begin storing, and the upper 32 bits of r17 hold the cookie.
+ Its execution time is linear on the
+ number of registers that actually have to be copied, and it is
+ optimized for structures larger than 64 bits, as opposed to
+ individual `long long' arguments. See sh.h for details on the
+ actual bit pattern. */
+
+ .global GLOBAL(GCC_shcompact_incoming_args)
+ FUNC(GLOBAL(GCC_shcompact_incoming_args))
+GLOBAL(GCC_shcompact_incoming_args):
+ ptabs/l r18, tr0 /* Prepare to return. */
+ shlri r17, 32, r0 /* Load the cookie. */
+ movi ((datalabel LOCAL(ia_main_table) - 31 * 2) >> 16) & 65535, r43
+ pt/l LOCAL(ia_loop), tr1
+ add.l r17, r63, r17
+ shori ((datalabel LOCAL(ia_main_table) - 31 * 2)) & 65535, r43
+LOCAL(ia_loop):
+ nsb r0, r36
+ shlli r36, 1, r37
+ ldx.w r43, r37, r38
+LOCAL(ia_main_label):
+ ptrel/l r38, tr2
+ blink tr2, r63
+LOCAL(ia_r2_ld): /* Store r2 and load its address. */
+ movi 3, r38
+ shlli r38, 29, r39
+ and r0, r39, r40
+ andc r0, r39, r0
+ stx.q r17, r63, r2
+ add.l r17, r63, r2
+ addi.l r17, 8, r17
+ beq/u r39, r40, tr1
+LOCAL(ia_r3_ld): /* Store r3 and load its address. */
+ movi 3, r38
+ shlli r38, 26, r39
+ and r0, r39, r40
+ andc r0, r39, r0
+ stx.q r17, r63, r3
+ add.l r17, r63, r3
+ addi.l r17, 8, r17
+ beq/u r39, r40, tr1
+LOCAL(ia_r4_ld): /* Store r4 and load its address. */
+ movi 3, r38
+ shlli r38, 23, r39
+ and r0, r39, r40
+ andc r0, r39, r0
+ stx.q r17, r63, r4
+ add.l r17, r63, r4
+ addi.l r17, 8, r17
+ beq/u r39, r40, tr1
+LOCAL(ia_r5_ld): /* Store r5 and load its address. */
+ movi 3, r38
+ shlli r38, 20, r39
+ and r0, r39, r40
+ andc r0, r39, r0
+ stx.q r17, r63, r5
+ add.l r17, r63, r5
+ addi.l r17, 8, r17
+ beq/u r39, r40, tr1
+LOCAL(ia_r6_ld): /* Store r6 and load its address. */
+ movi 3, r38
+ shlli r38, 16, r39
+ and r0, r39, r40
+ andc r0, r39, r0
+ stx.q r17, r63, r6
+ add.l r17, r63, r6
+ addi.l r17, 8, r17
+ beq/u r39, r40, tr1
+LOCAL(ia_r7_ld): /* Store r7 and load its address. */
+ movi 3 << 12, r39
+ and r0, r39, r40
+ andc r0, r39, r0
+ stx.q r17, r63, r7
+ add.l r17, r63, r7
+ addi.l r17, 8, r17
+ beq/u r39, r40, tr1
+LOCAL(ia_r8_ld): /* Store r8 and load its address. */
+ movi 3 << 8, r39
+ and r0, r39, r40
+ andc r0, r39, r0
+ stx.q r17, r63, r8
+ add.l r17, r63, r8
+ addi.l r17, 8, r17
+ beq/u r39, r40, tr1
+LOCAL(ia_r9_ld): /* Store r9 and load its address. */
+ stx.q r17, r63, r9
+ add.l r17, r63, r9
+ blink tr0, r63
+LOCAL(ia_r2_push): /* Push r2 onto the stack. */
+ movi 1, r38
+ shlli r38, 29, r39
+ andc r0, r39, r0
+ stx.q r17, r63, r2
+ addi.l r17, 8, r17
+ blink tr1, r63
+LOCAL(ia_r3_push): /* Push r3 onto the stack. */
+ movi 1, r38
+ shlli r38, 26, r39
+ andc r0, r39, r0
+ stx.q r17, r63, r3
+ addi.l r17, 8, r17
+ blink tr1, r63
+LOCAL(ia_r4_push): /* Push r4 onto the stack. */
+ movi 1, r38
+ shlli r38, 23, r39
+ andc r0, r39, r0
+ stx.q r17, r63, r4
+ addi.l r17, 8, r17
+ blink tr1, r63
+LOCAL(ia_r5_push): /* Push r5 onto the stack. */
+ movi 1, r38
+ shlli r38, 20, r39
+ andc r0, r39, r0
+ stx.q r17, r63, r5
+ addi.l r17, 8, r17
+ blink tr1, r63
+LOCAL(ia_r6_push): /* Push r6 onto the stack. */
+ movi 1, r38
+ shlli r38, 16, r39
+ andc r0, r39, r0
+ stx.q r17, r63, r6
+ addi.l r17, 8, r17
+ blink tr1, r63
+LOCAL(ia_r7_push): /* Push r7 onto the stack. */
+ movi 1 << 12, r39
+ andc r0, r39, r0
+ stx.q r17, r63, r7
+ addi.l r17, 8, r17
+ blink tr1, r63
+LOCAL(ia_r8_push): /* Push r8 onto the stack. */
+ movi 1 << 8, r39
+ andc r0, r39, r0
+ stx.q r17, r63, r8
+ addi.l r17, 8, r17
+ blink tr1, r63
+LOCAL(ia_push_seq): /* Push a sequence of registers onto the stack. */
+ andi r0, 7 << 1, r38
+ movi (LOCAL(ia_end_of_push_seq) >> 16) & 65535, r40
+ shlli r38, 2, r39
+ shori LOCAL(ia_end_of_push_seq) & 65535, r40
+ sub.l r40, r39, r41
+ ptabs/l r41, tr2
+ blink tr2, r63
+LOCAL(ia_stack_of_push_seq): /* Beginning of push sequence. */
+ stx.q r17, r63, r3
+ addi.l r17, 8, r17
+ stx.q r17, r63, r4
+ addi.l r17, 8, r17
+ stx.q r17, r63, r5
+ addi.l r17, 8, r17
+ stx.q r17, r63, r6
+ addi.l r17, 8, r17
+ stx.q r17, r63, r7
+ addi.l r17, 8, r17
+ stx.q r17, r63, r8
+ addi.l r17, 8, r17
+LOCAL(ia_r9_push): /* Push r9 onto the stack. */
+ stx.q r17, r63, r9
+LOCAL(ia_return): /* Return. */
+ blink tr0, r63
+LOCAL(ia_end_of_push_seq): /* Label used to compute the first push instruction. */
+ ENDFUNC(GLOBAL(GCC_shcompact_incoming_args))
+#endif /* L_shcompact_incoming_args */
+#endif
+#if __SH5__
+#ifdef L_nested_trampoline
+#if __SH5__ == 32
+ .section .text..SHmedia32,"ax"
+#else
+ .text
+#endif
+ .align 3 /* It is copied in units of 8 bytes in SHmedia mode. */
+ .global GLOBAL(GCC_nested_trampoline)
+ HIDDEN_FUNC(GLOBAL(GCC_nested_trampoline))
+GLOBAL(GCC_nested_trampoline):
+ .mode SHmedia
+ ptrel/u r63, tr0
+ gettr tr0, r0
+#if __SH5__ == 64
+ ld.q r0, 24, r1
+#else
+ ld.l r0, 24, r1
+#endif
+ ptabs/l r1, tr1
+#if __SH5__ == 64
+ ld.q r0, 32, r1
+#else
+ ld.l r0, 28, r1
+#endif
+ blink tr1, r63
+
+ ENDFUNC(GLOBAL(GCC_nested_trampoline))
+#endif /* L_nested_trampoline */
+#endif /* __SH5__ */
+#if __SH5__ == 32
+#ifdef L_push_pop_shmedia_regs
+ .section .text..SHmedia32,"ax"
+ .mode SHmedia
+ .align 2
+#ifndef __SH4_NOFPU__
+ .global GLOBAL(GCC_push_shmedia_regs)
+ FUNC(GLOBAL(GCC_push_shmedia_regs))
+GLOBAL(GCC_push_shmedia_regs):
+ addi.l r15, -14*8, r15
+ fst.d r15, 13*8, dr62
+ fst.d r15, 12*8, dr60
+ fst.d r15, 11*8, dr58
+ fst.d r15, 10*8, dr56
+ fst.d r15, 9*8, dr54
+ fst.d r15, 8*8, dr52
+ fst.d r15, 7*8, dr50
+ fst.d r15, 6*8, dr48
+ fst.d r15, 5*8, dr46
+ fst.d r15, 4*8, dr44
+ fst.d r15, 3*8, dr42
+ fst.d r15, 2*8, dr40
+ fst.d r15, 1*8, dr38
+ fst.d r15, 0*8, dr36
+#else /* ! __SH4_NOFPU__ */
+ .global GLOBAL(GCC_push_shmedia_regs_nofpu)
+ FUNC(GLOBAL(GCC_push_shmedia_regs_nofpu))
+GLOBAL(GCC_push_shmedia_regs_nofpu):
+#endif /* ! __SH4_NOFPU__ */
+ ptabs/l r18, tr0
+ addi.l r15, -27*8, r15
+ gettr tr7, r62
+ gettr tr6, r61
+ gettr tr5, r60
+ st.q r15, 26*8, r62
+ st.q r15, 25*8, r61
+ st.q r15, 24*8, r60
+ st.q r15, 23*8, r59
+ st.q r15, 22*8, r58
+ st.q r15, 21*8, r57
+ st.q r15, 20*8, r56
+ st.q r15, 19*8, r55
+ st.q r15, 18*8, r54
+ st.q r15, 17*8, r53
+ st.q r15, 16*8, r52
+ st.q r15, 15*8, r51
+ st.q r15, 14*8, r50
+ st.q r15, 13*8, r49
+ st.q r15, 12*8, r48
+ st.q r15, 11*8, r47
+ st.q r15, 10*8, r46
+ st.q r15, 9*8, r45
+ st.q r15, 8*8, r44
+ st.q r15, 7*8, r35
+ st.q r15, 6*8, r34
+ st.q r15, 5*8, r33
+ st.q r15, 4*8, r32
+ st.q r15, 3*8, r31
+ st.q r15, 2*8, r30
+ st.q r15, 1*8, r29
+ st.q r15, 0*8, r28
+ blink tr0, r63
+#ifndef __SH4_NOFPU__
+ ENDFUNC(GLOBAL(GCC_push_shmedia_regs))
+#else
+ ENDFUNC(GLOBAL(GCC_push_shmedia_regs_nofpu))
+#endif
+#ifndef __SH4_NOFPU__
+ .global GLOBAL(GCC_pop_shmedia_regs)
+ FUNC(GLOBAL(GCC_pop_shmedia_regs))
+GLOBAL(GCC_pop_shmedia_regs):
+ pt .L0, tr1
+ movi 41*8, r0
+ fld.d r15, 40*8, dr62
+ fld.d r15, 39*8, dr60
+ fld.d r15, 38*8, dr58
+ fld.d r15, 37*8, dr56
+ fld.d r15, 36*8, dr54
+ fld.d r15, 35*8, dr52
+ fld.d r15, 34*8, dr50
+ fld.d r15, 33*8, dr48
+ fld.d r15, 32*8, dr46
+ fld.d r15, 31*8, dr44
+ fld.d r15, 30*8, dr42
+ fld.d r15, 29*8, dr40
+ fld.d r15, 28*8, dr38
+ fld.d r15, 27*8, dr36
+ blink tr1, r63
+#else /* ! __SH4_NOFPU__ */
+ .global GLOBAL(GCC_pop_shmedia_regs_nofpu)
+ FUNC(GLOBAL(GCC_pop_shmedia_regs_nofpu))
+GLOBAL(GCC_pop_shmedia_regs_nofpu):
+#endif /* ! __SH4_NOFPU__ */
+ movi 27*8, r0
+.L0:
+ ptabs r18, tr0
+ ld.q r15, 26*8, r62
+ ld.q r15, 25*8, r61
+ ld.q r15, 24*8, r60
+ ptabs r62, tr7
+ ptabs r61, tr6
+ ptabs r60, tr5
+ ld.q r15, 23*8, r59
+ ld.q r15, 22*8, r58
+ ld.q r15, 21*8, r57
+ ld.q r15, 20*8, r56
+ ld.q r15, 19*8, r55
+ ld.q r15, 18*8, r54
+ ld.q r15, 17*8, r53
+ ld.q r15, 16*8, r52
+ ld.q r15, 15*8, r51
+ ld.q r15, 14*8, r50
+ ld.q r15, 13*8, r49
+ ld.q r15, 12*8, r48
+ ld.q r15, 11*8, r47
+ ld.q r15, 10*8, r46
+ ld.q r15, 9*8, r45
+ ld.q r15, 8*8, r44
+ ld.q r15, 7*8, r35
+ ld.q r15, 6*8, r34
+ ld.q r15, 5*8, r33
+ ld.q r15, 4*8, r32
+ ld.q r15, 3*8, r31
+ ld.q r15, 2*8, r30
+ ld.q r15, 1*8, r29
+ ld.q r15, 0*8, r28
+ add.l r15, r0, r15
+ blink tr0, r63
+
+#ifndef __SH4_NOFPU__
+ ENDFUNC(GLOBAL(GCC_pop_shmedia_regs))
+#else
+ ENDFUNC(GLOBAL(GCC_pop_shmedia_regs_nofpu))
+#endif
+#endif /* __SH5__ == 32 */
+#endif /* L_push_pop_shmedia_regs */
+
+#ifdef L_div_table
+#if __SH5__
+#if defined(__pic__) && defined(__SHMEDIA__)
+ .global GLOBAL(sdivsi3)
+ FUNC(GLOBAL(sdivsi3))
+#if __SH5__ == 32
+ .section .text..SHmedia32,"ax"
+#else
+ .text
+#endif
+#if 0
+/* ??? FIXME: Presumably due to a linker bug, exporting data symbols
+ in a text section does not work (at least for shared libraries):
+ the linker sets the LSB of the address as if this was SHmedia code. */
+#define TEXT_DATA_BUG
+#endif
+ .align 2
+ // inputs: r4,r5
+ // clobbered: r1,r18,r19,r20,r21,r25,tr0
+ // result in r0
+ .global GLOBAL(sdivsi3)
+GLOBAL(sdivsi3):
+#ifdef TEXT_DATA_BUG
+ ptb datalabel Local_div_table,tr0
+#else
+ ptb GLOBAL(div_table_internal),tr0
+#endif
+ nsb r5, r1
+ shlld r5, r1, r25 // normalize; [-2 ..1, 1..2) in s2.62
+ shari r25, 58, r21 // extract 5(6) bit index (s2.4 with hole -1..1)
+ /* bubble */
+ gettr tr0,r20
+ ldx.ub r20, r21, r19 // u0.8
+ shari r25, 32, r25 // normalize to s2.30
+ shlli r21, 1, r21
+ muls.l r25, r19, r19 // s2.38
+ ldx.w r20, r21, r21 // s2.14
+ ptabs r18, tr0
+ shari r19, 24, r19 // truncate to s2.14
+ sub r21, r19, r19 // some 11 bit inverse in s1.14
+ muls.l r19, r19, r21 // u0.28
+ sub r63, r1, r1
+ addi r1, 92, r1
+ muls.l r25, r21, r18 // s2.58
+ shlli r19, 45, r19 // multiply by two and convert to s2.58
+ /* bubble */
+ sub r19, r18, r18
+ shari r18, 28, r18 // some 22 bit inverse in s1.30
+ muls.l r18, r25, r0 // s2.60
+ muls.l r18, r4, r25 // s32.30
+ /* bubble */
+ shari r0, 16, r19 // s-16.44
+ muls.l r19, r18, r19 // s-16.74
+ shari r25, 63, r0
+ shari r4, 14, r18 // s19.-14
+ shari r19, 30, r19 // s-16.44
+ muls.l r19, r18, r19 // s15.30
+ xor r21, r0, r21 // You could also use the constant 1 << 27.
+ add r21, r25, r21
+ sub r21, r19, r21
+ shard r21, r1, r21
+ sub r21, r0, r0
+ blink tr0, r63
+ ENDFUNC(GLOBAL(sdivsi3))
+/* This table has been generated by divtab.c .
+Defects for bias -330:
+ Max defect: 6.081536e-07 at -1.000000e+00
+ Min defect: 2.849516e-08 at 1.030651e+00
+ Max 2nd step defect: 9.606539e-12 at -1.000000e+00
+ Min 2nd step defect: 0.000000e+00 at 0.000000e+00
+ Defect at 1: 1.238659e-07
+ Defect at -2: 1.061708e-07 */
+#else /* ! __pic__ || ! __SHMEDIA__ */
+ .section .rodata
+#endif /* __pic__ */
+#if defined(TEXT_DATA_BUG) && defined(__pic__) && defined(__SHMEDIA__)
+ .balign 2
+ .type Local_div_table,@object
+ .size Local_div_table,128
+/* negative division constants */
+ .word -16638
+ .word -17135
+ .word -17737
+ .word -18433
+ .word -19103
+ .word -19751
+ .word -20583
+ .word -21383
+ .word -22343
+ .word -23353
+ .word -24407
+ .word -25582
+ .word -26863
+ .word -28382
+ .word -29965
+ .word -31800
+/* negative division factors */
+ .byte 66
+ .byte 70
+ .byte 75
+ .byte 81
+ .byte 87
+ .byte 93
+ .byte 101
+ .byte 109
+ .byte 119
+ .byte 130
+ .byte 142
+ .byte 156
+ .byte 172
+ .byte 192
+ .byte 214
+ .byte 241
+ .skip 16
+Local_div_table:
+ .skip 16
+/* positive division factors */
+ .byte 241
+ .byte 214
+ .byte 192
+ .byte 172
+ .byte 156
+ .byte 142
+ .byte 130
+ .byte 119
+ .byte 109
+ .byte 101
+ .byte 93
+ .byte 87
+ .byte 81
+ .byte 75
+ .byte 70
+ .byte 66
+/* positive division constants */
+ .word 31801
+ .word 29966
+ .word 28383
+ .word 26864
+ .word 25583
+ .word 24408
+ .word 23354
+ .word 22344
+ .word 21384
+ .word 20584
+ .word 19752
+ .word 19104
+ .word 18434
+ .word 17738
+ .word 17136
+ .word 16639
+ .section .rodata
+#endif /* TEXT_DATA_BUG */
+ .balign 2
+ .type GLOBAL(div_table),@object
+ .size GLOBAL(div_table),128
+/* negative division constants */
+ .word -16638
+ .word -17135
+ .word -17737
+ .word -18433
+ .word -19103
+ .word -19751
+ .word -20583
+ .word -21383
+ .word -22343
+ .word -23353
+ .word -24407
+ .word -25582
+ .word -26863
+ .word -28382
+ .word -29965
+ .word -31800
+/* negative division factors */
+ .byte 66
+ .byte 70
+ .byte 75
+ .byte 81
+ .byte 87
+ .byte 93
+ .byte 101
+ .byte 109
+ .byte 119
+ .byte 130
+ .byte 142
+ .byte 156
+ .byte 172
+ .byte 192
+ .byte 214
+ .byte 241
+ .skip 16
+ .global GLOBAL(div_table)
+GLOBAL(div_table):
+ HIDDEN_ALIAS(div_table_internal,div_table)
+ .skip 16
+/* positive division factors */
+ .byte 241
+ .byte 214
+ .byte 192
+ .byte 172
+ .byte 156
+ .byte 142
+ .byte 130
+ .byte 119
+ .byte 109
+ .byte 101
+ .byte 93
+ .byte 87
+ .byte 81
+ .byte 75
+ .byte 70
+ .byte 66
+/* positive division constants */
+ .word 31801
+ .word 29966
+ .word 28383
+ .word 26864
+ .word 25583
+ .word 24408
+ .word 23354
+ .word 22344
+ .word 21384
+ .word 20584
+ .word 19752
+ .word 19104
+ .word 18434
+ .word 17738
+ .word 17136
+ .word 16639
+
+#elif defined (__SH3__) || defined (__SH3E__) || defined (__SH4__) || defined (__SH4_SINGLE__) || defined (__SH4_SINGLE_ONLY__) || defined (__SH4_NOFPU__)
+/* This code used shld, thus is not suitable for SH1 / SH2. */
+
+/* Signed / unsigned division without use of FPU, optimized for SH4.
+ Uses a lookup table for divisors in the range -128 .. +128, and
+ div1 with case distinction for larger divisors in three more ranges.
+ The code is lumped together with the table to allow the use of mova. */
+#ifdef __LITTLE_ENDIAN__
+#define L_LSB 0
+#define L_LSWMSB 1
+#define L_MSWLSB 2
+#else
+#define L_LSB 3
+#define L_LSWMSB 2
+#define L_MSWLSB 1
+#endif
+
+ .balign 4
+ .global GLOBAL(udivsi3_i4i)
+ FUNC(GLOBAL(udivsi3_i4i))
+GLOBAL(udivsi3_i4i):
+ mov.w LOCAL(c128_w), r1
+ div0u
+ mov r4,r0
+ shlr8 r0
+ cmp/hi r1,r5
+ extu.w r5,r1
+ bf LOCAL(udiv_le128)
+ cmp/eq r5,r1
+ bf LOCAL(udiv_ge64k)
+ shlr r0
+ mov r5,r1
+ shll16 r5
+ mov.l r4,@-r15
+ div1 r5,r0
+ mov.l r1,@-r15
+ div1 r5,r0
+ div1 r5,r0
+ bra LOCAL(udiv_25)
+ div1 r5,r0
+
+LOCAL(div_le128):
+ mova LOCAL(div_table_ix),r0
+ bra LOCAL(div_le128_2)
+ mov.b @(r0,r5),r1
+LOCAL(udiv_le128):
+ mov.l r4,@-r15
+ mova LOCAL(div_table_ix),r0
+ mov.b @(r0,r5),r1
+ mov.l r5,@-r15
+LOCAL(div_le128_2):
+ mova LOCAL(div_table_inv),r0
+ mov.l @(r0,r1),r1
+ mov r5,r0
+ tst #0xfe,r0
+ mova LOCAL(div_table_clz),r0
+ dmulu.l r1,r4
+ mov.b @(r0,r5),r1
+ bt/s LOCAL(div_by_1)
+ mov r4,r0
+ mov.l @r15+,r5
+ sts mach,r0
+ /* clrt */
+ addc r4,r0
+ mov.l @r15+,r4
+ rotcr r0
+ rts
+ shld r1,r0
+
+LOCAL(div_by_1_neg):
+ neg r4,r0
+LOCAL(div_by_1):
+ mov.l @r15+,r5
+ rts
+ mov.l @r15+,r4
+
+LOCAL(div_ge64k):
+ bt/s LOCAL(div_r8)
+ div0u
+ shll8 r5
+ bra LOCAL(div_ge64k_2)
+ div1 r5,r0
+LOCAL(udiv_ge64k):
+ cmp/hi r0,r5
+ mov r5,r1
+ bt LOCAL(udiv_r8)
+ shll8 r5
+ mov.l r4,@-r15
+ div1 r5,r0
+ mov.l r1,@-r15
+LOCAL(div_ge64k_2):
+ div1 r5,r0
+ mov.l LOCAL(zero_l),r1
+ .rept 4
+ div1 r5,r0
+ .endr
+ mov.l r1,@-r15
+ div1 r5,r0
+ mov.w LOCAL(m256_w),r1
+ div1 r5,r0
+ mov.b r0,@(L_LSWMSB,r15)
+ xor r4,r0
+ and r1,r0
+ bra LOCAL(div_ge64k_end)
+ xor r4,r0
+
+LOCAL(div_r8):
+ shll16 r4
+ bra LOCAL(div_r8_2)
+ shll8 r4
+LOCAL(udiv_r8):
+ mov.l r4,@-r15
+ shll16 r4
+ clrt
+ shll8 r4
+ mov.l r5,@-r15
+LOCAL(div_r8_2):
+ rotcl r4
+ mov r0,r1
+ div1 r5,r1
+ mov r4,r0
+ rotcl r0
+ mov r5,r4
+ div1 r5,r1
+ .rept 5
+ rotcl r0; div1 r5,r1
+ .endr
+ rotcl r0
+ mov.l @r15+,r5
+ div1 r4,r1
+ mov.l @r15+,r4
+ rts
+ rotcl r0
+
+ ENDFUNC(GLOBAL(udivsi3_i4i))
+
+ .global GLOBAL(sdivsi3_i4i)
+ FUNC(GLOBAL(sdivsi3_i4i))
+ /* This is link-compatible with a GLOBAL(sdivsi3) call,
+ but we effectively clobber only r1. */
+GLOBAL(sdivsi3_i4i):
+ mov.l r4,@-r15
+ cmp/pz r5
+ mov.w LOCAL(c128_w), r1
+ bt/s LOCAL(pos_divisor)
+ cmp/pz r4
+ mov.l r5,@-r15
+ neg r5,r5
+ bt/s LOCAL(neg_result)
+ cmp/hi r1,r5
+ neg r4,r4
+LOCAL(pos_result):
+ extu.w r5,r0
+ bf LOCAL(div_le128)
+ cmp/eq r5,r0
+ mov r4,r0
+ shlr8 r0
+ bf/s LOCAL(div_ge64k)
+ cmp/hi r0,r5
+ div0u
+ shll16 r5
+ div1 r5,r0
+ div1 r5,r0
+ div1 r5,r0
+LOCAL(udiv_25):
+ mov.l LOCAL(zero_l),r1
+ div1 r5,r0
+ div1 r5,r0
+ mov.l r1,@-r15
+ .rept 3
+ div1 r5,r0
+ .endr
+ mov.b r0,@(L_MSWLSB,r15)
+ xtrct r4,r0
+ swap.w r0,r0
+ .rept 8
+ div1 r5,r0
+ .endr
+ mov.b r0,@(L_LSWMSB,r15)
+LOCAL(div_ge64k_end):
+ .rept 8
+ div1 r5,r0
+ .endr
+ mov.l @r15+,r4 ! zero-extension and swap using LS unit.
+ extu.b r0,r0
+ mov.l @r15+,r5
+ or r4,r0
+ mov.l @r15+,r4
+ rts
+ rotcl r0
+
+LOCAL(div_le128_neg):
+ tst #0xfe,r0
+ mova LOCAL(div_table_ix),r0
+ mov.b @(r0,r5),r1
+ mova LOCAL(div_table_inv),r0
+ bt/s LOCAL(div_by_1_neg)
+ mov.l @(r0,r1),r1
+ mova LOCAL(div_table_clz),r0
+ dmulu.l r1,r4
+ mov.b @(r0,r5),r1
+ mov.l @r15+,r5
+ sts mach,r0
+ /* clrt */
+ addc r4,r0
+ mov.l @r15+,r4
+ rotcr r0
+ shld r1,r0
+ rts
+ neg r0,r0
+
+LOCAL(pos_divisor):
+ mov.l r5,@-r15
+ bt/s LOCAL(pos_result)
+ cmp/hi r1,r5
+ neg r4,r4
+LOCAL(neg_result):
+ extu.w r5,r0
+ bf LOCAL(div_le128_neg)
+ cmp/eq r5,r0
+ mov r4,r0
+ shlr8 r0
+ bf/s LOCAL(div_ge64k_neg)
+ cmp/hi r0,r5
+ div0u
+ mov.l LOCAL(zero_l),r1
+ shll16 r5
+ div1 r5,r0
+ mov.l r1,@-r15
+ .rept 7
+ div1 r5,r0
+ .endr
+ mov.b r0,@(L_MSWLSB,r15)
+ xtrct r4,r0
+ swap.w r0,r0
+ .rept 8
+ div1 r5,r0
+ .endr
+ mov.b r0,@(L_LSWMSB,r15)
+LOCAL(div_ge64k_neg_end):
+ .rept 8
+ div1 r5,r0
+ .endr
+ mov.l @r15+,r4 ! zero-extension and swap using LS unit.
+ extu.b r0,r1
+ mov.l @r15+,r5
+ or r4,r1
+LOCAL(div_r8_neg_end):
+ mov.l @r15+,r4
+ rotcl r1
+ rts
+ neg r1,r0
+
+LOCAL(div_ge64k_neg):
+ bt/s LOCAL(div_r8_neg)
+ div0u
+ shll8 r5
+ mov.l LOCAL(zero_l),r1
+ .rept 6
+ div1 r5,r0
+ .endr
+ mov.l r1,@-r15
+ div1 r5,r0
+ mov.w LOCAL(m256_w),r1
+ div1 r5,r0
+ mov.b r0,@(L_LSWMSB,r15)
+ xor r4,r0
+ and r1,r0
+ bra LOCAL(div_ge64k_neg_end)
+ xor r4,r0
+
+LOCAL(c128_w):
+ .word 128
+
+LOCAL(div_r8_neg):
+ clrt
+ shll16 r4
+ mov r4,r1
+ shll8 r1
+ mov r5,r4
+ .rept 7
+ rotcl r1; div1 r5,r0
+ .endr
+ mov.l @r15+,r5
+ rotcl r1
+ bra LOCAL(div_r8_neg_end)
+ div1 r4,r0
+
+LOCAL(m256_w):
+ .word 0xff00
+/* This table has been generated by divtab-sh4.c. */
+ .balign 4
+LOCAL(div_table_clz):
+ .byte 0
+ .byte 1
+ .byte 0
+ .byte -1
+ .byte -1
+ .byte -2
+ .byte -2
+ .byte -2
+ .byte -2
+ .byte -3
+ .byte -3
+ .byte -3
+ .byte -3
+ .byte -3
+ .byte -3
+ .byte -3
+ .byte -3
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -4
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -5
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+ .byte -6
+/* Lookup table translating positive divisor to index into table of
+ normalized inverse. N.B. the '0' entry is also the last entry of the
+ previous table, and causes an unaligned access for division by zero. */
+LOCAL(div_table_ix):
+ .byte -6
+ .byte -128
+ .byte -128
+ .byte 0
+ .byte -128
+ .byte -64
+ .byte 0
+ .byte 64
+ .byte -128
+ .byte -96
+ .byte -64
+ .byte -32
+ .byte 0
+ .byte 32
+ .byte 64
+ .byte 96
+ .byte -128
+ .byte -112
+ .byte -96
+ .byte -80
+ .byte -64
+ .byte -48
+ .byte -32
+ .byte -16
+ .byte 0
+ .byte 16
+ .byte 32
+ .byte 48
+ .byte 64
+ .byte 80
+ .byte 96
+ .byte 112
+ .byte -128
+ .byte -120
+ .byte -112
+ .byte -104
+ .byte -96
+ .byte -88
+ .byte -80
+ .byte -72
+ .byte -64
+ .byte -56
+ .byte -48
+ .byte -40
+ .byte -32
+ .byte -24
+ .byte -16
+ .byte -8
+ .byte 0
+ .byte 8
+ .byte 16
+ .byte 24
+ .byte 32
+ .byte 40
+ .byte 48
+ .byte 56
+ .byte 64
+ .byte 72
+ .byte 80
+ .byte 88
+ .byte 96
+ .byte 104
+ .byte 112
+ .byte 120
+ .byte -128
+ .byte -124
+ .byte -120
+ .byte -116
+ .byte -112
+ .byte -108
+ .byte -104
+ .byte -100
+ .byte -96
+ .byte -92
+ .byte -88
+ .byte -84
+ .byte -80
+ .byte -76
+ .byte -72
+ .byte -68
+ .byte -64
+ .byte -60
+ .byte -56
+ .byte -52
+ .byte -48
+ .byte -44
+ .byte -40
+ .byte -36
+ .byte -32
+ .byte -28
+ .byte -24
+ .byte -20
+ .byte -16
+ .byte -12
+ .byte -8
+ .byte -4
+ .byte 0
+ .byte 4
+ .byte 8
+ .byte 12
+ .byte 16
+ .byte 20
+ .byte 24
+ .byte 28
+ .byte 32
+ .byte 36
+ .byte 40
+ .byte 44
+ .byte 48
+ .byte 52
+ .byte 56
+ .byte 60
+ .byte 64
+ .byte 68
+ .byte 72
+ .byte 76
+ .byte 80
+ .byte 84
+ .byte 88
+ .byte 92
+ .byte 96
+ .byte 100
+ .byte 104
+ .byte 108
+ .byte 112
+ .byte 116
+ .byte 120
+ .byte 124
+ .byte -128
+/* 1/64 .. 1/127, normalized. There is an implicit leading 1 in bit 32. */
+ .balign 4
+LOCAL(zero_l):
+ .long 0x0
+ .long 0xF81F81F9
+ .long 0xF07C1F08
+ .long 0xE9131AC0
+ .long 0xE1E1E1E2
+ .long 0xDAE6076C
+ .long 0xD41D41D5
+ .long 0xCD856891
+ .long 0xC71C71C8
+ .long 0xC0E07039
+ .long 0xBACF914D
+ .long 0xB4E81B4F
+ .long 0xAF286BCB
+ .long 0xA98EF607
+ .long 0xA41A41A5
+ .long 0x9EC8E952
+ .long 0x9999999A
+ .long 0x948B0FCE
+ .long 0x8F9C18FA
+ .long 0x8ACB90F7
+ .long 0x86186187
+ .long 0x81818182
+ .long 0x7D05F418
+ .long 0x78A4C818
+ .long 0x745D1746
+ .long 0x702E05C1
+ .long 0x6C16C16D
+ .long 0x68168169
+ .long 0x642C8591
+ .long 0x60581606
+ .long 0x5C9882BA
+ .long 0x58ED2309
+LOCAL(div_table_inv):
+ .long 0x55555556
+ .long 0x51D07EAF
+ .long 0x4E5E0A73
+ .long 0x4AFD6A06
+ .long 0x47AE147B
+ .long 0x446F8657
+ .long 0x41414142
+ .long 0x3E22CBCF
+ .long 0x3B13B13C
+ .long 0x38138139
+ .long 0x3521CFB3
+ .long 0x323E34A3
+ .long 0x2F684BDB
+ .long 0x2C9FB4D9
+ .long 0x29E4129F
+ .long 0x27350B89
+ .long 0x24924925
+ .long 0x21FB7813
+ .long 0x1F7047DD
+ .long 0x1CF06ADB
+ .long 0x1A7B9612
+ .long 0x18118119
+ .long 0x15B1E5F8
+ .long 0x135C8114
+ .long 0x11111112
+ .long 0xECF56BF
+ .long 0xC9714FC
+ .long 0xA6810A7
+ .long 0x8421085
+ .long 0x624DD30
+ .long 0x4104105
+ .long 0x2040811
+ /* maximum error: 0.987342 scaled: 0.921875*/
+
+ ENDFUNC(GLOBAL(sdivsi3_i4i))
+#endif /* SH3 / SH4 */
+
+#endif /* L_div_table */
+
+#ifdef L_udiv_qrnnd_16
+#if !__SHMEDIA__
+ HIDDEN_FUNC(GLOBAL(udiv_qrnnd_16))
+ /* r0: rn r1: qn */ /* r0: n1 r4: n0 r5: d r6: d1 */ /* r2: __m */
+ /* n1 < d, but n1 might be larger than d1. */
+ .global GLOBAL(udiv_qrnnd_16)
+ .balign 8
+GLOBAL(udiv_qrnnd_16):
+ div0u
+ cmp/hi r6,r0
+ bt .Lots
+ .rept 16
+ div1 r6,r0
+ .endr
+ extu.w r0,r1
+ bt 0f
+ add r6,r0
+0: rotcl r1
+ mulu.w r1,r5
+ xtrct r4,r0
+ swap.w r0,r0
+ sts macl,r2
+ cmp/hs r2,r0
+ sub r2,r0
+ bt 0f
+ addc r5,r0
+ add #-1,r1
+ bt 0f
+1: add #-1,r1
+ rts
+ add r5,r0
+ .balign 8
+.Lots:
+ sub r5,r0
+ swap.w r4,r1
+ xtrct r0,r1
+ clrt
+ mov r1,r0
+ addc r5,r0
+ mov #-1,r1
+ SL1(bf, 1b,
+ shlr16 r1)
+0: rts
+ nop
+ ENDFUNC(GLOBAL(udiv_qrnnd_16))
+#endif /* !__SHMEDIA__ */
+#endif /* L_udiv_qrnnd_16 */
diff --git a/gcc/config/sh/lib1funcs.h b/gcc/config/sh/lib1funcs.h
new file mode 100644
index 000000000..af4b41cc3
--- /dev/null
+++ b/gcc/config/sh/lib1funcs.h
@@ -0,0 +1,76 @@
+/* Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+ 2004, 2005, 2006, 2009
+ Free Software Foundation, Inc.
+
+This file is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3, or (at your option) any
+later version.
+
+This file is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+#ifdef __ELF__
+#define LOCAL(X) .L_##X
+#define FUNC(X) .type X,@function
+#define HIDDEN_FUNC(X) FUNC(X); .hidden X
+#define HIDDEN_ALIAS(X,Y) ALIAS (X,Y); .hidden GLOBAL(X)
+#define ENDFUNC0(X) .Lfe_##X: .size X,.Lfe_##X-X
+#define ENDFUNC(X) ENDFUNC0(X)
+#else
+#define LOCAL(X) L_##X
+#define FUNC(X)
+#define HIDDEN_FUNC(X)
+#define HIDDEN_ALIAS(X,Y) ALIAS (X,Y)
+#define ENDFUNC(X)
+#endif
+
+#define CONCAT(A,B) A##B
+#define GLOBAL0(U,X) CONCAT(U,__##X)
+#define GLOBAL(X) GLOBAL0(__USER_LABEL_PREFIX__,X)
+
+#define ALIAS(X,Y) .global GLOBAL(X); .set GLOBAL(X),GLOBAL(Y)
+
+#if defined __SH2A__ && defined __FMOVD_ENABLED__
+#undef FMOVD_WORKS
+#define FMOVD_WORKS
+#endif
+
+#ifdef __LITTLE_ENDIAN__
+#define DR00 fr1
+#define DR01 fr0
+#define DR20 fr3
+#define DR21 fr2
+#define DR40 fr5
+#define DR41 fr4
+#else /* !__LITTLE_ENDIAN__ */
+#define DR00 fr0
+#define DR01 fr1
+#define DR20 fr2
+#define DR21 fr3
+#define DR40 fr4
+#define DR41 fr5
+#endif /* !__LITTLE_ENDIAN__ */
+
+#ifdef __sh1__
+#define SL(branch, dest, in_slot, in_slot_arg2) \
+ in_slot, in_slot_arg2; branch dest
+#define SL1(branch, dest, in_slot) \
+ in_slot; branch dest
+#else /* ! __sh1__ */
+#define SL(branch, dest, in_slot, in_slot_arg2) \
+ branch##.s dest; in_slot, in_slot_arg2
+#define SL1(branch, dest, in_slot) \
+ branch##/s dest; in_slot
+#endif /* !__sh1__ */
diff --git a/gcc/config/sh/libgcc-excl.ver b/gcc/config/sh/libgcc-excl.ver
new file mode 100644
index 000000000..325c74054
--- /dev/null
+++ b/gcc/config/sh/libgcc-excl.ver
@@ -0,0 +1,8 @@
+# Exclude various symbols which should not be visible in libgcc.so for SH.
+%exclude {
+ __ashlsi3
+ __ashrsi3
+ __lshrsi3
+ __mulsi3 # this is an SH1-only symbol.
+ __udivsi3
+}
diff --git a/gcc/config/sh/libgcc-glibc.ver b/gcc/config/sh/libgcc-glibc.ver
new file mode 100644
index 000000000..b8ec32653
--- /dev/null
+++ b/gcc/config/sh/libgcc-glibc.ver
@@ -0,0 +1,48 @@
+# Copyright (C) 2002, 2008 Free Software Foundation, Inc.
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 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
+# <http://www.gnu.org/licenses/>.
+
+# In order to work around the very problems that force us to now generally
+# create a libgcc.so, glibc reexported a number of routines from libgcc.a.
+# By now choosing the same version tags for these specific routines, we
+# maintain enough binary compatibility to allow future versions of glibc
+# to defer implementation of these routines to libgcc.so via DT_AUXILIARY.
+
+# Note that we cannot use the default libgcc-glibc.ver file on sh,
+# because GLIBC_2.0 does not exist on this architecture, as the first
+# ever glibc release on the platform was GLIBC_2.2.
+
+%exclude {
+ __register_frame
+ __register_frame_table
+ __deregister_frame
+ __register_frame_info
+ __deregister_frame_info
+ __frame_state_for
+ __register_frame_info_table
+}
+
+%inherit GCC_3.0 GLIBC_2.2
+GLIBC_2.2 {
+ __register_frame
+ __register_frame_table
+ __deregister_frame
+ __register_frame_info
+ __deregister_frame_info
+ __frame_state_for
+ __register_frame_info_table
+}
diff --git a/gcc/config/sh/linux-atomic.asm b/gcc/config/sh/linux-atomic.asm
new file mode 100644
index 000000000..743c61bb7
--- /dev/null
+++ b/gcc/config/sh/linux-atomic.asm
@@ -0,0 +1,223 @@
+/* Copyright (C) 2006, 2008, 2009 Free Software Foundation, Inc.
+
+ This file is part of GCC.
+
+ GCC is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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.
+
+ Under Section 7 of GPL version 3, you are granted additional
+ permissions described in the GCC Runtime Library Exception, version
+ 3.1, as published by the Free Software Foundation.
+
+ You should have received a copy of the GNU General Public License and
+ a copy of the GCC Runtime Library Exception along with this program;
+ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+
+!! Linux specific atomic routines for the Renesas / SuperH SH CPUs.
+!! Linux kernel for SH3/4 has implemented the support for software
+!! atomic sequences.
+
+#define FUNC(X) .type X,@function
+#define HIDDEN_FUNC(X) FUNC(X); .hidden X
+#define ENDFUNC0(X) .Lfe_##X: .size X,.Lfe_##X-X
+#define ENDFUNC(X) ENDFUNC0(X)
+
+#if ! __SH5__
+
+#define ATOMIC_TEST_AND_SET(N,T,EXT) \
+ .global __sync_lock_test_and_set_##N; \
+ HIDDEN_FUNC(__sync_lock_test_and_set_##N); \
+ .align 2; \
+__sync_lock_test_and_set_##N:; \
+ mova 1f, r0; \
+ nop; \
+ mov r15, r1; \
+ mov #(0f-1f), r15; \
+0: mov.##T @r4, r2; \
+ mov.##T r5, @r4; \
+1: mov r1, r15; \
+ rts; \
+ EXT r2, r0; \
+ ENDFUNC(__sync_lock_test_and_set_##N)
+
+ATOMIC_TEST_AND_SET (1,b,extu.b)
+ATOMIC_TEST_AND_SET (2,w,extu.w)
+ATOMIC_TEST_AND_SET (4,l,mov)
+
+#define ATOMIC_COMPARE_AND_SWAP(N,T,EXTS,EXT) \
+ .global __sync_val_compare_and_swap_##N; \
+ HIDDEN_FUNC(__sync_val_compare_and_swap_##N); \
+ .align 2; \
+__sync_val_compare_and_swap_##N:; \
+ mova 1f, r0; \
+ EXTS r5, r5; \
+ mov r15, r1; \
+ mov #(0f-1f), r15; \
+0: mov.##T @r4, r2; \
+ cmp/eq r2, r5; \
+ bf 1f; \
+ mov.##T r6, @r4; \
+1: mov r1, r15; \
+ rts; \
+ EXT r2, r0; \
+ ENDFUNC(__sync_val_compare_and_swap_##N)
+
+ATOMIC_COMPARE_AND_SWAP (1,b,exts.b,extu.b)
+ATOMIC_COMPARE_AND_SWAP (2,w,exts.w,extu.w)
+ATOMIC_COMPARE_AND_SWAP (4,l,mov,mov)
+
+#define ATOMIC_BOOL_COMPARE_AND_SWAP(N,T,EXTS) \
+ .global __sync_bool_compare_and_swap_##N; \
+ HIDDEN_FUNC(__sync_bool_compare_and_swap_##N); \
+ .align 2; \
+__sync_bool_compare_and_swap_##N:; \
+ mova 1f, r0; \
+ EXTS r5, r5; \
+ mov r15, r1; \
+ mov #(0f-1f), r15; \
+0: mov.##T @r4, r2; \
+ cmp/eq r2, r5; \
+ bf 1f; \
+ mov.##T r6, @r4; \
+1: mov r1, r15; \
+ rts; \
+ movt r0; \
+ ENDFUNC(__sync_bool_compare_and_swap_##N)
+
+ATOMIC_BOOL_COMPARE_AND_SWAP (1,b,exts.b)
+ATOMIC_BOOL_COMPARE_AND_SWAP (2,w,exts.w)
+ATOMIC_BOOL_COMPARE_AND_SWAP (4,l,mov)
+
+#define ATOMIC_FETCH_AND_OP(OP,N,T,EXT) \
+ .global __sync_fetch_and_##OP##_##N; \
+ HIDDEN_FUNC(__sync_fetch_and_##OP##_##N); \
+ .align 2; \
+__sync_fetch_and_##OP##_##N:; \
+ mova 1f, r0; \
+ nop; \
+ mov r15, r1; \
+ mov #(0f-1f), r15; \
+0: mov.##T @r4, r2; \
+ mov r5, r3; \
+ OP r2, r3; \
+ mov.##T r3, @r4; \
+1: mov r1, r15; \
+ rts; \
+ EXT r2, r0; \
+ ENDFUNC(__sync_fetch_and_##OP##_##N)
+
+ATOMIC_FETCH_AND_OP(add,1,b,extu.b)
+ATOMIC_FETCH_AND_OP(add,2,w,extu.w)
+ATOMIC_FETCH_AND_OP(add,4,l,mov)
+
+ATOMIC_FETCH_AND_OP(or,1,b,extu.b)
+ATOMIC_FETCH_AND_OP(or,2,w,extu.w)
+ATOMIC_FETCH_AND_OP(or,4,l,mov)
+
+ATOMIC_FETCH_AND_OP(and,1,b,extu.b)
+ATOMIC_FETCH_AND_OP(and,2,w,extu.w)
+ATOMIC_FETCH_AND_OP(and,4,l,mov)
+
+ATOMIC_FETCH_AND_OP(xor,1,b,extu.b)
+ATOMIC_FETCH_AND_OP(xor,2,w,extu.w)
+ATOMIC_FETCH_AND_OP(xor,4,l,mov)
+
+#define ATOMIC_FETCH_AND_COMBOP(OP,OP0,OP1,N,T,EXT) \
+ .global __sync_fetch_and_##OP##_##N; \
+ HIDDEN_FUNC(__sync_fetch_and_##OP##_##N); \
+ .align 2; \
+__sync_fetch_and_##OP##_##N:; \
+ mova 1f, r0; \
+ mov r15, r1; \
+ mov #(0f-1f), r15; \
+0: mov.##T @r4, r2; \
+ mov r5, r3; \
+ OP0 r2, r3; \
+ OP1 r3, r3; \
+ mov.##T r3, @r4; \
+1: mov r1, r15; \
+ rts; \
+ EXT r2, r0; \
+ ENDFUNC(__sync_fetch_and_##OP##_##N)
+
+ATOMIC_FETCH_AND_COMBOP(sub,sub,neg,1,b,extu.b)
+ATOMIC_FETCH_AND_COMBOP(sub,sub,neg,2,w,extu.w)
+ATOMIC_FETCH_AND_COMBOP(sub,sub,neg,4,l,mov)
+
+ATOMIC_FETCH_AND_COMBOP(nand,and,not,1,b,extu.b)
+ATOMIC_FETCH_AND_COMBOP(nand,and,not,2,w,extu.w)
+ATOMIC_FETCH_AND_COMBOP(nand,and,not,4,l,mov)
+
+#define ATOMIC_OP_AND_FETCH(OP,N,T,EXT) \
+ .global __sync_##OP##_and_fetch_##N; \
+ HIDDEN_FUNC(__sync_##OP##_and_fetch_##N); \
+ .align 2; \
+__sync_##OP##_and_fetch_##N:; \
+ mova 1f, r0; \
+ nop; \
+ mov r15, r1; \
+ mov #(0f-1f), r15; \
+0: mov.##T @r4, r2; \
+ mov r5, r3; \
+ OP r2, r3; \
+ mov.##T r3, @r4; \
+1: mov r1, r15; \
+ rts; \
+ EXT r3, r0; \
+ ENDFUNC(__sync_##OP##_and_fetch_##N)
+
+ATOMIC_OP_AND_FETCH(add,1,b,extu.b)
+ATOMIC_OP_AND_FETCH(add,2,w,extu.w)
+ATOMIC_OP_AND_FETCH(add,4,l,mov)
+
+ATOMIC_OP_AND_FETCH(or,1,b,extu.b)
+ATOMIC_OP_AND_FETCH(or,2,w,extu.w)
+ATOMIC_OP_AND_FETCH(or,4,l,mov)
+
+ATOMIC_OP_AND_FETCH(and,1,b,extu.b)
+ATOMIC_OP_AND_FETCH(and,2,w,extu.w)
+ATOMIC_OP_AND_FETCH(and,4,l,mov)
+
+ATOMIC_OP_AND_FETCH(xor,1,b,extu.b)
+ATOMIC_OP_AND_FETCH(xor,2,w,extu.w)
+ATOMIC_OP_AND_FETCH(xor,4,l,mov)
+
+#define ATOMIC_COMBOP_AND_FETCH(OP,OP0,OP1,N,T,EXT) \
+ .global __sync_##OP##_and_fetch_##N; \
+ HIDDEN_FUNC(__sync_##OP##_and_fetch_##N); \
+ .align 2; \
+__sync_##OP##_and_fetch_##N:; \
+ mova 1f, r0; \
+ mov r15, r1; \
+ mov #(0f-1f), r15; \
+0: mov.##T @r4, r2; \
+ mov r5, r3; \
+ OP0 r2, r3; \
+ OP1 r3, r3; \
+ mov.##T r3, @r4; \
+1: mov r1, r15; \
+ rts; \
+ EXT r3, r0; \
+ ENDFUNC(__sync_##OP##_and_fetch_##N)
+
+ATOMIC_COMBOP_AND_FETCH(sub,sub,neg,1,b,extu.b)
+ATOMIC_COMBOP_AND_FETCH(sub,sub,neg,2,w,extu.w)
+ATOMIC_COMBOP_AND_FETCH(sub,sub,neg,4,l,mov)
+
+ATOMIC_COMBOP_AND_FETCH(nand,and,not,1,b,extu.b)
+ATOMIC_COMBOP_AND_FETCH(nand,and,not,2,w,extu.w)
+ATOMIC_COMBOP_AND_FETCH(nand,and,not,4,l,mov)
+
+.section .note.GNU-stack,"",%progbits
+.previous
+
+#endif /* ! __SH5__ */
diff --git a/gcc/config/sh/linux-unwind.h b/gcc/config/sh/linux-unwind.h
new file mode 100644
index 000000000..5a78e3172
--- /dev/null
+++ b/gcc/config/sh/linux-unwind.h
@@ -0,0 +1,256 @@
+/* DWARF2 EH unwinding support for SH Linux.
+ Copyright (C) 2004, 2005, 2006, 2007, 2009, 2012 Free Software Foundation,
+ Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+
+/* Do code reading to identify a signal frame, and set the frame
+ state data appropriately. See unwind-dw2.c for the structs.
+ Don't use this at all if inhibit_libc is used. */
+
+#ifndef inhibit_libc
+
+#include <signal.h>
+#include <sys/ucontext.h>
+#include "insn-constants.h"
+
+# if defined (__SH5__)
+#define SH_DWARF_FRAME_GP0 0
+#define SH_DWARF_FRAME_FP0 77
+#define SH_DWARF_FRAME_BT0 68
+#define SH_DWARF_FRAME_PR_MEDIA 18
+#define SH_DWARF_FRAME_SR 65
+#define SH_DWARF_FRAME_FPSCR 76
+#else
+#define SH_DWARF_FRAME_GP0 0
+#define SH_DWARF_FRAME_FP0 25
+#define SH_DWARF_FRAME_XD0 87
+#define SH_DWARF_FRAME_PR 17
+#define SH_DWARF_FRAME_GBR 18
+#define SH_DWARF_FRAME_MACH 20
+#define SH_DWARF_FRAME_MACL 21
+#define SH_DWARF_FRAME_PC 16
+#define SH_DWARF_FRAME_SR 22
+#define SH_DWARF_FRAME_FPUL 23
+#define SH_DWARF_FRAME_FPSCR 24
+#endif /* defined (__SH5__) */
+
+#if defined (__SH5__)
+
+#define MD_FALLBACK_FRAME_STATE_FOR shmedia_fallback_frame_state
+
+static _Unwind_Reason_Code
+shmedia_fallback_frame_state (struct _Unwind_Context *context,
+ _Unwind_FrameState *fs)
+{
+ unsigned char *pc = context->ra;
+ struct sigcontext *sc;
+ long new_cfa;
+ int i, r;
+
+ /* movi 0x10,r9; shori 0x77,r9; trapa r9; nop (sigreturn) */
+ /* movi 0x10,r9; shori 0xad,r9; trapa r9; nop (rt_sigreturn) */
+ if ((*(unsigned long *) (pc-1) == 0xcc004090)
+ && (*(unsigned long *) (pc+3) == 0xc801dc90)
+ && (*(unsigned long *) (pc+7) == 0x6c91fff0)
+ && (*(unsigned long *) (pc+11) == 0x6ff0fff0))
+ sc = context->cfa;
+ else if ((*(unsigned long *) (pc-1) == 0xcc004090)
+ && (*(unsigned long *) (pc+3) == 0xc802b490)
+ && (*(unsigned long *) (pc+7) == 0x6c91fff0)
+ && (*(unsigned long *) (pc+11) == 0x6ff0fff0))
+ {
+ struct rt_sigframe {
+ siginfo_t *pinfo;
+ void *puc;
+ siginfo_t info;
+ struct ucontext uc;
+ } *rt_ = context->cfa;
+ /* The void * cast is necessary to avoid an aliasing warning.
+ The aliasing warning is correct, but should not be a problem
+ because it does not alias anything. */
+ sc = (struct sigcontext *) (void *) &rt_->uc.uc_mcontext;
+ }
+ else
+ return _URC_END_OF_STACK;
+
+ new_cfa = sc->sc_regs[15];
+ fs->regs.cfa_how = CFA_REG_OFFSET;
+ fs->regs.cfa_reg = 15;
+ fs->regs.cfa_offset = new_cfa - (long) context->cfa;
+
+ for (i = 0; i < 63; i++)
+ {
+ if (i == 15)
+ continue;
+
+ fs->regs.reg[i].how = REG_SAVED_OFFSET;
+ fs->regs.reg[i].loc.offset
+ = (long)&(sc->sc_regs[i]) - new_cfa;
+ }
+
+ fs->regs.reg[SH_DWARF_FRAME_SR].how = REG_SAVED_OFFSET;
+ fs->regs.reg[SH_DWARF_FRAME_SR].loc.offset
+ = (long)&(sc->sc_sr) - new_cfa;
+
+ r = SH_DWARF_FRAME_BT0;
+ for (i = 0; i < 8; i++)
+ {
+ fs->regs.reg[r+i].how = REG_SAVED_OFFSET;
+ fs->regs.reg[r+i].loc.offset
+ = (long)&(sc->sc_tregs[i]) - new_cfa;
+ }
+
+ r = SH_DWARF_FRAME_FP0;
+ for (i = 0; i < 32; i++)
+ {
+ fs->regs.reg[r+i].how = REG_SAVED_OFFSET;
+ fs->regs.reg[r+i].loc.offset
+ = (long)&(sc->sc_fpregs[i]) - new_cfa;
+ }
+
+ fs->regs.reg[SH_DWARF_FRAME_FPSCR].how = REG_SAVED_OFFSET;
+ fs->regs.reg[SH_DWARF_FRAME_FPSCR].loc.offset
+ = (long)&(sc->sc_fpscr) - new_cfa;
+
+ /* We use the slot for the zero register to save return address. */
+ fs->regs.reg[63].how = REG_SAVED_OFFSET;
+ fs->regs.reg[63].loc.offset
+ = (long)&(sc->sc_pc) - new_cfa;
+ fs->retaddr_column = 63;
+ fs->signal_frame = 1;
+ return _URC_NO_REASON;
+}
+
+#else /* defined (__SH5__) */
+
+#define MD_FALLBACK_FRAME_STATE_FOR sh_fallback_frame_state
+
+static _Unwind_Reason_Code
+sh_fallback_frame_state (struct _Unwind_Context *context,
+ _Unwind_FrameState *fs)
+{
+ unsigned char *pc = context->ra;
+ struct sigcontext *sc;
+ long new_cfa;
+ int i;
+#if defined (__SH3E__) || defined (__SH4__)
+ int r;
+#endif
+
+ /* mov.w 1f,r3; trapa #0x10; 1: .short 0x77 (sigreturn) */
+ /* mov.w 1f,r3; trapa #0x10; 1: .short 0xad (rt_sigreturn) */
+ /* Newer kernel uses pad instructions to avoid an SH-4 core bug. */
+ /* mov.w 1f,r3; trapa #0x10; or r0,r0; or r0,r0; or r0,r0; or r0,r0;
+ or r0,r0; 1: .short 0x77 (sigreturn) */
+ /* mov.w 1f,r3; trapa #0x10; or r0,r0; or r0,r0; or r0,r0; or r0,r0;
+ or r0,r0; 1: .short 0xad (rt_sigreturn) */
+ if (((*(unsigned short *) (pc+0) == 0x9300)
+ && (*(unsigned short *) (pc+2) == 0xc310)
+ && (*(unsigned short *) (pc+4) == 0x0077))
+ || (((*(unsigned short *) (pc+0) == 0x9305)
+ && (*(unsigned short *) (pc+2) == 0xc310)
+ && (*(unsigned short *) (pc+14) == 0x0077))))
+ sc = context->cfa;
+ else if (((*(unsigned short *) (pc+0) == 0x9300)
+ && (*(unsigned short *) (pc+2) == 0xc310)
+ && (*(unsigned short *) (pc+4) == 0x00ad))
+ || (((*(unsigned short *) (pc+0) == 0x9305)
+ && (*(unsigned short *) (pc+2) == 0xc310)
+ && (*(unsigned short *) (pc+14) == 0x00ad))))
+ {
+ struct rt_sigframe {
+ siginfo_t info;
+ struct ucontext uc;
+ } *rt_ = context->cfa;
+ /* The void * cast is necessary to avoid an aliasing warning.
+ The aliasing warning is correct, but should not be a problem
+ because it does not alias anything. */
+ sc = (struct sigcontext *) (void *) &rt_->uc.uc_mcontext;
+ }
+ else
+ return _URC_END_OF_STACK;
+
+ new_cfa = sc->sc_regs[15];
+ fs->regs.cfa_how = CFA_REG_OFFSET;
+ fs->regs.cfa_reg = 15;
+ fs->regs.cfa_offset = new_cfa - (long) context->cfa;
+
+ for (i = 0; i < 15; i++)
+ {
+ fs->regs.reg[i].how = REG_SAVED_OFFSET;
+ fs->regs.reg[i].loc.offset
+ = (long)&(sc->sc_regs[i]) - new_cfa;
+ }
+
+ fs->regs.reg[SH_DWARF_FRAME_PR].how = REG_SAVED_OFFSET;
+ fs->regs.reg[SH_DWARF_FRAME_PR].loc.offset
+ = (long)&(sc->sc_pr) - new_cfa;
+ fs->regs.reg[SH_DWARF_FRAME_SR].how = REG_SAVED_OFFSET;
+ fs->regs.reg[SH_DWARF_FRAME_SR].loc.offset
+ = (long)&(sc->sc_sr) - new_cfa;
+ fs->regs.reg[SH_DWARF_FRAME_GBR].how = REG_SAVED_OFFSET;
+ fs->regs.reg[SH_DWARF_FRAME_GBR].loc.offset
+ = (long)&(sc->sc_gbr) - new_cfa;
+ fs->regs.reg[SH_DWARF_FRAME_MACH].how = REG_SAVED_OFFSET;
+ fs->regs.reg[SH_DWARF_FRAME_MACH].loc.offset
+ = (long)&(sc->sc_mach) - new_cfa;
+ fs->regs.reg[SH_DWARF_FRAME_MACL].how = REG_SAVED_OFFSET;
+ fs->regs.reg[SH_DWARF_FRAME_MACL].loc.offset
+ = (long)&(sc->sc_macl) - new_cfa;
+
+#if defined (__SH3E__) || defined (__SH4__)
+ r = SH_DWARF_FRAME_FP0;
+ for (i = 0; i < 16; i++)
+ {
+ fs->regs.reg[r+i].how = REG_SAVED_OFFSET;
+ fs->regs.reg[r+i].loc.offset
+ = (long)&(sc->sc_fpregs[i]) - new_cfa;
+ }
+
+ r = SH_DWARF_FRAME_XD0;
+ for (i = 0; i < 8; i++)
+ {
+ fs->regs.reg[r+i].how = REG_SAVED_OFFSET;
+ fs->regs.reg[r+i].loc.offset
+ = (long)&(sc->sc_xfpregs[2*i]) - new_cfa;
+ }
+
+ fs->regs.reg[SH_DWARF_FRAME_FPUL].how = REG_SAVED_OFFSET;
+ fs->regs.reg[SH_DWARF_FRAME_FPUL].loc.offset
+ = (long)&(sc->sc_fpul) - new_cfa;
+ fs->regs.reg[SH_DWARF_FRAME_FPSCR].how = REG_SAVED_OFFSET;
+ fs->regs.reg[SH_DWARF_FRAME_FPSCR].loc.offset
+ = (long)&(sc->sc_fpscr) - new_cfa;
+#endif
+
+ fs->regs.reg[SH_DWARF_FRAME_PC].how = REG_SAVED_OFFSET;
+ fs->regs.reg[SH_DWARF_FRAME_PC].loc.offset
+ = (long)&(sc->sc_pc) - new_cfa;
+ fs->retaddr_column = SH_DWARF_FRAME_PC;
+ fs->signal_frame = 1;
+ return _URC_NO_REASON;
+}
+#endif /* defined (__SH5__) */
+
+#endif /* inhibit_libc */
diff --git a/gcc/config/sh/linux.h b/gcc/config/sh/linux.h
new file mode 100644
index 000000000..a090dae1c
--- /dev/null
+++ b/gcc/config/sh/linux.h
@@ -0,0 +1,137 @@
+/* Definitions for SH running Linux-based GNU systems using ELF
+ Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007, 2010
+ Free Software Foundation, Inc.
+ Contributed by Kazumoto Kojima <kkojima@rr.iij4u.or.jp>
+
+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
+<http://www.gnu.org/licenses/>. */
+
+/* Run-time Target Specification. */
+#undef TARGET_VERSION
+#define TARGET_VERSION fputs (" (SH GNU/Linux with ELF)", stderr);
+
+/* Enable DWARF 2 exceptions. */
+#undef DWARF2_UNWIND_INFO
+#define DWARF2_UNWIND_INFO 1
+
+#undef SUBTARGET_CPP_SPEC
+#define SUBTARGET_CPP_SPEC "\
+ %{posix:-D_POSIX_SOURCE} \
+ %{pthread:-D_REENTRANT -D_PTHREADS} \
+"
+
+#define TARGET_OS_CPP_BUILTINS() \
+ do \
+ { \
+ LINUX_TARGET_OS_CPP_BUILTINS(); \
+ } \
+ while (0)
+
+#undef TARGET_DEFAULT
+#define TARGET_DEFAULT \
+ (TARGET_CPU_DEFAULT | MASK_USERMODE | TARGET_ENDIAN_DEFAULT \
+ | TARGET_OPT_DEFAULT)
+
+#define TARGET_ASM_FILE_END file_end_indicate_exec_stack
+
+#define GLIBC_DYNAMIC_LINKER "/lib/ld-linux.so.2"
+
+#undef SUBTARGET_LINK_EMUL_SUFFIX
+#define SUBTARGET_LINK_EMUL_SUFFIX "_linux"
+#undef SUBTARGET_LINK_SPEC
+#define SUBTARGET_LINK_SPEC \
+ "%{shared:-shared} \
+ %{!static: \
+ %{rdynamic:-export-dynamic} \
+ -dynamic-linker " LINUX_DYNAMIC_LINKER "} \
+ %{static:-static}"
+
+/* Output assembler code to STREAM to call the profiler. */
+
+#undef FUNCTION_PROFILER
+#define FUNCTION_PROFILER(STREAM,LABELNO) \
+ do { \
+ if (TARGET_SHMEDIA) \
+ { \
+ fprintf (STREAM, "\tpt\t1f,tr1\n"); \
+ fprintf (STREAM, "\taddi.l\tr15,-8,r15\n"); \
+ fprintf (STREAM, "\tst.l\tr15,0,r18\n"); \
+ if (flag_pic) \
+ { \
+ const char *gofs = "(datalabel _GLOBAL_OFFSET_TABLE_-(0f-.))"; \
+ fprintf (STREAM, "\tmovi\t((%s>>16)&0xffff),r21\n", gofs); \
+ fprintf (STREAM, "\tshori\t(%s & 0xffff),r21\n", gofs); \
+ fprintf (STREAM, "0:\tptrel/u\tr21,tr0\n"); \
+ fprintf (STREAM, "\tmovi\t((mcount@GOTPLT)&0xffff),r22\n"); \
+ fprintf (STREAM, "\tgettr\ttr0,r21\n"); \
+ fprintf (STREAM, "\tadd.l\tr21,r22,r21\n"); \
+ fprintf (STREAM, "\tld.l\tr21,0,r21\n"); \
+ fprintf (STREAM, "\tptabs\tr21,tr0\n"); \
+ } \
+ else \
+ fprintf (STREAM, "\tpt\tmcount,tr0\n"); \
+ fprintf (STREAM, "\tgettr\ttr1,r18\n"); \
+ fprintf (STREAM, "\tblink\ttr0,r63\n"); \
+ fprintf (STREAM, "1:\tld.l\tr15,0,r18\n"); \
+ fprintf (STREAM, "\taddi.l\tr15,8,r15\n"); \
+ } \
+ else \
+ { \
+ if (flag_pic) \
+ { \
+ fprintf (STREAM, "\tmov.l\t3f,r1\n"); \
+ fprintf (STREAM, "\tmova\t3f,r0\n"); \
+ fprintf (STREAM, "\tadd\tr1,r0\n"); \
+ fprintf (STREAM, "\tmov.l\t1f,r1\n"); \
+ fprintf (STREAM, "\tmov.l\t@(r0,r1),r1\n"); \
+ } \
+ else \
+ fprintf (STREAM, "\tmov.l\t1f,r1\n"); \
+ fprintf (STREAM, "\tsts.l\tpr,@-r15\n"); \
+ fprintf (STREAM, "\tmova\t2f,r0\n"); \
+ fprintf (STREAM, "\tjmp\t@r1\n"); \
+ fprintf (STREAM, "\tlds\tr0,pr\n"); \
+ fprintf (STREAM, "\t.align\t2\n"); \
+ if (flag_pic) \
+ { \
+ fprintf (STREAM, "1:\t.long\tmcount@GOT\n"); \
+ fprintf (STREAM, "3:\t.long\t_GLOBAL_OFFSET_TABLE_\n"); \
+ } \
+ else \
+ fprintf (STREAM, "1:\t.long\tmcount\n"); \
+ fprintf (STREAM, "2:\tlds.l\t@r15+,pr\n"); \
+ } \
+ } while (0)
+
+#define MD_UNWIND_SUPPORT "config/sh/linux-unwind.h"
+
+/* For SH3 and SH4, we use a slot of the unwind frame which correspond
+ to a fake register number 16 as a placeholder for the return address
+ in MD_FALLBACK_FRAME_STATE_FOR and its content will be read with
+ _Unwind_GetGR which uses dwarf_reg_size_table to get the size of
+ the register. So the entry of dwarf_reg_size_table corresponding to
+ this slot must be set. To do this, we redefine DBX_REGISTER_NUMBER
+ so as to return itself for 16. */
+#undef DBX_REGISTER_NUMBER
+#define DBX_REGISTER_NUMBER(REGNO) \
+ ((! TARGET_SH5 && (REGNO) == 16) ? 16 : SH_DBX_REGISTER_NUMBER (REGNO))
+
+/* Since libgcc is compiled with -fpic for this target, we can't use
+ __sdivsi3_1 as the division strategy for -O0 and -Os. */
+#undef SH_DIV_STRATEGY_DEFAULT
+#define SH_DIV_STRATEGY_DEFAULT SH_DIV_CALL2
+#undef SH_DIV_STR_FOR_SIZE
+#define SH_DIV_STR_FOR_SIZE "call2"
diff --git a/gcc/config/sh/little.h b/gcc/config/sh/little.h
new file mode 100644
index 000000000..f87c7b77d
--- /dev/null
+++ b/gcc/config/sh/little.h
@@ -0,0 +1,21 @@
+/* Definition of little endian SH machine for GNU compiler.
+
+ Copyright (C) 2002, 2007 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 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
+<http://www.gnu.org/licenses/>. */
+
+#define TARGET_ENDIAN_DEFAULT MASK_LITTLE_ENDIAN
diff --git a/gcc/config/sh/netbsd-elf.h b/gcc/config/sh/netbsd-elf.h
new file mode 100644
index 000000000..50bb2f2db
--- /dev/null
+++ b/gcc/config/sh/netbsd-elf.h
@@ -0,0 +1,117 @@
+/* Definitions for SH running NetBSD using ELF
+ Copyright (C) 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
+ Contributed by Wasabi Systems, 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
+<http://www.gnu.org/licenses/>. */
+
+/* Run-time Target Specification. */
+#if TARGET_ENDIAN_DEFAULT == MASK_LITTLE_ENDIAN
+#define TARGET_VERSION_ENDIAN "le"
+#else
+#define TARGET_VERSION_ENDIAN ""
+#endif
+
+#if TARGET_CPU_DEFAULT & MASK_SH5
+#if TARGET_CPU_DEFAULT & MASK_SH_E
+#define TARGET_VERSION_CPU "sh5"
+#else
+#define TARGET_VERSION_CPU "sh64"
+#endif /* MASK_SH_E */
+#else
+#define TARGET_VERSION_CPU "sh"
+#endif /* MASK_SH5 */
+
+#undef TARGET_VERSION
+#define TARGET_VERSION fprintf (stderr, " (NetBSD/%s%s ELF)", \
+ TARGET_VERSION_CPU, TARGET_VERSION_ENDIAN)
+
+
+/* Extra specs needed for NetBSD SuperH ELF targets. */
+
+#undef SUBTARGET_EXTRA_SPECS
+#define SUBTARGET_EXTRA_SPECS \
+ { "netbsd_entry_point", NETBSD_ENTRY_POINT },
+
+
+#define TARGET_OS_CPP_BUILTINS() \
+ do \
+ { \
+ NETBSD_OS_CPP_BUILTINS_ELF(); \
+ builtin_define ("__NO_LEADING_UNDERSCORES__"); \
+ } \
+ while (0)
+
+/* Provide a LINK_SPEC appropriate for a NetBSD/sh ELF target.
+ We use the SH_LINK_SPEC from sh/sh.h, and define the appropriate
+ SUBTARGET_LINK_SPEC that pulls in what we need from a generic
+ NetBSD ELF LINK_SPEC. */
+
+/* LINK_EMUL_PREFIX from sh/elf.h */
+
+#undef SUBTARGET_LINK_EMUL_SUFFIX
+#define SUBTARGET_LINK_EMUL_SUFFIX "_nbsd"
+
+#undef SUBTARGET_LINK_SPEC
+#define SUBTARGET_LINK_SPEC NETBSD_LINK_SPEC_ELF
+
+#undef LINK_SPEC
+#define LINK_SPEC SH_LINK_SPEC
+
+#define NETBSD_ENTRY_POINT "__start"
+
+/* Provide a CPP_SPEC appropriate for NetBSD. */
+#undef SUBTARGET_CPP_SPEC
+#define SUBTARGET_CPP_SPEC NETBSD_CPP_SPEC
+
+#undef TARGET_DEFAULT
+#define TARGET_DEFAULT \
+ (TARGET_CPU_DEFAULT | MASK_USERMODE | TARGET_ENDIAN_DEFAULT)
+
+/* Define because we use the label and we do not need them. */
+#define NO_PROFILE_COUNTERS 1
+
+#undef FUNCTION_PROFILER
+#define FUNCTION_PROFILER(STREAM,LABELNO) \
+do \
+ { \
+ if (TARGET_SHMEDIA32 || TARGET_SHMEDIA64) \
+ { \
+ /* FIXME */ \
+ sorry ("unimplemented-shmedia profiling"); \
+ } \
+ else \
+ { \
+ fprintf((STREAM), "\tmov.l\t%sLP%d,r1\n", \
+ LOCAL_LABEL_PREFIX, (LABELNO)); \
+ fprintf((STREAM), "\tmova\t%sLP%dr,r0\n", \
+ LOCAL_LABEL_PREFIX, (LABELNO)); \
+ fprintf((STREAM), "\tjmp\t@r1\n"); \
+ fprintf((STREAM), "\tnop\n"); \
+ fprintf((STREAM), "\t.align\t2\n"); \
+ fprintf((STREAM), "%sLP%d:\t.long\t__mcount\n", \
+ LOCAL_LABEL_PREFIX, (LABELNO)); \
+ fprintf((STREAM), "%sLP%dr:\n", LOCAL_LABEL_PREFIX, (LABELNO)); \
+ } \
+ } \
+while (0)
+
+/* Since libgcc is compiled with -fpic for this target, we can't use
+ __sdivsi3_1 as the division strategy for -O0 and -Os. */
+#undef SH_DIV_STRATEGY_DEFAULT
+#define SH_DIV_STRATEGY_DEFAULT SH_DIV_CALL2
+#undef SH_DIV_STR_FOR_SIZE
+#define SH_DIV_STR_FOR_SIZE "call2"
diff --git a/gcc/config/sh/newlib.h b/gcc/config/sh/newlib.h
new file mode 100644
index 000000000..13099c1f8
--- /dev/null
+++ b/gcc/config/sh/newlib.h
@@ -0,0 +1,25 @@
+/* Definitions of target machine for gcc for Super-H using sh-superh-elf.
+ Copyright (C) 2001, 2007 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, 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 GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+
+/* This header file is used when with_libgloss is enabled during gcc
+ configuration. */
+
+#undef LIB_SPEC
+#define LIB_SPEC "-lc -lgloss"
diff --git a/gcc/config/sh/predicates.md b/gcc/config/sh/predicates.md
new file mode 100644
index 000000000..b6508b70d
--- /dev/null
+++ b/gcc/config/sh/predicates.md
@@ -0,0 +1,833 @@
+;; Predicate definitions for Renesas / SuperH SH.
+;; Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010
+;; Free Software Foundation, Inc.
+;;
+;; This file is part of GCC.
+;;
+;; GCC is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 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
+;; <http://www.gnu.org/licenses/>.
+
+;; TODO: Add a comment here.
+
+(define_predicate "trapping_target_operand"
+ (match_code "if_then_else")
+{
+ rtx cond, mem, res, tar, and_expr;
+
+ if (GET_MODE (op) != PDImode)
+ return 0;
+ cond = XEXP (op, 0);
+ mem = XEXP (op, 1);
+ res = XEXP (op, 2);
+ if (!MEM_P (mem)
+ || (GET_CODE (res) != SIGN_EXTEND && GET_CODE (res) != TRUNCATE))
+ return 0;
+ tar = XEXP (res, 0);
+ if (!rtx_equal_p (XEXP (mem, 0), tar)
+ || GET_MODE (tar) != Pmode)
+ return 0;
+ if (GET_CODE (cond) == CONST)
+ {
+ cond = XEXP (cond, 0);
+ if (!satisfies_constraint_Csy (tar))
+ return 0;
+ if (GET_CODE (tar) == CONST)
+ tar = XEXP (tar, 0);
+ }
+ else if (!arith_reg_operand (tar, VOIDmode)
+ && ! satisfies_constraint_Csy (tar))
+ return 0;
+ if (GET_CODE (cond) != EQ)
+ return 0;
+ and_expr = XEXP (cond, 0);
+ return (GET_CODE (and_expr) == AND
+ && rtx_equal_p (XEXP (and_expr, 0), tar)
+ && CONST_INT_P (XEXP (and_expr, 1))
+ && CONST_INT_P (XEXP (cond, 1))
+ && INTVAL (XEXP (and_expr, 1)) == 3
+ && INTVAL (XEXP (cond, 1)) == 3);
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "and_operand"
+ (match_code "subreg,reg,const_int")
+{
+ if (logical_operand (op, mode))
+ return 1;
+
+ /* Check mshflo.l / mshflhi.l opportunities. */
+ if (TARGET_SHMEDIA
+ && mode == DImode
+ && satisfies_constraint_J16 (op))
+ return 1;
+
+ return 0;
+})
+
+;; Like arith_reg_dest, but this predicate is defined with
+;; define_special_predicate, not define_predicate.
+
+(define_special_predicate "any_arith_reg_dest"
+ (match_code "subreg,reg")
+{
+ return arith_reg_dest (op, mode);
+})
+
+;; Like register_operand, but this predicate is defined with
+;; define_special_predicate, not define_predicate.
+
+(define_special_predicate "any_register_operand"
+ (match_code "subreg,reg")
+{
+ return register_operand (op, mode);
+})
+
+;; Returns 1 if OP is a valid source operand for an arithmetic insn.
+
+(define_predicate "arith_operand"
+ (match_code "subreg,reg,const_int,truncate")
+{
+ if (arith_reg_operand (op, mode))
+ return 1;
+
+ if (TARGET_SHMEDIA)
+ {
+ /* FIXME: We should be checking whether the CONST_INT fits in a
+ signed 16-bit here, but this causes reload_cse to crash when
+ attempting to transform a sequence of two 64-bit sets of the
+ same register from literal constants into a set and an add,
+ when the difference is too wide for an add. */
+ if (CONST_INT_P (op)
+ || satisfies_constraint_Css (op))
+ return 1;
+ else if (GET_CODE (op) == TRUNCATE
+ && REG_P (XEXP (op, 0))
+ && ! system_reg_operand (XEXP (op, 0), VOIDmode)
+ && (mode == VOIDmode || mode == GET_MODE (op))
+ && (GET_MODE_SIZE (GET_MODE (op))
+ < GET_MODE_SIZE (GET_MODE (XEXP (op, 0))))
+ && (! FP_REGISTER_P (REGNO (XEXP (op, 0)))
+ || GET_MODE_SIZE (GET_MODE (op)) == 4))
+ return register_operand (XEXP (op, 0), VOIDmode);
+ else
+ return 0;
+ }
+ else if (satisfies_constraint_I08 (op))
+ return 1;
+
+ return 0;
+})
+
+;; Like above, but for DImode destinations: forbid paradoxical DImode
+;; subregs, because this would lead to missing sign extensions when
+;; truncating from DImode to SImode.
+
+(define_predicate "arith_reg_dest"
+ (match_code "subreg,reg")
+{
+ if (mode == DImode && GET_CODE (op) == SUBREG
+ && GET_MODE_SIZE (GET_MODE (SUBREG_REG (op))) < 8
+ && TARGET_SHMEDIA)
+ return 0;
+ return arith_reg_operand (op, mode);
+})
+
+;; Returns 1 if OP is a normal arithmetic register.
+
+(define_predicate "arith_reg_operand"
+ (match_code "subreg,reg,sign_extend")
+{
+ if (register_operand (op, mode))
+ {
+ int regno;
+
+ if (REG_P (op))
+ regno = REGNO (op);
+ else if (GET_CODE (op) == SUBREG && REG_P (SUBREG_REG (op)))
+ regno = REGNO (SUBREG_REG (op));
+ else
+ return 1;
+
+ return (regno != T_REG && regno != PR_REG
+ && ! TARGET_REGISTER_P (regno)
+ && (regno != FPUL_REG || TARGET_SH4)
+ && regno != MACH_REG && regno != MACL_REG);
+ }
+ /* Allow a no-op sign extension - compare LOAD_EXTEND_OP.
+ We allow SImode here, as not using an FP register is just a matter of
+ proper register allocation. */
+ if (TARGET_SHMEDIA
+ && GET_MODE (op) == DImode && GET_CODE (op) == SIGN_EXTEND
+ && GET_MODE (XEXP (op, 0)) == SImode
+ && GET_CODE (XEXP (op, 0)) != SUBREG)
+ return register_operand (XEXP (op, 0), VOIDmode);
+#if 0 /* Can't do this because of PROMOTE_MODE for unsigned vars. */
+ if (GET_MODE (op) == SImode && GET_CODE (op) == SIGN_EXTEND
+ && GET_MODE (XEXP (op, 0)) == HImode
+ && REG_P (XEXP (op, 0))
+ && REGNO (XEXP (op, 0)) <= LAST_GENERAL_REG)
+ return register_operand (XEXP (op, 0), VOIDmode);
+#endif
+ if (GET_MODE_CLASS (GET_MODE (op)) == MODE_VECTOR_INT
+ && GET_CODE (op) == SUBREG
+ && GET_MODE (SUBREG_REG (op)) == DImode
+ && GET_CODE (SUBREG_REG (op)) == SIGN_EXTEND
+ && GET_MODE (XEXP (SUBREG_REG (op), 0)) == SImode
+ && GET_CODE (XEXP (SUBREG_REG (op), 0)) != SUBREG)
+ return register_operand (XEXP (SUBREG_REG (op), 0), VOIDmode);
+ return 0;
+})
+
+;; Returns 1 if OP is a valid source operand for a compare insn.
+
+(define_predicate "arith_reg_or_0_operand"
+ (match_code "subreg,reg,const_int,const_vector")
+{
+ if (arith_reg_operand (op, mode))
+ return 1;
+
+ if (satisfies_constraint_Z (op))
+ return 1;
+
+ return 0;
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "binary_float_operator"
+ (and (match_code "plus,minus,mult,div")
+ (match_test "GET_MODE (op) == mode")))
+
+;; TODO: Add a comment here.
+
+(define_predicate "binary_logical_operator"
+ (and (match_code "and,ior,xor")
+ (match_test "GET_MODE (op) == mode")))
+
+;; Return 1 of OP is an address suitable for a cache manipulation operation.
+;; MODE has the meaning as in address_operand.
+
+(define_special_predicate "cache_address_operand"
+ (match_code "plus,reg")
+{
+ if (GET_CODE (op) == PLUS)
+ {
+ if (!REG_P (XEXP (op, 0)))
+ return 0;
+ if (!CONST_INT_P (XEXP (op, 1))
+ || (INTVAL (XEXP (op, 1)) & 31))
+ return 0;
+ }
+ else if (!REG_P (op))
+ return 0;
+ return address_operand (op, mode);
+})
+
+;; Return 1 if OP is a valid source operand for shmedia cmpgt / cmpgtu.
+
+(define_predicate "cmp_operand"
+ (match_code "subreg,reg,const_int")
+{
+ if (satisfies_constraint_N (op))
+ return 1;
+ if (TARGET_SHMEDIA
+ && mode != DImode && GET_CODE (op) == SUBREG
+ && GET_MODE_SIZE (GET_MODE (SUBREG_REG (op))) > 4)
+ return 0;
+ return arith_reg_operand (op, mode);
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "cmpsi_operand"
+ (match_code "subreg,reg,const_int")
+{
+ if (REG_P (op) && REGNO (op) == T_REG
+ && GET_MODE (op) == SImode
+ && TARGET_SH1)
+ return 1;
+ return arith_operand (op, mode);
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "commutative_float_operator"
+ (and (match_code "plus,mult")
+ (match_test "GET_MODE (op) == mode")))
+
+;; TODO: Add a comment here.
+
+(define_predicate "equality_comparison_operator"
+ (match_code "eq,ne"))
+
+;; TODO: Add a comment here.
+
+(define_predicate "extend_reg_operand"
+ (match_code "subreg,reg,truncate")
+{
+ return (GET_CODE (op) == TRUNCATE
+ ? arith_operand
+ : arith_reg_operand) (op, mode);
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "extend_reg_or_0_operand"
+ (match_code "subreg,reg,truncate,const_int")
+{
+ return (GET_CODE (op) == TRUNCATE
+ ? arith_operand
+ : arith_reg_or_0_operand) (op, mode);
+})
+
+;; Like arith_reg_operand, but this predicate does not accept SIGN_EXTEND.
+
+(define_predicate "ext_dest_operand"
+ (match_code "subreg,reg")
+{
+ return arith_reg_operand (op, mode);
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "fp_arith_reg_dest"
+ (match_code "subreg,reg")
+{
+ if (mode == DImode && GET_CODE (op) == SUBREG
+ && GET_MODE_SIZE (GET_MODE (SUBREG_REG (op))) < 8)
+ return 0;
+ return fp_arith_reg_operand (op, mode);
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "fp_arith_reg_operand"
+ (match_code "subreg,reg")
+{
+ if (register_operand (op, mode))
+ {
+ int regno;
+
+ if (REG_P (op))
+ regno = REGNO (op);
+ else if (GET_CODE (op) == SUBREG && REG_P (SUBREG_REG (op)))
+ regno = REGNO (SUBREG_REG (op));
+ else
+ return 1;
+
+ return (regno >= FIRST_PSEUDO_REGISTER
+ || FP_REGISTER_P (regno));
+ }
+ return 0;
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "fpscr_operand"
+ (match_code "reg")
+{
+ return (REG_P (op)
+ && (REGNO (op) == FPSCR_REG
+ || (REGNO (op) >= FIRST_PSEUDO_REGISTER
+ && !(reload_in_progress || reload_completed)))
+ && GET_MODE (op) == PSImode);
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "fpul_operand"
+ (match_code "reg")
+{
+ if (TARGET_SHMEDIA)
+ return fp_arith_reg_operand (op, mode);
+
+ return (REG_P (op)
+ && (REGNO (op) == FPUL_REG || REGNO (op) >= FIRST_PSEUDO_REGISTER)
+ && GET_MODE (op) == mode);
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "general_extend_operand"
+ (match_code "subreg,reg,mem,truncate")
+{
+ return (GET_CODE (op) == TRUNCATE
+ ? arith_operand
+ : nonimmediate_operand) (op, mode);
+})
+
+;; Returns 1 if OP can be source of a simple move operation. Same as
+;; general_operand, but a LABEL_REF is valid, PRE_DEC is invalid as
+;; are subregs of system registers.
+
+(define_predicate "general_movsrc_operand"
+ (match_code "subreg,reg,const_int,const_double,mem,symbol_ref,label_ref,const,const_vector")
+{
+ if (MEM_P (op))
+ {
+ rtx inside = XEXP (op, 0);
+ if (GET_CODE (inside) == CONST)
+ inside = XEXP (inside, 0);
+
+ if (GET_CODE (inside) == LABEL_REF)
+ return 1;
+
+ if (GET_CODE (inside) == PLUS
+ && GET_CODE (XEXP (inside, 0)) == LABEL_REF
+ && CONST_INT_P (XEXP (inside, 1)))
+ return 1;
+
+ /* Only post inc allowed. */
+ if (GET_CODE (inside) == PRE_DEC)
+ return 0;
+ }
+
+ if (TARGET_SHMEDIA
+ && (GET_CODE (op) == PARALLEL || GET_CODE (op) == CONST_VECTOR)
+ && sh_rep_vec (op, mode))
+ return 1;
+ if (TARGET_SHMEDIA && 1
+ && GET_CODE (op) == SUBREG && GET_MODE (op) == mode
+ && SUBREG_REG (op) == const0_rtx && subreg_lowpart_p (op))
+ /* FIXME */ abort (); /* return 1; */
+ return general_operand (op, mode);
+})
+
+;; Returns 1 if OP can be a destination of a move. Same as
+;; general_operand, but no preinc allowed.
+
+(define_predicate "general_movdst_operand"
+ (match_code "subreg,reg,mem")
+{
+ /* Only pre dec allowed. */
+ if (MEM_P (op) && GET_CODE (XEXP (op, 0)) == POST_INC)
+ return 0;
+ if (mode == DImode && TARGET_SHMEDIA && GET_CODE (op) == SUBREG
+ && GET_MODE_SIZE (GET_MODE (SUBREG_REG (op))) < 8
+ && ! (high_life_started || reload_completed))
+ return 0;
+
+ return general_operand (op, mode);
+})
+
+
+;; Returns 1 if OP is a POST_INC on stack pointer register.
+
+(define_predicate "sh_no_delay_pop_operand"
+ (match_code "mem")
+{
+ rtx inside;
+ inside = XEXP (op, 0);
+
+ if (GET_CODE (op) == MEM && GET_MODE (op) == SImode
+ && GET_CODE (inside) == POST_INC
+ && GET_CODE (XEXP (inside, 0)) == REG
+ && REGNO (XEXP (inside, 0)) == SP_REG)
+ return 1;
+
+ return 0;
+})
+
+
+;; Returns 1 if OP is a MEM that can be source of a simple move operation.
+
+(define_predicate "unaligned_load_operand"
+ (match_code "mem")
+{
+ rtx inside;
+
+ if (!MEM_P (op) || GET_MODE (op) != mode)
+ return 0;
+
+ inside = XEXP (op, 0);
+
+ if (GET_CODE (inside) == POST_INC)
+ inside = XEXP (inside, 0);
+
+ if (REG_P (inside))
+ return 1;
+
+ return 0;
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "greater_comparison_operator"
+ (match_code "gt,ge,gtu,geu"))
+
+;; TODO: Add a comment here.
+
+(define_predicate "inqhi_operand"
+ (match_code "truncate")
+{
+ if (GET_CODE (op) != TRUNCATE || mode != GET_MODE (op))
+ return 0;
+ op = XEXP (op, 0);
+ /* Can't use true_regnum here because copy_cost wants to know about
+ SECONDARY_INPUT_RELOAD_CLASS. */
+ return REG_P (op) && FP_REGISTER_P (REGNO (op));
+})
+
+;; TODO: Add a comment here.
+
+(define_special_predicate "int_gpr_dest"
+ (match_code "subreg,reg")
+{
+ enum machine_mode op_mode = GET_MODE (op);
+
+ if (GET_MODE_CLASS (op_mode) != MODE_INT
+ || GET_MODE_SIZE (op_mode) >= UNITS_PER_WORD)
+ return 0;
+ if (! reload_completed)
+ return 0;
+ return true_regnum (op) <= LAST_GENERAL_REG;
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "less_comparison_operator"
+ (match_code "lt,le,ltu,leu"))
+
+;; Returns 1 if OP is a valid source operand for a logical operation.
+
+(define_predicate "logical_operand"
+ (match_code "subreg,reg,const_int")
+{
+ if (TARGET_SHMEDIA
+ && mode != DImode && GET_CODE (op) == SUBREG
+ && GET_MODE_SIZE (GET_MODE (SUBREG_REG (op))) > 4)
+ return 0;
+
+ if (arith_reg_operand (op, mode))
+ return 1;
+
+ if (TARGET_SHMEDIA)
+ {
+ if (satisfies_constraint_I10 (op))
+ return 1;
+ else
+ return 0;
+ }
+ else if (satisfies_constraint_K08 (op))
+ return 1;
+
+ return 0;
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "logical_operator"
+ (match_code "and,ior,xor"))
+
+;; Like arith_reg_operand, but for register source operands of narrow
+;; logical SHMEDIA operations: forbid subregs of DImode / TImode regs.
+
+(define_predicate "logical_reg_operand"
+ (match_code "subreg,reg")
+{
+ if (TARGET_SHMEDIA
+ && GET_CODE (op) == SUBREG
+ && GET_MODE_SIZE (GET_MODE (SUBREG_REG (op))) > 4
+ && mode != DImode)
+ return 0;
+ return arith_reg_operand (op, mode);
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "mextr_bit_offset"
+ (match_code "const_int")
+{
+ HOST_WIDE_INT i;
+
+ if (!CONST_INT_P (op))
+ return 0;
+ i = INTVAL (op);
+ return i >= 1 * 8 && i <= 7 * 8 && (i & 7) == 0;
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "minuend_operand"
+ (match_code "subreg,reg,truncate,const_int")
+{
+ return op == constm1_rtx || extend_reg_or_0_operand (op, mode);
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "noncommutative_float_operator"
+ (and (match_code "minus,div")
+ (match_test "GET_MODE (op) == mode")))
+
+;; UNORDERED is only supported on SHMEDIA.
+
+(define_predicate "sh_float_comparison_operator"
+ (ior (match_operand 0 "ordered_comparison_operator")
+ (and (match_test "TARGET_SHMEDIA")
+ (match_code "unordered"))))
+
+(define_predicate "shmedia_cbranch_comparison_operator"
+ (ior (match_operand 0 "equality_comparison_operator")
+ (match_operand 0 "greater_comparison_operator")))
+
+;; TODO: Add a comment here.
+
+(define_predicate "sh_const_vec"
+ (match_code "const_vector")
+{
+ int i;
+
+ if (GET_CODE (op) != CONST_VECTOR
+ || (GET_MODE (op) != mode && mode != VOIDmode))
+ return 0;
+ i = XVECLEN (op, 0) - 1;
+ for (; i >= 0; i--)
+ if (!CONST_INT_P (XVECEXP (op, 0, i)))
+ return 0;
+ return 1;
+})
+
+;; Determine if OP is a constant vector matching MODE with only one
+;; element that is not a sign extension. Two byte-sized elements
+;; count as one.
+
+(define_predicate "sh_1el_vec"
+ (match_code "const_vector")
+{
+ int unit_size;
+ int i, last, least, sign_ix;
+ rtx sign;
+
+ if (GET_CODE (op) != CONST_VECTOR
+ || (GET_MODE (op) != mode && mode != VOIDmode))
+ return 0;
+ /* Determine numbers of last and of least significant elements. */
+ last = XVECLEN (op, 0) - 1;
+ least = TARGET_LITTLE_ENDIAN ? 0 : last;
+ if (!CONST_INT_P (XVECEXP (op, 0, least)))
+ return 0;
+ sign_ix = least;
+ if (GET_MODE_UNIT_SIZE (mode) == 1)
+ sign_ix = TARGET_LITTLE_ENDIAN ? 1 : last - 1;
+ if (!CONST_INT_P (XVECEXP (op, 0, sign_ix)))
+ return 0;
+ unit_size = GET_MODE_UNIT_SIZE (GET_MODE (op));
+ sign = (INTVAL (XVECEXP (op, 0, sign_ix)) >> (unit_size * BITS_PER_UNIT - 1)
+ ? constm1_rtx : const0_rtx);
+ i = XVECLEN (op, 0) - 1;
+ do
+ if (i != least && i != sign_ix && XVECEXP (op, 0, i) != sign)
+ return 0;
+ while (--i);
+ return 1;
+})
+
+;; Like register_operand, but take into account that SHMEDIA can use
+;; the constant zero like a general register.
+
+(define_predicate "sh_register_operand"
+ (match_code "reg,subreg,const_int,const_double")
+{
+ if (op == CONST0_RTX (mode) && TARGET_SHMEDIA)
+ return 1;
+ return register_operand (op, mode);
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "sh_rep_vec"
+ (match_code "const_vector,parallel")
+{
+ int i;
+ rtx x, y;
+
+ if ((GET_CODE (op) != CONST_VECTOR && GET_CODE (op) != PARALLEL)
+ || (GET_MODE (op) != mode && mode != VOIDmode))
+ return 0;
+ i = XVECLEN (op, 0) - 2;
+ x = XVECEXP (op, 0, i + 1);
+ if (GET_MODE_UNIT_SIZE (mode) == 1)
+ {
+ y = XVECEXP (op, 0, i);
+ for (i -= 2; i >= 0; i -= 2)
+ if (! rtx_equal_p (XVECEXP (op, 0, i + 1), x)
+ || ! rtx_equal_p (XVECEXP (op, 0, i), y))
+ return 0;
+ }
+ else
+ for (; i >= 0; i--)
+ if (XVECEXP (op, 0, i) != x)
+ return 0;
+ return 1;
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "shift_count_operand"
+ (match_code "const_int,const_double,const,symbol_ref,label_ref,subreg,reg,zero_extend,sign_extend")
+{
+ return (CONSTANT_P (op)
+ ? (CONST_INT_P (op)
+ ? (unsigned) INTVAL (op) < GET_MODE_BITSIZE (mode)
+ : nonmemory_operand (op, mode))
+ : shift_count_reg_operand (op, mode));
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "shift_count_reg_operand"
+ (match_code "subreg,reg,zero_extend,sign_extend")
+{
+ if ((GET_CODE (op) == ZERO_EXTEND || GET_CODE (op) == SIGN_EXTEND
+ || (GET_CODE (op) == SUBREG && SUBREG_BYTE (op) == 0))
+ && (mode == VOIDmode || mode == GET_MODE (op))
+ && GET_MODE_BITSIZE (GET_MODE (XEXP (op, 0))) >= 6
+ && GET_MODE_CLASS (GET_MODE (XEXP (op, 0))) == MODE_INT)
+ {
+ mode = VOIDmode;
+ do
+ op = XEXP (op, 0);
+ while ((GET_CODE (op) == ZERO_EXTEND || GET_CODE (op) == SIGN_EXTEND
+ || GET_CODE (op) == TRUNCATE)
+ && GET_MODE_BITSIZE (GET_MODE (XEXP (op, 0))) >= 6
+ && GET_MODE_CLASS (GET_MODE (XEXP (op, 0))) == MODE_INT);
+
+ }
+ return arith_reg_operand (op, mode);
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "shift_operator"
+ (match_code "ashift,ashiftrt,lshiftrt"))
+
+;; TODO: Add a comment here.
+
+(define_predicate "symbol_ref_operand"
+ (match_code "symbol_ref"))
+
+;; Same as target_reg_operand, except that label_refs and symbol_refs
+;; are accepted before reload.
+
+(define_special_predicate "target_operand"
+ (match_code "subreg,reg,label_ref,symbol_ref,const,unspec")
+{
+ if (mode != VOIDmode && mode != Pmode)
+ return 0;
+
+ if ((GET_MODE (op) == Pmode || GET_MODE (op) == VOIDmode)
+ && satisfies_constraint_Csy (op))
+ return ! reload_completed;
+
+ return target_reg_operand (op, mode);
+})
+
+;; Accept pseudos and branch target registers.
+
+(define_special_predicate "target_reg_operand"
+ (match_code "subreg,reg")
+{
+ if (mode == VOIDmode
+ ? GET_MODE (op) != Pmode && GET_MODE (op) != PDImode
+ : mode != GET_MODE (op))
+ return 0;
+
+ if (GET_CODE (op) == SUBREG)
+ op = XEXP (op, 0);
+
+ if (!REG_P (op))
+ return 0;
+
+ /* We must protect ourselves from matching pseudos that are virtual
+ register, because they will eventually be replaced with hardware
+ registers that aren't branch-target registers. */
+ if (REGNO (op) > LAST_VIRTUAL_REGISTER
+ || TARGET_REGISTER_P (REGNO (op)))
+ return 1;
+
+ return 0;
+})
+
+;; TODO: Add a comment here.
+
+(define_special_predicate "trunc_hi_operand"
+ (match_code "subreg,reg,truncate")
+{
+ enum machine_mode op_mode = GET_MODE (op);
+
+ if (op_mode != SImode && op_mode != DImode
+ && op_mode != V4HImode && op_mode != V2SImode)
+ return 0;
+ return extend_reg_operand (op, mode);
+})
+
+;; Return 1 of OP is an address suitable for an unaligned access instruction.
+
+(define_special_predicate "ua_address_operand"
+ (match_code "subreg,reg,plus")
+{
+ if (GET_CODE (op) == PLUS
+ && (! satisfies_constraint_I06 (XEXP (op, 1))))
+ return 0;
+ return address_operand (op, QImode);
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "ua_offset"
+ (match_code "const_int")
+{
+ return satisfies_constraint_I06 (op);
+})
+
+;; TODO: Add a comment here.
+
+(define_predicate "unary_float_operator"
+ (and (match_code "abs,neg,sqrt")
+ (match_test "GET_MODE (op) == mode")))
+
+;; Return 1 if OP is a valid source operand for xor.
+
+(define_predicate "xor_operand"
+ (match_code "subreg,reg,const_int")
+{
+ if (CONST_INT_P (op))
+ return (TARGET_SHMEDIA
+ ? (satisfies_constraint_I06 (op)
+ || (!can_create_pseudo_p () && INTVAL (op) == 0xff))
+ : satisfies_constraint_K08 (op));
+ if (TARGET_SHMEDIA
+ && mode != DImode && GET_CODE (op) == SUBREG
+ && GET_MODE_SIZE (GET_MODE (SUBREG_REG (op))) > 4)
+ return 0;
+ return arith_reg_operand (op, mode);
+})
+
+(define_predicate "bitwise_memory_operand"
+ (match_code "mem")
+{
+ if (MEM_P (op))
+ {
+ if (REG_P (XEXP (op, 0)))
+ return 1;
+
+ if (GET_CODE (XEXP (op, 0)) == PLUS
+ && REG_P (XEXP (XEXP (op, 0), 0))
+ && satisfies_constraint_K12 (XEXP (XEXP (op, 0), 1)))
+ return 1;
+ }
+ return 0;
+})
diff --git a/gcc/config/sh/rtems.h b/gcc/config/sh/rtems.h
new file mode 100644
index 000000000..61fab07e0
--- /dev/null
+++ b/gcc/config/sh/rtems.h
@@ -0,0 +1,26 @@
+/* Definitions for rtems targeting a SH using COFF.
+ Copyright (C) 1997, 1998, 2000, 2002, 2007 Free Software Foundation, Inc.
+ Contributed by Joel Sherrill (joel@OARcorp.com).
+
+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
+<http://www.gnu.org/licenses/>. */
+
+/* Specify predefined symbols in preprocessor. */
+
+#define TARGET_OS_CPP_BUILTINS() do { \
+ builtin_define( "__rtems__" ); \
+ builtin_assert( "system=rtems" ); \
+} while (0)
diff --git a/gcc/config/sh/rtemself.h b/gcc/config/sh/rtemself.h
new file mode 100644
index 000000000..aba98f686
--- /dev/null
+++ b/gcc/config/sh/rtemself.h
@@ -0,0 +1,26 @@
+/* Definitions for rtems targeting a SH using elf.
+ Copyright (C) 1997, 1998, 2000, 2002, 2007 Free Software Foundation, Inc.
+ Contributed by Joel Sherrill (joel@OARcorp.com).
+
+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
+<http://www.gnu.org/licenses/>. */
+
+/* Specify predefined symbols in preprocessor. */
+
+#define TARGET_OS_CPP_BUILTINS() do { \
+ builtin_define( "__rtems__" ); \
+ builtin_assert( "system=rtems" ); \
+} while (0)
diff --git a/gcc/config/sh/sh-c.c b/gcc/config/sh/sh-c.c
new file mode 100644
index 000000000..2fdff542b
--- /dev/null
+++ b/gcc/config/sh/sh-c.c
@@ -0,0 +1,68 @@
+/* Pragma handling for GCC for Renesas / SuperH SH.
+ Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+ 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+ Contributed by Joern Rennecke <joern.rennecke@st.com>.
+
+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
+<http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "tm_p.h"
+
+/* Handle machine specific pragmas to be semi-compatible with Renesas
+ compiler. */
+
+/* Add ATTR to the attributes of the current function. If there is no
+ such function, save it to be added to the attributes of the next
+ function. */
+static void
+sh_add_function_attribute (const char *attr)
+{
+ tree id = get_identifier (attr);
+
+ if (current_function_decl)
+ decl_attributes (&current_function_decl,
+ tree_cons (id, NULL_TREE, NULL_TREE), 0);
+ else
+ {
+ *sh_deferred_function_attributes_tail
+ = tree_cons (id, NULL_TREE, *sh_deferred_function_attributes_tail);
+ sh_deferred_function_attributes_tail
+ = &TREE_CHAIN (*sh_deferred_function_attributes_tail);
+ }
+}
+
+void
+sh_pr_interrupt (struct cpp_reader *pfile ATTRIBUTE_UNUSED)
+{
+ sh_add_function_attribute ("interrupt_handler");
+}
+
+void
+sh_pr_trapa (struct cpp_reader *pfile ATTRIBUTE_UNUSED)
+{
+ sh_add_function_attribute ("trapa_handler");
+}
+
+void
+sh_pr_nosave_low_regs (struct cpp_reader *pfile ATTRIBUTE_UNUSED)
+{
+ sh_add_function_attribute ("nosave_low_regs");
+}
diff --git a/gcc/config/sh/sh-modes.def b/gcc/config/sh/sh-modes.def
new file mode 100644
index 000000000..98e974a1a
--- /dev/null
+++ b/gcc/config/sh/sh-modes.def
@@ -0,0 +1,34 @@
+/* SH extra machine modes.
+ Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 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
+<http://www.gnu.org/licenses/>. */
+
+/* The SH uses a partial integer mode to represent the FPSCR register. */
+PARTIAL_INT_MODE (SI);
+/* PDI mode is used to represent a function address in a target register. */
+PARTIAL_INT_MODE (DI);
+
+/* Vector modes. */
+VECTOR_MODE (INT, QI, 2); /* V2QI */
+VECTOR_MODES (INT, 4); /* V4QI V2HI */
+VECTOR_MODES (INT, 8); /* V8QI V4HI V2SI */
+VECTOR_MODES (INT, 16); /* V16QI V8HI V4SI V2DI */
+VECTOR_MODES (FLOAT, 8); /* V4HF V2SF */
+VECTOR_MODES (FLOAT, 16); /* V8HF V4SF V2DF */
+VECTOR_MODE (INT, DI, 4); /* V4DI */
+VECTOR_MODE (INT, DI, 8); /* V8DI */
+VECTOR_MODE (FLOAT, SF, 16); /* V16SF */
diff --git a/gcc/config/sh/sh-protos.h b/gcc/config/sh/sh-protos.h
new file mode 100644
index 000000000..d29147c98
--- /dev/null
+++ b/gcc/config/sh/sh-protos.h
@@ -0,0 +1,186 @@
+/* Definitions of target machine for GNU compiler for Renesas / SuperH SH.
+ Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2003,
+ 2004, 2005, 2006, 2007, 2008, 2009, 2010
+ Free Software Foundation, Inc.
+ Contributed by Steve Chamberlain (sac@cygnus.com).
+ Improved by Jim Wilson (wilson@cygnus.com).
+
+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
+<http://www.gnu.org/licenses/>. */
+
+#ifndef GCC_SH_PROTOS_H
+#define GCC_SH_PROTOS_H
+
+enum sh_function_kind {
+ /* A function with normal C ABI */
+ FUNCTION_ORDINARY,
+ /* A special function that guarantees that some otherwise call-clobbered
+ registers are not clobbered. These can't go through the SH5 resolver,
+ because it only saves argument passing registers. */
+ SFUNC_GOT,
+ /* A special function that should be linked statically. These are typically
+ smaller or not much larger than a PLT entry.
+ Some also have a non-standard ABI which precludes dynamic linking. */
+ SFUNC_STATIC
+};
+
+#ifdef RTX_CODE
+extern rtx sh_fsca_sf2int (void);
+extern rtx sh_fsca_df2int (void);
+extern rtx sh_fsca_int2sf (void);
+
+/* Declare functions defined in sh.c and used in templates. */
+
+extern const char *output_branch (int, rtx, rtx *);
+extern const char *output_ieee_ccmpeq (rtx, rtx *);
+extern const char *output_branchy_insn (enum rtx_code, const char *, rtx, rtx *);
+extern const char *output_movedouble (rtx, rtx[], enum machine_mode);
+extern const char *output_movepcrel (rtx, rtx[], enum machine_mode);
+extern const char *output_far_jump (rtx, rtx);
+
+extern struct rtx_def *sfunc_uses_reg (rtx);
+extern int barrier_align (rtx);
+extern int sh_loop_align (rtx);
+extern int fp_zero_operand (rtx);
+extern int fp_one_operand (rtx);
+extern int fp_int_operand (rtx);
+extern rtx get_fpscr_rtx (void);
+extern bool sh_legitimate_index_p (enum machine_mode, rtx);
+extern bool sh_legitimize_reload_address (rtx *, enum machine_mode, int, int);
+extern rtx legitimize_pic_address (rtx, enum machine_mode, rtx);
+extern int nonpic_symbol_mentioned_p (rtx);
+extern void emit_sf_insn (rtx);
+extern void emit_df_insn (rtx);
+extern void output_pic_addr_const (FILE *, rtx);
+extern int expand_block_move (rtx *);
+extern int prepare_move_operands (rtx[], enum machine_mode mode);
+extern enum rtx_code prepare_cbranch_operands (rtx *, enum machine_mode mode,
+ enum rtx_code comparison);
+extern void expand_cbranchsi4 (rtx *operands, enum rtx_code comparison, int);
+extern bool expand_cbranchdi4 (rtx *operands, enum rtx_code comparison);
+extern void sh_emit_scc_to_t (enum rtx_code, rtx, rtx);
+extern rtx sh_emit_cheap_store_flag (enum machine_mode, enum rtx_code, rtx, rtx);
+extern void sh_emit_compare_and_branch (rtx *, enum machine_mode);
+extern void sh_emit_compare_and_set (rtx *, enum machine_mode);
+extern int shift_insns_rtx (rtx);
+extern void gen_ashift (int, int, rtx);
+extern void gen_ashift_hi (int, int, rtx);
+extern void gen_shifty_op (int, rtx *);
+extern void gen_shifty_hi_op (int, rtx *);
+extern int expand_ashiftrt (rtx *);
+extern int sh_dynamicalize_shift_p (rtx);
+extern int shl_and_kind (rtx, rtx, int *);
+extern int shl_and_length (rtx);
+extern int shl_and_scr_length (rtx);
+extern int gen_shl_and (rtx, rtx, rtx, rtx);
+extern int shl_sext_kind (rtx, rtx, int *);
+extern int shl_sext_length (rtx);
+extern int gen_shl_sext (rtx, rtx, rtx, rtx);
+extern rtx gen_datalabel_ref (rtx);
+extern int regs_used (rtx, int);
+extern void fixup_addr_diff_vecs (rtx);
+extern int get_dest_uid (rtx, int);
+extern void final_prescan_insn (rtx, rtx *, int);
+extern int symbol_ref_operand (rtx, enum machine_mode);
+extern enum tls_model tls_symbolic_operand (rtx, enum machine_mode);
+extern int system_reg_operand (rtx, enum machine_mode);
+extern int general_movsrc_operand (rtx, enum machine_mode);
+extern int general_movdst_operand (rtx, enum machine_mode);
+extern int arith_reg_operand (rtx, enum machine_mode);
+extern int fp_arith_reg_operand (rtx, enum machine_mode);
+extern int arith_operand (rtx, enum machine_mode);
+extern int arith_reg_or_0_operand (rtx, enum machine_mode);
+extern int logical_operand (rtx, enum machine_mode);
+extern int tertiary_reload_operand (rtx, enum machine_mode);
+extern int fpscr_operand (rtx, enum machine_mode);
+extern int fpul_operand (rtx, enum machine_mode);
+extern int commutative_float_operator (rtx, enum machine_mode);
+extern int noncommutative_float_operator (rtx, enum machine_mode);
+extern int reg_unused_after (rtx, rtx);
+extern void expand_sf_unop (rtx (*)(rtx, rtx, rtx), rtx *);
+extern void expand_sf_binop (rtx (*)(rtx, rtx, rtx, rtx), rtx *);
+extern void expand_df_unop (rtx (*)(rtx, rtx, rtx), rtx *);
+extern void expand_df_binop (rtx (*)(rtx, rtx, rtx, rtx), rtx *);
+extern void expand_fp_branch (rtx (*)(void), rtx (*)(void));
+extern int sh_insn_length_adjustment (rtx);
+extern int sh_can_redirect_branch (rtx, rtx);
+extern void sh_expand_unop_v2sf (enum rtx_code, rtx, rtx);
+extern void sh_expand_binop_v2sf (enum rtx_code, rtx, rtx, rtx);
+extern int sh_expand_t_scc (rtx *);
+extern rtx sh_gen_truncate (enum machine_mode, rtx, int);
+extern bool sh_vector_mode_supported_p (enum machine_mode);
+#endif /* RTX_CODE */
+
+extern const char *output_jump_label_table (void);
+extern int sh_handle_pragma (int (*)(void), void (*)(int), const char *);
+extern struct rtx_def *get_fpscr_rtx (void);
+extern int sh_media_register_for_return (void);
+extern void sh_expand_prologue (void);
+extern void sh_expand_epilogue (bool);
+extern int sh_need_epilogue (void);
+extern void sh_set_return_address (rtx, rtx);
+extern int initial_elimination_offset (int, int);
+extern int fldi_ok (void);
+extern int sh_hard_regno_rename_ok (unsigned int, unsigned int);
+extern int sh_cfun_interrupt_handler_p (void);
+extern int sh_cfun_resbank_handler_p (void);
+extern int sh_attr_renesas_p (const_tree);
+extern int sh_cfun_attr_renesas_p (void);
+extern bool sh_cannot_change_mode_class
+ (enum machine_mode, enum machine_mode, enum reg_class);
+extern bool sh_small_register_classes_for_mode_p (enum machine_mode);
+extern void sh_mark_label (rtx, int);
+extern int check_use_sfunc_addr (rtx, rtx);
+
+#ifdef HARD_CONST
+extern void fpscr_set_from_mem (int, HARD_REG_SET);
+#endif
+
+extern void sh_pr_interrupt (struct cpp_reader *);
+extern void sh_pr_trapa (struct cpp_reader *);
+extern void sh_pr_nosave_low_regs (struct cpp_reader *);
+extern rtx function_symbol (rtx, const char *, enum sh_function_kind);
+extern rtx sh_get_pr_initial_val (void);
+
+extern int sh_pass_in_reg_p (CUMULATIVE_ARGS *, enum machine_mode, tree);
+extern void sh_init_cumulative_args (CUMULATIVE_ARGS *, tree, rtx, tree, signed int, enum machine_mode);
+extern rtx sh_dwarf_register_span (rtx);
+
+extern rtx replace_n_hard_rtx (rtx, rtx *, int , int);
+extern int shmedia_cleanup_truncate (rtx *, void *);
+
+extern int sh_contains_memref_p (rtx);
+extern int sh_loads_bankedreg_p (rtx);
+extern rtx shmedia_prepare_call_address (rtx fnaddr, int is_sibcall);
+extern int sh2a_get_function_vector_number (rtx);
+extern int sh2a_is_function_vector_call (rtx);
+extern void sh_fix_range (const char *);
+extern bool sh_hard_regno_mode_ok (unsigned int, enum machine_mode);
+#endif /* ! GCC_SH_PROTOS_H */
+
+#ifdef SYMBIAN
+extern const char * sh_symbian_strip_name_encoding (const char *);
+extern bool sh_symbian_is_dllexported_name (const char *);
+#ifdef TREE_CODE
+extern bool sh_symbian_is_dllexported (tree);
+extern int sh_symbian_import_export_class (tree, int);
+extern tree sh_symbian_handle_dll_attribute (tree *, tree, tree, int, bool *);
+#ifdef RTX_CODE
+extern void sh_symbian_encode_section_info (tree, rtx, int);
+#endif
+#endif
+#endif /* SYMBIAN */
+
diff --git a/gcc/config/sh/sh-symbian.h b/gcc/config/sh/sh-symbian.h
new file mode 100644
index 000000000..2e37d2bbc
--- /dev/null
+++ b/gcc/config/sh/sh-symbian.h
@@ -0,0 +1,42 @@
+/* header file for GCC for a Symbian OS targeted SH backend.
+ Copyright (C) 2004, 2005, 2007, 2009 Free Software Foundation, Inc.
+ Contributed by RedHat.
+ Most of this code is stolen from i386/winnt.c.
+
+ 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
+ <http://www.gnu.org/licenses/>. */
+
+/* A unique character to encode declspec encoded objects. */
+#define SH_SYMBIAN_FLAG_CHAR "$"
+
+/* Unique strings to prefix exported and imported objects. */
+#define DLL_IMPORT_PREFIX SH_SYMBIAN_FLAG_CHAR "i."
+#define DLL_EXPORT_PREFIX SH_SYMBIAN_FLAG_CHAR "e."
+
+/* Select the level of debugging information to display.
+ 0 for no debugging.
+ 1 for informative messages about decisions to add attributes
+ 2 for verbose information about what is being done. */
+#define SYMBIAN_DEBUG 0
+/* #define SYMBIAN_DEBUG 1 */
+/* #define SYMBIAN_DEBUG 2 */
+
+/* Functions exported from symbian-base.c. */
+extern tree sh_symbian_associated_type (tree);
+
+/* Functions exported from symbian-[c|c++].c. */
+extern bool sh_symbian_is_dllimported (tree);
+
diff --git a/gcc/config/sh/sh.c b/gcc/config/sh/sh.c
new file mode 100644
index 000000000..11e537b1f
--- /dev/null
+++ b/gcc/config/sh/sh.c
@@ -0,0 +1,12610 @@
+/* Output routines for GCC for Renesas / SuperH SH.
+ Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+ 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+ Free Software Foundation, Inc.
+ Contributed by Steve Chamberlain (sac@cygnus.com).
+ Improved by Jim Wilson (wilson@cygnus.com).
+
+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
+<http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "insn-config.h"
+#include "rtl.h"
+#include "tree.h"
+#include "flags.h"
+#include "expr.h"
+#include "optabs.h"
+#include "reload.h"
+#include "function.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "output.h"
+#include "insn-attr.h"
+#include "diagnostic-core.h"
+#include "recog.h"
+#include "integrate.h"
+#include "dwarf2.h"
+#include "tm_p.h"
+#include "target.h"
+#include "target-def.h"
+#include "langhooks.h"
+#include "basic-block.h"
+#include "df.h"
+#include "cfglayout.h"
+#include "intl.h"
+#include "sched-int.h"
+#include "params.h"
+#include "ggc.h"
+#include "gimple.h"
+#include "cfgloop.h"
+#include "alloc-pool.h"
+#include "tm-constrs.h"
+
+
+int code_for_indirect_jump_scratch = CODE_FOR_indirect_jump_scratch;
+
+#define MSW (TARGET_LITTLE_ENDIAN ? 1 : 0)
+#define LSW (TARGET_LITTLE_ENDIAN ? 0 : 1)
+
+/* These are some macros to abstract register modes. */
+#define CONST_OK_FOR_ADD(size) \
+ (TARGET_SHMEDIA ? CONST_OK_FOR_I10 (size) : CONST_OK_FOR_I08 (size))
+#define GEN_MOV (*(TARGET_SHMEDIA64 ? gen_movdi : gen_movsi))
+#define GEN_ADD3 (*(TARGET_SHMEDIA64 ? gen_adddi3 : gen_addsi3))
+#define GEN_SUB3 (*(TARGET_SHMEDIA64 ? gen_subdi3 : gen_subsi3))
+
+/* Used to simplify the logic below. Find the attributes wherever
+ they may be. */
+#define SH_ATTRIBUTES(decl) \
+ (TYPE_P (decl)) ? TYPE_ATTRIBUTES (decl) \
+ : DECL_ATTRIBUTES (decl) \
+ ? (DECL_ATTRIBUTES (decl)) \
+ : TYPE_ATTRIBUTES (TREE_TYPE (decl))
+
+/* Set to 1 by expand_prologue() when the function is an interrupt handler. */
+int current_function_interrupt;
+
+tree sh_deferred_function_attributes;
+tree *sh_deferred_function_attributes_tail = &sh_deferred_function_attributes;
+
+/* Global variables for machine-dependent things. */
+
+/* Which cpu are we scheduling for. */
+enum processor_type sh_cpu;
+
+/* Definitions used in ready queue reordering for first scheduling pass. */
+
+/* Reg weights arrays for modes SFmode and SImode, indexed by insn LUID. */
+static short *regmode_weight[2];
+
+/* Total SFmode and SImode weights of scheduled insns. */
+static int curr_regmode_pressure[2];
+
+/* Number of r0 life regions. */
+static int r0_life_regions;
+
+/* If true, skip cycles for Q -> R movement. */
+static int skip_cycles = 0;
+
+/* Cached value of can_issue_more. This is cached in sh_variable_issue hook
+ and returned from sh_reorder2. */
+static short cached_can_issue_more;
+
+/* Unique number for UNSPEC_BBR pattern. */
+static unsigned int unspec_bbr_uid = 1;
+
+/* Provides the class number of the smallest class containing
+ reg number. */
+
+enum reg_class regno_reg_class[FIRST_PSEUDO_REGISTER] =
+{
+ R0_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+ FP0_REGS,FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+ TARGET_REGS, TARGET_REGS, TARGET_REGS, TARGET_REGS,
+ TARGET_REGS, TARGET_REGS, TARGET_REGS, TARGET_REGS,
+ DF_REGS, DF_REGS, DF_REGS, DF_REGS,
+ DF_REGS, DF_REGS, DF_REGS, DF_REGS,
+ NO_REGS, GENERAL_REGS, PR_REGS, T_REGS,
+ MAC_REGS, MAC_REGS, FPUL_REGS, FPSCR_REGS,
+ GENERAL_REGS, GENERAL_REGS,
+};
+
+char sh_register_names[FIRST_PSEUDO_REGISTER] \
+ [MAX_REGISTER_NAME_LENGTH + 1] = SH_REGISTER_NAMES_INITIALIZER;
+
+char sh_additional_register_names[ADDREGNAMES_SIZE] \
+ [MAX_ADDITIONAL_REGISTER_NAME_LENGTH + 1]
+ = SH_ADDITIONAL_REGISTER_NAMES_INITIALIZER;
+
+int assembler_dialect;
+
+static bool shmedia_space_reserved_for_target_registers;
+
+static bool sh_handle_option (size_t, const char *, int);
+static void split_branches (rtx);
+static int branch_dest (rtx);
+static void force_into (rtx, rtx);
+static void print_slot (rtx);
+static rtx add_constant (rtx, enum machine_mode, rtx);
+static void dump_table (rtx, rtx);
+static int hi_const (rtx);
+static int broken_move (rtx);
+static int mova_p (rtx);
+static rtx find_barrier (int, rtx, rtx);
+static int noncall_uses_reg (rtx, rtx, rtx *);
+static rtx gen_block_redirect (rtx, int, int);
+static void sh_reorg (void);
+static void sh_option_override (void);
+static void sh_option_init_struct (struct gcc_options *);
+static void sh_option_default_params (void);
+static void output_stack_adjust (int, rtx, int, HARD_REG_SET *, bool);
+static rtx frame_insn (rtx);
+static rtx push (int);
+static void pop (int);
+static void push_regs (HARD_REG_SET *, int);
+static int calc_live_regs (HARD_REG_SET *);
+static HOST_WIDE_INT rounded_frame_size (int);
+static bool sh_frame_pointer_required (void);
+static rtx mark_constant_pool_use (rtx);
+static tree sh_handle_interrupt_handler_attribute (tree *, tree, tree, int, bool *);
+static tree sh_handle_resbank_handler_attribute (tree *, tree,
+ tree, int, bool *);
+static tree sh2a_handle_function_vector_handler_attribute (tree *, tree,
+ tree, int, bool *);
+static tree sh_handle_sp_switch_attribute (tree *, tree, tree, int, bool *);
+static tree sh_handle_trap_exit_attribute (tree *, tree, tree, int, bool *);
+static tree sh_handle_renesas_attribute (tree *, tree, tree, int, bool *);
+static void sh_print_operand (FILE *, rtx, int);
+static void sh_print_operand_address (FILE *, rtx);
+static bool sh_print_operand_punct_valid_p (unsigned char code);
+static bool sh_asm_output_addr_const_extra (FILE *file, rtx x);
+static void sh_output_function_epilogue (FILE *, HOST_WIDE_INT);
+static void sh_insert_attributes (tree, tree *);
+static const char *sh_check_pch_target_flags (int);
+static int sh_register_move_cost (enum machine_mode, reg_class_t, reg_class_t);
+static int sh_adjust_cost (rtx, rtx, rtx, int);
+static int sh_issue_rate (void);
+static int sh_dfa_new_cycle (FILE *, int, rtx, int, int, int *sort_p);
+static short find_set_regmode_weight (rtx, enum machine_mode);
+static short find_insn_regmode_weight (rtx, enum machine_mode);
+static void find_regmode_weight (basic_block, enum machine_mode);
+static int find_r0_life_regions (basic_block);
+static void sh_md_init_global (FILE *, int, int);
+static void sh_md_finish_global (FILE *, int);
+static int rank_for_reorder (const void *, const void *);
+static void swap_reorder (rtx *, int);
+static void ready_reorder (rtx *, int);
+static short high_pressure (enum machine_mode);
+static int sh_reorder (FILE *, int, rtx *, int *, int);
+static int sh_reorder2 (FILE *, int, rtx *, int *, int);
+static void sh_md_init (FILE *, int, int);
+static int sh_variable_issue (FILE *, int, rtx, int);
+
+static bool sh_function_ok_for_sibcall (tree, tree);
+
+static bool sh_cannot_modify_jumps_p (void);
+static reg_class_t sh_target_reg_class (void);
+static bool sh_optimize_target_register_callee_saved (bool);
+static bool sh_ms_bitfield_layout_p (const_tree);
+
+static void sh_init_builtins (void);
+static tree sh_builtin_decl (unsigned, bool);
+static void sh_media_init_builtins (void);
+static tree sh_media_builtin_decl (unsigned, bool);
+static rtx sh_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
+static void sh_output_mi_thunk (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree);
+static void sh_file_start (void);
+static int flow_dependent_p (rtx, rtx);
+static void flow_dependent_p_1 (rtx, const_rtx, void *);
+static int shiftcosts (rtx);
+static int andcosts (rtx);
+static int addsubcosts (rtx);
+static int multcosts (rtx);
+static bool unspec_caller_rtx_p (rtx);
+static bool sh_cannot_copy_insn_p (rtx);
+static bool sh_rtx_costs (rtx, int, int, int *, bool);
+static int sh_address_cost (rtx, bool);
+static int sh_pr_n_sets (void);
+static rtx sh_allocate_initial_value (rtx);
+static reg_class_t sh_preferred_reload_class (rtx, reg_class_t);
+static reg_class_t sh_secondary_reload (bool, rtx, reg_class_t,
+ enum machine_mode,
+ struct secondary_reload_info *);
+static bool sh_legitimate_address_p (enum machine_mode, rtx, bool);
+static rtx sh_legitimize_address (rtx, rtx, enum machine_mode);
+static rtx sh_delegitimize_address (rtx);
+static int shmedia_target_regs_stack_space (HARD_REG_SET *);
+static int shmedia_reserve_space_for_target_registers_p (int, HARD_REG_SET *);
+static int shmedia_target_regs_stack_adjust (HARD_REG_SET *);
+static int scavenge_reg (HARD_REG_SET *s);
+struct save_schedule_s;
+static struct save_entry_s *sh5_schedule_saves (HARD_REG_SET *,
+ struct save_schedule_s *, int);
+
+static rtx sh_struct_value_rtx (tree, int);
+static rtx sh_function_value (const_tree, const_tree, bool);
+static bool sh_function_value_regno_p (const unsigned int);
+static rtx sh_libcall_value (enum machine_mode, const_rtx);
+static bool sh_return_in_memory (const_tree, const_tree);
+static rtx sh_builtin_saveregs (void);
+static void sh_setup_incoming_varargs (CUMULATIVE_ARGS *, enum machine_mode, tree, int *, int);
+static bool sh_strict_argument_naming (CUMULATIVE_ARGS *);
+static bool sh_pretend_outgoing_varargs_named (CUMULATIVE_ARGS *);
+static tree sh_build_builtin_va_list (void);
+static void sh_va_start (tree, rtx);
+static tree sh_gimplify_va_arg_expr (tree, tree, gimple_seq *, gimple_seq *);
+static bool sh_promote_prototypes (const_tree);
+static enum machine_mode sh_promote_function_mode (const_tree type,
+ enum machine_mode,
+ int *punsignedp,
+ const_tree funtype,
+ int for_return);
+static bool sh_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode,
+ const_tree, bool);
+static bool sh_callee_copies (CUMULATIVE_ARGS *, enum machine_mode,
+ const_tree, bool);
+static int sh_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
+ tree, bool);
+static void sh_function_arg_advance (CUMULATIVE_ARGS *, enum machine_mode,
+ const_tree, bool);
+static rtx sh_function_arg (CUMULATIVE_ARGS *, enum machine_mode,
+ const_tree, bool);
+static bool sh_scalar_mode_supported_p (enum machine_mode);
+static int sh_dwarf_calling_convention (const_tree);
+static void sh_encode_section_info (tree, rtx, int);
+static int sh2a_function_vector_p (tree);
+static void sh_trampoline_init (rtx, tree, rtx);
+static rtx sh_trampoline_adjust_address (rtx);
+static void sh_conditional_register_usage (void);
+
+static const struct attribute_spec sh_attribute_table[] =
+{
+ /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
+ { "interrupt_handler", 0, 0, true, false, false, sh_handle_interrupt_handler_attribute },
+ { "sp_switch", 1, 1, true, false, false, sh_handle_sp_switch_attribute },
+ { "trap_exit", 1, 1, true, false, false, sh_handle_trap_exit_attribute },
+ { "renesas", 0, 0, false, true, false, sh_handle_renesas_attribute },
+ { "trapa_handler", 0, 0, true, false, false, sh_handle_interrupt_handler_attribute },
+ { "nosave_low_regs", 0, 0, true, false, false, sh_handle_interrupt_handler_attribute },
+ { "resbank", 0, 0, true, false, false, sh_handle_resbank_handler_attribute },
+ { "function_vector", 1, 1, true, false, false, sh2a_handle_function_vector_handler_attribute },
+#ifdef SYMBIAN
+ /* Symbian support adds three new attributes:
+ dllexport - for exporting a function/variable that will live in a dll
+ dllimport - for importing a function/variable from a dll
+
+ Microsoft allows multiple declspecs in one __declspec, separating
+ them with spaces. We do NOT support this. Instead, use __declspec
+ multiple times. */
+ { "dllimport", 0, 0, true, false, false, sh_symbian_handle_dll_attribute },
+ { "dllexport", 0, 0, true, false, false, sh_symbian_handle_dll_attribute },
+#endif
+ { NULL, 0, 0, false, false, false, NULL }
+};
+
+/* Set default optimization options. */
+static const struct default_options sh_option_optimization_table[] =
+ {
+ { OPT_LEVELS_1_PLUS, OPT_fomit_frame_pointer, NULL, 1 },
+ { OPT_LEVELS_1_PLUS_SPEED_ONLY, OPT_mdiv_, "inv:minlat", 1 },
+ { OPT_LEVELS_SIZE, OPT_mdiv_, SH_DIV_STR_FOR_SIZE, 1 },
+ { OPT_LEVELS_0_ONLY, OPT_mdiv_, "", 1 },
+ { OPT_LEVELS_SIZE, OPT_mcbranchdi, NULL, 0 },
+ /* We can't meaningfully test TARGET_SHMEDIA here, because -m
+ options haven't been parsed yet, hence we'd read only the
+ default. sh_target_reg_class will return NO_REGS if this is
+ not SHMEDIA, so it's OK to always set
+ flag_branch_target_load_optimize. */
+ { OPT_LEVELS_2_PLUS, OPT_fbranch_target_load_optimize, NULL, 1 },
+ { OPT_LEVELS_NONE, 0, NULL, 0 }
+ };
+
+/* Initialize the GCC target structure. */
+#undef TARGET_ATTRIBUTE_TABLE
+#define TARGET_ATTRIBUTE_TABLE sh_attribute_table
+
+/* The next two are used for debug info when compiling with -gdwarf. */
+#undef TARGET_ASM_UNALIGNED_HI_OP
+#define TARGET_ASM_UNALIGNED_HI_OP "\t.uaword\t"
+#undef TARGET_ASM_UNALIGNED_SI_OP
+#define TARGET_ASM_UNALIGNED_SI_OP "\t.ualong\t"
+
+/* These are NULLed out on non-SH5 in TARGET_OPTION_OVERRIDE. */
+#undef TARGET_ASM_UNALIGNED_DI_OP
+#define TARGET_ASM_UNALIGNED_DI_OP "\t.uaquad\t"
+#undef TARGET_ASM_ALIGNED_DI_OP
+#define TARGET_ASM_ALIGNED_DI_OP "\t.quad\t"
+
+#undef TARGET_OPTION_OVERRIDE
+#define TARGET_OPTION_OVERRIDE sh_option_override
+#undef TARGET_OPTION_OPTIMIZATION_TABLE
+#define TARGET_OPTION_OPTIMIZATION_TABLE sh_option_optimization_table
+#undef TARGET_OPTION_INIT_STRUCT
+#define TARGET_OPTION_INIT_STRUCT sh_option_init_struct
+#undef TARGET_OPTION_DEFAULT_PARAMS
+#define TARGET_OPTION_DEFAULT_PARAMS sh_option_default_params
+
+#undef TARGET_PRINT_OPERAND
+#define TARGET_PRINT_OPERAND sh_print_operand
+#undef TARGET_PRINT_OPERAND_ADDRESS
+#define TARGET_PRINT_OPERAND_ADDRESS sh_print_operand_address
+#undef TARGET_PRINT_OPERAND_PUNCT_VALID_P
+#define TARGET_PRINT_OPERAND_PUNCT_VALID_P sh_print_operand_punct_valid_p
+#undef TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA
+#define TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA sh_asm_output_addr_const_extra
+
+#undef TARGET_ASM_FUNCTION_EPILOGUE
+#define TARGET_ASM_FUNCTION_EPILOGUE sh_output_function_epilogue
+
+#undef TARGET_ASM_OUTPUT_MI_THUNK
+#define TARGET_ASM_OUTPUT_MI_THUNK sh_output_mi_thunk
+
+#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
+#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_const_tree_hwi_hwi_const_tree_true
+
+#undef TARGET_ASM_FILE_START
+#define TARGET_ASM_FILE_START sh_file_start
+#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
+#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
+
+#undef TARGET_DEFAULT_TARGET_FLAGS
+#define TARGET_DEFAULT_TARGET_FLAGS TARGET_DEFAULT
+#undef TARGET_HANDLE_OPTION
+#define TARGET_HANDLE_OPTION sh_handle_option
+
+#undef TARGET_REGISTER_MOVE_COST
+#define TARGET_REGISTER_MOVE_COST sh_register_move_cost
+
+#undef TARGET_INSERT_ATTRIBUTES
+#define TARGET_INSERT_ATTRIBUTES sh_insert_attributes
+
+#undef TARGET_SCHED_ADJUST_COST
+#define TARGET_SCHED_ADJUST_COST sh_adjust_cost
+
+#undef TARGET_SCHED_ISSUE_RATE
+#define TARGET_SCHED_ISSUE_RATE sh_issue_rate
+
+/* The next 5 hooks have been implemented for reenabling sched1. With the
+ help of these macros we are limiting the movement of insns in sched1 to
+ reduce the register pressure. The overall idea is to keep count of SImode
+ and SFmode regs required by already scheduled insns. When these counts
+ cross some threshold values; give priority to insns that free registers.
+ The insn that frees registers is most likely to be the insn with lowest
+ LUID (original insn order); but such an insn might be there in the stalled
+ queue (Q) instead of the ready queue (R). To solve this, we skip cycles
+ upto a max of 8 cycles so that such insns may move from Q -> R.
+
+ The description of the hooks are as below:
+
+ TARGET_SCHED_INIT_GLOBAL: Added a new target hook in the generic
+ scheduler; it is called inside the sched_init function just after
+ find_insn_reg_weights function call. It is used to calculate the SImode
+ and SFmode weights of insns of basic blocks; much similar to what
+ find_insn_reg_weights does.
+ TARGET_SCHED_FINISH_GLOBAL: Corresponding cleanup hook.
+
+ TARGET_SCHED_DFA_NEW_CYCLE: Skip cycles if high register pressure is
+ indicated by TARGET_SCHED_REORDER2; doing this may move insns from
+ (Q)->(R).
+
+ TARGET_SCHED_REORDER: If the register pressure for SImode or SFmode is
+ high; reorder the ready queue so that the insn with lowest LUID will be
+ issued next.
+
+ TARGET_SCHED_REORDER2: If the register pressure is high, indicate to
+ TARGET_SCHED_DFA_NEW_CYCLE to skip cycles.
+
+ TARGET_SCHED_VARIABLE_ISSUE: Cache the value of can_issue_more so that it
+ can be returned from TARGET_SCHED_REORDER2.
+
+ TARGET_SCHED_INIT: Reset the register pressure counting variables. */
+
+#undef TARGET_SCHED_DFA_NEW_CYCLE
+#define TARGET_SCHED_DFA_NEW_CYCLE sh_dfa_new_cycle
+
+#undef TARGET_SCHED_INIT_GLOBAL
+#define TARGET_SCHED_INIT_GLOBAL sh_md_init_global
+
+#undef TARGET_SCHED_FINISH_GLOBAL
+#define TARGET_SCHED_FINISH_GLOBAL sh_md_finish_global
+
+#undef TARGET_SCHED_VARIABLE_ISSUE
+#define TARGET_SCHED_VARIABLE_ISSUE sh_variable_issue
+
+#undef TARGET_SCHED_REORDER
+#define TARGET_SCHED_REORDER sh_reorder
+
+#undef TARGET_SCHED_REORDER2
+#define TARGET_SCHED_REORDER2 sh_reorder2
+
+#undef TARGET_SCHED_INIT
+#define TARGET_SCHED_INIT sh_md_init
+
+#undef TARGET_DELEGITIMIZE_ADDRESS
+#define TARGET_DELEGITIMIZE_ADDRESS sh_delegitimize_address
+
+#undef TARGET_LEGITIMIZE_ADDRESS
+#define TARGET_LEGITIMIZE_ADDRESS sh_legitimize_address
+
+#undef TARGET_CANNOT_MODIFY_JUMPS_P
+#define TARGET_CANNOT_MODIFY_JUMPS_P sh_cannot_modify_jumps_p
+#undef TARGET_BRANCH_TARGET_REGISTER_CLASS
+#define TARGET_BRANCH_TARGET_REGISTER_CLASS sh_target_reg_class
+#undef TARGET_BRANCH_TARGET_REGISTER_CALLEE_SAVED
+#define TARGET_BRANCH_TARGET_REGISTER_CALLEE_SAVED \
+ sh_optimize_target_register_callee_saved
+
+#undef TARGET_MS_BITFIELD_LAYOUT_P
+#define TARGET_MS_BITFIELD_LAYOUT_P sh_ms_bitfield_layout_p
+
+#undef TARGET_INIT_BUILTINS
+#define TARGET_INIT_BUILTINS sh_init_builtins
+#undef TARGET_BUILTIN_DECL
+#define TARGET_BUILTIN_DECL sh_builtin_decl
+#undef TARGET_EXPAND_BUILTIN
+#define TARGET_EXPAND_BUILTIN sh_expand_builtin
+
+#undef TARGET_FUNCTION_OK_FOR_SIBCALL
+#define TARGET_FUNCTION_OK_FOR_SIBCALL sh_function_ok_for_sibcall
+
+#undef TARGET_CANNOT_COPY_INSN_P
+#define TARGET_CANNOT_COPY_INSN_P sh_cannot_copy_insn_p
+#undef TARGET_RTX_COSTS
+#define TARGET_RTX_COSTS sh_rtx_costs
+#undef TARGET_ADDRESS_COST
+#define TARGET_ADDRESS_COST sh_address_cost
+#undef TARGET_ALLOCATE_INITIAL_VALUE
+#define TARGET_ALLOCATE_INITIAL_VALUE sh_allocate_initial_value
+
+#undef TARGET_MACHINE_DEPENDENT_REORG
+#define TARGET_MACHINE_DEPENDENT_REORG sh_reorg
+
+#undef TARGET_DWARF_REGISTER_SPAN
+#define TARGET_DWARF_REGISTER_SPAN sh_dwarf_register_span
+
+#ifdef HAVE_AS_TLS
+#undef TARGET_HAVE_TLS
+#define TARGET_HAVE_TLS true
+#endif
+
+#undef TARGET_PROMOTE_PROTOTYPES
+#define TARGET_PROMOTE_PROTOTYPES sh_promote_prototypes
+#undef TARGET_PROMOTE_FUNCTION_MODE
+#define TARGET_PROMOTE_FUNCTION_MODE sh_promote_function_mode
+
+#undef TARGET_FUNCTION_VALUE
+#define TARGET_FUNCTION_VALUE sh_function_value
+#undef TARGET_FUNCTION_VALUE_REGNO_P
+#define TARGET_FUNCTION_VALUE_REGNO_P sh_function_value_regno_p
+#undef TARGET_LIBCALL_VALUE
+#define TARGET_LIBCALL_VALUE sh_libcall_value
+#undef TARGET_STRUCT_VALUE_RTX
+#define TARGET_STRUCT_VALUE_RTX sh_struct_value_rtx
+#undef TARGET_RETURN_IN_MEMORY
+#define TARGET_RETURN_IN_MEMORY sh_return_in_memory
+
+#undef TARGET_EXPAND_BUILTIN_SAVEREGS
+#define TARGET_EXPAND_BUILTIN_SAVEREGS sh_builtin_saveregs
+#undef TARGET_SETUP_INCOMING_VARARGS
+#define TARGET_SETUP_INCOMING_VARARGS sh_setup_incoming_varargs
+#undef TARGET_STRICT_ARGUMENT_NAMING
+#define TARGET_STRICT_ARGUMENT_NAMING sh_strict_argument_naming
+#undef TARGET_PRETEND_OUTGOING_VARARGS_NAMED
+#define TARGET_PRETEND_OUTGOING_VARARGS_NAMED sh_pretend_outgoing_varargs_named
+#undef TARGET_MUST_PASS_IN_STACK
+#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size
+#undef TARGET_PASS_BY_REFERENCE
+#define TARGET_PASS_BY_REFERENCE sh_pass_by_reference
+#undef TARGET_CALLEE_COPIES
+#define TARGET_CALLEE_COPIES sh_callee_copies
+#undef TARGET_ARG_PARTIAL_BYTES
+#define TARGET_ARG_PARTIAL_BYTES sh_arg_partial_bytes
+#undef TARGET_FUNCTION_ARG
+#define TARGET_FUNCTION_ARG sh_function_arg
+#undef TARGET_FUNCTION_ARG_ADVANCE
+#define TARGET_FUNCTION_ARG_ADVANCE sh_function_arg_advance
+
+#undef TARGET_BUILD_BUILTIN_VA_LIST
+#define TARGET_BUILD_BUILTIN_VA_LIST sh_build_builtin_va_list
+#undef TARGET_EXPAND_BUILTIN_VA_START
+#define TARGET_EXPAND_BUILTIN_VA_START sh_va_start
+#undef TARGET_GIMPLIFY_VA_ARG_EXPR
+#define TARGET_GIMPLIFY_VA_ARG_EXPR sh_gimplify_va_arg_expr
+
+#undef TARGET_SCALAR_MODE_SUPPORTED_P
+#define TARGET_SCALAR_MODE_SUPPORTED_P sh_scalar_mode_supported_p
+#undef TARGET_VECTOR_MODE_SUPPORTED_P
+#define TARGET_VECTOR_MODE_SUPPORTED_P sh_vector_mode_supported_p
+
+#undef TARGET_CHECK_PCH_TARGET_FLAGS
+#define TARGET_CHECK_PCH_TARGET_FLAGS sh_check_pch_target_flags
+
+#undef TARGET_DWARF_CALLING_CONVENTION
+#define TARGET_DWARF_CALLING_CONVENTION sh_dwarf_calling_convention
+
+#undef TARGET_FRAME_POINTER_REQUIRED
+#define TARGET_FRAME_POINTER_REQUIRED sh_frame_pointer_required
+
+/* Return regmode weight for insn. */
+#define INSN_REGMODE_WEIGHT(INSN, MODE) regmode_weight[((MODE) == SImode) ? 0 : 1][INSN_UID (INSN)]
+
+/* Return current register pressure for regmode. */
+#define CURR_REGMODE_PRESSURE(MODE) curr_regmode_pressure[((MODE) == SImode) ? 0 : 1]
+
+#undef TARGET_ENCODE_SECTION_INFO
+#define TARGET_ENCODE_SECTION_INFO sh_encode_section_info
+
+#ifdef SYMBIAN
+
+#undef TARGET_ENCODE_SECTION_INFO
+#define TARGET_ENCODE_SECTION_INFO sh_symbian_encode_section_info
+#undef TARGET_STRIP_NAME_ENCODING
+#define TARGET_STRIP_NAME_ENCODING sh_symbian_strip_name_encoding
+#undef TARGET_CXX_IMPORT_EXPORT_CLASS
+#define TARGET_CXX_IMPORT_EXPORT_CLASS sh_symbian_import_export_class
+
+#endif /* SYMBIAN */
+
+#undef TARGET_SECONDARY_RELOAD
+#define TARGET_SECONDARY_RELOAD sh_secondary_reload
+
+#undef TARGET_PREFERRED_RELOAD_CLASS
+#define TARGET_PREFERRED_RELOAD_CLASS sh_preferred_reload_class
+
+#undef TARGET_CONDITIONAL_REGISTER_USAGE
+#define TARGET_CONDITIONAL_REGISTER_USAGE sh_conditional_register_usage
+
+#undef TARGET_LEGITIMATE_ADDRESS_P
+#define TARGET_LEGITIMATE_ADDRESS_P sh_legitimate_address_p
+
+#undef TARGET_TRAMPOLINE_INIT
+#define TARGET_TRAMPOLINE_INIT sh_trampoline_init
+#undef TARGET_TRAMPOLINE_ADJUST_ADDRESS
+#define TARGET_TRAMPOLINE_ADJUST_ADDRESS sh_trampoline_adjust_address
+
+/* Machine-specific symbol_ref flags. */
+#define SYMBOL_FLAG_FUNCVEC_FUNCTION (SYMBOL_FLAG_MACH_DEP << 0)
+
+struct gcc_target targetm = TARGET_INITIALIZER;
+
+/* Implement TARGET_HANDLE_OPTION. */
+
+static bool
+sh_handle_option (size_t code, const char *arg ATTRIBUTE_UNUSED,
+ int value ATTRIBUTE_UNUSED)
+{
+ switch (code)
+ {
+ case OPT_m1:
+ target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH1;
+ return true;
+
+ case OPT_m2:
+ target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH2;
+ return true;
+
+ case OPT_m2a:
+ target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH2A;
+ return true;
+
+ case OPT_m2a_nofpu:
+ target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH2A_NOFPU;
+ return true;
+
+ case OPT_m2a_single:
+ target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH2A_SINGLE;
+ return true;
+
+ case OPT_m2a_single_only:
+ target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH2A_SINGLE_ONLY;
+ return true;
+
+ case OPT_m2e:
+ target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH2E;
+ return true;
+
+ case OPT_m3:
+ target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH3;
+ return true;
+
+ case OPT_m3e:
+ target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH3E;
+ return true;
+
+ case OPT_m4:
+ case OPT_m4_100:
+ case OPT_m4_200:
+ case OPT_m4_300:
+ target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4;
+ return true;
+
+ case OPT_m4_nofpu:
+ case OPT_m4_100_nofpu:
+ case OPT_m4_200_nofpu:
+ case OPT_m4_300_nofpu:
+ case OPT_m4_340:
+ case OPT_m4_400:
+ case OPT_m4_500:
+ target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4_NOFPU;
+ return true;
+
+ case OPT_m4_single:
+ case OPT_m4_100_single:
+ case OPT_m4_200_single:
+ case OPT_m4_300_single:
+ target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4_SINGLE;
+ return true;
+
+ case OPT_m4_single_only:
+ case OPT_m4_100_single_only:
+ case OPT_m4_200_single_only:
+ case OPT_m4_300_single_only:
+ target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4_SINGLE_ONLY;
+ return true;
+
+ case OPT_m4a:
+ target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4A;
+ return true;
+
+ case OPT_m4a_nofpu:
+ case OPT_m4al:
+ target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4A_NOFPU;
+ return true;
+
+ case OPT_m4a_single:
+ target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4A_SINGLE;
+ return true;
+
+ case OPT_m4a_single_only:
+ target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4A_SINGLE_ONLY;
+ return true;
+
+ case OPT_m5_32media:
+ target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH5_32MEDIA;
+ return true;
+
+ case OPT_m5_32media_nofpu:
+ target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH5_32MEDIA_NOFPU;
+ return true;
+
+ case OPT_m5_64media:
+ target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH5_64MEDIA;
+ return true;
+
+ case OPT_m5_64media_nofpu:
+ target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH5_64MEDIA_NOFPU;
+ return true;
+
+ case OPT_m5_compact:
+ target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH5_COMPACT;
+ return true;
+
+ case OPT_m5_compact_nofpu:
+ target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH5_COMPACT_NOFPU;
+ return true;
+
+ default:
+ return true;
+ }
+}
+
+/* Implement TARGET_OPTION_INIT_STRUCT. */
+static void
+sh_option_init_struct (struct gcc_options *opts)
+{
+ /* We can't meaningfully test TARGET_SH2E / TARGET_IEEE
+ here, so leave it to TARGET_OPTION_OVERRIDE to set
+ flag_finite_math_only. We set it to 2 here so we know if the user
+ explicitly requested this to be on or off. */
+ opts->x_flag_finite_math_only = 2;
+}
+
+/* Implement TARGET_OPTION_DEFAULT_PARAMS. */
+static void
+sh_option_default_params (void)
+{
+ set_default_param_value (PARAM_SIMULTANEOUS_PREFETCHES, 2);
+}
+
+/* Implement TARGET_OPTION_OVERRIDE macro. Validate and override
+ various options, and do some machine dependent initialization. */
+static void
+sh_option_override (void)
+{
+ int regno;
+
+ SUBTARGET_OVERRIDE_OPTIONS;
+ if (optimize > 1 && !optimize_size)
+ target_flags |= MASK_SAVE_ALL_TARGET_REGS;
+ sh_cpu = PROCESSOR_SH1;
+ assembler_dialect = 0;
+ if (TARGET_SH2)
+ sh_cpu = PROCESSOR_SH2;
+ if (TARGET_SH2E)
+ sh_cpu = PROCESSOR_SH2E;
+ if (TARGET_SH2A)
+ sh_cpu = PROCESSOR_SH2A;
+ if (TARGET_SH3)
+ sh_cpu = PROCESSOR_SH3;
+ if (TARGET_SH3E)
+ sh_cpu = PROCESSOR_SH3E;
+ if (TARGET_SH4)
+ {
+ assembler_dialect = 1;
+ sh_cpu = PROCESSOR_SH4;
+ }
+ if (TARGET_SH4A_ARCH)
+ {
+ assembler_dialect = 1;
+ sh_cpu = PROCESSOR_SH4A;
+ }
+ if (TARGET_SH5)
+ {
+ sh_cpu = PROCESSOR_SH5;
+ target_flags |= MASK_ALIGN_DOUBLE;
+ if (TARGET_SHMEDIA_FPU)
+ target_flags |= MASK_FMOVD;
+ if (TARGET_SHMEDIA)
+ {
+ /* There are no delay slots on SHmedia. */
+ flag_delayed_branch = 0;
+ /* Relaxation isn't yet supported for SHmedia */
+ target_flags &= ~MASK_RELAX;
+ /* After reload, if conversion does little good but can cause
+ ICEs:
+ - find_if_block doesn't do anything for SH because we don't
+ have conditional execution patterns. (We use conditional
+ move patterns, which are handled differently, and only
+ before reload).
+ - find_cond_trap doesn't do anything for the SH because we
+ don't have conditional traps.
+ - find_if_case_1 uses redirect_edge_and_branch_force in
+ the only path that does an optimization, and this causes
+ an ICE when branch targets are in registers.
+ - find_if_case_2 doesn't do anything for the SHmedia after
+ reload except when it can redirect a tablejump - and
+ that's rather rare. */
+ flag_if_conversion2 = 0;
+ if (! strcmp (sh_div_str, "call"))
+ sh_div_strategy = SH_DIV_CALL;
+ else if (! strcmp (sh_div_str, "call2"))
+ sh_div_strategy = SH_DIV_CALL2;
+ if (! strcmp (sh_div_str, "fp") && TARGET_FPU_ANY)
+ sh_div_strategy = SH_DIV_FP;
+ else if (! strcmp (sh_div_str, "inv"))
+ sh_div_strategy = SH_DIV_INV;
+ else if (! strcmp (sh_div_str, "inv:minlat"))
+ sh_div_strategy = SH_DIV_INV_MINLAT;
+ else if (! strcmp (sh_div_str, "inv20u"))
+ sh_div_strategy = SH_DIV_INV20U;
+ else if (! strcmp (sh_div_str, "inv20l"))
+ sh_div_strategy = SH_DIV_INV20L;
+ else if (! strcmp (sh_div_str, "inv:call2"))
+ sh_div_strategy = SH_DIV_INV_CALL2;
+ else if (! strcmp (sh_div_str, "inv:call"))
+ sh_div_strategy = SH_DIV_INV_CALL;
+ else if (! strcmp (sh_div_str, "inv:fp"))
+ {
+ if (TARGET_FPU_ANY)
+ sh_div_strategy = SH_DIV_INV_FP;
+ else
+ sh_div_strategy = SH_DIV_INV;
+ }
+ TARGET_CBRANCHDI4 = 0;
+ /* Assembler CFI isn't yet fully supported for SHmedia. */
+ flag_dwarf2_cfi_asm = 0;
+ }
+ }
+ else
+ {
+ /* Only the sh64-elf assembler fully supports .quad properly. */
+ targetm.asm_out.aligned_op.di = NULL;
+ targetm.asm_out.unaligned_op.di = NULL;
+ }
+ if (TARGET_SH1)
+ {
+ if (! strcmp (sh_div_str, "call-div1"))
+ sh_div_strategy = SH_DIV_CALL_DIV1;
+ else if (! strcmp (sh_div_str, "call-fp")
+ && (TARGET_FPU_DOUBLE
+ || (TARGET_HARD_SH4 && TARGET_SH2E)
+ || (TARGET_SHCOMPACT && TARGET_FPU_ANY)))
+ sh_div_strategy = SH_DIV_CALL_FP;
+ else if (! strcmp (sh_div_str, "call-table") && TARGET_SH2)
+ sh_div_strategy = SH_DIV_CALL_TABLE;
+ else
+ /* Pick one that makes most sense for the target in general.
+ It is not much good to use different functions depending
+ on -Os, since then we'll end up with two different functions
+ when some of the code is compiled for size, and some for
+ speed. */
+
+ /* SH4 tends to emphasize speed. */
+ if (TARGET_HARD_SH4)
+ sh_div_strategy = SH_DIV_CALL_TABLE;
+ /* These have their own way of doing things. */
+ else if (TARGET_SH2A)
+ sh_div_strategy = SH_DIV_INTRINSIC;
+ /* ??? Should we use the integer SHmedia function instead? */
+ else if (TARGET_SHCOMPACT && TARGET_FPU_ANY)
+ sh_div_strategy = SH_DIV_CALL_FP;
+ /* SH1 .. SH3 cores often go into small-footprint systems, so
+ default to the smallest implementation available. */
+ else if (TARGET_SH2) /* ??? EXPERIMENTAL */
+ sh_div_strategy = SH_DIV_CALL_TABLE;
+ else
+ sh_div_strategy = SH_DIV_CALL_DIV1;
+ }
+ if (!TARGET_SH1)
+ TARGET_PRETEND_CMOVE = 0;
+ if (sh_divsi3_libfunc[0])
+ ; /* User supplied - leave it alone. */
+ else if (TARGET_DIVIDE_CALL_FP)
+ sh_divsi3_libfunc = "__sdivsi3_i4";
+ else if (TARGET_DIVIDE_CALL_TABLE)
+ sh_divsi3_libfunc = "__sdivsi3_i4i";
+ else if (TARGET_SH5)
+ sh_divsi3_libfunc = "__sdivsi3_1";
+ else
+ sh_divsi3_libfunc = "__sdivsi3";
+ if (sh_branch_cost == -1)
+ sh_branch_cost
+ = TARGET_SH5 ? 1 : ! TARGET_SH2 || TARGET_HARD_SH4 ? 2 : 1;
+
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if (! VALID_REGISTER_P (regno))
+ sh_register_names[regno][0] = '\0';
+
+ for (regno = 0; regno < ADDREGNAMES_SIZE; regno++)
+ if (! VALID_REGISTER_P (ADDREGNAMES_REGNO (regno)))
+ sh_additional_register_names[regno][0] = '\0';
+
+ if ((flag_pic && ! TARGET_PREFERGOT)
+ || (TARGET_SHMEDIA && !TARGET_PT_FIXED))
+ flag_no_function_cse = 1;
+
+ if (targetm.small_register_classes_for_mode_p (VOIDmode)) \
+ {
+ /* Never run scheduling before reload, since that can
+ break global alloc, and generates slower code anyway due
+ to the pressure on R0. */
+ /* Enable sched1 for SH4 if the user explicitly requests.
+ When sched1 is enabled, the ready queue will be reordered by
+ the target hooks if pressure is high. We can not do this for
+ PIC, SH3 and lower as they give spill failures for R0. */
+ if (!TARGET_HARD_SH4 || flag_pic)
+ flag_schedule_insns = 0;
+ /* ??? Current exception handling places basic block boundaries
+ after call_insns. It causes the high pressure on R0 and gives
+ spill failures for R0 in reload. See PR 22553 and the thread
+ on gcc-patches
+ <http://gcc.gnu.org/ml/gcc-patches/2005-10/msg00816.html>. */
+ else if (flag_exceptions)
+ {
+ if (flag_schedule_insns && global_options_set.x_flag_schedule_insns)
+ warning (0, "ignoring -fschedule-insns because of exception handling bug");
+ flag_schedule_insns = 0;
+ }
+ else if (flag_schedule_insns
+ && !global_options_set.x_flag_schedule_insns)
+ flag_schedule_insns = 0;
+ }
+
+ /* Unwind info is not correct around the CFG unless either a frame
+ pointer is present or M_A_O_A is set. Fixing this requires rewriting
+ unwind info generation to be aware of the CFG and propagating states
+ around edges. */
+ if ((flag_unwind_tables || flag_asynchronous_unwind_tables
+ || flag_exceptions || flag_non_call_exceptions)
+ && flag_omit_frame_pointer && !TARGET_ACCUMULATE_OUTGOING_ARGS)
+ {
+ warning (0, "unwind tables currently require either a frame pointer "
+ "or -maccumulate-outgoing-args for correctness");
+ TARGET_ACCUMULATE_OUTGOING_ARGS = 1;
+ }
+
+ /* Unwinding with -freorder-blocks-and-partition does not work on this
+ architecture, because it requires far jumps to label crossing between
+ hot/cold sections which are rejected on this architecture. */
+ if (flag_reorder_blocks_and_partition)
+ {
+ if (flag_exceptions)
+ {
+ inform (input_location,
+ "-freorder-blocks-and-partition does not work with "
+ "exceptions on this architecture");
+ flag_reorder_blocks_and_partition = 0;
+ flag_reorder_blocks = 1;
+ }
+ else if (flag_unwind_tables)
+ {
+ inform (input_location,
+ "-freorder-blocks-and-partition does not support unwind "
+ "info on this architecture");
+ flag_reorder_blocks_and_partition = 0;
+ flag_reorder_blocks = 1;
+ }
+ }
+
+ if (align_loops == 0)
+ align_loops = 1 << (TARGET_SH5 ? 3 : 2);
+ if (align_jumps == 0)
+ align_jumps = 1 << CACHE_LOG;
+ else if (align_jumps < (TARGET_SHMEDIA ? 4 : 2))
+ align_jumps = TARGET_SHMEDIA ? 4 : 2;
+
+ /* Allocation boundary (in *bytes*) for the code of a function.
+ SH1: 32 bit alignment is faster, because instructions are always
+ fetched as a pair from a longword boundary.
+ SH2 .. SH5 : align to cache line start. */
+ if (align_functions == 0)
+ align_functions
+ = optimize_size ? FUNCTION_BOUNDARY/8 : (1 << CACHE_LOG);
+ /* The linker relaxation code breaks when a function contains
+ alignments that are larger than that at the start of a
+ compilation unit. */
+ if (TARGET_RELAX)
+ {
+ int min_align
+ = align_loops > align_jumps ? align_loops : align_jumps;
+
+ /* Also take possible .long constants / mova tables int account. */
+ if (min_align < 4)
+ min_align = 4;
+ if (align_functions < min_align)
+ align_functions = min_align;
+ }
+
+ /* If the -mieee option was not explicitly set by the user, turn it on
+ unless -ffinite-math-only was specified. See also PR 33135. */
+ if (! global_options_set.x_TARGET_IEEE)
+ TARGET_IEEE = ! flag_finite_math_only;
+
+ if (sh_fixed_range_str)
+ sh_fix_range (sh_fixed_range_str);
+
+ /* This target defaults to strict volatile bitfields. */
+ if (flag_strict_volatile_bitfields < 0 && abi_version_at_least(2))
+ flag_strict_volatile_bitfields = 1;
+}
+
+/* Print the operand address in x to the stream. */
+
+static void
+sh_print_operand_address (FILE *stream, rtx x)
+{
+ switch (GET_CODE (x))
+ {
+ case REG:
+ case SUBREG:
+ fprintf (stream, "@%s", reg_names[true_regnum (x)]);
+ break;
+
+ case PLUS:
+ {
+ rtx base = XEXP (x, 0);
+ rtx index = XEXP (x, 1);
+
+ switch (GET_CODE (index))
+ {
+ case CONST_INT:
+ fprintf (stream, "@(%d,%s)", (int) INTVAL (index),
+ reg_names[true_regnum (base)]);
+ break;
+
+ case REG:
+ case SUBREG:
+ {
+ int base_num = true_regnum (base);
+ int index_num = true_regnum (index);
+
+ fprintf (stream, "@(r0,%s)",
+ reg_names[MAX (base_num, index_num)]);
+ break;
+ }
+
+ default:
+ gcc_unreachable ();
+ }
+ }
+ break;
+
+ case PRE_DEC:
+ fprintf (stream, "@-%s", reg_names[true_regnum (XEXP (x, 0))]);
+ break;
+
+ case POST_INC:
+ fprintf (stream, "@%s+", reg_names[true_regnum (XEXP (x, 0))]);
+ break;
+
+ default:
+ x = mark_constant_pool_use (x);
+ output_addr_const (stream, x);
+ break;
+ }
+}
+
+/* Print operand x (an rtx) in assembler syntax to file stream
+ according to modifier code.
+
+ '.' print a .s if insn needs delay slot
+ ',' print LOCAL_LABEL_PREFIX
+ '@' print trap, rte or rts depending upon pragma interruptness
+ '#' output a nop if there is nothing to put in the delay slot
+ ''' print likelihood suffix (/u for unlikely).
+ '>' print branch target if -fverbose-asm
+ 'O' print a constant without the #
+ 'R' print the LSW of a dp value - changes if in little endian
+ 'S' print the MSW of a dp value - changes if in little endian
+ 'T' print the next word of a dp value - same as 'R' in big endian mode.
+ 'M' SHMEDIA: print an `x' if `m' will print `base,index'.
+ otherwise: print .b / .w / .l / .s / .d suffix if operand is a MEM.
+ 'N' print 'r63' if the operand is (const_int 0).
+ 'd' print a V2SF reg as dN instead of fpN.
+ 'm' print a pair `base,offset' or `base,index', for LD and ST.
+ 'U' Likewise for {LD,ST}{HI,LO}.
+ 'V' print the position of a single bit set.
+ 'W' print the position of a single bit cleared.
+ 't' print a memory address which is a register.
+ 'u' prints the lowest 16 bits of CONST_INT, as an unsigned value.
+ 'o' output an operator. */
+
+static void
+sh_print_operand (FILE *stream, rtx x, int code)
+{
+ int regno;
+ enum machine_mode mode;
+
+ switch (code)
+ {
+ tree trapa_attr;
+
+ case '.':
+ if (final_sequence
+ && ! INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0))
+ && get_attr_length (XVECEXP (final_sequence, 0, 1)))
+ fprintf (stream, ASSEMBLER_DIALECT ? "/s" : ".s");
+ break;
+ case ',':
+ fprintf (stream, "%s", LOCAL_LABEL_PREFIX);
+ break;
+ case '@':
+ trapa_attr = lookup_attribute ("trap_exit",
+ DECL_ATTRIBUTES (current_function_decl));
+ if (trapa_attr)
+ fprintf (stream, "trapa #%ld",
+ (long) TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (trapa_attr))));
+ else if (sh_cfun_interrupt_handler_p ())
+ {
+ if (sh_cfun_resbank_handler_p ())
+ fprintf (stream, "resbank\n");
+ fprintf (stream, "rte");
+ }
+ else
+ fprintf (stream, "rts");
+ break;
+ case '#':
+ /* Output a nop if there's nothing in the delay slot. */
+ if (dbr_sequence_length () == 0)
+ fprintf (stream, "\n\tnop");
+ break;
+ case '\'':
+ {
+ rtx note = find_reg_note (current_output_insn, REG_BR_PROB, 0);
+
+ if (note && INTVAL (XEXP (note, 0)) * 2 < REG_BR_PROB_BASE)
+ fputs ("/u", stream);
+ break;
+ }
+ case '>':
+ if (flag_verbose_asm && JUMP_LABEL (current_output_insn))
+ {
+ fputs ("\t! target: ", stream);
+ output_addr_const (stream, JUMP_LABEL (current_output_insn));
+ }
+ break;
+ case 'O':
+ x = mark_constant_pool_use (x);
+ output_addr_const (stream, x);
+ break;
+ /* N.B.: %R / %S / %T adjust memory addresses by four.
+ For SHMEDIA, that means they can be used to access the first and
+ second 32 bit part of a 64 bit (or larger) value that
+ might be held in floating point registers or memory.
+ While they can be used to access 64 bit parts of a larger value
+ held in general purpose registers, that won't work with memory -
+ neither for fp registers, since the frxx names are used. */
+ case 'R':
+ if (REG_P (x) || GET_CODE (x) == SUBREG)
+ {
+ regno = true_regnum (x);
+ regno += FP_REGISTER_P (regno) ? 1 : LSW;
+ fputs (reg_names[regno], (stream));
+ }
+ else if (MEM_P (x))
+ {
+ x = adjust_address (x, SImode, 4 * LSW);
+ sh_print_operand_address (stream, XEXP (x, 0));
+ }
+ else
+ {
+ rtx sub = NULL_RTX;
+
+ mode = GET_MODE (x);
+ if (mode == VOIDmode)
+ mode = DImode;
+ if (GET_MODE_SIZE (mode) >= 8)
+ sub = simplify_subreg (SImode, x, mode, 4 * LSW);
+ if (sub)
+ sh_print_operand (stream, sub, 0);
+ else
+ output_operand_lossage ("invalid operand to %%R");
+ }
+ break;
+ case 'S':
+ if (REG_P (x) || GET_CODE (x) == SUBREG)
+ {
+ regno = true_regnum (x);
+ regno += FP_REGISTER_P (regno) ? 0 : MSW;
+ fputs (reg_names[regno], (stream));
+ }
+ else if (MEM_P (x))
+ {
+ x = adjust_address (x, SImode, 4 * MSW);
+ sh_print_operand_address (stream, XEXP (x, 0));
+ }
+ else
+ {
+ rtx sub = NULL_RTX;
+
+ mode = GET_MODE (x);
+ if (mode == VOIDmode)
+ mode = DImode;
+ if (GET_MODE_SIZE (mode) >= 8)
+ sub = simplify_subreg (SImode, x, mode, 4 * MSW);
+ if (sub)
+ sh_print_operand (stream, sub, 0);
+ else
+ output_operand_lossage ("invalid operand to %%S");
+ }
+ break;
+ case 'T':
+ /* Next word of a double. */
+ switch (GET_CODE (x))
+ {
+ case REG:
+ fputs (reg_names[REGNO (x) + 1], (stream));
+ break;
+ case MEM:
+ if (GET_CODE (XEXP (x, 0)) != PRE_DEC
+ && GET_CODE (XEXP (x, 0)) != POST_INC)
+ x = adjust_address (x, SImode, 4);
+ sh_print_operand_address (stream, XEXP (x, 0));
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case 't':
+ gcc_assert (MEM_P (x));
+ x = XEXP (x, 0);
+ switch (GET_CODE (x))
+ {
+ case REG:
+ case SUBREG:
+ sh_print_operand (stream, x, 0);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case 'o':
+ switch (GET_CODE (x))
+ {
+ case PLUS: fputs ("add", stream); break;
+ case MINUS: fputs ("sub", stream); break;
+ case MULT: fputs ("mul", stream); break;
+ case DIV: fputs ("div", stream); break;
+ case EQ: fputs ("eq", stream); break;
+ case NE: fputs ("ne", stream); break;
+ case GT: case LT: fputs ("gt", stream); break;
+ case GE: case LE: fputs ("ge", stream); break;
+ case GTU: case LTU: fputs ("gtu", stream); break;
+ case GEU: case LEU: fputs ("geu", stream); break;
+ default:
+ break;
+ }
+ break;
+ case 'M':
+ if (TARGET_SHMEDIA)
+ {
+ if (MEM_P (x)
+ && GET_CODE (XEXP (x, 0)) == PLUS
+ && (REG_P (XEXP (XEXP (x, 0), 1))
+ || GET_CODE (XEXP (XEXP (x, 0), 1)) == SUBREG))
+ fputc ('x', stream);
+ }
+ else
+ {
+ if (MEM_P (x))
+ {
+ switch (GET_MODE (x))
+ {
+ case QImode: fputs (".b", stream); break;
+ case HImode: fputs (".w", stream); break;
+ case SImode: fputs (".l", stream); break;
+ case SFmode: fputs (".s", stream); break;
+ case DFmode: fputs (".d", stream); break;
+ default: gcc_unreachable ();
+ }
+ }
+ }
+ break;
+
+ case 'm':
+ gcc_assert (MEM_P (x));
+ x = XEXP (x, 0);
+ /* Fall through. */
+ case 'U':
+ switch (GET_CODE (x))
+ {
+ case REG:
+ case SUBREG:
+ sh_print_operand (stream, x, 0);
+ fputs (", 0", stream);
+ break;
+
+ case PLUS:
+ sh_print_operand (stream, XEXP (x, 0), 0);
+ fputs (", ", stream);
+ sh_print_operand (stream, XEXP (x, 1), 0);
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ break;
+
+ case 'V':
+ {
+ int num = exact_log2 (INTVAL (x));
+ gcc_assert (num >= 0);
+ fprintf (stream, "#%d", num);
+ }
+ break;
+
+ case 'W':
+ {
+ int num = exact_log2 (~INTVAL (x));
+ gcc_assert (num >= 0);
+ fprintf (stream, "#%d", num);
+ }
+ break;
+
+ case 'd':
+ gcc_assert (REG_P (x) && GET_MODE (x) == V2SFmode);
+
+ fprintf ((stream), "d%s", reg_names[REGNO (x)] + 1);
+ break;
+
+ case 'N':
+ if (x == CONST0_RTX (GET_MODE (x)))
+ {
+ fprintf ((stream), "r63");
+ break;
+ }
+ goto default_output;
+ case 'u':
+ if (CONST_INT_P (x))
+ {
+ fprintf ((stream), "%u", (unsigned) INTVAL (x) & (0x10000 - 1));
+ break;
+ }
+ /* Fall through. */
+
+ default_output:
+ default:
+ regno = 0;
+ mode = GET_MODE (x);
+
+ switch (GET_CODE (x))
+ {
+ case TRUNCATE:
+ {
+ rtx inner = XEXP (x, 0);
+ int offset = 0;
+ enum machine_mode inner_mode;
+
+ /* We might see SUBREGs with vector mode registers inside. */
+ if (GET_CODE (inner) == SUBREG
+ && (GET_MODE_SIZE (GET_MODE (inner))
+ == GET_MODE_SIZE (GET_MODE (SUBREG_REG (inner))))
+ && subreg_lowpart_p (inner))
+ inner = SUBREG_REG (inner);
+ if (CONST_INT_P (inner))
+ {
+ x = GEN_INT (trunc_int_for_mode (INTVAL (inner), GET_MODE (x)));
+ goto default_output;
+ }
+ inner_mode = GET_MODE (inner);
+ if (GET_CODE (inner) == SUBREG
+ && (GET_MODE_SIZE (GET_MODE (inner))
+ < GET_MODE_SIZE (GET_MODE (SUBREG_REG (inner))))
+ && REG_P (SUBREG_REG (inner)))
+ {
+ offset = subreg_regno_offset (REGNO (SUBREG_REG (inner)),
+ GET_MODE (SUBREG_REG (inner)),
+ SUBREG_BYTE (inner),
+ GET_MODE (inner));
+ inner = SUBREG_REG (inner);
+ }
+ if (!REG_P (inner) || GET_MODE_SIZE (inner_mode) > 8)
+ abort ();
+ /* Floating point register pairs are always big endian;
+ general purpose registers are 64 bit wide. */
+ regno = REGNO (inner);
+ regno = (HARD_REGNO_NREGS (regno, inner_mode)
+ - HARD_REGNO_NREGS (regno, mode))
+ + offset;
+ x = inner;
+ goto reg;
+ }
+ case SIGN_EXTEND:
+ x = XEXP (x, 0);
+ goto reg;
+ /* FIXME: We need this on SHmedia32 because reload generates
+ some sign-extended HI or QI loads into DImode registers
+ but, because Pmode is SImode, the address ends up with a
+ subreg:SI of the DImode register. Maybe reload should be
+ fixed so as to apply alter_subreg to such loads? */
+ case IF_THEN_ELSE:
+ gcc_assert (trapping_target_operand (x, VOIDmode));
+ x = XEXP (XEXP (x, 2), 0);
+ goto default_output;
+ case SUBREG:
+ gcc_assert (SUBREG_BYTE (x) == 0
+ && REG_P (SUBREG_REG (x)));
+
+ x = SUBREG_REG (x);
+ /* Fall through. */
+
+ reg:
+ case REG:
+ regno += REGNO (x);
+ if (FP_REGISTER_P (regno)
+ && mode == V16SFmode)
+ fprintf ((stream), "mtrx%s", reg_names[regno] + 2);
+ else if (FP_REGISTER_P (REGNO (x))
+ && mode == V4SFmode)
+ fprintf ((stream), "fv%s", reg_names[regno] + 2);
+ else if (REG_P (x)
+ && mode == V2SFmode)
+ fprintf ((stream), "fp%s", reg_names[regno] + 2);
+ else if (FP_REGISTER_P (REGNO (x))
+ && GET_MODE_SIZE (mode) > 4)
+ fprintf ((stream), "d%s", reg_names[regno] + 1);
+ else
+ fputs (reg_names[regno], (stream));
+ break;
+
+ case MEM:
+ output_address (XEXP (x, 0));
+ break;
+
+ default:
+ if (TARGET_SH1)
+ fputc ('#', stream);
+ output_addr_const (stream, x);
+ break;
+ }
+ break;
+ }
+}
+
+static bool
+sh_print_operand_punct_valid_p (unsigned char code)
+{
+ return (code == '.' || code == '#' || code == '@' || code == ','
+ || code == '$' || code == '\'' || code == '>');
+}
+
+/* Implement TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA. */
+
+static bool
+sh_asm_output_addr_const_extra (FILE *file, rtx x)
+{
+ if (GET_CODE (x) == UNSPEC)
+ {
+ switch (XINT (x, 1))
+ {
+ case UNSPEC_DATALABEL:
+ fputs ("datalabel ", file);
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ break;
+ case UNSPEC_PIC:
+ /* GLOBAL_OFFSET_TABLE or local symbols, no suffix. */
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ break;
+ case UNSPEC_GOT:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputs ("@GOT", file);
+ break;
+ case UNSPEC_GOTOFF:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputs ("@GOTOFF", file);
+ break;
+ case UNSPEC_PLT:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputs ("@PLT", file);
+ break;
+ case UNSPEC_GOTPLT:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputs ("@GOTPLT", file);
+ break;
+ case UNSPEC_DTPOFF:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputs ("@DTPOFF", file);
+ break;
+ case UNSPEC_GOTTPOFF:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputs ("@GOTTPOFF", file);
+ break;
+ case UNSPEC_TPOFF:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputs ("@TPOFF", file);
+ break;
+ case UNSPEC_CALLER:
+ {
+ char name[32];
+ /* LPCS stands for Label for PIC Call Site. */
+ targetm.asm_out.generate_internal_label (name, "LPCS",
+ INTVAL (XVECEXP (x, 0, 0)));
+ assemble_name (file, name);
+ }
+ break;
+ case UNSPEC_EXTRACT_S16:
+ case UNSPEC_EXTRACT_U16:
+ {
+ rtx val, shift;
+
+ val = XVECEXP (x, 0, 0);
+ shift = XVECEXP (x, 0, 1);
+ fputc ('(', file);
+ if (shift != const0_rtx)
+ fputc ('(', file);
+ if (GET_CODE (val) == CONST
+ || GET_RTX_CLASS (GET_CODE (val)) != RTX_OBJ)
+ {
+ fputc ('(', file);
+ output_addr_const (file, val);
+ fputc (')', file);
+ }
+ else
+ output_addr_const (file, val);
+ if (shift != const0_rtx)
+ {
+ fputs (" >> ", file);
+ output_addr_const (file, shift);
+ fputc (')', file);
+ }
+ fputs (" & 65535)", file);
+ }
+ break;
+ case UNSPEC_SYMOFF:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputc ('-', file);
+ if (GET_CODE (XVECEXP (x, 0, 1)) == CONST)
+ {
+ fputc ('(', file);
+ output_addr_const (file, XVECEXP (x, 0, 1));
+ fputc (')', file);
+ }
+ else
+ output_addr_const (file, XVECEXP (x, 0, 1));
+ break;
+ case UNSPEC_PCREL_SYMOFF:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputs ("-(", file);
+ output_addr_const (file, XVECEXP (x, 0, 1));
+ fputs ("-.)", file);
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+ else
+ return false;
+}
+
+
+/* Encode symbol attributes of a SYMBOL_REF into its
+ SYMBOL_REF_FLAGS. */
+static void
+sh_encode_section_info (tree decl, rtx rtl, int first)
+{
+ default_encode_section_info (decl, rtl, first);
+
+ if (TREE_CODE (decl) == FUNCTION_DECL
+ && sh2a_function_vector_p (decl) && TARGET_SH2A)
+ SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= SYMBOL_FLAG_FUNCVEC_FUNCTION;
+}
+
+/* Like force_operand, but guarantees that VALUE ends up in TARGET. */
+static void
+force_into (rtx value, rtx target)
+{
+ value = force_operand (value, target);
+ if (! rtx_equal_p (value, target))
+ emit_insn (gen_move_insn (target, value));
+}
+
+/* Emit code to perform a block move. Choose the best method.
+
+ OPERANDS[0] is the destination.
+ OPERANDS[1] is the source.
+ OPERANDS[2] is the size.
+ OPERANDS[3] is the alignment safe to use. */
+
+int
+expand_block_move (rtx *operands)
+{
+ int align = INTVAL (operands[3]);
+ int constp = (CONST_INT_P (operands[2]));
+ int bytes = (constp ? INTVAL (operands[2]) : 0);
+
+ if (! constp)
+ return 0;
+
+ /* If we could use mov.l to move words and dest is word-aligned, we
+ can use movua.l for loads and still generate a relatively short
+ and efficient sequence. */
+ if (TARGET_SH4A_ARCH && align < 4
+ && MEM_ALIGN (operands[0]) >= 32
+ && can_move_by_pieces (bytes, 32))
+ {
+ rtx dest = copy_rtx (operands[0]);
+ rtx src = copy_rtx (operands[1]);
+ /* We could use different pseudos for each copied word, but
+ since movua can only load into r0, it's kind of
+ pointless. */
+ rtx temp = gen_reg_rtx (SImode);
+ rtx src_addr = copy_addr_to_reg (XEXP (src, 0));
+ int copied = 0;
+
+ while (copied + 4 <= bytes)
+ {
+ rtx to = adjust_address (dest, SImode, copied);
+ rtx from = adjust_automodify_address (src, BLKmode,
+ src_addr, copied);
+
+ set_mem_size (from, GEN_INT (4));
+ emit_insn (gen_movua (temp, from));
+ emit_move_insn (src_addr, plus_constant (src_addr, 4));
+ emit_move_insn (to, temp);
+ copied += 4;
+ }
+
+ if (copied < bytes)
+ move_by_pieces (adjust_address (dest, BLKmode, copied),
+ adjust_automodify_address (src, BLKmode,
+ src_addr, copied),
+ bytes - copied, align, 0);
+
+ return 1;
+ }
+
+ /* If it isn't a constant number of bytes, or if it doesn't have 4 byte
+ alignment, or if it isn't a multiple of 4 bytes, then fail. */
+ if (align < 4 || (bytes % 4 != 0))
+ return 0;
+
+ if (TARGET_HARD_SH4)
+ {
+ if (bytes < 12)
+ return 0;
+ else if (bytes == 12)
+ {
+ rtx func_addr_rtx = gen_reg_rtx (Pmode);
+ rtx r4 = gen_rtx_REG (SImode, 4);
+ rtx r5 = gen_rtx_REG (SImode, 5);
+
+ function_symbol (func_addr_rtx, "__movmemSI12_i4", SFUNC_STATIC);
+ force_into (XEXP (operands[0], 0), r4);
+ force_into (XEXP (operands[1], 0), r5);
+ emit_insn (gen_block_move_real_i4 (func_addr_rtx));
+ return 1;
+ }
+ else if (! optimize_size)
+ {
+ const char *entry_name;
+ rtx func_addr_rtx = gen_reg_rtx (Pmode);
+ int dwords;
+ rtx r4 = gen_rtx_REG (SImode, 4);
+ rtx r5 = gen_rtx_REG (SImode, 5);
+ rtx r6 = gen_rtx_REG (SImode, 6);
+
+ entry_name = (bytes & 4 ? "__movmem_i4_odd" : "__movmem_i4_even");
+ function_symbol (func_addr_rtx, entry_name, SFUNC_STATIC);
+ force_into (XEXP (operands[0], 0), r4);
+ force_into (XEXP (operands[1], 0), r5);
+
+ dwords = bytes >> 3;
+ emit_insn (gen_move_insn (r6, GEN_INT (dwords - 1)));
+ emit_insn (gen_block_lump_real_i4 (func_addr_rtx));
+ return 1;
+ }
+ else
+ return 0;
+ }
+ if (bytes < 64)
+ {
+ char entry[30];
+ rtx func_addr_rtx = gen_reg_rtx (Pmode);
+ rtx r4 = gen_rtx_REG (SImode, 4);
+ rtx r5 = gen_rtx_REG (SImode, 5);
+
+ sprintf (entry, "__movmemSI%d", bytes);
+ function_symbol (func_addr_rtx, entry, SFUNC_STATIC);
+ force_into (XEXP (operands[0], 0), r4);
+ force_into (XEXP (operands[1], 0), r5);
+ emit_insn (gen_block_move_real (func_addr_rtx));
+ return 1;
+ }
+
+ /* This is the same number of bytes as a memcpy call, but to a different
+ less common function name, so this will occasionally use more space. */
+ if (! optimize_size)
+ {
+ rtx func_addr_rtx = gen_reg_rtx (Pmode);
+ int final_switch, while_loop;
+ rtx r4 = gen_rtx_REG (SImode, 4);
+ rtx r5 = gen_rtx_REG (SImode, 5);
+ rtx r6 = gen_rtx_REG (SImode, 6);
+
+ function_symbol (func_addr_rtx, "__movmem", SFUNC_STATIC);
+ force_into (XEXP (operands[0], 0), r4);
+ force_into (XEXP (operands[1], 0), r5);
+
+ /* r6 controls the size of the move. 16 is decremented from it
+ for each 64 bytes moved. Then the negative bit left over is used
+ as an index into a list of move instructions. e.g., a 72 byte move
+ would be set up with size(r6) = 14, for one iteration through the
+ big while loop, and a switch of -2 for the last part. */
+
+ final_switch = 16 - ((bytes / 4) % 16);
+ while_loop = ((bytes / 4) / 16 - 1) * 16;
+ emit_insn (gen_move_insn (r6, GEN_INT (while_loop + final_switch)));
+ emit_insn (gen_block_lump_real (func_addr_rtx));
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Prepare operands for a move define_expand; specifically, one of the
+ operands must be in a register. */
+
+int
+prepare_move_operands (rtx operands[], enum machine_mode mode)
+{
+ if ((mode == SImode || mode == DImode)
+ && flag_pic
+ && ! ((mode == Pmode || mode == ptr_mode)
+ && tls_symbolic_operand (operands[1], Pmode) != TLS_MODEL_NONE))
+ {
+ rtx temp;
+ if (SYMBOLIC_CONST_P (operands[1]))
+ {
+ if (MEM_P (operands[0]))
+ operands[1] = force_reg (Pmode, operands[1]);
+ else if (TARGET_SHMEDIA
+ && GET_CODE (operands[1]) == LABEL_REF
+ && target_reg_operand (operands[0], mode))
+ /* It's ok. */;
+ else
+ {
+ temp = (!can_create_pseudo_p ()
+ ? operands[0]
+ : gen_reg_rtx (Pmode));
+ operands[1] = legitimize_pic_address (operands[1], mode, temp);
+ }
+ }
+ else if (GET_CODE (operands[1]) == CONST
+ && GET_CODE (XEXP (operands[1], 0)) == PLUS
+ && SYMBOLIC_CONST_P (XEXP (XEXP (operands[1], 0), 0)))
+ {
+ temp = !can_create_pseudo_p () ? operands[0] : gen_reg_rtx (Pmode);
+ temp = legitimize_pic_address (XEXP (XEXP (operands[1], 0), 0),
+ mode, temp);
+ operands[1] = expand_binop (mode, add_optab, temp,
+ XEXP (XEXP (operands[1], 0), 1),
+ (!can_create_pseudo_p ()
+ ? temp
+ : gen_reg_rtx (Pmode)),
+ 0, OPTAB_LIB_WIDEN);
+ }
+ }
+
+ if (! reload_in_progress && ! reload_completed)
+ {
+ /* Copy the source to a register if both operands aren't registers. */
+ if (! register_operand (operands[0], mode)
+ && ! sh_register_operand (operands[1], mode))
+ operands[1] = copy_to_mode_reg (mode, operands[1]);
+
+ if (MEM_P (operands[0]) && ! memory_operand (operands[0], mode))
+ {
+ /* This is like change_address_1 (operands[0], mode, 0, 1) ,
+ except that we can't use that function because it is static. */
+ rtx new_rtx = change_address (operands[0], mode, 0);
+ MEM_COPY_ATTRIBUTES (new_rtx, operands[0]);
+ operands[0] = new_rtx;
+ }
+
+ /* This case can happen while generating code to move the result
+ of a library call to the target. Reject `st r0,@(rX,rY)' because
+ reload will fail to find a spill register for rX, since r0 is already
+ being used for the source. */
+ else if (TARGET_SH1
+ && refers_to_regno_p (R0_REG, R0_REG + 1, operands[1], (rtx *)0)
+ && MEM_P (operands[0])
+ && GET_CODE (XEXP (operands[0], 0)) == PLUS
+ && REG_P (XEXP (XEXP (operands[0], 0), 1)))
+ operands[1] = copy_to_mode_reg (mode, operands[1]);
+ }
+
+ if (mode == Pmode || mode == ptr_mode)
+ {
+ rtx op0, op1, opc;
+ enum tls_model tls_kind;
+
+ op0 = operands[0];
+ op1 = operands[1];
+ if (GET_CODE (op1) == CONST
+ && GET_CODE (XEXP (op1, 0)) == PLUS
+ && (tls_symbolic_operand (XEXP (XEXP (op1, 0), 0), Pmode)
+ != TLS_MODEL_NONE))
+ {
+ opc = XEXP (XEXP (op1, 0), 1);
+ op1 = XEXP (XEXP (op1, 0), 0);
+ }
+ else
+ opc = NULL_RTX;
+
+ if ((tls_kind = tls_symbolic_operand (op1, Pmode)) != TLS_MODEL_NONE)
+ {
+ rtx tga_op1, tga_ret, tmp, tmp2;
+
+ switch (tls_kind)
+ {
+ case TLS_MODEL_GLOBAL_DYNAMIC:
+ tga_ret = gen_rtx_REG (Pmode, R0_REG);
+ emit_call_insn (gen_tls_global_dynamic (tga_ret, op1));
+ op1 = tga_ret;
+ break;
+
+ case TLS_MODEL_LOCAL_DYNAMIC:
+ tga_ret = gen_rtx_REG (Pmode, R0_REG);
+ emit_call_insn (gen_tls_local_dynamic (tga_ret, op1));
+
+ tmp = gen_reg_rtx (Pmode);
+ emit_move_insn (tmp, tga_ret);
+
+ if (register_operand (op0, Pmode))
+ tmp2 = op0;
+ else
+ tmp2 = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symDTPOFF2reg (tmp2, op1, tmp));
+ op1 = tmp2;
+ break;
+
+ case TLS_MODEL_INITIAL_EXEC:
+ if (! flag_pic)
+ {
+ /* Don't schedule insns for getting GOT address when
+ the first scheduling is enabled, to avoid spill
+ failures for R0. */
+ if (flag_schedule_insns)
+ emit_insn (gen_blockage ());
+ emit_insn (gen_GOTaddr2picreg ());
+ emit_use (gen_rtx_REG (SImode, PIC_REG));
+ if (flag_schedule_insns)
+ emit_insn (gen_blockage ());
+ }
+ tga_op1 = !can_create_pseudo_p () ? op0 : gen_reg_rtx (Pmode);
+ tmp = gen_sym2GOTTPOFF (op1);
+ emit_insn (gen_tls_initial_exec (tga_op1, tmp));
+ op1 = tga_op1;
+ break;
+
+ case TLS_MODEL_LOCAL_EXEC:
+ tmp2 = gen_reg_rtx (Pmode);
+ emit_insn (gen_load_gbr (tmp2));
+ tmp = gen_reg_rtx (Pmode);
+ emit_insn (gen_symTPOFF2reg (tmp, op1));
+
+ if (register_operand (op0, Pmode))
+ op1 = op0;
+ else
+ op1 = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_addsi3 (op1, tmp, tmp2));
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ if (opc)
+ emit_insn (gen_addsi3 (op1, op1, force_reg (SImode, opc)));
+ operands[1] = op1;
+ }
+ }
+
+ return 0;
+}
+
+enum rtx_code
+prepare_cbranch_operands (rtx *operands, enum machine_mode mode,
+ enum rtx_code comparison)
+{
+ rtx op1;
+ rtx scratch = NULL_RTX;
+
+ if (comparison == LAST_AND_UNUSED_RTX_CODE)
+ comparison = GET_CODE (operands[0]);
+ else
+ scratch = operands[4];
+ if (CONST_INT_P (operands[1])
+ && !CONST_INT_P (operands[2]))
+ {
+ rtx tmp = operands[1];
+
+ operands[1] = operands[2];
+ operands[2] = tmp;
+ comparison = swap_condition (comparison);
+ }
+ if (CONST_INT_P (operands[2]))
+ {
+ HOST_WIDE_INT val = INTVAL (operands[2]);
+ if ((val == -1 || val == -0x81)
+ && (comparison == GT || comparison == LE))
+ {
+ comparison = (comparison == GT) ? GE : LT;
+ operands[2] = gen_int_mode (val + 1, mode);
+ }
+ else if ((val == 1 || val == 0x80)
+ && (comparison == GE || comparison == LT))
+ {
+ comparison = (comparison == GE) ? GT : LE;
+ operands[2] = gen_int_mode (val - 1, mode);
+ }
+ else if (val == 1 && (comparison == GEU || comparison == LTU))
+ {
+ comparison = (comparison == GEU) ? NE : EQ;
+ operands[2] = CONST0_RTX (mode);
+ }
+ else if (val == 0x80 && (comparison == GEU || comparison == LTU))
+ {
+ comparison = (comparison == GEU) ? GTU : LEU;
+ operands[2] = gen_int_mode (val - 1, mode);
+ }
+ else if (val == 0 && (comparison == GTU || comparison == LEU))
+ comparison = (comparison == GTU) ? NE : EQ;
+ else if (mode == SImode
+ && ((val == 0x7fffffff
+ && (comparison == GTU || comparison == LEU))
+ || ((unsigned HOST_WIDE_INT) val
+ == (unsigned HOST_WIDE_INT) 0x7fffffff + 1
+ && (comparison == GEU || comparison == LTU))))
+ {
+ comparison = (comparison == GTU || comparison == GEU) ? LT : GE;
+ operands[2] = CONST0_RTX (mode);
+ }
+ }
+ op1 = operands[1];
+ if (can_create_pseudo_p ())
+ operands[1] = force_reg (mode, op1);
+ /* When we are handling DImode comparisons, we want to keep constants so
+ that we can optimize the component comparisons; however, memory loads
+ are better issued as a whole so that they can be scheduled well.
+ SImode equality comparisons allow I08 constants, but only when they
+ compare r0. Hence, if operands[1] has to be loaded from somewhere else
+ into a register, that register might as well be r0, and we allow the
+ constant. If it is already in a register, this is likely to be
+ allocated to a different hard register, thus we load the constant into
+ a register unless it is zero. */
+ if (!REG_P (operands[2])
+ && (!CONST_INT_P (operands[2])
+ || (mode == SImode && operands[2] != CONST0_RTX (SImode)
+ && ((comparison != EQ && comparison != NE)
+ || (REG_P (op1) && REGNO (op1) != R0_REG)
+ || !satisfies_constraint_I08 (operands[2])))))
+ {
+ if (scratch && GET_MODE (scratch) == mode)
+ {
+ emit_move_insn (scratch, operands[2]);
+ operands[2] = scratch;
+ }
+ else if (can_create_pseudo_p ())
+ operands[2] = force_reg (mode, operands[2]);
+ }
+ return comparison;
+}
+
+void
+expand_cbranchsi4 (rtx *operands, enum rtx_code comparison, int probability)
+{
+ rtx (*branch_expander) (rtx) = gen_branch_true;
+ rtx jump;
+
+ comparison = prepare_cbranch_operands (operands, SImode, comparison);
+ switch (comparison)
+ {
+ case NE: case LT: case LE: case LTU: case LEU:
+ comparison = reverse_condition (comparison);
+ branch_expander = gen_branch_false;
+ default: ;
+ }
+ emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, T_REG),
+ gen_rtx_fmt_ee (comparison, SImode,
+ operands[1], operands[2])));
+ jump = emit_jump_insn (branch_expander (operands[3]));
+ if (probability >= 0)
+ add_reg_note (jump, REG_BR_PROB, GEN_INT (probability));
+
+}
+
+/* ??? How should we distribute probabilities when more than one branch
+ is generated. So far we only have soem ad-hoc observations:
+ - If the operands are random, they are likely to differ in both parts.
+ - If comparing items in a hash chain, the operands are random or equal;
+ operation should be EQ or NE.
+ - If items are searched in an ordered tree from the root, we can expect
+ the highpart to be unequal about half of the time; operation should be
+ an inequality comparison, operands non-constant, and overall probability
+ about 50%. Likewise for quicksort.
+ - Range checks will be often made against constants. Even if we assume for
+ simplicity an even distribution of the non-constant operand over a
+ sub-range here, the same probability could be generated with differently
+ wide sub-ranges - as long as the ratio of the part of the subrange that
+ is before the threshold to the part that comes after the threshold stays
+ the same. Thus, we can't really tell anything here;
+ assuming random distribution is at least simple.
+ */
+
+bool
+expand_cbranchdi4 (rtx *operands, enum rtx_code comparison)
+{
+ enum rtx_code msw_taken, msw_skip, lsw_taken;
+ rtx skip_label = NULL_RTX;
+ rtx op1h, op1l, op2h, op2l;
+ int num_branches;
+ int prob, rev_prob;
+ int msw_taken_prob = -1, msw_skip_prob = -1, lsw_taken_prob = -1;
+ rtx scratch = operands[4];
+
+ comparison = prepare_cbranch_operands (operands, DImode, comparison);
+ op1h = gen_highpart_mode (SImode, DImode, operands[1]);
+ op2h = gen_highpart_mode (SImode, DImode, operands[2]);
+ op1l = gen_lowpart (SImode, operands[1]);
+ op2l = gen_lowpart (SImode, operands[2]);
+ msw_taken = msw_skip = lsw_taken = LAST_AND_UNUSED_RTX_CODE;
+ prob = split_branch_probability;
+ rev_prob = REG_BR_PROB_BASE - prob;
+ switch (comparison)
+ {
+ /* ??? Should we use the cmpeqdi_t pattern for equality comparisons?
+ That costs 1 cycle more when the first branch can be predicted taken,
+ but saves us mispredicts because only one branch needs prediction.
+ It also enables generating the cmpeqdi_t-1 pattern. */
+ case EQ:
+ if (TARGET_CMPEQDI_T)
+ {
+ emit_insn (gen_cmpeqdi_t (operands[1], operands[2]));
+ emit_jump_insn (gen_branch_true (operands[3]));
+ return true;
+ }
+ msw_skip = NE;
+ lsw_taken = EQ;
+ if (prob >= 0)
+ {
+ /* If we had more precision, we'd use rev_prob - (rev_prob >> 32) .
+ */
+ msw_skip_prob = rev_prob;
+ if (REG_BR_PROB_BASE <= 65535)
+ lsw_taken_prob = prob ? REG_BR_PROB_BASE : 0;
+ else
+ {
+ gcc_assert (HOST_BITS_PER_WIDEST_INT >= 64);
+ lsw_taken_prob
+ = (prob
+ ? (REG_BR_PROB_BASE
+ - ((HOST_WIDEST_INT) REG_BR_PROB_BASE * rev_prob
+ / ((HOST_WIDEST_INT) prob << 32)))
+ : 0);
+ }
+ }
+ break;
+ case NE:
+ if (TARGET_CMPEQDI_T)
+ {
+ emit_insn (gen_cmpeqdi_t (operands[1], operands[2]));
+ emit_jump_insn (gen_branch_false (operands[3]));
+ return true;
+ }
+ msw_taken = NE;
+ msw_taken_prob = prob;
+ lsw_taken = NE;
+ lsw_taken_prob = 0;
+ break;
+ case GTU: case GT:
+ msw_taken = comparison;
+ if (CONST_INT_P (op2l) && INTVAL (op2l) == -1)
+ break;
+ if (comparison != GTU || op2h != CONST0_RTX (SImode))
+ msw_skip = swap_condition (msw_taken);
+ lsw_taken = GTU;
+ break;
+ case GEU: case GE:
+ if (op2l == CONST0_RTX (SImode))
+ msw_taken = comparison;
+ else
+ {
+ msw_taken = comparison == GE ? GT : GTU;
+ msw_skip = swap_condition (msw_taken);
+ lsw_taken = GEU;
+ }
+ break;
+ case LTU: case LT:
+ msw_taken = comparison;
+ if (op2l == CONST0_RTX (SImode))
+ break;
+ msw_skip = swap_condition (msw_taken);
+ lsw_taken = LTU;
+ break;
+ case LEU: case LE:
+ if (CONST_INT_P (op2l) && INTVAL (op2l) == -1)
+ msw_taken = comparison;
+ else
+ {
+ lsw_taken = LEU;
+ if (comparison == LE)
+ msw_taken = LT;
+ else if (op2h != CONST0_RTX (SImode))
+ msw_taken = LTU;
+ else
+ {
+ msw_skip = swap_condition (LTU);
+ break;
+ }
+ msw_skip = swap_condition (msw_taken);
+ }
+ break;
+ default: return false;
+ }
+ num_branches = ((msw_taken != LAST_AND_UNUSED_RTX_CODE)
+ + (msw_skip != LAST_AND_UNUSED_RTX_CODE)
+ + (lsw_taken != LAST_AND_UNUSED_RTX_CODE));
+ if (comparison != EQ && comparison != NE && num_branches > 1)
+ {
+ if (!CONSTANT_P (operands[2])
+ && prob >= (int) (REG_BR_PROB_BASE * 3 / 8U)
+ && prob <= (int) (REG_BR_PROB_BASE * 5 / 8U))
+ {
+ msw_taken_prob = prob / 2U;
+ msw_skip_prob
+ = REG_BR_PROB_BASE * rev_prob / (REG_BR_PROB_BASE + rev_prob);
+ lsw_taken_prob = prob;
+ }
+ else
+ {
+ msw_taken_prob = prob;
+ msw_skip_prob = REG_BR_PROB_BASE;
+ /* ??? If we have a constant op2h, should we use that when
+ calculating lsw_taken_prob? */
+ lsw_taken_prob = prob;
+ }
+ }
+ operands[1] = op1h;
+ operands[2] = op2h;
+ operands[4] = NULL_RTX;
+ if (reload_completed
+ && ! arith_reg_or_0_operand (op2h, SImode)
+ && (true_regnum (op1h) || (comparison != EQ && comparison != NE))
+ && (msw_taken != LAST_AND_UNUSED_RTX_CODE
+ || msw_skip != LAST_AND_UNUSED_RTX_CODE))
+ {
+ emit_move_insn (scratch, operands[2]);
+ operands[2] = scratch;
+ }
+ if (msw_taken != LAST_AND_UNUSED_RTX_CODE)
+ expand_cbranchsi4 (operands, msw_taken, msw_taken_prob);
+ if (msw_skip != LAST_AND_UNUSED_RTX_CODE)
+ {
+ rtx taken_label = operands[3];
+
+ /* Operands were possibly modified, but msw_skip doesn't expect this.
+ Always use the original ones. */
+ if (msw_taken != LAST_AND_UNUSED_RTX_CODE)
+ {
+ operands[1] = op1h;
+ operands[2] = op2h;
+ if (reload_completed
+ && ! arith_reg_or_0_operand (op2h, SImode)
+ && (true_regnum (op1h) || (comparison != EQ && comparison != NE)))
+ {
+ emit_move_insn (scratch, operands[2]);
+ operands[2] = scratch;
+ }
+ }
+
+ operands[3] = skip_label = gen_label_rtx ();
+ expand_cbranchsi4 (operands, msw_skip, msw_skip_prob);
+ operands[3] = taken_label;
+ }
+ operands[1] = op1l;
+ operands[2] = op2l;
+ if (lsw_taken != LAST_AND_UNUSED_RTX_CODE)
+ {
+ if (reload_completed
+ && ! arith_reg_or_0_operand (op2l, SImode)
+ && (true_regnum (op1l) || (lsw_taken != EQ && lsw_taken != NE)))
+ {
+ emit_move_insn (scratch, operands[2]);
+ operands[2] = scratch;
+ }
+ expand_cbranchsi4 (operands, lsw_taken, lsw_taken_prob);
+ }
+ if (msw_skip != LAST_AND_UNUSED_RTX_CODE)
+ emit_label (skip_label);
+ return true;
+}
+
+/* Emit INSN, possibly in a PARALLEL with an USE of fpscr for SH4. */
+
+static void
+sh_emit_set_t_insn (rtx insn, enum machine_mode mode)
+{
+ if ((TARGET_SH4 || TARGET_SH2A) && GET_MODE_CLASS (mode) == MODE_FLOAT)
+ {
+ insn = gen_rtx_PARALLEL (VOIDmode,
+ gen_rtvec (2, insn,
+ gen_rtx_USE (VOIDmode, get_fpscr_rtx ())));
+ (mode == SFmode ? emit_sf_insn : emit_df_insn) (insn);
+ }
+ else
+ emit_insn (insn);
+}
+
+/* Prepare the operands for an scc instruction; make sure that the
+ compare has been done and the result is in T_REG. */
+void
+sh_emit_scc_to_t (enum rtx_code code, rtx op0, rtx op1)
+{
+ rtx t_reg = gen_rtx_REG (SImode, T_REG);
+ enum rtx_code oldcode = code;
+ enum machine_mode mode;
+
+ /* First need a compare insn. */
+ switch (code)
+ {
+ case NE:
+ /* It isn't possible to handle this case. */
+ gcc_unreachable ();
+ case LT:
+ code = GT;
+ break;
+ case LE:
+ code = GE;
+ break;
+ case LTU:
+ code = GTU;
+ break;
+ case LEU:
+ code = GEU;
+ break;
+ default:
+ break;
+ }
+ if (code != oldcode)
+ {
+ rtx tmp = op0;
+ op0 = op1;
+ op1 = tmp;
+ }
+
+ mode = GET_MODE (op0);
+ if (mode == VOIDmode)
+ mode = GET_MODE (op1);
+
+ op0 = force_reg (mode, op0);
+ if ((code != EQ && code != NE
+ && (op1 != const0_rtx
+ || code == GTU || code == GEU || code == LTU || code == LEU))
+ || (mode == DImode && op1 != const0_rtx)
+ || (TARGET_SH2E && GET_MODE_CLASS (mode) == MODE_FLOAT))
+ op1 = force_reg (mode, op1);
+
+ sh_emit_set_t_insn (gen_rtx_SET (VOIDmode, t_reg,
+ gen_rtx_fmt_ee (code, SImode, op0, op1)),
+ mode);
+}
+
+rtx
+sh_emit_cheap_store_flag (enum machine_mode mode, enum rtx_code code,
+ rtx op0, rtx op1)
+{
+ rtx target = gen_reg_rtx (SImode);
+ rtx tmp;
+
+ gcc_assert (TARGET_SHMEDIA);
+ switch (code)
+ {
+ case EQ:
+ case GT:
+ case LT:
+ case UNORDERED:
+ case GTU:
+ case LTU:
+ tmp = gen_rtx_fmt_ee (code, SImode, op0, op1);
+ emit_insn (gen_cstore4_media (target, tmp, op0, op1));
+ code = NE;
+ break;
+
+ case NE:
+ case GE:
+ case LE:
+ case ORDERED:
+ case GEU:
+ case LEU:
+ tmp = gen_rtx_fmt_ee (reverse_condition (code), mode, op0, op1);
+ emit_insn (gen_cstore4_media (target, tmp, op0, op1));
+ code = EQ;
+ break;
+
+ case UNEQ:
+ case UNGE:
+ case UNGT:
+ case UNLE:
+ case UNLT:
+ case LTGT:
+ return NULL_RTX;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ if (mode == DImode)
+ {
+ rtx t2 = gen_reg_rtx (DImode);
+ emit_insn (gen_extendsidi2 (t2, target));
+ target = t2;
+ }
+
+ return gen_rtx_fmt_ee (code, VOIDmode, target, const0_rtx);
+}
+
+/* Called from the md file, set up the operands of a compare instruction. */
+
+void
+sh_emit_compare_and_branch (rtx *operands, enum machine_mode mode)
+{
+ enum rtx_code code = GET_CODE (operands[0]);
+ enum rtx_code branch_code;
+ rtx op0 = operands[1];
+ rtx op1 = operands[2];
+ rtx insn, tem;
+ bool need_ccmpeq = false;
+
+ if (TARGET_SH2E && GET_MODE_CLASS (mode) == MODE_FLOAT)
+ {
+ op0 = force_reg (mode, op0);
+ op1 = force_reg (mode, op1);
+ }
+ else
+ {
+ if (code != EQ || mode == DImode)
+ {
+ /* Force args into regs, since we can't use constants here. */
+ op0 = force_reg (mode, op0);
+ if (op1 != const0_rtx || code == GTU || code == GEU)
+ op1 = force_reg (mode, op1);
+ }
+ }
+
+ if (GET_MODE_CLASS (mode) == MODE_FLOAT)
+ {
+ if (code == LT
+ || (code == LE && TARGET_IEEE && TARGET_SH2E)
+ || (code == GE && !(TARGET_IEEE && TARGET_SH2E)))
+ {
+ tem = op0, op0 = op1, op1 = tem;
+ code = swap_condition (code);
+ }
+
+ /* GE becomes fcmp/gt+fcmp/eq, for SH2E and TARGET_IEEE only. */
+ if (code == GE)
+ {
+ gcc_assert (TARGET_IEEE && TARGET_SH2E);
+ need_ccmpeq = true;
+ code = GT;
+ }
+
+ /* Now we can have EQ, NE, GT, LE. NE and LE are then transformed
+ to EQ/GT respectively. */
+ gcc_assert (code == EQ || code == GT || code == NE || code == LE);
+ }
+
+ switch (code)
+ {
+ case EQ:
+ case GT:
+ case GE:
+ case GTU:
+ case GEU:
+ branch_code = code;
+ break;
+ case NE:
+ case LT:
+ case LE:
+ case LTU:
+ case LEU:
+ branch_code = reverse_condition (code);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+
+ insn = gen_rtx_SET (VOIDmode,
+ gen_rtx_REG (SImode, T_REG),
+ gen_rtx_fmt_ee (branch_code, SImode, op0, op1));
+
+ sh_emit_set_t_insn (insn, mode);
+ if (need_ccmpeq)
+ sh_emit_set_t_insn (gen_ieee_ccmpeqsf_t (op0, op1), mode);
+
+ if (branch_code == code)
+ emit_jump_insn (gen_branch_true (operands[3]));
+ else
+ emit_jump_insn (gen_branch_false (operands[3]));
+}
+
+void
+sh_emit_compare_and_set (rtx *operands, enum machine_mode mode)
+{
+ enum rtx_code code = GET_CODE (operands[1]);
+ rtx op0 = operands[2];
+ rtx op1 = operands[3];
+ rtx lab = NULL_RTX;
+ bool invert = false;
+ rtx tem;
+
+ op0 = force_reg (mode, op0);
+ if ((code != EQ && code != NE
+ && (op1 != const0_rtx
+ || code == GTU || code == GEU || code == LTU || code == LEU))
+ || (mode == DImode && op1 != const0_rtx)
+ || (TARGET_SH2E && GET_MODE_CLASS (mode) == MODE_FLOAT))
+ op1 = force_reg (mode, op1);
+
+ if (GET_MODE_CLASS (mode) == MODE_FLOAT)
+ {
+ if (code == LT || code == LE)
+ {
+ code = swap_condition (code);
+ tem = op0, op0 = op1, op1 = tem;
+ }
+ if (code == GE)
+ {
+ if (TARGET_IEEE)
+ {
+ lab = gen_label_rtx ();
+ sh_emit_scc_to_t (EQ, op0, op1);
+ emit_jump_insn (gen_branch_true (lab));
+ code = GT;
+ }
+ else
+ {
+ code = LT;
+ invert = true;
+ }
+ }
+ }
+
+ if (code == NE)
+ {
+ code = EQ;
+ invert = true;
+ }
+
+ sh_emit_scc_to_t (code, op0, op1);
+ if (lab)
+ emit_label (lab);
+ if (invert)
+ emit_insn (gen_movnegt (operands[0]));
+ else
+ emit_move_insn (operands[0], gen_rtx_REG (SImode, T_REG));
+}
+
+/* Functions to output assembly code. */
+
+/* Return a sequence of instructions to perform DI or DF move.
+
+ Since the SH cannot move a DI or DF in one instruction, we have
+ to take care when we see overlapping source and dest registers. */
+
+const char *
+output_movedouble (rtx insn ATTRIBUTE_UNUSED, rtx operands[],
+ enum machine_mode mode)
+{
+ rtx dst = operands[0];
+ rtx src = operands[1];
+
+ if (MEM_P (dst)
+ && GET_CODE (XEXP (dst, 0)) == PRE_DEC)
+ return "mov.l %T1,%0\n\tmov.l %1,%0";
+
+ if (register_operand (dst, mode)
+ && register_operand (src, mode))
+ {
+ if (REGNO (src) == MACH_REG)
+ return "sts mach,%S0\n\tsts macl,%R0";
+
+ /* When mov.d r1,r2 do r2->r3 then r1->r2;
+ when mov.d r1,r0 do r1->r0 then r2->r1. */
+
+ if (REGNO (src) + 1 == REGNO (dst))
+ return "mov %T1,%T0\n\tmov %1,%0";
+ else
+ return "mov %1,%0\n\tmov %T1,%T0";
+ }
+ else if (CONST_INT_P (src))
+ {
+ if (INTVAL (src) < 0)
+ output_asm_insn ("mov #-1,%S0", operands);
+ else
+ output_asm_insn ("mov #0,%S0", operands);
+
+ return "mov %1,%R0";
+ }
+ else if (MEM_P (src))
+ {
+ int ptrreg = -1;
+ int dreg = REGNO (dst);
+ rtx inside = XEXP (src, 0);
+
+ switch (GET_CODE (inside))
+ {
+ case REG:
+ ptrreg = REGNO (inside);
+ break;
+
+ case SUBREG:
+ ptrreg = subreg_regno (inside);
+ break;
+
+ case PLUS:
+ ptrreg = REGNO (XEXP (inside, 0));
+ /* ??? A r0+REG address shouldn't be possible here, because it isn't
+ an offsettable address. Unfortunately, offsettable addresses use
+ QImode to check the offset, and a QImode offsettable address
+ requires r0 for the other operand, which is not currently
+ supported, so we can't use the 'o' constraint.
+ Thus we must check for and handle r0+REG addresses here.
+ We punt for now, since this is likely very rare. */
+ gcc_assert (!REG_P (XEXP (inside, 1)));
+ break;
+
+ case LABEL_REF:
+ return "mov.l %1,%0\n\tmov.l %1+4,%T0";
+ case POST_INC:
+ return "mov.l %1,%0\n\tmov.l %1,%T0";
+ default:
+ gcc_unreachable ();
+ }
+
+ /* Work out the safe way to copy. Copy into the second half first. */
+ if (dreg == ptrreg)
+ return "mov.l %T1,%T0\n\tmov.l %1,%0";
+ }
+
+ return "mov.l %1,%0\n\tmov.l %T1,%T0";
+}
+
+/* Print an instruction which would have gone into a delay slot after
+ another instruction, but couldn't because the other instruction expanded
+ into a sequence where putting the slot insn at the end wouldn't work. */
+
+static void
+print_slot (rtx insn)
+{
+ final_scan_insn (XVECEXP (insn, 0, 1), asm_out_file, optimize, 1, NULL);
+
+ INSN_DELETED_P (XVECEXP (insn, 0, 1)) = 1;
+}
+
+const char *
+output_far_jump (rtx insn, rtx op)
+{
+ struct { rtx lab, reg, op; } this_jmp;
+ rtx braf_base_lab = NULL_RTX;
+ const char *jump;
+ int far;
+ int offset = branch_dest (insn) - INSN_ADDRESSES (INSN_UID (insn));
+ rtx prev;
+
+ this_jmp.lab = gen_label_rtx ();
+
+ if (TARGET_SH2
+ && offset >= -32764
+ && offset - get_attr_length (insn) <= 32766)
+ {
+ far = 0;
+ jump = "mov.w %O0,%1; braf %1";
+ }
+ else
+ {
+ far = 1;
+ if (flag_pic)
+ {
+ if (TARGET_SH2)
+ jump = "mov.l %O0,%1; braf %1";
+ else
+ jump = "mov.l r0,@-r15; mova %O0,r0; mov.l @r0,%1; add r0,%1; mov.l @r15+,r0; jmp @%1";
+ }
+ else
+ jump = "mov.l %O0,%1; jmp @%1";
+ }
+ /* If we have a scratch register available, use it. */
+ if (NONJUMP_INSN_P ((prev = prev_nonnote_insn (insn)))
+ && INSN_CODE (prev) == CODE_FOR_indirect_jump_scratch)
+ {
+ this_jmp.reg = SET_DEST (XVECEXP (PATTERN (prev), 0, 0));
+ if (REGNO (this_jmp.reg) == R0_REG && flag_pic && ! TARGET_SH2)
+ jump = "mov.l r1,@-r15; mova %O0,r0; mov.l @r0,r1; add r1,r0; mov.l @r15+,r1; jmp @%1";
+ output_asm_insn (jump, &this_jmp.lab);
+ if (dbr_sequence_length ())
+ print_slot (final_sequence);
+ else
+ output_asm_insn ("nop", 0);
+ }
+ else
+ {
+ /* Output the delay slot insn first if any. */
+ if (dbr_sequence_length ())
+ print_slot (final_sequence);
+
+ this_jmp.reg = gen_rtx_REG (SImode, 13);
+ /* We must keep the stack aligned to 8-byte boundaries on SH5.
+ Fortunately, MACL is fixed and call-clobbered, and we never
+ need its value across jumps, so save r13 in it instead of in
+ the stack. */
+ if (TARGET_SH5)
+ output_asm_insn ("lds r13, macl", 0);
+ else
+ output_asm_insn ("mov.l r13,@-r15", 0);
+ output_asm_insn (jump, &this_jmp.lab);
+ if (TARGET_SH5)
+ output_asm_insn ("sts macl, r13", 0);
+ else
+ output_asm_insn ("mov.l @r15+,r13", 0);
+ }
+ if (far && flag_pic && TARGET_SH2)
+ {
+ braf_base_lab = gen_label_rtx ();
+ (*targetm.asm_out.internal_label) (asm_out_file, "L",
+ CODE_LABEL_NUMBER (braf_base_lab));
+ }
+ if (far)
+ output_asm_insn (".align 2", 0);
+ (*targetm.asm_out.internal_label) (asm_out_file, "L", CODE_LABEL_NUMBER (this_jmp.lab));
+ this_jmp.op = op;
+ if (far && flag_pic)
+ {
+ if (TARGET_SH2)
+ this_jmp.lab = braf_base_lab;
+ output_asm_insn (".long %O2-%O0", &this_jmp.lab);
+ }
+ else
+ output_asm_insn (far ? ".long %O2" : ".word %O2-%O0", &this_jmp.lab);
+ return "";
+}
+
+/* Local label counter, used for constants in the pool and inside
+ pattern branches. */
+
+static int lf = 100;
+
+/* Output code for ordinary branches. */
+
+const char *
+output_branch (int logic, rtx insn, rtx *operands)
+{
+ switch (get_attr_length (insn))
+ {
+ case 6:
+ /* This can happen if filling the delay slot has caused a forward
+ branch to exceed its range (we could reverse it, but only
+ when we know we won't overextend other branches; this should
+ best be handled by relaxation).
+ It can also happen when other condbranches hoist delay slot insn
+ from their destination, thus leading to code size increase.
+ But the branch will still be in the range -4092..+4098 bytes. */
+
+ if (! TARGET_RELAX)
+ {
+ int label = lf++;
+ /* The call to print_slot will clobber the operands. */
+ rtx op0 = operands[0];
+
+ /* If the instruction in the delay slot is annulled (true), then
+ there is no delay slot where we can put it now. The only safe
+ place for it is after the label. final will do that by default. */
+
+ if (final_sequence
+ && ! INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0))
+ && get_attr_length (XVECEXP (final_sequence, 0, 1)))
+ {
+ asm_fprintf (asm_out_file, "\tb%s%ss\t%LLF%d\n", logic ? "f" : "t",
+ ASSEMBLER_DIALECT ? "/" : ".", label);
+ print_slot (final_sequence);
+ }
+ else
+ asm_fprintf (asm_out_file, "\tb%s\t%LLF%d\n", logic ? "f" : "t", label);
+
+ output_asm_insn ("bra\t%l0", &op0);
+ fprintf (asm_out_file, "\tnop\n");
+ (*targetm.asm_out.internal_label) (asm_out_file, "LF", label);
+
+ return "";
+ }
+ /* When relaxing, handle this like a short branch. The linker
+ will fix it up if it still doesn't fit after relaxation. */
+ case 2:
+ return logic ? "bt%.\t%l0" : "bf%.\t%l0";
+
+ /* These are for SH2e, in which we have to account for the
+ extra nop because of the hardware bug in annulled branches. */
+ case 8:
+ if (! TARGET_RELAX)
+ {
+ int label = lf++;
+
+ gcc_assert (!final_sequence
+ || !(INSN_ANNULLED_BRANCH_P
+ (XVECEXP (final_sequence, 0, 0))));
+ asm_fprintf (asm_out_file, "b%s%ss\t%LLF%d\n",
+ logic ? "f" : "t",
+ ASSEMBLER_DIALECT ? "/" : ".", label);
+ fprintf (asm_out_file, "\tnop\n");
+ output_asm_insn ("bra\t%l0", operands);
+ fprintf (asm_out_file, "\tnop\n");
+ (*targetm.asm_out.internal_label) (asm_out_file, "LF", label);
+
+ return "";
+ }
+ /* When relaxing, fall through. */
+ case 4:
+ {
+ char buffer[10];
+
+ sprintf (buffer, "b%s%ss\t%%l0",
+ logic ? "t" : "f",
+ ASSEMBLER_DIALECT ? "/" : ".");
+ output_asm_insn (buffer, &operands[0]);
+ return "nop";
+ }
+
+ default:
+ /* There should be no longer branches now - that would
+ indicate that something has destroyed the branches set
+ up in machine_dependent_reorg. */
+ gcc_unreachable ();
+ }
+}
+
+/* Output a code sequence for INSN using TEMPL with OPERANDS; but before,
+ fill in operands 9 as a label to the successor insn.
+ We try to use jump threading where possible.
+ IF CODE matches the comparison in the IF_THEN_ELSE of a following jump,
+ we assume the jump is taken. I.e. EQ means follow jmp and bf, NE means
+ follow jmp and bt, if the address is in range. */
+const char *
+output_branchy_insn (enum rtx_code code, const char *templ,
+ rtx insn, rtx *operands)
+{
+ rtx next_insn = NEXT_INSN (insn);
+
+ if (next_insn && JUMP_P (next_insn) && condjump_p (next_insn))
+ {
+ rtx src = SET_SRC (PATTERN (next_insn));
+ if (GET_CODE (src) == IF_THEN_ELSE && GET_CODE (XEXP (src, 0)) != code)
+ {
+ /* Following branch not taken */
+ operands[9] = gen_label_rtx ();
+ emit_label_after (operands[9], next_insn);
+ INSN_ADDRESSES_NEW (operands[9],
+ INSN_ADDRESSES (INSN_UID (next_insn))
+ + get_attr_length (next_insn));
+ return templ;
+ }
+ else
+ {
+ int offset = (branch_dest (next_insn)
+ - INSN_ADDRESSES (INSN_UID (next_insn)) + 4);
+ if (offset >= -252 && offset <= 258)
+ {
+ if (GET_CODE (src) == IF_THEN_ELSE)
+ /* branch_true */
+ src = XEXP (src, 1);
+ operands[9] = src;
+ return templ;
+ }
+ }
+ }
+ operands[9] = gen_label_rtx ();
+ emit_label_after (operands[9], insn);
+ INSN_ADDRESSES_NEW (operands[9],
+ INSN_ADDRESSES (INSN_UID (insn))
+ + get_attr_length (insn));
+ return templ;
+}
+
+const char *
+output_ieee_ccmpeq (rtx insn, rtx *operands)
+{
+ return output_branchy_insn (NE, "bt\t%l9\n\tfcmp/eq\t%1,%0",
+ insn, operands);
+}
+
+/* Output the start of the assembler file. */
+
+static void
+sh_file_start (void)
+{
+ default_file_start ();
+
+#ifdef SYMBIAN
+ /* Declare the .directive section before it is used. */
+ fputs ("\t.section .directive, \"SM\", @progbits, 1\n", asm_out_file);
+ fputs ("\t.asciz \"#<SYMEDIT>#\\n\"\n", asm_out_file);
+#endif
+
+ if (TARGET_ELF)
+ /* We need to show the text section with the proper
+ attributes as in TEXT_SECTION_ASM_OP, before dwarf2out
+ emits it without attributes in TEXT_SECTION_ASM_OP, else GAS
+ will complain. We can teach GAS specifically about the
+ default attributes for our choice of text section, but
+ then we would have to change GAS again if/when we change
+ the text section name. */
+ fprintf (asm_out_file, "%s\n", TEXT_SECTION_ASM_OP);
+ else
+ /* Switch to the data section so that the coffsem symbol
+ isn't in the text section. */
+ switch_to_section (data_section);
+
+ if (TARGET_LITTLE_ENDIAN)
+ fputs ("\t.little\n", asm_out_file);
+
+ if (!TARGET_ELF)
+ {
+ if (TARGET_SHCOMPACT)
+ fputs ("\t.mode\tSHcompact\n", asm_out_file);
+ else if (TARGET_SHMEDIA)
+ fprintf (asm_out_file, "\t.mode\tSHmedia\n\t.abi\t%i\n",
+ TARGET_SHMEDIA64 ? 64 : 32);
+ }
+}
+
+/* Check if PAT includes UNSPEC_CALLER unspec pattern. */
+
+static bool
+unspec_caller_rtx_p (rtx pat)
+{
+ rtx base, offset;
+ int i;
+
+ split_const (pat, &base, &offset);
+ if (GET_CODE (base) == UNSPEC)
+ {
+ if (XINT (base, 1) == UNSPEC_CALLER)
+ return true;
+ for (i = 0; i < XVECLEN (base, 0); i++)
+ if (unspec_caller_rtx_p (XVECEXP (base, 0, i)))
+ return true;
+ }
+ return false;
+}
+
+/* Indicate that INSN cannot be duplicated. This is true for insn
+ that generates a unique label. */
+
+static bool
+sh_cannot_copy_insn_p (rtx insn)
+{
+ rtx pat;
+
+ if (!reload_completed || !flag_pic)
+ return false;
+
+ if (!NONJUMP_INSN_P (insn))
+ return false;
+ if (asm_noperands (insn) >= 0)
+ return false;
+
+ pat = PATTERN (insn);
+ if (GET_CODE (pat) != SET)
+ return false;
+ pat = SET_SRC (pat);
+
+ if (unspec_caller_rtx_p (pat))
+ return true;
+
+ return false;
+}
+
+/* Actual number of instructions used to make a shift by N. */
+static const char ashiftrt_insns[] =
+ { 0,1,2,3,4,5,8,8,8,8,8,8,8,8,8,8,2,3,4,5,8,8,8,8,8,8,8,8,8,8,8,2};
+
+/* Left shift and logical right shift are the same. */
+static const char shift_insns[] =
+ { 0,1,1,2,2,3,3,4,1,2,2,3,3,4,3,3,1,2,2,3,3,4,3,3,2,3,3,4,4,4,3,3};
+
+/* Individual shift amounts needed to get the above length sequences.
+ One bit right shifts clobber the T bit, so when possible, put one bit
+ shifts in the middle of the sequence, so the ends are eligible for
+ branch delay slots. */
+static const short shift_amounts[32][5] = {
+ {0}, {1}, {2}, {2, 1},
+ {2, 2}, {2, 1, 2}, {2, 2, 2}, {2, 2, 1, 2},
+ {8}, {8, 1}, {8, 2}, {8, 1, 2},
+ {8, 2, 2}, {8, 2, 1, 2}, {8, -2, 8}, {8, -1, 8},
+ {16}, {16, 1}, {16, 2}, {16, 1, 2},
+ {16, 2, 2}, {16, 2, 1, 2}, {16, -2, 8}, {16, -1, 8},
+ {16, 8}, {16, 1, 8}, {16, 8, 2}, {16, 8, 1, 2},
+ {16, 8, 2, 2}, {16, -1, -2, 16}, {16, -2, 16}, {16, -1, 16}};
+
+/* Likewise, but for shift amounts < 16, up to three highmost bits
+ might be clobbered. This is typically used when combined with some
+ kind of sign or zero extension. */
+
+static const char ext_shift_insns[] =
+ { 0,1,1,2,2,3,2,2,1,2,2,3,3,3,2,2,1,2,2,3,3,4,3,3,2,3,3,4,4,4,3,3};
+
+static const short ext_shift_amounts[32][4] = {
+ {0}, {1}, {2}, {2, 1},
+ {2, 2}, {2, 1, 2}, {8, -2}, {8, -1},
+ {8}, {8, 1}, {8, 2}, {8, 1, 2},
+ {8, 2, 2}, {16, -2, -1}, {16, -2}, {16, -1},
+ {16}, {16, 1}, {16, 2}, {16, 1, 2},
+ {16, 2, 2}, {16, 2, 1, 2}, {16, -2, 8}, {16, -1, 8},
+ {16, 8}, {16, 1, 8}, {16, 8, 2}, {16, 8, 1, 2},
+ {16, 8, 2, 2}, {16, -1, -2, 16}, {16, -2, 16}, {16, -1, 16}};
+
+/* Assuming we have a value that has been sign-extended by at least one bit,
+ can we use the ext_shift_amounts with the last shift turned to an arithmetic shift
+ to shift it by N without data loss, and quicker than by other means? */
+#define EXT_SHIFT_SIGNED(n) (((n) | 8) == 15)
+
+/* This is used in length attributes in sh.md to help compute the length
+ of arbitrary constant shift instructions. */
+
+int
+shift_insns_rtx (rtx insn)
+{
+ rtx set_src = SET_SRC (XVECEXP (PATTERN (insn), 0, 0));
+ int shift_count = INTVAL (XEXP (set_src, 1)) & 31;
+ enum rtx_code shift_code = GET_CODE (set_src);
+
+ switch (shift_code)
+ {
+ case ASHIFTRT:
+ return ashiftrt_insns[shift_count];
+ case LSHIFTRT:
+ case ASHIFT:
+ return shift_insns[shift_count];
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* Return the cost of a shift. */
+
+static inline int
+shiftcosts (rtx x)
+{
+ int value;
+
+ if (TARGET_SHMEDIA)
+ return 1;
+
+ if (GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD)
+ {
+ if (GET_MODE (x) == DImode
+ && CONST_INT_P (XEXP (x, 1))
+ && INTVAL (XEXP (x, 1)) == 1)
+ return 2;
+
+ /* Everything else is invalid, because there is no pattern for it. */
+ return MAX_COST;
+ }
+ /* If shift by a non constant, then this will be expensive. */
+ if (!CONST_INT_P (XEXP (x, 1)))
+ return SH_DYNAMIC_SHIFT_COST;
+
+ /* Otherwise, return the true cost in instructions. Cope with out of range
+ shift counts more or less arbitrarily. */
+ value = INTVAL (XEXP (x, 1)) & 31;
+
+ if (GET_CODE (x) == ASHIFTRT)
+ {
+ int cost = ashiftrt_insns[value];
+ /* If SH3, then we put the constant in a reg and use shad. */
+ if (cost > 1 + SH_DYNAMIC_SHIFT_COST)
+ cost = 1 + SH_DYNAMIC_SHIFT_COST;
+ return cost;
+ }
+ else
+ return shift_insns[value];
+}
+
+/* Return the cost of an AND operation. */
+
+static inline int
+andcosts (rtx x)
+{
+ int i;
+
+ /* Anding with a register is a single cycle and instruction. */
+ if (!CONST_INT_P (XEXP (x, 1)))
+ return 1;
+
+ i = INTVAL (XEXP (x, 1));
+
+ if (TARGET_SHMEDIA)
+ {
+ if (satisfies_constraint_I10 (XEXP (x, 1))
+ || satisfies_constraint_J16 (XEXP (x, 1)))
+ return 1;
+ else
+ return 1 + rtx_cost (XEXP (x, 1), AND, !optimize_size);
+ }
+
+ /* These constants are single cycle extu.[bw] instructions. */
+ if (i == 0xff || i == 0xffff)
+ return 1;
+ /* Constants that can be used in an and immediate instruction in a single
+ cycle, but this requires r0, so make it a little more expensive. */
+ if (CONST_OK_FOR_K08 (i))
+ return 2;
+ /* Constants that can be loaded with a mov immediate and an and.
+ This case is probably unnecessary. */
+ if (CONST_OK_FOR_I08 (i))
+ return 2;
+ /* Any other constants requires a 2 cycle pc-relative load plus an and.
+ This case is probably unnecessary. */
+ return 3;
+}
+
+/* Return the cost of an addition or a subtraction. */
+
+static inline int
+addsubcosts (rtx x)
+{
+ /* Adding a register is a single cycle insn. */
+ if (REG_P (XEXP (x, 1))
+ || GET_CODE (XEXP (x, 1)) == SUBREG)
+ return 1;
+
+ /* Likewise for small constants. */
+ if (CONST_INT_P (XEXP (x, 1))
+ && CONST_OK_FOR_ADD (INTVAL (XEXP (x, 1))))
+ return 1;
+
+ if (TARGET_SHMEDIA)
+ switch (GET_CODE (XEXP (x, 1)))
+ {
+ case CONST:
+ case LABEL_REF:
+ case SYMBOL_REF:
+ return TARGET_SHMEDIA64 ? 5 : 3;
+
+ case CONST_INT:
+ if (CONST_OK_FOR_I16 (INTVAL (XEXP (x, 1))))
+ return 2;
+ else if (CONST_OK_FOR_I16 (INTVAL (XEXP (x, 1)) >> 16))
+ return 3;
+ else if (CONST_OK_FOR_I16 ((INTVAL (XEXP (x, 1)) >> 16) >> 16))
+ return 4;
+
+ /* Fall through. */
+ default:
+ return 5;
+ }
+
+ /* Any other constant requires a 2 cycle pc-relative load plus an
+ addition. */
+ return 3;
+}
+
+/* Return the cost of a multiply. */
+static inline int
+multcosts (rtx x ATTRIBUTE_UNUSED)
+{
+ if (sh_multcost >= 0)
+ return sh_multcost;
+ if (TARGET_SHMEDIA)
+ /* ??? We have a mul insn, but it has a latency of three, and doesn't
+ accept constants. Ideally, we would use a cost of one or two and
+ add the cost of the operand, but disregard the latter when inside loops
+ and loop invariant code motion is still to follow.
+ Using a multiply first and splitting it later if it's a loss
+ doesn't work because of different sign / zero extension semantics
+ of multiplies vs. shifts. */
+ return optimize_size ? 2 : 3;
+
+ if (TARGET_SH2)
+ {
+ /* We have a mul insn, so we can never take more than the mul and the
+ read of the mac reg, but count more because of the latency and extra
+ reg usage. */
+ if (optimize_size)
+ return 2;
+ return 3;
+ }
+
+ /* If we're aiming at small code, then just count the number of
+ insns in a multiply call sequence. */
+ if (optimize_size)
+ return 5;
+
+ /* Otherwise count all the insns in the routine we'd be calling too. */
+ return 20;
+}
+
+/* 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
+sh_rtx_costs (rtx x, int code, int outer_code, int *total,
+ bool speed ATTRIBUTE_UNUSED)
+{
+ switch (code)
+ {
+ case CONST_INT:
+ if (TARGET_SHMEDIA)
+ {
+ if (INTVAL (x) == 0)
+ *total = 0;
+ else if (outer_code == AND && and_operand ((x), DImode))
+ *total = 0;
+ else if ((outer_code == IOR || outer_code == XOR
+ || outer_code == PLUS)
+ && CONST_OK_FOR_I10 (INTVAL (x)))
+ *total = 0;
+ else if (CONST_OK_FOR_I16 (INTVAL (x)))
+ *total = COSTS_N_INSNS (outer_code != SET);
+ else if (CONST_OK_FOR_I16 (INTVAL (x) >> 16))
+ *total = COSTS_N_INSNS ((outer_code != SET) + 1);
+ else if (CONST_OK_FOR_I16 ((INTVAL (x) >> 16) >> 16))
+ *total = COSTS_N_INSNS ((outer_code != SET) + 2);
+ else
+ *total = COSTS_N_INSNS ((outer_code != SET) + 3);
+ return true;
+ }
+ if (CONST_OK_FOR_I08 (INTVAL (x)))
+ *total = 0;
+ else if ((outer_code == AND || outer_code == IOR || outer_code == XOR)
+ && CONST_OK_FOR_K08 (INTVAL (x)))
+ *total = 1;
+ /* prepare_cmp_insn will force costly constants int registers before
+ the cbranch[sd]i4 patterns can see them, so preserve potentially
+ interesting ones not covered by I08 above. */
+ else if (outer_code == COMPARE
+ && ((unsigned HOST_WIDE_INT) INTVAL (x)
+ == (unsigned HOST_WIDE_INT) 0x7fffffff + 1
+ || INTVAL (x) == 0x7fffffff
+ || INTVAL (x) == 0x80 || INTVAL (x) == -0x81))
+ *total = 1;
+ else
+ *total = 8;
+ return true;
+
+ case CONST:
+ case LABEL_REF:
+ case SYMBOL_REF:
+ if (TARGET_SHMEDIA64)
+ *total = COSTS_N_INSNS (4);
+ else if (TARGET_SHMEDIA32)
+ *total = COSTS_N_INSNS (2);
+ else
+ *total = 5;
+ return true;
+
+ case CONST_DOUBLE:
+ if (TARGET_SHMEDIA)
+ *total = COSTS_N_INSNS (4);
+ /* prepare_cmp_insn will force costly constants int registers before
+ the cbranchdi4 pattern can see them, so preserve potentially
+ interesting ones. */
+ else if (outer_code == COMPARE && GET_MODE (x) == DImode)
+ *total = 1;
+ else
+ *total = 10;
+ return true;
+ case CONST_VECTOR:
+ if (x == CONST0_RTX (GET_MODE (x)))
+ *total = 0;
+ else if (sh_1el_vec (x, VOIDmode))
+ *total = outer_code != SET;
+ if (sh_rep_vec (x, VOIDmode))
+ *total = ((GET_MODE_UNIT_SIZE (GET_MODE (x)) + 3) / 4
+ + (outer_code != SET));
+ *total = COSTS_N_INSNS (3) + (outer_code != SET);
+ return true;
+
+ case PLUS:
+ case MINUS:
+ *total = COSTS_N_INSNS (addsubcosts (x));
+ return true;
+
+ case AND:
+ *total = COSTS_N_INSNS (andcosts (x));
+ return true;
+
+ case MULT:
+ *total = COSTS_N_INSNS (multcosts (x));
+ return true;
+
+ case ASHIFT:
+ case ASHIFTRT:
+ case LSHIFTRT:
+ *total = COSTS_N_INSNS (shiftcosts (x));
+ return true;
+
+ case DIV:
+ case UDIV:
+ case MOD:
+ case UMOD:
+ *total = COSTS_N_INSNS (20);
+ return true;
+
+ case PARALLEL:
+ if (sh_1el_vec (x, VOIDmode))
+ *total = outer_code != SET;
+ if (sh_rep_vec (x, VOIDmode))
+ *total = ((GET_MODE_UNIT_SIZE (GET_MODE (x)) + 3) / 4
+ + (outer_code != SET));
+ *total = COSTS_N_INSNS (3) + (outer_code != SET);
+ return true;
+
+ case FLOAT:
+ case FIX:
+ *total = 100;
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/* Compute the cost of an address. For the SH, all valid addresses are
+ the same cost. Use a slightly higher cost for reg + reg addressing,
+ since it increases pressure on r0. */
+
+static int
+sh_address_cost (rtx X,
+ bool speed ATTRIBUTE_UNUSED)
+{
+ return (GET_CODE (X) == PLUS
+ && ! CONSTANT_P (XEXP (X, 1))
+ && ! TARGET_SHMEDIA ? 1 : 0);
+}
+
+/* Code to expand a shift. */
+
+void
+gen_ashift (int type, int n, rtx reg)
+{
+ /* Negative values here come from the shift_amounts array. */
+ if (n < 0)
+ {
+ if (type == ASHIFT)
+ type = LSHIFTRT;
+ else
+ type = ASHIFT;
+ n = -n;
+ }
+
+ switch (type)
+ {
+ case ASHIFTRT:
+ emit_insn (gen_ashrsi3_k (reg, reg, GEN_INT (n)));
+ break;
+ case LSHIFTRT:
+ if (n == 1)
+ emit_insn (gen_lshrsi3_m (reg, reg, GEN_INT (n)));
+ else
+ emit_insn (gen_lshrsi3_k (reg, reg, GEN_INT (n)));
+ break;
+ case ASHIFT:
+ emit_insn (gen_ashlsi3_std (reg, reg, GEN_INT (n)));
+ break;
+ }
+}
+
+/* Same for HImode */
+
+void
+gen_ashift_hi (int type, int n, rtx reg)
+{
+ /* Negative values here come from the shift_amounts array. */
+ if (n < 0)
+ {
+ if (type == ASHIFT)
+ type = LSHIFTRT;
+ else
+ type = ASHIFT;
+ n = -n;
+ }
+
+ switch (type)
+ {
+ case ASHIFTRT:
+ case LSHIFTRT:
+ /* We don't have HImode right shift operations because using the
+ ordinary 32 bit shift instructions for that doesn't generate proper
+ zero/sign extension.
+ gen_ashift_hi is only called in contexts where we know that the
+ sign extension works out correctly. */
+ {
+ int offset = 0;
+ if (GET_CODE (reg) == SUBREG)
+ {
+ offset = SUBREG_BYTE (reg);
+ reg = SUBREG_REG (reg);
+ }
+ gen_ashift (type, n, gen_rtx_SUBREG (SImode, reg, offset));
+ break;
+ }
+ case ASHIFT:
+ emit_insn (gen_ashlhi3_k (reg, reg, GEN_INT (n)));
+ break;
+ }
+}
+
+/* Output RTL to split a constant shift into its component SH constant
+ shift instructions. */
+
+void
+gen_shifty_op (int code, rtx *operands)
+{
+ int value = INTVAL (operands[2]);
+ int max, i;
+
+ /* Truncate the shift count in case it is out of bounds. */
+ value = value & 31;
+
+ if (value == 31)
+ {
+ if (code == LSHIFTRT)
+ {
+ emit_insn (gen_rotlsi3_1 (operands[0], operands[0]));
+ emit_insn (gen_movt (operands[0]));
+ return;
+ }
+ else if (code == ASHIFT)
+ {
+ /* There is a two instruction sequence for 31 bit left shifts,
+ but it requires r0. */
+ if (REG_P (operands[0]) && REGNO (operands[0]) == 0)
+ {
+ emit_insn (gen_andsi3 (operands[0], operands[0], const1_rtx));
+ emit_insn (gen_rotlsi3_31 (operands[0], operands[0]));
+ return;
+ }
+ }
+ }
+ else if (value == 0)
+ {
+ /* This can happen even when optimizing, if there were subregs before
+ reload. Don't output a nop here, as this is never optimized away;
+ use a no-op move instead. */
+ emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[0]));
+ return;
+ }
+
+ max = shift_insns[value];
+ for (i = 0; i < max; i++)
+ gen_ashift (code, shift_amounts[value][i], operands[0]);
+}
+
+/* Same as above, but optimized for values where the topmost bits don't
+ matter. */
+
+void
+gen_shifty_hi_op (int code, rtx *operands)
+{
+ int value = INTVAL (operands[2]);
+ int max, i;
+ void (*gen_fun) (int, int, rtx);
+
+ /* This operation is used by and_shl for SImode values with a few
+ high bits known to be cleared. */
+ value &= 31;
+ if (value == 0)
+ {
+ emit_insn (gen_nop ());
+ return;
+ }
+
+ gen_fun = GET_MODE (operands[0]) == HImode ? gen_ashift_hi : gen_ashift;
+ if (code == ASHIFT)
+ {
+ max = ext_shift_insns[value];
+ for (i = 0; i < max; i++)
+ gen_fun (code, ext_shift_amounts[value][i], operands[0]);
+ }
+ else
+ /* When shifting right, emit the shifts in reverse order, so that
+ solitary negative values come first. */
+ for (i = ext_shift_insns[value] - 1; i >= 0; i--)
+ gen_fun (code, ext_shift_amounts[value][i], operands[0]);
+}
+
+/* Output RTL for an arithmetic right shift. */
+
+/* ??? Rewrite to use super-optimizer sequences. */
+
+int
+expand_ashiftrt (rtx *operands)
+{
+ rtx wrk;
+ char func[18];
+ int value;
+
+ if (TARGET_SH3)
+ {
+ if (!CONST_INT_P (operands[2]))
+ {
+ rtx count = copy_to_mode_reg (SImode, operands[2]);
+ emit_insn (gen_negsi2 (count, count));
+ emit_insn (gen_ashrsi3_d (operands[0], operands[1], count));
+ return 1;
+ }
+ else if (ashiftrt_insns[INTVAL (operands[2]) & 31]
+ > 1 + SH_DYNAMIC_SHIFT_COST)
+ {
+ rtx count
+ = force_reg (SImode, GEN_INT (- (INTVAL (operands[2]) & 31)));
+ emit_insn (gen_ashrsi3_d (operands[0], operands[1], count));
+ return 1;
+ }
+ }
+ if (!CONST_INT_P (operands[2]))
+ return 0;
+
+ value = INTVAL (operands[2]) & 31;
+
+ if (value == 31)
+ {
+ /* If we are called from abs expansion, arrange things so that we
+ we can use a single MT instruction that doesn't clobber the source,
+ if LICM can hoist out the load of the constant zero. */
+ if (currently_expanding_to_rtl)
+ {
+ emit_insn (gen_cmpgtsi_t (force_reg (SImode, CONST0_RTX (SImode)),
+ operands[1]));
+ emit_insn (gen_mov_neg_si_t (operands[0]));
+ return 1;
+ }
+ emit_insn (gen_ashrsi2_31 (operands[0], operands[1]));
+ return 1;
+ }
+ else if (value >= 16 && value <= 19)
+ {
+ wrk = gen_reg_rtx (SImode);
+ emit_insn (gen_ashrsi2_16 (wrk, operands[1]));
+ value -= 16;
+ while (value--)
+ gen_ashift (ASHIFTRT, 1, wrk);
+ emit_move_insn (operands[0], wrk);
+ return 1;
+ }
+ /* Expand a short sequence inline, longer call a magic routine. */
+ else if (value <= 5)
+ {
+ wrk = gen_reg_rtx (SImode);
+ emit_move_insn (wrk, operands[1]);
+ while (value--)
+ gen_ashift (ASHIFTRT, 1, wrk);
+ emit_move_insn (operands[0], wrk);
+ return 1;
+ }
+
+ wrk = gen_reg_rtx (Pmode);
+
+ /* Load the value into an arg reg and call a helper. */
+ emit_move_insn (gen_rtx_REG (SImode, 4), operands[1]);
+ sprintf (func, "__ashiftrt_r4_%d", value);
+ function_symbol (wrk, func, SFUNC_STATIC);
+ emit_insn (gen_ashrsi3_n (GEN_INT (value), wrk));
+ emit_move_insn (operands[0], gen_rtx_REG (SImode, 4));
+ return 1;
+}
+
+int
+sh_dynamicalize_shift_p (rtx count)
+{
+ return shift_insns[INTVAL (count) & 31] > 1 + SH_DYNAMIC_SHIFT_COST;
+}
+
+/* Try to find a good way to implement the combiner pattern
+ [(set (match_operand:SI 0 "register_operand" "r")
+ (and:SI (ashift:SI (match_operand:SI 1 "register_operand" "r")
+ (match_operand:SI 2 "const_int_operand" "n"))
+ (match_operand:SI 3 "const_int_operand" "n"))) .
+ LEFT_RTX is operand 2 in the above pattern, and MASK_RTX is operand 3.
+ return 0 for simple right / left or left/right shift combination.
+ return 1 for a combination of shifts with zero_extend.
+ return 2 for a combination of shifts with an AND that needs r0.
+ return 3 for a combination of shifts with an AND that needs an extra
+ scratch register, when the three highmost bits of the AND mask are clear.
+ return 4 for a combination of shifts with an AND that needs an extra
+ scratch register, when any of the three highmost bits of the AND mask
+ is set.
+ If ATTRP is set, store an initial right shift width in ATTRP[0],
+ and the instruction length in ATTRP[1] . These values are not valid
+ when returning 0.
+ When ATTRP is set and returning 1, ATTRP[2] gets set to the index into
+ shift_amounts for the last shift value that is to be used before the
+ sign extend. */
+int
+shl_and_kind (rtx left_rtx, rtx mask_rtx, int *attrp)
+{
+ unsigned HOST_WIDE_INT mask, lsb, mask2, lsb2;
+ int left = INTVAL (left_rtx), right;
+ int best = 0;
+ int cost, best_cost = 10000;
+ int best_right = 0, best_len = 0;
+ int i;
+ int can_ext;
+
+ if (left < 0 || left > 31)
+ return 0;
+ if (CONST_INT_P (mask_rtx))
+ mask = (unsigned HOST_WIDE_INT) INTVAL (mask_rtx) >> left;
+ else
+ mask = (unsigned HOST_WIDE_INT) GET_MODE_MASK (SImode) >> left;
+ /* Can this be expressed as a right shift / left shift pair? */
+ lsb = ((mask ^ (mask - 1)) >> 1) + 1;
+ right = exact_log2 (lsb);
+ mask2 = ~(mask + lsb - 1);
+ lsb2 = ((mask2 ^ (mask2 - 1)) >> 1) + 1;
+ /* mask has no zeroes but trailing zeroes <==> ! mask2 */
+ if (! mask2)
+ best_cost = shift_insns[right] + shift_insns[right + left];
+ /* mask has no trailing zeroes <==> ! right */
+ else if (! right && mask2 == ~(lsb2 - 1))
+ {
+ int late_right = exact_log2 (lsb2);
+ best_cost = shift_insns[left + late_right] + shift_insns[late_right];
+ }
+ /* Try to use zero extend. */
+ if (mask2 == ~(lsb2 - 1))
+ {
+ int width, first;
+
+ for (width = 8; width <= 16; width += 8)
+ {
+ /* Can we zero-extend right away? */
+ if (lsb2 == (unsigned HOST_WIDE_INT) 1 << width)
+ {
+ cost
+ = 1 + ext_shift_insns[right] + ext_shift_insns[left + right];
+ if (cost < best_cost)
+ {
+ best = 1;
+ best_cost = cost;
+ best_right = right;
+ best_len = cost;
+ if (attrp)
+ attrp[2] = -1;
+ }
+ continue;
+ }
+ /* ??? Could try to put zero extend into initial right shift,
+ or even shift a bit left before the right shift. */
+ /* Determine value of first part of left shift, to get to the
+ zero extend cut-off point. */
+ first = width - exact_log2 (lsb2) + right;
+ if (first >= 0 && right + left - first >= 0)
+ {
+ cost = ext_shift_insns[right] + ext_shift_insns[first] + 1
+ + ext_shift_insns[right + left - first];
+ if (cost < best_cost)
+ {
+ best = 1;
+ best_cost = cost;
+ best_right = right;
+ best_len = cost;
+ if (attrp)
+ attrp[2] = first;
+ }
+ }
+ }
+ }
+ /* Try to use r0 AND pattern */
+ for (i = 0; i <= 2; i++)
+ {
+ if (i > right)
+ break;
+ if (! CONST_OK_FOR_K08 (mask >> i))
+ continue;
+ cost = (i != 0) + 2 + ext_shift_insns[left + i];
+ if (cost < best_cost)
+ {
+ best = 2;
+ best_cost = cost;
+ best_right = i;
+ best_len = cost - 1;
+ }
+ }
+ /* Try to use a scratch register to hold the AND operand. */
+ can_ext = ((mask << left) & ((unsigned HOST_WIDE_INT) 3 << 30)) == 0;
+ for (i = 0; i <= 2; i++)
+ {
+ if (i > right)
+ break;
+ cost = (i != 0) + (CONST_OK_FOR_I08 (mask >> i) ? 2 : 3)
+ + (can_ext ? ext_shift_insns : shift_insns)[left + i];
+ if (cost < best_cost)
+ {
+ best = 4 - can_ext;
+ best_cost = cost;
+ best_right = i;
+ best_len = cost - 1 - ! CONST_OK_FOR_I08 (mask >> i);
+ }
+ }
+
+ if (attrp)
+ {
+ attrp[0] = best_right;
+ attrp[1] = best_len;
+ }
+ return best;
+}
+
+/* This is used in length attributes of the unnamed instructions
+ corresponding to shl_and_kind return values of 1 and 2. */
+int
+shl_and_length (rtx insn)
+{
+ rtx set_src, left_rtx, mask_rtx;
+ int attributes[3];
+
+ set_src = SET_SRC (XVECEXP (PATTERN (insn), 0, 0));
+ left_rtx = XEXP (XEXP (set_src, 0), 1);
+ mask_rtx = XEXP (set_src, 1);
+ shl_and_kind (left_rtx, mask_rtx, attributes);
+ return attributes[1];
+}
+
+/* This is used in length attribute of the and_shl_scratch instruction. */
+
+int
+shl_and_scr_length (rtx insn)
+{
+ rtx set_src = SET_SRC (XVECEXP (PATTERN (insn), 0, 0));
+ int len = shift_insns[INTVAL (XEXP (set_src, 1)) & 31];
+ rtx op = XEXP (set_src, 0);
+ len += shift_insns[INTVAL (XEXP (op, 1)) & 31] + 1;
+ op = XEXP (XEXP (op, 0), 0);
+ return len + shift_insns[INTVAL (XEXP (op, 1)) & 31];
+}
+
+/* Generate rtl for instructions for which shl_and_kind advised a particular
+ method of generating them, i.e. returned zero. */
+
+int
+gen_shl_and (rtx dest, rtx left_rtx, rtx mask_rtx, rtx source)
+{
+ int attributes[3];
+ unsigned HOST_WIDE_INT mask;
+ int kind = shl_and_kind (left_rtx, mask_rtx, attributes);
+ int right, total_shift;
+ void (*shift_gen_fun) (int, rtx *) = gen_shifty_hi_op;
+
+ right = attributes[0];
+ total_shift = INTVAL (left_rtx) + right;
+ mask = (unsigned HOST_WIDE_INT) INTVAL (mask_rtx) >> total_shift;
+ switch (kind)
+ {
+ default:
+ return -1;
+ case 1:
+ {
+ int first = attributes[2];
+ rtx operands[3];
+
+ if (first < 0)
+ {
+ emit_insn ((mask << right) <= 0xff
+ ? gen_zero_extendqisi2 (dest,
+ gen_lowpart (QImode, source))
+ : gen_zero_extendhisi2 (dest,
+ gen_lowpart (HImode, source)));
+ source = dest;
+ }
+ if (source != dest)
+ emit_insn (gen_movsi (dest, source));
+ operands[0] = dest;
+ if (right)
+ {
+ operands[2] = GEN_INT (right);
+ gen_shifty_hi_op (LSHIFTRT, operands);
+ }
+ if (first > 0)
+ {
+ operands[2] = GEN_INT (first);
+ gen_shifty_hi_op (ASHIFT, operands);
+ total_shift -= first;
+ mask <<= first;
+ }
+ if (first >= 0)
+ emit_insn (mask <= 0xff
+ ? gen_zero_extendqisi2 (dest, gen_lowpart (QImode, dest))
+ : gen_zero_extendhisi2 (dest, gen_lowpart (HImode, dest)));
+ if (total_shift > 0)
+ {
+ operands[2] = GEN_INT (total_shift);
+ gen_shifty_hi_op (ASHIFT, operands);
+ }
+ break;
+ }
+ case 4:
+ shift_gen_fun = gen_shifty_op;
+ case 3:
+ /* If the topmost bit that matters is set, set the topmost bits
+ that don't matter. This way, we might be able to get a shorter
+ signed constant. */
+ if (mask & ((HOST_WIDE_INT) 1 << (31 - total_shift)))
+ mask |= (HOST_WIDE_INT) ~0 << (31 - total_shift);
+ case 2:
+ /* Don't expand fine-grained when combining, because that will
+ make the pattern fail. */
+ if (currently_expanding_to_rtl
+ || reload_in_progress || reload_completed)
+ {
+ rtx operands[3];
+
+ /* Cases 3 and 4 should be handled by this split
+ only while combining */
+ gcc_assert (kind <= 2);
+ if (right)
+ {
+ emit_insn (gen_lshrsi3 (dest, source, GEN_INT (right)));
+ source = dest;
+ }
+ emit_insn (gen_andsi3 (dest, source, GEN_INT (mask)));
+ if (total_shift)
+ {
+ operands[0] = dest;
+ operands[1] = dest;
+ operands[2] = GEN_INT (total_shift);
+ shift_gen_fun (ASHIFT, operands);
+ }
+ break;
+ }
+ else
+ {
+ int neg = 0;
+ if (kind != 4 && total_shift < 16)
+ {
+ neg = -ext_shift_amounts[total_shift][1];
+ if (neg > 0)
+ neg -= ext_shift_amounts[total_shift][2];
+ else
+ neg = 0;
+ }
+ emit_insn (gen_and_shl_scratch (dest, source,
+ GEN_INT (right),
+ GEN_INT (mask),
+ GEN_INT (total_shift + neg),
+ GEN_INT (neg)));
+ emit_insn (gen_movsi (dest, dest));
+ break;
+ }
+ }
+ return 0;
+}
+
+/* Try to find a good way to implement the combiner pattern
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (sign_extract:SI (ashift:SI (match_operand:SI 1 "register_operand" "r")
+ (match_operand:SI 2 "const_int_operand" "n")
+ (match_operand:SI 3 "const_int_operand" "n")
+ (const_int 0)))
+ (clobber (reg:SI T_REG))]
+ LEFT_RTX is operand 2 in the above pattern, and SIZE_RTX is operand 3.
+ return 0 for simple left / right shift combination.
+ return 1 for left shift / 8 bit sign extend / left shift.
+ return 2 for left shift / 16 bit sign extend / left shift.
+ return 3 for left shift / 8 bit sign extend / shift / sign extend.
+ return 4 for left shift / 16 bit sign extend / shift / sign extend.
+ return 5 for left shift / 16 bit sign extend / right shift
+ return 6 for < 8 bit sign extend / left shift.
+ return 7 for < 8 bit sign extend / left shift / single right shift.
+ If COSTP is nonzero, assign the calculated cost to *COSTP. */
+
+int
+shl_sext_kind (rtx left_rtx, rtx size_rtx, int *costp)
+{
+ int left, size, insize, ext;
+ int cost = 0, best_cost;
+ int kind;
+
+ left = INTVAL (left_rtx);
+ size = INTVAL (size_rtx);
+ insize = size - left;
+ gcc_assert (insize > 0);
+ /* Default to left / right shift. */
+ kind = 0;
+ best_cost = shift_insns[32 - insize] + ashiftrt_insns[32 - size];
+ if (size <= 16)
+ {
+ /* 16 bit shift / sign extend / 16 bit shift */
+ cost = shift_insns[16 - insize] + 1 + ashiftrt_insns[16 - size];
+ /* If ashiftrt_insns[16 - size] is 8, this choice will be overridden
+ below, by alternative 3 or something even better. */
+ if (cost < best_cost)
+ {
+ kind = 5;
+ best_cost = cost;
+ }
+ }
+ /* Try a plain sign extend between two shifts. */
+ for (ext = 16; ext >= insize; ext -= 8)
+ {
+ if (ext <= size)
+ {
+ cost = ext_shift_insns[ext - insize] + 1 + shift_insns[size - ext];
+ if (cost < best_cost)
+ {
+ kind = ext / (unsigned) 8;
+ best_cost = cost;
+ }
+ }
+ /* Check if we can do a sloppy shift with a final signed shift
+ restoring the sign. */
+ if (EXT_SHIFT_SIGNED (size - ext))
+ cost = ext_shift_insns[ext - insize] + ext_shift_insns[size - ext] + 1;
+ /* If not, maybe it's still cheaper to do the second shift sloppy,
+ and do a final sign extend? */
+ else if (size <= 16)
+ cost = ext_shift_insns[ext - insize] + 1
+ + ext_shift_insns[size > ext ? size - ext : ext - size] + 1;
+ else
+ continue;
+ if (cost < best_cost)
+ {
+ kind = ext / (unsigned) 8 + 2;
+ best_cost = cost;
+ }
+ }
+ /* Check if we can sign extend in r0 */
+ if (insize < 8)
+ {
+ cost = 3 + shift_insns[left];
+ if (cost < best_cost)
+ {
+ kind = 6;
+ best_cost = cost;
+ }
+ /* Try the same with a final signed shift. */
+ if (left < 31)
+ {
+ cost = 3 + ext_shift_insns[left + 1] + 1;
+ if (cost < best_cost)
+ {
+ kind = 7;
+ best_cost = cost;
+ }
+ }
+ }
+ if (TARGET_SH3)
+ {
+ /* Try to use a dynamic shift. */
+ cost = shift_insns[32 - insize] + 1 + SH_DYNAMIC_SHIFT_COST;
+ if (cost < best_cost)
+ {
+ kind = 0;
+ best_cost = cost;
+ }
+ }
+ if (costp)
+ *costp = cost;
+ return kind;
+}
+
+/* Function to be used in the length attribute of the instructions
+ implementing this pattern. */
+
+int
+shl_sext_length (rtx insn)
+{
+ rtx set_src, left_rtx, size_rtx;
+ int cost;
+
+ set_src = SET_SRC (XVECEXP (PATTERN (insn), 0, 0));
+ left_rtx = XEXP (XEXP (set_src, 0), 1);
+ size_rtx = XEXP (set_src, 1);
+ shl_sext_kind (left_rtx, size_rtx, &cost);
+ return cost;
+}
+
+/* Generate rtl for this pattern */
+
+int
+gen_shl_sext (rtx dest, rtx left_rtx, rtx size_rtx, rtx source)
+{
+ int kind;
+ int left, size, insize, cost;
+ rtx operands[3];
+
+ kind = shl_sext_kind (left_rtx, size_rtx, &cost);
+ left = INTVAL (left_rtx);
+ size = INTVAL (size_rtx);
+ insize = size - left;
+ switch (kind)
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ {
+ int ext = kind & 1 ? 8 : 16;
+ int shift2 = size - ext;
+
+ /* Don't expand fine-grained when combining, because that will
+ make the pattern fail. */
+ if (! currently_expanding_to_rtl
+ && ! reload_in_progress && ! reload_completed)
+ {
+ emit_insn (gen_shl_sext_ext (dest, source, left_rtx, size_rtx));
+ emit_insn (gen_movsi (dest, source));
+ break;
+ }
+ if (dest != source)
+ emit_insn (gen_movsi (dest, source));
+ operands[0] = dest;
+ if (ext - insize)
+ {
+ operands[2] = GEN_INT (ext - insize);
+ gen_shifty_hi_op (ASHIFT, operands);
+ }
+ emit_insn (kind & 1
+ ? gen_extendqisi2 (dest, gen_lowpart (QImode, dest))
+ : gen_extendhisi2 (dest, gen_lowpart (HImode, dest)));
+ if (kind <= 2)
+ {
+ if (shift2)
+ {
+ operands[2] = GEN_INT (shift2);
+ gen_shifty_op (ASHIFT, operands);
+ }
+ }
+ else
+ {
+ if (shift2 > 0)
+ {
+ if (EXT_SHIFT_SIGNED (shift2))
+ {
+ operands[2] = GEN_INT (shift2 + 1);
+ gen_shifty_op (ASHIFT, operands);
+ operands[2] = const1_rtx;
+ gen_shifty_op (ASHIFTRT, operands);
+ break;
+ }
+ operands[2] = GEN_INT (shift2);
+ gen_shifty_hi_op (ASHIFT, operands);
+ }
+ else if (shift2)
+ {
+ operands[2] = GEN_INT (-shift2);
+ gen_shifty_hi_op (LSHIFTRT, operands);
+ }
+ emit_insn (size <= 8
+ ? gen_extendqisi2 (dest, gen_lowpart (QImode, dest))
+ : gen_extendhisi2 (dest, gen_lowpart (HImode, dest)));
+ }
+ break;
+ }
+ case 5:
+ {
+ int i = 16 - size;
+ if (! currently_expanding_to_rtl
+ && ! reload_in_progress && ! reload_completed)
+ emit_insn (gen_shl_sext_ext (dest, source, left_rtx, size_rtx));
+ else
+ {
+ operands[0] = dest;
+ operands[2] = GEN_INT (16 - insize);
+ gen_shifty_hi_op (ASHIFT, operands);
+ emit_insn (gen_extendhisi2 (dest, gen_lowpart (HImode, dest)));
+ }
+ /* Don't use gen_ashrsi3 because it generates new pseudos. */
+ while (--i >= 0)
+ gen_ashift (ASHIFTRT, 1, dest);
+ break;
+ }
+ case 6:
+ case 7:
+ /* Don't expand fine-grained when combining, because that will
+ make the pattern fail. */
+ if (! currently_expanding_to_rtl
+ && ! reload_in_progress && ! reload_completed)
+ {
+ emit_insn (gen_shl_sext_ext (dest, source, left_rtx, size_rtx));
+ emit_insn (gen_movsi (dest, source));
+ break;
+ }
+ emit_insn (gen_andsi3 (dest, source, GEN_INT ((1 << insize) - 1)));
+ emit_insn (gen_xorsi3 (dest, dest, GEN_INT (1 << (insize - 1))));
+ emit_insn (gen_addsi3 (dest, dest, GEN_INT (-1 << (insize - 1))));
+ operands[0] = dest;
+ operands[2] = kind == 7 ? GEN_INT (left + 1) : left_rtx;
+ gen_shifty_op (ASHIFT, operands);
+ if (kind == 7)
+ emit_insn (gen_ashrsi3_k (dest, dest, const1_rtx));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/* Prefix a symbol_ref name with "datalabel". */
+
+rtx
+gen_datalabel_ref (rtx sym)
+{
+ const char *str;
+
+ if (GET_CODE (sym) == LABEL_REF)
+ return gen_rtx_CONST (GET_MODE (sym),
+ gen_rtx_UNSPEC (GET_MODE (sym),
+ gen_rtvec (1, sym),
+ UNSPEC_DATALABEL));
+
+ gcc_assert (GET_CODE (sym) == SYMBOL_REF);
+
+ str = XSTR (sym, 0);
+ /* Share all SYMBOL_REF strings with the same value - that is important
+ for cse. */
+ str = IDENTIFIER_POINTER (get_identifier (str));
+ XSTR (sym, 0) = str;
+
+ return sym;
+}
+
+
+static alloc_pool label_ref_list_pool;
+
+typedef struct label_ref_list_d
+{
+ rtx label;
+ struct label_ref_list_d *next;
+} *label_ref_list_t;
+
+/* The SH cannot load a large constant into a register, constants have to
+ come from a pc relative load. The reference of a pc relative load
+ instruction must be less than 1k in front of the instruction. This
+ means that we often have to dump a constant inside a function, and
+ generate code to branch around it.
+
+ It is important to minimize this, since the branches will slow things
+ down and make things bigger.
+
+ Worst case code looks like:
+
+ mov.l L1,rn
+ bra L2
+ nop
+ align
+ L1: .long value
+ L2:
+ ..
+
+ mov.l L3,rn
+ bra L4
+ nop
+ align
+ L3: .long value
+ L4:
+ ..
+
+ We fix this by performing a scan before scheduling, which notices which
+ instructions need to have their operands fetched from the constant table
+ and builds the table.
+
+ The algorithm is:
+
+ scan, find an instruction which needs a pcrel move. Look forward, find the
+ last barrier which is within MAX_COUNT bytes of the requirement.
+ If there isn't one, make one. Process all the instructions between
+ the find and the barrier.
+
+ In the above example, we can tell that L3 is within 1k of L1, so
+ the first move can be shrunk from the 3 insn+constant sequence into
+ just 1 insn, and the constant moved to L3 to make:
+
+ mov.l L1,rn
+ ..
+ mov.l L3,rn
+ bra L4
+ nop
+ align
+ L3:.long value
+ L4:.long value
+
+ Then the second move becomes the target for the shortening process. */
+
+typedef struct
+{
+ rtx value; /* Value in table. */
+ rtx label; /* Label of value. */
+ label_ref_list_t wend; /* End of window. */
+ enum machine_mode mode; /* Mode of value. */
+
+ /* True if this constant is accessed as part of a post-increment
+ sequence. Note that HImode constants are never accessed in this way. */
+ bool part_of_sequence_p;
+} pool_node;
+
+/* The maximum number of constants that can fit into one pool, since
+ constants in the range 0..510 are at least 2 bytes long, and in the
+ range from there to 1018 at least 4 bytes. */
+
+#define MAX_POOL_SIZE 372
+static pool_node pool_vector[MAX_POOL_SIZE];
+static int pool_size;
+static rtx pool_window_label;
+static int pool_window_last;
+
+static int max_labelno_before_reorg;
+
+/* ??? If we need a constant in HImode which is the truncated value of a
+ constant we need in SImode, we could combine the two entries thus saving
+ two bytes. Is this common enough to be worth the effort of implementing
+ it? */
+
+/* ??? This stuff should be done at the same time that we shorten branches.
+ As it is now, we must assume that all branches are the maximum size, and
+ this causes us to almost always output constant pools sooner than
+ necessary. */
+
+/* Add a constant to the pool and return its label. */
+
+static rtx
+add_constant (rtx x, enum machine_mode mode, rtx last_value)
+{
+ int i;
+ rtx lab, new_rtx;
+ label_ref_list_t ref, newref;
+
+ /* First see if we've already got it. */
+ for (i = 0; i < pool_size; i++)
+ {
+ if (x->code == pool_vector[i].value->code
+ && mode == pool_vector[i].mode)
+ {
+ if (x->code == CODE_LABEL)
+ {
+ if (XINT (x, 3) != XINT (pool_vector[i].value, 3))
+ continue;
+ }
+ if (rtx_equal_p (x, pool_vector[i].value))
+ {
+ lab = new_rtx = 0;
+ if (! last_value
+ || ! i
+ || ! rtx_equal_p (last_value, pool_vector[i-1].value))
+ {
+ new_rtx = gen_label_rtx ();
+ LABEL_REFS (new_rtx) = pool_vector[i].label;
+ pool_vector[i].label = lab = new_rtx;
+ }
+ if (lab && pool_window_label)
+ {
+ newref = (label_ref_list_t) pool_alloc (label_ref_list_pool);
+ newref->label = pool_window_label;
+ ref = pool_vector[pool_window_last].wend;
+ newref->next = ref;
+ pool_vector[pool_window_last].wend = newref;
+ }
+ if (new_rtx)
+ pool_window_label = new_rtx;
+ pool_window_last = i;
+ return lab;
+ }
+ }
+ }
+
+ /* Need a new one. */
+ pool_vector[pool_size].value = x;
+ if (last_value && rtx_equal_p (last_value, pool_vector[pool_size - 1].value))
+ {
+ lab = 0;
+ pool_vector[pool_size - 1].part_of_sequence_p = true;
+ }
+ else
+ lab = gen_label_rtx ();
+ pool_vector[pool_size].mode = mode;
+ pool_vector[pool_size].label = lab;
+ pool_vector[pool_size].wend = NULL;
+ pool_vector[pool_size].part_of_sequence_p = (lab == 0);
+ if (lab && pool_window_label)
+ {
+ newref = (label_ref_list_t) pool_alloc (label_ref_list_pool);
+ newref->label = pool_window_label;
+ ref = pool_vector[pool_window_last].wend;
+ newref->next = ref;
+ pool_vector[pool_window_last].wend = newref;
+ }
+ if (lab)
+ pool_window_label = lab;
+ pool_window_last = pool_size;
+ pool_size++;
+ return lab;
+}
+
+/* Output the literal table. START, if nonzero, is the first instruction
+ this table is needed for, and also indicates that there is at least one
+ casesi_worker_2 instruction; We have to emit the operand3 labels from
+ these insns at a 4-byte aligned position. BARRIER is the barrier
+ after which we are to place the table. */
+
+static void
+dump_table (rtx start, rtx barrier)
+{
+ rtx scan = barrier;
+ int i;
+ int need_align = 1;
+ rtx lab;
+ label_ref_list_t ref;
+ int have_df = 0;
+
+ /* Do two passes, first time dump out the HI sized constants. */
+
+ for (i = 0; i < pool_size; i++)
+ {
+ pool_node *p = &pool_vector[i];
+
+ if (p->mode == HImode)
+ {
+ if (need_align)
+ {
+ scan = emit_insn_after (gen_align_2 (), scan);
+ need_align = 0;
+ }
+ for (lab = p->label; lab; lab = LABEL_REFS (lab))
+ scan = emit_label_after (lab, scan);
+ scan = emit_insn_after (gen_consttable_2 (p->value, const0_rtx),
+ scan);
+ for (ref = p->wend; ref; ref = ref->next)
+ {
+ lab = ref->label;
+ scan = emit_insn_after (gen_consttable_window_end (lab), scan);
+ }
+ }
+ else if (p->mode == DFmode)
+ have_df = 1;
+ }
+
+ need_align = 1;
+
+ if (start)
+ {
+ scan = emit_insn_after (gen_align_4 (), scan);
+ need_align = 0;
+ for (; start != barrier; start = NEXT_INSN (start))
+ if (NONJUMP_INSN_P (start)
+ && recog_memoized (start) == CODE_FOR_casesi_worker_2)
+ {
+ rtx src = SET_SRC (XVECEXP (PATTERN (start), 0, 0));
+ rtx lab = XEXP (XVECEXP (src, 0, 3), 0);
+
+ scan = emit_label_after (lab, scan);
+ }
+ }
+ if (TARGET_FMOVD && TARGET_ALIGN_DOUBLE && have_df)
+ {
+ rtx align_insn = NULL_RTX;
+
+ scan = emit_label_after (gen_label_rtx (), scan);
+ scan = emit_insn_after (gen_align_log (GEN_INT (3)), scan);
+ need_align = 0;
+
+ for (i = 0; i < pool_size; i++)
+ {
+ pool_node *p = &pool_vector[i];
+
+ switch (p->mode)
+ {
+ case HImode:
+ break;
+ case SImode:
+ case SFmode:
+ if (align_insn && !p->part_of_sequence_p)
+ {
+ for (lab = p->label; lab; lab = LABEL_REFS (lab))
+ emit_label_before (lab, align_insn);
+ emit_insn_before (gen_consttable_4 (p->value, const0_rtx),
+ align_insn);
+ for (ref = p->wend; ref; ref = ref->next)
+ {
+ lab = ref->label;
+ emit_insn_before (gen_consttable_window_end (lab),
+ align_insn);
+ }
+ delete_insn (align_insn);
+ align_insn = NULL_RTX;
+ continue;
+ }
+ else
+ {
+ for (lab = p->label; lab; lab = LABEL_REFS (lab))
+ scan = emit_label_after (lab, scan);
+ scan = emit_insn_after (gen_consttable_4 (p->value,
+ const0_rtx), scan);
+ need_align = ! need_align;
+ }
+ break;
+ case DFmode:
+ if (need_align)
+ {
+ scan = emit_insn_after (gen_align_log (GEN_INT (3)), scan);
+ align_insn = scan;
+ need_align = 0;
+ }
+ case DImode:
+ for (lab = p->label; lab; lab = LABEL_REFS (lab))
+ scan = emit_label_after (lab, scan);
+ scan = emit_insn_after (gen_consttable_8 (p->value, const0_rtx),
+ scan);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+
+ if (p->mode != HImode)
+ {
+ for (ref = p->wend; ref; ref = ref->next)
+ {
+ lab = ref->label;
+ scan = emit_insn_after (gen_consttable_window_end (lab),
+ scan);
+ }
+ }
+ }
+
+ pool_size = 0;
+ }
+
+ for (i = 0; i < pool_size; i++)
+ {
+ pool_node *p = &pool_vector[i];
+
+ switch (p->mode)
+ {
+ case HImode:
+ break;
+ case SImode:
+ case SFmode:
+ if (need_align)
+ {
+ need_align = 0;
+ scan = emit_label_after (gen_label_rtx (), scan);
+ scan = emit_insn_after (gen_align_4 (), scan);
+ }
+ for (lab = p->label; lab; lab = LABEL_REFS (lab))
+ scan = emit_label_after (lab, scan);
+ scan = emit_insn_after (gen_consttable_4 (p->value, const0_rtx),
+ scan);
+ break;
+ case DFmode:
+ case DImode:
+ if (need_align)
+ {
+ need_align = 0;
+ scan = emit_label_after (gen_label_rtx (), scan);
+ scan = emit_insn_after (gen_align_4 (), scan);
+ }
+ for (lab = p->label; lab; lab = LABEL_REFS (lab))
+ scan = emit_label_after (lab, scan);
+ scan = emit_insn_after (gen_consttable_8 (p->value, const0_rtx),
+ scan);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+
+ if (p->mode != HImode)
+ {
+ for (ref = p->wend; ref; ref = ref->next)
+ {
+ lab = ref->label;
+ scan = emit_insn_after (gen_consttable_window_end (lab), scan);
+ }
+ }
+ }
+
+ scan = emit_insn_after (gen_consttable_end (), scan);
+ scan = emit_barrier_after (scan);
+ pool_size = 0;
+ pool_window_label = NULL_RTX;
+ pool_window_last = 0;
+}
+
+/* Return nonzero if constant would be an ok source for a
+ mov.w instead of a mov.l. */
+
+static int
+hi_const (rtx src)
+{
+ return (CONST_INT_P (src)
+ && INTVAL (src) >= -32768
+ && INTVAL (src) <= 32767);
+}
+
+#define MOVA_LABELREF(mova) XVECEXP (SET_SRC (PATTERN (mova)), 0, 0)
+
+/* Nonzero if the insn is a move instruction which needs to be fixed. */
+
+/* ??? For a DImode/DFmode moves, we don't need to fix it if each half of the
+ CONST_DOUBLE input value is CONST_OK_FOR_I08. For a SFmode move, we don't
+ need to fix it if the input value is CONST_OK_FOR_I08. */
+
+static int
+broken_move (rtx insn)
+{
+ if (NONJUMP_INSN_P (insn))
+ {
+ rtx pat = PATTERN (insn);
+ if (GET_CODE (pat) == PARALLEL)
+ pat = XVECEXP (pat, 0, 0);
+ if (GET_CODE (pat) == SET
+ /* We can load any 8-bit value if we don't care what the high
+ order bits end up as. */
+ && GET_MODE (SET_DEST (pat)) != QImode
+ && (CONSTANT_P (SET_SRC (pat))
+ /* Match mova_const. */
+ || (GET_CODE (SET_SRC (pat)) == UNSPEC
+ && XINT (SET_SRC (pat), 1) == UNSPEC_MOVA
+ && GET_CODE (XVECEXP (SET_SRC (pat), 0, 0)) == CONST))
+ && ! (TARGET_SH2E
+ && GET_CODE (SET_SRC (pat)) == CONST_DOUBLE
+ && (fp_zero_operand (SET_SRC (pat))
+ || fp_one_operand (SET_SRC (pat)))
+ /* In general we don't know the current setting of fpscr, so disable fldi.
+ There is an exception if this was a register-register move
+ before reload - and hence it was ascertained that we have
+ single precision setting - and in a post-reload optimization
+ we changed this to do a constant load. In that case
+ we don't have an r0 clobber, hence we must use fldi. */
+ && (TARGET_FMOVD
+ || (GET_CODE (XEXP (XVECEXP (PATTERN (insn), 0, 2), 0))
+ == SCRATCH))
+ && REG_P (SET_DEST (pat))
+ && FP_REGISTER_P (REGNO (SET_DEST (pat))))
+ && ! (TARGET_SH2A
+ && GET_MODE (SET_DEST (pat)) == SImode
+ && (satisfies_constraint_I20 (SET_SRC (pat))
+ || satisfies_constraint_I28 (SET_SRC (pat))))
+ && ! satisfies_constraint_I08 (SET_SRC (pat)))
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+mova_p (rtx insn)
+{
+ return (NONJUMP_INSN_P (insn)
+ && GET_CODE (PATTERN (insn)) == SET
+ && GET_CODE (SET_SRC (PATTERN (insn))) == UNSPEC
+ && XINT (SET_SRC (PATTERN (insn)), 1) == UNSPEC_MOVA
+ /* Don't match mova_const. */
+ && GET_CODE (MOVA_LABELREF (insn)) == LABEL_REF);
+}
+
+/* Fix up a mova from a switch that went out of range. */
+static void
+fixup_mova (rtx mova)
+{
+ PUT_MODE (XEXP (MOVA_LABELREF (mova), 0), QImode);
+ if (! flag_pic)
+ {
+ SET_SRC (PATTERN (mova)) = MOVA_LABELREF (mova);
+ INSN_CODE (mova) = -1;
+ }
+ else
+ {
+ rtx worker = mova;
+ rtx lab = gen_label_rtx ();
+ rtx wpat, wpat0, wpat1, wsrc, target, base, diff;
+
+ do
+ {
+ worker = NEXT_INSN (worker);
+ gcc_assert (worker
+ && !LABEL_P (worker)
+ && !JUMP_P (worker));
+ } while (NOTE_P (worker)
+ || recog_memoized (worker) != CODE_FOR_casesi_worker_1);
+ wpat = PATTERN (worker);
+ wpat0 = XVECEXP (wpat, 0, 0);
+ wpat1 = XVECEXP (wpat, 0, 1);
+ wsrc = SET_SRC (wpat0);
+ PATTERN (worker) = (gen_casesi_worker_2
+ (SET_DEST (wpat0), XVECEXP (wsrc, 0, 1),
+ XEXP (XVECEXP (wsrc, 0, 2), 0), lab,
+ XEXP (wpat1, 0)));
+ INSN_CODE (worker) = -1;
+ target = XVECEXP (SET_SRC (PATTERN (mova)), 0, 0);
+ base = gen_rtx_LABEL_REF (Pmode, lab);
+ diff = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, target, base), UNSPEC_SYMOFF);
+ SET_SRC (PATTERN (mova)) = gen_rtx_CONST (Pmode, diff);
+ INSN_CODE (mova) = -1;
+ }
+}
+
+/* NEW_MOVA is a mova we've just encountered while scanning forward. Update
+ *num_mova, and check if the new mova is not nested within the first one.
+ return 0 if *first_mova was replaced, 1 if new_mova was replaced,
+ 2 if new_mova has been assigned to *first_mova, -1 otherwise.. */
+static int
+untangle_mova (int *num_mova, rtx *first_mova, rtx new_mova)
+{
+ int n_addr = 0; /* Initialization to shut up spurious warning. */
+ int f_target, n_target = 0; /* Likewise. */
+
+ if (optimize)
+ {
+ /* If NEW_MOVA has no address yet, it will be handled later. */
+ if (INSN_ADDRESSES_SIZE() <= (unsigned) INSN_UID (new_mova))
+ return -1;
+
+ n_addr = INSN_ADDRESSES (INSN_UID (new_mova));
+ n_target = INSN_ADDRESSES (INSN_UID (XEXP (MOVA_LABELREF (new_mova), 0)));
+ if (n_addr > n_target || n_addr + 1022 < n_target)
+ {
+ /* Change the mova into a load.
+ broken_move will then return true for it. */
+ fixup_mova (new_mova);
+ return 1;
+ }
+ }
+ if (!(*num_mova)++)
+ {
+ *first_mova = new_mova;
+ return 2;
+ }
+ if (!optimize
+ || ((f_target
+ = INSN_ADDRESSES (INSN_UID (XEXP (MOVA_LABELREF (*first_mova), 0))))
+ >= n_target))
+ return -1;
+
+ (*num_mova)--;
+ if (f_target - INSN_ADDRESSES (INSN_UID (*first_mova))
+ > n_target - n_addr)
+ {
+ fixup_mova (*first_mova);
+ return 0;
+ }
+ else
+ {
+ fixup_mova (new_mova);
+ return 1;
+ }
+}
+
+/* Find the last barrier from insn FROM which is close enough to hold the
+ constant pool. If we can't find one, then create one near the end of
+ the range. */
+
+static rtx
+find_barrier (int num_mova, rtx mova, rtx from)
+{
+ int count_si = 0;
+ int count_hi = 0;
+ int found_hi = 0;
+ int found_si = 0;
+ int found_di = 0;
+ int hi_align = 2;
+ int si_align = 2;
+ int leading_mova = num_mova;
+ rtx barrier_before_mova = 0, found_barrier = 0, good_barrier = 0;
+ int si_limit;
+ int hi_limit;
+ rtx orig = from;
+ rtx last_got = NULL_RTX;
+ rtx last_symoff = NULL_RTX;
+
+ /* For HImode: range is 510, add 4 because pc counts from address of
+ second instruction after this one, subtract 2 for the jump instruction
+ that we may need to emit before the table, subtract 2 for the instruction
+ that fills the jump delay slot (in very rare cases, reorg will take an
+ instruction from after the constant pool or will leave the delay slot
+ empty). This gives 510.
+ For SImode: range is 1020, add 4 because pc counts from address of
+ second instruction after this one, subtract 2 in case pc is 2 byte
+ aligned, subtract 2 for the jump instruction that we may need to emit
+ before the table, subtract 2 for the instruction that fills the jump
+ delay slot. This gives 1018. */
+
+ /* The branch will always be shortened now that the reference address for
+ forward branches is the successor address, thus we need no longer make
+ adjustments to the [sh]i_limit for -O0. */
+
+ si_limit = 1018;
+ hi_limit = 510;
+
+ while (from && count_si < si_limit && count_hi < hi_limit)
+ {
+ int inc = get_attr_length (from);
+ int new_align = 1;
+
+ /* If this is a label that existed at the time of the compute_alignments
+ call, determine the alignment. N.B. When find_barrier recurses for
+ an out-of-reach mova, we might see labels at the start of previously
+ inserted constant tables. */
+ if (LABEL_P (from)
+ && CODE_LABEL_NUMBER (from) <= max_labelno_before_reorg)
+ {
+ if (optimize)
+ new_align = 1 << label_to_alignment (from);
+ else if (BARRIER_P (prev_nonnote_insn (from)))
+ new_align = 1 << barrier_align (from);
+ else
+ new_align = 1;
+ inc = 0;
+ }
+ /* In case we are scanning a constant table because of recursion, check
+ for explicit alignments. If the table is long, we might be forced
+ to emit the new table in front of it; the length of the alignment
+ might be the last straw. */
+ else if (NONJUMP_INSN_P (from)
+ && GET_CODE (PATTERN (from)) == UNSPEC_VOLATILE
+ && XINT (PATTERN (from), 1) == UNSPECV_ALIGN)
+ new_align = INTVAL (XVECEXP (PATTERN (from), 0, 0));
+ /* When we find the end of a constant table, paste the new constant
+ at the end. That is better than putting it in front because
+ this way, we don't need extra alignment for adding a 4-byte-aligned
+ mov(a) label to a 2/4 or 8/4 byte aligned table. */
+ else if (NONJUMP_INSN_P (from)
+ && GET_CODE (PATTERN (from)) == UNSPEC_VOLATILE
+ && XINT (PATTERN (from), 1) == UNSPECV_CONST_END)
+ return from;
+
+ if (BARRIER_P (from))
+ {
+ rtx next;
+
+ found_barrier = from;
+
+ /* If we are at the end of the function, or in front of an alignment
+ instruction, we need not insert an extra alignment. We prefer
+ this kind of barrier. */
+ if (barrier_align (from) > 2)
+ good_barrier = from;
+
+ /* If we are at the end of a hot/cold block, dump the constants
+ here. */
+ next = NEXT_INSN (from);
+ if (next
+ && NOTE_P (next)
+ && NOTE_KIND (next) == NOTE_INSN_SWITCH_TEXT_SECTIONS)
+ break;
+ }
+
+ if (broken_move (from))
+ {
+ rtx pat, src, dst;
+ enum machine_mode mode;
+
+ pat = PATTERN (from);
+ if (GET_CODE (pat) == PARALLEL)
+ pat = XVECEXP (pat, 0, 0);
+ src = SET_SRC (pat);
+ dst = SET_DEST (pat);
+ mode = GET_MODE (dst);
+
+ /* GOT pcrelat setting comes in pair of
+ mova .L8,r0
+ mov.l .L8,r12
+ instructions. (plus add r0,r12).
+ Remember if we see one without the other. */
+ if (GET_CODE (src) == UNSPEC && PIC_ADDR_P (XVECEXP (src, 0, 0)))
+ last_got = last_got ? NULL_RTX : from;
+ else if (PIC_ADDR_P (src))
+ last_got = last_got ? NULL_RTX : from;
+
+ /* We must explicitly check the mode, because sometimes the
+ front end will generate code to load unsigned constants into
+ HImode targets without properly sign extending them. */
+ if (mode == HImode
+ || (mode == SImode && hi_const (src) && REGNO (dst) != FPUL_REG))
+ {
+ found_hi += 2;
+ /* We put the short constants before the long constants, so
+ we must count the length of short constants in the range
+ for the long constants. */
+ /* ??? This isn't optimal, but is easy to do. */
+ si_limit -= 2;
+ }
+ else
+ {
+ /* We dump DF/DI constants before SF/SI ones, because
+ the limit is the same, but the alignment requirements
+ are higher. We may waste up to 4 additional bytes
+ for alignment, and the DF/DI constant may have
+ another SF/SI constant placed before it. */
+ if (TARGET_SHCOMPACT
+ && ! found_di
+ && (mode == DFmode || mode == DImode))
+ {
+ found_di = 1;
+ si_limit -= 8;
+ }
+ while (si_align > 2 && found_si + si_align - 2 > count_si)
+ si_align >>= 1;
+ if (found_si > count_si)
+ count_si = found_si;
+ found_si += GET_MODE_SIZE (mode);
+ if (num_mova)
+ si_limit -= GET_MODE_SIZE (mode);
+ }
+ }
+
+ if (mova_p (from))
+ {
+ switch (untangle_mova (&num_mova, &mova, from))
+ {
+ case 1:
+ if (flag_pic)
+ {
+ rtx src = SET_SRC (PATTERN (from));
+ if (GET_CODE (src) == CONST
+ && GET_CODE (XEXP (src, 0)) == UNSPEC
+ && XINT (XEXP (src, 0), 1) == UNSPEC_SYMOFF)
+ last_symoff = from;
+ }
+ break;
+ case 0: return find_barrier (0, 0, mova);
+ case 2:
+ {
+ leading_mova = 0;
+ barrier_before_mova
+ = good_barrier ? good_barrier : found_barrier;
+ }
+ default: break;
+ }
+ if (found_si > count_si)
+ count_si = found_si;
+ }
+ else if (JUMP_TABLE_DATA_P (from))
+ {
+ if ((num_mova > 1 && GET_MODE (prev_nonnote_insn (from)) == VOIDmode)
+ || (num_mova
+ && (prev_nonnote_insn (from)
+ == XEXP (MOVA_LABELREF (mova), 0))))
+ num_mova--;
+ if (barrier_align (next_real_insn (from)) == align_jumps_log)
+ {
+ /* We have just passed the barrier in front of the
+ ADDR_DIFF_VEC, which is stored in found_barrier. Since
+ the ADDR_DIFF_VEC is accessed as data, just like our pool
+ constants, this is a good opportunity to accommodate what
+ we have gathered so far.
+ If we waited any longer, we could end up at a barrier in
+ front of code, which gives worse cache usage for separated
+ instruction / data caches. */
+ good_barrier = found_barrier;
+ break;
+ }
+ else
+ {
+ rtx body = PATTERN (from);
+ inc = XVECLEN (body, 1) * GET_MODE_SIZE (GET_MODE (body));
+ }
+ }
+ /* For the SH1, we generate alignments even after jumps-around-jumps. */
+ else if (JUMP_P (from)
+ && ! TARGET_SH2
+ && ! optimize_size)
+ new_align = 4;
+
+ /* There is a possibility that a bf is transformed into a bf/s by the
+ delay slot scheduler. */
+ if (JUMP_P (from) && !JUMP_TABLE_DATA_P (from)
+ && get_attr_type (from) == TYPE_CBRANCH
+ && GET_CODE (PATTERN (NEXT_INSN (PREV_INSN (from)))) != SEQUENCE)
+ inc += 2;
+
+ if (found_si)
+ {
+ count_si += inc;
+ if (new_align > si_align)
+ {
+ si_limit -= (count_si - 1) & (new_align - si_align);
+ si_align = new_align;
+ }
+ count_si = (count_si + new_align - 1) & -new_align;
+ }
+ if (found_hi)
+ {
+ count_hi += inc;
+ if (new_align > hi_align)
+ {
+ hi_limit -= (count_hi - 1) & (new_align - hi_align);
+ hi_align = new_align;
+ }
+ count_hi = (count_hi + new_align - 1) & -new_align;
+ }
+ from = NEXT_INSN (from);
+ }
+
+ if (num_mova)
+ {
+ if (leading_mova)
+ {
+ /* Try as we might, the leading mova is out of range. Change
+ it into a load (which will become a pcload) and retry. */
+ fixup_mova (mova);
+ return find_barrier (0, 0, mova);
+ }
+ else
+ {
+ /* Insert the constant pool table before the mova instruction,
+ to prevent the mova label reference from going out of range. */
+ from = mova;
+ good_barrier = found_barrier = barrier_before_mova;
+ }
+ }
+
+ if (found_barrier)
+ {
+ if (good_barrier && next_real_insn (found_barrier))
+ found_barrier = good_barrier;
+ }
+ else
+ {
+ /* We didn't find a barrier in time to dump our stuff,
+ so we'll make one. */
+ rtx label = gen_label_rtx ();
+
+ /* Don't emit a constant table in the middle of insns for
+ casesi_worker_2. This is a bit overkill but is enough
+ because casesi_worker_2 wouldn't appear so frequently. */
+ if (last_symoff)
+ from = last_symoff;
+
+ /* If we exceeded the range, then we must back up over the last
+ instruction we looked at. Otherwise, we just need to undo the
+ NEXT_INSN at the end of the loop. */
+ if (PREV_INSN (from) != orig
+ && (count_hi > hi_limit || count_si > si_limit))
+ from = PREV_INSN (PREV_INSN (from));
+ else
+ from = PREV_INSN (from);
+
+ /* Don't emit a constant table int the middle of global pointer setting,
+ since that that would move the addressing base GOT into another table.
+ We need the first mov instruction before the _GLOBAL_OFFSET_TABLE_
+ in the pool anyway, so just move up the whole constant pool. */
+ if (last_got)
+ from = PREV_INSN (last_got);
+
+ /* Don't insert the constant pool table at the position which
+ may be the landing pad. */
+ if (flag_exceptions
+ && CALL_P (from)
+ && find_reg_note (from, REG_EH_REGION, NULL_RTX))
+ from = PREV_INSN (from);
+
+ /* Walk back to be just before any jump or label.
+ Putting it before a label reduces the number of times the branch
+ around the constant pool table will be hit. Putting it before
+ a jump makes it more likely that the bra delay slot will be
+ filled. */
+ while (NOTE_P (from) || JUMP_P (from)
+ || LABEL_P (from))
+ from = PREV_INSN (from);
+
+ from = emit_jump_insn_after (gen_jump (label), from);
+ JUMP_LABEL (from) = label;
+ LABEL_NUSES (label) = 1;
+ found_barrier = emit_barrier_after (from);
+ emit_label_after (label, found_barrier);
+ }
+
+ return found_barrier;
+}
+
+/* If the instruction INSN is implemented by a special function, and we can
+ positively find the register that is used to call the sfunc, and this
+ register is not used anywhere else in this instruction - except as the
+ destination of a set, return this register; else, return 0. */
+rtx
+sfunc_uses_reg (rtx insn)
+{
+ int i;
+ rtx pattern, part, reg_part, reg;
+
+ if (!NONJUMP_INSN_P (insn))
+ return 0;
+ pattern = PATTERN (insn);
+ if (GET_CODE (pattern) != PARALLEL || get_attr_type (insn) != TYPE_SFUNC)
+ return 0;
+
+ for (reg_part = 0, i = XVECLEN (pattern, 0) - 1; i >= 1; i--)
+ {
+ part = XVECEXP (pattern, 0, i);
+ if (GET_CODE (part) == USE && GET_MODE (XEXP (part, 0)) == SImode)
+ reg_part = part;
+ }
+ if (! reg_part)
+ return 0;
+ reg = XEXP (reg_part, 0);
+ for (i = XVECLEN (pattern, 0) - 1; i >= 0; i--)
+ {
+ part = XVECEXP (pattern, 0, i);
+ if (part == reg_part || GET_CODE (part) == CLOBBER)
+ continue;
+ if (reg_mentioned_p (reg, ((GET_CODE (part) == SET
+ && REG_P (SET_DEST (part)))
+ ? SET_SRC (part) : part)))
+ return 0;
+ }
+ return reg;
+}
+
+/* See if the only way in which INSN uses REG is by calling it, or by
+ setting it while calling it. Set *SET to a SET rtx if the register
+ is set by INSN. */
+
+static int
+noncall_uses_reg (rtx reg, rtx insn, rtx *set)
+{
+ rtx pattern, reg2;
+
+ *set = NULL_RTX;
+
+ reg2 = sfunc_uses_reg (insn);
+ if (reg2 && REGNO (reg2) == REGNO (reg))
+ {
+ pattern = single_set (insn);
+ if (pattern
+ && REG_P (SET_DEST (pattern))
+ && REGNO (reg) == REGNO (SET_DEST (pattern)))
+ *set = pattern;
+ return 0;
+ }
+ if (!CALL_P (insn))
+ {
+ /* We don't use rtx_equal_p because we don't care if the mode is
+ different. */
+ pattern = single_set (insn);
+ if (pattern
+ && REG_P (SET_DEST (pattern))
+ && REGNO (reg) == REGNO (SET_DEST (pattern)))
+ {
+ rtx par, part;
+ int i;
+
+ *set = pattern;
+ par = PATTERN (insn);
+ if (GET_CODE (par) == PARALLEL)
+ for (i = XVECLEN (par, 0) - 1; i >= 0; i--)
+ {
+ part = XVECEXP (par, 0, i);
+ if (GET_CODE (part) != SET && reg_mentioned_p (reg, part))
+ return 1;
+ }
+ return reg_mentioned_p (reg, SET_SRC (pattern));
+ }
+
+ return 1;
+ }
+
+ pattern = PATTERN (insn);
+
+ if (GET_CODE (pattern) == PARALLEL)
+ {
+ int i;
+
+ for (i = XVECLEN (pattern, 0) - 1; i >= 1; i--)
+ if (reg_mentioned_p (reg, XVECEXP (pattern, 0, i)))
+ return 1;
+ pattern = XVECEXP (pattern, 0, 0);
+ }
+
+ if (GET_CODE (pattern) == SET)
+ {
+ if (reg_mentioned_p (reg, SET_DEST (pattern)))
+ {
+ /* We don't use rtx_equal_p, because we don't care if the
+ mode is different. */
+ if (!REG_P (SET_DEST (pattern))
+ || REGNO (reg) != REGNO (SET_DEST (pattern)))
+ return 1;
+
+ *set = pattern;
+ }
+
+ pattern = SET_SRC (pattern);
+ }
+
+ if (GET_CODE (pattern) != CALL
+ || !MEM_P (XEXP (pattern, 0))
+ || ! rtx_equal_p (reg, XEXP (XEXP (pattern, 0), 0)))
+ return 1;
+
+ return 0;
+}
+
+/* Given a X, a pattern of an insn or a part of it, return a mask of used
+ general registers. Bits 0..15 mean that the respective registers
+ are used as inputs in the instruction. Bits 16..31 mean that the
+ registers 0..15, respectively, are used as outputs, or are clobbered.
+ IS_DEST should be set to 16 if X is the destination of a SET, else to 0. */
+int
+regs_used (rtx x, int is_dest)
+{
+ enum rtx_code code;
+ const char *fmt;
+ int i, used = 0;
+
+ if (! x)
+ return used;
+ code = GET_CODE (x);
+ switch (code)
+ {
+ case REG:
+ if (REGNO (x) < 16)
+ return (((1 << HARD_REGNO_NREGS (0, GET_MODE (x))) - 1)
+ << (REGNO (x) + is_dest));
+ return 0;
+ case SUBREG:
+ {
+ rtx y = SUBREG_REG (x);
+
+ if (!REG_P (y))
+ break;
+ if (REGNO (y) < 16)
+ return (((1 << HARD_REGNO_NREGS (0, GET_MODE (x))) - 1)
+ << (REGNO (y) +
+ subreg_regno_offset (REGNO (y),
+ GET_MODE (y),
+ SUBREG_BYTE (x),
+ GET_MODE (x)) + is_dest));
+ return 0;
+ }
+ case SET:
+ return regs_used (SET_SRC (x), 0) | regs_used (SET_DEST (x), 16);
+ case RETURN:
+ /* If there was a return value, it must have been indicated with USE. */
+ return 0x00ffff00;
+ case CLOBBER:
+ is_dest = 1;
+ break;
+ case MEM:
+ is_dest = 0;
+ break;
+ case CALL:
+ used |= 0x00ff00f0;
+ break;
+ default:
+ break;
+ }
+
+ fmt = GET_RTX_FORMAT (code);
+
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'E')
+ {
+ register int j;
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ used |= regs_used (XVECEXP (x, i, j), is_dest);
+ }
+ else if (fmt[i] == 'e')
+ used |= regs_used (XEXP (x, i), is_dest);
+ }
+ return used;
+}
+
+/* Create an instruction that prevents redirection of a conditional branch
+ to the destination of the JUMP with address ADDR.
+ If the branch needs to be implemented as an indirect jump, try to find
+ a scratch register for it.
+ If NEED_BLOCK is 0, don't do anything unless we need a scratch register.
+ If any preceding insn that doesn't fit into a delay slot is good enough,
+ pass 1. Pass 2 if a definite blocking insn is needed.
+ -1 is used internally to avoid deep recursion.
+ If a blocking instruction is made or recognized, return it. */
+
+static rtx
+gen_block_redirect (rtx jump, int addr, int need_block)
+{
+ int dead = 0;
+ rtx prev = prev_nonnote_insn (jump);
+ rtx dest;
+
+ /* First, check if we already have an instruction that satisfies our need. */
+ if (prev && NONJUMP_INSN_P (prev) && ! INSN_DELETED_P (prev))
+ {
+ if (INSN_CODE (prev) == CODE_FOR_indirect_jump_scratch)
+ return prev;
+ if (GET_CODE (PATTERN (prev)) == USE
+ || GET_CODE (PATTERN (prev)) == CLOBBER
+ || get_attr_in_delay_slot (prev) == IN_DELAY_SLOT_YES)
+ prev = jump;
+ else if ((need_block &= ~1) < 0)
+ return prev;
+ else if (recog_memoized (prev) == CODE_FOR_block_branch_redirect)
+ need_block = 0;
+ }
+ if (GET_CODE (PATTERN (jump)) == RETURN)
+ {
+ if (! need_block)
+ return prev;
+ /* Reorg even does nasty things with return insns that cause branches
+ to go out of range - see find_end_label and callers. */
+ return emit_insn_before (gen_block_branch_redirect (const0_rtx) , jump);
+ }
+ /* We can't use JUMP_LABEL here because it might be undefined
+ when not optimizing. */
+ dest = XEXP (SET_SRC (PATTERN (jump)), 0);
+ /* If the branch is out of range, try to find a scratch register for it. */
+ if (optimize
+ && (INSN_ADDRESSES (INSN_UID (dest)) - addr + (unsigned) 4092
+ > 4092 + 4098))
+ {
+ rtx scan;
+ /* Don't look for the stack pointer as a scratch register,
+ it would cause trouble if an interrupt occurred. */
+ unsigned attempt = 0x7fff, used;
+ int jump_left = flag_expensive_optimizations + 1;
+
+ /* It is likely that the most recent eligible instruction is wanted for
+ the delay slot. Therefore, find out which registers it uses, and
+ try to avoid using them. */
+
+ for (scan = jump; (scan = PREV_INSN (scan)); )
+ {
+ enum rtx_code code;
+
+ if (INSN_DELETED_P (scan))
+ continue;
+ code = GET_CODE (scan);
+ if (code == CODE_LABEL || code == JUMP_INSN)
+ break;
+ if (code == INSN
+ && GET_CODE (PATTERN (scan)) != USE
+ && GET_CODE (PATTERN (scan)) != CLOBBER
+ && get_attr_in_delay_slot (scan) == IN_DELAY_SLOT_YES)
+ {
+ attempt &= ~regs_used (PATTERN (scan), 0);
+ break;
+ }
+ }
+ for (used = dead = 0, scan = JUMP_LABEL (jump);
+ (scan = NEXT_INSN (scan)); )
+ {
+ enum rtx_code code;
+
+ if (INSN_DELETED_P (scan))
+ continue;
+ code = GET_CODE (scan);
+ if (INSN_P (scan))
+ {
+ used |= regs_used (PATTERN (scan), 0);
+ if (code == CALL_INSN)
+ used |= regs_used (CALL_INSN_FUNCTION_USAGE (scan), 0);
+ dead |= (used >> 16) & ~used;
+ if (dead & attempt)
+ {
+ dead &= attempt;
+ break;
+ }
+ if (code == JUMP_INSN)
+ {
+ if (jump_left-- && simplejump_p (scan))
+ scan = JUMP_LABEL (scan);
+ else
+ break;
+ }
+ }
+ }
+ /* Mask out the stack pointer again, in case it was
+ the only 'free' register we have found. */
+ dead &= 0x7fff;
+ }
+ /* If the immediate destination is still in range, check for possible
+ threading with a jump beyond the delay slot insn.
+ Don't check if we are called recursively; the jump has been or will be
+ checked in a different invocation then. */
+
+ else if (optimize && need_block >= 0)
+ {
+ rtx next = next_active_insn (next_active_insn (dest));
+ if (next && JUMP_P (next)
+ && GET_CODE (PATTERN (next)) == SET
+ && recog_memoized (next) == CODE_FOR_jump_compact)
+ {
+ dest = JUMP_LABEL (next);
+ if (dest
+ && (INSN_ADDRESSES (INSN_UID (dest)) - addr + (unsigned) 4092
+ > 4092 + 4098))
+ gen_block_redirect (next, INSN_ADDRESSES (INSN_UID (next)), -1);
+ }
+ }
+
+ if (dead)
+ {
+ rtx reg = gen_rtx_REG (SImode, exact_log2 (dead & -dead));
+
+ /* It would be nice if we could convert the jump into an indirect
+ jump / far branch right now, and thus exposing all constituent
+ instructions to further optimization. However, reorg uses
+ simplejump_p to determine if there is an unconditional jump where
+ it should try to schedule instructions from the target of the
+ branch; simplejump_p fails for indirect jumps even if they have
+ a JUMP_LABEL. */
+ rtx insn = emit_insn_before (gen_indirect_jump_scratch
+ (reg, GEN_INT (unspec_bbr_uid++)),
+ jump);
+ /* ??? We would like this to have the scope of the jump, but that
+ scope will change when a delay slot insn of an inner scope is added.
+ Hence, after delay slot scheduling, we'll have to expect
+ NOTE_INSN_BLOCK_END notes between the indirect_jump_scratch and
+ the jump. */
+
+ INSN_LOCATOR (insn) = INSN_LOCATOR (jump);
+ INSN_CODE (insn) = CODE_FOR_indirect_jump_scratch;
+ return insn;
+ }
+ else if (need_block)
+ /* We can't use JUMP_LABEL here because it might be undefined
+ when not optimizing. */
+ return emit_insn_before (gen_block_branch_redirect
+ (GEN_INT (unspec_bbr_uid++)),
+ jump);
+ return prev;
+}
+
+#define CONDJUMP_MIN -252
+#define CONDJUMP_MAX 262
+struct far_branch
+{
+ /* A label (to be placed) in front of the jump
+ that jumps to our ultimate destination. */
+ rtx near_label;
+ /* Where we are going to insert it if we cannot move the jump any farther,
+ or the jump itself if we have picked up an existing jump. */
+ rtx insert_place;
+ /* The ultimate destination. */
+ rtx far_label;
+ struct far_branch *prev;
+ /* If the branch has already been created, its address;
+ else the address of its first prospective user. */
+ int address;
+};
+
+static void gen_far_branch (struct far_branch *);
+enum mdep_reorg_phase_e mdep_reorg_phase;
+static void
+gen_far_branch (struct far_branch *bp)
+{
+ rtx insn = bp->insert_place;
+ rtx jump;
+ rtx label = gen_label_rtx ();
+ int ok;
+
+ emit_label_after (label, insn);
+ if (bp->far_label)
+ {
+ jump = emit_jump_insn_after (gen_jump (bp->far_label), insn);
+ LABEL_NUSES (bp->far_label)++;
+ }
+ else
+ jump = emit_jump_insn_after (gen_return (), insn);
+ /* Emit a barrier so that reorg knows that any following instructions
+ are not reachable via a fall-through path.
+ But don't do this when not optimizing, since we wouldn't suppress the
+ alignment for the barrier then, and could end up with out-of-range
+ pc-relative loads. */
+ if (optimize)
+ emit_barrier_after (jump);
+ emit_label_after (bp->near_label, insn);
+ JUMP_LABEL (jump) = bp->far_label;
+ ok = invert_jump (insn, label, 1);
+ gcc_assert (ok);
+
+ /* If we are branching around a jump (rather than a return), prevent
+ reorg from using an insn from the jump target as the delay slot insn -
+ when reorg did this, it pessimized code (we rather hide the delay slot)
+ and it could cause branches to go out of range. */
+ if (bp->far_label)
+ (emit_insn_after
+ (gen_stuff_delay_slot
+ (GEN_INT (unspec_bbr_uid++),
+ GEN_INT (recog_memoized (insn) == CODE_FOR_branch_false)),
+ insn));
+ /* Prevent reorg from undoing our splits. */
+ gen_block_redirect (jump, bp->address += 2, 2);
+}
+
+/* Fix up ADDR_DIFF_VECs. */
+void
+fixup_addr_diff_vecs (rtx first)
+{
+ rtx insn;
+
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ {
+ rtx vec_lab, pat, prev, prevpat, x, braf_label;
+
+ if (!JUMP_P (insn)
+ || GET_CODE (PATTERN (insn)) != ADDR_DIFF_VEC)
+ continue;
+ pat = PATTERN (insn);
+ vec_lab = XEXP (XEXP (pat, 0), 0);
+
+ /* Search the matching casesi_jump_2. */
+ for (prev = vec_lab; ; prev = PREV_INSN (prev))
+ {
+ if (!JUMP_P (prev))
+ continue;
+ prevpat = PATTERN (prev);
+ if (GET_CODE (prevpat) != PARALLEL || XVECLEN (prevpat, 0) != 2)
+ continue;
+ x = XVECEXP (prevpat, 0, 1);
+ if (GET_CODE (x) != USE)
+ continue;
+ x = XEXP (x, 0);
+ if (GET_CODE (x) == LABEL_REF && XEXP (x, 0) == vec_lab)
+ break;
+ }
+ /* FIXME: This is a bug in the optimizer, but it seems harmless
+ to just avoid panicing. */
+ if (!prev)
+ continue;
+
+ /* Emit the reference label of the braf where it belongs, right after
+ the casesi_jump_2 (i.e. braf). */
+ braf_label = XEXP (XEXP (SET_SRC (XVECEXP (prevpat, 0, 0)), 1), 0);
+ emit_label_after (braf_label, prev);
+
+ /* Fix up the ADDR_DIF_VEC to be relative
+ to the reference address of the braf. */
+ XEXP (XEXP (pat, 0), 0) = braf_label;
+ }
+}
+
+/* BARRIER_OR_LABEL is either a BARRIER or a CODE_LABEL immediately following
+ a barrier. Return the base 2 logarithm of the desired alignment. */
+int
+barrier_align (rtx barrier_or_label)
+{
+ rtx next = next_real_insn (barrier_or_label), pat, prev;
+ int slot, credit, jump_to_next = 0;
+
+ if (! next)
+ return 0;
+
+ pat = PATTERN (next);
+
+ if (GET_CODE (pat) == ADDR_DIFF_VEC)
+ return 2;
+
+ if (GET_CODE (pat) == UNSPEC_VOLATILE && XINT (pat, 1) == UNSPECV_ALIGN)
+ /* This is a barrier in front of a constant table. */
+ return 0;
+
+ prev = prev_real_insn (barrier_or_label);
+ if (GET_CODE (PATTERN (prev)) == ADDR_DIFF_VEC)
+ {
+ pat = PATTERN (prev);
+ /* If this is a very small table, we want to keep the alignment after
+ the table to the minimum for proper code alignment. */
+ return ((optimize_size
+ || ((unsigned) XVECLEN (pat, 1) * GET_MODE_SIZE (GET_MODE (pat))
+ <= (unsigned) 1 << (CACHE_LOG - 2)))
+ ? 1 << TARGET_SHMEDIA : align_jumps_log);
+ }
+
+ if (optimize_size)
+ return 0;
+
+ if (! TARGET_SH2 || ! optimize)
+ return align_jumps_log;
+
+ /* When fixing up pcloads, a constant table might be inserted just before
+ the basic block that ends with the barrier. Thus, we can't trust the
+ instruction lengths before that. */
+ if (mdep_reorg_phase > SH_FIXUP_PCLOAD)
+ {
+ /* Check if there is an immediately preceding branch to the insn beyond
+ the barrier. We must weight the cost of discarding useful information
+ from the current cache line when executing this branch and there is
+ an alignment, against that of fetching unneeded insn in front of the
+ branch target when there is no alignment. */
+
+ /* There are two delay_slot cases to consider. One is the simple case
+ where the preceding branch is to the insn beyond the barrier (simple
+ delay slot filling), and the other is where the preceding branch has
+ a delay slot that is a duplicate of the insn after the barrier
+ (fill_eager_delay_slots) and the branch is to the insn after the insn
+ after the barrier. */
+
+ /* PREV is presumed to be the JUMP_INSN for the barrier under
+ investigation. Skip to the insn before it. */
+ prev = prev_real_insn (prev);
+
+ for (slot = 2, credit = (1 << (CACHE_LOG - 2)) + 2;
+ credit >= 0 && prev && NONJUMP_INSN_P (prev);
+ prev = prev_real_insn (prev))
+ {
+ jump_to_next = 0;
+ if (GET_CODE (PATTERN (prev)) == USE
+ || GET_CODE (PATTERN (prev)) == CLOBBER)
+ continue;
+ if (GET_CODE (PATTERN (prev)) == SEQUENCE)
+ {
+ prev = XVECEXP (PATTERN (prev), 0, 1);
+ if (INSN_UID (prev) == INSN_UID (next))
+ {
+ /* Delay slot was filled with insn at jump target. */
+ jump_to_next = 1;
+ continue;
+ }
+ }
+
+ if (slot &&
+ get_attr_in_delay_slot (prev) == IN_DELAY_SLOT_YES)
+ slot = 0;
+ credit -= get_attr_length (prev);
+ }
+ if (prev
+ && JUMP_P (prev)
+ && JUMP_LABEL (prev))
+ {
+ rtx x;
+ if (jump_to_next
+ || next_real_insn (JUMP_LABEL (prev)) == next
+ /* If relax_delay_slots() decides NEXT was redundant
+ with some previous instruction, it will have
+ redirected PREV's jump to the following insn. */
+ || JUMP_LABEL (prev) == next_nonnote_insn (next)
+ /* There is no upper bound on redundant instructions
+ that might have been skipped, but we must not put an
+ alignment where none had been before. */
+ || (x = (NEXT_INSN (NEXT_INSN (PREV_INSN (prev)))),
+ (INSN_P (x)
+ && (INSN_CODE (x) == CODE_FOR_block_branch_redirect
+ || INSN_CODE (x) == CODE_FOR_indirect_jump_scratch
+ || INSN_CODE (x) == CODE_FOR_stuff_delay_slot))))
+ {
+ rtx pat = PATTERN (prev);
+ if (GET_CODE (pat) == PARALLEL)
+ pat = XVECEXP (pat, 0, 0);
+ if (credit - slot >= (GET_CODE (SET_SRC (pat)) == PC ? 2 : 0))
+ return 0;
+ }
+ }
+ }
+
+ return align_jumps_log;
+}
+
+/* If we are inside a phony loop, almost any kind of label can turn up as the
+ first one in the loop. Aligning a braf label causes incorrect switch
+ destination addresses; we can detect braf labels because they are
+ followed by a BARRIER.
+ Applying loop alignment to small constant or switch tables is a waste
+ of space, so we suppress this too. */
+int
+sh_loop_align (rtx label)
+{
+ rtx next = label;
+
+ do
+ next = next_nonnote_insn (next);
+ while (next && LABEL_P (next));
+
+ if (! next
+ || ! INSN_P (next)
+ || GET_CODE (PATTERN (next)) == ADDR_DIFF_VEC
+ || recog_memoized (next) == CODE_FOR_consttable_2)
+ return 0;
+
+ return align_loops_log;
+}
+
+/* Do a final pass over the function, just before delayed branch
+ scheduling. */
+
+static void
+sh_reorg (void)
+{
+ rtx first, insn, mova = NULL_RTX;
+ int num_mova;
+ rtx r0_rtx = gen_rtx_REG (Pmode, 0);
+ rtx r0_inc_rtx = gen_rtx_POST_INC (Pmode, r0_rtx);
+
+ first = get_insns ();
+ max_labelno_before_reorg = max_label_num ();
+
+ /* We must split call insns before introducing `mova's. If we're
+ optimizing, they'll have already been split. Otherwise, make
+ sure we don't split them too late. */
+ if (! optimize)
+ split_all_insns_noflow ();
+
+ if (TARGET_SHMEDIA)
+ return;
+
+ /* If relaxing, generate pseudo-ops to associate function calls with
+ the symbols they call. It does no harm to not generate these
+ pseudo-ops. However, when we can generate them, it enables to
+ linker to potentially relax the jsr to a bsr, and eliminate the
+ register load and, possibly, the constant pool entry. */
+
+ mdep_reorg_phase = SH_INSERT_USES_LABELS;
+ if (TARGET_RELAX)
+ {
+ /* Remove all REG_LABEL_OPERAND notes. We want to use them for our
+ own purposes. This works because none of the remaining passes
+ need to look at them.
+
+ ??? But it may break in the future. We should use a machine
+ dependent REG_NOTE, or some other approach entirely. */
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ {
+ if (INSN_P (insn))
+ {
+ rtx note;
+
+ while ((note = find_reg_note (insn, REG_LABEL_OPERAND,
+ NULL_RTX)) != 0)
+ remove_note (insn, note);
+ }
+ }
+
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ {
+ rtx pattern, reg, link, set, scan, dies, label;
+ int rescan = 0, foundinsn = 0;
+
+ if (CALL_P (insn))
+ {
+ pattern = PATTERN (insn);
+
+ if (GET_CODE (pattern) == PARALLEL)
+ pattern = XVECEXP (pattern, 0, 0);
+ if (GET_CODE (pattern) == SET)
+ pattern = SET_SRC (pattern);
+
+ if (GET_CODE (pattern) != CALL
+ || !MEM_P (XEXP (pattern, 0)))
+ continue;
+
+ reg = XEXP (XEXP (pattern, 0), 0);
+ }
+ else
+ {
+ reg = sfunc_uses_reg (insn);
+ if (! reg)
+ continue;
+ }
+
+ if (!REG_P (reg))
+ continue;
+
+ /* Try scanning backward to find where the register is set. */
+ link = NULL;
+ for (scan = PREV_INSN (insn);
+ scan && !LABEL_P (scan);
+ scan = PREV_INSN (scan))
+ {
+ if (! INSN_P (scan))
+ continue;
+
+ if (! reg_mentioned_p (reg, scan))
+ continue;
+
+ if (noncall_uses_reg (reg, scan, &set))
+ break;
+
+ if (set)
+ {
+ link = scan;
+ break;
+ }
+ }
+
+ if (! link)
+ continue;
+
+ /* The register is set at LINK. */
+
+ /* We can only optimize the function call if the register is
+ being set to a symbol. In theory, we could sometimes
+ optimize calls to a constant location, but the assembler
+ and linker do not support that at present. */
+ if (GET_CODE (SET_SRC (set)) != SYMBOL_REF
+ && GET_CODE (SET_SRC (set)) != LABEL_REF)
+ continue;
+
+ /* Scan forward from LINK to the place where REG dies, and
+ make sure that the only insns which use REG are
+ themselves function calls. */
+
+ /* ??? This doesn't work for call targets that were allocated
+ by reload, since there may not be a REG_DEAD note for the
+ register. */
+
+ dies = NULL_RTX;
+ for (scan = NEXT_INSN (link); scan; scan = NEXT_INSN (scan))
+ {
+ rtx scanset;
+
+ /* Don't try to trace forward past a CODE_LABEL if we haven't
+ seen INSN yet. Ordinarily, we will only find the setting insn
+ if it is in the same basic block. However,
+ cross-jumping can insert code labels in between the load and
+ the call, and can result in situations where a single call
+ insn may have two targets depending on where we came from. */
+
+ if (LABEL_P (scan) && ! foundinsn)
+ break;
+
+ if (! INSN_P (scan))
+ continue;
+
+ /* Don't try to trace forward past a JUMP. To optimize
+ safely, we would have to check that all the
+ instructions at the jump destination did not use REG. */
+
+ if (JUMP_P (scan))
+ break;
+
+ if (! reg_mentioned_p (reg, scan))
+ continue;
+
+ if (noncall_uses_reg (reg, scan, &scanset))
+ break;
+
+ if (scan == insn)
+ foundinsn = 1;
+
+ if (scan != insn
+ && (CALL_P (scan) || sfunc_uses_reg (scan)))
+ {
+ /* There is a function call to this register other
+ than the one we are checking. If we optimize
+ this call, we need to rescan again below. */
+ rescan = 1;
+ }
+
+ /* ??? We shouldn't have to worry about SCANSET here.
+ We should just be able to check for a REG_DEAD note
+ on a function call. However, the REG_DEAD notes are
+ apparently not dependable around libcalls; c-torture
+ execute/920501-2 is a test case. If SCANSET is set,
+ then this insn sets the register, so it must have
+ died earlier. Unfortunately, this will only handle
+ the cases in which the register is, in fact, set in a
+ later insn. */
+
+ /* ??? We shouldn't have to use FOUNDINSN here.
+ This dates back to when we used LOG_LINKS to find
+ the most recent insn which sets the register. */
+
+ if (foundinsn
+ && (scanset
+ || find_reg_note (scan, REG_DEAD, reg)))
+ {
+ dies = scan;
+ break;
+ }
+ }
+
+ if (! dies)
+ {
+ /* Either there was a branch, or some insn used REG
+ other than as a function call address. */
+ continue;
+ }
+
+ /* Create a code label, and put it in a REG_LABEL_OPERAND note
+ on the insn which sets the register, and on each call insn
+ which uses the register. In final_prescan_insn we look for
+ the REG_LABEL_OPERAND notes, and output the appropriate label
+ or pseudo-op. */
+
+ label = gen_label_rtx ();
+ add_reg_note (link, REG_LABEL_OPERAND, label);
+ add_reg_note (insn, REG_LABEL_OPERAND, label);
+ if (rescan)
+ {
+ scan = link;
+ do
+ {
+ rtx reg2;
+
+ scan = NEXT_INSN (scan);
+ if (scan != insn
+ && ((CALL_P (scan)
+ && reg_mentioned_p (reg, scan))
+ || ((reg2 = sfunc_uses_reg (scan))
+ && REGNO (reg2) == REGNO (reg))))
+ add_reg_note (scan, REG_LABEL_OPERAND, label);
+ }
+ while (scan != dies);
+ }
+ }
+ }
+
+ if (TARGET_SH2)
+ fixup_addr_diff_vecs (first);
+
+ if (optimize)
+ {
+ mdep_reorg_phase = SH_SHORTEN_BRANCHES0;
+ shorten_branches (first);
+ }
+
+ /* Scan the function looking for move instructions which have to be
+ changed to pc-relative loads and insert the literal tables. */
+ label_ref_list_pool = create_alloc_pool ("label references list",
+ sizeof (struct label_ref_list_d),
+ 30);
+ mdep_reorg_phase = SH_FIXUP_PCLOAD;
+ for (insn = first, num_mova = 0; insn; insn = NEXT_INSN (insn))
+ {
+ if (mova_p (insn))
+ {
+ /* ??? basic block reordering can move a switch table dispatch
+ below the switch table. Check if that has happened.
+ We only have the addresses available when optimizing; but then,
+ this check shouldn't be needed when not optimizing. */
+ if (!untangle_mova (&num_mova, &mova, insn))
+ {
+ insn = mova;
+ num_mova = 0;
+ }
+ }
+ else if (JUMP_P (insn)
+ && GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC
+ && num_mova
+ /* ??? loop invariant motion can also move a mova out of a
+ loop. Since loop does this code motion anyway, maybe we
+ should wrap UNSPEC_MOVA into a CONST, so that reload can
+ move it back. */
+ && ((num_mova > 1
+ && GET_MODE (prev_nonnote_insn (insn)) == VOIDmode)
+ || (prev_nonnote_insn (insn)
+ == XEXP (MOVA_LABELREF (mova), 0))))
+ {
+ rtx scan;
+ int total;
+
+ num_mova--;
+
+ /* Some code might have been inserted between the mova and
+ its ADDR_DIFF_VEC. Check if the mova is still in range. */
+ for (scan = mova, total = 0; scan != insn; scan = NEXT_INSN (scan))
+ total += get_attr_length (scan);
+
+ /* range of mova is 1020, add 4 because pc counts from address of
+ second instruction after this one, subtract 2 in case pc is 2
+ byte aligned. Possible alignment needed for the ADDR_DIFF_VEC
+ cancels out with alignment effects of the mova itself. */
+ if (total > 1022)
+ {
+ /* Change the mova into a load, and restart scanning
+ there. broken_move will then return true for mova. */
+ fixup_mova (mova);
+ insn = mova;
+ }
+ }
+ if (broken_move (insn)
+ || (NONJUMP_INSN_P (insn)
+ && recog_memoized (insn) == CODE_FOR_casesi_worker_2))
+ {
+ rtx scan;
+ /* Scan ahead looking for a barrier to stick the constant table
+ behind. */
+ rtx barrier = find_barrier (num_mova, mova, insn);
+ rtx last_float_move = NULL_RTX, last_float = 0, *last_float_addr = NULL;
+ int need_aligned_label = 0;
+
+ if (num_mova && ! mova_p (mova))
+ {
+ /* find_barrier had to change the first mova into a
+ pcload; thus, we have to start with this new pcload. */
+ insn = mova;
+ num_mova = 0;
+ }
+ /* Now find all the moves between the points and modify them. */
+ for (scan = insn; scan != barrier; scan = NEXT_INSN (scan))
+ {
+ if (LABEL_P (scan))
+ last_float = 0;
+ if (NONJUMP_INSN_P (scan)
+ && recog_memoized (scan) == CODE_FOR_casesi_worker_2)
+ need_aligned_label = 1;
+ if (broken_move (scan))
+ {
+ rtx *patp = &PATTERN (scan), pat = *patp;
+ rtx src, dst;
+ rtx lab;
+ rtx newsrc;
+ enum machine_mode mode;
+
+ if (GET_CODE (pat) == PARALLEL)
+ patp = &XVECEXP (pat, 0, 0), pat = *patp;
+ src = SET_SRC (pat);
+ dst = SET_DEST (pat);
+ mode = GET_MODE (dst);
+
+ if (mode == SImode && hi_const (src)
+ && REGNO (dst) != FPUL_REG)
+ {
+ int offset = 0;
+
+ mode = HImode;
+ while (GET_CODE (dst) == SUBREG)
+ {
+ offset += subreg_regno_offset (REGNO (SUBREG_REG (dst)),
+ GET_MODE (SUBREG_REG (dst)),
+ SUBREG_BYTE (dst),
+ GET_MODE (dst));
+ dst = SUBREG_REG (dst);
+ }
+ dst = gen_rtx_REG (HImode, REGNO (dst) + offset);
+ }
+ if (REG_P (dst) && FP_ANY_REGISTER_P (REGNO (dst)))
+ {
+ /* This must be an insn that clobbers r0. */
+ rtx *clobberp = &XVECEXP (PATTERN (scan), 0,
+ XVECLEN (PATTERN (scan), 0)
+ - 1);
+ rtx clobber = *clobberp;
+
+ gcc_assert (GET_CODE (clobber) == CLOBBER
+ && rtx_equal_p (XEXP (clobber, 0), r0_rtx));
+
+ if (last_float
+ && reg_set_between_p (r0_rtx, last_float_move, scan))
+ last_float = 0;
+ if (last_float
+ && TARGET_SHCOMPACT
+ && GET_MODE_SIZE (mode) != 4
+ && GET_MODE_SIZE (GET_MODE (last_float)) == 4)
+ last_float = 0;
+ lab = add_constant (src, mode, last_float);
+ if (lab)
+ emit_insn_before (gen_mova (lab), scan);
+ else
+ {
+ /* There will be a REG_UNUSED note for r0 on
+ LAST_FLOAT_MOVE; we have to change it to REG_INC,
+ lest reorg:mark_target_live_regs will not
+ consider r0 to be used, and we end up with delay
+ slot insn in front of SCAN that clobbers r0. */
+ rtx note
+ = find_regno_note (last_float_move, REG_UNUSED, 0);
+
+ /* If we are not optimizing, then there may not be
+ a note. */
+ if (note)
+ PUT_REG_NOTE_KIND (note, REG_INC);
+
+ *last_float_addr = r0_inc_rtx;
+ }
+ last_float_move = scan;
+ last_float = src;
+ newsrc = gen_const_mem (mode,
+ (((TARGET_SH4 && ! TARGET_FMOVD)
+ || REGNO (dst) == FPUL_REG)
+ ? r0_inc_rtx
+ : r0_rtx));
+ last_float_addr = &XEXP (newsrc, 0);
+
+ /* Remove the clobber of r0. */
+ *clobberp = gen_rtx_CLOBBER (GET_MODE (clobber),
+ gen_rtx_SCRATCH (Pmode));
+ }
+ /* This is a mova needing a label. Create it. */
+ else if (GET_CODE (src) == UNSPEC
+ && XINT (src, 1) == UNSPEC_MOVA
+ && GET_CODE (XVECEXP (src, 0, 0)) == CONST)
+ {
+ lab = add_constant (XVECEXP (src, 0, 0), mode, 0);
+ newsrc = gen_rtx_LABEL_REF (VOIDmode, lab);
+ newsrc = gen_rtx_UNSPEC (SImode,
+ gen_rtvec (1, newsrc),
+ UNSPEC_MOVA);
+ }
+ else
+ {
+ lab = add_constant (src, mode, 0);
+ newsrc = gen_rtx_LABEL_REF (VOIDmode, lab);
+ newsrc = gen_const_mem (mode, newsrc);
+ }
+ *patp = gen_rtx_SET (VOIDmode, dst, newsrc);
+ INSN_CODE (scan) = -1;
+ }
+ }
+ dump_table (need_aligned_label ? insn : 0, barrier);
+ insn = barrier;
+ }
+ }
+ free_alloc_pool (label_ref_list_pool);
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ PUT_MODE (insn, VOIDmode);
+
+ mdep_reorg_phase = SH_SHORTEN_BRANCHES1;
+ INSN_ADDRESSES_FREE ();
+ split_branches (first);
+
+ /* The INSN_REFERENCES_ARE_DELAYED in sh.h is problematic because it
+ also has an effect on the register that holds the address of the sfunc.
+ Insert an extra dummy insn in front of each sfunc that pretends to
+ use this register. */
+ if (flag_delayed_branch)
+ {
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ {
+ rtx reg = sfunc_uses_reg (insn);
+
+ if (! reg)
+ continue;
+ emit_insn_before (gen_use_sfunc_addr (reg), insn);
+ }
+ }
+#if 0
+ /* fpscr is not actually a user variable, but we pretend it is for the
+ sake of the previous optimization passes, since we want it handled like
+ one. However, we don't have any debugging information for it, so turn
+ it into a non-user variable now. */
+ if (TARGET_SH4)
+ REG_USERVAR_P (get_fpscr_rtx ()) = 0;
+#endif
+ mdep_reorg_phase = SH_AFTER_MDEP_REORG;
+}
+
+int
+get_dest_uid (rtx label, int max_uid)
+{
+ rtx dest = next_real_insn (label);
+ int dest_uid;
+ if (! dest)
+ /* This can happen for an undefined label. */
+ return 0;
+ dest_uid = INSN_UID (dest);
+ /* If this is a newly created branch redirection blocking instruction,
+ we cannot index the branch_uid or insn_addresses arrays with its
+ uid. But then, we won't need to, because the actual destination is
+ the following branch. */
+ while (dest_uid >= max_uid)
+ {
+ dest = NEXT_INSN (dest);
+ dest_uid = INSN_UID (dest);
+ }
+ if (JUMP_P (dest) && GET_CODE (PATTERN (dest)) == RETURN)
+ return 0;
+ return dest_uid;
+}
+
+/* Split condbranches that are out of range. Also add clobbers for
+ scratch registers that are needed in far jumps.
+ We do this before delay slot scheduling, so that it can take our
+ newly created instructions into account. It also allows us to
+ find branches with common targets more easily. */
+
+static void
+split_branches (rtx first)
+{
+ rtx insn;
+ struct far_branch **uid_branch, *far_branch_list = 0;
+ int max_uid = get_max_uid ();
+ int ok;
+
+ /* Find out which branches are out of range. */
+ shorten_branches (first);
+
+ uid_branch = (struct far_branch **) alloca (max_uid * sizeof *uid_branch);
+ memset ((char *) uid_branch, 0, max_uid * sizeof *uid_branch);
+
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ if (! INSN_P (insn))
+ continue;
+ else if (INSN_DELETED_P (insn))
+ {
+ /* Shorten_branches would split this instruction again,
+ so transform it into a note. */
+ SET_INSN_DELETED (insn);
+ }
+ else if (JUMP_P (insn)
+ /* Don't mess with ADDR_DIFF_VEC */
+ && (GET_CODE (PATTERN (insn)) == SET
+ || GET_CODE (PATTERN (insn)) == RETURN))
+ {
+ enum attr_type type = get_attr_type (insn);
+ if (type == TYPE_CBRANCH)
+ {
+ rtx next, beyond;
+
+ if (get_attr_length (insn) > 4)
+ {
+ rtx src = SET_SRC (PATTERN (insn));
+ rtx olabel = XEXP (XEXP (src, 1), 0);
+ int addr = INSN_ADDRESSES (INSN_UID (insn));
+ rtx label = 0;
+ int dest_uid = get_dest_uid (olabel, max_uid);
+ struct far_branch *bp = uid_branch[dest_uid];
+
+ /* redirect_jump needs a valid JUMP_LABEL, and it might delete
+ the label if the LABEL_NUSES count drops to zero. There is
+ always a jump_optimize pass that sets these values, but it
+ proceeds to delete unreferenced code, and then if not
+ optimizing, to un-delete the deleted instructions, thus
+ leaving labels with too low uses counts. */
+ if (! optimize)
+ {
+ JUMP_LABEL (insn) = olabel;
+ LABEL_NUSES (olabel)++;
+ }
+ if (! bp)
+ {
+ bp = (struct far_branch *) alloca (sizeof *bp);
+ uid_branch[dest_uid] = bp;
+ bp->prev = far_branch_list;
+ far_branch_list = bp;
+ bp->far_label
+ = XEXP (XEXP (SET_SRC (PATTERN (insn)), 1), 0);
+ LABEL_NUSES (bp->far_label)++;
+ }
+ else
+ {
+ label = bp->near_label;
+ if (! label && bp->address - addr >= CONDJUMP_MIN)
+ {
+ rtx block = bp->insert_place;
+
+ if (GET_CODE (PATTERN (block)) == RETURN)
+ block = PREV_INSN (block);
+ else
+ block = gen_block_redirect (block,
+ bp->address, 2);
+ label = emit_label_after (gen_label_rtx (),
+ PREV_INSN (block));
+ bp->near_label = label;
+ }
+ else if (label && ! NEXT_INSN (label))
+ {
+ if (addr + 2 - bp->address <= CONDJUMP_MAX)
+ bp->insert_place = insn;
+ else
+ gen_far_branch (bp);
+ }
+ }
+ if (! label
+ || (NEXT_INSN (label) && bp->address - addr < CONDJUMP_MIN))
+ {
+ bp->near_label = label = gen_label_rtx ();
+ bp->insert_place = insn;
+ bp->address = addr;
+ }
+ ok = redirect_jump (insn, label, 0);
+ gcc_assert (ok);
+ }
+ else
+ {
+ /* get_attr_length (insn) == 2 */
+ /* Check if we have a pattern where reorg wants to redirect
+ the branch to a label from an unconditional branch that
+ is too far away. */
+ /* We can't use JUMP_LABEL here because it might be undefined
+ when not optimizing. */
+ /* A syntax error might cause beyond to be NULL_RTX. */
+ beyond
+ = next_active_insn (XEXP (XEXP (SET_SRC (PATTERN (insn)), 1),
+ 0));
+
+ if (beyond
+ && (JUMP_P (beyond)
+ || ((beyond = next_active_insn (beyond))
+ && JUMP_P (beyond)))
+ && GET_CODE (PATTERN (beyond)) == SET
+ && recog_memoized (beyond) == CODE_FOR_jump_compact
+ && ((INSN_ADDRESSES
+ (INSN_UID (XEXP (SET_SRC (PATTERN (beyond)), 0)))
+ - INSN_ADDRESSES (INSN_UID (insn)) + (unsigned) 252)
+ > 252 + 258 + 2))
+ gen_block_redirect (beyond,
+ INSN_ADDRESSES (INSN_UID (beyond)), 1);
+ }
+
+ next = next_active_insn (insn);
+
+ if (next
+ && (JUMP_P (next)
+ || ((next = next_active_insn (next))
+ && JUMP_P (next)))
+ && GET_CODE (PATTERN (next)) == SET
+ && recog_memoized (next) == CODE_FOR_jump_compact
+ && ((INSN_ADDRESSES
+ (INSN_UID (XEXP (SET_SRC (PATTERN (next)), 0)))
+ - INSN_ADDRESSES (INSN_UID (insn)) + (unsigned) 252)
+ > 252 + 258 + 2))
+ gen_block_redirect (next, INSN_ADDRESSES (INSN_UID (next)), 1);
+ }
+ else if (type == TYPE_JUMP || type == TYPE_RETURN)
+ {
+ int addr = INSN_ADDRESSES (INSN_UID (insn));
+ rtx far_label = 0;
+ int dest_uid = 0;
+ struct far_branch *bp;
+
+ if (type == TYPE_JUMP)
+ {
+ far_label = XEXP (SET_SRC (PATTERN (insn)), 0);
+ dest_uid = get_dest_uid (far_label, max_uid);
+ if (! dest_uid)
+ {
+ /* Parse errors can lead to labels outside
+ the insn stream. */
+ if (! NEXT_INSN (far_label))
+ continue;
+
+ if (! optimize)
+ {
+ JUMP_LABEL (insn) = far_label;
+ LABEL_NUSES (far_label)++;
+ }
+ redirect_jump (insn, NULL_RTX, 1);
+ far_label = 0;
+ }
+ }
+ bp = uid_branch[dest_uid];
+ if (! bp)
+ {
+ bp = (struct far_branch *) alloca (sizeof *bp);
+ uid_branch[dest_uid] = bp;
+ bp->prev = far_branch_list;
+ far_branch_list = bp;
+ bp->near_label = 0;
+ bp->far_label = far_label;
+ if (far_label)
+ LABEL_NUSES (far_label)++;
+ }
+ else if (bp->near_label && ! NEXT_INSN (bp->near_label))
+ if (addr - bp->address <= CONDJUMP_MAX)
+ emit_label_after (bp->near_label, PREV_INSN (insn));
+ else
+ {
+ gen_far_branch (bp);
+ bp->near_label = 0;
+ }
+ else
+ bp->near_label = 0;
+ bp->address = addr;
+ bp->insert_place = insn;
+ if (! far_label)
+ emit_insn_before (gen_block_branch_redirect (const0_rtx), insn);
+ else
+ gen_block_redirect (insn, addr, bp->near_label ? 2 : 0);
+ }
+ }
+ /* Generate all pending far branches,
+ and free our references to the far labels. */
+ while (far_branch_list)
+ {
+ if (far_branch_list->near_label
+ && ! NEXT_INSN (far_branch_list->near_label))
+ gen_far_branch (far_branch_list);
+ if (optimize
+ && far_branch_list->far_label
+ && ! --LABEL_NUSES (far_branch_list->far_label))
+ delete_insn (far_branch_list->far_label);
+ far_branch_list = far_branch_list->prev;
+ }
+
+ /* Instruction length information is no longer valid due to the new
+ instructions that have been generated. */
+ init_insn_lengths ();
+}
+
+/* Dump out instruction addresses, which is useful for debugging the
+ constant pool table stuff.
+
+ If relaxing, output the label and pseudo-ops used to link together
+ calls and the instruction which set the registers. */
+
+/* ??? The addresses printed by this routine for insns are nonsense for
+ insns which are inside of a sequence where none of the inner insns have
+ variable length. This is because the second pass of shorten_branches
+ does not bother to update them. */
+
+void
+final_prescan_insn (rtx insn, rtx *opvec ATTRIBUTE_UNUSED,
+ int noperands ATTRIBUTE_UNUSED)
+{
+ if (TARGET_DUMPISIZE)
+ fprintf (asm_out_file, "\n! at %04x\n", INSN_ADDRESSES (INSN_UID (insn)));
+
+ if (TARGET_RELAX)
+ {
+ rtx note;
+
+ note = find_reg_note (insn, REG_LABEL_OPERAND, NULL_RTX);
+ if (note)
+ {
+ rtx pattern;
+
+ pattern = PATTERN (insn);
+ if (GET_CODE (pattern) == PARALLEL)
+ pattern = XVECEXP (pattern, 0, 0);
+ switch (GET_CODE (pattern))
+ {
+ case SET:
+ if (GET_CODE (SET_SRC (pattern)) != CALL
+ && get_attr_type (insn) != TYPE_SFUNC)
+ {
+ targetm.asm_out.internal_label
+ (asm_out_file, "L", CODE_LABEL_NUMBER (XEXP (note, 0)));
+ break;
+ }
+ /* else FALLTHROUGH */
+ case CALL:
+ asm_fprintf (asm_out_file, "\t.uses %LL%d\n",
+ CODE_LABEL_NUMBER (XEXP (note, 0)));
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ }
+ }
+}
+
+/* Dump out any constants accumulated in the final pass. These will
+ only be labels. */
+
+const char *
+output_jump_label_table (void)
+{
+ int i;
+
+ if (pool_size)
+ {
+ fprintf (asm_out_file, "\t.align 2\n");
+ for (i = 0; i < pool_size; i++)
+ {
+ pool_node *p = &pool_vector[i];
+
+ (*targetm.asm_out.internal_label) (asm_out_file, "L",
+ CODE_LABEL_NUMBER (p->label));
+ output_asm_insn (".long %O0", &p->value);
+ }
+ pool_size = 0;
+ }
+
+ return "";
+}
+
+/* A full frame looks like:
+
+ arg-5
+ arg-4
+ [ if current_function_anonymous_args
+ arg-3
+ arg-2
+ arg-1
+ arg-0 ]
+ saved-fp
+ saved-r10
+ saved-r11
+ saved-r12
+ saved-pr
+ local-n
+ ..
+ local-1
+ local-0 <- fp points here. */
+
+/* Number of bytes pushed for anonymous args, used to pass information
+ between expand_prologue and expand_epilogue. */
+
+/* Adjust the stack by SIZE bytes. REG holds the rtl of the register to be
+ adjusted. If epilogue_p is zero, this is for a prologue; otherwise, it's
+ for an epilogue and a negative value means that it's for a sibcall
+ epilogue. If LIVE_REGS_MASK is nonzero, it points to a HARD_REG_SET of
+ all the registers that are about to be restored, and hence dead. */
+
+static void
+output_stack_adjust (int size, rtx reg, int epilogue_p,
+ HARD_REG_SET *live_regs_mask, bool frame_p)
+{
+ rtx (*emit_fn) (rtx) = frame_p ? &frame_insn : &emit_insn;
+ if (size)
+ {
+ HOST_WIDE_INT align = STACK_BOUNDARY / BITS_PER_UNIT;
+
+/* This test is bogus, as output_stack_adjust is used to re-align the
+ stack. */
+#if 0
+ gcc_assert (!(size % align));
+#endif
+
+ if (CONST_OK_FOR_ADD (size))
+ emit_fn (GEN_ADD3 (reg, reg, GEN_INT (size)));
+ /* Try to do it with two partial adjustments; however, we must make
+ sure that the stack is properly aligned at all times, in case
+ an interrupt occurs between the two partial adjustments. */
+ else if (CONST_OK_FOR_ADD (size / 2 & -align)
+ && CONST_OK_FOR_ADD (size - (size / 2 & -align)))
+ {
+ emit_fn (GEN_ADD3 (reg, reg, GEN_INT (size / 2 & -align)));
+ emit_fn (GEN_ADD3 (reg, reg, GEN_INT (size - (size / 2 & -align))));
+ }
+ else
+ {
+ rtx const_reg;
+ rtx insn;
+ int temp = epilogue_p ? 7 : (TARGET_SH5 ? 0 : 1);
+ int i;
+
+ /* If TEMP is invalid, we could temporarily save a general
+ register to MACL. However, there is currently no need
+ to handle this case, so just die when we see it. */
+ if (epilogue_p < 0
+ || current_function_interrupt
+ || ! call_really_used_regs[temp] || fixed_regs[temp])
+ temp = -1;
+ if (temp < 0 && ! current_function_interrupt
+ && (TARGET_SHMEDIA || epilogue_p >= 0))
+ {
+ HARD_REG_SET temps;
+ COPY_HARD_REG_SET (temps, call_used_reg_set);
+ AND_COMPL_HARD_REG_SET (temps, call_fixed_reg_set);
+ if (epilogue_p > 0)
+ {
+ int nreg = 0;
+ if (crtl->return_rtx)
+ {
+ enum machine_mode mode;
+ mode = GET_MODE (crtl->return_rtx);
+ if (BASE_RETURN_VALUE_REG (mode) == FIRST_RET_REG)
+ nreg = HARD_REGNO_NREGS (FIRST_RET_REG, mode);
+ }
+ for (i = 0; i < nreg; i++)
+ CLEAR_HARD_REG_BIT (temps, FIRST_RET_REG + i);
+ if (crtl->calls_eh_return)
+ {
+ CLEAR_HARD_REG_BIT (temps, EH_RETURN_STACKADJ_REGNO);
+ for (i = 0; i <= 3; i++)
+ CLEAR_HARD_REG_BIT (temps, EH_RETURN_DATA_REGNO (i));
+ }
+ }
+ if (TARGET_SHMEDIA && epilogue_p < 0)
+ for (i = FIRST_TARGET_REG; i <= LAST_TARGET_REG; i++)
+ CLEAR_HARD_REG_BIT (temps, i);
+ if (epilogue_p <= 0)
+ {
+ for (i = FIRST_PARM_REG;
+ i < FIRST_PARM_REG + NPARM_REGS (SImode); i++)
+ CLEAR_HARD_REG_BIT (temps, i);
+ if (cfun->static_chain_decl != NULL)
+ CLEAR_HARD_REG_BIT (temps, STATIC_CHAIN_REGNUM);
+ }
+ temp = scavenge_reg (&temps);
+ }
+ if (temp < 0 && live_regs_mask)
+ {
+ HARD_REG_SET temps;
+
+ COPY_HARD_REG_SET (temps, *live_regs_mask);
+ CLEAR_HARD_REG_BIT (temps, REGNO (reg));
+ temp = scavenge_reg (&temps);
+ }
+ if (temp < 0)
+ {
+ rtx adj_reg, tmp_reg, mem;
+
+ /* If we reached here, the most likely case is the (sibcall)
+ epilogue for non SHmedia. Put a special push/pop sequence
+ for such case as the last resort. This looks lengthy but
+ would not be problem because it seems to be very
+ rare. */
+
+ gcc_assert (!TARGET_SHMEDIA && epilogue_p);
+
+
+ /* ??? There is still the slight possibility that r4 or
+ r5 have been reserved as fixed registers or assigned
+ as global registers, and they change during an
+ interrupt. There are possible ways to handle this:
+
+ - If we are adjusting the frame pointer (r14), we can do
+ with a single temp register and an ordinary push / pop
+ on the stack.
+ - Grab any call-used or call-saved registers (i.e. not
+ fixed or globals) for the temps we need. We might
+ also grab r14 if we are adjusting the stack pointer.
+ If we can't find enough available registers, issue
+ a diagnostic and die - the user must have reserved
+ way too many registers.
+ But since all this is rather unlikely to happen and
+ would require extra testing, we just die if r4 / r5
+ are not available. */
+ gcc_assert (!fixed_regs[4] && !fixed_regs[5]
+ && !global_regs[4] && !global_regs[5]);
+
+ adj_reg = gen_rtx_REG (GET_MODE (reg), 4);
+ tmp_reg = gen_rtx_REG (GET_MODE (reg), 5);
+ emit_move_insn (gen_tmp_stack_mem (Pmode, reg), adj_reg);
+ emit_insn (GEN_MOV (adj_reg, GEN_INT (size)));
+ emit_insn (GEN_ADD3 (adj_reg, adj_reg, reg));
+ mem = gen_tmp_stack_mem (Pmode, gen_rtx_PRE_DEC (Pmode, adj_reg));
+ emit_move_insn (mem, tmp_reg);
+ emit_move_insn (tmp_reg, gen_tmp_stack_mem (Pmode, reg));
+ mem = gen_tmp_stack_mem (Pmode, gen_rtx_PRE_DEC (Pmode, adj_reg));
+ emit_move_insn (mem, tmp_reg);
+ emit_move_insn (reg, adj_reg);
+ mem = gen_tmp_stack_mem (Pmode, gen_rtx_POST_INC (Pmode, reg));
+ emit_move_insn (adj_reg, mem);
+ mem = gen_tmp_stack_mem (Pmode, gen_rtx_POST_INC (Pmode, reg));
+ emit_move_insn (tmp_reg, mem);
+ /* Tell flow the insns that pop r4/r5 aren't dead. */
+ emit_use (tmp_reg);
+ emit_use (adj_reg);
+ return;
+ }
+ const_reg = gen_rtx_REG (GET_MODE (reg), temp);
+
+ /* If SIZE is negative, subtract the positive value.
+ This sometimes allows a constant pool entry to be shared
+ between prologue and epilogue code. */
+ if (size < 0)
+ {
+ emit_insn (GEN_MOV (const_reg, GEN_INT (-size)));
+ insn = emit_fn (GEN_SUB3 (reg, reg, const_reg));
+ }
+ else
+ {
+ emit_insn (GEN_MOV (const_reg, GEN_INT (size)));
+ insn = emit_fn (GEN_ADD3 (reg, reg, const_reg));
+ }
+ if (! epilogue_p)
+ add_reg_note (insn, REG_FRAME_RELATED_EXPR,
+ gen_rtx_SET (VOIDmode, reg,
+ gen_rtx_PLUS (SImode, reg,
+ GEN_INT (size))));
+ }
+ }
+}
+
+static rtx
+frame_insn (rtx x)
+{
+ x = emit_insn (x);
+ RTX_FRAME_RELATED_P (x) = 1;
+ return x;
+}
+
+/* Output RTL to push register RN onto the stack. */
+
+static rtx
+push (int rn)
+{
+ rtx x;
+ if (rn == FPUL_REG)
+ x = gen_push_fpul ();
+ else if (rn == FPSCR_REG)
+ x = gen_push_fpscr ();
+ else if ((TARGET_SH4 || TARGET_SH2A_DOUBLE) && TARGET_FMOVD && ! TARGET_FPU_SINGLE
+ && FP_OR_XD_REGISTER_P (rn))
+ {
+ if (FP_REGISTER_P (rn) && (rn - FIRST_FP_REG) & 1)
+ return NULL_RTX;
+ x = gen_push_4 (gen_rtx_REG (DFmode, rn));
+ }
+ else if (TARGET_SH2E && FP_REGISTER_P (rn))
+ x = gen_push_e (gen_rtx_REG (SFmode, rn));
+ else
+ x = gen_push (gen_rtx_REG (SImode, rn));
+
+ x = frame_insn (x);
+ add_reg_note (x, REG_INC, gen_rtx_REG (SImode, STACK_POINTER_REGNUM));
+ return x;
+}
+
+/* Output RTL to pop register RN from the stack. */
+
+static void
+pop (int rn)
+{
+ rtx x;
+ if (rn == FPUL_REG)
+ x = gen_pop_fpul ();
+ else if (rn == FPSCR_REG)
+ x = gen_pop_fpscr ();
+ else if ((TARGET_SH4 || TARGET_SH2A_DOUBLE) && TARGET_FMOVD && ! TARGET_FPU_SINGLE
+ && FP_OR_XD_REGISTER_P (rn))
+ {
+ if (FP_REGISTER_P (rn) && (rn - FIRST_FP_REG) & 1)
+ return;
+ x = gen_pop_4 (gen_rtx_REG (DFmode, rn));
+ }
+ else if (TARGET_SH2E && FP_REGISTER_P (rn))
+ x = gen_pop_e (gen_rtx_REG (SFmode, rn));
+ else
+ x = gen_pop (gen_rtx_REG (SImode, rn));
+
+ x = emit_insn (x);
+ add_reg_note (x, REG_INC, gen_rtx_REG (SImode, STACK_POINTER_REGNUM));
+}
+
+/* Generate code to push the regs specified in the mask. */
+
+static void
+push_regs (HARD_REG_SET *mask, int interrupt_handler)
+{
+ int i = interrupt_handler ? LAST_BANKED_REG + 1 : 0;
+ int skip_fpscr = 0;
+
+ /* Push PR last; this gives better latencies after the prologue, and
+ candidates for the return delay slot when there are no general
+ registers pushed. */
+ for (; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ /* If this is an interrupt handler, and the SZ bit varies,
+ and we have to push any floating point register, we need
+ to switch to the correct precision first. */
+ if (i == FIRST_FP_REG && interrupt_handler && TARGET_FMOVD
+ && hard_reg_set_intersect_p (*mask, reg_class_contents[DF_REGS]))
+ {
+ HARD_REG_SET unsaved;
+
+ push (FPSCR_REG);
+ COMPL_HARD_REG_SET (unsaved, *mask);
+ fpscr_set_from_mem (NORMAL_MODE (FP_MODE), unsaved);
+ skip_fpscr = 1;
+ }
+ if (i != PR_REG
+ && (i != FPSCR_REG || ! skip_fpscr)
+ && TEST_HARD_REG_BIT (*mask, i))
+ {
+ /* If the ISR has RESBANK attribute assigned, don't push any of
+ the following registers - R0-R14, MACH, MACL and GBR. */
+ if (! (sh_cfun_resbank_handler_p ()
+ && ((i >= FIRST_GENERAL_REG && i < LAST_GENERAL_REG)
+ || i == MACH_REG
+ || i == MACL_REG
+ || i == GBR_REG)))
+ push (i);
+ }
+ }
+
+ /* Push banked registers last to improve delay slot opportunities. */
+ if (interrupt_handler)
+ {
+ bool use_movml = false;
+
+ if (TARGET_SH2A)
+ {
+ unsigned int count = 0;
+
+ for (i = FIRST_BANKED_REG; i <= LAST_BANKED_REG; i++)
+ if (TEST_HARD_REG_BIT (*mask, i))
+ count++;
+ else
+ break;
+
+ /* Use movml when all banked registers are pushed. */
+ if (count == LAST_BANKED_REG - FIRST_BANKED_REG + 1)
+ use_movml = true;
+ }
+
+ if (use_movml)
+ {
+ rtx x, mem, reg, set;
+ rtx sp_reg = gen_rtx_REG (SImode, STACK_POINTER_REGNUM);
+
+ /* We must avoid scheduling multiple store insn with another
+ insns. */
+ emit_insn (gen_blockage ());
+ x = gen_movml_push_banked (sp_reg);
+ x = frame_insn (x);
+ for (i = FIRST_BANKED_REG; i <= LAST_BANKED_REG; i++)
+ {
+ mem = gen_rtx_MEM (SImode, plus_constant (sp_reg, i * 4));
+ reg = gen_rtx_REG (SImode, i);
+ add_reg_note (x, REG_CFA_OFFSET, gen_rtx_SET (SImode, mem, reg));
+ }
+
+ set = gen_rtx_SET (SImode, sp_reg, plus_constant (sp_reg, - 32));
+ add_reg_note (x, REG_CFA_ADJUST_CFA, set);
+ emit_insn (gen_blockage ());
+ }
+ else
+ for (i = FIRST_BANKED_REG; i <= LAST_BANKED_REG; i++)
+ if (TEST_HARD_REG_BIT (*mask, i))
+ push (i);
+ }
+
+ /* Don't push PR register for an ISR with RESBANK attribute assigned. */
+ if (TEST_HARD_REG_BIT (*mask, PR_REG) && !sh_cfun_resbank_handler_p ())
+ push (PR_REG);
+}
+
+/* Calculate how much extra space is needed to save all callee-saved
+ target registers.
+ LIVE_REGS_MASK is the register mask calculated by calc_live_regs. */
+
+static int
+shmedia_target_regs_stack_space (HARD_REG_SET *live_regs_mask)
+{
+ int reg;
+ int stack_space = 0;
+ int interrupt_handler = sh_cfun_interrupt_handler_p ();
+
+ for (reg = LAST_TARGET_REG; reg >= FIRST_TARGET_REG; reg--)
+ if ((! call_really_used_regs[reg] || interrupt_handler)
+ && ! TEST_HARD_REG_BIT (*live_regs_mask, reg))
+ /* Leave space to save this target register on the stack,
+ in case target register allocation wants to use it. */
+ stack_space += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg));
+ return stack_space;
+}
+
+/* Decide whether we should reserve space for callee-save target registers,
+ in case target register allocation wants to use them. REGS_SAVED is
+ the space, in bytes, that is already required for register saves.
+ LIVE_REGS_MASK is the register mask calculated by calc_live_regs. */
+
+static int
+shmedia_reserve_space_for_target_registers_p (int regs_saved,
+ HARD_REG_SET *live_regs_mask)
+{
+ if (optimize_size)
+ return 0;
+ return shmedia_target_regs_stack_space (live_regs_mask) <= regs_saved;
+}
+
+/* Decide how much space to reserve for callee-save target registers
+ in case target register allocation wants to use them.
+ LIVE_REGS_MASK is the register mask calculated by calc_live_regs. */
+
+static int
+shmedia_target_regs_stack_adjust (HARD_REG_SET *live_regs_mask)
+{
+ if (shmedia_space_reserved_for_target_registers)
+ return shmedia_target_regs_stack_space (live_regs_mask);
+ else
+ return 0;
+}
+
+/* Work out the registers which need to be saved, both as a mask and a
+ count of saved words. Return the count.
+
+ If doing a pragma interrupt function, then push all regs used by the
+ function, and if we call another function (we can tell by looking at PR),
+ make sure that all the regs it clobbers are safe too. */
+
+static int
+calc_live_regs (HARD_REG_SET *live_regs_mask)
+{
+ unsigned int reg;
+ int count;
+ tree attrs;
+ bool interrupt_or_trapa_handler, trapa_handler, interrupt_handler;
+ bool nosave_low_regs;
+ int pr_live, has_call;
+
+ attrs = DECL_ATTRIBUTES (current_function_decl);
+ interrupt_or_trapa_handler = sh_cfun_interrupt_handler_p ();
+ trapa_handler = lookup_attribute ("trapa_handler", attrs) != NULL_TREE;
+ interrupt_handler = interrupt_or_trapa_handler && ! trapa_handler;
+ nosave_low_regs = lookup_attribute ("nosave_low_regs", attrs) != NULL_TREE;
+
+ CLEAR_HARD_REG_SET (*live_regs_mask);
+ if ((TARGET_SH4 || TARGET_SH2A_DOUBLE) && TARGET_FMOVD && interrupt_handler
+ && df_regs_ever_live_p (FPSCR_REG))
+ target_flags &= ~MASK_FPU_SINGLE;
+ /* If we can save a lot of saves by switching to double mode, do that. */
+ else if ((TARGET_SH4 || TARGET_SH2A_DOUBLE) && TARGET_FMOVD && TARGET_FPU_SINGLE)
+ for (count = 0, reg = FIRST_FP_REG; reg <= LAST_FP_REG; reg += 2)
+ if (df_regs_ever_live_p (reg) && df_regs_ever_live_p (reg+1)
+ && (! call_really_used_regs[reg]
+ || interrupt_handler)
+ && ++count > 2)
+ {
+ target_flags &= ~MASK_FPU_SINGLE;
+ break;
+ }
+ /* PR_MEDIA_REG is a general purpose register, thus global_alloc already
+ knows how to use it. That means the pseudo originally allocated for
+ the initial value can become the PR_MEDIA_REG hard register, as seen for
+ execute/20010122-1.c:test9. */
+ if (TARGET_SHMEDIA)
+ /* ??? this function is called from initial_elimination_offset, hence we
+ can't use the result of sh_media_register_for_return here. */
+ pr_live = sh_pr_n_sets ();
+ else
+ {
+ rtx pr_initial = has_hard_reg_initial_val (Pmode, PR_REG);
+ pr_live = (pr_initial
+ ? (!REG_P (pr_initial)
+ || REGNO (pr_initial) != (PR_REG))
+ : df_regs_ever_live_p (PR_REG));
+ /* For Shcompact, if not optimizing, we end up with a memory reference
+ using the return address pointer for __builtin_return_address even
+ though there is no actual need to put the PR register on the stack. */
+ pr_live |= df_regs_ever_live_p (RETURN_ADDRESS_POINTER_REGNUM);
+ }
+ /* Force PR to be live if the prologue has to call the SHmedia
+ argument decoder or register saver. */
+ if (TARGET_SHCOMPACT
+ && ((crtl->args.info.call_cookie
+ & ~ CALL_COOKIE_RET_TRAMP (1))
+ || crtl->saves_all_registers))
+ pr_live = 1;
+ has_call = TARGET_SHMEDIA ? ! leaf_function_p () : pr_live;
+ for (count = 0, reg = FIRST_PSEUDO_REGISTER; reg-- != 0; )
+ {
+ if (reg == (TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG)
+ ? pr_live
+ : interrupt_handler
+ ? (/* Need to save all the regs ever live. */
+ (df_regs_ever_live_p (reg)
+ || (call_really_used_regs[reg]
+ && (! fixed_regs[reg] || reg == MACH_REG || reg == MACL_REG
+ || reg == PIC_OFFSET_TABLE_REGNUM)
+ && has_call)
+ || (TARGET_SHMEDIA && has_call
+ && REGISTER_NATURAL_MODE (reg) == SImode
+ && (GENERAL_REGISTER_P (reg) || TARGET_REGISTER_P (reg))))
+ && reg != STACK_POINTER_REGNUM && reg != ARG_POINTER_REGNUM
+ && reg != RETURN_ADDRESS_POINTER_REGNUM
+ && reg != T_REG && reg != GBR_REG
+ /* Push fpscr only on targets which have FPU */
+ && (reg != FPSCR_REG || TARGET_FPU_ANY))
+ : (/* Only push those regs which are used and need to be saved. */
+ (TARGET_SHCOMPACT
+ && flag_pic
+ && crtl->args.info.call_cookie
+ && reg == PIC_OFFSET_TABLE_REGNUM)
+ || (df_regs_ever_live_p (reg)
+ && ((!call_really_used_regs[reg]
+ && !(reg != PIC_OFFSET_TABLE_REGNUM
+ && fixed_regs[reg] && call_used_regs[reg]))
+ || (trapa_handler && reg == FPSCR_REG && TARGET_FPU_ANY)))
+ || (crtl->calls_eh_return
+ && (reg == EH_RETURN_DATA_REGNO (0)
+ || reg == EH_RETURN_DATA_REGNO (1)
+ || reg == EH_RETURN_DATA_REGNO (2)
+ || reg == EH_RETURN_DATA_REGNO (3)))
+ || ((reg == MACL_REG || reg == MACH_REG)
+ && df_regs_ever_live_p (reg)
+ && sh_cfun_attr_renesas_p ())
+ ))
+ {
+ SET_HARD_REG_BIT (*live_regs_mask, reg);
+ count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg));
+
+ if ((TARGET_SH4 || TARGET_SH2A_DOUBLE || TARGET_SH5) && TARGET_FMOVD
+ && GET_MODE_CLASS (REGISTER_NATURAL_MODE (reg)) == MODE_FLOAT)
+ {
+ if (FP_REGISTER_P (reg))
+ {
+ if (! TARGET_FPU_SINGLE && ! df_regs_ever_live_p (reg ^ 1))
+ {
+ SET_HARD_REG_BIT (*live_regs_mask, (reg ^ 1));
+ count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg ^ 1));
+ }
+ }
+ else if (XD_REGISTER_P (reg))
+ {
+ /* Must switch to double mode to access these registers. */
+ target_flags &= ~MASK_FPU_SINGLE;
+ }
+ }
+ }
+ if (nosave_low_regs && reg == R8_REG)
+ break;
+ }
+ /* If we have a target register optimization pass after prologue / epilogue
+ threading, we need to assume all target registers will be live even if
+ they aren't now. */
+ if (flag_branch_target_load_optimize2
+ && TARGET_SAVE_ALL_TARGET_REGS
+ && shmedia_space_reserved_for_target_registers)
+ for (reg = LAST_TARGET_REG; reg >= FIRST_TARGET_REG; reg--)
+ if ((! call_really_used_regs[reg] || interrupt_handler)
+ && ! TEST_HARD_REG_BIT (*live_regs_mask, reg))
+ {
+ SET_HARD_REG_BIT (*live_regs_mask, reg);
+ count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg));
+ }
+ /* If this is an interrupt handler, we don't have any call-clobbered
+ registers we can conveniently use for target register save/restore.
+ Make sure we save at least one general purpose register when we need
+ to save target registers. */
+ if (interrupt_handler
+ && hard_reg_set_intersect_p (*live_regs_mask,
+ reg_class_contents[TARGET_REGS])
+ && ! hard_reg_set_intersect_p (*live_regs_mask,
+ reg_class_contents[GENERAL_REGS]))
+ {
+ SET_HARD_REG_BIT (*live_regs_mask, R0_REG);
+ count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (R0_REG));
+ }
+
+ return count;
+}
+
+/* Code to generate prologue and epilogue sequences */
+
+/* PUSHED is the number of bytes that are being pushed on the
+ stack for register saves. Return the frame size, padded
+ appropriately so that the stack stays properly aligned. */
+static HOST_WIDE_INT
+rounded_frame_size (int pushed)
+{
+ HOST_WIDE_INT size = get_frame_size ();
+ HOST_WIDE_INT align = STACK_BOUNDARY / BITS_PER_UNIT;
+
+ if (ACCUMULATE_OUTGOING_ARGS)
+ size += crtl->outgoing_args_size;
+
+ return ((size + pushed + align - 1) & -align) - pushed;
+}
+
+/* Choose a call-clobbered target-branch register that remains
+ unchanged along the whole function. We set it up as the return
+ value in the prologue. */
+int
+sh_media_register_for_return (void)
+{
+ int regno;
+ int tr0_used;
+
+ if (! current_function_is_leaf)
+ return -1;
+ if (lookup_attribute ("interrupt_handler",
+ DECL_ATTRIBUTES (current_function_decl)))
+ return -1;
+ if (sh_cfun_interrupt_handler_p ())
+ return -1;
+
+ tr0_used = flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM);
+
+ for (regno = FIRST_TARGET_REG + tr0_used; regno <= LAST_TARGET_REG; regno++)
+ if (call_really_used_regs[regno] && ! df_regs_ever_live_p (regno))
+ return regno;
+
+ return -1;
+}
+
+/* The maximum registers we need to save are:
+ - 62 general purpose registers (r15 is stack pointer, r63 is zero)
+ - 32 floating point registers (for each pair, we save none,
+ one single precision value, or a double precision value).
+ - 8 target registers
+ - add 1 entry for a delimiter. */
+#define MAX_SAVED_REGS (62+32+8)
+
+typedef struct save_entry_s
+{
+ unsigned char reg;
+ unsigned char mode;
+ short offset;
+} save_entry;
+
+#define MAX_TEMPS 4
+
+/* There will be a delimiter entry with VOIDmode both at the start and the
+ end of a filled in schedule. The end delimiter has the offset of the
+ save with the smallest (i.e. most negative) offset. */
+typedef struct save_schedule_s
+{
+ save_entry entries[MAX_SAVED_REGS + 2];
+ int temps[MAX_TEMPS+1];
+} save_schedule;
+
+/* Fill in SCHEDULE according to LIVE_REGS_MASK. If RESTORE is nonzero,
+ use reverse order. Returns the last entry written to (not counting
+ the delimiter). OFFSET_BASE is a number to be added to all offset
+ entries. */
+
+static save_entry *
+sh5_schedule_saves (HARD_REG_SET *live_regs_mask, save_schedule *schedule,
+ int offset_base)
+{
+ int align, i;
+ save_entry *entry = schedule->entries;
+ int tmpx = 0;
+ int offset;
+
+ if (! current_function_interrupt)
+ for (i = FIRST_GENERAL_REG; tmpx < MAX_TEMPS && i <= LAST_GENERAL_REG; i++)
+ if (call_really_used_regs[i] && ! fixed_regs[i] && i != PR_MEDIA_REG
+ && ! FUNCTION_ARG_REGNO_P (i)
+ && i != FIRST_RET_REG
+ && ! (cfun->static_chain_decl != NULL && i == STATIC_CHAIN_REGNUM)
+ && ! (crtl->calls_eh_return
+ && (i == EH_RETURN_STACKADJ_REGNO
+ || ((unsigned) i >= EH_RETURN_DATA_REGNO (0)
+ && (unsigned) i <= EH_RETURN_DATA_REGNO (3)))))
+ schedule->temps[tmpx++] = i;
+ entry->reg = -1;
+ entry->mode = VOIDmode;
+ entry->offset = offset_base;
+ entry++;
+ /* We loop twice: first, we save 8-byte aligned registers in the
+ higher addresses, that are known to be aligned. Then, we
+ proceed to saving 32-bit registers that don't need 8-byte
+ alignment.
+ If this is an interrupt function, all registers that need saving
+ need to be saved in full. moreover, we need to postpone saving
+ target registers till we have saved some general purpose registers
+ we can then use as scratch registers. */
+ offset = offset_base;
+ for (align = 1; align >= 0; align--)
+ {
+ for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--)
+ if (TEST_HARD_REG_BIT (*live_regs_mask, i))
+ {
+ enum machine_mode mode = REGISTER_NATURAL_MODE (i);
+ int reg = i;
+
+ if (current_function_interrupt)
+ {
+ if (TARGET_REGISTER_P (i))
+ continue;
+ if (GENERAL_REGISTER_P (i))
+ mode = DImode;
+ }
+ if (mode == SFmode && (i % 2) == 1
+ && ! TARGET_FPU_SINGLE && FP_REGISTER_P (i)
+ && (TEST_HARD_REG_BIT (*live_regs_mask, (i ^ 1))))
+ {
+ mode = DFmode;
+ i--;
+ reg--;
+ }
+
+ /* If we're doing the aligned pass and this is not aligned,
+ or we're doing the unaligned pass and this is aligned,
+ skip it. */
+ if ((GET_MODE_SIZE (mode) % (STACK_BOUNDARY / BITS_PER_UNIT) == 0)
+ != align)
+ continue;
+
+ if (current_function_interrupt
+ && GENERAL_REGISTER_P (i)
+ && tmpx < MAX_TEMPS)
+ schedule->temps[tmpx++] = i;
+
+ offset -= GET_MODE_SIZE (mode);
+ entry->reg = i;
+ entry->mode = mode;
+ entry->offset = offset;
+ entry++;
+ }
+ if (align && current_function_interrupt)
+ for (i = LAST_TARGET_REG; i >= FIRST_TARGET_REG; i--)
+ if (TEST_HARD_REG_BIT (*live_regs_mask, i))
+ {
+ offset -= GET_MODE_SIZE (DImode);
+ entry->reg = i;
+ entry->mode = DImode;
+ entry->offset = offset;
+ entry++;
+ }
+ }
+ entry->reg = -1;
+ entry->mode = VOIDmode;
+ entry->offset = offset;
+ schedule->temps[tmpx] = -1;
+ return entry - 1;
+}
+
+void
+sh_expand_prologue (void)
+{
+ HARD_REG_SET live_regs_mask;
+ int d, i;
+ int d_rounding = 0;
+ int save_flags = target_flags;
+ int pretend_args;
+ int stack_usage;
+ tree sp_switch_attr
+ = lookup_attribute ("sp_switch", DECL_ATTRIBUTES (current_function_decl));
+
+ current_function_interrupt = sh_cfun_interrupt_handler_p ();
+
+ /* We have pretend args if we had an object sent partially in registers
+ and partially on the stack, e.g. a large structure. */
+ pretend_args = crtl->args.pretend_args_size;
+ if (TARGET_VARARGS_PRETEND_ARGS (current_function_decl)
+ && (NPARM_REGS(SImode)
+ > crtl->args.info.arg_count[(int) SH_ARG_INT]))
+ pretend_args = 0;
+
+ output_stack_adjust (-pretend_args
+ - crtl->args.info.stack_regs * 8,
+ stack_pointer_rtx, 0, NULL, true);
+ stack_usage = pretend_args + crtl->args.info.stack_regs * 8;
+
+ if (TARGET_SHCOMPACT && flag_pic && crtl->args.info.call_cookie)
+ /* We're going to use the PIC register to load the address of the
+ incoming-argument decoder and/or of the return trampoline from
+ the GOT, so make sure the PIC register is preserved and
+ initialized. */
+ df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
+
+ if (TARGET_SHCOMPACT
+ && (crtl->args.info.call_cookie & ~ CALL_COOKIE_RET_TRAMP(1)))
+ {
+ int reg;
+
+ /* First, make all registers with incoming arguments that will
+ be pushed onto the stack live, so that register renaming
+ doesn't overwrite them. */
+ for (reg = 0; reg < NPARM_REGS (SImode); reg++)
+ if (CALL_COOKIE_STACKSEQ_GET (crtl->args.info.call_cookie)
+ >= NPARM_REGS (SImode) - reg)
+ for (; reg < NPARM_REGS (SImode); reg++)
+ emit_insn (gen_shcompact_preserve_incoming_args
+ (gen_rtx_REG (SImode, FIRST_PARM_REG + reg)));
+ else if (CALL_COOKIE_INT_REG_GET
+ (crtl->args.info.call_cookie, reg) == 1)
+ emit_insn (gen_shcompact_preserve_incoming_args
+ (gen_rtx_REG (SImode, FIRST_PARM_REG + reg)));
+
+ emit_move_insn (gen_rtx_REG (Pmode, MACL_REG),
+ stack_pointer_rtx);
+ emit_move_insn (gen_rtx_REG (SImode, R0_REG),
+ GEN_INT (crtl->args.info.call_cookie));
+ emit_move_insn (gen_rtx_REG (SImode, MACH_REG),
+ gen_rtx_REG (SImode, R0_REG));
+ }
+ else if (TARGET_SHMEDIA)
+ {
+ int tr = sh_media_register_for_return ();
+
+ if (tr >= 0)
+ emit_move_insn (gen_rtx_REG (DImode, tr),
+ gen_rtx_REG (DImode, PR_MEDIA_REG));
+ }
+
+ /* Emit the code for SETUP_VARARGS. */
+ if (cfun->stdarg)
+ {
+ if (TARGET_VARARGS_PRETEND_ARGS (current_function_decl))
+ {
+ /* Push arg regs as if they'd been provided by caller in stack. */
+ for (i = 0; i < NPARM_REGS(SImode); i++)
+ {
+ int rn = NPARM_REGS(SImode) + FIRST_PARM_REG - i - 1;
+
+ if (i >= (NPARM_REGS(SImode)
+ - crtl->args.info.arg_count[(int) SH_ARG_INT]
+ ))
+ break;
+ push (rn);
+ stack_usage += GET_MODE_SIZE (SImode);
+ }
+ }
+ }
+
+ /* If we're supposed to switch stacks at function entry, do so now. */
+ if (sp_switch_attr)
+ {
+ rtx lab, newsrc;
+ /* The argument specifies a variable holding the address of the
+ stack the interrupt function should switch to/from at entry/exit. */
+ tree arg = TREE_VALUE ( TREE_VALUE (sp_switch_attr));
+ const char *s
+ = ggc_strdup (TREE_STRING_POINTER (arg));
+ rtx sp_switch = gen_rtx_SYMBOL_REF (Pmode, s);
+
+ lab = add_constant (sp_switch, SImode, 0);
+ newsrc = gen_rtx_LABEL_REF (VOIDmode, lab);
+ newsrc = gen_const_mem (SImode, newsrc);
+
+ emit_insn (gen_sp_switch_1 (newsrc));
+ }
+
+ d = calc_live_regs (&live_regs_mask);
+ /* ??? Maybe we could save some switching if we can move a mode switch
+ that already happens to be at the function start into the prologue. */
+ if (target_flags != save_flags && ! current_function_interrupt)
+ emit_insn (gen_toggle_sz ());
+
+ if (TARGET_SH5)
+ {
+ int offset_base, offset;
+ rtx r0 = NULL_RTX;
+ int offset_in_r0 = -1;
+ int sp_in_r0 = 0;
+ int tregs_space = shmedia_target_regs_stack_adjust (&live_regs_mask);
+ int total_size, save_size;
+ save_schedule schedule;
+ save_entry *entry;
+ int *tmp_pnt;
+
+ if (call_really_used_regs[R0_REG] && ! fixed_regs[R0_REG]
+ && ! current_function_interrupt)
+ r0 = gen_rtx_REG (Pmode, R0_REG);
+
+ /* D is the actual number of bytes that we need for saving registers,
+ however, in initial_elimination_offset we have committed to using
+ an additional TREGS_SPACE amount of bytes - in order to keep both
+ addresses to arguments supplied by the caller and local variables
+ valid, we must keep this gap. Place it between the incoming
+ arguments and the actually saved registers in a bid to optimize
+ locality of reference. */
+ total_size = d + tregs_space;
+ total_size += rounded_frame_size (total_size);
+ save_size = total_size - rounded_frame_size (d);
+ if (save_size % (STACK_BOUNDARY / BITS_PER_UNIT))
+ d_rounding = ((STACK_BOUNDARY / BITS_PER_UNIT)
+ - save_size % (STACK_BOUNDARY / BITS_PER_UNIT));
+
+ /* If adjusting the stack in a single step costs nothing extra, do so.
+ I.e. either if a single addi is enough, or we need a movi anyway,
+ and we don't exceed the maximum offset range (the test for the
+ latter is conservative for simplicity). */
+ if (TARGET_SHMEDIA
+ && (CONST_OK_FOR_I10 (-total_size)
+ || (! CONST_OK_FOR_I10 (-(save_size + d_rounding))
+ && total_size <= 2044)))
+ d_rounding = total_size - save_size;
+
+ offset_base = d + d_rounding;
+
+ output_stack_adjust (-(save_size + d_rounding), stack_pointer_rtx,
+ 0, NULL, true);
+ stack_usage += save_size + d_rounding;
+
+ sh5_schedule_saves (&live_regs_mask, &schedule, offset_base);
+ tmp_pnt = schedule.temps;
+ for (entry = &schedule.entries[1]; entry->mode != VOIDmode; entry++)
+ {
+ enum machine_mode mode = (enum machine_mode) entry->mode;
+ unsigned int reg = entry->reg;
+ rtx reg_rtx, mem_rtx, pre_dec = NULL_RTX;
+ rtx orig_reg_rtx;
+
+ offset = entry->offset;
+
+ reg_rtx = gen_rtx_REG (mode, reg);
+
+ mem_rtx = gen_frame_mem (mode,
+ gen_rtx_PLUS (Pmode,
+ stack_pointer_rtx,
+ GEN_INT (offset)));
+
+ if (!memory_address_p (mode, XEXP (mem_rtx, 0)))
+ {
+ gcc_assert (r0);
+ mem_rtx = NULL_RTX;
+ }
+
+ if (HAVE_PRE_DECREMENT
+ && (offset_in_r0 - offset == GET_MODE_SIZE (mode)
+ || mem_rtx == NULL_RTX
+ || reg == PR_REG || SPECIAL_REGISTER_P (reg)))
+ {
+ pre_dec = gen_frame_mem (mode, gen_rtx_PRE_DEC (Pmode, r0));
+
+ if (!memory_address_p (mode, XEXP (pre_dec, 0)))
+ pre_dec = NULL_RTX;
+ else
+ {
+ mem_rtx = NULL_RTX;
+ offset += GET_MODE_SIZE (mode);
+ }
+ }
+
+ if (mem_rtx != NULL_RTX)
+ goto addr_ok;
+
+ if (offset_in_r0 == -1)
+ {
+ emit_move_insn (r0, GEN_INT (offset));
+ offset_in_r0 = offset;
+ }
+ else if (offset != offset_in_r0)
+ {
+ emit_move_insn (r0,
+ gen_rtx_PLUS
+ (Pmode, r0,
+ GEN_INT (offset - offset_in_r0)));
+ offset_in_r0 += offset - offset_in_r0;
+ }
+
+ if (pre_dec != NULL_RTX)
+ {
+ if (! sp_in_r0)
+ {
+ emit_move_insn (r0,
+ gen_rtx_PLUS
+ (Pmode, r0, stack_pointer_rtx));
+ sp_in_r0 = 1;
+ }
+
+ offset -= GET_MODE_SIZE (mode);
+ offset_in_r0 -= GET_MODE_SIZE (mode);
+
+ mem_rtx = pre_dec;
+ }
+ else if (sp_in_r0)
+ mem_rtx = gen_frame_mem (mode, r0);
+ else
+ mem_rtx = gen_frame_mem (mode,
+ gen_rtx_PLUS (Pmode,
+ stack_pointer_rtx,
+ r0));
+
+ /* We must not use an r0-based address for target-branch
+ registers or for special registers without pre-dec
+ memory addresses, since we store their values in r0
+ first. */
+ gcc_assert (!TARGET_REGISTER_P (reg)
+ && ((reg != PR_REG && !SPECIAL_REGISTER_P (reg))
+ || mem_rtx == pre_dec));
+
+ addr_ok:
+ orig_reg_rtx = reg_rtx;
+ if (TARGET_REGISTER_P (reg)
+ || ((reg == PR_REG || SPECIAL_REGISTER_P (reg))
+ && mem_rtx != pre_dec))
+ {
+ rtx tmp_reg = gen_rtx_REG (GET_MODE (reg_rtx), *tmp_pnt);
+
+ emit_move_insn (tmp_reg, reg_rtx);
+
+ if (REGNO (tmp_reg) == R0_REG)
+ {
+ offset_in_r0 = -1;
+ sp_in_r0 = 0;
+ gcc_assert (!refers_to_regno_p
+ (R0_REG, R0_REG+1, mem_rtx, (rtx *) 0));
+ }
+
+ if (*++tmp_pnt <= 0)
+ tmp_pnt = schedule.temps;
+
+ reg_rtx = tmp_reg;
+ }
+ {
+ rtx insn;
+
+ /* Mark as interesting for dwarf cfi generator */
+ insn = emit_move_insn (mem_rtx, reg_rtx);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ /* If we use an intermediate register for the save, we can't
+ describe this exactly in cfi as a copy of the to-be-saved
+ register into the temporary register and then the temporary
+ register on the stack, because the temporary register can
+ have a different natural size than the to-be-saved register.
+ Thus, we gloss over the intermediate copy and pretend we do
+ a direct save from the to-be-saved register. */
+ if (REGNO (reg_rtx) != reg)
+ {
+ rtx set;
+
+ set = gen_rtx_SET (VOIDmode, mem_rtx, orig_reg_rtx);
+ add_reg_note (insn, REG_FRAME_RELATED_EXPR, set);
+ }
+
+ if (TARGET_SHCOMPACT && (offset_in_r0 != -1))
+ {
+ rtx reg_rtx = gen_rtx_REG (mode, reg);
+ rtx set;
+ rtx mem_rtx = gen_frame_mem (mode,
+ gen_rtx_PLUS (Pmode,
+ stack_pointer_rtx,
+ GEN_INT (offset)));
+
+ set = gen_rtx_SET (VOIDmode, mem_rtx, reg_rtx);
+ add_reg_note (insn, REG_FRAME_RELATED_EXPR, set);
+ }
+ }
+ }
+
+ gcc_assert (entry->offset == d_rounding);
+ }
+ else
+ {
+ push_regs (&live_regs_mask, current_function_interrupt);
+ stack_usage += d;
+ }
+
+ if (flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM))
+ emit_insn (gen_GOTaddr2picreg ());
+
+ if (SHMEDIA_REGS_STACK_ADJUST ())
+ {
+ /* This must NOT go through the PLT, otherwise mach and macl
+ may be clobbered. */
+ function_symbol (gen_rtx_REG (Pmode, R0_REG),
+ (TARGET_FPU_ANY
+ ? "__GCC_push_shmedia_regs"
+ : "__GCC_push_shmedia_regs_nofpu"), SFUNC_GOT);
+ emit_insn (gen_shmedia_save_restore_regs_compact
+ (GEN_INT (-SHMEDIA_REGS_STACK_ADJUST ())));
+ }
+
+ if (target_flags != save_flags && ! current_function_interrupt)
+ emit_insn (gen_toggle_sz ());
+
+ target_flags = save_flags;
+
+ output_stack_adjust (-rounded_frame_size (d) + d_rounding,
+ stack_pointer_rtx, 0, NULL, true);
+ stack_usage += rounded_frame_size (d) - d_rounding;
+
+ if (frame_pointer_needed)
+ frame_insn (GEN_MOV (hard_frame_pointer_rtx, stack_pointer_rtx));
+
+ if (TARGET_SHCOMPACT
+ && (crtl->args.info.call_cookie & ~ CALL_COOKIE_RET_TRAMP(1)))
+ {
+ /* This must NOT go through the PLT, otherwise mach and macl
+ may be clobbered. */
+ function_symbol (gen_rtx_REG (Pmode, R0_REG),
+ "__GCC_shcompact_incoming_args", SFUNC_GOT);
+ emit_insn (gen_shcompact_incoming_args ());
+ }
+
+ if (flag_stack_usage)
+ current_function_static_stack_size = stack_usage;
+}
+
+void
+sh_expand_epilogue (bool sibcall_p)
+{
+ HARD_REG_SET live_regs_mask;
+ int d, i;
+ int d_rounding = 0;
+
+ int save_flags = target_flags;
+ int frame_size, save_size;
+ int fpscr_deferred = 0;
+ int e = sibcall_p ? -1 : 1;
+
+ d = calc_live_regs (&live_regs_mask);
+
+ save_size = d;
+ frame_size = rounded_frame_size (d);
+
+ if (TARGET_SH5)
+ {
+ int tregs_space = shmedia_target_regs_stack_adjust (&live_regs_mask);
+ int total_size;
+ if (d % (STACK_BOUNDARY / BITS_PER_UNIT))
+ d_rounding = ((STACK_BOUNDARY / BITS_PER_UNIT)
+ - d % (STACK_BOUNDARY / BITS_PER_UNIT));
+
+ total_size = d + tregs_space;
+ total_size += rounded_frame_size (total_size);
+ save_size = total_size - frame_size;
+
+ /* If adjusting the stack in a single step costs nothing extra, do so.
+ I.e. either if a single addi is enough, or we need a movi anyway,
+ and we don't exceed the maximum offset range (the test for the
+ latter is conservative for simplicity). */
+ if (TARGET_SHMEDIA
+ && ! frame_pointer_needed
+ && (CONST_OK_FOR_I10 (total_size)
+ || (! CONST_OK_FOR_I10 (save_size + d_rounding)
+ && total_size <= 2044)))
+ d_rounding = frame_size;
+
+ frame_size -= d_rounding;
+ }
+
+ if (frame_pointer_needed)
+ {
+ /* We must avoid scheduling the epilogue with previous basic blocks.
+ See PR/18032 and PR/40313. */
+ emit_insn (gen_blockage ());
+ output_stack_adjust (frame_size, hard_frame_pointer_rtx, e,
+ &live_regs_mask, false);
+
+ /* We must avoid moving the stack pointer adjustment past code
+ which reads from the local frame, else an interrupt could
+ occur after the SP adjustment and clobber data in the local
+ frame. */
+ emit_insn (gen_blockage ());
+ emit_insn (GEN_MOV (stack_pointer_rtx, hard_frame_pointer_rtx));
+ }
+ else if (frame_size)
+ {
+ /* We must avoid moving the stack pointer adjustment past code
+ which reads from the local frame, else an interrupt could
+ occur after the SP adjustment and clobber data in the local
+ frame. */
+ emit_insn (gen_blockage ());
+ output_stack_adjust (frame_size, stack_pointer_rtx, e,
+ &live_regs_mask, false);
+ }
+
+ if (SHMEDIA_REGS_STACK_ADJUST ())
+ {
+ function_symbol (gen_rtx_REG (Pmode, R0_REG),
+ (TARGET_FPU_ANY
+ ? "__GCC_pop_shmedia_regs"
+ : "__GCC_pop_shmedia_regs_nofpu"), SFUNC_GOT);
+ /* This must NOT go through the PLT, otherwise mach and macl
+ may be clobbered. */
+ emit_insn (gen_shmedia_save_restore_regs_compact
+ (GEN_INT (SHMEDIA_REGS_STACK_ADJUST ())));
+ }
+
+ /* Pop all the registers. */
+
+ if (target_flags != save_flags && ! current_function_interrupt)
+ emit_insn (gen_toggle_sz ());
+ if (TARGET_SH5)
+ {
+ int offset_base, offset;
+ int offset_in_r0 = -1;
+ int sp_in_r0 = 0;
+ rtx r0 = gen_rtx_REG (Pmode, R0_REG);
+ save_schedule schedule;
+ save_entry *entry;
+ int *tmp_pnt;
+
+ entry = sh5_schedule_saves (&live_regs_mask, &schedule, d_rounding);
+ offset_base = -entry[1].offset + d_rounding;
+ tmp_pnt = schedule.temps;
+ for (; entry->mode != VOIDmode; entry--)
+ {
+ enum machine_mode mode = (enum machine_mode) entry->mode;
+ int reg = entry->reg;
+ rtx reg_rtx, mem_rtx, post_inc = NULL_RTX;
+
+ offset = offset_base + entry->offset;
+ reg_rtx = gen_rtx_REG (mode, reg);
+
+ mem_rtx = gen_frame_mem (mode,
+ gen_rtx_PLUS (Pmode,
+ stack_pointer_rtx,
+ GEN_INT (offset)));
+
+ if (!memory_address_p (mode, XEXP (mem_rtx, 0)))
+ mem_rtx = NULL_RTX;
+
+ if (HAVE_POST_INCREMENT
+ && (offset == offset_in_r0
+ || (offset + GET_MODE_SIZE (mode) != d + d_rounding
+ && mem_rtx == NULL_RTX)
+ || reg == PR_REG || SPECIAL_REGISTER_P (reg)))
+ {
+ post_inc = gen_frame_mem (mode, gen_rtx_POST_INC (Pmode, r0));
+
+ if (!memory_address_p (mode, XEXP (post_inc, 0)))
+ post_inc = NULL_RTX;
+ else
+ mem_rtx = NULL_RTX;
+ }
+
+ if (mem_rtx != NULL_RTX)
+ goto addr_ok;
+
+ if (offset_in_r0 == -1)
+ {
+ emit_move_insn (r0, GEN_INT (offset));
+ offset_in_r0 = offset;
+ }
+ else if (offset != offset_in_r0)
+ {
+ emit_move_insn (r0,
+ gen_rtx_PLUS
+ (Pmode, r0,
+ GEN_INT (offset - offset_in_r0)));
+ offset_in_r0 += offset - offset_in_r0;
+ }
+
+ if (post_inc != NULL_RTX)
+ {
+ if (! sp_in_r0)
+ {
+ emit_move_insn (r0,
+ gen_rtx_PLUS
+ (Pmode, r0, stack_pointer_rtx));
+ sp_in_r0 = 1;
+ }
+
+ mem_rtx = post_inc;
+
+ offset_in_r0 += GET_MODE_SIZE (mode);
+ }
+ else if (sp_in_r0)
+ mem_rtx = gen_frame_mem (mode, r0);
+ else
+ mem_rtx = gen_frame_mem (mode,
+ gen_rtx_PLUS (Pmode,
+ stack_pointer_rtx,
+ r0));
+
+ gcc_assert ((reg != PR_REG && !SPECIAL_REGISTER_P (reg))
+ || mem_rtx == post_inc);
+
+ addr_ok:
+ if ((reg == PR_REG || SPECIAL_REGISTER_P (reg))
+ && mem_rtx != post_inc)
+ {
+ emit_move_insn (r0, mem_rtx);
+ mem_rtx = r0;
+ }
+ else if (TARGET_REGISTER_P (reg))
+ {
+ rtx tmp_reg = gen_rtx_REG (mode, *tmp_pnt);
+
+ /* Give the scheduler a bit of freedom by using up to
+ MAX_TEMPS registers in a round-robin fashion. */
+ emit_move_insn (tmp_reg, mem_rtx);
+ mem_rtx = tmp_reg;
+ if (*++tmp_pnt < 0)
+ tmp_pnt = schedule.temps;
+ }
+
+ emit_move_insn (reg_rtx, mem_rtx);
+ }
+
+ gcc_assert (entry->offset + offset_base == d + d_rounding);
+ }
+ else /* ! TARGET_SH5 */
+ {
+ int last_reg;
+
+ save_size = 0;
+ /* For an ISR with RESBANK attribute assigned, don't pop PR
+ register. */
+ if (TEST_HARD_REG_BIT (live_regs_mask, PR_REG)
+ && !sh_cfun_resbank_handler_p ())
+ {
+ if (!frame_pointer_needed)
+ emit_insn (gen_blockage ());
+ pop (PR_REG);
+ }
+
+ /* Banked registers are popped first to avoid being scheduled in the
+ delay slot. RTE switches banks before the ds instruction. */
+ if (current_function_interrupt)
+ {
+ bool use_movml = false;
+
+ if (TARGET_SH2A)
+ {
+ unsigned int count = 0;
+
+ for (i = FIRST_BANKED_REG; i <= LAST_BANKED_REG; i++)
+ if (TEST_HARD_REG_BIT (live_regs_mask, i))
+ count++;
+ else
+ break;
+
+ /* Use movml when all banked register are poped. */
+ if (count == LAST_BANKED_REG - FIRST_BANKED_REG + 1)
+ use_movml = true;
+ }
+
+ if (use_movml)
+ {
+ rtx sp_reg = gen_rtx_REG (SImode, STACK_POINTER_REGNUM);
+
+ /* We must avoid scheduling multiple load insn with another
+ insns. */
+ emit_insn (gen_blockage ());
+ emit_insn (gen_movml_pop_banked (sp_reg));
+ emit_insn (gen_blockage ());
+ }
+ else
+ for (i = LAST_BANKED_REG; i >= FIRST_BANKED_REG; i--)
+ if (TEST_HARD_REG_BIT (live_regs_mask, i))
+ pop (i);
+
+ last_reg = FIRST_PSEUDO_REGISTER - LAST_BANKED_REG - 1;
+ }
+ else
+ last_reg = FIRST_PSEUDO_REGISTER;
+
+ for (i = 0; i < last_reg; i++)
+ {
+ int j = (FIRST_PSEUDO_REGISTER - 1) - i;
+
+ if (j == FPSCR_REG && current_function_interrupt && TARGET_FMOVD
+ && hard_reg_set_intersect_p (live_regs_mask,
+ reg_class_contents[DF_REGS]))
+ fpscr_deferred = 1;
+ /* For an ISR with RESBANK attribute assigned, don't pop
+ following registers, R0-R14, MACH, MACL and GBR. */
+ else if (j != PR_REG && TEST_HARD_REG_BIT (live_regs_mask, j)
+ && ! (sh_cfun_resbank_handler_p ()
+ && ((j >= FIRST_GENERAL_REG
+ && j < LAST_GENERAL_REG)
+ || j == MACH_REG
+ || j == MACL_REG
+ || j == GBR_REG)))
+ pop (j);
+
+ if (j == FIRST_FP_REG && fpscr_deferred)
+ pop (FPSCR_REG);
+ }
+ }
+ if (target_flags != save_flags && ! current_function_interrupt)
+ emit_insn (gen_toggle_sz ());
+ target_flags = save_flags;
+
+ output_stack_adjust (crtl->args.pretend_args_size
+ + save_size + d_rounding
+ + crtl->args.info.stack_regs * 8,
+ stack_pointer_rtx, e, NULL, false);
+
+ if (crtl->calls_eh_return)
+ emit_insn (GEN_ADD3 (stack_pointer_rtx, stack_pointer_rtx,
+ EH_RETURN_STACKADJ_RTX));
+
+ /* Switch back to the normal stack if necessary. */
+ if (lookup_attribute ("sp_switch", DECL_ATTRIBUTES (current_function_decl)))
+ emit_insn (gen_sp_switch_2 ());
+
+ /* Tell flow the insn that pops PR isn't dead. */
+ /* PR_REG will never be live in SHmedia mode, and we don't need to
+ USE PR_MEDIA_REG, since it will be explicitly copied to TR0_REG
+ by the return pattern. */
+ if (TEST_HARD_REG_BIT (live_regs_mask, PR_REG))
+ emit_use (gen_rtx_REG (SImode, PR_REG));
+}
+
+static int sh_need_epilogue_known = 0;
+
+int
+sh_need_epilogue (void)
+{
+ if (! sh_need_epilogue_known)
+ {
+ rtx epilogue;
+
+ start_sequence ();
+ sh_expand_epilogue (0);
+ epilogue = get_insns ();
+ end_sequence ();
+ sh_need_epilogue_known = (epilogue == NULL ? -1 : 1);
+ }
+ return sh_need_epilogue_known > 0;
+}
+
+/* Emit code to change the current function's return address to RA.
+ TEMP is available as a scratch register, if needed. */
+
+void
+sh_set_return_address (rtx ra, rtx tmp)
+{
+ HARD_REG_SET live_regs_mask;
+ int d;
+ int pr_reg = TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG;
+ int pr_offset;
+
+ d = calc_live_regs (&live_regs_mask);
+
+ /* If pr_reg isn't life, we can set it (or the register given in
+ sh_media_register_for_return) directly. */
+ if (! TEST_HARD_REG_BIT (live_regs_mask, pr_reg))
+ {
+ rtx rr;
+
+ if (TARGET_SHMEDIA)
+ {
+ int rr_regno = sh_media_register_for_return ();
+
+ if (rr_regno < 0)
+ rr_regno = pr_reg;
+
+ rr = gen_rtx_REG (DImode, rr_regno);
+ }
+ else
+ rr = gen_rtx_REG (SImode, pr_reg);
+
+ emit_insn (GEN_MOV (rr, ra));
+ /* Tell flow the register for return isn't dead. */
+ emit_use (rr);
+ return;
+ }
+
+ if (TARGET_SH5)
+ {
+ int offset;
+ save_schedule schedule;
+ save_entry *entry;
+
+ entry = sh5_schedule_saves (&live_regs_mask, &schedule, 0);
+ offset = entry[1].offset;
+ for (; entry->mode != VOIDmode; entry--)
+ if (entry->reg == pr_reg)
+ goto found;
+
+ /* We can't find pr register. */
+ gcc_unreachable ();
+
+ found:
+ offset = entry->offset - offset;
+ pr_offset = (rounded_frame_size (d) + offset
+ + SHMEDIA_REGS_STACK_ADJUST ());
+ }
+ else
+ pr_offset = rounded_frame_size (d);
+
+ emit_insn (GEN_MOV (tmp, GEN_INT (pr_offset)));
+
+ if (frame_pointer_needed)
+ emit_insn (GEN_ADD3 (tmp, tmp, hard_frame_pointer_rtx));
+ else
+ emit_insn (GEN_ADD3 (tmp, tmp, stack_pointer_rtx));
+
+ tmp = gen_frame_mem (Pmode, tmp);
+ emit_insn (GEN_MOV (tmp, ra));
+ /* Tell this store isn't dead. */
+ emit_use (tmp);
+}
+
+/* Clear variables at function end. */
+
+static void
+sh_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
+ HOST_WIDE_INT size ATTRIBUTE_UNUSED)
+{
+ sh_need_epilogue_known = 0;
+}
+
+static rtx
+sh_builtin_saveregs (void)
+{
+ /* First unnamed integer register. */
+ int first_intreg = crtl->args.info.arg_count[(int) SH_ARG_INT];
+ /* Number of integer registers we need to save. */
+ int n_intregs = MAX (0, NPARM_REGS (SImode) - first_intreg);
+ /* First unnamed SFmode float reg */
+ int first_floatreg = crtl->args.info.arg_count[(int) SH_ARG_FLOAT];
+ /* Number of SFmode float regs to save. */
+ int n_floatregs = MAX (0, NPARM_REGS (SFmode) - first_floatreg);
+ rtx regbuf, fpregs;
+ int bufsize, regno;
+ alias_set_type alias_set;
+
+ if (TARGET_SH5)
+ {
+ if (n_intregs)
+ {
+ int pushregs = n_intregs;
+
+ while (pushregs < NPARM_REGS (SImode) - 1
+ && (CALL_COOKIE_INT_REG_GET
+ (crtl->args.info.call_cookie,
+ NPARM_REGS (SImode) - pushregs)
+ == 1))
+ {
+ crtl->args.info.call_cookie
+ &= ~ CALL_COOKIE_INT_REG (NPARM_REGS (SImode)
+ - pushregs, 1);
+ pushregs++;
+ }
+
+ if (pushregs == NPARM_REGS (SImode))
+ crtl->args.info.call_cookie
+ |= (CALL_COOKIE_INT_REG (0, 1)
+ | CALL_COOKIE_STACKSEQ (pushregs - 1));
+ else
+ crtl->args.info.call_cookie
+ |= CALL_COOKIE_STACKSEQ (pushregs);
+
+ crtl->args.pretend_args_size += 8 * n_intregs;
+ }
+ if (TARGET_SHCOMPACT)
+ return const0_rtx;
+ }
+
+ if (! TARGET_SH2E && ! TARGET_SH4 && ! TARGET_SH5)
+ {
+ error ("__builtin_saveregs not supported by this subtarget");
+ return const0_rtx;
+ }
+
+ if (TARGET_SHMEDIA)
+ n_floatregs = 0;
+
+ /* Allocate block of memory for the regs. */
+ /* ??? If n_intregs + n_floatregs == 0, should we allocate at least 1 byte?
+ Or can assign_stack_local accept a 0 SIZE argument? */
+ bufsize = (n_intregs * UNITS_PER_WORD) + (n_floatregs * UNITS_PER_WORD);
+
+ if (TARGET_SHMEDIA)
+ regbuf = gen_frame_mem (BLKmode, gen_rtx_REG (Pmode, ARG_POINTER_REGNUM));
+ else if (n_floatregs & 1)
+ {
+ rtx addr;
+
+ regbuf = assign_stack_local (BLKmode, bufsize + UNITS_PER_WORD, 0);
+ addr = copy_to_mode_reg (Pmode, XEXP (regbuf, 0));
+ emit_insn (gen_iorsi3 (addr, addr, GEN_INT (UNITS_PER_WORD)));
+ regbuf = change_address (regbuf, BLKmode, addr);
+ }
+ else if (STACK_BOUNDARY < 64 && TARGET_FPU_DOUBLE && n_floatregs)
+ {
+ rtx addr, mask;
+
+ regbuf = assign_stack_local (BLKmode, bufsize + UNITS_PER_WORD, 0);
+ addr = copy_to_mode_reg (Pmode, plus_constant (XEXP (regbuf, 0), 4));
+ mask = copy_to_mode_reg (Pmode, GEN_INT (-8));
+ emit_insn (gen_andsi3 (addr, addr, mask));
+ regbuf = change_address (regbuf, BLKmode, addr);
+ }
+ else
+ regbuf = assign_stack_local (BLKmode, bufsize, TARGET_FPU_DOUBLE ? 64 : 0);
+ alias_set = get_varargs_alias_set ();
+ set_mem_alias_set (regbuf, alias_set);
+
+ /* Save int args.
+ This is optimized to only save the regs that are necessary. Explicitly
+ named args need not be saved. */
+ if (n_intregs > 0)
+ move_block_from_reg (BASE_ARG_REG (SImode) + first_intreg,
+ adjust_address (regbuf, BLKmode,
+ n_floatregs * UNITS_PER_WORD),
+ n_intregs);
+
+ if (TARGET_SHMEDIA)
+ /* Return the address of the regbuf. */
+ return XEXP (regbuf, 0);
+
+ /* Save float args.
+ This is optimized to only save the regs that are necessary. Explicitly
+ named args need not be saved.
+ We explicitly build a pointer to the buffer because it halves the insn
+ count when not optimizing (otherwise the pointer is built for each reg
+ saved).
+ We emit the moves in reverse order so that we can use predecrement. */
+
+ fpregs = copy_to_mode_reg (Pmode,
+ plus_constant (XEXP (regbuf, 0),
+ n_floatregs * UNITS_PER_WORD));
+ if (TARGET_SH4 || TARGET_SH2A_DOUBLE)
+ {
+ rtx mem;
+ for (regno = NPARM_REGS (DFmode) - 2; regno >= first_floatreg; regno -= 2)
+ {
+ emit_insn (gen_addsi3 (fpregs, fpregs,
+ GEN_INT (-2 * UNITS_PER_WORD)));
+ mem = change_address (regbuf, DFmode, fpregs);
+ emit_move_insn (mem,
+ gen_rtx_REG (DFmode, BASE_ARG_REG (DFmode) + regno));
+ }
+ regno = first_floatreg;
+ if (regno & 1)
+ {
+ emit_insn (gen_addsi3 (fpregs, fpregs, GEN_INT (-UNITS_PER_WORD)));
+ mem = change_address (regbuf, SFmode, fpregs);
+ emit_move_insn (mem,
+ gen_rtx_REG (SFmode, BASE_ARG_REG (SFmode) + regno
+ - (TARGET_LITTLE_ENDIAN != 0)));
+ }
+ }
+ else
+ for (regno = NPARM_REGS (SFmode) - 1; regno >= first_floatreg; regno--)
+ {
+ rtx mem;
+
+ emit_insn (gen_addsi3 (fpregs, fpregs, GEN_INT (-UNITS_PER_WORD)));
+ mem = change_address (regbuf, SFmode, fpregs);
+ emit_move_insn (mem,
+ gen_rtx_REG (SFmode, BASE_ARG_REG (SFmode) + regno));
+ }
+
+ /* Return the address of the regbuf. */
+ return XEXP (regbuf, 0);
+}
+
+/* Define the `__builtin_va_list' type for the ABI. */
+
+static tree
+sh_build_builtin_va_list (void)
+{
+ tree f_next_o, f_next_o_limit, f_next_fp, f_next_fp_limit, f_next_stack;
+ tree record, type_decl;
+
+ if (TARGET_SH5 || (! TARGET_SH2E && ! TARGET_SH4)
+ || TARGET_HITACHI || sh_cfun_attr_renesas_p ())
+ return ptr_type_node;
+
+ record = (*lang_hooks.types.make_type) (RECORD_TYPE);
+ type_decl = build_decl (BUILTINS_LOCATION,
+ TYPE_DECL, get_identifier ("__va_list_tag"), record);
+
+ f_next_o = build_decl (BUILTINS_LOCATION,
+ FIELD_DECL, get_identifier ("__va_next_o"),
+ ptr_type_node);
+ f_next_o_limit = build_decl (BUILTINS_LOCATION,
+ FIELD_DECL,
+ get_identifier ("__va_next_o_limit"),
+ ptr_type_node);
+ f_next_fp = build_decl (BUILTINS_LOCATION,
+ FIELD_DECL, get_identifier ("__va_next_fp"),
+ ptr_type_node);
+ f_next_fp_limit = build_decl (BUILTINS_LOCATION,
+ FIELD_DECL,
+ get_identifier ("__va_next_fp_limit"),
+ ptr_type_node);
+ f_next_stack = build_decl (BUILTINS_LOCATION,
+ FIELD_DECL, get_identifier ("__va_next_stack"),
+ ptr_type_node);
+
+ DECL_FIELD_CONTEXT (f_next_o) = record;
+ DECL_FIELD_CONTEXT (f_next_o_limit) = record;
+ DECL_FIELD_CONTEXT (f_next_fp) = record;
+ DECL_FIELD_CONTEXT (f_next_fp_limit) = record;
+ DECL_FIELD_CONTEXT (f_next_stack) = record;
+
+ TYPE_STUB_DECL (record) = type_decl;
+ TYPE_NAME (record) = type_decl;
+ TYPE_FIELDS (record) = f_next_o;
+ DECL_CHAIN (f_next_o) = f_next_o_limit;
+ DECL_CHAIN (f_next_o_limit) = f_next_fp;
+ DECL_CHAIN (f_next_fp) = f_next_fp_limit;
+ DECL_CHAIN (f_next_fp_limit) = f_next_stack;
+
+ layout_type (record);
+
+ return record;
+}
+
+/* Implement `va_start' for varargs and stdarg. */
+
+static void
+sh_va_start (tree valist, rtx nextarg)
+{
+ tree f_next_o, f_next_o_limit, f_next_fp, f_next_fp_limit, f_next_stack;
+ tree next_o, next_o_limit, next_fp, next_fp_limit, next_stack;
+ tree t, u;
+ int nfp, nint;
+
+ if (TARGET_SH5)
+ {
+ expand_builtin_saveregs ();
+ std_expand_builtin_va_start (valist, nextarg);
+ return;
+ }
+
+ if ((! TARGET_SH2E && ! TARGET_SH4)
+ || TARGET_HITACHI || sh_cfun_attr_renesas_p ())
+ {
+ std_expand_builtin_va_start (valist, nextarg);
+ return;
+ }
+
+ f_next_o = TYPE_FIELDS (va_list_type_node);
+ f_next_o_limit = DECL_CHAIN (f_next_o);
+ f_next_fp = DECL_CHAIN (f_next_o_limit);
+ f_next_fp_limit = DECL_CHAIN (f_next_fp);
+ f_next_stack = DECL_CHAIN (f_next_fp_limit);
+
+ next_o = build3 (COMPONENT_REF, TREE_TYPE (f_next_o), valist, f_next_o,
+ NULL_TREE);
+ next_o_limit = build3 (COMPONENT_REF, TREE_TYPE (f_next_o_limit),
+ valist, f_next_o_limit, NULL_TREE);
+ next_fp = build3 (COMPONENT_REF, TREE_TYPE (f_next_fp), valist, f_next_fp,
+ NULL_TREE);
+ next_fp_limit = build3 (COMPONENT_REF, TREE_TYPE (f_next_fp_limit),
+ valist, f_next_fp_limit, NULL_TREE);
+ next_stack = build3 (COMPONENT_REF, TREE_TYPE (f_next_stack),
+ valist, f_next_stack, NULL_TREE);
+
+ /* Call __builtin_saveregs. */
+ u = make_tree (sizetype, expand_builtin_saveregs ());
+ u = fold_convert (ptr_type_node, u);
+ t = build2 (MODIFY_EXPR, ptr_type_node, next_fp, u);
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ nfp = crtl->args.info.arg_count[SH_ARG_FLOAT];
+ if (nfp < 8)
+ nfp = 8 - nfp;
+ else
+ nfp = 0;
+ u = fold_build2 (POINTER_PLUS_EXPR, ptr_type_node, u,
+ size_int (UNITS_PER_WORD * nfp));
+ t = build2 (MODIFY_EXPR, ptr_type_node, next_fp_limit, u);
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ t = build2 (MODIFY_EXPR, ptr_type_node, next_o, u);
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ nint = crtl->args.info.arg_count[SH_ARG_INT];
+ if (nint < 4)
+ nint = 4 - nint;
+ else
+ nint = 0;
+ u = fold_build2 (POINTER_PLUS_EXPR, ptr_type_node, u,
+ size_int (UNITS_PER_WORD * nint));
+ t = build2 (MODIFY_EXPR, ptr_type_node, next_o_limit, u);
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ u = make_tree (ptr_type_node, nextarg);
+ t = build2 (MODIFY_EXPR, ptr_type_node, next_stack, u);
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+}
+
+/* TYPE is a RECORD_TYPE. If there is only a single nonzero-sized
+ member, return it. */
+static tree
+find_sole_member (tree type)
+{
+ tree field, member = NULL_TREE;
+
+ for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+ {
+ if (TREE_CODE (field) != FIELD_DECL)
+ continue;
+ if (!DECL_SIZE (field))
+ return NULL_TREE;
+ if (integer_zerop (DECL_SIZE (field)))
+ continue;
+ if (member)
+ return NULL_TREE;
+ member = field;
+ }
+ return member;
+}
+/* Implement `va_arg'. */
+
+static tree
+sh_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
+ gimple_seq *post_p ATTRIBUTE_UNUSED)
+{
+ HOST_WIDE_INT size, rsize;
+ tree tmp, pptr_type_node;
+ tree addr, lab_over = NULL, result = NULL;
+ int pass_by_ref = targetm.calls.must_pass_in_stack (TYPE_MODE (type), type);
+ tree eff_type;
+
+ if (pass_by_ref)
+ type = build_pointer_type (type);
+
+ size = int_size_in_bytes (type);
+ rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
+ pptr_type_node = build_pointer_type (ptr_type_node);
+
+ if (! TARGET_SH5 && (TARGET_SH2E || TARGET_SH4)
+ && ! (TARGET_HITACHI || sh_cfun_attr_renesas_p ()))
+ {
+ tree f_next_o, f_next_o_limit, f_next_fp, f_next_fp_limit, f_next_stack;
+ tree next_o, next_o_limit, next_fp, next_fp_limit, next_stack;
+ int pass_as_float;
+ tree lab_false;
+ tree member;
+
+ f_next_o = TYPE_FIELDS (va_list_type_node);
+ f_next_o_limit = DECL_CHAIN (f_next_o);
+ f_next_fp = DECL_CHAIN (f_next_o_limit);
+ f_next_fp_limit = DECL_CHAIN (f_next_fp);
+ f_next_stack = DECL_CHAIN (f_next_fp_limit);
+
+ next_o = build3 (COMPONENT_REF, TREE_TYPE (f_next_o), valist, f_next_o,
+ NULL_TREE);
+ next_o_limit = build3 (COMPONENT_REF, TREE_TYPE (f_next_o_limit),
+ valist, f_next_o_limit, NULL_TREE);
+ next_fp = build3 (COMPONENT_REF, TREE_TYPE (f_next_fp),
+ valist, f_next_fp, NULL_TREE);
+ next_fp_limit = build3 (COMPONENT_REF, TREE_TYPE (f_next_fp_limit),
+ valist, f_next_fp_limit, NULL_TREE);
+ next_stack = build3 (COMPONENT_REF, TREE_TYPE (f_next_stack),
+ valist, f_next_stack, NULL_TREE);
+
+ /* Structures with a single member with a distinct mode are passed
+ like their member. This is relevant if the latter has a REAL_TYPE
+ or COMPLEX_TYPE type. */
+ eff_type = type;
+ while (TREE_CODE (eff_type) == RECORD_TYPE
+ && (member = find_sole_member (eff_type))
+ && (TREE_CODE (TREE_TYPE (member)) == REAL_TYPE
+ || TREE_CODE (TREE_TYPE (member)) == COMPLEX_TYPE
+ || TREE_CODE (TREE_TYPE (member)) == RECORD_TYPE))
+ {
+ tree field_type = TREE_TYPE (member);
+
+ if (TYPE_MODE (eff_type) == TYPE_MODE (field_type))
+ eff_type = field_type;
+ else
+ {
+ gcc_assert ((TYPE_ALIGN (eff_type)
+ < GET_MODE_ALIGNMENT (TYPE_MODE (field_type)))
+ || (TYPE_ALIGN (eff_type)
+ > GET_MODE_BITSIZE (TYPE_MODE (field_type))));
+ break;
+ }
+ }
+
+ if (TARGET_SH4 || TARGET_SH2A_DOUBLE)
+ {
+ pass_as_float = ((TREE_CODE (eff_type) == REAL_TYPE && size <= 8)
+ || (TREE_CODE (eff_type) == COMPLEX_TYPE
+ && TREE_CODE (TREE_TYPE (eff_type)) == REAL_TYPE
+ && size <= 16));
+ }
+ else
+ {
+ pass_as_float = (TREE_CODE (eff_type) == REAL_TYPE && size == 4);
+ }
+
+ addr = create_tmp_var (pptr_type_node, NULL);
+ lab_false = create_artificial_label (UNKNOWN_LOCATION);
+ lab_over = create_artificial_label (UNKNOWN_LOCATION);
+
+ valist = build_simple_mem_ref (addr);
+
+ if (pass_as_float)
+ {
+ tree next_fp_tmp = create_tmp_var (TREE_TYPE (f_next_fp), NULL);
+ tree cmp;
+ bool is_double = size == 8 && TREE_CODE (eff_type) == REAL_TYPE;
+
+ tmp = build1 (ADDR_EXPR, pptr_type_node, unshare_expr (next_fp));
+ gimplify_assign (unshare_expr (addr), tmp, pre_p);
+
+ gimplify_assign (unshare_expr (next_fp_tmp), valist, pre_p);
+ tmp = next_fp_limit;
+ if (size > 4 && !is_double)
+ tmp = build2 (POINTER_PLUS_EXPR, TREE_TYPE (tmp),
+ unshare_expr (tmp), size_int (4 - size));
+ tmp = build2 (GE_EXPR, boolean_type_node,
+ unshare_expr (next_fp_tmp), unshare_expr (tmp));
+ cmp = build3 (COND_EXPR, void_type_node, tmp,
+ build1 (GOTO_EXPR, void_type_node,
+ unshare_expr (lab_false)), NULL_TREE);
+ if (!is_double)
+ gimplify_and_add (cmp, pre_p);
+
+ if (TYPE_ALIGN (eff_type) > BITS_PER_WORD
+ || (is_double || size == 16))
+ {
+ tmp = fold_convert (sizetype, next_fp_tmp);
+ tmp = build2 (BIT_AND_EXPR, sizetype, tmp,
+ size_int (UNITS_PER_WORD));
+ tmp = build2 (POINTER_PLUS_EXPR, ptr_type_node,
+ unshare_expr (next_fp_tmp), tmp);
+ gimplify_assign (unshare_expr (next_fp_tmp), tmp, pre_p);
+ }
+ if (is_double)
+ gimplify_and_add (cmp, pre_p);
+
+#ifdef FUNCTION_ARG_SCmode_WART
+ if (TYPE_MODE (eff_type) == SCmode
+ && TARGET_SH4 && TARGET_LITTLE_ENDIAN)
+ {
+ tree subtype = TREE_TYPE (eff_type);
+ tree real, imag;
+
+ imag
+ = std_gimplify_va_arg_expr (next_fp_tmp, subtype, pre_p, NULL);
+ imag = get_initialized_tmp_var (imag, pre_p, NULL);
+
+ real
+ = std_gimplify_va_arg_expr (next_fp_tmp, subtype, pre_p, NULL);
+ real = get_initialized_tmp_var (real, pre_p, NULL);
+
+ result = build2 (COMPLEX_EXPR, eff_type, real, imag);
+ if (type != eff_type)
+ result = build1 (VIEW_CONVERT_EXPR, type, result);
+ result = get_initialized_tmp_var (result, pre_p, NULL);
+ }
+#endif /* FUNCTION_ARG_SCmode_WART */
+
+ tmp = build1 (GOTO_EXPR, void_type_node, unshare_expr (lab_over));
+ gimplify_and_add (tmp, pre_p);
+
+ tmp = build1 (LABEL_EXPR, void_type_node, unshare_expr (lab_false));
+ gimplify_and_add (tmp, pre_p);
+
+ tmp = build1 (ADDR_EXPR, pptr_type_node, unshare_expr (next_stack));
+ gimplify_assign (unshare_expr (addr), tmp, pre_p);
+ gimplify_assign (unshare_expr (next_fp_tmp),
+ unshare_expr (valist), pre_p);
+
+ gimplify_assign (unshare_expr (valist),
+ unshare_expr (next_fp_tmp), post_p);
+ valist = next_fp_tmp;
+ }
+ else
+ {
+ tmp = build2 (POINTER_PLUS_EXPR, ptr_type_node,
+ unshare_expr (next_o), size_int (rsize));
+ tmp = build2 (GT_EXPR, boolean_type_node, tmp,
+ unshare_expr (next_o_limit));
+ tmp = build3 (COND_EXPR, void_type_node, tmp,
+ build1 (GOTO_EXPR, void_type_node,
+ unshare_expr (lab_false)),
+ NULL_TREE);
+ gimplify_and_add (tmp, pre_p);
+
+ tmp = build1 (ADDR_EXPR, pptr_type_node, unshare_expr (next_o));
+ gimplify_assign (unshare_expr (addr), tmp, pre_p);
+
+ tmp = build1 (GOTO_EXPR, void_type_node, unshare_expr (lab_over));
+ gimplify_and_add (tmp, pre_p);
+
+ tmp = build1 (LABEL_EXPR, void_type_node, unshare_expr (lab_false));
+ gimplify_and_add (tmp, pre_p);
+
+ if (size > 4 && ! (TARGET_SH4 || TARGET_SH2A))
+ gimplify_assign (unshare_expr (next_o),
+ unshare_expr (next_o_limit), pre_p);
+
+ tmp = build1 (ADDR_EXPR, pptr_type_node, unshare_expr (next_stack));
+ gimplify_assign (unshare_expr (addr), tmp, pre_p);
+ }
+
+ if (!result)
+ {
+ tmp = build1 (LABEL_EXPR, void_type_node, unshare_expr (lab_over));
+ gimplify_and_add (tmp, pre_p);
+ }
+ }
+
+ /* ??? In va-sh.h, there had been code to make values larger than
+ size 8 indirect. This does not match the FUNCTION_ARG macros. */
+
+ tmp = std_gimplify_va_arg_expr (valist, type, pre_p, NULL);
+ if (result)
+ {
+ gimplify_assign (result, tmp, pre_p);
+ result = build1 (NOP_EXPR, TREE_TYPE (result), result);
+ tmp = build1 (LABEL_EXPR, void_type_node, unshare_expr (lab_over));
+ gimplify_and_add (tmp, pre_p);
+ }
+ else
+ result = tmp;
+
+ if (pass_by_ref)
+ result = build_va_arg_indirect_ref (result);
+
+ return result;
+}
+
+/* 64 bit floating points memory transfers are paired single precision loads
+ or store. So DWARF information needs fixing in little endian (unless
+ PR=SZ=1 in FPSCR). */
+rtx
+sh_dwarf_register_span (rtx reg)
+{
+ unsigned regno = REGNO (reg);
+
+ if (WORDS_BIG_ENDIAN || GET_MODE (reg) != DFmode)
+ return NULL_RTX;
+
+ return
+ gen_rtx_PARALLEL (VOIDmode,
+ gen_rtvec (2,
+ gen_rtx_REG (SFmode,
+ DBX_REGISTER_NUMBER (regno+1)),
+ gen_rtx_REG (SFmode,
+ DBX_REGISTER_NUMBER (regno))));
+}
+
+static enum machine_mode
+sh_promote_function_mode (const_tree type, enum machine_mode mode,
+ int *punsignedp, const_tree funtype,
+ int for_return)
+{
+ if (sh_promote_prototypes (funtype))
+ return promote_mode (type, mode, punsignedp);
+ else
+ return default_promote_function_mode (type, mode, punsignedp, funtype,
+ for_return);
+}
+
+static bool
+sh_promote_prototypes (const_tree type)
+{
+ if (TARGET_HITACHI)
+ return 0;
+ if (! type)
+ return 1;
+ return ! sh_attr_renesas_p (type);
+}
+
+/* Whether an argument must be passed by reference. On SHcompact, we
+ pretend arguments wider than 32-bits that would have been passed in
+ registers are passed by reference, so that an SHmedia trampoline
+ loads them into the full 64-bits registers. */
+
+static int
+shcompact_byref (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ const_tree type, bool named)
+{
+ unsigned HOST_WIDE_INT size;
+
+ if (type)
+ size = int_size_in_bytes (type);
+ else
+ size = GET_MODE_SIZE (mode);
+
+ if (cum->arg_count[SH_ARG_INT] < NPARM_REGS (SImode)
+ && (!named
+ || GET_SH_ARG_CLASS (mode) == SH_ARG_INT
+ || (GET_SH_ARG_CLASS (mode) == SH_ARG_FLOAT
+ && cum->arg_count[SH_ARG_FLOAT] >= NPARM_REGS (SFmode)))
+ && size > 4
+ && !SHCOMPACT_FORCE_ON_STACK (mode, type)
+ && !SH5_WOULD_BE_PARTIAL_NREGS (*cum, mode, type, named))
+ return size;
+ else
+ return 0;
+}
+
+static bool
+sh_pass_by_reference (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ const_tree type, bool named)
+{
+ if (targetm.calls.must_pass_in_stack (mode, type))
+ return true;
+
+ /* ??? std_gimplify_va_arg_expr passes NULL for cum. That function
+ wants to know about pass-by-reference semantics for incoming
+ arguments. */
+ if (! cum)
+ return false;
+
+ if (TARGET_SHCOMPACT)
+ {
+ cum->byref = shcompact_byref (cum, mode, type, named);
+ return cum->byref != 0;
+ }
+
+ return false;
+}
+
+static bool
+sh_callee_copies (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ const_tree type, bool named ATTRIBUTE_UNUSED)
+{
+ /* ??? How can it possibly be correct to return true only on the
+ caller side of the equation? Is there someplace else in the
+ sh backend that's magically producing the copies? */
+ return (cum->outgoing
+ && ((mode == BLKmode ? TYPE_ALIGN (type) : GET_MODE_ALIGNMENT (mode))
+ % SH_MIN_ALIGN_FOR_CALLEE_COPY == 0));
+}
+
+static int
+sh_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ tree type, bool named ATTRIBUTE_UNUSED)
+{
+ int words = 0;
+
+ if (!TARGET_SH5
+ && PASS_IN_REG_P (*cum, mode, type)
+ && !(TARGET_SH4 || TARGET_SH2A_DOUBLE)
+ && (ROUND_REG (*cum, mode)
+ + (mode != BLKmode
+ ? ROUND_ADVANCE (GET_MODE_SIZE (mode))
+ : ROUND_ADVANCE (int_size_in_bytes (type)))
+ > NPARM_REGS (mode)))
+ words = NPARM_REGS (mode) - ROUND_REG (*cum, mode);
+
+ else if (!TARGET_SHCOMPACT
+ && SH5_WOULD_BE_PARTIAL_NREGS (*cum, mode, type, named))
+ words = NPARM_REGS (SImode) - cum->arg_count[SH_ARG_INT];
+
+ return words * UNITS_PER_WORD;
+}
+
+
+/* Define where to put the arguments to a function.
+ Value is zero to push the argument on the stack,
+ or a hard register in which to store the argument.
+
+ MODE is the argument's machine mode.
+ TYPE is the data type of the argument (as a tree).
+ This is null for libcalls where that information may
+ not be available.
+ CUM is a variable of type CUMULATIVE_ARGS which gives info about
+ the preceding args and about the function being called.
+ NAMED is nonzero if this argument is a named parameter
+ (otherwise it is an extra parameter matching an ellipsis).
+
+ On SH the first args are normally in registers
+ and the rest are pushed. Any arg that starts within the first
+ NPARM_REGS words is at least partially passed in a register unless
+ its data type forbids. */
+
+static rtx
+sh_function_arg (CUMULATIVE_ARGS *ca, enum machine_mode mode,
+ const_tree type, bool named)
+{
+ if (! TARGET_SH5 && mode == VOIDmode)
+ return GEN_INT (ca->renesas_abi ? 1 : 0);
+
+ if (! TARGET_SH5
+ && PASS_IN_REG_P (*ca, mode, type)
+ && (named || ! (TARGET_HITACHI || ca->renesas_abi)))
+ {
+ int regno;
+
+ if (mode == SCmode && TARGET_SH4 && TARGET_LITTLE_ENDIAN
+ && (! FUNCTION_ARG_SCmode_WART || (ROUND_REG (*ca, mode) & 1)))
+ {
+ rtx r1 = gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_REG (SFmode,
+ BASE_ARG_REG (mode)
+ + (ROUND_REG (*ca, mode) ^ 1)),
+ const0_rtx);
+ rtx r2 = gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_REG (SFmode,
+ BASE_ARG_REG (mode)
+ + ((ROUND_REG (*ca, mode) + 1) ^ 1)),
+ GEN_INT (4));
+ return gen_rtx_PARALLEL(SCmode, gen_rtvec(2, r1, r2));
+ }
+
+ /* If the alignment of a DF value causes an SF register to be
+ skipped, we will use that skipped register for the next SF
+ value. */
+ if ((TARGET_HITACHI || ca->renesas_abi)
+ && ca->free_single_fp_reg
+ && mode == SFmode)
+ return gen_rtx_REG (mode, ca->free_single_fp_reg);
+
+ regno = (BASE_ARG_REG (mode) + ROUND_REG (*ca, mode))
+ ^ (mode == SFmode && TARGET_SH4
+ && TARGET_LITTLE_ENDIAN != 0
+ && ! TARGET_HITACHI && ! ca->renesas_abi);
+ return gen_rtx_REG (mode, regno);
+
+ }
+
+ if (TARGET_SH5)
+ {
+ if (mode == VOIDmode && TARGET_SHCOMPACT)
+ return GEN_INT (ca->call_cookie);
+
+ /* The following test assumes unnamed arguments are promoted to
+ DFmode. */
+ if (mode == SFmode && ca->free_single_fp_reg)
+ return SH5_PROTOTYPED_FLOAT_ARG (*ca, mode, ca->free_single_fp_reg);
+
+ if ((GET_SH_ARG_CLASS (mode) == SH_ARG_FLOAT)
+ && (named || ! ca->prototype_p)
+ && ca->arg_count[(int) SH_ARG_FLOAT] < NPARM_REGS (SFmode))
+ {
+ if (! ca->prototype_p && TARGET_SHMEDIA)
+ return SH5_PROTOTYPELESS_FLOAT_ARG (*ca, mode);
+
+ return SH5_PROTOTYPED_FLOAT_ARG (*ca, mode,
+ FIRST_FP_PARM_REG
+ + ca->arg_count[(int) SH_ARG_FLOAT]);
+ }
+
+ if (ca->arg_count[(int) SH_ARG_INT] < NPARM_REGS (SImode)
+ && (! TARGET_SHCOMPACT
+ || (! SHCOMPACT_FORCE_ON_STACK (mode, type)
+ && ! SH5_WOULD_BE_PARTIAL_NREGS (*ca, mode,
+ type, named))))
+ {
+ return gen_rtx_REG (mode, (FIRST_PARM_REG
+ + ca->arg_count[(int) SH_ARG_INT]));
+ }
+
+ return 0;
+ }
+
+ return 0;
+}
+
+/* Update the data in CUM to advance over an argument
+ of mode MODE and data type TYPE.
+ (TYPE is null for libcalls where that information may not be
+ available.) */
+
+static void
+sh_function_arg_advance (CUMULATIVE_ARGS *ca, enum machine_mode mode,
+ const_tree type, bool named)
+{
+ if (ca->force_mem)
+ ca->force_mem = 0;
+ else if (TARGET_SH5)
+ {
+ const_tree type2 = (ca->byref && type
+ ? TREE_TYPE (type)
+ : type);
+ enum machine_mode mode2 = (ca->byref && type
+ ? TYPE_MODE (type2)
+ : mode);
+ int dwords = ((ca->byref
+ ? ca->byref
+ : mode2 == BLKmode
+ ? int_size_in_bytes (type2)
+ : GET_MODE_SIZE (mode2)) + 7) / 8;
+ int numregs = MIN (dwords, NPARM_REGS (SImode)
+ - ca->arg_count[(int) SH_ARG_INT]);
+
+ if (numregs)
+ {
+ ca->arg_count[(int) SH_ARG_INT] += numregs;
+ if (TARGET_SHCOMPACT
+ && SHCOMPACT_FORCE_ON_STACK (mode2, type2))
+ {
+ ca->call_cookie
+ |= CALL_COOKIE_INT_REG (ca->arg_count[(int) SH_ARG_INT]
+ - numregs, 1);
+ /* N.B. We want this also for outgoing. */
+ ca->stack_regs += numregs;
+ }
+ else if (ca->byref)
+ {
+ if (! ca->outgoing)
+ ca->stack_regs += numregs;
+ ca->byref_regs += numregs;
+ ca->byref = 0;
+ do
+ ca->call_cookie
+ |= CALL_COOKIE_INT_REG (ca->arg_count[(int) SH_ARG_INT]
+ - numregs, 2);
+ while (--numregs);
+ ca->call_cookie
+ |= CALL_COOKIE_INT_REG (ca->arg_count[(int) SH_ARG_INT]
+ - 1, 1);
+ }
+ else if (dwords > numregs)
+ {
+ int pushregs = numregs;
+
+ if (TARGET_SHCOMPACT)
+ ca->stack_regs += numregs;
+ while (pushregs < NPARM_REGS (SImode) - 1
+ && (CALL_COOKIE_INT_REG_GET
+ (ca->call_cookie,
+ NPARM_REGS (SImode) - pushregs)
+ == 1))
+ {
+ ca->call_cookie
+ &= ~ CALL_COOKIE_INT_REG (NPARM_REGS (SImode)
+ - pushregs, 1);
+ pushregs++;
+ }
+ if (numregs == NPARM_REGS (SImode))
+ ca->call_cookie
+ |= CALL_COOKIE_INT_REG (0, 1)
+ | CALL_COOKIE_STACKSEQ (numregs - 1);
+ else
+ ca->call_cookie
+ |= CALL_COOKIE_STACKSEQ (numregs);
+ }
+ }
+ if (GET_SH_ARG_CLASS (mode2) == SH_ARG_FLOAT
+ && (named || ! ca->prototype_p))
+ {
+ if (mode2 == SFmode && ca->free_single_fp_reg)
+ ca->free_single_fp_reg = 0;
+ else if (ca->arg_count[(int) SH_ARG_FLOAT]
+ < NPARM_REGS (SFmode))
+ {
+ int numfpregs
+ = MIN ((GET_MODE_SIZE (mode2) + 7) / 8 * 2,
+ NPARM_REGS (SFmode)
+ - ca->arg_count[(int) SH_ARG_FLOAT]);
+
+ ca->arg_count[(int) SH_ARG_FLOAT] += numfpregs;
+
+ if (TARGET_SHCOMPACT && ! ca->prototype_p)
+ {
+ if (ca->outgoing && numregs > 0)
+ do
+ {
+ ca->call_cookie
+ |= (CALL_COOKIE_INT_REG
+ (ca->arg_count[(int) SH_ARG_INT]
+ - numregs + ((numfpregs - 2) / 2),
+ 4 + (ca->arg_count[(int) SH_ARG_FLOAT]
+ - numfpregs) / 2));
+ }
+ while (numfpregs -= 2);
+ }
+ else if (mode2 == SFmode && (named)
+ && (ca->arg_count[(int) SH_ARG_FLOAT]
+ < NPARM_REGS (SFmode)))
+ ca->free_single_fp_reg
+ = FIRST_FP_PARM_REG - numfpregs
+ + ca->arg_count[(int) SH_ARG_FLOAT] + 1;
+ }
+ }
+ return;
+ }
+
+ if ((TARGET_HITACHI || ca->renesas_abi) && TARGET_FPU_DOUBLE)
+ {
+ /* Note that we've used the skipped register. */
+ if (mode == SFmode && ca->free_single_fp_reg)
+ {
+ ca->free_single_fp_reg = 0;
+ return;
+ }
+ /* When we have a DF after an SF, there's an SF register that get
+ skipped in order to align the DF value. We note this skipped
+ register, because the next SF value will use it, and not the
+ SF that follows the DF. */
+ if (mode == DFmode
+ && ROUND_REG (*ca, DFmode) != ROUND_REG (*ca, SFmode))
+ {
+ ca->free_single_fp_reg = (ROUND_REG (*ca, SFmode)
+ + BASE_ARG_REG (mode));
+ }
+ }
+
+ if (! ((TARGET_SH4 || TARGET_SH2A) || ca->renesas_abi)
+ || PASS_IN_REG_P (*ca, mode, type))
+ (ca->arg_count[(int) GET_SH_ARG_CLASS (mode)]
+ = (ROUND_REG (*ca, mode)
+ + (mode == BLKmode
+ ? ROUND_ADVANCE (int_size_in_bytes (type))
+ : ROUND_ADVANCE (GET_MODE_SIZE (mode)))));
+}
+
+/* The Renesas calling convention doesn't quite fit into this scheme since
+ the address is passed like an invisible argument, but one that is always
+ passed in memory. */
+static rtx
+sh_struct_value_rtx (tree fndecl, int incoming ATTRIBUTE_UNUSED)
+{
+ if (TARGET_HITACHI || sh_attr_renesas_p (fndecl))
+ return 0;
+ return gen_rtx_REG (Pmode, 2);
+}
+
+/* Worker function for TARGET_FUNCTION_VALUE.
+
+ For the SH, this is like LIBCALL_VALUE, except that we must change the
+ mode like PROMOTE_MODE does.
+ ??? PROMOTE_MODE is ignored for non-scalar types. The set of types
+ tested here has to be kept in sync with the one in explow.c:promote_mode.
+*/
+
+static rtx
+sh_function_value (const_tree valtype,
+ const_tree fn_decl_or_type,
+ bool outgoing ATTRIBUTE_UNUSED)
+{
+ if (fn_decl_or_type
+ && !DECL_P (fn_decl_or_type))
+ fn_decl_or_type = NULL;
+
+ return gen_rtx_REG (
+ ((GET_MODE_CLASS (TYPE_MODE (valtype)) == MODE_INT
+ && GET_MODE_SIZE (TYPE_MODE (valtype)) < 4
+ && (TREE_CODE (valtype) == INTEGER_TYPE
+ || TREE_CODE (valtype) == ENUMERAL_TYPE
+ || TREE_CODE (valtype) == BOOLEAN_TYPE
+ || TREE_CODE (valtype) == REAL_TYPE
+ || TREE_CODE (valtype) == OFFSET_TYPE))
+ && sh_promote_prototypes (fn_decl_or_type)
+ ? (TARGET_SHMEDIA64 ? DImode : SImode) : TYPE_MODE (valtype)),
+ BASE_RETURN_VALUE_REG (TYPE_MODE (valtype)));
+}
+
+/* Worker function for TARGET_LIBCALL_VALUE. */
+
+static rtx
+sh_libcall_value (enum machine_mode mode, const_rtx fun ATTRIBUTE_UNUSED)
+{
+ return gen_rtx_REG (mode, BASE_RETURN_VALUE_REG (mode));
+}
+
+/* Return true if N is a possible register number of function value. */
+
+static bool
+sh_function_value_regno_p (const unsigned int regno)
+{
+ return ((regno) == FIRST_RET_REG
+ || (TARGET_SH2E && (regno) == FIRST_FP_RET_REG)
+ || (TARGET_SHMEDIA_FPU && (regno) == FIRST_FP_RET_REG));
+}
+
+/* Worker function for TARGET_RETURN_IN_MEMORY. */
+
+static bool
+sh_return_in_memory (const_tree type, const_tree fndecl)
+{
+ if (TARGET_SH5)
+ {
+ if (TYPE_MODE (type) == BLKmode)
+ return ((unsigned HOST_WIDE_INT) int_size_in_bytes (type)) > 8;
+ else
+ return GET_MODE_SIZE (TYPE_MODE (type)) > 8;
+ }
+ else
+ {
+ return (TYPE_MODE (type) == BLKmode
+ || ((TARGET_HITACHI || sh_attr_renesas_p (fndecl))
+ && TREE_CODE (type) == RECORD_TYPE));
+ }
+}
+
+/* We actually emit the code in sh_expand_prologue. We used to use
+ a static variable to flag that we need to emit this code, but that
+ doesn't when inlining, when functions are deferred and then emitted
+ later. Fortunately, we already have two flags that are part of struct
+ function that tell if a function uses varargs or stdarg. */
+static void
+sh_setup_incoming_varargs (CUMULATIVE_ARGS *ca,
+ enum machine_mode mode,
+ tree type,
+ int *pretend_arg_size,
+ int second_time ATTRIBUTE_UNUSED)
+{
+ gcc_assert (cfun->stdarg);
+ if (TARGET_VARARGS_PRETEND_ARGS (current_function_decl))
+ {
+ int named_parm_regs, anon_parm_regs;
+
+ named_parm_regs = (ROUND_REG (*ca, mode)
+ + (mode == BLKmode
+ ? ROUND_ADVANCE (int_size_in_bytes (type))
+ : ROUND_ADVANCE (GET_MODE_SIZE (mode))));
+ anon_parm_regs = NPARM_REGS (SImode) - named_parm_regs;
+ if (anon_parm_regs > 0)
+ *pretend_arg_size = anon_parm_regs * 4;
+ }
+}
+
+static bool
+sh_strict_argument_naming (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED)
+{
+ return TARGET_SH5;
+}
+
+static bool
+sh_pretend_outgoing_varargs_named (CUMULATIVE_ARGS *ca)
+{
+ return ! (TARGET_HITACHI || ca->renesas_abi) && ! TARGET_SH5;
+}
+
+
+/* Define the offset between two registers, one to be eliminated, and
+ the other its replacement, at the start of a routine. */
+
+int
+initial_elimination_offset (int from, int to)
+{
+ int regs_saved;
+ int regs_saved_rounding = 0;
+ int total_saved_regs_space;
+ int total_auto_space;
+ int save_flags = target_flags;
+ int copy_flags;
+ HARD_REG_SET live_regs_mask;
+
+ shmedia_space_reserved_for_target_registers = false;
+ regs_saved = calc_live_regs (&live_regs_mask);
+ regs_saved += SHMEDIA_REGS_STACK_ADJUST ();
+
+ if (shmedia_reserve_space_for_target_registers_p (regs_saved, &live_regs_mask))
+ {
+ shmedia_space_reserved_for_target_registers = true;
+ regs_saved += shmedia_target_regs_stack_adjust (&live_regs_mask);
+ }
+
+ if (TARGET_SH5 && regs_saved % (STACK_BOUNDARY / BITS_PER_UNIT))
+ regs_saved_rounding = ((STACK_BOUNDARY / BITS_PER_UNIT)
+ - regs_saved % (STACK_BOUNDARY / BITS_PER_UNIT));
+
+ total_auto_space = rounded_frame_size (regs_saved) - regs_saved_rounding;
+ copy_flags = target_flags;
+ target_flags = save_flags;
+
+ total_saved_regs_space = regs_saved + regs_saved_rounding;
+
+ if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
+ return total_saved_regs_space + total_auto_space
+ + crtl->args.info.byref_regs * 8;
+
+ if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
+ return total_saved_regs_space + total_auto_space
+ + crtl->args.info.byref_regs * 8;
+
+ /* Initial gap between fp and sp is 0. */
+ if (from == HARD_FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
+ return 0;
+
+ if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
+ return rounded_frame_size (0);
+
+ if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
+ return rounded_frame_size (0);
+
+ gcc_assert (from == RETURN_ADDRESS_POINTER_REGNUM
+ && (to == HARD_FRAME_POINTER_REGNUM
+ || to == STACK_POINTER_REGNUM));
+ if (TARGET_SH5)
+ {
+ int n = total_saved_regs_space;
+ int pr_reg = TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG;
+ save_schedule schedule;
+ save_entry *entry;
+
+ n += total_auto_space;
+
+ /* If it wasn't saved, there's not much we can do. */
+ if (! TEST_HARD_REG_BIT (live_regs_mask, pr_reg))
+ return n;
+
+ target_flags = copy_flags;
+
+ sh5_schedule_saves (&live_regs_mask, &schedule, n);
+ for (entry = &schedule.entries[1]; entry->mode != VOIDmode; entry++)
+ if (entry->reg == pr_reg)
+ {
+ target_flags = save_flags;
+ return entry->offset;
+ }
+ gcc_unreachable ();
+ }
+ else
+ return total_auto_space;
+}
+
+/* Parse the -mfixed-range= option string. */
+void
+sh_fix_range (const char *const_str)
+{
+ int i, first, last;
+ char *str, *dash, *comma;
+
+ /* str must be of the form REG1'-'REG2{,REG1'-'REG} where REG1 and
+ REG2 are either register names or register numbers. The effect
+ of this option is to mark the registers in the range from REG1 to
+ REG2 as ``fixed'' so they won't be used by the compiler. */
+
+ i = strlen (const_str);
+ str = (char *) alloca (i + 1);
+ memcpy (str, const_str, i + 1);
+
+ while (1)
+ {
+ dash = strchr (str, '-');
+ if (!dash)
+ {
+ warning (0, "value of -mfixed-range must have form REG1-REG2");
+ return;
+ }
+ *dash = '\0';
+ comma = strchr (dash + 1, ',');
+ if (comma)
+ *comma = '\0';
+
+ first = decode_reg_name (str);
+ if (first < 0)
+ {
+ warning (0, "unknown register name: %s", str);
+ return;
+ }
+
+ last = decode_reg_name (dash + 1);
+ if (last < 0)
+ {
+ warning (0, "unknown register name: %s", dash + 1);
+ return;
+ }
+
+ *dash = '-';
+
+ if (first > last)
+ {
+ warning (0, "%s-%s is an empty range", str, dash + 1);
+ return;
+ }
+
+ for (i = first; i <= last; ++i)
+ fixed_regs[i] = call_used_regs[i] = 1;
+
+ if (!comma)
+ break;
+
+ *comma = ',';
+ str = comma + 1;
+ }
+}
+
+/* Insert any deferred function attributes from earlier pragmas. */
+static void
+sh_insert_attributes (tree node, tree *attributes)
+{
+ tree attrs;
+
+ if (TREE_CODE (node) != FUNCTION_DECL)
+ return;
+
+ /* We are only interested in fields. */
+ if (!DECL_P (node))
+ return;
+
+ /* Append the attributes to the deferred attributes. */
+ *sh_deferred_function_attributes_tail = *attributes;
+ attrs = sh_deferred_function_attributes;
+ if (!attrs)
+ return;
+
+ /* Some attributes imply or require the interrupt attribute. */
+ if (!lookup_attribute ("interrupt_handler", attrs)
+ && !lookup_attribute ("interrupt_handler", DECL_ATTRIBUTES (node)))
+ {
+ /* If we have a trapa_handler, but no interrupt_handler attribute,
+ insert an interrupt_handler attribute. */
+ if (lookup_attribute ("trapa_handler", attrs) != NULL_TREE)
+ /* We can't use sh_pr_interrupt here because that's not in the
+ java frontend. */
+ attrs
+ = tree_cons (get_identifier("interrupt_handler"), NULL_TREE, attrs);
+ /* However, for sp_switch, trap_exit, nosave_low_regs and resbank,
+ if the interrupt attribute is missing, we ignore the attribute
+ and warn. */
+ else if (lookup_attribute ("sp_switch", attrs)
+ || lookup_attribute ("trap_exit", attrs)
+ || lookup_attribute ("nosave_low_regs", attrs)
+ || lookup_attribute ("resbank", attrs))
+ {
+ tree *tail;
+
+ for (tail = attributes; attrs; attrs = TREE_CHAIN (attrs))
+ {
+ if (is_attribute_p ("sp_switch", TREE_PURPOSE (attrs))
+ || is_attribute_p ("trap_exit", TREE_PURPOSE (attrs))
+ || is_attribute_p ("nosave_low_regs", TREE_PURPOSE (attrs))
+ || is_attribute_p ("resbank", TREE_PURPOSE (attrs)))
+ warning (OPT_Wattributes,
+ "%qE attribute only applies to interrupt functions",
+ TREE_PURPOSE (attrs));
+ else
+ {
+ *tail = tree_cons (TREE_PURPOSE (attrs), NULL_TREE,
+ NULL_TREE);
+ tail = &TREE_CHAIN (*tail);
+ }
+ }
+ attrs = *attributes;
+ }
+ }
+
+ /* Install the processed list. */
+ *attributes = attrs;
+
+ /* Clear deferred attributes. */
+ sh_deferred_function_attributes = NULL_TREE;
+ sh_deferred_function_attributes_tail = &sh_deferred_function_attributes;
+
+ return;
+}
+
+/* Supported attributes:
+
+ interrupt_handler -- specifies this function is an interrupt handler.
+
+ trapa_handler - like above, but don't save all registers.
+
+ sp_switch -- specifies an alternate stack for an interrupt handler
+ to run on.
+
+ trap_exit -- use a trapa to exit an interrupt function instead of
+ an rte instruction.
+
+ nosave_low_regs - don't save r0..r7 in an interrupt handler.
+ This is useful on the SH3 and upwards,
+ which has a separate set of low regs for User and Supervisor modes.
+ This should only be used for the lowest level of interrupts. Higher levels
+ of interrupts must save the registers in case they themselves are
+ interrupted.
+
+ renesas -- use Renesas calling/layout conventions (functions and
+ structures).
+
+ resbank -- In case of an ISR, use a register bank to save registers
+ R0-R14, MACH, MACL, GBR and PR. This is useful only on SH2A targets.
+*/
+
+/* Handle a 'resbank' attribute. */
+static tree
+sh_handle_resbank_handler_attribute (tree * node, tree name,
+ tree args ATTRIBUTE_UNUSED,
+ int flags ATTRIBUTE_UNUSED,
+ bool * no_add_attrs)
+{
+ if (!TARGET_SH2A)
+ {
+ warning (OPT_Wattributes, "%qE attribute is supported only for SH2A",
+ name);
+ *no_add_attrs = true;
+ }
+ if (TREE_CODE (*node) != FUNCTION_DECL)
+ {
+ warning (OPT_Wattributes, "%qE attribute only applies to functions",
+ name);
+ *no_add_attrs = true;
+ }
+
+ return NULL_TREE;
+}
+
+/* Handle an "interrupt_handler" attribute; arguments as in
+ struct attribute_spec.handler. */
+static tree
+sh_handle_interrupt_handler_attribute (tree *node, tree name,
+ tree args ATTRIBUTE_UNUSED,
+ int flags ATTRIBUTE_UNUSED,
+ bool *no_add_attrs)
+{
+ if (TREE_CODE (*node) != FUNCTION_DECL)
+ {
+ warning (OPT_Wattributes, "%qE attribute only applies to functions",
+ name);
+ *no_add_attrs = true;
+ }
+ else if (TARGET_SHCOMPACT)
+ {
+ error ("attribute interrupt_handler is not compatible with -m5-compact");
+ *no_add_attrs = true;
+ }
+
+ return NULL_TREE;
+}
+
+/* Handle an 'function_vector' attribute; arguments as in
+ struct attribute_spec.handler. */
+static tree
+sh2a_handle_function_vector_handler_attribute (tree * node, tree name,
+ tree args ATTRIBUTE_UNUSED,
+ int flags ATTRIBUTE_UNUSED,
+ bool * no_add_attrs)
+{
+ if (!TARGET_SH2A)
+ {
+ warning (OPT_Wattributes, "%qE attribute only applies to SH2A",
+ name);
+ *no_add_attrs = true;
+ }
+ else if (TREE_CODE (*node) != FUNCTION_DECL)
+ {
+ warning (OPT_Wattributes, "%qE attribute only applies to functions",
+ name);
+ *no_add_attrs = true;
+ }
+ else if (TREE_CODE (TREE_VALUE (args)) != INTEGER_CST)
+ {
+ /* The argument must be a constant integer. */
+ warning (OPT_Wattributes,
+ "%qE attribute argument not an integer constant",
+ name);
+ *no_add_attrs = true;
+ }
+ else if (TREE_INT_CST_LOW (TREE_VALUE (args)) > 255)
+ {
+ /* The argument value must be between 0 to 255. */
+ warning (OPT_Wattributes,
+ "%qE attribute argument should be between 0 to 255",
+ name);
+ *no_add_attrs = true;
+ }
+ return NULL_TREE;
+}
+
+/* Returns 1 if current function has been assigned the attribute
+ 'function_vector'. */
+int
+sh2a_is_function_vector_call (rtx x)
+{
+ if (GET_CODE (x) == SYMBOL_REF
+ && (SYMBOL_REF_FLAGS (x) & SYMBOL_FLAG_FUNCVEC_FUNCTION))
+ {
+ tree tr = SYMBOL_REF_DECL (x);
+
+ if (sh2a_function_vector_p (tr))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Returns the function vector number, if the the attribute
+ 'function_vector' is assigned, otherwise returns zero. */
+int
+sh2a_get_function_vector_number (rtx x)
+{
+ int num;
+ tree list, t;
+
+ if ((GET_CODE (x) == SYMBOL_REF)
+ && (SYMBOL_REF_FLAGS (x) & SYMBOL_FLAG_FUNCVEC_FUNCTION))
+ {
+ t = SYMBOL_REF_DECL (x);
+
+ if (TREE_CODE (t) != FUNCTION_DECL)
+ return 0;
+
+ list = SH_ATTRIBUTES (t);
+ while (list)
+ {
+ if (is_attribute_p ("function_vector", TREE_PURPOSE (list)))
+ {
+ num = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (list)));
+ return num;
+ }
+
+ list = TREE_CHAIN (list);
+ }
+
+ return 0;
+ }
+ else
+ return 0;
+}
+
+/* Handle an "sp_switch" attribute; arguments as in
+ struct attribute_spec.handler. */
+static tree
+sh_handle_sp_switch_attribute (tree *node, tree name, tree args,
+ int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
+{
+ if (TREE_CODE (*node) != FUNCTION_DECL)
+ {
+ warning (OPT_Wattributes, "%qE attribute only applies to functions",
+ name);
+ *no_add_attrs = true;
+ }
+ else if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+ {
+ /* The argument must be a constant string. */
+ warning (OPT_Wattributes, "%qE attribute argument not a string constant",
+ name);
+ *no_add_attrs = true;
+ }
+
+ return NULL_TREE;
+}
+
+/* Handle an "trap_exit" attribute; arguments as in
+ struct attribute_spec.handler. */
+static tree
+sh_handle_trap_exit_attribute (tree *node, tree name, tree args,
+ int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
+{
+ if (TREE_CODE (*node) != FUNCTION_DECL)
+ {
+ warning (OPT_Wattributes, "%qE attribute only applies to functions",
+ name);
+ *no_add_attrs = true;
+ }
+ /* The argument specifies a trap number to be used in a trapa instruction
+ at function exit (instead of an rte instruction). */
+ else if (TREE_CODE (TREE_VALUE (args)) != INTEGER_CST)
+ {
+ /* The argument must be a constant integer. */
+ warning (OPT_Wattributes, "%qE attribute argument not an "
+ "integer constant", name);
+ *no_add_attrs = true;
+ }
+
+ return NULL_TREE;
+}
+
+static tree
+sh_handle_renesas_attribute (tree *node ATTRIBUTE_UNUSED,
+ tree name ATTRIBUTE_UNUSED,
+ tree args ATTRIBUTE_UNUSED,
+ int flags ATTRIBUTE_UNUSED,
+ bool *no_add_attrs ATTRIBUTE_UNUSED)
+{
+ return NULL_TREE;
+}
+
+/* True if __attribute__((renesas)) or -mrenesas. */
+int
+sh_attr_renesas_p (const_tree td)
+{
+ if (TARGET_HITACHI)
+ return 1;
+ if (td == 0)
+ return 0;
+ if (DECL_P (td))
+ td = TREE_TYPE (td);
+ if (td == error_mark_node)
+ return 0;
+ return (lookup_attribute ("renesas", TYPE_ATTRIBUTES (td))
+ != NULL_TREE);
+}
+
+/* True if __attribute__((renesas)) or -mrenesas, for the current
+ function. */
+int
+sh_cfun_attr_renesas_p (void)
+{
+ return sh_attr_renesas_p (current_function_decl);
+}
+
+int
+sh_cfun_interrupt_handler_p (void)
+{
+ return (lookup_attribute ("interrupt_handler",
+ DECL_ATTRIBUTES (current_function_decl))
+ != NULL_TREE);
+}
+
+/* Returns 1 if FUNC has been assigned the attribute
+ "function_vector". */
+int
+sh2a_function_vector_p (tree func)
+{
+ tree list;
+ if (TREE_CODE (func) != FUNCTION_DECL)
+ return 0;
+
+ list = SH_ATTRIBUTES (func);
+ while (list)
+ {
+ if (is_attribute_p ("function_vector", TREE_PURPOSE (list)))
+ return 1;
+
+ list = TREE_CHAIN (list);
+ }
+ return 0;
+}
+
+/* Returns TRUE if given tree has the "resbank" attribute. */
+
+int
+sh_cfun_resbank_handler_p (void)
+{
+ return ((lookup_attribute ("resbank",
+ DECL_ATTRIBUTES (current_function_decl))
+ != NULL_TREE)
+ && (lookup_attribute ("interrupt_handler",
+ DECL_ATTRIBUTES (current_function_decl))
+ != NULL_TREE) && TARGET_SH2A);
+}
+
+/* Implement TARGET_CHECK_PCH_TARGET_FLAGS. */
+
+static const char *
+sh_check_pch_target_flags (int old_flags)
+{
+ if ((old_flags ^ target_flags) & (MASK_SH1 | MASK_SH2 | MASK_SH3
+ | MASK_SH_E | MASK_HARD_SH4
+ | MASK_FPU_SINGLE | MASK_SH4))
+ return _("created and used with different architectures / ABIs");
+ if ((old_flags ^ target_flags) & MASK_HITACHI)
+ return _("created and used with different ABIs");
+ if ((old_flags ^ target_flags) & MASK_LITTLE_ENDIAN)
+ return _("created and used with different endianness");
+ return NULL;
+}
+
+/* Predicates used by the templates. */
+
+/* Returns 1 if OP is MACL, MACH or PR. The input must be a REG rtx.
+ Used only in general_movsrc_operand. */
+
+int
+system_reg_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ switch (REGNO (op))
+ {
+ case PR_REG:
+ case MACL_REG:
+ case MACH_REG:
+ return 1;
+ }
+ return 0;
+}
+
+/* Nonzero if OP is a floating point value with value 0.0. */
+
+int
+fp_zero_operand (rtx op)
+{
+ REAL_VALUE_TYPE r;
+
+ if (GET_MODE (op) != SFmode)
+ return 0;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (r, op);
+ return REAL_VALUES_EQUAL (r, dconst0) && ! REAL_VALUE_MINUS_ZERO (r);
+}
+
+/* Nonzero if OP is a floating point value with value 1.0. */
+
+int
+fp_one_operand (rtx op)
+{
+ REAL_VALUE_TYPE r;
+
+ if (GET_MODE (op) != SFmode)
+ return 0;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (r, op);
+ return REAL_VALUES_EQUAL (r, dconst1);
+}
+
+/* In general mode switching is used. If we are
+ compiling without -mfmovd, movsf_ie isn't taken into account for
+ mode switching. We could check in machine_dependent_reorg for
+ cases where we know we are in single precision mode, but there is
+ interface to find that out during reload, so we must avoid
+ choosing an fldi alternative during reload and thus failing to
+ allocate a scratch register for the constant loading. */
+int
+fldi_ok (void)
+{
+ return 1;
+}
+
+int
+tertiary_reload_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ enum rtx_code code = GET_CODE (op);
+ return code == MEM || (TARGET_SH4 && code == CONST_DOUBLE);
+}
+
+/* Return the TLS type for TLS symbols, 0 for otherwise. */
+enum tls_model
+tls_symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ if (GET_CODE (op) != SYMBOL_REF)
+ return TLS_MODEL_NONE;
+ return SYMBOL_REF_TLS_MODEL (op);
+}
+
+/* Return the destination address of a branch. */
+
+static int
+branch_dest (rtx branch)
+{
+ rtx dest = SET_SRC (PATTERN (branch));
+ int dest_uid;
+
+ if (GET_CODE (dest) == IF_THEN_ELSE)
+ dest = XEXP (dest, 1);
+ dest = XEXP (dest, 0);
+ dest_uid = INSN_UID (dest);
+ return INSN_ADDRESSES (dest_uid);
+}
+
+/* Return nonzero if REG is not used after INSN.
+ We assume REG is a reload reg, and therefore does
+ not live past labels. It may live past calls or jumps though. */
+int
+reg_unused_after (rtx reg, rtx insn)
+{
+ enum rtx_code code;
+ rtx set;
+
+ /* If the reg is set by this instruction, then it is safe for our
+ case. Disregard the case where this is a store to memory, since
+ we are checking a register used in the store address. */
+ set = single_set (insn);
+ if (set && !MEM_P (SET_DEST (set))
+ && reg_overlap_mentioned_p (reg, SET_DEST (set)))
+ return 1;
+
+ while ((insn = NEXT_INSN (insn)))
+ {
+ rtx set;
+ if (!INSN_P (insn))
+ continue;
+
+ code = GET_CODE (insn);
+
+#if 0
+ /* If this is a label that existed before reload, then the register
+ if dead here. However, if this is a label added by reorg, then
+ the register may still be live here. We can't tell the difference,
+ so we just ignore labels completely. */
+ if (code == CODE_LABEL)
+ return 1;
+ /* else */
+#endif
+
+ if (code == JUMP_INSN)
+ return 0;
+
+ /* If this is a sequence, we must handle them all at once.
+ We could have for instance a call that sets the target register,
+ and an insn in a delay slot that uses the register. In this case,
+ we must return 0. */
+ else if (code == INSN && GET_CODE (PATTERN (insn)) == SEQUENCE)
+ {
+ int i;
+ int retval = 0;
+
+ for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
+ {
+ rtx this_insn = XVECEXP (PATTERN (insn), 0, i);
+ rtx set = single_set (this_insn);
+
+ if (CALL_P (this_insn))
+ code = CALL_INSN;
+ else if (JUMP_P (this_insn))
+ {
+ if (INSN_ANNULLED_BRANCH_P (this_insn))
+ return 0;
+ code = JUMP_INSN;
+ }
+
+ if (set && reg_overlap_mentioned_p (reg, SET_SRC (set)))
+ return 0;
+ if (set && reg_overlap_mentioned_p (reg, SET_DEST (set)))
+ {
+ if (!MEM_P (SET_DEST (set)))
+ retval = 1;
+ else
+ return 0;
+ }
+ if (set == 0
+ && reg_overlap_mentioned_p (reg, PATTERN (this_insn)))
+ return 0;
+ }
+ if (retval == 1)
+ return 1;
+ else if (code == JUMP_INSN)
+ return 0;
+ }
+
+ set = single_set (insn);
+ if (set && reg_overlap_mentioned_p (reg, SET_SRC (set)))
+ return 0;
+ if (set && reg_overlap_mentioned_p (reg, SET_DEST (set)))
+ return !MEM_P (SET_DEST (set));
+ if (set == 0 && reg_overlap_mentioned_p (reg, PATTERN (insn)))
+ return 0;
+
+ if (code == CALL_INSN && call_really_used_regs[REGNO (reg)])
+ return 1;
+ }
+ return 1;
+}
+
+#include "ggc.h"
+
+static GTY(()) rtx fpscr_rtx;
+rtx
+get_fpscr_rtx (void)
+{
+ if (! fpscr_rtx)
+ {
+ fpscr_rtx = gen_rtx_REG (PSImode, FPSCR_REG);
+ REG_USERVAR_P (fpscr_rtx) = 1;
+ mark_user_reg (fpscr_rtx);
+ }
+ if (! reload_completed || mdep_reorg_phase != SH_AFTER_MDEP_REORG)
+ mark_user_reg (fpscr_rtx);
+ return fpscr_rtx;
+}
+
+static GTY(()) tree fpscr_values;
+
+static void
+emit_fpu_switch (rtx scratch, int index)
+{
+ rtx dst, src;
+
+ if (fpscr_values == NULL)
+ {
+ tree t;
+
+ t = build_index_type (integer_one_node);
+ t = build_array_type (integer_type_node, t);
+ t = build_decl (BUILTINS_LOCATION,
+ VAR_DECL, get_identifier ("__fpscr_values"), t);
+ DECL_ARTIFICIAL (t) = 1;
+ DECL_IGNORED_P (t) = 1;
+ DECL_EXTERNAL (t) = 1;
+ TREE_STATIC (t) = 1;
+ TREE_PUBLIC (t) = 1;
+ TREE_USED (t) = 1;
+
+ fpscr_values = t;
+ }
+
+ src = DECL_RTL (fpscr_values);
+ if (!can_create_pseudo_p ())
+ {
+ emit_move_insn (scratch, XEXP (src, 0));
+ if (index != 0)
+ emit_insn (gen_addsi3 (scratch, scratch, GEN_INT (index * 4)));
+ src = adjust_automodify_address (src, PSImode, scratch, index * 4);
+ }
+ else
+ src = adjust_address (src, PSImode, index * 4);
+
+ dst = get_fpscr_rtx ();
+ emit_move_insn (dst, src);
+}
+
+void
+emit_sf_insn (rtx pat)
+{
+ emit_insn (pat);
+}
+
+void
+emit_df_insn (rtx pat)
+{
+ emit_insn (pat);
+}
+
+void
+expand_sf_unop (rtx (*fun) (rtx, rtx, rtx), rtx *operands)
+{
+ emit_sf_insn ((*fun) (operands[0], operands[1], get_fpscr_rtx ()));
+}
+
+void
+expand_sf_binop (rtx (*fun) (rtx, rtx, rtx, rtx), rtx *operands)
+{
+ emit_sf_insn ((*fun) (operands[0], operands[1], operands[2],
+ get_fpscr_rtx ()));
+}
+
+void
+expand_df_unop (rtx (*fun) (rtx, rtx, rtx), rtx *operands)
+{
+ emit_df_insn ((*fun) (operands[0], operands[1], get_fpscr_rtx ()));
+}
+
+void
+expand_df_binop (rtx (*fun) (rtx, rtx, rtx, rtx), rtx *operands)
+{
+ emit_df_insn ((*fun) (operands[0], operands[1], operands[2],
+ get_fpscr_rtx ()));
+}
+
+static rtx get_free_reg (HARD_REG_SET);
+
+/* This function returns a register to use to load the address to load
+ the fpscr from. Currently it always returns r1 or r7, but when we are
+ able to use pseudo registers after combine, or have a better mechanism
+ for choosing a register, it should be done here. */
+/* REGS_LIVE is the liveness information for the point for which we
+ need this allocation. In some bare-bones exit blocks, r1 is live at the
+ start. We can even have all of r0..r3 being live:
+__complex__ long long f (double d) { if (d == 0) return 2; else return 3; }
+ INSN before which new insns are placed with will clobber the register
+ we return. If a basic block consists only of setting the return value
+ register to a pseudo and using that register, the return value is not
+ live before or after this block, yet we we'll insert our insns right in
+ the middle. */
+
+static rtx
+get_free_reg (HARD_REG_SET regs_live)
+{
+ if (! TEST_HARD_REG_BIT (regs_live, 1))
+ return gen_rtx_REG (Pmode, 1);
+
+ /* Hard reg 1 is live; since this is a small register classes target,
+ there shouldn't be anything but a jump before the function end. */
+ gcc_assert (!TEST_HARD_REG_BIT (regs_live, 7));
+ return gen_rtx_REG (Pmode, 7);
+}
+
+/* This function will set the fpscr from memory.
+ MODE is the mode we are setting it to. */
+void
+fpscr_set_from_mem (int mode, HARD_REG_SET regs_live)
+{
+ enum attr_fp_mode fp_mode = (enum attr_fp_mode) mode;
+ enum attr_fp_mode norm_mode = ACTUAL_NORMAL_MODE (FP_MODE);
+ rtx addr_reg;
+
+ addr_reg = !can_create_pseudo_p () ? get_free_reg (regs_live) : NULL_RTX;
+ emit_fpu_switch (addr_reg, fp_mode == norm_mode);
+}
+
+/* Is the given character a logical line separator for the assembler? */
+#ifndef IS_ASM_LOGICAL_LINE_SEPARATOR
+#define IS_ASM_LOGICAL_LINE_SEPARATOR(C, STR) ((C) == ';')
+#endif
+
+int
+sh_insn_length_adjustment (rtx insn)
+{
+ /* Instructions with unfilled delay slots take up an extra two bytes for
+ the nop in the delay slot. */
+ if (((NONJUMP_INSN_P (insn)
+ && GET_CODE (PATTERN (insn)) != USE
+ && GET_CODE (PATTERN (insn)) != CLOBBER)
+ || CALL_P (insn)
+ || (JUMP_P (insn) && !JUMP_TABLE_DATA_P (insn)))
+ && GET_CODE (PATTERN (NEXT_INSN (PREV_INSN (insn)))) != SEQUENCE
+ && get_attr_needs_delay_slot (insn) == NEEDS_DELAY_SLOT_YES)
+ return 2;
+
+ /* SH2e has a bug that prevents the use of annulled branches, so if
+ the delay slot is not filled, we'll have to put a NOP in it. */
+ if (sh_cpu_attr == CPU_SH2E
+ && JUMP_P (insn) && !JUMP_TABLE_DATA_P (insn)
+ && get_attr_type (insn) == TYPE_CBRANCH
+ && GET_CODE (PATTERN (NEXT_INSN (PREV_INSN (insn)))) != SEQUENCE)
+ return 2;
+
+ /* sh-dsp parallel processing insn take four bytes instead of two. */
+
+ if (NONJUMP_INSN_P (insn))
+ {
+ int sum = 0;
+ rtx body = PATTERN (insn);
+ const char *templ;
+ char c;
+ int maybe_label = 1;
+
+ if (GET_CODE (body) == ASM_INPUT)
+ templ = XSTR (body, 0);
+ else if (asm_noperands (body) >= 0)
+ templ
+ = decode_asm_operands (body, NULL, NULL, NULL, NULL, NULL);
+ else
+ return 0;
+ do
+ {
+ int ppi_adjust = 0;
+
+ do
+ c = *templ++;
+ while (c == ' ' || c == '\t');
+ /* all sh-dsp parallel-processing insns start with p.
+ The only non-ppi sh insn starting with p is pref.
+ The only ppi starting with pr is prnd. */
+ if ((c == 'p' || c == 'P') && strncasecmp ("re", templ, 2))
+ ppi_adjust = 2;
+ /* The repeat pseudo-insn expands two three insns, a total of
+ six bytes in size. */
+ else if ((c == 'r' || c == 'R')
+ && ! strncasecmp ("epeat", templ, 5))
+ ppi_adjust = 4;
+ while (c && c != '\n'
+ && ! IS_ASM_LOGICAL_LINE_SEPARATOR (c, templ))
+ {
+ /* If this is a label, it is obviously not a ppi insn. */
+ if (c == ':' && maybe_label)
+ {
+ ppi_adjust = 0;
+ break;
+ }
+ else if (c == '\'' || c == '"')
+ maybe_label = 0;
+ c = *templ++;
+ }
+ sum += ppi_adjust;
+ maybe_label = c != ':';
+ }
+ while (c);
+ return sum;
+ }
+ return 0;
+}
+
+/* Return TRUE for a valid displacement for the REG+disp addressing
+ with MODE. */
+
+/* ??? The SH2e does not have the REG+disp addressing mode when loading values
+ into the FRx registers. We implement this by setting the maximum offset
+ to zero when the value is SFmode. This also restricts loading of SFmode
+ values into the integer registers, but that can't be helped. */
+
+/* The SH allows a displacement in a QI or HI amode, but only when the
+ other operand is R0. GCC doesn't handle this very well, so we forgot
+ all of that.
+
+ A legitimate index for a QI or HI is 0, SI can be any number 0..63,
+ DI can be any number 0..60. */
+
+bool
+sh_legitimate_index_p (enum machine_mode mode, rtx op)
+{
+ if (CONST_INT_P (op))
+ {
+ if (TARGET_SHMEDIA)
+ {
+ int size;
+
+ /* Check if this the address of an unaligned load / store. */
+ if (mode == VOIDmode)
+ return CONST_OK_FOR_I06 (INTVAL (op));
+
+ size = GET_MODE_SIZE (mode);
+ return (!(INTVAL (op) & (size - 1))
+ && INTVAL (op) >= -512 * size
+ && INTVAL (op) < 512 * size);
+ }
+
+ if (TARGET_SH2A)
+ {
+ if (GET_MODE_SIZE (mode) == 1
+ && (unsigned) INTVAL (op) < 4096)
+ return true;
+ }
+
+ if ((GET_MODE_SIZE (mode) == 4
+ && (unsigned) INTVAL (op) < 64
+ && !(INTVAL (op) & 3)
+ && !(TARGET_SH2E && mode == SFmode))
+ || (GET_MODE_SIZE (mode) == 4
+ && (unsigned) INTVAL (op) < 16383
+ && !(INTVAL (op) & 3) && TARGET_SH2A))
+ return true;
+
+ if ((GET_MODE_SIZE (mode) == 8
+ && (unsigned) INTVAL (op) < 60
+ && !(INTVAL (op) & 3)
+ && !((TARGET_SH4 || TARGET_SH2A) && mode == DFmode))
+ || ((GET_MODE_SIZE (mode)==8)
+ && (unsigned) INTVAL (op) < 8192
+ && !(INTVAL (op) & (TARGET_SH2A_DOUBLE ? 7 : 3))
+ && (TARGET_SH2A && mode == DFmode)))
+ return true;
+ }
+
+ return false;
+}
+
+/* Recognize an RTL expression that is a valid memory address for
+ an instruction.
+ The MODE argument is the machine mode for the MEM expression
+ that wants to use this address.
+ Allow REG
+ REG+disp
+ REG+r0
+ REG++
+ --REG */
+
+static bool
+sh_legitimate_address_p (enum machine_mode mode, rtx x, bool strict)
+{
+ if (MAYBE_BASE_REGISTER_RTX_P (x, strict))
+ return true;
+ else if ((GET_CODE (x) == POST_INC || GET_CODE (x) == PRE_DEC)
+ && ! TARGET_SHMEDIA
+ && MAYBE_BASE_REGISTER_RTX_P (XEXP (x, 0), strict))
+ return true;
+ else if (GET_CODE (x) == PLUS
+ && (mode != PSImode || reload_completed))
+ {
+ rtx xop0 = XEXP (x, 0);
+ rtx xop1 = XEXP (x, 1);
+
+ if (GET_MODE_SIZE (mode) <= 8
+ && MAYBE_BASE_REGISTER_RTX_P (xop0, strict)
+ && sh_legitimate_index_p (mode, xop1))
+ return true;
+
+ if ((ALLOW_INDEXED_ADDRESS || GET_MODE (x) == DImode
+ || ((xop0 == stack_pointer_rtx
+ || xop0 == hard_frame_pointer_rtx)
+ && REG_P (xop1) && REGNO (xop1) == R0_REG)
+ || ((xop1 == stack_pointer_rtx
+ || xop1 == hard_frame_pointer_rtx)
+ && REG_P (xop0) && REGNO (xop0) == R0_REG))
+ && ((!TARGET_SHMEDIA && GET_MODE_SIZE (mode) <= 4)
+ || (TARGET_SHMEDIA && GET_MODE_SIZE (mode) <= 8)
+ || ((TARGET_SH4 || TARGET_SH2A_DOUBLE)
+ && TARGET_FMOVD && mode == DFmode)))
+ {
+ if (MAYBE_BASE_REGISTER_RTX_P (xop1, strict)
+ && MAYBE_INDEX_REGISTER_RTX_P (xop0, strict))
+ return true;
+ if (MAYBE_INDEX_REGISTER_RTX_P (xop1, strict)
+ && MAYBE_BASE_REGISTER_RTX_P (xop0, strict))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* Return TRUE if X references a SYMBOL_REF or LABEL_REF whose symbol
+ isn't protected by a PIC unspec. */
+int
+nonpic_symbol_mentioned_p (rtx x)
+{
+ register const char *fmt;
+ register int i;
+
+ if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF
+ || GET_CODE (x) == PC)
+ return 1;
+
+ /* We don't want to look into the possible MEM location of a
+ CONST_DOUBLE, since we're not going to use it, in general. */
+ if (GET_CODE (x) == CONST_DOUBLE)
+ return 0;
+
+ if (GET_CODE (x) == UNSPEC
+ && (XINT (x, 1) == UNSPEC_PIC
+ || XINT (x, 1) == UNSPEC_GOT
+ || XINT (x, 1) == UNSPEC_GOTOFF
+ || XINT (x, 1) == UNSPEC_GOTPLT
+ || XINT (x, 1) == UNSPEC_GOTTPOFF
+ || XINT (x, 1) == UNSPEC_DTPOFF
+ || XINT (x, 1) == UNSPEC_TPOFF
+ || XINT (x, 1) == UNSPEC_PLT
+ || XINT (x, 1) == UNSPEC_SYMOFF
+ || XINT (x, 1) == UNSPEC_PCREL_SYMOFF))
+ return 0;
+
+ fmt = GET_RTX_FORMAT (GET_CODE (x));
+ for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'E')
+ {
+ register int j;
+
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ if (nonpic_symbol_mentioned_p (XVECEXP (x, i, j)))
+ return 1;
+ }
+ else if (fmt[i] == 'e' && nonpic_symbol_mentioned_p (XEXP (x, i)))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Convert a non-PIC address in `orig' to a PIC address using @GOT or
+ @GOTOFF in `reg'. */
+rtx
+legitimize_pic_address (rtx orig, enum machine_mode mode ATTRIBUTE_UNUSED,
+ rtx reg)
+{
+ if (tls_symbolic_operand (orig, Pmode) != TLS_MODEL_NONE)
+ return orig;
+
+ if (GET_CODE (orig) == LABEL_REF
+ || (GET_CODE (orig) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (orig)))
+ {
+ if (reg == 0)
+ reg = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symGOTOFF2reg (reg, orig));
+ return reg;
+ }
+ else if (GET_CODE (orig) == SYMBOL_REF)
+ {
+ if (reg == 0)
+ reg = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symGOT2reg (reg, orig));
+ return reg;
+ }
+ return orig;
+}
+
+/* Try machine-dependent ways of modifying an illegitimate address
+ to be legitimate. If we find one, return the new, valid address.
+ Otherwise, return X.
+
+ For the SH, if X is almost suitable for indexing, but the offset is
+ out of range, convert it into a normal form so that CSE has a chance
+ of reducing the number of address registers used. */
+
+static rtx
+sh_legitimize_address (rtx x, rtx oldx, enum machine_mode mode)
+{
+ if (flag_pic)
+ x = legitimize_pic_address (oldx, mode, NULL_RTX);
+
+ if (GET_CODE (x) == PLUS
+ && (GET_MODE_SIZE (mode) == 4
+ || GET_MODE_SIZE (mode) == 8)
+ && CONST_INT_P (XEXP (x, 1))
+ && BASE_REGISTER_RTX_P (XEXP (x, 0))
+ && ! TARGET_SHMEDIA
+ && ! ((TARGET_SH4 || TARGET_SH2A_DOUBLE) && mode == DFmode)
+ && ! (TARGET_SH2E && mode == SFmode))
+ {
+ rtx index_rtx = XEXP (x, 1);
+ HOST_WIDE_INT offset = INTVAL (index_rtx), offset_base;
+ rtx sum;
+
+ /* On rare occasions, we might get an unaligned pointer
+ that is indexed in a way to give an aligned address.
+ Therefore, keep the lower two bits in offset_base. */
+ /* Instead of offset_base 128..131 use 124..127, so that
+ simple add suffices. */
+ if (offset > 127)
+ offset_base = ((offset + 4) & ~60) - 4;
+ else
+ offset_base = offset & ~60;
+
+ /* Sometimes the normal form does not suit DImode. We
+ could avoid that by using smaller ranges, but that
+ would give less optimized code when SImode is
+ prevalent. */
+ if (GET_MODE_SIZE (mode) + offset - offset_base <= 64)
+ {
+ sum = expand_binop (Pmode, add_optab, XEXP (x, 0),
+ GEN_INT (offset_base), NULL_RTX, 0,
+ OPTAB_LIB_WIDEN);
+
+ return gen_rtx_PLUS (Pmode, sum, GEN_INT (offset - offset_base));
+ }
+ }
+
+ return x;
+}
+
+/* Attempt to replace *P, which is an address that needs reloading, with
+ a valid memory address for an operand of mode MODE.
+ Like for sh_legitimize_address, for the SH we try to get a normal form
+ of the address. That will allow inheritance of the address reloads. */
+
+bool
+sh_legitimize_reload_address (rtx *p, enum machine_mode mode, int opnum,
+ int itype)
+{
+ enum reload_type type = (enum reload_type) itype;
+
+ if (GET_CODE (*p) == PLUS
+ && (GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8)
+ && CONST_INT_P (XEXP (*p, 1))
+ && MAYBE_BASE_REGISTER_RTX_P (XEXP (*p, 0), true)
+ && ! TARGET_SHMEDIA
+ && ! (TARGET_SH4 && mode == DFmode)
+ && ! (mode == PSImode && type == RELOAD_FOR_INPUT_ADDRESS)
+ && (ALLOW_INDEXED_ADDRESS
+ || XEXP (*p, 0) == stack_pointer_rtx
+ || XEXP (*p, 0) == hard_frame_pointer_rtx))
+ {
+ rtx index_rtx = XEXP (*p, 1);
+ HOST_WIDE_INT offset = INTVAL (index_rtx), offset_base;
+ rtx sum;
+
+ if (TARGET_SH2A && mode == DFmode && (offset & 0x7))
+ {
+ push_reload (*p, NULL_RTX, p, NULL,
+ BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, opnum, type);
+ goto win;
+ }
+ if (TARGET_SH2E && mode == SFmode)
+ {
+ *p = copy_rtx (*p);
+ push_reload (*p, NULL_RTX, p, NULL,
+ BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, opnum, type);
+ goto win;
+ }
+ /* Instead of offset_base 128..131 use 124..127, so that
+ simple add suffices. */
+ if (offset > 127)
+ offset_base = ((offset + 4) & ~60) - 4;
+ else
+ offset_base = offset & ~60;
+ /* Sometimes the normal form does not suit DImode. We could avoid
+ that by using smaller ranges, but that would give less optimized
+ code when SImode is prevalent. */
+ if (GET_MODE_SIZE (mode) + offset - offset_base <= 64)
+ {
+ sum = gen_rtx_PLUS (Pmode, XEXP (*p, 0), GEN_INT (offset_base));
+ *p = gen_rtx_PLUS (Pmode, sum, GEN_INT (offset - offset_base));
+ push_reload (sum, NULL_RTX, &XEXP (*p, 0), NULL,
+ BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, opnum, type);
+ goto win;
+ }
+ }
+ /* We must re-recognize what we created before. */
+ else if (GET_CODE (*p) == PLUS
+ && (GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8)
+ && GET_CODE (XEXP (*p, 0)) == PLUS
+ && CONST_INT_P (XEXP (XEXP (*p, 0), 1))
+ && MAYBE_BASE_REGISTER_RTX_P (XEXP (XEXP (*p, 0), 0), true)
+ && CONST_INT_P (XEXP (*p, 1))
+ && ! TARGET_SHMEDIA
+ && ! (TARGET_SH2E && mode == SFmode))
+ {
+ /* Because this address is so complex, we know it must have
+ been created by LEGITIMIZE_RELOAD_ADDRESS before; thus,
+ it is already unshared, and needs no further unsharing. */
+ push_reload (XEXP (*p, 0), NULL_RTX, &XEXP (*p, 0), NULL,
+ BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, opnum, type);
+ goto win;
+ }
+
+ return false;
+
+ win:
+ return true;
+}
+
+/* In the name of slightly smaller debug output, and to cater to
+ general assembler lossage, recognize various UNSPEC sequences
+ and turn them back into a direct symbol reference. */
+
+static rtx
+sh_delegitimize_address (rtx orig_x)
+{
+ rtx x, y;
+
+ orig_x = delegitimize_mem_from_attrs (orig_x);
+
+ x = orig_x;
+ if (MEM_P (x))
+ x = XEXP (x, 0);
+ if (GET_CODE (x) == CONST)
+ {
+ y = XEXP (x, 0);
+ if (GET_CODE (y) == UNSPEC)
+ {
+ if (XINT (y, 1) == UNSPEC_GOT
+ || XINT (y, 1) == UNSPEC_GOTOFF)
+ return XVECEXP (y, 0, 0);
+ else if (TARGET_SHMEDIA
+ && (XINT (y, 1) == UNSPEC_EXTRACT_S16
+ || XINT (y, 1) == UNSPEC_EXTRACT_U16))
+ {
+ rtx offset = XVECEXP (y, 0, 1);
+
+ x = gen_rtx_PLUS (Pmode, XVECEXP (y, 0, 0), offset);
+ if (MEM_P (orig_x))
+ x = replace_equiv_address_nv (orig_x, x);
+ return x;
+ }
+ }
+ }
+
+ return orig_x;
+}
+
+/* Mark the use of a constant in the literal table. If the constant
+ has multiple labels, make it unique. */
+static rtx
+mark_constant_pool_use (rtx x)
+{
+ rtx insn, lab, pattern;
+
+ if (x == NULL)
+ return x;
+
+ switch (GET_CODE (x))
+ {
+ case LABEL_REF:
+ x = XEXP (x, 0);
+ case CODE_LABEL:
+ break;
+ default:
+ return x;
+ }
+
+ /* Get the first label in the list of labels for the same constant
+ and delete another labels in the list. */
+ lab = x;
+ for (insn = PREV_INSN (x); insn; insn = PREV_INSN (insn))
+ {
+ if (!LABEL_P (insn)
+ || LABEL_REFS (insn) != NEXT_INSN (insn))
+ break;
+ lab = insn;
+ }
+
+ for (insn = LABEL_REFS (lab); insn; insn = LABEL_REFS (insn))
+ INSN_DELETED_P (insn) = 1;
+
+ /* Mark constants in a window. */
+ for (insn = NEXT_INSN (x); insn; insn = NEXT_INSN (insn))
+ {
+ if (!NONJUMP_INSN_P (insn))
+ continue;
+
+ pattern = PATTERN (insn);
+ if (GET_CODE (pattern) != UNSPEC_VOLATILE)
+ continue;
+
+ switch (XINT (pattern, 1))
+ {
+ case UNSPECV_CONST2:
+ case UNSPECV_CONST4:
+ case UNSPECV_CONST8:
+ XVECEXP (pattern, 0, 1) = const1_rtx;
+ break;
+ case UNSPECV_WINDOW_END:
+ if (XVECEXP (pattern, 0, 0) == x)
+ return lab;
+ break;
+ case UNSPECV_CONST_END:
+ return lab;
+ default:
+ break;
+ }
+ }
+
+ return lab;
+}
+
+/* Return true if it's possible to redirect BRANCH1 to the destination
+ of an unconditional jump BRANCH2. We only want to do this if the
+ resulting branch will have a short displacement. */
+int
+sh_can_redirect_branch (rtx branch1, rtx branch2)
+{
+ if (flag_expensive_optimizations && simplejump_p (branch2))
+ {
+ rtx dest = XEXP (SET_SRC (single_set (branch2)), 0);
+ rtx insn;
+ int distance;
+
+ for (distance = 0, insn = NEXT_INSN (branch1);
+ insn && distance < 256;
+ insn = PREV_INSN (insn))
+ {
+ if (insn == dest)
+ return 1;
+ else
+ distance += get_attr_length (insn);
+ }
+ for (distance = 0, insn = NEXT_INSN (branch1);
+ insn && distance < 256;
+ insn = NEXT_INSN (insn))
+ {
+ if (insn == dest)
+ return 1;
+ else
+ distance += get_attr_length (insn);
+ }
+ }
+ return 0;
+}
+
+/* Return nonzero if register old_reg can be renamed to register new_reg. */
+int
+sh_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED,
+ unsigned int new_reg)
+{
+ /* Interrupt functions can only use registers that have already been
+ saved by the prologue, even if they would normally be
+ call-clobbered. */
+
+ if (sh_cfun_interrupt_handler_p () && !df_regs_ever_live_p (new_reg))
+ return 0;
+
+ return 1;
+}
+
+/* Function to update the integer COST
+ based on the relationship between INSN that is dependent on
+ DEP_INSN through the dependence LINK. The default is to make no
+ adjustment to COST. This can be used for example to specify to
+ the scheduler that an output- or anti-dependence does not incur
+ the same cost as a data-dependence. The return value should be
+ the new value for COST. */
+static int
+sh_adjust_cost (rtx insn, rtx link ATTRIBUTE_UNUSED, rtx dep_insn, int cost)
+{
+ rtx reg, use_pat;
+
+ if (TARGET_SHMEDIA)
+ {
+ /* On SHmedia, if the dependence is an anti-dependence or
+ output-dependence, there is no cost. */
+ if (REG_NOTE_KIND (link) != 0)
+ {
+ /* However, dependencies between target register loads and
+ uses of the register in a subsequent block that are separated
+ by a conditional branch are not modelled - we have to do with
+ the anti-dependency between the target register load and the
+ conditional branch that ends the current block. */
+ if (REG_NOTE_KIND (link) == REG_DEP_ANTI
+ && GET_CODE (PATTERN (dep_insn)) == SET
+ && (get_attr_type (dep_insn) == TYPE_PT_MEDIA
+ || get_attr_type (dep_insn) == TYPE_PTABS_MEDIA)
+ && get_attr_type (insn) == TYPE_CBRANCH_MEDIA)
+ {
+ int orig_cost = cost;
+ rtx note = find_reg_note (insn, REG_BR_PROB, 0);
+ rtx target = ((! note
+ || INTVAL (XEXP (note, 0)) * 2 < REG_BR_PROB_BASE)
+ ? insn : JUMP_LABEL (insn));
+ /* On the likely path, the branch costs 1, on the unlikely path,
+ it costs 3. */
+ cost--;
+ do
+ target = next_active_insn (target);
+ while (target && ! flow_dependent_p (target, dep_insn)
+ && --cost > 0);
+ /* If two branches are executed in immediate succession, with the
+ first branch properly predicted, this causes a stall at the
+ second branch, hence we won't need the target for the
+ second branch for two cycles after the launch of the first
+ branch. */
+ if (cost > orig_cost - 2)
+ cost = orig_cost - 2;
+ }
+ else
+ cost = 0;
+ }
+
+ else if (get_attr_is_mac_media (insn)
+ && get_attr_is_mac_media (dep_insn))
+ cost = 1;
+
+ else if (! reload_completed
+ && GET_CODE (PATTERN (insn)) == SET
+ && GET_CODE (SET_SRC (PATTERN (insn))) == FLOAT
+ && GET_CODE (PATTERN (dep_insn)) == SET
+ && fp_arith_reg_operand (SET_SRC (PATTERN (dep_insn)), VOIDmode)
+ && cost < 4)
+ cost = 4;
+ /* Schedule the ptabs for a casesi_jump_media in preference to stuff
+ that is needed at the target. */
+ else if (get_attr_type (insn) == TYPE_JUMP_MEDIA
+ && ! flow_dependent_p (insn, dep_insn))
+ cost--;
+ }
+ else if (REG_NOTE_KIND (link) == 0)
+ {
+ enum attr_type type;
+ rtx dep_set;
+
+ if (recog_memoized (insn) < 0
+ || recog_memoized (dep_insn) < 0)
+ return cost;
+
+ dep_set = single_set (dep_insn);
+
+ /* The latency that we specify in the scheduling description refers
+ to the actual output, not to an auto-increment register; for that,
+ the latency is one. */
+ if (dep_set && MEM_P (SET_SRC (dep_set)) && cost > 1)
+ {
+ rtx set = single_set (insn);
+
+ if (set
+ && !reg_mentioned_p (SET_DEST (dep_set), SET_SRC (set))
+ && (!MEM_P (SET_DEST (set))
+ || !reg_mentioned_p (SET_DEST (dep_set),
+ XEXP (SET_DEST (set), 0))))
+ cost = 1;
+ }
+ /* The only input for a call that is timing-critical is the
+ function's address. */
+ if (CALL_P (insn))
+ {
+ rtx call = PATTERN (insn);
+
+ if (GET_CODE (call) == PARALLEL)
+ call = XVECEXP (call, 0 ,0);
+ if (GET_CODE (call) == SET)
+ call = SET_SRC (call);
+ if (GET_CODE (call) == CALL && MEM_P (XEXP (call, 0))
+ /* sibcalli_thunk uses a symbol_ref in an unspec. */
+ && (GET_CODE (XEXP (XEXP (call, 0), 0)) == UNSPEC
+ || ! reg_set_p (XEXP (XEXP (call, 0), 0), dep_insn)))
+ cost -= TARGET_SH4_300 ? 3 : 6;
+ }
+ /* Likewise, the most timing critical input for an sfuncs call
+ is the function address. However, sfuncs typically start
+ using their arguments pretty quickly.
+ Assume a four cycle delay for SH4 before they are needed.
+ Cached ST40-300 calls are quicker, so assume only a one
+ cycle delay there.
+ ??? Maybe we should encode the delays till input registers
+ are needed by sfuncs into the sfunc call insn. */
+ /* All sfunc calls are parallels with at least four components.
+ Exploit this to avoid unnecessary calls to sfunc_uses_reg. */
+ else if (GET_CODE (PATTERN (insn)) == PARALLEL
+ && XVECLEN (PATTERN (insn), 0) >= 4
+ && (reg = sfunc_uses_reg (insn)))
+ {
+ if (! reg_set_p (reg, dep_insn))
+ cost -= TARGET_SH4_300 ? 1 : 4;
+ }
+ if (TARGET_HARD_SH4 && !TARGET_SH4_300)
+ {
+ enum attr_type dep_type = get_attr_type (dep_insn);
+
+ if (dep_type == TYPE_FLOAD || dep_type == TYPE_PCFLOAD)
+ cost--;
+ else if ((dep_type == TYPE_LOAD_SI || dep_type == TYPE_PCLOAD_SI)
+ && (type = get_attr_type (insn)) != TYPE_CALL
+ && type != TYPE_SFUNC)
+ cost--;
+ /* When the preceding instruction loads the shift amount of
+ the following SHAD/SHLD, the latency of the load is increased
+ by 1 cycle. */
+ if (get_attr_type (insn) == TYPE_DYN_SHIFT
+ && get_attr_any_int_load (dep_insn) == ANY_INT_LOAD_YES
+ && reg_overlap_mentioned_p (SET_DEST (dep_set),
+ XEXP (SET_SRC (single_set (insn)),
+ 1)))
+ cost++;
+ /* When an LS group instruction with a latency of less than
+ 3 cycles is followed by a double-precision floating-point
+ instruction, FIPR, or FTRV, the latency of the first
+ instruction is increased to 3 cycles. */
+ else if (cost < 3
+ && get_attr_insn_class (dep_insn) == INSN_CLASS_LS_GROUP
+ && get_attr_dfp_comp (insn) == DFP_COMP_YES)
+ cost = 3;
+ /* The lsw register of a double-precision computation is ready one
+ cycle earlier. */
+ else if (reload_completed
+ && get_attr_dfp_comp (dep_insn) == DFP_COMP_YES
+ && (use_pat = single_set (insn))
+ && ! regno_use_in (REGNO (SET_DEST (single_set (dep_insn))),
+ SET_SRC (use_pat)))
+ cost -= 1;
+
+ if (get_attr_any_fp_comp (dep_insn) == ANY_FP_COMP_YES
+ && get_attr_late_fp_use (insn) == LATE_FP_USE_YES)
+ cost -= 1;
+ }
+ else if (TARGET_SH4_300)
+ {
+ /* Stores need their input register two cycles later. */
+ if (dep_set && cost >= 1
+ && ((type = get_attr_type (insn)) == TYPE_STORE
+ || type == TYPE_PSTORE
+ || type == TYPE_FSTORE || type == TYPE_MAC_MEM))
+ {
+ rtx set = single_set (insn);
+
+ if (!reg_mentioned_p (SET_SRC (set), XEXP (SET_DEST (set), 0))
+ && rtx_equal_p (SET_SRC (set), SET_DEST (dep_set)))
+ {
+ cost -= 2;
+ /* But don't reduce the cost below 1 if the address depends
+ on a side effect of dep_insn. */
+ if (cost < 1
+ && modified_in_p (XEXP (SET_DEST (set), 0), dep_insn))
+ cost = 1;
+ }
+ }
+ }
+ }
+ /* An anti-dependence penalty of two applies if the first insn is a double
+ precision fadd / fsub / fmul. */
+ else if (!TARGET_SH4_300
+ && REG_NOTE_KIND (link) == REG_DEP_ANTI
+ && recog_memoized (dep_insn) >= 0
+ && (get_attr_type (dep_insn) == TYPE_DFP_ARITH
+ || get_attr_type (dep_insn) == TYPE_DFP_MUL)
+ /* A lot of alleged anti-flow dependences are fake,
+ so check this one is real. */
+ && flow_dependent_p (dep_insn, insn))
+ cost = 2;
+
+ return cost;
+}
+
+/* Check if INSN is flow-dependent on DEP_INSN. Can also be used to check
+ if DEP_INSN is anti-flow dependent on INSN. */
+static int
+flow_dependent_p (rtx insn, rtx dep_insn)
+{
+ rtx tmp = PATTERN (insn);
+
+ note_stores (PATTERN (dep_insn), flow_dependent_p_1, &tmp);
+ return tmp == NULL_RTX;
+}
+
+/* A helper function for flow_dependent_p called through note_stores. */
+static void
+flow_dependent_p_1 (rtx x, const_rtx pat ATTRIBUTE_UNUSED, void *data)
+{
+ rtx * pinsn = (rtx *) data;
+
+ if (*pinsn && reg_referenced_p (x, *pinsn))
+ *pinsn = NULL_RTX;
+}
+
+/* For use by sh_allocate_initial_value. Note that sh.md contains some
+ 'special function' patterns (type sfunc) that clobber pr, but that
+ do not look like function calls to leaf_function_p. Hence we must
+ do this extra check. */
+static int
+sh_pr_n_sets (void)
+{
+ return DF_REG_DEF_COUNT (TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG);
+}
+
+/* Return where to allocate pseudo for a given hard register initial
+ value. */
+static rtx
+sh_allocate_initial_value (rtx hard_reg)
+{
+ rtx x;
+
+ if (REGNO (hard_reg) == (TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG))
+ {
+ if (current_function_is_leaf
+ && ! sh_pr_n_sets ()
+ && ! (TARGET_SHCOMPACT
+ && ((crtl->args.info.call_cookie
+ & ~ CALL_COOKIE_RET_TRAMP (1))
+ || crtl->saves_all_registers)))
+ x = hard_reg;
+ else
+ x = gen_frame_mem (Pmode, return_address_pointer_rtx);
+ }
+ else
+ x = NULL_RTX;
+
+ return x;
+}
+
+/* This function returns "2" to indicate dual issue for the SH4
+ processor. To be used by the DFA pipeline description. */
+static int
+sh_issue_rate (void)
+{
+ if (TARGET_SUPERSCALAR)
+ return 2;
+ else
+ return 1;
+}
+
+/* Functions for ready queue reordering for sched1. */
+
+/* Get weight for mode for a set x. */
+static short
+find_set_regmode_weight (rtx x, enum machine_mode mode)
+{
+ if (GET_CODE (x) == CLOBBER && register_operand (SET_DEST (x), mode))
+ return 1;
+ if (GET_CODE (x) == SET && register_operand (SET_DEST (x), mode))
+ {
+ if (REG_P (SET_DEST (x)))
+ {
+ if (!reg_mentioned_p (SET_DEST (x), SET_SRC (x)))
+ return 1;
+ else
+ return 0;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+/* Get regmode weight for insn. */
+static short
+find_insn_regmode_weight (rtx insn, enum machine_mode mode)
+{
+ short reg_weight = 0;
+ rtx x;
+
+ /* Increment weight for each register born here. */
+ x = PATTERN (insn);
+ reg_weight += find_set_regmode_weight (x, mode);
+ if (GET_CODE (x) == PARALLEL)
+ {
+ int j;
+ for (j = XVECLEN (x, 0) - 1; j >= 0; j--)
+ {
+ x = XVECEXP (PATTERN (insn), 0, j);
+ reg_weight += find_set_regmode_weight (x, mode);
+ }
+ }
+ /* Decrement weight for each register that dies here. */
+ for (x = REG_NOTES (insn); x; x = XEXP (x, 1))
+ {
+ if (REG_NOTE_KIND (x) == REG_DEAD || REG_NOTE_KIND (x) == REG_UNUSED)
+ {
+ rtx note = XEXP (x, 0);
+ if (REG_P (note) && GET_MODE (note) == mode)
+ reg_weight--;
+ }
+ }
+ return reg_weight;
+}
+
+/* Calculate regmode weights for all insns of a basic block. */
+static void
+find_regmode_weight (basic_block b, enum machine_mode mode)
+{
+ rtx insn, next_tail, head, tail;
+
+ get_ebb_head_tail (b, b, &head, &tail);
+ next_tail = NEXT_INSN (tail);
+
+ for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
+ {
+ /* Handle register life information. */
+ if (!INSN_P (insn))
+ continue;
+
+ if (mode == SFmode)
+ INSN_REGMODE_WEIGHT (insn, mode) =
+ find_insn_regmode_weight (insn, mode) + 2 * find_insn_regmode_weight (insn, DFmode);
+ else if (mode == SImode)
+ INSN_REGMODE_WEIGHT (insn, mode) =
+ find_insn_regmode_weight (insn, mode) + 2 * find_insn_regmode_weight (insn, DImode);
+ }
+}
+
+/* Comparison function for ready queue sorting. */
+static int
+rank_for_reorder (const void *x, const void *y)
+{
+ rtx tmp = *(const rtx *) y;
+ rtx tmp2 = *(const rtx *) x;
+
+ /* The insn in a schedule group should be issued the first. */
+ if (SCHED_GROUP_P (tmp) != SCHED_GROUP_P (tmp2))
+ return SCHED_GROUP_P (tmp2) ? 1 : -1;
+
+ /* If insns are equally good, sort by INSN_LUID (original insn order), This
+ minimizes instruction movement, thus minimizing sched's effect on
+ register pressure. */
+ return INSN_LUID (tmp) - INSN_LUID (tmp2);
+}
+
+/* Resort the array A in which only element at index N may be out of order. */
+static void
+swap_reorder (rtx *a, int n)
+{
+ rtx insn = a[n - 1];
+ int i = n - 2;
+
+ while (i >= 0 && rank_for_reorder (a + i, &insn) >= 0)
+ {
+ a[i + 1] = a[i];
+ i -= 1;
+ }
+ a[i + 1] = insn;
+}
+
+#define SCHED_REORDER(READY, N_READY) \
+ do \
+ { \
+ if ((N_READY) == 2) \
+ swap_reorder (READY, N_READY); \
+ else if ((N_READY) > 2) \
+ qsort (READY, N_READY, sizeof (rtx), rank_for_reorder); \
+ } \
+ while (0)
+
+/* Sort the ready list READY by ascending priority, using the SCHED_REORDER
+ macro. */
+static void
+ready_reorder (rtx *ready, int nready)
+{
+ SCHED_REORDER (ready, nready);
+}
+
+/* Count life regions of r0 for a block. */
+static int
+find_r0_life_regions (basic_block b)
+{
+ rtx end, insn;
+ rtx pset;
+ rtx r0_reg;
+ int live;
+ int set;
+ int death = 0;
+
+ if (REGNO_REG_SET_P (df_get_live_in (b), R0_REG))
+ {
+ set = 1;
+ live = 1;
+ }
+ else
+ {
+ set = 0;
+ live = 0;
+ }
+
+ insn = BB_HEAD (b);
+ end = BB_END (b);
+ r0_reg = gen_rtx_REG (SImode, R0_REG);
+ while (1)
+ {
+ if (INSN_P (insn))
+ {
+ if (find_regno_note (insn, REG_DEAD, R0_REG))
+ {
+ death++;
+ live = 0;
+ }
+ if (!live
+ && (pset = single_set (insn))
+ && reg_overlap_mentioned_p (r0_reg, SET_DEST (pset))
+ && !find_regno_note (insn, REG_UNUSED, R0_REG))
+ {
+ set++;
+ live = 1;
+ }
+ }
+ if (insn == end)
+ break;
+ insn = NEXT_INSN (insn);
+ }
+ return set - death;
+}
+
+/* Calculate regmode weights for all insns of all basic block. */
+static void
+sh_md_init_global (FILE *dump ATTRIBUTE_UNUSED,
+ int verbose ATTRIBUTE_UNUSED,
+ int old_max_uid)
+{
+ basic_block b;
+
+ regmode_weight[0] = (short *) xcalloc (old_max_uid, sizeof (short));
+ regmode_weight[1] = (short *) xcalloc (old_max_uid, sizeof (short));
+ r0_life_regions = 0;
+
+ FOR_EACH_BB_REVERSE (b)
+ {
+ find_regmode_weight (b, SImode);
+ find_regmode_weight (b, SFmode);
+ if (!reload_completed)
+ r0_life_regions += find_r0_life_regions (b);
+ }
+
+ CURR_REGMODE_PRESSURE (SImode) = 0;
+ CURR_REGMODE_PRESSURE (SFmode) = 0;
+
+}
+
+/* Cleanup. */
+static void
+sh_md_finish_global (FILE *dump ATTRIBUTE_UNUSED,
+ int verbose ATTRIBUTE_UNUSED)
+{
+ if (regmode_weight[0])
+ {
+ free (regmode_weight[0]);
+ regmode_weight[0] = NULL;
+ }
+ if (regmode_weight[1])
+ {
+ free (regmode_weight[1]);
+ regmode_weight[1] = NULL;
+ }
+}
+
+/* The scalar modes supported differs from the default version in TImode
+ for 32-bit SHMEDIA. */
+static bool
+sh_scalar_mode_supported_p (enum machine_mode mode)
+{
+ if (TARGET_SHMEDIA32 && mode == TImode)
+ return false;
+
+ return default_scalar_mode_supported_p (mode);
+}
+
+/* Cache the can_issue_more so that we can return it from reorder2. Also,
+ keep count of register pressures on SImode and SFmode. */
+static int
+sh_variable_issue (FILE *dump ATTRIBUTE_UNUSED,
+ int sched_verbose ATTRIBUTE_UNUSED,
+ rtx insn,
+ int can_issue_more)
+{
+ if (GET_CODE (PATTERN (insn)) != USE
+ && GET_CODE (PATTERN (insn)) != CLOBBER)
+ cached_can_issue_more = can_issue_more - 1;
+ else
+ cached_can_issue_more = can_issue_more;
+
+ if (reload_completed)
+ return cached_can_issue_more;
+
+ CURR_REGMODE_PRESSURE (SImode) += INSN_REGMODE_WEIGHT (insn, SImode);
+ CURR_REGMODE_PRESSURE (SFmode) += INSN_REGMODE_WEIGHT (insn, SFmode);
+
+ return cached_can_issue_more;
+}
+
+static void
+sh_md_init (FILE *dump ATTRIBUTE_UNUSED,
+ int verbose ATTRIBUTE_UNUSED,
+ int veclen ATTRIBUTE_UNUSED)
+{
+ CURR_REGMODE_PRESSURE (SImode) = 0;
+ CURR_REGMODE_PRESSURE (SFmode) = 0;
+}
+
+/* Some magic numbers. */
+/* Pressure on register r0 can lead to spill failures. so avoid sched1 for
+ functions that already have high pressure on r0. */
+#define R0_MAX_LIFE_REGIONS 2
+/* Register Pressure thresholds for SImode and SFmode registers. */
+#define SIMODE_MAX_WEIGHT 5
+#define SFMODE_MAX_WEIGHT 10
+
+/* Return true if the pressure is high for MODE. */
+static short
+high_pressure (enum machine_mode mode)
+{
+ /* Pressure on register r0 can lead to spill failures. so avoid sched1 for
+ functions that already have high pressure on r0. */
+ if (r0_life_regions >= R0_MAX_LIFE_REGIONS)
+ return 1;
+
+ if (mode == SFmode)
+ return (CURR_REGMODE_PRESSURE (SFmode) > SFMODE_MAX_WEIGHT);
+ else
+ return (CURR_REGMODE_PRESSURE (SImode) > SIMODE_MAX_WEIGHT);
+}
+
+/* Reorder ready queue if register pressure is high. */
+static int
+sh_reorder (FILE *dump ATTRIBUTE_UNUSED,
+ int sched_verbose ATTRIBUTE_UNUSED,
+ rtx *ready,
+ int *n_readyp,
+ int clock_var ATTRIBUTE_UNUSED)
+{
+ if (reload_completed)
+ return sh_issue_rate ();
+
+ if (high_pressure (SFmode) || high_pressure (SImode))
+ {
+ ready_reorder (ready, *n_readyp);
+ }
+
+ return sh_issue_rate ();
+}
+
+/* Skip cycles if the current register pressure is high. */
+static int
+sh_reorder2 (FILE *dump ATTRIBUTE_UNUSED,
+ int sched_verbose ATTRIBUTE_UNUSED,
+ rtx *ready ATTRIBUTE_UNUSED,
+ int *n_readyp ATTRIBUTE_UNUSED,
+ int clock_var ATTRIBUTE_UNUSED)
+{
+ if (reload_completed)
+ return cached_can_issue_more;
+
+ if (high_pressure(SFmode) || high_pressure (SImode))
+ skip_cycles = 1;
+
+ return cached_can_issue_more;
+}
+
+/* Skip cycles without sorting the ready queue. This will move insn from
+ Q->R. If this is the last cycle we are skipping; allow sorting of ready
+ queue by sh_reorder. */
+
+/* Generally, skipping these many cycles are sufficient for all insns to move
+ from Q -> R. */
+#define MAX_SKIPS 8
+
+static int
+sh_dfa_new_cycle (FILE *sched_dump ATTRIBUTE_UNUSED,
+ int sched_verbose ATTRIBUTE_UNUSED,
+ rtx insn ATTRIBUTE_UNUSED,
+ int last_clock_var,
+ int clock_var,
+ int *sort_p)
+{
+ if (reload_completed)
+ return 0;
+
+ if (skip_cycles)
+ {
+ if ((clock_var - last_clock_var) < MAX_SKIPS)
+ {
+ *sort_p = 0;
+ return 1;
+ }
+ /* If this is the last cycle we are skipping, allow reordering of R. */
+ if ((clock_var - last_clock_var) == MAX_SKIPS)
+ {
+ *sort_p = 1;
+ return 1;
+ }
+ }
+
+ skip_cycles = 0;
+
+ return 0;
+}
+
+/* SHmedia requires registers for branches, so we can't generate new
+ branches past reload. */
+static bool
+sh_cannot_modify_jumps_p (void)
+{
+ return (TARGET_SHMEDIA && (reload_in_progress || reload_completed));
+}
+
+static reg_class_t
+sh_target_reg_class (void)
+{
+ return TARGET_SHMEDIA ? TARGET_REGS : NO_REGS;
+}
+
+static bool
+sh_optimize_target_register_callee_saved (bool after_prologue_epilogue_gen)
+{
+ HARD_REG_SET dummy;
+#if 0
+ rtx insn;
+#endif
+
+ if (! shmedia_space_reserved_for_target_registers)
+ return 0;
+ if (after_prologue_epilogue_gen && ! TARGET_SAVE_ALL_TARGET_REGS)
+ return 0;
+ if (calc_live_regs (&dummy) >= 6 * 8)
+ return 1;
+ return 0;
+}
+
+static bool
+sh_ms_bitfield_layout_p (const_tree record_type ATTRIBUTE_UNUSED)
+{
+ return (TARGET_SH5 || TARGET_HITACHI || sh_attr_renesas_p (record_type));
+}
+
+/*
+ On the SH1..SH4, the trampoline looks like
+ 2 0002 D202 mov.l l2,r2
+ 1 0000 D301 mov.l l1,r3
+ 3 0004 422B jmp @r2
+ 4 0006 0009 nop
+ 5 0008 00000000 l1: .long area
+ 6 000c 00000000 l2: .long function
+
+ SH5 (compact) uses r1 instead of r3 for the static chain. */
+
+
+/* Emit RTL insns to initialize the variable parts of a trampoline.
+ FNADDR is an RTX for the address of the function's pure code.
+ CXT is an RTX for the static chain value for the function. */
+
+static void
+sh_trampoline_init (rtx tramp_mem, tree fndecl, rtx cxt)
+{
+ rtx fnaddr = XEXP (DECL_RTL (fndecl), 0);
+ rtx tramp = force_reg (Pmode, XEXP (tramp_mem, 0));
+
+ if (TARGET_SHMEDIA64)
+ {
+ rtx tramp_templ;
+ int fixed_len;
+
+ rtx movi1 = GEN_INT (0xcc000010);
+ rtx shori1 = GEN_INT (0xc8000010);
+ rtx src, dst;
+
+ /* The following trampoline works within a +- 128 KB range for cxt:
+ ptb/u cxt,tr1; movi fnaddr >> 48,r0; shori fnaddr >> 32,r0;
+ shori fnaddr >> 16,r0; shori fnaddr,r0; ptabs/l r0,tr0
+ gettr tr1,r1; blink tr0,r63 */
+ /* Address rounding makes it hard to compute the exact bounds of the
+ offset for this trampoline, but we have a rather generous offset
+ range, so frame_offset should do fine as an upper bound. */
+ if (cxt == virtual_stack_vars_rtx && frame_offset < 0x20000)
+ {
+ /* ??? could optimize this trampoline initialization
+ by writing DImode words with two insns each. */
+ rtx mask = force_reg (DImode, GEN_INT (0x3fffc00));
+ rtx insn = gen_rtx_MINUS (DImode, cxt, tramp);
+ insn = gen_rtx_ASHIFT (DImode, insn, GEN_INT (10-2));
+ insn = gen_rtx_AND (DImode, insn, mask);
+ /* Or in ptb/u .,tr1 pattern */
+ insn = gen_rtx_IOR (DImode, insn, gen_int_mode (0xec000010, SImode));
+ insn = force_operand (insn, NULL_RTX);
+ insn = gen_lowpart (SImode, insn);
+ emit_move_insn (change_address (tramp_mem, SImode, NULL_RTX), insn);
+ insn = gen_rtx_LSHIFTRT (DImode, fnaddr, GEN_INT (38));
+ insn = gen_rtx_AND (DImode, insn, mask);
+ insn = force_operand (gen_rtx_IOR (DImode, movi1, insn), NULL_RTX);
+ insn = gen_lowpart (SImode, insn);
+ emit_move_insn (adjust_address (tramp_mem, SImode, 4), insn);
+ insn = gen_rtx_LSHIFTRT (DImode, fnaddr, GEN_INT (22));
+ insn = gen_rtx_AND (DImode, insn, mask);
+ insn = force_operand (gen_rtx_IOR (DImode, shori1, insn), NULL_RTX);
+ insn = gen_lowpart (SImode, insn);
+ emit_move_insn (adjust_address (tramp_mem, SImode, 8), insn);
+ insn = gen_rtx_LSHIFTRT (DImode, fnaddr, GEN_INT (6));
+ insn = gen_rtx_AND (DImode, insn, mask);
+ insn = force_operand (gen_rtx_IOR (DImode, shori1, insn), NULL_RTX);
+ insn = gen_lowpart (SImode, insn);
+ emit_move_insn (adjust_address (tramp_mem, SImode, 12), insn);
+ insn = gen_rtx_ASHIFT (DImode, fnaddr, GEN_INT (10));
+ insn = gen_rtx_AND (DImode, insn, mask);
+ insn = force_operand (gen_rtx_IOR (DImode, shori1, insn), NULL_RTX);
+ insn = gen_lowpart (SImode, insn);
+ emit_move_insn (adjust_address (tramp_mem, SImode, 16), insn);
+ emit_move_insn (adjust_address (tramp_mem, SImode, 20),
+ GEN_INT (0x6bf10600));
+ emit_move_insn (adjust_address (tramp_mem, SImode, 24),
+ GEN_INT (0x4415fc10));
+ emit_move_insn (adjust_address (tramp_mem, SImode, 28),
+ GEN_INT (0x4401fff0));
+ emit_insn (gen_ic_invalidate_line (tramp));
+ return;
+ }
+ tramp_templ = gen_rtx_SYMBOL_REF (Pmode,"__GCC_nested_trampoline");
+ fixed_len = TRAMPOLINE_SIZE - 2 * GET_MODE_SIZE (Pmode);
+
+ tramp_templ = gen_datalabel_ref (tramp_templ);
+ dst = tramp_mem;
+ src = gen_const_mem (BLKmode, tramp_templ);
+ set_mem_align (dst, 256);
+ set_mem_align (src, 64);
+ emit_block_move (dst, src, GEN_INT (fixed_len), BLOCK_OP_NORMAL);
+
+ emit_move_insn (adjust_address (tramp_mem, Pmode, fixed_len), fnaddr);
+ emit_move_insn (adjust_address (tramp_mem, Pmode,
+ fixed_len + GET_MODE_SIZE (Pmode)),
+ cxt);
+ emit_insn (gen_ic_invalidate_line (tramp));
+ return;
+ }
+ else if (TARGET_SHMEDIA)
+ {
+ /* movi fnaddr >> 16,r1; shori fnaddr,r1; ptabs/l r1,tr0
+ movi cxt >> 16,r1; shori cxt,r1; blink tr0,r63 */
+ rtx quad0 = gen_reg_rtx (DImode), cxtload = gen_reg_rtx (DImode);
+ rtx quad1 = gen_reg_rtx (DImode), quad2 = gen_reg_rtx (DImode);
+ /* movi 0,r1: 0xcc000010 shori 0,r1: c8000010 concatenated,
+ rotated 10 right, and higher 16 bit of every 32 selected. */
+ rtx movishori
+ = force_reg (V2HImode, (simplify_gen_subreg
+ (V2HImode, GEN_INT (0x4330432), SImode, 0)));
+ rtx ptabs = force_reg (DImode, GEN_INT (0x6bf10600));
+ rtx blink = force_reg (DImode, GEN_INT (0x4401fff0));
+
+ fnaddr = force_reg (SImode, fnaddr);
+ cxt = force_reg (SImode, cxt);
+ emit_insn (gen_mshflo_w_x (gen_rtx_SUBREG (V4HImode, quad0, 0),
+ gen_rtx_SUBREG (V2HImode, fnaddr, 0),
+ movishori));
+ emit_insn (gen_rotrdi3_mextr (quad0, quad0,
+ GEN_INT (TARGET_LITTLE_ENDIAN ? 24 : 56)));
+ emit_insn (gen_ashldi3_media (quad0, quad0, const2_rtx));
+ emit_move_insn (change_address (tramp_mem, DImode, NULL_RTX), quad0);
+ emit_insn (gen_mshflo_w_x (gen_rtx_SUBREG (V4HImode, cxtload, 0),
+ gen_rtx_SUBREG (V2HImode, cxt, 0),
+ movishori));
+ emit_insn (gen_rotrdi3_mextr (cxtload, cxtload,
+ GEN_INT (TARGET_LITTLE_ENDIAN ? 24 : 56)));
+ emit_insn (gen_ashldi3_media (cxtload, cxtload, const2_rtx));
+ if (TARGET_LITTLE_ENDIAN)
+ {
+ emit_insn (gen_mshflo_l_di (quad1, ptabs, cxtload));
+ emit_insn (gen_mextr4 (quad2, cxtload, blink));
+ }
+ else
+ {
+ emit_insn (gen_mextr4 (quad1, cxtload, ptabs));
+ emit_insn (gen_mshflo_l_di (quad2, blink, cxtload));
+ }
+ emit_move_insn (adjust_address (tramp_mem, DImode, 8), quad1);
+ emit_move_insn (adjust_address (tramp_mem, DImode, 16), quad2);
+ emit_insn (gen_ic_invalidate_line (tramp));
+ return;
+ }
+ else if (TARGET_SHCOMPACT)
+ {
+ emit_insn (gen_initialize_trampoline (tramp, cxt, fnaddr));
+ return;
+ }
+ emit_move_insn (change_address (tramp_mem, SImode, NULL_RTX),
+ gen_int_mode (TARGET_LITTLE_ENDIAN ? 0xd301d202 : 0xd202d301,
+ SImode));
+ emit_move_insn (adjust_address (tramp_mem, SImode, 4),
+ gen_int_mode (TARGET_LITTLE_ENDIAN ? 0x0009422b : 0x422b0009,
+ SImode));
+ emit_move_insn (adjust_address (tramp_mem, SImode, 8), cxt);
+ emit_move_insn (adjust_address (tramp_mem, SImode, 12), fnaddr);
+ if (TARGET_HARVARD)
+ {
+ if (!TARGET_INLINE_IC_INVALIDATE
+ || (!(TARGET_SH4A_ARCH || TARGET_SH4_300) && TARGET_USERMODE))
+ emit_library_call (function_symbol (NULL, "__ic_invalidate",
+ FUNCTION_ORDINARY),
+ LCT_NORMAL, VOIDmode, 1, tramp, SImode);
+ else
+ emit_insn (gen_ic_invalidate_line (tramp));
+ }
+}
+
+/* On SH5, trampolines are SHmedia code, so add 1 to the address. */
+
+static rtx
+sh_trampoline_adjust_address (rtx tramp)
+{
+ if (TARGET_SHMEDIA)
+ tramp = expand_simple_binop (Pmode, PLUS, tramp, const1_rtx,
+ gen_reg_rtx (Pmode), 0, OPTAB_LIB_WIDEN);
+ return tramp;
+}
+
+/* FIXME: This is overly conservative. A SHcompact function that
+ receives arguments ``by reference'' will have them stored in its
+ own stack frame, so it must not pass pointers or references to
+ these arguments to other functions by means of sibling calls. */
+/* If PIC, we cannot make sibling calls to global functions
+ because the PLT requires r12 to be live. */
+static bool
+sh_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
+{
+ return (1
+ && (! TARGET_SHCOMPACT
+ || crtl->args.info.stack_regs == 0)
+ && ! sh_cfun_interrupt_handler_p ()
+ && (! flag_pic
+ || (decl && ! TREE_PUBLIC (decl))
+ || (decl && DECL_VISIBILITY (decl) != VISIBILITY_DEFAULT)));
+}
+
+/* Machine specific built-in functions. */
+
+struct builtin_description
+{
+ const enum insn_code icode;
+ const char *const name;
+ int signature;
+ tree fndecl;
+};
+
+/* describe number and signedness of arguments; arg[0] == result
+ (1: unsigned, 2: signed, 4: don't care, 8: pointer 0: no argument */
+/* 9: 64-bit pointer, 10: 32-bit pointer */
+static const char signature_args[][4] =
+{
+#define SH_BLTIN_V2SI2 0
+ { 4, 4 },
+#define SH_BLTIN_V4HI2 1
+ { 4, 4 },
+#define SH_BLTIN_V2SI3 2
+ { 4, 4, 4 },
+#define SH_BLTIN_V4HI3 3
+ { 4, 4, 4 },
+#define SH_BLTIN_V8QI3 4
+ { 4, 4, 4 },
+#define SH_BLTIN_MAC_HISI 5
+ { 1, 4, 4, 1 },
+#define SH_BLTIN_SH_HI 6
+ { 4, 4, 1 },
+#define SH_BLTIN_SH_SI 7
+ { 4, 4, 1 },
+#define SH_BLTIN_V4HI2V2SI 8
+ { 4, 4, 4 },
+#define SH_BLTIN_V4HI2V8QI 9
+ { 4, 4, 4 },
+#define SH_BLTIN_SISF 10
+ { 4, 2 },
+#define SH_BLTIN_LDUA_L 11
+ { 2, 10 },
+#define SH_BLTIN_LDUA_Q 12
+ { 1, 10 },
+#define SH_BLTIN_STUA_L 13
+ { 0, 10, 2 },
+#define SH_BLTIN_STUA_Q 14
+ { 0, 10, 1 },
+#define SH_BLTIN_LDUA_L64 15
+ { 2, 9 },
+#define SH_BLTIN_LDUA_Q64 16
+ { 1, 9 },
+#define SH_BLTIN_STUA_L64 17
+ { 0, 9, 2 },
+#define SH_BLTIN_STUA_Q64 18
+ { 0, 9, 1 },
+#define SH_BLTIN_NUM_SHARED_SIGNATURES 19
+#define SH_BLTIN_2 19
+#define SH_BLTIN_SU 19
+ { 1, 2 },
+#define SH_BLTIN_3 20
+#define SH_BLTIN_SUS 20
+ { 2, 2, 1 },
+#define SH_BLTIN_PSSV 21
+ { 0, 8, 2, 2 },
+#define SH_BLTIN_XXUU 22
+#define SH_BLTIN_UUUU 22
+ { 1, 1, 1, 1 },
+#define SH_BLTIN_PV 23
+ { 0, 8 },
+};
+/* mcmv: operands considered unsigned. */
+/* mmulsum_wq, msad_ubq: result considered unsigned long long. */
+/* mperm: control value considered unsigned int. */
+/* mshalds, mshard, mshards, mshlld, mshlrd: shift count is unsigned int. */
+/* mshards_q: returns signed short. */
+/* nsb: takes long long arg, returns unsigned char. */
+static struct builtin_description bdesc[] =
+{
+ { CODE_FOR_absv2si2, "__builtin_absv2si2", SH_BLTIN_V2SI2, 0 },
+ { CODE_FOR_absv4hi2, "__builtin_absv4hi2", SH_BLTIN_V4HI2, 0 },
+ { CODE_FOR_addv2si3, "__builtin_addv2si3", SH_BLTIN_V2SI3, 0 },
+ { CODE_FOR_addv4hi3, "__builtin_addv4hi3", SH_BLTIN_V4HI3, 0 },
+ { CODE_FOR_ssaddv2si3,"__builtin_ssaddv2si3", SH_BLTIN_V2SI3, 0 },
+ { CODE_FOR_usaddv8qi3,"__builtin_usaddv8qi3", SH_BLTIN_V8QI3, 0 },
+ { CODE_FOR_ssaddv4hi3,"__builtin_ssaddv4hi3", SH_BLTIN_V4HI3, 0 },
+ { CODE_FOR_alloco_i, "__builtin_sh_media_ALLOCO", SH_BLTIN_PV, 0 },
+ { CODE_FOR_negcmpeqv8qi,"__builtin_sh_media_MCMPEQ_B", SH_BLTIN_V8QI3, 0 },
+ { CODE_FOR_negcmpeqv2si,"__builtin_sh_media_MCMPEQ_L", SH_BLTIN_V2SI3, 0 },
+ { CODE_FOR_negcmpeqv4hi,"__builtin_sh_media_MCMPEQ_W", SH_BLTIN_V4HI3, 0 },
+ { CODE_FOR_negcmpgtuv8qi,"__builtin_sh_media_MCMPGT_UB", SH_BLTIN_V8QI3, 0 },
+ { CODE_FOR_negcmpgtv2si,"__builtin_sh_media_MCMPGT_L", SH_BLTIN_V2SI3, 0 },
+ { CODE_FOR_negcmpgtv4hi,"__builtin_sh_media_MCMPGT_W", SH_BLTIN_V4HI3, 0 },
+ { CODE_FOR_mcmv, "__builtin_sh_media_MCMV", SH_BLTIN_UUUU, 0 },
+ { CODE_FOR_mcnvs_lw, "__builtin_sh_media_MCNVS_LW", SH_BLTIN_3, 0 },
+ { CODE_FOR_mcnvs_wb, "__builtin_sh_media_MCNVS_WB", SH_BLTIN_V4HI2V8QI, 0 },
+ { CODE_FOR_mcnvs_wub, "__builtin_sh_media_MCNVS_WUB", SH_BLTIN_V4HI2V8QI, 0 },
+ { CODE_FOR_mextr1, "__builtin_sh_media_MEXTR1", SH_BLTIN_V8QI3, 0 },
+ { CODE_FOR_mextr2, "__builtin_sh_media_MEXTR2", SH_BLTIN_V8QI3, 0 },
+ { CODE_FOR_mextr3, "__builtin_sh_media_MEXTR3", SH_BLTIN_V8QI3, 0 },
+ { CODE_FOR_mextr4, "__builtin_sh_media_MEXTR4", SH_BLTIN_V8QI3, 0 },
+ { CODE_FOR_mextr5, "__builtin_sh_media_MEXTR5", SH_BLTIN_V8QI3, 0 },
+ { CODE_FOR_mextr6, "__builtin_sh_media_MEXTR6", SH_BLTIN_V8QI3, 0 },
+ { CODE_FOR_mextr7, "__builtin_sh_media_MEXTR7", SH_BLTIN_V8QI3, 0 },
+ { CODE_FOR_mmacfx_wl, "__builtin_sh_media_MMACFX_WL", SH_BLTIN_MAC_HISI, 0 },
+ { CODE_FOR_mmacnfx_wl,"__builtin_sh_media_MMACNFX_WL", SH_BLTIN_MAC_HISI, 0 },
+ { CODE_FOR_mulv2si3, "__builtin_mulv2si3", SH_BLTIN_V2SI3, 0 },
+ { CODE_FOR_mulv4hi3, "__builtin_mulv4hi3", SH_BLTIN_V4HI3, 0 },
+ { CODE_FOR_mmulfx_l, "__builtin_sh_media_MMULFX_L", SH_BLTIN_V2SI3, 0 },
+ { CODE_FOR_mmulfx_w, "__builtin_sh_media_MMULFX_W", SH_BLTIN_V4HI3, 0 },
+ { CODE_FOR_mmulfxrp_w,"__builtin_sh_media_MMULFXRP_W", SH_BLTIN_V4HI3, 0 },
+ { CODE_FOR_mmulhi_wl, "__builtin_sh_media_MMULHI_WL", SH_BLTIN_V4HI2V2SI, 0 },
+ { CODE_FOR_mmullo_wl, "__builtin_sh_media_MMULLO_WL", SH_BLTIN_V4HI2V2SI, 0 },
+ { CODE_FOR_mmulsum_wq,"__builtin_sh_media_MMULSUM_WQ", SH_BLTIN_XXUU, 0 },
+ { CODE_FOR_mperm_w, "__builtin_sh_media_MPERM_W", SH_BLTIN_SH_HI, 0 },
+ { CODE_FOR_msad_ubq, "__builtin_sh_media_MSAD_UBQ", SH_BLTIN_XXUU, 0 },
+ { CODE_FOR_mshalds_l, "__builtin_sh_media_MSHALDS_L", SH_BLTIN_SH_SI, 0 },
+ { CODE_FOR_mshalds_w, "__builtin_sh_media_MSHALDS_W", SH_BLTIN_SH_HI, 0 },
+ { CODE_FOR_ashrv2si3, "__builtin_ashrv2si3", SH_BLTIN_SH_SI, 0 },
+ { CODE_FOR_ashrv4hi3, "__builtin_ashrv4hi3", SH_BLTIN_SH_HI, 0 },
+ { CODE_FOR_mshards_q, "__builtin_sh_media_MSHARDS_Q", SH_BLTIN_SUS, 0 },
+ { CODE_FOR_mshfhi_b, "__builtin_sh_media_MSHFHI_B", SH_BLTIN_V8QI3, 0 },
+ { CODE_FOR_mshfhi_l, "__builtin_sh_media_MSHFHI_L", SH_BLTIN_V2SI3, 0 },
+ { CODE_FOR_mshfhi_w, "__builtin_sh_media_MSHFHI_W", SH_BLTIN_V4HI3, 0 },
+ { CODE_FOR_mshflo_b, "__builtin_sh_media_MSHFLO_B", SH_BLTIN_V8QI3, 0 },
+ { CODE_FOR_mshflo_l, "__builtin_sh_media_MSHFLO_L", SH_BLTIN_V2SI3, 0 },
+ { CODE_FOR_mshflo_w, "__builtin_sh_media_MSHFLO_W", SH_BLTIN_V4HI3, 0 },
+ { CODE_FOR_ashlv2si3, "__builtin_ashlv2si3", SH_BLTIN_SH_SI, 0 },
+ { CODE_FOR_ashlv4hi3, "__builtin_ashlv4hi3", SH_BLTIN_SH_HI, 0 },
+ { CODE_FOR_lshrv2si3, "__builtin_lshrv2si3", SH_BLTIN_SH_SI, 0 },
+ { CODE_FOR_lshrv4hi3, "__builtin_lshrv4hi3", SH_BLTIN_SH_HI, 0 },
+ { CODE_FOR_subv2si3, "__builtin_subv2si3", SH_BLTIN_V2SI3, 0 },
+ { CODE_FOR_subv4hi3, "__builtin_subv4hi3", SH_BLTIN_V4HI3, 0 },
+ { CODE_FOR_sssubv2si3,"__builtin_sssubv2si3", SH_BLTIN_V2SI3, 0 },
+ { CODE_FOR_ussubv8qi3,"__builtin_ussubv8qi3", SH_BLTIN_V8QI3, 0 },
+ { CODE_FOR_sssubv4hi3,"__builtin_sssubv4hi3", SH_BLTIN_V4HI3, 0 },
+ { CODE_FOR_fcosa_s, "__builtin_sh_media_FCOSA_S", SH_BLTIN_SISF, 0 },
+ { CODE_FOR_fsina_s, "__builtin_sh_media_FSINA_S", SH_BLTIN_SISF, 0 },
+ { CODE_FOR_fipr, "__builtin_sh_media_FIPR_S", SH_BLTIN_3, 0 },
+ { CODE_FOR_ftrv, "__builtin_sh_media_FTRV_S", SH_BLTIN_3, 0 },
+ { CODE_FOR_mac_media, "__builtin_sh_media_FMAC_S", SH_BLTIN_3, 0 },
+ { CODE_FOR_sqrtdf2, "__builtin_sh_media_FSQRT_D", SH_BLTIN_2, 0 },
+ { CODE_FOR_sqrtsf2, "__builtin_sh_media_FSQRT_S", SH_BLTIN_2, 0 },
+ { CODE_FOR_fsrra_s, "__builtin_sh_media_FSRRA_S", SH_BLTIN_2, 0 },
+ { CODE_FOR_ldhi_l, "__builtin_sh_media_LDHI_L", SH_BLTIN_LDUA_L, 0 },
+ { CODE_FOR_ldhi_q, "__builtin_sh_media_LDHI_Q", SH_BLTIN_LDUA_Q, 0 },
+ { CODE_FOR_ldlo_l, "__builtin_sh_media_LDLO_L", SH_BLTIN_LDUA_L, 0 },
+ { CODE_FOR_ldlo_q, "__builtin_sh_media_LDLO_Q", SH_BLTIN_LDUA_Q, 0 },
+ { CODE_FOR_sthi_l, "__builtin_sh_media_STHI_L", SH_BLTIN_STUA_L, 0 },
+ { CODE_FOR_sthi_q, "__builtin_sh_media_STHI_Q", SH_BLTIN_STUA_Q, 0 },
+ { CODE_FOR_stlo_l, "__builtin_sh_media_STLO_L", SH_BLTIN_STUA_L, 0 },
+ { CODE_FOR_stlo_q, "__builtin_sh_media_STLO_Q", SH_BLTIN_STUA_Q, 0 },
+ { CODE_FOR_ldhi_l64, "__builtin_sh_media_LDHI_L", SH_BLTIN_LDUA_L64, 0 },
+ { CODE_FOR_ldhi_q64, "__builtin_sh_media_LDHI_Q", SH_BLTIN_LDUA_Q64, 0 },
+ { CODE_FOR_ldlo_l64, "__builtin_sh_media_LDLO_L", SH_BLTIN_LDUA_L64, 0 },
+ { CODE_FOR_ldlo_q64, "__builtin_sh_media_LDLO_Q", SH_BLTIN_LDUA_Q64, 0 },
+ { CODE_FOR_sthi_l64, "__builtin_sh_media_STHI_L", SH_BLTIN_STUA_L64, 0 },
+ { CODE_FOR_sthi_q64, "__builtin_sh_media_STHI_Q", SH_BLTIN_STUA_Q64, 0 },
+ { CODE_FOR_stlo_l64, "__builtin_sh_media_STLO_L", SH_BLTIN_STUA_L64, 0 },
+ { CODE_FOR_stlo_q64, "__builtin_sh_media_STLO_Q", SH_BLTIN_STUA_Q64, 0 },
+ { CODE_FOR_nsb, "__builtin_sh_media_NSB", SH_BLTIN_SU, 0 },
+ { CODE_FOR_byterev, "__builtin_sh_media_BYTEREV", SH_BLTIN_2, 0 },
+ { CODE_FOR_prefetch, "__builtin_sh_media_PREFO", SH_BLTIN_PSSV, 0 },
+};
+
+static void
+sh_media_init_builtins (void)
+{
+ tree shared[SH_BLTIN_NUM_SHARED_SIGNATURES];
+ struct builtin_description *d;
+
+ memset (shared, 0, sizeof shared);
+ for (d = bdesc; d - bdesc < (int) ARRAY_SIZE (bdesc); d++)
+ {
+ tree type, arg_type = 0;
+ int signature = d->signature;
+ int i;
+
+ if (signature < SH_BLTIN_NUM_SHARED_SIGNATURES && shared[signature])
+ type = shared[signature];
+ else
+ {
+ int has_result = signature_args[signature][0] != 0;
+
+ if ((signature_args[signature][1] & 8)
+ && (((signature_args[signature][1] & 1) && TARGET_SHMEDIA32)
+ || ((signature_args[signature][1] & 2) && TARGET_SHMEDIA64)))
+ continue;
+ if (! TARGET_FPU_ANY
+ && FLOAT_MODE_P (insn_data[d->icode].operand[0].mode))
+ continue;
+ type = void_list_node;
+ for (i = 3; ; i--)
+ {
+ int arg = signature_args[signature][i];
+ int opno = i - 1 + has_result;
+
+ if (arg & 8)
+ arg_type = ptr_type_node;
+ else if (arg)
+ arg_type = (*lang_hooks.types.type_for_mode)
+ (insn_data[d->icode].operand[opno].mode,
+ (arg & 1));
+ else if (i)
+ continue;
+ else
+ arg_type = void_type_node;
+ if (i == 0)
+ break;
+ type = tree_cons (NULL_TREE, arg_type, type);
+ }
+ type = build_function_type (arg_type, type);
+ if (signature < SH_BLTIN_NUM_SHARED_SIGNATURES)
+ shared[signature] = type;
+ }
+ d->fndecl =
+ add_builtin_function (d->name, type, d - bdesc, BUILT_IN_MD,
+ NULL, NULL_TREE);
+ }
+}
+
+/* Returns the shmedia builtin decl for CODE. */
+
+static tree
+sh_media_builtin_decl (unsigned code, bool initialize_p ATTRIBUTE_UNUSED)
+{
+ if (code >= ARRAY_SIZE (bdesc))
+ return error_mark_node;
+
+ return bdesc[code].fndecl;
+}
+
+/* Implements target hook vector_mode_supported_p. */
+bool
+sh_vector_mode_supported_p (enum machine_mode mode)
+{
+ if (TARGET_FPU_ANY
+ && ((mode == V2SFmode)
+ || (mode == V4SFmode)
+ || (mode == V16SFmode)))
+ return true;
+
+ else if (TARGET_SHMEDIA
+ && ((mode == V8QImode)
+ || (mode == V2HImode)
+ || (mode == V4HImode)
+ || (mode == V2SImode)))
+ return true;
+
+ return false;
+}
+
+bool
+sh_frame_pointer_required (void)
+{
+/* If needed override this in other tm.h files to cope with various OS
+ lossage requiring a frame pointer. */
+ if (SUBTARGET_FRAME_POINTER_REQUIRED)
+ return true;
+
+ if (crtl->profile)
+ return true;
+
+ return false;
+}
+
+/* Implements target hook dwarf_calling_convention. Return an enum
+ of dwarf_calling_convention. */
+int
+sh_dwarf_calling_convention (const_tree func)
+{
+ if (sh_attr_renesas_p (func))
+ return DW_CC_GNU_renesas_sh;
+
+ return DW_CC_normal;
+}
+
+static void
+sh_init_builtins (void)
+{
+ if (TARGET_SHMEDIA)
+ sh_media_init_builtins ();
+}
+
+/* Returns the sh builtin decl for CODE. */
+
+static tree
+sh_builtin_decl (unsigned code, bool initialize_p ATTRIBUTE_UNUSED)
+{
+ if (TARGET_SHMEDIA)
+ return sh_media_builtin_decl (code, initialize_p);
+
+ return error_mark_node;
+}
+
+/* Expand an expression EXP that calls a built-in function,
+ with result going to TARGET if that's convenient
+ (and in mode MODE if that's convenient).
+ SUBTARGET may be used as the target for computing one of EXP's operands.
+ IGNORE is nonzero if the value is to be ignored. */
+
+static rtx
+sh_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
+ enum machine_mode mode ATTRIBUTE_UNUSED, int ignore)
+{
+ tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
+ unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
+ const struct builtin_description *d = &bdesc[fcode];
+ enum insn_code icode = d->icode;
+ int signature = d->signature;
+ enum machine_mode tmode = VOIDmode;
+ int nop = 0, i;
+ rtx op[4];
+ rtx pat = 0;
+
+ if (signature_args[signature][0])
+ {
+ if (ignore)
+ return 0;
+
+ tmode = insn_data[icode].operand[0].mode;
+ if (! target
+ || GET_MODE (target) != tmode
+ || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
+ target = gen_reg_rtx (tmode);
+ op[nop++] = target;
+ }
+ else
+ target = 0;
+
+ for (i = 1; i <= 3; i++, nop++)
+ {
+ tree arg;
+ enum machine_mode opmode, argmode;
+ tree optype;
+
+ if (! signature_args[signature][i])
+ break;
+ arg = CALL_EXPR_ARG (exp, i - 1);
+ if (arg == error_mark_node)
+ return const0_rtx;
+ if (signature_args[signature][i] & 8)
+ {
+ opmode = ptr_mode;
+ optype = ptr_type_node;
+ }
+ else
+ {
+ opmode = insn_data[icode].operand[nop].mode;
+ optype = (*lang_hooks.types.type_for_mode) (opmode, 0);
+ }
+ argmode = TYPE_MODE (TREE_TYPE (arg));
+ if (argmode != opmode)
+ arg = build1 (NOP_EXPR, optype, arg);
+ op[nop] = expand_expr (arg, NULL_RTX, opmode, EXPAND_NORMAL);
+ if (! (*insn_data[icode].operand[nop].predicate) (op[nop], opmode))
+ op[nop] = copy_to_mode_reg (opmode, op[nop]);
+ }
+
+ switch (nop)
+ {
+ case 1:
+ pat = (*insn_data[d->icode].genfun) (op[0]);
+ break;
+ case 2:
+ pat = (*insn_data[d->icode].genfun) (op[0], op[1]);
+ break;
+ case 3:
+ pat = (*insn_data[d->icode].genfun) (op[0], op[1], op[2]);
+ break;
+ case 4:
+ pat = (*insn_data[d->icode].genfun) (op[0], op[1], op[2], op[3]);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ if (! pat)
+ return 0;
+ emit_insn (pat);
+ return target;
+}
+
+void
+sh_expand_unop_v2sf (enum rtx_code code, rtx op0, rtx op1)
+{
+ rtx sel0 = const0_rtx;
+ rtx sel1 = const1_rtx;
+ rtx (*fn) (rtx, rtx, rtx, rtx, rtx) = gen_unary_sf_op;
+ rtx op = gen_rtx_fmt_e (code, SFmode, op1);
+
+ emit_insn ((*fn) (op0, op1, op, sel0, sel0));
+ emit_insn ((*fn) (op0, op1, op, sel1, sel1));
+}
+
+void
+sh_expand_binop_v2sf (enum rtx_code code, rtx op0, rtx op1, rtx op2)
+{
+ rtx op = gen_rtx_fmt_ee (code, SFmode, op1, op2);
+
+ emit_insn (gen_binary_sf_op0 (op0, op1, op2, op));
+ emit_insn (gen_binary_sf_op1 (op0, op1, op2, op));
+}
+
+/* Return true if hard register REGNO can hold a value of machine-mode MODE.
+ We can allow any mode in any general register. The special registers
+ only allow SImode. Don't allow any mode in the PR.
+
+ We cannot hold DCmode values in the XD registers because alter_reg
+ handles subregs of them incorrectly. We could work around this by
+ spacing the XD registers like the DR registers, but this would require
+ additional memory in every compilation to hold larger register vectors.
+ We could hold SFmode / SCmode values in XD registers, but that
+ would require a tertiary reload when reloading from / to memory,
+ and a secondary reload to reload from / to general regs; that
+ seems to be a loosing proposition.
+
+ We want to allow TImode FP regs so that when V4SFmode is loaded as TImode,
+ it won't be ferried through GP registers first. */
+
+bool
+sh_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
+{
+ if (SPECIAL_REGISTER_P (regno))
+ return mode == SImode;
+
+ if (regno == FPUL_REG)
+ return (mode == SImode || mode == SFmode);
+
+ if (FP_REGISTER_P (regno) && mode == SFmode)
+ return true;
+
+ if (mode == V2SFmode)
+ {
+ if (((FP_REGISTER_P (regno) && (regno - FIRST_FP_REG) % 2 == 0)
+ || GENERAL_REGISTER_P (regno)))
+ return true;
+ else
+ return false;
+ }
+
+ if (mode == V4SFmode)
+ {
+ if ((FP_REGISTER_P (regno) && (regno - FIRST_FP_REG) % 4 == 0)
+ || GENERAL_REGISTER_P (regno))
+ return true;
+ else
+ return false;
+ }
+
+ if (mode == V16SFmode)
+ {
+ if (TARGET_SHMEDIA)
+ {
+ if (FP_REGISTER_P (regno) && (regno - FIRST_FP_REG) % 16 == 0)
+ return true;
+ else
+ return false;
+ }
+ else
+ return regno == FIRST_XD_REG;
+ }
+
+ if (FP_REGISTER_P (regno))
+ {
+ if (mode == SFmode
+ || mode == SImode
+ || ((TARGET_SH2E || TARGET_SHMEDIA) && mode == SCmode)
+ || ((((TARGET_SH4 || TARGET_SH2A_DOUBLE) && mode == DFmode)
+ || mode == DCmode
+ || (TARGET_SHMEDIA
+ && (mode == DFmode || mode == DImode
+ || mode == V2SFmode || mode == TImode)))
+ && ((regno - FIRST_FP_REG) & 1) == 0)
+ || ((TARGET_SH4 || TARGET_SHMEDIA) && mode == TImode
+ && ((regno - FIRST_FP_REG) & 3) == 0))
+ return true;
+ else
+ return false;
+ }
+
+ if (XD_REGISTER_P (regno))
+ return mode == DFmode;
+
+ if (TARGET_REGISTER_P (regno))
+ return (mode == DImode || mode == SImode || mode == PDImode);
+
+ if (regno == PR_REG)
+ return mode == SImode;
+
+ if (regno == FPSCR_REG)
+ return mode == PSImode;
+
+ /* FIXME. This works around PR target/37633 for -O0. */
+ if (!optimize && TARGET_SHMEDIA32 && GET_MODE_SIZE (mode) > 4)
+ {
+ unsigned int n = GET_MODE_SIZE (mode) / 8;
+
+ if (regno >= FIRST_GENERAL_REG + 10 - n + 1
+ && regno <= FIRST_GENERAL_REG + 14)
+ return false;
+ }
+
+ return true;
+}
+
+/* Return the class of registers for which a mode change from FROM to TO
+ is invalid. */
+bool
+sh_cannot_change_mode_class (enum machine_mode from, enum machine_mode to,
+ enum reg_class rclass)
+{
+ /* We want to enable the use of SUBREGs as a means to
+ VEC_SELECT a single element of a vector. */
+ if (to == SFmode && VECTOR_MODE_P (from) && GET_MODE_INNER (from) == SFmode)
+ return (reg_classes_intersect_p (GENERAL_REGS, rclass));
+
+ if (GET_MODE_SIZE (from) != GET_MODE_SIZE (to))
+ {
+ if (TARGET_LITTLE_ENDIAN)
+ {
+ if (GET_MODE_SIZE (to) < 8 || GET_MODE_SIZE (from) < 8)
+ return reg_classes_intersect_p (DF_REGS, rclass);
+ }
+ else
+ {
+ if (GET_MODE_SIZE (from) < 8)
+ return reg_classes_intersect_p (DF_HI_REGS, rclass);
+ }
+ }
+ return 0;
+}
+
+/* Return true if registers in machine mode MODE will likely be
+ allocated to registers in small register classes. */
+
+bool
+sh_small_register_classes_for_mode_p (enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return (! TARGET_SHMEDIA);
+}
+
+/* If ADDRESS refers to a CODE_LABEL, add NUSES to the number of times
+ that label is used. */
+
+void
+sh_mark_label (rtx address, int nuses)
+{
+ if (GOTOFF_P (address))
+ {
+ /* Extract the label or symbol. */
+ address = XEXP (address, 0);
+ if (GET_CODE (address) == PLUS)
+ address = XEXP (address, 0);
+ address = XVECEXP (address, 0, 0);
+ }
+ if (GET_CODE (address) == LABEL_REF
+ && LABEL_P (XEXP (address, 0)))
+ LABEL_NUSES (XEXP (address, 0)) += nuses;
+}
+
+/* Compute extra cost of moving data between one register class
+ and another. */
+
+/* If SECONDARY*_RELOAD_CLASS says something about the src/dst pair, regclass
+ uses this information. Hence, the general register <-> floating point
+ register information here is not used for SFmode. */
+
+static int
+sh_register_move_cost (enum machine_mode mode,
+ reg_class_t srcclass, reg_class_t dstclass)
+{
+ if (dstclass == T_REGS || dstclass == PR_REGS)
+ return 10;
+
+ if (dstclass == MAC_REGS && srcclass == MAC_REGS)
+ return 4;
+
+ if (mode == SImode && ! TARGET_SHMEDIA && TARGET_FMOVD
+ && REGCLASS_HAS_FP_REG (srcclass)
+ && REGCLASS_HAS_FP_REG (dstclass))
+ return 4;
+
+ if (REGCLASS_HAS_FP_REG (dstclass) && srcclass == T_REGS)
+ return ((TARGET_HARD_SH4 && !optimize_size) ? 10 : 7);
+
+ if ((REGCLASS_HAS_FP_REG (dstclass) && srcclass == MAC_REGS)
+ || (dstclass == MAC_REGS && REGCLASS_HAS_FP_REG (srcclass)))
+ return 9;
+
+ if ((REGCLASS_HAS_FP_REG (dstclass)
+ && REGCLASS_HAS_GENERAL_REG (srcclass))
+ || (REGCLASS_HAS_GENERAL_REG (dstclass)
+ && REGCLASS_HAS_FP_REG (srcclass)))
+ return ((TARGET_SHMEDIA ? 4 : TARGET_FMOVD ? 8 : 12)
+ * ((GET_MODE_SIZE (mode) + 7) / 8U));
+
+ if ((dstclass == FPUL_REGS
+ && REGCLASS_HAS_GENERAL_REG (srcclass))
+ || (srcclass == FPUL_REGS
+ && REGCLASS_HAS_GENERAL_REG (dstclass)))
+ return 5;
+
+ if ((dstclass == FPUL_REGS
+ && (srcclass == PR_REGS || srcclass == MAC_REGS || srcclass == T_REGS))
+ || (srcclass == FPUL_REGS
+ && (dstclass == PR_REGS || dstclass == MAC_REGS)))
+ return 7;
+
+ if ((srcclass == TARGET_REGS && ! REGCLASS_HAS_GENERAL_REG (dstclass))
+ || ((dstclass) == TARGET_REGS && ! REGCLASS_HAS_GENERAL_REG (srcclass)))
+ return 20;
+
+ /* ??? ptabs faults on (value & 0x3) == 0x3 */
+ if (TARGET_SHMEDIA
+ && ((srcclass) == TARGET_REGS || (srcclass) == SIBCALL_REGS))
+ {
+ if (sh_gettrcost >= 0)
+ return sh_gettrcost;
+ else if (!TARGET_PT_FIXED)
+ return 100;
+ }
+
+ if ((srcclass == FPSCR_REGS && ! REGCLASS_HAS_GENERAL_REG (dstclass))
+ || (dstclass == FPSCR_REGS && ! REGCLASS_HAS_GENERAL_REG (srcclass)))
+ return 4;
+
+ if (TARGET_SHMEDIA
+ || (TARGET_FMOVD
+ && ! REGCLASS_HAS_GENERAL_REG (srcclass)
+ && ! REGCLASS_HAS_GENERAL_REG (dstclass)))
+ return 2 * ((GET_MODE_SIZE (mode) + 7) / 8U);
+
+ return 2 * ((GET_MODE_SIZE (mode) + 3) / 4U);
+}
+
+static rtx emit_load_ptr (rtx, rtx);
+
+static rtx
+emit_load_ptr (rtx reg, rtx addr)
+{
+ rtx mem = gen_const_mem (ptr_mode, addr);
+
+ if (Pmode != ptr_mode)
+ mem = gen_rtx_SIGN_EXTEND (Pmode, mem);
+ return emit_move_insn (reg, mem);
+}
+
+static void
+sh_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
+ HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
+ tree function)
+{
+ CUMULATIVE_ARGS cum;
+ int structure_value_byref = 0;
+ rtx this_rtx, this_value, sibcall, insns, funexp;
+ tree funtype = TREE_TYPE (function);
+ int simple_add = CONST_OK_FOR_ADD (delta);
+ int did_load = 0;
+ rtx scratch0, scratch1, scratch2;
+ unsigned i;
+
+ reload_completed = 1;
+ epilogue_completed = 1;
+ current_function_uses_only_leaf_regs = 1;
+
+ emit_note (NOTE_INSN_PROLOGUE_END);
+
+ /* Find the "this" pointer. We have such a wide range of ABIs for the
+ SH that it's best to do this completely machine independently.
+ "this" is passed as first argument, unless a structure return pointer
+ comes first, in which case "this" comes second. */
+ INIT_CUMULATIVE_ARGS (cum, funtype, NULL_RTX, 0, 1);
+#ifndef PCC_STATIC_STRUCT_RETURN
+ if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
+ structure_value_byref = 1;
+#endif /* not PCC_STATIC_STRUCT_RETURN */
+ if (structure_value_byref && sh_struct_value_rtx (function, 0) == 0)
+ {
+ tree ptype = build_pointer_type (TREE_TYPE (funtype));
+
+ sh_function_arg_advance (&cum, Pmode, ptype, true);
+ }
+ this_rtx = sh_function_arg (&cum, Pmode, ptr_type_node, true);
+
+ /* For SHcompact, we only have r0 for a scratch register: r1 is the
+ static chain pointer (even if you can't have nested virtual functions
+ right now, someone might implement them sometime), and the rest of the
+ registers are used for argument passing, are callee-saved, or reserved. */
+ /* We need to check call_used_regs / fixed_regs in case -fcall_saved-reg /
+ -ffixed-reg has been used. */
+ if (! call_used_regs[0] || fixed_regs[0])
+ error ("r0 needs to be available as a call-clobbered register");
+ scratch0 = scratch1 = scratch2 = gen_rtx_REG (Pmode, 0);
+ if (! TARGET_SH5)
+ {
+ if (call_used_regs[1] && ! fixed_regs[1])
+ scratch1 = gen_rtx_REG (ptr_mode, 1);
+ /* N.B., if not TARGET_HITACHI, register 2 is used to pass the pointer
+ pointing where to return struct values. */
+ if (call_used_regs[3] && ! fixed_regs[3])
+ scratch2 = gen_rtx_REG (Pmode, 3);
+ }
+ else if (TARGET_SHMEDIA)
+ {
+ for (i = FIRST_GENERAL_REG; i <= LAST_GENERAL_REG; i++)
+ if (i != REGNO (scratch0) &&
+ call_used_regs[i] && ! fixed_regs[i] && ! FUNCTION_ARG_REGNO_P (i))
+ {
+ scratch1 = gen_rtx_REG (ptr_mode, i);
+ break;
+ }
+ if (scratch1 == scratch0)
+ error ("need a second call-clobbered general purpose register");
+ for (i = FIRST_TARGET_REG; i <= LAST_TARGET_REG; i++)
+ if (call_used_regs[i] && ! fixed_regs[i])
+ {
+ scratch2 = gen_rtx_REG (Pmode, i);
+ break;
+ }
+ if (scratch2 == scratch0)
+ error ("need a call-clobbered target register");
+ }
+
+ this_value = plus_constant (this_rtx, delta);
+ if (vcall_offset
+ && (simple_add || scratch0 != scratch1)
+ && strict_memory_address_p (ptr_mode, this_value))
+ {
+ emit_load_ptr (scratch0, this_value);
+ did_load = 1;
+ }
+
+ if (!delta)
+ ; /* Do nothing. */
+ else if (simple_add)
+ emit_move_insn (this_rtx, this_value);
+ else
+ {
+ emit_move_insn (scratch1, GEN_INT (delta));
+ emit_insn (gen_add2_insn (this_rtx, scratch1));
+ }
+
+ if (vcall_offset)
+ {
+ rtx offset_addr;
+
+ if (!did_load)
+ emit_load_ptr (scratch0, this_rtx);
+
+ offset_addr = plus_constant (scratch0, vcall_offset);
+ if (strict_memory_address_p (ptr_mode, offset_addr))
+ ; /* Do nothing. */
+ else if (! TARGET_SH5 && scratch0 != scratch1)
+ {
+ /* scratch0 != scratch1, and we have indexed loads. Get better
+ schedule by loading the offset into r1 and using an indexed
+ load - then the load of r1 can issue before the load from
+ (this_rtx + delta) finishes. */
+ emit_move_insn (scratch1, GEN_INT (vcall_offset));
+ offset_addr = gen_rtx_PLUS (Pmode, scratch0, scratch1);
+ }
+ else if (CONST_OK_FOR_ADD (vcall_offset))
+ {
+ emit_insn (gen_add2_insn (scratch0, GEN_INT (vcall_offset)));
+ offset_addr = scratch0;
+ }
+ else if (scratch0 != scratch1)
+ {
+ emit_move_insn (scratch1, GEN_INT (vcall_offset));
+ emit_insn (gen_add2_insn (scratch0, scratch1));
+ offset_addr = scratch0;
+ }
+ else
+ gcc_unreachable (); /* FIXME */
+ emit_load_ptr (scratch0, offset_addr);
+
+ if (Pmode != ptr_mode)
+ scratch0 = gen_rtx_TRUNCATE (ptr_mode, scratch0);
+ emit_insn (gen_add2_insn (this_rtx, scratch0));
+ }
+
+ /* Generate a tail call to the target function. */
+ if (! TREE_USED (function))
+ {
+ assemble_external (function);
+ TREE_USED (function) = 1;
+ }
+ funexp = XEXP (DECL_RTL (function), 0);
+ /* If the function is overridden, so is the thunk, hence we don't
+ need GOT addressing even if this is a public symbol. */
+#if 0
+ if (TARGET_SH1 && ! flag_weak)
+ sibcall = gen_sibcalli_thunk (funexp, const0_rtx);
+ else
+#endif
+ if (TARGET_SH2 && flag_pic)
+ {
+ sibcall = gen_sibcall_pcrel (funexp, const0_rtx);
+ XEXP (XVECEXP (sibcall, 0, 2), 0) = scratch2;
+ }
+ else
+ {
+ if (TARGET_SHMEDIA && flag_pic)
+ {
+ funexp = gen_sym2PIC (funexp);
+ PUT_MODE (funexp, Pmode);
+ }
+ emit_move_insn (scratch2, funexp);
+ funexp = gen_rtx_MEM (FUNCTION_MODE, scratch2);
+ sibcall = gen_sibcall (funexp, const0_rtx, NULL_RTX);
+ }
+ sibcall = emit_call_insn (sibcall);
+ SIBLING_CALL_P (sibcall) = 1;
+ use_reg (&CALL_INSN_FUNCTION_USAGE (sibcall), this_rtx);
+ emit_barrier ();
+
+ /* Run just enough of rest_of_compilation to do scheduling and get
+ the insns emitted. Note that use_thunk calls
+ assemble_start_function and assemble_end_function. */
+
+ insn_locators_alloc ();
+ insns = get_insns ();
+
+ if (optimize > 0)
+ {
+ if (! cfun->cfg)
+ init_flow (cfun);
+ split_all_insns_noflow ();
+ }
+
+ sh_reorg ();
+
+ if (optimize > 0 && flag_delayed_branch)
+ dbr_schedule (insns);
+
+ shorten_branches (insns);
+ final_start_function (insns, file, 1);
+ final (insns, file, 1);
+ final_end_function ();
+
+ reload_completed = 0;
+ epilogue_completed = 0;
+}
+
+rtx
+function_symbol (rtx target, const char *name, enum sh_function_kind kind)
+{
+ rtx sym;
+
+ /* If this is not an ordinary function, the name usually comes from a
+ string literal or an sprintf buffer. Make sure we use the same
+ string consistently, so that cse will be able to unify address loads. */
+ if (kind != FUNCTION_ORDINARY)
+ name = IDENTIFIER_POINTER (get_identifier (name));
+ sym = gen_rtx_SYMBOL_REF (Pmode, name);
+ SYMBOL_REF_FLAGS (sym) = SYMBOL_FLAG_FUNCTION;
+ if (flag_pic)
+ switch (kind)
+ {
+ case FUNCTION_ORDINARY:
+ break;
+ case SFUNC_GOT:
+ {
+ rtx reg = target ? target : gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symGOT2reg (reg, sym));
+ sym = reg;
+ break;
+ }
+ case SFUNC_STATIC:
+ {
+ /* ??? To allow cse to work, we use GOTOFF relocations.
+ we could add combiner patterns to transform this into
+ straight pc-relative calls with sym2PIC / bsrf when
+ label load and function call are still 1:1 and in the
+ same basic block during combine. */
+ rtx reg = target ? target : gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symGOTOFF2reg (reg, sym));
+ sym = reg;
+ break;
+ }
+ }
+ if (target && sym != target)
+ {
+ emit_move_insn (target, sym);
+ return target;
+ }
+ return sym;
+}
+
+/* Find the number of a general purpose register in S. */
+static int
+scavenge_reg (HARD_REG_SET *s)
+{
+ int r;
+ for (r = FIRST_GENERAL_REG; r <= LAST_GENERAL_REG; r++)
+ if (TEST_HARD_REG_BIT (*s, r))
+ return r;
+ return -1;
+}
+
+rtx
+sh_get_pr_initial_val (void)
+{
+ rtx val;
+
+ /* ??? Unfortunately, get_hard_reg_initial_val doesn't always work for the
+ PR register on SHcompact, because it might be clobbered by the prologue.
+ We check first if that is known to be the case. */
+ if (TARGET_SHCOMPACT
+ && ((crtl->args.info.call_cookie
+ & ~ CALL_COOKIE_RET_TRAMP (1))
+ || crtl->saves_all_registers))
+ return gen_frame_mem (SImode, return_address_pointer_rtx);
+
+ /* If we haven't finished rtl generation, there might be a nonlocal label
+ that we haven't seen yet.
+ ??? get_hard_reg_initial_val fails if it is called after register
+ allocation has started, unless it has been called before for the
+ same register. And even then, we end in trouble if we didn't use
+ the register in the same basic block before. So call
+ get_hard_reg_initial_val now and wrap it in an unspec if we might
+ need to replace it. */
+ /* ??? We also must do this for TARGET_SH1 in general, because otherwise
+ combine can put the pseudo returned by get_hard_reg_initial_val into
+ instructions that need a general purpose registers, which will fail to
+ be recognized when the pseudo becomes allocated to PR. */
+ val
+ = get_hard_reg_initial_val (Pmode, TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG);
+ if (TARGET_SH1)
+ return gen_rtx_UNSPEC (SImode, gen_rtvec (1, val), UNSPEC_RA);
+ return val;
+}
+
+int
+sh_expand_t_scc (rtx operands[])
+{
+ enum rtx_code code = GET_CODE (operands[1]);
+ rtx target = operands[0];
+ rtx op0 = operands[2];
+ rtx op1 = operands[3];
+ rtx result = target;
+ HOST_WIDE_INT val;
+
+ if (!REG_P (op0) || REGNO (op0) != T_REG
+ || !CONST_INT_P (op1))
+ return 0;
+ if (!REG_P (result))
+ result = gen_reg_rtx (SImode);
+ val = INTVAL (op1);
+ if ((code == EQ && val == 1) || (code == NE && val == 0))
+ emit_insn (gen_movt (result));
+ else if (TARGET_SH2A && ((code == EQ && val == 0)
+ || (code == NE && val == 1)))
+ emit_insn (gen_xorsi3_movrt (result));
+ else if ((code == EQ && val == 0) || (code == NE && val == 1))
+ {
+ emit_clobber (result);
+ emit_insn (gen_subc (result, result, result));
+ emit_insn (gen_addsi3 (result, result, const1_rtx));
+ }
+ else if (code == EQ || code == NE)
+ emit_insn (gen_move_insn (result, GEN_INT (code == NE)));
+ else
+ return 0;
+ if (result != target)
+ emit_move_insn (target, result);
+ return 1;
+}
+
+/* INSN is an sfunc; return the rtx that describes the address used. */
+static rtx
+extract_sfunc_addr (rtx insn)
+{
+ rtx pattern, part = NULL_RTX;
+ int len, i;
+
+ pattern = PATTERN (insn);
+ len = XVECLEN (pattern, 0);
+ for (i = 0; i < len; i++)
+ {
+ part = XVECEXP (pattern, 0, i);
+ if (GET_CODE (part) == USE && GET_MODE (XEXP (part, 0)) == Pmode
+ && GENERAL_REGISTER_P (true_regnum (XEXP (part, 0))))
+ return XEXP (part, 0);
+ }
+ gcc_assert (GET_CODE (XVECEXP (pattern, 0, 0)) == UNSPEC_VOLATILE);
+ return XVECEXP (XVECEXP (pattern, 0, 0), 0, 1);
+}
+
+/* Verify that the register in use_sfunc_addr still agrees with the address
+ used in the sfunc. This prevents fill_slots_from_thread from changing
+ use_sfunc_addr.
+ INSN is the use_sfunc_addr instruction, and REG is the register it
+ guards. */
+int
+check_use_sfunc_addr (rtx insn, rtx reg)
+{
+ /* Search for the sfunc. It should really come right after INSN. */
+ while ((insn = NEXT_INSN (insn)))
+ {
+ if (LABEL_P (insn) || JUMP_P (insn))
+ break;
+ if (! INSN_P (insn))
+ continue;
+
+ if (GET_CODE (PATTERN (insn)) == SEQUENCE)
+ insn = XVECEXP (PATTERN (insn), 0, 0);
+ if (GET_CODE (PATTERN (insn)) != PARALLEL
+ || get_attr_type (insn) != TYPE_SFUNC)
+ continue;
+ return rtx_equal_p (extract_sfunc_addr (insn), reg);
+ }
+ gcc_unreachable ();
+}
+
+/* This function returns a constant rtx that represents pi / 2**15 in
+ SFmode. it's used to scale SFmode angles, in radians, to a
+ fixed-point signed 16.16-bit fraction of a full circle, i.e., 2*pi
+ maps to 0x10000). */
+
+static GTY(()) rtx sh_fsca_sf2int_rtx;
+
+rtx
+sh_fsca_sf2int (void)
+{
+ if (! sh_fsca_sf2int_rtx)
+ {
+ REAL_VALUE_TYPE rv;
+
+ real_from_string (&rv, "10430.378350470453");
+ sh_fsca_sf2int_rtx = const_double_from_real_value (rv, SFmode);
+ }
+
+ return sh_fsca_sf2int_rtx;
+}
+
+/* This function returns a constant rtx that represents pi / 2**15 in
+ DFmode. it's used to scale DFmode angles, in radians, to a
+ fixed-point signed 16.16-bit fraction of a full circle, i.e., 2*pi
+ maps to 0x10000). */
+
+static GTY(()) rtx sh_fsca_df2int_rtx;
+
+rtx
+sh_fsca_df2int (void)
+{
+ if (! sh_fsca_df2int_rtx)
+ {
+ REAL_VALUE_TYPE rv;
+
+ real_from_string (&rv, "10430.378350470453");
+ sh_fsca_df2int_rtx = const_double_from_real_value (rv, DFmode);
+ }
+
+ return sh_fsca_df2int_rtx;
+}
+
+/* This function returns a constant rtx that represents 2**15 / pi in
+ SFmode. it's used to scale a fixed-point signed 16.16-bit fraction
+ of a full circle back to a SFmode value, i.e., 0x10000 maps to
+ 2*pi). */
+
+static GTY(()) rtx sh_fsca_int2sf_rtx;
+
+rtx
+sh_fsca_int2sf (void)
+{
+ if (! sh_fsca_int2sf_rtx)
+ {
+ REAL_VALUE_TYPE rv;
+
+ real_from_string (&rv, "9.587379924285257e-5");
+ sh_fsca_int2sf_rtx = const_double_from_real_value (rv, SFmode);
+ }
+
+ return sh_fsca_int2sf_rtx;
+}
+
+/* Initialize the CUMULATIVE_ARGS structure. */
+
+void
+sh_init_cumulative_args (CUMULATIVE_ARGS * pcum,
+ tree fntype,
+ rtx libname ATTRIBUTE_UNUSED,
+ tree fndecl,
+ signed int n_named_args,
+ enum machine_mode mode)
+{
+ pcum->arg_count [(int) SH_ARG_FLOAT] = 0;
+ pcum->free_single_fp_reg = 0;
+ pcum->stack_regs = 0;
+ pcum->byref_regs = 0;
+ pcum->byref = 0;
+ pcum->outgoing = (n_named_args == -1) ? 0 : 1;
+
+ /* XXX - Should we check TARGET_HITACHI here ??? */
+ pcum->renesas_abi = sh_attr_renesas_p (fntype) ? 1 : 0;
+
+ if (fntype)
+ {
+ pcum->force_mem = ((TARGET_HITACHI || pcum->renesas_abi)
+ && aggregate_value_p (TREE_TYPE (fntype), fndecl));
+ pcum->prototype_p = prototype_p (fntype);
+ pcum->arg_count [(int) SH_ARG_INT]
+ = TARGET_SH5 && aggregate_value_p (TREE_TYPE (fntype), fndecl);
+
+ pcum->call_cookie
+ = CALL_COOKIE_RET_TRAMP (TARGET_SHCOMPACT
+ && pcum->arg_count [(int) SH_ARG_INT] == 0
+ && (TYPE_MODE (TREE_TYPE (fntype)) == BLKmode
+ ? int_size_in_bytes (TREE_TYPE (fntype))
+ : GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (fntype)))) > 4
+ && (BASE_RETURN_VALUE_REG (TYPE_MODE (TREE_TYPE (fntype)))
+ == FIRST_RET_REG));
+ }
+ else
+ {
+ pcum->arg_count [(int) SH_ARG_INT] = 0;
+ pcum->prototype_p = FALSE;
+ if (mode != VOIDmode)
+ {
+ pcum->call_cookie =
+ CALL_COOKIE_RET_TRAMP (TARGET_SHCOMPACT
+ && GET_MODE_SIZE (mode) > 4
+ && BASE_RETURN_VALUE_REG (mode) == FIRST_RET_REG);
+
+ /* If the default ABI is the Renesas ABI then all library
+ calls must assume that the library will be using the
+ Renesas ABI. So if the function would return its result
+ in memory then we must force the address of this memory
+ block onto the stack. Ideally we would like to call
+ targetm.calls.return_in_memory() here but we do not have
+ the TYPE or the FNDECL available so we synthesize the
+ contents of that function as best we can. */
+ pcum->force_mem =
+ (TARGET_DEFAULT & MASK_HITACHI)
+ && (mode == BLKmode
+ || (GET_MODE_SIZE (mode) > 4
+ && !(mode == DFmode
+ && TARGET_FPU_DOUBLE)));
+ }
+ else
+ {
+ pcum->call_cookie = 0;
+ pcum->force_mem = FALSE;
+ }
+ }
+}
+
+/* Replace any occurrence of FROM(n) in X with TO(n). The function does
+ not enter into CONST_DOUBLE for the replace.
+
+ Note that copying is not done so X must not be shared unless all copies
+ are to be modified.
+
+ This is like replace_rtx, except that we operate on N_REPLACEMENTS
+ replacements simultaneously - FROM(n) is replacements[n*2] and to(n) is
+ replacements[n*2+1] - and that we take mode changes into account.
+
+ If a replacement is ambiguous, return NULL_RTX.
+
+ If MODIFY is zero, don't modify any rtl in place,
+ just return zero or nonzero for failure / success. */
+
+rtx
+replace_n_hard_rtx (rtx x, rtx *replacements, int n_replacements, int modify)
+{
+ int i, j;
+ const char *fmt;
+
+ /* The following prevents loops occurrence when we change MEM in
+ CONST_DOUBLE onto the same CONST_DOUBLE. */
+ if (x != 0 && GET_CODE (x) == CONST_DOUBLE)
+ return x;
+
+ for (i = n_replacements - 1; i >= 0 ; i--)
+ if (x == replacements[i*2] && GET_MODE (x) == GET_MODE (replacements[i*2+1]))
+ return replacements[i*2+1];
+
+ /* Allow this function to make replacements in EXPR_LISTs. */
+ if (x == 0)
+ return 0;
+
+ if (GET_CODE (x) == SUBREG)
+ {
+ rtx new_rtx = replace_n_hard_rtx (SUBREG_REG (x), replacements,
+ n_replacements, modify);
+
+ if (CONST_INT_P (new_rtx))
+ {
+ x = simplify_subreg (GET_MODE (x), new_rtx,
+ GET_MODE (SUBREG_REG (x)),
+ SUBREG_BYTE (x));
+ if (! x)
+ abort ();
+ }
+ else if (modify)
+ SUBREG_REG (x) = new_rtx;
+
+ return x;
+ }
+ else if (REG_P (x))
+ {
+ unsigned regno = REGNO (x);
+ unsigned nregs = (regno < FIRST_PSEUDO_REGISTER
+ ? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1);
+ rtx result = NULL_RTX;
+
+ for (i = n_replacements - 1; i >= 0; i--)
+ {
+ rtx from = replacements[i*2];
+ rtx to = replacements[i*2+1];
+ unsigned from_regno, from_nregs, to_regno, new_regno;
+
+ if (!REG_P (from))
+ continue;
+ from_regno = REGNO (from);
+ from_nregs = (from_regno < FIRST_PSEUDO_REGISTER
+ ? HARD_REGNO_NREGS (from_regno, GET_MODE (from)) : 1);
+ if (regno < from_regno + from_nregs && regno + nregs > from_regno)
+ {
+ if (regno < from_regno
+ || regno + nregs > from_regno + nregs
+ || !REG_P (to)
+ || result)
+ return NULL_RTX;
+ to_regno = REGNO (to);
+ if (to_regno < FIRST_PSEUDO_REGISTER)
+ {
+ new_regno = regno + to_regno - from_regno;
+ if ((unsigned) HARD_REGNO_NREGS (new_regno, GET_MODE (x))
+ != nregs)
+ return NULL_RTX;
+ result = gen_rtx_REG (GET_MODE (x), new_regno);
+ }
+ else if (GET_MODE (x) <= GET_MODE (to))
+ result = gen_lowpart_common (GET_MODE (x), to);
+ else
+ result = gen_lowpart_SUBREG (GET_MODE (x), to);
+ }
+ }
+ return result ? result : x;
+ }
+ else if (GET_CODE (x) == ZERO_EXTEND)
+ {
+ rtx new_rtx = replace_n_hard_rtx (XEXP (x, 0), replacements,
+ n_replacements, modify);
+
+ if (CONST_INT_P (new_rtx))
+ {
+ x = simplify_unary_operation (ZERO_EXTEND, GET_MODE (x),
+ new_rtx, GET_MODE (XEXP (x, 0)));
+ if (! x)
+ abort ();
+ }
+ else if (modify)
+ XEXP (x, 0) = new_rtx;
+
+ return x;
+ }
+
+ fmt = GET_RTX_FORMAT (GET_CODE (x));
+ for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+ {
+ rtx new_rtx;
+
+ if (fmt[i] == 'e')
+ {
+ new_rtx = replace_n_hard_rtx (XEXP (x, i), replacements,
+ n_replacements, modify);
+ if (!new_rtx)
+ return NULL_RTX;
+ if (modify)
+ XEXP (x, i) = new_rtx;
+ }
+ else if (fmt[i] == 'E')
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ {
+ new_rtx = replace_n_hard_rtx (XVECEXP (x, i, j), replacements,
+ n_replacements, modify);
+ if (!new_rtx)
+ return NULL_RTX;
+ if (modify)
+ XVECEXP (x, i, j) = new_rtx;
+ }
+ }
+
+ return x;
+}
+
+rtx
+sh_gen_truncate (enum machine_mode mode, rtx x, int need_sign_ext)
+{
+ enum rtx_code code = TRUNCATE;
+
+ if (GET_CODE (x) == ZERO_EXTEND || GET_CODE (x) == SIGN_EXTEND)
+ {
+ rtx inner = XEXP (x, 0);
+ enum machine_mode inner_mode = GET_MODE (inner);
+
+ if (inner_mode == mode)
+ return inner;
+ else if (GET_MODE_SIZE (inner_mode) >= GET_MODE_SIZE (mode))
+ x = inner;
+ else if (GET_MODE_SIZE (inner_mode) < GET_MODE_SIZE (mode)
+ && (! need_sign_ext || GET_CODE (x) == SIGN_EXTEND))
+ {
+ code = GET_CODE (x);
+ x = inner;
+ }
+ }
+ return gen_rtx_fmt_e (code, mode, x);
+}
+
+/* called via for_each_rtx after reload, to clean up truncates of
+ registers that span multiple actual hard registers. */
+int
+shmedia_cleanup_truncate (rtx *p, void *n_changes)
+{
+ rtx x = *p, reg;
+
+ if (GET_CODE (x) != TRUNCATE)
+ return 0;
+ reg = XEXP (x, 0);
+ if (GET_MODE_SIZE (GET_MODE (reg)) > 8 && REG_P (reg))
+ {
+ enum machine_mode reg_mode = GET_MODE (reg);
+ XEXP (x, 0) = simplify_subreg (DImode, reg, reg_mode,
+ subreg_lowpart_offset (DImode, reg_mode));
+ *(int*) n_changes += 1;
+ return -1;
+ }
+ return 0;
+}
+
+/* Load and store depend on the highpart of the address. However,
+ set_attr_alternative does not give well-defined results before reload,
+ so we must look at the rtl ourselves to see if any of the feeding
+ registers is used in a memref. */
+
+/* Called by sh_contains_memref_p via for_each_rtx. */
+static int
+sh_contains_memref_p_1 (rtx *loc, void *data ATTRIBUTE_UNUSED)
+{
+ return (MEM_P (*loc));
+}
+
+/* Return nonzero iff INSN contains a MEM. */
+int
+sh_contains_memref_p (rtx insn)
+{
+ return for_each_rtx (&PATTERN (insn), &sh_contains_memref_p_1, NULL);
+}
+
+/* Return nonzero iff INSN loads a banked register. */
+int
+sh_loads_bankedreg_p (rtx insn)
+{
+ if (GET_CODE (PATTERN (insn)) == SET)
+ {
+ rtx op = SET_DEST (PATTERN(insn));
+ if (REG_P (op) && BANKED_REGISTER_P (REGNO (op)))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* FNADDR is the MEM expression from a call expander. Return an address
+ to use in an SHmedia insn pattern. */
+rtx
+shmedia_prepare_call_address (rtx fnaddr, int is_sibcall)
+{
+ int is_sym;
+
+ fnaddr = XEXP (fnaddr, 0);
+ is_sym = GET_CODE (fnaddr) == SYMBOL_REF;
+ if (flag_pic && is_sym)
+ {
+ if (! SYMBOL_REF_LOCAL_P (fnaddr))
+ {
+ rtx reg = gen_reg_rtx (Pmode);
+
+ /* We must not use GOTPLT for sibcalls, because PIC_REG
+ must be restored before the PLT code gets to run. */
+ if (is_sibcall)
+ emit_insn (gen_symGOT2reg (reg, fnaddr));
+ else
+ emit_insn (gen_symGOTPLT2reg (reg, fnaddr));
+ fnaddr = reg;
+ }
+ else
+ {
+ fnaddr = gen_sym2PIC (fnaddr);
+ PUT_MODE (fnaddr, Pmode);
+ }
+ }
+ /* If ptabs might trap, make this visible to the rest of the compiler.
+ We generally assume that symbols pertain to valid locations, but
+ it is possible to generate invalid symbols with asm or linker tricks.
+ In a list of functions where each returns its successor, an invalid
+ symbol might denote an empty list. */
+ if (!TARGET_PT_FIXED
+ && (!is_sym || TARGET_INVALID_SYMBOLS)
+ && (!REG_P (fnaddr) || ! TARGET_REGISTER_P (REGNO (fnaddr))))
+ {
+ rtx tr = gen_reg_rtx (PDImode);
+
+ emit_insn (gen_ptabs (tr, fnaddr));
+ fnaddr = tr;
+ }
+ else if (! target_reg_operand (fnaddr, Pmode))
+ fnaddr = copy_to_mode_reg (Pmode, fnaddr);
+ return fnaddr;
+}
+
+/* Implement TARGET_PREFERRED_RELOAD_CLASS. */
+
+static reg_class_t
+sh_preferred_reload_class (rtx x, reg_class_t rclass)
+{
+ if (rclass == NO_REGS
+ && TARGET_SHMEDIA
+ && (CONST_DOUBLE_P (x)
+ || GET_CODE (x) == SYMBOL_REF
+ || PIC_ADDR_P (x)))
+ return GENERAL_REGS;
+
+ return rclass;
+}
+
+/* Implement TARGET_SECONDARY_RELOAD. */
+
+static reg_class_t
+sh_secondary_reload (bool in_p, rtx x, reg_class_t rclass_i,
+ enum machine_mode mode, secondary_reload_info *sri)
+{
+ enum reg_class rclass = (enum reg_class) rclass_i;
+
+ if (in_p)
+ {
+ if (REGCLASS_HAS_FP_REG (rclass)
+ && ! TARGET_SHMEDIA
+ && immediate_operand ((x), mode)
+ && ! ((fp_zero_operand (x) || fp_one_operand (x))
+ && mode == SFmode && fldi_ok ()))
+ switch (mode)
+ {
+ case SFmode:
+ sri->icode = CODE_FOR_reload_insf__frn;
+ return NO_REGS;
+ case DFmode:
+ sri->icode = CODE_FOR_reload_indf__frn;
+ return NO_REGS;
+ case SImode:
+ /* ??? If we knew that we are in the appropriate mode -
+ single precision - we could use a reload pattern directly. */
+ return FPUL_REGS;
+ default:
+ abort ();
+ }
+ if (rclass == FPUL_REGS
+ && ((REG_P (x)
+ && (REGNO (x) == MACL_REG || REGNO (x) == MACH_REG
+ || REGNO (x) == T_REG))
+ || GET_CODE (x) == PLUS))
+ return GENERAL_REGS;
+ if (rclass == FPUL_REGS && immediate_operand (x, mode))
+ {
+ if (satisfies_constraint_I08 (x) || fp_zero_operand (x))
+ return GENERAL_REGS;
+ else if (mode == SFmode)
+ return FP_REGS;
+ sri->icode = CODE_FOR_reload_insi__i_fpul;
+ return NO_REGS;
+ }
+ if (rclass == FPSCR_REGS
+ && ((REG_P (x) && REGNO (x) >= FIRST_PSEUDO_REGISTER)
+ || (MEM_P (x) && GET_CODE (XEXP (x, 0)) == PLUS)))
+ return GENERAL_REGS;
+ if (REGCLASS_HAS_FP_REG (rclass)
+ && TARGET_SHMEDIA
+ && immediate_operand (x, mode)
+ && x != CONST0_RTX (GET_MODE (x))
+ && GET_MODE (x) != V4SFmode)
+ return GENERAL_REGS;
+ if ((mode == QImode || mode == HImode)
+ && TARGET_SHMEDIA && inqhi_operand (x, mode))
+ {
+ sri->icode = ((mode == QImode)
+ ? CODE_FOR_reload_inqi : CODE_FOR_reload_inhi);
+ return NO_REGS;
+ }
+ if (TARGET_SHMEDIA && rclass == GENERAL_REGS
+ && (GET_CODE (x) == LABEL_REF || PIC_ADDR_P (x)))
+ return TARGET_REGS;
+ } /* end of input-only processing. */
+
+ if (((REGCLASS_HAS_FP_REG (rclass)
+ && (REG_P (x)
+ && (GENERAL_OR_AP_REGISTER_P (REGNO (x))
+ || (FP_REGISTER_P (REGNO (x)) && mode == SImode
+ && TARGET_FMOVD))))
+ || (REGCLASS_HAS_GENERAL_REG (rclass)
+ && REG_P (x)
+ && FP_REGISTER_P (REGNO (x))))
+ && ! TARGET_SHMEDIA
+ && (mode == SFmode || mode == SImode))
+ return FPUL_REGS;
+ if ((rclass == FPUL_REGS
+ || (REGCLASS_HAS_FP_REG (rclass)
+ && ! TARGET_SHMEDIA && mode == SImode))
+ && (MEM_P (x)
+ || (REG_P (x)
+ && (REGNO (x) >= FIRST_PSEUDO_REGISTER
+ || REGNO (x) == T_REG
+ || system_reg_operand (x, VOIDmode)))))
+ {
+ if (rclass == FPUL_REGS)
+ return GENERAL_REGS;
+ return FPUL_REGS;
+ }
+ if ((rclass == TARGET_REGS
+ || (TARGET_SHMEDIA && rclass == SIBCALL_REGS))
+ && !satisfies_constraint_Csy (x)
+ && (!REG_P (x) || ! GENERAL_REGISTER_P (REGNO (x))))
+ return GENERAL_REGS;
+ if ((rclass == MAC_REGS || rclass == PR_REGS)
+ && REG_P (x) && ! GENERAL_REGISTER_P (REGNO (x))
+ && rclass != REGNO_REG_CLASS (REGNO (x)))
+ return GENERAL_REGS;
+ if (rclass != GENERAL_REGS && REG_P (x)
+ && TARGET_REGISTER_P (REGNO (x)))
+ return GENERAL_REGS;
+ return NO_REGS;
+}
+
+static void
+sh_conditional_register_usage (void)
+{
+ int regno;
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno ++)
+ if (! VALID_REGISTER_P (regno))
+ fixed_regs[regno] = call_used_regs[regno] = 1;
+ /* R8 and R9 are call-clobbered on SH5, but not on earlier SH ABIs. */
+ if (TARGET_SH5)
+ {
+ call_used_regs[FIRST_GENERAL_REG + 8]
+ = call_used_regs[FIRST_GENERAL_REG + 9] = 1;
+ call_really_used_regs[FIRST_GENERAL_REG + 8]
+ = call_really_used_regs[FIRST_GENERAL_REG + 9] = 1;
+ }
+ if (TARGET_SHMEDIA)
+ {
+ regno_reg_class[FIRST_GENERAL_REG] = GENERAL_REGS;
+ CLEAR_HARD_REG_SET (reg_class_contents[FP0_REGS]);
+ regno_reg_class[FIRST_FP_REG] = FP_REGS;
+ }
+ if (flag_pic)
+ {
+ fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
+ call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
+ }
+ /* Renesas saves and restores mac registers on call. */
+ if (TARGET_HITACHI && ! TARGET_NOMACSAVE)
+ {
+ call_really_used_regs[MACH_REG] = 0;
+ call_really_used_regs[MACL_REG] = 0;
+ }
+ for (regno = FIRST_FP_REG + (TARGET_LITTLE_ENDIAN != 0);
+ regno <= LAST_FP_REG; regno += 2)
+ SET_HARD_REG_BIT (reg_class_contents[DF_HI_REGS], regno);
+ if (TARGET_SHMEDIA)
+ {
+ for (regno = FIRST_TARGET_REG; regno <= LAST_TARGET_REG; regno ++)
+ if (! fixed_regs[regno] && call_really_used_regs[regno])
+ SET_HARD_REG_BIT (reg_class_contents[SIBCALL_REGS], regno);
+ }
+ else
+ for (regno = FIRST_GENERAL_REG; regno <= LAST_GENERAL_REG; regno++)
+ if (! fixed_regs[regno] && call_really_used_regs[regno])
+ SET_HARD_REG_BIT (reg_class_contents[SIBCALL_REGS], regno);
+}
+
+
+enum sh_divide_strategy_e sh_div_strategy = SH_DIV_STRATEGY_DEFAULT;
+
+#include "gt-sh.h"
diff --git a/gcc/config/sh/sh.h b/gcc/config/sh/sh.h
new file mode 100644
index 000000000..4579af327
--- /dev/null
+++ b/gcc/config/sh/sh.h
@@ -0,0 +1,2511 @@
+/* Definitions of target machine for GNU compiler for Renesas / SuperH SH.
+ Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+ 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+ Free Software Foundation, Inc.
+ Contributed by Steve Chamberlain (sac@cygnus.com).
+ Improved by Jim Wilson (wilson@cygnus.com).
+
+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
+<http://www.gnu.org/licenses/>. */
+
+#ifndef GCC_SH_H
+#define GCC_SH_H
+
+#include "config/vxworks-dummy.h"
+
+#define TARGET_VERSION \
+ fputs (" (Hitachi SH)", stderr);
+
+/* Unfortunately, insn-attrtab.c doesn't include insn-codes.h. We can't
+ include it here, because bconfig.h is also included by gencodes.c . */
+/* ??? No longer true. */
+extern int code_for_indirect_jump_scratch;
+
+#define TARGET_CPU_CPP_BUILTINS() \
+do { \
+ builtin_define ("__sh__"); \
+ builtin_assert ("cpu=sh"); \
+ builtin_assert ("machine=sh"); \
+ switch ((int) sh_cpu) \
+ { \
+ case PROCESSOR_SH1: \
+ builtin_define ("__sh1__"); \
+ break; \
+ case PROCESSOR_SH2: \
+ builtin_define ("__sh2__"); \
+ break; \
+ case PROCESSOR_SH2E: \
+ builtin_define ("__SH2E__"); \
+ break; \
+ case PROCESSOR_SH2A: \
+ builtin_define ("__SH2A__"); \
+ builtin_define (TARGET_SH2A_DOUBLE \
+ ? (TARGET_FPU_SINGLE ? "__SH2A_SINGLE__" : "__SH2A_DOUBLE__") \
+ : TARGET_FPU_ANY ? "__SH2A_SINGLE_ONLY__" \
+ : "__SH2A_NOFPU__"); \
+ break; \
+ case PROCESSOR_SH3: \
+ builtin_define ("__sh3__"); \
+ builtin_define ("__SH3__"); \
+ if (TARGET_HARD_SH4) \
+ builtin_define ("__SH4_NOFPU__"); \
+ break; \
+ case PROCESSOR_SH3E: \
+ builtin_define (TARGET_HARD_SH4 ? "__SH4_SINGLE_ONLY__" : "__SH3E__"); \
+ break; \
+ case PROCESSOR_SH4: \
+ builtin_define (TARGET_FPU_SINGLE ? "__SH4_SINGLE__" : "__SH4__"); \
+ break; \
+ case PROCESSOR_SH4A: \
+ builtin_define ("__SH4A__"); \
+ builtin_define (TARGET_SH4 \
+ ? (TARGET_FPU_SINGLE ? "__SH4_SINGLE__" : "__SH4__") \
+ : TARGET_FPU_ANY ? "__SH4_SINGLE_ONLY__" \
+ : "__SH4_NOFPU__"); \
+ break; \
+ case PROCESSOR_SH5: \
+ { \
+ builtin_define_with_value ("__SH5__", \
+ TARGET_SHMEDIA64 ? "64" : "32", 0); \
+ builtin_define_with_value ("__SHMEDIA__", \
+ TARGET_SHMEDIA ? "1" : "0", 0); \
+ if (! TARGET_FPU_DOUBLE) \
+ builtin_define ("__SH4_NOFPU__"); \
+ } \
+ } \
+ if (TARGET_FPU_ANY) \
+ builtin_define ("__SH_FPU_ANY__"); \
+ if (TARGET_FPU_DOUBLE) \
+ builtin_define ("__SH_FPU_DOUBLE__"); \
+ if (TARGET_HITACHI) \
+ builtin_define ("__HITACHI__"); \
+ if (TARGET_FMOVD) \
+ builtin_define ("__FMOVD_ENABLED__"); \
+ builtin_define (TARGET_LITTLE_ENDIAN \
+ ? "__LITTLE_ENDIAN__" : "__BIG_ENDIAN__"); \
+} while (0)
+
+/* Value should be nonzero if functions must have frame pointers.
+ Zero means the frame pointer need not be set up (and parms may be accessed
+ via the stack pointer) in functions that seem suitable. */
+
+#ifndef SUBTARGET_FRAME_POINTER_REQUIRED
+#define SUBTARGET_FRAME_POINTER_REQUIRED 0
+#endif
+
+
+/* Nonzero if this is an ELF target - compile time only */
+#define TARGET_ELF 0
+
+/* Nonzero if we should generate code using type 2E insns. */
+#define TARGET_SH2E (TARGET_SH2 && TARGET_SH_E)
+
+/* Nonzero if we should generate code using type 2A insns. */
+#define TARGET_SH2A TARGET_HARD_SH2A
+/* Nonzero if we should generate code using type 2A SF insns. */
+#define TARGET_SH2A_SINGLE (TARGET_SH2A && TARGET_SH2E)
+/* Nonzero if we should generate code using type 2A DF insns. */
+#define TARGET_SH2A_DOUBLE (TARGET_HARD_SH2A_DOUBLE && TARGET_SH2A)
+
+/* Nonzero if we should generate code using type 3E insns. */
+#define TARGET_SH3E (TARGET_SH3 && TARGET_SH_E)
+
+/* Nonzero if the cache line size is 32. */
+#define TARGET_CACHE32 (TARGET_HARD_SH4 || TARGET_SH5)
+
+/* Nonzero if we schedule for a superscalar implementation. */
+#define TARGET_SUPERSCALAR TARGET_HARD_SH4
+
+/* Nonzero if the target has separate instruction and data caches. */
+#define TARGET_HARVARD (TARGET_HARD_SH4 || TARGET_SH5)
+
+/* Nonzero if a double-precision FPU is available. */
+#define TARGET_FPU_DOUBLE \
+ ((target_flags & MASK_SH4) != 0 || TARGET_SH2A_DOUBLE)
+
+/* Nonzero if an FPU is available. */
+#define TARGET_FPU_ANY (TARGET_SH2E || TARGET_FPU_DOUBLE)
+
+/* Nonzero if we should generate code using type 4 insns. */
+#undef TARGET_SH4
+#define TARGET_SH4 ((target_flags & MASK_SH4) != 0 && TARGET_SH1)
+
+/* Nonzero if we're generating code for the common subset of
+ instructions present on both SH4a and SH4al-dsp. */
+#define TARGET_SH4A_ARCH TARGET_SH4A
+
+/* Nonzero if we're generating code for SH4a, unless the use of the
+ FPU is disabled (which makes it compatible with SH4al-dsp). */
+#define TARGET_SH4A_FP (TARGET_SH4A_ARCH && TARGET_FPU_ANY)
+
+/* Nonzero if we should generate code using the SHcompact instruction
+ set and 32-bit ABI. */
+#define TARGET_SHCOMPACT (TARGET_SH5 && TARGET_SH1)
+
+/* Nonzero if we should generate code using the SHmedia instruction
+ set and ABI. */
+#define TARGET_SHMEDIA (TARGET_SH5 && ! TARGET_SH1)
+
+/* Nonzero if we should generate code using the SHmedia ISA and 32-bit
+ ABI. */
+#define TARGET_SHMEDIA32 (TARGET_SH5 && ! TARGET_SH1 && TARGET_SH_E)
+
+/* Nonzero if we should generate code using the SHmedia ISA and 64-bit
+ ABI. */
+#define TARGET_SHMEDIA64 (TARGET_SH5 && ! TARGET_SH1 && ! TARGET_SH_E)
+
+/* Nonzero if we should generate code using SHmedia FPU instructions. */
+#define TARGET_SHMEDIA_FPU (TARGET_SHMEDIA && TARGET_FPU_DOUBLE)
+
+/* This is not used by the SH2E calling convention */
+#define TARGET_VARARGS_PRETEND_ARGS(FUN_DECL) \
+ (TARGET_SH1 && ! TARGET_SH2E && ! TARGET_SH5 \
+ && ! (TARGET_HITACHI || sh_attr_renesas_p (FUN_DECL)))
+
+#ifndef TARGET_CPU_DEFAULT
+#define TARGET_CPU_DEFAULT SELECT_SH1
+#define SUPPORT_SH1 1
+#define SUPPORT_SH2E 1
+#define SUPPORT_SH4 1
+#define SUPPORT_SH4_SINGLE 1
+#define SUPPORT_SH2A 1
+#define SUPPORT_SH2A_SINGLE 1
+#endif
+
+#define TARGET_DIVIDE_INV \
+ (sh_div_strategy == SH_DIV_INV || sh_div_strategy == SH_DIV_INV_MINLAT \
+ || sh_div_strategy == SH_DIV_INV20U || sh_div_strategy == SH_DIV_INV20L \
+ || sh_div_strategy == SH_DIV_INV_CALL \
+ || sh_div_strategy == SH_DIV_INV_CALL2 || sh_div_strategy == SH_DIV_INV_FP)
+#define TARGET_DIVIDE_FP (sh_div_strategy == SH_DIV_FP)
+#define TARGET_DIVIDE_INV_FP (sh_div_strategy == SH_DIV_INV_FP)
+#define TARGET_DIVIDE_CALL2 (sh_div_strategy == SH_DIV_CALL2)
+#define TARGET_DIVIDE_INV_MINLAT (sh_div_strategy == SH_DIV_INV_MINLAT)
+#define TARGET_DIVIDE_INV20U (sh_div_strategy == SH_DIV_INV20U)
+#define TARGET_DIVIDE_INV20L (sh_div_strategy == SH_DIV_INV20L)
+#define TARGET_DIVIDE_INV_CALL (sh_div_strategy == SH_DIV_INV_CALL)
+#define TARGET_DIVIDE_INV_CALL2 (sh_div_strategy == SH_DIV_INV_CALL2)
+#define TARGET_DIVIDE_CALL_DIV1 (sh_div_strategy == SH_DIV_CALL_DIV1)
+#define TARGET_DIVIDE_CALL_FP (sh_div_strategy == SH_DIV_CALL_FP)
+#define TARGET_DIVIDE_CALL_TABLE (sh_div_strategy == SH_DIV_CALL_TABLE)
+
+#define SELECT_SH1 (MASK_SH1)
+#define SELECT_SH2 (MASK_SH2 | SELECT_SH1)
+#define SELECT_SH2E (MASK_SH_E | MASK_SH2 | MASK_SH1 \
+ | MASK_FPU_SINGLE)
+#define SELECT_SH2A (MASK_SH_E | MASK_HARD_SH2A \
+ | MASK_HARD_SH2A_DOUBLE \
+ | MASK_SH2 | MASK_SH1)
+#define SELECT_SH2A_NOFPU (MASK_HARD_SH2A | MASK_SH2 | MASK_SH1)
+#define SELECT_SH2A_SINGLE_ONLY (MASK_SH_E | MASK_HARD_SH2A | MASK_SH2 \
+ | MASK_SH1 | MASK_FPU_SINGLE)
+#define SELECT_SH2A_SINGLE (MASK_SH_E | MASK_HARD_SH2A \
+ | MASK_FPU_SINGLE | MASK_HARD_SH2A_DOUBLE \
+ | MASK_SH2 | MASK_SH1)
+#define SELECT_SH3 (MASK_SH3 | SELECT_SH2)
+#define SELECT_SH3E (MASK_SH_E | MASK_FPU_SINGLE | SELECT_SH3)
+#define SELECT_SH4_NOFPU (MASK_HARD_SH4 | SELECT_SH3)
+#define SELECT_SH4_SINGLE_ONLY (MASK_HARD_SH4 | SELECT_SH3E)
+#define SELECT_SH4 (MASK_SH4 | MASK_SH_E | MASK_HARD_SH4 \
+ | SELECT_SH3)
+#define SELECT_SH4_SINGLE (MASK_FPU_SINGLE | SELECT_SH4)
+#define SELECT_SH4A_NOFPU (MASK_SH4A | SELECT_SH4_NOFPU)
+#define SELECT_SH4A_SINGLE_ONLY (MASK_SH4A | SELECT_SH4_SINGLE_ONLY)
+#define SELECT_SH4A (MASK_SH4A | SELECT_SH4)
+#define SELECT_SH4A_SINGLE (MASK_SH4A | SELECT_SH4_SINGLE)
+#define SELECT_SH5_64MEDIA (MASK_SH5 | MASK_SH4)
+#define SELECT_SH5_64MEDIA_NOFPU (MASK_SH5)
+#define SELECT_SH5_32MEDIA (MASK_SH5 | MASK_SH4 | MASK_SH_E)
+#define SELECT_SH5_32MEDIA_NOFPU (MASK_SH5 | MASK_SH_E)
+#define SELECT_SH5_COMPACT (MASK_SH5 | MASK_SH4 | SELECT_SH3E)
+#define SELECT_SH5_COMPACT_NOFPU (MASK_SH5 | SELECT_SH3)
+
+#if SUPPORT_SH1
+#define SUPPORT_SH2 1
+#endif
+#if SUPPORT_SH2
+#define SUPPORT_SH3 1
+#define SUPPORT_SH2A_NOFPU 1
+#endif
+#if SUPPORT_SH3
+#define SUPPORT_SH4_NOFPU 1
+#endif
+#if SUPPORT_SH4_NOFPU
+#define SUPPORT_SH4A_NOFPU 1
+#define SUPPORT_SH4AL 1
+#endif
+
+#if SUPPORT_SH2E
+#define SUPPORT_SH3E 1
+#define SUPPORT_SH2A_SINGLE_ONLY 1
+#endif
+#if SUPPORT_SH3E
+#define SUPPORT_SH4_SINGLE_ONLY 1
+#endif
+#if SUPPORT_SH4_SINGLE_ONLY
+#define SUPPORT_SH4A_SINGLE_ONLY 1
+#endif
+
+#if SUPPORT_SH4
+#define SUPPORT_SH4A 1
+#endif
+
+#if SUPPORT_SH4_SINGLE
+#define SUPPORT_SH4A_SINGLE 1
+#endif
+
+#if SUPPORT_SH5_COMPAT
+#define SUPPORT_SH5_32MEDIA 1
+#endif
+
+#if SUPPORT_SH5_COMPACT_NOFPU
+#define SUPPORT_SH5_32MEDIA_NOFPU 1
+#endif
+
+#define SUPPORT_ANY_SH5_32MEDIA \
+ (SUPPORT_SH5_32MEDIA || SUPPORT_SH5_32MEDIA_NOFPU)
+#define SUPPORT_ANY_SH5_64MEDIA \
+ (SUPPORT_SH5_64MEDIA || SUPPORT_SH5_64MEDIA_NOFPU)
+#define SUPPORT_ANY_SH5 \
+ (SUPPORT_ANY_SH5_32MEDIA || SUPPORT_ANY_SH5_64MEDIA)
+
+/* Reset all target-selection flags. */
+#define MASK_ARCH (MASK_SH1 | MASK_SH2 | MASK_SH3 | MASK_SH_E | MASK_SH4 \
+ | MASK_HARD_SH2A | MASK_HARD_SH2A_DOUBLE | MASK_SH4A \
+ | MASK_HARD_SH4 | MASK_FPU_SINGLE | MASK_SH5)
+
+/* This defaults us to big-endian. */
+#ifndef TARGET_ENDIAN_DEFAULT
+#define TARGET_ENDIAN_DEFAULT 0
+#endif
+
+#ifndef TARGET_OPT_DEFAULT
+#define TARGET_OPT_DEFAULT MASK_ADJUST_UNROLL
+#endif
+
+#define TARGET_DEFAULT \
+ (TARGET_CPU_DEFAULT | TARGET_ENDIAN_DEFAULT | TARGET_OPT_DEFAULT)
+
+#ifndef SH_MULTILIB_CPU_DEFAULT
+#define SH_MULTILIB_CPU_DEFAULT "m1"
+#endif
+
+#if TARGET_ENDIAN_DEFAULT
+#define MULTILIB_DEFAULTS { "ml", SH_MULTILIB_CPU_DEFAULT }
+#else
+#define MULTILIB_DEFAULTS { "mb", SH_MULTILIB_CPU_DEFAULT }
+#endif
+
+#define CPP_SPEC " %(subtarget_cpp_spec) "
+
+#ifndef SUBTARGET_CPP_SPEC
+#define SUBTARGET_CPP_SPEC ""
+#endif
+
+#ifndef SUBTARGET_EXTRA_SPECS
+#define SUBTARGET_EXTRA_SPECS
+#endif
+
+#define EXTRA_SPECS \
+ { "subtarget_cpp_spec", SUBTARGET_CPP_SPEC }, \
+ { "link_emul_prefix", LINK_EMUL_PREFIX }, \
+ { "link_default_cpu_emul", LINK_DEFAULT_CPU_EMUL }, \
+ { "subtarget_link_emul_suffix", SUBTARGET_LINK_EMUL_SUFFIX }, \
+ { "subtarget_link_spec", SUBTARGET_LINK_SPEC }, \
+ { "subtarget_asm_endian_spec", SUBTARGET_ASM_ENDIAN_SPEC }, \
+ { "subtarget_asm_relax_spec", SUBTARGET_ASM_RELAX_SPEC }, \
+ { "subtarget_asm_isa_spec", SUBTARGET_ASM_ISA_SPEC }, \
+ { "subtarget_asm_spec", SUBTARGET_ASM_SPEC }, \
+ SUBTARGET_EXTRA_SPECS
+
+#if TARGET_CPU_DEFAULT & MASK_HARD_SH4
+#define SUBTARGET_ASM_RELAX_SPEC "%{!m1:%{!m2:%{!m3*:%{!m5*:-isa=sh4-up}}}}"
+#else
+#define SUBTARGET_ASM_RELAX_SPEC "%{m4*:-isa=sh4-up}"
+#endif
+
+#define SH_ASM_SPEC \
+ "%(subtarget_asm_endian_spec) %{mrelax:-relax %(subtarget_asm_relax_spec)}\
+%(subtarget_asm_isa_spec) %(subtarget_asm_spec)\
+%{m2a:--isa=sh2a} \
+%{m2a-single:--isa=sh2a} \
+%{m2a-single-only:--isa=sh2a} \
+%{m2a-nofpu:--isa=sh2a-nofpu} \
+%{m5-compact*:--isa=SHcompact} \
+%{m5-32media*:--isa=SHmedia --abi=32} \
+%{m5-64media*:--isa=SHmedia --abi=64} \
+%{m4al:-dsp} %{mcut2-workaround:-cut2-workaround}"
+
+#define ASM_SPEC SH_ASM_SPEC
+
+#ifndef SUBTARGET_ASM_ENDIAN_SPEC
+#if TARGET_ENDIAN_DEFAULT == MASK_LITTLE_ENDIAN
+#define SUBTARGET_ASM_ENDIAN_SPEC "%{mb:-big} %{!mb:-little}"
+#else
+#define SUBTARGET_ASM_ENDIAN_SPEC "%{ml:-little} %{!ml:-big}"
+#endif
+#endif
+
+#if STRICT_NOFPU == 1
+/* Strict nofpu means that the compiler should tell the assembler
+ to reject FPU instructions. E.g. from ASM inserts. */
+#if TARGET_CPU_DEFAULT & MASK_HARD_SH4 && !(TARGET_CPU_DEFAULT & MASK_SH_E)
+#define SUBTARGET_ASM_ISA_SPEC "%{!m1:%{!m2:%{!m3*:%{m4-nofpu|!m4*:%{!m5:-isa=sh4-nofpu}}}}}"
+#else
+/* If there were an -isa option for sh5-nofpu then it would also go here. */
+#define SUBTARGET_ASM_ISA_SPEC \
+ "%{m4-nofpu:-isa=sh4-nofpu} " ASM_ISA_DEFAULT_SPEC
+#endif
+#else /* ! STRICT_NOFPU */
+#define SUBTARGET_ASM_ISA_SPEC ASM_ISA_DEFAULT_SPEC
+#endif
+
+#ifndef SUBTARGET_ASM_SPEC
+#define SUBTARGET_ASM_SPEC ""
+#endif
+
+#if TARGET_ENDIAN_DEFAULT == MASK_LITTLE_ENDIAN
+#define LINK_EMUL_PREFIX "sh%{!mb:l}"
+#else
+#define LINK_EMUL_PREFIX "sh%{ml:l}"
+#endif
+
+#if TARGET_CPU_DEFAULT & MASK_SH5
+#if TARGET_CPU_DEFAULT & MASK_SH_E
+#define LINK_DEFAULT_CPU_EMUL "32"
+#if TARGET_CPU_DEFAULT & MASK_SH1
+#define ASM_ISA_SPEC_DEFAULT "--isa=SHcompact"
+#else
+#define ASM_ISA_SPEC_DEFAULT "--isa=SHmedia --abi=32"
+#endif /* MASK_SH1 */
+#else /* !MASK_SH_E */
+#define LINK_DEFAULT_CPU_EMUL "64"
+#define ASM_ISA_SPEC_DEFAULT "--isa=SHmedia --abi=64"
+#endif /* MASK_SH_E */
+#define ASM_ISA_DEFAULT_SPEC \
+" %{!m1:%{!m2*:%{!m3*:%{!m4*:%{!m5*:" ASM_ISA_SPEC_DEFAULT "}}}}}"
+#else /* !MASK_SH5 */
+#define LINK_DEFAULT_CPU_EMUL ""
+#define ASM_ISA_DEFAULT_SPEC ""
+#endif /* MASK_SH5 */
+
+#define SUBTARGET_LINK_EMUL_SUFFIX ""
+#define SUBTARGET_LINK_SPEC ""
+
+/* Go via SH_LINK_SPEC to avoid code replication. */
+#define LINK_SPEC SH_LINK_SPEC
+
+#define SH_LINK_SPEC "\
+-m %(link_emul_prefix)\
+%{m5-compact*|m5-32media*:32}\
+%{m5-64media*:64}\
+%{!m1:%{!m2:%{!m3*:%{!m4*:%{!m5*:%(link_default_cpu_emul)}}}}}\
+%(subtarget_link_emul_suffix) \
+%{mrelax:-relax} %(subtarget_link_spec)"
+
+#ifndef SH_DIV_STR_FOR_SIZE
+#define SH_DIV_STR_FOR_SIZE "call"
+#endif
+
+#define DRIVER_SELF_SPECS "%{m2a:%{ml:%eSH2a does not support little-endian}}"
+
+#define ASSEMBLER_DIALECT assembler_dialect
+
+extern int assembler_dialect;
+
+enum sh_divide_strategy_e {
+ /* SH5 strategies. */
+ SH_DIV_CALL,
+ SH_DIV_CALL2,
+ SH_DIV_FP, /* We could do this also for SH4. */
+ SH_DIV_INV,
+ SH_DIV_INV_MINLAT,
+ SH_DIV_INV20U,
+ SH_DIV_INV20L,
+ SH_DIV_INV_CALL,
+ SH_DIV_INV_CALL2,
+ SH_DIV_INV_FP,
+ /* SH1 .. SH4 strategies. Because of the small number of registers
+ available, the compiler uses knowledge of the actual set of registers
+ being clobbered by the different functions called. */
+ SH_DIV_CALL_DIV1, /* No FPU, medium size, highest latency. */
+ SH_DIV_CALL_FP, /* FPU needed, small size, high latency. */
+ SH_DIV_CALL_TABLE, /* No FPU, large size, medium latency. */
+ SH_DIV_INTRINSIC
+};
+
+extern enum sh_divide_strategy_e sh_div_strategy;
+
+#ifndef SH_DIV_STRATEGY_DEFAULT
+#define SH_DIV_STRATEGY_DEFAULT SH_DIV_CALL
+#endif
+
+#define SUBTARGET_OVERRIDE_OPTIONS (void) 0
+
+
+/* Target machine storage layout. */
+
+/* Define this if most significant bit is lowest numbered
+ in instructions that operate on numbered bit-fields. */
+
+#define BITS_BIG_ENDIAN 0
+
+/* Define this if most significant byte of a word is the lowest numbered. */
+#define BYTES_BIG_ENDIAN (TARGET_LITTLE_ENDIAN == 0)
+
+/* Define this if most significant word of a multiword number is the lowest
+ numbered. */
+#define WORDS_BIG_ENDIAN (TARGET_LITTLE_ENDIAN == 0)
+
+#define MAX_BITS_PER_WORD 64
+
+/* Width in bits of an `int'. We want just 32-bits, even if words are
+ longer. */
+#define INT_TYPE_SIZE 32
+
+/* Width in bits of a `long'. */
+#define LONG_TYPE_SIZE (TARGET_SHMEDIA64 ? 64 : 32)
+
+/* Width in bits of a `long long'. */
+#define LONG_LONG_TYPE_SIZE 64
+
+/* Width in bits of a `long double'. */
+#define LONG_DOUBLE_TYPE_SIZE 64
+
+/* Width of a word, in units (bytes). */
+#define UNITS_PER_WORD (TARGET_SHMEDIA ? 8 : 4)
+#define MIN_UNITS_PER_WORD 4
+
+/* Scaling factor for Dwarf data offsets for CFI information.
+ The dwarf2out.c default would use -UNITS_PER_WORD, which is -8 for
+ SHmedia; however, since we do partial register saves for the registers
+ visible to SHcompact, and for target registers for SHMEDIA32, we have
+ to allow saves that are only 4-byte aligned. */
+#define DWARF_CIE_DATA_ALIGNMENT -4
+
+/* Width in bits of a pointer.
+ See also the macro `Pmode' defined below. */
+#define POINTER_SIZE (TARGET_SHMEDIA64 ? 64 : 32)
+
+/* Allocation boundary (in *bits*) for storing arguments in argument list. */
+#define PARM_BOUNDARY (TARGET_SH5 ? 64 : 32)
+
+/* Boundary (in *bits*) on which stack pointer should be aligned. */
+#define STACK_BOUNDARY BIGGEST_ALIGNMENT
+
+/* The log (base 2) of the cache line size, in bytes. Processors prior to
+ SH2 have no actual cache, but they fetch code in chunks of 4 bytes.
+ The SH2/3 have 16 byte cache lines, and the SH4 has a 32 byte cache line */
+#define CACHE_LOG (TARGET_CACHE32 ? 5 : TARGET_SH2 ? 4 : 2)
+
+/* ABI given & required minimum allocation boundary (in *bits*) for the
+ code of a function. */
+#define FUNCTION_BOUNDARY (16 << TARGET_SHMEDIA)
+
+/* On SH5, the lowest bit is used to indicate SHmedia functions, so
+ the vbit must go into the delta field of
+ pointers-to-member-functions. */
+#define TARGET_PTRMEMFUNC_VBIT_LOCATION \
+ (TARGET_SH5 ? ptrmemfunc_vbit_in_delta : ptrmemfunc_vbit_in_pfn)
+
+/* Alignment of field after `int : 0' in a structure. */
+#define EMPTY_FIELD_BOUNDARY 32
+
+/* No data type wants to be aligned rounder than this. */
+#define BIGGEST_ALIGNMENT (TARGET_ALIGN_DOUBLE ? 64 : 32)
+
+/* The best alignment to use in cases where we have a choice. */
+#define FASTEST_ALIGNMENT (TARGET_SH5 ? 64 : 32)
+
+/* Make strings word-aligned so strcpy from constants will be faster. */
+#define CONSTANT_ALIGNMENT(EXP, ALIGN) \
+ ((TREE_CODE (EXP) == STRING_CST \
+ && (ALIGN) < FASTEST_ALIGNMENT) \
+ ? FASTEST_ALIGNMENT : (ALIGN))
+
+/* get_mode_alignment assumes complex values are always held in multiple
+ registers, but that is not the case on the SH; CQImode and CHImode are
+ held in a single integer register. SH5 also holds CSImode and SCmode
+ values in integer registers. This is relevant for argument passing on
+ SHcompact as we use a stack temp in order to pass CSImode by reference. */
+#define LOCAL_ALIGNMENT(TYPE, ALIGN) \
+ ((GET_MODE_CLASS (TYPE_MODE (TYPE)) == MODE_COMPLEX_INT \
+ || GET_MODE_CLASS (TYPE_MODE (TYPE)) == MODE_COMPLEX_FLOAT) \
+ ? (unsigned) MIN (BIGGEST_ALIGNMENT, GET_MODE_BITSIZE (TYPE_MODE (TYPE))) \
+ : (unsigned) DATA_ALIGNMENT(TYPE, ALIGN))
+
+/* Make arrays of chars word-aligned for the same reasons. */
+#define DATA_ALIGNMENT(TYPE, ALIGN) \
+ (TREE_CODE (TYPE) == ARRAY_TYPE \
+ && TYPE_MODE (TREE_TYPE (TYPE)) == QImode \
+ && (ALIGN) < FASTEST_ALIGNMENT ? FASTEST_ALIGNMENT : (ALIGN))
+
+/* Number of bits which any structure or union's size must be a
+ multiple of. Each structure or union's size is rounded up to a
+ multiple of this. */
+#define STRUCTURE_SIZE_BOUNDARY (TARGET_PADSTRUCT ? 32 : 8)
+
+/* Set this nonzero if move instructions will actually fail to work
+ when given unaligned data. */
+#define STRICT_ALIGNMENT 1
+
+/* If LABEL_AFTER_BARRIER demands an alignment, return its base 2 logarithm. */
+#define LABEL_ALIGN_AFTER_BARRIER(LABEL_AFTER_BARRIER) \
+ barrier_align (LABEL_AFTER_BARRIER)
+
+#define LOOP_ALIGN(A_LABEL) \
+ ((! optimize || TARGET_HARD_SH4 || optimize_size) \
+ ? 0 : sh_loop_align (A_LABEL))
+
+#define LABEL_ALIGN(A_LABEL) \
+( \
+ (PREV_INSN (A_LABEL) \
+ && NONJUMP_INSN_P (PREV_INSN (A_LABEL)) \
+ && GET_CODE (PATTERN (PREV_INSN (A_LABEL))) == UNSPEC_VOLATILE \
+ && XINT (PATTERN (PREV_INSN (A_LABEL)), 1) == UNSPECV_ALIGN) \
+ /* explicit alignment insn in constant tables. */ \
+ ? INTVAL (XVECEXP (PATTERN (PREV_INSN (A_LABEL)), 0, 0)) \
+ : 0)
+
+/* Jump tables must be 32 bit aligned, no matter the size of the element. */
+#define ADDR_VEC_ALIGN(ADDR_VEC) 2
+
+/* The base two logarithm of the known minimum alignment of an insn length. */
+#define INSN_LENGTH_ALIGNMENT(A_INSN) \
+ (NONJUMP_INSN_P (A_INSN) \
+ ? 1 << TARGET_SHMEDIA \
+ : JUMP_P (A_INSN) || CALL_P (A_INSN) \
+ ? 1 << TARGET_SHMEDIA \
+ : CACHE_LOG)
+
+/* Standard register usage. */
+
+/* Register allocation for the Renesas calling convention:
+
+ r0 arg return
+ r1..r3 scratch
+ r4..r7 args in
+ r8..r13 call saved
+ r14 frame pointer/call saved
+ r15 stack pointer
+ ap arg pointer (doesn't really exist, always eliminated)
+ pr subroutine return address
+ t t bit
+ mach multiply/accumulate result, high part
+ macl multiply/accumulate result, low part.
+ fpul fp/int communication register
+ rap return address pointer register
+ fr0 fp arg return
+ fr1..fr3 scratch floating point registers
+ fr4..fr11 fp args in
+ fr12..fr15 call saved floating point registers */
+
+#define MAX_REGISTER_NAME_LENGTH 5
+extern char sh_register_names[][MAX_REGISTER_NAME_LENGTH + 1];
+
+#define SH_REGISTER_NAMES_INITIALIZER \
+{ \
+ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", \
+ "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", \
+ "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", \
+ "r32", "r33", "r34", "r35", "r36", "r37", "r38", "r39", \
+ "r40", "r41", "r42", "r43", "r44", "r45", "r46", "r47", \
+ "r48", "r49", "r50", "r51", "r52", "r53", "r54", "r55", \
+ "r56", "r57", "r58", "r59", "r60", "r61", "r62", "r63", \
+ "fr0", "fr1", "fr2", "fr3", "fr4", "fr5", "fr6", "fr7", \
+ "fr8", "fr9", "fr10", "fr11", "fr12", "fr13", "fr14", "fr15", \
+ "fr16", "fr17", "fr18", "fr19", "fr20", "fr21", "fr22", "fr23", \
+ "fr24", "fr25", "fr26", "fr27", "fr28", "fr29", "fr30", "fr31", \
+ "fr32", "fr33", "fr34", "fr35", "fr36", "fr37", "fr38", "fr39", \
+ "fr40", "fr41", "fr42", "fr43", "fr44", "fr45", "fr46", "fr47", \
+ "fr48", "fr49", "fr50", "fr51", "fr52", "fr53", "fr54", "fr55", \
+ "fr56", "fr57", "fr58", "fr59", "fr60", "fr61", "fr62", "fr63", \
+ "tr0", "tr1", "tr2", "tr3", "tr4", "tr5", "tr6", "tr7", \
+ "xd0", "xd2", "xd4", "xd6", "xd8", "xd10", "xd12", "xd14", \
+ "gbr", "ap", "pr", "t", "mach", "macl", "fpul", "fpscr", \
+ "rap", "sfp" \
+}
+
+#define REGNAMES_ARR_INDEX_1(index) \
+ (sh_register_names[index])
+#define REGNAMES_ARR_INDEX_2(index) \
+ REGNAMES_ARR_INDEX_1 ((index)), REGNAMES_ARR_INDEX_1 ((index)+1)
+#define REGNAMES_ARR_INDEX_4(index) \
+ REGNAMES_ARR_INDEX_2 ((index)), REGNAMES_ARR_INDEX_2 ((index)+2)
+#define REGNAMES_ARR_INDEX_8(index) \
+ REGNAMES_ARR_INDEX_4 ((index)), REGNAMES_ARR_INDEX_4 ((index)+4)
+#define REGNAMES_ARR_INDEX_16(index) \
+ REGNAMES_ARR_INDEX_8 ((index)), REGNAMES_ARR_INDEX_8 ((index)+8)
+#define REGNAMES_ARR_INDEX_32(index) \
+ REGNAMES_ARR_INDEX_16 ((index)), REGNAMES_ARR_INDEX_16 ((index)+16)
+#define REGNAMES_ARR_INDEX_64(index) \
+ REGNAMES_ARR_INDEX_32 ((index)), REGNAMES_ARR_INDEX_32 ((index)+32)
+
+#define REGISTER_NAMES \
+{ \
+ REGNAMES_ARR_INDEX_64 (0), \
+ REGNAMES_ARR_INDEX_64 (64), \
+ REGNAMES_ARR_INDEX_8 (128), \
+ REGNAMES_ARR_INDEX_8 (136), \
+ REGNAMES_ARR_INDEX_8 (144), \
+ REGNAMES_ARR_INDEX_2 (152) \
+}
+
+#define ADDREGNAMES_SIZE 32
+#define MAX_ADDITIONAL_REGISTER_NAME_LENGTH 4
+extern char sh_additional_register_names[ADDREGNAMES_SIZE] \
+ [MAX_ADDITIONAL_REGISTER_NAME_LENGTH + 1];
+
+#define SH_ADDITIONAL_REGISTER_NAMES_INITIALIZER \
+{ \
+ "dr0", "dr2", "dr4", "dr6", "dr8", "dr10", "dr12", "dr14", \
+ "dr16", "dr18", "dr20", "dr22", "dr24", "dr26", "dr28", "dr30", \
+ "dr32", "dr34", "dr36", "dr38", "dr40", "dr42", "dr44", "dr46", \
+ "dr48", "dr50", "dr52", "dr54", "dr56", "dr58", "dr60", "dr62" \
+}
+
+#define ADDREGNAMES_REGNO(index) \
+ ((index < 32) ? (FIRST_FP_REG + (index) * 2) \
+ : (-1))
+
+#define ADDREGNAMES_ARR_INDEX_1(index) \
+ { (sh_additional_register_names[index]), ADDREGNAMES_REGNO (index) }
+#define ADDREGNAMES_ARR_INDEX_2(index) \
+ ADDREGNAMES_ARR_INDEX_1 ((index)), ADDREGNAMES_ARR_INDEX_1 ((index)+1)
+#define ADDREGNAMES_ARR_INDEX_4(index) \
+ ADDREGNAMES_ARR_INDEX_2 ((index)), ADDREGNAMES_ARR_INDEX_2 ((index)+2)
+#define ADDREGNAMES_ARR_INDEX_8(index) \
+ ADDREGNAMES_ARR_INDEX_4 ((index)), ADDREGNAMES_ARR_INDEX_4 ((index)+4)
+#define ADDREGNAMES_ARR_INDEX_16(index) \
+ ADDREGNAMES_ARR_INDEX_8 ((index)), ADDREGNAMES_ARR_INDEX_8 ((index)+8)
+#define ADDREGNAMES_ARR_INDEX_32(index) \
+ ADDREGNAMES_ARR_INDEX_16 ((index)), ADDREGNAMES_ARR_INDEX_16 ((index)+16)
+
+#define ADDITIONAL_REGISTER_NAMES \
+{ \
+ ADDREGNAMES_ARR_INDEX_32 (0) \
+}
+
+/* Number of actual hardware registers.
+ The hardware registers are assigned numbers for the compiler
+ from 0 to just below FIRST_PSEUDO_REGISTER.
+ All registers that the compiler knows about must be given numbers,
+ even those that are not normally considered general registers. */
+
+/* There are many other relevant definitions in sh.md's md_constants. */
+
+#define FIRST_GENERAL_REG R0_REG
+#define LAST_GENERAL_REG (FIRST_GENERAL_REG + (TARGET_SHMEDIA ? 63 : 15))
+#define FIRST_FP_REG DR0_REG
+#define LAST_FP_REG (FIRST_FP_REG + \
+ (TARGET_SHMEDIA_FPU ? 63 : TARGET_SH2E ? 15 : -1))
+#define FIRST_XD_REG XD0_REG
+#define LAST_XD_REG (FIRST_XD_REG + ((TARGET_SH4 && TARGET_FMOVD) ? 7 : -1))
+#define FIRST_TARGET_REG TR0_REG
+#define LAST_TARGET_REG (FIRST_TARGET_REG + (TARGET_SHMEDIA ? 7 : -1))
+
+/* Registers that can be accessed through bank0 or bank1 depending on sr.md. */
+
+#define FIRST_BANKED_REG R0_REG
+#define LAST_BANKED_REG R7_REG
+
+#define BANKED_REGISTER_P(REGNO) \
+ IN_RANGE ((REGNO), \
+ (unsigned HOST_WIDE_INT) FIRST_BANKED_REG, \
+ (unsigned HOST_WIDE_INT) LAST_BANKED_REG)
+
+#define GENERAL_REGISTER_P(REGNO) \
+ IN_RANGE ((REGNO), \
+ (unsigned HOST_WIDE_INT) FIRST_GENERAL_REG, \
+ (unsigned HOST_WIDE_INT) LAST_GENERAL_REG)
+
+#define GENERAL_OR_AP_REGISTER_P(REGNO) \
+ (GENERAL_REGISTER_P (REGNO) || ((REGNO) == AP_REG) \
+ || ((REGNO) == FRAME_POINTER_REGNUM))
+
+#define FP_REGISTER_P(REGNO) \
+ ((int) (REGNO) >= FIRST_FP_REG && (int) (REGNO) <= LAST_FP_REG)
+
+#define XD_REGISTER_P(REGNO) \
+ ((int) (REGNO) >= FIRST_XD_REG && (int) (REGNO) <= LAST_XD_REG)
+
+#define FP_OR_XD_REGISTER_P(REGNO) \
+ (FP_REGISTER_P (REGNO) || XD_REGISTER_P (REGNO))
+
+#define FP_ANY_REGISTER_P(REGNO) \
+ (FP_REGISTER_P (REGNO) || XD_REGISTER_P (REGNO) || (REGNO) == FPUL_REG)
+
+#define SPECIAL_REGISTER_P(REGNO) \
+ ((REGNO) == GBR_REG || (REGNO) == T_REG \
+ || (REGNO) == MACH_REG || (REGNO) == MACL_REG)
+
+#define TARGET_REGISTER_P(REGNO) \
+ ((int) (REGNO) >= FIRST_TARGET_REG && (int) (REGNO) <= LAST_TARGET_REG)
+
+#define SHMEDIA_REGISTER_P(REGNO) \
+ (GENERAL_REGISTER_P (REGNO) || FP_REGISTER_P (REGNO) \
+ || TARGET_REGISTER_P (REGNO))
+
+/* This is to be used in TARGET_CONDITIONAL_REGISTER_USAGE, to mark
+ registers that should be fixed. */
+#define VALID_REGISTER_P(REGNO) \
+ (SHMEDIA_REGISTER_P (REGNO) || XD_REGISTER_P (REGNO) \
+ || (REGNO) == AP_REG || (REGNO) == RAP_REG \
+ || (REGNO) == FRAME_POINTER_REGNUM \
+ || (TARGET_SH1 && (SPECIAL_REGISTER_P (REGNO) || (REGNO) == PR_REG)) \
+ || (TARGET_SH2E && (REGNO) == FPUL_REG))
+
+/* The mode that should be generally used to store a register by
+ itself in the stack, or to load it back. */
+#define REGISTER_NATURAL_MODE(REGNO) \
+ (FP_REGISTER_P (REGNO) ? SFmode \
+ : XD_REGISTER_P (REGNO) ? DFmode \
+ : TARGET_SHMEDIA && ! HARD_REGNO_CALL_PART_CLOBBERED ((REGNO), DImode) \
+ ? DImode \
+ : SImode)
+
+#define FIRST_PSEUDO_REGISTER 154
+
+/* Don't count soft frame pointer. */
+#define DWARF_FRAME_REGISTERS (FIRST_PSEUDO_REGISTER - 1)
+
+/* 1 for registers that have pervasive standard uses
+ and are not available for the register allocator.
+
+ Mach register is fixed 'cause it's only 10 bits wide for SH1.
+ It is 32 bits wide for SH2. */
+
+#define FIXED_REGISTERS \
+{ \
+/* Regular registers. */ \
+ 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 1, \
+ /* r16 is reserved, r18 is the former pr. */ \
+ 1, 0, 0, 0, 0, 0, 0, 0, \
+ /* r24 is reserved for the OS; r25, for the assembler or linker. */ \
+ /* r26 is a global variable data pointer; r27 is for constants. */ \
+ 1, 1, 1, 1, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 1, \
+/* FP registers. */ \
+ 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, \
+/* Branch target registers. */ \
+ 0, 0, 0, 0, 0, 0, 0, 0, \
+/* XD registers. */ \
+ 0, 0, 0, 0, 0, 0, 0, 0, \
+/*"gbr", "ap", "pr", "t", "mach", "macl", "fpul", "fpscr", */ \
+ 1, 1, 1, 1, 1, 1, 0, 1, \
+/*"rap", "sfp" */ \
+ 1, 1, \
+}
+
+/* 1 for registers not available across function calls.
+ These must include the FIXED_REGISTERS and also any
+ registers that can be used without being saved.
+ The latter must include the registers where values are returned
+ and the register where structure-value addresses are passed.
+ Aside from that, you can include as many other registers as you like. */
+
+#define CALL_USED_REGISTERS \
+{ \
+/* Regular registers. */ \
+ 1, 1, 1, 1, 1, 1, 1, 1, \
+ /* R8 and R9 are call-clobbered on SH5, but not on earlier SH ABIs. \
+ Only the lower 32bits of R10-R14 are guaranteed to be preserved \
+ across SH5 function calls. */ \
+ 0, 0, 0, 0, 0, 0, 0, 1, \
+ 1, 1, 1, 1, 1, 1, 1, 1, \
+ 1, 1, 1, 1, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 1, 1, 1, 1, \
+ 1, 1, 1, 1, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 1, 1, 1, 1, \
+/* FP registers. */ \
+ 1, 1, 1, 1, 1, 1, 1, 1, \
+ 1, 1, 1, 1, 0, 0, 0, 0, \
+ 1, 1, 1, 1, 1, 1, 1, 1, \
+ 1, 1, 1, 1, 1, 1, 1, 1, \
+ 1, 1, 1, 1, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, \
+/* Branch target registers. */ \
+ 1, 1, 1, 1, 1, 0, 0, 0, \
+/* XD registers. */ \
+ 1, 1, 1, 1, 1, 1, 0, 0, \
+/*"gbr", "ap", "pr", "t", "mach", "macl", "fpul", "fpscr", */ \
+ 1, 1, 1, 1, 1, 1, 1, 1, \
+/*"rap", "sfp" */ \
+ 1, 1, \
+}
+
+/* TARGET_CONDITIONAL_REGISTER_USAGE might want to make a register
+ call-used, yet fixed, like PIC_OFFSET_TABLE_REGNUM. */
+#define CALL_REALLY_USED_REGISTERS CALL_USED_REGISTERS
+
+/* Only the lower 32-bits of R10-R14 are guaranteed to be preserved
+ across SHcompact function calls. We can't tell whether a called
+ function is SHmedia or SHcompact, so we assume it may be when
+ compiling SHmedia code with the 32-bit ABI, since that's the only
+ ABI that can be linked with SHcompact code. */
+#define HARD_REGNO_CALL_PART_CLOBBERED(REGNO,MODE) \
+ (TARGET_SHMEDIA32 \
+ && GET_MODE_SIZE (MODE) > 4 \
+ && (((REGNO) >= FIRST_GENERAL_REG + 10 \
+ && (REGNO) <= FIRST_GENERAL_REG + 15) \
+ || TARGET_REGISTER_P (REGNO) \
+ || (REGNO) == PR_MEDIA_REG))
+
+/* Return number of consecutive hard regs needed starting at reg REGNO
+ to hold something of mode MODE.
+ This is ordinarily the length in words of a value of mode MODE
+ but can be less for certain modes in special long registers.
+
+ On the SH all but the XD regs are UNITS_PER_WORD bits wide. */
+
+#define HARD_REGNO_NREGS(REGNO, MODE) \
+ (XD_REGISTER_P (REGNO) \
+ ? ((GET_MODE_SIZE (MODE) + (2*UNITS_PER_WORD - 1)) / (2*UNITS_PER_WORD)) \
+ : (TARGET_SHMEDIA && FP_REGISTER_P (REGNO)) \
+ ? ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD/2 - 1) / (UNITS_PER_WORD/2)) \
+ : ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD))
+
+/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE. */
+
+#define HARD_REGNO_MODE_OK(REGNO, MODE) \
+ sh_hard_regno_mode_ok ((REGNO), (MODE))
+
+/* Value is 1 if it is a good idea to tie two pseudo registers
+ when one has mode MODE1 and one has mode MODE2.
+ If HARD_REGNO_MODE_OK could produce different values for MODE1 and MODE2,
+ for any hard reg, then this must be 0 for correct output.
+ That's the case for xd registers: we don't hold SFmode values in
+ them, so we can't tie an SFmode pseudos with one in another
+ floating-point mode. */
+
+#define MODES_TIEABLE_P(MODE1, MODE2) \
+ ((MODE1) == (MODE2) \
+ || (TARGET_SHMEDIA \
+ && GET_MODE_SIZE (MODE1) == GET_MODE_SIZE (MODE2) \
+ && INTEGRAL_MODE_P (MODE1) && INTEGRAL_MODE_P (MODE2)) \
+ || (GET_MODE_CLASS (MODE1) == GET_MODE_CLASS (MODE2) \
+ && (TARGET_SHMEDIA ? ((GET_MODE_SIZE (MODE1) <= 4) \
+ && (GET_MODE_SIZE (MODE2) <= 4)) \
+ : ((MODE1) != SFmode && (MODE2) != SFmode))))
+
+/* A C expression that is nonzero if hard register NEW_REG can be
+ considered for use as a rename register for OLD_REG register */
+
+#define HARD_REGNO_RENAME_OK(OLD_REG, NEW_REG) \
+ sh_hard_regno_rename_ok (OLD_REG, NEW_REG)
+
+/* Specify the registers used for certain standard purposes.
+ The values of these macros are register numbers. */
+
+/* Define this if the program counter is overloaded on a register. */
+/* #define PC_REGNUM 15*/
+
+/* Register to use for pushing function arguments. */
+#define STACK_POINTER_REGNUM SP_REG
+
+/* Base register for access to local variables of the function. */
+#define HARD_FRAME_POINTER_REGNUM FP_REG
+
+/* Base register for access to local variables of the function. */
+#define FRAME_POINTER_REGNUM 153
+
+/* Fake register that holds the address on the stack of the
+ current function's return address. */
+#define RETURN_ADDRESS_POINTER_REGNUM RAP_REG
+
+/* Register to hold the addressing base for position independent
+ code access to data items. */
+#define PIC_OFFSET_TABLE_REGNUM (flag_pic ? PIC_REG : INVALID_REGNUM)
+
+#define GOT_SYMBOL_NAME "*_GLOBAL_OFFSET_TABLE_"
+
+/* Definitions for register eliminations.
+
+ We have three registers that can be eliminated on the SH. First, the
+ frame pointer register can often be eliminated in favor of the stack
+ pointer register. Secondly, the argument pointer register can always be
+ eliminated; it is replaced with either the stack or frame pointer.
+ Third, there is the return address pointer, which can also be replaced
+ with either the stack or the frame pointer. */
+
+/* This is an array of structures. Each structure initializes one pair
+ of eliminable registers. The "from" register number is given first,
+ followed by "to". Eliminations of the same "from" register are listed
+ in order of preference. */
+
+/* If you add any registers here that are not actually hard registers,
+ and that have any alternative of elimination that doesn't always
+ apply, you need to amend calc_live_regs to exclude it, because
+ reload spills all eliminable registers where it sees an
+ can_eliminate == 0 entry, thus making them 'live' .
+ If you add any hard registers that can be eliminated in different
+ ways, you have to patch reload to spill them only when all alternatives
+ of elimination fail. */
+
+#define ELIMINABLE_REGS \
+{{ HARD_FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
+ { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
+ { FRAME_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}, \
+ { RETURN_ADDRESS_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
+ { RETURN_ADDRESS_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}, \
+ { ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \
+ { ARG_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM},}
+
+/* Define the offset between two registers, one to be eliminated, and the other
+ its replacement, at the start of a routine. */
+
+#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \
+ OFFSET = initial_elimination_offset ((FROM), (TO))
+
+/* Base register for access to arguments of the function. */
+#define ARG_POINTER_REGNUM AP_REG
+
+/* Register in which the static-chain is passed to a function. */
+#define STATIC_CHAIN_REGNUM (TARGET_SH5 ? 1 : 3)
+
+/* Don't default to pcc-struct-return, because we have already specified
+ exactly how to return structures in the TARGET_RETURN_IN_MEMORY
+ target hook. */
+
+#define DEFAULT_PCC_STRUCT_RETURN 0
+
+#define SHMEDIA_REGS_STACK_ADJUST() \
+ (TARGET_SHCOMPACT && crtl->saves_all_registers \
+ ? (8 * (/* r28-r35 */ 8 + /* r44-r59 */ 16 + /* tr5-tr7 */ 3) \
+ + (TARGET_FPU_ANY ? 4 * (/* fr36 - fr63 */ 28) : 0)) \
+ : 0)
+
+
+/* Define the classes of registers for register constraints in the
+ machine description. Also define ranges of constants.
+
+ One of the classes must always be named ALL_REGS and include all hard regs.
+ If there is more than one class, another class must be named NO_REGS
+ and contain no registers.
+
+ The name GENERAL_REGS must be the name of a class (or an alias for
+ another name such as ALL_REGS). This is the class of registers
+ that is allowed by "g" or "r" in a register constraint.
+ Also, registers outside this class are allocated only when
+ instructions express preferences for them.
+
+ The classes must be numbered in nondecreasing order; that is,
+ a larger-numbered class must never be contained completely
+ in a smaller-numbered class.
+
+ For any two classes, it is very desirable that there be another
+ class that represents their union. */
+
+/* The SH has two sorts of general registers, R0 and the rest. R0 can
+ be used as the destination of some of the arithmetic ops. There are
+ also some special purpose registers; the T bit register, the
+ Procedure Return Register and the Multiply Accumulate Registers. */
+/* Place GENERAL_REGS after FPUL_REGS so that it will be preferred by
+ reg_class_subunion. We don't want to have an actual union class
+ of these, because it would only be used when both classes are calculated
+ to give the same cost, but there is only one FPUL register.
+ Besides, regclass fails to notice the different REGISTER_MOVE_COSTS
+ applying to the actual instruction alternative considered. E.g., the
+ y/r alternative of movsi_ie is considered to have no more cost that
+ the r/r alternative, which is patently untrue. */
+
+enum reg_class
+{
+ NO_REGS,
+ R0_REGS,
+ PR_REGS,
+ T_REGS,
+ MAC_REGS,
+ FPUL_REGS,
+ SIBCALL_REGS,
+ GENERAL_REGS,
+ FP0_REGS,
+ FP_REGS,
+ DF_HI_REGS,
+ DF_REGS,
+ FPSCR_REGS,
+ GENERAL_FP_REGS,
+ GENERAL_DF_REGS,
+ TARGET_REGS,
+ ALL_REGS,
+ LIM_REG_CLASSES
+};
+
+#define N_REG_CLASSES (int) LIM_REG_CLASSES
+
+/* Give names of register classes as strings for dump file. */
+#define REG_CLASS_NAMES \
+{ \
+ "NO_REGS", \
+ "R0_REGS", \
+ "PR_REGS", \
+ "T_REGS", \
+ "MAC_REGS", \
+ "FPUL_REGS", \
+ "SIBCALL_REGS", \
+ "GENERAL_REGS", \
+ "FP0_REGS", \
+ "FP_REGS", \
+ "DF_HI_REGS", \
+ "DF_REGS", \
+ "FPSCR_REGS", \
+ "GENERAL_FP_REGS", \
+ "GENERAL_DF_REGS", \
+ "TARGET_REGS", \
+ "ALL_REGS", \
+}
+
+/* Define which registers fit in which classes.
+ This is an initializer for a vector of HARD_REG_SET
+ of length N_REG_CLASSES. */
+
+#define REG_CLASS_CONTENTS \
+{ \
+/* NO_REGS: */ \
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, \
+/* R0_REGS: */ \
+ { 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, \
+/* PR_REGS: */ \
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00040000 }, \
+/* T_REGS: */ \
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00080000 }, \
+/* MAC_REGS: */ \
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00300000 }, \
+/* FPUL_REGS: */ \
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00400000 }, \
+/* SIBCALL_REGS: Initialized in TARGET_CONDITIONAL_REGISTER_USAGE. */ \
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, \
+/* GENERAL_REGS: */ \
+ { 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x03020000 }, \
+/* FP0_REGS: */ \
+ { 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000000 }, \
+/* FP_REGS: */ \
+ { 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x00000000 }, \
+/* DF_HI_REGS: Initialized in TARGET_CONDITIONAL_REGISTER_USAGE. */ \
+ { 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x0000ff00 }, \
+/* DF_REGS: */ \
+ { 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, 0x0000ff00 }, \
+/* FPSCR_REGS: */ \
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00800000 }, \
+/* GENERAL_FP_REGS: */ \
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x03020000 }, \
+/* GENERAL_DF_REGS: */ \
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x0302ff00 }, \
+/* TARGET_REGS: */ \
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000000ff }, \
+/* ALL_REGS: */ \
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x03ffffff }, \
+}
+
+/* The same information, inverted:
+ Return the class number of the smallest class containing
+ reg number REGNO. This could be a conditional expression
+ or could index an array. */
+
+extern enum reg_class regno_reg_class[FIRST_PSEUDO_REGISTER];
+#define REGNO_REG_CLASS(REGNO) regno_reg_class[(REGNO)]
+
+/* The following macro defines cover classes for Integrated Register
+ Allocator. Cover classes is a set of non-intersected register
+ classes covering all hard registers used for register allocation
+ purpose. Any move between two registers of a cover class should be
+ cheaper than load or store of the registers. The macro value is
+ array of register classes with LIM_REG_CLASSES used as the end
+ marker. */
+
+#define IRA_COVER_CLASSES \
+{ \
+ GENERAL_REGS, FP_REGS, PR_REGS, T_REGS, MAC_REGS, TARGET_REGS, \
+ FPUL_REGS, LIM_REG_CLASSES \
+}
+
+/* When this hook returns true for MODE, the compiler allows
+ registers explicitly used in the rtl to be used as spill registers
+ but prevents the compiler from extending the lifetime of these
+ registers. */
+#define TARGET_SMALL_REGISTER_CLASSES_FOR_MODE_P \
+ sh_small_register_classes_for_mode_p
+
+/* The order in which register should be allocated. */
+/* Sometimes FP0_REGS becomes the preferred class of a floating point pseudo,
+ and GENERAL_FP_REGS the alternate class. Since FP0 is likely to be
+ spilled or used otherwise, we better have the FP_REGS allocated first. */
+#define REG_ALLOC_ORDER \
+ {/* Caller-saved FPRs */ \
+ 65, 66, 67, 68, 69, 70, 71, 64, \
+ 72, 73, 74, 75, 80, 81, 82, 83, \
+ 84, 85, 86, 87, 88, 89, 90, 91, \
+ 92, 93, 94, 95, 96, 97, 98, 99, \
+ /* Callee-saved FPRs */ \
+ 76, 77, 78, 79,100,101,102,103, \
+ 104,105,106,107,108,109,110,111, \
+ 112,113,114,115,116,117,118,119, \
+ 120,121,122,123,124,125,126,127, \
+ 136,137,138,139,140,141,142,143, \
+ /* FPSCR */ 151, \
+ /* Caller-saved GPRs (except 8/9 on SH1-4) */ \
+ 1, 2, 3, 7, 6, 5, 4, 0, \
+ 8, 9, 17, 19, 20, 21, 22, 23, \
+ 36, 37, 38, 39, 40, 41, 42, 43, \
+ 60, 61, 62, \
+ /* SH1-4 callee-saved saved GPRs / SH5 partially-saved GPRs */ \
+ 10, 11, 12, 13, 14, 18, \
+ /* SH5 callee-saved GPRs */ \
+ 28, 29, 30, 31, 32, 33, 34, 35, \
+ 44, 45, 46, 47, 48, 49, 50, 51, \
+ 52, 53, 54, 55, 56, 57, 58, 59, \
+ /* FPUL */ 150, \
+ /* SH5 branch target registers */ \
+ 128,129,130,131,132,133,134,135, \
+ /* Fixed registers */ \
+ 15, 16, 24, 25, 26, 27, 63,144, \
+ 145,146,147,148,149,152,153 }
+
+/* The class value for index registers, and the one for base regs. */
+#define INDEX_REG_CLASS \
+ (!ALLOW_INDEXED_ADDRESS ? NO_REGS : TARGET_SHMEDIA ? GENERAL_REGS : R0_REGS)
+#define BASE_REG_CLASS GENERAL_REGS
+
+/* Defines for sh.md and constraints.md. */
+
+#define CONST_OK_FOR_I06(VALUE) (((HOST_WIDE_INT)(VALUE)) >= -32 \
+ && ((HOST_WIDE_INT)(VALUE)) <= 31)
+#define CONST_OK_FOR_I08(VALUE) (((HOST_WIDE_INT)(VALUE))>= -128 \
+ && ((HOST_WIDE_INT)(VALUE)) <= 127)
+#define CONST_OK_FOR_I10(VALUE) (((HOST_WIDE_INT)(VALUE)) >= -512 \
+ && ((HOST_WIDE_INT)(VALUE)) <= 511)
+#define CONST_OK_FOR_I16(VALUE) (((HOST_WIDE_INT)(VALUE)) >= -32768 \
+ && ((HOST_WIDE_INT)(VALUE)) <= 32767)
+
+#define CONST_OK_FOR_J16(VALUE) \
+ ((HOST_BITS_PER_WIDE_INT >= 64 && (VALUE) == (HOST_WIDE_INT) 0xffffffff) \
+ || (HOST_BITS_PER_WIDE_INT >= 64 && (VALUE) == (HOST_WIDE_INT) -1 << 32))
+
+#define CONST_OK_FOR_K08(VALUE) (((HOST_WIDE_INT)(VALUE))>= 0 \
+ && ((HOST_WIDE_INT)(VALUE)) <= 255)
+
+#if 0
+#define SECONDARY_INOUT_RELOAD_CLASS(CLASS,MODE,X,ELSE) \
+ ((((REGCLASS_HAS_FP_REG (CLASS) \
+ && (REG_P (X) \
+ && (GENERAL_OR_AP_REGISTER_P (REGNO (X)) \
+ || (FP_REGISTER_P (REGNO (X)) && (MODE) == SImode \
+ && TARGET_FMOVD)))) \
+ || (REGCLASS_HAS_GENERAL_REG (CLASS) \
+ && REG_P (X) \
+ && FP_REGISTER_P (REGNO (X)))) \
+ && ! TARGET_SHMEDIA \
+ && ((MODE) == SFmode || (MODE) == SImode)) \
+ ? FPUL_REGS \
+ : (((CLASS) == FPUL_REGS \
+ || (REGCLASS_HAS_FP_REG (CLASS) \
+ && ! TARGET_SHMEDIA && MODE == SImode)) \
+ && (MEM_P (X) \
+ || (REG_P (X) \
+ && (REGNO (X) >= FIRST_PSEUDO_REGISTER \
+ || REGNO (X) == T_REG \
+ || system_reg_operand (X, VOIDmode))))) \
+ ? GENERAL_REGS \
+ : (((CLASS) == TARGET_REGS \
+ || (TARGET_SHMEDIA && (CLASS) == SIBCALL_REGS)) \
+ && !satisfies_constraint_Csy (X) \
+ && (!REG_P (X) || ! GENERAL_REGISTER_P (REGNO (X)))) \
+ ? GENERAL_REGS \
+ : (((CLASS) == MAC_REGS || (CLASS) == PR_REGS) \
+ && REG_P (X) && ! GENERAL_REGISTER_P (REGNO (X)) \
+ && (CLASS) != REGNO_REG_CLASS (REGNO (X))) \
+ ? GENERAL_REGS \
+ : ((CLASS) != GENERAL_REGS && REG_P (X) \
+ && TARGET_REGISTER_P (REGNO (X))) \
+ ? GENERAL_REGS : (ELSE))
+
+#define SECONDARY_OUTPUT_RELOAD_CLASS(CLASS,MODE,X) \
+ SECONDARY_INOUT_RELOAD_CLASS(CLASS,MODE,X,NO_REGS)
+
+#define SECONDARY_INPUT_RELOAD_CLASS(CLASS,MODE,X) \
+ ((REGCLASS_HAS_FP_REG (CLASS) \
+ && ! TARGET_SHMEDIA \
+ && immediate_operand ((X), (MODE)) \
+ && ! ((fp_zero_operand (X) || fp_one_operand (X)) \
+ && (MODE) == SFmode && fldi_ok ())) \
+ ? R0_REGS \
+ : ((CLASS) == FPUL_REGS \
+ && ((REG_P (X) \
+ && (REGNO (X) == MACL_REG || REGNO (X) == MACH_REG \
+ || REGNO (X) == T_REG)) \
+ || GET_CODE (X) == PLUS)) \
+ ? GENERAL_REGS \
+ : (CLASS) == FPUL_REGS && immediate_operand ((X), (MODE)) \
+ ? (satisfies_constraint_I08 (X) \
+ ? GENERAL_REGS \
+ : R0_REGS) \
+ : ((CLASS) == FPSCR_REGS \
+ && ((REG_P (X) && REGNO (X) >= FIRST_PSEUDO_REGISTER) \
+ || (MEM_P (X) && GET_CODE (XEXP ((X), 0)) == PLUS))) \
+ ? GENERAL_REGS \
+ : (REGCLASS_HAS_FP_REG (CLASS) \
+ && TARGET_SHMEDIA \
+ && immediate_operand ((X), (MODE)) \
+ && (X) != CONST0_RTX (GET_MODE (X)) \
+ && GET_MODE (X) != V4SFmode) \
+ ? GENERAL_REGS \
+ : (((MODE) == QImode || (MODE) == HImode) \
+ && TARGET_SHMEDIA && inqhi_operand ((X), (MODE))) \
+ ? GENERAL_REGS \
+ : (TARGET_SHMEDIA && (CLASS) == GENERAL_REGS \
+ && (GET_CODE (X) == LABEL_REF || PIC_ADDR_P (X))) \
+ ? TARGET_REGS \
+ : SECONDARY_INOUT_RELOAD_CLASS((CLASS),(MODE),(X), NO_REGS))
+#endif
+
+/* Return the maximum number of consecutive registers
+ needed to represent mode MODE in a register of class CLASS.
+
+ If TARGET_SHMEDIA, we need two FP registers per word.
+ Otherwise we will need at most one register per word. */
+#define CLASS_MAX_NREGS(CLASS, MODE) \
+ (TARGET_SHMEDIA \
+ && TEST_HARD_REG_BIT (reg_class_contents[CLASS], FIRST_FP_REG) \
+ ? (GET_MODE_SIZE (MODE) + UNITS_PER_WORD/2 - 1) / (UNITS_PER_WORD/2) \
+ : (GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
+
+/* If defined, gives a class of registers that cannot be used as the
+ operand of a SUBREG that changes the mode of the object illegally. */
+/* ??? We need to renumber the internal numbers for the frnn registers
+ when in little endian in order to allow mode size changes. */
+
+#define CANNOT_CHANGE_MODE_CLASS(FROM, TO, CLASS) \
+ sh_cannot_change_mode_class (FROM, TO, CLASS)
+
+/* Stack layout; function entry, exit and calling. */
+
+/* Define the number of registers that can hold parameters.
+ These macros are used only in other macro definitions below. */
+
+#define NPARM_REGS(MODE) \
+ (TARGET_FPU_ANY && (MODE) == SFmode \
+ ? (TARGET_SH5 ? 12 : 8) \
+ : (TARGET_SH4 || TARGET_SH2A_DOUBLE) && (GET_MODE_CLASS (MODE) == MODE_FLOAT \
+ || GET_MODE_CLASS (MODE) == MODE_COMPLEX_FLOAT) \
+ ? (TARGET_SH5 ? 12 : 8) \
+ : (TARGET_SH5 ? 8 : 4))
+
+#define FIRST_PARM_REG (FIRST_GENERAL_REG + (TARGET_SH5 ? 2 : 4))
+#define FIRST_RET_REG (FIRST_GENERAL_REG + (TARGET_SH5 ? 2 : 0))
+
+#define FIRST_FP_PARM_REG (FIRST_FP_REG + (TARGET_SH5 ? 0 : 4))
+#define FIRST_FP_RET_REG FIRST_FP_REG
+
+/* Define this if pushing a word on the stack
+ makes the stack pointer a smaller address. */
+#define STACK_GROWS_DOWNWARD
+
+/* Define this macro to nonzero if the addresses of local variable slots
+ are at negative offsets from the frame pointer. */
+#define FRAME_GROWS_DOWNWARD 1
+
+/* Offset from the frame pointer to the first local variable slot to
+ be allocated. */
+#define STARTING_FRAME_OFFSET 0
+
+/* If we generate an insn to push BYTES bytes,
+ this says how many the stack pointer really advances by. */
+/* Don't define PUSH_ROUNDING, since the hardware doesn't do this.
+ When PUSH_ROUNDING is not defined, PARM_BOUNDARY will cause gcc to
+ do correct alignment. */
+#if 0
+#define PUSH_ROUNDING(NPUSHED) (((NPUSHED) + 3) & ~3)
+#endif
+
+/* Offset of first parameter from the argument pointer register value. */
+#define FIRST_PARM_OFFSET(FNDECL) 0
+
+/* Value is the number of bytes of arguments automatically popped when
+ calling a subroutine.
+ CUM is the accumulated argument list.
+
+ On SHcompact, the call trampoline pops arguments off the stack. */
+#define CALL_POPS_ARGS(CUM) (TARGET_SHCOMPACT ? (CUM).stack_regs * 8 : 0)
+
+/* Some subroutine macros specific to this machine. */
+
+#define BASE_RETURN_VALUE_REG(MODE) \
+ ((TARGET_FPU_ANY && ((MODE) == SFmode)) \
+ ? FIRST_FP_RET_REG \
+ : TARGET_FPU_ANY && (MODE) == SCmode \
+ ? FIRST_FP_RET_REG \
+ : (TARGET_FPU_DOUBLE \
+ && ((MODE) == DFmode || (MODE) == SFmode \
+ || (MODE) == DCmode || (MODE) == SCmode )) \
+ ? FIRST_FP_RET_REG \
+ : FIRST_RET_REG)
+
+#define BASE_ARG_REG(MODE) \
+ ((TARGET_SH2E && ((MODE) == SFmode)) \
+ ? FIRST_FP_PARM_REG \
+ : (TARGET_SH4 || TARGET_SH2A_DOUBLE) && (GET_MODE_CLASS (MODE) == MODE_FLOAT \
+ || GET_MODE_CLASS (MODE) == MODE_COMPLEX_FLOAT)\
+ ? FIRST_FP_PARM_REG \
+ : FIRST_PARM_REG)
+
+/* 1 if N is a possible register number for function argument passing. */
+/* ??? There are some callers that pass REGNO as int, and others that pass
+ it as unsigned. We get warnings unless we do casts everywhere. */
+#define FUNCTION_ARG_REGNO_P(REGNO) \
+ (((unsigned) (REGNO) >= (unsigned) FIRST_PARM_REG \
+ && (unsigned) (REGNO) < (unsigned) (FIRST_PARM_REG + NPARM_REGS (SImode)))\
+ || (TARGET_FPU_ANY \
+ && (unsigned) (REGNO) >= (unsigned) FIRST_FP_PARM_REG \
+ && (unsigned) (REGNO) < (unsigned) (FIRST_FP_PARM_REG \
+ + NPARM_REGS (SFmode))))
+
+/* Define a data type for recording info about an argument list
+ during the scan of that argument list. This data type should
+ hold all necessary information about the function itself
+ and about the args processed so far, enough to enable macros
+ such as FUNCTION_ARG to determine where the next arg should go.
+
+ On SH, this is a single integer, which is a number of words
+ of arguments scanned so far (including the invisible argument,
+ if any, which holds the structure-value-address).
+ Thus NARGREGS or more means all following args should go on the stack. */
+
+enum sh_arg_class { SH_ARG_INT = 0, SH_ARG_FLOAT = 1 };
+struct sh_args {
+ int arg_count[2];
+ int force_mem;
+ /* Nonzero if a prototype is available for the function. */
+ int prototype_p;
+ /* The number of an odd floating-point register, that should be used
+ for the next argument of type float. */
+ int free_single_fp_reg;
+ /* Whether we're processing an outgoing function call. */
+ int outgoing;
+ /* The number of general-purpose registers that should have been
+ used to pass partial arguments, that are passed totally on the
+ stack. On SHcompact, a call trampoline will pop them off the
+ stack before calling the actual function, and, if the called
+ function is implemented in SHcompact mode, the incoming arguments
+ decoder will push such arguments back onto the stack. For
+ incoming arguments, STACK_REGS also takes into account other
+ arguments passed by reference, that the decoder will also push
+ onto the stack. */
+ int stack_regs;
+ /* The number of general-purpose registers that should have been
+ used to pass arguments, if the arguments didn't have to be passed
+ by reference. */
+ int byref_regs;
+ /* Set as by shcompact_byref if the current argument is to be passed
+ by reference. */
+ int byref;
+
+ /* call_cookie is a bitmask used by call expanders, as well as
+ function prologue and epilogues, to allow SHcompact to comply
+ with the SH5 32-bit ABI, that requires 64-bit registers to be
+ used even though only the lower 32-bit half is visible in
+ SHcompact mode. The strategy is to call SHmedia trampolines.
+
+ The alternatives for each of the argument-passing registers are
+ (a) leave it unchanged; (b) pop it off the stack; (c) load its
+ contents from the address in it; (d) add 8 to it, storing the
+ result in the next register, then (c); (e) copy it from some
+ floating-point register,
+
+ Regarding copies from floating-point registers, r2 may only be
+ copied from dr0. r3 may be copied from dr0 or dr2. r4 maybe
+ copied from dr0, dr2 or dr4. r5 maybe copied from dr0, dr2,
+ dr4 or dr6. r6 may be copied from dr0, dr2, dr4, dr6 or dr8.
+ r7 through to r9 may be copied from dr0, dr2, dr4, dr8, dr8 or
+ dr10.
+
+ The bit mask is structured as follows:
+
+ - 1 bit to tell whether to set up a return trampoline.
+
+ - 3 bits to count the number consecutive registers to pop off the
+ stack.
+
+ - 4 bits for each of r9, r8, r7 and r6.
+
+ - 3 bits for each of r5, r4, r3 and r2.
+
+ - 3 bits set to 0 (the most significant ones)
+
+ 3 2 1 0
+ 1098 7654 3210 9876 5432 1098 7654 3210
+ FLPF LPFL PFLP FFLP FFLP FFLP FFLP SSST
+ 2223 3344 4555 6666 7777 8888 9999 SSS-
+
+ - If F is set, the register must be copied from an FP register,
+ whose number is encoded in the remaining bits.
+
+ - Else, if L is set, the register must be loaded from the address
+ contained in it. If the P bit is *not* set, the address of the
+ following dword should be computed first, and stored in the
+ following register.
+
+ - Else, if P is set, the register alone should be popped off the
+ stack.
+
+ - After all this processing, the number of registers represented
+ in SSS will be popped off the stack. This is an optimization
+ for pushing/popping consecutive registers, typically used for
+ varargs and large arguments partially passed in registers.
+
+ - If T is set, a return trampoline will be set up for 64-bit
+ return values to be split into 2 32-bit registers. */
+ long call_cookie;
+
+ /* This is set to nonzero when the call in question must use the Renesas ABI,
+ even without the -mrenesas option. */
+ int renesas_abi;
+};
+
+#define CALL_COOKIE_RET_TRAMP_SHIFT 0
+#define CALL_COOKIE_RET_TRAMP(VAL) ((VAL) << CALL_COOKIE_RET_TRAMP_SHIFT)
+#define CALL_COOKIE_STACKSEQ_SHIFT 1
+#define CALL_COOKIE_STACKSEQ(VAL) ((VAL) << CALL_COOKIE_STACKSEQ_SHIFT)
+#define CALL_COOKIE_STACKSEQ_GET(COOKIE) \
+ (((COOKIE) >> CALL_COOKIE_STACKSEQ_SHIFT) & 7)
+#define CALL_COOKIE_INT_REG_SHIFT(REG) \
+ (4 * (7 - (REG)) + (((REG) <= 2) ? ((REG) - 2) : 1) + 3)
+#define CALL_COOKIE_INT_REG(REG, VAL) \
+ ((VAL) << CALL_COOKIE_INT_REG_SHIFT (REG))
+#define CALL_COOKIE_INT_REG_GET(COOKIE, REG) \
+ (((COOKIE) >> CALL_COOKIE_INT_REG_SHIFT (REG)) & ((REG) < 4 ? 7 : 15))
+
+#define CUMULATIVE_ARGS struct sh_args
+
+#define GET_SH_ARG_CLASS(MODE) \
+ ((TARGET_FPU_ANY && (MODE) == SFmode) \
+ ? SH_ARG_FLOAT \
+ /* There's no mention of complex float types in the SH5 ABI, so we
+ should presumably handle them as aggregate types. */ \
+ : TARGET_SH5 && GET_MODE_CLASS (MODE) == MODE_COMPLEX_FLOAT \
+ ? SH_ARG_INT \
+ : TARGET_FPU_DOUBLE && (GET_MODE_CLASS (MODE) == MODE_FLOAT \
+ || GET_MODE_CLASS (MODE) == MODE_COMPLEX_FLOAT) \
+ ? SH_ARG_FLOAT : SH_ARG_INT)
+
+#define ROUND_ADVANCE(SIZE) \
+ (((SIZE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
+
+/* Round a register number up to a proper boundary for an arg of mode
+ MODE.
+
+ The SH doesn't care about double alignment, so we only
+ round doubles to even regs when asked to explicitly. */
+
+#define ROUND_REG(CUM, MODE) \
+ (((TARGET_ALIGN_DOUBLE \
+ || ((TARGET_SH4 || TARGET_SH2A_DOUBLE) && ((MODE) == DFmode || (MODE) == DCmode) \
+ && (CUM).arg_count[(int) SH_ARG_FLOAT] < NPARM_REGS (MODE)))\
+ && GET_MODE_UNIT_SIZE ((MODE)) > UNITS_PER_WORD) \
+ ? ((CUM).arg_count[(int) GET_SH_ARG_CLASS (MODE)] \
+ + ((CUM).arg_count[(int) GET_SH_ARG_CLASS (MODE)] & 1)) \
+ : (CUM).arg_count[(int) GET_SH_ARG_CLASS (MODE)])
+
+/* Initialize a variable CUM of type CUMULATIVE_ARGS
+ for a call to a function whose data type is FNTYPE.
+ For a library call, FNTYPE is 0.
+
+ On SH, the offset always starts at 0: the first parm reg is always
+ the same reg for a given argument class.
+
+ For TARGET_HITACHI, the structure value pointer is passed in memory. */
+
+#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, FNDECL, N_NAMED_ARGS) \
+ sh_init_cumulative_args (& (CUM), (FNTYPE), (LIBNAME), (FNDECL), (N_NAMED_ARGS), VOIDmode)
+
+#define INIT_CUMULATIVE_LIBCALL_ARGS(CUM, MODE, LIBNAME) \
+ sh_init_cumulative_args (& (CUM), NULL_TREE, (LIBNAME), NULL_TREE, 0, (MODE))
+
+/* Return boolean indicating arg of mode MODE will be passed in a reg.
+ This macro is only used in this file. */
+
+#define PASS_IN_REG_P(CUM, MODE, TYPE) \
+ (((TYPE) == 0 \
+ || (! TREE_ADDRESSABLE ((TYPE)) \
+ && (! (TARGET_HITACHI || (CUM).renesas_abi) \
+ || ! (AGGREGATE_TYPE_P (TYPE) \
+ || (!TARGET_FPU_ANY \
+ && (GET_MODE_CLASS (MODE) == MODE_FLOAT \
+ && GET_MODE_SIZE (MODE) > GET_MODE_SIZE (SFmode))))))) \
+ && ! (CUM).force_mem \
+ && (TARGET_SH2E \
+ ? ((MODE) == BLKmode \
+ ? (((CUM).arg_count[(int) SH_ARG_INT] * UNITS_PER_WORD \
+ + int_size_in_bytes (TYPE)) \
+ <= NPARM_REGS (SImode) * UNITS_PER_WORD) \
+ : ((ROUND_REG((CUM), (MODE)) \
+ + HARD_REGNO_NREGS (BASE_ARG_REG (MODE), (MODE))) \
+ <= NPARM_REGS (MODE))) \
+ : ROUND_REG ((CUM), (MODE)) < NPARM_REGS (MODE)))
+
+/* By accident we got stuck with passing SCmode on SH4 little endian
+ in two registers that are nominally successive - which is different from
+ two single SFmode values, where we take endianness translation into
+ account. That does not work at all if an odd number of registers is
+ already in use, so that got fixed, but library functions are still more
+ likely to use complex numbers without mixing them with SFmode arguments
+ (which in C would have to be structures), so for the sake of ABI
+ compatibility the way SCmode values are passed when an even number of
+ FP registers is in use remains different from a pair of SFmode values for
+ now.
+ I.e.:
+ foo (double); a: fr5,fr4
+ foo (float a, float b); a: fr5 b: fr4
+ foo (__complex float a); a.real fr4 a.imag: fr5 - for consistency,
+ this should be the other way round...
+ foo (float a, __complex float b); a: fr5 b.real: fr4 b.imag: fr7 */
+#define FUNCTION_ARG_SCmode_WART 1
+
+/* If an argument of size 5, 6 or 7 bytes is to be passed in a 64-bit
+ register in SHcompact mode, it must be padded in the most
+ significant end. This means that passing it by reference wouldn't
+ pad properly on a big-endian machine. In this particular case, we
+ pass this argument on the stack, in a way that the call trampoline
+ will load its value into the appropriate register. */
+#define SHCOMPACT_FORCE_ON_STACK(MODE,TYPE) \
+ ((MODE) == BLKmode \
+ && TARGET_SHCOMPACT \
+ && ! TARGET_LITTLE_ENDIAN \
+ && int_size_in_bytes (TYPE) > 4 \
+ && int_size_in_bytes (TYPE) < 8)
+
+/* Minimum alignment for an argument to be passed by callee-copy
+ reference. We need such arguments to be aligned to 8 byte
+ boundaries, because they'll be loaded using quad loads. */
+#define SH_MIN_ALIGN_FOR_CALLEE_COPY (8 * BITS_PER_UNIT)
+
+/* The SH5 ABI requires floating-point arguments to be passed to
+ functions without a prototype in both an FP register and a regular
+ register or the stack. When passing the argument in both FP and
+ general-purpose registers, list the FP register first. */
+#define SH5_PROTOTYPELESS_FLOAT_ARG(CUM,MODE) \
+ (gen_rtx_PARALLEL \
+ ((MODE), \
+ gen_rtvec (2, \
+ gen_rtx_EXPR_LIST \
+ (VOIDmode, \
+ ((CUM).arg_count[(int) SH_ARG_INT] < NPARM_REGS (SImode) \
+ ? gen_rtx_REG ((MODE), FIRST_FP_PARM_REG \
+ + (CUM).arg_count[(int) SH_ARG_FLOAT]) \
+ : NULL_RTX), \
+ const0_rtx), \
+ gen_rtx_EXPR_LIST \
+ (VOIDmode, \
+ ((CUM).arg_count[(int) SH_ARG_INT] < NPARM_REGS (SImode) \
+ ? gen_rtx_REG ((MODE), FIRST_PARM_REG \
+ + (CUM).arg_count[(int) SH_ARG_INT]) \
+ : gen_rtx_REG ((MODE), FIRST_FP_PARM_REG \
+ + (CUM).arg_count[(int) SH_ARG_FLOAT])), \
+ const0_rtx))))
+
+/* The SH5 ABI requires regular registers or stack slots to be
+ reserved for floating-point arguments. Registers are taken care of
+ in FUNCTION_ARG_ADVANCE, but stack slots must be reserved here.
+ Unfortunately, there's no way to just reserve a stack slot, so
+ we'll end up needlessly storing a copy of the argument in the
+ stack. For incoming arguments, however, the PARALLEL will be
+ optimized to the register-only form, and the value in the stack
+ slot won't be used at all. */
+#define SH5_PROTOTYPED_FLOAT_ARG(CUM,MODE,REG) \
+ ((CUM).arg_count[(int) SH_ARG_INT] < NPARM_REGS (SImode) \
+ ? gen_rtx_REG ((MODE), (REG)) \
+ : gen_rtx_PARALLEL ((MODE), \
+ gen_rtvec (2, \
+ gen_rtx_EXPR_LIST \
+ (VOIDmode, NULL_RTX, \
+ const0_rtx), \
+ gen_rtx_EXPR_LIST \
+ (VOIDmode, gen_rtx_REG ((MODE), \
+ (REG)), \
+ const0_rtx))))
+
+#define SH5_WOULD_BE_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) \
+ (TARGET_SH5 \
+ && ((MODE) == BLKmode || (MODE) == TImode || (MODE) == CDImode \
+ || (MODE) == DCmode) \
+ && ((CUM).arg_count[(int) SH_ARG_INT] \
+ + (((MODE) == BLKmode ? int_size_in_bytes (TYPE) \
+ : GET_MODE_SIZE (MODE)) \
+ + 7) / 8) > NPARM_REGS (SImode))
+
+/* Perform any needed actions needed for a function that is receiving a
+ variable number of arguments. */
+
+/* Call the function profiler with a given profile label.
+ We use two .aligns, so as to make sure that both the .long is aligned
+ on a 4 byte boundary, and that the .long is a fixed distance (2 bytes)
+ from the trapa instruction. */
+
+#define FUNCTION_PROFILER(STREAM,LABELNO) \
+{ \
+ if (TARGET_SHMEDIA) \
+ { \
+ fprintf((STREAM), "\tmovi\t33,r0\n"); \
+ fprintf((STREAM), "\ttrapa\tr0\n"); \
+ asm_fprintf((STREAM), "\t.long\t%LLP%d\n", (LABELNO)); \
+ } \
+ else \
+ { \
+ fprintf((STREAM), "\t.align\t2\n"); \
+ fprintf((STREAM), "\ttrapa\t#33\n"); \
+ fprintf((STREAM), "\t.align\t2\n"); \
+ asm_fprintf((STREAM), "\t.long\t%LLP%d\n", (LABELNO)); \
+ } \
+}
+
+/* Define this macro if the code for function profiling should come
+ before the function prologue. Normally, the profiling code comes
+ after. */
+
+#define PROFILE_BEFORE_PROLOGUE
+
+/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function,
+ the stack pointer does not matter. The value is tested only in
+ functions that have frame pointers.
+ No definition is equivalent to always zero. */
+
+#define EXIT_IGNORE_STACK 1
+
+/*
+ On the SH, the trampoline looks like
+ 2 0002 D202 mov.l l2,r2
+ 1 0000 D301 mov.l l1,r3
+ 3 0004 422B jmp @r2
+ 4 0006 0009 nop
+ 5 0008 00000000 l1: .long area
+ 6 000c 00000000 l2: .long function */
+
+/* Length in units of the trampoline for entering a nested function. */
+#define TRAMPOLINE_SIZE (TARGET_SHMEDIA64 ? 40 : TARGET_SH5 ? 24 : 16)
+
+/* Alignment required for a trampoline in bits . */
+#define TRAMPOLINE_ALIGNMENT \
+ ((CACHE_LOG < 3 || (optimize_size && ! TARGET_HARVARD)) ? 32 \
+ : TARGET_SHMEDIA ? 256 : 64)
+
+/* A C expression whose value is RTL representing the value of the return
+ address for the frame COUNT steps up from the current frame.
+ FRAMEADDR is already the frame pointer of the COUNT frame, so we
+ can ignore COUNT. */
+
+#define RETURN_ADDR_RTX(COUNT, FRAME) \
+ (((COUNT) == 0) ? sh_get_pr_initial_val () : (rtx) 0)
+
+/* A C expression whose value is RTL representing the location of the
+ incoming return address at the beginning of any function, before the
+ prologue. This RTL is either a REG, indicating that the return
+ value is saved in REG, or a MEM representing a location in
+ the stack. */
+#define INCOMING_RETURN_ADDR_RTX \
+ gen_rtx_REG (Pmode, TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG)
+
+/* Addressing modes, and classification of registers for them. */
+#define HAVE_POST_INCREMENT TARGET_SH1
+#define HAVE_PRE_DECREMENT TARGET_SH1
+
+#define USE_LOAD_POST_INCREMENT(mode) ((mode == SImode || mode == DImode) \
+ ? 0 : TARGET_SH1)
+#define USE_LOAD_PRE_DECREMENT(mode) 0
+#define USE_STORE_POST_INCREMENT(mode) 0
+#define USE_STORE_PRE_DECREMENT(mode) ((mode == SImode || mode == DImode) \
+ ? 0 : TARGET_SH1)
+
+#define MOVE_BY_PIECES_P(SIZE, ALIGN) \
+ (move_by_pieces_ninsns (SIZE, ALIGN, MOVE_MAX_PIECES + 1) \
+ < (optimize_size ? 2 : ((ALIGN >= 32) ? 16 : 2)))
+
+#define STORE_BY_PIECES_P(SIZE, ALIGN) \
+ (move_by_pieces_ninsns (SIZE, ALIGN, STORE_MAX_PIECES + 1) \
+ < (optimize_size ? 2 : ((ALIGN >= 32) ? 16 : 2)))
+
+#define SET_BY_PIECES_P(SIZE, ALIGN) STORE_BY_PIECES_P(SIZE, ALIGN)
+
+/* Macros to check register numbers against specific register classes. */
+
+/* These assume that REGNO is a hard or pseudo reg number.
+ They give nonzero only if REGNO is a hard reg of the suitable class
+ or a pseudo reg currently allocated to a suitable hard reg.
+ Since they use reg_renumber, they are safe only once reg_renumber
+ has been allocated, which happens in local-alloc.c. */
+
+#define REGNO_OK_FOR_BASE_P(REGNO) \
+ (GENERAL_OR_AP_REGISTER_P (REGNO) \
+ || GENERAL_OR_AP_REGISTER_P (reg_renumber[(REGNO)]))
+#define REGNO_OK_FOR_INDEX_P(REGNO) \
+ (TARGET_SHMEDIA \
+ ? (GENERAL_REGISTER_P (REGNO) \
+ || GENERAL_REGISTER_P ((unsigned) reg_renumber[(REGNO)])) \
+ : (REGNO) == R0_REG || (unsigned) reg_renumber[(REGNO)] == R0_REG)
+
+/* Maximum number of registers that can appear in a valid memory
+ address. */
+
+#define MAX_REGS_PER_ADDRESS 2
+
+/* Recognize any constant value that is a valid address. */
+
+#define CONSTANT_ADDRESS_P(X) (GET_CODE (X) == LABEL_REF)
+
+/* Nonzero if the constant value X is a legitimate general operand. */
+/* can_store_by_pieces constructs VOIDmode CONST_DOUBLEs. */
+
+#define LEGITIMATE_CONSTANT_P(X) \
+ (TARGET_SHMEDIA \
+ ? ((GET_MODE (X) != DFmode \
+ && GET_MODE_CLASS (GET_MODE (X)) != MODE_VECTOR_FLOAT) \
+ || (X) == CONST0_RTX (GET_MODE (X)) \
+ || ! TARGET_SHMEDIA_FPU \
+ || TARGET_SHMEDIA64) \
+ : (GET_CODE (X) != CONST_DOUBLE \
+ || GET_MODE (X) == DFmode || GET_MODE (X) == SFmode \
+ || GET_MODE (X) == DImode || GET_MODE (X) == VOIDmode))
+
+/* The macros REG_OK_FOR..._P assume that the arg is a REG rtx
+ and check its validity for a certain class.
+ The suitable hard regs are always accepted and all pseudo regs
+ are also accepted if STRICT is not set. */
+
+/* Nonzero if X is a reg that can be used as a base reg. */
+#define REG_OK_FOR_BASE_P(X, STRICT) \
+ (GENERAL_OR_AP_REGISTER_P (REGNO (X)) \
+ || (!STRICT && REGNO (X) >= FIRST_PSEUDO_REGISTER))
+
+/* Nonzero if X is a reg that can be used as an index. */
+#define REG_OK_FOR_INDEX_P(X, STRICT) \
+ ((TARGET_SHMEDIA ? GENERAL_REGISTER_P (REGNO (X)) \
+ : REGNO (X) == R0_REG) \
+ || (!STRICT && REGNO (X) >= FIRST_PSEUDO_REGISTER))
+
+/* Nonzero if X/OFFSET is a reg that can be used as an index. */
+#define SUBREG_OK_FOR_INDEX_P(X, OFFSET, STRICT) \
+ ((TARGET_SHMEDIA ? GENERAL_REGISTER_P (REGNO (X)) \
+ : REGNO (X) == R0_REG && OFFSET == 0) \
+ || (!STRICT && REGNO (X) >= FIRST_PSEUDO_REGISTER))
+
+/* Macros for extra constraints. */
+
+#define IS_PC_RELATIVE_LOAD_ADDR_P(OP) \
+ ((GET_CODE ((OP)) == LABEL_REF) \
+ || (GET_CODE ((OP)) == CONST \
+ && GET_CODE (XEXP ((OP), 0)) == PLUS \
+ && GET_CODE (XEXP (XEXP ((OP), 0), 0)) == LABEL_REF \
+ && CONST_INT_P (XEXP (XEXP ((OP), 0), 1))))
+
+#define IS_NON_EXPLICIT_CONSTANT_P(OP) \
+ (CONSTANT_P (OP) \
+ && !CONST_INT_P (OP) \
+ && GET_CODE (OP) != CONST_DOUBLE \
+ && (!flag_pic \
+ || (LEGITIMATE_PIC_OPERAND_P (OP) \
+ && !PIC_ADDR_P (OP) \
+ && GET_CODE (OP) != LABEL_REF)))
+
+/* Check whether OP is a datalabel unspec. */
+#define DATALABEL_REF_NO_CONST_P(OP) \
+ (GET_CODE (OP) == UNSPEC \
+ && XINT ((OP), 1) == UNSPEC_DATALABEL \
+ && XVECLEN ((OP), 0) == 1 \
+ && GET_CODE (XVECEXP ((OP), 0, 0)) == LABEL_REF)
+
+#define GOT_ENTRY_P(OP) \
+ (GET_CODE (OP) == CONST && GET_CODE (XEXP ((OP), 0)) == UNSPEC \
+ && XINT (XEXP ((OP), 0), 1) == UNSPEC_GOT)
+
+#define GOTPLT_ENTRY_P(OP) \
+ (GET_CODE (OP) == CONST && GET_CODE (XEXP ((OP), 0)) == UNSPEC \
+ && XINT (XEXP ((OP), 0), 1) == UNSPEC_GOTPLT)
+
+#define UNSPEC_GOTOFF_P(OP) \
+ (GET_CODE (OP) == UNSPEC && XINT ((OP), 1) == UNSPEC_GOTOFF)
+
+#define GOTOFF_P(OP) \
+ (GET_CODE (OP) == CONST \
+ && (UNSPEC_GOTOFF_P (XEXP ((OP), 0)) \
+ || (GET_CODE (XEXP ((OP), 0)) == PLUS \
+ && UNSPEC_GOTOFF_P (XEXP (XEXP ((OP), 0), 0)) \
+ && CONST_INT_P (XEXP (XEXP ((OP), 0), 1)))))
+
+#define PIC_ADDR_P(OP) \
+ (GET_CODE (OP) == CONST && GET_CODE (XEXP ((OP), 0)) == UNSPEC \
+ && XINT (XEXP ((OP), 0), 1) == UNSPEC_PIC)
+
+#define PCREL_SYMOFF_P(OP) \
+ (GET_CODE (OP) == CONST \
+ && GET_CODE (XEXP ((OP), 0)) == UNSPEC \
+ && XINT (XEXP ((OP), 0), 1) == UNSPEC_PCREL_SYMOFF)
+
+#define NON_PIC_REFERENCE_P(OP) \
+ (GET_CODE (OP) == LABEL_REF || GET_CODE (OP) == SYMBOL_REF \
+ || (GET_CODE (OP) == CONST \
+ && (GET_CODE (XEXP ((OP), 0)) == LABEL_REF \
+ || GET_CODE (XEXP ((OP), 0)) == SYMBOL_REF \
+ || DATALABEL_REF_NO_CONST_P (XEXP ((OP), 0)))) \
+ || (GET_CODE (OP) == CONST && GET_CODE (XEXP ((OP), 0)) == PLUS \
+ && (GET_CODE (XEXP (XEXP ((OP), 0), 0)) == SYMBOL_REF \
+ || GET_CODE (XEXP (XEXP ((OP), 0), 0)) == LABEL_REF \
+ || DATALABEL_REF_NO_CONST_P (XEXP (XEXP ((OP), 0), 0))) \
+ && CONST_INT_P (XEXP (XEXP ((OP), 0), 1))))
+
+#define PIC_REFERENCE_P(OP) \
+ (GOT_ENTRY_P (OP) || GOTPLT_ENTRY_P (OP) \
+ || GOTOFF_P (OP) || PIC_ADDR_P (OP))
+
+#define MOVI_SHORI_BASE_OPERAND_P(OP) \
+ (flag_pic \
+ ? (GOT_ENTRY_P (OP) || GOTPLT_ENTRY_P (OP) || GOTOFF_P (OP) \
+ || PCREL_SYMOFF_P (OP)) \
+ : NON_PIC_REFERENCE_P (OP))
+
+#define MAYBE_BASE_REGISTER_RTX_P(X, STRICT) \
+ ((REG_P (X) && REG_OK_FOR_BASE_P (X, STRICT)) \
+ || (GET_CODE (X) == SUBREG \
+ && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (GET_MODE ((X))), \
+ GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (X)))) \
+ && REG_P (SUBREG_REG (X)) \
+ && REG_OK_FOR_BASE_P (SUBREG_REG (X), STRICT)))
+
+/* Since this must be r0, which is a single register class, we must check
+ SUBREGs more carefully, to be sure that we don't accept one that extends
+ outside the class. */
+#define MAYBE_INDEX_REGISTER_RTX_P(X, STRICT) \
+ ((REG_P (X) && REG_OK_FOR_INDEX_P (X, STRICT)) \
+ || (GET_CODE (X) == SUBREG \
+ && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (GET_MODE ((X))), \
+ GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (X)))) \
+ && REG_P (SUBREG_REG (X)) \
+ && SUBREG_OK_FOR_INDEX_P (SUBREG_REG (X), SUBREG_BYTE (X), STRICT)))
+
+#ifdef REG_OK_STRICT
+#define BASE_REGISTER_RTX_P(X) MAYBE_BASE_REGISTER_RTX_P(X, true)
+#define INDEX_REGISTER_RTX_P(X) MAYBE_INDEX_REGISTER_RTX_P(X, true)
+#else
+#define BASE_REGISTER_RTX_P(X) MAYBE_BASE_REGISTER_RTX_P(X, false)
+#define INDEX_REGISTER_RTX_P(X) MAYBE_INDEX_REGISTER_RTX_P(X, false)
+#endif
+
+#define ALLOW_INDEXED_ADDRESS \
+ ((!TARGET_SHMEDIA32 && !TARGET_SHCOMPACT) || TARGET_ALLOW_INDEXED_ADDRESS)
+
+#define GO_IF_LEGITIMATE_INDEX(MODE, OP, WIN) \
+ do { \
+ if (sh_legitimate_index_p ((MODE), (OP))) \
+ goto WIN; \
+ } while (0)
+
+/* A C compound statement that attempts to replace X, which is an address
+ that needs reloading, with a valid memory address for an operand of
+ mode MODE. WIN is a C statement label elsewhere in the code. */
+
+#define LEGITIMIZE_RELOAD_ADDRESS(X,MODE,OPNUM,TYPE,IND_LEVELS,WIN) \
+ do { \
+ if (sh_legitimize_reload_address (&(X), (MODE), (OPNUM), (TYPE))) \
+ goto WIN; \
+ } while (0)
+
+/* Specify the machine mode that this machine uses
+ for the index in the tablejump instruction. */
+#define CASE_VECTOR_MODE ((! optimize || TARGET_BIGTABLE) ? SImode : HImode)
+
+#define CASE_VECTOR_SHORTEN_MODE(MIN_OFFSET, MAX_OFFSET, BODY) \
+((MIN_OFFSET) >= 0 && (MAX_OFFSET) <= 127 \
+ ? (ADDR_DIFF_VEC_FLAGS (BODY).offset_unsigned = 0, QImode) \
+ : (MIN_OFFSET) >= 0 && (MAX_OFFSET) <= 255 \
+ ? (ADDR_DIFF_VEC_FLAGS (BODY).offset_unsigned = 1, QImode) \
+ : (MIN_OFFSET) >= -32768 && (MAX_OFFSET) <= 32767 ? HImode \
+ : SImode)
+
+/* Define as C expression which evaluates to nonzero if the tablejump
+ instruction expects the table to contain offsets from the address of the
+ table.
+ Do not define this if the table should contain absolute addresses. */
+#define CASE_VECTOR_PC_RELATIVE 1
+
+/* Define it here, so that it doesn't get bumped to 64-bits on SHmedia. */
+#define FLOAT_TYPE_SIZE 32
+
+/* Since the SH2e has only `float' support, it is desirable to make all
+ floating point types equivalent to `float'. */
+#define DOUBLE_TYPE_SIZE ((TARGET_SH2E && ! TARGET_SH4 && ! TARGET_SH2A_DOUBLE) ? 32 : 64)
+
+/* 'char' is signed by default. */
+#define DEFAULT_SIGNED_CHAR 1
+
+/* The type of size_t unsigned int. */
+#define SIZE_TYPE (TARGET_SH5 ? "long unsigned int" : "unsigned int")
+
+#undef PTRDIFF_TYPE
+#define PTRDIFF_TYPE (TARGET_SH5 ? "long int" : "int")
+
+#define WCHAR_TYPE "short unsigned int"
+#define WCHAR_TYPE_SIZE 16
+
+#define SH_ELF_WCHAR_TYPE "long int"
+
+/* Max number of bytes we can move from memory to memory
+ in one reasonably fast instruction. */
+#define MOVE_MAX (TARGET_SHMEDIA ? 8 : 4)
+
+/* Maximum value possibly taken by MOVE_MAX. Must be defined whenever
+ MOVE_MAX is not a compile-time constant. */
+#define MAX_MOVE_MAX 8
+
+/* Max number of bytes we want move_by_pieces to be able to copy
+ efficiently. */
+#define MOVE_MAX_PIECES (TARGET_SH4 || TARGET_SHMEDIA ? 8 : 4)
+
+/* Define if operations between registers always perform the operation
+ on the full register even if a narrower mode is specified. */
+#define WORD_REGISTER_OPERATIONS
+
+/* Define if loading in MODE, an integral mode narrower than BITS_PER_WORD
+ will either zero-extend or sign-extend. The value of this macro should
+ be the code that says which one of the two operations is implicitly
+ done, UNKNOWN if none. */
+/* For SHmedia, we can truncate to QImode easier using zero extension. */
+/* FP registers can load SImode values, but don't implicitly sign-extend
+ them to DImode. */
+#define LOAD_EXTEND_OP(MODE) \
+ (((MODE) == QImode && TARGET_SHMEDIA) ? ZERO_EXTEND \
+ : (MODE) != SImode ? SIGN_EXTEND : UNKNOWN)
+
+/* Define if loading short immediate values into registers sign extends. */
+#define SHORT_IMMEDIATES_SIGN_EXTEND
+
+/* Nonzero if access to memory by bytes is no faster than for words. */
+#define SLOW_BYTE_ACCESS 1
+
+/* Immediate shift counts are truncated by the output routines (or was it
+ the assembler?). Shift counts in a register are truncated by SH. Note
+ that the native compiler puts too large (> 32) immediate shift counts
+ into a register and shifts by the register, letting the SH decide what
+ to do instead of doing that itself. */
+/* ??? The library routines in lib1funcs.asm truncate the shift count.
+ However, the SH3 has hardware shifts that do not truncate exactly as gcc
+ expects - the sign bit is significant - so it appears that we need to
+ leave this zero for correct SH3 code. */
+#define SHIFT_COUNT_TRUNCATED (! TARGET_SH3 && ! TARGET_SH2A)
+
+/* All integers have the same format so truncation is easy. */
+/* But SHmedia must sign-extend DImode when truncating to SImode. */
+#define TRULY_NOOP_TRUNCATION(OUTPREC,INPREC) \
+ (!TARGET_SHMEDIA || (INPREC) < 64 || (OUTPREC) >= 64)
+
+/* Define this if addresses of constant functions
+ shouldn't be put through pseudo regs where they can be cse'd.
+ Desirable on machines where ordinary constants are expensive
+ but a CALL with constant address is cheap. */
+/*#define NO_FUNCTION_CSE 1*/
+
+/* The machine modes of pointers and functions. */
+#define Pmode (TARGET_SHMEDIA64 ? DImode : SImode)
+#define FUNCTION_MODE Pmode
+
+/* The multiply insn on the SH1 and the divide insns on the SH1 and SH2
+ are actually function calls with some special constraints on arguments
+ and register usage.
+
+ These macros tell reorg that the references to arguments and
+ register clobbers for insns of type sfunc do not appear to happen
+ until after the millicode call. This allows reorg to put insns
+ which set the argument registers into the delay slot of the millicode
+ call -- thus they act more like traditional CALL_INSNs.
+
+ get_attr_is_sfunc will try to recognize the given insn, so make sure to
+ filter out things it will not accept -- SEQUENCE, USE and CLOBBER insns
+ in particular. */
+
+#define INSN_SETS_ARE_DELAYED(X) \
+ ((NONJUMP_INSN_P (X) \
+ && GET_CODE (PATTERN (X)) != SEQUENCE \
+ && GET_CODE (PATTERN (X)) != USE \
+ && GET_CODE (PATTERN (X)) != CLOBBER \
+ && get_attr_is_sfunc (X)))
+
+#define INSN_REFERENCES_ARE_DELAYED(X) \
+ ((NONJUMP_INSN_P (X) \
+ && GET_CODE (PATTERN (X)) != SEQUENCE \
+ && GET_CODE (PATTERN (X)) != USE \
+ && GET_CODE (PATTERN (X)) != CLOBBER \
+ && get_attr_is_sfunc (X)))
+
+
+/* Position Independent Code. */
+
+/* We can't directly access anything that contains a symbol,
+ nor can we indirect via the constant pool. */
+#define LEGITIMATE_PIC_OPERAND_P(X) \
+ ((! nonpic_symbol_mentioned_p (X) \
+ && (GET_CODE (X) != SYMBOL_REF \
+ || ! CONSTANT_POOL_ADDRESS_P (X) \
+ || ! nonpic_symbol_mentioned_p (get_pool_constant (X)))) \
+ || (TARGET_SHMEDIA && GET_CODE (X) == LABEL_REF))
+
+#define SYMBOLIC_CONST_P(X) \
+((GET_CODE (X) == SYMBOL_REF || GET_CODE (X) == LABEL_REF) \
+ && nonpic_symbol_mentioned_p (X))
+
+/* Compute extra cost of moving data between one register class
+ and another. */
+
+/* If SECONDARY*_RELOAD_CLASS says something about the src/dst pair, regclass
+ uses this information. Hence, the general register <-> floating point
+ register information here is not used for SFmode. */
+
+#define REGCLASS_HAS_GENERAL_REG(CLASS) \
+ ((CLASS) == GENERAL_REGS || (CLASS) == R0_REGS \
+ || (! TARGET_SHMEDIA && (CLASS) == SIBCALL_REGS))
+
+#define REGCLASS_HAS_FP_REG(CLASS) \
+ ((CLASS) == FP0_REGS || (CLASS) == FP_REGS \
+ || (CLASS) == DF_REGS || (CLASS) == DF_HI_REGS)
+
+/* ??? Perhaps make MEMORY_MOVE_COST depend on compiler option? This
+ would be so that people with slow memory systems could generate
+ different code that does fewer memory accesses. */
+
+/* A C expression for the cost of a branch instruction. A value of 1
+ is the default; other values are interpreted relative to that.
+ The SH1 does not have delay slots, hence we get a pipeline stall
+ at every branch. The SH4 is superscalar, so the single delay slot
+ is not sufficient to keep both pipelines filled. */
+#define BRANCH_COST(speed_p, predictable_p) \
+ (TARGET_SH5 ? 1 : ! TARGET_SH2 || TARGET_HARD_SH4 ? 2 : 1)
+
+/* Assembler output control. */
+
+/* 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 "!"
+
+#define ASM_APP_ON ""
+#define ASM_APP_OFF ""
+#define FILE_ASM_OP "\t.file\n"
+#define SET_ASM_OP "\t.set\t"
+
+/* How to change between sections. */
+
+#define TEXT_SECTION_ASM_OP (TARGET_SHMEDIA32 ? "\t.section\t.text..SHmedia32,\"ax\"" : "\t.text")
+#define DATA_SECTION_ASM_OP "\t.data"
+
+#if defined CRT_BEGIN || defined CRT_END
+/* Arrange for TEXT_SECTION_ASM_OP to be a compile-time constant. */
+# undef TEXT_SECTION_ASM_OP
+# if __SHMEDIA__ == 1 && __SH5__ == 32
+# define TEXT_SECTION_ASM_OP "\t.section\t.text..SHmedia32,\"ax\""
+# else
+# define TEXT_SECTION_ASM_OP "\t.text"
+# endif
+#endif
+
+
+/* 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. */
+#ifndef BSS_SECTION_ASM_OP
+#define BSS_SECTION_ASM_OP "\t.section\t.bss"
+#endif
+
+/* 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. */
+#ifndef ASM_OUTPUT_ALIGNED_BSS
+#define ASM_OUTPUT_ALIGNED_BSS(FILE, DECL, NAME, SIZE, ALIGN) \
+ asm_output_aligned_bss (FILE, DECL, NAME, SIZE, ALIGN)
+#endif
+
+/* Define this so that jump tables go in same section as the current function,
+ which could be text or it could be a user defined section. */
+#define JUMP_TABLES_IN_TEXT_SECTION 1
+
+#undef DO_GLOBAL_CTORS_BODY
+#define DO_GLOBAL_CTORS_BODY \
+{ \
+ typedef void (*pfunc) (void); \
+ extern pfunc __ctors[]; \
+ extern pfunc __ctors_end[]; \
+ pfunc *p; \
+ for (p = __ctors_end; p > __ctors; ) \
+ { \
+ (*--p)(); \
+ } \
+}
+
+#undef DO_GLOBAL_DTORS_BODY
+#define DO_GLOBAL_DTORS_BODY \
+{ \
+ typedef void (*pfunc) (void); \
+ extern pfunc __dtors[]; \
+ extern pfunc __dtors_end[]; \
+ pfunc *p; \
+ for (p = __dtors; p < __dtors_end; p++) \
+ { \
+ (*p)(); \
+ } \
+}
+
+#define ASM_OUTPUT_REG_PUSH(file, v) \
+{ \
+ if (TARGET_SHMEDIA) \
+ { \
+ fprintf ((file), "\taddi.l\tr15,-8,r15\n"); \
+ fprintf ((file), "\tst.q\tr15,0,r%d\n", (v)); \
+ } \
+ else \
+ fprintf ((file), "\tmov.l\tr%d,@-r15\n", (v)); \
+}
+
+#define ASM_OUTPUT_REG_POP(file, v) \
+{ \
+ if (TARGET_SHMEDIA) \
+ { \
+ fprintf ((file), "\tld.q\tr15,0,r%d\n", (v)); \
+ fprintf ((file), "\taddi.l\tr15,8,r15\n"); \
+ } \
+ else \
+ fprintf ((file), "\tmov.l\t@r15+,r%d\n", (v)); \
+}
+
+/* DBX register number for a given compiler register number. */
+/* GDB has FPUL at 23 and FP0 at 25, so we must add one to all FP registers
+ to match gdb. */
+/* expand_builtin_init_dwarf_reg_sizes uses this to test if a
+ register exists, so we should return -1 for invalid register numbers. */
+#define DBX_REGISTER_NUMBER(REGNO) SH_DBX_REGISTER_NUMBER (REGNO)
+
+/* SHcompact PR_REG used to use the encoding 241, and SHcompact FP registers
+ used to use the encodings 245..260, but that doesn't make sense:
+ PR_REG and PR_MEDIA_REG are actually the same register, and likewise
+ the FP registers stay the same when switching between compact and media
+ mode. Hence, we also need to use the same dwarf frame columns.
+ Likewise, we need to support unwind information for SHmedia registers
+ even in compact code. */
+#define SH_DBX_REGISTER_NUMBER(REGNO) \
+ (IN_RANGE ((REGNO), \
+ (unsigned HOST_WIDE_INT) FIRST_GENERAL_REG, \
+ FIRST_GENERAL_REG + (TARGET_SH5 ? 63U :15U)) \
+ ? ((unsigned) (REGNO) - FIRST_GENERAL_REG) \
+ : ((int) (REGNO) >= FIRST_FP_REG \
+ && ((int) (REGNO) \
+ <= (FIRST_FP_REG + \
+ ((TARGET_SH5 && TARGET_FPU_ANY) ? 63 : TARGET_SH2E ? 15 : -1)))) \
+ ? ((unsigned) (REGNO) - FIRST_FP_REG \
+ + (TARGET_SH5 ? 77 : 25)) \
+ : XD_REGISTER_P (REGNO) \
+ ? ((unsigned) (REGNO) - FIRST_XD_REG + (TARGET_SH5 ? 289 : 87)) \
+ : TARGET_REGISTER_P (REGNO) \
+ ? ((unsigned) (REGNO) - FIRST_TARGET_REG + 68) \
+ : (REGNO) == PR_REG \
+ ? (TARGET_SH5 ? 18 : 17) \
+ : (REGNO) == PR_MEDIA_REG \
+ ? (TARGET_SH5 ? 18 : (unsigned) -1) \
+ : (REGNO) == GBR_REG \
+ ? (TARGET_SH5 ? 238 : 18) \
+ : (REGNO) == MACH_REG \
+ ? (TARGET_SH5 ? 239 : 20) \
+ : (REGNO) == MACL_REG \
+ ? (TARGET_SH5 ? 240 : 21) \
+ : (REGNO) == T_REG \
+ ? (TARGET_SH5 ? 242 : 22) \
+ : (REGNO) == FPUL_REG \
+ ? (TARGET_SH5 ? 244 : 23) \
+ : (REGNO) == FPSCR_REG \
+ ? (TARGET_SH5 ? 243 : 24) \
+ : (unsigned) -1)
+
+/* This is how to output a reference to a symbol_ref. On SH5,
+ references to non-code symbols must be preceded by `datalabel'. */
+#define ASM_OUTPUT_SYMBOL_REF(FILE,SYM) \
+ do \
+ { \
+ if (TARGET_SH5 && !SYMBOL_REF_FUNCTION_P (SYM)) \
+ fputs ("datalabel ", (FILE)); \
+ assemble_name ((FILE), XSTR ((SYM), 0)); \
+ } \
+ while (0)
+
+/* This is how to output an assembler line
+ that says to advance the location counter
+ to a multiple of 2**LOG bytes. */
+
+#define ASM_OUTPUT_ALIGN(FILE,LOG) \
+ if ((LOG) != 0) \
+ fprintf ((FILE), "\t.align %d\n", (LOG))
+
+/* Globalizing directive for a label. */
+#define GLOBAL_ASM_OP "\t.global\t"
+
+/* #define ASM_OUTPUT_CASE_END(STREAM,NUM,TABLE) */
+
+/* Output a relative address table. */
+
+#define ASM_OUTPUT_ADDR_DIFF_ELT(STREAM,BODY,VALUE,REL) \
+ switch (GET_MODE (BODY)) \
+ { \
+ case SImode: \
+ if (TARGET_SH5) \
+ { \
+ asm_fprintf ((STREAM), "\t.long\t%LL%d-datalabel %LL%d\n", \
+ (VALUE), (REL)); \
+ break; \
+ } \
+ asm_fprintf ((STREAM), "\t.long\t%LL%d-%LL%d\n", (VALUE),(REL)); \
+ break; \
+ case HImode: \
+ if (TARGET_SH5) \
+ { \
+ asm_fprintf ((STREAM), "\t.word\t%LL%d-datalabel %LL%d\n", \
+ (VALUE), (REL)); \
+ break; \
+ } \
+ asm_fprintf ((STREAM), "\t.word\t%LL%d-%LL%d\n", (VALUE),(REL)); \
+ break; \
+ case QImode: \
+ if (TARGET_SH5) \
+ { \
+ asm_fprintf ((STREAM), "\t.byte\t%LL%d-datalabel %LL%d\n", \
+ (VALUE), (REL)); \
+ break; \
+ } \
+ asm_fprintf ((STREAM), "\t.byte\t%LL%d-%LL%d\n", (VALUE),(REL)); \
+ break; \
+ default: \
+ break; \
+ }
+
+/* Output an absolute table element. */
+
+#define ASM_OUTPUT_ADDR_VEC_ELT(STREAM,VALUE) \
+ if (! optimize || TARGET_BIGTABLE) \
+ asm_fprintf ((STREAM), "\t.long\t%LL%d\n", (VALUE)); \
+ else \
+ asm_fprintf ((STREAM), "\t.word\t%LL%d\n", (VALUE));
+
+
+/* A C statement to be executed just prior to the output of
+ assembler code for INSN, to modify the extracted operands so
+ they will be output differently.
+
+ Here the argument OPVEC is the vector containing the operands
+ extracted from INSN, and NOPERANDS is the number of elements of
+ the vector which contain meaningful data for this insn.
+ The contents of this vector are what will be used to convert the insn
+ template into assembler code, so you can change the assembler output
+ by changing the contents of the vector. */
+
+#define FINAL_PRESCAN_INSN(INSN, OPVEC, NOPERANDS) \
+ final_prescan_insn ((INSN), (OPVEC), (NOPERANDS))
+
+
+extern struct rtx_def *sh_compare_op0;
+extern struct rtx_def *sh_compare_op1;
+
+/* Which processor to schedule for. The elements of the enumeration must
+ match exactly the cpu attribute in the sh.md file. */
+
+enum processor_type {
+ PROCESSOR_SH1,
+ PROCESSOR_SH2,
+ PROCESSOR_SH2E,
+ PROCESSOR_SH2A,
+ PROCESSOR_SH3,
+ PROCESSOR_SH3E,
+ PROCESSOR_SH4,
+ PROCESSOR_SH4A,
+ PROCESSOR_SH5
+};
+
+#define sh_cpu_attr ((enum attr_cpu)sh_cpu)
+extern enum processor_type sh_cpu;
+
+enum mdep_reorg_phase_e
+{
+ SH_BEFORE_MDEP_REORG,
+ SH_INSERT_USES_LABELS,
+ SH_SHORTEN_BRANCHES0,
+ SH_FIXUP_PCLOAD,
+ SH_SHORTEN_BRANCHES1,
+ SH_AFTER_MDEP_REORG
+};
+
+extern enum mdep_reorg_phase_e mdep_reorg_phase;
+
+/* Handle Renesas compiler's pragmas. */
+#define REGISTER_TARGET_PRAGMAS() do { \
+ c_register_pragma (0, "interrupt", sh_pr_interrupt); \
+ c_register_pragma (0, "trapa", sh_pr_trapa); \
+ c_register_pragma (0, "nosave_low_regs", sh_pr_nosave_low_regs); \
+} while (0)
+
+extern tree sh_deferred_function_attributes;
+extern tree *sh_deferred_function_attributes_tail;
+
+/* Set when processing a function with interrupt attribute. */
+
+extern int current_function_interrupt;
+
+
+/* Instructions with unfilled delay slots take up an
+ extra two bytes for the nop in the delay slot.
+ sh-dsp parallel processing insns are four bytes long. */
+
+#define ADJUST_INSN_LENGTH(X, LENGTH) \
+ (LENGTH) += sh_insn_length_adjustment (X);
+
+/* Define this macro if it is advisable to hold scalars in registers
+ in a wider mode than that declared by the program. In such cases,
+ the value is constrained to be within the bounds of the declared
+ type, but kept valid in the wider mode. The signedness of the
+ extension may differ from that of the type.
+
+ Leaving the unsignedp unchanged gives better code than always setting it
+ to 0. This is despite the fact that we have only signed char and short
+ load instructions. */
+#define PROMOTE_MODE(MODE, UNSIGNEDP, TYPE) \
+ if (GET_MODE_CLASS (MODE) == MODE_INT \
+ && GET_MODE_SIZE (MODE) < 4/* ! UNITS_PER_WORD */)\
+ (UNSIGNEDP) = ((MODE) == SImode ? 0 : (UNSIGNEDP)), \
+ (MODE) = (TARGET_SH1 ? SImode \
+ : TARGET_SHMEDIA32 ? SImode : DImode);
+
+#define MAX_FIXED_MODE_SIZE (TARGET_SH5 ? 128 : 64)
+
+#define SIDI_OFF (TARGET_LITTLE_ENDIAN ? 0 : 4)
+
+/* Better to allocate once the maximum space for outgoing args in the
+ prologue rather than duplicate around each call. */
+#define ACCUMULATE_OUTGOING_ARGS TARGET_ACCUMULATE_OUTGOING_ARGS
+
+#define SH_DYNAMIC_SHIFT_COST \
+ (TARGET_HARD_SH4 ? 1 : TARGET_SH3 ? (optimize_size ? 1 : 2) : 20)
+
+
+#define NUM_MODES_FOR_MODE_SWITCHING { FP_MODE_NONE }
+
+#define OPTIMIZE_MODE_SWITCHING(ENTITY) (TARGET_SH4 || TARGET_SH2A_DOUBLE)
+
+#define ACTUAL_NORMAL_MODE(ENTITY) \
+ (TARGET_FPU_SINGLE ? FP_MODE_SINGLE : FP_MODE_DOUBLE)
+
+#define NORMAL_MODE(ENTITY) \
+ (sh_cfun_interrupt_handler_p () \
+ ? (TARGET_FMOVD ? FP_MODE_DOUBLE : FP_MODE_NONE) \
+ : ACTUAL_NORMAL_MODE (ENTITY))
+
+#define MODE_ENTRY(ENTITY) NORMAL_MODE (ENTITY)
+
+#define MODE_EXIT(ENTITY) \
+ (sh_cfun_attr_renesas_p () ? FP_MODE_NONE : NORMAL_MODE (ENTITY))
+
+#define EPILOGUE_USES(REGNO) ((TARGET_SH2E || TARGET_SH4) \
+ && (REGNO) == FPSCR_REG)
+
+#define MODE_NEEDED(ENTITY, INSN) \
+ (recog_memoized (INSN) >= 0 \
+ ? get_attr_fp_mode (INSN) \
+ : FP_MODE_NONE)
+
+#define MODE_AFTER(MODE, INSN) \
+ (TARGET_HITACHI \
+ && recog_memoized (INSN) >= 0 \
+ && get_attr_fp_set (INSN) != FP_SET_NONE \
+ ? (int) get_attr_fp_set (INSN) \
+ : (MODE))
+
+#define MODE_PRIORITY_TO_MODE(ENTITY, N) \
+ ((TARGET_FPU_SINGLE != 0) ^ (N) ? FP_MODE_SINGLE : FP_MODE_DOUBLE)
+
+#define EMIT_MODE_SET(ENTITY, MODE, HARD_REGS_LIVE) \
+ fpscr_set_from_mem ((MODE), (HARD_REGS_LIVE))
+
+#define MD_CAN_REDIRECT_BRANCH(INSN, SEQ) \
+ sh_can_redirect_branch ((INSN), (SEQ))
+
+#define DWARF_FRAME_RETURN_COLUMN \
+ (TARGET_SH5 ? DWARF_FRAME_REGNUM (PR_MEDIA_REG) : DWARF_FRAME_REGNUM (PR_REG))
+
+#define EH_RETURN_DATA_REGNO(N) \
+ ((N) < 4 ? (N) + (TARGET_SH5 ? 2U : 4U) : INVALID_REGNUM)
+
+#define EH_RETURN_STACKADJ_REGNO STATIC_CHAIN_REGNUM
+#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (Pmode, EH_RETURN_STACKADJ_REGNO)
+
+/* We have to distinguish between code and data, so that we apply
+ datalabel where and only where appropriate. Use sdataN for data. */
+#define ASM_PREFERRED_EH_DATA_FORMAT(CODE, GLOBAL) \
+ ((flag_pic && (GLOBAL) ? DW_EH_PE_indirect : 0) \
+ | (flag_pic ? DW_EH_PE_pcrel : DW_EH_PE_absptr) \
+ | ((CODE) ? 0 : (TARGET_SHMEDIA64 ? DW_EH_PE_sdata8 : DW_EH_PE_sdata4)))
+
+/* Handle special EH pointer encodings. Absolute, pc-relative, and
+ indirect are handled automatically. */
+#define ASM_MAYBE_OUTPUT_ENCODED_ADDR_RTX(FILE, ENCODING, SIZE, ADDR, DONE) \
+ do { \
+ if (((ENCODING) & 0xf) != DW_EH_PE_sdata4 \
+ && ((ENCODING) & 0xf) != DW_EH_PE_sdata8) \
+ { \
+ gcc_assert (GET_CODE (ADDR) == SYMBOL_REF); \
+ SYMBOL_REF_FLAGS (ADDR) |= SYMBOL_FLAG_FUNCTION; \
+ if (0) goto DONE; \
+ } \
+ } while (0)
+
+#if (defined CRT_BEGIN || defined CRT_END) && ! __SHMEDIA__
+/* SH constant pool breaks the devices in crtstuff.c to control section
+ in where code resides. We have to write it as asm code. */
+#define CRT_CALL_STATIC_FUNCTION(SECTION_OP, FUNC) \
+ asm (SECTION_OP "\n\
+ mov.l 1f,r1\n\
+ mova 2f,r0\n\
+ braf r1\n\
+ lds r0,pr\n\
+0: .p2align 2\n\
+1: .long " USER_LABEL_PREFIX #FUNC " - 0b\n\
+2:\n" TEXT_SECTION_ASM_OP);
+#endif /* (defined CRT_BEGIN || defined CRT_END) && ! __SHMEDIA__ */
+
+/* FIXME: middle-end support for highpart optimizations is missing. */
+#define high_life_started reload_in_progress
+
+#endif /* ! GCC_SH_H */
diff --git a/gcc/config/sh/sh.md b/gcc/config/sh/sh.md
new file mode 100644
index 000000000..e261d3339
--- /dev/null
+++ b/gcc/config/sh/sh.md
@@ -0,0 +1,13490 @@
+;;- Machine description for Renesas / SuperH SH.
+;; Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+;; 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+;; Free Software Foundation, Inc.
+;; Contributed by Steve Chamberlain (sac@cygnus.com).
+;; Improved by Jim Wilson (wilson@cygnus.com).
+
+;; 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
+;; <http://www.gnu.org/licenses/>.
+
+
+;; ??? Should prepend a * to all pattern names which are not used.
+;; This will make the compiler smaller, and rebuilds after changes faster.
+
+;; ??? Should be enhanced to include support for many more GNU superoptimizer
+;; sequences. Especially the sequences for arithmetic right shifts.
+
+;; ??? Should check all DImode patterns for consistency and usefulness.
+
+;; ??? The MAC.W and MAC.L instructions are not supported. There is no
+;; way to generate them.
+
+;; ??? The cmp/str instruction is not supported. Perhaps it can be used
+;; for a str* inline function.
+
+;; BSR is not generated by the compiler proper, but when relaxing, it
+;; generates .uses pseudo-ops that allow linker relaxation to create
+;; BSR. This is actually implemented in bfd/{coff,elf32}-sh.c
+
+;; Special constraints for SH machine description:
+;;
+;; t -- T
+;; x -- mac
+;; l -- pr
+;; z -- r0
+;;
+;; Special formats used for outputting SH instructions:
+;;
+;; %. -- print a .s if insn needs delay slot
+;; %@ -- print rte/rts if is/isn't an interrupt function
+;; %# -- output a nop if there is nothing to put in the delay slot
+;; %O -- print a constant without the #
+;; %R -- print the lsw reg of a double
+;; %S -- print the msw reg of a double
+;; %T -- print next word of a double REG or MEM
+;;
+;; Special predicates:
+;;
+;; arith_operand -- operand is valid source for arithmetic op
+;; arith_reg_operand -- operand is valid register for arithmetic op
+;; general_movdst_operand -- operand is valid move destination
+;; general_movsrc_operand -- operand is valid move source
+;; logical_operand -- operand is valid source for logical op
+
+;; -------------------------------------------------------------------------
+;; Constants
+;; -------------------------------------------------------------------------
+
+(define_constants [
+ (AP_REG 145)
+ (PR_REG 146)
+ (T_REG 147)
+ (GBR_REG 144)
+ (MACH_REG 148)
+ (MACL_REG 149)
+ (FPUL_REG 150)
+ (RAP_REG 152)
+
+ (FPSCR_REG 151)
+
+ (PIC_REG 12)
+ (FP_REG 14)
+ (SP_REG 15)
+
+ (PR_MEDIA_REG 18)
+ (T_MEDIA_REG 19)
+
+ (R0_REG 0)
+ (R1_REG 1)
+ (R2_REG 2)
+ (R3_REG 3)
+ (R4_REG 4)
+ (R5_REG 5)
+ (R6_REG 6)
+ (R7_REG 7)
+ (R8_REG 8)
+ (R9_REG 9)
+ (R10_REG 10)
+ (R20_REG 20)
+ (R21_REG 21)
+ (R22_REG 22)
+ (R23_REG 23)
+
+ (DR0_REG 64)
+ (DR2_REG 66)
+ (DR4_REG 68)
+ (FR23_REG 87)
+
+ (TR0_REG 128)
+ (TR1_REG 129)
+ (TR2_REG 130)
+
+ (XD0_REG 136)
+
+ ;; These are used with unspec.
+ (UNSPEC_COMPACT_ARGS 0)
+ (UNSPEC_MOVA 1)
+ (UNSPEC_CASESI 2)
+ (UNSPEC_DATALABEL 3)
+ (UNSPEC_BBR 4)
+ (UNSPEC_SFUNC 5)
+ (UNSPEC_PIC 6)
+ (UNSPEC_GOT 7)
+ (UNSPEC_GOTOFF 8)
+ (UNSPEC_PLT 9)
+ (UNSPEC_CALLER 10)
+ (UNSPEC_GOTPLT 11)
+ (UNSPEC_ICACHE 12)
+ (UNSPEC_INIT_TRAMP 13)
+ (UNSPEC_FCOSA 14)
+ (UNSPEC_FSRRA 15)
+ (UNSPEC_FSINA 16)
+ (UNSPEC_NSB 17)
+ (UNSPEC_ALLOCO 18)
+ (UNSPEC_TLSGD 20)
+ (UNSPEC_TLSLDM 21)
+ (UNSPEC_TLSIE 22)
+ (UNSPEC_DTPOFF 23)
+ (UNSPEC_GOTTPOFF 24)
+ (UNSPEC_TPOFF 25)
+ (UNSPEC_RA 26)
+ (UNSPEC_DIV_INV_M0 30)
+ (UNSPEC_DIV_INV_M1 31)
+ (UNSPEC_DIV_INV_M2 32)
+ (UNSPEC_DIV_INV_M3 33)
+ (UNSPEC_DIV_INV20 34)
+ (UNSPEC_DIV_INV_TABLE 37)
+ (UNSPEC_ASHIFTRT 35)
+ (UNSPEC_THUNK 36)
+ (UNSPEC_CHKADD 38)
+ (UNSPEC_SP_SET 40)
+ (UNSPEC_SP_TEST 41)
+ (UNSPEC_MOVUA 42)
+
+ ;; (unspec [VAL SHIFT] UNSPEC_EXTRACT_S16) computes (short) (VAL >> SHIFT).
+ ;; UNSPEC_EXTRACT_U16 is the unsigned equivalent.
+ (UNSPEC_EXTRACT_S16 43)
+ (UNSPEC_EXTRACT_U16 44)
+
+ ;; (unspec [TARGET ANCHOR] UNSPEC_SYMOFF) == TARGET - ANCHOR.
+ (UNSPEC_SYMOFF 45)
+
+ ;; (unspec [OFFSET ANCHOR] UNSPEC_PCREL_SYMOFF) == OFFSET - (ANCHOR - .).
+ (UNSPEC_PCREL_SYMOFF 46)
+
+ ;; These are used with unspec_volatile.
+ (UNSPECV_BLOCKAGE 0)
+ (UNSPECV_ALIGN 1)
+ (UNSPECV_CONST2 2)
+ (UNSPECV_CONST4 4)
+ (UNSPECV_CONST8 6)
+ (UNSPECV_WINDOW_END 10)
+ (UNSPECV_CONST_END 11)
+ (UNSPECV_EH_RETURN 12)
+])
+
+;; -------------------------------------------------------------------------
+;; Attributes
+;; -------------------------------------------------------------------------
+
+;; Target CPU.
+
+(define_attr "cpu"
+ "sh1,sh2,sh2e,sh2a,sh3,sh3e,sh4,sh4a,sh5"
+ (const (symbol_ref "sh_cpu_attr")))
+
+(define_attr "endian" "big,little"
+ (const (if_then_else (symbol_ref "TARGET_LITTLE_ENDIAN")
+ (const_string "little") (const_string "big"))))
+
+;; Indicate if the default fpu mode is single precision.
+(define_attr "fpu_single" "yes,no"
+ (const (if_then_else (symbol_ref "TARGET_FPU_SINGLE")
+ (const_string "yes") (const_string "no"))))
+
+(define_attr "fmovd" "yes,no"
+ (const (if_then_else (symbol_ref "TARGET_FMOVD")
+ (const_string "yes") (const_string "no"))))
+;; pipeline model
+(define_attr "pipe_model" "sh1,sh4,sh5media"
+ (const
+ (cond [(symbol_ref "TARGET_SHMEDIA") (const_string "sh5media")
+ (symbol_ref "TARGET_SUPERSCALAR") (const_string "sh4")]
+ (const_string "sh1"))))
+
+;; cbranch conditional branch instructions
+;; jump unconditional jumps
+;; arith ordinary arithmetic
+;; arith3 a compound insn that behaves similarly to a sequence of
+;; three insns of type arith
+;; arith3b like above, but might end with a redirected branch
+;; load from memory
+;; load_si Likewise, SImode variant for general register.
+;; fload Likewise, but load to fp register.
+;; store to memory
+;; fstore floating point register to memory
+;; move general purpose register to register
+;; movi8 8-bit immediate to general purpose register
+;; mt_group other sh4 mt instructions
+;; fmove register to register, floating point
+;; smpy word precision integer multiply
+;; dmpy longword or doublelongword precision integer multiply
+;; return rts
+;; pload load of pr reg, which can't be put into delay slot of rts
+;; prset copy register to pr reg, ditto
+;; pstore store of pr reg, which can't be put into delay slot of jsr
+;; prget copy pr to register, ditto
+;; pcload pc relative load of constant value
+;; pcfload Likewise, but load to fp register.
+;; pcload_si Likewise, SImode variant for general register.
+;; rte return from exception
+;; sfunc special function call with known used registers
+;; call function call
+;; fp floating point
+;; fpscr_toggle toggle a bit in the fpscr
+;; fdiv floating point divide (or square root)
+;; gp_fpul move from general purpose register to fpul
+;; fpul_gp move from fpul to general purpose register
+;; mac_gp move from mac[lh] to general purpose register
+;; gp_mac move from general purpose register to mac[lh]
+;; mac_mem move from mac[lh] to memory
+;; mem_mac move from memory to mac[lh]
+;; dfp_arith,dfp_mul, fp_cmp,dfp_cmp,dfp_conv
+;; ftrc_s fix_truncsfsi2_i4
+;; dfdiv double precision floating point divide (or square root)
+;; cwb ic_invalidate_line_i
+;; movua SH4a unaligned load
+;; fsrra square root reciprocal approximate
+;; fsca sine and cosine approximate
+;; tls_load load TLS related address
+;; arith_media SHmedia arithmetic, logical, and shift instructions
+;; cbranch_media SHmedia conditional branch instructions
+;; cmp_media SHmedia compare instructions
+;; dfdiv_media SHmedia double precision divide and square root
+;; dfmul_media SHmedia double precision multiply instruction
+;; dfparith_media SHmedia double precision floating point arithmetic
+;; dfpconv_media SHmedia double precision floating point conversions
+;; dmpy_media SHmedia longword multiply
+;; fcmp_media SHmedia floating point compare instructions
+;; fdiv_media SHmedia single precision divide and square root
+;; fload_media SHmedia floating point register load instructions
+;; fmove_media SHmedia floating point register moves (inc. fabs and fneg)
+;; fparith_media SHmedia single precision floating point arithmetic
+;; fpconv_media SHmedia single precision floating point conversions
+;; fstore_media SHmedia floating point register store instructions
+;; gettr_media SHmedia gettr instruction
+;; invalidate_line_media SHmedia invalidate_line sequence
+;; jump_media SHmedia unconditional branch instructions
+;; load_media SHmedia general register load instructions
+;; pt_media SHmedia pt instruction (expanded by assembler)
+;; ptabs_media SHmedia ptabs instruction
+;; store_media SHmedia general register store instructions
+;; mcmp_media SHmedia multimedia compare, absolute, saturating ops
+;; mac_media SHmedia mac-style fixed point operations
+;; d2mpy_media SHmedia: two 32-bit integer multiplies
+;; atrans_media SHmedia approximate transcendental functions
+;; ustore_media SHmedia unaligned stores
+;; nil no-op move, will be deleted.
+
+(define_attr "type"
+ "mt_group,cbranch,jump,jump_ind,arith,arith3,arith3b,dyn_shift,load,load_si,fload,store,fstore,move,movi8,fmove,smpy,dmpy,return,pload,prset,pstore,prget,pcload,pcload_si,pcfload,rte,sfunc,call,fp,fpscr_toggle,fdiv,ftrc_s,dfp_arith,dfp_mul,fp_cmp,dfp_cmp,dfp_conv,dfdiv,gp_fpul,fpul_gp,mac_gp,gp_mac,mac_mem,mem_mac,mem_fpscr,gp_fpscr,cwb,movua,fsrra,fsca,tls_load,arith_media,cbranch_media,cmp_media,dfdiv_media,dfmul_media,dfparith_media,dfpconv_media,dmpy_media,fcmp_media,fdiv_media,fload_media,fmove_media,fparith_media,fpconv_media,fstore_media,gettr_media,invalidate_line_media,jump_media,load_media,pt_media,ptabs_media,store_media,mcmp_media,mac_media,d2mpy_media,atrans_media,ustore_media,nil,other"
+ (const_string "other"))
+
+;; We define a new attribute namely "insn_class".We use
+;; this for the DFA based pipeline description.
+;;
+;; mt_group SH4 "mt" group instructions.
+;;
+;; ex_group SH4 "ex" group instructions.
+;;
+;; ls_group SH4 "ls" group instructions.
+;;
+
+(define_attr "insn_class"
+ "mt_group,ex_group,ls_group,br_group,fe_group,co_group,none"
+ (cond [(eq_attr "type" "move,mt_group") (const_string "mt_group")
+ (eq_attr "type" "movi8,arith,dyn_shift") (const_string "ex_group")
+ (eq_attr "type" "fmove,load,pcload,load_si,pcload_si,fload,pcfload,store,fstore,gp_fpul,fpul_gp") (const_string "ls_group")
+ (eq_attr "type" "cbranch,jump") (const_string "br_group")
+ (eq_attr "type" "fp,fp_cmp,fdiv,ftrc_s,dfp_arith,dfp_mul,dfp_conv,dfdiv")
+ (const_string "fe_group")
+ (eq_attr "type" "jump_ind,smpy,dmpy,mac_gp,return,pload,prset,pstore,prget,rte,sfunc,call,dfp_cmp,mem_fpscr,gp_fpscr,cwb,gp_mac,mac_mem,mem_mac") (const_string "co_group")]
+ (const_string "none")))
+;; nil are zero instructions, and arith3 / arith3b are multiple instructions,
+;; so these do not belong in an insn group, although they are modeled
+;; with their own define_insn_reservations.
+
+;; Indicate what precision must be selected in fpscr for this insn, if any.
+
+(define_attr "fp_mode" "single,double,none" (const_string "none"))
+
+;; Indicate if the fpu mode is set by this instruction
+;; "unknown" must have the value as "none" in fp_mode, and means
+;; that the instruction/abi has left the processor in an unknown
+;; state.
+;; "none" means that nothing has changed and no mode is set.
+;; This attribute is only used for the Renesas ABI.
+(define_attr "fp_set" "single,double,unknown,none" (const_string "none"))
+
+; If a conditional branch destination is within -252..258 bytes away
+; from the instruction it can be 2 bytes long. Something in the
+; range -4090..4100 bytes can be 6 bytes long. All other conditional
+; branches are initially assumed to be 16 bytes long.
+; In machine_dependent_reorg, we split all branches that are longer than
+; 2 bytes.
+
+;; The maximum range used for SImode constant pool entries is 1018. A final
+;; instruction can add 8 bytes while only being 4 bytes in size, thus we
+;; can have a total of 1022 bytes in the pool. Add 4 bytes for a branch
+;; instruction around the pool table, 2 bytes of alignment before the table,
+;; and 30 bytes of alignment after the table. That gives a maximum total
+;; pool size of 1058 bytes.
+;; Worst case code/pool content size ratio is 1:2 (using asms).
+;; Thus, in the worst case, there is one instruction in front of a maximum
+;; sized pool, and then there are 1052 bytes of pool for every 508 bytes of
+;; code. For the last n bytes of code, there are 2n + 36 bytes of pool.
+;; If we have a forward branch, the initial table will be put after the
+;; unconditional branch.
+;;
+;; ??? We could do much better by keeping track of the actual pcloads within
+;; the branch range and in the pcload range in front of the branch range.
+
+;; ??? This looks ugly because genattrtab won't allow if_then_else or cond
+;; inside an le.
+(define_attr "short_cbranch_p" "no,yes"
+ (cond [(ne (symbol_ref "mdep_reorg_phase <= SH_FIXUP_PCLOAD") (const_int 0))
+ (const_string "no")
+ (leu (plus (minus (match_dup 0) (pc)) (const_int 252)) (const_int 506))
+ (const_string "yes")
+ (ne (symbol_ref "NEXT_INSN (PREV_INSN (insn)) != insn") (const_int 0))
+ (const_string "no")
+ (leu (plus (minus (match_dup 0) (pc)) (const_int 252)) (const_int 508))
+ (const_string "yes")
+ ] (const_string "no")))
+
+(define_attr "med_branch_p" "no,yes"
+ (cond [(leu (plus (minus (match_dup 0) (pc)) (const_int 990))
+ (const_int 1988))
+ (const_string "yes")
+ (ne (symbol_ref "mdep_reorg_phase <= SH_FIXUP_PCLOAD") (const_int 0))
+ (const_string "no")
+ (leu (plus (minus (match_dup 0) (pc)) (const_int 4092))
+ (const_int 8186))
+ (const_string "yes")
+ ] (const_string "no")))
+
+(define_attr "med_cbranch_p" "no,yes"
+ (cond [(leu (plus (minus (match_dup 0) (pc)) (const_int 988))
+ (const_int 1986))
+ (const_string "yes")
+ (ne (symbol_ref "mdep_reorg_phase <= SH_FIXUP_PCLOAD") (const_int 0))
+ (const_string "no")
+ (leu (plus (minus (match_dup 0) (pc)) (const_int 4090))
+ (const_int 8184))
+ (const_string "yes")
+ ] (const_string "no")))
+
+(define_attr "braf_branch_p" "no,yes"
+ (cond [(ne (symbol_ref "! TARGET_SH2") (const_int 0))
+ (const_string "no")
+ (leu (plus (minus (match_dup 0) (pc)) (const_int 10330))
+ (const_int 20660))
+ (const_string "yes")
+ (ne (symbol_ref "mdep_reorg_phase <= SH_FIXUP_PCLOAD") (const_int 0))
+ (const_string "no")
+ (leu (plus (minus (match_dup 0) (pc)) (const_int 32764))
+ (const_int 65530))
+ (const_string "yes")
+ ] (const_string "no")))
+
+(define_attr "braf_cbranch_p" "no,yes"
+ (cond [(ne (symbol_ref "! TARGET_SH2") (const_int 0))
+ (const_string "no")
+ (leu (plus (minus (match_dup 0) (pc)) (const_int 10328))
+ (const_int 20658))
+ (const_string "yes")
+ (ne (symbol_ref "mdep_reorg_phase <= SH_FIXUP_PCLOAD") (const_int 0))
+ (const_string "no")
+ (leu (plus (minus (match_dup 0) (pc)) (const_int 32762))
+ (const_int 65528))
+ (const_string "yes")
+ ] (const_string "no")))
+
+; An unconditional jump in the range -4092..4098 can be 2 bytes long.
+; For wider ranges, we need a combination of a code and a data part.
+; If we can get a scratch register for a long range jump, the code
+; part can be 4 bytes long; otherwise, it must be 8 bytes long.
+; If the jump is in the range -32764..32770, the data part can be 2 bytes
+; long; otherwise, it must be 6 bytes long.
+
+; All other instructions are two bytes long by default.
+
+;; ??? This should use something like *branch_p (minus (match_dup 0) (pc)),
+;; but getattrtab doesn't understand this.
+(define_attr "length" ""
+ (cond [(eq_attr "type" "cbranch")
+ (cond [(eq_attr "short_cbranch_p" "yes")
+ (const_int 2)
+ (eq_attr "med_cbranch_p" "yes")
+ (const_int 6)
+ (eq_attr "braf_cbranch_p" "yes")
+ (const_int 12)
+;; ??? using pc is not computed transitively.
+ (ne (match_dup 0) (match_dup 0))
+ (const_int 14)
+ (ne (symbol_ref ("flag_pic")) (const_int 0))
+ (const_int 24)
+ ] (const_int 16))
+ (eq_attr "type" "jump")
+ (cond [(eq_attr "med_branch_p" "yes")
+ (const_int 2)
+ (and (ne (symbol_ref "prev_nonnote_insn (insn)")
+ (const_int 0))
+ (and (eq (symbol_ref "GET_CODE (prev_nonnote_insn (insn))")
+ (symbol_ref "INSN"))
+ (eq (symbol_ref "INSN_CODE (prev_nonnote_insn (insn))")
+ (symbol_ref "code_for_indirect_jump_scratch"))))
+ (cond [(eq_attr "braf_branch_p" "yes")
+ (const_int 6)
+ (eq (symbol_ref "flag_pic") (const_int 0))
+ (const_int 10)
+ (ne (symbol_ref "TARGET_SH2") (const_int 0))
+ (const_int 10)] (const_int 18))
+ (eq_attr "braf_branch_p" "yes")
+ (const_int 10)
+;; ??? using pc is not computed transitively.
+ (ne (match_dup 0) (match_dup 0))
+ (const_int 12)
+ (ne (symbol_ref ("flag_pic")) (const_int 0))
+ (const_int 22)
+ ] (const_int 14))
+ (eq_attr "type" "pt_media")
+ (if_then_else (ne (symbol_ref "TARGET_SHMEDIA64") (const_int 0))
+ (const_int 20) (const_int 12))
+ (and (eq_attr "type" "jump_media")
+ (ne (symbol_ref "TARGET_SH5_CUT2_WORKAROUND") (const_int 0)))
+ (const_int 8)
+ ] (if_then_else (ne (symbol_ref "TARGET_SHMEDIA") (const_int 0))
+ (const_int 4)
+ (const_int 2))))
+
+;; DFA descriptions for the pipelines
+
+(include "sh1.md")
+(include "shmedia.md")
+(include "sh4.md")
+
+(include "predicates.md")
+(include "constraints.md")
+
+;; Definitions for filling delay slots
+
+(define_attr "needs_delay_slot" "yes,no" (const_string "no"))
+
+(define_attr "banked" "yes,no"
+ (cond [(eq (symbol_ref "sh_loads_bankedreg_p (insn)")
+ (const_int 1))
+ (const_string "yes")]
+ (const_string "no")))
+
+;; ??? This should be (nil) instead of (const_int 0)
+(define_attr "hit_stack" "yes,no"
+ (cond [(eq (symbol_ref "find_regno_note (insn, REG_INC, SP_REG)")
+ (const_int 0))
+ (const_string "no")]
+ (const_string "yes")))
+
+(define_attr "interrupt_function" "no,yes"
+ (const (symbol_ref "current_function_interrupt")))
+
+(define_attr "in_delay_slot" "yes,no"
+ (cond [(eq_attr "type" "cbranch") (const_string "no")
+ (eq_attr "type" "pcload,pcload_si") (const_string "no")
+ (eq_attr "needs_delay_slot" "yes") (const_string "no")
+ (eq_attr "length" "2") (const_string "yes")
+ ] (const_string "no")))
+
+(define_attr "cond_delay_slot" "yes,no"
+ (cond [(eq_attr "in_delay_slot" "yes") (const_string "yes")
+ ] (const_string "no")))
+
+(define_attr "is_sfunc" ""
+ (if_then_else (eq_attr "type" "sfunc") (const_int 1) (const_int 0)))
+
+(define_attr "is_mac_media" ""
+ (if_then_else (eq_attr "type" "mac_media") (const_int 1) (const_int 0)))
+
+(define_attr "branch_zero" "yes,no"
+ (cond [(eq_attr "type" "!cbranch") (const_string "no")
+ (ne (symbol_ref "(next_active_insn (insn)\
+ == (prev_active_insn\
+ (XEXP (SET_SRC (PATTERN (insn)), 1))))\
+ && get_attr_length (next_active_insn (insn)) == 2")
+ (const_int 0))
+ (const_string "yes")]
+ (const_string "no")))
+
+;; SH4 Double-precision computation with double-precision result -
+;; the two halves are ready at different times.
+(define_attr "dfp_comp" "yes,no"
+ (cond [(eq_attr "type" "dfp_arith,dfp_mul,dfp_conv,dfdiv") (const_string "yes")]
+ (const_string "no")))
+
+;; Insns for which the latency of a preceding fp insn is decreased by one.
+(define_attr "late_fp_use" "yes,no" (const_string "no"))
+;; And feeding insns for which this relevant.
+(define_attr "any_fp_comp" "yes,no"
+ (cond [(eq_attr "type" "fp,fdiv,ftrc_s,dfp_arith,dfp_mul,dfp_conv,dfdiv")
+ (const_string "yes")]
+ (const_string "no")))
+
+(define_attr "any_int_load" "yes,no"
+ (cond [(eq_attr "type" "load,load_si,pcload,pcload_si")
+ (const_string "yes")]
+ (const_string "no")))
+
+(define_attr "highpart" "user, ignore, extend, depend, must_split"
+ (const_string "user"))
+
+(define_delay
+ (eq_attr "needs_delay_slot" "yes")
+ [(eq_attr "in_delay_slot" "yes") (nil) (nil)])
+
+;; On the SH and SH2, the rte instruction reads the return pc from the stack,
+;; and thus we can't put a pop instruction in its delay slot.
+;; ??? On the SH3, the rte instruction does not use the stack, so a pop
+;; instruction can go in the delay slot.
+
+;; Since a normal return (rts) implicitly uses the PR register,
+;; we can't allow PR register loads in an rts delay slot.
+
+(define_delay
+ (eq_attr "type" "return")
+ [(and (eq_attr "in_delay_slot" "yes")
+ (ior (and (eq_attr "interrupt_function" "no")
+ (eq_attr "type" "!pload,prset"))
+ (and (eq_attr "interrupt_function" "yes")
+ (ior
+ (eq (symbol_ref "TARGET_SH3") (const_int 0))
+ (eq_attr "hit_stack" "no")
+ (eq_attr "banked" "no"))))) (nil) (nil)])
+
+;; Since a call implicitly uses the PR register, we can't allow
+;; a PR register store in a jsr delay slot.
+
+(define_delay
+ (ior (eq_attr "type" "call") (eq_attr "type" "sfunc"))
+ [(and (eq_attr "in_delay_slot" "yes")
+ (eq_attr "type" "!pstore,prget")) (nil) (nil)])
+
+;; Say that we have annulled true branches, since this gives smaller and
+;; faster code when branches are predicted as not taken.
+
+;; ??? The non-annulled condition should really be "in_delay_slot",
+;; but insns that can be filled in non-annulled get priority over insns
+;; that can only be filled in anulled.
+
+(define_delay
+ (and (eq_attr "type" "cbranch")
+ (ne (symbol_ref "TARGET_SH2") (const_int 0)))
+ ;; SH2e has a hardware bug that pretty much prohibits the use of
+ ;; annuled delay slots.
+ [(eq_attr "cond_delay_slot" "yes") (and (eq_attr "cond_delay_slot" "yes")
+ (not (eq_attr "cpu" "sh2e"))) (nil)])
+
+;; -------------------------------------------------------------------------
+;; SImode signed integer comparisons
+;; -------------------------------------------------------------------------
+
+(define_insn ""
+ [(set (reg:SI T_REG)
+ (eq:SI (and:SI (match_operand:SI 0 "arith_reg_operand" "z,r")
+ (match_operand:SI 1 "logical_operand" "K08,r"))
+ (const_int 0)))]
+ "TARGET_SH1"
+ "tst %1,%0"
+ [(set_attr "type" "mt_group")])
+
+;; ??? Perhaps should only accept reg/constant if the register is reg 0.
+;; That would still allow reload to create cmpi instructions, but would
+;; perhaps allow forcing the constant into a register when that is better.
+;; Probably should use r0 for mem/imm compares, but force constant into a
+;; register for pseudo/imm compares.
+
+(define_insn "cmpeqsi_t"
+ [(set (reg:SI T_REG)
+ (eq:SI (match_operand:SI 0 "arith_reg_operand" "r,z,r")
+ (match_operand:SI 1 "arith_operand" "N,rI08,r")))]
+ "TARGET_SH1"
+ "@
+ tst %0,%0
+ cmp/eq %1,%0
+ cmp/eq %1,%0"
+ [(set_attr "type" "mt_group")])
+
+(define_insn "cmpgtsi_t"
+ [(set (reg:SI T_REG)
+ (gt:SI (match_operand:SI 0 "arith_reg_operand" "r,r")
+ (match_operand:SI 1 "arith_reg_or_0_operand" "r,N")))]
+ "TARGET_SH1"
+ "@
+ cmp/gt %1,%0
+ cmp/pl %0"
+ [(set_attr "type" "mt_group")])
+
+(define_insn "cmpgesi_t"
+ [(set (reg:SI T_REG)
+ (ge:SI (match_operand:SI 0 "arith_reg_operand" "r,r")
+ (match_operand:SI 1 "arith_reg_or_0_operand" "r,N")))]
+ "TARGET_SH1"
+ "@
+ cmp/ge %1,%0
+ cmp/pz %0"
+ [(set_attr "type" "mt_group")])
+
+;; -------------------------------------------------------------------------
+;; SImode compare and branch
+;; -------------------------------------------------------------------------
+
+(define_expand "cbranchsi4"
+ [(set (pc)
+ (if_then_else (match_operator 0 "comparison_operator"
+ [(match_operand:SI 1 "arith_operand" "")
+ (match_operand:SI 2 "arith_operand" "")])
+ (label_ref (match_operand 3 "" ""))
+ (pc)))
+ (clobber (reg:SI T_REG))]
+ ""
+ "if (TARGET_SHMEDIA)
+ emit_jump_insn (gen_cbranchint4_media (operands[0], operands[1],
+ operands[2], operands[3]));
+ else if (TARGET_CBRANCHDI4)
+ expand_cbranchsi4 (operands, LAST_AND_UNUSED_RTX_CODE, -1);
+ else
+ sh_emit_compare_and_branch (operands, SImode);
+ DONE;")
+
+;; -------------------------------------------------------------------------
+;; SImode unsigned integer comparisons
+;; -------------------------------------------------------------------------
+
+(define_insn_and_split "cmpgeusi_t"
+ [(set (reg:SI T_REG)
+ (geu:SI (match_operand:SI 0 "arith_reg_operand" "r")
+ (match_operand:SI 1 "arith_reg_or_0_operand" "rN")))]
+ "TARGET_SH1"
+ "cmp/hs %1,%0"
+ "&& operands[1] == CONST0_RTX (SImode)"
+ [(pc)]
+ "
+{
+ emit_insn (gen_sett ());
+ DONE;
+}"
+ [(set_attr "type" "mt_group")])
+
+(define_insn "cmpgtusi_t"
+ [(set (reg:SI T_REG)
+ (gtu:SI (match_operand:SI 0 "arith_reg_operand" "r")
+ (match_operand:SI 1 "arith_reg_operand" "r")))]
+ "TARGET_SH1"
+ "cmp/hi %1,%0"
+ [(set_attr "type" "mt_group")])
+
+
+;; -------------------------------------------------------------------------
+;; DImode compare and branch
+;; -------------------------------------------------------------------------
+
+
+;; arith3 patterns don't work well with the sh4-300 branch prediction mechanism.
+;; Therefore, we aim to have a set of three branches that go straight to the
+;; destination, i.e. only one of them is taken at any one time.
+;; This mechanism should also be slightly better for the sh4-200.
+
+(define_expand "cbranchdi4"
+ [(set (pc)
+ (if_then_else (match_operator 0 "comparison_operator"
+ [(match_operand:DI 1 "arith_operand" "")
+ (match_operand:DI 2 "arith_operand" "")])
+ (label_ref (match_operand 3 "" ""))
+ (pc)))
+ (clobber (match_dup 4))
+ (clobber (reg:SI T_REG))]
+ "TARGET_CBRANCHDI4 || TARGET_SH2 || TARGET_SHMEDIA"
+ "
+{
+ enum rtx_code comparison;
+
+ if (TARGET_SHMEDIA)
+ {
+ emit_jump_insn (gen_cbranchint4_media (operands[0], operands[1],
+ operands[2], operands[3]));
+ DONE;
+ }
+
+ else if (!TARGET_CBRANCHDI4)
+ {
+ sh_emit_compare_and_branch (operands, DImode);
+ DONE;
+ }
+
+ else
+ {
+ if (expand_cbranchdi4 (operands, LAST_AND_UNUSED_RTX_CODE))
+ DONE;
+
+ comparison = prepare_cbranch_operands (operands, DImode,
+ LAST_AND_UNUSED_RTX_CODE);
+ if (comparison != GET_CODE (operands[0]))
+ operands[0]
+ = gen_rtx_fmt_ee (comparison, VOIDmode, operands[1], operands[2]);
+ operands[4] = gen_rtx_SCRATCH (SImode);
+ }
+}")
+
+(define_insn_and_split "cbranchdi4_i"
+ [(set (pc)
+ (if_then_else (match_operator 0 "comparison_operator"
+ [(match_operand:DI 1 "arith_operand" "r,r")
+ (match_operand:DI 2 "arith_operand" "rN,I08")])
+ (label_ref (match_operand 3 "" ""))
+ (pc)))
+ (clobber (match_scratch:SI 4 "=X,&r"))
+ (clobber (reg:SI T_REG))]
+ "TARGET_CBRANCHDI4"
+ "#"
+ "&& reload_completed"
+ [(pc)]
+ "
+{
+ if (!expand_cbranchdi4 (operands, GET_CODE (operands[0])))
+ FAIL;
+ DONE;
+}")
+
+;; -------------------------------------------------------------------------
+;; DImode signed integer comparisons
+;; -------------------------------------------------------------------------
+
+(define_insn ""
+ [(set (reg:SI T_REG)
+ (eq:SI (and:DI (match_operand:DI 0 "arith_reg_operand" "r")
+ (match_operand:DI 1 "arith_operand" "r"))
+ (const_int 0)))]
+ "TARGET_SH1"
+ "* return output_branchy_insn (EQ, \"tst\\t%S1,%S0\;bf\\t%l9\;tst\\t%R1,%R0\",
+ insn, operands);"
+ [(set_attr "length" "6")
+ (set_attr "type" "arith3b")])
+
+(define_insn "cmpeqdi_t"
+ [(set (reg:SI T_REG)
+ (eq:SI (match_operand:DI 0 "arith_reg_operand" "r,r")
+ (match_operand:DI 1 "arith_reg_or_0_operand" "N,r")))]
+ "TARGET_SH1"
+ "@
+ tst %S0,%S0\;bf %,Ldi%=\;tst %R0,%R0\\n%,Ldi%=:
+ cmp/eq %S1,%S0\;bf %,Ldi%=\;cmp/eq %R1,%R0\\n%,Ldi%=:"
+ [(set_attr "length" "6")
+ (set_attr "type" "arith3b")])
+
+(define_split
+ [(set (reg:SI T_REG)
+ (eq:SI (match_operand:DI 0 "arith_reg_operand" "")
+ (match_operand:DI 1 "arith_reg_or_0_operand" "")))]
+;; If we applied this split when not optimizing, it would only be
+;; applied during the machine-dependent reorg, when no new basic blocks
+;; may be created.
+ "TARGET_SH1 && reload_completed && optimize"
+ [(set (reg:SI T_REG) (eq:SI (match_dup 2) (match_dup 3)))
+ (set (pc) (if_then_else (eq (reg:SI T_REG) (const_int 0))
+ (label_ref (match_dup 6))
+ (pc)))
+ (set (reg:SI T_REG) (eq:SI (match_dup 4) (match_dup 5)))
+ (match_dup 6)]
+ "
+{
+ operands[2]
+ = gen_rtx_REG (SImode,
+ true_regnum (operands[0]) + (TARGET_LITTLE_ENDIAN ? 1 : 0));
+ operands[3]
+ = (operands[1] == const0_rtx
+ ? const0_rtx
+ : gen_rtx_REG (SImode,
+ true_regnum (operands[1])
+ + (TARGET_LITTLE_ENDIAN ? 1 : 0)));
+ operands[4] = gen_lowpart (SImode, operands[0]);
+ operands[5] = gen_lowpart (SImode, operands[1]);
+ operands[6] = gen_label_rtx ();
+}")
+
+(define_insn "cmpgtdi_t"
+ [(set (reg:SI T_REG)
+ (gt:SI (match_operand:DI 0 "arith_reg_operand" "r,r")
+ (match_operand:DI 1 "arith_reg_or_0_operand" "r,N")))]
+ "TARGET_SH2"
+ "@
+ cmp/eq\\t%S1,%S0\;bf{.|/}s\\t%,Ldi%=\;cmp/gt\\t%S1,%S0\;cmp/hi\\t%R1,%R0\\n%,Ldi%=:
+ tst\\t%S0,%S0\;bf{.|/}s\\t%,Ldi%=\;cmp/pl\\t%S0\;cmp/hi\\t%S0,%R0\\n%,Ldi%=:"
+ [(set_attr "length" "8")
+ (set_attr "type" "arith3")])
+
+(define_insn "cmpgedi_t"
+ [(set (reg:SI T_REG)
+ (ge:SI (match_operand:DI 0 "arith_reg_operand" "r,r")
+ (match_operand:DI 1 "arith_reg_or_0_operand" "r,N")))]
+ "TARGET_SH2"
+ "@
+ cmp/eq\\t%S1,%S0\;bf{.|/}s\\t%,Ldi%=\;cmp/ge\\t%S1,%S0\;cmp/hs\\t%R1,%R0\\n%,Ldi%=:
+ cmp/pz\\t%S0"
+ [(set_attr "length" "8,2")
+ (set_attr "type" "arith3,mt_group")])
+
+;; -------------------------------------------------------------------------
+;; DImode unsigned integer comparisons
+;; -------------------------------------------------------------------------
+
+(define_insn "cmpgeudi_t"
+ [(set (reg:SI T_REG)
+ (geu:SI (match_operand:DI 0 "arith_reg_operand" "r")
+ (match_operand:DI 1 "arith_reg_operand" "r")))]
+ "TARGET_SH2"
+ "cmp/eq\\t%S1,%S0\;bf{.|/}s\\t%,Ldi%=\;cmp/hs\\t%S1,%S0\;cmp/hs\\t%R1,%R0\\n%,Ldi%=:"
+ [(set_attr "length" "8")
+ (set_attr "type" "arith3")])
+
+(define_insn "cmpgtudi_t"
+ [(set (reg:SI T_REG)
+ (gtu:SI (match_operand:DI 0 "arith_reg_operand" "r")
+ (match_operand:DI 1 "arith_reg_operand" "r")))]
+ "TARGET_SH2"
+ "cmp/eq\\t%S1,%S0\;bf{.|/}s\\t%,Ldi%=\;cmp/hi\\t%S1,%S0\;cmp/hi\\t%R1,%R0\\n%,Ldi%=:"
+ [(set_attr "length" "8")
+ (set_attr "type" "arith3")])
+
+(define_insn "cmpeqsi_media"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (eq:SI (match_operand:SI 1 "logical_operand" "%r")
+ (match_operand:SI 2 "cmp_operand" "Nr")))]
+ "TARGET_SHMEDIA"
+ "cmpeq %1, %N2, %0"
+ [(set_attr "type" "cmp_media")])
+
+(define_insn "cmpeqdi_media"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (eq:SI (match_operand:DI 1 "register_operand" "%r")
+ (match_operand:DI 2 "cmp_operand" "Nr")))]
+ "TARGET_SHMEDIA"
+ "cmpeq %1, %N2, %0"
+ [(set_attr "type" "cmp_media")])
+
+(define_insn "cmpgtsi_media"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (gt:SI (match_operand:SI 1 "cmp_operand" "Nr")
+ (match_operand:SI 2 "cmp_operand" "rN")))]
+ "TARGET_SHMEDIA"
+ "cmpgt %N1, %N2, %0"
+ [(set_attr "type" "cmp_media")])
+
+(define_insn "cmpgtdi_media"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (gt:SI (match_operand:DI 1 "arith_reg_or_0_operand" "Nr")
+ (match_operand:DI 2 "arith_reg_or_0_operand" "rN")))]
+ "TARGET_SHMEDIA"
+ "cmpgt %N1, %N2, %0"
+ [(set_attr "type" "cmp_media")])
+
+(define_insn "cmpgtusi_media"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (gtu:SI (match_operand:SI 1 "cmp_operand" "Nr")
+ (match_operand:SI 2 "cmp_operand" "rN")))]
+ "TARGET_SHMEDIA"
+ "cmpgtu %N1, %N2, %0"
+ [(set_attr "type" "cmp_media")])
+
+(define_insn "cmpgtudi_media"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (gtu:SI (match_operand:DI 1 "arith_reg_or_0_operand" "Nr")
+ (match_operand:DI 2 "arith_reg_or_0_operand" "rN")))]
+ "TARGET_SHMEDIA"
+ "cmpgtu %N1, %N2, %0"
+ [(set_attr "type" "cmp_media")])
+
+; These two patterns are for combine.
+(define_insn "*cmpne0sisi_media"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (ne:SI (match_operand:SI 1 "arith_reg_operand" "r") (const_int 0)))]
+ "TARGET_SHMEDIA"
+ "cmpgtu %1,r63,%0"
+ [(set_attr "type" "cmp_media")])
+
+;; -------------------------------------------------------------------------
+;; Conditional move instructions
+;; -------------------------------------------------------------------------
+
+;; The insn names may seem reversed, but note that cmveq performs the move
+;; if op1 == 0, and cmvne does it if op1 != 0.
+
+(define_insn "movdicc_false"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (if_then_else:DI (eq (match_operand:DI 1 "arith_reg_operand" "r")
+ (const_int 0))
+ (match_operand:DI 2 "arith_reg_or_0_operand" "rN")
+ (match_operand:DI 3 "arith_reg_operand" "0")))]
+ "TARGET_SHMEDIA"
+ "cmveq %1, %N2, %0"
+ [(set_attr "type" "arith_media")])
+
+(define_insn "movdicc_true"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (if_then_else:DI (ne (match_operand:DI 1 "arith_reg_operand" "r")
+ (const_int 0))
+ (match_operand:DI 2 "arith_reg_or_0_operand" "rN")
+ (match_operand:DI 3 "arith_reg_operand" "0")))]
+ "TARGET_SHMEDIA"
+ "cmvne %1, %N2, %0"
+ [(set_attr "type" "arith_media")])
+
+(define_peephole2
+ [(set (match_operand:DI 0 "arith_reg_dest" "")
+ (if_then_else:DI (match_operator 3 "equality_comparison_operator"
+ [(match_operand:DI 1 "arith_reg_operand" "")
+ (const_int 0)])
+ (match_operand:DI 2 "arith_reg_dest" "")
+ (match_dup 0)))
+ (set (match_dup 2) (match_dup 0))]
+ "TARGET_SHMEDIA && peep2_reg_dead_p (2, operands[0])"
+ [(set (match_dup 2)
+ (if_then_else:DI (match_dup 3) (match_dup 0) (match_dup 2)))]
+ "
+{
+ operands[3] = gen_rtx_fmt_ee (reverse_condition (GET_CODE (operands[3])),
+ VOIDmode, operands[1], CONST0_RTX (DImode));
+}")
+
+(define_peephole2
+ [(set (match_operand:DI 0 "general_movdst_operand" "")
+ (match_operand:DI 1 "arith_reg_or_0_operand" ""))
+ (set (match_operand:DI 2 "arith_reg_dest" "")
+ (if_then_else:DI (match_operator 4 "equality_comparison_operator"
+ [(match_operand:DI 3 "arith_reg_operand" "")
+ (const_int 0)])
+ (match_dup 0)
+ (match_dup 2)))]
+ "TARGET_SHMEDIA && peep2_reg_dead_p (2, operands[0])"
+ [(set (match_dup 2)
+ (if_then_else:DI (match_dup 4) (match_dup 1) (match_dup 2)))]
+ "")
+
+(define_expand "movdicc"
+ [(set (match_operand:DI 0 "register_operand" "")
+ (if_then_else:DI (match_operand 1 "comparison_operator" "")
+ (match_operand:DI 2 "register_operand" "")
+ (match_operand:DI 3 "register_operand" "")))]
+ "TARGET_SHMEDIA"
+ "
+{
+ if ((GET_CODE (operands[1]) == EQ || GET_CODE (operands[1]) == NE)
+ && GET_MODE (XEXP (operands[1], 0)) == DImode
+ && XEXP (operands[1], 1) == const0_rtx)
+ ;
+ else
+ {
+ if (!can_create_pseudo_p ())
+ FAIL;
+
+ operands[1] = sh_emit_cheap_store_flag (GET_MODE (operands[0]),
+ GET_CODE (operands[1]),
+ XEXP (operands[1], 0),
+ XEXP (operands[1], 1));
+ if (!operands[1])
+ FAIL;
+ }
+}")
+
+;; Add SImode variants for cmveq / cmvne to compensate for not promoting
+;; SImode to DImode.
+(define_insn "movsicc_false"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (if_then_else:SI (eq (match_operand:SI 1 "arith_reg_operand" "r")
+ (const_int 0))
+ (match_operand:SI 2 "arith_reg_or_0_operand" "rN")
+ (match_operand:SI 3 "arith_reg_operand" "0")))]
+ "TARGET_SHMEDIA"
+ "cmveq %1, %N2, %0"
+ [(set_attr "type" "arith_media")])
+
+(define_insn "movsicc_true"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (if_then_else:SI (ne (match_operand:SI 1 "arith_reg_operand" "r")
+ (const_int 0))
+ (match_operand:SI 2 "arith_reg_or_0_operand" "rN")
+ (match_operand:SI 3 "arith_reg_operand" "0")))]
+ "TARGET_SHMEDIA"
+ "cmvne %1, %N2, %0"
+ [(set_attr "type" "arith_media")])
+
+(define_peephole2
+ [(set (match_operand:SI 0 "arith_reg_dest" "")
+ (if_then_else:SI (match_operator 3 "equality_comparison_operator"
+ [(match_operand:SI 1 "arith_reg_operand" "")
+ (const_int 0)])
+ (match_operand:SI 2 "arith_reg_dest" "")
+ (match_dup 0)))
+ (set (match_dup 2) (match_dup 0))]
+ "TARGET_SHMEDIA && peep2_reg_dead_p (2, operands[0])"
+ [(set (match_dup 2)
+ (if_then_else:SI (match_dup 3) (match_dup 0) (match_dup 2)))]
+ "
+{
+ operands[3] = gen_rtx_fmt_ee (reverse_condition (GET_CODE (operands[3])),
+ VOIDmode, operands[1], CONST0_RTX (SImode));
+}")
+
+(define_peephole2
+ [(set (match_operand:SI 0 "general_movdst_operand" "")
+ (match_operand:SI 1 "arith_reg_or_0_operand" ""))
+ (set (match_operand:SI 2 "arith_reg_dest" "")
+ (if_then_else:SI (match_operator 4 "equality_comparison_operator"
+ [(match_operand:SI 3 "arith_reg_operand" "")
+ (const_int 0)])
+ (match_dup 0)
+ (match_dup 2)))]
+ "TARGET_SHMEDIA && peep2_reg_dead_p (2, operands[0])
+ && (!REG_P (operands[1]) || GENERAL_REGISTER_P (REGNO (operands[1])))"
+ [(set (match_dup 2)
+ (if_then_else:SI (match_dup 4) (match_dup 1) (match_dup 2)))]
+ "
+{
+ replace_rtx (operands[4], operands[0], operands[1]);
+}")
+
+(define_peephole2
+ [(set (match_operand 0 "any_register_operand" "")
+ (match_operand 1 "any_register_operand" ""))
+ (set (match_operand 2 "any_register_operand" "") (match_operand 3 "" ""))
+ (set (match_operand 4 "" "") (match_operand 5 "" ""))]
+ "(HARD_REGNO_NREGS (REGNO (operands[0]), GET_MODE (operands[2]))
+ <= HARD_REGNO_NREGS (REGNO (operands[0]), GET_MODE (operands[0])))
+ && peep2_reg_dead_p (3, operands[0]) && peep2_reg_dead_p (3, operands[2])
+ && ! FIND_REG_INC_NOTE (peep2_next_insn (2), operands[0])
+ && ! FIND_REG_INC_NOTE (peep2_next_insn (2), operands[2])
+ && ! reg_overlap_mentioned_p (operands[0], operands[3])
+ && ! reg_overlap_mentioned_p (operands[2], operands[0])
+ && ! reg_overlap_mentioned_p (operands[0], operands[1])
+ && (REGNO_REG_CLASS (REGNO (operands[0]))
+ == REGNO_REG_CLASS (REGNO (operands[2])))
+ && (REGNO_REG_CLASS (REGNO (operands[1]))
+ == REGNO_REG_CLASS (REGNO (operands[0])))"
+ [(set (match_dup 0) (match_dup 3))
+ (set (match_dup 4) (match_dup 5))]
+ "
+{
+ rtx set1, set2, insn2;
+ rtx replacements[4];
+
+ /* We want to replace occurrences of operands[0] with operands[1] and
+ operands[2] with operands[0] in operands[4]/operands[5].
+ Doing just two replace_rtx calls naively would result in the second
+ replacement undoing all that the first did if operands[1] and operands[2]
+ are identical, so we must do this simultaneously. */
+ replacements[0] = operands[0];
+ replacements[1] = operands[1];
+ replacements[2] = operands[2];
+ replacements[3] = operands[0];
+ if (!replace_n_hard_rtx (operands[5], replacements, 2, 0)
+ || !replace_n_hard_rtx (operands[4], replacements, 2, 0)
+ || !replace_n_hard_rtx (operands[2], replacements, 2, 0))
+ FAIL;
+
+ operands[5] = replace_n_hard_rtx (operands[5], replacements, 2, 1);
+ replace_n_hard_rtx (operands[4], replacements, 2, 1);
+ operands[2] = replace_n_hard_rtx (operands[2], replacements, 2, 1);
+ /* The operands array is aliased to recog_data.operand, which gets
+ clobbered by extract_insn, so finish with it now. */
+ set1 = gen_rtx_SET (VOIDmode, operands[2], operands[3]);
+ set2 = gen_rtx_SET (VOIDmode, operands[4], operands[5]);
+ /* ??? The last insn might be a jump insn, but the generic peephole2 code
+ always uses emit_insn. */
+ /* Check that we don't violate matching constraints or earlyclobbers. */
+ extract_insn (emit_insn (set1));
+ if (! constrain_operands (1))
+ goto failure;
+ insn2 = emit (set2);
+ if (GET_CODE (insn2) == BARRIER)
+ goto failure;
+ extract_insn (insn2);
+ if (! constrain_operands (1))
+ {
+ rtx tmp;
+ failure:
+ tmp = replacements[0];
+ replacements[0] = replacements[1];
+ replacements[1] = tmp;
+ tmp = replacements[2];
+ replacements[2] = replacements[3];
+ replacements[3] = tmp;
+ replace_n_hard_rtx (SET_DEST (set1), replacements, 2, 1);
+ replace_n_hard_rtx (SET_DEST (set2), replacements, 2, 1);
+ replace_n_hard_rtx (SET_SRC (set2), replacements, 2, 1);
+ FAIL;
+ }
+ DONE;
+}")
+
+;; The register allocator is rather clumsy in handling multi-way conditional
+;; moves, so allow the combiner to make them, and we split them up after
+;; reload. */
+(define_insn_and_split "*movsicc_umin"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=&r")
+ (umin:SI (if_then_else:SI
+ (eq (match_operand:SI 1 "arith_reg_operand" "r")
+ (const_int 0))
+ (match_operand:SI 2 "arith_reg_or_0_operand" "rN")
+ (match_operand:SI 3 "register_operand" "0"))
+ (match_operand:SI 4 "arith_reg_or_0_operand" "r")))
+ (clobber (match_scratch:SI 5 "=&r"))]
+ "TARGET_SHMEDIA && !can_create_pseudo_p ()"
+ "#"
+ "TARGET_SHMEDIA && reload_completed"
+ [(pc)]
+ "
+{
+ emit_insn (gen_movsicc_false (operands[0], operands[1], operands[2],
+ operands[3]));
+ emit_insn (gen_cmpgtusi_media (operands[5], operands[4], operands[0]));
+ emit_insn (gen_movsicc_false (operands[0], operands[5], operands[4],
+ operands[0]));
+ DONE;
+}")
+
+(define_insn "*movsicc_t_false"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r,r")
+ (if_then_else (eq (reg:SI T_REG) (const_int 0))
+ (match_operand:SI 1 "general_movsrc_operand" "r,I08")
+ (match_operand:SI 2 "arith_reg_operand" "0,0")))]
+ "TARGET_PRETEND_CMOVE
+ && (arith_reg_operand (operands[1], SImode)
+ || (immediate_operand (operands[1], SImode)
+ && satisfies_constraint_I08 (operands[1])))"
+ "bt 0f\;mov %1,%0\\n0:"
+ [(set_attr "type" "mt_group,arith") ;; poor approximation
+ (set_attr "length" "4")])
+
+(define_insn "*movsicc_t_true"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r,r")
+ (if_then_else (ne (reg:SI T_REG) (const_int 0))
+ (match_operand:SI 1 "general_movsrc_operand" "r,I08")
+ (match_operand:SI 2 "arith_reg_operand" "0,0")))]
+ "TARGET_PRETEND_CMOVE
+ && (arith_reg_operand (operands[1], SImode)
+ || (immediate_operand (operands[1], SImode)
+ && satisfies_constraint_I08 (operands[1])))"
+ "bf 0f\;mov %1,%0\\n0:"
+ [(set_attr "type" "mt_group,arith") ;; poor approximation
+ (set_attr "length" "4")])
+
+(define_expand "movsicc"
+ [(set (match_operand:SI 0 "arith_reg_dest" "")
+ (if_then_else:SI (match_operand 1 "comparison_operator" "")
+ (match_operand:SI 2 "arith_reg_or_0_operand" "")
+ (match_operand:SI 3 "arith_reg_operand" "")))]
+ "TARGET_SHMEDIA || TARGET_PRETEND_CMOVE"
+ "
+{
+ if ((GET_CODE (operands[1]) == EQ || GET_CODE (operands[1]) == NE)
+ && GET_MODE (XEXP (operands[1], 0)) == SImode
+ && (TARGET_SHMEDIA
+ || (REG_P (XEXP (operands[1], 0))
+ && REGNO (XEXP (operands[1], 0)) == T_REG))
+ && XEXP (operands[1], 1) == const0_rtx)
+ ;
+
+ else if (TARGET_PRETEND_CMOVE)
+ {
+ enum rtx_code code = GET_CODE (operands[1]);
+ enum rtx_code new_code = code;
+ rtx op0 = XEXP (operands[1], 0);
+ rtx op1 = XEXP (operands[1], 1);
+
+ if (! currently_expanding_to_rtl)
+ FAIL;
+ switch (code)
+ {
+ case LT: case LE: case LEU: case LTU:
+ if (GET_MODE_CLASS (GET_MODE (op0)) != MODE_INT)
+ break;
+ case NE:
+ new_code = reverse_condition (code);
+ break;
+ case EQ: case GT: case GE: case GEU: case GTU:
+ break;
+ default:
+ FAIL;
+ }
+ sh_emit_scc_to_t (new_code, op0, op1);
+ operands[1] = gen_rtx_fmt_ee (new_code == code ? NE : EQ, VOIDmode,
+ gen_rtx_REG (SImode, T_REG), const0_rtx);
+ }
+ else
+ {
+ if (!can_create_pseudo_p ())
+ FAIL;
+
+ operands[1] = sh_emit_cheap_store_flag (GET_MODE (operands[0]),
+ GET_CODE (operands[1]),
+ XEXP (operands[1], 0),
+ XEXP (operands[1], 1));
+ if (!operands[1])
+ FAIL;
+ }
+}")
+
+(define_expand "movqicc"
+ [(set (match_operand:QI 0 "register_operand" "")
+ (if_then_else:QI (match_operand 1 "comparison_operator" "")
+ (match_operand:QI 2 "register_operand" "")
+ (match_operand:QI 3 "register_operand" "")))]
+ "TARGET_SHMEDIA"
+ "
+{
+ operands[0] = simplify_gen_subreg (SImode, operands[0], QImode, 0);
+ operands[2] = simplify_gen_subreg (SImode, operands[2], QImode, 0);
+ operands[3] = simplify_gen_subreg (SImode, operands[3], QImode, 0);
+ emit (gen_movsicc (operands[0], operands[1], operands[2], operands[3]));
+ DONE;
+}")
+
+;; -------------------------------------------------------------------------
+;; Addition instructions
+;; -------------------------------------------------------------------------
+
+(define_expand "adddi3"
+ [(set (match_operand:DI 0 "arith_reg_operand" "")
+ (plus:DI (match_operand:DI 1 "arith_reg_operand" "")
+ (match_operand:DI 2 "arith_operand" "")))]
+ ""
+ "
+{
+ if (TARGET_SH1)
+ {
+ if (!can_create_pseudo_p () && ! arith_reg_operand (operands[2], DImode))
+ FAIL;
+ operands[2] = force_reg (DImode, operands[2]);
+ emit_insn (gen_adddi3_compact (operands[0], operands[1], operands[2]));
+ DONE;
+ }
+}")
+
+(define_insn "*adddi3_media"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r,r")
+ (plus:DI (match_operand:DI 1 "arith_reg_operand" "%r,r")
+ (match_operand:DI 2 "arith_operand" "r,I10")))]
+ "TARGET_SHMEDIA"
+ "@
+ add %1, %2, %0
+ addi %1, %2, %0"
+ [(set_attr "type" "arith_media")])
+
+(define_insn "*adddisi3_media"
+ [(set (subreg:DI (match_operand:SI 0 "arith_reg_operand" "=r,r") 0)
+ (plus:DI (match_operand:DI 1 "arith_reg_operand" "%r,r")
+ (match_operand:DI 2 "arith_operand" "r,I10")))]
+ "TARGET_SHMEDIA"
+ "@
+ add.l %1, %2, %0
+ addi.l %1, %2, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "ignore")])
+
+(define_insn "adddi3z_media"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (zero_extend:DI
+ (plus:SI (match_operand:SI 1 "extend_reg_operand" "r")
+ (match_operand:SI 2 "extend_reg_or_0_operand" "rN"))))]
+ "TARGET_SHMEDIA"
+ "addz.l %1, %N2, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "ignore")])
+
+(define_insn "adddi3_compact"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=&r")
+ (plus:DI (match_operand:DI 1 "arith_reg_operand" "%0")
+ (match_operand:DI 2 "arith_reg_operand" "r")))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1"
+ "#"
+ [(set_attr "length" "6")])
+
+(define_split
+ [(set (match_operand:DI 0 "arith_reg_dest" "")
+ (plus:DI (match_operand:DI 1 "arith_reg_operand" "")
+ (match_operand:DI 2 "arith_reg_operand" "")))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1 && reload_completed"
+ [(const_int 0)]
+ "
+{
+ rtx high0, high2, low0 = gen_lowpart (SImode, operands[0]);
+ high0 = gen_rtx_REG (SImode,
+ true_regnum (operands[0])
+ + (TARGET_LITTLE_ENDIAN ? 1 : 0));
+ high2 = gen_rtx_REG (SImode,
+ true_regnum (operands[2])
+ + (TARGET_LITTLE_ENDIAN ? 1 : 0));
+ emit_insn (gen_clrt ());
+ emit_insn (gen_addc (low0, low0, gen_lowpart (SImode, operands[2])));
+ emit_insn (gen_addc1 (high0, high0, high2));
+ DONE;
+}")
+
+(define_insn "addc"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (plus:SI (plus:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (match_operand:SI 2 "arith_reg_operand" "r"))
+ (reg:SI T_REG)))
+ (set (reg:SI T_REG)
+ (ltu:SI (plus:SI (match_dup 1) (match_dup 2)) (match_dup 1)))]
+ "TARGET_SH1"
+ "addc %2,%0"
+ [(set_attr "type" "arith")])
+
+(define_insn "addc1"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (plus:SI (plus:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (match_operand:SI 2 "arith_reg_operand" "r"))
+ (reg:SI T_REG)))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1"
+ "addc %2,%0"
+ [(set_attr "type" "arith")])
+
+(define_expand "addsi3"
+ [(set (match_operand:SI 0 "arith_reg_operand" "")
+ (plus:SI (match_operand:SI 1 "arith_operand" "")
+ (match_operand:SI 2 "arith_operand" "")))]
+ ""
+ "
+{
+ if (TARGET_SHMEDIA)
+ operands[1] = force_reg (SImode, operands[1]);
+}")
+
+(define_insn "addsi3_media"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r,r")
+ (plus:SI (match_operand:SI 1 "extend_reg_operand" "%r,r")
+ (match_operand:SI 2 "arith_operand" "r,I10")))]
+ "TARGET_SHMEDIA"
+ "@
+ add.l %1, %2, %0
+ addi.l %1, %2, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "ignore")])
+
+(define_insn "addsidi3_media"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r,r")
+ (sign_extend:DI (plus:SI (match_operand:SI 1 "extend_reg_operand"
+ "%r,r")
+ (match_operand:SI 2 "arith_operand"
+ "r,I10"))))]
+ "TARGET_SHMEDIA"
+ "@
+ add.l %1, %2, %0
+ addi.l %1, %2, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "ignore")])
+
+(define_insn "*addsi3_compact"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (plus:SI (match_operand:SI 1 "arith_operand" "%0")
+ (match_operand:SI 2 "arith_operand" "rI08")))]
+ "TARGET_SH1"
+ "add %2,%0"
+ [(set_attr "type" "arith")])
+
+;; -------------------------------------------------------------------------
+;; Subtraction instructions
+;; -------------------------------------------------------------------------
+
+(define_expand "subdi3"
+ [(set (match_operand:DI 0 "arith_reg_operand" "")
+ (minus:DI (match_operand:DI 1 "arith_reg_or_0_operand" "")
+ (match_operand:DI 2 "arith_reg_operand" "")))]
+ ""
+ "
+{
+ if (TARGET_SH1)
+ {
+ operands[1] = force_reg (DImode, operands[1]);
+ emit_insn (gen_subdi3_compact (operands[0], operands[1], operands[2]));
+ DONE;
+ }
+}")
+
+(define_insn "*subdi3_media"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (minus:DI (match_operand:DI 1 "arith_reg_or_0_operand" "rN")
+ (match_operand:DI 2 "arith_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "sub %N1, %2, %0"
+ [(set_attr "type" "arith_media")])
+
+(define_insn "subdisi3_media"
+ [(set (subreg:DI (match_operand:SI 0 "arith_reg_operand" "=r") 0)
+ (minus:DI (match_operand:DI 1 "arith_reg_or_0_operand" "rN")
+ (match_operand:DI 2 "arith_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "sub.l %N1, %2, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "ignore")])
+
+(define_insn "subdi3_compact"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=&r")
+ (minus:DI (match_operand:DI 1 "arith_reg_operand" "0")
+ (match_operand:DI 2 "arith_reg_operand" "r")))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1"
+ "#"
+ [(set_attr "length" "6")])
+
+(define_split
+ [(set (match_operand:DI 0 "arith_reg_dest" "")
+ (minus:DI (match_operand:DI 1 "arith_reg_operand" "")
+ (match_operand:DI 2 "arith_reg_operand" "")))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1 && reload_completed"
+ [(const_int 0)]
+ "
+{
+ rtx high0, high2, low0 = gen_lowpart (SImode, operands[0]);
+ high0 = gen_rtx_REG (SImode,
+ true_regnum (operands[0])
+ + (TARGET_LITTLE_ENDIAN ? 1 : 0));
+ high2 = gen_rtx_REG (SImode,
+ true_regnum (operands[2])
+ + (TARGET_LITTLE_ENDIAN ? 1 : 0));
+ emit_insn (gen_clrt ());
+ emit_insn (gen_subc (low0, low0, gen_lowpart (SImode, operands[2])));
+ emit_insn (gen_subc1 (high0, high0, high2));
+ DONE;
+}")
+
+(define_insn "subc"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (minus:SI (minus:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (match_operand:SI 2 "arith_reg_operand" "r"))
+ (reg:SI T_REG)))
+ (set (reg:SI T_REG)
+ (gtu:SI (minus:SI (minus:SI (match_dup 1) (match_dup 2))
+ (reg:SI T_REG))
+ (match_dup 1)))]
+ "TARGET_SH1"
+ "subc %2,%0"
+ [(set_attr "type" "arith")])
+
+(define_insn "subc1"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (minus:SI (minus:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (match_operand:SI 2 "arith_reg_operand" "r"))
+ (reg:SI T_REG)))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1"
+ "subc %2,%0"
+ [(set_attr "type" "arith")])
+
+;; life_analysis thinks rn is live before subc rn,rn, so make a special
+;; pattern for this case. This helps multimedia applications that compute
+;; the sum of absolute differences.
+(define_insn "mov_neg_si_t"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r") (neg:SI (reg:SI T_REG)))]
+ "TARGET_SH1"
+ "subc %0,%0"
+ [(set_attr "type" "arith")])
+
+(define_insn "*subsi3_internal"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (minus:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (match_operand:SI 2 "arith_reg_operand" "r")))]
+ "TARGET_SH1"
+ "sub %2,%0"
+ [(set_attr "type" "arith")])
+
+(define_insn_and_split "*subsi3_media"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (minus:SI (match_operand:SI 1 "minuend_operand" "rN")
+ (match_operand:SI 2 "extend_reg_operand" "r")))]
+ "TARGET_SHMEDIA
+ && (operands[1] != constm1_rtx
+ || (GET_CODE (operands[2]) != TRUNCATE
+ && GET_CODE (operands[2]) != SUBREG))"
+ "sub.l %N1, %2, %0"
+ "operands[1] == constm1_rtx"
+ [(set (match_dup 0) (xor:SI (match_dup 2) (match_dup 1)))]
+ ""
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "ignore")])
+
+(define_split
+ [(set (match_operand:SI 0 "arith_reg_dest" "")
+ (zero_extend:SI (subreg:QI (not:SI (subreg:SI (match_operand:QI 1
+ "general_extend_operand"
+ "") 0)) 0)))]
+ "TARGET_SHMEDIA && TARGET_LITTLE_ENDIAN"
+ [(set (match_dup 0) (zero_extend:SI (match_dup 1)))
+ (set (match_dup 0) (xor:SI (match_dup 0) (const_int 255)))]
+ "")
+
+(define_split
+ [(set (match_operand:SI 0 "arith_reg_dest" "")
+ (zero_extend:SI (subreg:QI (not:SI (subreg:SI (match_operand:QI 1
+ "general_extend_operand"
+ "") 0)) 3)))]
+ "TARGET_SHMEDIA && ! TARGET_LITTLE_ENDIAN"
+ [(set (match_dup 0) (zero_extend:SI (match_dup 1)))
+ (set (match_dup 0) (xor:SI (match_dup 0) (const_int 255)))]
+ "")
+;; Convert `constant - reg' to `neg rX; add rX, #const' since this
+;; will sometimes save one instruction. Otherwise we might get
+;; `mov #const, rY; sub rY,rX; mov rX, rY' if the source and dest regs
+;; are the same.
+
+(define_expand "subsi3"
+ [(set (match_operand:SI 0 "arith_reg_operand" "")
+ (minus:SI (match_operand:SI 1 "arith_operand" "")
+ (match_operand:SI 2 "arith_reg_operand" "")))]
+ ""
+ "
+{
+ if (TARGET_SH1 && CONST_INT_P (operands[1]))
+ {
+ emit_insn (gen_negsi2 (operands[0], operands[2]));
+ emit_insn (gen_addsi3 (operands[0], operands[0], operands[1]));
+ DONE;
+ }
+ if (TARGET_SHMEDIA)
+ {
+ if (!can_create_pseudo_p ()
+ && ! arith_reg_or_0_operand (operands[1], SImode))
+ FAIL;
+ if (operands[1] != const0_rtx && GET_CODE (operands[1]) != SUBREG)
+ operands[1] = force_reg (SImode, operands[1]);
+ }
+}")
+
+;; -------------------------------------------------------------------------
+;; Division instructions
+;; -------------------------------------------------------------------------
+
+;; We take advantage of the library routines which don't clobber as many
+;; registers as a normal function call would.
+
+;; The INSN_REFERENCES_ARE_DELAYED in sh.h is problematic because it
+;; also has an effect on the register that holds the address of the sfunc.
+;; To make this work, we have an extra dummy insn that shows the use
+;; of this register for reorg.
+
+(define_insn "use_sfunc_addr"
+ [(set (reg:SI PR_REG)
+ (unspec:SI [(match_operand:SI 0 "register_operand" "r")] UNSPEC_SFUNC))]
+ "TARGET_SH1 && check_use_sfunc_addr (insn, operands[0])"
+ ""
+ [(set_attr "length" "0")])
+
+(define_insn "udivsi3_sh2a"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (udiv:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (match_operand:SI 2 "arith_reg_operand" "z")))]
+ "TARGET_SH2A"
+ "divu %2,%1"
+ [(set_attr "type" "arith")
+ (set_attr "in_delay_slot" "no")])
+
+;; We must use a pseudo-reg forced to reg 0 in the SET_DEST rather than
+;; hard register 0. If we used hard register 0, then the next instruction
+;; would be a move from hard register 0 to a pseudo-reg. If the pseudo-reg
+;; gets allocated to a stack slot that needs its address reloaded, then
+;; there is nothing to prevent reload from using r0 to reload the address.
+;; This reload would clobber the value in r0 we are trying to store.
+;; If we let reload allocate r0, then this problem can never happen.
+
+(define_insn "udivsi3_i1"
+ [(set (match_operand:SI 0 "register_operand" "=z")
+ (udiv:SI (reg:SI R4_REG) (reg:SI R5_REG)))
+ (clobber (reg:SI T_REG))
+ (clobber (reg:SI PR_REG))
+ (clobber (reg:SI R4_REG))
+ (use (match_operand:SI 1 "arith_reg_operand" "r"))]
+ "TARGET_SH1 && ! TARGET_SH4"
+ "jsr @%1%#"
+ [(set_attr "type" "sfunc")
+ (set_attr "needs_delay_slot" "yes")])
+
+; Since shmedia-nofpu code could be linked against shcompact code, and
+; the udivsi3 libcall has the same name, we must consider all registers
+; clobbered that are in the union of the registers clobbered by the
+; shmedia and the shcompact implementation. Note, if the shcompact
+; implementation actually used shcompact code, we'd need to clobber
+; also r23 and fr23.
+(define_insn "udivsi3_i1_media"
+ [(set (match_operand:SI 0 "register_operand" "=z")
+ (udiv:SI (reg:SI R4_REG) (reg:SI R5_REG)))
+ (clobber (reg:SI T_MEDIA_REG))
+ (clobber (reg:SI PR_MEDIA_REG))
+ (clobber (reg:SI R20_REG))
+ (clobber (reg:SI R21_REG))
+ (clobber (reg:SI R22_REG))
+ (clobber (reg:DI TR0_REG))
+ (clobber (reg:DI TR1_REG))
+ (clobber (reg:DI TR2_REG))
+ (use (match_operand 1 "target_reg_operand" "b"))]
+ "TARGET_SHMEDIA && (! TARGET_SHMEDIA_FPU || ! TARGET_DIVIDE_FP)"
+ "blink %1, r18"
+ [(set_attr "type" "sfunc")
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_expand "udivsi3_i4_media"
+ [(set (match_dup 3)
+ (zero_extend:DI (match_operand:SI 1 "register_operand" "")))
+ (set (match_dup 4)
+ (zero_extend:DI (match_operand:SI 2 "register_operand" "")))
+ (set (match_dup 5) (float:DF (match_dup 3)))
+ (set (match_dup 6) (float:DF (match_dup 4)))
+ (set (match_dup 7) (div:DF (match_dup 5) (match_dup 6)))
+ (set (match_dup 8) (fix:DI (match_dup 7)))
+ (set (match_operand:SI 0 "register_operand" "")
+ (truncate:SI (match_dup 8)))]
+ "TARGET_SHMEDIA_FPU"
+ "
+{
+ operands[3] = gen_reg_rtx (DImode);
+ operands[4] = gen_reg_rtx (DImode);
+ operands[5] = gen_reg_rtx (DFmode);
+ operands[6] = gen_reg_rtx (DFmode);
+ operands[7] = gen_reg_rtx (DFmode);
+ operands[8] = gen_reg_rtx (DImode);
+}")
+
+(define_insn "udivsi3_i4"
+ [(set (match_operand:SI 0 "register_operand" "=y")
+ (udiv:SI (reg:SI R4_REG) (reg:SI R5_REG)))
+ (clobber (reg:SI T_REG))
+ (clobber (reg:SI PR_REG))
+ (clobber (reg:DF DR0_REG))
+ (clobber (reg:DF DR2_REG))
+ (clobber (reg:DF DR4_REG))
+ (clobber (reg:SI R0_REG))
+ (clobber (reg:SI R1_REG))
+ (clobber (reg:SI R4_REG))
+ (clobber (reg:SI R5_REG))
+ (use (reg:PSI FPSCR_REG))
+ (use (match_operand:SI 1 "arith_reg_operand" "r"))]
+ "TARGET_SH4 && ! TARGET_FPU_SINGLE"
+ "jsr @%1%#"
+ [(set_attr "type" "sfunc")
+ (set_attr "fp_mode" "double")
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_insn "udivsi3_i4_single"
+ [(set (match_operand:SI 0 "register_operand" "=y")
+ (udiv:SI (reg:SI R4_REG) (reg:SI R5_REG)))
+ (clobber (reg:SI T_REG))
+ (clobber (reg:SI PR_REG))
+ (clobber (reg:DF DR0_REG))
+ (clobber (reg:DF DR2_REG))
+ (clobber (reg:DF DR4_REG))
+ (clobber (reg:SI R0_REG))
+ (clobber (reg:SI R1_REG))
+ (clobber (reg:SI R4_REG))
+ (clobber (reg:SI R5_REG))
+ (use (match_operand:SI 1 "arith_reg_operand" "r"))]
+ "(TARGET_HARD_SH4 || TARGET_SHCOMPACT) && TARGET_FPU_SINGLE"
+ "jsr @%1%#"
+ [(set_attr "type" "sfunc")
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_insn "udivsi3_i4_int"
+ [(set (match_operand:SI 0 "register_operand" "=z")
+ (udiv:SI (reg:SI R4_REG) (reg:SI R5_REG)))
+ (clobber (reg:SI T_REG))
+ (clobber (reg:SI R1_REG))
+ (clobber (reg:SI PR_REG))
+ (clobber (reg:SI MACH_REG))
+ (clobber (reg:SI MACL_REG))
+ (use (match_operand:SI 1 "arith_reg_operand" "r"))]
+ "TARGET_SH1"
+ "jsr @%1%#"
+ [(set_attr "type" "sfunc")
+ (set_attr "needs_delay_slot" "yes")])
+
+
+(define_expand "udivsi3"
+ [(set (match_dup 3) (symbol_ref:SI "__udivsi3"))
+ (set (reg:SI R4_REG) (match_operand:SI 1 "general_operand" ""))
+ (set (reg:SI R5_REG) (match_operand:SI 2 "general_operand" ""))
+ (parallel [(set (match_operand:SI 0 "register_operand" "")
+ (udiv:SI (reg:SI R4_REG)
+ (reg:SI R5_REG)))
+ (clobber (reg:SI T_REG))
+ (clobber (reg:SI PR_REG))
+ (clobber (reg:SI R4_REG))
+ (use (match_dup 3))])]
+ ""
+ "
+{
+ rtx last;
+
+ operands[3] = gen_reg_rtx (Pmode);
+ /* Emit the move of the address to a pseudo outside of the libcall. */
+ if (TARGET_DIVIDE_CALL_TABLE)
+ {
+ /* libgcc2:__udivmoddi4 is not supposed to use an actual division, since
+ that causes problems when the divide code is supposed to come from a
+ separate library. Division by zero is undefined, so dividing 1 can be
+ implemented by comparing with the divisor. */
+ if (operands[1] == const1_rtx && currently_expanding_to_rtl)
+ {
+ rtx test = gen_rtx_GEU (VOIDmode, operands[1], operands[2]);
+ emit_insn (gen_cstoresi4 (operands[0], test,
+ operands[1], operands[2]));
+ DONE;
+ }
+ else if (operands[2] == const0_rtx)
+ {
+ emit_move_insn (operands[0], operands[2]);
+ DONE;
+ }
+ function_symbol (operands[3], \"__udivsi3_i4i\", SFUNC_GOT);
+ last = gen_udivsi3_i4_int (operands[0], operands[3]);
+ }
+ else if (TARGET_DIVIDE_CALL_FP)
+ {
+ function_symbol (operands[3], \"__udivsi3_i4\", SFUNC_STATIC);
+ if (TARGET_FPU_SINGLE)
+ last = gen_udivsi3_i4_single (operands[0], operands[3]);
+ else
+ last = gen_udivsi3_i4 (operands[0], operands[3]);
+ }
+ else if (TARGET_SHMEDIA_FPU)
+ {
+ operands[1] = force_reg (SImode, operands[1]);
+ operands[2] = force_reg (SImode, operands[2]);
+ emit_insn (gen_udivsi3_i4_media (operands[0], operands[1], operands[2]));
+ DONE;
+ }
+ else if (TARGET_SH2A)
+ {
+ operands[1] = force_reg (SImode, operands[1]);
+ operands[2] = force_reg (SImode, operands[2]);
+ emit_insn (gen_udivsi3_sh2a (operands[0], operands[1], operands[2]));
+ DONE;
+ }
+ else if (TARGET_SH5)
+ {
+ function_symbol (operands[3],
+ TARGET_FPU_ANY ? \"__udivsi3_i4\" : \"__udivsi3\",
+ SFUNC_STATIC);
+
+ if (TARGET_SHMEDIA)
+ last = gen_udivsi3_i1_media (operands[0], operands[3]);
+ else if (TARGET_FPU_ANY)
+ last = gen_udivsi3_i4_single (operands[0], operands[3]);
+ else
+ last = gen_udivsi3_i1 (operands[0], operands[3]);
+ }
+ else
+ {
+ function_symbol (operands[3], \"__udivsi3\", SFUNC_STATIC);
+ last = gen_udivsi3_i1 (operands[0], operands[3]);
+ }
+ emit_move_insn (gen_rtx_REG (SImode, 4), operands[1]);
+ emit_move_insn (gen_rtx_REG (SImode, 5), operands[2]);
+ emit_insn (last);
+ DONE;
+}")
+
+(define_insn "divsi3_sh2a"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (div:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (match_operand:SI 2 "arith_reg_operand" "z")))]
+ "TARGET_SH2A"
+ "divs %2,%1"
+ [(set_attr "type" "arith")
+ (set_attr "in_delay_slot" "no")])
+
+(define_insn "divsi3_i1"
+ [(set (match_operand:SI 0 "register_operand" "=z")
+ (div:SI (reg:SI R4_REG) (reg:SI R5_REG)))
+ (clobber (reg:SI T_REG))
+ (clobber (reg:SI PR_REG))
+ (clobber (reg:SI R1_REG))
+ (clobber (reg:SI R2_REG))
+ (clobber (reg:SI R3_REG))
+ (use (match_operand:SI 1 "arith_reg_operand" "r"))]
+ "TARGET_SH1 && ! TARGET_SH4"
+ "jsr @%1%#"
+ [(set_attr "type" "sfunc")
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_insn "divsi3_i1_media"
+ [(set (match_operand:SI 0 "register_operand" "=z")
+ (div:SI (reg:SI R4_REG) (reg:SI R5_REG)))
+ (clobber (reg:SI T_MEDIA_REG))
+ (clobber (reg:SI PR_MEDIA_REG))
+ (clobber (reg:SI R1_REG))
+ (clobber (reg:SI R20_REG))
+ (clobber (reg:SI R21_REG))
+ (clobber (reg:SI TR0_REG))
+ (use (match_operand 1 "target_reg_operand" "b"))]
+ "TARGET_SHMEDIA && (! TARGET_SHMEDIA_FPU || ! TARGET_DIVIDE_FP)"
+ "blink %1, r18"
+ [(set_attr "type" "sfunc")])
+
+(define_insn "divsi3_media_2"
+ [(set (match_operand:SI 0 "register_operand" "=z")
+ (div:SI (reg:SI R4_REG) (reg:SI R5_REG)))
+ (clobber (reg:SI T_MEDIA_REG))
+ (clobber (reg:SI PR_MEDIA_REG))
+ (clobber (reg:SI R1_REG))
+ (clobber (reg:SI R21_REG))
+ (clobber (reg:SI TR0_REG))
+ (use (reg:SI R20_REG))
+ (use (match_operand 1 "target_reg_operand" "b"))]
+ "TARGET_SHMEDIA && (! TARGET_SHMEDIA_FPU || ! TARGET_DIVIDE_FP)"
+ "blink %1, r18"
+ [(set_attr "type" "sfunc")])
+
+;; This pattern acts as a placeholder for -mdiv=inv:call to carry
+;; hard reg clobbers and data dependencies that we need when we want
+;; to rematerialize the division into a call.
+(define_insn_and_split "divsi_inv_call"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (div:SI (match_operand:SI 1 "register_operand" "r")
+ (match_operand:SI 2 "register_operand" "r")))
+ (clobber (reg:SI R4_REG))
+ (clobber (reg:SI R5_REG))
+ (clobber (reg:SI T_MEDIA_REG))
+ (clobber (reg:SI PR_MEDIA_REG))
+ (clobber (reg:SI R1_REG))
+ (clobber (reg:SI R21_REG))
+ (clobber (reg:SI TR0_REG))
+ (clobber (reg:SI R20_REG))
+ (use (match_operand:SI 3 "register_operand" "r"))]
+ "TARGET_SHMEDIA"
+ "#"
+ "&& (high_life_started || reload_completed)"
+ [(set (match_dup 0) (match_dup 3))]
+ ""
+ [(set_attr "highpart" "must_split")])
+
+;; This is the combiner pattern for -mdiv=inv:call .
+(define_insn_and_split "*divsi_inv_call_combine"
+ [(set (match_operand:SI 0 "register_operand" "=z")
+ (div:SI (match_operand:SI 1 "register_operand" "r")
+ (match_operand:SI 2 "register_operand" "r")))
+ (clobber (reg:SI R4_REG))
+ (clobber (reg:SI R5_REG))
+ (clobber (reg:SI T_MEDIA_REG))
+ (clobber (reg:SI PR_MEDIA_REG))
+ (clobber (reg:SI R1_REG))
+ (clobber (reg:SI R21_REG))
+ (clobber (reg:SI TR0_REG))
+ (clobber (reg:SI R20_REG))
+ (use (unspec:SI [(match_dup 1)
+ (match_operand:SI 3 "" "")
+ (unspec:SI [(match_operand:SI 4 "" "")
+ (match_dup 3)
+ (match_operand:DI 5 "" "")]
+ UNSPEC_DIV_INV_M2)
+ (match_operand:DI 6 "" "")
+ (const_int 0)
+ (const_int 0)]
+ UNSPEC_DIV_INV_M3))]
+ "TARGET_SHMEDIA"
+ "#"
+ "&& (high_life_started || reload_completed)"
+ [(pc)]
+ "
+{
+ const char *name = sh_divsi3_libfunc;
+ enum sh_function_kind kind = SFUNC_GOT;
+ rtx sym;
+
+ emit_move_insn (gen_rtx_REG (SImode, R4_REG), operands[1]);
+ emit_move_insn (gen_rtx_REG (SImode, R5_REG), operands[2]);
+ while (TARGET_DIVIDE_INV_CALL2)
+ {
+ rtx x = operands[3];
+
+ if (GET_CODE (x) != UNSPEC || XINT (x, 1) != UNSPEC_DIV_INV_M1)
+ break;
+ x = XVECEXP (x, 0, 0);
+ name = \"__sdivsi3_2\";
+ kind = SFUNC_STATIC;
+ emit_move_insn (gen_rtx_REG (DImode, R20_REG), x);
+ break;
+ }
+ sym = function_symbol (NULL, name, kind);
+ emit_insn (gen_divsi3_media_2 (operands[0], sym));
+ DONE;
+}"
+ [(set_attr "highpart" "must_split")])
+
+(define_expand "divsi3_i4_media"
+ [(set (match_dup 3) (float:DF (match_operand:SI 1 "register_operand" "r")))
+ (set (match_dup 4) (float:DF (match_operand:SI 2 "register_operand" "r")))
+ (set (match_dup 5) (div:DF (match_dup 3) (match_dup 4)))
+ (set (match_operand:SI 0 "register_operand" "=r")
+ (fix:SI (match_dup 5)))]
+ "TARGET_SHMEDIA_FPU"
+ "
+{
+ operands[3] = gen_reg_rtx (DFmode);
+ operands[4] = gen_reg_rtx (DFmode);
+ operands[5] = gen_reg_rtx (DFmode);
+}")
+
+(define_insn "divsi3_i4"
+ [(set (match_operand:SI 0 "register_operand" "=y")
+ (div:SI (reg:SI R4_REG) (reg:SI R5_REG)))
+ (clobber (reg:SI PR_REG))
+ (clobber (reg:DF DR0_REG))
+ (clobber (reg:DF DR2_REG))
+ (use (reg:PSI FPSCR_REG))
+ (use (match_operand:SI 1 "arith_reg_operand" "r"))]
+ "TARGET_SH4 && ! TARGET_FPU_SINGLE"
+ "jsr @%1%#"
+ [(set_attr "type" "sfunc")
+ (set_attr "fp_mode" "double")
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_insn "divsi3_i4_single"
+ [(set (match_operand:SI 0 "register_operand" "=y")
+ (div:SI (reg:SI R4_REG) (reg:SI R5_REG)))
+ (clobber (reg:SI PR_REG))
+ (clobber (reg:DF DR0_REG))
+ (clobber (reg:DF DR2_REG))
+ (clobber (reg:SI R2_REG))
+ (use (match_operand:SI 1 "arith_reg_operand" "r"))]
+ "(TARGET_HARD_SH4 || TARGET_SHCOMPACT) && TARGET_FPU_SINGLE"
+ "jsr @%1%#"
+ [(set_attr "type" "sfunc")
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_insn "divsi3_i4_int"
+ [(set (match_operand:SI 0 "register_operand" "=z")
+ (div:SI (reg:SI R4_REG) (reg:SI R5_REG)))
+ (clobber (reg:SI T_REG))
+ (clobber (reg:SI PR_REG))
+ (clobber (reg:SI R1_REG))
+ (clobber (reg:SI MACH_REG))
+ (clobber (reg:SI MACL_REG))
+ (use (match_operand:SI 1 "arith_reg_operand" "r"))]
+ "TARGET_SH1"
+ "jsr @%1%#"
+ [(set_attr "type" "sfunc")
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_expand "divsi3"
+ [(set (match_dup 3) (symbol_ref:SI "__sdivsi3"))
+ (set (reg:SI R4_REG) (match_operand:SI 1 "general_operand" ""))
+ (set (reg:SI R5_REG) (match_operand:SI 2 "general_operand" ""))
+ (parallel [(set (match_operand:SI 0 "register_operand" "")
+ (div:SI (reg:SI R4_REG)
+ (reg:SI R5_REG)))
+ (clobber (reg:SI T_REG))
+ (clobber (reg:SI PR_REG))
+ (clobber (reg:SI R1_REG))
+ (clobber (reg:SI R2_REG))
+ (clobber (reg:SI R3_REG))
+ (use (match_dup 3))])]
+ ""
+ "
+{
+ rtx last;
+
+ operands[3] = gen_reg_rtx (Pmode);
+ /* Emit the move of the address to a pseudo outside of the libcall. */
+ if (TARGET_DIVIDE_CALL_TABLE)
+ {
+ function_symbol (operands[3], sh_divsi3_libfunc, SFUNC_GOT);
+ last = gen_divsi3_i4_int (operands[0], operands[3]);
+ }
+ else if (TARGET_DIVIDE_CALL_FP)
+ {
+ function_symbol (operands[3], sh_divsi3_libfunc, SFUNC_STATIC);
+ if (TARGET_FPU_SINGLE)
+ last = gen_divsi3_i4_single (operands[0], operands[3]);
+ else
+ last = gen_divsi3_i4 (operands[0], operands[3]);
+ }
+ else if (TARGET_SH2A)
+ {
+ operands[1] = force_reg (SImode, operands[1]);
+ operands[2] = force_reg (SImode, operands[2]);
+ emit_insn (gen_divsi3_sh2a (operands[0], operands[1], operands[2]));
+ DONE;
+ }
+ else if (TARGET_DIVIDE_INV)
+ {
+ rtx dividend = operands[1];
+ rtx divisor = operands[2];
+ rtx tab_base;
+ rtx nsb_res = gen_reg_rtx (DImode);
+ rtx norm64 = gen_reg_rtx (DImode);
+ rtx tab_ix = gen_reg_rtx (DImode);
+ rtx norm32 = gen_reg_rtx (SImode);
+ rtx i92 = force_reg (DImode, GEN_INT (92));
+ rtx scratch0a = gen_reg_rtx (DImode);
+ rtx scratch0b = gen_reg_rtx (DImode);
+ rtx inv0 = gen_reg_rtx (SImode);
+ rtx scratch1a = gen_reg_rtx (DImode);
+ rtx scratch1b = gen_reg_rtx (DImode);
+ rtx shift = gen_reg_rtx (DImode);
+ rtx i2p27, i43;
+ rtx inv1 = gen_reg_rtx (SImode);
+ rtx scratch2a = gen_reg_rtx (DImode);
+ rtx scratch2b = gen_reg_rtx (SImode);
+ rtx inv2 = gen_reg_rtx (SImode);
+ rtx scratch3a = gen_reg_rtx (DImode);
+ rtx scratch3b = gen_reg_rtx (DImode);
+ rtx scratch3c = gen_reg_rtx (DImode);
+ rtx scratch3d = gen_reg_rtx (SImode);
+ rtx scratch3e = gen_reg_rtx (DImode);
+ rtx result = gen_reg_rtx (SImode);
+
+ if (! arith_reg_or_0_operand (dividend, SImode))
+ dividend = force_reg (SImode, dividend);
+ if (! arith_reg_operand (divisor, SImode))
+ divisor = force_reg (SImode, divisor);
+ if (flag_pic && Pmode != DImode)
+ {
+ tab_base = gen_rtx_SYMBOL_REF (Pmode, \"__div_table\");
+ tab_base = gen_datalabel_ref (tab_base);
+ tab_base = force_reg (DImode, gen_rtx_SIGN_EXTEND (DImode, tab_base));
+ }
+ else
+ {
+ tab_base = gen_rtx_SYMBOL_REF (DImode, \"__div_table\");
+ tab_base = gen_datalabel_ref (tab_base);
+ tab_base = force_reg (DImode, tab_base);
+ }
+ if (TARGET_DIVIDE_INV20U)
+ i2p27 = force_reg (DImode, GEN_INT (-2 << 27));
+ else
+ i2p27 = GEN_INT (0);
+ if (TARGET_DIVIDE_INV20U || TARGET_DIVIDE_INV20L)
+ i43 = force_reg (DImode, GEN_INT (43));
+ else
+ i43 = GEN_INT (0);
+ emit_insn (gen_nsbdi (nsb_res,
+ simplify_gen_subreg (DImode, divisor, SImode, 0)));
+ emit_insn (gen_ashldi3_media (norm64,
+ gen_rtx_SUBREG (DImode, divisor, 0),
+ nsb_res));
+ emit_insn (gen_ashrdi3_media (tab_ix, norm64, GEN_INT (58)));
+ emit_insn (gen_ashrdisi3_media_high (norm32, norm64, GEN_INT (32)));
+ emit_insn (gen_divsi_inv_m1 (inv1, tab_base, tab_ix, norm32,
+ inv0, scratch0a, scratch0b,
+ scratch1a, scratch1b));
+ emit_insn (gen_subdi3 (shift, i92, nsb_res));
+ emit_insn (gen_divsi_inv_m2 (inv2, norm32, inv1, i92,
+ scratch2a));
+ emit_insn (gen_divsi_inv_m3 (result, dividend, inv1, inv2, shift,
+ i2p27, i43,
+ scratch3a, scratch3b, scratch3c,
+ scratch2a, scratch2b, scratch3d, scratch3e));
+ if (TARGET_DIVIDE_INV_CALL || TARGET_DIVIDE_INV_CALL2)
+ emit_insn (gen_divsi_inv_call (operands[0], dividend, divisor, result));
+ else if (TARGET_DIVIDE_INV_FP)
+ emit_insn (gen_divsi_inv_fp (operands[0], dividend, divisor, result,
+ gen_reg_rtx (SImode), gen_reg_rtx (SImode),
+ gen_reg_rtx (DFmode), gen_reg_rtx (DFmode),
+ gen_reg_rtx (DFmode)));
+ else
+ emit_move_insn (operands[0], result);
+ DONE;
+ }
+ else if (TARGET_SHMEDIA_FPU && TARGET_DIVIDE_FP)
+ {
+ operands[1] = force_reg (SImode, operands[1]);
+ operands[2] = force_reg (SImode, operands[2]);
+ emit_insn (gen_divsi3_i4_media (operands[0], operands[1], operands[2]));
+ DONE;
+ }
+ else if (TARGET_SH5)
+ {
+ if (TARGET_DIVIDE_CALL2)
+ {
+ rtx tab_base = gen_rtx_SYMBOL_REF (Pmode, \"__div_table\");
+ tab_base = gen_datalabel_ref (tab_base);
+ emit_move_insn (gen_rtx_REG (Pmode, R20_REG), tab_base);
+ }
+ if (TARGET_FPU_ANY && TARGET_SH1)
+ function_symbol (operands[3], sh_divsi3_libfunc, SFUNC_STATIC);
+ else if (TARGET_DIVIDE_CALL2)
+ function_symbol (operands[3], \"__sdivsi3_2\", SFUNC_STATIC);
+ else
+ function_symbol (operands[3], sh_divsi3_libfunc, SFUNC_GOT);
+
+ if (TARGET_SHMEDIA)
+ last = ((TARGET_DIVIDE_CALL2 ? gen_divsi3_media_2 : gen_divsi3_i1_media)
+ (operands[0], operands[3]));
+ else if (TARGET_FPU_ANY)
+ last = gen_divsi3_i4_single (operands[0], operands[3]);
+ else
+ last = gen_divsi3_i1 (operands[0], operands[3]);
+ }
+ else
+ {
+ function_symbol (operands[3], sh_divsi3_libfunc, SFUNC_GOT);
+ last = gen_divsi3_i1 (operands[0], operands[3]);
+ }
+ emit_move_insn (gen_rtx_REG (SImode, 4), operands[1]);
+ emit_move_insn (gen_rtx_REG (SImode, 5), operands[2]);
+ emit_insn (last);
+ DONE;
+}")
+
+;; operands: scratch, tab_base, tab_ix
+;; These are unspecs because we could generate an indexed addressing mode
+;; even if -m5-32media, where INDEX_REG_CLASS == NO_REGS, and this would
+;; confuse reload. See PR27117.
+
+(define_insn "divsi_inv_qitable"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (zero_extend:DI (unspec:QI [(match_operand:DI 1 "register_operand" "r")
+ (match_operand:DI 2 "register_operand" "r")]
+ UNSPEC_DIV_INV_TABLE)))]
+ "TARGET_SHMEDIA"
+ "@
+ ldx.ub %1, %2, %0"
+ [(set_attr "type" "load_media")
+ (set_attr "highpart" "user")])
+
+;; operands: scratch, tab_base, tab_ix
+(define_insn "divsi_inv_hitable"
+ [(set (match_operand:DI 0 "register_operand" "=r")
+ (sign_extend:DI (unspec:HI [(match_operand:DI 1 "register_operand" "r")
+ (match_operand:DI 2 "register_operand" "r")]
+ UNSPEC_DIV_INV_TABLE)))]
+ "TARGET_SHMEDIA"
+ "@
+ ldx.w %1, %2, %0"
+ [(set_attr "type" "load_media")
+ (set_attr "highpart" "user")])
+
+;; operands: inv0, tab_base, tab_ix, norm32
+;; scratch equiv in sdivsi3_2: r19, r21
+(define_expand "divsi_inv_m0"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (unspec:SI [(match_operand:DI 1 "register_operand" "r")
+ (match_operand:DI 2 "register_operand" "r")
+ (match_operand:SI 3 "register_operand" "r")]
+ UNSPEC_DIV_INV_M0))
+ (clobber (match_operand:DI 4 "register_operand" "=r"))
+ (clobber (match_operand:DI 5 "register_operand" "=r"))]
+ "TARGET_SHMEDIA"
+ "
+{
+/*
+tab_base: r20
+tab_ix: r21
+norm32: r25
+ ldx.ub r20, r21, r19 // u0.8
+ shlli r21, 1, r21
+ muls.l r25, r19, r19 // s2.38
+ ldx.w r20, r21, r21 // s2.14
+ shari r19, 24, r19 // truncate to s2.14
+ sub r21, r19, r19 // some 11 bit inverse in s1.14
+*/
+
+ rtx inv0 = operands[0];
+ rtx tab_base = operands[1];
+ rtx tab_ix = operands[2];
+ rtx norm32 = operands[3];
+ rtx scratch0 = operands[4];
+ rtx scratch0_si = simplify_gen_subreg (SImode, scratch0, DImode, SIDI_OFF);
+ rtx scratch1 = operands[5];
+
+ emit_insn (gen_divsi_inv_qitable (scratch0, tab_base, tab_ix));
+ emit_insn (gen_ashldi3_media (scratch1, tab_ix, GEN_INT (1)));
+ emit_insn (gen_mulsidi3_media (scratch0, norm32, scratch0_si));
+ emit_insn (gen_divsi_inv_hitable (scratch1, tab_base, scratch1));
+ emit_insn (gen_ashrdi3_media (scratch0, scratch0, GEN_INT (24)));
+ emit_insn (gen_subdisi3_media (inv0, scratch1, scratch0));
+ DONE;
+}")
+
+;; operands: inv1, tab_base, tab_ix, norm32
+(define_insn_and_split "divsi_inv_m1"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (unspec:SI [(match_operand:DI 1 "register_operand" "r")
+ (match_operand:DI 2 "register_operand" "r")
+ (match_operand:SI 3 "register_operand" "r")]
+ UNSPEC_DIV_INV_M1))
+ (clobber (match_operand:SI 4 "register_operand" "=r"))
+ (clobber (match_operand:DI 5 "register_operand" "=r"))
+ (clobber (match_operand:DI 6 "register_operand" "=r"))
+ (clobber (match_operand:DI 7 "register_operand" "=r"))
+ (clobber (match_operand:DI 8 "register_operand" "=r"))]
+ "TARGET_SHMEDIA"
+ "#"
+ "&& !can_create_pseudo_p ()"
+ [(pc)]
+ "
+{
+/* inv0: r19
+ muls.l r19, r19, r18 // u0.28
+ muls.l r25, r18, r18 // s2.58
+ shlli r19, 45, r0 // multiply by two and convert to s2.58
+ sub r0, r18, r18
+ shari r18, 28, r18 // some 18 bit inverse in s1.30
+*/
+
+ rtx inv1 = operands[0];
+ rtx tab_base = operands[1];
+ rtx tab_ix = operands[2];
+ rtx norm32 = operands[3];
+ rtx inv0 = operands[4];
+ rtx inv0_di = simplify_gen_subreg (DImode, inv0, SImode, 0);
+ rtx scratch0a = operands[5];
+ rtx scratch0b = operands[6];
+ rtx scratch0 = operands[7];
+ rtx scratch1 = operands[8];
+ rtx scratch1_si = simplify_gen_subreg (SImode, scratch1, DImode, SIDI_OFF);
+
+ emit_insn (gen_divsi_inv_m0 (inv0, tab_base, tab_ix, norm32,
+ scratch0a, scratch0b));
+ emit_insn (gen_mulsidi3_media (scratch1, inv0, inv0));
+ emit_insn (gen_mulsidi3_media (scratch1, norm32, scratch1_si));
+ emit_insn (gen_ashldi3_media (scratch0, inv0_di, GEN_INT (45)));
+ emit_insn (gen_subdi3 (scratch1, scratch0, scratch1));
+ emit_insn (gen_ashrdisi3_media_opaque (inv1, scratch1, GEN_INT (28)));
+ DONE;
+}")
+
+;; operands: inv2, norm32, inv1, i92
+(define_insn_and_split "divsi_inv_m2"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (unspec:SI [(match_operand:SI 1 "register_operand" "r")
+ (match_operand:SI 2 "register_operand" "r")
+ (match_operand:DI 3 "register_operand" "r")]
+ UNSPEC_DIV_INV_M2))
+ (clobber (match_operand:DI 4 "register_operand" "=r"))]
+ "TARGET_SHMEDIA"
+ "#"
+ "&& !can_create_pseudo_p ()"
+ [(pc)]
+ "
+{
+/*
+ muls.l r18, r25, r0 // s2.60
+ shari r0, 16, r0 // s-16.44
+ sub
+ muls.l r0, r18, r19 // s-16.74
+ shari r19, 30, r19 // s-16.44
+*/
+ rtx inv2 = operands[0];
+ rtx norm32 = operands[1];
+ rtx inv1 = operands[2];
+ rtx i92 = operands[3];
+ rtx scratch0 = operands[4];
+ rtx scratch0_si = simplify_gen_subreg (SImode, scratch0, DImode, SIDI_OFF);
+
+ emit_insn (gen_mulsidi3_media (scratch0, inv1, norm32));
+ emit_insn (gen_ashrdi3_media (scratch0, scratch0, GEN_INT (16)));
+ emit_insn (gen_subdi3 (scratch0, i92, scratch0));
+ emit_insn (gen_mulsidi3_media (scratch0, scratch0_si, inv1));
+ emit_insn (gen_ashrdisi3_media_opaque (inv2, scratch0, GEN_INT (30)));
+ DONE;
+}")
+
+(define_insn_and_split "divsi_inv_m3"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (unspec:SI [(match_operand:SI 1 "arith_reg_or_0_operand" "rN")
+ (match_operand:SI 2 "register_operand" "r")
+ (match_operand:SI 3 "register_operand" "r")
+ (match_operand:DI 4 "register_operand" "r")
+ (match_operand:DI 5 "arith_reg_or_0_operand" "rN")
+ (match_operand:DI 6 "arith_reg_or_0_operand" "rN")]
+ UNSPEC_DIV_INV_M3))
+ (clobber (match_operand:DI 7 "register_operand" "=r"))
+ (clobber (match_operand:DI 8 "register_operand" "=r"))
+ (clobber (match_operand:DI 9 "register_operand" "=r"))
+ (clobber (match_operand:DI 10 "register_operand" "=r"))
+ (clobber (match_operand:SI 11 "register_operand" "=r"))
+ (clobber (match_operand:SI 12 "register_operand" "=r"))
+ (clobber (match_operand:DI 13 "register_operand" "=r"))]
+ "TARGET_SHMEDIA"
+ "#"
+ "&& !can_create_pseudo_p ()"
+ [(pc)]
+ "
+{
+/*
+ r0: result r1: shift r4: dividend r18: inv1 r19: inv2
+ r0: scratch0 r19: scratch1 r21: scratch2
+
+ muls.l r18, r4, r25 // s32.30
+ muls.l r19, r4, r19 // s15.30
+ shari r25, 63, r21
+ shari r19, 14, r19 // s18.-14
+ sub r25, r19, r0
+ shard r0, r1, r0
+ sub r0, r21, r0
+*/
+
+ rtx result = operands[0];
+ rtx dividend = operands[1];
+ rtx inv1 = operands[2];
+ rtx inv2 = operands[3];
+ rtx shift = operands[4];
+ rtx scratch0 = operands[7];
+ rtx scratch1 = operands[8];
+ rtx scratch2 = operands[9];
+
+ if (satisfies_constraint_N (dividend))
+ {
+ emit_move_insn (result, dividend);
+ DONE;
+ }
+
+ emit_insn (gen_mulsidi3_media (scratch0, inv1, dividend));
+ emit_insn (gen_mulsidi3_media (scratch1, inv2, dividend));
+ emit_insn (gen_ashrdi3_media (scratch2, scratch0, GEN_INT (63)));
+ emit_insn (gen_ashrdi3_media (scratch1, scratch1, GEN_INT (14)));
+ emit_insn (gen_adddi3 (scratch0, scratch0, scratch1));
+ emit_insn (gen_ashrdi3_media (scratch0, scratch0, shift));
+ emit_insn (gen_subdisi3_media (result, scratch0, scratch2));
+ DONE;
+}")
+
+;; operands: quotient, dividend, inv1, inv2, shift, i2p27, i43
+;; inv1: tab_base, tab_ix, norm32
+;; inv2: norm32, inv1, i92
+(define_insn_and_split "divsi_inv_m1_3"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (unspec:SI [(match_operand:SI 1 "arith_reg_or_0_operand" "rN")
+ (unspec:SI [(match_operand:DI 2 "register_operand" "r")
+ (match_operand:DI 3 "register_operand" "r")
+ (match_operand:SI 4 "register_operand" "r")]
+ UNSPEC_DIV_INV_M1)
+ (unspec:SI [(match_dup 4)
+ (unspec:SI [(match_dup 2)
+ (match_dup 3)
+ (match_dup 4)] UNSPEC_DIV_INV_M1)
+ (match_operand:SI 5 "" "")]
+ UNSPEC_DIV_INV_M2)
+ (match_operand:DI 6 "register_operand" "r")
+ (match_operand:DI 7 "arith_reg_or_0_operand" "rN")
+ (match_operand:DI 8 "arith_reg_or_0_operand" "rN")]
+ UNSPEC_DIV_INV_M3))
+ (clobber (match_operand:DI 9 "register_operand" "=r"))
+ (clobber (match_operand:DI 10 "register_operand" "=r"))
+ (clobber (match_operand:DI 11 "register_operand" "=r"))
+ (clobber (match_operand:DI 12 "register_operand" "=r"))
+ (clobber (match_operand:SI 13 "register_operand" "=r"))
+ (clobber (match_operand:SI 14 "register_operand" "=r"))
+ (clobber (match_operand:DI 15 "register_operand" "=r"))]
+ "TARGET_SHMEDIA
+ && (TARGET_DIVIDE_INV_MINLAT
+ || TARGET_DIVIDE_INV20U || TARGET_DIVIDE_INV20L)"
+ "#"
+ "&& !can_create_pseudo_p ()"
+ [(pc)]
+ "
+{
+ rtx result = operands[0];
+ rtx dividend = operands[1];
+ rtx tab_base = operands[2];
+ rtx tab_ix = operands[3];
+ rtx norm32 = operands[4];
+ /* rtx i92 = operands[5]; */
+ rtx shift = operands[6];
+ rtx i2p27 = operands[7];
+ rtx i43 = operands[8];
+ rtx scratch0 = operands[9];
+ rtx scratch0_si = simplify_gen_subreg (SImode, scratch0, DImode, SIDI_OFF);
+ rtx scratch1 = operands[10];
+ rtx scratch1_si = simplify_gen_subreg (SImode, scratch1, DImode, SIDI_OFF);
+ rtx scratch2 = operands[11];
+ rtx scratch3 = operands[12];
+ rtx scratch4 = operands[13];
+ rtx scratch4_di = simplify_gen_subreg (DImode, scratch4, SImode, 0);
+ rtx scratch5 = operands[14];
+ rtx scratch5_di = simplify_gen_subreg (DImode, scratch5, SImode, 0);
+ rtx scratch6 = operands[15];
+
+ emit_insn (gen_divsi_inv_m0 (scratch4, tab_base, tab_ix, norm32,
+ scratch0, scratch1));
+ /* inv0 == scratch4 */
+ if (! TARGET_DIVIDE_INV20U)
+ {
+ emit_insn (gen_mulsidi3_media (scratch0, scratch4, scratch4));
+ i2p27 = scratch0;
+ emit_insn (gen_mulsidi3_media (scratch1, norm32, scratch0_si));
+ }
+ else
+ {
+ emit_insn (gen_mulsidi3_media (scratch1, scratch4, scratch4));
+ emit_insn (gen_mulsidi3_media (scratch1, norm32, scratch1_si));
+ }
+ emit_insn (gen_ashldi3_media (scratch2, scratch4_di, GEN_INT (45)));
+ emit_insn (gen_subdi3 (scratch1, scratch2, scratch1));
+ emit_insn (gen_ashrdisi3_media_opaque (scratch4, scratch1, GEN_INT (28)));
+ /* inv1 == scratch4 */
+
+ if (TARGET_DIVIDE_INV_MINLAT)
+ {
+ emit_insn (gen_mulsidi3_media (scratch1, scratch4, norm32));
+ emit_insn (gen_mulsidi3_media (scratch2, dividend, scratch4));
+ emit_insn (gen_ashrdi3_media (scratch1, scratch1, GEN_INT (16)));
+ emit_insn (gen_mulsidi3_media (scratch1, scratch1_si, scratch4));
+ emit_insn (gen_ashrdi3_media (scratch3, scratch2, GEN_INT (63)));
+ emit_insn (gen_ashrsi3_media (scratch5, dividend, GEN_INT (14)));
+ emit_insn (gen_ashrdi3_media (scratch1, scratch1, GEN_INT (30)));
+ emit_insn (gen_mulsidi3_media (scratch1, scratch1_si, scratch5));
+ emit_insn (gen_xordi3 (scratch0, scratch3, i2p27));
+ emit_insn (gen_adddi3 (scratch2, scratch2, scratch0));
+ emit_insn (gen_subdi3 (scratch2, scratch2, scratch1));
+ }
+ else
+ {
+ rtx label = gen_rtx_LABEL_REF (Pmode, gen_label_rtx ());
+ /* Use separate scratch regs for nsb and sign to allow scheduling. */
+ emit_insn (gen_nsbdi (scratch6,
+ simplify_gen_subreg (DImode, dividend, SImode, 0)));
+ emit_insn (gen_xorsi3 (scratch5, dividend, norm32));
+ emit_insn (gen_ashrdi3_media (scratch3, scratch5_di, GEN_INT (63)));
+ emit_insn (gen_divsi_inv20 (scratch2,
+ norm32, scratch4, dividend,
+ scratch6, scratch3, i43,
+ /* scratch0 may be shared with i2p27. */
+ scratch0, scratch1, scratch5,
+ label, label, i2p27));
+ }
+ emit_insn (gen_ashrdi3_media (scratch2, scratch2, shift));
+ emit_insn (gen_subdisi3_media (result, scratch2, scratch3));
+ DONE;
+}")
+
+(define_insn "divsi_inv20"
+ [(set (match_operand:DI 0 "register_operand" "=&r")
+ (unspec:DI [(match_operand:SI 1 "register_operand" "r")
+ (match_operand:SI 2 "register_operand" "r")
+ (match_operand:SI 3 "register_operand" "r")
+ (match_operand:DI 4 "register_operand" "r")
+ (match_operand:DI 5 "register_operand" "r")
+ (match_operand:DI 6 "register_operand" "r")
+ (match_operand:DI 12 "register_operand" "r")
+ (match_operand 10 "target_operand" "b")
+ (match_operand 11 "immediate_operand" "i")]
+ UNSPEC_DIV_INV20))
+ (clobber (match_operand:DI 7 "register_operand" "=&r"))
+ (clobber (match_operand:DI 8 "register_operand" "=&r"))
+ (clobber (match_operand:SI 9 "register_operand" "=r"))]
+ "TARGET_SHMEDIA
+ && (TARGET_DIVIDE_INV20U || TARGET_DIVIDE_INV20L)"
+ "*
+{
+/* operands: %0 div_result, %1 norm32, %2 inv1, %3 dividend,
+ %4 dividend_nsb, %5 result_sign, %6 i43, %12 i2p27,
+ %7 round_scratch, %8 scratch0 (di), %9 scratch1 (si)
+ %10 label (tr), %11 label (imm)
+
+ muls.l inv1, norm32, scratch0 // s2.60
+ muls.l inv1, dividend, result // s32.30
+ xor i2p27, result_sign, round_scratch
+ bge/u dividend_nsb, i43, tr.. (label)
+ shari scratch0, 16, scratch0 // s-16.44
+ muls.l sratch0_si, inv1, scratch0 // s-16.74
+ sub result, round_scratch, result
+ shari dividend, 14, scratch1 // s19.-14
+ shari scratch0, 30, scratch0 // s-16.44
+ muls.l scratch0, scratch1, round_scratch // s15.30
+label:
+ sub result, round_scratch, result */
+
+ int likely = TARGET_DIVIDE_INV20L;
+
+ if (! likely) output_asm_insn (\"muls.l\t%2, %1 , %8\", operands);
+ output_asm_insn (\"muls.l\t%2, %3, %0\;xor\t%12, %5, %7\", operands);
+ output_asm_insn (likely
+ ? \"bge/l\t%4, %6, %10\;muls.l\t%2, %1 , %8\"
+ : \"bge/u\t%4, %6, %10\", operands);
+ output_asm_insn (\"shari\t%8, 16, %8\;muls.l\t%8, %2, %8\", operands);
+ if (! likely) output_asm_insn (\"sub\t%0, %7, %0\", operands);
+ output_asm_insn (\"shari\t%3, 14, %9\;shari\t%8, 30, %8\", operands);
+ return (likely
+ ? \"muls.l\t%8, %9, %8\;sub\t%0, %8, %0\n%11:\tadd\t%0, %7, %0\"
+ : \"muls.l\t%8, %9, %7\n%11:\tsub\t%0, %7, %0\");
+}")
+
+(define_insn_and_split "divsi_inv_fp"
+ [(set (match_operand:SI 0 "general_movdst_operand" "=rf")
+ (div:SI (match_operand:SI 1 "general_movsrc_operand" "rf")
+ (match_operand:SI 2 "register_operand" "rf")))
+ (use (match_operand:SI 3 "general_movsrc_operand" "r"))
+ (clobber (match_operand:SI 4 "register_operand" "=r"))
+ (clobber (match_operand:SI 5 "register_operand" "=r"))
+ (clobber (match_operand:DF 6 "register_operand" "=r"))
+ (clobber (match_operand:DF 7 "register_operand" "=r"))
+ (clobber (match_operand:DF 8 "register_operand" "=r"))]
+ "TARGET_SHMEDIA_FPU"
+ "#"
+ "&& (high_life_started || reload_completed)"
+ [(set (match_dup 0) (match_dup 3))]
+ ""
+ [(set_attr "highpart" "must_split")])
+
+;; If a matching group of divide-by-inverse instructions is in the same
+;; basic block after gcse & loop optimizations, we want to transform them
+;; to a straight division using floating point for TARGET_DIVIDE_INV_FP.
+(define_insn_and_split "*divsi_inv_fp_combine"
+ [(set (match_operand:SI 0 "register_operand" "=f")
+ (div:SI (match_operand:SI 1 "register_operand" "f")
+ (match_operand:SI 2 "register_operand" "f")))
+ (use (unspec:SI [(match_dup 1)
+ (match_operand:SI 3 "" "")
+ (unspec:SI [(match_operand:SI 4 "" "")
+ (match_dup 3)
+ (match_operand:DI 5 "" "")] UNSPEC_DIV_INV_M2)
+ (match_operand:DI 6 "" "")
+ (const_int 0)
+ (const_int 0)] UNSPEC_DIV_INV_M3))
+ (clobber (match_operand:SI 7 "fp_arith_reg_operand" ""))
+ (clobber (match_operand:SI 8 "fp_arith_reg_operand" ""))
+ (clobber (match_operand:DF 9 "fp_arith_reg_operand" ""))
+ (clobber (match_operand:DF 10 "fp_arith_reg_operand" ""))
+ (clobber (match_operand:DF 11 "fp_arith_reg_operand" ""))]
+ "TARGET_SHMEDIA_FPU && TARGET_DIVIDE_INV_FP && !can_create_pseudo_p ()"
+ "#"
+ "&& 1"
+ [(set (match_dup 9) (float:DF (match_dup 1)))
+ (set (match_dup 10) (float:DF (match_dup 2)))
+ (set (match_dup 11) (div:DF (match_dup 9) (match_dup 10)))
+ (set (match_dup 8)
+ (fix:SI (match_dup 11)))
+ (set (match_dup 0) (match_dup 8))]
+ "
+{
+ if (! fp_arith_reg_operand (operands[1], SImode))
+ {
+ emit_move_insn (operands[7], operands[1]);
+ operands[1] = operands[7];
+ }
+ if (! fp_arith_reg_operand (operands[2], SImode))
+ {
+ emit_move_insn (operands[8], operands[2]);
+ operands[2] = operands[8];
+ }
+}"
+ [(set_attr "highpart" "must_split")])
+
+;; -------------------------------------------------------------------------
+;; Multiplication instructions
+;; -------------------------------------------------------------------------
+
+(define_insn "umulhisi3_i"
+ [(set (reg:SI MACL_REG)
+ (mult:SI (zero_extend:SI
+ (match_operand:HI 0 "arith_reg_operand" "r"))
+ (zero_extend:SI
+ (match_operand:HI 1 "arith_reg_operand" "r"))))]
+ "TARGET_SH1"
+ "mulu.w %1,%0"
+ [(set_attr "type" "smpy")])
+
+(define_insn "mulhisi3_i"
+ [(set (reg:SI MACL_REG)
+ (mult:SI (sign_extend:SI
+ (match_operand:HI 0 "arith_reg_operand" "r"))
+ (sign_extend:SI
+ (match_operand:HI 1 "arith_reg_operand" "r"))))]
+ "TARGET_SH1"
+ "muls.w %1,%0"
+ [(set_attr "type" "smpy")])
+
+(define_expand "mulhisi3"
+ [(set (reg:SI MACL_REG)
+ (mult:SI (sign_extend:SI
+ (match_operand:HI 1 "arith_reg_operand" ""))
+ (sign_extend:SI
+ (match_operand:HI 2 "arith_reg_operand" ""))))
+ (set (match_operand:SI 0 "arith_reg_operand" "")
+ (reg:SI MACL_REG))]
+ "TARGET_SH1"
+ "
+{
+ rtx insn, macl;
+
+ macl = gen_rtx_REG (SImode, MACL_REG);
+ start_sequence ();
+ emit_insn (gen_mulhisi3_i (operands[1], operands[2]));
+ insn = get_insns ();
+ end_sequence ();
+ /* expand_binop can't find a suitable code in umul_widen_optab to
+ make a REG_EQUAL note from, so make one here.
+ See also smulsi3_highpart.
+ ??? Alternatively, we could put this at the calling site of expand_binop,
+ i.e. expand_expr. */
+ /* Use emit_libcall_block for loop invariant code motion and to make
+ a REG_EQUAL note. */
+ emit_libcall_block (insn, operands[0], macl, SET_SRC (single_set (insn)));
+
+ DONE;
+}")
+
+(define_expand "umulhisi3"
+ [(set (reg:SI MACL_REG)
+ (mult:SI (zero_extend:SI
+ (match_operand:HI 1 "arith_reg_operand" ""))
+ (zero_extend:SI
+ (match_operand:HI 2 "arith_reg_operand" ""))))
+ (set (match_operand:SI 0 "arith_reg_operand" "")
+ (reg:SI MACL_REG))]
+ "TARGET_SH1"
+ "
+{
+ rtx insn, macl;
+
+ macl = gen_rtx_REG (SImode, MACL_REG);
+ start_sequence ();
+ emit_insn (gen_umulhisi3_i (operands[1], operands[2]));
+ insn = get_insns ();
+ end_sequence ();
+ /* expand_binop can't find a suitable code in umul_widen_optab to
+ make a REG_EQUAL note from, so make one here.
+ See also smulsi3_highpart.
+ ??? Alternatively, we could put this at the calling site of expand_binop,
+ i.e. expand_expr. */
+ /* Use emit_libcall_block for loop invariant code motion and to make
+ a REG_EQUAL note. */
+ emit_libcall_block (insn, operands[0], macl, SET_SRC (single_set (insn)));
+
+ DONE;
+}")
+
+;; mulsi3 on the SH2 can be done in one instruction, on the SH1 we generate
+;; a call to a routine which clobbers known registers.
+
+(define_insn ""
+ [(set (match_operand:SI 1 "register_operand" "=z")
+ (mult:SI (reg:SI R4_REG) (reg:SI R5_REG)))
+ (clobber (reg:SI MACL_REG))
+ (clobber (reg:SI T_REG))
+ (clobber (reg:SI PR_REG))
+ (clobber (reg:SI R3_REG))
+ (clobber (reg:SI R2_REG))
+ (clobber (reg:SI R1_REG))
+ (use (match_operand:SI 0 "arith_reg_operand" "r"))]
+ "TARGET_SH1"
+ "jsr @%0%#"
+ [(set_attr "type" "sfunc")
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_expand "mulsi3_call"
+ [(set (reg:SI R4_REG) (match_operand:SI 1 "general_operand" ""))
+ (set (reg:SI R5_REG) (match_operand:SI 2 "general_operand" ""))
+ (parallel[(set (match_operand:SI 0 "register_operand" "")
+ (mult:SI (reg:SI R4_REG)
+ (reg:SI R5_REG)))
+ (clobber (reg:SI MACL_REG))
+ (clobber (reg:SI T_REG))
+ (clobber (reg:SI PR_REG))
+ (clobber (reg:SI R3_REG))
+ (clobber (reg:SI R2_REG))
+ (clobber (reg:SI R1_REG))
+ (use (match_operand:SI 3 "register_operand" ""))])]
+ "TARGET_SH1"
+ "")
+
+(define_insn "mul_r"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (mult:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (match_operand:SI 2 "arith_reg_operand" "z")))]
+ "TARGET_SH2A"
+ "mulr %2,%0"
+ [(set_attr "type" "dmpy")])
+
+(define_insn "mul_l"
+ [(set (reg:SI MACL_REG)
+ (mult:SI (match_operand:SI 0 "arith_reg_operand" "r")
+ (match_operand:SI 1 "arith_reg_operand" "r")))]
+ "TARGET_SH2"
+ "mul.l %1,%0"
+ [(set_attr "type" "dmpy")])
+
+(define_expand "mulsi3"
+ [(set (reg:SI MACL_REG)
+ (mult:SI (match_operand:SI 1 "arith_reg_operand" "")
+ (match_operand:SI 2 "arith_reg_operand" "")))
+ (set (match_operand:SI 0 "arith_reg_operand" "")
+ (reg:SI MACL_REG))]
+ "TARGET_SH1"
+ "
+{
+ if (!TARGET_SH2)
+ {
+ /* The address must be set outside the libcall,
+ since it goes into a pseudo. */
+ rtx sym = function_symbol (NULL, \"__mulsi3\", SFUNC_STATIC);
+ rtx addr = force_reg (SImode, sym);
+ rtx insns = gen_mulsi3_call (operands[0], operands[1],
+ operands[2], addr);
+ emit_insn (insns);
+ }
+ else
+ {
+ rtx macl = gen_rtx_REG (SImode, MACL_REG);
+
+ emit_insn (gen_mul_l (operands[1], operands[2]));
+ /* consec_sets_giv can only recognize the first insn that sets a
+ giv as the giv insn. So we must tag this also with a REG_EQUAL
+ note. */
+ emit_insn (gen_movsi_i ((operands[0]), macl));
+ }
+ DONE;
+}")
+
+(define_insn "mulsidi3_i"
+ [(set (reg:SI MACH_REG)
+ (truncate:SI
+ (lshiftrt:DI
+ (mult:DI
+ (sign_extend:DI (match_operand:SI 0 "arith_reg_operand" "r"))
+ (sign_extend:DI (match_operand:SI 1 "arith_reg_operand" "r")))
+ (const_int 32))))
+ (set (reg:SI MACL_REG)
+ (mult:SI (match_dup 0)
+ (match_dup 1)))]
+ "TARGET_SH2"
+ "dmuls.l %1,%0"
+ [(set_attr "type" "dmpy")])
+
+(define_expand "mulsidi3"
+ [(set (match_operand:DI 0 "arith_reg_operand" "=r")
+ (mult:DI (sign_extend:DI (match_operand:SI 1 "arith_reg_operand" "r"))
+ (sign_extend:DI (match_operand:SI 2 "arith_reg_operand" "r"))))]
+ "TARGET_SH2 || TARGET_SHMEDIA"
+ "
+{
+ if (TARGET_SH2)
+ {
+ emit_insn (gen_mulsidi3_compact (operands[0], operands[1],
+ operands[2]));
+ DONE;
+ }
+}")
+
+(define_insn "mulsidi3_media"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (mult:DI (sign_extend:DI (match_operand:SI 1 "extend_reg_operand" "%r"))
+ (sign_extend:DI (match_operand:SI 2 "extend_reg_operand" "r"))))]
+ "TARGET_SHMEDIA"
+ "muls.l %1, %2, %0"
+ [(set_attr "type" "dmpy_media")
+ (set_attr "highpart" "ignore")])
+
+(define_insn "mulsidi3_compact"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (mult:DI
+ (sign_extend:DI (match_operand:SI 1 "arith_reg_operand" "r"))
+ (sign_extend:DI (match_operand:SI 2 "arith_reg_operand" "r"))))
+ (clobber (reg:SI MACH_REG))
+ (clobber (reg:SI MACL_REG))]
+ "TARGET_SH2"
+ "#")
+
+(define_split
+ [(set (match_operand:DI 0 "arith_reg_dest" "")
+ (mult:DI
+ (sign_extend:DI (match_operand:SI 1 "arith_reg_operand" ""))
+ (sign_extend:DI (match_operand:SI 2 "arith_reg_operand" ""))))
+ (clobber (reg:SI MACH_REG))
+ (clobber (reg:SI MACL_REG))]
+ "TARGET_SH2"
+ [(const_int 0)]
+ "
+{
+ rtx low_dst = gen_lowpart (SImode, operands[0]);
+ rtx high_dst = gen_highpart (SImode, operands[0]);
+
+ emit_insn (gen_mulsidi3_i (operands[1], operands[2]));
+
+ emit_move_insn (low_dst, gen_rtx_REG (SImode, MACL_REG));
+ emit_move_insn (high_dst, gen_rtx_REG (SImode, MACH_REG));
+ /* We need something to tag the possible REG_EQUAL notes on to. */
+ emit_move_insn (operands[0], operands[0]);
+ DONE;
+}")
+
+(define_insn "umulsidi3_i"
+ [(set (reg:SI MACH_REG)
+ (truncate:SI
+ (lshiftrt:DI
+ (mult:DI
+ (zero_extend:DI (match_operand:SI 0 "arith_reg_operand" "r"))
+ (zero_extend:DI (match_operand:SI 1 "arith_reg_operand" "r")))
+ (const_int 32))))
+ (set (reg:SI MACL_REG)
+ (mult:SI (match_dup 0)
+ (match_dup 1)))]
+ "TARGET_SH2"
+ "dmulu.l %1,%0"
+ [(set_attr "type" "dmpy")])
+
+(define_expand "umulsidi3"
+ [(set (match_operand:DI 0 "arith_reg_operand" "=r")
+ (mult:DI (zero_extend:DI (match_operand:SI 1 "arith_reg_operand" "r"))
+ (zero_extend:DI (match_operand:SI 2 "arith_reg_operand" "r"))))]
+ "TARGET_SH2 || TARGET_SHMEDIA"
+ "
+{
+ if (TARGET_SH2)
+ {
+ emit_insn (gen_umulsidi3_compact (operands[0], operands[1],
+ operands[2]));
+ DONE;
+ }
+}")
+
+(define_insn "umulsidi3_media"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (mult:DI (zero_extend:DI (match_operand:SI 1 "extend_reg_operand" "%r"))
+ (zero_extend:DI (match_operand:SI 2 "extend_reg_operand" "r"))))]
+ "TARGET_SHMEDIA"
+ "mulu.l %1, %2, %0"
+ [(set_attr "type" "dmpy_media")
+ (set_attr "highpart" "ignore")])
+
+(define_insn "umulsidi3_compact"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (mult:DI
+ (zero_extend:DI (match_operand:SI 1 "arith_reg_operand" "r"))
+ (zero_extend:DI (match_operand:SI 2 "arith_reg_operand" "r"))))
+ (clobber (reg:SI MACH_REG))
+ (clobber (reg:SI MACL_REG))]
+ "TARGET_SH2"
+ "#")
+
+(define_split
+ [(set (match_operand:DI 0 "arith_reg_dest" "")
+ (mult:DI (zero_extend:DI (match_operand:SI 1 "arith_reg_operand" ""))
+ (zero_extend:DI (match_operand:SI 2 "arith_reg_operand" ""))))
+ (clobber (reg:SI MACH_REG))
+ (clobber (reg:SI MACL_REG))]
+ "TARGET_SH2"
+ [(const_int 0)]
+ "
+{
+ rtx low_dst = gen_lowpart (SImode, operands[0]);
+ rtx high_dst = gen_highpart (SImode, operands[0]);
+
+ emit_insn (gen_umulsidi3_i (operands[1], operands[2]));
+
+ emit_move_insn (low_dst, gen_rtx_REG (SImode, MACL_REG));
+ emit_move_insn (high_dst, gen_rtx_REG (SImode, MACH_REG));
+ /* We need something to tag the possible REG_EQUAL notes on to. */
+ emit_move_insn (operands[0], operands[0]);
+ DONE;
+}")
+
+(define_insn "smulsi3_highpart_i"
+ [(set (reg:SI MACH_REG)
+ (truncate:SI
+ (lshiftrt:DI
+ (mult:DI
+ (sign_extend:DI (match_operand:SI 0 "arith_reg_operand" "r"))
+ (sign_extend:DI (match_operand:SI 1 "arith_reg_operand" "r")))
+ (const_int 32))))
+ (clobber (reg:SI MACL_REG))]
+ "TARGET_SH2"
+ "dmuls.l %1,%0"
+ [(set_attr "type" "dmpy")])
+
+(define_expand "smulsi3_highpart"
+ [(parallel
+ [(set (reg:SI MACH_REG)
+ (truncate:SI
+ (lshiftrt:DI
+ (mult:DI
+ (sign_extend:DI (match_operand:SI 1 "arith_reg_operand" ""))
+ (sign_extend:DI (match_operand:SI 2 "arith_reg_operand" "")))
+ (const_int 32))))
+ (clobber (reg:SI MACL_REG))])
+ (set (match_operand:SI 0 "arith_reg_operand" "")
+ (reg:SI MACH_REG))]
+ "TARGET_SH2"
+ "
+{
+ rtx insn, mach;
+
+ mach = gen_rtx_REG (SImode, MACH_REG);
+ start_sequence ();
+ emit_insn (gen_smulsi3_highpart_i (operands[1], operands[2]));
+ insn = get_insns ();
+ end_sequence ();
+ /* expand_binop can't find a suitable code in mul_highpart_optab to
+ make a REG_EQUAL note from, so make one here.
+ See also {,u}mulhisi.
+ ??? Alternatively, we could put this at the calling site of expand_binop,
+ i.e. expand_mult_highpart. */
+ /* Use emit_libcall_block for loop invariant code motion and to make
+ a REG_EQUAL note. */
+ emit_libcall_block (insn, operands[0], mach, SET_SRC (single_set (insn)));
+
+ DONE;
+}")
+
+(define_insn "umulsi3_highpart_i"
+ [(set (reg:SI MACH_REG)
+ (truncate:SI
+ (lshiftrt:DI
+ (mult:DI
+ (zero_extend:DI (match_operand:SI 0 "arith_reg_operand" "r"))
+ (zero_extend:DI (match_operand:SI 1 "arith_reg_operand" "r")))
+ (const_int 32))))
+ (clobber (reg:SI MACL_REG))]
+ "TARGET_SH2"
+ "dmulu.l %1,%0"
+ [(set_attr "type" "dmpy")])
+
+(define_expand "umulsi3_highpart"
+ [(parallel
+ [(set (reg:SI MACH_REG)
+ (truncate:SI
+ (lshiftrt:DI
+ (mult:DI
+ (zero_extend:DI (match_operand:SI 1 "arith_reg_operand" ""))
+ (zero_extend:DI (match_operand:SI 2 "arith_reg_operand" "")))
+ (const_int 32))))
+ (clobber (reg:SI MACL_REG))])
+ (set (match_operand:SI 0 "arith_reg_operand" "")
+ (reg:SI MACH_REG))]
+ "TARGET_SH2"
+ "
+{
+ rtx insn, mach;
+
+ mach = gen_rtx_REG (SImode, MACH_REG);
+ start_sequence ();
+ emit_insn (gen_umulsi3_highpart_i (operands[1], operands[2]));
+ insn = get_insns ();
+ end_sequence ();
+ /* Use emit_libcall_block for loop invariant code motion and to make
+ a REG_EQUAL note. */
+ emit_libcall_block (insn, operands[0], mach, SET_SRC (single_set (insn)));
+
+ DONE;
+}")
+
+(define_insn_and_split "muldi3"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (mult:DI (match_operand:DI 1 "arith_reg_operand" "r")
+ (match_operand:DI 2 "arith_reg_operand" "r")))
+ (clobber (match_scratch:DI 3 "=&r"))
+ (clobber (match_scratch:DI 4 "=r"))]
+ "TARGET_SHMEDIA"
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+ "
+{
+ rtx op3_v2si, op2_v2si;
+
+ op3_v2si = operands[3];
+ if (GET_CODE (op3_v2si) == SIGN_EXTEND)
+ {
+ op3_v2si = XEXP (op3_v2si, 0);
+ op3_v2si = simplify_gen_subreg (DImode, op3_v2si, GET_MODE (op3_v2si), 0);
+ }
+ op3_v2si = simplify_gen_subreg (V2SImode, op3_v2si, DImode, 0);
+ op2_v2si = operands[2];
+ if (GET_CODE (op2_v2si) == SIGN_EXTEND)
+ {
+ op2_v2si = XEXP (op2_v2si, 0);
+ op2_v2si = simplify_gen_subreg (DImode, op2_v2si, GET_MODE (op2_v2si), 0);
+ }
+ op2_v2si = simplify_gen_subreg (V2SImode, op2_v2si, DImode, 0);
+ emit_insn (gen_rotldi3 (operands[3], operands[1], GEN_INT (32)));
+ emit_insn (gen_mulv2si3 (op3_v2si, op3_v2si, op2_v2si));
+ emit_insn (gen_umulsidi3_media (operands[4],
+ sh_gen_truncate (SImode, operands[1], 0),
+ sh_gen_truncate (SImode, operands[2], 0)));
+ emit_insn (gen_anddi3 (operands[0], operands[3], GEN_INT (0xffffffff00000000LL)));
+ emit_insn (gen_ashldi3_media (operands[3], operands[3], GEN_INT (32)));
+ emit_insn (gen_adddi3 (operands[0], operands[3], operands[0]));
+ emit_insn (gen_adddi3 (operands[0], operands[4], operands[0]));
+ DONE;
+}")
+
+
+;; -------------------------------------------------------------------------
+;; Logical operations
+;; -------------------------------------------------------------------------
+
+(define_insn "*andsi3_compact"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r,z")
+ (and:SI (match_operand:SI 1 "arith_reg_operand" "%0,0")
+ (match_operand:SI 2 "logical_operand" "r,K08")))]
+ "TARGET_SH1"
+ "and %2,%0"
+ [(set_attr "type" "arith")])
+
+(define_insn "*andsi3_media"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r,r")
+ (and:SI (match_operand:SI 1 "logical_reg_operand" "%r,r")
+ (match_operand:SI 2 "logical_operand" "r,I10")))]
+ "TARGET_SHMEDIA"
+ "@
+ and %1, %2, %0
+ andi %1, %2, %0"
+ [(set_attr "type" "arith_media")])
+
+(define_insn "*andsi3_bclr"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (and:SI (match_operand:SI 1 "arith_reg_operand" "%0")
+ (match_operand:SI 2 "const_int_operand" "Psz")))]
+ "TARGET_SH2A && satisfies_constraint_Psz (operands[2])"
+ "bclr\\t%W2,%0"
+ [(set_attr "type" "arith")])
+
+;; If the constant is 255, then emit an extu.b instruction instead of an
+;; and, since that will give better code.
+
+(define_expand "andsi3"
+ [(set (match_operand:SI 0 "arith_reg_operand" "")
+ (and:SI (match_operand:SI 1 "logical_reg_operand" "")
+ (match_operand:SI 2 "logical_operand" "")))]
+ ""
+ "
+{
+ if (TARGET_SH1
+ && CONST_INT_P (operands[2]) && INTVAL (operands[2]) == 255)
+ {
+ emit_insn (gen_zero_extendqisi2 (operands[0],
+ gen_lowpart (QImode, operands[1])));
+ DONE;
+ }
+}")
+
+(define_insn_and_split "anddi3"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r,r,r")
+ (and:DI (match_operand:DI 1 "arith_reg_operand" "%r,r,r")
+ (match_operand:DI 2 "and_operand" "r,I10,J16")))]
+ "TARGET_SHMEDIA"
+ "@
+ and %1, %2, %0
+ andi %1, %2, %0
+ #"
+ "reload_completed
+ && ! logical_operand (operands[2], DImode)"
+ [(const_int 0)]
+ "
+{
+ if ((unsigned)INTVAL (operands[2]) == (unsigned) 0xffffffff)
+ emit_insn (gen_mshflo_l_di (operands[0], operands[1], CONST0_RTX (DImode)));
+ else
+ emit_insn (gen_mshfhi_l_di (operands[0], CONST0_RTX (DImode), operands[1]));
+ DONE;
+}"
+ [(set_attr "type" "arith_media")])
+
+(define_insn "andcsi3"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (and:SI (match_operand:SI 1 "arith_reg_operand" "r")
+ (not:SI (match_operand:SI 2 "arith_reg_operand" "r"))))]
+ "TARGET_SHMEDIA"
+ "andc %1,%2,%0"
+ [(set_attr "type" "arith_media")])
+
+(define_insn "andcdi3"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (and:DI (match_operand:DI 1 "arith_reg_operand" "r")
+ (not:DI (match_operand:DI 2 "arith_reg_operand" "r"))))]
+ "TARGET_SHMEDIA"
+ "andc %1,%2,%0"
+ [(set_attr "type" "arith_media")])
+
+(define_expand "iorsi3"
+ [(set (match_operand:SI 0 "arith_reg_operand" "")
+ (ior:SI (match_operand:SI 1 "logical_reg_operand" "")
+ (match_operand:SI 2 "logical_operand" "")))]
+ ""
+ "")
+
+(define_insn "*iorsi3_compact"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r,z")
+ (ior:SI (match_operand:SI 1 "arith_reg_operand" "%0,0")
+ (match_operand:SI 2 "logical_operand" "r,K08")))]
+ "TARGET_SH1
+ && !(TARGET_SH2A && satisfies_constraint_Pso (operands[2]))"
+ "or %2,%0"
+ [(set_attr "type" "arith")])
+
+(define_insn "*iorsi3_media"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r,r")
+ (ior:SI (match_operand:SI 1 "logical_reg_operand" "%r,r")
+ (match_operand:SI 2 "logical_operand" "r,I10")))]
+ "TARGET_SHMEDIA"
+ "@
+ or %1, %2, %0
+ ori %1, %2, %0"
+ [(set_attr "type" "arith_media")])
+
+(define_insn "*iorsi3_bset"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (ior:SI (match_operand:SI 1 "arith_reg_operand" "%0")
+ (match_operand:SI 2 "const_int_operand" "Pso")))]
+ "TARGET_SH2A && satisfies_constraint_Pso (operands[2])"
+ "bset\\t%V2,%0"
+ [(set_attr "type" "arith")])
+
+(define_insn "iordi3"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r,r")
+ (ior:DI (match_operand:DI 1 "arith_reg_operand" "%r,r")
+ (match_operand:DI 2 "logical_operand" "r,I10")))]
+ "TARGET_SHMEDIA"
+ "@
+ or %1, %2, %0
+ ori %1, %2, %0"
+ [(set_attr "type" "arith_media")])
+
+(define_insn_and_split "*logical_sidi3"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r,r")
+ (sign_extend:DI (match_operator:SI 3 "logical_operator"
+ [(match_operand:SI 1 "arith_reg_operand" "%r,r")
+ (match_operand:SI 2 "logical_operand" "r,I10")])))]
+ "TARGET_SHMEDIA"
+ "#"
+ "&& reload_completed"
+ [(set (match_dup 0) (match_dup 3))]
+ "
+{
+ operands[3]
+ = gen_rtx_fmt_ee (GET_CODE (operands[3]), DImode,
+ simplify_gen_subreg (DImode, operands[1], SImode, 0),
+ simplify_gen_subreg (DImode, operands[2], SImode, 0));
+}")
+
+(define_insn_and_split "*logical_sidisi3"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r,r")
+ (truncate:SI (sign_extend:DI
+ (match_operator:SI 3 "logical_operator"
+ [(match_operand:SI 1 "arith_reg_operand" "%r,r")
+ (match_operand:SI 2 "logical_operand" "r,I10")]))))]
+ "TARGET_SHMEDIA"
+ "#"
+ "&& 1"
+ [(set (match_dup 0) (match_dup 3))])
+
+(define_insn_and_split "*logical_sidi3_2"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r,r")
+ (sign_extend:DI (truncate:SI (sign_extend:DI
+ (match_operator:SI 3 "logical_operator"
+ [(match_operand:SI 1 "arith_reg_operand" "%r,r")
+ (match_operand:SI 2 "logical_operand" "r,I10")])))))]
+ "TARGET_SHMEDIA"
+ "#"
+ "&& 1"
+ [(set (match_dup 0) (sign_extend:DI (match_dup 3)))])
+
+(define_expand "xorsi3"
+ [(set (match_operand:SI 0 "arith_reg_operand" "")
+ (xor:SI (match_operand:SI 1 "logical_reg_operand" "")
+ (match_operand:SI 2 "xor_operand" "")))]
+ ""
+ "")
+
+(define_insn "*xorsi3_compact"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=z,r")
+ (xor:SI (match_operand:SI 1 "arith_reg_operand" "%0,0")
+ (match_operand:SI 2 "logical_operand" "K08,r")))]
+ "TARGET_SH1"
+ "xor %2,%0"
+ [(set_attr "type" "arith")])
+
+(define_insn "*xorsi3_media"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r,r")
+ (xor:SI (match_operand:SI 1 "logical_reg_operand" "%r,r")
+ (match_operand:SI 2 "xor_operand" "r,I06")))]
+ "TARGET_SHMEDIA"
+ "@
+ xor %1, %2, %0
+ xori %1, %2, %0"
+ [(set_attr "type" "arith_media")])
+
+;; Store the complements of the T bit in a register.
+(define_insn "xorsi3_movrt"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (xor:SI (reg:SI T_REG)
+ (const_int 1)))]
+ "TARGET_SH2A"
+ "movrt\\t%0"
+ [(set_attr "type" "arith")])
+
+(define_insn "xordi3"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r,r")
+ (xor:DI (match_operand:DI 1 "arith_reg_operand" "%r,r")
+ (match_operand:DI 2 "xor_operand" "r,I06")))]
+ "TARGET_SHMEDIA"
+ "@
+ xor %1, %2, %0
+ xori %1, %2, %0"
+ [(set_attr "type" "arith_media")])
+
+;; Combiner bridge pattern for 2 * sign extend -> logical op -> truncate.
+;; converts 2 * sign extend -> logical op into logical op -> sign extend
+(define_split
+ [(set (match_operand:DI 0 "arith_reg_dest" "")
+ (sign_extend:DI (match_operator 4 "binary_logical_operator"
+ [(match_operand 1 "any_register_operand" "")
+ (match_operand 2 "any_register_operand" "")])))]
+ "TARGET_SHMEDIA"
+ [(set (match_dup 5) (match_dup 4))
+ (set (match_dup 0) (sign_extend:DI (match_dup 5)))]
+"
+{
+ enum machine_mode inmode = GET_MODE (operands[1]);
+ int offset = 0;
+
+ if (GET_CODE (operands[0]) == SUBREG)
+ {
+ offset = SUBREG_BYTE (operands[0]);
+ operands[0] = SUBREG_REG (operands[0]);
+ }
+ gcc_assert (REG_P (operands[0]));
+ if (! TARGET_LITTLE_ENDIAN)
+ offset += 8 - GET_MODE_SIZE (inmode);
+ operands[5] = gen_rtx_SUBREG (inmode, operands[0], offset);
+}")
+
+;; -------------------------------------------------------------------------
+;; Shifts and rotates
+;; -------------------------------------------------------------------------
+
+(define_expand "rotldi3"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (rotate:DI (match_operand:DI 1 "arith_reg_operand" "r")
+ (match_operand:HI 2 "mextr_bit_offset" "i")))]
+ "TARGET_SHMEDIA"
+ "if (! mextr_bit_offset (operands[2], HImode)) FAIL;")
+
+(define_insn "rotldi3_mextr"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (rotate:DI (match_operand:DI 1 "arith_reg_operand" "r")
+ (match_operand:HI 2 "mextr_bit_offset" "i")))]
+ "TARGET_SHMEDIA"
+ "*
+{
+ static char templ[16];
+
+ sprintf (templ, \"mextr%d\\t%%1,%%1,%%0\",
+ 8 - (int) (INTVAL (operands[2]) >> 3));
+ return templ;
+}"
+ [(set_attr "type" "arith_media")])
+
+(define_expand "rotrdi3"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (rotatert:DI (match_operand:DI 1 "arith_reg_operand" "r")
+ (match_operand:HI 2 "mextr_bit_offset" "i")))]
+ "TARGET_SHMEDIA"
+ "if (! mextr_bit_offset (operands[2], HImode)) FAIL;")
+
+(define_insn "rotrdi3_mextr"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (rotatert:DI (match_operand:DI 1 "arith_reg_operand" "r")
+ (match_operand:HI 2 "mextr_bit_offset" "i")))]
+ "TARGET_SHMEDIA"
+ "*
+{
+ static char templ[16];
+
+ sprintf (templ, \"mextr%d\\t%%1,%%1,%%0\", (int) INTVAL (operands[2]) >> 3);
+ return templ;
+}"
+ [(set_attr "type" "arith_media")])
+
+(define_split
+ [(set (match_operand:DI 0 "arith_reg_dest" "")
+ (ior:DI (zero_extend:DI (mem:QI (match_operand 1
+ "ua_address_operand" "")))
+ (ashift:DI (match_operand:DI 2 "arith_reg_operand" "")
+ (const_int 8))))
+ (clobber (match_operand:DI 3 "register_operand" ""))]
+ "TARGET_SHMEDIA"
+ [(match_dup 4) (match_dup 5)]
+ "
+{
+ operands[4] = ((TARGET_LITTLE_ENDIAN ? gen_ldhi_q : gen_ldlo_q)
+ (operands[3], operands[1]));
+ operands[5] = gen_mextr_rl (operands[0], operands[3], operands[2],
+ GEN_INT (56), GEN_INT (8));
+}")
+
+(define_insn "rotlsi3_1"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (rotate:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (const_int 1)))
+ (set (reg:SI T_REG)
+ (lshiftrt:SI (match_dup 1) (const_int 31)))]
+ "TARGET_SH1"
+ "rotl %0"
+ [(set_attr "type" "arith")])
+
+(define_insn "rotlsi3_31"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (rotate:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (const_int 31)))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1"
+ "rotr %0"
+ [(set_attr "type" "arith")])
+
+(define_insn "rotlsi3_16"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (rotate:SI (match_operand:SI 1 "arith_reg_operand" "r")
+ (const_int 16)))]
+ "TARGET_SH1"
+ "swap.w %1,%0"
+ [(set_attr "type" "arith")])
+
+(define_expand "rotlsi3"
+ [(set (match_operand:SI 0 "arith_reg_dest" "")
+ (rotate:SI (match_operand:SI 1 "arith_reg_operand" "")
+ (match_operand:SI 2 "immediate_operand" "")))]
+ "TARGET_SH1"
+ "
+{
+ static const char rot_tab[] = {
+ 000, 000, 000, 000, 000, 000, 010, 001,
+ 001, 001, 011, 013, 003, 003, 003, 003,
+ 003, 003, 003, 003, 003, 013, 012, 002,
+ 002, 002, 010, 000, 000, 000, 000, 000,
+ };
+
+ int count, choice;
+
+ if (!CONST_INT_P (operands[2]))
+ FAIL;
+ count = INTVAL (operands[2]);
+ choice = rot_tab[count];
+ if (choice & 010 && SH_DYNAMIC_SHIFT_COST <= 1)
+ FAIL;
+ choice &= 7;
+ switch (choice)
+ {
+ case 0:
+ emit_move_insn (operands[0], operands[1]);
+ count -= (count & 16) * 2;
+ break;
+ case 3:
+ emit_insn (gen_rotlsi3_16 (operands[0], operands[1]));
+ count -= 16;
+ break;
+ case 1:
+ case 2:
+ {
+ rtx parts[2];
+ parts[0] = gen_reg_rtx (SImode);
+ parts[1] = gen_reg_rtx (SImode);
+ emit_insn (gen_rotlsi3_16 (parts[2-choice], operands[1]));
+ emit_move_insn (parts[choice-1], operands[1]);
+ emit_insn (gen_ashlsi3 (parts[0], parts[0], GEN_INT (8)));
+ emit_insn (gen_lshrsi3 (parts[1], parts[1], GEN_INT (8)));
+ emit_insn (gen_iorsi3 (operands[0], parts[0], parts[1]));
+ count = (count & ~16) - 8;
+ }
+ }
+
+ for (; count > 0; count--)
+ emit_insn (gen_rotlsi3_1 (operands[0], operands[0]));
+ for (; count < 0; count++)
+ emit_insn (gen_rotlsi3_31 (operands[0], operands[0]));
+
+ DONE;
+}")
+
+(define_insn "*rotlhi3_8"
+ [(set (match_operand:HI 0 "arith_reg_dest" "=r")
+ (rotate:HI (match_operand:HI 1 "arith_reg_operand" "r")
+ (const_int 8)))]
+ "TARGET_SH1"
+ "swap.b %1,%0"
+ [(set_attr "type" "arith")])
+
+(define_expand "rotlhi3"
+ [(set (match_operand:HI 0 "arith_reg_operand" "")
+ (rotate:HI (match_operand:HI 1 "arith_reg_operand" "")
+ (match_operand:HI 2 "immediate_operand" "")))]
+ "TARGET_SH1"
+ "
+{
+ if (!CONST_INT_P (operands[2]) || INTVAL (operands[2]) != 8)
+ FAIL;
+}")
+
+;;
+;; shift left
+
+(define_insn "ashlsi3_sh2a"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (ashift:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (match_operand:SI 2 "arith_reg_operand" "r")))]
+ "TARGET_SH2A"
+ "shad %2,%0"
+ [(set_attr "type" "arith")
+ (set_attr "length" "4")])
+
+;; This pattern is used by init_expmed for computing the costs of shift
+;; insns.
+
+(define_insn_and_split "ashlsi3_std"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r,r,r,r")
+ (ashift:SI (match_operand:SI 1 "arith_reg_operand" "0,0,0,0")
+ (match_operand:SI 2 "nonmemory_operand" "r,M,P27,?ri")))
+ (clobber (match_scratch:SI 3 "=X,X,X,&r"))]
+ "TARGET_SH3
+ || (TARGET_SH1 && satisfies_constraint_P27 (operands[2]))"
+ "@
+ shld %2,%0
+ add %0,%0
+ shll%O2 %0
+ #"
+ "TARGET_SH3
+ && reload_completed
+ && CONST_INT_P (operands[2])
+ && ! satisfies_constraint_P27 (operands[2])"
+ [(set (match_dup 3) (match_dup 2))
+ (parallel
+ [(set (match_dup 0) (ashift:SI (match_dup 1) (match_dup 3)))
+ (clobber (match_dup 4))])]
+ "operands[4] = gen_rtx_SCRATCH (SImode);"
+ [(set_attr "length" "*,*,*,4")
+ (set_attr "type" "dyn_shift,arith,arith,arith")])
+
+(define_insn "ashlhi3_k"
+ [(set (match_operand:HI 0 "arith_reg_dest" "=r,r")
+ (ashift:HI (match_operand:HI 1 "arith_reg_operand" "0,0")
+ (match_operand:HI 2 "const_int_operand" "M,P27")))]
+ "TARGET_SH1 && satisfies_constraint_P27 (operands[2])"
+ "@
+ add %0,%0
+ shll%O2 %0"
+ [(set_attr "type" "arith")])
+
+(define_insn "ashlsi3_n"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (ashift:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (match_operand:SI 2 "const_int_operand" "n")))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1 && ! sh_dynamicalize_shift_p (operands[2])"
+ "#"
+ [(set (attr "length")
+ (cond [(eq (symbol_ref "shift_insns_rtx (insn)") (const_int 1))
+ (const_string "2")
+ (eq (symbol_ref "shift_insns_rtx (insn)") (const_int 2))
+ (const_string "4")
+ (eq (symbol_ref "shift_insns_rtx (insn)") (const_int 3))
+ (const_string "6")]
+ (const_string "8")))
+ (set_attr "type" "arith")])
+
+(define_split
+ [(set (match_operand:SI 0 "arith_reg_dest" "")
+ (ashift:SI (match_operand:SI 1 "arith_reg_operand" "")
+ (match_operand:SI 2 "const_int_operand" "")))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1 && reload_completed"
+ [(use (reg:SI R0_REG))]
+ "
+{
+ gen_shifty_op (ASHIFT, operands);
+ DONE;
+}")
+
+(define_insn "ashlsi3_media"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r,r")
+ (ashift:SI (match_operand:SI 1 "extend_reg_operand" "r,r")
+ (match_operand:SI 2 "shift_count_operand" "r,n")))]
+ "TARGET_SHMEDIA"
+ "@
+ shlld.l %1, %2, %0
+ shlli.l %1, %2, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "ignore")])
+
+(define_expand "ashlsi3"
+ [(parallel [(set (match_operand:SI 0 "arith_reg_operand" "")
+ (ashift:SI (match_operand:SI 1 "arith_reg_operand" "")
+ (match_operand:SI 2 "nonmemory_operand" "")))
+ (clobber (reg:SI T_REG))])]
+ ""
+ "
+{
+ if (TARGET_SHMEDIA)
+ {
+ emit_insn (gen_ashlsi3_media (operands[0], operands[1], operands[2]));
+ DONE;
+ }
+ if (CONST_INT_P (operands[2])
+ && sh_dynamicalize_shift_p (operands[2]))
+ operands[2] = force_reg (SImode, operands[2]);
+ if (TARGET_SH3)
+ {
+ emit_insn (gen_ashlsi3_std (operands[0], operands[1], operands[2]));
+ DONE;
+ }
+ if (! immediate_operand (operands[2], GET_MODE (operands[2])))
+ FAIL;
+}")
+
+(define_insn "*ashlhi3_n"
+ [(set (match_operand:HI 0 "arith_reg_dest" "=r")
+ (ashift:HI (match_operand:HI 1 "arith_reg_operand" "0")
+ (match_operand:HI 2 "const_int_operand" "n")))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1"
+ "#"
+ [(set (attr "length")
+ (cond [(eq (symbol_ref "shift_insns_rtx (insn)") (const_int 1))
+ (const_string "2")
+ (eq (symbol_ref "shift_insns_rtx (insn)") (const_int 2))
+ (const_string "4")]
+ (const_string "6")))
+ (set_attr "type" "arith")])
+
+(define_expand "ashlhi3"
+ [(parallel [(set (match_operand:HI 0 "arith_reg_operand" "")
+ (ashift:HI (match_operand:HI 1 "arith_reg_operand" "")
+ (match_operand:SI 2 "nonmemory_operand" "")))
+ (clobber (reg:SI T_REG))])]
+ "TARGET_SH1"
+ "
+{
+ if (!CONST_INT_P (operands[2]))
+ FAIL;
+ /* It may be possible to call gen_ashlhi3 directly with more generic
+ operands. Make sure operands[1] is a HImode register here. */
+ if (!arith_reg_operand (operands[1], HImode))
+ operands[1] = copy_to_mode_reg (HImode, operands[1]);
+}")
+
+(define_split
+ [(set (match_operand:HI 0 "arith_reg_dest" "")
+ (ashift:HI (match_operand:HI 1 "arith_reg_operand" "")
+ (match_operand:HI 2 "const_int_operand" "")))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1 && reload_completed"
+ [(use (reg:SI R0_REG))]
+ "
+{
+ gen_shifty_hi_op (ASHIFT, operands);
+ DONE;
+}")
+
+;
+; arithmetic shift right
+;
+
+(define_insn "ashrsi3_sh2a"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (ashiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (neg:SI (match_operand:SI 2 "arith_reg_operand" "r"))))]
+ "TARGET_SH2A"
+ "shad %2,%0"
+ [(set_attr "type" "dyn_shift")
+ (set_attr "length" "4")])
+
+(define_insn "ashrsi3_k"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (ashiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (match_operand:SI 2 "const_int_operand" "M")))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1 && INTVAL (operands[2]) == 1"
+ "shar %0"
+ [(set_attr "type" "arith")])
+
+;; We can't do HImode right shifts correctly unless we start out with an
+;; explicit zero / sign extension; doing that would result in worse overall
+;; code, so just let the machine independent code widen the mode.
+;; That's why we don't have ashrhi3_k / lshrhi3_k / lshrhi3_m / lshrhi3 .
+
+
+;; ??? This should be a define expand.
+
+(define_insn "ashrsi2_16"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (ashiftrt:SI (match_operand:SI 1 "arith_reg_operand" "r")
+ (const_int 16)))]
+ "TARGET_SH1"
+ "#"
+ [(set_attr "length" "4")])
+
+(define_split
+ [(set (match_operand:SI 0 "arith_reg_dest" "")
+ (ashiftrt:SI (match_operand:SI 1 "arith_reg_operand" "")
+ (const_int 16)))]
+ "TARGET_SH1"
+ [(set (match_dup 0) (rotate:SI (match_dup 1) (const_int 16)))
+ (set (match_dup 0) (sign_extend:SI (match_dup 2)))]
+ "operands[2] = gen_lowpart (HImode, operands[0]);")
+
+;; ??? This should be a define expand.
+
+(define_insn "ashrsi2_31"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (ashiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (const_int 31)))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1"
+ "#"
+ [(set_attr "length" "4")])
+
+(define_split
+ [(set (match_operand:SI 0 "arith_reg_dest" "")
+ (ashiftrt:SI (match_operand:SI 1 "arith_reg_operand" "")
+ (const_int 31)))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1"
+ [(const_int 0)]
+ "
+{
+ emit_insn (gen_ashlsi_c (operands[0], operands[1]));
+ emit_insn (gen_mov_neg_si_t (copy_rtx (operands[0])));
+ DONE;
+}")
+
+(define_peephole2
+ [(set (match_operand:SI 0 "arith_reg_dest" "") (const_int 0))
+ (set (reg:SI T_REG)
+ (gt:SI (match_dup 0) (match_operand:SI 1 "arith_reg_operand" "")))]
+ "TARGET_SH1
+ && peep2_reg_dead_p (2, operands[0])
+ && peep2_reg_dead_p (2, operands[1])"
+ [(const_int 0)]
+ "
+{
+ emit_insn (gen_ashlsi_c (operands[1], operands[1]));
+ DONE;
+}")
+
+(define_insn "ashlsi_c"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (ashift:SI (match_operand:SI 1 "arith_reg_operand" "0") (const_int 1)))
+ (set (reg:SI T_REG)
+ (lt:SI (match_dup 1) (const_int 0)))]
+ "TARGET_SH1"
+ "shll %0"
+ [(set_attr "type" "arith")])
+
+(define_insn "*ashlsi_c_void"
+ [(set (reg:SI T_REG)
+ (lt:SI (match_operand:SI 0 "arith_reg_operand" "r") (const_int 0)))
+ (clobber (match_scratch:SI 1 "=0"))]
+ "TARGET_SH1 && cse_not_expected"
+ "shll %0"
+ [(set_attr "type" "arith")])
+
+(define_insn "ashrsi3_d"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (ashiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (neg:SI (match_operand:SI 2 "arith_reg_operand" "r"))))]
+ "TARGET_SH3"
+ "shad %2,%0"
+ [(set_attr "type" "dyn_shift")])
+
+(define_insn "ashrsi3_n"
+ [(set (reg:SI R4_REG)
+ (ashiftrt:SI (reg:SI R4_REG)
+ (match_operand:SI 0 "const_int_operand" "i")))
+ (clobber (reg:SI T_REG))
+ (clobber (reg:SI PR_REG))
+ (use (match_operand:SI 1 "arith_reg_operand" "r"))]
+ "TARGET_SH1"
+ "jsr @%1%#"
+ [(set_attr "type" "sfunc")
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_insn "ashrsi3_media"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r,r")
+ (ashiftrt:SI (match_operand:SI 1 "extend_reg_operand" "r,r")
+ (match_operand:SI 2 "shift_count_operand" "r,n")))]
+ "TARGET_SHMEDIA"
+ "@
+ shard.l %1, %2, %0
+ shari.l %1, %2, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "ignore")])
+
+(define_expand "ashrsi3"
+ [(parallel [(set (match_operand:SI 0 "arith_reg_operand" "")
+ (ashiftrt:SI (match_operand:SI 1 "arith_reg_operand" "")
+ (match_operand:SI 2 "nonmemory_operand" "")))
+ (clobber (reg:SI T_REG))])]
+ ""
+ "
+{
+ if (TARGET_SHMEDIA)
+ {
+ emit_insn (gen_ashrsi3_media (operands[0], operands[1], operands[2]));
+ DONE;
+ }
+ if (expand_ashiftrt (operands))
+ DONE;
+ else
+ FAIL;
+}")
+
+;; logical shift right
+
+(define_insn "lshrsi3_sh2a"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (neg:SI (match_operand:SI 2 "arith_reg_operand" "r"))))]
+ "TARGET_SH2A"
+ "shld %2,%0"
+ [(set_attr "type" "dyn_shift")
+ (set_attr "length" "4")])
+
+(define_insn "lshrsi3_d"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (neg:SI (match_operand:SI 2 "arith_reg_operand" "r"))))]
+ "TARGET_SH3"
+ "shld %2,%0"
+ [(set_attr "type" "dyn_shift")])
+
+;; Only the single bit shift clobbers the T bit.
+
+(define_insn "lshrsi3_m"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (match_operand:SI 2 "const_int_operand" "M")))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1 && satisfies_constraint_M (operands[2])"
+ "shlr %0"
+ [(set_attr "type" "arith")])
+
+(define_insn "lshrsi3_k"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (match_operand:SI 2 "const_int_operand" "P27")))]
+ "TARGET_SH1 && satisfies_constraint_P27 (operands[2])
+ && ! satisfies_constraint_M (operands[2])"
+ "shlr%O2 %0"
+ [(set_attr "type" "arith")])
+
+(define_insn "lshrsi3_n"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (match_operand:SI 2 "const_int_operand" "n")))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1 && ! sh_dynamicalize_shift_p (operands[2])"
+ "#"
+ [(set (attr "length")
+ (cond [(eq (symbol_ref "shift_insns_rtx (insn)") (const_int 1))
+ (const_string "2")
+ (eq (symbol_ref "shift_insns_rtx (insn)") (const_int 2))
+ (const_string "4")
+ (eq (symbol_ref "shift_insns_rtx (insn)") (const_int 3))
+ (const_string "6")]
+ (const_string "8")))
+ (set_attr "type" "arith")])
+
+(define_split
+ [(set (match_operand:SI 0 "arith_reg_dest" "")
+ (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "")
+ (match_operand:SI 2 "const_int_operand" "")))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1 && reload_completed"
+ [(use (reg:SI R0_REG))]
+ "
+{
+ gen_shifty_op (LSHIFTRT, operands);
+ DONE;
+}")
+
+(define_insn "lshrsi3_media"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r,r")
+ (lshiftrt:SI (match_operand:SI 1 "extend_reg_operand" "r,r")
+ (match_operand:SI 2 "shift_count_operand" "r,n")))]
+ "TARGET_SHMEDIA"
+ "@
+ shlrd.l %1, %2, %0
+ shlri.l %1, %2, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "ignore")])
+
+(define_expand "lshrsi3"
+ [(parallel [(set (match_operand:SI 0 "arith_reg_dest" "")
+ (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "")
+ (match_operand:SI 2 "nonmemory_operand" "")))
+ (clobber (reg:SI T_REG))])]
+ ""
+ "
+{
+ if (TARGET_SHMEDIA)
+ {
+ emit_insn (gen_lshrsi3_media (operands[0], operands[1], operands[2]));
+ DONE;
+ }
+ if (CONST_INT_P (operands[2])
+ && sh_dynamicalize_shift_p (operands[2]))
+ operands[2] = force_reg (SImode, operands[2]);
+ if (TARGET_SH3 && arith_reg_operand (operands[2], GET_MODE (operands[2])))
+ {
+ rtx count = copy_to_mode_reg (SImode, operands[2]);
+ emit_insn (gen_negsi2 (count, count));
+ emit_insn (gen_lshrsi3_d (operands[0], operands[1], count));
+ DONE;
+ }
+ if (! immediate_operand (operands[2], GET_MODE (operands[2])))
+ FAIL;
+}")
+
+;; ??? This should be a define expand.
+
+(define_insn "ashldi3_k"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (ashift:DI (match_operand:DI 1 "arith_reg_operand" "0")
+ (const_int 1)))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1"
+ "shll %R0\;rotcl %S0"
+ [(set_attr "length" "4")
+ (set_attr "type" "arith")])
+
+;; Expander for DImode shift left with SImode operations.
+
+(define_expand "ashldi3_std"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (ashift:DI (match_operand:DI 1 "arith_reg_operand" "r")
+ (match_operand:DI 2 "const_int_operand" "n")))]
+ "TARGET_SH1 && INTVAL (operands[2]) < 32"
+ "
+{
+ int low_word = (TARGET_LITTLE_ENDIAN ? 0 : 1);
+ int high_word = (TARGET_LITTLE_ENDIAN ? 1 : 0);
+ rtx low_src = operand_subword (operands[1], low_word, 0, DImode);
+ rtx high_src = operand_subword (operands[1], high_word, 0, DImode);
+ rtx dst = gen_reg_rtx (DImode);
+ rtx low_dst = operand_subword (dst, low_word, 1, DImode);
+ rtx high_dst = operand_subword (dst, high_word, 1, DImode);
+ rtx tmp0, tmp1;
+
+ tmp0 = gen_reg_rtx (SImode);
+ tmp1 = gen_reg_rtx (SImode);
+ emit_insn (gen_lshrsi3 (tmp0, low_src, GEN_INT (32 - INTVAL (operands[2]))));
+ emit_insn (gen_ashlsi3 (low_dst, low_src, operands[2]));
+ emit_insn (gen_ashlsi3 (tmp1, high_src, operands[2]));
+ emit_insn (gen_iorsi3 (high_dst, tmp0, tmp1));
+ emit_move_insn (operands[0], dst);
+ DONE;
+}")
+
+(define_insn "ashldi3_media"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r,r")
+ (ashift:DI (match_operand:DI 1 "arith_reg_operand" "r,r")
+ (match_operand:DI 2 "shift_count_operand" "r,n")))]
+ "TARGET_SHMEDIA"
+ "@
+ shlld %1, %2, %0
+ shlli %1, %2, %0"
+ [(set_attr "type" "arith_media")])
+
+(define_insn "*ashldisi3_media"
+ [(set (subreg:DI (match_operand:SI 0 "arith_reg_operand" "=r") 0)
+ (ashift:DI (match_operand:DI 1 "arith_reg_operand" "r")
+ (match_operand:DI 2 "const_int_operand" "n")))]
+ "TARGET_SHMEDIA && INTVAL (operands[2]) < 32"
+ "shlli.l %1, %2, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "ignore")])
+
+(define_expand "ashldi3"
+ [(parallel [(set (match_operand:DI 0 "arith_reg_operand" "")
+ (ashift:DI (match_operand:DI 1 "arith_reg_operand" "")
+ (match_operand:DI 2 "immediate_operand" "")))
+ (clobber (reg:SI T_REG))])]
+ ""
+ "
+{
+ if (TARGET_SHMEDIA)
+ {
+ emit_insn (gen_ashldi3_media (operands[0], operands[1], operands[2]));
+ DONE;
+ }
+ if (CONST_INT_P (operands[2])
+ && INTVAL (operands[2]) == 1)
+ {
+ emit_insn (gen_ashldi3_k (operands[0], operands[1]));
+ DONE;
+ }
+ else if (CONST_INT_P (operands[2])
+ && INTVAL (operands[2]) < 32)
+ {
+ emit_insn (gen_ashldi3_std (operands[0], operands[1], operands[2]));
+ DONE;
+ }
+ else
+ FAIL;
+}")
+
+;; ??? This should be a define expand.
+
+(define_insn "lshrdi3_k"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (lshiftrt:DI (match_operand:DI 1 "arith_reg_operand" "0")
+ (const_int 1)))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1"
+ "shlr %S0\;rotcr %R0"
+ [(set_attr "length" "4")
+ (set_attr "type" "arith")])
+
+(define_insn "lshrdi3_media"
+ [(set (match_operand:DI 0 "ext_dest_operand" "=r,r")
+ (lshiftrt:DI (match_operand:DI 1 "arith_reg_operand" "r,r")
+ (match_operand:DI 2 "shift_count_operand" "r,n")))]
+ "TARGET_SHMEDIA
+ && (arith_reg_dest (operands[0], DImode)
+ || (CONST_INT_P (operands[2]) && INTVAL (operands[2]) > 32))"
+ "@
+ shlrd %1, %2, %0
+ shlri %1, %2, %0"
+ [(set_attr "type" "arith_media")])
+
+(define_insn "*lshrdisi3_media"
+ [(set (subreg:DI (match_operand:SI 0 "arith_reg_operand" "=r") 0)
+ (lshiftrt:DI (match_operand:DI 1 "arith_reg_operand" "r")
+ (match_operand:DI 2 "const_int_operand" "n")))]
+ "TARGET_SHMEDIA && INTVAL (operands[2]) < 32"
+ "shlri.l %1, %2, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "ignore")])
+
+(define_expand "lshrdi3"
+ [(parallel [(set (match_operand:DI 0 "arith_reg_operand" "")
+ (lshiftrt:DI (match_operand:DI 1 "arith_reg_operand" "")
+ (match_operand:DI 2 "immediate_operand" "")))
+ (clobber (reg:SI T_REG))])]
+ ""
+ "
+{
+ if (TARGET_SHMEDIA)
+ {
+ emit_insn (gen_lshrdi3_media (operands[0], operands[1], operands[2]));
+ DONE;
+ }
+ if (!CONST_INT_P (operands[2])
+ || INTVAL (operands[2]) != 1)
+ FAIL;
+}")
+
+;; ??? This should be a define expand.
+
+(define_insn "ashrdi3_k"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (ashiftrt:DI (match_operand:DI 1 "arith_reg_operand" "0")
+ (const_int 1)))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1"
+ "shar %S0\;rotcr %R0"
+ [(set_attr "length" "4")
+ (set_attr "type" "arith")])
+
+(define_insn "ashrdi3_media"
+ [(set (match_operand:DI 0 "ext_dest_operand" "=r,r")
+ (ashiftrt:DI (match_operand:DI 1 "arith_reg_operand" "r,r")
+ (match_operand:DI 2 "shift_count_operand" "r,n")))]
+ "TARGET_SHMEDIA
+ && (arith_reg_dest (operands[0], DImode)
+ || (CONST_INT_P (operands[2]) && INTVAL (operands[2]) >= 32))"
+ "@
+ shard %1, %2, %0
+ shari %1, %2, %0"
+ [(set_attr "type" "arith_media")])
+
+(define_insn "*ashrdisi3_media"
+ [(set (subreg:DI (match_operand:SI 0 "arith_reg_operand" "=r") 0)
+ (ashiftrt:DI (match_operand:DI 1 "arith_reg_operand" "r")
+ (match_operand:DI 2 "const_int_operand" "n")))]
+ "TARGET_SHMEDIA && INTVAL (operands[2]) < 32"
+ "shari.l %1, %2, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "ignore")])
+
+(define_insn "ashrdisi3_media_high"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (truncate:SI
+ (ashiftrt:DI (match_operand:DI 1 "arith_reg_operand" "r")
+ (match_operand:DI 2 "const_int_operand" "n"))))]
+ "TARGET_SHMEDIA && INTVAL (operands[2]) >= 32"
+ "shari %1, %2, %0"
+ [(set_attr "type" "arith_media")])
+
+(define_insn "ashrdisi3_media_opaque"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (unspec:SI [(match_operand:DI 1 "arith_reg_operand" "r")
+ (match_operand:DI 2 "const_int_operand" "n")]
+ UNSPEC_ASHIFTRT))]
+ "TARGET_SHMEDIA"
+ "shari %1, %2, %0"
+ [(set_attr "type" "arith_media")])
+
+(define_expand "ashrdi3"
+ [(parallel [(set (match_operand:DI 0 "arith_reg_operand" "")
+ (ashiftrt:DI (match_operand:DI 1 "arith_reg_operand" "")
+ (match_operand:DI 2 "immediate_operand" "")))
+ (clobber (reg:SI T_REG))])]
+ ""
+ "
+{
+ if (TARGET_SHMEDIA)
+ {
+ emit_insn (gen_ashrdi3_media (operands[0], operands[1], operands[2]));
+ DONE;
+ }
+ if (!CONST_INT_P (operands[2])
+ || INTVAL (operands[2]) != 1)
+ FAIL;
+}")
+
+;; combined left/right shift
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (and:SI (ashift:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "const_int_operand" ""))
+ (match_operand:SI 3 "const_int_operand" "")))]
+ "TARGET_SH1 && reload_completed && (unsigned)INTVAL (operands[2]) < 32"
+ [(use (reg:SI R0_REG))]
+ "if (gen_shl_and (operands[0], operands[2], operands[3], operands[1])) FAIL;
+ DONE;")
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (and:SI (ashift:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "const_int_operand" ""))
+ (match_operand:SI 3 "const_int_operand" "")))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1 && reload_completed && (unsigned)INTVAL (operands[2]) < 32"
+ [(use (reg:SI R0_REG))]
+ "if (gen_shl_and (operands[0], operands[2], operands[3], operands[1])) FAIL;
+ DONE;")
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (and:SI (ashift:SI (match_operand:SI 1 "register_operand" "0")
+ (match_operand:SI 2 "const_int_operand" "n"))
+ (match_operand:SI 3 "const_int_operand" "n")))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1 && shl_and_kind (operands[2], operands[3], 0) == 1"
+ "#"
+ [(set (attr "length")
+ (cond [(eq (symbol_ref "shl_and_length (insn)") (const_int 2))
+ (const_string "4")
+ (eq (symbol_ref "shl_and_length (insn)") (const_int 3))
+ (const_string "6")
+ (eq (symbol_ref "shl_and_length (insn)") (const_int 4))
+ (const_string "8")
+ (eq (symbol_ref "shl_and_length (insn)") (const_int 5))
+ (const_string "10")
+ (eq (symbol_ref "shl_and_length (insn)") (const_int 6))
+ (const_string "12")
+ (eq (symbol_ref "shl_and_length (insn)") (const_int 7))
+ (const_string "14")
+ (eq (symbol_ref "shl_and_length (insn)") (const_int 8))
+ (const_string "16")]
+ (const_string "18")))
+ (set_attr "type" "arith")])
+
+(define_insn ""
+ [(set (match_operand:SI 0 "register_operand" "=z")
+ (and:SI (ashift:SI (match_operand:SI 1 "register_operand" "0")
+ (match_operand:SI 2 "const_int_operand" "n"))
+ (match_operand:SI 3 "const_int_operand" "n")))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1 && shl_and_kind (operands[2], operands[3], 0) == 2"
+ "#"
+ [(set (attr "length")
+ (cond [(eq (symbol_ref "shl_and_length (insn)") (const_int 2))
+ (const_string "4")
+ (eq (symbol_ref "shl_and_length (insn)") (const_int 3))
+ (const_string "6")
+ (eq (symbol_ref "shl_and_length (insn)") (const_int 4))
+ (const_string "8")]
+ (const_string "10")))
+ (set_attr "type" "arith")])
+
+;; shift left / and combination with a scratch register: The combine pass
+;; does not accept the individual instructions, even though they are
+;; cheap. But it needs a precise description so that it is usable after
+;; reload.
+(define_insn "and_shl_scratch"
+ [(set (match_operand:SI 0 "register_operand" "=r,&r")
+ (lshiftrt:SI
+ (ashift:SI
+ (and:SI
+ (lshiftrt:SI (match_operand:SI 1 "register_operand" "r,0")
+ (match_operand:SI 2 "const_int_operand" "N,n"))
+ (match_operand:SI 3 "" "0,r"))
+ (match_operand:SI 4 "const_int_operand" "n,n"))
+ (match_operand:SI 5 "const_int_operand" "n,n")))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1"
+ "#"
+ [(set (attr "length")
+ (cond [(eq (symbol_ref "shl_and_scr_length (insn)") (const_int 2))
+ (const_string "4")
+ (eq (symbol_ref "shl_and_scr_length (insn)") (const_int 3))
+ (const_string "6")
+ (eq (symbol_ref "shl_and_scr_length (insn)") (const_int 4))
+ (const_string "8")
+ (eq (symbol_ref "shl_and_scr_length (insn)") (const_int 5))
+ (const_string "10")]
+ (const_string "12")))
+ (set_attr "type" "arith")])
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (lshiftrt:SI
+ (ashift:SI
+ (and:SI
+ (lshiftrt:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "const_int_operand" ""))
+ (match_operand:SI 3 "register_operand" ""))
+ (match_operand:SI 4 "const_int_operand" ""))
+ (match_operand:SI 5 "const_int_operand" "")))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1"
+ [(use (reg:SI R0_REG))]
+ "
+{
+ rtx and_source = operands[rtx_equal_p (operands[0], operands[1]) ? 3 : 1];
+
+ if (INTVAL (operands[2]))
+ {
+ gen_shifty_op (LSHIFTRT, operands);
+ }
+ emit_insn (gen_andsi3 (operands[0], operands[0], and_source));
+ operands[2] = operands[4];
+ gen_shifty_op (ASHIFT, operands);
+ if (INTVAL (operands[5]))
+ {
+ operands[2] = operands[5];
+ gen_shifty_op (LSHIFTRT, operands);
+ }
+ DONE;
+}")
+
+;; signed left/right shift combination.
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (sign_extract:SI
+ (ashift:SI (match_operand:SI 1 "register_operand" "")
+ (match_operand:SI 2 "const_int_operand" ""))
+ (match_operand:SI 3 "const_int_operand" "")
+ (const_int 0)))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1"
+ [(use (reg:SI R0_REG))]
+ "if (gen_shl_sext (operands[0], operands[2], operands[3], operands[1])) FAIL;
+ DONE;")
+
+(define_insn "shl_sext_ext"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (sign_extract:SI
+ (ashift:SI (match_operand:SI 1 "register_operand" "0")
+ (match_operand:SI 2 "const_int_operand" "n"))
+ (match_operand:SI 3 "const_int_operand" "n")
+ (const_int 0)))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1 && (unsigned)shl_sext_kind (operands[2], operands[3], 0) - 1 < 5"
+ "#"
+ [(set (attr "length")
+ (cond [(eq (symbol_ref "shl_sext_length (insn)") (const_int 1))
+ (const_string "2")
+ (eq (symbol_ref "shl_sext_length (insn)") (const_int 2))
+ (const_string "4")
+ (eq (symbol_ref "shl_sext_length (insn)") (const_int 3))
+ (const_string "6")
+ (eq (symbol_ref "shl_sext_length (insn)") (const_int 4))
+ (const_string "8")
+ (eq (symbol_ref "shl_sext_length (insn)") (const_int 5))
+ (const_string "10")
+ (eq (symbol_ref "shl_sext_length (insn)") (const_int 6))
+ (const_string "12")
+ (eq (symbol_ref "shl_sext_length (insn)") (const_int 7))
+ (const_string "14")
+ (eq (symbol_ref "shl_sext_length (insn)") (const_int 8))
+ (const_string "16")]
+ (const_string "18")))
+ (set_attr "type" "arith")])
+
+(define_insn "shl_sext_sub"
+ [(set (match_operand:SI 0 "register_operand" "=z")
+ (sign_extract:SI
+ (ashift:SI (match_operand:SI 1 "register_operand" "0")
+ (match_operand:SI 2 "const_int_operand" "n"))
+ (match_operand:SI 3 "const_int_operand" "n")
+ (const_int 0)))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1 && (shl_sext_kind (operands[2], operands[3], 0) & ~1) == 6"
+ "#"
+ [(set (attr "length")
+ (cond [(eq (symbol_ref "shl_sext_length (insn)") (const_int 3))
+ (const_string "6")
+ (eq (symbol_ref "shl_sext_length (insn)") (const_int 4))
+ (const_string "8")
+ (eq (symbol_ref "shl_sext_length (insn)") (const_int 5))
+ (const_string "10")
+ (eq (symbol_ref "shl_sext_length (insn)") (const_int 6))
+ (const_string "12")]
+ (const_string "14")))
+ (set_attr "type" "arith")])
+
+;; These patterns are found in expansions of DImode shifts by 16, and
+;; allow the xtrct instruction to be generated from C source.
+
+(define_insn "xtrct_left"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (ior:SI (ashift:SI (match_operand:SI 1 "arith_reg_operand" "r")
+ (const_int 16))
+ (lshiftrt:SI (match_operand:SI 2 "arith_reg_operand" "0")
+ (const_int 16))))]
+ "TARGET_SH1"
+ "xtrct %1,%0"
+ [(set_attr "type" "arith")])
+
+(define_insn "xtrct_right"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (ior:SI (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (const_int 16))
+ (ashift:SI (match_operand:SI 2 "arith_reg_operand" "r")
+ (const_int 16))))]
+ "TARGET_SH1"
+ "xtrct %2,%0"
+ [(set_attr "type" "arith")])
+
+;; -------------------------------------------------------------------------
+;; Unary arithmetic
+;; -------------------------------------------------------------------------
+
+(define_insn "negc"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (neg:SI (plus:SI (reg:SI T_REG)
+ (match_operand:SI 1 "arith_reg_operand" "r"))))
+ (set (reg:SI T_REG)
+ (ne:SI (ior:SI (reg:SI T_REG) (match_dup 1))
+ (const_int 0)))]
+ "TARGET_SH1"
+ "negc %1,%0"
+ [(set_attr "type" "arith")])
+
+(define_insn "*negdi_media"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (neg:DI (match_operand:DI 1 "arith_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "sub r63, %1, %0"
+ [(set_attr "type" "arith_media")])
+
+(define_expand "negdi2"
+ [(set (match_operand:DI 0 "arith_reg_operand" "")
+ (neg:DI (match_operand:DI 1 "arith_reg_operand" "")))]
+ ""
+ "
+{
+ if (TARGET_SH1)
+ {
+ int low_word = (TARGET_LITTLE_ENDIAN ? 0 : 1);
+ int high_word = (TARGET_LITTLE_ENDIAN ? 1 : 0);
+
+ rtx low_src = operand_subword (operands[1], low_word, 0, DImode);
+ rtx high_src = operand_subword (operands[1], high_word, 0, DImode);
+
+ rtx low_dst = operand_subword (operands[0], low_word, 1, DImode);
+ rtx high_dst = operand_subword (operands[0], high_word, 1, DImode);
+
+ emit_insn (gen_clrt ());
+ emit_insn (gen_negc (low_dst, low_src));
+ emit_insn (gen_negc (high_dst, high_src));
+ DONE;
+ }
+}")
+
+(define_insn "negsi2"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (neg:SI (match_operand:SI 1 "arith_reg_operand" "r")))]
+ "TARGET_SH1"
+ "neg %1,%0"
+ [(set_attr "type" "arith")])
+
+(define_insn "one_cmplsi2"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (not:SI (match_operand:SI 1 "arith_reg_operand" "r")))]
+ "TARGET_SH1"
+ "not %1,%0"
+ [(set_attr "type" "arith")])
+
+(define_expand "one_cmpldi2"
+ [(set (match_operand:DI 0 "arith_reg_dest" "")
+ (xor:DI (match_operand:DI 1 "arith_reg_operand" "")
+ (const_int -1)))]
+ "TARGET_SHMEDIA" "")
+
+/* The SH4 202 can do zero-offset branches without pipeline stalls.
+ This can be used as some kind of conditional execution, which is useful
+ for abs. */
+(define_split
+ [(set (match_operand:SI 0 "arith_reg_dest" "")
+ (plus:SI (xor:SI (neg:SI (reg:SI T_REG))
+ (match_operand:SI 1 "arith_reg_operand" ""))
+ (reg:SI T_REG)))]
+ "TARGET_HARD_SH4"
+ [(const_int 0)]
+ "emit_insn (gen_movsi_i (operands[0], operands[1]));
+ emit_insn (gen_cneg (operands[0], operands[0], operands[0]));
+ DONE;")
+
+(define_insn "cneg"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (if_then_else:SI (eq:SI (reg:SI T_REG) (const_int 0))
+ (match_operand:SI 1 "arith_reg_operand" "0")
+ (neg:SI (match_operand:SI 2 "arith_reg_operand" "r"))))]
+ "TARGET_HARD_SH4"
+ "bf 0f\;neg %2,%0\\n0:"
+ [(set_attr "type" "arith") ;; poor approximation
+ (set_attr "length" "4")])
+
+
+;; -------------------------------------------------------------------------
+;; Zero extension instructions
+;; -------------------------------------------------------------------------
+
+(define_insn "zero_extendsidi2"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (zero_extend:DI (match_operand:SI 1 "extend_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "addz.l %1, r63, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "extend")])
+
+(define_insn "zero_extendhidi2"
+ [(set (match_operand:DI 0 "register_operand" "=r,r")
+ (zero_extend:DI (match_operand:HI 1 "general_extend_operand" "r,m")))]
+ "TARGET_SHMEDIA"
+ "@
+ #
+ ld%M1.uw %m1, %0"
+ [(set_attr "type" "*,load_media")
+ (set (attr "highpart")
+ (cond [(ne (symbol_ref "sh_contains_memref_p (insn)") (const_int 0))
+ (const_string "user")]
+ (const_string "ignore")))])
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (zero_extend:DI (match_operand:HI 1 "extend_reg_operand" "")))]
+ "TARGET_SHMEDIA && reload_completed"
+ [(set (match_dup 0) (ashift:DI (subreg:DI (match_dup 1) 0) (const_int 48)))
+ (set (match_dup 0) (lshiftrt:DI (match_dup 0) (const_int 48)))]
+ "
+{
+ if (GET_CODE (operands[1]) == TRUNCATE)
+ operands[1] = XEXP (operands[1], 0);
+}")
+
+;; ??? when a truncated input to a zero_extend is reloaded, reload will
+;; reload the entire truncate expression.
+(define_insn_and_split "*loaddi_trunc"
+ [(set (match_operand 0 "any_register_operand" "=r")
+ (truncate (match_operand:DI 1 "memory_operand" "m")))]
+ "TARGET_SHMEDIA && reload_completed"
+ "#"
+ "TARGET_SHMEDIA && reload_completed"
+ [(set (match_dup 0) (match_dup 1))]
+ "operands[0] = gen_rtx_REG (DImode, true_regnum (operands[0]));")
+
+(define_insn "zero_extendqidi2"
+ [(set (match_operand:DI 0 "register_operand" "=r,r")
+ (zero_extend:DI (match_operand:QI 1 "general_extend_operand" "r,m")))]
+ "TARGET_SHMEDIA"
+ "@
+ andi %1, 255, %0
+ ld%M1.ub %m1, %0"
+ [(set_attr "type" "arith_media,load_media")
+ (set (attr "highpart")
+ (cond [(ne (symbol_ref "sh_contains_memref_p (insn)") (const_int 0))
+ (const_string "user")]
+ (const_string "ignore")))])
+
+(define_expand "zero_extendhisi2"
+ [(set (match_operand:SI 0 "arith_reg_operand" "")
+ (zero_extend:SI (match_operand:HI 1 "general_extend_operand" "")))]
+ ""
+ "
+{
+ if (! TARGET_SHMEDIA && ! arith_reg_operand (operands[1], HImode))
+ operands[1] = copy_to_mode_reg (HImode, operands[1]);
+}")
+
+(define_insn "*zero_extendhisi2_compact"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (zero_extend:SI (match_operand:HI 1 "arith_reg_operand" "r")))]
+ "TARGET_SH1"
+ "extu.w %1,%0"
+ [(set_attr "type" "arith")])
+
+(define_insn "*zero_extendhisi2_media"
+ [(set (match_operand:SI 0 "register_operand" "=r,r")
+ (zero_extend:SI (match_operand:HI 1 "general_extend_operand" "r,m")))]
+ "TARGET_SHMEDIA"
+ "@
+ #
+ ld%M1.uw %m1, %0"
+ [(set_attr "type" "arith_media,load_media")
+ (set (attr "highpart")
+ (cond [(ne (symbol_ref "sh_contains_memref_p (insn)") (const_int 0))
+ (const_string "user")]
+ (const_string "ignore")))])
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (zero_extend:SI (match_operand:HI 1 "extend_reg_operand" "")))]
+ "TARGET_SHMEDIA && reload_completed"
+ [(set (match_dup 0) (ashift:SI (match_dup 2) (const_int 16)))
+ (set (match_dup 0) (lshiftrt:SI (match_dup 0) (const_int 16)))]
+ "
+{
+ rtx op1 = operands[1];
+
+ if (GET_CODE (op1) == TRUNCATE)
+ op1 = XEXP (op1, 0);
+ operands[2]
+ = simplify_gen_subreg (SImode, op1, GET_MODE (op1),
+ subreg_lowpart_offset (SImode, GET_MODE (op1)));
+}")
+
+(define_expand "zero_extendqisi2"
+ [(set (match_operand:SI 0 "arith_reg_operand" "")
+ (zero_extend:SI (match_operand:QI 1 "general_extend_operand" "")))]
+ ""
+ "
+{
+ if (! TARGET_SHMEDIA && ! arith_reg_operand (operands[1], QImode))
+ operands[1] = copy_to_mode_reg (QImode, operands[1]);
+}")
+
+(define_insn "*zero_extendqisi2_compact"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (zero_extend:SI (match_operand:QI 1 "arith_reg_operand" "r")))]
+ "TARGET_SH1"
+ "extu.b %1,%0"
+ [(set_attr "type" "arith")])
+
+(define_insn "*zero_extendqisi2_media"
+ [(set (match_operand:SI 0 "register_operand" "=r,r")
+ (zero_extend:SI (match_operand:QI 1 "general_extend_operand" "r,m")))]
+ "TARGET_SHMEDIA"
+ "@
+ andi %1, 255, %0
+ ld%M1.ub %m1, %0"
+ [(set_attr "type" "arith_media,load_media")
+ (set (attr "highpart")
+ (cond [(ne (symbol_ref "sh_contains_memref_p (insn)") (const_int 0))
+ (const_string "user")]
+ (const_string "ignore")))])
+
+(define_insn "zero_extendqihi2"
+ [(set (match_operand:HI 0 "arith_reg_dest" "=r")
+ (zero_extend:HI (match_operand:QI 1 "arith_reg_operand" "r")))]
+ "TARGET_SH1"
+ "extu.b %1,%0"
+ [(set_attr "type" "arith")])
+
+;; -------------------------------------------------------------------------
+;; Sign extension instructions
+;; -------------------------------------------------------------------------
+
+;; ??? This should be a define expand.
+;; ??? Or perhaps it should be dropped?
+
+;; convert_move generates good code for SH[1-4].
+(define_insn "extendsidi2"
+ [(set (match_operand:DI 0 "register_operand" "=r,r,r")
+ (sign_extend:DI (match_operand:SI 1 "nonimmediate_operand" "r,m,?f")))]
+ "TARGET_SHMEDIA"
+ "@
+ add.l %1, r63, %0
+ ld%M1.l %m1, %0
+ fmov.sl %1, %0"
+ [(set_attr "type" "arith_media,load_media,fpconv_media")
+ (set (attr "highpart")
+ (cond [(ne (symbol_ref "sh_contains_memref_p (insn)") (const_int 0))
+ (const_string "user")]
+ (const_string "extend")))])
+
+(define_insn "extendhidi2"
+ [(set (match_operand:DI 0 "register_operand" "=r,r")
+ (sign_extend:DI (match_operand:HI 1 "general_extend_operand" "r,m")))]
+ "TARGET_SHMEDIA"
+ "@
+ #
+ ld%M1.w %m1, %0"
+ [(set_attr "type" "*,load_media")
+ (set (attr "highpart")
+ (cond [(ne (symbol_ref "sh_contains_memref_p (insn)") (const_int 0))
+ (const_string "user")]
+ (const_string "ignore")))])
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (sign_extend:DI (match_operand:HI 1 "extend_reg_operand" "")))]
+ "TARGET_SHMEDIA && reload_completed"
+ [(set (match_dup 0) (ashift:DI (subreg:DI (match_dup 1) 0) (const_int 48)))
+ (set (match_dup 0) (ashiftrt:DI (match_dup 0) (const_int 48)))]
+ "
+{
+ if (GET_CODE (operands[1]) == TRUNCATE)
+ operands[1] = XEXP (operands[1], 0);
+}")
+
+(define_insn "extendqidi2"
+ [(set (match_operand:DI 0 "register_operand" "=r,r")
+ (sign_extend:DI (match_operand:QI 1 "general_extend_operand" "r,m")))]
+ "TARGET_SHMEDIA"
+ "@
+ #
+ ld%M1.b %m1, %0"
+ [(set_attr "type" "*,load_media")
+ (set (attr "highpart")
+ (cond [(ne (symbol_ref "sh_contains_memref_p (insn)") (const_int 0))
+ (const_string "user")]
+ (const_string "ignore")))])
+
+(define_split
+ [(set (match_operand:DI 0 "register_operand" "")
+ (sign_extend:DI (match_operand:QI 1 "extend_reg_operand" "")))]
+ "TARGET_SHMEDIA && reload_completed"
+ [(set (match_dup 0) (ashift:DI (subreg:DI (match_dup 1) 0) (const_int 56)))
+ (set (match_dup 0) (ashiftrt:DI (match_dup 0) (const_int 56)))]
+ "
+{
+ if (GET_CODE (operands[1]) == TRUNCATE)
+ operands[1] = XEXP (operands[1], 0);
+}")
+
+(define_expand "extendhisi2"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r,r")
+ (sign_extend:SI (match_operand:HI 1 "general_extend_operand" "r,m")))]
+ ""
+ "")
+
+(define_insn "*extendhisi2_compact"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r,r")
+ (sign_extend:SI (match_operand:HI 1 "general_movsrc_operand" "r,m")))]
+ "TARGET_SH1"
+ "@
+ exts.w %1,%0
+ mov.w %1,%0"
+ [(set_attr "type" "arith,load")])
+
+(define_insn "*extendhisi2_media"
+ [(set (match_operand:SI 0 "register_operand" "=r,r")
+ (sign_extend:SI (match_operand:HI 1 "general_extend_operand" "r,m")))]
+ "TARGET_SHMEDIA"
+ "@
+ #
+ ld%M1.w %m1, %0"
+ [(set_attr "type" "arith_media,load_media")
+ (set (attr "highpart")
+ (cond [(ne (symbol_ref "sh_contains_memref_p (insn)") (const_int 0))
+ (const_string "user")]
+ (const_string "ignore")))])
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (sign_extend:SI (match_operand:HI 1 "extend_reg_operand" "")))]
+ "TARGET_SHMEDIA && reload_completed"
+ [(set (match_dup 0) (ashift:SI (match_dup 2) (const_int 16)))
+ (set (match_dup 0) (ashiftrt:SI (match_dup 0) (const_int 16)))]
+ "
+{
+ rtx op1 = operands[1];
+ if (GET_CODE (op1) == TRUNCATE)
+ op1 = XEXP (op1, 0);
+ operands[2]
+ = simplify_gen_subreg (SImode, op1, GET_MODE (op1),
+ subreg_lowpart_offset (SImode, GET_MODE (op1)));
+}")
+
+(define_expand "extendqisi2"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r,r")
+ (sign_extend:SI (match_operand:QI 1 "general_extend_operand" "r,m")))]
+ ""
+ "")
+
+(define_insn "*extendqisi2_compact"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r,r")
+ (sign_extend:SI (match_operand:QI 1 "general_movsrc_operand" "r,m")))]
+ "TARGET_SH1"
+ "@
+ exts.b %1,%0
+ mov.b %1,%0"
+ [(set_attr "type" "arith,load")
+ (set_attr_alternative "length"
+ [(const_int 2)
+ (if_then_else
+ (ne (symbol_ref "TARGET_SH2A") (const_int 0))
+ (const_int 4) (const_int 2))])])
+
+(define_insn "*extendqisi2_media"
+ [(set (match_operand:SI 0 "register_operand" "=r,r")
+ (sign_extend:SI (match_operand:QI 1 "general_extend_operand" "r,m")))]
+ "TARGET_SHMEDIA"
+ "@
+ #
+ ld%M1.b %m1, %0"
+ [(set_attr "type" "arith_media,load_media")
+ (set (attr "highpart")
+ (cond [(ne (symbol_ref "sh_contains_memref_p (insn)") (const_int 0))
+ (const_string "user")]
+ (const_string "ignore")))])
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (sign_extend:SI (match_operand:QI 1 "extend_reg_operand" "")))]
+ "TARGET_SHMEDIA && reload_completed"
+ [(set (match_dup 0) (ashift:SI (match_dup 2) (const_int 24)))
+ (set (match_dup 0) (ashiftrt:SI (match_dup 0) (const_int 24)))]
+ "
+{
+ rtx op1 = operands[1];
+ if (GET_CODE (op1) == TRUNCATE)
+ op1 = XEXP (op1, 0);
+ operands[2]
+ = simplify_gen_subreg (SImode, op1, GET_MODE (op1),
+ subreg_lowpart_offset (SImode, GET_MODE (op1)));
+}")
+
+(define_insn "extendqihi2"
+ [(set (match_operand:HI 0 "arith_reg_dest" "=r,r")
+ (sign_extend:HI (match_operand:QI 1 "general_movsrc_operand" "r,m")))]
+ "TARGET_SH1"
+ "@
+ exts.b %1,%0
+ mov.b %1,%0"
+ [(set_attr "type" "arith,load")
+ (set_attr_alternative "length"
+ [(const_int 2)
+ (if_then_else
+ (ne (symbol_ref "TARGET_SH2A") (const_int 0))
+ (const_int 4) (const_int 2))])])
+
+/* It would seem useful to combine the truncXi patterns into the movXi
+ patterns, but unary operators are ignored when matching constraints,
+ so we need separate patterns. */
+(define_insn "truncdisi2"
+ [(set (match_operand:SI 0 "general_movdst_operand" "=r,m,m,f,r,f")
+ (truncate:SI (match_operand:DI 1 "register_operand" "r,r,f,r,f,f")))]
+ "TARGET_SHMEDIA"
+ "@
+ add.l %1, r63, %0
+ st%M0.l %m0, %1
+ fst%M0.s %m0, %T1
+ fmov.ls %1, %0
+ fmov.sl %T1, %0
+ fmov.s %T1, %0"
+ [(set_attr "type" "arith_media,store_media,fstore_media,fload_media,fpconv_media,fmove_media")
+ (set (attr "highpart")
+ (cond [(ne (symbol_ref "sh_contains_memref_p (insn)") (const_int 0))
+ (const_string "user")]
+ (const_string "extend")))])
+
+(define_insn "truncdihi2"
+ [(set (match_operand:HI 0 "general_movdst_operand" "=?r,m")
+ (truncate:HI (match_operand:DI 1 "register_operand" "r,r")))]
+ "TARGET_SHMEDIA"
+ "@
+ shlli\\t%1,48,%0\;shlri\\t%0,48,%0
+ st%M0.w %m0, %1"
+ [(set_attr "type" "arith_media,store_media")
+ (set_attr "length" "8,4")
+ (set (attr "highpart")
+ (cond [(ne (symbol_ref "sh_contains_memref_p (insn)") (const_int 0))
+ (const_string "user")]
+ (const_string "extend")))])
+
+; N.B. This should agree with LOAD_EXTEND_OP and movqi.
+; Because we use zero extension, we can't provide signed QImode compares
+; using a simple compare or conditional branch insn.
+(define_insn "truncdiqi2"
+ [(set (match_operand:QI 0 "general_movdst_operand" "=r,m")
+ (truncate:QI (match_operand:DI 1 "register_operand" "r,r")))]
+ "TARGET_SHMEDIA"
+ "@
+ andi %1, 255, %0
+ st%M0.b %m0, %1"
+ [(set_attr "type" "arith_media,store")
+ (set (attr "highpart")
+ (cond [(ne (symbol_ref "sh_contains_memref_p (insn)") (const_int 0))
+ (const_string "user")]
+ (const_string "extend")))])
+;; -------------------------------------------------------------------------
+;; Move instructions
+;; -------------------------------------------------------------------------
+
+;; define push and pop so it is easy for sh.c
+;; We can't use push and pop on SHcompact because the stack must always
+;; be 8-byte aligned.
+
+(define_expand "push"
+ [(set (mem:SI (pre_dec:SI (reg:SI SP_REG)))
+ (match_operand:SI 0 "register_operand" "r,l,x"))]
+ "TARGET_SH1 && ! TARGET_SH5"
+ "")
+
+(define_expand "pop"
+ [(set (match_operand:SI 0 "register_operand" "=r,l,x")
+ (mem:SI (post_inc:SI (reg:SI SP_REG))))]
+ "TARGET_SH1 && ! TARGET_SH5"
+ "")
+
+(define_expand "push_e"
+ [(parallel [(set (mem:SF (pre_dec:SI (reg:SI SP_REG)))
+ (match_operand:SF 0 "" ""))
+ (use (reg:PSI FPSCR_REG))
+ (clobber (scratch:SI))])]
+ "TARGET_SH1 && ! TARGET_SH5"
+ "")
+
+(define_insn "push_fpul"
+ [(set (mem:SF (pre_dec:SI (reg:SI SP_REG))) (reg:SF FPUL_REG))]
+ "TARGET_SH2E && ! TARGET_SH5"
+ "sts.l fpul,@-r15"
+ [(set_attr "type" "fstore")
+ (set_attr "late_fp_use" "yes")
+ (set_attr "hit_stack" "yes")])
+
+;; DFmode pushes for sh4 require a lot of what is defined for movdf_i4,
+;; so use that.
+(define_expand "push_4"
+ [(parallel [(set (mem:DF (pre_dec:SI (reg:SI SP_REG)))
+ (match_operand:DF 0 "" ""))
+ (use (reg:PSI FPSCR_REG))
+ (clobber (scratch:SI))])]
+ "TARGET_SH1 && ! TARGET_SH5"
+ "")
+
+(define_expand "pop_e"
+ [(parallel [(set (match_operand:SF 0 "" "")
+ (mem:SF (post_inc:SI (reg:SI SP_REG))))
+ (use (reg:PSI FPSCR_REG))
+ (clobber (scratch:SI))])]
+ "TARGET_SH1 && ! TARGET_SH5"
+ "")
+
+(define_insn "pop_fpul"
+ [(set (reg:SF FPUL_REG) (mem:SF (post_inc:SI (reg:SI SP_REG))))]
+ "TARGET_SH2E && ! TARGET_SH5"
+ "lds.l @r15+,fpul"
+ [(set_attr "type" "load")
+ (set_attr "hit_stack" "yes")])
+
+(define_expand "pop_4"
+ [(parallel [(set (match_operand:DF 0 "" "")
+ (mem:DF (post_inc:SI (reg:SI SP_REG))))
+ (use (reg:PSI FPSCR_REG))
+ (clobber (scratch:SI))])]
+ "TARGET_SH1 && ! TARGET_SH5"
+ "")
+
+(define_expand "push_fpscr"
+ [(const_int 0)]
+ "TARGET_SH2E"
+ "
+{
+ rtx insn = emit_insn (gen_fpu_switch (gen_frame_mem (PSImode,
+ gen_rtx_PRE_DEC (Pmode,
+ stack_pointer_rtx)),
+ get_fpscr_rtx ()));
+ add_reg_note (insn, REG_INC, stack_pointer_rtx);
+ DONE;
+}")
+
+(define_expand "pop_fpscr"
+ [(const_int 0)]
+ "TARGET_SH2E"
+ "
+{
+ rtx insn = emit_insn (gen_fpu_switch (get_fpscr_rtx (),
+ gen_frame_mem (PSImode,
+ gen_rtx_POST_INC (Pmode,
+ stack_pointer_rtx))));
+ add_reg_note (insn, REG_INC, stack_pointer_rtx);
+ DONE;
+}")
+
+;; These two patterns can happen as the result of optimization, when
+;; comparisons get simplified to a move of zero or 1 into the T reg.
+;; They don't disappear completely, because the T reg is a fixed hard reg.
+
+(define_insn "clrt"
+ [(set (reg:SI T_REG) (const_int 0))]
+ "TARGET_SH1"
+ "clrt")
+
+(define_insn "sett"
+ [(set (reg:SI T_REG) (const_int 1))]
+ "TARGET_SH1"
+ "sett")
+
+;; Define additional pop for SH1 and SH2 so it does not get
+;; placed in the delay slot.
+(define_insn "*movsi_pop"
+ [(set (match_operand:SI 0 "register_operand" "=r,x,l")
+ (match_operand:SI 1 "sh_no_delay_pop_operand" ">,>,>"))]
+ "(TARGET_SH1 || TARGET_SH2E || TARGET_SH2A)
+ && ! TARGET_SH3"
+ "@
+ mov.l %1,%0
+ lds.l %1,%0
+ lds.l %1,%0"
+ [(set_attr "type" "load_si,mem_mac,pload")
+ (set_attr "length" "2,2,2")
+ (set_attr "in_delay_slot" "no,no,no")])
+
+;; t/r must come after r/r, lest reload will try to reload stuff like
+;; (set (subreg:SI (mem:QI (plus:SI (reg:SI SP_REG) (const_int 12)) 0) 0)
+;; (made from (set (subreg:SI (reg:QI ###) 0) ) into T.
+(define_insn "movsi_i"
+ [(set (match_operand:SI 0 "general_movdst_operand"
+ "=r,r,r,t,r,r,r,r,m,<,<,x,l,x,l,r")
+ (match_operand:SI 1 "general_movsrc_operand"
+ "Q,r,I08,r,mr,x,l,t,r,x,l,r,r,>,>,i"))]
+ "TARGET_SH1
+ && ! TARGET_SH2E
+ && ! TARGET_SH2A
+ && (register_operand (operands[0], SImode)
+ || register_operand (operands[1], SImode))"
+ "@
+ mov.l %1,%0
+ mov %1,%0
+ mov %1,%0
+ cmp/pl %1
+ mov.l %1,%0
+ sts %1,%0
+ sts %1,%0
+ movt %0
+ mov.l %1,%0
+ sts.l %1,%0
+ sts.l %1,%0
+ lds %1,%0
+ lds %1,%0
+ lds.l %1,%0
+ lds.l %1,%0
+ fake %1,%0"
+ [(set_attr "type" "pcload_si,move,movi8,mt_group,load_si,mac_gp,prget,arith,store,mac_mem,pstore,gp_mac,prset,mem_mac,pload,pcload_si")
+ (set_attr "length" "*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*")])
+
+;; t/r must come after r/r, lest reload will try to reload stuff like
+;; (subreg:SI (reg:SF FR14_REG) 0) into T (compiling stdlib/strtod.c -m3e -O2)
+;; ??? This allows moves from macl to fpul to be recognized, but these moves
+;; will require a reload.
+;; ??? We can't include f/f because we need the proper FPSCR setting when
+;; TARGET_FMOVD is in effect, and mode switching is done before reload.
+(define_insn "movsi_ie"
+ [(set (match_operand:SI 0 "general_movdst_operand"
+ "=r,r,r,r,r,t,r,r,r,r,m,<,<,x,l,x,l,y,<,r,y,r,*f,y,*f,y")
+ (match_operand:SI 1 "general_movsrc_operand"
+ "Q,r,I08,I20,I28,r,mr,x,l,t,r,x,l,r,r,>,>,>,y,i,r,y,y,*f,*f,y"))]
+ "(TARGET_SH2E || TARGET_SH2A)
+ && (register_operand (operands[0], SImode)
+ || register_operand (operands[1], SImode))"
+ "@
+ mov.l %1,%0
+ mov %1,%0
+ mov %1,%0
+ movi20 %1,%0
+ movi20s %1,%0
+ cmp/pl %1
+ mov.l %1,%0
+ sts %1,%0
+ sts %1,%0
+ movt %0
+ mov.l %1,%0
+ sts.l %1,%0
+ sts.l %1,%0
+ lds %1,%0
+ lds %1,%0
+ lds.l %1,%0
+ lds.l %1,%0
+ lds.l %1,%0
+ sts.l %1,%0
+ fake %1,%0
+ lds %1,%0
+ sts %1,%0
+ fsts fpul,%0
+ flds %1,fpul
+ fmov %1,%0
+ ! move optimized away"
+ [(set_attr "type" "pcload_si,move,movi8,move,move,*,load_si,mac_gp,prget,arith,store,mac_mem,pstore,gp_mac,prset,mem_mac,pload,load,fstore,pcload_si,gp_fpul,fpul_gp,fmove,fmove,fmove,nil")
+ (set_attr "late_fp_use" "*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,yes,*,*,yes,*,*,*,*")
+ (set_attr_alternative "length"
+ [(const_int 2)
+ (const_int 2)
+ (const_int 2)
+ (const_int 4)
+ (const_int 4)
+ (const_int 2)
+ (if_then_else
+ (ne (symbol_ref "TARGET_SH2A") (const_int 0))
+ (const_int 4) (const_int 2))
+ (const_int 2)
+ (const_int 2)
+ (const_int 2)
+ (if_then_else
+ (ne (symbol_ref "TARGET_SH2A") (const_int 0))
+ (const_int 4) (const_int 2))
+ (const_int 2)
+ (const_int 2)
+ (const_int 2)
+ (const_int 2)
+ (const_int 2)
+ (const_int 2)
+ (const_int 2)
+ (const_int 2)
+ (const_int 2)
+ (const_int 2)
+ (const_int 2)
+ (const_int 2)
+ (const_int 2)
+ (const_int 2)
+ (const_int 0)])])
+
+(define_insn "movsi_i_lowpart"
+ [(set (strict_low_part (match_operand:SI 0 "general_movdst_operand" "+r,r,r,r,r,r,r,m,r"))
+ (match_operand:SI 1 "general_movsrc_operand" "Q,r,I08,mr,x,l,t,r,i"))]
+ "TARGET_SH1
+ && (register_operand (operands[0], SImode)
+ || register_operand (operands[1], SImode))"
+ "@
+ mov.l %1,%0
+ mov %1,%0
+ mov %1,%0
+ mov.l %1,%0
+ sts %1,%0
+ sts %1,%0
+ movt %0
+ mov.l %1,%0
+ fake %1,%0"
+ [(set_attr "type" "pcload,move,arith,load,mac_gp,prget,arith,store,pcload")])
+
+(define_insn_and_split "load_ra"
+ [(set (match_operand:SI 0 "general_movdst_operand" "")
+ (unspec:SI [(match_operand:SI 1 "register_operand" "")] UNSPEC_RA))]
+ "TARGET_SH1"
+ "#"
+ "&& ! currently_expanding_to_rtl"
+ [(set (match_dup 0) (match_dup 1))]
+ "
+{
+ if (TARGET_SHCOMPACT && crtl->saves_all_registers)
+ operands[1] = gen_frame_mem (SImode, return_address_pointer_rtx);
+}")
+
+;; The '?'s in the following constraints may not reflect the time taken
+;; to perform the move. They are there to discourage the use of floating-
+;; point registers for storing integer values.
+(define_insn "*movsi_media"
+ [(set (match_operand:SI 0 "general_movdst_operand"
+ "=r,r,r,r,m,f?,m,f?,r,f?,*b,r,b")
+ (match_operand:SI 1 "general_movsrc_operand"
+ "r,I16Css,nCpg,m,rZ,m,f?,rZ,f?,f?,r,*b,Csy"))]
+ "TARGET_SHMEDIA_FPU
+ && (register_operand (operands[0], SImode)
+ || sh_register_operand (operands[1], SImode)
+ || GET_CODE (operands[1]) == TRUNCATE)"
+ "@
+ add.l %1, r63, %0
+ movi %1, %0
+ #
+ ld%M1.l %m1, %0
+ st%M0.l %m0, %N1
+ fld%M1.s %m1, %0
+ fst%M0.s %m0, %1
+ fmov.ls %N1, %0
+ fmov.sl %1, %0
+ fmov.s %1, %0
+ ptabs %1, %0
+ gettr %1, %0
+ pt %1, %0"
+ [(set_attr "type" "arith_media,arith_media,*,load_media,store_media,fload_media,fstore_media,fload_media,fpconv_media,fmove_media,ptabs_media,gettr_media,pt_media")
+ (set_attr "length" "4,4,8,4,4,4,4,4,4,4,4,4,12")
+ (set (attr "highpart")
+ (cond [(ne (symbol_ref "sh_contains_memref_p (insn)") (const_int 0))
+ (const_string "user")]
+ (const_string "ignore")))])
+
+(define_insn "*movsi_media_nofpu"
+ [(set (match_operand:SI 0 "general_movdst_operand"
+ "=r,r,r,r,m,*b,r,*b")
+ (match_operand:SI 1 "general_movsrc_operand"
+ "r,I16Css,nCpg,m,rZ,r,*b,Csy"))]
+ "TARGET_SHMEDIA
+ && (register_operand (operands[0], SImode)
+ || sh_register_operand (operands[1], SImode)
+ || GET_CODE (operands[1]) == TRUNCATE)"
+ "@
+ add.l %1, r63, %0
+ movi %1, %0
+ #
+ ld%M1.l %m1, %0
+ st%M0.l %m0, %N1
+ ptabs %1, %0
+ gettr %1, %0
+ pt %1, %0"
+ [(set_attr "type" "arith_media,arith_media,*,load_media,store_media,ptabs_media,gettr_media,pt_media")
+ (set_attr "length" "4,4,8,4,4,4,4,12")
+ (set (attr "highpart")
+ (cond [(ne (symbol_ref "sh_contains_memref_p (insn)") (const_int 0))
+ (const_string "user")]
+ (const_string "ignore")))])
+
+(define_expand "movsi_const"
+ [(set (match_operand:SI 0 "arith_reg_operand" "=r")
+ (const:SI (unspec:SI [(match_operand:DI 1 "immediate_operand" "s")
+ (const_int 16)] UNSPEC_EXTRACT_S16)))
+ (set (match_dup 0)
+ (ior:SI (ashift:SI (match_dup 0) (const_int 16))
+ (const:SI (unspec:SI [(match_dup 1)
+ (const_int 0)] UNSPEC_EXTRACT_U16))))]
+ "TARGET_SHMEDIA && reload_completed
+ && MOVI_SHORI_BASE_OPERAND_P (operands[1])"
+ "
+{
+ if (GET_CODE (operands[1]) == LABEL_REF
+ && GET_CODE (XEXP (operands[1], 0)) == CODE_LABEL)
+ LABEL_NUSES (XEXP (operands[1], 0)) += 2;
+ else if (GOTOFF_P (operands[1]))
+ {
+ rtx unspec = XEXP (operands[1], 0);
+
+ if (! UNSPEC_GOTOFF_P (unspec))
+ {
+ unspec = XEXP (unspec, 0);
+ if (! UNSPEC_GOTOFF_P (unspec))
+ abort ();
+ }
+ if (GET_CODE (XVECEXP (unspec , 0, 0)) == LABEL_REF
+ && (GET_CODE (XEXP (XVECEXP (unspec, 0, 0), 0)) == CODE_LABEL))
+ LABEL_NUSES (XEXP (XVECEXP (unspec, 0, 0), 0)) += 2;
+ }
+}")
+
+(define_expand "movsi_const_16bit"
+ [(set (match_operand:SI 0 "arith_reg_operand" "=r")
+ (const:SI (unspec:SI [(match_operand:DI 1 "immediate_operand" "s")
+ (const_int 0)] UNSPEC_EXTRACT_S16)))]
+ "TARGET_SHMEDIA && flag_pic && reload_completed
+ && GET_CODE (operands[1]) == SYMBOL_REF"
+ "")
+
+(define_split
+ [(set (match_operand:SI 0 "arith_reg_dest" "")
+ (match_operand:SI 1 "immediate_operand" ""))]
+ "TARGET_SHMEDIA && reload_completed
+ && MOVI_SHORI_BASE_OPERAND_P (operands[1])"
+ [(const_int 0)]
+ "
+{
+ rtx insn = emit_insn (gen_movsi_const (operands[0], operands[1]));
+
+ set_unique_reg_note (insn, REG_EQUAL, copy_rtx (operands[1]));
+
+ DONE;
+}")
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (match_operand:SI 1 "immediate_operand" ""))]
+ "TARGET_SHMEDIA && reload_completed
+ && ((CONST_INT_P (operands[1])
+ && ! satisfies_constraint_I16 (operands[1]))
+ || GET_CODE (operands[1]) == CONST_DOUBLE)"
+ [(set (subreg:DI (match_dup 0) 0) (match_dup 1))])
+
+(define_expand "movsi"
+ [(set (match_operand:SI 0 "general_movdst_operand" "")
+ (match_operand:SI 1 "general_movsrc_operand" ""))]
+ ""
+ "{ if (prepare_move_operands (operands, SImode)) DONE; }")
+
+(define_expand "ic_invalidate_line"
+ [(parallel [(unspec_volatile [(match_operand:SI 0 "register_operand" "+r")
+ (match_dup 1)] UNSPEC_ICACHE)
+ (clobber (scratch:SI))])]
+ "TARGET_HARD_SH4 || TARGET_SH5"
+ "
+{
+ if (TARGET_SHMEDIA)
+ {
+ emit_insn (gen_ic_invalidate_line_media (operands[0]));
+ DONE;
+ }
+ else if (TARGET_SHCOMPACT)
+ {
+ operands[1] = function_symbol (NULL, \"__ic_invalidate\", SFUNC_STATIC);
+ operands[1] = force_reg (Pmode, operands[1]);
+ emit_insn (gen_ic_invalidate_line_compact (operands[0], operands[1]));
+ DONE;
+ }
+ else if (TARGET_SH4A_ARCH || TARGET_SH4_300)
+ {
+ emit_insn (gen_ic_invalidate_line_sh4a (operands[0]));
+ DONE;
+ }
+ operands[0] = force_reg (Pmode, operands[0]);
+ operands[1] = force_reg (Pmode, GEN_INT (trunc_int_for_mode (0xf0000008,
+ Pmode)));
+}")
+
+;; The address %0 is assumed to be 4-aligned at least. Thus, by ORing
+;; 0xf0000008, we get the low-oder bits *1*00 (binary), which fits
+;; the requirement *1*00 for associative address writes. The alignment of
+;; %0 implies that its least significant bit is cleared,
+;; thus we clear the V bit of a matching entry if there is one.
+(define_insn "ic_invalidate_line_i"
+ [(unspec_volatile [(match_operand:SI 0 "register_operand" "r")
+ (match_operand:SI 1 "register_operand" "r")]
+ UNSPEC_ICACHE)
+ (clobber (match_scratch:SI 2 "=&r"))]
+ "TARGET_HARD_SH4"
+ "ocbwb\\t@%0\;extu.w\\t%0,%2\;or\\t%1,%2\;mov.l\\t%0,@%2"
+ [(set_attr "length" "8")
+ (set_attr "type" "cwb")])
+
+(define_insn "ic_invalidate_line_sh4a"
+ [(unspec_volatile [(match_operand:SI 0 "register_operand" "r")]
+ UNSPEC_ICACHE)]
+ "TARGET_SH4A_ARCH || TARGET_SH4_300"
+ "ocbwb\\t@%0\;synco\;icbi\\t@%0"
+ [(set_attr "length" "16")
+ (set_attr "type" "cwb")])
+
+;; ??? could make arg 0 an offsettable memory operand to allow to save
+;; an add in the code that calculates the address.
+(define_insn "ic_invalidate_line_media"
+ [(unspec_volatile [(match_operand 0 "any_register_operand" "r")]
+ UNSPEC_ICACHE)]
+ "TARGET_SHMEDIA"
+ "ocbwb %0,0\;synco\;icbi %0, 0\;synci"
+ [(set_attr "length" "16")
+ (set_attr "type" "invalidate_line_media")])
+
+(define_insn "ic_invalidate_line_compact"
+ [(unspec_volatile [(match_operand:SI 0 "register_operand" "z")
+ (match_operand:SI 1 "register_operand" "r")]
+ UNSPEC_ICACHE)
+ (clobber (reg:SI PR_REG))]
+ "TARGET_SHCOMPACT"
+ "jsr @%1%#"
+ [(set_attr "type" "sfunc")
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_expand "initialize_trampoline"
+ [(match_operand:SI 0 "" "")
+ (match_operand:SI 1 "" "")
+ (match_operand:SI 2 "" "")]
+ "TARGET_SHCOMPACT"
+ "
+{
+ rtx sfun, tramp;
+
+ tramp = force_reg (Pmode, operands[0]);
+ sfun = force_reg (Pmode, function_symbol (NULL, \"__init_trampoline\",
+ SFUNC_STATIC));
+ emit_move_insn (gen_rtx_REG (SImode, R2_REG), operands[1]);
+ emit_move_insn (gen_rtx_REG (SImode, R3_REG), operands[2]);
+
+ emit_insn (gen_initialize_trampoline_compact (tramp, sfun));
+ DONE;
+}")
+
+(define_insn "initialize_trampoline_compact"
+ [(unspec_volatile [(match_operand:SI 0 "register_operand" "z")
+ (match_operand:SI 1 "register_operand" "r")
+ (reg:SI R2_REG) (reg:SI R3_REG)]
+ UNSPEC_INIT_TRAMP)
+
+ (clobber (reg:SI PR_REG))]
+ "TARGET_SHCOMPACT"
+ "jsr @%1%#"
+ [(set_attr "type" "sfunc")
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_insn "movqi_i"
+ [(set (match_operand:QI 0 "general_movdst_operand" "=r,r,r,m,r,r,l")
+ (match_operand:QI 1 "general_movsrc_operand" "r,i,m,r,t,l,r"))]
+ "TARGET_SH1
+ && (arith_reg_operand (operands[0], QImode)
+ || arith_reg_operand (operands[1], QImode))"
+ "@
+ mov %1,%0
+ mov %1,%0
+ mov.b %1,%0
+ mov.b %1,%0
+ movt %0
+ sts %1,%0
+ lds %1,%0"
+ [(set_attr "type" "move,movi8,load,store,arith,prget,prset")
+ (set_attr_alternative "length"
+ [(const_int 2)
+ (const_int 2)
+ (if_then_else
+ (ne (symbol_ref "TARGET_SH2A") (const_int 0))
+ (const_int 4) (const_int 2))
+ (if_then_else
+ (ne (symbol_ref "TARGET_SH2A") (const_int 0))
+ (const_int 4) (const_int 2))
+ (const_int 2)
+ (const_int 2)
+ (const_int 2)])])
+
+(define_insn "*movqi_media"
+ [(set (match_operand:QI 0 "general_movdst_operand" "=r,r,r,m")
+ (match_operand:QI 1 "general_movsrc_operand" "r,I16Css,m,rZ"))]
+ "TARGET_SHMEDIA
+ && (arith_reg_operand (operands[0], QImode)
+ || extend_reg_or_0_operand (operands[1], QImode))"
+ "@
+ add.l %1, r63, %0
+ movi %1, %0
+ ld%M1.ub %m1, %0
+ st%M0.b %m0, %N1"
+ [(set_attr "type" "arith_media,arith_media,load_media,store_media")
+ (set (attr "highpart")
+ (cond [(ne (symbol_ref "sh_contains_memref_p (insn)") (const_int 0))
+ (const_string "user")]
+ (const_string "ignore")))])
+
+(define_expand "movqi"
+ [(set (match_operand:QI 0 "general_operand" "")
+ (match_operand:QI 1 "general_operand" ""))]
+ ""
+ "{ if (prepare_move_operands (operands, QImode)) DONE; }")
+
+(define_expand "reload_inqi"
+ [(set (match_operand:SI 2 "" "=&r")
+ (match_operand:QI 1 "inqhi_operand" ""))
+ (set (match_operand:QI 0 "arith_reg_operand" "=r")
+ (truncate:QI (match_dup 3)))]
+ "TARGET_SHMEDIA"
+ "
+{
+ rtx inner = XEXP (operands[1], 0);
+ int regno = REGNO (inner);
+
+ regno += HARD_REGNO_NREGS (regno, GET_MODE (inner)) - 1;
+ operands[1] = gen_rtx_REG (SImode, regno);
+ operands[3] = gen_rtx_REG (DImode, REGNO (operands[2]));
+}")
+
+/* When storing r0, we have to avoid reg+reg addressing. */
+(define_insn "movhi_i"
+ [(set (match_operand:HI 0 "general_movdst_operand" "=r,r,r,r,m,r,l,r")
+ (match_operand:HI 1 "general_movsrc_operand" "Q,rI08,m,t,r,l,r,i"))]
+ "TARGET_SH1
+ && (arith_reg_operand (operands[0], HImode)
+ || arith_reg_operand (operands[1], HImode))
+ && (!MEM_P (operands[0])
+ || GET_CODE (XEXP (operands[0], 0)) != PLUS
+ || !REG_P (XEXP (XEXP (operands[0], 0), 1))
+ || ! refers_to_regno_p (R0_REG, R0_REG + 1, operands[1], (rtx *)0))"
+ "@
+ mov.w %1,%0
+ mov %1,%0
+ mov.w %1,%0
+ movt %0
+ mov.w %1,%0
+ sts %1,%0
+ lds %1,%0
+ fake %1,%0"
+ [(set_attr "type" "pcload,move,load,move,store,move,move,pcload")])
+
+(define_insn "*movhi_media"
+ [(set (match_operand:HI 0 "general_movdst_operand" "=r,r,r,r,m")
+ (match_operand:HI 1 "general_movsrc_operand" "r,I16Css,n,m,rZ"))]
+ "TARGET_SHMEDIA
+ && (arith_reg_operand (operands[0], HImode)
+ || arith_reg_or_0_operand (operands[1], HImode))"
+ "@
+ add.l %1, r63, %0
+ movi %1, %0
+ #
+ ld%M1.w %m1, %0
+ st%M0.w %m0, %N1"
+ [(set_attr "type" "arith_media,arith_media,*,load_media,store_media")
+ (set (attr "highpart")
+ (cond [(ne (symbol_ref "sh_contains_memref_p (insn)") (const_int 0))
+ (const_string "user")]
+ (const_string "ignore")))])
+
+(define_split
+ [(set (match_operand:HI 0 "register_operand" "")
+ (match_operand:HI 1 "immediate_operand" ""))]
+ "TARGET_SHMEDIA && reload_completed
+ && ! satisfies_constraint_I16 (operands[1])"
+ [(set (subreg:DI (match_dup 0) 0) (match_dup 1))])
+
+(define_expand "movhi"
+ [(set (match_operand:HI 0 "general_movdst_operand" "")
+ (match_operand:HI 1 "general_movsrc_operand" ""))]
+ ""
+ "{ if (prepare_move_operands (operands, HImode)) DONE; }")
+
+(define_expand "reload_inhi"
+ [(set (match_operand:SI 2 "" "=&r")
+ (match_operand:HI 1 "inqhi_operand" ""))
+ (set (match_operand:HI 0 "arith_reg_operand" "=r")
+ (truncate:HI (match_dup 3)))]
+ "TARGET_SHMEDIA"
+ "
+{
+ rtx inner = XEXP (operands[1], 0);
+ int regno = REGNO (inner);
+
+ regno += HARD_REGNO_NREGS (regno, GET_MODE (inner)) - 1;
+ operands[1] = gen_rtx_REG (SImode, regno);
+ operands[3] = gen_rtx_REG (DImode, REGNO (operands[2]));
+}")
+
+;; x/r can be created by inlining/cse, e.g. for execute/961213-1.c
+;; compiled with -m2 -ml -O3 -funroll-loops
+(define_insn "*movdi_i"
+ [(set (match_operand:DI 0 "general_movdst_operand" "=r,r,r,m,r,r,r,*!x")
+ (match_operand:DI 1 "general_movsrc_operand" "Q,r,m,r,I08,i,x,r"))]
+ "TARGET_SH1
+ && (arith_reg_operand (operands[0], DImode)
+ || arith_reg_operand (operands[1], DImode))"
+ "* return output_movedouble (insn, operands, DImode);"
+ [(set_attr "length" "4")
+ (set_attr "type" "pcload,move,load,store,move,pcload,move,move")])
+
+;; If the output is a register and the input is memory or a register, we have
+;; to be careful and see which word needs to be loaded first.
+
+(define_split
+ [(set (match_operand:DI 0 "general_movdst_operand" "")
+ (match_operand:DI 1 "general_movsrc_operand" ""))]
+ "TARGET_SH1 && reload_completed"
+ [(set (match_dup 2) (match_dup 3))
+ (set (match_dup 4) (match_dup 5))]
+ "
+{
+ int regno;
+
+ if ((MEM_P (operands[0])
+ && GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
+ || (MEM_P (operands[1])
+ && GET_CODE (XEXP (operands[1], 0)) == POST_INC))
+ FAIL;
+
+ switch (GET_CODE (operands[0]))
+ {
+ case REG:
+ regno = REGNO (operands[0]);
+ break;
+ case SUBREG:
+ regno = subreg_regno (operands[0]);
+ break;
+ case MEM:
+ regno = -1;
+ break;
+ default:
+ gcc_unreachable ();
+ }
+
+ if (regno == -1
+ || ! refers_to_regno_p (regno, regno + 1, operands[1], 0))
+ {
+ operands[2] = operand_subword (operands[0], 0, 0, DImode);
+ operands[3] = operand_subword (operands[1], 0, 0, DImode);
+ operands[4] = operand_subword (operands[0], 1, 0, DImode);
+ operands[5] = operand_subword (operands[1], 1, 0, DImode);
+ }
+ else
+ {
+ operands[2] = operand_subword (operands[0], 1, 0, DImode);
+ operands[3] = operand_subword (operands[1], 1, 0, DImode);
+ operands[4] = operand_subword (operands[0], 0, 0, DImode);
+ operands[5] = operand_subword (operands[1], 0, 0, DImode);
+ }
+
+ if (operands[2] == 0 || operands[3] == 0
+ || operands[4] == 0 || operands[5] == 0)
+ FAIL;
+}")
+
+;; The '?'s in the following constraints may not reflect the time taken
+;; to perform the move. They are there to discourage the use of floating-
+;; point registers for storing integer values.
+(define_insn "*movdi_media"
+ [(set (match_operand:DI 0 "general_movdst_operand"
+ "=r,r,r,rl,m,f?,m,f?,r,f?,*b,r,*b")
+ (match_operand:DI 1 "general_movsrc_operand"
+ "r,I16Css,nCpgF,m,rlZ,m,f?,rZ,f?,f?,r,*b,Csy"))]
+ "TARGET_SHMEDIA_FPU
+ && (register_operand (operands[0], DImode)
+ || sh_register_operand (operands[1], DImode))"
+ "@
+ add %1, r63, %0
+ movi %1, %0
+ #
+ ld%M1.q %m1, %0
+ st%M0.q %m0, %N1
+ fld%M1.d %m1, %0
+ fst%M0.d %m0, %1
+ fmov.qd %N1, %0
+ fmov.dq %1, %0
+ fmov.d %1, %0
+ ptabs %1, %0
+ gettr %1, %0
+ pt %1, %0"
+ [(set_attr "type" "arith_media,arith_media,*,load_media,store_media,fload_media,fstore_media,fload_media,dfpconv_media,fmove_media,ptabs_media,gettr_media,pt_media")
+ (set_attr "length" "4,4,16,4,4,4,4,4,4,4,4,4,*")])
+
+(define_insn "*movdi_media_nofpu"
+ [(set (match_operand:DI 0 "general_movdst_operand" "=r,r,r,rl,m,*b,r,*b");
+ (match_operand:DI 1 "general_movsrc_operand" "r,I16Css,nCpgF,m,rlZ,r,*b,Csy"))]
+ "TARGET_SHMEDIA
+ && (register_operand (operands[0], DImode)
+ || sh_register_operand (operands[1], DImode))"
+ "@
+ add %1, r63, %0
+ movi %1, %0
+ #
+ ld%M1.q %m1, %0
+ st%M0.q %m0, %N1
+ ptabs %1, %0
+ gettr %1, %0
+ pt %1, %0"
+ [(set_attr "type" "arith_media,arith_media,*,load_media,store_media,ptabs_media,gettr_media,pt_media")
+ (set_attr "length" "4,4,16,4,4,4,4,*")])
+
+(define_insn "*movdi_media_I16"
+ [(set (match_operand:DI 0 "ext_dest_operand" "=r")
+ (match_operand:DI 1 "const_int_operand" "I16"))]
+ "TARGET_SHMEDIA && reload_completed"
+ "movi %1, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "length" "4")])
+
+(define_split
+ [(set (match_operand:DI 0 "arith_reg_dest" "")
+ (match_operand:DI 1 "immediate_operand" ""))]
+ "TARGET_SHMEDIA && reload_completed
+ && MOVI_SHORI_BASE_OPERAND_P (operands[1])"
+ [(set (match_dup 0) (match_dup 1))]
+ "
+{
+ rtx insn;
+
+ if (TARGET_SHMEDIA64)
+ insn = emit_insn (gen_movdi_const (operands[0], operands[1]));
+ else
+ insn = emit_insn (gen_movdi_const_32bit (operands[0], operands[1]));
+
+ set_unique_reg_note (insn, REG_EQUAL, copy_rtx (operands[1]));
+
+ DONE;
+}")
+
+(define_expand "movdi_const"
+ [(set (match_operand:DI 0 "arith_reg_operand" "=r")
+ (const:DI (unspec:DI [(match_operand:DI 1 "immediate_operand" "s")
+ (const_int 48)] UNSPEC_EXTRACT_S16)))
+ (set (match_dup 0)
+ (ior:DI (ashift:DI (match_dup 0) (const_int 16))
+ (const:DI (unspec:DI [(match_dup 1)
+ (const_int 32)] UNSPEC_EXTRACT_U16))))
+ (set (match_dup 0)
+ (ior:DI (ashift:DI (match_dup 0) (const_int 16))
+ (const:DI (unspec:DI [(match_dup 1)
+ (const_int 16)] UNSPEC_EXTRACT_U16))))
+ (set (match_dup 0)
+ (ior:DI (ashift:DI (match_dup 0) (const_int 16))
+ (const:DI (unspec:DI [(match_dup 1)
+ (const_int 0)] UNSPEC_EXTRACT_U16))))]
+ "TARGET_SHMEDIA64 && reload_completed
+ && MOVI_SHORI_BASE_OPERAND_P (operands[1])"
+ "
+{
+ sh_mark_label (operands[1], 4);
+}")
+
+(define_expand "movdi_const_32bit"
+ [(set (match_operand:DI 0 "arith_reg_operand" "=r")
+ (const:DI (unspec:DI [(match_operand:DI 1 "immediate_operand" "s")
+ (const_int 16)] UNSPEC_EXTRACT_S16)))
+ (set (match_dup 0)
+ (ior:DI (ashift:DI (match_dup 0) (const_int 16))
+ (const:DI (unspec:DI [(match_dup 1)
+ (const_int 0)] UNSPEC_EXTRACT_U16))))]
+ "TARGET_SHMEDIA32 && reload_completed
+ && MOVI_SHORI_BASE_OPERAND_P (operands[1])"
+ "
+{
+ sh_mark_label (operands[1], 2);
+}")
+
+(define_expand "movdi_const_16bit"
+ [(set (match_operand:DI 0 "arith_reg_operand" "=r")
+ (const:DI (unspec:DI [(match_operand:DI 1 "immediate_operand" "s")
+ (const_int 0)] UNSPEC_EXTRACT_S16)))]
+ "TARGET_SHMEDIA && flag_pic && reload_completed
+ && GET_CODE (operands[1]) == SYMBOL_REF"
+ "")
+
+(define_split
+ [(set (match_operand:DI 0 "ext_dest_operand" "")
+ (match_operand:DI 1 "immediate_operand" ""))]
+ "TARGET_SHMEDIA && reload_completed
+ && CONST_INT_P (operands[1])
+ && ! satisfies_constraint_I16 (operands[1])"
+ [(set (match_dup 0) (match_dup 2))
+ (match_dup 1)]
+ "
+{
+ unsigned HOST_WIDE_INT val = INTVAL (operands[1]);
+ unsigned HOST_WIDE_INT low = val;
+ unsigned HOST_WIDE_INT high = val;
+ unsigned HOST_WIDE_INT sign;
+ unsigned HOST_WIDE_INT val2 = val ^ (val-1);
+
+ /* Zero-extend the 16 least-significant bits. */
+ low &= 0xffff;
+
+ /* Arithmetic shift right the word by 16 bits. */
+ high >>= 16;
+ if (GET_CODE (operands[0]) == SUBREG
+ && GET_MODE (SUBREG_REG (operands[0])) == SImode)
+ {
+ high &= 0xffff;
+ high ^= 0x8000;
+ high -= 0x8000;
+ }
+ else
+ {
+ sign = 1;
+ sign <<= (HOST_BITS_PER_WIDE_INT - 16 - 1);
+ high ^= sign;
+ high -= sign;
+ }
+ do
+ {
+ /* If we can't generate the constant with a two-insn movi / shori
+ sequence, try some other strategies. */
+ if (! CONST_OK_FOR_I16 (high))
+ {
+ /* Try constant load / left shift. We know VAL != 0. */
+ val2 = val ^ (val-1);
+ if (val2 > 0x1ffff)
+ {
+ int trailing_zeroes = exact_log2 ((val2 >> 16) + 1) + 15;
+
+ if (CONST_OK_FOR_I16 (val >> trailing_zeroes)
+ || (! CONST_OK_FOR_I16 (high >> 16)
+ && CONST_OK_FOR_I16 (val >> (trailing_zeroes + 16))))
+ {
+ val2 = (HOST_WIDE_INT) val >> trailing_zeroes;
+ operands[1] = gen_ashldi3_media (operands[0], operands[0],
+ GEN_INT (trailing_zeroes));
+ break;
+ }
+ }
+ /* Try constant load / right shift. */
+ val2 = (val >> 15) + 1;
+ if (val2 == (val2 & -val2))
+ {
+ int shift = 49 - exact_log2 (val2);
+
+ val2 = trunc_int_for_mode (val << shift, DImode);
+ if (CONST_OK_FOR_I16 (val2))
+ {
+ operands[1] = gen_lshrdi3_media (operands[0], operands[0],
+ GEN_INT (shift));
+ break;
+ }
+ }
+ /* Try mperm.w . */
+ val2 = val & 0xffff;
+ if ((val >> 16 & 0xffff) == val2
+ && (val >> 32 & 0xffff) == val2
+ && (val >> 48 & 0xffff) == val2)
+ {
+ val2 = (HOST_WIDE_INT) val >> 48;
+ operands[1] = gen_rtx_REG (V4HImode, true_regnum (operands[0]));
+ operands[1] = gen_mperm_w0 (operands[1], operands[1]);
+ break;
+ }
+ /* Try movi / mshflo.l */
+ val2 = (HOST_WIDE_INT) val >> 32;
+ if (val2 == ((unsigned HOST_WIDE_INT)
+ trunc_int_for_mode (val, SImode)))
+ {
+ operands[1] = gen_mshflo_l_di (operands[0], operands[0],
+ operands[0]);
+ break;
+ }
+ /* Try movi / mshflo.l w/ r63. */
+ val2 = val + ((HOST_WIDE_INT) -1 << 32);
+ if ((HOST_WIDE_INT) val2 < 0 && CONST_OK_FOR_I16 (val2))
+ {
+ operands[1] = gen_mshflo_l_di (operands[0], operands[0],
+ const0_rtx);
+ break;
+ }
+ }
+ val2 = high;
+ operands[1] = gen_shori_media (operands[0], operands[0], GEN_INT (low));
+ }
+ while (0);
+ operands[2] = GEN_INT (val2);
+}")
+
+(define_split
+ [(set (match_operand:DI 0 "ext_dest_operand" "")
+ (match_operand:DI 1 "immediate_operand" ""))]
+ "TARGET_SHMEDIA && reload_completed
+ && GET_CODE (operands[1]) == CONST_DOUBLE"
+ [(set (match_dup 0) (match_dup 2))
+ (set (match_dup 0)
+ (ior:DI (ashift:DI (match_dup 0) (const_int 16)) (match_dup 1)))]
+ "
+{
+ unsigned HOST_WIDE_INT low = CONST_DOUBLE_LOW (operands[1]);
+ unsigned HOST_WIDE_INT high = CONST_DOUBLE_HIGH (operands[1]);
+ unsigned HOST_WIDE_INT val = low;
+ unsigned HOST_WIDE_INT sign;
+
+ /* Zero-extend the 16 least-significant bits. */
+ val &= 0xffff;
+ operands[1] = GEN_INT (val);
+
+ /* Arithmetic shift right the double-word by 16 bits. */
+ low >>= 16;
+ low |= (high & 0xffff) << (HOST_BITS_PER_WIDE_INT - 16);
+ high >>= 16;
+ sign = 1;
+ sign <<= (HOST_BITS_PER_WIDE_INT - 16 - 1);
+ high ^= sign;
+ high -= sign;
+
+ /* This will only be true if high is a sign-extension of low, i.e.,
+ it must be either 0 or (unsigned)-1, and be zero iff the
+ most-significant bit of low is set. */
+ if (high + (low >> (HOST_BITS_PER_WIDE_INT - 1)) == 0)
+ operands[2] = GEN_INT (low);
+ else
+ operands[2] = immed_double_const (low, high, DImode);
+}")
+
+(define_insn "shori_media"
+ [(set (match_operand:DI 0 "ext_dest_operand" "=r,r")
+ (ior:DI (ashift:DI (match_operand:DI 1 "arith_reg_operand" "0,0")
+ (const_int 16))
+ (match_operand:DI 2 "immediate_operand" "K16Csu,nF")))]
+ "TARGET_SHMEDIA && (reload_completed || arith_reg_dest (operands[0], DImode))"
+ "@
+ shori %u2, %0
+ #"
+ [(set_attr "type" "arith_media,*")])
+
+(define_insn "*shori_media_si"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (ior:SI (ashift:SI (match_operand:SI 1 "arith_reg_operand" "0")
+ (const_int 16))
+ (match_operand:SI 2 "immediate_operand" "K16Csu")))]
+ "TARGET_SHMEDIA"
+ "shori %u2, %0")
+
+(define_expand "movdi"
+ [(set (match_operand:DI 0 "general_movdst_operand" "")
+ (match_operand:DI 1 "general_movsrc_operand" ""))]
+ ""
+ "{ if (prepare_move_operands (operands, DImode)) DONE; }")
+
+(define_insn "movdf_media"
+ [(set (match_operand:DF 0 "general_movdst_operand" "=f,f,r,r,r,f,m,r,m")
+ (match_operand:DF 1 "general_movsrc_operand" "f,rZ,f,r,F,m,f,m,rZ"))]
+ "TARGET_SHMEDIA_FPU
+ && (register_operand (operands[0], DFmode)
+ || sh_register_operand (operands[1], DFmode))"
+ "@
+ fmov.d %1, %0
+ fmov.qd %N1, %0
+ fmov.dq %1, %0
+ add %1, r63, %0
+ #
+ fld%M1.d %m1, %0
+ fst%M0.d %m0, %1
+ ld%M1.q %m1, %0
+ st%M0.q %m0, %N1"
+ [(set_attr "type" "fmove_media,fload_media,dfpconv_media,arith_media,*,fload_media,fstore_media,load_media,store_media")])
+
+(define_insn "movdf_media_nofpu"
+ [(set (match_operand:DF 0 "general_movdst_operand" "=r,r,r,m")
+ (match_operand:DF 1 "general_movsrc_operand" "r,F,m,rZ"))]
+ "TARGET_SHMEDIA
+ && (register_operand (operands[0], DFmode)
+ || sh_register_operand (operands[1], DFmode))"
+ "@
+ add %1, r63, %0
+ #
+ ld%M1.q %m1, %0
+ st%M0.q %m0, %N1"
+ [(set_attr "type" "arith_media,*,load_media,store_media")])
+
+(define_split
+ [(set (match_operand:DF 0 "arith_reg_dest" "")
+ (match_operand:DF 1 "immediate_operand" ""))]
+ "TARGET_SHMEDIA && reload_completed"
+ [(set (match_dup 3) (match_dup 2))]
+ "
+{
+ int endian = WORDS_BIG_ENDIAN ? 1 : 0;
+ long values[2];
+ REAL_VALUE_TYPE value;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (value, operands[1]);
+ REAL_VALUE_TO_TARGET_DOUBLE (value, values);
+
+ if (HOST_BITS_PER_WIDE_INT >= 64)
+ operands[2] = immed_double_const ((unsigned long) values[endian]
+ | ((HOST_WIDE_INT) values[1 - endian]
+ << 32), 0, DImode);
+ else
+ {
+ gcc_assert (HOST_BITS_PER_WIDE_INT == 32);
+ operands[2] = immed_double_const (values[endian], values[1 - endian],
+ DImode);
+ }
+
+ operands[3] = gen_rtx_REG (DImode, true_regnum (operands[0]));
+}")
+
+;; ??? This should be a define expand.
+
+(define_insn "movdf_k"
+ [(set (match_operand:DF 0 "general_movdst_operand" "=r,r,r,m")
+ (match_operand:DF 1 "general_movsrc_operand" "r,FQ,m,r"))]
+ "TARGET_SH1
+ && (! (TARGET_SH4 || TARGET_SH2A_DOUBLE) || reload_completed
+ /* ??? We provide some insn so that direct_{load,store}[DFmode] get set */
+ || (REG_P (operands[0]) && REGNO (operands[0]) == 3)
+ || (REG_P (operands[1]) && REGNO (operands[1]) == 3))
+ && (arith_reg_operand (operands[0], DFmode)
+ || arith_reg_operand (operands[1], DFmode))"
+ "* return output_movedouble (insn, operands, DFmode);"
+ [(set_attr "length" "4")
+ (set_attr "type" "move,pcload,load,store")])
+
+;; All alternatives of movdf_i4 are split for ! TARGET_FMOVD.
+;; However, the d/F/c/z alternative cannot be split directly; it is converted
+;; with special code in machine_dependent_reorg into a load of the R0_REG and
+;; the d/m/c/X alternative, which is split later into single-precision
+;; instructions. And when not optimizing, no splits are done before fixing
+;; up pcloads, so we need usable length information for that.
+(define_insn "movdf_i4"
+ [(set (match_operand:DF 0 "general_movdst_operand" "=d,r,d,d,m,r,r,m,!??r,!???d")
+ (match_operand:DF 1 "general_movsrc_operand" "d,r,F,m,d,FQ,m,r,d,r"))
+ (use (match_operand:PSI 2 "fpscr_operand" "c,c,c,c,c,c,c,c,c,c"))
+ (clobber (match_scratch:SI 3 "=X,X,&z,X,X,X,X,X,X,X"))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE)
+ && (arith_reg_operand (operands[0], DFmode)
+ || arith_reg_operand (operands[1], DFmode))"
+ {
+ switch (which_alternative)
+ {
+ case 0:
+ if (TARGET_FMOVD)
+ return "fmov %1,%0";
+ else if (REGNO (operands[0]) != REGNO (operands[1]) + 1)
+ return "fmov %R1,%R0\n\tfmov %S1,%S0";
+ else
+ return "fmov %S1,%S0\n\tfmov %R1,%R0";
+ case 3:
+ case 4:
+ return "fmov.d %1,%0";
+ default:
+ return "#";
+ }
+ }
+ [(set_attr_alternative "length"
+ [(if_then_else (eq_attr "fmovd" "yes") (const_int 4) (const_int 8))
+ (const_int 4)
+ (if_then_else (eq_attr "fmovd" "yes") (const_int 4) (const_int 6))
+ (if_then_else (eq_attr "fmovd" "yes") (const_int 4) (const_int 6))
+ (if_then_else (eq_attr "fmovd" "yes") (const_int 4) (const_int 6))
+ (const_int 4)
+ (const_int 8) (const_int 8) ;; these need only 8 bytes for @(r0,rn)
+ ;; We can't use 4-byte push/pop on SHcompact, so we have to
+ ;; increment or decrement r15 explicitly.
+ (if_then_else
+ (ne (symbol_ref "TARGET_SHCOMPACT") (const_int 0))
+ (const_int 10) (const_int 8))
+ (if_then_else
+ (ne (symbol_ref "TARGET_SHCOMPACT") (const_int 0))
+ (const_int 10) (const_int 8))])
+ (set_attr "type" "fmove,move,pcfload,fload,fstore,pcload,load,store,load,fload")
+ (set_attr "late_fp_use" "*,*,*,*,yes,*,*,*,*,*")
+ (set (attr "fp_mode") (if_then_else (eq_attr "fmovd" "yes")
+ (const_string "double")
+ (const_string "none")))])
+
+;; Moving DFmode between fp/general registers through memory
+;; (the top of the stack) is faster than moving through fpul even for
+;; little endian. Because the type of an instruction is important for its
+;; scheduling, it is beneficial to split these operations, rather than
+;; emitting them in one single chunk, even if this will expose a stack
+;; use that will prevent scheduling of other stack accesses beyond this
+;; instruction.
+(define_split
+ [(set (match_operand:DF 0 "register_operand" "")
+ (match_operand:DF 1 "register_operand" ""))
+ (use (match_operand:PSI 2 "fpscr_operand" ""))
+ (clobber (match_scratch:SI 3 "=X"))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE) && reload_completed
+ && (true_regnum (operands[0]) < 16) != (true_regnum (operands[1]) < 16)"
+ [(const_int 0)]
+ "
+{
+ rtx insn, tos;
+
+ if (TARGET_SH5 && true_regnum (operands[1]) < 16)
+ {
+ emit_move_insn (stack_pointer_rtx,
+ plus_constant (stack_pointer_rtx, -8));
+ tos = gen_tmp_stack_mem (DFmode, stack_pointer_rtx);
+ }
+ else
+ tos = gen_tmp_stack_mem (DFmode,
+ gen_rtx_PRE_DEC (Pmode, stack_pointer_rtx));
+ insn = emit_insn (gen_movdf_i4 (tos, operands[1], operands[2]));
+ if (! (TARGET_SH5 && true_regnum (operands[1]) < 16))
+ add_reg_note (insn, REG_INC, stack_pointer_rtx);
+ if (TARGET_SH5 && true_regnum (operands[0]) < 16)
+ tos = gen_tmp_stack_mem (DFmode, stack_pointer_rtx);
+ else
+ tos = gen_tmp_stack_mem (DFmode,
+ gen_rtx_POST_INC (Pmode, stack_pointer_rtx));
+ insn = emit_insn (gen_movdf_i4 (operands[0], tos, operands[2]));
+ if (TARGET_SH5 && true_regnum (operands[0]) < 16)
+ emit_move_insn (stack_pointer_rtx, plus_constant (stack_pointer_rtx, 8));
+ else
+ add_reg_note (insn, REG_INC, stack_pointer_rtx);
+ DONE;
+}")
+
+;; local-alloc sometimes allocates scratch registers even when not required,
+;; so we must be prepared to handle these.
+
+;; Remove the use and clobber from a movdf_i4 so that we can use movdf_k.
+(define_split
+ [(set (match_operand:DF 0 "general_movdst_operand" "")
+ (match_operand:DF 1 "general_movsrc_operand" ""))
+ (use (match_operand:PSI 2 "fpscr_operand" ""))
+ (clobber (match_scratch:SI 3 ""))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE)
+ && reload_completed
+ && true_regnum (operands[0]) < 16
+ && true_regnum (operands[1]) < 16"
+ [(set (match_dup 0) (match_dup 1))]
+ "
+{
+ /* If this was a reg <-> mem operation with base + index reg addressing,
+ we have to handle this in a special way. */
+ rtx mem = operands[0];
+ int store_p = 1;
+ if (! memory_operand (mem, DFmode))
+ {
+ mem = operands[1];
+ store_p = 0;
+ }
+ if (GET_CODE (mem) == SUBREG && SUBREG_BYTE (mem) == 0)
+ mem = SUBREG_REG (mem);
+ if (MEM_P (mem))
+ {
+ rtx addr = XEXP (mem, 0);
+ if (GET_CODE (addr) == PLUS
+ && REG_P (XEXP (addr, 0))
+ && REG_P (XEXP (addr, 1)))
+ {
+ int offset;
+ rtx reg0 = gen_rtx_REG (Pmode, 0);
+ rtx regop = operands[store_p], word0 ,word1;
+
+ if (GET_CODE (regop) == SUBREG)
+ alter_subreg (&regop);
+ if (REGNO (XEXP (addr, 0)) == REGNO (XEXP (addr, 1)))
+ offset = 2;
+ else
+ offset = 4;
+ mem = copy_rtx (mem);
+ PUT_MODE (mem, SImode);
+ word0 = gen_rtx_SUBREG (SImode, regop, 0);
+ alter_subreg (&word0);
+ word1 = gen_rtx_SUBREG (SImode, regop, 4);
+ alter_subreg (&word1);
+ if (store_p || ! refers_to_regno_p (REGNO (word0),
+ REGNO (word0) + 1, addr, 0))
+ {
+ emit_insn (store_p
+ ? gen_movsi_ie (mem, word0)
+ : gen_movsi_ie (word0, mem));
+ emit_insn (gen_addsi3 (reg0, reg0, GEN_INT (offset)));
+ mem = copy_rtx (mem);
+ emit_insn (store_p
+ ? gen_movsi_ie (mem, word1)
+ : gen_movsi_ie (word1, mem));
+ emit_insn (gen_addsi3 (reg0, reg0, GEN_INT (-offset)));
+ }
+ else
+ {
+ emit_insn (gen_addsi3 (reg0, reg0, GEN_INT (offset)));
+ emit_insn (gen_movsi_ie (word1, mem));
+ emit_insn (gen_addsi3 (reg0, reg0, GEN_INT (-offset)));
+ mem = copy_rtx (mem);
+ emit_insn (gen_movsi_ie (word0, mem));
+ }
+ DONE;
+ }
+ }
+}")
+
+;; Split away the clobber of r0 after machine_dependent_reorg has fixed pcloads.
+(define_split
+ [(set (match_operand:DF 0 "register_operand" "")
+ (match_operand:DF 1 "memory_operand" ""))
+ (use (match_operand:PSI 2 "fpscr_operand" ""))
+ (clobber (reg:SI R0_REG))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE) && reload_completed"
+ [(parallel [(set (match_dup 0) (match_dup 1))
+ (use (match_dup 2))
+ (clobber (scratch:SI))])]
+ "")
+
+(define_expand "reload_indf__frn"
+ [(parallel [(set (match_operand:DF 0 "register_operand" "=a")
+ (match_operand:DF 1 "immediate_operand" "FQ"))
+ (use (reg:PSI FPSCR_REG))
+ (clobber (match_operand:SI 2 "register_operand" "=&z"))])]
+ "TARGET_SH1"
+ "")
+
+(define_expand "reload_outdf__RnFRm"
+ [(parallel [(set (match_operand:DF 0 "register_operand" "=r,f")
+ (match_operand:DF 1 "register_operand" "af,r"))
+ (clobber (match_operand:SI 2 "register_operand" "=&y,y"))])]
+ "TARGET_SH1"
+ "")
+
+;; Simplify no-op moves.
+(define_split
+ [(set (match_operand:SF 0 "register_operand" "")
+ (match_operand:SF 1 "register_operand" ""))
+ (use (match_operand:PSI 2 "fpscr_operand" ""))
+ (clobber (match_scratch:SI 3 ""))]
+ "TARGET_SH2E && reload_completed
+ && true_regnum (operands[0]) == true_regnum (operands[1])"
+ [(set (match_dup 0) (match_dup 0))]
+ "")
+
+;; fmovd substitute post-reload splits
+(define_split
+ [(set (match_operand:DF 0 "register_operand" "")
+ (match_operand:DF 1 "register_operand" ""))
+ (use (match_operand:PSI 2 "fpscr_operand" ""))
+ (clobber (match_scratch:SI 3 ""))]
+ "TARGET_SH4 && ! TARGET_FMOVD && reload_completed
+ && FP_OR_XD_REGISTER_P (true_regnum (operands[0]))
+ && FP_OR_XD_REGISTER_P (true_regnum (operands[1]))"
+ [(const_int 0)]
+ "
+{
+ int dst = true_regnum (operands[0]), src = true_regnum (operands[1]);
+ emit_insn (gen_movsf_ie (gen_rtx_REG (SFmode, dst),
+ gen_rtx_REG (SFmode, src), operands[2]));
+ emit_insn (gen_movsf_ie (gen_rtx_REG (SFmode, dst + 1),
+ gen_rtx_REG (SFmode, src + 1), operands[2]));
+ DONE;
+}")
+
+(define_split
+ [(set (match_operand:DF 0 "register_operand" "")
+ (mem:DF (match_operand:SI 1 "register_operand" "")))
+ (use (match_operand:PSI 2 "fpscr_operand" ""))
+ (clobber (match_scratch:SI 3 ""))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE) && ! TARGET_FMOVD && reload_completed
+ && FP_OR_XD_REGISTER_P (true_regnum (operands[0]))
+ && find_regno_note (insn, REG_DEAD, true_regnum (operands[1]))"
+ [(const_int 0)]
+ "
+{
+ int regno = true_regnum (operands[0]);
+ rtx insn;
+ rtx mem = SET_SRC (XVECEXP (PATTERN (curr_insn), 0, 0));
+ rtx mem2
+ = change_address (mem, SFmode, gen_rtx_POST_INC (Pmode, operands[1]));
+ insn = emit_insn (gen_movsf_ie (gen_rtx_REG (SFmode,
+ regno + !! TARGET_LITTLE_ENDIAN),
+ mem2, operands[2]));
+ add_reg_note (insn, REG_INC, operands[1]);
+ insn = emit_insn (gen_movsf_ie (gen_rtx_REG (SFmode,
+ regno + ! TARGET_LITTLE_ENDIAN),
+ change_address (mem, SFmode, NULL_RTX),
+ operands[2]));
+ DONE;
+}")
+
+(define_split
+ [(set (match_operand:DF 0 "register_operand" "")
+ (match_operand:DF 1 "memory_operand" ""))
+ (use (match_operand:PSI 2 "fpscr_operand" ""))
+ (clobber (match_scratch:SI 3 ""))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE) && ! TARGET_FMOVD && reload_completed
+ && FP_OR_XD_REGISTER_P (true_regnum (operands[0]))"
+ [(const_int 0)]
+{
+ int regno = true_regnum (operands[0]);
+ rtx addr, insn;
+ rtx mem2 = change_address (operands[1], SFmode, NULL_RTX);
+ rtx reg0 = gen_rtx_REG (SFmode, regno + (TARGET_LITTLE_ENDIAN ? 1 : 0));
+ rtx reg1 = gen_rtx_REG (SFmode, regno + (TARGET_LITTLE_ENDIAN ? 0 : 1));
+
+ operands[1] = copy_rtx (mem2);
+ addr = XEXP (mem2, 0);
+
+ switch (GET_CODE (addr))
+ {
+ case REG:
+ /* This is complicated. If the register is an arithmetic register
+ we can just fall through to the REG+DISP case below. Otherwise
+ we have to use a combination of POST_INC and REG addressing... */
+ if (! arith_reg_operand (operands[1], SFmode))
+ {
+ XEXP (mem2, 0) = addr = gen_rtx_POST_INC (SImode, addr);
+ insn = emit_insn (gen_movsf_ie (reg0, mem2, operands[2]));
+ add_reg_note (insn, REG_INC, XEXP (addr, 0));
+
+ emit_insn (gen_movsf_ie (reg1, operands[1], operands[2]));
+
+ /* If we have modified the stack pointer, the value that we have
+ read with post-increment might be modified by an interrupt,
+ so write it back. */
+ if (REGNO (XEXP (addr, 0)) == STACK_POINTER_REGNUM)
+ emit_insn (gen_push_e (reg0));
+ else
+ emit_insn (gen_addsi3 (XEXP (operands[1], 0), XEXP (operands[1], 0), GEN_INT (-4)));
+ break;
+ }
+ /* Fall through. */
+
+ case PLUS:
+ emit_insn (gen_movsf_ie (reg0, operands[1], operands[2]));
+ operands[1] = copy_rtx (operands[1]);
+ XEXP (operands[1], 0) = plus_constant (addr, 4);
+ emit_insn (gen_movsf_ie (reg1, operands[1], operands[2]));
+ break;
+
+ case POST_INC:
+ insn = emit_insn (gen_movsf_ie (reg0, operands[1], operands[2]));
+ add_reg_note (insn, REG_INC, XEXP (addr, 0));
+
+ insn = emit_insn (gen_movsf_ie (reg1, operands[1], operands[2]));
+ add_reg_note (insn, REG_INC, XEXP (addr, 0));
+ break;
+
+ default:
+ debug_rtx (addr);
+ gcc_unreachable ();
+ }
+
+ DONE;
+})
+
+(define_split
+ [(set (match_operand:DF 0 "memory_operand" "")
+ (match_operand:DF 1 "register_operand" ""))
+ (use (match_operand:PSI 2 "fpscr_operand" ""))
+ (clobber (match_scratch:SI 3 ""))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE) && ! TARGET_FMOVD && reload_completed
+ && FP_OR_XD_REGISTER_P (true_regnum (operands[1]))"
+ [(const_int 0)]
+{
+ int regno = true_regnum (operands[1]);
+ rtx insn, addr;
+ rtx reg0 = gen_rtx_REG (SFmode, regno + (TARGET_LITTLE_ENDIAN ? 1 : 0));
+ rtx reg1 = gen_rtx_REG (SFmode, regno + (TARGET_LITTLE_ENDIAN ? 0 : 1));
+
+ operands[0] = copy_rtx (operands[0]);
+ PUT_MODE (operands[0], SFmode);
+ addr = XEXP (operands[0], 0);
+
+ switch (GET_CODE (addr))
+ {
+ case REG:
+ /* This is complicated. If the register is an arithmetic register
+ we can just fall through to the REG+DISP case below. Otherwise
+ we have to use a combination of REG and PRE_DEC addressing... */
+ if (! arith_reg_operand (operands[0], SFmode))
+ {
+ emit_insn (gen_addsi3 (addr, addr, GEN_INT (4)));
+ emit_insn (gen_movsf_ie (operands[0], reg1, operands[2]));
+
+ operands[0] = copy_rtx (operands[0]);
+ XEXP (operands[0], 0) = addr = gen_rtx_PRE_DEC (SImode, addr);
+
+ insn = emit_insn (gen_movsf_ie (operands[0], reg0, operands[2]));
+ add_reg_note (insn, REG_INC, XEXP (addr, 0));
+ break;
+ }
+ /* Fall through. */
+
+ case PLUS:
+ /* Since REG+DISP addressing has already been decided upon by gcc
+ we can rely upon it having chosen an arithmetic register as the
+ register component of the address. Just emit the lower numbered
+ register first, to the lower address, then the higher numbered
+ register to the higher address. */
+ emit_insn (gen_movsf_ie (operands[0], reg0, operands[2]));
+
+ operands[0] = copy_rtx (operands[0]);
+ XEXP (operands[0], 0) = plus_constant (addr, 4);
+
+ emit_insn (gen_movsf_ie (operands[0], reg1, operands[2]));
+ break;
+
+ case PRE_DEC:
+ /* This is easy. Output the word to go to the higher address
+ first (ie the word in the higher numbered register) then the
+ word to go to the lower address. */
+
+ insn = emit_insn (gen_movsf_ie (operands[0], reg1, operands[2]));
+ add_reg_note (insn, REG_INC, XEXP (addr, 0));
+
+ insn = emit_insn (gen_movsf_ie (operands[0], reg0, operands[2]));
+ add_reg_note (insn, REG_INC, XEXP (addr, 0));
+ break;
+
+ default:
+ /* FAIL; */
+ debug_rtx (addr);
+ gcc_unreachable ();
+ }
+
+ DONE;
+})
+
+;; If the output is a register and the input is memory or a register, we have
+;; to be careful and see which word needs to be loaded first.
+
+(define_split
+ [(set (match_operand:DF 0 "general_movdst_operand" "")
+ (match_operand:DF 1 "general_movsrc_operand" ""))]
+ "TARGET_SH1 && reload_completed"
+ [(set (match_dup 2) (match_dup 3))
+ (set (match_dup 4) (match_dup 5))]
+ "
+{
+ int regno;
+
+ if ((MEM_P (operands[0])
+ && GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
+ || (MEM_P (operands[1])
+ && GET_CODE (XEXP (operands[1], 0)) == POST_INC))
+ FAIL;
+
+ switch (GET_CODE (operands[0]))
+ {
+ case REG:
+ regno = REGNO (operands[0]);
+ break;
+ case SUBREG:
+ regno = subreg_regno (operands[0]);
+ break;
+ case MEM:
+ regno = -1;
+ break;
+ default:
+ gcc_unreachable ();
+ }
+
+ if (regno == -1
+ || ! refers_to_regno_p (regno, regno + 1, operands[1], 0))
+ {
+ operands[2] = operand_subword (operands[0], 0, 0, DFmode);
+ operands[3] = operand_subword (operands[1], 0, 0, DFmode);
+ operands[4] = operand_subword (operands[0], 1, 0, DFmode);
+ operands[5] = operand_subword (operands[1], 1, 0, DFmode);
+ }
+ else
+ {
+ operands[2] = operand_subword (operands[0], 1, 0, DFmode);
+ operands[3] = operand_subword (operands[1], 1, 0, DFmode);
+ operands[4] = operand_subword (operands[0], 0, 0, DFmode);
+ operands[5] = operand_subword (operands[1], 0, 0, DFmode);
+ }
+
+ if (operands[2] == 0 || operands[3] == 0
+ || operands[4] == 0 || operands[5] == 0)
+ FAIL;
+}")
+
+;; If a base address generated by LEGITIMIZE_ADDRESS for SImode is
+;; used only once, let combine add in the index again.
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (match_operand:SI 1 "" ""))
+ (clobber (match_operand 2 "register_operand" ""))]
+ "TARGET_SH1 && ! reload_in_progress && ! reload_completed
+ && ALLOW_INDEXED_ADDRESS"
+ [(use (reg:SI R0_REG))]
+ "
+{
+ rtx addr, reg, const_int;
+
+ if (!MEM_P (operands[1]))
+ FAIL;
+ addr = XEXP (operands[1], 0);
+ if (GET_CODE (addr) != PLUS)
+ FAIL;
+ reg = XEXP (addr, 0);
+ const_int = XEXP (addr, 1);
+ if (! (BASE_REGISTER_RTX_P (reg) && INDEX_REGISTER_RTX_P (operands[2])
+ && CONST_INT_P (const_int)))
+ FAIL;
+ emit_move_insn (operands[2], const_int);
+ emit_move_insn (operands[0],
+ change_address (operands[1], VOIDmode,
+ gen_rtx_PLUS (SImode, reg, operands[2])));
+ DONE;
+}")
+
+(define_split
+ [(set (match_operand:SI 1 "" "")
+ (match_operand:SI 0 "register_operand" ""))
+ (clobber (match_operand 2 "register_operand" ""))]
+ "TARGET_SH1 && ! reload_in_progress && ! reload_completed
+ && ALLOW_INDEXED_ADDRESS"
+ [(use (reg:SI R0_REG))]
+ "
+{
+ rtx addr, reg, const_int;
+
+ if (!MEM_P (operands[1]))
+ FAIL;
+ addr = XEXP (operands[1], 0);
+ if (GET_CODE (addr) != PLUS)
+ FAIL;
+ reg = XEXP (addr, 0);
+ const_int = XEXP (addr, 1);
+ if (! (BASE_REGISTER_RTX_P (reg) && INDEX_REGISTER_RTX_P (operands[2])
+ && CONST_INT_P (const_int)))
+ FAIL;
+ emit_move_insn (operands[2], const_int);
+ emit_move_insn (change_address (operands[1], VOIDmode,
+ gen_rtx_PLUS (SImode, reg, operands[2])),
+ operands[0]);
+ DONE;
+}")
+
+(define_expand "movdf"
+ [(set (match_operand:DF 0 "general_movdst_operand" "")
+ (match_operand:DF 1 "general_movsrc_operand" ""))]
+ ""
+ "
+{
+ if (prepare_move_operands (operands, DFmode)) DONE;
+ if (TARGET_SHMEDIA)
+ {
+ if (TARGET_SHMEDIA_FPU)
+ emit_insn (gen_movdf_media (operands[0], operands[1]));
+ else
+ emit_insn (gen_movdf_media_nofpu (operands[0], operands[1]));
+ DONE;
+ }
+ if (TARGET_SH4 || TARGET_SH2A_DOUBLE)
+ {
+ emit_df_insn (gen_movdf_i4 (operands[0], operands[1], get_fpscr_rtx ()));
+ DONE;
+ }
+}")
+
+;;This is incompatible with the way gcc uses subregs.
+;;(define_insn "movv2sf_i"
+;; [(set (match_operand:V2SF 0 "nonimmediate_operand" "=f,f,m")
+;; (match_operand:V2SF 1 "nonimmediate_operand" "f,m,f"))]
+;; "TARGET_SHMEDIA_FPU
+;; && (fp_arith_reg_operand (operands[0], V2SFmode)
+;; || fp_arith_reg_operand (operands[1], V2SFmode))"
+;; "@
+;; #
+;; fld%M1.p %m1, %0
+;; fst%M0.p %m0, %1"
+;; [(set_attr "type" "*,fload_media,fstore_media")])
+
+(define_insn_and_split "movv2sf_i"
+ [(set (match_operand:V2SF 0 "general_movdst_operand" "=f,rf,r,m,mf")
+ (match_operand:V2SF 1 "general_operand" "fm,rfm?,F?,f,rfZ?"))]
+ "TARGET_SHMEDIA_FPU"
+ "#"
+ "TARGET_SHMEDIA_FPU && reload_completed"
+ [(set (match_dup 0) (match_dup 1))]
+ "
+{
+ operands[0] = simplify_gen_subreg (DFmode, operands[0], V2SFmode, 0);
+ operands[1] = simplify_gen_subreg (DFmode, operands[1], V2SFmode, 0);
+}")
+
+(define_expand "movv2sf"
+ [(set (match_operand:V2SF 0 "general_movdst_operand" "")
+ (match_operand:V2SF 1 "nonimmediate_operand" ""))]
+ "TARGET_SHMEDIA_FPU"
+ "
+{
+ if (prepare_move_operands (operands, V2SFmode))
+ DONE;
+}")
+
+(define_expand "addv2sf3"
+ [(match_operand:V2SF 0 "fp_arith_reg_operand" "")
+ (match_operand:V2SF 1 "fp_arith_reg_operand" "")
+ (match_operand:V2SF 2 "fp_arith_reg_operand" "")]
+ "TARGET_SHMEDIA_FPU"
+ "
+{
+ sh_expand_binop_v2sf (PLUS, operands[0], operands[1], operands[2]);
+ DONE;
+}")
+
+(define_expand "subv2sf3"
+ [(match_operand:V2SF 0 "fp_arith_reg_operand" "")
+ (match_operand:V2SF 1 "fp_arith_reg_operand" "")
+ (match_operand:V2SF 2 "fp_arith_reg_operand" "")]
+ "TARGET_SHMEDIA_FPU"
+ "
+{
+ sh_expand_binop_v2sf (MINUS, operands[0], operands[1], operands[2]);
+ DONE;
+}")
+
+(define_expand "mulv2sf3"
+ [(match_operand:V2SF 0 "fp_arith_reg_operand" "")
+ (match_operand:V2SF 1 "fp_arith_reg_operand" "")
+ (match_operand:V2SF 2 "fp_arith_reg_operand" "")]
+ "TARGET_SHMEDIA_FPU"
+ "
+{
+ sh_expand_binop_v2sf (MULT, operands[0], operands[1], operands[2]);
+ DONE;
+}")
+
+(define_expand "divv2sf3"
+ [(match_operand:V2SF 0 "fp_arith_reg_operand" "")
+ (match_operand:V2SF 1 "fp_arith_reg_operand" "")
+ (match_operand:V2SF 2 "fp_arith_reg_operand" "")]
+ "TARGET_SHMEDIA_FPU"
+ "
+{
+ sh_expand_binop_v2sf (DIV, operands[0], operands[1], operands[2]);
+ DONE;
+}")
+
+(define_insn_and_split "*movv4sf_i"
+ [(set (match_operand:V4SF 0 "general_movdst_operand" "=f,rf,r,m,mf")
+ (match_operand:V4SF 1 "general_operand" "fm,rfm?,F?,f,rfZ?"))]
+ "TARGET_SHMEDIA_FPU"
+ "#"
+ "&& reload_completed"
+ [(const_int 0)]
+ "
+{
+ int i;
+
+ for (i = 0; i < 4/2; i++)
+ {
+ rtx x, y;
+
+ if (MEM_P (operands[0]))
+ x = adjust_address (operands[0], V2SFmode,
+ i * GET_MODE_SIZE (V2SFmode));
+ else
+ x = simplify_gen_subreg (V2SFmode, operands[0], V4SFmode, i * 8);
+
+ if (MEM_P (operands[1]))
+ y = adjust_address (operands[1], V2SFmode,
+ i * GET_MODE_SIZE (V2SFmode));
+ else
+ y = simplify_gen_subreg (V2SFmode, operands[1], V4SFmode, i * 8);
+
+ emit_insn (gen_movv2sf_i (x, y));
+ }
+
+ DONE;
+}"
+ [(set_attr "length" "8")])
+
+(define_expand "movv4sf"
+ [(set (match_operand:V4SF 0 "nonimmediate_operand" "")
+ (match_operand:V4SF 1 "general_operand" ""))]
+ "TARGET_SHMEDIA_FPU"
+ "
+{
+ if (prepare_move_operands (operands, V4SFmode))
+ DONE;
+}")
+
+(define_insn_and_split "*movv16sf_i"
+ [(set (match_operand:V16SF 0 "nonimmediate_operand" "=f,f,m")
+ (match_operand:V16SF 1 "nonimmediate_operand" "f,m,f"))]
+ "TARGET_SHMEDIA_FPU"
+ "#"
+ "&& reload_completed"
+ [(const_int 0)]
+ "
+{
+ int i;
+
+ for (i = 0; i < 16/2; i++)
+ {
+ rtx x,y;
+
+ if (MEM_P (operands[0]))
+ x = adjust_address (operands[0], V2SFmode,
+ i * GET_MODE_SIZE (V2SFmode));
+ else
+ {
+ x = gen_rtx_SUBREG (V2SFmode, operands[0], i * 8);
+ alter_subreg (&x);
+ }
+
+ if (MEM_P (operands[1]))
+ y = adjust_address (operands[1], V2SFmode,
+ i * GET_MODE_SIZE (V2SFmode));
+ else
+ {
+ y = gen_rtx_SUBREG (V2SFmode, operands[1], i * 8);
+ alter_subreg (&y);
+ }
+
+ emit_insn (gen_movv2sf_i (x, y));
+ }
+
+ DONE;
+}"
+ [(set_attr "length" "32")])
+
+(define_expand "movv16sf"
+ [(set (match_operand:V16SF 0 "nonimmediate_operand" "=f,f,m")
+ (match_operand:V16SF 1 "nonimmediate_operand" "f,m,f"))]
+ "TARGET_SHMEDIA_FPU"
+ "
+{
+ if (prepare_move_operands (operands, V16SFmode))
+ DONE;
+}")
+
+(define_insn "movsf_media"
+ [(set (match_operand:SF 0 "general_movdst_operand" "=f,f,r,r,r,f,m,r,m")
+ (match_operand:SF 1 "general_movsrc_operand" "f,rZ,f,r,F,m,f,m,rZ"))]
+ "TARGET_SHMEDIA_FPU
+ && (register_operand (operands[0], SFmode)
+ || sh_register_operand (operands[1], SFmode))"
+ "@
+ fmov.s %1, %0
+ fmov.ls %N1, %0
+ fmov.sl %1, %0
+ add.l %1, r63, %0
+ #
+ fld%M1.s %m1, %0
+ fst%M0.s %m0, %1
+ ld%M1.l %m1, %0
+ st%M0.l %m0, %N1"
+ [(set_attr "type" "fmove_media,fload_media,fpconv_media,arith_media,*,fload_media,fstore_media,load_media,store_media")
+ (set (attr "highpart")
+ (cond [(ne (symbol_ref "sh_contains_memref_p (insn)") (const_int 0))
+ (const_string "user")]
+ (const_string "ignore")))])
+
+(define_insn "movsf_media_nofpu"
+ [(set (match_operand:SF 0 "general_movdst_operand" "=r,r,r,m")
+ (match_operand:SF 1 "general_movsrc_operand" "r,F,m,rZ"))]
+ "TARGET_SHMEDIA
+ && (register_operand (operands[0], SFmode)
+ || sh_register_operand (operands[1], SFmode))"
+ "@
+ add.l %1, r63, %0
+ #
+ ld%M1.l %m1, %0
+ st%M0.l %m0, %N1"
+ [(set_attr "type" "arith_media,*,load_media,store_media")
+ (set (attr "highpart")
+ (cond [(ne (symbol_ref "sh_contains_memref_p (insn)") (const_int 0))
+ (const_string "user")]
+ (const_string "ignore")))])
+
+(define_split
+ [(set (match_operand:SF 0 "arith_reg_dest" "")
+ (match_operand:SF 1 "immediate_operand" ""))]
+ "TARGET_SHMEDIA && reload_completed
+ && ! FP_REGISTER_P (true_regnum (operands[0]))"
+ [(set (match_dup 3) (match_dup 2))]
+ "
+{
+ long values;
+ REAL_VALUE_TYPE value;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (value, operands[1]);
+ REAL_VALUE_TO_TARGET_SINGLE (value, values);
+ operands[2] = GEN_INT (values);
+
+ operands[3] = gen_rtx_REG (DImode, true_regnum (operands[0]));
+}")
+
+(define_insn "movsf_i"
+ [(set (match_operand:SF 0 "general_movdst_operand" "=r,r,r,r,m,l,r")
+ (match_operand:SF 1 "general_movsrc_operand" "r,G,FQ,mr,r,r,l"))]
+ "TARGET_SH1
+ && (! TARGET_SH2E
+ /* ??? We provide some insn so that direct_{load,store}[SFmode] get set */
+ || (REG_P (operands[0]) && REGNO (operands[0]) == 3)
+ || (REG_P (operands[1]) && REGNO (operands[1]) == 3))
+ && (arith_reg_operand (operands[0], SFmode)
+ || arith_reg_operand (operands[1], SFmode))"
+ "@
+ mov %1,%0
+ mov #0,%0
+ mov.l %1,%0
+ mov.l %1,%0
+ mov.l %1,%0
+ lds %1,%0
+ sts %1,%0"
+ [(set_attr "type" "move,move,pcload,load,store,move,move")])
+
+;; We may not split the ry/yr/XX alternatives to movsi_ie, since
+;; update_flow_info would not know where to put REG_EQUAL notes
+;; when the destination changes mode.
+(define_insn "movsf_ie"
+ [(set (match_operand:SF 0 "general_movdst_operand"
+ "=f,r,f,f,fy,f,m,r,r,m,f,y,y,rf,r,y,<,y,y")
+ (match_operand:SF 1 "general_movsrc_operand"
+ "f,r,G,H,FQ,mf,f,FQ,mr,r,y,f,>,fr,y,r,y,>,y"))
+ (use (match_operand:PSI 2 "fpscr_operand" "c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c"))
+ (clobber (match_scratch:SI 3 "=X,X,Bsc,Bsc,&z,X,X,X,X,X,X,X,X,y,X,X,X,X,X"))]
+
+ "TARGET_SH2E
+ && (arith_reg_operand (operands[0], SFmode)
+ || arith_reg_operand (operands[1], SFmode)
+ || arith_reg_operand (operands[3], SImode)
+ || (fpul_operand (operands[0], SFmode)
+ && memory_operand (operands[1], SFmode)
+ && GET_CODE (XEXP (operands[1], 0)) == POST_INC)
+ || (fpul_operand (operands[1], SFmode)
+ && memory_operand (operands[0], SFmode)
+ && GET_CODE (XEXP (operands[0], 0)) == PRE_DEC))"
+ "@
+ fmov %1,%0
+ mov %1,%0
+ fldi0 %0
+ fldi1 %0
+ #
+ fmov.s %1,%0
+ fmov.s %1,%0
+ mov.l %1,%0
+ mov.l %1,%0
+ mov.l %1,%0
+ fsts fpul,%0
+ flds %1,fpul
+ lds.l %1,%0
+ #
+ sts %1,%0
+ lds %1,%0
+ sts.l %1,%0
+ lds.l %1,%0
+ ! move optimized away"
+ [(set_attr "type" "fmove,move,fmove,fmove,pcfload,fload,fstore,pcload,load,store,fmove,fmove,load,*,fpul_gp,gp_fpul,fstore,load,nil")
+ (set_attr "late_fp_use" "*,*,*,*,*,*,yes,*,*,*,*,*,*,*,yes,*,yes,*,*")
+ (set_attr_alternative "length"
+ [(const_int 2)
+ (const_int 2)
+ (const_int 2)
+ (const_int 2)
+ (const_int 4)
+ (if_then_else
+ (ne (symbol_ref "TARGET_SH2A") (const_int 0))
+ (const_int 4) (const_int 2))
+ (if_then_else
+ (ne (symbol_ref "TARGET_SH2A") (const_int 0))
+ (const_int 4) (const_int 2))
+ (const_int 2)
+ (if_then_else
+ (ne (symbol_ref "TARGET_SH2A") (const_int 0))
+ (const_int 4) (const_int 2))
+ (if_then_else
+ (ne (symbol_ref "TARGET_SH2A") (const_int 0))
+ (const_int 4) (const_int 2))
+ (const_int 2)
+ (const_int 2)
+ (const_int 2)
+ (const_int 4)
+ (const_int 2)
+ (const_int 2)
+ (const_int 2)
+ (const_int 2)
+ (const_int 0)])
+ (set (attr "fp_mode") (if_then_else (eq_attr "fmovd" "yes")
+ (const_string "single")
+ (const_string "single")))])
+
+(define_split
+ [(set (match_operand:SF 0 "register_operand" "")
+ (match_operand:SF 1 "register_operand" ""))
+ (use (match_operand:PSI 2 "fpscr_operand" ""))
+ (clobber (reg:SI FPUL_REG))]
+ "TARGET_SH1"
+ [(parallel [(set (reg:SF FPUL_REG) (match_dup 1))
+ (use (match_dup 2))
+ (clobber (scratch:SI))])
+ (parallel [(set (match_dup 0) (reg:SF FPUL_REG))
+ (use (match_dup 2))
+ (clobber (scratch:SI))])]
+ "")
+
+(define_expand "movsf"
+ [(set (match_operand:SF 0 "general_movdst_operand" "")
+ (match_operand:SF 1 "general_movsrc_operand" ""))]
+ ""
+ "
+{
+ if (prepare_move_operands (operands, SFmode))
+ DONE;
+ if (TARGET_SHMEDIA)
+ {
+ if (TARGET_SHMEDIA_FPU)
+ emit_insn (gen_movsf_media (operands[0], operands[1]));
+ else
+ emit_insn (gen_movsf_media_nofpu (operands[0], operands[1]));
+ DONE;
+ }
+ if (TARGET_SH2E)
+ {
+ emit_sf_insn (gen_movsf_ie (operands[0], operands[1], get_fpscr_rtx ()));
+ DONE;
+ }
+}")
+
+(define_insn "mov_nop"
+ [(set (match_operand 0 "any_register_operand" "") (match_dup 0))]
+ "TARGET_SH2E"
+ ""
+ [(set_attr "length" "0")
+ (set_attr "type" "nil")])
+
+(define_expand "reload_insf__frn"
+ [(parallel [(set (match_operand:SF 0 "register_operand" "=a")
+ (match_operand:SF 1 "immediate_operand" "FQ"))
+ (use (reg:PSI FPSCR_REG))
+ (clobber (match_operand:SI 2 "register_operand" "=&z"))])]
+ "TARGET_SH1"
+ "")
+
+(define_expand "reload_insi__i_fpul"
+ [(parallel [(set (match_operand:SI 0 "fpul_operand" "=y")
+ (match_operand:SI 1 "immediate_operand" "i"))
+ (clobber (match_operand:SI 2 "register_operand" "=&z"))])]
+ "TARGET_SH1"
+ "")
+
+(define_expand "ptabs"
+ [(set (match_operand 0 "" "=b") (match_operand 1 "" "r"))]
+ "TARGET_SHMEDIA"
+ "
+{
+ if (!TARGET_PT_FIXED)
+ {
+ rtx eq = operands[1];
+
+ /* ??? For canonical RTL we really should remove any CONST from EQ
+ before wrapping it in the AND, and finally wrap the EQ into a
+ const if is constant. However, for reload we must expose the
+ input register or symbolic constant, and we can't have
+ different insn structures outside of the operands for different
+ alternatives of the same pattern. */
+ eq = gen_rtx_EQ (SImode, gen_rtx_AND (Pmode, eq, GEN_INT (3)),
+ GEN_INT (3));
+ operands[1]
+ = (gen_rtx_IF_THEN_ELSE
+ (PDImode,
+ eq,
+ gen_rtx_MEM (PDImode, operands[1]),
+ gen_rtx_fmt_e (TARGET_SHMEDIA32 ? SIGN_EXTEND : TRUNCATE,
+ PDImode, operands[1])));
+ }
+}")
+
+;; expanded by ptabs expander.
+(define_insn "*extendsipdi_media"
+ [(set (match_operand:PDI 0 "target_reg_operand" "=b,b");
+ (if_then_else:PDI (eq (and:SI (match_operand:SI 1 "target_operand"
+ "r,Csy")
+ (const_int 3))
+ (const_int 3))
+ (mem:PDI (match_dup 1))
+ (sign_extend:PDI (match_dup 1))))]
+ "TARGET_SHMEDIA && !TARGET_PT_FIXED"
+ "@
+ ptabs %1, %0
+ pt %1, %0"
+ [(set_attr "type" "ptabs_media,pt_media")
+ (set_attr "length" "4,*")])
+
+(define_insn "*truncdipdi_media"
+ [(set (match_operand:PDI 0 "target_reg_operand" "=b,b");
+ (if_then_else:PDI (eq (and:DI (match_operand:DI 1 "target_operand"
+ "r,Csy")
+ (const_int 3))
+ (const_int 3))
+ (mem:PDI (match_dup 1))
+ (truncate:PDI (match_dup 1))))]
+ "TARGET_SHMEDIA && !TARGET_PT_FIXED"
+ "@
+ ptabs %1, %0
+ pt %1, %0"
+ [(set_attr "type" "ptabs_media,pt_media")
+ (set_attr "length" "4,*")])
+
+(define_insn "*movsi_y"
+ [(set (match_operand:SI 0 "register_operand" "=y,y")
+ (match_operand:SI 1 "immediate_operand" "Qi,I08"))
+ (clobber (match_scratch:SI 2 "=&z,r"))]
+ "TARGET_SH2E
+ && (reload_in_progress || reload_completed)"
+ "#"
+ [(set_attr "length" "4")
+ (set_attr "type" "pcload,move")])
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (match_operand:SI 1 "immediate_operand" ""))
+ (clobber (match_operand:SI 2 "register_operand" ""))]
+ "TARGET_SH1"
+ [(set (match_dup 2) (match_dup 1))
+ (set (match_dup 0) (match_dup 2))]
+ "")
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (match_operand:SI 1 "memory_operand" ""))
+ (clobber (reg:SI R0_REG))]
+ "TARGET_SH1"
+ [(set (match_dup 0) (match_dup 1))]
+ "")
+
+;; ------------------------------------------------------------------------
+;; Define the real conditional branch instructions.
+;; ------------------------------------------------------------------------
+
+(define_insn "branch_true"
+ [(set (pc) (if_then_else (ne (reg:SI T_REG) (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ "TARGET_SH1"
+ "* return output_branch (1, insn, operands);"
+ [(set_attr "type" "cbranch")])
+
+(define_insn "branch_false"
+ [(set (pc) (if_then_else (eq (reg:SI T_REG) (const_int 0))
+ (label_ref (match_operand 0 "" ""))
+ (pc)))]
+ "TARGET_SH1"
+ "* return output_branch (0, insn, operands);"
+ [(set_attr "type" "cbranch")])
+
+;; Patterns to prevent reorg from re-combining a condbranch with a branch
+;; which destination is too far away.
+;; The const_int_operand is distinct for each branch target; it avoids
+;; unwanted matches with redundant_insn.
+(define_insn "block_branch_redirect"
+ [(set (pc) (unspec [(match_operand 0 "const_int_operand" "")] UNSPEC_BBR))]
+ "TARGET_SH1"
+ ""
+ [(set_attr "length" "0")])
+
+;; This one has the additional purpose to record a possible scratch register
+;; for the following branch.
+;; ??? Unfortunately, just setting the scratch register is not good enough,
+;; because the insn then might be deemed dead and deleted. And we can't
+;; make the use in the jump insn explicit because that would disable
+;; delay slot scheduling from the target.
+(define_insn "indirect_jump_scratch"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (unspec:SI [(match_operand 1 "const_int_operand" "")] UNSPEC_BBR))
+ (set (pc) (unspec [(const_int 0)] UNSPEC_BBR))]
+ "TARGET_SH1"
+ ""
+ [(set_attr "length" "0")])
+
+;; This one is used to preemt an insn from beyond the bra / braf / jmp
+;; being pulled into the delay slot of a condbranch that has been made to
+;; jump around the unconditional jump because it was out of range.
+(define_insn "stuff_delay_slot"
+ [(set (pc)
+ (unspec [(match_operand:SI 0 "const_int_operand" "") (pc)
+ (match_operand:SI 1 "const_int_operand" "")] UNSPEC_BBR))]
+ "TARGET_SH1"
+ ""
+ [(set_attr "length" "0")
+ (set_attr "cond_delay_slot" "yes")])
+
+;; Conditional branch insns
+
+(define_expand "cbranchint4_media"
+ [(set (pc)
+ (if_then_else (match_operator 0 "shmedia_cbranch_comparison_operator"
+ [(match_operand 1 "" "")
+ (match_operand 2 "" "")])
+ (match_operand 3 "" "")
+ (pc)))]
+ "TARGET_SHMEDIA"
+ "
+{
+ enum machine_mode mode = GET_MODE (operands[1]);
+ if (mode == VOIDmode)
+ mode = GET_MODE (operands[2]);
+ if (GET_CODE (operands[0]) == EQ || GET_CODE (operands[0]) == NE)
+ {
+ operands[1] = force_reg (mode, operands[1]);
+ if (CONSTANT_P (operands[2])
+ && (! satisfies_constraint_I06 (operands[2])))
+ operands[2] = force_reg (mode, operands[2]);
+ }
+ else
+ {
+ if (operands[1] != const0_rtx)
+ operands[1] = force_reg (mode, operands[1]);
+ if (operands[2] != const0_rtx)
+ operands[2] = force_reg (mode, operands[2]);
+ }
+ switch (GET_CODE (operands[0]))
+ {
+ case LEU:
+ case LE:
+ case LTU:
+ case LT:
+ operands[0] = gen_rtx_fmt_ee (swap_condition (GET_CODE (operands[0])),
+ VOIDmode, operands[2], operands[1]);
+ operands[1] = XEXP (operands[0], 0);
+ operands[2] = XEXP (operands[0], 1);
+ break;
+ default:
+ operands[0] = gen_rtx_fmt_ee (GET_CODE (operands[0]),
+ VOIDmode, operands[1], operands[2]);
+ break;
+ }
+ operands[3] = gen_rtx_LABEL_REF (Pmode, operands[3]);
+}")
+
+(define_expand "cbranchfp4_media"
+ [(set (pc)
+ (if_then_else (match_operator 0 "sh_float_comparison_operator"
+ [(match_operand 1 "" "")
+ (match_operand 2 "" "")])
+ (match_operand 3 "" "")
+ (pc)))]
+ "TARGET_SHMEDIA"
+ "
+{
+ rtx tmp = gen_reg_rtx (SImode);
+ rtx cmp;
+ if (GET_CODE (operands[0]) == NE)
+ cmp = gen_rtx_EQ (SImode, operands[1], operands[2]);
+ else
+ cmp = gen_rtx_fmt_ee (GET_CODE (operands[0]), SImode,
+ operands[1], operands[2]);
+
+ emit_insn (gen_cstore4_media (tmp, cmp, operands[1], operands[2]));
+
+ if (GET_CODE (cmp) == GET_CODE (operands[0]))
+ operands[0] = gen_rtx_NE (VOIDmode, tmp, const0_rtx);
+ else
+ operands[0] = gen_rtx_EQ (VOIDmode, tmp, const0_rtx);
+ operands[1] = tmp;
+ operands[2] = const0_rtx;
+ operands[3] = gen_rtx_LABEL_REF (Pmode, operands[3]);
+}")
+
+(define_insn "*beq_media_i"
+ [(set (pc)
+ (if_then_else (match_operator 3 "equality_comparison_operator"
+ [(match_operand:DI 1 "arith_reg_operand" "r,r")
+ (match_operand:DI 2 "arith_operand" "r,I06")])
+ (match_operand 0 "target_operand" "b,b")
+ (pc)))]
+ "TARGET_SHMEDIA"
+ "@
+ b%o3%' %1, %2, %0%>
+ b%o3i%' %1, %2, %0%>"
+ [(set_attr "type" "cbranch_media")])
+
+(define_insn "*beq_media_i32"
+ [(set (pc)
+ (if_then_else (match_operator 3 "equality_comparison_operator"
+ [(match_operand:SI 1 "arith_reg_operand" "r,r")
+ (match_operand:SI 2 "arith_operand" "r,I06")])
+ (match_operand 0 "target_operand" "b,b")
+ (pc)))]
+ "TARGET_SHMEDIA"
+ "@
+ b%o3%' %1, %2, %0%>
+ b%o3i%' %1, %2, %0%>"
+ [(set_attr "type" "cbranch_media")])
+
+(define_insn "*bgt_media_i"
+ [(set (pc)
+ (if_then_else (match_operator 3 "greater_comparison_operator"
+ [(match_operand:DI 1 "arith_reg_or_0_operand" "rN")
+ (match_operand:DI 2 "arith_reg_or_0_operand" "rN")])
+ (match_operand 0 "target_operand" "b")
+ (pc)))]
+ "TARGET_SHMEDIA"
+ "b%o3%' %N1, %N2, %0%>"
+ [(set_attr "type" "cbranch_media")])
+
+(define_insn "*bgt_media_i32"
+ [(set (pc)
+ (if_then_else (match_operator 3 "greater_comparison_operator"
+ [(match_operand:SI 1 "arith_reg_or_0_operand" "rN")
+ (match_operand:SI 2 "arith_reg_or_0_operand" "rN")])
+ (match_operand 0 "target_operand" "b")
+ (pc)))]
+ "TARGET_SHMEDIA"
+ "b%o3%' %N1, %N2, %0%>"
+ [(set_attr "type" "cbranch_media")])
+
+;; These are only needed to make invert_jump() happy - otherwise, jump
+;; optimization will be silently disabled.
+(define_insn "*blt_media_i"
+ [(set (pc)
+ (if_then_else (match_operator 3 "less_comparison_operator"
+ [(match_operand:DI 1 "arith_reg_or_0_operand" "rN")
+ (match_operand:DI 2 "arith_reg_or_0_operand" "rN")])
+ (match_operand 0 "target_operand" "b")
+ (pc)))]
+ "TARGET_SHMEDIA"
+ "b%o3%' %N2, %N1, %0%>"
+ [(set_attr "type" "cbranch_media")])
+
+(define_insn "*blt_media_i32"
+ [(set (pc)
+ (if_then_else (match_operator 3 "less_comparison_operator"
+ [(match_operand:SI 1 "arith_reg_or_0_operand" "rN")
+ (match_operand:SI 2 "arith_reg_or_0_operand" "rN")])
+ (match_operand 0 "target_operand" "b")
+ (pc)))]
+ "TARGET_SHMEDIA"
+ "b%o3%' %N2, %N1, %0%>"
+ [(set_attr "type" "cbranch_media")])
+
+;; combiner splitter for test-and-branch on single bit in register. This
+;; is endian dependent because the non-paradoxical subreg looks different
+;; on big endian.
+(define_split
+ [(set (pc)
+ (if_then_else
+ (match_operator 3 "equality_comparison_operator"
+ [(subreg:SI (zero_extract:DI (subreg:DI (match_operand:SI 1
+ "extend_reg_operand" "")
+ 0)
+ (const_int 1)
+ (match_operand 2
+ "const_int_operand" "")) 0)
+ (const_int 0)])
+ (match_operand 0 "target_operand" "")
+ (pc)))
+ (clobber (match_operand:SI 4 "arith_reg_dest" ""))]
+ "TARGET_SHMEDIA && TARGET_LITTLE_ENDIAN"
+ [(set (match_dup 4) (ashift:SI (match_dup 1) (match_dup 5)))
+ (set (pc) (if_then_else (match_dup 6) (match_dup 0) (pc)))]
+
+ "
+{
+ operands[5] = GEN_INT (31 - INTVAL (operands[2]));
+ operands[6] = (GET_CODE (operands[3]) == EQ
+ ? gen_rtx_GE (VOIDmode, operands[4], const0_rtx)
+ : gen_rtx_GT (VOIDmode, const0_rtx, operands[4]));
+}")
+
+; operand 0 is the loop count pseudo register
+; operand 1 is the number of loop iterations or 0 if it is unknown
+; operand 2 is the maximum number of loop iterations
+; operand 3 is the number of levels of enclosed loops
+; operand 4 is the label to jump to at the top of the loop
+
+(define_expand "doloop_end"
+ [(parallel [(set (pc) (if_then_else
+ (ne:SI (match_operand:SI 0 "" "")
+ (const_int 1))
+ (label_ref (match_operand 4 "" ""))
+ (pc)))
+ (set (match_dup 0)
+ (plus:SI (match_dup 0) (const_int -1)))
+ (clobber (reg:SI T_REG))])]
+ "TARGET_SH2"
+ "
+{
+ if (GET_MODE (operands[0]) != SImode)
+ FAIL;
+}
+")
+
+(define_insn_and_split "doloop_end_split"
+ [(set (pc)
+ (if_then_else (ne:SI (match_operand:SI 2 "arith_reg_dest" "0")
+ (const_int 1))
+ (label_ref (match_operand 1 "" ""))
+ (pc)))
+ (set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (plus (match_dup 2) (const_int -1)))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH2"
+ "#"
+ ""
+ [(parallel [(set (reg:SI T_REG)
+ (eq:SI (match_dup 2) (const_int 1)))
+ (set (match_dup 0) (plus:SI (match_dup 2) (const_int -1)))])
+ (set (pc) (if_then_else (eq (reg:SI T_REG) (const_int 0))
+ (label_ref (match_dup 1))
+ (pc)))]
+""
+ [(set_attr "type" "cbranch")])
+
+
+;; ------------------------------------------------------------------------
+;; Jump and linkage insns
+;; ------------------------------------------------------------------------
+
+(define_insn "jump_compact"
+ [(set (pc)
+ (label_ref (match_operand 0 "" "")))]
+ "TARGET_SH1 && !find_reg_note (insn, REG_CROSSING_JUMP, NULL_RTX)"
+ "*
+{
+ /* The length is 16 if the delay slot is unfilled. */
+ if (get_attr_length(insn) > 4)
+ return output_far_jump(insn, operands[0]);
+ else
+ return \"bra %l0%#\";
+}"
+ [(set_attr "type" "jump")
+ (set_attr "needs_delay_slot" "yes")])
+
+;; ??? It would be much saner to explicitly use the scratch register
+;; in the jump insn, and have indirect_jump_scratch only set it,
+;; but fill_simple_delay_slots would refuse to do delay slot filling
+;; from the target then, as it uses simplejump_p.
+;;(define_insn "jump_compact_far"
+;; [(set (pc)
+;; (label_ref (match_operand 0 "" "")))
+;; (use (match_operand 1 "register_operand" "r")]
+;; "TARGET_SH1"
+;; "* return output_far_jump(insn, operands[0], operands[1]);"
+;; [(set_attr "type" "jump")
+;; (set_attr "needs_delay_slot" "yes")])
+
+(define_insn "jump_media"
+ [(set (pc)
+ (match_operand 0 "target_operand" "b"))]
+ "TARGET_SHMEDIA"
+ "blink %0, r63%>"
+ [(set_attr "type" "jump_media")])
+
+(define_expand "jump"
+ [(set (pc)
+ (label_ref (match_operand 0 "" "")))]
+ ""
+ "
+{
+ if (TARGET_SH1)
+ emit_jump_insn (gen_jump_compact (operands[0]));
+ else if (TARGET_SHMEDIA)
+ {
+ if (reload_in_progress || reload_completed)
+ FAIL;
+ emit_jump_insn (gen_jump_media (gen_rtx_LABEL_REF (Pmode,
+ operands[0])));
+ }
+ DONE;
+}")
+
+(define_insn "force_mode_for_call"
+ [(use (reg:PSI FPSCR_REG))]
+ "TARGET_SHCOMPACT"
+ ""
+ [(set_attr "length" "0")
+ (set (attr "fp_mode")
+ (if_then_else (eq_attr "fpu_single" "yes")
+ (const_string "single") (const_string "double")))])
+
+(define_insn "calli"
+ [(call (mem:SI (match_operand:SI 0 "arith_reg_operand" "r"))
+ (match_operand 1 "" ""))
+ (use (reg:PSI FPSCR_REG))
+ (clobber (reg:SI PR_REG))]
+ "TARGET_SH1"
+ "*
+ {
+ if (TARGET_SH2A && (dbr_sequence_length () == 0))
+ return \"jsr/n\\t@%0\";
+ else
+ return \"jsr\\t@%0%#\";
+ }"
+
+ [(set_attr "type" "call")
+ (set (attr "fp_mode")
+ (if_then_else (eq_attr "fpu_single" "yes")
+ (const_string "single") (const_string "double")))
+ (set_attr "needs_delay_slot" "yes")
+ (set_attr "fp_set" "unknown")])
+
+;; This is TBR relative jump instruction for SH2A architecture.
+;; Its use is enabled assigning an attribute "function_vector"
+;; and the vector number to a function during its declaration.
+
+(define_insn "calli_tbr_rel"
+ [(call (mem (match_operand:SI 0 "symbol_ref_operand" ""))
+ (match_operand 1 "" ""))
+ (use (reg:PSI FPSCR_REG))
+ (clobber (reg:SI PR_REG))]
+ "TARGET_SH2A && sh2a_is_function_vector_call (operands[0])"
+ "*
+{
+ unsigned HOST_WIDE_INT vect_num;
+ vect_num = sh2a_get_function_vector_number (operands[0]);
+ operands[2] = GEN_INT (vect_num * 4);
+
+ return \"jsr/n\\t@@(%O2,tbr)\";
+}"
+ [(set_attr "type" "call")
+ (set (attr "fp_mode")
+ (if_then_else (eq_attr "fpu_single" "yes")
+ (const_string "single") (const_string "double")))
+ (set_attr "needs_delay_slot" "no")
+ (set_attr "fp_set" "unknown")])
+
+;; This is a pc-rel call, using bsrf, for use with PIC.
+
+(define_insn "calli_pcrel"
+ [(call (mem:SI (match_operand:SI 0 "arith_reg_operand" "r"))
+ (match_operand 1 "" ""))
+ (use (reg:PSI FPSCR_REG))
+ (use (reg:SI PIC_REG))
+ (use (match_operand 2 "" ""))
+ (clobber (reg:SI PR_REG))]
+ "TARGET_SH2"
+ "bsrf %0\\n%O2:%#"
+ [(set_attr "type" "call")
+ (set (attr "fp_mode")
+ (if_then_else (eq_attr "fpu_single" "yes")
+ (const_string "single") (const_string "double")))
+ (set_attr "needs_delay_slot" "yes")
+ (set_attr "fp_set" "unknown")])
+
+(define_insn_and_split "call_pcrel"
+ [(call (mem:SI (match_operand:SI 0 "symbol_ref_operand" ""))
+ (match_operand 1 "" ""))
+ (use (reg:PSI FPSCR_REG))
+ (use (reg:SI PIC_REG))
+ (clobber (reg:SI PR_REG))
+ (clobber (match_scratch:SI 2 "=r"))]
+ "TARGET_SH2"
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+ "
+{
+ rtx lab = PATTERN (gen_call_site ());
+
+ if (SYMBOL_REF_LOCAL_P (operands[0]))
+ emit_insn (gen_sym_label2reg (operands[2], operands[0], lab));
+ else
+ emit_insn (gen_symPLT_label2reg (operands[2], operands[0], lab));
+ emit_call_insn (gen_calli_pcrel (operands[2], operands[1], copy_rtx (lab)));
+ DONE;
+}"
+ [(set_attr "type" "call")
+ (set (attr "fp_mode")
+ (if_then_else (eq_attr "fpu_single" "yes")
+ (const_string "single") (const_string "double")))
+ (set_attr "needs_delay_slot" "yes")
+ (set_attr "fp_set" "unknown")])
+
+(define_insn "call_compact"
+ [(call (mem:SI (match_operand:SI 0 "arith_reg_operand" "r"))
+ (match_operand 1 "" ""))
+ (match_operand 2 "immediate_operand" "n")
+ (use (reg:SI R0_REG))
+ (use (reg:SI R1_REG))
+ (use (reg:PSI FPSCR_REG))
+ (clobber (reg:SI PR_REG))]
+ "TARGET_SHCOMPACT && ! (INTVAL (operands[2]) & CALL_COOKIE_RET_TRAMP (1))"
+ "jsr @%0%#"
+ [(set_attr "type" "call")
+ (set (attr "fp_mode")
+ (if_then_else (eq_attr "fpu_single" "yes")
+ (const_string "single") (const_string "double")))
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_insn "call_compact_rettramp"
+ [(call (mem:SI (match_operand:SI 0 "arith_reg_operand" "r"))
+ (match_operand 1 "" ""))
+ (match_operand 2 "immediate_operand" "n")
+ (use (reg:SI R0_REG))
+ (use (reg:SI R1_REG))
+ (use (reg:PSI FPSCR_REG))
+ (clobber (reg:SI R10_REG))
+ (clobber (reg:SI PR_REG))]
+ "TARGET_SHCOMPACT && (INTVAL (operands[2]) & CALL_COOKIE_RET_TRAMP (1))"
+ "jsr @%0%#"
+ [(set_attr "type" "call")
+ (set (attr "fp_mode")
+ (if_then_else (eq_attr "fpu_single" "yes")
+ (const_string "single") (const_string "double")))
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_insn "call_media"
+ [(call (mem:DI (match_operand 0 "target_reg_operand" "b"))
+ (match_operand 1 "" ""))
+ (clobber (reg:DI PR_MEDIA_REG))]
+ "TARGET_SHMEDIA"
+ "blink %0, r18"
+ [(set_attr "type" "jump_media")])
+
+(define_insn "call_valuei"
+ [(set (match_operand 0 "" "=rf")
+ (call (mem:SI (match_operand:SI 1 "arith_reg_operand" "r"))
+ (match_operand 2 "" "")))
+ (use (reg:PSI FPSCR_REG))
+ (clobber (reg:SI PR_REG))]
+ "TARGET_SH1"
+ "*
+ {
+ if (TARGET_SH2A && (dbr_sequence_length () == 0))
+ return \"jsr/n\\t@%1\";
+ else
+ return \"jsr\\t@%1%#\";
+ }"
+ [(set_attr "type" "call")
+ (set (attr "fp_mode")
+ (if_then_else (eq_attr "fpu_single" "yes")
+ (const_string "single") (const_string "double")))
+ (set_attr "needs_delay_slot" "yes")
+ (set_attr "fp_set" "unknown")])
+
+;; This is TBR relative jump instruction for SH2A architecture.
+;; Its use is enabled assigning an attribute "function_vector"
+;; and the vector number to a function during its declaration.
+
+(define_insn "call_valuei_tbr_rel"
+ [(set (match_operand 0 "" "=rf")
+ (call (mem:SI (match_operand:SI 1 "symbol_ref_operand" ""))
+ (match_operand 2 "" "")))
+ (use (reg:PSI FPSCR_REG))
+ (clobber (reg:SI PR_REG))]
+ "TARGET_SH2A && sh2a_is_function_vector_call (operands[1])"
+ "*
+{
+ unsigned HOST_WIDE_INT vect_num;
+ vect_num = sh2a_get_function_vector_number (operands[1]);
+ operands[3] = GEN_INT (vect_num * 4);
+
+ return \"jsr/n\\t@@(%O3,tbr)\";
+}"
+ [(set_attr "type" "call")
+ (set (attr "fp_mode")
+ (if_then_else (eq_attr "fpu_single" "yes")
+ (const_string "single") (const_string "double")))
+ (set_attr "needs_delay_slot" "no")
+ (set_attr "fp_set" "unknown")])
+
+(define_insn "call_valuei_pcrel"
+ [(set (match_operand 0 "" "=rf")
+ (call (mem:SI (match_operand:SI 1 "arith_reg_operand" "r"))
+ (match_operand 2 "" "")))
+ (use (reg:PSI FPSCR_REG))
+ (use (reg:SI PIC_REG))
+ (use (match_operand 3 "" ""))
+ (clobber (reg:SI PR_REG))]
+ "TARGET_SH2"
+ "bsrf %1\\n%O3:%#"
+ [(set_attr "type" "call")
+ (set (attr "fp_mode")
+ (if_then_else (eq_attr "fpu_single" "yes")
+ (const_string "single") (const_string "double")))
+ (set_attr "needs_delay_slot" "yes")
+ (set_attr "fp_set" "unknown")])
+
+(define_insn_and_split "call_value_pcrel"
+ [(set (match_operand 0 "" "=rf")
+ (call (mem:SI (match_operand:SI 1 "symbol_ref_operand" ""))
+ (match_operand 2 "" "")))
+ (use (reg:PSI FPSCR_REG))
+ (use (reg:SI PIC_REG))
+ (clobber (reg:SI PR_REG))
+ (clobber (match_scratch:SI 3 "=r"))]
+ "TARGET_SH2"
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+ "
+{
+ rtx lab = PATTERN (gen_call_site ());
+
+ if (SYMBOL_REF_LOCAL_P (operands[1]))
+ emit_insn (gen_sym_label2reg (operands[3], operands[1], lab));
+ else
+ emit_insn (gen_symPLT_label2reg (operands[3], operands[1], lab));
+ emit_call_insn (gen_call_valuei_pcrel (operands[0], operands[3],
+ operands[2], copy_rtx (lab)));
+ DONE;
+}"
+ [(set_attr "type" "call")
+ (set (attr "fp_mode")
+ (if_then_else (eq_attr "fpu_single" "yes")
+ (const_string "single") (const_string "double")))
+ (set_attr "needs_delay_slot" "yes")
+ (set_attr "fp_set" "unknown")])
+
+(define_insn "call_value_compact"
+ [(set (match_operand 0 "" "=rf")
+ (call (mem:SI (match_operand:SI 1 "arith_reg_operand" "r"))
+ (match_operand 2 "" "")))
+ (match_operand 3 "immediate_operand" "n")
+ (use (reg:SI R0_REG))
+ (use (reg:SI R1_REG))
+ (use (reg:PSI FPSCR_REG))
+ (clobber (reg:SI PR_REG))]
+ "TARGET_SHCOMPACT && ! (INTVAL (operands[3]) & CALL_COOKIE_RET_TRAMP (1))"
+ "jsr @%1%#"
+ [(set_attr "type" "call")
+ (set (attr "fp_mode")
+ (if_then_else (eq_attr "fpu_single" "yes")
+ (const_string "single") (const_string "double")))
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_insn "call_value_compact_rettramp"
+ [(set (match_operand 0 "" "=rf")
+ (call (mem:SI (match_operand:SI 1 "arith_reg_operand" "r"))
+ (match_operand 2 "" "")))
+ (match_operand 3 "immediate_operand" "n")
+ (use (reg:SI R0_REG))
+ (use (reg:SI R1_REG))
+ (use (reg:PSI FPSCR_REG))
+ (clobber (reg:SI R10_REG))
+ (clobber (reg:SI PR_REG))]
+ "TARGET_SHCOMPACT && (INTVAL (operands[3]) & CALL_COOKIE_RET_TRAMP (1))"
+ "jsr @%1%#"
+ [(set_attr "type" "call")
+ (set (attr "fp_mode")
+ (if_then_else (eq_attr "fpu_single" "yes")
+ (const_string "single") (const_string "double")))
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_insn "call_value_media"
+ [(set (match_operand 0 "" "=rf")
+ (call (mem:DI (match_operand 1 "target_reg_operand" "b"))
+ (match_operand 2 "" "")))
+ (clobber (reg:DI PR_MEDIA_REG))]
+ "TARGET_SHMEDIA"
+ "blink %1, r18"
+ [(set_attr "type" "jump_media")])
+
+(define_expand "call"
+ [(parallel [(call (mem:SI (match_operand 0 "arith_reg_operand" ""))
+ (match_operand 1 "" ""))
+ (match_operand 2 "" "")
+ (use (reg:PSI FPSCR_REG))
+ (clobber (reg:SI PR_REG))])]
+ ""
+ "
+{
+ if (TARGET_SHMEDIA)
+ {
+ operands[0] = shmedia_prepare_call_address (operands[0], 0);
+ emit_call_insn (gen_call_media (operands[0], operands[1]));
+ DONE;
+ }
+ else if (TARGET_SHCOMPACT && operands[2] && INTVAL (operands[2]))
+ {
+ rtx cookie_rtx = operands[2];
+ long cookie = INTVAL (cookie_rtx);
+ rtx func = XEXP (operands[0], 0);
+ rtx r0, r1;
+
+ if (flag_pic)
+ {
+ if (GET_CODE (func) == SYMBOL_REF && ! SYMBOL_REF_LOCAL_P (func))
+ {
+ rtx reg = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symGOTPLT2reg (reg, func));
+ func = reg;
+ }
+ else
+ func = legitimize_pic_address (func, Pmode, 0);
+ }
+
+ r0 = gen_rtx_REG (SImode, R0_REG);
+ r1 = gen_rtx_REG (SImode, R1_REG);
+
+ /* Since such a call function may use all call-clobbered
+ registers, we force a mode switch earlier, so that we don't
+ run out of registers when adjusting fpscr for the call. */
+ emit_insn (gen_force_mode_for_call ());
+
+ operands[0]
+ = function_symbol (NULL, \"__GCC_shcompact_call_trampoline\",
+ SFUNC_GOT);
+ operands[0] = force_reg (SImode, operands[0]);
+
+ emit_move_insn (r0, func);
+ emit_move_insn (r1, cookie_rtx);
+
+ if (cookie & CALL_COOKIE_RET_TRAMP (1))
+ emit_call_insn (gen_call_compact_rettramp (operands[0], operands[1],
+ operands[2]));
+ else
+ emit_call_insn (gen_call_compact (operands[0], operands[1],
+ operands[2]));
+
+ DONE;
+ }
+ else if (TARGET_SHCOMPACT && flag_pic
+ && GET_CODE (XEXP (operands[0], 0)) == SYMBOL_REF
+ && ! SYMBOL_REF_LOCAL_P (XEXP (operands[0], 0)))
+ {
+ rtx reg = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symGOTPLT2reg (reg, XEXP (operands[0], 0)));
+ XEXP (operands[0], 0) = reg;
+ }
+ if (!flag_pic && TARGET_SH2A
+ && MEM_P (operands[0])
+ && GET_CODE (XEXP (operands[0], 0)) == SYMBOL_REF)
+ {
+ if (sh2a_is_function_vector_call (XEXP (operands[0], 0)))
+ {
+ emit_call_insn (gen_calli_tbr_rel (XEXP (operands[0], 0),
+ operands[1]));
+ DONE;
+ }
+ }
+ if (flag_pic && TARGET_SH2
+ && MEM_P (operands[0])
+ && GET_CODE (XEXP (operands[0], 0)) == SYMBOL_REF)
+ {
+ emit_call_insn (gen_call_pcrel (XEXP (operands[0], 0), operands[1]));
+ DONE;
+ }
+ else
+ {
+ operands[0] = force_reg (SImode, XEXP (operands[0], 0));
+ operands[1] = operands[2];
+ }
+
+ emit_call_insn (gen_calli (operands[0], operands[1]));
+ DONE;
+}")
+
+(define_insn "call_pop_compact"
+ [(call (mem:SI (match_operand:SI 0 "arith_reg_operand" "r"))
+ (match_operand 1 "" ""))
+ (match_operand 2 "immediate_operand" "n")
+ (set (reg:SI SP_REG) (plus:SI (reg:SI SP_REG)
+ (match_operand 3 "immediate_operand" "n")))
+ (use (reg:SI R0_REG))
+ (use (reg:SI R1_REG))
+ (use (reg:PSI FPSCR_REG))
+ (clobber (reg:SI PR_REG))]
+ "TARGET_SHCOMPACT && ! (INTVAL (operands[2]) & CALL_COOKIE_RET_TRAMP (1))"
+ "jsr @%0%#"
+ [(set_attr "type" "call")
+ (set (attr "fp_mode")
+ (if_then_else (eq_attr "fpu_single" "yes")
+ (const_string "single") (const_string "double")))
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_insn "call_pop_compact_rettramp"
+ [(call (mem:SI (match_operand:SI 0 "arith_reg_operand" "r"))
+ (match_operand 1 "" ""))
+ (match_operand 2 "immediate_operand" "n")
+ (set (reg:SI SP_REG) (plus:SI (reg:SI SP_REG)
+ (match_operand 3 "immediate_operand" "n")))
+ (use (reg:SI R0_REG))
+ (use (reg:SI R1_REG))
+ (use (reg:PSI FPSCR_REG))
+ (clobber (reg:SI R10_REG))
+ (clobber (reg:SI PR_REG))]
+ "TARGET_SHCOMPACT && (INTVAL (operands[2]) & CALL_COOKIE_RET_TRAMP (1))"
+ "jsr @%0%#"
+ [(set_attr "type" "call")
+ (set (attr "fp_mode")
+ (if_then_else (eq_attr "fpu_single" "yes")
+ (const_string "single") (const_string "double")))
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_expand "call_pop"
+ [(parallel [(call (mem:SI (match_operand 0 "arith_reg_operand" ""))
+ (match_operand 1 "" ""))
+ (match_operand 2 "" "")
+ (set (reg:SI SP_REG) (plus:SI (reg:SI SP_REG)
+ (match_operand 3 "" "")))])]
+ "TARGET_SHCOMPACT"
+ "
+{
+ rtx cookie_rtx;
+ long cookie;
+ rtx func;
+ rtx r0, r1;
+
+ gcc_assert (operands[2] && INTVAL (operands[2]));
+ cookie_rtx = operands[2];
+ cookie = INTVAL (cookie_rtx);
+ func = XEXP (operands[0], 0);
+
+ if (flag_pic)
+ {
+ if (GET_CODE (func) == SYMBOL_REF && ! SYMBOL_REF_LOCAL_P (func))
+ {
+ rtx reg = gen_reg_rtx (Pmode);
+ emit_insn (gen_symGOTPLT2reg (reg, func));
+ func = reg;
+ }
+ else
+ func = legitimize_pic_address (func, Pmode, 0);
+ }
+
+ r0 = gen_rtx_REG (SImode, R0_REG);
+ r1 = gen_rtx_REG (SImode, R1_REG);
+
+ /* Since such a call function may use all call-clobbered
+ registers, we force a mode switch earlier, so that we don't
+ run out of registers when adjusting fpscr for the call. */
+ emit_insn (gen_force_mode_for_call ());
+
+ operands[0] = function_symbol (NULL, \"__GCC_shcompact_call_trampoline\",
+ SFUNC_GOT);
+ operands[0] = force_reg (SImode, operands[0]);
+
+ emit_move_insn (r0, func);
+ emit_move_insn (r1, cookie_rtx);
+
+ if (cookie & CALL_COOKIE_RET_TRAMP (1))
+ emit_call_insn (gen_call_pop_compact_rettramp
+ (operands[0], operands[1], operands[2], operands[3]));
+ else
+ emit_call_insn (gen_call_pop_compact
+ (operands[0], operands[1], operands[2], operands[3]));
+
+ DONE;
+}")
+
+(define_expand "call_value"
+ [(parallel [(set (match_operand 0 "arith_reg_operand" "")
+ (call (mem:SI (match_operand 1 "arith_reg_operand" ""))
+ (match_operand 2 "" "")))
+ (match_operand 3 "" "")
+ (use (reg:PSI FPSCR_REG))
+ (clobber (reg:SI PR_REG))])]
+ ""
+ "
+{
+ if (TARGET_SHMEDIA)
+ {
+ operands[1] = shmedia_prepare_call_address (operands[1], 0);
+ emit_call_insn (gen_call_value_media (operands[0], operands[1],
+ operands[2]));
+ DONE;
+ }
+ else if (TARGET_SHCOMPACT && operands[3] && INTVAL (operands[3]))
+ {
+ rtx cookie_rtx = operands[3];
+ long cookie = INTVAL (cookie_rtx);
+ rtx func = XEXP (operands[1], 0);
+ rtx r0, r1;
+
+ if (flag_pic)
+ {
+ if (GET_CODE (func) == SYMBOL_REF && ! SYMBOL_REF_LOCAL_P (func))
+ {
+ rtx reg = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symGOTPLT2reg (reg, func));
+ func = reg;
+ }
+ else
+ func = legitimize_pic_address (func, Pmode, 0);
+ }
+
+ r0 = gen_rtx_REG (SImode, R0_REG);
+ r1 = gen_rtx_REG (SImode, R1_REG);
+
+ /* Since such a call function may use all call-clobbered
+ registers, we force a mode switch earlier, so that we don't
+ run out of registers when adjusting fpscr for the call. */
+ emit_insn (gen_force_mode_for_call ());
+
+ operands[1]
+ = function_symbol (NULL, \"__GCC_shcompact_call_trampoline\",
+ SFUNC_GOT);
+ operands[1] = force_reg (SImode, operands[1]);
+
+ emit_move_insn (r0, func);
+ emit_move_insn (r1, cookie_rtx);
+
+ if (cookie & CALL_COOKIE_RET_TRAMP (1))
+ emit_call_insn (gen_call_value_compact_rettramp (operands[0],
+ operands[1],
+ operands[2],
+ operands[3]));
+ else
+ emit_call_insn (gen_call_value_compact (operands[0], operands[1],
+ operands[2], operands[3]));
+
+ DONE;
+ }
+ else if (TARGET_SHCOMPACT && flag_pic
+ && GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF
+ && ! SYMBOL_REF_LOCAL_P (XEXP (operands[1], 0)))
+ {
+ rtx reg = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symGOTPLT2reg (reg, XEXP (operands[1], 0)));
+ XEXP (operands[1], 0) = reg;
+ }
+ if (!flag_pic && TARGET_SH2A
+ && MEM_P (operands[1])
+ && GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF)
+ {
+ if (sh2a_is_function_vector_call (XEXP (operands[1], 0)))
+ {
+ emit_call_insn (gen_call_valuei_tbr_rel (operands[0],
+ XEXP (operands[1], 0), operands[2]));
+ DONE;
+ }
+ }
+ if (flag_pic && TARGET_SH2
+ && MEM_P (operands[1])
+ && GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF)
+ {
+ emit_call_insn (gen_call_value_pcrel (operands[0], XEXP (operands[1], 0),
+ operands[2]));
+ DONE;
+ }
+ else
+ operands[1] = force_reg (SImode, XEXP (operands[1], 0));
+
+ emit_call_insn (gen_call_valuei (operands[0], operands[1], operands[2]));
+ DONE;
+}")
+
+(define_insn "sibcalli"
+ [(call (mem:SI (match_operand:SI 0 "register_operand" "k"))
+ (match_operand 1 "" ""))
+ (use (reg:PSI FPSCR_REG))
+ (return)]
+ "TARGET_SH1"
+ "jmp @%0%#"
+ [(set_attr "needs_delay_slot" "yes")
+ (set (attr "fp_mode")
+ (if_then_else (eq_attr "fpu_single" "yes")
+ (const_string "single") (const_string "double")))
+ (set_attr "type" "jump_ind")])
+
+(define_insn "sibcalli_pcrel"
+ [(call (mem:SI (match_operand:SI 0 "arith_reg_operand" "k"))
+ (match_operand 1 "" ""))
+ (use (match_operand 2 "" ""))
+ (use (reg:PSI FPSCR_REG))
+ (return)]
+ "TARGET_SH2"
+ "braf %0\\n%O2:%#"
+ [(set_attr "needs_delay_slot" "yes")
+ (set (attr "fp_mode")
+ (if_then_else (eq_attr "fpu_single" "yes")
+ (const_string "single") (const_string "double")))
+ (set_attr "type" "jump_ind")])
+
+;; This uses an unspec to describe that the symbol_ref is very close.
+(define_insn "sibcalli_thunk"
+ [(call (mem:SI (unspec:SI [(match_operand:SI 0 "symbol_ref_operand" "")]
+ UNSPEC_THUNK))
+ (match_operand 1 "" ""))
+ (use (reg:PSI FPSCR_REG))
+ (return)]
+ "TARGET_SH1"
+ "bra %O0"
+ [(set_attr "needs_delay_slot" "yes")
+ (set (attr "fp_mode")
+ (if_then_else (eq_attr "fpu_single" "yes")
+ (const_string "single") (const_string "double")))
+ (set_attr "type" "jump")
+ (set_attr "length" "2")])
+
+(define_insn_and_split "sibcall_pcrel"
+ [(call (mem:SI (match_operand:SI 0 "symbol_ref_operand" ""))
+ (match_operand 1 "" ""))
+ (use (reg:PSI FPSCR_REG))
+ (clobber (match_scratch:SI 2 "=k"))
+ (return)]
+ "TARGET_SH2"
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+ "
+{
+ rtx lab = PATTERN (gen_call_site ());
+ rtx call_insn;
+
+ emit_insn (gen_sym_label2reg (operands[2], operands[0], lab));
+ call_insn = emit_call_insn (gen_sibcalli_pcrel (operands[2], operands[1],
+ copy_rtx (lab)));
+ SIBLING_CALL_P (call_insn) = 1;
+ DONE;
+}"
+ [(set_attr "needs_delay_slot" "yes")
+ (set (attr "fp_mode")
+ (if_then_else (eq_attr "fpu_single" "yes")
+ (const_string "single") (const_string "double")))
+ (set_attr "type" "jump_ind")])
+
+(define_insn "sibcall_compact"
+ [(call (mem:SI (match_operand:SI 0 "register_operand" "k,k"))
+ (match_operand 1 "" ""))
+ (return)
+ (use (match_operand:SI 2 "register_operand" "z,x"))
+ (use (reg:SI R1_REG))
+ (use (reg:PSI FPSCR_REG))
+ ;; We want to make sure the `x' above will only match MACH_REG
+ ;; because sibcall_epilogue may clobber MACL_REG.
+ (clobber (reg:SI MACL_REG))]
+ "TARGET_SHCOMPACT"
+ "@
+ jmp @%0%#
+ jmp @%0\\n sts %2, r0"
+ [(set_attr "needs_delay_slot" "yes,no")
+ (set_attr "length" "2,4")
+ (set (attr "fp_mode") (const_string "single"))
+ (set_attr "type" "jump_ind")])
+
+(define_insn "sibcall_media"
+ [(call (mem:DI (match_operand 0 "target_reg_operand" "k"))
+ (match_operand 1 "" ""))
+ (use (reg:SI PR_MEDIA_REG))
+ (return)]
+ "TARGET_SHMEDIA"
+ "blink %0, r63"
+ [(set_attr "type" "jump_media")])
+
+(define_expand "sibcall"
+ [(parallel
+ [(call (mem:SI (match_operand 0 "arith_reg_operand" ""))
+ (match_operand 1 "" ""))
+ (match_operand 2 "" "")
+ (use (reg:PSI FPSCR_REG))
+ (return)])]
+ ""
+ "
+{
+ if (TARGET_SHMEDIA)
+ {
+ operands[0] = shmedia_prepare_call_address (operands[0], 1);
+ emit_call_insn (gen_sibcall_media (operands[0], operands[1]));
+ DONE;
+ }
+ else if (TARGET_SHCOMPACT && operands[2]
+ && (INTVAL (operands[2]) & ~ CALL_COOKIE_RET_TRAMP (1)))
+ {
+ rtx cookie_rtx = operands[2];
+ long cookie = INTVAL (cookie_rtx);
+ rtx func = XEXP (operands[0], 0);
+ rtx mach, r1;
+
+ if (flag_pic)
+ {
+ if (GET_CODE (func) == SYMBOL_REF && ! SYMBOL_REF_LOCAL_P (func))
+ {
+ rtx reg = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symGOT2reg (reg, func));
+ func = reg;
+ }
+ else
+ func = legitimize_pic_address (func, Pmode, 0);
+ }
+
+ /* FIXME: if we could tell whether all argument registers are
+ already taken, we could decide whether to force the use of
+ MACH_REG or to stick to R0_REG. Unfortunately, there's no
+ simple way to tell. We could use the CALL_COOKIE, but we
+ can't currently tell a register used for regular argument
+ passing from one that is unused. If we leave it up to reload
+ to decide which register to use, it seems to always choose
+ R0_REG, which leaves no available registers in SIBCALL_REGS
+ to hold the address of the trampoline. */
+ mach = gen_rtx_REG (SImode, MACH_REG);
+ r1 = gen_rtx_REG (SImode, R1_REG);
+
+ /* Since such a call function may use all call-clobbered
+ registers, we force a mode switch earlier, so that we don't
+ run out of registers when adjusting fpscr for the call. */
+ emit_insn (gen_force_mode_for_call ());
+
+ operands[0]
+ = function_symbol (NULL, \"__GCC_shcompact_call_trampoline\",
+ SFUNC_GOT);
+ operands[0] = force_reg (SImode, operands[0]);
+
+ /* We don't need a return trampoline, since the callee will
+ return directly to the upper caller. */
+ if (cookie & CALL_COOKIE_RET_TRAMP (1))
+ {
+ cookie &= ~ CALL_COOKIE_RET_TRAMP (1);
+ cookie_rtx = GEN_INT (cookie);
+ }
+
+ emit_move_insn (mach, func);
+ emit_move_insn (r1, cookie_rtx);
+
+ emit_call_insn (gen_sibcall_compact (operands[0], operands[1], mach));
+ DONE;
+ }
+ else if (TARGET_SHCOMPACT && flag_pic
+ && GET_CODE (XEXP (operands[0], 0)) == SYMBOL_REF
+ && ! SYMBOL_REF_LOCAL_P (XEXP (operands[0], 0)))
+ {
+ rtx reg = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symGOT2reg (reg, XEXP (operands[0], 0)));
+ XEXP (operands[0], 0) = reg;
+ }
+ if (flag_pic && TARGET_SH2
+ && MEM_P (operands[0])
+ && GET_CODE (XEXP (operands[0], 0)) == SYMBOL_REF
+ /* The PLT needs the PIC register, but the epilogue would have
+ to restore it, so we can only use PC-relative PIC calls for
+ static functions. */
+ && SYMBOL_REF_LOCAL_P (XEXP (operands[0], 0)))
+ {
+ emit_call_insn (gen_sibcall_pcrel (XEXP (operands[0], 0), operands[1]));
+ DONE;
+ }
+ else
+ operands[0] = force_reg (SImode, XEXP (operands[0], 0));
+
+ emit_call_insn (gen_sibcalli (operands[0], operands[1]));
+ DONE;
+}")
+
+(define_insn "sibcall_valuei"
+ [(set (match_operand 0 "" "=rf")
+ (call (mem:SI (match_operand:SI 1 "register_operand" "k"))
+ (match_operand 2 "" "")))
+ (use (reg:PSI FPSCR_REG))
+ (return)]
+ "TARGET_SH1"
+ "jmp @%1%#"
+ [(set_attr "needs_delay_slot" "yes")
+ (set (attr "fp_mode")
+ (if_then_else (eq_attr "fpu_single" "yes")
+ (const_string "single") (const_string "double")))
+ (set_attr "type" "jump_ind")])
+
+(define_insn "sibcall_valuei_pcrel"
+ [(set (match_operand 0 "" "=rf")
+ (call (mem:SI (match_operand:SI 1 "arith_reg_operand" "k"))
+ (match_operand 2 "" "")))
+ (use (match_operand 3 "" ""))
+ (use (reg:PSI FPSCR_REG))
+ (return)]
+ "TARGET_SH2"
+ "braf %1\\n%O3:%#"
+ [(set_attr "needs_delay_slot" "yes")
+ (set (attr "fp_mode")
+ (if_then_else (eq_attr "fpu_single" "yes")
+ (const_string "single") (const_string "double")))
+ (set_attr "type" "jump_ind")])
+
+(define_insn_and_split "sibcall_value_pcrel"
+ [(set (match_operand 0 "" "=rf")
+ (call (mem:SI (match_operand:SI 1 "symbol_ref_operand" ""))
+ (match_operand 2 "" "")))
+ (use (reg:PSI FPSCR_REG))
+ (clobber (match_scratch:SI 3 "=k"))
+ (return)]
+ "TARGET_SH2"
+ "#"
+ "reload_completed"
+ [(const_int 0)]
+ "
+{
+ rtx lab = PATTERN (gen_call_site ());
+ rtx call_insn;
+
+ emit_insn (gen_sym_label2reg (operands[3], operands[1], lab));
+ call_insn = emit_call_insn (gen_sibcall_valuei_pcrel (operands[0],
+ operands[3],
+ operands[2],
+ copy_rtx (lab)));
+ SIBLING_CALL_P (call_insn) = 1;
+ DONE;
+}"
+ [(set_attr "needs_delay_slot" "yes")
+ (set (attr "fp_mode")
+ (if_then_else (eq_attr "fpu_single" "yes")
+ (const_string "single") (const_string "double")))
+ (set_attr "type" "jump_ind")])
+
+(define_insn "sibcall_value_compact"
+ [(set (match_operand 0 "" "=rf,rf")
+ (call (mem:SI (match_operand:SI 1 "register_operand" "k,k"))
+ (match_operand 2 "" "")))
+ (return)
+ (use (match_operand:SI 3 "register_operand" "z,x"))
+ (use (reg:SI R1_REG))
+ (use (reg:PSI FPSCR_REG))
+ ;; We want to make sure the `x' above will only match MACH_REG
+ ;; because sibcall_epilogue may clobber MACL_REG.
+ (clobber (reg:SI MACL_REG))]
+ "TARGET_SHCOMPACT"
+ "@
+ jmp @%1%#
+ jmp @%1\\n sts %3, r0"
+ [(set_attr "needs_delay_slot" "yes,no")
+ (set_attr "length" "2,4")
+ (set (attr "fp_mode") (const_string "single"))
+ (set_attr "type" "jump_ind")])
+
+(define_insn "sibcall_value_media"
+ [(set (match_operand 0 "" "=rf")
+ (call (mem:DI (match_operand 1 "target_reg_operand" "k"))
+ (match_operand 2 "" "")))
+ (use (reg:SI PR_MEDIA_REG))
+ (return)]
+ "TARGET_SHMEDIA"
+ "blink %1, r63"
+ [(set_attr "type" "jump_media")])
+
+(define_expand "sibcall_value"
+ [(parallel
+ [(set (match_operand 0 "arith_reg_operand" "")
+ (call (mem:SI (match_operand 1 "arith_reg_operand" ""))
+ (match_operand 2 "" "")))
+ (match_operand 3 "" "")
+ (use (reg:PSI FPSCR_REG))
+ (return)])]
+ ""
+ "
+{
+ if (TARGET_SHMEDIA)
+ {
+ operands[1] = shmedia_prepare_call_address (operands[1], 1);
+ emit_call_insn (gen_sibcall_value_media (operands[0], operands[1],
+ operands[2]));
+ DONE;
+ }
+ else if (TARGET_SHCOMPACT && operands[3]
+ && (INTVAL (operands[3]) & ~ CALL_COOKIE_RET_TRAMP (1)))
+ {
+ rtx cookie_rtx = operands[3];
+ long cookie = INTVAL (cookie_rtx);
+ rtx func = XEXP (operands[1], 0);
+ rtx mach, r1;
+
+ if (flag_pic)
+ {
+ if (GET_CODE (func) == SYMBOL_REF && ! SYMBOL_REF_LOCAL_P (func))
+ {
+ rtx reg = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symGOT2reg (reg, func));
+ func = reg;
+ }
+ else
+ func = legitimize_pic_address (func, Pmode, 0);
+ }
+
+ /* FIXME: if we could tell whether all argument registers are
+ already taken, we could decide whether to force the use of
+ MACH_REG or to stick to R0_REG. Unfortunately, there's no
+ simple way to tell. We could use the CALL_COOKIE, but we
+ can't currently tell a register used for regular argument
+ passing from one that is unused. If we leave it up to reload
+ to decide which register to use, it seems to always choose
+ R0_REG, which leaves no available registers in SIBCALL_REGS
+ to hold the address of the trampoline. */
+ mach = gen_rtx_REG (SImode, MACH_REG);
+ r1 = gen_rtx_REG (SImode, R1_REG);
+
+ /* Since such a call function may use all call-clobbered
+ registers, we force a mode switch earlier, so that we don't
+ run out of registers when adjusting fpscr for the call. */
+ emit_insn (gen_force_mode_for_call ());
+
+ operands[1]
+ = function_symbol (NULL, \"__GCC_shcompact_call_trampoline\",
+ SFUNC_GOT);
+ operands[1] = force_reg (SImode, operands[1]);
+
+ /* We don't need a return trampoline, since the callee will
+ return directly to the upper caller. */
+ if (cookie & CALL_COOKIE_RET_TRAMP (1))
+ {
+ cookie &= ~ CALL_COOKIE_RET_TRAMP (1);
+ cookie_rtx = GEN_INT (cookie);
+ }
+
+ emit_move_insn (mach, func);
+ emit_move_insn (r1, cookie_rtx);
+
+ emit_call_insn (gen_sibcall_value_compact (operands[0], operands[1],
+ operands[2], mach));
+ DONE;
+ }
+ else if (TARGET_SHCOMPACT && flag_pic
+ && GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF
+ && ! SYMBOL_REF_LOCAL_P (XEXP (operands[1], 0)))
+ {
+ rtx reg = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symGOT2reg (reg, XEXP (operands[1], 0)));
+ XEXP (operands[1], 0) = reg;
+ }
+ if (flag_pic && TARGET_SH2
+ && MEM_P (operands[1])
+ && GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF
+ /* The PLT needs the PIC register, but the epilogue would have
+ to restore it, so we can only use PC-relative PIC calls for
+ static functions. */
+ && SYMBOL_REF_LOCAL_P (XEXP (operands[1], 0)))
+ {
+ emit_call_insn (gen_sibcall_value_pcrel (operands[0],
+ XEXP (operands[1], 0),
+ operands[2]));
+ DONE;
+ }
+ else
+ operands[1] = force_reg (SImode, XEXP (operands[1], 0));
+
+ emit_call_insn (gen_sibcall_valuei (operands[0], operands[1], operands[2]));
+ DONE;
+}")
+
+(define_insn "call_value_pop_compact"
+ [(set (match_operand 0 "" "=rf")
+ (call (mem:SI (match_operand:SI 1 "arith_reg_operand" "r"))
+ (match_operand 2 "" "")))
+ (set (reg:SI SP_REG) (plus:SI (reg:SI SP_REG)
+ (match_operand 4 "immediate_operand" "n")))
+ (match_operand 3 "immediate_operand" "n")
+ (use (reg:SI R0_REG))
+ (use (reg:SI R1_REG))
+ (use (reg:PSI FPSCR_REG))
+ (clobber (reg:SI PR_REG))]
+ "TARGET_SHCOMPACT && ! (INTVAL (operands[3]) & CALL_COOKIE_RET_TRAMP (1))"
+ "jsr @%1%#"
+ [(set_attr "type" "call")
+ (set (attr "fp_mode")
+ (if_then_else (eq_attr "fpu_single" "yes")
+ (const_string "single") (const_string "double")))
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_insn "call_value_pop_compact_rettramp"
+ [(set (match_operand 0 "" "=rf")
+ (call (mem:SI (match_operand:SI 1 "arith_reg_operand" "r"))
+ (match_operand 2 "" "")))
+ (set (reg:SI SP_REG) (plus:SI (reg:SI SP_REG)
+ (match_operand 4 "immediate_operand" "n")))
+ (match_operand 3 "immediate_operand" "n")
+ (use (reg:SI R0_REG))
+ (use (reg:SI R1_REG))
+ (use (reg:PSI FPSCR_REG))
+ (clobber (reg:SI R10_REG))
+ (clobber (reg:SI PR_REG))]
+ "TARGET_SHCOMPACT && (INTVAL (operands[3]) & CALL_COOKIE_RET_TRAMP (1))"
+ "jsr @%1%#"
+ [(set_attr "type" "call")
+ (set (attr "fp_mode")
+ (if_then_else (eq_attr "fpu_single" "yes")
+ (const_string "single") (const_string "double")))
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_expand "call_value_pop"
+ [(parallel [(set (match_operand 0 "arith_reg_operand" "")
+ (call (mem:SI (match_operand 1 "arith_reg_operand" ""))
+ (match_operand 2 "" "")))
+ (match_operand 3 "" "")
+ (set (reg:SI SP_REG) (plus:SI (reg:SI SP_REG)
+ (match_operand 4 "" "")))])]
+ "TARGET_SHCOMPACT"
+ "
+{
+ rtx cookie_rtx;
+ long cookie;
+ rtx func;
+ rtx r0, r1;
+
+ gcc_assert (TARGET_SHCOMPACT && operands[3] && INTVAL (operands[3]));
+ cookie_rtx = operands[3];
+ cookie = INTVAL (cookie_rtx);
+ func = XEXP (operands[1], 0);
+
+ if (flag_pic)
+ {
+ if (GET_CODE (func) == SYMBOL_REF && ! SYMBOL_REF_LOCAL_P (func))
+ {
+ rtx reg = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symGOTPLT2reg (reg, func));
+ func = reg;
+ }
+ else
+ func = legitimize_pic_address (func, Pmode, 0);
+ }
+
+ r0 = gen_rtx_REG (SImode, R0_REG);
+ r1 = gen_rtx_REG (SImode, R1_REG);
+
+ /* Since such a call function may use all call-clobbered
+ registers, we force a mode switch earlier, so that we don't
+ run out of registers when adjusting fpscr for the call. */
+ emit_insn (gen_force_mode_for_call ());
+
+ operands[1] = function_symbol (NULL, \"__GCC_shcompact_call_trampoline\",
+ SFUNC_GOT);
+ operands[1] = force_reg (SImode, operands[1]);
+
+ emit_move_insn (r0, func);
+ emit_move_insn (r1, cookie_rtx);
+
+ if (cookie & CALL_COOKIE_RET_TRAMP (1))
+ emit_call_insn (gen_call_value_pop_compact_rettramp
+ (operands[0], operands[1], operands[2],
+ operands[3], operands[4]));
+ else
+ emit_call_insn (gen_call_value_pop_compact
+ (operands[0], operands[1], operands[2],
+ operands[3], operands[4]));
+
+ DONE;
+}")
+
+(define_expand "sibcall_epilogue"
+ [(return)]
+ ""
+ "
+{
+ sh_expand_epilogue (1);
+ if (TARGET_SHCOMPACT)
+ {
+ rtx insn, set;
+
+ /* If epilogue clobbers r0, preserve it in macl. */
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ if ((set = single_set (insn))
+ && REG_P (SET_DEST (set))
+ && REGNO (SET_DEST (set)) == R0_REG)
+ {
+ rtx r0 = gen_rtx_REG (SImode, R0_REG);
+ rtx tmp = gen_rtx_REG (SImode, MACL_REG);
+
+ /* We can't tell at this point whether the sibcall is a
+ sibcall_compact and, if it is, whether it uses r0 or
+ mach as operand 2, so let the instructions that
+ preserve r0 be optimized away if r0 turns out to be
+ dead. */
+ emit_insn_before (gen_rtx_SET (SImode, tmp, r0), insn);
+ emit_move_insn (r0, tmp);
+ break;
+ }
+ }
+ DONE;
+}")
+
+(define_insn "indirect_jump_compact"
+ [(set (pc)
+ (match_operand:SI 0 "arith_reg_operand" "r"))]
+ "TARGET_SH1"
+ "jmp @%0%#"
+ [(set_attr "needs_delay_slot" "yes")
+ (set_attr "type" "jump_ind")])
+
+(define_expand "indirect_jump"
+ [(set (pc)
+ (match_operand 0 "register_operand" ""))]
+ ""
+ "
+{
+ if (GET_MODE (operands[0]) != Pmode)
+ operands[0] = gen_rtx_SUBREG (Pmode, operands[0], 0);
+}")
+
+;; The use of operand 1 / 2 helps us distinguish case table jumps
+;; which can be present in structured code from indirect jumps which can not
+;; be present in structured code. This allows -fprofile-arcs to work.
+
+;; For SH1 processors.
+(define_insn "casesi_jump_1"
+ [(set (pc)
+ (match_operand:SI 0 "register_operand" "r"))
+ (use (label_ref (match_operand 1 "" "")))]
+ "TARGET_SH1"
+ "jmp @%0%#"
+ [(set_attr "needs_delay_slot" "yes")
+ (set_attr "type" "jump_ind")])
+
+;; For all later processors.
+(define_insn "casesi_jump_2"
+ [(set (pc) (plus:SI (match_operand:SI 0 "register_operand" "r")
+ (label_ref (match_operand 1 "" ""))))
+ (use (label_ref (match_operand 2 "" "")))]
+ "TARGET_SH2
+ && (! INSN_UID (operands[1]) || prev_real_insn (operands[1]) == insn)"
+ "braf %0%#"
+ [(set_attr "needs_delay_slot" "yes")
+ (set_attr "type" "jump_ind")])
+
+(define_insn "casesi_jump_media"
+ [(set (pc) (match_operand 0 "target_reg_operand" "b"))
+ (use (label_ref (match_operand 1 "" "")))]
+ "TARGET_SHMEDIA"
+ "blink %0, r63"
+ [(set_attr "type" "jump_media")])
+
+;; Call subroutine returning any type.
+;; ??? This probably doesn't work.
+
+(define_expand "untyped_call"
+ [(parallel [(call (match_operand 0 "" "")
+ (const_int 0))
+ (match_operand 1 "" "")
+ (match_operand 2 "" "")])]
+ "(TARGET_SH2E || TARGET_SH2A) || TARGET_SHMEDIA"
+ "
+{
+ int i;
+
+ emit_call_insn (gen_call (operands[0], const0_rtx, const0_rtx));
+
+ for (i = 0; i < XVECLEN (operands[2], 0); i++)
+ {
+ rtx set = XVECEXP (operands[2], 0, i);
+ emit_move_insn (SET_DEST (set), SET_SRC (set));
+ }
+
+ /* The optimizer does not know that the call sets the function value
+ registers we stored in the result block. We avoid problems by
+ claiming that all hard registers are used and clobbered at this
+ point. */
+ emit_insn (gen_blockage ());
+
+ DONE;
+}")
+
+;; ------------------------------------------------------------------------
+;; Misc insns
+;; ------------------------------------------------------------------------
+
+(define_insn "dect"
+ [(set (reg:SI T_REG)
+ (eq:SI (match_operand:SI 1 "arith_reg_dest" "0") (const_int 1)))
+ (set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (plus:SI (match_dup 1) (const_int -1)))]
+ "TARGET_SH2"
+ "dt %0"
+ [(set_attr "type" "arith")])
+
+(define_insn "nop"
+ [(const_int 0)]
+ ""
+ "nop")
+
+;; Load address of a label. This is only generated by the casesi expand,
+;; and by machine_dependent_reorg (fixing up fp moves).
+;; This must use unspec, because this only works for labels that are
+;; within range,
+
+(define_insn "mova"
+ [(set (reg:SI R0_REG)
+ (unspec:SI [(label_ref (match_operand 0 "" ""))] UNSPEC_MOVA))]
+ "TARGET_SH1"
+ "mova %O0,r0"
+ [(set_attr "in_delay_slot" "no")
+ (set_attr "type" "arith")])
+
+;; machine_dependent_reorg will make this a `mova'.
+(define_insn "mova_const"
+ [(set (reg:SI R0_REG)
+ (unspec:SI [(match_operand 0 "immediate_operand" "i")] UNSPEC_MOVA))]
+ "TARGET_SH1"
+ "#"
+ [(set_attr "in_delay_slot" "no")
+ (set_attr "type" "arith")])
+
+(define_expand "GOTaddr2picreg"
+ [(set (reg:SI R0_REG)
+ (unspec:SI [(const:SI (unspec:SI [(match_dup 1)] UNSPEC_PIC))]
+ UNSPEC_MOVA))
+ (set (match_dup 0) (const:SI (unspec:SI [(match_dup 1)] UNSPEC_PIC)))
+ (set (match_dup 0) (plus:SI (match_dup 0) (reg:SI R0_REG)))]
+ "" "
+{
+ if (TARGET_VXWORKS_RTP)
+ {
+ rtx gott_base = gen_rtx_SYMBOL_REF (Pmode, VXWORKS_GOTT_BASE);
+ rtx gott_index = gen_rtx_SYMBOL_REF (Pmode, VXWORKS_GOTT_INDEX);
+ emit_insn (gen_vxworks_picreg (gott_base, gott_index));
+ DONE;
+ }
+
+ operands[0] = gen_rtx_REG (Pmode, PIC_REG);
+ operands[1] = gen_rtx_SYMBOL_REF (VOIDmode, GOT_SYMBOL_NAME);
+
+ if (TARGET_SHMEDIA)
+ {
+ rtx tr = gen_rtx_REG (Pmode, TR0_REG);
+ rtx pic = operands[0];
+ rtx lab = PATTERN (gen_call_site ());
+ rtx insn, equiv;
+
+ equiv = operands[1];
+ operands[1] = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, operands[1], lab),
+ UNSPEC_PCREL_SYMOFF);
+ operands[1] = gen_rtx_CONST (Pmode, operands[1]);
+
+ if (Pmode == SImode)
+ {
+ emit_insn (gen_movsi_const (pic, operands[1]));
+ emit_insn (gen_ptrel_si (tr, pic, copy_rtx (lab)));
+ }
+ else
+ {
+ emit_insn (gen_movdi_const (pic, operands[1]));
+ emit_insn (gen_ptrel_di (tr, pic, copy_rtx (lab)));
+ }
+
+ insn = emit_move_insn (operands[0], tr);
+
+ set_unique_reg_note (insn, REG_EQUAL, equiv);
+
+ DONE;
+ }
+}
+")
+
+;; A helper for GOTaddr2picreg to finish up the initialization of the
+;; PIC register.
+
+(define_expand "vxworks_picreg"
+ [(set (reg:SI PIC_REG)
+ (const:SI (unspec:SI [(match_operand:SI 0 "" "")] UNSPEC_PIC)))
+ (set (reg:SI R0_REG)
+ (const:SI (unspec:SI [(match_operand:SI 1 "" "")] UNSPEC_PIC)))
+ (set (reg:SI PIC_REG)
+ (mem:SI (reg:SI PIC_REG)))
+ (set (reg:SI PIC_REG)
+ (mem:SI (plus:SI (reg:SI PIC_REG)
+ (reg:SI R0_REG))))]
+ "TARGET_VXWORKS_RTP")
+
+(define_insn "*ptb"
+ [(set (match_operand 0 "target_reg_operand" "=b")
+ (const (unspec [(match_operand 1 "" "Csy")]
+ UNSPEC_DATALABEL)))]
+ "TARGET_SHMEDIA && flag_pic
+ && satisfies_constraint_Csy (operands[1])"
+ "ptb/u datalabel %1, %0"
+ [(set_attr "type" "ptabs_media")
+ (set_attr "length" "*")])
+
+(define_insn "ptrel_si"
+ [(set (match_operand:SI 0 "target_reg_operand" "=b")
+ (plus:SI (match_operand:SI 1 "register_operand" "r")
+ (pc)))
+ (match_operand:SI 2 "" "")]
+ "TARGET_SHMEDIA"
+ "%O2: ptrel/u %1, %0"
+ [(set_attr "type" "ptabs_media")])
+
+(define_insn "ptrel_di"
+ [(set (match_operand:DI 0 "target_reg_operand" "=b")
+ (plus:DI (match_operand:DI 1 "register_operand" "r")
+ (pc)))
+ (match_operand:DI 2 "" "")]
+ "TARGET_SHMEDIA"
+ "%O2: ptrel/u %1, %0"
+ [(set_attr "type" "ptabs_media")])
+
+(define_expand "builtin_setjmp_receiver"
+ [(match_operand 0 "" "")]
+ "flag_pic"
+ "
+{
+ emit_insn (gen_GOTaddr2picreg ());
+ DONE;
+}")
+
+(define_expand "call_site"
+ [(unspec [(match_dup 0)] UNSPEC_CALLER)]
+ "TARGET_SH1"
+ "
+{
+ static HOST_WIDE_INT i = 0;
+ operands[0] = GEN_INT (i);
+ i++;
+}")
+
+;; op0 = op1 + r12 but hide it before reload completed. See the comment
+;; in symGOT_load expand.
+
+(define_insn_and_split "chk_guard_add"
+ [(set (match_operand:SI 0 "register_operand" "=&r")
+ (unspec:SI [(match_operand:SI 1 "register_operand" "r")
+ (reg:SI PIC_REG)]
+ UNSPEC_CHKADD))]
+ "TARGET_SH1"
+ "#"
+ "TARGET_SH1 && reload_completed"
+ [(set (match_dup 0) (reg:SI PIC_REG))
+ (set (match_dup 0) (plus:SI (match_dup 0) (match_dup 1)))]
+ ""
+ [(set_attr "type" "arith")])
+
+(define_expand "sym_label2reg"
+ [(set (match_operand:SI 0 "" "")
+ (const:SI (unspec:SI [(match_operand:SI 1 "" "")
+ (const (plus:SI (match_operand:SI 2 "" "")
+ (const_int 2)))]
+ UNSPEC_SYMOFF)))]
+ "TARGET_SH1" "")
+
+(define_expand "symGOT_load"
+ [(set (match_dup 2) (match_operand 1 "" ""))
+ (set (match_dup 3) (plus (match_dup 2) (reg PIC_REG)))
+ (set (match_operand 0 "" "") (mem (match_dup 3)))]
+ ""
+ "
+{
+ rtx mem;
+
+ operands[2] = !can_create_pseudo_p () ? operands[0] : gen_reg_rtx (Pmode);
+ operands[3] = !can_create_pseudo_p () ? operands[0] : gen_reg_rtx (Pmode);
+
+ if (TARGET_SHMEDIA)
+ {
+ rtx reg = operands[2];
+
+ if (Pmode == DImode)
+ {
+ if (flag_pic > 1)
+ emit_insn (gen_movdi_const_32bit (reg, operands[1]));
+ else
+ emit_insn (gen_movdi_const_16bit (reg, operands[1]));
+ }
+ else
+ {
+ if (flag_pic > 1)
+ emit_insn (gen_movsi_const (reg, operands[1]));
+ else
+ emit_insn (gen_movsi_const_16bit (reg, operands[1]));
+ }
+ }
+ else
+ emit_move_insn (operands[2], operands[1]);
+
+ /* When stack protector inserts codes after the result is set to
+ R0, @(rX, r12) will cause a spill failure for R0. Use a unspec
+ insn to avoid combining (set A (plus rX r12)) and (set op0 (mem A))
+ when rX is a GOT address for the guard symbol. Ugly but doesn't
+ matter because this is a rare situation. */
+ if (!TARGET_SHMEDIA
+ && flag_stack_protect
+ && GET_CODE (operands[1]) == CONST
+ && GET_CODE (XEXP (operands[1], 0)) == UNSPEC
+ && GET_CODE (XVECEXP (XEXP (operands[1], 0), 0, 0)) == SYMBOL_REF
+ && strcmp (XSTR (XVECEXP (XEXP (operands[1], 0), 0, 0), 0),
+ \"__stack_chk_guard\") == 0)
+ emit_insn (gen_chk_guard_add (operands[3], operands[2]));
+ else
+ emit_move_insn (operands[3], gen_rtx_PLUS (Pmode, operands[2],
+ gen_rtx_REG (Pmode, PIC_REG)));
+
+ /* N.B. This is not constant for a GOTPLT relocation. */
+ mem = gen_rtx_MEM (Pmode, operands[3]);
+ MEM_NOTRAP_P (mem) = 1;
+ /* ??? Should we have a special alias set for the GOT? */
+ emit_move_insn (operands[0], mem);
+
+ DONE;
+}")
+
+(define_expand "sym2GOT"
+ [(const (unspec [(match_operand 0 "" "")] UNSPEC_GOT))]
+ ""
+ "")
+
+(define_expand "symGOT2reg"
+ [(match_operand 0 "" "") (match_operand 1 "" "")]
+ ""
+ "
+{
+ rtx gotsym, insn;
+
+ gotsym = gen_sym2GOT (operands[1]);
+ PUT_MODE (gotsym, Pmode);
+ insn = emit_insn (gen_symGOT_load (operands[0], gotsym));
+
+ MEM_READONLY_P (SET_SRC (PATTERN (insn))) = 1;
+
+ DONE;
+}")
+
+(define_expand "symGOTPLT2reg"
+ [(match_operand 0 "" "") (match_operand 1 "" "")]
+ ""
+ "
+{
+ rtx pltsym = gen_rtx_CONST (Pmode,
+ gen_rtx_UNSPEC (Pmode,
+ gen_rtvec (1, operands[1]),
+ UNSPEC_GOTPLT));
+ emit_insn (gen_symGOT_load (operands[0], pltsym));
+ DONE;
+}")
+
+(define_expand "sym2GOTOFF"
+ [(const (unspec [(match_operand 0 "" "")] UNSPEC_GOTOFF))]
+ ""
+ "")
+
+(define_expand "symGOTOFF2reg"
+ [(match_operand 0 "" "") (match_operand 1 "" "")]
+ ""
+ "
+{
+ rtx gotoffsym, insn;
+ rtx t = (!can_create_pseudo_p ()
+ ? operands[0]
+ : gen_reg_rtx (GET_MODE (operands[0])));
+
+ gotoffsym = gen_sym2GOTOFF (operands[1]);
+ PUT_MODE (gotoffsym, Pmode);
+ emit_move_insn (t, gotoffsym);
+ insn = emit_move_insn (operands[0],
+ gen_rtx_PLUS (Pmode, t,
+ gen_rtx_REG (Pmode, PIC_REG)));
+
+ set_unique_reg_note (insn, REG_EQUAL, operands[1]);
+
+ DONE;
+}")
+
+(define_expand "symPLT_label2reg"
+ [(set (match_operand:SI 0 "" "")
+ (const:SI
+ (unspec:SI
+ [(const:SI (unspec:SI [(match_operand:SI 1 "" "")] UNSPEC_PLT))
+ (const:SI (plus:SI (match_operand:SI 2 "" "")
+ (const_int 2)))] UNSPEC_PCREL_SYMOFF)))
+ ;; Even though the PIC register is not really used by the call
+ ;; sequence in which this is expanded, the PLT code assumes the PIC
+ ;; register is set, so we must not skip its initialization. Since
+ ;; we only use this expand as part of calling sequences, and never
+ ;; to take the address of a function, this is the best point to
+ ;; insert the (use). Using the PLT to take the address of a
+ ;; function would be wrong, not only because the PLT entry could
+ ;; then be called from a function that doesn't initialize the PIC
+ ;; register to the proper GOT, but also because pointers to the
+ ;; same function might not compare equal, should they be set by
+ ;; different shared libraries.
+ (use (reg:SI PIC_REG))]
+ "TARGET_SH1"
+ "")
+
+(define_expand "sym2PIC"
+ [(const (unspec [(match_operand:SI 0 "" "")] UNSPEC_PIC))]
+ ""
+ "")
+
+;; TLS code generation.
+;; ??? this should be a define_insn_and_split
+;; See the thread [PATCH/RFA] SH TLS support on gcc-patches
+;; <http://gcc.gnu.org/ml/gcc-patches/2003-02/msg01898.html>
+;; for details.
+
+(define_insn "tls_global_dynamic"
+ [(set (match_operand:SI 0 "register_operand" "=&z")
+ (call:SI (mem:SI (unspec:SI [(match_operand:SI 1 "" "")]
+ UNSPEC_TLSGD))
+ (const_int 0)))
+ (use (reg:PSI FPSCR_REG))
+ (use (reg:SI PIC_REG))
+ (clobber (reg:SI PR_REG))
+ (clobber (scratch:SI))]
+ "TARGET_SH1"
+ "*
+{
+ return \"\\
+mov.l\\t1f,r4\\n\\
+\\tmova\\t2f,r0\\n\\
+\\tmov.l\\t2f,r1\\n\\
+\\tadd\\tr0,r1\\n\\
+\\tjsr\\t@r1\\n\\
+\\tadd\\tr12,r4\\n\\
+\\tbra\\t3f\\n\\
+\\tnop\\n\\
+\\t.align\\t2\\n\\
+1:\\t.long\\t%a1@TLSGD\\n\\
+2:\\t.long\\t__tls_get_addr@PLT\\n\\
+3:\";
+}"
+ [(set_attr "type" "tls_load")
+ (set_attr "length" "26")])
+
+(define_insn "tls_local_dynamic"
+ [(set (match_operand:SI 0 "register_operand" "=&z")
+ (call:SI (mem:SI (unspec:SI [(match_operand:SI 1 "" "")]
+ UNSPEC_TLSLDM))
+ (const_int 0)))
+ (use (reg:PSI FPSCR_REG))
+ (use (reg:SI PIC_REG))
+ (clobber (reg:SI PR_REG))
+ (clobber (scratch:SI))]
+ "TARGET_SH1"
+ "*
+{
+ return \"\\
+mov.l\\t1f,r4\\n\\
+\\tmova\\t2f,r0\\n\\
+\\tmov.l\\t2f,r1\\n\\
+\\tadd\\tr0,r1\\n\\
+\\tjsr\\t@r1\\n\\
+\\tadd\\tr12,r4\\n\\
+\\tbra\\t3f\\n\\
+\\tnop\\n\\
+\\t.align\\t2\\n\\
+1:\\t.long\\t%a1@TLSLDM\\n\\
+2:\\t.long\\t__tls_get_addr@PLT\\n\\
+3:\";
+}"
+ [(set_attr "type" "tls_load")
+ (set_attr "length" "26")])
+
+(define_expand "sym2DTPOFF"
+ [(const (unspec [(match_operand 0 "" "")] UNSPEC_DTPOFF))]
+ ""
+ "")
+
+(define_expand "symDTPOFF2reg"
+ [(match_operand 0 "" "") (match_operand 1 "" "") (match_operand 2 "" "")]
+ ""
+ "
+{
+ rtx dtpoffsym;
+ rtx t = (!can_create_pseudo_p ()
+ ? operands[0]
+ : gen_reg_rtx (GET_MODE (operands[0])));
+
+ dtpoffsym = gen_sym2DTPOFF (operands[1]);
+ PUT_MODE (dtpoffsym, Pmode);
+ emit_move_insn (t, dtpoffsym);
+ emit_move_insn (operands[0], gen_rtx_PLUS (Pmode, t, operands[2]));
+ DONE;
+}")
+
+(define_expand "sym2GOTTPOFF"
+ [(const (unspec [(match_operand 0 "" "")] UNSPEC_GOTTPOFF))]
+ ""
+ "")
+
+(define_insn "tls_initial_exec"
+ [(set (match_operand:SI 0 "register_operand" "=&r")
+ (unspec:SI [(match_operand:SI 1 "" "")]
+ UNSPEC_TLSIE))
+ (use (reg:SI GBR_REG))
+ (use (reg:SI PIC_REG))
+ (clobber (reg:SI R0_REG))]
+ ""
+ "*
+{
+ return \"\\
+mov.l\\t1f,r0\\n\\
+\\tstc\\tgbr,%0\\n\\
+\\tmov.l\\t@(r0,r12),r0\\n\\
+\\tbra\\t2f\\n\\
+\\tadd\\tr0,%0\\n\\
+\\t.align\\t2\\n\\
+1:\\t.long\\t%a1\\n\\
+2:\";
+}"
+ [(set_attr "type" "tls_load")
+ (set_attr "length" "16")])
+
+(define_expand "sym2TPOFF"
+ [(const (unspec [(match_operand 0 "" "")] UNSPEC_TPOFF))]
+ ""
+ "")
+
+(define_expand "symTPOFF2reg"
+ [(match_operand 0 "" "") (match_operand 1 "" "")]
+ ""
+ "
+{
+ rtx tpoffsym;
+
+ tpoffsym = gen_sym2TPOFF (operands[1]);
+ PUT_MODE (tpoffsym, Pmode);
+ emit_move_insn (operands[0], tpoffsym);
+ DONE;
+}")
+
+(define_insn "load_gbr"
+ [(set (match_operand:SI 0 "register_operand" "=r") (reg:SI GBR_REG))
+ (use (reg:SI GBR_REG))]
+ ""
+ "stc gbr,%0"
+ [(set_attr "type" "tls_load")])
+
+;; case instruction for switch statements.
+
+;; Operand 0 is index
+;; operand 1 is the minimum bound
+;; operand 2 is the maximum bound - minimum bound + 1
+;; operand 3 is CODE_LABEL for the table;
+;; operand 4 is the CODE_LABEL to go to if index out of range.
+
+(define_expand "casesi"
+ [(match_operand:SI 0 "arith_reg_operand" "")
+ (match_operand:SI 1 "arith_reg_operand" "")
+ (match_operand:SI 2 "arith_reg_operand" "")
+ (match_operand 3 "" "") (match_operand 4 "" "")]
+ ""
+ "
+{
+ rtx reg = gen_reg_rtx (SImode);
+ rtx reg2 = gen_reg_rtx (SImode);
+ if (TARGET_SHMEDIA)
+ {
+ rtx reg = gen_reg_rtx (DImode);
+ rtx reg2 = gen_reg_rtx (DImode);
+ rtx reg3 = gen_reg_rtx (Pmode);
+ rtx reg4 = gen_reg_rtx (Pmode);
+ rtx reg5 = gen_reg_rtx (Pmode);
+ rtx load, test;
+
+ operands[0] = convert_modes (DImode, SImode, operands[0], 0);
+ operands[1] = convert_modes (DImode, SImode, operands[1], 0);
+ operands[2] = convert_modes (DImode, SImode, operands[2], 1);
+
+ test = gen_rtx_GT (VOIDmode, operands[1], operands[0]);
+ emit_jump_insn (gen_cbranchdi4 (test, operands[1], operands[0], operands[4]));
+ emit_move_insn (reg, gen_rtx_MINUS (DImode, operands[0], operands[1]));
+ test = gen_rtx_GTU (VOIDmode, reg, operands[2]);
+ emit_jump_insn (gen_cbranchdi4 (test, reg, operands[2], operands[4]));
+ emit_insn (gen_casesi_shift_media (reg2, reg, operands[3]));
+ emit_move_insn (reg3, gen_datalabel_ref (gen_rtx_LABEL_REF
+ (Pmode, operands[3])));
+ /* Messy: can we subreg to clean this up? */
+ if (Pmode == DImode)
+ load = gen_casesi_load_media (reg4, reg3, reg2, operands[3]);
+ else
+ load = gen_casesi_load_media (reg4,
+ gen_rtx_SUBREG (DImode, reg3, 0),
+ reg2, operands[3]);
+ PUT_MODE (SET_SRC (load), Pmode);
+ emit_insn (load);
+ /* ??? The following add could be eliminated if we used ptrel. */
+ emit_move_insn (reg5, gen_rtx_PLUS (Pmode, reg3, reg4));
+ emit_jump_insn (gen_casesi_jump_media (reg5, operands[3]));
+ emit_barrier ();
+ DONE;
+ }
+ operands[1] = copy_to_mode_reg (SImode, operands[1]);
+ operands[2] = copy_to_mode_reg (SImode, operands[2]);
+ /* If optimizing, casesi_worker depends on the mode of the instruction
+ before label it 'uses' - operands[3]. */
+ emit_insn (gen_casesi_0 (operands[0], operands[1], operands[2], operands[4],
+ reg));
+ emit_insn (gen_casesi_worker_0 (reg2, reg, operands[3]));
+ if (TARGET_SH2)
+ emit_jump_insn (gen_casesi_jump_2 (reg2, gen_label_rtx (), operands[3]));
+ else
+ emit_jump_insn (gen_casesi_jump_1 (reg2, operands[3]));
+ /* For SH2 and newer, the ADDR_DIFF_VEC is not actually relative to
+ operands[3], but to lab. We will fix this up in
+ machine_dependent_reorg. */
+ emit_barrier ();
+ DONE;
+}")
+
+(define_expand "casesi_0"
+ [(set (match_operand:SI 4 "" "") (match_operand:SI 0 "arith_reg_operand" ""))
+ (set (match_dup 4) (minus:SI (match_dup 4)
+ (match_operand:SI 1 "arith_operand" "")))
+ (set (reg:SI T_REG)
+ (gtu:SI (match_dup 4)
+ (match_operand:SI 2 "arith_reg_operand" "")))
+ (set (pc)
+ (if_then_else (ne (reg:SI T_REG)
+ (const_int 0))
+ (label_ref (match_operand 3 "" ""))
+ (pc)))]
+ "TARGET_SH1"
+ "")
+
+;; ??? reload might clobber r0 if we use it explicitly in the RTL before
+;; reload; using a R0_REGS pseudo reg is likely to give poor code.
+;; So we keep the use of r0 hidden in a R0_REGS clobber until after reload.
+
+(define_insn "casesi_worker_0"
+ [(set (match_operand:SI 0 "register_operand" "=r,r")
+ (unspec:SI [(match_operand:SI 1 "register_operand" "0,r")
+ (label_ref (match_operand 2 "" ""))] UNSPEC_CASESI))
+ (clobber (match_scratch:SI 3 "=X,1"))
+ (clobber (match_scratch:SI 4 "=&z,z"))]
+ "TARGET_SH1"
+ "#")
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (unspec:SI [(match_operand:SI 1 "register_operand" "")
+ (label_ref (match_operand 2 "" ""))] UNSPEC_CASESI))
+ (clobber (match_scratch:SI 3 ""))
+ (clobber (match_scratch:SI 4 ""))]
+ "TARGET_SH1 && ! TARGET_SH2 && reload_completed"
+ [(set (reg:SI R0_REG) (unspec:SI [(label_ref (match_dup 2))] UNSPEC_MOVA))
+ (parallel [(set (match_dup 0)
+ (unspec:SI [(reg:SI R0_REG) (match_dup 1)
+ (label_ref (match_dup 2))] UNSPEC_CASESI))
+ (clobber (match_dup 3))])
+ (set (match_dup 0) (plus:SI (match_dup 0) (reg:SI R0_REG)))]
+ "if (GET_CODE (operands[2]) == CODE_LABEL) LABEL_NUSES (operands[2])++;")
+
+(define_split
+ [(set (match_operand:SI 0 "register_operand" "")
+ (unspec:SI [(match_operand:SI 1 "register_operand" "")
+ (label_ref (match_operand 2 "" ""))] UNSPEC_CASESI))
+ (clobber (match_scratch:SI 3 ""))
+ (clobber (match_scratch:SI 4 ""))]
+ "TARGET_SH2 && reload_completed"
+ [(set (reg:SI R0_REG) (unspec:SI [(label_ref (match_dup 2))] UNSPEC_MOVA))
+ (parallel [(set (match_dup 0)
+ (unspec:SI [(reg:SI R0_REG) (match_dup 1)
+ (label_ref (match_dup 2))] UNSPEC_CASESI))
+ (clobber (match_dup 3))])]
+ "if (GET_CODE (operands[2]) == CODE_LABEL) LABEL_NUSES (operands[2])++;")
+
+(define_insn "casesi_worker_1"
+ [(set (match_operand:SI 0 "register_operand" "=r,r")
+ (unspec:SI [(reg:SI R0_REG)
+ (match_operand:SI 1 "register_operand" "0,r")
+ (label_ref (match_operand 2 "" ""))] UNSPEC_CASESI))
+ (clobber (match_scratch:SI 3 "=X,1"))]
+ "TARGET_SH1"
+ "*
+{
+ rtx diff_vec = PATTERN (next_real_insn (operands[2]));
+
+ gcc_assert (GET_CODE (diff_vec) == ADDR_DIFF_VEC);
+
+ switch (GET_MODE (diff_vec))
+ {
+ case SImode:
+ return \"shll2 %1\;mov.l @(r0,%1),%0\";
+ case HImode:
+ return \"add %1,%1\;mov.w @(r0,%1),%0\";
+ case QImode:
+ if (ADDR_DIFF_VEC_FLAGS (diff_vec).offset_unsigned)
+ return \"mov.b @(r0,%1),%0\;extu.b %0,%0\";
+ return \"mov.b @(r0,%1),%0\";
+ default:
+ gcc_unreachable ();
+ }
+}"
+ [(set_attr "length" "4")])
+
+(define_insn "casesi_worker_2"
+ [(set (match_operand:SI 0 "register_operand" "=r,r")
+ (unspec:SI [(reg:SI R0_REG)
+ (match_operand:SI 1 "register_operand" "0,r")
+ (label_ref (match_operand 2 "" ""))
+ (label_ref (match_operand 3 "" ""))] UNSPEC_CASESI))
+ (clobber (match_operand:SI 4 "" "=X,1"))]
+ "TARGET_SH2 && reload_completed && flag_pic"
+ "*
+{
+ rtx diff_vec = PATTERN (next_real_insn (operands[2]));
+ const char *load;
+
+ gcc_assert (GET_CODE (diff_vec) == ADDR_DIFF_VEC);
+
+ switch (GET_MODE (diff_vec))
+ {
+ case SImode:
+ output_asm_insn (\"shll2 %1\", operands);
+ load = \"mov.l @(r0,%1),%0\"; break;
+ case HImode:
+ output_asm_insn (\"add %1,%1\", operands);
+ load = \"mov.w @(r0,%1),%0\"; break;
+ case QImode:
+ if (ADDR_DIFF_VEC_FLAGS (diff_vec).offset_unsigned)
+ load = \"mov.b @(r0,%1),%0\;extu.b %0,%0\";
+ else
+ load = \"mov.b @(r0,%1),%0\";
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ output_asm_insn (\"add\tr0,%1\;mova\t%O3,r0\\n\", operands);
+ return load;
+}"
+ [(set_attr "length" "8")])
+
+(define_insn "casesi_shift_media"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (ashift:DI (match_operand:DI 1 "arith_reg_operand" "r")
+ (unspec:DI [(label_ref:DI (match_operand 2 "" ""))]
+ UNSPEC_CASESI)))]
+ "TARGET_SHMEDIA"
+ "*
+{
+ rtx diff_vec = PATTERN (next_real_insn (operands[2]));
+
+ gcc_assert (GET_CODE (diff_vec) == ADDR_DIFF_VEC);
+
+ switch (GET_MODE (diff_vec))
+ {
+ case SImode:
+ return \"shlli %1, 2, %0\";
+ case HImode:
+ return \"shlli %1, 1, %0\";
+ case QImode:
+ if (rtx_equal_p (operands[0], operands[1]))
+ return \"\";
+ return \"add %1, r63, %0\";
+ default:
+ gcc_unreachable ();
+ }
+}"
+ [(set_attr "type" "arith_media")])
+
+(define_insn "casesi_load_media"
+ [(set (match_operand 0 "any_arith_reg_dest" "=r")
+ (mem (unspec [(match_operand:DI 1 "arith_reg_operand" "r")
+ (match_operand:DI 2 "arith_reg_operand" "r")
+ (label_ref:DI (match_operand 3 "" ""))] UNSPEC_CASESI)))]
+ "TARGET_SHMEDIA"
+ "*
+{
+ rtx diff_vec = PATTERN (next_real_insn (operands[3]));
+
+ gcc_assert (GET_CODE (diff_vec) == ADDR_DIFF_VEC);
+
+ switch (GET_MODE (diff_vec))
+ {
+ case SImode:
+ return \"ldx.l %1, %2, %0\";
+ case HImode:
+#if 0
+ if (ADDR_DIFF_VEC_FLAGS (diff_vec).offset_unsigned)
+ return \"ldx.uw %1, %2, %0\";
+#endif
+ return \"ldx.w %1, %2, %0\";
+ case QImode:
+ if (ADDR_DIFF_VEC_FLAGS (diff_vec).offset_unsigned)
+ return \"ldx.ub %1, %2, %0\";
+ return \"ldx.b %1, %2, %0\";
+ default:
+ gcc_unreachable ();
+ }
+}"
+ [(set_attr "type" "load_media")])
+
+(define_expand "return"
+ [(return)]
+ "reload_completed && ! sh_need_epilogue ()"
+ "
+{
+ if (TARGET_SHMEDIA)
+ {
+ emit_jump_insn (gen_return_media ());
+ DONE;
+ }
+
+ if (TARGET_SHCOMPACT
+ && (crtl->args.info.call_cookie & CALL_COOKIE_RET_TRAMP (1)))
+ {
+ emit_jump_insn (gen_shcompact_return_tramp ());
+ DONE;
+ }
+}")
+
+(define_insn "*return_i"
+ [(return)]
+ "TARGET_SH1 && ! (TARGET_SHCOMPACT
+ && (crtl->args.info.call_cookie
+ & CALL_COOKIE_RET_TRAMP (1)))
+ && reload_completed
+ && lookup_attribute (\"trap_exit\",
+ DECL_ATTRIBUTES (current_function_decl)) == NULL_TREE"
+ "*
+ {
+ if (TARGET_SH2A && (dbr_sequence_length () == 0)
+ && !current_function_interrupt)
+ return \"rts/n\";
+ else
+ return \"%@ %#\";
+ }"
+ [(set_attr "type" "return")
+ (set_attr "needs_delay_slot" "yes")])
+
+;; trapa has no delay slot.
+(define_insn "*return_trapa"
+ [(return)]
+ "TARGET_SH1 && !TARGET_SHCOMPACT
+ && reload_completed"
+ "%@"
+ [(set_attr "type" "return")])
+
+(define_expand "shcompact_return_tramp"
+ [(return)]
+ "TARGET_SHCOMPACT
+ && (crtl->args.info.call_cookie & CALL_COOKIE_RET_TRAMP (1))"
+ "
+{
+ rtx reg = gen_rtx_REG (Pmode, R0_REG);
+
+ function_symbol (reg, \"__GCC_shcompact_return_trampoline\", SFUNC_STATIC);
+ emit_jump_insn (gen_shcompact_return_tramp_i ());
+ DONE;
+}")
+
+(define_insn "shcompact_return_tramp_i"
+ [(parallel [(return) (use (reg:SI R0_REG))])]
+ "TARGET_SHCOMPACT
+ && (crtl->args.info.call_cookie & CALL_COOKIE_RET_TRAMP (1))"
+ "jmp @r0%#"
+ [(set_attr "type" "jump_ind")
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_insn "return_media_i"
+ [(parallel [(return) (use (match_operand 0 "target_reg_operand" "k"))])]
+ "TARGET_SHMEDIA && reload_completed"
+ "blink %0, r63"
+ [(set_attr "type" "jump_media")])
+
+(define_insn "return_media_rte"
+ [(return)]
+ "TARGET_SHMEDIA && reload_completed && current_function_interrupt"
+ "rte"
+ [(set_attr "type" "jump_media")])
+
+(define_expand "return_media"
+ [(return)]
+ "TARGET_SHMEDIA && reload_completed"
+ "
+{
+ int tr_regno = sh_media_register_for_return ();
+ rtx tr;
+
+ if (current_function_interrupt)
+ {
+ emit_jump_insn (gen_return_media_rte ());
+ DONE;
+ }
+ if (tr_regno < 0)
+ {
+ rtx r18 = gen_rtx_REG (Pmode, PR_MEDIA_REG);
+
+ gcc_assert (call_really_used_regs[TR0_REG] && !fixed_regs[TR0_REG]);
+ tr_regno = TR0_REG;
+ tr = gen_rtx_REG (Pmode, tr_regno);
+ emit_move_insn (tr, r18);
+ }
+ else
+ tr = gen_rtx_REG (Pmode, tr_regno);
+
+ emit_jump_insn (gen_return_media_i (tr));
+ DONE;
+}")
+
+(define_insn "shcompact_preserve_incoming_args"
+ [(set (match_operand:SI 0 "register_operand" "+r")
+ (unspec:SI [(match_dup 0)] UNSPEC_COMPACT_ARGS))]
+ "TARGET_SHCOMPACT"
+ ""
+ [(set_attr "length" "0")])
+
+(define_insn "shcompact_incoming_args"
+ [(set (reg:SI R2_REG) (unspec:SI [(reg:SI R2_REG)] UNSPEC_COMPACT_ARGS))
+ (set (reg:SI R3_REG) (unspec:SI [(reg:SI R3_REG)] UNSPEC_COMPACT_ARGS))
+ (set (reg:SI R4_REG) (unspec:SI [(reg:SI R4_REG)] UNSPEC_COMPACT_ARGS))
+ (set (reg:SI R5_REG) (unspec:SI [(reg:SI R5_REG)] UNSPEC_COMPACT_ARGS))
+ (set (reg:SI R6_REG) (unspec:SI [(reg:SI R6_REG)] UNSPEC_COMPACT_ARGS))
+ (set (reg:SI R7_REG) (unspec:SI [(reg:SI R7_REG)] UNSPEC_COMPACT_ARGS))
+ (set (reg:SI R8_REG) (unspec:SI [(reg:SI R8_REG)] UNSPEC_COMPACT_ARGS))
+ (set (reg:SI R9_REG) (unspec:SI [(reg:SI R9_REG)] UNSPEC_COMPACT_ARGS))
+ (set (mem:BLK (reg:SI MACL_REG))
+ (unspec:BLK [(reg:SI MACH_REG)] UNSPEC_COMPACT_ARGS))
+ (use (reg:SI R0_REG))
+ (clobber (reg:SI R0_REG))
+ (clobber (reg:SI MACL_REG))
+ (clobber (reg:SI MACH_REG))
+ (clobber (reg:SI PR_REG))]
+ "TARGET_SHCOMPACT"
+ "jsr @r0%#"
+ [(set_attr "needs_delay_slot" "yes")])
+
+(define_insn "shmedia_save_restore_regs_compact"
+ [(set (reg:SI SP_REG)
+ (plus:SI (reg:SI SP_REG)
+ (match_operand:SI 0 "immediate_operand" "i")))
+ (use (reg:SI R0_REG))
+ (clobber (reg:SI PR_REG))]
+ "TARGET_SHCOMPACT
+ && (INTVAL (operands[0]) == SHMEDIA_REGS_STACK_ADJUST ()
+ || INTVAL (operands[0]) == - SHMEDIA_REGS_STACK_ADJUST ())"
+ "jsr @r0%#"
+ [(set_attr "needs_delay_slot" "yes")])
+
+(define_expand "prologue"
+ [(const_int 0)]
+ ""
+ "sh_expand_prologue (); DONE;")
+
+(define_expand "epilogue"
+ [(return)]
+ ""
+ "
+{
+ sh_expand_epilogue (0);
+ emit_jump_insn (gen_return ());
+ DONE;
+}")
+
+(define_expand "eh_return"
+ [(use (match_operand 0 "register_operand" ""))]
+ ""
+{
+ rtx ra = operands[0];
+
+ if (TARGET_SHMEDIA64)
+ emit_insn (gen_eh_set_ra_di (ra));
+ else
+ emit_insn (gen_eh_set_ra_si (ra));
+
+ DONE;
+})
+
+;; Clobber the return address on the stack. We can't expand this
+;; until we know where it will be put in the stack frame.
+
+(define_insn "eh_set_ra_si"
+ [(unspec_volatile [(match_operand:SI 0 "register_operand" "r")]
+ UNSPECV_EH_RETURN)
+ (clobber (match_scratch:SI 1 "=&r"))]
+ "! TARGET_SHMEDIA64"
+ "#")
+
+(define_insn "eh_set_ra_di"
+ [(unspec_volatile [(match_operand:DI 0 "register_operand" "r")]
+ UNSPECV_EH_RETURN)
+ (clobber (match_scratch:DI 1 "=&r"))]
+ "TARGET_SHMEDIA64"
+ "#")
+
+(define_split
+ [(unspec_volatile [(match_operand 0 "register_operand" "")]
+ UNSPECV_EH_RETURN)
+ (clobber (match_scratch 1 ""))]
+ "reload_completed"
+ [(const_int 0)]
+ "
+{
+ sh_set_return_address (operands[0], operands[1]);
+ DONE;
+}")
+
+(define_insn "blockage"
+ [(unspec_volatile [(const_int 0)] UNSPECV_BLOCKAGE)]
+ ""
+ ""
+ [(set_attr "length" "0")])
+
+;; Define movml instructions for SH2A target. Currently they are
+;; used to push and pop all banked registers only.
+
+(define_insn "movml_push_banked"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (plus (match_dup 0) (const_int -32)))
+ (set (mem:SI (plus:SI (match_dup 0) (const_int 28))) (reg:SI R7_REG))
+ (set (mem:SI (plus:SI (match_dup 0) (const_int 24))) (reg:SI R6_REG))
+ (set (mem:SI (plus:SI (match_dup 0) (const_int 20))) (reg:SI R5_REG))
+ (set (mem:SI (plus:SI (match_dup 0) (const_int 16))) (reg:SI R4_REG))
+ (set (mem:SI (plus:SI (match_dup 0) (const_int 12))) (reg:SI R3_REG))
+ (set (mem:SI (plus:SI (match_dup 0) (const_int 8))) (reg:SI R2_REG))
+ (set (mem:SI (plus:SI (match_dup 0) (const_int 4))) (reg:SI R1_REG))
+ (set (mem:SI (plus:SI (match_dup 0) (const_int 0))) (reg:SI R0_REG))]
+ "TARGET_SH2A && REGNO (operands[0]) == 15"
+ "movml.l\tr7,@-r15"
+ [(set_attr "in_delay_slot" "no")])
+
+(define_insn "movml_pop_banked"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (plus (match_dup 0) (const_int 32)))
+ (set (reg:SI R0_REG) (mem:SI (plus:SI (match_dup 0) (const_int -32))))
+ (set (reg:SI R1_REG) (mem:SI (plus:SI (match_dup 0) (const_int -28))))
+ (set (reg:SI R2_REG) (mem:SI (plus:SI (match_dup 0) (const_int -24))))
+ (set (reg:SI R3_REG) (mem:SI (plus:SI (match_dup 0) (const_int -20))))
+ (set (reg:SI R4_REG) (mem:SI (plus:SI (match_dup 0) (const_int -16))))
+ (set (reg:SI R5_REG) (mem:SI (plus:SI (match_dup 0) (const_int -12))))
+ (set (reg:SI R6_REG) (mem:SI (plus:SI (match_dup 0) (const_int -8))))
+ (set (reg:SI R7_REG) (mem:SI (plus:SI (match_dup 0) (const_int -4))))]
+ "TARGET_SH2A && REGNO (operands[0]) == 15"
+ "movml.l\t@r15+,r7"
+ [(set_attr "in_delay_slot" "no")])
+
+;; ------------------------------------------------------------------------
+;; Scc instructions
+;; ------------------------------------------------------------------------
+
+(define_insn "movt"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (eq:SI (reg:SI T_REG) (const_int 1)))]
+ "TARGET_SH1"
+ "movt %0"
+ [(set_attr "type" "arith")])
+
+(define_expand "cstore4_media"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (match_operator:SI 1 "sh_float_comparison_operator"
+ [(match_operand 2 "logical_operand" "")
+ (match_operand 3 "cmp_operand" "")]))]
+ "TARGET_SHMEDIA"
+ "
+{
+ enum machine_mode mode = GET_MODE (operands[2]);
+ enum rtx_code code = GET_CODE (operands[1]);
+ bool invert, swap;
+ if (mode == VOIDmode)
+ mode = GET_MODE (operands[3]);
+ if (operands[2] == const0_rtx)
+ {
+ if (code == EQ || code == NE)
+ operands[2] = operands[3], operands[3] = const0_rtx;
+ }
+ else
+ operands[2] = force_reg (mode, operands[2]);
+ if (operands[3] != const0_rtx)
+ operands[3] = force_reg (mode, operands[3]);
+
+ switch (code)
+ {
+ case GEU:
+ case GE:
+ swap = invert = !FLOAT_MODE_P (mode);
+ break;
+
+ case LEU:
+ case LE:
+ swap = FLOAT_MODE_P (mode), invert = !swap;
+ break;
+
+ case LTU:
+ case LT:
+ swap = true, invert = false;
+ break;
+
+ case GTU:
+ case GT:
+ case EQ:
+ case UNORDERED:
+ swap = invert = false;
+ break;
+
+ case NE:
+ swap = invert = true;
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ if (swap)
+ {
+ rtx tem = operands[2];
+ operands[2] = operands[3];
+ operands[3] = tem;
+ code = swap_condition (code);
+ }
+
+ if (invert)
+ {
+ rtx tem = can_create_pseudo_p () ? gen_reg_rtx (SImode) : operands[0];
+ code = reverse_condition (code);
+ operands[1] = gen_rtx_fmt_ee (code, VOIDmode, operands[2], operands[3]);
+ emit_insn (gen_cstore4_media (tem, operands[1],
+ operands[2], operands[3]));
+ code = EQ;
+ operands[2] = tem;
+ operands[3] = const0_rtx;
+ }
+
+ operands[1] = gen_rtx_fmt_ee (code, VOIDmode, operands[2], operands[3]);
+}")
+
+(define_expand "cstoresi4"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (match_operator:SI 1 "comparison_operator"
+ [(match_operand:SI 2 "cmpsi_operand" "")
+ (match_operand:SI 3 "arith_operand" "")]))]
+ "TARGET_SH1 || TARGET_SHMEDIA"
+ "if (TARGET_SHMEDIA)
+ {
+ emit_insn (gen_cstore4_media (operands[0], operands[1],
+ operands[2], operands[3]));
+ DONE;
+ }
+
+ if ((GET_CODE (operands[1]) == EQ || GET_CODE (operands[1]) == NE)
+ && sh_expand_t_scc (operands))
+ DONE;
+
+ if (! currently_expanding_to_rtl)
+ FAIL;
+
+ sh_emit_compare_and_set (operands, SImode);
+ DONE;
+")
+
+(define_expand "cstoredi4"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (match_operator:SI 1 "comparison_operator"
+ [(match_operand:DI 2 "arith_operand" "")
+ (match_operand:DI 3 "arith_operand" "")]))]
+ "TARGET_SH2 || TARGET_SHMEDIA"
+ "if (TARGET_SHMEDIA)
+ {
+ emit_insn (gen_cstore4_media (operands[0], operands[1],
+ operands[2], operands[3]));
+ DONE;
+ }
+
+ if ((GET_CODE (operands[1]) == EQ || GET_CODE (operands[1]) == NE)
+ && sh_expand_t_scc (operands))
+ DONE;
+
+ if (! currently_expanding_to_rtl)
+ FAIL;
+
+ sh_emit_compare_and_set (operands, DImode);
+ DONE;
+")
+
+
+
+;; sne moves the complement of the T reg to DEST like this:
+;; cmp/eq ...
+;; mov #-1,temp
+;; negc temp,dest
+;; This is better than xoring compare result with 1 because it does
+;; not require r0 and further, the -1 may be CSE-ed or lifted out of a
+;; loop.
+
+(define_expand "movnegt"
+ [(set (match_dup 1) (const_int -1))
+ (parallel [(set (match_operand:SI 0 "" "")
+ (neg:SI (plus:SI (reg:SI T_REG)
+ (match_dup 1))))
+ (set (reg:SI T_REG)
+ (ne:SI (ior:SI (reg:SI T_REG) (match_dup 1))
+ (const_int 0)))])]
+ ""
+ "
+{
+ operands[1] = gen_reg_rtx (SImode);
+}")
+
+
+;; Recognize mov #-1/negc/neg sequence, and change it to movt/add #-1.
+;; This prevents a regression that occurred when we switched from xor to
+;; mov/neg for sne.
+
+(define_split
+ [(set (match_operand:SI 0 "arith_reg_dest" "")
+ (plus:SI (reg:SI T_REG)
+ (const_int -1)))]
+ "TARGET_SH1"
+ [(set (match_dup 0) (eq:SI (reg:SI T_REG) (const_int 1)))
+ (set (match_dup 0) (plus:SI (match_dup 0) (const_int -1)))]
+ "")
+
+(define_expand "cstoresf4"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (match_operator:SI 1 "sh_float_comparison_operator"
+ [(match_operand:SF 2 "arith_operand" "")
+ (match_operand:SF 3 "arith_operand" "")]))]
+ "TARGET_SH2E || TARGET_SHMEDIA_FPU"
+ "if (TARGET_SHMEDIA)
+ {
+ emit_insn (gen_cstore4_media (operands[0], operands[1],
+ operands[2], operands[3]));
+ DONE;
+ }
+
+ if (! currently_expanding_to_rtl)
+ FAIL;
+
+ sh_emit_compare_and_set (operands, SFmode);
+ DONE;
+")
+
+(define_expand "cstoredf4"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (match_operator:SI 1 "sh_float_comparison_operator"
+ [(match_operand:DF 2 "arith_operand" "")
+ (match_operand:DF 3 "arith_operand" "")]))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE) || TARGET_SHMEDIA_FPU"
+ "if (TARGET_SHMEDIA)
+ {
+ emit_insn (gen_cstore4_media (operands[0], operands[1],
+ operands[2], operands[3]));
+ DONE;
+ }
+
+ if (! currently_expanding_to_rtl)
+ FAIL;
+
+ sh_emit_compare_and_set (operands, DFmode);
+ DONE;
+")
+
+
+;; -------------------------------------------------------------------------
+;; Instructions to cope with inline literal tables
+;; -------------------------------------------------------------------------
+
+; 2 byte integer in line
+
+(define_insn "consttable_2"
+ [(unspec_volatile [(match_operand:SI 0 "general_operand" "=g")
+ (match_operand 1 "" "")]
+ UNSPECV_CONST2)]
+ ""
+ "*
+{
+ if (operands[1] != const0_rtx)
+ assemble_integer (operands[0], 2, BITS_PER_UNIT * 2, 1);
+ return \"\";
+}"
+ [(set_attr "length" "2")
+ (set_attr "in_delay_slot" "no")])
+
+; 4 byte integer in line
+
+(define_insn "consttable_4"
+ [(unspec_volatile [(match_operand:SI 0 "general_operand" "=g")
+ (match_operand 1 "" "")]
+ UNSPECV_CONST4)]
+ ""
+ "*
+{
+ if (operands[1] != const0_rtx)
+ {
+ assemble_integer (operands[0], 4, BITS_PER_UNIT * 4, 1);
+ mark_symbol_refs_as_used (operands[0]);
+ }
+ return \"\";
+}"
+ [(set_attr "length" "4")
+ (set_attr "in_delay_slot" "no")])
+
+; 8 byte integer in line
+
+(define_insn "consttable_8"
+ [(unspec_volatile [(match_operand:SI 0 "general_operand" "=g")
+ (match_operand 1 "" "")]
+ UNSPECV_CONST8)]
+ ""
+ "*
+{
+ if (operands[1] != const0_rtx)
+ assemble_integer (operands[0], 8, BITS_PER_UNIT * 8, 1);
+ return \"\";
+}"
+ [(set_attr "length" "8")
+ (set_attr "in_delay_slot" "no")])
+
+; 4 byte floating point
+
+(define_insn "consttable_sf"
+ [(unspec_volatile [(match_operand:SF 0 "general_operand" "=g")
+ (match_operand 1 "" "")]
+ UNSPECV_CONST4)]
+ ""
+ "*
+{
+ if (operands[1] != const0_rtx)
+ {
+ REAL_VALUE_TYPE d;
+ REAL_VALUE_FROM_CONST_DOUBLE (d, operands[0]);
+ assemble_real (d, SFmode, GET_MODE_ALIGNMENT (SFmode));
+ }
+ return \"\";
+}"
+ [(set_attr "length" "4")
+ (set_attr "in_delay_slot" "no")])
+
+; 8 byte floating point
+
+(define_insn "consttable_df"
+ [(unspec_volatile [(match_operand:DF 0 "general_operand" "=g")
+ (match_operand 1 "" "")]
+ UNSPECV_CONST8)]
+ ""
+ "*
+{
+ if (operands[1] != const0_rtx)
+ {
+ REAL_VALUE_TYPE d;
+ REAL_VALUE_FROM_CONST_DOUBLE (d, operands[0]);
+ assemble_real (d, DFmode, GET_MODE_ALIGNMENT (DFmode));
+ }
+ return \"\";
+}"
+ [(set_attr "length" "8")
+ (set_attr "in_delay_slot" "no")])
+
+;; Alignment is needed for some constant tables; it may also be added for
+;; Instructions at the start of loops, or after unconditional branches.
+;; ??? We would get more accurate lengths if we did instruction
+;; alignment based on the value of INSN_CURRENT_ADDRESS; the approach used
+;; here is too conservative.
+
+; align to a two byte boundary
+
+(define_expand "align_2"
+ [(unspec_volatile [(const_int 1)] UNSPECV_ALIGN)]
+ ""
+ "")
+
+; align to a four byte boundary
+;; align_4 and align_log are instructions for the starts of loops, or
+;; after unconditional branches, which may take up extra room.
+
+(define_expand "align_4"
+ [(unspec_volatile [(const_int 2)] UNSPECV_ALIGN)]
+ ""
+ "")
+
+; align to a cache line boundary
+
+(define_insn "align_log"
+ [(unspec_volatile [(match_operand 0 "const_int_operand" "")] UNSPECV_ALIGN)]
+ ""
+ ""
+ [(set_attr "length" "0")
+ (set_attr "in_delay_slot" "no")])
+
+; emitted at the end of the literal table, used to emit the
+; 32bit branch labels if needed.
+
+(define_insn "consttable_end"
+ [(unspec_volatile [(const_int 0)] UNSPECV_CONST_END)]
+ ""
+ "* return output_jump_label_table ();"
+ [(set_attr "in_delay_slot" "no")])
+
+; emitted at the end of the window in the literal table.
+
+(define_insn "consttable_window_end"
+ [(unspec_volatile [(match_operand 0 "" "")] UNSPECV_WINDOW_END)]
+ ""
+ ""
+ [(set_attr "length" "0")
+ (set_attr "in_delay_slot" "no")])
+
+;; -------------------------------------------------------------------------
+;; Misc
+;; -------------------------------------------------------------------------
+
+;; String/block move insn.
+
+(define_expand "movmemsi"
+ [(parallel [(set (mem:BLK (match_operand:BLK 0 "" ""))
+ (mem:BLK (match_operand:BLK 1 "" "")))
+ (use (match_operand:SI 2 "nonmemory_operand" ""))
+ (use (match_operand:SI 3 "immediate_operand" ""))
+ (clobber (reg:SI PR_REG))
+ (clobber (reg:SI R4_REG))
+ (clobber (reg:SI R5_REG))
+ (clobber (reg:SI R0_REG))])]
+ "TARGET_SH1 && ! TARGET_SH5"
+ "
+{
+ if(expand_block_move (operands))
+ DONE;
+ else FAIL;
+}")
+
+(define_insn "block_move_real"
+ [(parallel [(set (mem:BLK (reg:SI R4_REG))
+ (mem:BLK (reg:SI R5_REG)))
+ (use (match_operand:SI 0 "arith_reg_operand" "r"))
+ (clobber (reg:SI PR_REG))
+ (clobber (reg:SI R0_REG))])]
+ "TARGET_SH1 && ! TARGET_HARD_SH4"
+ "jsr @%0%#"
+ [(set_attr "type" "sfunc")
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_insn "block_lump_real"
+ [(parallel [(set (mem:BLK (reg:SI R4_REG))
+ (mem:BLK (reg:SI R5_REG)))
+ (use (match_operand:SI 0 "arith_reg_operand" "r"))
+ (use (reg:SI R6_REG))
+ (clobber (reg:SI PR_REG))
+ (clobber (reg:SI T_REG))
+ (clobber (reg:SI R4_REG))
+ (clobber (reg:SI R5_REG))
+ (clobber (reg:SI R6_REG))
+ (clobber (reg:SI R0_REG))])]
+ "TARGET_SH1 && ! TARGET_HARD_SH4"
+ "jsr @%0%#"
+ [(set_attr "type" "sfunc")
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_insn "block_move_real_i4"
+ [(parallel [(set (mem:BLK (reg:SI R4_REG))
+ (mem:BLK (reg:SI R5_REG)))
+ (use (match_operand:SI 0 "arith_reg_operand" "r"))
+ (clobber (reg:SI PR_REG))
+ (clobber (reg:SI R0_REG))
+ (clobber (reg:SI R1_REG))
+ (clobber (reg:SI R2_REG))])]
+ "TARGET_HARD_SH4"
+ "jsr @%0%#"
+ [(set_attr "type" "sfunc")
+ (set_attr "needs_delay_slot" "yes")])
+
+(define_insn "block_lump_real_i4"
+ [(parallel [(set (mem:BLK (reg:SI R4_REG))
+ (mem:BLK (reg:SI R5_REG)))
+ (use (match_operand:SI 0 "arith_reg_operand" "r"))
+ (use (reg:SI R6_REG))
+ (clobber (reg:SI PR_REG))
+ (clobber (reg:SI T_REG))
+ (clobber (reg:SI R4_REG))
+ (clobber (reg:SI R5_REG))
+ (clobber (reg:SI R6_REG))
+ (clobber (reg:SI R0_REG))
+ (clobber (reg:SI R1_REG))
+ (clobber (reg:SI R2_REG))
+ (clobber (reg:SI R3_REG))])]
+ "TARGET_HARD_SH4"
+ "jsr @%0%#"
+ [(set_attr "type" "sfunc")
+ (set_attr "needs_delay_slot" "yes")])
+
+;; -------------------------------------------------------------------------
+;; Floating point instructions.
+;; -------------------------------------------------------------------------
+
+;; ??? All patterns should have a type attribute.
+
+(define_expand "movpsi"
+ [(set (match_operand:PSI 0 "register_operand" "")
+ (match_operand:PSI 1 "general_movsrc_operand" ""))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE)"
+ "")
+
+;; The c / m alternative is a fake to guide reload to load directly into
+;; fpscr, since reload doesn't know how to use post-increment.
+;; TARGET_LEGITIMATE_ADDRESS_P guards about bogus addresses before reload,
+;; SECONDARY_INPUT_RELOAD_CLASS does this during reload, and the insn's
+;; predicate after reload.
+;; The mac_gp type for r/!c might look a bit odd, but it actually schedules
+;; like a mac -> gpr move.
+(define_insn "fpu_switch"
+ [(set (match_operand:PSI 0 "general_movdst_operand" "=c,c,r,c,c,r,m,r,<")
+ (match_operand:PSI 1 "general_movsrc_operand" "c,>,m,m,r,r,r,!c,c"))]
+ "TARGET_SH2E
+ && (! reload_completed
+ || true_regnum (operands[0]) != FPSCR_REG
+ || !MEM_P (operands[1])
+ || GET_CODE (XEXP (operands[1], 0)) != PLUS)"
+ "@
+ ! precision stays the same
+ lds.l %1,fpscr
+ mov.l %1,%0
+ #
+ lds %1,fpscr
+ mov %1,%0
+ mov.l %1,%0
+ sts fpscr,%0
+ sts.l fpscr,%0"
+ [(set_attr "length" "0,2,2,4,2,2,2,2,2")
+ (set_attr "type" "nil,mem_fpscr,load,mem_fpscr,gp_fpscr,move,store,mac_gp,fstore")])
+
+(define_peephole2
+ [(set (reg:PSI FPSCR_REG)
+ (mem:PSI (match_operand:SI 0 "register_operand" "")))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE) && peep2_reg_dead_p (1, operands[0])"
+ [(const_int 0)]
+{
+ rtx fpscr, mem, new_insn;
+
+ fpscr = SET_DEST (PATTERN (curr_insn));
+ mem = SET_SRC (PATTERN (curr_insn));
+ mem = replace_equiv_address (mem, gen_rtx_POST_INC (Pmode, operands[0]));
+
+ new_insn = emit_insn (gen_fpu_switch (fpscr, mem));
+ add_reg_note (new_insn, REG_INC, operands[0]);
+ DONE;
+})
+
+(define_split
+ [(set (reg:PSI FPSCR_REG)
+ (mem:PSI (match_operand:SI 0 "register_operand" "")))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE)
+ && (flag_peephole2 ? epilogue_completed : reload_completed)"
+ [(const_int 0)]
+{
+ rtx fpscr, mem, new_insn;
+
+ fpscr = SET_DEST (PATTERN (curr_insn));
+ mem = SET_SRC (PATTERN (curr_insn));
+ mem = replace_equiv_address (mem, gen_rtx_POST_INC (Pmode, operands[0]));
+
+ new_insn = emit_insn (gen_fpu_switch (fpscr, mem));
+ add_reg_note (new_insn, REG_INC, operands[0]);
+
+ if (!find_regno_note (curr_insn, REG_DEAD, true_regnum (operands[0])))
+ emit_insn (gen_addsi3 (operands[0], operands[0], GEN_INT (-4)));
+ DONE;
+})
+
+;; ??? This uses the fp unit, but has no type indicating that.
+;; If we did that, this would either give a bogus latency or introduce
+;; a bogus FIFO constraint.
+;; Since this insn is currently only used for prologues/epilogues,
+;; it is probably best to claim no function unit, which matches the
+;; current setting.
+(define_insn "toggle_sz"
+ [(set (reg:PSI FPSCR_REG)
+ (xor:PSI (reg:PSI FPSCR_REG) (const_int 1048576)))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE)"
+ "fschg"
+ [(set_attr "type" "fpscr_toggle") (set_attr "fp_set" "unknown")])
+
+;; There's no way we can use it today, since optimize mode switching
+;; doesn't enable us to know from which mode we're switching to the
+;; mode it requests, to tell whether we can use a relative mode switch
+;; (like toggle_pr) or an absolute switch (like loading fpscr from
+;; memory).
+(define_insn "toggle_pr"
+ [(set (reg:PSI FPSCR_REG)
+ (xor:PSI (reg:PSI FPSCR_REG) (const_int 524288)))]
+ "TARGET_SH4A_FP && ! TARGET_FPU_SINGLE"
+ "fpchg"
+ [(set_attr "type" "fpscr_toggle")])
+
+(define_expand "addsf3"
+ [(set (match_operand:SF 0 "arith_reg_operand" "")
+ (plus:SF (match_operand:SF 1 "arith_reg_operand" "")
+ (match_operand:SF 2 "arith_reg_operand" "")))]
+ "TARGET_SH2E || TARGET_SHMEDIA_FPU"
+ "
+{
+ if (TARGET_SH2E)
+ {
+ expand_sf_binop (&gen_addsf3_i, operands);
+ DONE;
+ }
+}")
+
+(define_insn "*addsf3_media"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "=f")
+ (plus:SF (match_operand:SF 1 "fp_arith_reg_operand" "%f")
+ (match_operand:SF 2 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "fadd.s %1, %2, %0"
+ [(set_attr "type" "fparith_media")])
+
+(define_insn_and_split "unary_sf_op"
+ [(set (match_operand:V2SF 0 "fp_arith_reg_operand" "=f")
+ (vec_select:V2SF
+ (vec_concat:V2SF
+ (vec_select:SF
+ (match_dup 0)
+ (parallel [(not:BI (match_operand 3 "const_int_operand" "n"))]))
+ (match_operator:SF 2 "unary_float_operator"
+ [(vec_select:SF (match_operand:V2SF 1 "fp_arith_reg_operand" "f")
+ (parallel [(match_operand 4
+ "const_int_operand" "n")]))]))
+ (parallel [(not:BI (match_dup 3)) (match_dup 3)])))]
+ "TARGET_SHMEDIA_FPU"
+ "#"
+ "TARGET_SHMEDIA_FPU && reload_completed"
+ [(set (match_dup 5) (match_dup 6))]
+ "
+{
+ int endian = TARGET_LITTLE_ENDIAN ? 0 : 1;
+ rtx op1 = gen_rtx_REG (SFmode,
+ (true_regnum (operands[1])
+ + (INTVAL (operands[4]) ^ endian)));
+
+ operands[7] = gen_rtx_REG (SFmode,
+ (true_regnum (operands[0])
+ + (INTVAL (operands[3]) ^ endian)));
+ operands[6] = gen_rtx_fmt_e (GET_CODE (operands[2]), SFmode, op1);
+}"
+ [(set_attr "type" "fparith_media")])
+
+(define_insn_and_split "binary_sf_op0"
+ [(set (match_operand:V2SF 0 "fp_arith_reg_operand" "=f")
+ (vec_concat:V2SF
+ (match_operator:SF 3 "binary_float_operator"
+ [(vec_select:SF (match_operand:V2SF 1 "fp_arith_reg_operand" "f")
+ (parallel [(const_int 0)]))
+ (vec_select:SF (match_operand:V2SF 2 "fp_arith_reg_operand" "f")
+ (parallel [(const_int 0)]))])
+ (vec_select:SF
+ (match_dup 0)
+ (parallel [(const_int 1)]))))]
+ "TARGET_SHMEDIA_FPU"
+ "#"
+ "&& reload_completed"
+ [(set (match_dup 4) (match_dup 5))]
+ "
+{
+ int endian = TARGET_LITTLE_ENDIAN ? 0 : 1;
+ rtx op1 = gen_rtx_REG (SFmode,
+ true_regnum (operands[1]) + endian);
+ rtx op2 = gen_rtx_REG (SFmode,
+ true_regnum (operands[2]) + endian);
+
+ operands[4] = gen_rtx_REG (SFmode,
+ true_regnum (operands[0]) + endian);
+ operands[5] = gen_rtx_fmt_ee (GET_CODE (operands[3]), SFmode, op1, op2);
+}"
+ [(set_attr "type" "fparith_media")])
+
+(define_insn_and_split "binary_sf_op1"
+ [(set (match_operand:V2SF 0 "fp_arith_reg_operand" "=f")
+ (vec_concat:V2SF
+ (vec_select:SF
+ (match_dup 0)
+ (parallel [(const_int 0)]))
+ (match_operator:SF 3 "binary_float_operator"
+ [(vec_select:SF (match_operand:V2SF 1 "fp_arith_reg_operand" "f")
+ (parallel [(const_int 1)]))
+ (vec_select:SF (match_operand:V2SF 2 "fp_arith_reg_operand" "f")
+ (parallel [(const_int 1)]))])))]
+ "TARGET_SHMEDIA_FPU"
+ "#"
+ "&& reload_completed"
+ [(set (match_dup 4) (match_dup 5))]
+ "
+{
+ int endian = TARGET_LITTLE_ENDIAN ? 0 : 1;
+ rtx op1 = gen_rtx_REG (SFmode,
+ true_regnum (operands[1]) + (1 ^ endian));
+ rtx op2 = gen_rtx_REG (SFmode,
+ true_regnum (operands[2]) + (1 ^ endian));
+
+ operands[4] = gen_rtx_REG (SFmode,
+ true_regnum (operands[0]) + (1 ^ endian));
+ operands[5] = gen_rtx_fmt_ee (GET_CODE (operands[3]), SFmode, op1, op2);
+}"
+ [(set_attr "type" "fparith_media")])
+
+(define_insn "addsf3_i"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "=f")
+ (plus:SF (match_operand:SF 1 "fp_arith_reg_operand" "%0")
+ (match_operand:SF 2 "fp_arith_reg_operand" "f")))
+ (use (match_operand:PSI 3 "fpscr_operand" "c"))]
+ "TARGET_SH2E"
+ "fadd %2,%0"
+ [(set_attr "type" "fp")
+ (set_attr "fp_mode" "single")])
+
+(define_expand "subsf3"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "")
+ (minus:SF (match_operand:SF 1 "fp_arith_reg_operand" "")
+ (match_operand:SF 2 "fp_arith_reg_operand" "")))]
+ "TARGET_SH2E || TARGET_SHMEDIA_FPU"
+ "
+{
+ if (TARGET_SH2E)
+ {
+ expand_sf_binop (&gen_subsf3_i, operands);
+ DONE;
+ }
+}")
+
+(define_insn "*subsf3_media"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "=f")
+ (minus:SF (match_operand:SF 1 "fp_arith_reg_operand" "f")
+ (match_operand:SF 2 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "fsub.s %1, %2, %0"
+ [(set_attr "type" "fparith_media")])
+
+(define_insn "subsf3_i"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "=f")
+ (minus:SF (match_operand:SF 1 "fp_arith_reg_operand" "0")
+ (match_operand:SF 2 "fp_arith_reg_operand" "f")))
+ (use (match_operand:PSI 3 "fpscr_operand" "c"))]
+ "TARGET_SH2E"
+ "fsub %2,%0"
+ [(set_attr "type" "fp")
+ (set_attr "fp_mode" "single")])
+
+(define_expand "mulsf3"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "")
+ (mult:SF (match_operand:SF 1 "fp_arith_reg_operand" "")
+ (match_operand:SF 2 "fp_arith_reg_operand" "")))]
+ "TARGET_SH2E || TARGET_SHMEDIA_FPU"
+ "")
+
+(define_insn "*mulsf3_media"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "=f")
+ (mult:SF (match_operand:SF 1 "fp_arith_reg_operand" "%f")
+ (match_operand:SF 2 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "fmul.s %1, %2, %0"
+ [(set_attr "type" "fparith_media")])
+
+;; Unfortunately, the combiner is unable to cope with the USE of the FPSCR
+;; register in feeding fp instructions. Thus, in order to generate fmac,
+;; we start out with a mulsf pattern that does not depend on fpscr.
+;; This is split after combine to introduce the dependency, in order to
+;; get mode switching and scheduling right.
+(define_insn_and_split "mulsf3_ie"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "=f")
+ (mult:SF (match_operand:SF 1 "fp_arith_reg_operand" "%0")
+ (match_operand:SF 2 "fp_arith_reg_operand" "f")))]
+ "TARGET_SH2E"
+ "fmul %2,%0"
+ "TARGET_SH4 || TARGET_SH2A_SINGLE"
+ [(const_int 0)]
+ "
+{
+ emit_insn (gen_mulsf3_i4 (operands[0], operands[1], operands[2],
+ get_fpscr_rtx ()));
+ DONE;
+}"
+ [(set_attr "type" "fp")])
+
+(define_insn "mulsf3_i4"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "=f")
+ (mult:SF (match_operand:SF 1 "fp_arith_reg_operand" "%0")
+ (match_operand:SF 2 "fp_arith_reg_operand" "f")))
+ (use (match_operand:PSI 3 "fpscr_operand" "c"))]
+ "TARGET_SH2E"
+ "fmul %2,%0"
+ [(set_attr "type" "fp")
+ (set_attr "fp_mode" "single")])
+
+(define_insn "mac_media"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "=f")
+ (plus:SF (mult:SF (match_operand:SF 1 "fp_arith_reg_operand" "%f")
+ (match_operand:SF 2 "fp_arith_reg_operand" "f"))
+ (match_operand:SF 3 "fp_arith_reg_operand" "0")))]
+ "TARGET_SHMEDIA_FPU && TARGET_FMAC"
+ "fmac.s %1, %2, %0"
+ [(set_attr "type" "fparith_media")])
+
+(define_insn "*macsf3"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "=f")
+ (plus:SF (mult:SF (match_operand:SF 1 "fp_arith_reg_operand" "%w")
+ (match_operand:SF 2 "fp_arith_reg_operand" "f"))
+ (match_operand:SF 3 "arith_reg_operand" "0")))
+ (use (match_operand:PSI 4 "fpscr_operand" "c"))]
+ "TARGET_SH2E && TARGET_FMAC"
+ "fmac fr0,%2,%0"
+ [(set_attr "type" "fp")
+ (set_attr "fp_mode" "single")])
+
+(define_expand "divsf3"
+ [(set (match_operand:SF 0 "arith_reg_operand" "")
+ (div:SF (match_operand:SF 1 "arith_reg_operand" "")
+ (match_operand:SF 2 "arith_reg_operand" "")))]
+ "TARGET_SH2E || TARGET_SHMEDIA_FPU"
+ "
+{
+ if (TARGET_SH2E)
+ {
+ expand_sf_binop (&gen_divsf3_i, operands);
+ DONE;
+ }
+}")
+
+(define_insn "*divsf3_media"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "=f")
+ (div:SF (match_operand:SF 1 "fp_arith_reg_operand" "f")
+ (match_operand:SF 2 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "fdiv.s %1, %2, %0"
+ [(set_attr "type" "fdiv_media")])
+
+(define_insn "divsf3_i"
+ [(set (match_operand:SF 0 "arith_reg_dest" "=f")
+ (div:SF (match_operand:SF 1 "arith_reg_operand" "0")
+ (match_operand:SF 2 "arith_reg_operand" "f")))
+ (use (match_operand:PSI 3 "fpscr_operand" "c"))]
+ "TARGET_SH2E"
+ "fdiv %2,%0"
+ [(set_attr "type" "fdiv")
+ (set_attr "fp_mode" "single")])
+
+(define_insn "floatdisf2"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "=f")
+ (float:SF (match_operand:DI 1 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "float.qs %1, %0"
+ [(set_attr "type" "fpconv_media")])
+
+(define_expand "floatsisf2"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "")
+ (float:SF (match_operand:SI 1 "fpul_operand" "")))]
+ "TARGET_SH2E || TARGET_SHMEDIA_FPU"
+ "
+{
+ if (TARGET_SH4 || TARGET_SH2A_SINGLE)
+ {
+ emit_sf_insn (gen_floatsisf2_i4 (operands[0], operands[1], get_fpscr_rtx ()));
+ DONE;
+ }
+}")
+
+(define_insn "*floatsisf2_media"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "=f")
+ (float:SF (match_operand:SI 1 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "float.ls %1, %0"
+ [(set_attr "type" "fpconv_media")])
+
+(define_insn "floatsisf2_i4"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "=f")
+ (float:SF (match_operand:SI 1 "fpul_operand" "y")))
+ (use (match_operand:PSI 2 "fpscr_operand" "c"))]
+ "(TARGET_SH4 || TARGET_SH2A_SINGLE)"
+ "float %1,%0"
+ [(set_attr "type" "fp")
+ (set_attr "fp_mode" "single")])
+
+(define_insn "*floatsisf2_ie"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "=f")
+ (float:SF (match_operand:SI 1 "fpul_operand" "y")))]
+ "TARGET_SH2E && ! (TARGET_SH4 || TARGET_SH2A_SINGLE)"
+ "float %1,%0"
+ [(set_attr "type" "fp")])
+
+(define_insn "fix_truncsfdi2"
+ [(set (match_operand:DI 0 "fp_arith_reg_dest" "=f")
+ (fix:DI (match_operand:SF 1 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "ftrc.sq %1, %0"
+ [(set_attr "type" "fpconv_media")])
+
+(define_expand "fix_truncsfsi2"
+ [(set (match_operand:SI 0 "fpul_operand" "=y")
+ (fix:SI (match_operand:SF 1 "fp_arith_reg_operand" "f")))]
+ "TARGET_SH2E || TARGET_SHMEDIA_FPU"
+ "
+{
+ if (TARGET_SH4 || TARGET_SH2A_SINGLE)
+ {
+ emit_sf_insn (gen_fix_truncsfsi2_i4 (operands[0], operands[1], get_fpscr_rtx ()));
+ DONE;
+ }
+}")
+
+(define_insn "*fix_truncsfsi2_media"
+ [(set (match_operand:SI 0 "fp_arith_reg_operand" "=f")
+ (fix:SI (match_operand:SF 1 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "ftrc.sl %1, %0"
+ [(set_attr "type" "fpconv_media")])
+
+(define_insn "fix_truncsfsi2_i4"
+ [(set (match_operand:SI 0 "fpul_operand" "=y")
+ (fix:SI (match_operand:SF 1 "fp_arith_reg_operand" "f")))
+ (use (match_operand:PSI 2 "fpscr_operand" "c"))]
+ "(TARGET_SH4 || TARGET_SH2A_SINGLE)"
+ "ftrc %1,%0"
+ [(set_attr "type" "ftrc_s")
+ (set_attr "fp_mode" "single")])
+
+;; ??? This pattern is used nowhere. fix_truncsfsi2 always expands to
+;; fix_truncsfsi2_i4.
+;; (define_insn "fix_truncsfsi2_i4_2"
+;; [(set (match_operand:SI 0 "arith_reg_operand" "=r")
+;; (fix:SI (match_operand:SF 1 "arith_reg_operand" "f")))
+;; (use (reg:PSI FPSCR_REG))
+;; (clobber (reg:SI FPUL_REG))]
+;; "TARGET_SH4"
+;; "#"
+;; [(set_attr "length" "4")
+;; (set_attr "fp_mode" "single")])
+
+;;(define_split
+;; [(set (match_operand:SI 0 "arith_reg_operand" "=r")
+;; (fix:SI (match_operand:SF 1 "arith_reg_operand" "f")))
+;; (use (match_operand:PSI 2 "fpscr_operand" "c"))
+;; (clobber (reg:SI FPUL_REG))]
+;; "TARGET_SH4"
+;; [(parallel [(set (reg:SI FPUL_REG) (fix:SI (match_dup 1)))
+;; (use (match_dup 2))])
+;; (set (match_dup 0) (reg:SI FPUL_REG))])
+
+(define_insn "*fixsfsi"
+ [(set (match_operand:SI 0 "fpul_operand" "=y")
+ (fix:SI (match_operand:SF 1 "fp_arith_reg_operand" "f")))]
+ "TARGET_SH2E && ! (TARGET_SH4 || TARGET_SH2A_SINGLE)"
+ "ftrc %1,%0"
+ [(set_attr "type" "fp")])
+
+(define_insn "cmpgtsf_t"
+ [(set (reg:SI T_REG)
+ (gt:SI (match_operand:SF 0 "fp_arith_reg_operand" "f")
+ (match_operand:SF 1 "fp_arith_reg_operand" "f")))]
+ "TARGET_SH2E && ! (TARGET_SH4 || TARGET_SH2A_SINGLE)"
+ "fcmp/gt %1,%0"
+ [(set_attr "type" "fp_cmp")
+ (set_attr "fp_mode" "single")])
+
+(define_insn "cmpeqsf_t"
+ [(set (reg:SI T_REG)
+ (eq:SI (match_operand:SF 0 "fp_arith_reg_operand" "f")
+ (match_operand:SF 1 "fp_arith_reg_operand" "f")))]
+ "TARGET_SH2E && ! (TARGET_SH4 || TARGET_SH2A_SINGLE)"
+ "fcmp/eq %1,%0"
+ [(set_attr "type" "fp_cmp")
+ (set_attr "fp_mode" "single")])
+
+(define_insn "ieee_ccmpeqsf_t"
+ [(set (reg:SI T_REG)
+ (ior:SI (reg:SI T_REG)
+ (eq:SI (match_operand:SF 0 "fp_arith_reg_operand" "f")
+ (match_operand:SF 1 "fp_arith_reg_operand" "f"))))]
+ "TARGET_SH2E && TARGET_IEEE && ! (TARGET_SH4 || TARGET_SH2A_SINGLE)"
+ "* return output_ieee_ccmpeq (insn, operands);"
+ [(set_attr "length" "4")])
+
+
+(define_insn "cmpgtsf_t_i4"
+ [(set (reg:SI T_REG)
+ (gt:SI (match_operand:SF 0 "fp_arith_reg_operand" "f")
+ (match_operand:SF 1 "fp_arith_reg_operand" "f")))
+ (use (match_operand:PSI 2 "fpscr_operand" "c"))]
+ "(TARGET_SH4 || TARGET_SH2A_SINGLE)"
+ "fcmp/gt %1,%0"
+ [(set_attr "type" "fp_cmp")
+ (set_attr "fp_mode" "single")])
+
+(define_insn "cmpeqsf_t_i4"
+ [(set (reg:SI T_REG)
+ (eq:SI (match_operand:SF 0 "fp_arith_reg_operand" "f")
+ (match_operand:SF 1 "fp_arith_reg_operand" "f")))
+ (use (match_operand:PSI 2 "fpscr_operand" "c"))]
+ "(TARGET_SH4 || TARGET_SH2A_SINGLE)"
+ "fcmp/eq %1,%0"
+ [(set_attr "type" "fp_cmp")
+ (set_attr "fp_mode" "single")])
+
+(define_insn "*ieee_ccmpeqsf_t_4"
+ [(set (reg:SI T_REG)
+ (ior:SI (reg:SI T_REG)
+ (eq:SI (match_operand:SF 0 "fp_arith_reg_operand" "f")
+ (match_operand:SF 1 "fp_arith_reg_operand" "f"))))
+ (use (match_operand:PSI 2 "fpscr_operand" "c"))]
+ "TARGET_IEEE && (TARGET_SH4 || TARGET_SH2A_SINGLE)"
+ "* return output_ieee_ccmpeq (insn, operands);"
+ [(set_attr "length" "4")
+ (set_attr "fp_mode" "single")])
+
+(define_insn "cmpeqsf_media"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (eq:SI (match_operand:SF 1 "fp_arith_reg_operand" "f")
+ (match_operand:SF 2 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "fcmpeq.s %1, %2, %0"
+ [(set_attr "type" "fcmp_media")])
+
+(define_insn "cmpgtsf_media"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (gt:SI (match_operand:SF 1 "fp_arith_reg_operand" "f")
+ (match_operand:SF 2 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "fcmpgt.s %1, %2, %0"
+ [(set_attr "type" "fcmp_media")])
+
+(define_insn "cmpgesf_media"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (ge:SI (match_operand:SF 1 "fp_arith_reg_operand" "f")
+ (match_operand:SF 2 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "fcmpge.s %1, %2, %0"
+ [(set_attr "type" "fcmp_media")])
+
+(define_insn "cmpunsf_media"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (unordered:SI (match_operand:SF 1 "fp_arith_reg_operand" "f")
+ (match_operand:SF 2 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "fcmpun.s %1, %2, %0"
+ [(set_attr "type" "fcmp_media")])
+
+(define_expand "cbranchsf4"
+ [(set (pc)
+ (if_then_else (match_operator 0 "sh_float_comparison_operator"
+ [(match_operand:SF 1 "arith_operand" "")
+ (match_operand:SF 2 "arith_operand" "")])
+ (match_operand 3 "" "")
+ (pc)))]
+ "TARGET_SH2E || TARGET_SHMEDIA_FPU"
+ "
+{
+ if (TARGET_SHMEDIA)
+ emit_jump_insn (gen_cbranchfp4_media (operands[0], operands[1], operands[2],
+ operands[3]));
+ else
+ sh_emit_compare_and_branch (operands, SFmode);
+ DONE;
+}")
+
+(define_expand "negsf2"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "")
+ (neg:SF (match_operand:SF 1 "fp_arith_reg_operand" "")))]
+ "TARGET_SH2E || TARGET_SHMEDIA_FPU"
+ "
+{
+ if (TARGET_SH2E)
+ {
+ expand_sf_unop (&gen_negsf2_i, operands);
+ DONE;
+ }
+}")
+
+(define_insn "*negsf2_media"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "=f")
+ (neg:SF (match_operand:SF 1 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "fneg.s %1, %0"
+ [(set_attr "type" "fmove_media")])
+
+(define_insn "negsf2_i"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "=f")
+ (neg:SF (match_operand:SF 1 "fp_arith_reg_operand" "0")))
+ (use (match_operand:PSI 2 "fpscr_operand" "c"))]
+ "TARGET_SH2E"
+ "fneg %0"
+ [(set_attr "type" "fmove")
+ (set_attr "fp_mode" "single")])
+
+(define_expand "sqrtsf2"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "")
+ (sqrt:SF (match_operand:SF 1 "fp_arith_reg_operand" "")))]
+ "TARGET_SH3E || TARGET_SHMEDIA_FPU"
+ "
+{
+ if (TARGET_SH3E)
+ {
+ expand_sf_unop (&gen_sqrtsf2_i, operands);
+ DONE;
+ }
+}")
+
+(define_insn "*sqrtsf2_media"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "=f")
+ (sqrt:SF (match_operand:SF 1 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "fsqrt.s %1, %0"
+ [(set_attr "type" "fdiv_media")])
+
+(define_insn "sqrtsf2_i"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "=f")
+ (sqrt:SF (match_operand:SF 1 "fp_arith_reg_operand" "0")))
+ (use (match_operand:PSI 2 "fpscr_operand" "c"))]
+ "TARGET_SH3E"
+ "fsqrt %0"
+ [(set_attr "type" "fdiv")
+ (set_attr "fp_mode" "single")])
+
+(define_insn "rsqrtsf2"
+ [(set (match_operand:SF 0 "register_operand" "=f")
+ (div:SF (match_operand:SF 1 "immediate_operand" "i")
+ (sqrt:SF (match_operand:SF 2 "register_operand" "0"))))
+ (use (match_operand:PSI 3 "fpscr_operand" "c"))]
+ "TARGET_SH4A_FP && flag_unsafe_math_optimizations
+ && operands[1] == CONST1_RTX (SFmode)"
+ "fsrra %0"
+ [(set_attr "type" "fsrra")
+ (set_attr "fp_mode" "single")])
+
+(define_insn "fsca"
+ [(set (match_operand:V2SF 0 "fp_arith_reg_operand" "=f")
+ (vec_concat:V2SF
+ (unspec:SF [(mult:SF
+ (float:SF (match_operand:SI 1 "fpul_operand" "y"))
+ (match_operand:SF 2 "immediate_operand" "i"))
+ ] UNSPEC_FSINA)
+ (unspec:SF [(mult:SF (float:SF (match_dup 1)) (match_dup 2))
+ ] UNSPEC_FCOSA)))
+ (use (match_operand:PSI 3 "fpscr_operand" "c"))]
+ "TARGET_SH4A_FP && flag_unsafe_math_optimizations
+ && operands[2] == sh_fsca_int2sf ()"
+ "fsca fpul,%d0"
+ [(set_attr "type" "fsca")
+ (set_attr "fp_mode" "single")])
+
+(define_expand "sinsf2"
+ [(set (match_operand:SF 0 "nonimmediate_operand" "")
+ (unspec:SF [(match_operand:SF 1 "fp_arith_reg_operand" "")]
+ UNSPEC_FSINA))]
+ "TARGET_SH4A_FP && flag_unsafe_math_optimizations"
+ "
+{
+ rtx scaled = gen_reg_rtx (SFmode);
+ rtx truncated = gen_reg_rtx (SImode);
+ rtx fsca = gen_reg_rtx (V2SFmode);
+ rtx scale_reg = force_reg (SFmode, sh_fsca_sf2int ());
+
+ emit_sf_insn (gen_mulsf3 (scaled, operands[1], scale_reg));
+ emit_sf_insn (gen_fix_truncsfsi2 (truncated, scaled));
+ emit_sf_insn (gen_fsca (fsca, truncated, sh_fsca_int2sf (),
+ get_fpscr_rtx ()));
+ emit_move_insn (operands[0], gen_rtx_SUBREG (SFmode, fsca, 0));
+ DONE;
+}")
+
+(define_expand "cossf2"
+ [(set (match_operand:SF 0 "nonimmediate_operand" "")
+ (unspec:SF [(match_operand:SF 1 "fp_arith_reg_operand" "")]
+ UNSPEC_FCOSA))]
+ "TARGET_SH4A_FP && flag_unsafe_math_optimizations"
+ "
+{
+ rtx scaled = gen_reg_rtx (SFmode);
+ rtx truncated = gen_reg_rtx (SImode);
+ rtx fsca = gen_reg_rtx (V2SFmode);
+ rtx scale_reg = force_reg (SFmode, sh_fsca_sf2int ());
+
+ emit_sf_insn (gen_mulsf3 (scaled, operands[1], scale_reg));
+ emit_sf_insn (gen_fix_truncsfsi2 (truncated, scaled));
+ emit_sf_insn (gen_fsca (fsca, truncated, sh_fsca_int2sf (),
+ get_fpscr_rtx ()));
+ emit_move_insn (operands[0], gen_rtx_SUBREG (SFmode, fsca, 4));
+ DONE;
+}")
+
+(define_expand "sindf2"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "")
+ (unspec:DF [(match_operand:DF 1 "fp_arith_reg_operand" "")]
+ UNSPEC_FSINA))]
+ "TARGET_SH4A_FP && ! TARGET_FPU_SINGLE && flag_unsafe_math_optimizations"
+ "
+{
+ rtx scaled = gen_reg_rtx (DFmode);
+ rtx truncated = gen_reg_rtx (SImode);
+ rtx fsca = gen_reg_rtx (V2SFmode);
+ rtx scale_reg = force_reg (DFmode, sh_fsca_df2int ());
+ rtx sfresult = gen_reg_rtx (SFmode);
+
+ emit_df_insn (gen_muldf3 (scaled, operands[1], scale_reg));
+ emit_df_insn (gen_fix_truncdfsi2 (truncated, scaled));
+ emit_sf_insn (gen_fsca (fsca, truncated, sh_fsca_int2sf (),
+ get_fpscr_rtx ()));
+ emit_move_insn (sfresult, gen_rtx_SUBREG (SFmode, fsca, 0));
+ emit_df_insn (gen_extendsfdf2 (operands[0], sfresult));
+ DONE;
+}")
+
+(define_expand "cosdf2"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "")
+ (unspec:DF [(match_operand:DF 1 "fp_arith_reg_operand" "")]
+ UNSPEC_FCOSA))]
+ "TARGET_SH4A_FP && ! TARGET_FPU_SINGLE && flag_unsafe_math_optimizations"
+ "
+{
+ rtx scaled = gen_reg_rtx (DFmode);
+ rtx truncated = gen_reg_rtx (SImode);
+ rtx fsca = gen_reg_rtx (V2SFmode);
+ rtx scale_reg = force_reg (DFmode, sh_fsca_df2int ());
+ rtx sfresult = gen_reg_rtx (SFmode);
+
+ emit_df_insn (gen_muldf3 (scaled, operands[1], scale_reg));
+ emit_df_insn (gen_fix_truncdfsi2 (truncated, scaled));
+ emit_sf_insn (gen_fsca (fsca, truncated, sh_fsca_int2sf (),
+ get_fpscr_rtx ()));
+ emit_move_insn (sfresult, gen_rtx_SUBREG (SFmode, fsca, 4));
+ emit_df_insn (gen_extendsfdf2 (operands[0], sfresult));
+ DONE;
+}")
+
+(define_expand "abssf2"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "")
+ (abs:SF (match_operand:SF 1 "fp_arith_reg_operand" "")))]
+ "TARGET_SH2E || TARGET_SHMEDIA_FPU"
+ "
+{
+ if (TARGET_SH2E)
+ {
+ expand_sf_unop (&gen_abssf2_i, operands);
+ DONE;
+ }
+}")
+
+(define_insn "*abssf2_media"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "=f")
+ (abs:SF (match_operand:SF 1 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "fabs.s %1, %0"
+ [(set_attr "type" "fmove_media")])
+
+(define_insn "abssf2_i"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "=f")
+ (abs:SF (match_operand:SF 1 "fp_arith_reg_operand" "0")))
+ (use (match_operand:PSI 2 "fpscr_operand" "c"))]
+ "TARGET_SH2E"
+ "fabs %0"
+ [(set_attr "type" "fmove")
+ (set_attr "fp_mode" "single")])
+
+(define_expand "adddf3"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "")
+ (plus:DF (match_operand:DF 1 "fp_arith_reg_operand" "")
+ (match_operand:DF 2 "fp_arith_reg_operand" "")))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE) || TARGET_SHMEDIA_FPU"
+ "
+{
+ if (TARGET_SH4 || TARGET_SH2A_DOUBLE)
+ {
+ expand_df_binop (&gen_adddf3_i, operands);
+ DONE;
+ }
+}")
+
+(define_insn "*adddf3_media"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "=f")
+ (plus:DF (match_operand:DF 1 "fp_arith_reg_operand" "%f")
+ (match_operand:DF 2 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "fadd.d %1, %2, %0"
+ [(set_attr "type" "dfparith_media")])
+
+(define_insn "adddf3_i"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "=f")
+ (plus:DF (match_operand:DF 1 "fp_arith_reg_operand" "%0")
+ (match_operand:DF 2 "fp_arith_reg_operand" "f")))
+ (use (match_operand:PSI 3 "fpscr_operand" "c"))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE)"
+ "fadd %2,%0"
+ [(set_attr "type" "dfp_arith")
+ (set_attr "fp_mode" "double")])
+
+(define_expand "subdf3"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "")
+ (minus:DF (match_operand:DF 1 "fp_arith_reg_operand" "")
+ (match_operand:DF 2 "fp_arith_reg_operand" "")))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE) || TARGET_SHMEDIA_FPU"
+ "
+{
+ if (TARGET_SH4 || TARGET_SH2A_DOUBLE)
+ {
+ expand_df_binop (&gen_subdf3_i, operands);
+ DONE;
+ }
+}")
+
+(define_insn "*subdf3_media"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "=f")
+ (minus:DF (match_operand:DF 1 "fp_arith_reg_operand" "f")
+ (match_operand:DF 2 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "fsub.d %1, %2, %0"
+ [(set_attr "type" "dfparith_media")])
+
+(define_insn "subdf3_i"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "=f")
+ (minus:DF (match_operand:DF 1 "fp_arith_reg_operand" "0")
+ (match_operand:DF 2 "fp_arith_reg_operand" "f")))
+ (use (match_operand:PSI 3 "fpscr_operand" "c"))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE)"
+ "fsub %2,%0"
+ [(set_attr "type" "dfp_arith")
+ (set_attr "fp_mode" "double")])
+
+(define_expand "muldf3"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "")
+ (mult:DF (match_operand:DF 1 "fp_arith_reg_operand" "")
+ (match_operand:DF 2 "fp_arith_reg_operand" "")))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE) || TARGET_SHMEDIA_FPU"
+ "
+{
+ if (TARGET_SH4 || TARGET_SH2A_DOUBLE)
+ {
+ expand_df_binop (&gen_muldf3_i, operands);
+ DONE;
+ }
+}")
+
+(define_insn "*muldf3_media"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "=f")
+ (mult:DF (match_operand:DF 1 "fp_arith_reg_operand" "%f")
+ (match_operand:DF 2 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "fmul.d %1, %2, %0"
+ [(set_attr "type" "dfmul_media")])
+
+(define_insn "muldf3_i"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "=f")
+ (mult:DF (match_operand:DF 1 "fp_arith_reg_operand" "%0")
+ (match_operand:DF 2 "fp_arith_reg_operand" "f")))
+ (use (match_operand:PSI 3 "fpscr_operand" "c"))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE)"
+ "fmul %2,%0"
+ [(set_attr "type" "dfp_mul")
+ (set_attr "fp_mode" "double")])
+
+(define_expand "divdf3"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "")
+ (div:DF (match_operand:DF 1 "fp_arith_reg_operand" "")
+ (match_operand:DF 2 "fp_arith_reg_operand" "")))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE) || TARGET_SHMEDIA_FPU"
+ "
+{
+ if (TARGET_SH4 || TARGET_SH2A_DOUBLE)
+ {
+ expand_df_binop (&gen_divdf3_i, operands);
+ DONE;
+ }
+}")
+
+(define_insn "*divdf3_media"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "=f")
+ (div:DF (match_operand:DF 1 "fp_arith_reg_operand" "f")
+ (match_operand:DF 2 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "fdiv.d %1, %2, %0"
+ [(set_attr "type" "dfdiv_media")])
+
+(define_insn "divdf3_i"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "=f")
+ (div:DF (match_operand:DF 1 "fp_arith_reg_operand" "0")
+ (match_operand:DF 2 "fp_arith_reg_operand" "f")))
+ (use (match_operand:PSI 3 "fpscr_operand" "c"))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE)"
+ "fdiv %2,%0"
+ [(set_attr "type" "dfdiv")
+ (set_attr "fp_mode" "double")])
+
+(define_insn "floatdidf2"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "=f")
+ (float:DF (match_operand:DI 1 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "float.qd %1, %0"
+ [(set_attr "type" "dfpconv_media")])
+
+(define_expand "floatsidf2"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "")
+ (float:DF (match_operand:SI 1 "fpul_operand" "")))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE) || TARGET_SHMEDIA_FPU"
+ "
+{
+ if (TARGET_SH4 || TARGET_SH2A_DOUBLE)
+ {
+ emit_df_insn (gen_floatsidf2_i (operands[0], operands[1],
+ get_fpscr_rtx ()));
+ DONE;
+ }
+}")
+
+(define_insn "*floatsidf2_media"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "=f")
+ (float:DF (match_operand:SI 1 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "float.ld %1, %0"
+ [(set_attr "type" "dfpconv_media")])
+
+(define_insn "floatsidf2_i"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "=f")
+ (float:DF (match_operand:SI 1 "fpul_operand" "y")))
+ (use (match_operand:PSI 2 "fpscr_operand" "c"))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE)"
+ "float %1,%0"
+ [(set_attr "type" "dfp_conv")
+ (set_attr "fp_mode" "double")])
+
+(define_insn "fix_truncdfdi2"
+ [(set (match_operand:DI 0 "fp_arith_reg_dest" "=f")
+ (fix:DI (match_operand:DF 1 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "ftrc.dq %1, %0"
+ [(set_attr "type" "dfpconv_media")])
+
+(define_expand "fix_truncdfsi2"
+ [(set (match_operand:SI 0 "fpul_operand" "")
+ (fix:SI (match_operand:DF 1 "fp_arith_reg_operand" "")))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE) || TARGET_SHMEDIA_FPU"
+ "
+{
+ if (TARGET_SH4 || TARGET_SH2A_DOUBLE)
+ {
+ emit_df_insn (gen_fix_truncdfsi2_i (operands[0], operands[1],
+ get_fpscr_rtx ()));
+ DONE;
+ }
+}")
+
+(define_insn "*fix_truncdfsi2_media"
+ [(set (match_operand:SI 0 "fp_arith_reg_operand" "=f")
+ (fix:SI (match_operand:DF 1 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "ftrc.dl %1, %0"
+ [(set_attr "type" "dfpconv_media")])
+
+(define_insn "fix_truncdfsi2_i"
+ [(set (match_operand:SI 0 "fpul_operand" "=y")
+ (fix:SI (match_operand:DF 1 "fp_arith_reg_operand" "f")))
+ (use (match_operand:PSI 2 "fpscr_operand" "c"))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE)"
+ "ftrc %1,%0"
+ [(set_attr "type" "dfp_conv")
+ (set_attr "dfp_comp" "no")
+ (set_attr "fp_mode" "double")])
+
+;; ??? This pattern is used nowhere. fix_truncdfsi2 always expands to
+;; fix_truncdfsi2_i.
+;; (define_insn "fix_truncdfsi2_i4"
+;; [(set (match_operand:SI 0 "arith_reg_operand" "=r")
+;; (fix:SI (match_operand:DF 1 "arith_reg_operand" "f")))
+;; (use (match_operand:PSI 2 "fpscr_operand" "c"))
+;; (clobber (reg:SI FPUL_REG))]
+;; "TARGET_SH4"
+;; "#"
+;; [(set_attr "length" "4")
+;; (set_attr "fp_mode" "double")])
+;;
+;; (define_split
+;; [(set (match_operand:SI 0 "arith_reg_operand" "=r")
+;; (fix:SI (match_operand:DF 1 "arith_reg_operand" "f")))
+;; (use (match_operand:PSI 2 "fpscr_operand" "c"))
+;; (clobber (reg:SI FPUL_REG))]
+;; "TARGET_SH4"
+;; [(parallel [(set (reg:SI FPUL_REG) (fix:SI (match_dup 1)))
+;; (use (match_dup 2))])
+;; (set (match_dup 0) (reg:SI FPUL_REG))])
+
+(define_insn "cmpgtdf_t"
+ [(set (reg:SI T_REG)
+ (gt:SI (match_operand:DF 0 "arith_reg_operand" "f")
+ (match_operand:DF 1 "arith_reg_operand" "f")))
+ (use (match_operand:PSI 2 "fpscr_operand" "c"))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE)"
+ "fcmp/gt %1,%0"
+ [(set_attr "type" "dfp_cmp")
+ (set_attr "fp_mode" "double")])
+
+(define_insn "cmpeqdf_t"
+ [(set (reg:SI T_REG)
+ (eq:SI (match_operand:DF 0 "arith_reg_operand" "f")
+ (match_operand:DF 1 "arith_reg_operand" "f")))
+ (use (match_operand:PSI 2 "fpscr_operand" "c"))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE)"
+ "fcmp/eq %1,%0"
+ [(set_attr "type" "dfp_cmp")
+ (set_attr "fp_mode" "double")])
+
+(define_insn "*ieee_ccmpeqdf_t"
+ [(set (reg:SI T_REG)
+ (ior:SI (reg:SI T_REG)
+ (eq:SI (match_operand:DF 0 "arith_reg_operand" "f")
+ (match_operand:DF 1 "arith_reg_operand" "f"))))
+ (use (match_operand:PSI 2 "fpscr_operand" "c"))]
+ "TARGET_IEEE && (TARGET_SH4 || TARGET_SH2A_DOUBLE)"
+ "* return output_ieee_ccmpeq (insn, operands);"
+ [(set_attr "length" "4")
+ (set_attr "fp_mode" "double")])
+
+(define_insn "cmpeqdf_media"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (eq:SI (match_operand:DF 1 "fp_arith_reg_operand" "f")
+ (match_operand:DF 2 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "fcmpeq.d %1,%2,%0"
+ [(set_attr "type" "fcmp_media")])
+
+(define_insn "cmpgtdf_media"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (gt:SI (match_operand:DF 1 "fp_arith_reg_operand" "f")
+ (match_operand:DF 2 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "fcmpgt.d %1,%2,%0"
+ [(set_attr "type" "fcmp_media")])
+
+(define_insn "cmpgedf_media"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (ge:SI (match_operand:DF 1 "fp_arith_reg_operand" "f")
+ (match_operand:DF 2 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "fcmpge.d %1,%2,%0"
+ [(set_attr "type" "fcmp_media")])
+
+(define_insn "cmpundf_media"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (unordered:SI (match_operand:DF 1 "fp_arith_reg_operand" "f")
+ (match_operand:DF 2 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "fcmpun.d %1,%2,%0"
+ [(set_attr "type" "fcmp_media")])
+
+(define_expand "cbranchdf4"
+ [(set (pc)
+ (if_then_else (match_operator 0 "sh_float_comparison_operator"
+ [(match_operand:DF 1 "arith_operand" "")
+ (match_operand:DF 2 "arith_operand" "")])
+ (match_operand 3 "" "")
+ (pc)))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE) || TARGET_SHMEDIA_FPU"
+ "
+{
+ if (TARGET_SHMEDIA)
+ emit_jump_insn (gen_cbranchfp4_media (operands[0], operands[1], operands[2],
+ operands[3]));
+ else
+ sh_emit_compare_and_branch (operands, DFmode);
+ DONE;
+}")
+
+
+(define_expand "negdf2"
+ [(set (match_operand:DF 0 "arith_reg_operand" "")
+ (neg:DF (match_operand:DF 1 "arith_reg_operand" "")))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE) || TARGET_SHMEDIA_FPU"
+ "
+{
+ if (TARGET_SH4 || TARGET_SH2A_DOUBLE)
+ {
+ expand_df_unop (&gen_negdf2_i, operands);
+ DONE;
+ }
+}")
+
+(define_insn "*negdf2_media"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "=f")
+ (neg:DF (match_operand:DF 1 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "fneg.d %1, %0"
+ [(set_attr "type" "fmove_media")])
+
+(define_insn "negdf2_i"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "=f")
+ (neg:DF (match_operand:DF 1 "fp_arith_reg_operand" "0")))
+ (use (match_operand:PSI 2 "fpscr_operand" "c"))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE)"
+ "fneg %0"
+ [(set_attr "type" "fmove")
+ (set_attr "fp_mode" "double")])
+
+(define_expand "sqrtdf2"
+ [(set (match_operand:DF 0 "arith_reg_operand" "")
+ (sqrt:DF (match_operand:DF 1 "arith_reg_operand" "")))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE) || TARGET_SHMEDIA_FPU"
+ "
+{
+ if (TARGET_SH4 || TARGET_SH2A_DOUBLE)
+ {
+ expand_df_unop (&gen_sqrtdf2_i, operands);
+ DONE;
+ }
+}")
+
+(define_insn "*sqrtdf2_media"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "=f")
+ (sqrt:DF (match_operand:DF 1 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "fsqrt.d %1, %0"
+ [(set_attr "type" "dfdiv_media")])
+
+(define_insn "sqrtdf2_i"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "=f")
+ (sqrt:DF (match_operand:DF 1 "fp_arith_reg_operand" "0")))
+ (use (match_operand:PSI 2 "fpscr_operand" "c"))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE)"
+ "fsqrt %0"
+ [(set_attr "type" "dfdiv")
+ (set_attr "fp_mode" "double")])
+
+(define_expand "absdf2"
+ [(set (match_operand:DF 0 "arith_reg_operand" "")
+ (abs:DF (match_operand:DF 1 "arith_reg_operand" "")))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE) || TARGET_SHMEDIA_FPU"
+ "
+{
+ if (TARGET_SH4 || TARGET_SH2A_DOUBLE)
+ {
+ expand_df_unop (&gen_absdf2_i, operands);
+ DONE;
+ }
+}")
+
+(define_insn "*absdf2_media"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "=f")
+ (abs:DF (match_operand:DF 1 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "fabs.d %1, %0"
+ [(set_attr "type" "fmove_media")])
+
+(define_insn "absdf2_i"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "=f")
+ (abs:DF (match_operand:DF 1 "fp_arith_reg_operand" "0")))
+ (use (match_operand:PSI 2 "fpscr_operand" "c"))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE)"
+ "fabs %0"
+ [(set_attr "type" "fmove")
+ (set_attr "fp_mode" "double")])
+
+(define_expand "extendsfdf2"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "")
+ (float_extend:DF (match_operand:SF 1 "fpul_operand" "")))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE) || TARGET_SHMEDIA_FPU"
+ "
+{
+ if (TARGET_SH4 || TARGET_SH2A_DOUBLE)
+ {
+ emit_df_insn (gen_extendsfdf2_i4 (operands[0], operands[1],
+ get_fpscr_rtx ()));
+ DONE;
+ }
+}")
+
+(define_insn "*extendsfdf2_media"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "=f")
+ (float_extend:DF (match_operand:SF 1 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "fcnv.sd %1, %0"
+ [(set_attr "type" "dfpconv_media")])
+
+(define_insn "extendsfdf2_i4"
+ [(set (match_operand:DF 0 "fp_arith_reg_operand" "=f")
+ (float_extend:DF (match_operand:SF 1 "fpul_operand" "y")))
+ (use (match_operand:PSI 2 "fpscr_operand" "c"))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE)"
+ "fcnvsd %1,%0"
+ [(set_attr "type" "fp")
+ (set_attr "fp_mode" "double")])
+
+(define_expand "truncdfsf2"
+ [(set (match_operand:SF 0 "fpul_operand" "")
+ (float_truncate:SF (match_operand:DF 1 "fp_arith_reg_operand" "")))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE) || TARGET_SHMEDIA_FPU"
+ "
+{
+ if (TARGET_SH4 || TARGET_SH2A_DOUBLE)
+ {
+ emit_df_insn (gen_truncdfsf2_i4 (operands[0], operands[1],
+ get_fpscr_rtx ()));
+ DONE;
+ }
+}")
+
+(define_insn "*truncdfsf2_media"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "=f")
+ (float_truncate:SF (match_operand:DF 1 "fp_arith_reg_operand" "f")))]
+ "TARGET_SHMEDIA_FPU"
+ "fcnv.ds %1, %0"
+ [(set_attr "type" "dfpconv_media")])
+
+(define_insn "truncdfsf2_i4"
+ [(set (match_operand:SF 0 "fpul_operand" "=y")
+ (float_truncate:SF (match_operand:DF 1 "fp_arith_reg_operand" "f")))
+ (use (match_operand:PSI 2 "fpscr_operand" "c"))]
+ "(TARGET_SH4 || TARGET_SH2A_DOUBLE)"
+ "fcnvds %1,%0"
+ [(set_attr "type" "fp")
+ (set_attr "fp_mode" "double")])
+
+;; Bit field extract patterns. These give better code for packed bitfields,
+;; because they allow auto-increment addresses to be generated.
+
+(define_expand "insv"
+ [(set (zero_extract:SI (match_operand:QI 0 "memory_operand" "")
+ (match_operand:SI 1 "immediate_operand" "")
+ (match_operand:SI 2 "immediate_operand" ""))
+ (match_operand:SI 3 "general_operand" ""))]
+ "TARGET_SH1 && ! TARGET_LITTLE_ENDIAN"
+ "
+{
+ rtx addr_target, orig_address, shift_reg, qi_val;
+ HOST_WIDE_INT bitsize, size, v = 0;
+ rtx x = operands[3];
+
+ if (TARGET_SH2A && TARGET_BITOPS
+ && (satisfies_constraint_Sbw (operands[0])
+ || satisfies_constraint_Sbv (operands[0]))
+ && satisfies_constraint_M (operands[1])
+ && satisfies_constraint_K03 (operands[2]))
+ {
+ if (satisfies_constraint_N (operands[3]))
+ {
+ emit_insn (gen_bclr_m2a (operands[0], operands[2]));
+ DONE;
+ }
+ else if (satisfies_constraint_M (operands[3]))
+ {
+ emit_insn (gen_bset_m2a (operands[0], operands[2]));
+ DONE;
+ }
+ else if ((REG_P (operands[3]) && REGNO (operands[3]) == T_REG)
+ && satisfies_constraint_M (operands[1]))
+ {
+ emit_insn (gen_bst_m2a (operands[0], operands[2]));
+ DONE;
+ }
+ else if (REG_P (operands[3])
+ && satisfies_constraint_M (operands[1]))
+ {
+ emit_insn (gen_bld_reg (operands[3], const0_rtx));
+ emit_insn (gen_bst_m2a (operands[0], operands[2]));
+ DONE;
+ }
+ }
+ /* ??? expmed doesn't care for non-register predicates. */
+ if (! memory_operand (operands[0], VOIDmode)
+ || ! immediate_operand (operands[1], VOIDmode)
+ || ! immediate_operand (operands[2], VOIDmode)
+ || ! general_operand (x, VOIDmode))
+ FAIL;
+ /* If this isn't a 16 / 24 / 32 bit field, or if
+ it doesn't start on a byte boundary, then fail. */
+ bitsize = INTVAL (operands[1]);
+ if (bitsize < 16 || bitsize > 32 || bitsize % 8 != 0
+ || (INTVAL (operands[2]) % 8) != 0)
+ FAIL;
+
+ size = bitsize / 8;
+ orig_address = XEXP (operands[0], 0);
+ shift_reg = gen_reg_rtx (SImode);
+ if (CONST_INT_P (x))
+ {
+ v = INTVAL (x);
+ qi_val = force_reg (QImode, GEN_INT (trunc_int_for_mode (v, QImode)));
+ }
+ else
+ {
+ emit_insn (gen_movsi (shift_reg, operands[3]));
+ qi_val = gen_rtx_SUBREG (QImode, shift_reg, 3);
+ }
+ addr_target = copy_addr_to_reg (plus_constant (orig_address, size - 1));
+
+ operands[0] = replace_equiv_address (operands[0], addr_target);
+ emit_insn (gen_movqi (operands[0], qi_val));
+
+ while (size -= 1)
+ {
+ if (CONST_INT_P (x))
+ qi_val
+ = force_reg (QImode, GEN_INT (trunc_int_for_mode (v >>= 8, QImode)));
+ else
+ {
+ emit_insn (gen_lshrsi3_k (shift_reg, shift_reg, GEN_INT (8)));
+ qi_val = gen_rtx_SUBREG (QImode, shift_reg, 3);
+ }
+ emit_insn (gen_addsi3 (addr_target, addr_target, constm1_rtx));
+ emit_insn (gen_movqi (operands[0], qi_val));
+ }
+
+ DONE;
+}")
+
+(define_insn "movua"
+ [(set (match_operand:SI 0 "register_operand" "=z")
+ (unspec:SI [(match_operand:BLK 1 "unaligned_load_operand" "Sua>")]
+ UNSPEC_MOVUA))]
+ "TARGET_SH4A_ARCH"
+ "movua.l %1,%0"
+ [(set_attr "type" "movua")])
+
+;; We shouldn't need this, but cse replaces increments with references
+;; to other regs before flow has a chance to create post_inc
+;; addressing modes, and only postreload's cse_move2add brings the
+;; increments back to a usable form.
+(define_peephole2
+ [(set (match_operand:SI 0 "register_operand" "")
+ (sign_extract:SI (mem:SI (match_operand:SI 1 "register_operand" ""))
+ (const_int 32) (const_int 0)))
+ (set (match_dup 1) (plus:SI (match_dup 1) (const_int 4)))]
+ "TARGET_SH4A_ARCH && REGNO (operands[0]) != REGNO (operands[1])"
+ [(set (match_operand:SI 0 "register_operand" "")
+ (sign_extract:SI (mem:SI (post_inc:SI
+ (match_operand:SI 1 "register_operand" "")))
+ (const_int 32) (const_int 0)))]
+ "")
+
+(define_expand "extv"
+ [(set (match_operand:SI 0 "register_operand" "")
+ (sign_extract:SI (match_operand:QI 1 "unaligned_load_operand" "")
+ (match_operand 2 "const_int_operand" "")
+ (match_operand 3 "const_int_operand" "")))]
+ "TARGET_SH4A_ARCH || TARGET_SH2A"
+{
+ if (TARGET_SH2A && TARGET_BITOPS
+ && (satisfies_constraint_Sbw (operands[1])
+ || satisfies_constraint_Sbv (operands[1]))
+ && satisfies_constraint_M (operands[2])
+ && satisfies_constraint_K03 (operands[3]))
+ {
+ emit_insn (gen_bldsign_m2a (operands[1], operands[3]));
+ if (REGNO (operands[0]) != T_REG)
+ emit_insn (gen_movsi (operands[0], gen_rtx_REG (SImode, T_REG)));
+ DONE;
+ }
+ if (TARGET_SH4A_ARCH
+ && INTVAL (operands[2]) == 32
+ && INTVAL (operands[3]) == -24 * (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN)
+ && MEM_P (operands[1]) && MEM_ALIGN (operands[1]) < 32)
+ {
+ rtx src = adjust_address (operands[1], BLKmode, 0);
+ set_mem_size (src, GEN_INT (4));
+ emit_insn (gen_movua (operands[0], src));
+ DONE;
+ }
+
+ FAIL;
+})
+
+(define_expand "extzv"
+ [(set (match_operand:SI 0 "register_operand" "")
+ (zero_extract:SI (match_operand:QI 1 "unaligned_load_operand" "")
+ (match_operand 2 "const_int_operand" "")
+ (match_operand 3 "const_int_operand" "")))]
+ "TARGET_SH4A_ARCH || TARGET_SH2A"
+{
+ if (TARGET_SH2A && TARGET_BITOPS
+ && (satisfies_constraint_Sbw (operands[1])
+ || satisfies_constraint_Sbv (operands[1]))
+ && satisfies_constraint_M (operands[2])
+ && satisfies_constraint_K03 (operands[3]))
+ {
+ emit_insn (gen_bld_m2a (operands[1], operands[3]));
+ if (REGNO (operands[0]) != T_REG)
+ emit_insn (gen_movsi (operands[0], gen_rtx_REG (SImode, T_REG)));
+ DONE;
+ }
+ if (TARGET_SH4A_ARCH
+ && INTVAL (operands[2]) == 32
+ && INTVAL (operands[3]) == -24 * (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN)
+ && MEM_P (operands[1]) && MEM_ALIGN (operands[1]) < 32)
+ {
+ rtx src = adjust_address (operands[1], BLKmode, 0);
+ set_mem_size (src, GEN_INT (4));
+ emit_insn (gen_movua (operands[0], src));
+ DONE;
+ }
+
+ FAIL;
+})
+
+;; SH2A instructions for bitwise operations.
+
+;; Clear a bit in a memory location.
+(define_insn "bclr_m2a"
+ [(set (match_operand:QI 0 "bitwise_memory_operand" "+Sbw,Sbv")
+ (and:QI
+ (not:QI (ashift:QI (const_int 1)
+ (match_operand:QI 1 "const_int_operand" "K03,K03")))
+ (match_dup 0)))]
+ "TARGET_SH2A && TARGET_BITOPS"
+ "@
+ bclr.b\\t%1,%0
+ bclr.b\\t%1,@(0,%t0)"
+[(set_attr "length" "4,4")])
+
+(define_insn "bclrmem_m2a"
+ [(set (match_operand:QI 0 "bitwise_memory_operand" "+Sbw,Sbv")
+ (and:QI (match_dup 0)
+ (match_operand:QI 1 "const_int_operand" "Psz,Psz")))]
+ "TARGET_SH2A && satisfies_constraint_Psz (operands[1]) && TARGET_BITOPS"
+ "@
+ bclr.b\\t%W1,%0
+ bclr.b\\t%W1,@(0,%t0)"
+ [(set_attr "length" "4,4")])
+
+;; Set a bit in a memory location.
+(define_insn "bset_m2a"
+ [(set (match_operand:QI 0 "bitwise_memory_operand" "+Sbw,Sbv")
+ (ior:QI
+ (ashift:QI (const_int 1)
+ (match_operand:QI 1 "const_int_operand" "K03,K03"))
+ (match_dup 0)))]
+ "TARGET_SH2A && TARGET_BITOPS"
+ "@
+ bset.b\\t%1,%0
+ bset.b\\t%1,@(0,%t0)"
+ [(set_attr "length" "4,4")])
+
+(define_insn "bsetmem_m2a"
+ [(set (match_operand:QI 0 "bitwise_memory_operand" "+Sbw,Sbv")
+ (ior:QI (match_dup 0)
+ (match_operand:QI 1 "const_int_operand" "Pso,Pso")))]
+ "TARGET_SH2A && satisfies_constraint_Pso (operands[1]) && TARGET_BITOPS"
+ "@
+ bset.b\\t%V1,%0
+ bset.b\\t%V1,@(0,%t0)"
+ [(set_attr "length" "4,4")])
+
+;;; Transfer the contents of the T bit to a specified bit of memory.
+(define_insn "bst_m2a"
+ [(set (match_operand:QI 0 "bitwise_memory_operand" "+Sbw,m")
+ (if_then_else (eq (reg:SI T_REG) (const_int 0))
+ (and:QI
+ (not:QI (ashift:QI (const_int 1)
+ (match_operand:QI 1 "const_int_operand" "K03,K03")))
+ (match_dup 0))
+ (ior:QI
+ (ashift:QI (const_int 1) (match_dup 1))
+ (match_dup 0))))]
+ "TARGET_SH2A && TARGET_BITOPS"
+ "@
+ bst.b\\t%1,%0
+ bst.b\\t%1,@(0,%t0)"
+ [(set_attr "length" "4")])
+
+;; Store a specified bit of memory in the T bit.
+(define_insn "bld_m2a"
+ [(set (reg:SI T_REG)
+ (zero_extract:SI
+ (match_operand:QI 0 "bitwise_memory_operand" "Sbw,Sbv")
+ (const_int 1)
+ (match_operand 1 "const_int_operand" "K03,K03")))]
+ "TARGET_SH2A && TARGET_BITOPS"
+ "@
+ bld.b\\t%1,%0
+ bld.b\\t%1,@(0,%t0)"
+ [(set_attr "length" "4,4")])
+
+;; Store a specified bit of memory in the T bit.
+(define_insn "bldsign_m2a"
+ [(set (reg:SI T_REG)
+ (sign_extract:SI
+ (match_operand:QI 0 "bitwise_memory_operand" "Sbw,m")
+ (const_int 1)
+ (match_operand 1 "const_int_operand" "K03,K03")))]
+ "TARGET_SH2A && TARGET_BITOPS"
+ "@
+ bld.b\\t%1,%0
+ bld.b\\t%1,@(0,%t0)"
+ [(set_attr "length" "4,4")])
+
+;; Store a specified bit of the LSB 8 bits of a register in the T bit.
+(define_insn "bld_reg"
+ [(set (reg:SI T_REG)
+ (zero_extract:SI (match_operand:SI 0 "arith_reg_operand" "r")
+ (const_int 1)
+ (match_operand 1 "const_int_operand" "K03")))]
+ "TARGET_SH2A"
+ "bld\\t%1,%0")
+
+(define_insn "*bld_regqi"
+ [(set (reg:SI T_REG)
+ (zero_extract:SI (match_operand:QI 0 "arith_reg_operand" "r")
+ (const_int 1)
+ (match_operand 1 "const_int_operand" "K03")))]
+ "TARGET_SH2A"
+ "bld\\t%1,%0")
+
+;; Take logical and of a specified bit of memory with the T bit and
+;; store its result in the T bit.
+(define_insn "band_m2a"
+ [(set (reg:SI T_REG)
+ (and:SI (reg:SI T_REG)
+ (zero_extract:SI
+ (match_operand:QI 0 "bitwise_memory_operand" "Sbw,m")
+ (const_int 1)
+ (match_operand 1 "const_int_operand" "K03,K03"))))]
+ "TARGET_SH2A && TARGET_BITOPS"
+ "@
+ band.b\\t%1,%0
+ band.b\\t%1,@(0,%t0)"
+ [(set_attr "length" "4,4")])
+
+(define_insn "bandreg_m2a"
+ [(set (match_operand:SI 0 "register_operand" "=r,r")
+ (and:SI (zero_extract:SI
+ (match_operand:QI 1 "bitwise_memory_operand" "Sbw,Sbv")
+ (const_int 1)
+ (match_operand 2 "const_int_operand" "K03,K03"))
+ (match_operand:SI 3 "register_operand" "r,r")))]
+ "TARGET_SH2A && TARGET_BITOPS"
+ "@
+ band.b\\t%2,%1\;movt\\t%0
+ band.b\\t%2,@(0,%t1)\;movt\\t%0"
+ [(set_attr "length" "6,6")])
+
+;; Take logical or of a specified bit of memory with the T bit and
+;; store its result in the T bit.
+(define_insn "bor_m2a"
+ [(set (reg:SI T_REG)
+ (ior:SI (reg:SI T_REG)
+ (zero_extract:SI
+ (match_operand:QI 0 "bitwise_memory_operand" "Sbw,m")
+ (const_int 1)
+ (match_operand 1 "const_int_operand" "K03,K03"))))]
+ "TARGET_SH2A && TARGET_BITOPS"
+ "@
+ bor.b\\t%1,%0
+ bor.b\\t%1,@(0,%t0)"
+ [(set_attr "length" "4,4")])
+
+(define_insn "borreg_m2a"
+ [(set (match_operand:SI 0 "register_operand" "=r,r")
+ (ior:SI (zero_extract:SI
+ (match_operand:QI 1 "bitwise_memory_operand" "Sbw,Sbv")
+ (const_int 1)
+ (match_operand 2 "const_int_operand" "K03,K03"))
+ (match_operand:SI 3 "register_operand" "=r,r")))]
+ "TARGET_SH2A && TARGET_BITOPS"
+ "@
+ bor.b\\t%2,%1\;movt\\t%0
+ bor.b\\t%2,@(0,%t1)\;movt\\t%0"
+ [(set_attr "length" "6,6")])
+
+;; Take exclusive or of a specified bit of memory with the T bit and
+;; store its result in the T bit.
+(define_insn "bxor_m2a"
+ [(set (reg:SI T_REG)
+ (xor:SI (reg:SI T_REG)
+ (zero_extract:SI
+ (match_operand:QI 0 "bitwise_memory_operand" "Sbw,m")
+ (const_int 1)
+ (match_operand 1 "const_int_operand" "K03,K03"))))]
+ "TARGET_SH2A && TARGET_BITOPS"
+ "@
+ bxor.b\\t%1,%0
+ bxor.b\\t%1,@(0,%t0)"
+ [(set_attr "length" "4,4")])
+
+(define_insn "bxorreg_m2a"
+ [(set (match_operand:SI 0 "register_operand" "=r,r")
+ (xor:SI (zero_extract:SI
+ (match_operand:QI 1 "bitwise_memory_operand" "Sbw,Sbv")
+ (const_int 1)
+ (match_operand 2 "const_int_operand" "K03,K03"))
+ (match_operand:SI 3 "register_operand" "=r,r")))]
+ "TARGET_SH2A && TARGET_BITOPS"
+ "@
+ bxor.b\\t%2,%1\;movt\\t%0
+ bxor.b\\t%2,@(0,%t1)\;movt\\t%0"
+ [(set_attr "length" "6,6")])
+
+
+;; -------------------------------------------------------------------------
+;; Peepholes
+;; -------------------------------------------------------------------------
+;; This matches cases where the bit in a memory location is set.
+(define_peephole2
+ [(set (match_operand:SI 0 "arith_reg_operand" "r,r")
+ (sign_extend:SI (match_operand:QI 1 "bitwise_memory_operand" "Sbw,Sbv")))
+ (set (match_dup 0)
+ (ior:SI (match_dup 0)
+ (match_operand:SI 2 "const_int_operand" "Pso,Pso")))
+ (set (match_dup 1)
+ (match_operand 3 "arith_reg_operand" "r,r"))]
+ "TARGET_SH2A && TARGET_BITOPS
+ && satisfies_constraint_Pso (operands[2])
+ && REGNO (operands[0]) == REGNO (operands[3])"
+ [(set (match_dup 1)
+ (ior:QI (match_dup 1)
+ (match_dup 2)))]
+ "")
+
+;; This matches cases where the bit in a memory location is cleared.
+(define_peephole2
+ [(set (match_operand:SI 0 "arith_reg_operand" "r,r")
+ (sign_extend:SI (match_operand:QI 1 "bitwise_memory_operand" "Sbw,Sbv")))
+ (set (match_dup 0)
+ (and:SI (match_dup 0)
+ (match_operand:SI 2 "const_int_operand" "Psz,Psz")))
+ (set (match_dup 1)
+ (match_operand 3 "arith_reg_operand" "r,r"))]
+ "TARGET_SH2A && TARGET_BITOPS
+ && satisfies_constraint_Psz (operands[2])
+ && REGNO (operands[0]) == REGNO (operands[3])"
+ [(set (match_dup 1)
+ (and:QI (match_dup 1)
+ (match_dup 2)))]
+ "")
+
+;; This matches cases where a stack pointer increment at the start of the
+;; epilogue combines with a stack slot read loading the return value.
+
+(define_peephole
+ [(set (match_operand:SI 0 "arith_reg_operand" "")
+ (mem:SI (match_operand:SI 1 "arith_reg_operand" "")))
+ (set (match_dup 1) (plus:SI (match_dup 1) (const_int 4)))]
+ "TARGET_SH1 && REGNO (operands[1]) != REGNO (operands[0])"
+ "mov.l @%1+,%0")
+
+;; See the comment on the dt combiner pattern above.
+
+(define_peephole
+ [(set (match_operand:SI 0 "arith_reg_operand" "=r")
+ (plus:SI (match_dup 0)
+ (const_int -1)))
+ (set (reg:SI T_REG)
+ (eq:SI (match_dup 0)
+ (const_int 0)))]
+ "TARGET_SH2"
+ "dt %0")
+
+;; These convert sequences such as `mov #k,r0; add r15,r0; mov.l @r0,rn'
+;; to `mov #k,r0; mov.l @(r0,r15),rn'. These sequences are generated by
+;; reload when the constant is too large for a reg+offset address.
+
+;; ??? We would get much better code if this was done in reload. This would
+;; require modifying find_reloads_address to recognize that if the constant
+;; is out-of-range for an immediate add, then we get better code by reloading
+;; the constant into a register than by reloading the sum into a register,
+;; since the former is one instruction shorter if the address does not need
+;; to be offsettable. Unfortunately this does not work, because there is
+;; only one register, r0, that can be used as an index register. This register
+;; is also the function return value register. So, if we try to force reload
+;; to use double-reg addresses, then we end up with some instructions that
+;; need to use r0 twice. The only way to fix this is to change the calling
+;; convention so that r0 is not used to return values.
+
+(define_peephole
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (plus:SI (match_dup 0) (match_operand:SI 1 "register_operand" "r")))
+ (set (mem:SI (match_dup 0))
+ (match_operand:SI 2 "general_movsrc_operand" ""))]
+ "TARGET_SH1 && REGNO (operands[0]) == 0 && reg_unused_after (operands[0], insn)"
+ "mov.l %2,@(%0,%1)")
+
+(define_peephole
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (plus:SI (match_dup 0) (match_operand:SI 1 "register_operand" "r")))
+ (set (match_operand:SI 2 "general_movdst_operand" "")
+ (mem:SI (match_dup 0)))]
+ "TARGET_SH1 && REGNO (operands[0]) == 0 && reg_unused_after (operands[0], insn)"
+ "mov.l @(%0,%1),%2")
+
+(define_peephole
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (plus:SI (match_dup 0) (match_operand:SI 1 "register_operand" "r")))
+ (set (mem:HI (match_dup 0))
+ (match_operand:HI 2 "general_movsrc_operand" ""))]
+ "TARGET_SH1 && REGNO (operands[0]) == 0 && reg_unused_after (operands[0], insn)"
+ "mov.w %2,@(%0,%1)")
+
+(define_peephole
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (plus:SI (match_dup 0) (match_operand:SI 1 "register_operand" "r")))
+ (set (match_operand:HI 2 "general_movdst_operand" "")
+ (mem:HI (match_dup 0)))]
+ "TARGET_SH1 && REGNO (operands[0]) == 0 && reg_unused_after (operands[0], insn)"
+ "mov.w @(%0,%1),%2")
+
+(define_peephole
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (plus:SI (match_dup 0) (match_operand:SI 1 "register_operand" "r")))
+ (set (mem:QI (match_dup 0))
+ (match_operand:QI 2 "general_movsrc_operand" ""))]
+ "TARGET_SH1 && REGNO (operands[0]) == 0 && reg_unused_after (operands[0], insn)"
+ "mov.b %2,@(%0,%1)")
+
+(define_peephole
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (plus:SI (match_dup 0) (match_operand:SI 1 "register_operand" "r")))
+ (set (match_operand:QI 2 "general_movdst_operand" "")
+ (mem:QI (match_dup 0)))]
+ "TARGET_SH1 && REGNO (operands[0]) == 0 && reg_unused_after (operands[0], insn)"
+ "mov.b @(%0,%1),%2")
+
+(define_peephole
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (plus:SI (match_dup 0) (match_operand:SI 1 "register_operand" "r")))
+ (set (mem:SF (match_dup 0))
+ (match_operand:SF 2 "general_movsrc_operand" ""))]
+ "TARGET_SH1 && REGNO (operands[0]) == 0
+ && ((REG_P (operands[2]) && REGNO (operands[2]) < 16)
+ || (GET_CODE (operands[2]) == SUBREG
+ && REGNO (SUBREG_REG (operands[2])) < 16))
+ && reg_unused_after (operands[0], insn)"
+ "mov.l %2,@(%0,%1)")
+
+(define_peephole
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (plus:SI (match_dup 0) (match_operand:SI 1 "register_operand" "r")))
+ (set (match_operand:SF 2 "general_movdst_operand" "")
+
+ (mem:SF (match_dup 0)))]
+ "TARGET_SH1 && REGNO (operands[0]) == 0
+ && ((REG_P (operands[2]) && REGNO (operands[2]) < 16)
+ || (GET_CODE (operands[2]) == SUBREG
+ && REGNO (SUBREG_REG (operands[2])) < 16))
+ && reg_unused_after (operands[0], insn)"
+ "mov.l @(%0,%1),%2")
+
+(define_peephole
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (plus:SI (match_dup 0) (match_operand:SI 1 "register_operand" "r")))
+ (set (mem:SF (match_dup 0))
+ (match_operand:SF 2 "general_movsrc_operand" ""))]
+ "TARGET_SH2E && REGNO (operands[0]) == 0
+ && ((REG_P (operands[2])
+ && FP_OR_XD_REGISTER_P (REGNO (operands[2])))
+ || (GET_CODE (operands[2]) == SUBREG
+ && FP_OR_XD_REGISTER_P (REGNO (SUBREG_REG (operands[2])))))
+ && reg_unused_after (operands[0], insn)"
+ "fmov{.s|} %2,@(%0,%1)")
+
+(define_peephole
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (plus:SI (match_dup 0) (match_operand:SI 1 "register_operand" "r")))
+ (set (match_operand:SF 2 "general_movdst_operand" "")
+
+ (mem:SF (match_dup 0)))]
+ "TARGET_SH2E && REGNO (operands[0]) == 0
+ && ((REG_P (operands[2])
+ && FP_OR_XD_REGISTER_P (REGNO (operands[2])))
+ || (GET_CODE (operands[2]) == SUBREG
+ && FP_OR_XD_REGISTER_P (REGNO (SUBREG_REG (operands[2])))))
+ && reg_unused_after (operands[0], insn)"
+ "fmov{.s|} @(%0,%1),%2")
+
+;; Switch to a new stack with its address in sp_switch (a SYMBOL_REF). */
+(define_insn "sp_switch_1"
+ [(const_int 1) (match_operand:SI 0 "symbol_ref_operand" "s")]
+ "TARGET_SH1"
+ "*
+{
+ output_asm_insn (\"mov.l r0,@-r15\;mov.l %0,r0\", operands);
+ output_asm_insn (\"mov.l @r0,r0\;mov.l r15,@-r0\", operands);
+ return \"mov r0,r15\";
+}"
+ [(set_attr "length" "10")])
+
+;; Switch back to the original stack for interrupt functions with the
+;; sp_switch attribute. */
+(define_insn "sp_switch_2"
+ [(const_int 2)]
+ "TARGET_SH1"
+ "mov.l @r15+,r15\;mov.l @r15+,r0"
+ [(set_attr "length" "4")])
+
+;; Integer vector moves
+
+(define_expand "movv8qi"
+ [(set (match_operand:V8QI 0 "general_movdst_operand" "")
+ (match_operand:V8QI 1 "general_movsrc_operand" ""))]
+ "TARGET_SHMEDIA"
+ "{ if (prepare_move_operands (operands, V8QImode)) DONE; }")
+
+(define_insn "movv8qi_i"
+ [(set (match_operand:V8QI 0 "general_movdst_operand" "=r,r,r,rl,m")
+ (match_operand:V8QI 1 "general_movsrc_operand" "r,I16CssZ,nW,m,rlZ"))]
+ "TARGET_SHMEDIA
+ && (register_operand (operands[0], V8QImode)
+ || sh_register_operand (operands[1], V8QImode))"
+ "@
+ add %1, r63, %0
+ movi %1, %0
+ #
+ ld%M1.q %m1, %0
+ st%M0.q %m0, %N1"
+ [(set_attr "type" "arith_media,arith_media,*,load_media,store_media")
+ (set_attr "length" "4,4,16,4,4")])
+
+(define_split
+ [(set (match_operand:V8QI 0 "arith_reg_dest" "")
+ (subreg:V8QI (const_int 0) 0))]
+ "TARGET_SHMEDIA"
+ [(set (match_dup 0)
+ (const_vector:V8QI [(const_int 0) (const_int 0) (const_int 0)
+ (const_int 0) (const_int 0) (const_int 0)
+ (const_int 0) (const_int 0)]))])
+
+(define_split
+ [(set (match_operand 0 "arith_reg_dest" "")
+ (match_operand 1 "sh_rep_vec" ""))]
+ "TARGET_SHMEDIA && reload_completed
+ && GET_MODE (operands[0]) == GET_MODE (operands[1])
+ && sh_vector_mode_supported_p (GET_MODE (operands[0]))
+ && GET_MODE_SIZE (GET_MODE (operands[0])) == 8
+ && (XVECEXP (operands[1], 0, 0) != const0_rtx
+ || XVECEXP (operands[1], 0, 1) != const0_rtx)
+ && (XVECEXP (operands[1], 0, 0) != constm1_rtx
+ || XVECEXP (operands[1], 0, 1) != constm1_rtx)"
+ [(set (match_dup 0) (match_dup 1))
+ (match_dup 2)]
+ "
+{
+ int unit_size = GET_MODE_UNIT_SIZE (GET_MODE (operands[1]));
+ rtx elt1 = XVECEXP (operands[1], 0, 1);
+
+ if (unit_size > 2)
+ operands[2] = gen_mshflo_l (operands[0], operands[0], operands[0]);
+ else
+ {
+ if (unit_size < 2)
+ operands[0] = gen_rtx_REG (V4HImode, true_regnum (operands[0]));
+ operands[2] = gen_mperm_w0 (operands[0], operands[0]);
+ }
+ operands[0] = gen_rtx_REG (DImode, true_regnum (operands[0]));
+ operands[1] = XVECEXP (operands[1], 0, 0);
+ if (unit_size < 2)
+ {
+ if (CONST_INT_P (operands[1]) && CONST_INT_P (elt1))
+ operands[1]
+ = GEN_INT (TARGET_LITTLE_ENDIAN
+ ? (INTVAL (operands[1]) & 0xff) + (INTVAL (elt1) << 8)
+ : (INTVAL (operands[1]) << 8) + (INTVAL (elt1) & 0xff));
+ else
+ {
+ operands[0] = gen_rtx_REG (V2QImode, true_regnum (operands[0]));
+ operands[1]
+ = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, operands[1], elt1));
+ }
+ }
+}")
+
+(define_split
+ [(set (match_operand 0 "arith_reg_dest" "")
+ (match_operand 1 "sh_const_vec" ""))]
+ "TARGET_SHMEDIA && reload_completed
+ && GET_MODE (operands[0]) == GET_MODE (operands[1])
+ && sh_vector_mode_supported_p (GET_MODE (operands[0]))"
+ [(set (match_dup 0) (match_dup 1))]
+ "
+{
+ rtx v = operands[1];
+ enum machine_mode new_mode
+ = mode_for_size (GET_MODE_BITSIZE (GET_MODE (v)), MODE_INT, 0);
+
+ operands[0] = gen_rtx_REG (new_mode, true_regnum (operands[0]));
+ operands[1]
+ = simplify_subreg (new_mode, operands[1], GET_MODE (operands[1]), 0);
+}")
+
+(define_expand "movv2hi"
+ [(set (match_operand:V2HI 0 "general_movdst_operand" "")
+ (match_operand:V2HI 1 "general_movsrc_operand" ""))]
+ "TARGET_SHMEDIA"
+ "{ if (prepare_move_operands (operands, V2HImode)) DONE; }")
+
+(define_insn "movv2hi_i"
+ [(set (match_operand:V2HI 0 "general_movdst_operand" "=r,r,r,rl,m")
+ (match_operand:V2HI 1 "general_movsrc_operand" "r,I16CssZ,nW,m,rlZ"))]
+ "TARGET_SHMEDIA
+ && (register_operand (operands[0], V2HImode)
+ || sh_register_operand (operands[1], V2HImode))"
+ "@
+ add.l %1, r63, %0
+ movi %1, %0
+ #
+ ld%M1.l %m1, %0
+ st%M0.l %m0, %N1"
+ [(set_attr "type" "arith_media,arith_media,*,load_media,store_media")
+ (set_attr "length" "4,4,16,4,4")
+ (set (attr "highpart")
+ (cond [(ne (symbol_ref "sh_contains_memref_p (insn)") (const_int 0))
+ (const_string "user")]
+ (const_string "ignore")))])
+
+(define_expand "movv4hi"
+ [(set (match_operand:V4HI 0 "general_movdst_operand" "")
+ (match_operand:V4HI 1 "general_movsrc_operand" ""))]
+ "TARGET_SHMEDIA"
+ "{ if (prepare_move_operands (operands, V4HImode)) DONE; }")
+
+(define_insn "movv4hi_i"
+ [(set (match_operand:V4HI 0 "general_movdst_operand" "=r,r,r,rl,m")
+ (match_operand:V4HI 1 "general_movsrc_operand" "r,I16CssZ,nW,m,rlZ"))]
+ "TARGET_SHMEDIA
+ && (register_operand (operands[0], V4HImode)
+ || sh_register_operand (operands[1], V4HImode))"
+ "@
+ add %1, r63, %0
+ movi %1, %0
+ #
+ ld%M1.q %m1, %0
+ st%M0.q %m0, %N1"
+ [(set_attr "type" "arith_media,arith_media,*,load_media,store_media")
+ (set_attr "length" "4,4,16,4,4")
+ (set_attr "highpart" "depend")])
+
+(define_expand "movv2si"
+ [(set (match_operand:V2SI 0 "general_movdst_operand" "")
+ (match_operand:V2SI 1 "general_movsrc_operand" ""))]
+ "TARGET_SHMEDIA"
+ "{ if (prepare_move_operands (operands, V2SImode)) DONE; }")
+
+(define_insn "movv2si_i"
+ [(set (match_operand:V2SI 0 "general_movdst_operand" "=r,r,r,rl,m")
+ (match_operand:V2SI 1 "general_movsrc_operand" "r,I16CssZ,nW,m,rlZ"))]
+ "TARGET_SHMEDIA
+ && (register_operand (operands[0], V2SImode)
+ || sh_register_operand (operands[1], V2SImode))"
+ "@
+ add %1, r63, %0
+ #
+ #
+ ld%M1.q %m1, %0
+ st%M0.q %m0, %N1"
+ [(set_attr "type" "arith_media,arith_media,*,load_media,store_media")
+ (set_attr "length" "4,4,16,4,4")
+ (set_attr "highpart" "depend")])
+
+;; Multimedia Intrinsics
+
+(define_insn "absv2si2"
+ [(set (match_operand:V2SI 0 "arith_reg_dest" "=r")
+ (abs:V2SI (match_operand:V2SI 1 "arith_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "mabs.l %1, %0"
+ [(set_attr "type" "mcmp_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "absv4hi2"
+ [(set (match_operand:V4HI 0 "arith_reg_dest" "=r")
+ (abs:V4HI (match_operand:V4HI 1 "arith_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "mabs.w %1, %0"
+ [(set_attr "type" "mcmp_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "addv2si3"
+ [(set (match_operand:V2SI 0 "arith_reg_dest" "=r")
+ (plus:V2SI (match_operand:V2SI 1 "arith_reg_operand" "%r")
+ (match_operand:V2SI 2 "arith_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "madd.l %1, %2, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "addv4hi3"
+ [(set (match_operand:V4HI 0 "arith_reg_dest" "=r")
+ (plus:V4HI (match_operand:V4HI 1 "arith_reg_operand" "%r")
+ (match_operand:V4HI 2 "arith_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "madd.w %1, %2, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn_and_split "addv2hi3"
+ [(set (match_operand:V2HI 0 "arith_reg_dest" "=r")
+ (plus:V2HI (match_operand:V2HI 1 "extend_reg_operand" "%r")
+ (match_operand:V2HI 2 "extend_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "#"
+ "TARGET_SHMEDIA"
+ [(const_int 0)]
+ "
+{
+ rtx src0 = simplify_gen_subreg (V4HImode, operands[1], V2HImode, 0);
+ rtx src1 = simplify_gen_subreg (V4HImode, operands[2], V2HImode, 0);
+ rtx v4hi_dst = simplify_gen_subreg (V4HImode, operands[0], V2HImode, 0);
+ rtx di_dst = simplify_gen_subreg (DImode, operands[0], V2HImode, 0);
+ rtx si_dst = simplify_gen_subreg (SImode, operands[0], V2HImode, 0);
+
+ emit_insn (gen_addv4hi3 (v4hi_dst, src0, src1));
+ emit_insn (gen_truncdisi2 (si_dst, di_dst));
+ DONE;
+}"
+ [(set_attr "highpart" "must_split")])
+
+(define_insn "ssaddv2si3"
+ [(set (match_operand:V2SI 0 "arith_reg_dest" "=r")
+ (ss_plus:V2SI (match_operand:V2SI 1 "arith_reg_operand" "%r")
+ (match_operand:V2SI 2 "arith_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "madds.l %1, %2, %0"
+ [(set_attr "type" "mcmp_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "usaddv8qi3"
+ [(set (match_operand:V8QI 0 "arith_reg_dest" "=r")
+ (us_plus:V8QI (match_operand:V8QI 1 "arith_reg_operand" "%r")
+ (match_operand:V8QI 2 "arith_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "madds.ub %1, %2, %0"
+ [(set_attr "type" "mcmp_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "ssaddv4hi3"
+ [(set (match_operand:V4HI 0 "arith_reg_dest" "=r")
+ (ss_plus:V4HI (match_operand:V4HI 1 "arith_reg_operand" "%r")
+ (match_operand:V4HI 2 "arith_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "madds.w %1, %2, %0"
+ [(set_attr "type" "mcmp_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "negcmpeqv8qi"
+ [(set (match_operand:V8QI 0 "arith_reg_dest" "=r")
+ (neg:V8QI (eq:V8QI (match_operand:V8QI 1 "arith_reg_or_0_operand" "%rZ")
+ (match_operand:V8QI 2 "arith_reg_or_0_operand" "rZ"))))]
+ "TARGET_SHMEDIA"
+ "mcmpeq.b %N1, %N2, %0"
+ [(set_attr "type" "mcmp_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "negcmpeqv2si"
+ [(set (match_operand:V2SI 0 "arith_reg_dest" "=r")
+ (neg:V2SI (eq:V2SI (match_operand:V2SI 1 "arith_reg_or_0_operand" "%rZ")
+ (match_operand:V2SI 2 "arith_reg_or_0_operand" "rZ"))))]
+ "TARGET_SHMEDIA"
+ "mcmpeq.l %N1, %N2, %0"
+ [(set_attr "type" "mcmp_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "negcmpeqv4hi"
+ [(set (match_operand:V4HI 0 "arith_reg_dest" "=r")
+ (neg:V4HI (eq:V4HI (match_operand:V4HI 1 "arith_reg_or_0_operand" "%rZ")
+ (match_operand:V4HI 2 "arith_reg_or_0_operand" "rZ"))))]
+ "TARGET_SHMEDIA"
+ "mcmpeq.w %N1, %N2, %0"
+ [(set_attr "type" "mcmp_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "negcmpgtuv8qi"
+ [(set (match_operand:V8QI 0 "arith_reg_dest" "=r")
+ (neg:V8QI (gtu:V8QI (match_operand:V8QI 1 "arith_reg_or_0_operand" "%rZ")
+ (match_operand:V8QI 2 "arith_reg_or_0_operand" "rZ"))))]
+ "TARGET_SHMEDIA"
+ "mcmpgt.ub %N1, %N2, %0"
+ [(set_attr "type" "mcmp_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "negcmpgtv2si"
+ [(set (match_operand:V2SI 0 "arith_reg_dest" "=r")
+ (neg:V2SI (gt:V2SI (match_operand:V2SI 1 "arith_reg_or_0_operand" "%rZ")
+ (match_operand:V2SI 2 "arith_reg_or_0_operand" "rZ"))))]
+ "TARGET_SHMEDIA"
+ "mcmpgt.l %N1, %N2, %0"
+ [(set_attr "type" "mcmp_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "negcmpgtv4hi"
+ [(set (match_operand:V4HI 0 "arith_reg_dest" "=r")
+ (neg:V4HI (gt:V4HI (match_operand:V4HI 1 "arith_reg_or_0_operand" "%rZ")
+ (match_operand:V4HI 2 "arith_reg_or_0_operand" "rZ"))))]
+ "TARGET_SHMEDIA"
+ "mcmpgt.w %N1, %N2, %0"
+ [(set_attr "type" "mcmp_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "mcmv"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (ior:DI (and:DI (match_operand:DI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:DI 2 "arith_reg_operand" "r"))
+ (and:DI (match_operand:DI 3 "arith_reg_operand" "0")
+ (not:DI (match_dup 2)))))]
+ "TARGET_SHMEDIA"
+ "mcmv %N1, %2, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "mcnvs_lw"
+ [(set (match_operand:V4HI 0 "arith_reg_dest" "=r")
+ (vec_concat:V4HI
+ (ss_truncate:V2HI (match_operand:V2SI 1 "arith_reg_or_0_operand" "rZ"))
+ (ss_truncate:V2HI (match_operand:V2SI 2 "arith_reg_or_0_operand" "rZ"))))]
+ "TARGET_SHMEDIA"
+ "mcnvs.lw %N1, %N2, %0"
+ [(set_attr "type" "mcmp_media")])
+
+(define_insn "mcnvs_wb"
+ [(set (match_operand:V8QI 0 "arith_reg_dest" "=r")
+ (vec_concat:V8QI
+ (ss_truncate:V4QI (match_operand:V4HI 1 "arith_reg_or_0_operand" "rZ"))
+ (ss_truncate:V4QI (match_operand:V4HI 2 "arith_reg_or_0_operand" "rZ"))))]
+ "TARGET_SHMEDIA"
+ "mcnvs.wb %N1, %N2, %0"
+ [(set_attr "type" "mcmp_media")])
+
+(define_insn "mcnvs_wub"
+ [(set (match_operand:V8QI 0 "arith_reg_dest" "=r")
+ (vec_concat:V8QI
+ (us_truncate:V4QI (match_operand:V4HI 1 "arith_reg_or_0_operand" "rZ"))
+ (us_truncate:V4QI (match_operand:V4HI 2 "arith_reg_or_0_operand" "rZ"))))]
+ "TARGET_SHMEDIA"
+ "mcnvs.wub %N1, %N2, %0"
+ [(set_attr "type" "mcmp_media")])
+
+(define_insn "mextr_rl"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (ior:DI (lshiftrt:DI (match_operand:DI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:HI 3 "mextr_bit_offset" "i"))
+ (ashift:DI (match_operand:DI 2 "arith_reg_or_0_operand" "rZ")
+ (match_operand:HI 4 "mextr_bit_offset" "i"))))]
+ "TARGET_SHMEDIA && INTVAL (operands[3]) + INTVAL (operands[4]) == 64"
+ "*
+{
+ static char templ[21];
+
+ sprintf (templ, \"mextr%d\\t%%N1, %%N2, %%0\",
+ (int) INTVAL (operands[3]) >> 3);
+ return templ;
+}"
+ [(set_attr "type" "arith_media")])
+
+(define_insn "*mextr_lr"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (ior:DI (ashift:DI (match_operand:DI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:HI 3 "mextr_bit_offset" "i"))
+ (lshiftrt:DI (match_operand:DI 2 "arith_reg_or_0_operand" "rZ")
+ (match_operand:HI 4 "mextr_bit_offset" "i"))))]
+ "TARGET_SHMEDIA && INTVAL (operands[3]) + INTVAL (operands[4]) == 64"
+ "*
+{
+ static char templ[21];
+
+ sprintf (templ, \"mextr%d\\t%%N2, %%N1, %%0\",
+ (int) INTVAL (operands[4]) >> 3);
+ return templ;
+}"
+ [(set_attr "type" "arith_media")])
+
+; mextrN can be modelled with vec_select / vec_concat, but the selection
+; vector then varies depending on endianness.
+(define_expand "mextr1"
+ [(match_operand:DI 0 "arith_reg_dest" "")
+ (match_operand:DI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:DI 2 "arith_reg_or_0_operand" "rZ")]
+ "TARGET_SHMEDIA"
+ "
+{
+ emit_insn (gen_mextr_rl (operands[0], operands[1], operands[2],
+ GEN_INT (1 * 8), GEN_INT (7 * 8)));
+ DONE;
+}")
+
+(define_expand "mextr2"
+ [(match_operand:DI 0 "arith_reg_dest" "")
+ (match_operand:DI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:DI 2 "arith_reg_or_0_operand" "rZ")]
+ "TARGET_SHMEDIA"
+ "
+{
+ emit_insn (gen_mextr_rl (operands[0], operands[1], operands[2],
+ GEN_INT (2 * 8), GEN_INT (6 * 8)));
+ DONE;
+}")
+
+(define_expand "mextr3"
+ [(match_operand:DI 0 "arith_reg_dest" "")
+ (match_operand:DI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:DI 2 "arith_reg_or_0_operand" "rZ")]
+ "TARGET_SHMEDIA"
+ "
+{
+ emit_insn (gen_mextr_rl (operands[0], operands[1], operands[2],
+ GEN_INT (3 * 8), GEN_INT (5 * 8)));
+ DONE;
+}")
+
+(define_expand "mextr4"
+ [(match_operand:DI 0 "arith_reg_dest" "")
+ (match_operand:DI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:DI 2 "arith_reg_or_0_operand" "rZ")]
+ "TARGET_SHMEDIA"
+ "
+{
+ emit_insn (gen_mextr_rl (operands[0], operands[1], operands[2],
+ GEN_INT (4 * 8), GEN_INT (4 * 8)));
+ DONE;
+}")
+
+(define_expand "mextr5"
+ [(match_operand:DI 0 "arith_reg_dest" "")
+ (match_operand:DI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:DI 2 "arith_reg_or_0_operand" "rZ")]
+ "TARGET_SHMEDIA"
+ "
+{
+ emit_insn (gen_mextr_rl (operands[0], operands[1], operands[2],
+ GEN_INT (5 * 8), GEN_INT (3 * 8)));
+ DONE;
+}")
+
+(define_expand "mextr6"
+ [(match_operand:DI 0 "arith_reg_dest" "")
+ (match_operand:DI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:DI 2 "arith_reg_or_0_operand" "rZ")]
+ "TARGET_SHMEDIA"
+ "
+{
+ emit_insn (gen_mextr_rl (operands[0], operands[1], operands[2],
+ GEN_INT (6 * 8), GEN_INT (2 * 8)));
+ DONE;
+}")
+
+(define_expand "mextr7"
+ [(match_operand:DI 0 "arith_reg_dest" "")
+ (match_operand:DI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:DI 2 "arith_reg_or_0_operand" "rZ")]
+ "TARGET_SHMEDIA"
+ "
+{
+ emit_insn (gen_mextr_rl (operands[0], operands[1], operands[2],
+ GEN_INT (7 * 8), GEN_INT (1 * 8)));
+ DONE;
+}")
+
+(define_expand "mmacfx_wl"
+ [(match_operand:V2SI 0 "arith_reg_dest" "")
+ (match_operand:V2HI 1 "extend_reg_operand" "")
+ (match_operand:V2HI 2 "extend_reg_operand" "")
+ (match_operand:V2SI 3 "arith_reg_operand" "")]
+ "TARGET_SHMEDIA"
+ "
+{
+ emit_insn (gen_mmacfx_wl_i (operands[0], operands[3],
+ operands[1], operands[2]));
+ DONE;
+}")
+
+;; This could be highpart ignore if it only had inputs 2 or 3, but input 1
+;; is depend
+(define_insn "mmacfx_wl_i"
+ [(set (match_operand:V2SI 0 "arith_reg_dest" "=r")
+ (ss_plus:V2SI
+ (match_operand:V2SI 1 "arith_reg_operand" "0")
+ (ss_truncate:V2SI
+ (ashift:V2DI
+ (sign_extend:V2DI
+ (mult:V2SI
+ (sign_extend:V2SI (match_operand:V2HI 2 "extend_reg_operand" "r"))
+ (sign_extend:V2SI (match_operand:V2HI 3 "extend_reg_operand" "r"))))
+ (const_int 1)))))]
+ "TARGET_SHMEDIA"
+ "mmacfx.wl %2, %3, %0"
+ [(set_attr "type" "mac_media")
+ (set_attr "highpart" "depend")])
+
+(define_expand "mmacnfx_wl"
+ [(match_operand:V2SI 0 "arith_reg_dest" "")
+ (match_operand:V2HI 1 "extend_reg_operand" "")
+ (match_operand:V2HI 2 "extend_reg_operand" "")
+ (match_operand:V2SI 3 "arith_reg_operand" "")]
+ "TARGET_SHMEDIA"
+ "
+{
+ emit_insn (gen_mmacnfx_wl_i (operands[0], operands[3],
+ operands[1], operands[2]));
+ DONE;
+}")
+
+(define_insn "mmacnfx_wl_i"
+ [(set (match_operand:V2SI 0 "arith_reg_dest" "=r")
+ (ss_minus:V2SI
+ (match_operand:V2SI 1 "arith_reg_operand" "0")
+ (ss_truncate:V2SI
+ (ashift:V2DI
+ (sign_extend:V2DI
+ (mult:V2SI
+ (sign_extend:V2SI (match_operand:V2HI 2 "extend_reg_operand" "r"))
+ (sign_extend:V2SI (match_operand:V2HI 3 "extend_reg_operand" "r"))))
+ (const_int 1)))))]
+ "TARGET_SHMEDIA"
+ "mmacnfx.wl %2, %3, %0"
+ [(set_attr "type" "mac_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "mulv2si3"
+ [(set (match_operand:V2SI 0 "arith_reg_dest" "=r")
+ (mult:V2SI (match_operand:V2SI 1 "arith_reg_operand" "r")
+ (match_operand:V2SI 2 "arith_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "mmul.l %1, %2, %0"
+ [(set_attr "type" "d2mpy_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "mulv4hi3"
+ [(set (match_operand:V4HI 0 "arith_reg_dest" "=r")
+ (mult:V4HI (match_operand:V4HI 1 "arith_reg_operand" "r")
+ (match_operand:V4HI 2 "arith_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "mmul.w %1, %2, %0"
+ [(set_attr "type" "dmpy_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "mmulfx_l"
+ [(set (match_operand:V2SI 0 "arith_reg_dest" "=r")
+ (ss_truncate:V2SI
+ (ashiftrt:V2DI
+ (mult:V2DI
+ (sign_extend:V2DI (match_operand:V2SI 1 "arith_reg_operand" "r"))
+ (sign_extend:V2DI (match_operand:V2SI 2 "arith_reg_operand" "r")))
+ (const_int 31))))]
+ "TARGET_SHMEDIA"
+ "mmulfx.l %1, %2, %0"
+ [(set_attr "type" "d2mpy_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "mmulfx_w"
+ [(set (match_operand:V4HI 0 "arith_reg_dest" "=r")
+ (ss_truncate:V4HI
+ (ashiftrt:V4SI
+ (mult:V4SI
+ (sign_extend:V4SI (match_operand:V4HI 1 "arith_reg_operand" "r"))
+ (sign_extend:V4SI (match_operand:V4HI 2 "arith_reg_operand" "r")))
+ (const_int 15))))]
+ "TARGET_SHMEDIA"
+ "mmulfx.w %1, %2, %0"
+ [(set_attr "type" "dmpy_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "mmulfxrp_w"
+ [(set (match_operand:V4HI 0 "arith_reg_dest" "=r")
+ (ss_truncate:V4HI
+ (ashiftrt:V4SI
+ (plus:V4SI
+ (mult:V4SI
+ (sign_extend:V4SI (match_operand:V4HI 1 "arith_reg_operand" "r"))
+ (sign_extend:V4SI (match_operand:V4HI 2 "arith_reg_operand" "r")))
+ (const_int 16384))
+ (const_int 15))))]
+ "TARGET_SHMEDIA"
+ "mmulfxrp.w %1, %2, %0"
+ [(set_attr "type" "dmpy_media")
+ (set_attr "highpart" "depend")])
+
+
+(define_expand "mmulhi_wl"
+ [(match_operand:V2SI 0 "arith_reg_dest" "")
+ (match_operand:V4HI 1 "arith_reg_operand" "")
+ (match_operand:V4HI 2 "arith_reg_operand" "")]
+ "TARGET_SHMEDIA"
+ "
+{
+ emit_insn ((TARGET_LITTLE_ENDIAN ? gen_mmul23_wl : gen_mmul01_wl)
+ (operands[0], operands[1], operands[2]));
+ DONE;
+}")
+
+(define_expand "mmullo_wl"
+ [(match_operand:V2SI 0 "arith_reg_dest" "")
+ (match_operand:V4HI 1 "arith_reg_operand" "")
+ (match_operand:V4HI 2 "arith_reg_operand" "")]
+ "TARGET_SHMEDIA"
+ "
+{
+ emit_insn ((TARGET_LITTLE_ENDIAN ? gen_mmul01_wl : gen_mmul23_wl)
+ (operands[0], operands[1], operands[2]));
+ DONE;
+}")
+
+(define_insn "mmul23_wl"
+ [(set (match_operand:V2SI 0 "arith_reg_dest" "=r")
+ (vec_select:V2SI
+ (mult:V4SI
+ (sign_extend:V4SI (match_operand:V4HI 1 "arith_reg_operand" "r"))
+ (sign_extend:V4SI (match_operand:V4HI 2 "arith_reg_operand" "r")))
+ (parallel [(const_int 2) (const_int 3)])))]
+ "TARGET_SHMEDIA"
+ "* return (TARGET_LITTLE_ENDIAN
+ ? \"mmulhi.wl %1, %2, %0\"
+ : \"mmullo.wl %1, %2, %0\");"
+ [(set_attr "type" "dmpy_media")
+ (set (attr "highpart")
+ (cond [(eq_attr "endian" "big") (const_string "ignore")]
+ (const_string "user")))])
+
+(define_insn "mmul01_wl"
+ [(set (match_operand:V2SI 0 "arith_reg_dest" "=r")
+ (vec_select:V2SI
+ (mult:V4SI
+ (sign_extend:V4SI (match_operand:V4HI 1 "arith_reg_operand" "r"))
+ (sign_extend:V4SI (match_operand:V4HI 2 "arith_reg_operand" "r")))
+ (parallel [(const_int 0) (const_int 1)])))]
+ "TARGET_SHMEDIA"
+ "* return (TARGET_LITTLE_ENDIAN
+ ? \"mmullo.wl %1, %2, %0\"
+ : \"mmulhi.wl %1, %2, %0\");"
+ [(set_attr "type" "dmpy_media")
+ (set (attr "highpart")
+ (cond [(eq_attr "endian" "little") (const_string "ignore")]
+ (const_string "user")))])
+
+
+(define_expand "mmulsum_wq"
+ [(match_operand:DI 0 "arith_reg_dest" "")
+ (match_operand:V4HI 1 "arith_reg_operand" "")
+ (match_operand:V4HI 2 "arith_reg_operand" "")
+ (match_operand:DI 3 "arith_reg_operand" "")]
+ "TARGET_SHMEDIA"
+ "
+{
+ emit_insn (gen_mmulsum_wq_i (operands[0], operands[3],
+ operands[1], operands[2]));
+ DONE;
+}")
+
+(define_insn "mmulsum_wq_i"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (plus:DI (match_operand:DI 1 "arith_reg_operand" "0")
+ (plus:DI
+ (plus:DI
+ (vec_select:DI
+ (mult:V4DI
+ (sign_extend:V4DI (match_operand:V4HI 2 "arith_reg_operand" "r"))
+ (sign_extend:V4DI (match_operand:V4HI 3 "arith_reg_operand" "r")))
+ (parallel [(const_int 0)]))
+ (vec_select:DI (mult:V4DI (sign_extend:V4DI (match_dup 2))
+ (sign_extend:V4DI (match_dup 3)))
+ (parallel [(const_int 1)])))
+ (plus:DI
+ (vec_select:DI (mult:V4DI (sign_extend:V4DI (match_dup 2))
+ (sign_extend:V4DI (match_dup 3)))
+ (parallel [(const_int 2)]))
+ (vec_select:DI (mult:V4DI (sign_extend:V4DI (match_dup 2))
+ (sign_extend:V4DI (match_dup 3)))
+ (parallel [(const_int 3)]))))))]
+ "TARGET_SHMEDIA"
+ "mmulsum.wq %2, %3, %0"
+ [(set_attr "type" "mac_media")])
+
+(define_expand "mperm_w"
+ [(match_operand:V4HI 0 "arith_reg_dest" "=r")
+ (match_operand:V4HI 1 "arith_reg_operand" "r")
+ (match_operand:QI 2 "extend_reg_or_0_operand" "rZ")]
+ "TARGET_SHMEDIA"
+ "
+{
+ emit_insn ((TARGET_LITTLE_ENDIAN ? gen_mperm_w_little : gen_mperm_w_big)
+ (operands[0], operands[1], operands[2]));
+ DONE;
+}")
+
+; This use of vec_select isn't exactly correct according to rtl.texi
+; (because not constant), but it seems a straightforward extension.
+(define_insn "mperm_w_little"
+ [(set (match_operand:V4HI 0 "arith_reg_dest" "=r")
+ (vec_select:V4HI
+ (match_operand:V4HI 1 "arith_reg_operand" "r")
+ (parallel
+ [(zero_extract:QI (match_operand:QI 2 "extend_reg_or_0_operand" "rZ")
+ (const_int 2) (const_int 0))
+ (zero_extract:QI (match_dup 2) (const_int 2) (const_int 2))
+ (zero_extract:QI (match_dup 2) (const_int 2) (const_int 4))
+ (zero_extract:QI (match_dup 2) (const_int 2) (const_int 6))])))]
+ "TARGET_SHMEDIA && TARGET_LITTLE_ENDIAN"
+ "mperm.w %1, %N2, %0"
+ [(set_attr "type" "arith_media")])
+
+(define_insn "mperm_w_big"
+ [(set (match_operand:V4HI 0 "arith_reg_dest" "=r")
+ (vec_select:V4HI
+ (match_operand:V4HI 1 "arith_reg_operand" "r")
+ (parallel
+ [(zero_extract:QI (not:QI (match_operand:QI 2
+ "extend_reg_or_0_operand" "rZ"))
+ (const_int 2) (const_int 0))
+ (zero_extract:QI (not:QI (match_dup 2)) (const_int 2) (const_int 2))
+ (zero_extract:QI (not:QI (match_dup 2)) (const_int 2) (const_int 4))
+ (zero_extract:QI (not:QI (match_dup 2))
+ (const_int 2) (const_int 6))])))]
+ "TARGET_SHMEDIA && ! TARGET_LITTLE_ENDIAN"
+ "mperm.w %1, %N2, %0"
+ [(set_attr "type" "arith_media")])
+
+(define_insn "mperm_w0"
+ [(set (match_operand:V4HI 0 "arith_reg_dest" "=r")
+ (vec_duplicate:V4HI (truncate:HI (match_operand 1
+ "trunc_hi_operand" "r"))))]
+ "TARGET_SHMEDIA"
+ "mperm.w %1, r63, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "ignore")])
+
+(define_expand "msad_ubq"
+ [(match_operand:DI 0 "arith_reg_dest" "")
+ (match_operand:V8QI 1 "arith_reg_or_0_operand" "")
+ (match_operand:V8QI 2 "arith_reg_or_0_operand" "")
+ (match_operand:DI 3 "arith_reg_operand" "")]
+ "TARGET_SHMEDIA"
+ "
+{
+ emit_insn (gen_msad_ubq_i (operands[0], operands[3],
+ operands[1], operands[2]));
+ DONE;
+}")
+
+(define_insn "msad_ubq_i"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (plus:DI
+ (plus:DI
+ (plus:DI
+ (plus:DI
+ (match_operand:DI 1 "arith_reg_operand" "0")
+ (abs:DI (vec_select:DI
+ (minus:V8DI
+ (zero_extend:V8DI
+ (match_operand:V8QI 2 "arith_reg_or_0_operand" "rZ"))
+ (zero_extend:V8DI
+ (match_operand:V8QI 3 "arith_reg_or_0_operand" "rZ")))
+ (parallel [(const_int 0)]))))
+ (abs:DI (vec_select:DI (minus:V8DI (zero_extend:V8DI (match_dup 2))
+ (zero_extend:V8DI (match_dup 3)))
+ (parallel [(const_int 1)]))))
+ (plus:DI
+ (abs:DI (vec_select:DI (minus:V8DI (zero_extend:V8DI (match_dup 2))
+ (zero_extend:V8DI (match_dup 3)))
+ (parallel [(const_int 2)])))
+ (abs:DI (vec_select:DI (minus:V8DI (zero_extend:V8DI (match_dup 2))
+ (zero_extend:V8DI (match_dup 3)))
+ (parallel [(const_int 3)])))))
+ (plus:DI
+ (plus:DI
+ (abs:DI (vec_select:DI (minus:V8DI (zero_extend:V8DI (match_dup 2))
+ (zero_extend:V8DI (match_dup 3)))
+ (parallel [(const_int 4)])))
+ (abs:DI (vec_select:DI (minus:V8DI (zero_extend:V8DI (match_dup 2))
+ (zero_extend:V8DI (match_dup 3)))
+ (parallel [(const_int 5)]))))
+ (plus:DI
+ (abs:DI (vec_select:DI (minus:V8DI (zero_extend:V8DI (match_dup 2))
+ (zero_extend:V8DI (match_dup 3)))
+ (parallel [(const_int 6)])))
+ (abs:DI (vec_select:DI (minus:V8DI (zero_extend:V8DI (match_dup 2))
+ (zero_extend:V8DI (match_dup 3)))
+ (parallel [(const_int 7)])))))))]
+ "TARGET_SHMEDIA"
+ "msad.ubq %N2, %N3, %0"
+ [(set_attr "type" "mac_media")])
+
+(define_insn "mshalds_l"
+ [(set (match_operand:V2SI 0 "arith_reg_dest" "=r")
+ (ss_truncate:V2SI
+ (ashift:V2DI
+ (sign_extend:V2DI (match_operand:V2SI 1 "arith_reg_operand" "r"))
+ (and:DI (match_operand:DI 2 "arith_reg_operand" "r")
+ (const_int 31)))))]
+ "TARGET_SHMEDIA"
+ "mshalds.l %1, %2, %0"
+ [(set_attr "type" "mcmp_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "mshalds_w"
+ [(set (match_operand:V4HI 0 "arith_reg_dest" "=r")
+ (ss_truncate:V4HI
+ (ashift:V4SI
+ (sign_extend:V4SI (match_operand:V4HI 1 "arith_reg_operand" "r"))
+ (and:DI (match_operand:DI 2 "arith_reg_operand" "r")
+ (const_int 15)))))]
+ "TARGET_SHMEDIA"
+ "mshalds.w %1, %2, %0"
+ [(set_attr "type" "mcmp_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "ashrv2si3"
+ [(set (match_operand:V2SI 0 "arith_reg_dest" "=r")
+ (ashiftrt:V2SI (match_operand:V2SI 1 "arith_reg_operand" "r")
+ (match_operand:DI 2 "arith_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "mshard.l %1, %2, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "ashrv4hi3"
+ [(set (match_operand:V4HI 0 "arith_reg_dest" "=r")
+ (ashiftrt:V4HI (match_operand:V4HI 1 "arith_reg_operand" "r")
+ (match_operand:DI 2 "arith_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "mshard.w %1, %2, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "mshards_q"
+ [(set (match_operand:HI 0 "arith_reg_dest" "=r")
+ (ss_truncate:HI
+ (ashiftrt:DI (match_operand:DI 1 "arith_reg_operand" "r")
+ (match_operand:DI 2 "arith_reg_or_0_operand" "rZ"))))]
+ "TARGET_SHMEDIA"
+ "mshards.q %1, %N2, %0"
+ [(set_attr "type" "mcmp_media")])
+
+(define_expand "mshfhi_b"
+ [(match_operand:V8QI 0 "arith_reg_dest" "")
+ (match_operand:V8QI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:V8QI 2 "arith_reg_or_0_operand" "rZ")]
+ "TARGET_SHMEDIA"
+ "
+{
+ emit_insn ((TARGET_LITTLE_ENDIAN ? gen_mshf4_b : gen_mshf0_b)
+ (operands[0], operands[1], operands[2]));
+ DONE;
+}")
+
+(define_expand "mshflo_b"
+ [(match_operand:V8QI 0 "arith_reg_dest" "")
+ (match_operand:V8QI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:V8QI 2 "arith_reg_or_0_operand" "rZ")]
+ "TARGET_SHMEDIA"
+ "
+{
+ emit_insn ((TARGET_LITTLE_ENDIAN ? gen_mshf0_b : gen_mshf4_b)
+ (operands[0], operands[1], operands[2]));
+ DONE;
+}")
+
+(define_insn "mshf4_b"
+ [(set
+ (match_operand:V8QI 0 "arith_reg_dest" "=r")
+ (vec_select:V8QI
+ (vec_concat:V16QI (match_operand:V8QI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:V8QI 2 "arith_reg_or_0_operand" "rZ"))
+ (parallel [(const_int 4) (const_int 12) (const_int 5) (const_int 13)
+ (const_int 6) (const_int 14) (const_int 7) (const_int 15)])))]
+ "TARGET_SHMEDIA"
+ "* return (TARGET_LITTLE_ENDIAN
+ ? \"mshfhi.b %N1, %N2, %0\"
+ : \"mshflo.b %N1, %N2, %0\");"
+ [(set_attr "type" "arith_media")
+ (set (attr "highpart")
+ (cond [(eq_attr "endian" "big") (const_string "ignore")]
+ (const_string "user")))])
+
+(define_insn "mshf0_b"
+ [(set
+ (match_operand:V8QI 0 "arith_reg_dest" "=r")
+ (vec_select:V8QI
+ (vec_concat:V16QI (match_operand:V8QI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:V8QI 2 "arith_reg_or_0_operand" "rZ"))
+ (parallel [(const_int 0) (const_int 8) (const_int 1) (const_int 9)
+ (const_int 2) (const_int 10) (const_int 3) (const_int 11)])))]
+ "TARGET_SHMEDIA"
+ "* return (TARGET_LITTLE_ENDIAN
+ ? \"mshflo.b %N1, %N2, %0\"
+ : \"mshfhi.b %N1, %N2, %0\");"
+ [(set_attr "type" "arith_media")
+ (set (attr "highpart")
+ (cond [(eq_attr "endian" "little") (const_string "ignore")]
+ (const_string "user")))])
+
+(define_expand "mshfhi_l"
+ [(match_operand:V2SI 0 "arith_reg_dest" "")
+ (match_operand:V2SI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:V2SI 2 "arith_reg_or_0_operand" "rZ")]
+ "TARGET_SHMEDIA"
+ "
+{
+ emit_insn ((TARGET_LITTLE_ENDIAN ? gen_mshf4_l : gen_mshf0_l)
+ (operands[0], operands[1], operands[2]));
+ DONE;
+}")
+
+(define_expand "mshflo_l"
+ [(match_operand:V2SI 0 "arith_reg_dest" "")
+ (match_operand:V2SI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:V2SI 2 "arith_reg_or_0_operand" "rZ")]
+ "TARGET_SHMEDIA"
+ "
+{
+ emit_insn ((TARGET_LITTLE_ENDIAN ? gen_mshf0_l : gen_mshf4_l)
+ (operands[0], operands[1], operands[2]));
+ DONE;
+}")
+
+(define_insn "mshf4_l"
+ [(set (match_operand:V2SI 0 "arith_reg_dest" "=r")
+ (vec_select:V2SI
+ (vec_concat:V4SI (match_operand:V2SI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:V2SI 2 "arith_reg_or_0_operand" "rZ"))
+ (parallel [(const_int 1) (const_int 3)])))]
+ "TARGET_SHMEDIA"
+ "* return (TARGET_LITTLE_ENDIAN
+ ? \"mshfhi.l %N1, %N2, %0\"
+ : \"mshflo.l %N1, %N2, %0\");"
+ [(set_attr "type" "arith_media")
+ (set (attr "highpart")
+ (cond [(eq_attr "endian" "big") (const_string "ignore")]
+ (const_string "user")))])
+
+(define_insn "mshf0_l"
+ [(set (match_operand:V2SI 0 "arith_reg_dest" "=r")
+ (vec_select:V2SI
+ (vec_concat:V4SI (match_operand:V2SI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:V2SI 2 "arith_reg_or_0_operand" "rZ"))
+ (parallel [(const_int 0) (const_int 2)])))]
+ "TARGET_SHMEDIA"
+ "* return (TARGET_LITTLE_ENDIAN
+ ? \"mshflo.l %N1, %N2, %0\"
+ : \"mshfhi.l %N1, %N2, %0\");"
+ [(set_attr "type" "arith_media")
+ (set (attr "highpart")
+ (cond [(eq_attr "endian" "little") (const_string "ignore")]
+ (const_string "user")))])
+
+(define_expand "mshfhi_w"
+ [(match_operand:V4HI 0 "arith_reg_dest" "")
+ (match_operand:V4HI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:V4HI 2 "arith_reg_or_0_operand" "rZ")]
+ "TARGET_SHMEDIA"
+ "
+{
+ emit_insn ((TARGET_LITTLE_ENDIAN ? gen_mshf4_w : gen_mshf0_w)
+ (operands[0], operands[1], operands[2]));
+ DONE;
+}")
+
+(define_expand "mshflo_w"
+ [(match_operand:V4HI 0 "arith_reg_dest" "")
+ (match_operand:V4HI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:V4HI 2 "arith_reg_or_0_operand" "rZ")]
+ "TARGET_SHMEDIA"
+ "
+{
+ emit_insn ((TARGET_LITTLE_ENDIAN ? gen_mshf0_w : gen_mshf4_w)
+ (operands[0], operands[1], operands[2]));
+ DONE;
+}")
+
+(define_insn "mshf4_w"
+ [(set (match_operand:V4HI 0 "arith_reg_dest" "=r")
+ (vec_select:V4HI
+ (vec_concat:V8HI (match_operand:V4HI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:V4HI 2 "arith_reg_or_0_operand" "rZ"))
+ (parallel [(const_int 2) (const_int 6) (const_int 3) (const_int 7)])))]
+ "TARGET_SHMEDIA"
+ "* return (TARGET_LITTLE_ENDIAN
+ ? \"mshfhi.w %N1, %N2, %0\"
+ : \"mshflo.w %N1, %N2, %0\");"
+ [(set_attr "type" "arith_media")
+ (set (attr "highpart")
+ (cond [(eq_attr "endian" "big") (const_string "ignore")]
+ (const_string "user")))])
+
+(define_insn "mshf0_w"
+ [(set (match_operand:V4HI 0 "arith_reg_dest" "=r")
+ (vec_select:V4HI
+ (vec_concat:V8HI (match_operand:V4HI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:V4HI 2 "arith_reg_or_0_operand" "rZ"))
+ (parallel [(const_int 0) (const_int 4) (const_int 1) (const_int 5)])))]
+ "TARGET_SHMEDIA"
+ "* return (TARGET_LITTLE_ENDIAN
+ ? \"mshflo.w %N1, %N2, %0\"
+ : \"mshfhi.w %N1, %N2, %0\");"
+ [(set_attr "type" "arith_media")
+ (set (attr "highpart")
+ (cond [(eq_attr "endian" "little") (const_string "ignore")]
+ (const_string "user")))])
+
+(define_insn "mshflo_w_x"
+ [(set (match_operand:V4HI 0 "arith_reg_dest" "=r")
+ (vec_select:V4HI
+ (vec_concat:V4HI (match_operand:V2HI 1 "extend_reg_or_0_operand" "rZ")
+ (match_operand:V2HI 2 "extend_reg_or_0_operand" "rZ"))
+ (parallel [(const_int 2) (const_int 0) (const_int 3) (const_int 1)])))]
+ "TARGET_SHMEDIA"
+ "mshflo.w %N1, %N2, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "ignore")])
+
+/* These are useful to expand ANDs and as combiner patterns. */
+(define_insn_and_split "mshfhi_l_di"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r,f")
+ (ior:DI (lshiftrt:DI (match_operand:DI 1 "arith_reg_or_0_operand" "rZ,f")
+ (const_int 32))
+ (and:DI (match_operand:DI 2 "arith_reg_or_0_operand" "rZ,?f")
+ (const_int -4294967296))))]
+ "TARGET_SHMEDIA"
+ "@
+ mshfhi.l %N1, %N2, %0
+ #"
+ "TARGET_SHMEDIA && reload_completed
+ && ! GENERAL_REGISTER_P (true_regnum (operands[0]))"
+ [(set (match_dup 3) (match_dup 4))
+ (set (match_dup 5) (match_dup 6))]
+ "
+{
+ operands[3] = gen_lowpart (SImode, operands[0]);
+ operands[4] = gen_highpart (SImode, operands[1]);
+ operands[5] = gen_highpart (SImode, operands[0]);
+ operands[6] = gen_highpart (SImode, operands[2]);
+}"
+ [(set_attr "type" "arith_media")])
+
+(define_insn "*mshfhi_l_di_rev"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (ior:DI (and:DI (match_operand:DI 1 "arith_reg_or_0_operand" "rZ")
+ (const_int -4294967296))
+ (lshiftrt:DI (match_operand:DI 2 "arith_reg_or_0_operand" "rZ")
+ (const_int 32))))]
+ "TARGET_SHMEDIA"
+ "mshfhi.l %N2, %N1, %0"
+ [(set_attr "type" "arith_media")])
+
+(define_split
+ [(set (match_operand:DI 0 "arith_reg_dest" "")
+ (ior:DI (zero_extend:DI (match_operand:SI 1
+ "extend_reg_or_0_operand" ""))
+ (and:DI (match_operand:DI 2 "arith_reg_or_0_operand" "")
+ (const_int -4294967296))))
+ (clobber (match_operand:DI 3 "arith_reg_dest" ""))]
+ "TARGET_SHMEDIA"
+ [(const_int 0)]
+ "
+{
+ emit_insn (gen_ashldi3_media (operands[3],
+ simplify_gen_subreg (DImode, operands[1],
+ SImode, 0),
+ GEN_INT (32)));
+ emit_insn (gen_mshfhi_l_di (operands[0], operands[3], operands[2]));
+ DONE;
+}")
+
+(define_insn "mshflo_l_di"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (ior:DI (and:DI (match_operand:DI 1 "arith_reg_or_0_operand" "rZ")
+ (const_int 4294967295))
+ (ashift:DI (match_operand:DI 2 "arith_reg_or_0_operand" "rZ")
+ (const_int 32))))]
+
+ "TARGET_SHMEDIA"
+ "mshflo.l %N1, %N2, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "ignore")])
+
+(define_insn "*mshflo_l_di_rev"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (ior:DI (ashift:DI (match_operand:DI 1 "arith_reg_or_0_operand" "rZ")
+ (const_int 32))
+ (and:DI (match_operand:DI 2 "arith_reg_or_0_operand" "rZ")
+ (const_int 4294967295))))]
+
+ "TARGET_SHMEDIA"
+ "mshflo.l %N2, %N1, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "ignore")])
+
+;; Combiner pattern for trampoline initialization.
+(define_insn_and_split "*double_shori"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (ior:DI (ashift:DI (match_operand:DI 1 "arith_reg_operand" "0")
+ (const_int 32))
+ (match_operand:DI 2 "const_int_operand" "n")))]
+ "TARGET_SHMEDIA
+ && ! (INTVAL (operands[2]) & ~(unsigned HOST_WIDE_INT) 0xffffffffUL)"
+ "#"
+ "rtx_equal_p (operands[0], operands[1])"
+ [(const_int 0)]
+ "
+{
+ HOST_WIDE_INT v = INTVAL (operands[2]);
+
+ emit_insn (gen_shori_media (operands[0], operands[0], GEN_INT (v >> 16)));
+ emit_insn (gen_shori_media (operands[0], operands[0], GEN_INT (v & 65535)));
+ DONE;
+}"
+ [(set_attr "highpart" "ignore")])
+
+
+(define_insn "*mshflo_l_di_x"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (ior:DI (zero_extend:DI (match_operand:SI 1 "extend_reg_or_0_operand"
+ "rZ"))
+ (ashift:DI (match_operand:DI 2 "arith_reg_or_0_operand" "rZ")
+ (const_int 32))))]
+
+ "TARGET_SHMEDIA"
+ "mshflo.l %N1, %N2, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "ignore")])
+
+(define_insn_and_split "concat_v2sf"
+ [(set (match_operand:V2SF 0 "register_operand" "=r,f,f?")
+;; (vec_concat:V2SF (match_operand:SF 1 "register_operand" "rZ,0,f")
+ (vec_concat:V2SF (match_operand:SF 1 "register_operand" "rZ,f,f")
+ (match_operand:SF 2 "register_operand" "rZ,f,f")))]
+
+ "TARGET_SHMEDIA"
+ "@
+ mshflo.l %N1, %N2, %0
+ #
+ #"
+ "TARGET_SHMEDIA && reload_completed
+ && ! GENERAL_REGISTER_P (true_regnum (operands[0]))"
+ [(set (match_dup 3) (match_dup 1))
+ (set (match_dup 4) (match_dup 2))]
+ "
+{
+ operands[3] = simplify_gen_subreg (SFmode, operands[0], V2SFmode, 0);
+ operands[4] = simplify_gen_subreg (SFmode, operands[0], V2SFmode, 4);
+}"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "ignore")])
+
+(define_insn "*mshflo_l_di_x_rev"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (ior:DI (ashift:DI (match_operand:DI 1 "arith_reg_or_0_operand" "rZ")
+ (const_int 32))
+ (zero_extend:DI (match_operand:SI 2 "extend_reg_or_0_operand" "rZ"))))]
+
+ "TARGET_SHMEDIA"
+ "mshflo.l %N2, %N1, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "ignore")])
+
+(define_insn "ashlv2si3"
+ [(set (match_operand:V2SI 0 "arith_reg_dest" "=r")
+ (ashift:V2SI (match_operand:V2SI 1 "arith_reg_operand" "r")
+ (match_operand:DI 2 "shift_count_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "mshlld.l %1, %2, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "depend")])
+
+(define_split
+ [(set (match_operand 0 "any_register_operand" "")
+ (match_operator 3 "shift_operator"
+ [(match_operand 1 "any_register_operand" "")
+ (match_operand 2 "shift_count_reg_operand" "")]))]
+ "TARGET_SHMEDIA && ! register_operand (operands[2], VOIDmode)"
+ [(set (match_dup 0) (match_dup 3))]
+ "
+{
+ rtx count = operands[2];
+ enum machine_mode outer_mode = GET_MODE (operands[2]), inner_mode;
+
+ while (GET_CODE (count) == ZERO_EXTEND || GET_CODE (count) == SIGN_EXTEND
+ || (GET_CODE (count) == SUBREG && SUBREG_BYTE (count) == 0)
+ || GET_CODE (count) == TRUNCATE)
+ count = XEXP (count, 0);
+ inner_mode = GET_MODE (count);
+ count = simplify_gen_subreg (outer_mode, count, inner_mode,
+ subreg_lowpart_offset (outer_mode, inner_mode));
+ operands[3] = gen_rtx_fmt_ee (GET_CODE (operands[3]), GET_MODE (operands[3]),
+ operands[1], count);
+}")
+
+(define_insn "ashlv4hi3"
+ [(set (match_operand:V4HI 0 "arith_reg_dest" "=r")
+ (ashift:V4HI (match_operand:V4HI 1 "arith_reg_operand" "r")
+ (match_operand:DI 2 "shift_count_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "mshlld.w %1, %2, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "lshrv2si3"
+ [(set (match_operand:V2SI 0 "arith_reg_dest" "=r")
+ (lshiftrt:V2SI (match_operand:V2SI 1 "arith_reg_operand" "r")
+ (match_operand:DI 2 "shift_count_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "mshlrd.l %1, %2, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "lshrv4hi3"
+ [(set (match_operand:V4HI 0 "arith_reg_dest" "=r")
+ (lshiftrt:V4HI (match_operand:V4HI 1 "arith_reg_operand" "r")
+ (match_operand:DI 2 "shift_count_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "mshlrd.w %1, %2, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "subv2si3"
+ [(set (match_operand:V2SI 0 "arith_reg_dest" "=r")
+ (minus:V2SI (match_operand:V2SI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:V2SI 2 "arith_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "msub.l %N1, %2, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "subv4hi3"
+ [(set (match_operand:V4HI 0 "arith_reg_dest" "=r")
+ (minus:V4HI (match_operand:V4HI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:V4HI 2 "arith_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "msub.w %N1, %2, %0"
+ [(set_attr "type" "arith_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn_and_split "subv2hi3"
+ [(set (match_operand:V2HI 0 "arith_reg_dest" "=r")
+ (minus:V2HI (match_operand:V2HI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:V2HI 2 "arith_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "#"
+ "TARGET_SHMEDIA"
+ [(const_int 0)]
+ "
+{
+ rtx src0 = simplify_gen_subreg (V4HImode, operands[1], V2HImode, 0);
+ rtx src1 = simplify_gen_subreg (V4HImode, operands[2], V2HImode, 0);
+ rtx v4hi_dst = simplify_gen_subreg (V4HImode, operands[0], V2HImode, 0);
+ rtx di_dst = simplify_gen_subreg (DImode, operands[0], V2HImode, 0);
+ rtx si_dst = simplify_gen_subreg (SImode, operands[0], V2HImode, 0);
+
+ emit_insn (gen_subv4hi3 (v4hi_dst, src0, src1));
+ emit_insn (gen_truncdisi2 (si_dst, di_dst));
+ DONE;
+}"
+ [(set_attr "highpart" "must_split")])
+
+(define_insn "sssubv2si3"
+ [(set (match_operand:V2SI 0 "arith_reg_dest" "=r")
+ (ss_minus:V2SI (match_operand:V2SI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:V2SI 2 "arith_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "msubs.l %N1, %2, %0"
+ [(set_attr "type" "mcmp_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "ussubv8qi3"
+ [(set (match_operand:V8QI 0 "arith_reg_dest" "=r")
+ (us_minus:V8QI (match_operand:V8QI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:V8QI 2 "arith_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "msubs.ub %N1, %2, %0"
+ [(set_attr "type" "mcmp_media")
+ (set_attr "highpart" "depend")])
+
+(define_insn "sssubv4hi3"
+ [(set (match_operand:V4HI 0 "arith_reg_dest" "=r")
+ (ss_minus:V4HI (match_operand:V4HI 1 "arith_reg_or_0_operand" "rZ")
+ (match_operand:V4HI 2 "arith_reg_operand" "r")))]
+ "TARGET_SHMEDIA"
+ "msubs.w %N1, %2, %0"
+ [(set_attr "type" "mcmp_media")
+ (set_attr "highpart" "depend")])
+
+;; Floating Point Intrinsics
+
+(define_insn "fcosa_s"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "=f")
+ (unspec:SF [(match_operand:SI 1 "fp_arith_reg_operand" "f")]
+ UNSPEC_FCOSA))]
+ "TARGET_SHMEDIA"
+ "fcosa.s %1, %0"
+ [(set_attr "type" "atrans_media")])
+
+(define_insn "fsina_s"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "=f")
+ (unspec:SF [(match_operand:SI 1 "fp_arith_reg_operand" "f")]
+ UNSPEC_FSINA))]
+ "TARGET_SHMEDIA"
+ "fsina.s %1, %0"
+ [(set_attr "type" "atrans_media")])
+
+(define_insn "fipr"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "=f")
+ (plus:SF (plus:SF (vec_select:SF (mult:V4SF (match_operand:V4SF 1
+ "fp_arith_reg_operand" "f")
+ (match_operand:V4SF 2
+ "fp_arith_reg_operand" "f"))
+ (parallel [(const_int 0)]))
+ (vec_select:SF (mult:V4SF (match_dup 1) (match_dup 2))
+ (parallel [(const_int 1)])))
+ (plus:SF (vec_select:SF (mult:V4SF (match_dup 1) (match_dup 2))
+ (parallel [(const_int 2)]))
+ (vec_select:SF (mult:V4SF (match_dup 1) (match_dup 2))
+ (parallel [(const_int 3)])))))]
+ "TARGET_SHMEDIA"
+ "fipr.s %1, %2, %0"
+ [(set_attr "type" "fparith_media")])
+
+(define_insn "fsrra_s"
+ [(set (match_operand:SF 0 "fp_arith_reg_operand" "=f")
+ (unspec:SF [(match_operand:SF 1 "fp_arith_reg_operand" "f")]
+ UNSPEC_FSRRA))]
+ "TARGET_SHMEDIA"
+ "fsrra.s %1, %0"
+ [(set_attr "type" "atrans_media")])
+
+(define_insn "ftrv"
+ [(set (match_operand:V4SF 0 "fp_arith_reg_operand" "=f")
+ (plus:V4SF
+ (plus:V4SF
+ (mult:V4SF
+ (vec_select:V4SF (match_operand:V16SF 1 "fp_arith_reg_operand" "f")
+ (parallel [(const_int 0) (const_int 5)
+ (const_int 10) (const_int 15)]))
+ (match_operand:V4SF 2 "fp_arith_reg_operand" "f"))
+ (mult:V4SF
+ (vec_select:V4SF (match_dup 1)
+ (parallel [(const_int 4) (const_int 9)
+ (const_int 14) (const_int 3)]))
+ (vec_select:V4SF (match_dup 2)
+ (parallel [(const_int 1) (const_int 2)
+ (const_int 3) (const_int 0)]))))
+ (plus:V4SF
+ (mult:V4SF
+ (vec_select:V4SF (match_dup 1)
+ (parallel [(const_int 8) (const_int 13)
+ (const_int 2) (const_int 7)]))
+ (vec_select:V4SF (match_dup 2)
+ (parallel [(const_int 2) (const_int 3)
+ (const_int 0) (const_int 1)])))
+ (mult:V4SF
+ (vec_select:V4SF (match_dup 1)
+ (parallel [(const_int 12) (const_int 1)
+ (const_int 6) (const_int 11)]))
+ (vec_select:V4SF (match_dup 2)
+ (parallel [(const_int 3) (const_int 0)
+ (const_int 1) (const_int 2)]))))))]
+ "TARGET_SHMEDIA"
+ "ftrv.s %1, %2, %0"
+ [(set_attr "type" "fparith_media")])
+
+(define_insn "ldhi_l"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (zero_extract:SI
+ (mem:SI (plus:SI (ior:SI (match_operand:QI 1 "ua_address_operand" "p")
+ (const_int 3))
+ (const_int -3)))
+ (plus:SI (and:SI (match_dup 1) (const_int 3)) (const_int 1))
+ (const_int 0)))]
+ "TARGET_SHMEDIA32"
+ "ldhi.l %U1, %0"
+ [(set_attr "type" "load_media")])
+
+(define_insn "ldhi_q"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (zero_extract:DI
+ (mem:DI (plus:SI (ior:SI (match_operand:QI 1 "ua_address_operand" "p")
+ (const_int 7))
+ (const_int -7)))
+ (plus:SI (and:SI (match_dup 1) (const_int 7)) (const_int 1))
+ (const_int 0)))]
+ "TARGET_SHMEDIA32"
+ "ldhi.q %U1, %0"
+ [(set_attr "type" "load_media")])
+
+(define_insn_and_split "*ldhi_q_comb0"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (zero_extract:DI
+ (mem:DI (plus:SI (ior:SI (plus:SI (match_operand:SI 1
+ "register_operand" "r")
+ (match_operand:SI 2
+ "ua_offset" "I06"))
+ (const_int 7))
+ (const_int -7)))
+ (plus:SI (and:SI (match_dup 1) (const_int 7))
+ (const_int 1))
+ (const_int 0)))]
+ "TARGET_SHMEDIA32 && (INTVAL (operands[2]) & 7) == 0"
+ "#"
+ ""
+ [(pc)]
+ "emit_insn (gen_ldhi_q (operands[0],
+ gen_rtx_PLUS (SImode, operands[1], operands[2])));
+ DONE;")
+
+
+(define_insn_and_split "*ldhi_q_comb1"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (zero_extract:DI
+ (mem:DI (plus:SI (ior:SI (plus:SI (match_operand:SI 1
+ "register_operand" "r")
+ (match_operand:SI 2
+ "ua_offset" "I06"))
+ (const_int 7))
+ (const_int -7)))
+ (plus:SI (and:SI (plus:SI (match_dup 1) (match_operand:SI 3
+ "ua_offset" "I06"))
+ (const_int 7))
+ (const_int 1))
+ (const_int 0)))]
+ "TARGET_SHMEDIA32 && (INTVAL (operands[2]) & -8)
+ && (INTVAL (operands[2]) & 7) == INTVAL (operands[3])"
+ "#"
+ ""
+ [(pc)]
+ "emit_insn (gen_ldhi_q (operands[0],
+ gen_rtx_PLUS (SImode, operands[1], operands[2])));
+ DONE;")
+
+
+(define_insn "ldlo_l"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (zero_extract:SI
+ (mem:SI (and:SI (match_operand:QI 1 "ua_address_operand" "p")
+ (const_int -4)))
+ (minus:SI (const_int 4) (and:SI (match_dup 1) (const_int 3)))
+ (and:SI (match_dup 1) (const_int 3))))]
+ "TARGET_SHMEDIA32"
+ "ldlo.l %U1, %0"
+ [(set_attr "type" "load_media")])
+
+(define_insn "ldlo_q"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (zero_extract:DI
+ (mem:DI (and:SI (match_operand:QI 1 "ua_address_operand" "p")
+ (const_int -8)))
+ (minus:SI (const_int 8) (and:SI (match_dup 1) (const_int 7)))
+ (and:SI (match_dup 1) (const_int 7))))]
+ "TARGET_SHMEDIA32"
+ "ldlo.q %U1, %0"
+ [(set_attr "type" "load_media")])
+
+(define_insn_and_split "*ldlo_q_comb0"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (zero_extract:DI
+ (mem:DI (and:SI (plus:SI (match_operand:SI 1 "register_operand" "r")
+ (match_operand:SI 2 "ua_offset" "I06"))
+ (const_int -8)))
+ (minus:SI (const_int 8) (and:SI (match_dup 1) (const_int 7)))
+ (and:SI (match_dup 1) (const_int 7))))]
+ "TARGET_SHMEDIA32 && (INTVAL (operands[2]) & 7) == 0"
+ "#"
+ ""
+ [(pc)]
+ "emit_insn (gen_ldlo_q (operands[0],
+ gen_rtx_PLUS (SImode, operands[1], operands[2])));
+ DONE;")
+
+(define_insn_and_split "*ldlo_q_comb1"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (zero_extract:DI
+ (mem:DI (and:SI (plus:SI (match_operand:SI 1 "register_operand" "r")
+ (match_operand:SI 2 "ua_offset" "I06"))
+ (const_int -8)))
+ (minus:SI (const_int 8)
+ (and:SI (plus:SI (match_dup 1)
+ (match_operand:SI 3 "ua_offset" "I06"))
+ (const_int 7)))
+ (and:SI (plus:SI (match_dup 1) (match_dup 3)) (const_int 7))))]
+ "TARGET_SHMEDIA32 && (INTVAL (operands[2]) & -8)
+ && (INTVAL (operands[2]) & 7) == INTVAL (operands[3])"
+ "#"
+ ""
+ [(pc)]
+ "emit_insn (gen_ldlo_q (operands[0],
+ gen_rtx_PLUS (SImode, operands[1], operands[2])));
+ DONE;")
+
+(define_insn "sthi_l"
+ [(set (zero_extract:SI
+ (mem:SI (plus:SI (ior:SI (match_operand:QI 0 "ua_address_operand" "p")
+ (const_int 3))
+ (const_int -3)))
+ (plus:SI (and:SI (match_dup 0) (const_int 3)) (const_int 1))
+ (const_int 0))
+ (match_operand:SI 1 "arith_reg_operand" "r"))]
+ "TARGET_SHMEDIA32"
+ "sthi.l %U0, %1"
+ [(set_attr "type" "ustore_media")])
+
+;; All unaligned stores are considered to be 'narrow' because they typically
+;; operate on less that a quadword, and when they operate on a full quadword,
+;; the vanilla store high / store low sequence will cause a stall if not
+;; scheduled apart.
+(define_insn "sthi_q"
+ [(set (zero_extract:DI
+ (mem:DI (plus:SI (ior:SI (match_operand:QI 0 "ua_address_operand" "p")
+ (const_int 7))
+ (const_int -7)))
+ (plus:SI (and:SI (match_dup 0) (const_int 7)) (const_int 1))
+ (const_int 0))
+ (match_operand:DI 1 "arith_reg_operand" "r"))]
+ "TARGET_SHMEDIA32"
+ "sthi.q %U0, %1"
+ [(set_attr "type" "ustore_media")])
+
+(define_insn_and_split "*sthi_q_comb0"
+ [(set (zero_extract:DI
+ (mem:DI (plus:SI (ior:SI (plus:SI (match_operand:SI 0
+ "register_operand" "r")
+ (match_operand:SI 1 "ua_offset"
+ "I06"))
+ (const_int 7))
+ (const_int -7)))
+ (plus:SI (and:SI (match_dup 0) (const_int 7)) (const_int 1))
+ (const_int 0))
+ (match_operand:DI 2 "arith_reg_operand" "r"))]
+ "TARGET_SHMEDIA32 && (INTVAL (operands[1]) & 7) == 0"
+ "#"
+ ""
+ [(pc)]
+ "emit_insn (gen_sthi_q (gen_rtx_PLUS (SImode, operands[0], operands[1]),
+ operands[2]));
+ DONE;")
+
+(define_insn_and_split "*sthi_q_comb1"
+ [(set (zero_extract:DI
+ (mem:DI (plus:SI (ior:SI (plus:SI (match_operand:SI 0
+ "register_operand" "r")
+ (match_operand:SI 1 "ua_offset"
+ "I06"))
+ (const_int 7))
+ (const_int -7)))
+ (plus:SI (and:SI (plus:SI (match_dup 0)
+ (match_operand:SI 2 "ua_offset" "I06"))
+ (const_int 7))
+ (const_int 1))
+ (const_int 0))
+ (match_operand:DI 3 "arith_reg_operand" "r"))]
+ "TARGET_SHMEDIA32 && (INTVAL (operands[1]) & -8)
+ && (INTVAL (operands[1]) & 7) == INTVAL (operands[2])"
+ "#"
+ ""
+ [(pc)]
+ "emit_insn (gen_sthi_q (gen_rtx_PLUS (SImode, operands[0], operands[1]),
+ operands[3]));
+ DONE;")
+
+;; This is highpart user because the address is used as full 64 bit.
+(define_insn "stlo_l"
+ [(set (zero_extract:SI
+ (mem:SI (and:SI (match_operand:QI 0 "ua_address_operand" "p")
+ (const_int -4)))
+ (minus:SI (const_int 4) (and:SI (match_dup 0) (const_int 3)))
+ (and:SI (match_dup 0) (const_int 3)))
+ (match_operand:SI 1 "arith_reg_operand" "r"))]
+ "TARGET_SHMEDIA32"
+ "stlo.l %U0, %1"
+ [(set_attr "type" "ustore_media")])
+
+(define_insn "stlo_q"
+ [(set (zero_extract:DI
+ (mem:DI (and:SI (match_operand:QI 0 "ua_address_operand" "p")
+ (const_int -8)))
+ (minus:SI (const_int 8) (and:SI (match_dup 0) (const_int 7)))
+ (and:SI (match_dup 0) (const_int 7)))
+ (match_operand:DI 1 "arith_reg_operand" "r"))]
+ "TARGET_SHMEDIA32"
+ "stlo.q %U0, %1"
+ [(set_attr "type" "ustore_media")])
+
+(define_insn_and_split "*stlo_q_comb0"
+ [(set (zero_extract:DI
+ (mem:DI (and:SI (plus:SI (match_operand:SI 0 "register_operand" "r")
+ (match_operand:SI 1 "ua_offset" "I06"))
+ (const_int -8)))
+ (minus:SI (const_int 8) (and:SI (match_dup 0) (const_int 7)))
+ (and:SI (match_dup 0) (const_int 7)))
+ (match_operand:DI 2 "arith_reg_operand" "r"))]
+ "TARGET_SHMEDIA32 && (INTVAL (operands[1]) & 7) == 0"
+ "#"
+ ""
+ [(pc)]
+ "emit_insn (gen_stlo_q (gen_rtx_PLUS (SImode, operands[0], operands[1]),
+ operands[2]));
+ DONE;")
+
+(define_insn_and_split "*stlo_q_comb1"
+ [(set (zero_extract:DI
+ (mem:DI (and:SI (plus:SI (match_operand:SI 0 "register_operand" "r")
+ (match_operand:SI 1 "ua_offset" "I06"))
+ (const_int -8)))
+ (minus:SI (const_int 8) (and:SI (plus:SI (match_dup 0)
+ (match_operand:SI 2
+ "ua_offset" "I06"))
+ (const_int 7)))
+ (and:SI (plus:SI (match_dup 0) (match_dup 2)) (const_int 7)))
+ (match_operand:DI 3 "arith_reg_operand" "r"))]
+ "TARGET_SHMEDIA32 && (INTVAL (operands[1]) & 7) == INTVAL (operands[2])"
+ "#"
+ ""
+ [(pc)]
+ "emit_insn (gen_stlo_q (gen_rtx_PLUS (SImode, operands[0], operands[1]),
+ operands[3]));
+ DONE;")
+
+(define_insn "ldhi_l64"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (zero_extract:SI
+ (mem:SI (plus:DI (ior:DI (match_operand:QI 1 "ua_address_operand" "p")
+ (const_int 3))
+ (const_int -3)))
+ (plus:DI (and:DI (match_dup 1) (const_int 3)) (const_int 1))
+ (const_int 0)))]
+ "TARGET_SHMEDIA64"
+ "ldhi.l %U1, %0"
+ [(set_attr "type" "load_media")])
+
+(define_insn "ldhi_q64"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (zero_extract:DI
+ (mem:DI (plus:DI (ior:DI (match_operand:QI 1 "ua_address_operand" "p")
+ (const_int 7))
+ (const_int -7)))
+ (plus:DI (and:DI (match_dup 1) (const_int 7)) (const_int 1))
+ (const_int 0)))]
+ "TARGET_SHMEDIA64"
+ "ldhi.q %U1, %0"
+ [(set_attr "type" "load_media")])
+
+(define_insn "ldlo_l64"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (zero_extract:SI
+ (mem:SI (and:DI (match_operand:QI 1 "ua_address_operand" "p")
+ (const_int -4)))
+ (minus:DI (const_int 4) (and:DI (match_dup 1) (const_int 3)))
+ (and:DI (match_dup 1) (const_int 3))))]
+ "TARGET_SHMEDIA64"
+ "ldlo.l %U1, %0"
+ [(set_attr "type" "load_media")])
+
+(define_insn "ldlo_q64"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (zero_extract:DI
+ (mem:DI (and:DI (match_operand:QI 1 "ua_address_operand" "p")
+ (const_int -8)))
+ (minus:DI (const_int 8) (and:DI (match_dup 1) (const_int 7)))
+ (and:DI (match_dup 1) (const_int 7))))]
+ "TARGET_SHMEDIA64"
+ "ldlo.q %U1, %0"
+ [(set_attr "type" "load_media")])
+
+(define_insn "sthi_l64"
+ [(set (zero_extract:SI
+ (mem:SI (plus:DI (ior:DI (match_operand:QI 0 "ua_address_operand" "p")
+ (const_int 3))
+ (const_int -3)))
+ (plus:DI (and:DI (match_dup 0) (const_int 3)) (const_int 1))
+ (const_int 0))
+ (match_operand:SI 1 "arith_reg_operand" "r"))]
+ "TARGET_SHMEDIA64"
+ "sthi.l %U0, %1"
+ [(set_attr "type" "ustore_media")])
+
+(define_insn "sthi_q64"
+ [(set (zero_extract:DI
+ (mem:DI (plus:DI (ior:DI (match_operand:QI 0 "ua_address_operand" "p")
+ (const_int 7))
+ (const_int -7)))
+ (plus:DI (and:DI (match_dup 0) (const_int 7)) (const_int 1))
+ (const_int 0))
+ (match_operand:DI 1 "arith_reg_operand" "r"))]
+ "TARGET_SHMEDIA64"
+ "sthi.q %U0, %1"
+ [(set_attr "type" "ustore_media")])
+
+(define_insn "stlo_l64"
+ [(set (zero_extract:SI
+ (mem:SI (and:DI (match_operand:QI 0 "ua_address_operand" "p")
+ (const_int -4)))
+ (minus:DI (const_int 4) (and:DI (match_dup 0) (const_int 3)))
+ (and:DI (match_dup 0) (const_int 3)))
+ (match_operand:SI 1 "arith_reg_operand" "r"))]
+ "TARGET_SHMEDIA64"
+ "stlo.l %U0, %1"
+ [(set_attr "type" "ustore_media")])
+
+(define_insn "stlo_q64"
+ [(set (zero_extract:DI
+ (mem:DI (and:DI (match_operand:QI 0 "ua_address_operand" "p")
+ (const_int -8)))
+ (minus:DI (const_int 8) (and:DI (match_dup 0) (const_int 7)))
+ (and:DI (match_dup 0) (const_int 7)))
+ (match_operand:DI 1 "arith_reg_operand" "r"))]
+ "TARGET_SHMEDIA64"
+ "stlo.q %U0, %1"
+ [(set_attr "type" "ustore_media")])
+
+(define_insn "nsb"
+ [(set (match_operand:QI 0 "arith_reg_dest" "=r")
+ (unspec:QI [(match_operand:DI 1 "arith_reg_operand" "r")]
+ UNSPEC_NSB))]
+ "TARGET_SHMEDIA"
+ "nsb %1, %0"
+ [(set_attr "type" "arith_media")])
+
+(define_insn "nsbsi"
+ [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+ (zero_extend:SI
+ (unspec:QI [(match_operand:DI 1 "arith_reg_operand" "r")]
+ UNSPEC_NSB)))]
+ "TARGET_SHMEDIA"
+ "nsb %1, %0"
+ [(set_attr "type" "arith_media")])
+
+(define_insn "nsbdi"
+ [(set (match_operand:DI 0 "arith_reg_dest" "=r")
+ (zero_extend:DI
+ (unspec:QI [(match_operand:DI 1 "arith_reg_operand" "r")]
+ UNSPEC_NSB)))]
+ "TARGET_SHMEDIA"
+ "nsb %1, %0"
+ [(set_attr "type" "arith_media")])
+
+(define_expand "ffsdi2"
+ [(set (match_operand:DI 0 "arith_reg_dest" "")
+ (ffs:DI (match_operand:DI 1 "arith_reg_operand" "")))]
+ "TARGET_SHMEDIA"
+ "
+{
+ rtx scratch = gen_reg_rtx (DImode);
+ rtx last;
+
+ emit_insn (gen_adddi3 (scratch, operands[1], constm1_rtx));
+ emit_insn (gen_xordi3 (scratch, operands[1], scratch));
+ emit_insn (gen_lshrdi3_media (scratch, scratch, const1_rtx));
+ emit_insn (gen_nsbdi (scratch, scratch));
+ emit_insn (gen_adddi3 (scratch, scratch, GEN_INT (-64)));
+ emit_insn (gen_movdicc_false (scratch, operands[1], const0_rtx, scratch));
+ last = emit_insn (gen_subdi3 (operands[0], const0_rtx, scratch));
+ set_unique_reg_note (last, REG_EQUAL, gen_rtx_FFS (DImode, operands[0]));
+
+ DONE;
+}")
+
+(define_expand "ffssi2"
+ [(set (match_operand:SI 0 "arith_reg_dest" "")
+ (ffs:SI (match_operand:SI 1 "arith_reg_operand" "")))]
+ "TARGET_SHMEDIA"
+ "
+{
+ rtx scratch = gen_reg_rtx (SImode);
+ rtx discratch = gen_reg_rtx (DImode);
+ rtx last;
+
+ emit_insn (gen_adddi3 (discratch,
+ simplify_gen_subreg (DImode, operands[1], SImode, 0),
+ constm1_rtx));
+ emit_insn (gen_andcdi3 (discratch,
+ simplify_gen_subreg (DImode, operands[1], SImode, 0),
+ discratch));
+ emit_insn (gen_nsbsi (scratch, discratch));
+ last = emit_insn (gen_subsi3 (operands[0],
+ force_reg (SImode, GEN_INT (63)), scratch));
+ set_unique_reg_note (last, REG_EQUAL, gen_rtx_FFS (SImode, operands[0]));
+
+ DONE;
+}")
+
+(define_insn "byterev"
+ [(set (match_operand:V8QI 0 "arith_reg_dest" "=r")
+ (vec_select:V8QI (match_operand:V8QI 1 "arith_reg_operand" "r")
+ (parallel [(const_int 7) (const_int 6) (const_int 5)
+ (const_int 4) (const_int 3) (const_int 2)
+ (const_int 1) (const_int 0)])))]
+ "TARGET_SHMEDIA"
+ "byterev %1, %0"
+ [(set_attr "type" "arith_media")])
+
+(define_insn "*prefetch_media"
+ [(prefetch (match_operand:QI 0 "address_operand" "p")
+ (match_operand:SI 1 "const_int_operand" "n")
+ (match_operand:SI 2 "const_int_operand" "n"))]
+ "TARGET_SHMEDIA"
+ "*
+{
+ operands[0] = gen_rtx_MEM (QImode, operands[0]);
+ output_asm_insn (\"ld%M0.b %m0,r63\", operands);
+ return \"\";
+}"
+ [(set_attr "type" "other")])
+
+(define_insn "*prefetch_i4"
+ [(prefetch (match_operand:SI 0 "register_operand" "r")
+ (match_operand:SI 1 "const_int_operand" "n")
+ (match_operand:SI 2 "const_int_operand" "n"))]
+ "(TARGET_HARD_SH4 || TARGET_SHCOMPACT) && !TARGET_VXWORKS_RTP"
+ "*
+{
+ return \"pref @%0\";
+}"
+ [(set_attr "type" "other")])
+
+;; In user mode, the "pref" instruction will raise a RADDERR exception
+;; for accesses to [0x80000000,0xffffffff]. This makes it an unsuitable
+;; implementation of __builtin_prefetch for VxWorks RTPs.
+(define_expand "prefetch"
+ [(prefetch (match_operand 0 "address_operand" "p")
+ (match_operand:SI 1 "const_int_operand" "n")
+ (match_operand:SI 2 "const_int_operand" "n"))]
+ "TARGET_SH2A || ((TARGET_HARD_SH4 || TARGET_SH5)
+ && (TARGET_SHMEDIA || !TARGET_VXWORKS_RTP))"
+ "
+{
+ if (GET_MODE (operands[0]) != Pmode
+ || !CONST_INT_P (operands[1])
+ || !CONST_INT_P (operands[2]))
+ FAIL;
+ if (! TARGET_SHMEDIA)
+ operands[0] = force_reg (Pmode, operands[0]);
+}")
+
+(define_insn "prefetch_m2a"
+ [(prefetch (match_operand:SI 0 "register_operand" "r")
+ (match_operand:SI 1 "const_int_operand" "n")
+ (match_operand:SI 2 "const_int_operand" "n"))]
+ "TARGET_SH2A"
+ "pref\\t@%0"
+ [(set_attr "type" "other")])
+
+(define_insn "alloco_i"
+ [(set (mem:BLK (match_operand:QI 0 "cache_address_operand" "p"))
+ (unspec:BLK [(const_int 0)] UNSPEC_ALLOCO))]
+ "TARGET_SHMEDIA32"
+ "*
+{
+ rtx xops[2];
+
+ if (GET_CODE (operands[0]) == PLUS)
+ {
+ xops[0] = XEXP (operands[0], 0);
+ xops[1] = XEXP (operands[0], 1);
+ }
+ else
+ {
+ xops[0] = operands[0];
+ xops[1] = const0_rtx;
+ }
+ output_asm_insn (\"alloco %0, %1\", xops);
+ return \"\";
+}"
+ [(set_attr "type" "other")])
+
+(define_split
+ [(set (match_operand 0 "any_register_operand" "")
+ (match_operand 1 "" ""))]
+ "TARGET_SHMEDIA && reload_completed"
+ [(set (match_dup 0) (match_dup 1))]
+ "
+{
+ int n_changes = 0;
+
+ for_each_rtx (&operands[1], shmedia_cleanup_truncate, &n_changes);
+ if (!n_changes)
+ FAIL;
+}")
+
+; Stack Protector Patterns
+
+(define_expand "stack_protect_set"
+ [(set (match_operand 0 "memory_operand" "")
+ (match_operand 1 "memory_operand" ""))]
+ ""
+{
+ if (TARGET_SHMEDIA)
+ {
+ if (TARGET_SHMEDIA64)
+ emit_insn (gen_stack_protect_set_di_media (operands[0], operands[1]));
+ else
+ emit_insn (gen_stack_protect_set_si_media (operands[0], operands[1]));
+ }
+ else
+ emit_insn (gen_stack_protect_set_si (operands[0], operands[1]));
+
+ DONE;
+})
+
+(define_insn "stack_protect_set_si"
+ [(set (match_operand:SI 0 "memory_operand" "=m")
+ (unspec:SI [(match_operand:SI 1 "memory_operand" "m")] UNSPEC_SP_SET))
+ (set (match_scratch:SI 2 "=&r") (const_int 0))]
+ "!TARGET_SHMEDIA"
+ "mov.l\t%1, %2\;mov.l\t%2, %0\;mov\t#0, %2"
+ [(set_attr "type" "other")
+ (set_attr "length" "6")])
+
+(define_insn "stack_protect_set_si_media"
+ [(set (match_operand:SI 0 "memory_operand" "=m")
+ (unspec:SI [(match_operand:SI 1 "memory_operand" "m")] UNSPEC_SP_SET))
+ (set (match_scratch:SI 2 "=&r") (const_int 0))]
+ "TARGET_SHMEDIA"
+ "ld%M1.l\t%m1, %2\;st%M0.l\t%m0, %2\;movi\t0, %2"
+ [(set_attr "type" "other")
+ (set_attr "length" "12")])
+
+(define_insn "stack_protect_set_di_media"
+ [(set (match_operand:DI 0 "memory_operand" "=m")
+ (unspec:DI [(match_operand:DI 1 "memory_operand" "m")] UNSPEC_SP_SET))
+ (set (match_scratch:DI 2 "=&r") (const_int 0))]
+ "TARGET_SHMEDIA64"
+ "ld%M1.q\t%m1, %2\;st%M0.q\t%m0, %2\;movi\t0, %2"
+ [(set_attr "type" "other")
+ (set_attr "length" "12")])
+
+(define_expand "stack_protect_test"
+ [(match_operand 0 "memory_operand" "")
+ (match_operand 1 "memory_operand" "")
+ (match_operand 2 "" "")]
+ ""
+{
+ if (TARGET_SHMEDIA)
+ {
+ rtx tmp = gen_reg_rtx (GET_MODE (operands[0]));
+ rtx test;
+
+ test = gen_rtx_NE (VOIDmode, tmp, const0_rtx);
+ if (TARGET_SHMEDIA64)
+ {
+ emit_insn (gen_stack_protect_test_di_media (tmp, operands[0],
+ operands[1]));
+ emit_jump_insn (gen_cbranchdi4 (test, tmp, const0_rtx, operands[2]));
+ }
+ else
+ {
+ emit_insn (gen_stack_protect_test_si_media (tmp, operands[0],
+ operands[1]));
+ emit_jump_insn (gen_cbranchsi4 (test, tmp, const0_rtx, operands[2]));
+ }
+ }
+ else
+ {
+ emit_insn (gen_stack_protect_test_si (operands[0], operands[1]));
+ emit_jump_insn (gen_branch_true (operands[2]));
+ }
+
+ DONE;
+})
+
+(define_insn "stack_protect_test_si"
+ [(set (reg:SI T_REG)
+ (unspec:SI [(match_operand:SI 0 "memory_operand" "m")
+ (match_operand:SI 1 "memory_operand" "m")]
+ UNSPEC_SP_TEST))
+ (set (match_scratch:SI 2 "=&r") (const_int 0))
+ (set (match_scratch:SI 3 "=&r") (const_int 0))]
+ "!TARGET_SHMEDIA"
+ "mov.l\t%0, %2\;mov.l\t%1, %3\;cmp/eq\t%2, %3\;mov\t#0, %2\;mov\t#0, %3"
+ [(set_attr "type" "other")
+ (set_attr "length" "10")])
+
+(define_insn "stack_protect_test_si_media"
+ [(set (match_operand:SI 0 "register_operand" "=&r")
+ (unspec:SI [(match_operand:SI 1 "memory_operand" "m")
+ (match_operand:SI 2 "memory_operand" "m")]
+ UNSPEC_SP_TEST))
+ (set (match_scratch:SI 3 "=&r") (const_int 0))]
+ "TARGET_SHMEDIA"
+ "ld%M1.l\t%m1, %0\;ld%M2.l\t%m2, %3\;cmpeq\t%0, %3, %0\;movi\t0, %3"
+ [(set_attr "type" "other")
+ (set_attr "length" "16")])
+
+(define_insn "stack_protect_test_di_media"
+ [(set (match_operand:DI 0 "register_operand" "=&r")
+ (unspec:DI [(match_operand:DI 1 "memory_operand" "m")
+ (match_operand:DI 2 "memory_operand" "m")]
+ UNSPEC_SP_TEST))
+ (set (match_scratch:DI 3 "=&r") (const_int 0))]
+ "TARGET_SHMEDIA64"
+ "ld%M1.q\t%m1, %0\;ld%M2.q\t%m2, %3\;cmpeq\t%0, %3, %0\;movi\t0, %3"
+ [(set_attr "type" "other")
+ (set_attr "length" "16")])
diff --git a/gcc/config/sh/sh.opt b/gcc/config/sh/sh.opt
new file mode 100644
index 000000000..8464bd75f
--- /dev/null
+++ b/gcc/config/sh/sh.opt
@@ -0,0 +1,338 @@
+; Options for the SH port of the compiler.
+
+; Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010
+; Free Software Foundation, Inc.
+;
+; This file is part of GCC.
+;
+; GCC is free software; you can redistribute it and/or modify it under
+; the terms of the GNU General Public License as published by the Free
+; Software Foundation; either version 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
+; <http://www.gnu.org/licenses/>.
+
+;; Used for various architecture options.
+Mask(SH_E)
+
+;; Set if the default precision of th FPU is single.
+Mask(FPU_SINGLE)
+
+;; Set if we should generate code using type 2A insns.
+Mask(HARD_SH2A)
+
+;; Set if we should generate code using type 2A DF insns.
+Mask(HARD_SH2A_DOUBLE)
+
+;; Set if compiling for SH4 hardware (to be used for insn costs etc.)
+Mask(HARD_SH4)
+
+;; Set if we should generate code for a SH5 CPU (either ISA).
+Mask(SH5)
+
+;; Set if we should save all target registers.
+Mask(SAVE_ALL_TARGET_REGS)
+
+m1
+Target RejectNegative Mask(SH1) Condition(SUPPORT_SH1)
+Generate SH1 code
+
+m2
+Target RejectNegative Mask(SH2) Condition(SUPPORT_SH2)
+Generate SH2 code
+
+m2a
+Target RejectNegative Condition(SUPPORT_SH2A)
+Generate default double-precision SH2a-FPU code
+
+m2a-nofpu
+Target RejectNegative Condition(SUPPORT_SH2A_NOFPU)
+Generate SH2a FPU-less code
+
+m2a-single
+Target RejectNegative Condition(SUPPORT_SH2A_SINGLE)
+Generate default single-precision SH2a-FPU code
+
+m2a-single-only
+Target RejectNegative Condition(SUPPORT_SH2A_SINGLE_ONLY)
+Generate only single-precision SH2a-FPU code
+
+m2e
+Target RejectNegative Condition(SUPPORT_SH2E)
+Generate SH2e code
+
+m3
+Target RejectNegative Mask(SH3) Condition(SUPPORT_SH3)
+Generate SH3 code
+
+m3e
+Target RejectNegative Condition(SUPPORT_SH3E)
+Generate SH3e code
+
+m4
+Target RejectNegative Mask(SH4) Condition(SUPPORT_SH4)
+Generate SH4 code
+
+m4-100
+Target RejectNegative Condition(SUPPORT_SH4)
+Generate SH4-100 code
+
+m4-200
+Target RejectNegative Condition(SUPPORT_SH4)
+Generate SH4-200 code
+
+;; TARGET_SH4_300 indicates if we have the ST40-300 instruction set and
+;; pipeline - irrespective of ABI.
+m4-300
+Target RejectNegative Condition(SUPPORT_SH4) Var(TARGET_SH4_300)
+Generate SH4-300 code
+
+m4-nofpu
+Target RejectNegative Condition(SUPPORT_SH4_NOFPU)
+Generate SH4 FPU-less code
+
+m4-100-nofpu
+Target RejectNegative Condition(SUPPORT_SH4_NOFPU)
+Generate SH4-100 FPU-less code
+
+m4-200-nofpu
+Target RejectNegative Condition(SUPPORT_SH4_NOFPU)
+Generate SH4-200 FPU-less code
+
+m4-300-nofpu
+Target RejectNegative Condition(SUPPORT_SH4_NOFPU) Var(TARGET_SH4_300)
+Generate SH4-300 FPU-less code
+
+m4-340
+Target RejectNegative Condition(SUPPORT_SH4_NOFPU) Var(TARGET_SH4_300)
+Generate code for SH4 340 series (MMU/FPU-less)
+;; passes -isa=sh4-nommu-nofpu to the assembler.
+
+m4-400
+Target RejectNegative Condition(SUPPORT_SH4_NOFPU)
+Generate code for SH4 400 series (MMU/FPU-less)
+;; passes -isa=sh4-nommu-nofpu to the assembler.
+
+m4-500
+Target RejectNegative Condition(SUPPORT_SH4_NOFPU)
+Generate code for SH4 500 series (FPU-less).
+;; passes -isa=sh4-nofpu to the assembler.
+
+m4-single
+Target RejectNegative Condition(SUPPORT_SH4_SINGLE)
+Generate default single-precision SH4 code
+
+m4-100-single
+Target RejectNegative Condition(SUPPORT_SH4_SINGLE)
+Generate default single-precision SH4-100 code
+
+m4-200-single
+Target RejectNegative Condition(SUPPORT_SH4_SINGLE)
+Generate default single-precision SH4-200 code
+
+m4-300-single
+Target RejectNegative Condition(SUPPORT_SH4_SINGLE) Var(TARGET_SH4_300)
+Generate default single-precision SH4-300 code
+
+m4-single-only
+Target RejectNegative Condition(SUPPORT_SH4_SINGLE_ONLY)
+Generate only single-precision SH4 code
+
+m4-100-single-only
+Target RejectNegative Condition(SUPPORT_SH4_SINGLE_ONLY)
+Generate only single-precision SH4-100 code
+
+m4-200-single-only
+Target RejectNegative Condition(SUPPORT_SH4_SINGLE_ONLY)
+Generate only single-precision SH4-200 code
+
+m4-300-single-only
+Target RejectNegative Condition(SUPPORT_SH4_SINGLE_ONLY) Var(TARGET_SH4_300)
+Generate only single-precision SH4-300 code
+
+m4a
+Target RejectNegative Mask(SH4A) Condition(SUPPORT_SH4A)
+Generate SH4a code
+
+m4a-nofpu
+Target RejectNegative Condition(SUPPORT_SH4A_NOFPU)
+Generate SH4a FPU-less code
+
+m4a-single
+Target RejectNegative Condition(SUPPORT_SH4A_SINGLE)
+Generate default single-precision SH4a code
+
+m4a-single-only
+Target RejectNegative Condition(SUPPORT_SH4A_SINGLE_ONLY)
+Generate only single-precision SH4a code
+
+m4al
+Target RejectNegative Condition(SUPPORT_SH4AL)
+Generate SH4al-dsp code
+
+m5-32media
+Target RejectNegative Condition(SUPPORT_SH5_32MEDIA)
+Generate 32-bit SHmedia code
+
+m5-32media-nofpu
+Target RejectNegative Condition(SUPPORT_SH5_32MEDIA_NOFPU)
+Generate 32-bit FPU-less SHmedia code
+
+m5-64media
+Target RejectNegative Condition(SUPPORT_SH5_64MEDIA)
+Generate 64-bit SHmedia code
+
+m5-64media-nofpu
+Target RejectNegative Condition(SUPPORT_SH5_64MEDIA_NOFPU)
+Generate 64-bit FPU-less SHmedia code
+
+m5-compact
+Target RejectNegative Condition(SUPPORT_SH5_32MEDIA)
+Generate SHcompact code
+
+m5-compact-nofpu
+Target RejectNegative Condition(SUPPORT_SH5_32MEDIA_NOFPU)
+Generate FPU-less SHcompact code
+
+maccumulate-outgoing-args
+Target Report Var(TARGET_ACCUMULATE_OUTGOING_ARGS) Init(1)
+Reserve space for outgoing arguments in the function prologue
+
+madjust-unroll
+Target Report Mask(ADJUST_UNROLL) Condition(SUPPORT_ANY_SH5)
+Throttle unrolling to avoid thrashing target registers unless the unroll benefit outweighs this
+
+mb
+Target Report RejectNegative InverseMask(LITTLE_ENDIAN)
+Generate code in big endian mode
+
+mbigtable
+Target Report RejectNegative Mask(BIGTABLE)
+Generate 32-bit offsets in switch tables
+
+mbitops
+Target Report RejectNegative Mask(BITOPS)
+Generate bit instructions
+
+mbranch-cost=
+Target RejectNegative Joined UInteger Var(sh_branch_cost) Init(-1)
+Cost to assume for a branch insn
+
+mcbranchdi
+Target Var(TARGET_CBRANCHDI4)
+Enable cbranchdi4 pattern
+
+mcmpeqdi
+Target Var(TARGET_CMPEQDI_T)
+Emit cmpeqdi_t pattern even when -mcbranchdi is in effect.
+
+mcut2-workaround
+Target RejectNegative Var(TARGET_SH5_CUT2_WORKAROUND)
+Enable SH5 cut2 workaround
+
+mdalign
+Target Report RejectNegative Mask(ALIGN_DOUBLE)
+Align doubles at 64-bit boundaries
+
+mdiv=
+Target RejectNegative Joined Var(sh_div_str) Init("")
+Division strategy, one of: call, call2, fp, inv, inv:minlat, inv20u, inv20l, inv:call, inv:call2, inv:fp, call-div1, call-fp, call-table
+
+mdivsi3_libfunc=
+Target RejectNegative Joined Var(sh_divsi3_libfunc) Init("")
+Specify name for 32 bit signed division function
+
+mfmovd
+Target RejectNegative Mask(FMOVD)
+Enable the use of 64-bit floating point registers in fmov instructions. See -mdalign if 64-bit alignment is required.
+
+mfixed-range=
+Target RejectNegative Joined Var(sh_fixed_range_str)
+Specify range of registers to make fixed
+
+mfused-madd
+Target Var(TARGET_FMAC)
+Enable the use of the fused floating point multiply-accumulate operation
+
+mgettrcost=
+Target RejectNegative Joined UInteger Var(sh_gettrcost) Init(-1)
+Cost to assume for gettr insn
+
+mhitachi
+Target Report RejectNegative Mask(HITACHI)
+Follow Renesas (formerly Hitachi) / SuperH calling conventions
+
+mieee
+Target Var(TARGET_IEEE)
+Increase the IEEE compliance for floating-point comparisons
+
+mindexed-addressing
+Target Report Mask(ALLOW_INDEXED_ADDRESS) Condition(SUPPORT_ANY_SH5_32MEDIA)
+Enable the use of the indexed addressing mode for SHmedia32/SHcompact
+
+minline-ic_invalidate
+Target Report Var(TARGET_INLINE_IC_INVALIDATE)
+inline code to invalidate instruction cache entries after setting up nested function trampolines
+
+minvalid-symbols
+Target Report Mask(INVALID_SYMBOLS) Condition(SUPPORT_ANY_SH5)
+Assume symbols might be invalid
+
+misize
+Target Report RejectNegative Mask(DUMPISIZE)
+Annotate assembler instructions with estimated addresses
+
+ml
+Target Report RejectNegative Mask(LITTLE_ENDIAN)
+Generate code in little endian mode
+
+mnomacsave
+Target Report RejectNegative Mask(NOMACSAVE)
+Mark MAC register as call-clobbered
+
+;; ??? This option is not useful, but is retained in case there are people
+;; who are still relying on it. It may be deleted in the future.
+mpadstruct
+Target Report RejectNegative Mask(PADSTRUCT)
+Make structs a multiple of 4 bytes (warning: ABI altered)
+
+mprefergot
+Target Report RejectNegative Mask(PREFERGOT)
+Emit function-calls using global offset table when generating PIC
+
+mpt-fixed
+Target Report Mask(PT_FIXED) Condition(SUPPORT_ANY_SH5)
+Assume pt* instructions won't trap
+
+mrelax
+Target Report RejectNegative Mask(RELAX)
+Shorten address references during linking
+
+mrenesas
+Target Mask(HITACHI) MaskExists
+Follow Renesas (formerly Hitachi) / SuperH calling conventions
+
+mspace
+Target RejectNegative Alias(Os)
+Deprecated. Use -Os instead
+
+multcost=
+Target RejectNegative Joined UInteger Var(sh_multcost) Init(-1)
+Cost to assume for a multiply insn
+
+musermode
+Target Report RejectNegative Mask(USERMODE)
+Don't generate privileged-mode only code; implies -mno-inline-ic_invalidate if the inline code would not work in user mode.
+
+;; We might want to enable this by default for TARGET_HARD_SH4, because
+;; zero-offset branches have zero latency. Needs some benchmarking.
+mpretend-cmove
+Target Var(TARGET_PRETEND_CMOVE)
+Pretend a branch-around-a-move is a conditional move.
diff --git a/gcc/config/sh/sh1.md b/gcc/config/sh/sh1.md
new file mode 100644
index 000000000..970f3fc06
--- /dev/null
+++ b/gcc/config/sh/sh1.md
@@ -0,0 +1,85 @@
+;; DFA scheduling description for Renesas / SuperH SH.
+;; Copyright (C) 2004, 2006, 2007 Free Software Foundation, Inc.
+
+;; This file is part of GCC.
+
+;; GCC is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 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
+;; <http://www.gnu.org/licenses/>.
+
+;; Load and store instructions save a cycle if they are aligned on a
+;; four byte boundary. Using a function unit for stores encourages
+;; gcc to separate load and store instructions by one instruction,
+;; which makes it more likely that the linker will be able to word
+;; align them when relaxing.
+
+;; SH-1 scheduling. This is just a conversion of the old scheduling
+;; model, using define_function_unit.
+
+(define_automaton "sh1")
+(define_cpu_unit "sh1memory,sh1int,sh1mpy,sh1fp" "sh1")
+
+;; Loads have a latency of two.
+;; However, call insns can have a delay slot, so that we want one more
+;; insn to be scheduled between the load of the function address and the call.
+;; This is equivalent to a latency of three.
+;; ADJUST_COST can only properly handle reductions of the cost, so we
+;; use a latency of three here.
+;; We only do this for SImode loads of general registers, to make the work
+;; for ADJUST_COST easier.
+(define_insn_reservation "sh1_load_si" 3
+ (and (eq_attr "pipe_model" "sh1")
+ (eq_attr "type" "load_si,pcload_si"))
+ "sh1memory*2")
+
+(define_insn_reservation "sh1_load_store" 2
+ (and (eq_attr "pipe_model" "sh1")
+ (eq_attr "type" "load,pcload,pload,mem_mac,store,fstore,pstore,mac_mem"))
+ "sh1memory*2")
+
+(define_insn_reservation "sh1_arith3" 3
+ (and (eq_attr "pipe_model" "sh1")
+ (eq_attr "type" "arith3,arith3b"))
+ "sh1int*3")
+
+(define_insn_reservation "sh1_dyn_shift" 2
+ (and (eq_attr "pipe_model" "sh1")
+ (eq_attr "type" "dyn_shift"))
+ "sh1int*2")
+
+(define_insn_reservation "sh1_int" 1
+ (and (eq_attr "pipe_model" "sh1")
+ (eq_attr "type" "!arith3,arith3b,dyn_shift"))
+ "sh1int")
+
+;; ??? These are approximations.
+(define_insn_reservation "sh1_smpy" 2
+ (and (eq_attr "pipe_model" "sh1")
+ (eq_attr "type" "smpy"))
+ "sh1mpy*2")
+
+(define_insn_reservation "sh1_dmpy" 3
+ (and (eq_attr "pipe_model" "sh1")
+ (eq_attr "type" "dmpy"))
+ "sh1mpy*3")
+
+(define_insn_reservation "sh1_fp" 2
+ (and (eq_attr "pipe_model" "sh1")
+ (eq_attr "type" "fp,fpscr_toggle,fp_cmp,fmove"))
+ "sh1fp")
+
+(define_insn_reservation "sh1_fdiv" 13
+ (and (eq_attr "pipe_model" "sh1")
+ (eq_attr "type" "fdiv"))
+ "sh1fp*12")
+
diff --git a/gcc/config/sh/sh4-300.md b/gcc/config/sh/sh4-300.md
new file mode 100644
index 000000000..a9fb07cac
--- /dev/null
+++ b/gcc/config/sh/sh4-300.md
@@ -0,0 +1,287 @@
+;; DFA scheduling description for ST40-300.
+;; Copyright (C) 2004, 2006, 2007 Free Software Foundation, Inc.
+
+;; This file is part of GCC.
+
+;; GCC is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 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
+;; <http://www.gnu.org/licenses/>.
+
+;; Load and store instructions save a cycle if they are aligned on a
+;; four byte boundary. Using a function unit for stores encourages
+;; gcc to separate load and store instructions by one instruction,
+;; which makes it more likely that the linker will be able to word
+;; align them when relaxing.
+
+;; The following description models the ST40-300 pipeline using the DFA based
+;; scheduler.
+
+;; Two automata are defined to reduce number of states
+;; which a single large automaton will have. (Factoring)
+
+(define_automaton "sh4_300_inst_pipeline,sh4_300_fpu_pipe")
+
+;; This unit is basically the decode unit of the processor.
+;; Since SH4 is a dual issue machine,it is as if there are two
+;; units so that any insn can be processed by either one
+;; of the decoding unit.
+
+(define_cpu_unit "sh4_300_pipe_01,sh4_300_pipe_02" "sh4_300_inst_pipeline")
+
+;; The floating point units.
+
+(define_cpu_unit "sh4_300_fpt,sh4_300_fpu,sh4_300_fds" "sh4_300_fpu_pipe")
+
+;; integer multiplier unit
+
+(define_cpu_unit "sh4_300_mul" "sh4_300_inst_pipeline")
+
+;; LS unit
+
+(define_cpu_unit "sh4_300_ls" "sh4_300_inst_pipeline")
+
+;; The address calculator used for branch instructions.
+;; This will be reserved after "issue" of branch instructions
+;; and this is to make sure that no two branch instructions
+;; can be issued in parallel.
+
+(define_cpu_unit "sh4_300_br" "sh4_300_inst_pipeline")
+
+;; ----------------------------------------------------
+;; This reservation is to simplify the dual issue description.
+
+(define_reservation "sh4_300_issue" "sh4_300_pipe_01|sh4_300_pipe_02")
+
+(define_reservation "all" "sh4_300_pipe_01+sh4_300_pipe_02")
+
+;;(define_insn_reservation "nil" 0 (eq_attr "type" "nil") "nothing")
+
+;; MOV RM,RN / MOV #imm8,RN / STS PR,RN
+(define_insn_reservation "sh4_300_mov" 0
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "move,movi8,prget"))
+ "sh4_300_issue")
+
+;; Fixed STS from MACL / MACH
+(define_insn_reservation "sh4_300_mac_gp" 0
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "mac_gp"))
+ "sh4_300_issue+sh4_300_mul")
+
+;; Fixed LDS to MACL / MACH
+(define_insn_reservation "sh4_300_gp_mac" 1
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "gp_mac"))
+ "sh4_300_issue+sh4_300_mul")
+
+;; Instructions without specific resource requirements with latency 1.
+
+(define_insn_reservation "sh4_300_simple_arith" 1
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "mt_group,arith,dyn_shift,prset"))
+ "sh4_300_issue")
+
+;; Load and store instructions have no alignment peculiarities for the ST40-300,
+;; but they use the load-store unit, which they share with the fmove type
+;; insns (fldi[01]; fmov frn,frm; flds; fsts; fabs; fneg) .
+;; Loads have a latency of three.
+
+;; Load Store instructions.
+(define_insn_reservation "sh4_300_load" 3
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "load,pcload,load_si,pcload_si,pload"))
+ "sh4_300_issue+sh4_300_ls")
+
+(define_insn_reservation "sh4_300_mac_load" 3
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "mem_mac"))
+ "sh4_300_issue+sh4_300_ls+sh4_300_mul")
+
+(define_insn_reservation "sh4_300_fload" 4
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "fload,pcfload"))
+ "sh4_300_issue+sh4_300_ls+sh4_300_fpt")
+
+;; sh_adjust_cost describes the reduced latency of the feeding insns of a store.
+;; The latency of an auto-increment register is 1; the latency of the memory
+;; output is not actually considered here anyway.
+(define_insn_reservation "sh4_300_store" 1
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "store,pstore"))
+ "sh4_300_issue+sh4_300_ls")
+
+(define_insn_reservation "sh4_300_fstore" 1
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "fstore"))
+ "sh4_300_issue+sh4_300_ls+sh4_300_fpt")
+
+;; Fixed STS.L from MACL / MACH
+(define_insn_reservation "sh4_300_mac_store" 1
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "mac_mem"))
+ "sh4_300_issue+sh4_300_mul+sh4_300_ls")
+
+(define_insn_reservation "sh4_300_gp_fpul" 2
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "gp_fpul"))
+ "sh4_300_issue+sh4_300_fpt")
+
+(define_insn_reservation "sh4_300_fpul_gp" 1
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "fpul_gp"))
+ "sh4_300_issue+sh4_300_fpt")
+
+;; Branch (BF,BF/S,BT,BT/S,BRA)
+;; Branch Far (JMP,RTS,BRAF)
+;; Group: BR
+;; When displacement is 0 for BF / BT, we have effectively conditional
+;; execution of one instruction, without pipeline disruption.
+;; Otherwise, the latency depends on prediction success.
+;; We can't really do much with the latency, even if we could express it,
+;; but the pairing restrictions are useful to take into account.
+;; ??? If the branch is likely, and not paired with a preceding insn,
+;; or likely and likely not predicted, we might want to fill the delay slot.
+;; However, there appears to be no machinery to make the compiler
+;; recognize these scenarios.
+
+(define_insn_reservation "sh4_300_branch" 1
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "cbranch,jump,return,jump_ind"))
+ "sh4_300_issue+sh4_300_br")
+
+;; RTE
+(define_insn_reservation "sh4_300_return_from_exp" 9
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "rte"))
+ "sh4_300_pipe_01+sh4_300_pipe_02*9")
+
+;; OCBP, OCBWB
+;; Group: CO
+;; Latency: 1-5
+;; Issue Rate: 1
+
+;; cwb is used for the sequence ocbwb @%0; extu.w %0,%2; or %1,%2; mov.l %0,@%2
+;; This description is likely inexact, but this pattern should not actually
+;; appear when compiling for sh4-300; we should use isbi instead.
+;; If a -mtune option is added later, we should use the icache array
+;; dispatch method instead.
+(define_insn_reservation "sh4_300_ocbwb" 3
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "cwb"))
+ "all*3")
+
+;; JSR,BSR,BSRF
+;; Calls have a mandatory delay slot, which we'd like to fill with an insn
+;; that can be paired with the call itself.
+;; Scheduling runs before reorg, so we approximate this by saying that we
+;; want the call to be paired with a preceding insn.
+;; In most cases, the insn that loads the address of the call should have
+;; a nonzero latency (mov rn,rm doesn't make sense since we could use rn
+;; for the address then). Thus, a preceding insn that can be paired with
+;; a call should be eligible for the delay slot.
+;;
+;; calls introduce a longisch delay that is likely to flush the pipelines
+;; of the caller's instructions. Ordinary functions tend to end with a
+;; load to restore a register (in the delay slot of rts), while sfuncs
+;; tend to end with an EX or MT insn. But that is not actually relevant,
+;; since there are no instructions that contend for memory access early.
+;; We could, of course, provide exact scheduling information for specific
+;; sfuncs, if that should prove useful.
+
+(define_insn_reservation "sh4_300_call" 16
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "call,sfunc"))
+ "sh4_300_issue+sh4_300_br,all*15")
+
+;; FMOV.S / FMOV.D
+(define_insn_reservation "sh4_300_fmov" 1
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "fmove"))
+ "sh4_300_issue+sh4_300_fpt")
+
+;; LDS to FPSCR
+(define_insn_reservation "sh4_300_fpscr_load" 8
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "gp_fpscr"))
+ "sh4_300_issue+sh4_300_fpu+sh4_300_fpt")
+
+;; LDS.L to FPSCR
+(define_insn_reservation "sh4_300_fpscr_load_mem" 8
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "mem_fpscr"))
+ "sh4_300_issue+sh4_300_fpu+sh4_300_fpt+sh4_300_ls")
+
+
+;; Fixed point multiplication (DMULS.L DMULU.L MUL.L MULS.W,MULU.W)
+(define_insn_reservation "multi" 2
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "smpy,dmpy"))
+ "sh4_300_issue+sh4_300_mul")
+
+;; FPCHG, FRCHG, FSCHG
+(define_insn_reservation "fpscr_toggle" 1
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "fpscr_toggle"))
+ "sh4_300_issue+sh4_300_fpu+sh4_300_fpt")
+
+;; FCMP/EQ, FCMP/GT
+(define_insn_reservation "fp_cmp" 3
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "fp_cmp,dfp_cmp"))
+ "sh4_300_issue+sh4_300_fpu")
+
+;; Single precision floating point (FADD,FLOAT,FMAC,FMUL,FSUB,FTRC)
+;; Double-precision floating-point (FADD,FCNVDS,FCNVSD,FLOAT,FSUB,FTRC)
+(define_insn_reservation "fp_arith" 6
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "fp,ftrc_s,dfp_arith,dfp_conv"))
+ "sh4_300_issue+sh4_300_fpu")
+
+;; Single Precision FDIV/SQRT
+(define_insn_reservation "fp_div" 19
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "fdiv"))
+ "sh4_300_issue+sh4_300_fpu+sh4_300_fds,sh4_300_fds*15")
+
+;; Double-precision floating-point FMUL
+(define_insn_reservation "dfp_mul" 9
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "dfp_mul"))
+ "sh4_300_issue+sh4_300_fpu,sh4_300_fpu*3")
+
+;; Double precision FDIV/SQRT
+(define_insn_reservation "dp_div" 35
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "dfdiv"))
+ "sh4_300_issue+sh4_300_fpu+sh4_300_fds,sh4_300_fds*31")
+
+
+;; ??? We don't really want these for sh4-300.
+;; this pattern itself is likely to finish in 3 cycles, but also
+;; to disrupt branch prediction for taken branches for the following
+;; condbranch.
+(define_insn_reservation "sh4_300_arith3" 5
+ (and (eq_attr "pipe_model" "sh4_300")
+ (eq_attr "type" "arith3"))
+ "sh4_300_issue,all*4")
+
+;; arith3b insns without brach redirection make use of the 0-offset 0-latency
+;; branch feature, and thus schedule the same no matter if the branch is taken
+;; or not. If the branch is redirected, the taken branch might take longer,
+;; but then, we don't have to take the next branch.
+;; ??? should we suppress branch redirection for sh4-300 to improve branch
+;; target hit rates?
+(define_insn_reservation "arith3b" 2
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "arith3"))
+ "issue,all")
diff --git a/gcc/config/sh/sh4.md b/gcc/config/sh/sh4.md
new file mode 100644
index 000000000..0fb4a9aec
--- /dev/null
+++ b/gcc/config/sh/sh4.md
@@ -0,0 +1,486 @@
+;; DFA scheduling description for SH4.
+;; Copyright (C) 2004, 2006, 2007 Free Software Foundation, Inc.
+
+;; This file is part of GCC.
+
+;; GCC is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 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
+;; <http://www.gnu.org/licenses/>.
+
+;; Load and store instructions save a cycle if they are aligned on a
+;; four byte boundary. Using a function unit for stores encourages
+;; gcc to separate load and store instructions by one instruction,
+;; which makes it more likely that the linker will be able to word
+;; align them when relaxing.
+
+;; The following description models the SH4 pipeline using the DFA based
+;; scheduler. The DFA based description is better way to model a
+;; superscalar pipeline as compared to function unit reservation model.
+;; 1. The function unit based model is oriented to describe at most one
+;; unit reservation by each insn. It is difficult to model unit reservations
+;; in multiple pipeline units by same insn. This can be done using DFA
+;; based description.
+;; 2. The execution performance of DFA based scheduler does not depend on
+;; processor complexity.
+;; 3. Writing all unit reservations for an instruction class is a more natural
+;; description of the pipeline and makes the interface to the hazard
+;; recognizer simpler than the old function unit based model.
+;; 4. The DFA model is richer and is a part of greater overall framework
+;; of RCSP.
+
+
+;; Two automata are defined to reduce number of states
+;; which a single large automaton will have. (Factoring)
+
+(define_automaton "inst_pipeline,fpu_pipe")
+
+;; This unit is basically the decode unit of the processor.
+;; Since SH4 is a dual issue machine,it is as if there are two
+;; units so that any insn can be processed by either one
+;; of the decoding unit.
+
+(define_cpu_unit "pipe_01,pipe_02" "inst_pipeline")
+
+
+;; The fixed point arithmetic calculator(?? EX Unit).
+
+(define_cpu_unit "int" "inst_pipeline")
+
+;; f1_1 and f1_2 are floating point units.Actually there is
+;; a f1 unit which can overlap with other f1 unit but
+;; not another F1 unit.It is as though there were two
+;; f1 units.
+
+(define_cpu_unit "f1_1,f1_2" "fpu_pipe")
+
+;; The floating point units (except FS - F2 always precedes it.)
+
+(define_cpu_unit "F0,F1,F2,F3" "fpu_pipe")
+
+;; This is basically the MA unit of SH4
+;; used in LOAD/STORE pipeline.
+
+(define_cpu_unit "memory" "inst_pipeline")
+
+;; However, there are LS group insns that don't use it, even ones that
+;; complete in 0 cycles. So we use an extra unit for the issue of LS insns.
+(define_cpu_unit "load_store" "inst_pipeline")
+
+;; The address calculator used for branch instructions.
+;; This will be reserved after "issue" of branch instructions
+;; and this is to make sure that no two branch instructions
+;; can be issued in parallel.
+
+(define_cpu_unit "pcr_addrcalc" "inst_pipeline")
+
+;; ----------------------------------------------------
+;; This reservation is to simplify the dual issue description.
+
+(define_reservation "issue" "pipe_01|pipe_02")
+
+;; This is to express the locking of D stage.
+;; Note that the issue of a CO group insn also effectively locks the D stage.
+
+(define_reservation "d_lock" "pipe_01+pipe_02")
+
+;; Every FE instruction but fipr / ftrv starts with issue and this.
+(define_reservation "F01" "F0+F1")
+
+;; This is to simplify description where F1,F2,FS
+;; are used simultaneously.
+
+(define_reservation "fpu" "F1+F2")
+
+;; This is to highlight the fact that f1
+;; cannot overlap with F1.
+
+(exclusion_set "f1_1,f1_2" "F1")
+
+(define_insn_reservation "nil" 0 (eq_attr "type" "nil") "nothing")
+
+;; Although reg moves have a latency of zero
+;; we need to highlight that they use D stage
+;; for one cycle.
+
+;; Group: MT
+
+(define_insn_reservation "reg_mov" 0
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "move"))
+ "issue")
+
+;; Group: LS
+
+(define_insn_reservation "freg_mov" 0
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "fmove"))
+ "issue+load_store")
+
+;; We don't model all pipeline stages; we model the issue ('D') stage
+;; inasmuch as we allow only two instructions to issue simultaneously,
+;; and CO instructions prevent any simultaneous issue of another instruction.
+;; (This uses pipe_01 and pipe_02).
+;; Double issue of EX insns is prevented by using the int unit in the EX stage.
+;; Double issue of EX / BR insns is prevented by using the int unit /
+;; pcr_addrcalc unit in the EX stage.
+;; Double issue of BR / LS instructions is prevented by using the
+;; pcr_addrcalc / load_store unit in the issue cycle.
+;; Double issue of FE instructions is prevented by using F0 in the first
+;; pipeline stage after the first D stage.
+;; There is no need to describe the [ES]X / [MN]A / S stages after a D stage
+;; (except in the cases outlined above), nor to describe the FS stage after
+;; the F2 stage.
+
+;; Other MT group instructions(1 step operations)
+;; Group: MT
+;; Latency: 1
+;; Issue Rate: 1
+
+(define_insn_reservation "mt" 1
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "mt_group"))
+ "issue")
+
+;; Fixed Point Arithmetic Instructions(1 step operations)
+;; Group: EX
+;; Latency: 1
+;; Issue Rate: 1
+
+(define_insn_reservation "sh4_simple_arith" 1
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "insn_class" "ex_group"))
+ "issue,int")
+
+;; Load and store instructions have no alignment peculiarities for the SH4,
+;; but they use the load-store unit, which they share with the fmove type
+;; insns (fldi[01]; fmov frn,frm; flds; fsts; fabs; fneg) .
+;; Loads have a latency of two.
+;; However, call insns can only paired with a preceding insn, and have
+;; a delay slot, so that we want two more insns to be scheduled between the
+;; load of the function address and the call. This is equivalent to a
+;; latency of three.
+;; ADJUST_COST can only properly handle reductions of the cost, so we
+;; use a latency of three here, which gets multiplied by 10 to yield 30.
+;; We only do this for SImode loads of general registers, to make the work
+;; for ADJUST_COST easier.
+
+;; Load Store instructions. (MOV.[BWL]@(d,GBR)
+;; Group: LS
+;; Latency: 2
+;; Issue Rate: 1
+
+(define_insn_reservation "sh4_load" 2
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "load,pcload"))
+ "issue+load_store,nothing,memory")
+
+;; calls / sfuncs need an extra instruction for their delay slot.
+;; Moreover, estimating the latency for SImode loads as 3 will also allow
+;; adjust_cost to meaningfully bump it back up to 3 if they load the shift
+;; count of a dynamic shift.
+(define_insn_reservation "sh4_load_si" 3
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "load_si,pcload_si"))
+ "issue+load_store,nothing,memory")
+
+;; (define_bypass 2 "sh4_load_si" "!sh4_call")
+
+;; The load latency is upped to three higher if the dependent insn does
+;; double precision computation. We want the 'default' latency to reflect
+;; that increased latency because otherwise the insn priorities won't
+;; allow proper scheduling.
+(define_insn_reservation "sh4_fload" 3
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "fload,pcfload"))
+ "issue+load_store,nothing,memory")
+
+;; (define_bypass 2 "sh4_fload" "!")
+
+(define_insn_reservation "sh4_store" 1
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "store,fstore"))
+ "issue+load_store,nothing,memory")
+
+(define_insn_reservation "mac_mem" 1
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "mac_mem"))
+ "d_lock,nothing,memory")
+
+;; Load Store instructions.
+;; Group: LS
+;; Latency: 1
+;; Issue Rate: 1
+
+(define_insn_reservation "sh4_gp_fpul" 1
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "gp_fpul"))
+ "issue+load_store")
+
+;; Load Store instructions.
+;; Group: LS
+;; Latency: 3
+;; Issue Rate: 1
+
+(define_insn_reservation "sh4_fpul_gp" 3
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "fpul_gp"))
+ "issue+load_store")
+
+;; Branch (BF,BF/S,BT,BT/S,BRA)
+;; Group: BR
+;; Latency when taken: 2 (or 1)
+;; Issue Rate: 1
+;; The latency is 1 when displacement is 0.
+;; We can't really do much with the latency, even if we could express it,
+;; but the pairing restrictions are useful to take into account.
+;; ??? If the branch is likely, we might want to fill the delay slot;
+;; if the branch is likely, but not very likely, should we pretend to use
+;; a resource that CO instructions use, to get a pairable delay slot insn?
+
+(define_insn_reservation "sh4_branch" 1
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "cbranch,jump"))
+ "issue+pcr_addrcalc")
+
+;; Branch Far (JMP,RTS,BRAF)
+;; Group: CO
+;; Latency: 3
+;; Issue Rate: 2
+;; ??? Scheduling happens before branch shortening, and hence jmp and braf
+;; can't be distinguished from bra for the "jump" pattern.
+
+(define_insn_reservation "sh4_return" 3
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "return,jump_ind"))
+ "d_lock*2")
+
+;; RTE
+;; Group: CO
+;; Latency: 5
+;; Issue Rate: 5
+;; this instruction can be executed in any of the pipelines
+;; and blocks the pipeline for next 4 stages.
+
+(define_insn_reservation "sh4_return_from_exp" 5
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "rte"))
+ "d_lock*5")
+
+;; OCBP, OCBWB
+;; Group: CO
+;; Latency: 1-5
+;; Issue Rate: 1
+
+;; cwb is used for the sequence ocbwb @%0; extu.w %0,%2; or %1,%2; mov.l %0,@%2
+;; ocbwb on its own would be "d_lock,nothing,memory*5"
+(define_insn_reservation "ocbwb" 6
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "cwb"))
+ "d_lock*2,(d_lock+memory)*3,issue+load_store+memory,memory*2")
+
+;; LDS to PR,JSR
+;; Group: CO
+;; Latency: 3
+;; Issue Rate: 2
+;; The SX stage is blocked for last 2 cycles.
+;; OTOH, the only time that has an effect for insns generated by the compiler
+;; is when lds to PR is followed by sts from PR - and that is highly unlikely -
+;; or when we are doing a function call - and we don't do inter-function
+;; scheduling. For the function call case, it's really best that we end with
+;; something that models an rts.
+
+(define_insn_reservation "sh4_lds_to_pr" 3
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "prset") )
+ "d_lock*2")
+
+;; calls introduce a longisch delay that is likely to flush the pipelines
+;; of the caller's instructions. Ordinary functions tend to end with a
+;; load to restore a register (in the delay slot of rts), while sfuncs
+;; tend to end with an EX or MT insn. But that is not actually relevant,
+;; since there are no instructions that contend for memory access early.
+;; We could, of course, provide exact scheduling information for specific
+;; sfuncs, if that should prove useful.
+
+(define_insn_reservation "sh4_call" 16
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "call,sfunc"))
+ "d_lock*16")
+
+;; LDS.L to PR
+;; Group: CO
+;; Latency: 3
+;; Issue Rate: 2
+;; The SX unit is blocked for last 2 cycles.
+
+(define_insn_reservation "ldsmem_to_pr" 3
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "pload"))
+ "d_lock*2")
+
+;; STS from PR
+;; Group: CO
+;; Latency: 2
+;; Issue Rate: 2
+;; The SX unit in second and third cycles.
+
+(define_insn_reservation "sts_from_pr" 2
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "prget"))
+ "d_lock*2")
+
+;; STS.L from PR
+;; Group: CO
+;; Latency: 2
+;; Issue Rate: 2
+
+(define_insn_reservation "sh4_prstore_mem" 2
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "pstore"))
+ "d_lock*2,nothing,memory")
+
+;; LDS to FPSCR
+;; Group: CO
+;; Latency: 4
+;; Issue Rate: 1
+;; F1 is blocked for last three cycles.
+
+(define_insn_reservation "fpscr_load" 4
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "gp_fpscr"))
+ "d_lock,nothing,F1*3")
+
+;; LDS.L to FPSCR
+;; Group: CO
+;; Latency: 1 / 4
+;; Latency to update Rn is 1 and latency to update FPSCR is 4
+;; Issue Rate: 1
+;; F1 is blocked for last three cycles.
+
+(define_insn_reservation "fpscr_load_mem" 4
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "mem_fpscr"))
+ "d_lock,nothing,(F1+memory),F1*2")
+
+
+;; Fixed point multiplication (DMULS.L DMULU.L MUL.L MULS.W,MULU.W)
+;; Group: CO
+;; Latency: 4 / 4
+;; Issue Rate: 2
+
+(define_insn_reservation "multi" 4
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "smpy,dmpy"))
+ "d_lock,(d_lock+f1_1),(f1_1|f1_2)*3,F2")
+
+;; Fixed STS from, and LDS to MACL / MACH
+;; Group: CO
+;; Latency: 3
+;; Issue Rate: 1
+
+(define_insn_reservation "sh4_mac_gp" 3
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "mac_gp,gp_mac,mem_mac"))
+ "d_lock")
+
+
+;; Single precision floating point computation FCMP/EQ,
+;; FCMP/GT, FADD, FLOAT, FMAC, FMUL, FSUB, FTRC, FRCHG, FSCHG
+;; Group: FE
+;; Latency: 3/4
+;; Issue Rate: 1
+
+(define_insn_reservation "fp_arith" 3
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "fp,fp_cmp"))
+ "issue,F01,F2")
+
+;; We don't model the resource usage of this exactly because that would
+;; introduce a bogus latency.
+(define_insn_reservation "sh4_fpscr_toggle" 1
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "fpscr_toggle"))
+ "issue")
+
+(define_insn_reservation "fp_arith_ftrc" 3
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "ftrc_s"))
+ "issue,F01,F2")
+
+(define_bypass 1 "fp_arith_ftrc" "sh4_fpul_gp")
+
+;; Single Precision FDIV/SQRT
+;; Group: FE
+;; Latency: 12/13 (FDIV); 11/12 (FSQRT)
+;; Issue Rate: 1
+;; We describe fdiv here; fsqrt is actually one cycle faster.
+
+(define_insn_reservation "fp_div" 12
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "fdiv"))
+ "issue,F01+F3,F2+F3,F3*7,F1+F3,F2")
+
+;; Double Precision floating point computation
+;; (FCNVDS, FCNVSD, FLOAT, FTRC)
+;; Group: FE
+;; Latency: (3,4)/5
+;; Issue Rate: 1
+
+(define_insn_reservation "dp_float" 4
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "dfp_conv"))
+ "issue,F01,F1+F2,F2")
+
+;; Double-precision floating-point (FADD,FMUL,FSUB)
+;; Group: FE
+;; Latency: (7,8)/9
+;; Issue Rate: 1
+
+(define_insn_reservation "fp_double_arith" 8
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "dfp_arith,dfp_mul"))
+ "issue,F01,F1+F2,fpu*4,F2")
+
+;; Double-precision FCMP (FCMP/EQ,FCMP/GT)
+;; Group: CO
+;; Latency: 3/5
+;; Issue Rate: 2
+
+(define_insn_reservation "fp_double_cmp" 3
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "dfp_cmp"))
+ "d_lock,(d_lock+F01),F1+F2,F2")
+
+;; Double precision FDIV/SQRT
+;; Group: FE
+;; Latency: (24,25)/26
+;; Issue Rate: 1
+
+(define_insn_reservation "dp_div" 25
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "dfdiv"))
+ "issue,F01+F3,F1+F2+F3,F2+F3,F3*16,F1+F3,(fpu+F3)*2,F2")
+
+
+;; Use the branch-not-taken case to model arith3 insns. For the branch taken
+;; case, we'd get a d_lock instead of issue at the end.
+(define_insn_reservation "arith3" 3
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "arith3"))
+ "issue,d_lock+pcr_addrcalc,issue")
+
+;; arith3b insns schedule the same no matter if the branch is taken or not.
+(define_insn_reservation "arith3b" 2
+ (and (eq_attr "pipe_model" "sh4")
+ (eq_attr "type" "arith3"))
+ "issue,d_lock+pcr_addrcalc")
diff --git a/gcc/config/sh/sh4a.md b/gcc/config/sh/sh4a.md
new file mode 100644
index 000000000..75f239f53
--- /dev/null
+++ b/gcc/config/sh/sh4a.md
@@ -0,0 +1,236 @@
+;; Scheduling description for Renesas SH4a
+;; Copyright (C) 2003, 2004, 2006, 2007 Free Software Foundation, Inc.
+;;
+;; This file is part of GCC.
+;;
+;; 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 3, 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 GCC; see the file COPYING3. If not see
+;; <http://www.gnu.org/licenses/>.
+
+;; The following description models the SH4A pipeline
+;; using the DFA based scheduler.
+
+(define_automaton "sh4a")
+
+(define_cpu_unit "sh4a_ex" "sh4a")
+(define_cpu_unit "sh4a_ls" "sh4a")
+(define_cpu_unit "sh4a_fex" "sh4a")
+(define_cpu_unit "sh4a_fls" "sh4a")
+(define_cpu_unit "sh4a_mult" "sh4a")
+(define_cpu_unit "sh4a_fdiv" "sh4a")
+
+;; Decoding is done on the integer pipeline like the
+;; sh4. Define issue to be the | of the two pipelines
+;; to control how often instructions are issued.
+(define_reservation "ID_or" "sh4a_ex|sh4a_ls")
+(define_reservation "ID_and" "sh4a_ex+sh4a_ls")
+
+
+;; =======================================================
+;; Locking Descriptions
+
+;; Sh4a_Memory access on the LS pipeline.
+(define_cpu_unit "sh4a_memory" "sh4a")
+
+;; Other access on the LS pipeline.
+(define_cpu_unit "sh4a_load_store" "sh4a")
+
+;; The address calculator used for branch instructions.
+;; This will be reserved after "issue" of branch instructions
+;; and this is to make sure that no two branch instructions
+;; can be issued in parallel.
+(define_reservation "sh4a_addrcalc" "sh4a_ex")
+
+;; =======================================================
+;; Reservations
+
+;; Branch (BF,BF/S,BT,BT/S,BRA,BSR)
+;; Group: BR
+;; Latency when taken: 2
+(define_insn_reservation "sh4a_branch" 2
+ (and (eq_attr "cpu" "sh4a")
+ (eq_attr "type" "cbranch,jump"))
+ "ID_or+sh4a_addrcalc")
+
+;; Jump (JSR,JMP,RTS)
+;; Group: BR
+;; Latency: 3
+(define_insn_reservation "sh4a_jump" 3
+ (and (eq_attr "cpu" "sh4a")
+ (eq_attr "type" "return,jump_ind"))
+ "ID_or+sh4a_addrcalc")
+
+;; RTE
+;; Group: CO
+;; Latency: 3
+(define_insn_reservation "sh4a_rte" 3
+ (and (eq_attr "cpu" "sh4a")
+ (eq_attr "type" "rte"))
+ "ID_and*4")
+
+;; EX Group Single
+;; Group: EX
+;; Latency: 0
+(define_insn_reservation "sh4a_ex" 0
+ (and (eq_attr "cpu" "sh4a")
+ (eq_attr "insn_class" "ex_group"))
+ "sh4a_ex")
+
+;; MOVA
+;; Group: LS
+;; Latency: 1
+(define_insn_reservation "sh4a_mova" 1
+ (and (eq_attr "cpu" "sh4a")
+ (eq_attr "type" "mova"))
+ "sh4a_ls+sh4a_load_store")
+
+;; MOV
+;; Group: MT
+;; Latency: 0
+;; ??? not sure if movi8 belongs here, but that's where it was
+;; effectively before.
+(define_insn_reservation "sh4a_mov" 0
+ (and (eq_attr "cpu" "sh4a")
+ (eq_attr "type" "move,movi8,gp_mac"))
+ "ID_or")
+
+;; Load
+;; Group: LS
+;; Latency: 3
+(define_insn_reservation "sh4a_load" 3
+ (and (eq_attr "cpu" "sh4a")
+ (eq_attr "type" "load,pcload,mem_mac"))
+ "sh4a_ls+sh4a_memory")
+
+(define_insn_reservation "sh4a_load_si" 3
+ (and (eq_attr "cpu" "sh4a")
+ (eq_attr "type" "load_si,pcload_si"))
+ "sh4a_ls+sh4a_memory")
+
+;; Store
+;; Group: LS
+;; Latency: 0
+(define_insn_reservation "sh4a_store" 0
+ (and (eq_attr "cpu" "sh4a")
+ (eq_attr "type" "store,fstore,mac_mem"))
+ "sh4a_ls+sh4a_memory")
+
+;; CWB TYPE
+
+;; MOVUA
+;; Group: LS
+;; Latency: 3
+(define_insn_reservation "sh4a_movua" 3
+ (and (eq_attr "cpu" "sh4a")
+ (eq_attr "type" "movua"))
+ "sh4a_ls+sh4a_memory*2")
+
+;; Fixed point multiplication (single)
+;; Group: CO
+;; Latency: 2
+(define_insn_reservation "sh4a_smult" 2
+ (and (eq_attr "cpu" "sh4a")
+ (eq_attr "type" "smpy"))
+ "ID_or+sh4a_mult")
+
+;; Fixed point multiplication (double)
+;; Group: CO
+;; Latency: 3
+(define_insn_reservation "sh4a_dmult" 3
+ (and (eq_attr "cpu" "sh4a")
+ (eq_attr "type" "dmpy"))
+ "ID_or+sh4a_mult")
+
+(define_insn_reservation "sh4a_mac_gp" 3
+ (and (eq_attr "cpu" "sh4a")
+ (eq_attr "type" "mac_gp"))
+ "ID_and")
+
+;; Other MT group instructions(1 step operations)
+;; Group: MT
+;; Latency: 1
+(define_insn_reservation "sh4a_mt" 1
+ (and (eq_attr "cpu" "sh4a")
+ (eq_attr "type" "mt_group"))
+ "ID_or")
+
+;; Floating point reg move
+;; Group: LS
+;; Latency: 2
+(define_insn_reservation "sh4a_freg_mov" 2
+ (and (eq_attr "cpu" "sh4a")
+ (eq_attr "type" "fmove"))
+ "sh4a_ls,sh4a_fls")
+
+;; Single precision floating point computation FCMP/EQ,
+;; FCMP/GT, FADD, FLOAT, FMAC, FMUL, FSUB, FTRC, FRVHG, FSCHG
+;; Group: FE
+;; Latency: 3
+(define_insn_reservation "sh4a_fp_arith" 3
+ (and (eq_attr "cpu" "sh4a")
+ (eq_attr "type" "fp,fp_cmp,fpscr_toggle"))
+ "ID_or,sh4a_fex")
+
+(define_insn_reservation "sh4a_fp_arith_ftrc" 3
+ (and (eq_attr "cpu" "sh4a")
+ (eq_attr "type" "ftrc_s"))
+ "ID_or,sh4a_fex")
+
+;; Single-precision FDIV/FSQRT
+;; Group: FE
+;; Latency: 20
+(define_insn_reservation "sh4a_fdiv" 20
+ (and (eq_attr "cpu" "sh4a")
+ (eq_attr "type" "fdiv"))
+ "ID_or,sh4a_fex+sh4a_fdiv,sh4a_fex")
+
+;; Double Precision floating point computation
+;; (FCNVDS, FCNVSD, FLOAT, FTRC)
+;; Group: FE
+;; Latency: 3
+(define_insn_reservation "sh4a_dp_float" 3
+ (and (eq_attr "cpu" "sh4a")
+ (eq_attr "type" "dfp_conv"))
+ "ID_or,sh4a_fex")
+
+;; Double-precision floating-point (FADD,FMUL,FSUB)
+;; Group: FE
+;; Latency: 5
+(define_insn_reservation "sh4a_fp_double_arith" 5
+ (and (eq_attr "cpu" "sh4a")
+ (eq_attr "type" "dfp_arith,dfp_mul"))
+ "ID_or,sh4a_fex*3")
+
+;; Double precision FDIV/SQRT
+;; Group: FE
+;; Latency: 36
+(define_insn_reservation "sh4a_dp_div" 36
+ (and (eq_attr "cpu" "sh4a")
+ (eq_attr "type" "dfdiv"))
+ "ID_or,sh4a_fex+sh4a_fdiv,sh4a_fex*2")
+
+;; FSRRA
+;; Group: FE
+;; Latency: 5
+(define_insn_reservation "sh4a_fsrra" 5
+ (and (eq_attr "cpu" "sh4a")
+ (eq_attr "type" "fsrra"))
+ "ID_or,sh4a_fex")
+
+;; FSCA
+;; Group: FE
+;; Latency: 7
+(define_insn_reservation "sh4a_fsca" 7
+ (and (eq_attr "cpu" "sh4a")
+ (eq_attr "type" "fsca"))
+ "ID_or,sh4a_fex*3")
diff --git a/gcc/config/sh/sh64.h b/gcc/config/sh/sh64.h
new file mode 100644
index 000000000..c954d72ca
--- /dev/null
+++ b/gcc/config/sh/sh64.h
@@ -0,0 +1,26 @@
+/* Definitions of target machine for GNU compiler for SuperH SH 5.
+ Copyright 2000, 2001, 2002, 2003, 2004, 2007 Free Software Foundation, Inc.
+ Contributed by Alexandre Oliva <aoliva@redhat.com>
+
+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
+<http://www.gnu.org/licenses/>. */
+
+#undef TARGET_VERSION
+#define TARGET_VERSION \
+ fputs (" (SuperH SH)", stderr);
+
+#undef SH_ELF_WCHAR_TYPE
+#define SH_ELF_WCHAR_TYPE "int"
diff --git a/gcc/config/sh/shmedia.h b/gcc/config/sh/shmedia.h
new file mode 100644
index 000000000..d78a5e573
--- /dev/null
+++ b/gcc/config/sh/shmedia.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2000, 2009 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef _SHMEDIA_H
+#define _SHMEDIA_H
+
+#include <ushmedia.h>
+#include <sshmedia.h>
+
+#endif
diff --git a/gcc/config/sh/shmedia.md b/gcc/config/sh/shmedia.md
new file mode 100644
index 000000000..47c1ce694
--- /dev/null
+++ b/gcc/config/sh/shmedia.md
@@ -0,0 +1,94 @@
+;; DFA scheduling description for SH-5 SHmedia instructions.
+;; Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
+
+;; This file is part of GCC.
+
+;; GCC is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 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
+;; <http://www.gnu.org/licenses/>.
+
+;; This is just a conversion of the old model using define_function_unit.
+
+;; When executing SHmedia code, the SH-5 is a fairly straightforward
+;; single-issue machine. It has four pipelines, the branch unit (br),
+;; the integer and multimedia unit (imu), the load/store unit (lsu), and
+;; the floating point unit (fpu).
+
+(define_automaton "sh5inst_pipe, sh5fpu_pipe")
+
+(define_cpu_unit "sh5issue" "sh5inst_pipe")
+
+(define_cpu_unit "sh5fds" "sh5fpu_pipe")
+
+;; Every instruction on SH-5 occupies the issue resource for at least one
+;; cycle.
+(define_insn_reservation "shmedia1" 1
+ (and (eq_attr "pipe_model" "sh5media")
+ (eq_attr "type" "!pt_media,ptabs_media,invalidate_line_media,dmpy_media,load_media,fload_media,fcmp_media,fmove_media,fparith_media,dfparith_media,fpconv_media,dfpconv_media,dfmul_media,store_media,fstore_media,mcmp_media,mac_media,d2mpy_media,atrans_media,ustore_media"))
+ "sh5issue")
+
+;; Specify the various types of instruction which have latency > 1
+(define_insn_reservation "shmedia2" 2
+ (and (eq_attr "pipe_model" "sh5media")
+ (eq_attr "type" "mcmp_media"))
+ "sh5issue")
+
+(define_insn_reservation "shmedia3" 3
+ (and (eq_attr "pipe_model" "sh5media")
+ (eq_attr "type" "dmpy_media,load_media,fcmp_media,mac_media"))
+ "sh5issue")
+;; but see sh_adjust_cost for mac_media exception.
+
+(define_insn_reservation "shmedia4" 4
+ (and (eq_attr "pipe_model" "sh5media")
+ (eq_attr "type" "fload_media,fmove_media"))
+ "sh5issue")
+
+(define_insn_reservation "shmedia_d2mpy" 4
+ (and (eq_attr "pipe_model" "sh5media")
+ (eq_attr "type" "d2mpy_media"))
+ "sh5issue*2")
+
+(define_insn_reservation "shmedia5" 5
+ (and (eq_attr "pipe_model" "sh5media")
+ (eq_attr "type" "pt_media,ptabs_media"))
+ "sh5issue")
+
+(define_insn_reservation "shmedia6" 6
+ (and (eq_attr "pipe_model" "sh5media")
+ (eq_attr "type" "fparith_media,dfparith_media,fpconv_media,dfpconv_media"))
+ "sh5issue")
+
+(define_insn_reservation "shmedia_invalidate" 7
+ (and (eq_attr "pipe_model" "sh5media")
+ (eq_attr "type" "invalidate_line_media"))
+ "sh5issue*7")
+
+(define_insn_reservation "shmedia_dfmul" 9
+ (and (eq_attr "pipe_model" "sh5media") (eq_attr "type" "dfmul_media"))
+ "sh5issue*4")
+
+(define_insn_reservation "shmedia_atrans" 10
+ (and (eq_attr "pipe_model" "sh5media") (eq_attr "type" "atrans_media"))
+ "sh5issue*5")
+
+;; Floating-point divide and square-root occupy an additional resource,
+;; which is not internally pipelined. However, other instructions
+;; can continue to issue.
+(define_insn_reservation "shmedia_fdiv" 19
+ (and (eq_attr "pipe_model" "sh5media") (eq_attr "type" "fdiv_media"))
+ "sh5issue+sh5fds,sh5fds*18")
+
+(define_insn_reservation "shmedia_dfdiv" 35
+ (and (eq_attr "pipe_model" "sh5media") (eq_attr "type" "dfdiv_media"))
+ "sh5issue+sh5fds,sh5fds*34")
diff --git a/gcc/config/sh/sshmedia.h b/gcc/config/sh/sshmedia.h
new file mode 100644
index 000000000..f8245042a
--- /dev/null
+++ b/gcc/config/sh/sshmedia.h
@@ -0,0 +1,78 @@
+/* Copyright (C) 2000, 2001, 2009 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+
+/* sshmedia.h: Intrinsics corresponding to SHmedia instructions that
+ may only be executed in privileged mode. */
+
+#ifndef _SSHMEDIA_H
+#define _SSHMEDIA_H
+
+#if __SHMEDIA__
+__inline__ static unsigned long long sh_media_GETCON (unsigned int k)
+ __attribute__((always_inline));
+
+__inline__ static
+unsigned long long
+sh_media_GETCON (unsigned int k)
+{
+ unsigned long long res;
+ __asm__ __volatile__ ("getcon cr%1, %0" : "=r" (res) : "n" (k));
+ return res;
+}
+
+__inline__ static void sh_media_PUTCON (unsigned long long mm, unsigned int k)
+ __attribute__((always_inline));
+
+__inline__ static
+void
+sh_media_PUTCON (unsigned long long mm, unsigned int k)
+{
+ __asm__ __volatile__ ("putcon %0, cr%1" : : "r" (mm), "n" (k));
+}
+
+__inline__ static
+unsigned long long
+sh_media_GETCFG (unsigned long long mm, int s)
+{
+ unsigned long long res;
+ __asm__ __volatile__ ("getcfg %1, %2, %0" : "=r" (res) : "r" (mm), "n" (s));
+ return res;
+}
+
+__inline__ static
+void
+sh_media_PUTCFG (unsigned long long mm, int s, unsigned long long mw)
+{
+ __asm__ __volatile__ ("putcfg %0, %1, %2" : : "r" (mm), "n" (s), "r" (mw));
+}
+
+__inline__ static
+void
+sh_media_SLEEP (void)
+{
+ __asm__ __volatile__ ("sleep");
+}
+#endif
+
+#endif
diff --git a/gcc/config/sh/superh.h b/gcc/config/sh/superh.h
new file mode 100644
index 000000000..88920739e
--- /dev/null
+++ b/gcc/config/sh/superh.h
@@ -0,0 +1,107 @@
+/* Definitions of target machine for gcc for Super-H using sh-superh-elf.
+ Copyright (C) 2001, 2006, 2007 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, 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 GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+
+/* This header file is used when the vendor name is set to 'superh'.
+ config.gcc already configured the compiler for SH4 only and switched
+ the default endianess to little (although big endian is still available).
+ This file configures the spec file to the default board configuration
+ but in such a way that it can be overridden by a boardspecs file
+ (using the -specs= option). This file is expected to disable the
+ defaults and provide options --defsym _start and --defsym _stack
+ which are required by the SuperH configuration of GNU ld.
+
+ This file is intended to override sh.h. */
+
+
+#ifndef _SUPERH_H
+#define _SUPERH_H
+#endif
+
+
+#undef TARGET_VERSION
+#define TARGET_VERSION fprintf (stderr, " (SuperH SH special %s)", __DATE__);
+
+/* Override the linker spec strings to use the new emulation
+ The specstrings are concatenated as follows
+ LINK_EMUL_PREFIX.(''|'32'|'64'|LINK_DEFAULT_CPU_EMUL).SUBTARGET_LINK_EMUL_SUFFIX
+*/
+#undef LINK_EMUL_PREFIX
+#undef SUBTARGET_LINK_EMUL_SUFFIX
+
+#define LINK_EMUL_PREFIX "superh"
+#define SUBTARGET_LINK_EMUL_SUFFIX ""
+
+/* Add the SUBTARGET_LINK_SPEC to add the board and runtime support and
+ change the endianness */
+#undef SUBTARGET_LINK_SPEC
+#if TARGET_ENDIAN_DEFAULT == MASK_LITTLE_ENDIAN
+#define SUBTARGET_LINK_SPEC "%(board_link) %(ldruntime) %{ml|!mb:-EL}%{mb:-EB}"
+#else
+#define SUBTARGET_LINK_SPEC "%(board_link) %(ldruntime) %{ml:-EL}%{mb|!ml:-EB}"
+#endif
+
+
+/* This is used by the link spec if the boardspecs file is not used (for whatever reason).
+ If the boardspecs file overrides this then an alternative can be used. */
+#undef SUBTARGET_EXTRA_SPECS
+#define SUBTARGET_EXTRA_SPECS \
+{ "board_link", "--defsym _start=0x1000 --defsym _stack=0x30000" }, \
+{ "asruntime", "" }, \
+{ "cppruntime", "-D__GDB_SIM__" }, \
+{ "cc1runtime", "" }, \
+{ "ldruntime", "" }, \
+{ "libruntime", "-lc -lgloss" }
+
+
+/* Set the SUBTARGET_CPP_SPEC to define __EMBEDDED_CROSS__ which has an effect
+ on newlib and provide the runtime support */
+#undef SUBTARGET_CPP_SPEC
+#define SUBTARGET_CPP_SPEC \
+"-D__EMBEDDED_CROSS__ %{m4-100*:-D__SH4_100__} %{m4-200*:-D__SH4_200__} %{m4-300*:-D__SH4_300__} %{m4-340:-D__SH4_340__} %{m4-400:-D__SH4_400__} %{m4-500:-D__SH4_500__} \
+%(cppruntime)"
+
+/* Override the SUBTARGET_ASM_SPEC to add the runtime support */
+#undef SUBTARGET_ASM_SPEC
+#define SUBTARGET_ASM_SPEC "%{m4-100*|m4-200*:-isa=sh4} %{m4-400|m4-340:-isa=sh4-nommu-nofpu} %{m4-500:-isa=sh4-nofpu} %(asruntime)"
+
+/* Override the SUBTARGET_ASM_RELAX_SPEC so it doesn't interfere with the
+ runtime support by adding -isa=sh4 in the wrong place. */
+#undef SUBTARGET_ASM_RELAX_SPEC
+#define SUBTARGET_ASM_RELAX_SPEC "%{!m4-100*:%{!m4-200*:%{!m4-300*:%{!m4-340:%{!m4-400:%{!m4-500:-isa=sh4}}}}}}"
+
+/* Create the CC1_SPEC to add the runtime support */
+#undef CC1_SPEC
+#define CC1_SPEC "%(cc1runtime)"
+
+#undef CC1PLUS_SPEC
+#define CC1PLUS_SPEC "%(cc1runtime)"
+
+
+/* Override the LIB_SPEC to add the runtime support */
+#undef LIB_SPEC
+#define LIB_SPEC "%{!shared:%{!symbolic:%(libruntime) -lc}} %{pg:-lprofile -lc}"
+
+/* Override STARTFILE_SPEC to add profiling and MMU support. */
+#undef STARTFILE_SPEC
+#define STARTFILE_SPEC \
+ "%{!shared: %{!m4-400*:%{!m4-340*: %{pg:gcrt1-mmu.o%s}%{!pg:crt1-mmu.o%s}}}} \
+ %{!shared: %{m4-340*|m4-400*: %{pg:gcrt1.o%s}%{!pg:crt1.o%s}}} \
+ crti.o%s \
+ %{!shared:crtbegin.o%s} %{shared:crtbeginS.o%s}"
diff --git a/gcc/config/sh/superh.opt b/gcc/config/sh/superh.opt
new file mode 100644
index 000000000..b85abddaf
--- /dev/null
+++ b/gcc/config/sh/superh.opt
@@ -0,0 +1,10 @@
+;; The -mboard and -mruntime options need only be accepted here, they are
+;; actually processed by supplementary specs files.
+
+mboard=
+Target RejectNegative Joined
+Board name [and memory region].
+
+mruntime=
+Target RejectNegative Joined
+Runtime name.
diff --git a/gcc/config/sh/symbian-base.c b/gcc/config/sh/symbian-base.c
new file mode 100644
index 000000000..f8e678be3
--- /dev/null
+++ b/gcc/config/sh/symbian-base.c
@@ -0,0 +1,244 @@
+/* Routines for GCC for a Symbian OS targeted SH backend, shared by
+ both the C and C++ compilers.
+ Copyright (C) 2004, 2005, 2007, 2009, 2010 Free Software Foundation, Inc.
+ Contributed by RedHat.
+ Most of this code is stolen from i386/winnt.c.
+
+ 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
+ <http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "rtl.h"
+#include "output.h"
+#include "flags.h"
+#include "tree.h"
+#include "expr.h"
+#include "tm_p.h"
+#include "diagnostic-core.h"
+#include "sh-symbian.h"
+
+/* Return nonzero if SYMBOL is marked as being dllexport'd. */
+
+bool
+sh_symbian_is_dllexported_name (const char *symbol)
+{
+ return strncmp (DLL_EXPORT_PREFIX, symbol,
+ strlen (DLL_EXPORT_PREFIX)) == 0;
+}
+
+/* Return nonzero if SYMBOL is marked as being dllimport'd. */
+
+static bool
+sh_symbian_is_dllimported_name (const char *symbol)
+{
+ return strncmp (DLL_IMPORT_PREFIX, symbol,
+ strlen (DLL_IMPORT_PREFIX)) == 0;
+}
+
+/* Return nonzero if DECL is a dllexport'd object. */
+
+bool
+sh_symbian_is_dllexported (tree decl)
+{
+ tree exp;
+
+ if ( TREE_CODE (decl) != VAR_DECL
+ && TREE_CODE (decl) != FUNCTION_DECL)
+ return false;
+
+ exp = lookup_attribute ("dllexport", DECL_ATTRIBUTES (decl));
+
+ /* Class members get the dllexport status of their class. */
+ if (exp == NULL)
+ {
+ tree class = sh_symbian_associated_type (decl);
+
+ if (class)
+ exp = lookup_attribute ("dllexport", TYPE_ATTRIBUTES (class));
+ }
+#if SYMBIAN_DEBUG
+ if (exp)
+ {
+ print_node_brief (stderr, "dllexport:", decl, 0);
+ fprintf (stderr, "\n");
+ }
+ else
+#if SYMBIAN_DEBUG < 2
+ if (TREE_CODE (decl) != FUNCTION_DECL)
+#endif
+ {
+ print_node_brief (stderr, "no dllexport:", decl, 0);
+ fprintf (stderr, "\n");
+ }
+#endif
+ return exp ? true : false;
+}
+
+/* Mark a DECL as being dllimport'd. */
+
+static void
+sh_symbian_mark_dllimport (tree decl)
+{
+ const char *oldname;
+ char *newname;
+ tree idp;
+ rtx rtlname;
+ rtx newrtl;
+
+ rtlname = XEXP (DECL_RTL (decl), 0);
+ if (MEM_P (rtlname))
+ rtlname = XEXP (rtlname, 0);
+ gcc_assert (GET_CODE (rtlname) == SYMBOL_REF);
+ oldname = XSTR (rtlname, 0);
+
+ if (sh_symbian_is_dllexported_name (oldname))
+ {
+ error ("%qE declared as both exported to and imported from a DLL",
+ DECL_NAME (decl));
+ }
+ else if (sh_symbian_is_dllimported_name (oldname))
+ {
+ /* Already done, but do a sanity check to prevent assembler errors. */
+ if (!DECL_EXTERNAL (decl) || !TREE_PUBLIC (decl))
+ error ("failure in redeclaration of %q+D: dllimport%'d symbol lacks external linkage",
+ decl);
+ }
+ else
+ {
+ newname = (char *) alloca (strlen (DLL_IMPORT_PREFIX) + strlen (oldname) + 1);
+ sprintf (newname, "%s%s", DLL_IMPORT_PREFIX, oldname);
+
+ /* We pass newname through get_identifier to ensure it has a unique
+ address. RTL processing can sometimes peek inside the symbol ref
+ and compare the string's addresses to see if two symbols are
+ identical. */
+ idp = get_identifier (newname);
+ newrtl = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (idp));
+ XEXP (DECL_RTL (decl), 0) = newrtl;
+ }
+}
+
+/* Mark a DECL as being dllexport'd.
+ Note that we override the previous setting (e.g.: dllimport). */
+
+static void
+sh_symbian_mark_dllexport (tree decl)
+{
+ const char *oldname;
+ char *newname;
+ rtx rtlname;
+ tree idp;
+
+ rtlname = XEXP (DECL_RTL (decl), 0);
+ if (MEM_P (rtlname))
+ rtlname = XEXP (rtlname, 0);
+ gcc_assert (GET_CODE (rtlname) == SYMBOL_REF);
+ oldname = XSTR (rtlname, 0);
+
+ if (sh_symbian_is_dllimported_name (oldname))
+ {
+ /* Remove DLL_IMPORT_PREFIX.
+ Note - we do not issue a warning here. In Symbian's environment it
+ is legitimate for a prototype to be marked as dllimport and the
+ corresponding definition to be marked as dllexport. The prototypes
+ are in headers used everywhere and the definition is in a translation
+ unit which has included the header in order to ensure argument
+ correctness. */
+ oldname += strlen (DLL_IMPORT_PREFIX);
+ DECL_DLLIMPORT_P (decl) = 0;
+ }
+ else if (sh_symbian_is_dllexported_name (oldname))
+ return; /* Already done. */
+
+ newname = (char *) alloca (strlen (DLL_EXPORT_PREFIX) + strlen (oldname) + 1);
+ sprintf (newname, "%s%s", DLL_EXPORT_PREFIX, oldname);
+
+ /* We pass newname through get_identifier to ensure it has a unique
+ address. RTL processing can sometimes peek inside the symbol ref
+ and compare the string's addresses to see if two symbols are
+ identical. */
+ idp = get_identifier (newname);
+
+ XEXP (DECL_RTL (decl), 0) =
+ gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (idp));
+}
+
+void
+sh_symbian_encode_section_info (tree decl, rtx rtl, int first)
+{
+ default_encode_section_info (decl, rtl, first);
+
+ /* Mark the decl so we can tell from the rtl whether
+ the object is dllexport'd or dllimport'd. */
+ if (sh_symbian_is_dllexported (decl))
+ sh_symbian_mark_dllexport (decl);
+ else if (sh_symbian_is_dllimported (decl))
+ sh_symbian_mark_dllimport (decl);
+ /* It might be that DECL has already been marked as dllimport, but a
+ subsequent definition nullified that. The attribute is gone but
+ DECL_RTL still has (DLL_IMPORT_PREFIX) prefixed. We need to remove
+ that. Ditto for the DECL_DLLIMPORT_P flag. */
+ else if ( (TREE_CODE (decl) == FUNCTION_DECL
+ || TREE_CODE (decl) == VAR_DECL)
+ && DECL_RTL (decl) != NULL_RTX
+ && MEM_P (DECL_RTL (decl))
+ && MEM_P (XEXP (DECL_RTL (decl), 0))
+ && GET_CODE (XEXP (XEXP (DECL_RTL (decl), 0), 0)) == SYMBOL_REF
+ && sh_symbian_is_dllimported_name (XSTR (XEXP (XEXP (DECL_RTL (decl), 0), 0), 0)))
+ {
+ const char * oldname = XSTR (XEXP (XEXP (DECL_RTL (decl), 0), 0), 0);
+ /* Remove DLL_IMPORT_PREFIX. */
+ tree idp = get_identifier (oldname + strlen (DLL_IMPORT_PREFIX));
+ rtx newrtl = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (idp));
+
+ warning (0, "%s %q+D %s after being referenced with dllimport linkage",
+ TREE_CODE (decl) == VAR_DECL ? "variable" : "function",
+ decl, (DECL_INITIAL (decl) || !DECL_EXTERNAL (decl))
+ ? "defined locally" : "redeclared without dllimport attribute");
+
+ XEXP (DECL_RTL (decl), 0) = newrtl;
+
+ DECL_DLLIMPORT_P (decl) = 0;
+ }
+}
+
+/* Return the length of a function name prefix
+ that starts with the character 'c'. */
+
+static int
+sh_symbian_get_strip_length (int c)
+{
+ /* XXX Assumes strlen (DLL_EXPORT_PREFIX) == strlen (DLL_IMPORT_PREFIX). */
+ return (c == SH_SYMBIAN_FLAG_CHAR[0]) ? strlen (DLL_EXPORT_PREFIX) : 0;
+}
+
+/* Return a pointer to a function's name with any
+ and all prefix encodings stripped from it. */
+
+const char *
+sh_symbian_strip_name_encoding (const char *name)
+{
+ int skip;
+
+ while ((skip = sh_symbian_get_strip_length (*name)))
+ name += skip;
+
+ return name;
+}
+
diff --git a/gcc/config/sh/symbian-c.c b/gcc/config/sh/symbian-c.c
new file mode 100644
index 000000000..c93922a39
--- /dev/null
+++ b/gcc/config/sh/symbian-c.c
@@ -0,0 +1,181 @@
+/* Routines for C compiler part of GCC for a Symbian OS targeted SH backend.
+ Copyright (C) 2004, 2005, 2007, 2009, 2010 Free Software Foundation, Inc.
+ Contributed by RedHat.
+ Most of this code is stolen from i386/winnt.c.
+
+ 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
+ <http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "rtl.h"
+#include "output.h"
+#include "flags.h"
+#include "tree.h"
+#include "expr.h"
+#include "tm_p.h"
+#include "diagnostic-core.h"
+#include "sh-symbian.h"
+
+
+/* Return the type that we should use to determine if DECL is
+ imported or exported. */
+
+tree
+sh_symbian_associated_type (tree decl)
+{
+ tree t = NULL_TREE;
+
+ /* We can just take the DECL_CONTEXT as normal. */
+ if (DECL_CONTEXT (decl) && TYPE_P (DECL_CONTEXT (decl)))
+ t = DECL_CONTEXT (decl);
+
+ return t;
+}
+
+/* Return nonzero if DECL is a dllimport'd object. */
+
+bool
+sh_symbian_is_dllimported (tree decl)
+{
+ tree imp;
+
+ if ( TREE_CODE (decl) != VAR_DECL
+ && TREE_CODE (decl) != FUNCTION_DECL)
+ return false;
+
+ imp = lookup_attribute ("dllimport", DECL_ATTRIBUTES (decl));
+ if (imp)
+ return true;
+
+ /* Class members get the dllimport status of their class. */
+ imp = sh_symbian_associated_type (decl);
+ if (! imp)
+ return false;
+
+ imp = lookup_attribute ("dllimport", TYPE_ATTRIBUTES (imp));
+ if (!imp)
+ return false;
+
+ /* Don't mark defined functions as dllimport. If the definition itself
+ was marked with dllimport, then sh_symbian_handle_dll_attribute reports
+ an error. This handles the case when the definition overrides an
+ earlier declaration. */
+ if (TREE_CODE (decl) == FUNCTION_DECL
+ && DECL_INITIAL (decl)
+ && ! DECL_DECLARED_INLINE_P (decl))
+ {
+ warning (OPT_Wattributes, "function %q+D is defined after prior "
+ "declaration as dllimport: attribute ignored",
+ decl);
+ return false;
+ }
+
+ /* Don't allow definitions of static data members in dllimport
+ class. Just ignore the attribute for vtable data. */
+ else if (TREE_CODE (decl) == VAR_DECL
+ && TREE_STATIC (decl)
+ && TREE_PUBLIC (decl)
+ && !DECL_EXTERNAL (decl))
+ {
+ error ("definition of static data member %q+D of dllimport%'d class",
+ decl);
+ return false;
+ }
+
+ return true;
+}
+
+/* Handle a "dllimport" or "dllexport" attribute;
+ arguments as in struct attribute_spec.handler. */
+
+tree
+sh_symbian_handle_dll_attribute (tree *pnode, tree name, tree args,
+ int flags, bool *no_add_attrs)
+{
+ tree node = *pnode;
+ const char *attr = IDENTIFIER_POINTER (name);
+
+ /* These attributes may apply to structure and union types being
+ created, but otherwise should pass to the declaration involved. */
+ if (!DECL_P (node))
+ {
+ if (flags & ((int) ATTR_FLAG_DECL_NEXT
+ | (int) ATTR_FLAG_FUNCTION_NEXT
+ | (int) ATTR_FLAG_ARRAY_NEXT))
+ {
+ warning (OPT_Wattributes, "%qs attribute ignored", attr);
+ *no_add_attrs = true;
+ return tree_cons (name, args, NULL_TREE);
+ }
+
+ if (TREE_CODE (node) != RECORD_TYPE && TREE_CODE (node) != UNION_TYPE)
+ {
+ warning (OPT_Wattributes, "%qs attribute ignored", attr);
+ *no_add_attrs = true;
+ }
+
+ return NULL_TREE;
+ }
+
+ /* Report error on dllimport ambiguities
+ seen now before they cause any damage. */
+ else if (is_attribute_p ("dllimport", name))
+ {
+ if (TREE_CODE (node) == VAR_DECL)
+ {
+ if (DECL_INITIAL (node))
+ {
+ error ("variable %q+D definition is marked dllimport",
+ node);
+ *no_add_attrs = true;
+ }
+
+ /* `extern' needn't be specified with dllimport.
+ Specify `extern' now and hope for the best. Sigh. */
+ DECL_EXTERNAL (node) = 1;
+ /* Also, implicitly give dllimport'd variables declared within
+ a function global scope, unless declared static. */
+ if (current_function_decl != NULL_TREE && ! TREE_STATIC (node))
+ TREE_PUBLIC (node) = 1;
+ }
+ }
+
+ /* Report error if symbol is not accessible at global scope. */
+ if (!TREE_PUBLIC (node)
+ && ( TREE_CODE (node) == VAR_DECL
+ || TREE_CODE (node) == FUNCTION_DECL))
+ {
+ error ("external linkage required for symbol %q+D because of %qE attribute",
+ node, name);
+ *no_add_attrs = true;
+ }
+
+#if SYMBIAN_DEBUG
+ print_node_brief (stderr, "mark node", node, 0);
+ fprintf (stderr, " as %s\n", attr);
+#endif
+
+ return NULL_TREE;
+}
+
+int
+sh_symbian_import_export_class (tree ctype ATTRIBUTE_UNUSED, int import_export)
+{
+ return import_export;
+}
diff --git a/gcc/config/sh/symbian-cxx.c b/gcc/config/sh/symbian-cxx.c
new file mode 100644
index 000000000..c0f8b71f6
--- /dev/null
+++ b/gcc/config/sh/symbian-cxx.c
@@ -0,0 +1,662 @@
+/* Routines for C++ support for GCC for a Symbian OS targeted SH backend.
+ Copyright (C) 2004, 2005, 2007, 2009, 2010 Free Software Foundation, Inc.
+ Contributed by RedHat.
+ Most of this code is stolen from i386/winnt.c.
+
+ 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
+ <http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "rtl.h"
+#include "output.h"
+#include "flags.h"
+#include "tree.h"
+#include "expr.h"
+#include "tm_p.h"
+#include "cp/cp-tree.h" /* We need access to the OVL_... macros. */
+#include "diagnostic-core.h"
+#include "sh-symbian.h"
+
+
+/* Return the type that we should use to determine if DECL is
+ imported or exported. */
+
+tree
+sh_symbian_associated_type (tree decl)
+{
+ tree t = NULL_TREE;
+
+ if (TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE)
+ /* Methods now inherit their dllimport/dllexport attributes correctly
+ so there is no need to check their class. In fact it is wrong to
+ check their class since a method can remain unexported from an
+ exported class. */
+ return t;
+
+ /* Otherwise we can just take the DECL_CONTEXT as normal. */
+ if (DECL_CONTEXT (decl) && TYPE_P (DECL_CONTEXT (decl)))
+ t = DECL_CONTEXT (decl);
+
+ return t;
+}
+
+
+/* Return nonzero if DECL is a dllimport'd object. */
+
+bool
+sh_symbian_is_dllimported (tree decl)
+{
+ tree imp;
+
+ if ( TREE_CODE (decl) != VAR_DECL
+ && TREE_CODE (decl) != FUNCTION_DECL)
+ return false;
+
+ imp = lookup_attribute ("dllimport", DECL_ATTRIBUTES (decl));
+ if (imp)
+ return true;
+
+ /* Class members get the dllimport status of their class. */
+ imp = sh_symbian_associated_type (decl);
+ if (! imp)
+ return false;
+
+ imp = lookup_attribute ("dllimport", TYPE_ATTRIBUTES (imp));
+ if (!imp)
+ return false;
+
+ /* Don't mark defined functions as dllimport. If the definition itself
+ was marked with dllimport, then sh_symbian_handle_dll_attribute reports
+ an error. This handles the case when the definition overrides an
+ earlier declaration. */
+ if (TREE_CODE (decl) == FUNCTION_DECL
+ && DECL_INITIAL (decl)
+ && ! DECL_DECLARED_INLINE_P (decl))
+ {
+ /* Don't warn about artificial methods. */
+ if (!DECL_ARTIFICIAL (decl))
+ warning (OPT_Wattributes, "function %q+D is defined after prior "
+ "declaration as dllimport: attribute ignored",
+ decl);
+ return false;
+ }
+
+ /* We ignore the dllimport attribute for inline member functions.
+ This differs from MSVC behavior which treats it like GNUC
+ 'extern inline' extension. */
+ else if (TREE_CODE (decl) == FUNCTION_DECL && DECL_DECLARED_INLINE_P (decl))
+ {
+ if (extra_warnings)
+ warning (OPT_Wattributes, "inline function %q+D is declared as "
+ "dllimport: attribute ignored",
+ decl);
+ return false;
+ }
+
+ /* Don't allow definitions of static data members in dllimport
+ class. Just ignore the attribute for vtable data. */
+ else if (TREE_CODE (decl) == VAR_DECL
+ && TREE_STATIC (decl)
+ && TREE_PUBLIC (decl)
+ && !DECL_EXTERNAL (decl))
+ {
+ if (!DECL_VIRTUAL_P (decl))
+ error ("definition of static data member %q+D of dllimport%'d class",
+ decl);
+ return false;
+ }
+
+ /* Since we can't treat a pointer to a dllimport'd symbol as a
+ constant address, we turn off the attribute on C++ virtual
+ methods to allow creation of vtables using thunks. Don't mark
+ artificial methods either (in sh_symbian_associated_type, only
+ COMDAT artificial method get import status from class context). */
+ else if (TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE
+ && (DECL_VIRTUAL_P (decl) || DECL_ARTIFICIAL (decl)))
+ return false;
+
+ return true;
+}
+
+
+/* This code implements a specification for exporting the vtable and rtti of
+ classes that have members with the dllexport or dllexport attributes.
+ This specification is defined here:
+
+ http://www.armdevzone.com/EABI/exported_class.txt
+
+ Basically it says that a class's vtable and rtti should be exported if
+ the following rules apply:
+
+ - If it has any non-inline non-pure virtual functions,
+ at least one of these need to be declared dllimport
+ OR any of the constructors is declared dllimport.
+
+ AND
+
+ - The class has an inline constructor/destructor and
+ a key-function (placement of vtable uniquely defined) that
+ is defined in this translation unit.
+
+ The specification also says that for classes which will have their
+ vtables and rtti exported that their base class(es) might also need a
+ similar exporting if:
+
+ - Every base class needs to have its vtable & rtti exported
+ as well, if the following the conditions hold true:
+ + The base class has a non-inline declared non-pure virtual function
+ + The base class is polymorphic (has or inherits any virtual functions)
+ or the base class has any virtual base classes. */
+
+/* Decide if a base class of a class should
+ also have its vtable and rtti exported. */
+
+static void
+sh_symbian_possibly_export_base_class (tree base_class)
+{
+ VEC(tree,gc) *method_vec;
+ int len;
+
+ if (! (TYPE_CONTAINS_VPTR_P (base_class)))
+ return;
+
+ method_vec = CLASSTYPE_METHOD_VEC (base_class);
+ len = method_vec ? VEC_length (tree, method_vec) : 0;
+
+ for (;len --;)
+ {
+ tree member = VEC_index (tree, method_vec, len);
+
+ if (! member)
+ continue;
+
+ for (member = OVL_CURRENT (member); member; member = OVL_NEXT (member))
+ {
+ if (TREE_CODE (member) != FUNCTION_DECL)
+ continue;
+
+ if (DECL_CONSTRUCTOR_P (member) || DECL_DESTRUCTOR_P (member))
+ continue;
+
+ if (! DECL_VIRTUAL_P (member))
+ continue;
+
+ if (DECL_PURE_VIRTUAL_P (member))
+ continue;
+
+ if (DECL_DECLARED_INLINE_P (member))
+ continue;
+
+ break;
+ }
+
+ if (member)
+ break;
+ }
+
+ if (len < 0)
+ return;
+
+ /* FIXME: According to the spec this base class should be exported, but
+ a) how do we do this ? and
+ b) it does not appear to be necessary for compliance with the Symbian
+ OS which so far is the only consumer of this code. */
+#if SYMBIAN_DEBUG
+ print_node_brief (stderr, "", base_class, 0);
+ fprintf (stderr, " EXPORTed [base class of exported class]\n");
+#endif
+}
+
+/* Add the named attribute to the given node. Copes with both DECLs and
+ TYPEs. Will only add the attribute if it is not already present. */
+
+static void
+sh_symbian_add_attribute (tree node, const char *attr_name)
+{
+ tree attrs;
+ tree attr;
+
+ attrs = DECL_P (node) ? DECL_ATTRIBUTES (node) : TYPE_ATTRIBUTES (node);
+
+ if (lookup_attribute (attr_name, attrs) != NULL_TREE)
+ return;
+
+ attr = get_identifier (attr_name);
+
+ if (DECL_P (node))
+ DECL_ATTRIBUTES (node) = tree_cons (attr, NULL_TREE, attrs);
+ else
+ TYPE_ATTRIBUTES (node) = tree_cons (attr, NULL_TREE, attrs);
+
+#if SYMBIAN_DEBUG
+ fprintf (stderr, "propagate %s attribute", attr_name);
+ print_node_brief (stderr, " to", node, 0);
+ fprintf (stderr, "\n");
+#endif
+}
+
+/* Add the named attribute to a class and its vtable and rtti. */
+
+static void
+sh_symbian_add_attribute_to_class_vtable_and_rtti (tree ctype, const char *attr_name)
+{
+ sh_symbian_add_attribute (ctype, attr_name);
+
+ /* If the vtable exists then they need annotating as well. */
+ if (CLASSTYPE_VTABLES (ctype))
+ /* XXX - Do we need to annotate any vtables other than the primary ? */
+ sh_symbian_add_attribute (CLASSTYPE_VTABLES (ctype), attr_name);
+
+ /* If the rtti exists then it needs annotating as well. */
+ if (TYPE_MAIN_VARIANT (ctype)
+ && CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (ctype)))
+ sh_symbian_add_attribute (CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (ctype)),
+ attr_name);
+}
+
+/* Decide if a class needs to have an attribute because
+ one of its member functions has the attribute. */
+
+static bool
+sh_symbian_class_needs_attribute (tree ctype, const char *attribute_name)
+{
+ VEC(tree,gc) *method_vec;
+
+ method_vec = CLASSTYPE_METHOD_VEC (ctype);
+
+ /* If the key function has the attribute then the class needs it too. */
+ if (TYPE_POLYMORPHIC_P (ctype)
+ && method_vec
+ && tree_contains_struct [TREE_CODE (ctype), TS_DECL_COMMON] == 1
+ && lookup_attribute (attribute_name,
+ DECL_ATTRIBUTES (VEC_index (tree, method_vec, 0))))
+ return true;
+
+ /* Check the class's member functions. */
+ if (TREE_CODE (ctype) == RECORD_TYPE)
+ {
+ unsigned int len;
+
+ len = method_vec ? VEC_length (tree, method_vec) : 0;
+
+ for (;len --;)
+ {
+ tree member = VEC_index (tree, method_vec, len);
+
+ if (! member)
+ continue;
+
+ for (member = OVL_CURRENT (member);
+ member;
+ member = OVL_NEXT (member))
+ {
+ if (TREE_CODE (member) != FUNCTION_DECL)
+ continue;
+
+ if (DECL_PURE_VIRTUAL_P (member))
+ continue;
+
+ if (! DECL_VIRTUAL_P (member))
+ continue;
+
+ if (lookup_attribute (attribute_name, DECL_ATTRIBUTES (member)))
+ {
+#if SYMBIAN_DEBUG
+ print_node_brief (stderr, "", ctype, 0);
+ fprintf (stderr, " inherits %s because", attribute_name);
+ print_node_brief (stderr, "", member, 0);
+ fprintf (stderr, " has it.\n");
+#endif
+ return true;
+ }
+ }
+ }
+ }
+
+#if SYMBIAN_DEBUG
+ print_node_brief (stderr, "", ctype, 0);
+ fprintf (stderr, " does not inherit %s\n", attribute_name);
+#endif
+ return false;
+}
+
+/* Decide if a class needs its vtable and rtti exporting. */
+
+static bool
+symbian_export_vtable_and_rtti_p (tree ctype)
+{
+ bool inline_ctor_dtor;
+ bool dllimport_ctor_dtor;
+ bool dllimport_member;
+ tree binfo, base_binfo;
+ VEC(tree,gc) *method_vec;
+ tree key;
+ int i;
+ int len;
+
+ /* Make sure that we are examining a class... */
+ if (TREE_CODE (ctype) != RECORD_TYPE)
+ {
+#if SYMBIAN_DEBUG
+ print_node_brief (stderr, "", ctype, 0);
+ fprintf (stderr, " does NOT need to be EXPORTed [not a class]\n");
+#endif
+ return false;
+ }
+
+ /* If the class does not have a key function it
+ does not need to have its vtable exported. */
+ if ((key = CLASSTYPE_KEY_METHOD (ctype)) == NULL_TREE)
+ {
+#if SYMBIAN_DEBUG
+ print_node_brief (stderr, "", ctype, 0);
+ fprintf (stderr, " does NOT need to be EXPORTed [no key function]\n");
+#endif
+ return false;
+ }
+
+ /* If the key fn has not been defined
+ then the class should not be exported. */
+ if (! TREE_ASM_WRITTEN (key))
+ {
+#if SYMBIAN_DEBUG
+ print_node_brief (stderr, "", ctype, 0);
+ fprintf (stderr, " does NOT need to be EXPORTed [key function not defined]\n");
+#endif
+ return false;
+ }
+
+ /* Check the class's member functions. */
+ inline_ctor_dtor = false;
+ dllimport_ctor_dtor = false;
+ dllimport_member = false;
+
+ method_vec = CLASSTYPE_METHOD_VEC (ctype);
+ len = method_vec ? VEC_length (tree, method_vec) : 0;
+
+ for (;len --;)
+ {
+ tree member = VEC_index (tree, method_vec, len);
+
+ if (! member)
+ continue;
+
+ for (member = OVL_CURRENT (member); member; member = OVL_NEXT (member))
+ {
+ if (TREE_CODE (member) != FUNCTION_DECL)
+ continue;
+
+ if (DECL_CONSTRUCTOR_P (member) || DECL_DESTRUCTOR_P (member))
+ {
+ if (DECL_DECLARED_INLINE_P (member)
+ /* Ignore C++ backend created inline ctors/dtors. */
+ && ( DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (member)
+ || DECL_MAYBE_IN_CHARGE_DESTRUCTOR_P (member)))
+ inline_ctor_dtor = true;
+
+ if (lookup_attribute ("dllimport", DECL_ATTRIBUTES (member)))
+ dllimport_ctor_dtor = true;
+ }
+ else
+ {
+ if (DECL_PURE_VIRTUAL_P (member))
+ continue;
+
+ if (! DECL_VIRTUAL_P (member))
+ continue;
+
+ if (DECL_DECLARED_INLINE_P (member))
+ continue;
+
+ if (lookup_attribute ("dllimport", DECL_ATTRIBUTES (member)))
+ dllimport_member = true;
+ }
+ }
+ }
+
+ if (! dllimport_member && ! dllimport_ctor_dtor)
+ {
+#if SYMBIAN_DEBUG
+ print_node_brief (stderr, "", ctype, 0);
+ fprintf (stderr,
+ " does NOT need to be EXPORTed [no non-pure virtuals or ctors/dtors with dllimport]\n");
+#endif
+ return false;
+ }
+
+ if (! inline_ctor_dtor)
+ {
+#if SYMBIAN_DEBUG
+ print_node_brief (stderr, "", ctype, 0);
+ fprintf (stderr,
+ " does NOT need to be EXPORTed [no inline ctor/dtor]\n");
+#endif
+ return false;
+ }
+
+#if SYMBIAN_DEBUG
+ print_node_brief (stderr, "", ctype, 0);
+ fprintf (stderr, " DOES need to be EXPORTed\n");
+#endif
+
+ /* Now we must check and possibly export the base classes. */
+ for (i = 0, binfo = TYPE_BINFO (ctype);
+ BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
+ sh_symbian_possibly_export_base_class (BINFO_TYPE (base_binfo));
+
+ return true;
+}
+
+/* Possibly override the decision to export class TYPE. Upon entry
+ IMPORT_EXPORT will contain 1 if the class is going to be exported,
+ -1 if it is going to be imported and 0 otherwise. This function
+ should return the modified value and perform any other actions
+ necessary to support the backend's targeted operating system. */
+
+int
+sh_symbian_import_export_class (tree ctype, int import_export)
+{
+ const char *attr_name = NULL;
+
+ /* If we are exporting the class but it does not have the dllexport
+ attribute then we may need to add it. Similarly imported classes
+ may need the dllimport attribute. */
+ switch (import_export)
+ {
+ case 1: attr_name = "dllexport"; break;
+ case -1: attr_name = "dllimport"; break;
+ default: break;
+ }
+
+ if (attr_name
+ && ! lookup_attribute (attr_name, TYPE_ATTRIBUTES (ctype)))
+ {
+ if (sh_symbian_class_needs_attribute (ctype, attr_name))
+ sh_symbian_add_attribute_to_class_vtable_and_rtti (ctype, attr_name);
+
+ /* Classes can be forced to export their
+ vtable and rtti under certain conditions. */
+ if (symbian_export_vtable_and_rtti_p (ctype))
+ {
+ sh_symbian_add_attribute_to_class_vtable_and_rtti (ctype, "dllexport");
+
+ /* Make sure that the class and its vtable are exported. */
+ import_export = 1;
+
+ if (CLASSTYPE_VTABLES (ctype))
+ DECL_EXTERNAL (CLASSTYPE_VTABLES (ctype)) = 1;
+
+ /* Check to make sure that if the class has a key method that
+ it is now on the list of keyed classes. That way its vtable
+ will be emitted. */
+ if (CLASSTYPE_KEY_METHOD (ctype))
+ {
+ tree class;
+
+ for (class = keyed_classes; class; class = TREE_CHAIN (class))
+ if (class == ctype)
+ break;
+
+ if (class == NULL_TREE)
+ {
+#if SYMBIAN_DEBUG
+ print_node_brief (stderr, "Add node", ctype, 0);
+ fprintf (stderr, " to the keyed classes list\n");
+#endif
+ keyed_classes = tree_cons (NULL_TREE, ctype, keyed_classes);
+ }
+ }
+
+ /* Make sure that the typeinfo will be emitted as well. */
+ if (CLASS_TYPE_P (ctype))
+ TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (ctype)))) = 1;
+ }
+ }
+
+ return import_export;
+}
+
+/* Handle a "dllimport" or "dllexport" attribute;
+ arguments as in struct attribute_spec.handler. */
+
+tree
+sh_symbian_handle_dll_attribute (tree *pnode, tree name, tree args,
+ int flags, bool *no_add_attrs)
+{
+ tree thunk;
+ tree node = *pnode;
+ const char *attr = IDENTIFIER_POINTER (name);
+
+ /* These attributes may apply to structure and union types being
+ created, but otherwise should pass to the declaration involved. */
+ if (!DECL_P (node))
+ {
+ if (flags & ((int) ATTR_FLAG_DECL_NEXT
+ | (int) ATTR_FLAG_FUNCTION_NEXT
+ | (int) ATTR_FLAG_ARRAY_NEXT))
+ {
+ warning (OPT_Wattributes, "%qs attribute ignored", attr);
+ *no_add_attrs = true;
+ return tree_cons (name, args, NULL_TREE);
+ }
+
+ if (TREE_CODE (node) != RECORD_TYPE && TREE_CODE (node) != UNION_TYPE)
+ {
+ warning (OPT_Wattributes, "%qs attribute ignored", attr);
+ *no_add_attrs = true;
+ }
+
+ return NULL_TREE;
+ }
+
+ /* Report error on dllimport ambiguities
+ seen now before they cause any damage. */
+ else if (is_attribute_p ("dllimport", name))
+ {
+ if (TREE_CODE (node) == VAR_DECL)
+ {
+ if (DECL_INITIAL (node))
+ {
+ error ("variable %q+D definition is marked dllimport",
+ node);
+ *no_add_attrs = true;
+ }
+
+ /* `extern' needn't be specified with dllimport.
+ Specify `extern' now and hope for the best. Sigh. */
+ DECL_EXTERNAL (node) = 1;
+ /* Also, implicitly give dllimport'd variables declared within
+ a function global scope, unless declared static. */
+ if (current_function_decl != NULL_TREE && ! TREE_STATIC (node))
+ TREE_PUBLIC (node) = 1;
+ }
+ }
+
+ /* If the node is an overloaded constructor or destructor, then we must
+ make sure that the attribute is propagated along the overload chain,
+ as it is these overloaded functions which will be emitted, rather than
+ the user declared constructor itself. */
+ if (TREE_CODE (TREE_TYPE (node)) == METHOD_TYPE
+ && (DECL_CONSTRUCTOR_P (node) || DECL_DESTRUCTOR_P (node)))
+ {
+ tree overload;
+
+ for (overload = OVL_CHAIN (node); overload; overload = OVL_CHAIN (overload))
+ {
+ tree node_args;
+ tree func_args;
+ tree function = OVL_CURRENT (overload);
+
+ if (! function
+ || ! DECL_P (function)
+ || (DECL_CONSTRUCTOR_P (node) && ! DECL_CONSTRUCTOR_P (function))
+ || (DECL_DESTRUCTOR_P (node) && ! DECL_DESTRUCTOR_P (function)))
+ continue;
+
+ /* The arguments must match as well. */
+ for (node_args = DECL_ARGUMENTS (node), func_args = DECL_ARGUMENTS (function);
+ node_args && func_args;
+ node_args = TREE_CHAIN (node_args), func_args = TREE_CHAIN (func_args))
+ if (TREE_TYPE (node_args) != TREE_TYPE (func_args))
+ break;
+
+ if (node_args || func_args)
+ {
+ /* We can ignore an extraneous __in_chrg arguments in the node.
+ GCC generated destructors, for example, will have this. */
+ if ((node_args == NULL_TREE
+ || func_args != NULL_TREE)
+ && strcmp (IDENTIFIER_POINTER (DECL_NAME (node)), "__in_chrg") != 0)
+ continue;
+ }
+
+ sh_symbian_add_attribute (function, attr);
+
+ /* Propagate the attribute to any function thunks as well. */
+ for (thunk = DECL_THUNKS (function); thunk; thunk = DECL_CHAIN (thunk))
+ if (TREE_CODE (thunk) == FUNCTION_DECL)
+ sh_symbian_add_attribute (thunk, attr);
+ }
+ }
+
+ if (TREE_CODE (node) == FUNCTION_DECL && DECL_VIRTUAL_P (node))
+ {
+ /* Propagate the attribute to any thunks of this function. */
+ for (thunk = DECL_THUNKS (node); thunk; thunk = DECL_CHAIN (thunk))
+ if (TREE_CODE (thunk) == FUNCTION_DECL)
+ sh_symbian_add_attribute (thunk, attr);
+ }
+
+ /* Report error if symbol is not accessible at global scope. */
+ if (!TREE_PUBLIC (node)
+ && ( TREE_CODE (node) == VAR_DECL
+ || TREE_CODE (node) == FUNCTION_DECL))
+ {
+ error ("external linkage required for symbol %q+D because of %qE attribute",
+ node, name);
+ *no_add_attrs = true;
+ }
+
+#if SYMBIAN_DEBUG
+ print_node_brief (stderr, "mark node", node, 0);
+ fprintf (stderr, " as %s\n", attr);
+#endif
+
+ return NULL_TREE;
+}
diff --git a/gcc/config/sh/symbian-post.h b/gcc/config/sh/symbian-post.h
new file mode 100644
index 000000000..a4497b969
--- /dev/null
+++ b/gcc/config/sh/symbian-post.h
@@ -0,0 +1,88 @@
+/* Definitions for the Symbian OS running on an SH part.
+ This file is included after all the other target specific headers.
+
+ Copyright (C) 2004, 2007 Free Software Foundation, Inc.
+ Contributed by Red Hat.
+
+ 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
+ <http://www.gnu.org/licenses/>. */
+
+#undef TARGET_VERSION
+#define TARGET_VERSION \
+ fputs (" (Renesas SH for Symbian OS)", stderr);
+
+#undef LINK_EMUL_PREFIX
+#define LINK_EMUL_PREFIX "shlsymbian"
+
+
+#define SYMBIAN_EXPORT_NAME(NAME,FILE,DECL) \
+ do \
+ { \
+ if ((DECL && sh_symbian_is_dllexported (DECL)) \
+ || sh_symbian_is_dllexported_name (NAME)) \
+ { \
+ fprintf ((FILE), "\t.pushsection .directive\n"); \
+ fprintf ((FILE), "\t.asciz \"EXPORT %s\\n\"\n", \
+ sh_symbian_strip_name_encoding (NAME)); \
+ fprintf ((FILE), "\t.popsection\n"); \
+ } \
+ } \
+ while (0)
+
+/* Output a function definition label. */
+#undef ASM_DECLARE_FUNCTION_NAME
+#define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL) \
+ do \
+ { \
+ SYMBIAN_EXPORT_NAME ((NAME), (FILE), (DECL)); \
+ ASM_OUTPUT_TYPE_DIRECTIVE ((FILE), (NAME), "function"); \
+ ASM_DECLARE_RESULT ((FILE), DECL_RESULT (DECL)); \
+ ASM_OUTPUT_LABEL ((FILE), (NAME)); \
+ } \
+ while (0)
+
+/* Output the label for an initialized variable. */
+#undef ASM_DECLARE_OBJECT_NAME
+#define ASM_DECLARE_OBJECT_NAME(FILE, NAME, DECL) \
+ do \
+ { \
+ HOST_WIDE_INT size; \
+ \
+ SYMBIAN_EXPORT_NAME ((NAME), (FILE), (DECL)); \
+ ASM_OUTPUT_TYPE_DIRECTIVE ((FILE), (NAME), "object"); \
+ \
+ size_directive_output = 0; \
+ if (!flag_inhibit_size_directive \
+ && (DECL) \
+ && DECL_SIZE (DECL)) \
+ { \
+ size_directive_output = 1; \
+ size = int_size_in_bytes (TREE_TYPE (DECL)); \
+ ASM_OUTPUT_SIZE_DIRECTIVE ((FILE), (NAME), size); \
+ } \
+ \
+ ASM_OUTPUT_LABEL ((FILE), (NAME)); \
+ } \
+ while (0)
+
+#undef ASM_OUTPUT_LABELREF
+#define ASM_OUTPUT_LABELREF(FILE, NAME) \
+ do \
+ { \
+ asm_fprintf ((FILE), "%U%s", \
+ sh_symbian_strip_name_encoding (NAME)); \
+ } \
+ while (0)
diff --git a/gcc/config/sh/symbian-pre.h b/gcc/config/sh/symbian-pre.h
new file mode 100644
index 000000000..d2229e071
--- /dev/null
+++ b/gcc/config/sh/symbian-pre.h
@@ -0,0 +1,40 @@
+/* Definitions for the Symbian OS running on an SH part.
+ This file is included before any other target specific headers.
+
+ Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
+ Contributed by Red Hat.
+
+ 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
+ <http://www.gnu.org/licenses/>. */
+
+/* Enable Symbian specific code. */
+#define SYMBIAN 1
+
+/* Default to using the Renesas ABI. */
+#define TARGET_ABI_DEFAULT MASK_HITACHI
+
+#define SUBTARGET_CPP_SPEC ""
+
+/* Get tree.c to declare merge_dllimport_decl_attributes(). */
+#define TARGET_DLLIMPORT_DECL_ATTRIBUTES 1
+
+/* The Symbian OS currently does not support exception handling. */
+#define SUBTARGET_CC1PLUS_SPEC "-fno-exceptions"
+
+/* Create constructor/destructor sections without the writable flag.
+ Symbian puts them into the text segment and munges them later on. */
+#define CTORS_SECTION_ASM_OP "\t.section\t.ctors,\"ax\",@progbits"
+#define DTORS_SECTION_ASM_OP "\t.section\t.dtors,\"ax\",@progbits"
diff --git a/gcc/config/sh/t-elf b/gcc/config/sh/t-elf
new file mode 100644
index 000000000..333efb54e
--- /dev/null
+++ b/gcc/config/sh/t-elf
@@ -0,0 +1,10 @@
+EXTRA_MULTILIB_PARTS= crt1.o crti.o crtn.o \
+ crtbegin.o crtend.o crtbeginS.o crtendS.o $(IC_EXTRA_PARTS) $(OPT_EXTRA_PARTS)
+
+# Compile crtbeginS.o and crtendS.o with pic.
+CRTSTUFF_T_CFLAGS_S = -fPIC
+
+# Don't compile libgcc with -fpic for now. It's unlikely that we'll
+# build shared libraries for embedded SH.
+# Linux / Netbsd will already have set TARGET_LIBGCC2_CFLAGS.
+# TARGET_LIBGCC2_CFLAGS = -fpic
diff --git a/gcc/config/sh/t-linux b/gcc/config/sh/t-linux
new file mode 100644
index 000000000..13ff848dd
--- /dev/null
+++ b/gcc/config/sh/t-linux
@@ -0,0 +1,8 @@
+LIB1ASMFUNCS_CACHE = _ic_invalidate _ic_invalidate_array
+
+LIB2FUNCS_EXTRA= $(srcdir)/config/sh/linux-atomic.asm
+
+MULTILIB_DIRNAMES=
+MULTILIB_MATCHES =
+
+EXTRA_MULTILIB_PARTS= crtbegin.o crtend.o crtbeginS.o crtendS.o crtbeginT.o
diff --git a/gcc/config/sh/t-linux64 b/gcc/config/sh/t-linux64
new file mode 100644
index 000000000..126b01637
--- /dev/null
+++ b/gcc/config/sh/t-linux64
@@ -0,0 +1 @@
+EXTRA_MULTILIB_PARTS= crtbegin.o crtend.o crtbeginS.o crtendS.o crtbeginT.o
diff --git a/gcc/config/sh/t-netbsd b/gcc/config/sh/t-netbsd
new file mode 100644
index 000000000..b2794a006
--- /dev/null
+++ b/gcc/config/sh/t-netbsd
@@ -0,0 +1,31 @@
+# Copyright (C) 2002, 2004, 2009 Free Software Foundation, Inc.
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 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
+# <http://www.gnu.org/licenses/>.
+
+TARGET_LIBGCC2_CFLAGS = -fpic -mieee
+LIB1ASMFUNCS_CACHE = _ic_invalidate
+
+LIB2FUNCS_EXTRA=
+
+EXTRA_MULTILIB_PARTS=
+
+# NetBSD's C library includes a fast software FP library that
+# has support for setting/setting the rounding mode, exception
+# mask, etc. Therefore, we don't want to include software FP
+# in libgcc.
+FPBIT =
+DPBIT =
diff --git a/gcc/config/sh/t-netbsd-sh5-64 b/gcc/config/sh/t-netbsd-sh5-64
new file mode 100644
index 000000000..8fc6bd1ea
--- /dev/null
+++ b/gcc/config/sh/t-netbsd-sh5-64
@@ -0,0 +1 @@
+MULTILIB_DIRNAMES= $(MULTILIB_RAW_DIRNAMES:/media64=)
diff --git a/gcc/config/sh/t-rtems b/gcc/config/sh/t-rtems
new file mode 100644
index 000000000..9fd262cf8
--- /dev/null
+++ b/gcc/config/sh/t-rtems
@@ -0,0 +1,7 @@
+# Custom multilibs for RTEMS
+
+MULTILIB_ENDIAN = ml
+MULTILIB_OPTIONS= $(MULTILIB_ENDIAN) m2/m2e/m4-single-only/m4-single/m4
+MULTILIB_DIRNAMES=
+MULTILIB_MATCHES = m2=m3 m2e=m3e m2=m4-nofpu
+MULTILIB_EXCEPTIONS = ml
diff --git a/gcc/config/sh/t-sh b/gcc/config/sh/t-sh
new file mode 100644
index 000000000..a897bfffb
--- /dev/null
+++ b/gcc/config/sh/t-sh
@@ -0,0 +1,166 @@
+# Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2006, 2008, 2009 Free Software Foundation, Inc.
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 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
+# <http://www.gnu.org/licenses/>.
+
+sh-c.o: $(srcdir)/config/sh/sh-c.c \
+ $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(TM_H) $(TM_P_H) coretypes.h
+ $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
+ $(srcdir)/config/sh/sh-c.c
+
+LIB1ASMSRC = sh/lib1funcs.asm
+LIB1ASMFUNCS = _ashiftrt _ashiftrt_n _ashiftlt _lshiftrt _movmem \
+ _movmem_i4 _mulsi3 _sdivsi3 _sdivsi3_i4 _udivsi3 _udivsi3_i4 _set_fpscr \
+ _div_table _udiv_qrnnd_16 \
+ $(LIB1ASMFUNCS_CACHE)
+LIB1ASMFUNCS_CACHE = _ic_invalidate _ic_invalidate_array
+
+TARGET_LIBGCC2_CFLAGS = -mieee
+
+# 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
+
+dp-bit.c: $(srcdir)/config/fp-bit.c
+ echo '#ifdef __LITTLE_ENDIAN__' > dp-bit.c
+ echo '#define FLOAT_BIT_ORDER_MISMATCH' >>dp-bit.c
+ echo '#endif' >> dp-bit.c
+ cat $(srcdir)/config/fp-bit.c >> dp-bit.c
+
+fp-bit.c: $(srcdir)/config/fp-bit.c
+ echo '#define FLOAT' > fp-bit.c
+ echo '#ifdef __LITTLE_ENDIAN__' >> fp-bit.c
+ echo '#define FLOAT_BIT_ORDER_MISMATCH' >>fp-bit.c
+ echo '#endif' >> fp-bit.c
+ cat $(srcdir)/config/fp-bit.c >> fp-bit.c
+
+DEFAULT_ENDIAN = $(word 1,$(TM_ENDIAN_CONFIG))
+OTHER_ENDIAN = $(word 2,$(TM_ENDIAN_CONFIG))
+
+MULTILIB_OPTIONS= $(OTHER_ENDIAN) $(TM_MULTILIB_CONFIG)
+MULTILIB_DIRNAMES=
+
+# The separate entries for m2a-nofpu and m2a-single-only with
+# duplicate base libraries are there to make sure we don't ever use an
+# m4* multilib for m2a or vice-versa; they are not compatible. This
+# is why sh2a and sh2a-single need their own multilibs.
+MULTILIB_MATCHES = $(shell \
+ multilibs="$(MULTILIB_OPTIONS)" ; \
+ for abi in m1,m2,m3,m4-nofpu,m4-100-nofpu,m4-200-nofpu,m4-400,m4-500,m4-340,m4-300-nofpu,m4al,m4a-nofpu \
+ m1,m2,m2a-nofpu \
+ m2e,m3e,m4-single-only,m4-100-single-only,m4-200-single-only,m4-300-single-only,m4a-single-only \
+ m2e,m2a-single-only \
+ m4-single,m4-100-single,m4-200-single,m4-300-single,m4a-single \
+ m4,m4-100,m4-200,m4-300,m4a \
+ m5-32media,m5-compact,m5-32media \
+ m5-32media-nofpu,m5-compact-nofpu,m5-32media-nofpu; do \
+ subst= ; \
+ for lib in `echo $$abi|tr , ' '` ; do \
+ if test "`echo $$multilibs|sed s/$$lib//`" != "$$multilibs"; then \
+ subst=$$lib ; \
+ elif test x$$subst != x ; then \
+ echo $$subst=$$lib ; \
+ fi \
+ done \
+ done)
+
+# SH1 only supports big endian.
+MULTILIB_EXCEPTIONS = ml/m1 ml/m2a* $(TM_MULTILIB_EXCEPTIONS_CONFIG)
+
+MULTILIB_OSDIRNAMES = \
+ $(OTHER_ENDIAN)=!$(OTHER_ENDIAN) \
+ m1=!m1 $(OTHER_ENDIAN)/m1=!$(OTHER_ENDIAN)/m1 \
+ m2a=!m2a $(OTHER_ENDIAN)/m2a=!$(OTHER_ENDIAN)/m2a \
+ m2a-nofpu=!m2a-nofpu $(OTHER_ENDIAN)/m2a-nofpu=!$(OTHER_ENDIAN)/m2a-nofpu \
+ m2a-single-only=!m2a-single-only $(OTHER_ENDIAN)/m2a-single-only=!$(OTHER_ENDIAN)/m2a-single-only \
+ m2a-single=!m2a-single $(OTHER_ENDIAN)/m2a-single=!$(OTHER_ENDIAN)/m2a-single \
+ m2e=!m2e $(OTHER_ENDIAN)/m2e=!$(OTHER_ENDIAN)/m2e \
+ m2=!m2 $(OTHER_ENDIAN)/m2=!$(OTHER_ENDIAN)/m2 \
+ m3e=!m3e $(OTHER_ENDIAN)/m3e=!$(OTHER_ENDIAN)/m3e \
+ m3=!m3 $(OTHER_ENDIAN)/m3=!$(OTHER_ENDIAN)/m3 \
+ m4-nofpu=!m4-nofpu $(OTHER_ENDIAN)/m4-nofpu=!$(OTHER_ENDIAN)/m4-nofpu \
+ m4-single-only=!m4-single-only $(OTHER_ENDIAN)/m4-single-only=!$(OTHER_ENDIAN)/m4-single-only \
+ m4-single=!m4-single $(OTHER_ENDIAN)/m4-single=!$(OTHER_ENDIAN)/m4-single \
+ m4=!m4 $(OTHER_ENDIAN)/m4=!$(OTHER_ENDIAN)/m4 \
+ m4a-nofpu=!m4a-nofpu $(OTHER_ENDIAN)/m4a-nofpu=!$(OTHER_ENDIAN)/m4a-nofpu \
+ m4a-single-only=!m4a-single-only $(OTHER_ENDIAN)/m4a-single-only=!$(OTHER_ENDIAN)/m4a-single-only \
+ m4a-single=!m4a-single $(OTHER_ENDIAN)/m4a-single=!$(OTHER_ENDIAN)/m4a-single \
+ m4a=!m4a $(OTHER_ENDIAN)/m4a=!$(OTHER_ENDIAN)/m4a \
+ m4al=!m4al $(OTHER_ENDIAN)/m4al=!$(OTHER_ENDIAN)/m4al \
+ m5-32media=!m5-32media $(OTHER_ENDIAN)/m5-32media=!$(OTHER_ENDIAN)/m5-32media \
+ m5-32media-nofpu=!m5-32media-nofpu $(OTHER_ENDIAN)/m5-32media-nofpu=!$(OTHER_ENDIAN)/m5-32media-nofpu \
+ m5-compact=!m5-compact $(OTHER_ENDIAN)/m5-compact=!$(OTHER_ENDIAN)/m5-compact \
+ m5-compact-nofpu=!m5-compact-nofpu $(OTHER_ENDIAN)/m5-compact-nofpu=!$(OTHER_ENDIAN)/m5-compact-nofpu \
+ m5-64media=!m5-64media $(OTHER_ENDIAN)/m5-64media=!$(OTHER_ENDIAN)/m5-64media \
+ m5-64media-nofpu=!m5-64media-nofpu $(OTHER_ENDIAN)/m5-64media-nofpu=!$(OTHER_ENDIAN)/m5-64media-nofpu
+
+LIBGCC = stmp-multilib
+INSTALL_LIBGCC = install-multilib
+
+$(T)crt1.o: $(srcdir)/config/sh/crt1.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(MULTILIB_CFLAGS) -c -o $(T)crt1.o -x assembler-with-cpp $(srcdir)/config/sh/crt1.asm
+$(T)crti.o: $(srcdir)/config/sh/crti.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(MULTILIB_CFLAGS) -c -o $(T)crti.o -x assembler-with-cpp $(srcdir)/config/sh/crti.asm
+$(T)crtn.o: $(srcdir)/config/sh/crtn.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(MULTILIB_CFLAGS) -c -o $(T)crtn.o -x assembler-with-cpp $(srcdir)/config/sh/crtn.asm
+
+$(out_object_file): gt-sh.h
+gt-sh.h : s-gtype ; @true
+
+# These are not suitable for COFF.
+# EXTRA_MULTILIB_PARTS= crt1.o crti.o crtn.o crtbegin.o crtend.o
+
+IC_EXTRA_PARTS= libic_invalidate_array_4-100.a libic_invalidate_array_4-200.a \
+libic_invalidate_array_4a.a
+OPT_EXTRA_PARTS= libgcc-Os-4-200.a libgcc-4-300.a
+EXTRA_MULTILIB_PARTS= $(IC_EXTRA_PARTS) $(OPT_EXTRA_PARTS)
+
+$(T)ic_invalidate_array_4-100.o: $(srcdir)/config/sh/lib1funcs.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(MULTILIB_CFLAGS) -c -o $(T)ic_invalidate_array_4-100.o -DL_ic_invalidate_array -DWAYS=1 -DWAY_SIZE=0x2000 -x assembler-with-cpp $(srcdir)/config/sh/lib1funcs.asm
+$(T)libic_invalidate_array_4-100.a: $(T)ic_invalidate_array_4-100.o $(GCC_PASSES)
+ $(AR_CREATE_FOR_TARGET) $(T)libic_invalidate_array_4-100.a $(T)ic_invalidate_array_4-100.o
+
+$(T)ic_invalidate_array_4-200.o: $(srcdir)/config/sh/lib1funcs.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(MULTILIB_CFLAGS) -c -o $(T)ic_invalidate_array_4-200.o -DL_ic_invalidate_array -DWAYS=2 -DWAY_SIZE=0x2000 -x assembler-with-cpp $(srcdir)/config/sh/lib1funcs.asm
+$(T)libic_invalidate_array_4-200.a: $(T)ic_invalidate_array_4-200.o $(GCC_PASSES)
+ $(AR_CREATE_FOR_TARGET) $(T)libic_invalidate_array_4-200.a $(T)ic_invalidate_array_4-200.o
+
+$(T)ic_invalidate_array_4a.o: $(srcdir)/config/sh/lib1funcs.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(MULTILIB_CFLAGS) -c -o $(T)ic_invalidate_array_4a.o -DL_ic_invalidate_array -D__FORCE_SH4A__ -x assembler-with-cpp $(srcdir)/config/sh/lib1funcs.asm
+$(T)libic_invalidate_array_4a.a: $(T)ic_invalidate_array_4a.o $(GCC_PASSES)
+ $(AR_CREATE_FOR_TARGET) $(T)libic_invalidate_array_4a.a $(T)ic_invalidate_array_4a.o
+
+$(T)sdivsi3_i4i-Os-4-200.o: $(srcdir)/config/sh/lib1funcs-Os-4-200.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(MULTILIB_CFLAGS) -c -o $@ -DL_sdivsi3_i4i -x assembler-with-cpp $<
+$(T)udivsi3_i4i-Os-4-200.o: $(srcdir)/config/sh/lib1funcs-Os-4-200.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(MULTILIB_CFLAGS) -c -o $@ -DL_udivsi3_i4i -x assembler-with-cpp $<
+$(T)unwind-dw2-Os-4-200.o: $(srcdir)/unwind-dw2.c $(srcdir)/unwind-generic.h unwind-pe.h unwind.inc unwind-dw2-fde.h unwind-dw2.h $(CONFIG_H) coretypes.h $(TM_H) $(MACHMODE_H) longlong.h config.status stmp-int-hdrs tsystem.h $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(MULTILIB_CFLAGS) $(LIBGCC2_CFLAGS) $(INCLUDES) $(vis_hide) -fexceptions -Os -c -o $@ $<
+OBJS_Os_4_200=$(T)sdivsi3_i4i-Os-4-200.o $(T)udivsi3_i4i-Os-4-200.o $(T)unwind-dw2-Os-4-200.o
+$(T)libgcc-Os-4-200.a: $(OBJS_Os_4_200) $(GCC_PASSES)
+ $(AR_CREATE_FOR_TARGET) $@ $(OBJS_Os_4_200)
+
+$(T)div_table-4-300.o: $(srcdir)/config/sh/lib1funcs-4-300.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(MULTILIB_CFLAGS) -c -o $@ -DL_div_table -x assembler-with-cpp $<
+
+$(T)libgcc-4-300.a: $(T)div_table-4-300.o $(GCC_PASSES)
+ $(AR_CREATE_FOR_TARGET) $@ $(T)div_table-4-300.o
+
+# Local Variables:
+# mode: Makefile
+# End:
diff --git a/gcc/config/sh/t-sh64 b/gcc/config/sh/t-sh64
new file mode 100644
index 000000000..d88f929fd
--- /dev/null
+++ b/gcc/config/sh/t-sh64
@@ -0,0 +1,29 @@
+# Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 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
+# <http://www.gnu.org/licenses/>.
+
+LIB1ASMFUNCS = \
+ _sdivsi3 _sdivsi3_i4 _udivsi3 _udivsi3_i4 _set_fpscr \
+ _shcompact_call_trampoline _shcompact_return_trampoline \
+ _shcompact_incoming_args _ic_invalidate _nested_trampoline \
+ _push_pop_shmedia_regs \
+ _udivdi3 _divdi3 _umoddi3 _moddi3 _div_table
+
+MULTILIB_CPU_DIRS= $(ML_sh1) $(ML_sh2e) $(ML_sh2) $(ML_sh3e) $(ML_sh3) $(ML_sh4_nofpu) $(ML_sh4_single_only) $(ML_sh4_single) $(ML_sh4) $(ML_sh5_32media:m5-32media/=media32) $(ML_sh5_32media_nofpu:m5-32media-nofpu/=nofpu/media32) $(ML_sh5_compact:m5-compact/=compact) $(ML_sh5_compact_nofpu:m5-compact-nofpu/=nofpu/compact) $(ML_sh5_64media:m5-64media/=media64) $(ML_sh5_64media_nofpu:m5-64media-nofpu/=nofpu/media64)
+
+MULTILIB_RAW_DIRNAMES= $(MULTILIB_ENDIAN:/mb= mb) $(MULTILIB_CPU_DIRS:/=)
+MULTILIB_DIRNAMES= $(MULTILIB_RAW_DIRNAMES)
diff --git a/gcc/config/sh/t-superh b/gcc/config/sh/t-superh
new file mode 100644
index 000000000..4e2d83dcb
--- /dev/null
+++ b/gcc/config/sh/t-superh
@@ -0,0 +1,33 @@
+# Copyright (C) 2005, 2006 Free Software Foundation, Inc.
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 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
+# <http://www.gnu.org/licenses/>.
+
+EXTRA_MULTILIB_PARTS= crt1.o crti.o crtn.o \
+ crtbegin.o crtend.o crtbeginS.o crtendS.o \
+ crt1-mmu.o gcrt1-mmu.o gcrt1.o $(IC_EXTRA_PARTS) $(OPT_EXTRA_PARTS)
+
+# Compile crt1-mmu.o as crt1.o with -DMMU_SUPPORT
+$(T)crt1-mmu.o: $(srcdir)/config/sh/crt1.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(MULTILIB_CFLAGS) -c -o $(T)crt1-mmu.o -DMMU_SUPPORT -x assembler-with-cpp $(srcdir)/config/sh/crt1.asm
+
+# Compile gcrt1-mmu.o as crt1-mmu.o with -DPROFILE
+$(T)gcrt1-mmu.o: $(srcdir)/config/sh/crt1.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(MULTILIB_CFLAGS) -c -o $(T)gcrt1-mmu.o -DPROFILE -DMMU_SUPPORT -x assembler-with-cpp $(srcdir)/config/sh/crt1.asm
+
+# For sh4-400: Compile gcrt1.o as crt1.o with -DPROFILE
+$(T)gcrt1.o: $(srcdir)/config/sh/crt1.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(MULTILIB_CFLAGS) -c -o $(T)gcrt1.o -DPROFILE -x assembler-with-cpp $(srcdir)/config/sh/crt1.asm
diff --git a/gcc/config/sh/t-symbian b/gcc/config/sh/t-symbian
new file mode 100644
index 000000000..f0b7dabd4
--- /dev/null
+++ b/gcc/config/sh/t-symbian
@@ -0,0 +1,81 @@
+# Copyright (C) 2004, 2006, 2008, 2009 Free Software Foundation, Inc.
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 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
+# <http://www.gnu.org/licenses/>.
+
+sh-c.o: $(srcdir)/config/sh/sh-c.c \
+ $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(TM_H) $(TM_P_H) coretypes.h
+ $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
+ $(srcdir)/config/sh/sh-c.c
+
+symbian-cxx.o: \
+ $(srcdir)/config/sh/symbian-cxx.c \
+ $(srcdir)/config/sh/sh-symbian.h \
+ $(CONFIG_H) $(SYSTEM_H) $(TM_H) $(TREE_H) $(RTL_H) \
+ toplev.h output.h coretypes.h flags.h expr.h
+ $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $<
+
+symbian-c.o: \
+ $(srcdir)/config/sh/symbian-c.c \
+ $(srcdir)/config/sh/sh-symbian.h \
+ $(CONFIG_H) $(SYSTEM_H) $(TM_H) $(TREE_H) $(RTL_H) \
+ toplev.h output.h coretypes.h flags.h expr.h
+ $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $<
+
+symbian-base.o: \
+ $(srcdir)/config/sh/symbian-base.c \
+ $(srcdir)/config/sh/sh-symbian.h \
+ $(CONFIG_H) $(SYSTEM_H) $(TM_H) $(TREE_H) $(RTL_H) \
+ toplev.h output.h coretypes.h flags.h expr.h
+ $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $<
+
+
+LIB1ASMSRC = sh/lib1funcs.asm
+LIB1ASMFUNCS = _ashiftrt _ashiftrt_n _ashiftlt _lshiftrt _movstr \
+ _movstr_i4 _mulsi3 _sdivsi3 _sdivsi3_i4 _udivsi3 _udivsi3_i4 _set_fpscr \
+ $(LIB1ASMFUNCS_CACHE)
+
+# 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
+
+dp-bit.c: $(srcdir)/config/fp-bit.c
+ cat $(srcdir)/config/fp-bit.c >> 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
+
+$(T)crt1.o: $(srcdir)/config/sh/crt1.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(MULTILIB_CFLAGS) -c -o $(T)crt1.o -x assembler-with-cpp $(srcdir)/config/sh/crt1.asm
+$(T)crti.o: $(srcdir)/config/sh/crti.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(MULTILIB_CFLAGS) -c -o $(T)crti.o -x assembler-with-cpp $(srcdir)/config/sh/crti.asm
+$(T)crtn.o: $(srcdir)/config/sh/crtn.asm $(GCC_PASSES)
+ $(GCC_FOR_TARGET) $(MULTILIB_CFLAGS) -c -o $(T)crtn.o -x assembler-with-cpp $(srcdir)/config/sh/crtn.asm
+
+$(out_object_file): gt-sh.h
+gt-sh.h : s-gtype ; @true
+
+symbian.o: $(srcdir)/config/sh/symbian.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
+ $(RTL_H) output.h flags.h $(TREE_H) expr.h toplev.h $(TM_P_H)
+ $(COMPILER) -c $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) \
+ $(srcdir)/config/sh/symbian.c
+
+
+# Local Variables:
+# mode: Makefile
+# End:
diff --git a/gcc/config/sh/t-vxworks b/gcc/config/sh/t-vxworks
new file mode 100644
index 000000000..66aa7091a
--- /dev/null
+++ b/gcc/config/sh/t-vxworks
@@ -0,0 +1,9 @@
+# Multilibs for VxWorks.
+
+MULTILIB_OPTIONS = mrtp fPIC m2/m3/m4/m4a ml
+# Don't build -fPIC without -mrtp, or -ml without -m3/-m4.
+MULTILIB_EXCEPTIONS = fPIC* ml* mrtp/ml* mrtp/fPIC/ml* *m2/ml*
+MULTILIB_MATCHES = m2=m4-nofpu fPIC=fpic
+
+# Restore a variable from t-vxworks clobbered by t-elf.
+EXTRA_MULTILIB_PARTS =
diff --git a/gcc/config/sh/ushmedia.h b/gcc/config/sh/ushmedia.h
new file mode 100644
index 000000000..2f1f55583
--- /dev/null
+++ b/gcc/config/sh/ushmedia.h
@@ -0,0 +1,1087 @@
+/* Copyright (C) 2000, 2001, 2004, 2005, 2009 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>. */
+
+
+/* ushmedia.h: Intrinsics corresponding to SHmedia instructions that
+ may be executed in both user and privileged mode. */
+
+#ifndef _USHMEDIA_H
+#define _USHMEDIA_H
+
+#if __SHMEDIA__
+#if ! __SH4_NO_FPU
+typedef float __GCC_FV __attribute__ ((vector_size (4 * sizeof (float))));
+typedef float __GCC_MTRX __attribute__ ((vector_size (16 * sizeof (float))));
+#endif
+
+static __inline unsigned long long
+sh_media_MABS_L (unsigned long long mm)
+{
+ typedef float v2si __attribute__ ((mode(V2SI)));
+
+ return (unsigned long long) __builtin_absv2si2 ((v2si) mm);
+}
+
+static __inline unsigned long long
+sh_media_MABS_W (unsigned long long mm)
+{
+ typedef float v4hi __attribute__ ((mode(V4HI)));
+
+ return (unsigned long long) __builtin_absv4hi2 ((v4hi) mm);
+}
+
+static __inline unsigned long long
+sh_media_MADD_L (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v2si __attribute__ ((mode(V2SI)));
+
+ return (unsigned long long) __builtin_addv2si3 ((v2si) mm, (v2si) mn);
+}
+
+static __inline unsigned long long
+sh_media_MADD_W (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v4hi __attribute__ ((mode(V4HI)));
+
+ return (unsigned long long) __builtin_addv4hi3 ((v4hi) mm, (v4hi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MADDS_L (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v2si __attribute__ ((mode(V2SI)));
+
+ return (unsigned long long) __builtin_ssaddv2si3 ((v2si) mm, (v2si) mn);
+}
+
+static __inline unsigned long long
+sh_media_MADDS_UB (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v8qi __attribute__ ((mode(V8QI)));
+
+ return (unsigned long long) __builtin_usaddv8qi3 ((v8qi) mm, (v8qi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MADDS_W (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v4hi __attribute__ ((mode(V4HI)));
+
+ return (unsigned long long) __builtin_ssaddv4hi3 ((v4hi) mm, (v4hi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MCMPEQ_B (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v8qi __attribute__ ((mode(V8QI)));
+
+ return (unsigned long long) __builtin_sh_media_MCMPEQ_B ((v8qi) mm,
+ (v8qi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MCMPEQ_L (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v2si __attribute__ ((mode(V2SI)));
+
+ return (unsigned long long) __builtin_sh_media_MCMPEQ_L ((v2si) mm,
+ (v2si) mn);
+}
+
+static __inline unsigned long long
+sh_media_MCMPEQ_W (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v4hi __attribute__ ((mode(V4HI)));
+
+ return (unsigned long long) __builtin_sh_media_MCMPEQ_W ((v4hi) mm,
+ (v4hi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MCMPGT_UB (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v8qi __attribute__ ((mode(V8QI)));
+
+ return (unsigned long long) __builtin_sh_media_MCMPGT_UB ((v8qi) mm,
+ (v8qi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MCMPGT_L (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v2si __attribute__ ((mode(V2SI)));
+
+ return (unsigned long long) __builtin_sh_media_MCMPGT_L ((v2si) mm,
+ (v2si) mn);
+}
+
+static __inline unsigned long long
+sh_media_MCMPGT_W (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v4hi __attribute__ ((mode(V4HI)));
+
+ return (unsigned long long) __builtin_sh_media_MCMPGT_W ((v4hi) mm,
+ (v4hi) mn);
+}
+
+#define sh_media_MCMV __builtin_sh_media_MCMV
+
+static __inline unsigned long long
+sh_media_MCNVS_LW (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v2si __attribute__ ((mode(V2SI)));
+ typedef unsigned int uv2si __attribute__ ((mode(V2SI)));
+
+ return (unsigned long long) __builtin_sh_media_MCNVS_LW ((v2si) mm,
+ (uv2si) mn);
+}
+
+static __inline unsigned long long
+sh_media_MCNVS_WB (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v4hi __attribute__ ((mode(V4HI)));
+
+ return (unsigned long long) __builtin_sh_media_MCNVS_WB ((v4hi) mm,
+ (v4hi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MCNVS_WUB (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v4hi __attribute__ ((mode(V4HI)));
+
+ return (unsigned long long) __builtin_sh_media_MCNVS_WUB ((v4hi) mm,
+ (v4hi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MEXTR1 (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v8qi __attribute__ ((mode(V8QI)));
+
+ return (unsigned long long) __builtin_sh_media_MEXTR1 ((v8qi) mm,
+ (v8qi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MEXTR2 (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v8qi __attribute__ ((mode(V8QI)));
+
+ return (unsigned long long) __builtin_sh_media_MEXTR2 ((v8qi) mm,
+ (v8qi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MEXTR3 (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v8qi __attribute__ ((mode(V8QI)));
+
+ return (unsigned long long) __builtin_sh_media_MEXTR3 ((v8qi) mm,
+ (v8qi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MEXTR4 (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v8qi __attribute__ ((mode(V8QI)));
+
+ return (unsigned long long) __builtin_sh_media_MEXTR4 ((v8qi) mm,
+ (v8qi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MEXTR5 (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v8qi __attribute__ ((mode(V8QI)));
+
+ return (unsigned long long) __builtin_sh_media_MEXTR5 ((v8qi) mm,
+ (v8qi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MEXTR6 (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v8qi __attribute__ ((mode(V8QI)));
+
+ return (unsigned long long) __builtin_sh_media_MEXTR6 ((v8qi) mm,
+ (v8qi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MEXTR7 (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v8qi __attribute__ ((mode(V8QI)));
+
+ return (unsigned long long) __builtin_sh_media_MEXTR7 ((v8qi) mm,
+ (v8qi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MMACFX_WL (unsigned long long mm, unsigned long long mn,
+ unsigned long long mw)
+{
+ typedef float v2hi __attribute__ ((mode(V2HI)));
+ typedef float v2si __attribute__ ((mode(V2SI)));
+ typedef unsigned int uv2si __attribute__ ((mode(V2SI)));
+
+ long mm_l = (long) mm;
+ long mn_l = (long) mn;
+
+ return ((unsigned long long)
+ __builtin_sh_media_MMACFX_WL ((v2hi) mm_l, (v2hi) mn_l,
+ (uv2si) mw));
+}
+
+static __inline unsigned long long
+sh_media_MMACNFX_WL (unsigned long long mm, unsigned long long mn,
+ unsigned long long mw)
+{
+ typedef float v2hi __attribute__ ((mode(V2HI)));
+ typedef float v2si __attribute__ ((mode(V2SI)));
+ typedef unsigned int uv2si __attribute__ ((mode(V2SI)));
+
+ long mm_l = (long) mm;
+ long mn_l = (long) mn;
+
+ return ((unsigned long long)
+ __builtin_sh_media_MMACNFX_WL ((v2hi) mm_l, (v2hi) mn_l,
+ (uv2si) mw));
+}
+
+static __inline unsigned long long
+sh_media_MMUL_L (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v2si __attribute__ ((mode(V2SI)));
+
+ return (unsigned long long) __builtin_mulv2si3 ((v2si) mm, (v2si) mn);
+}
+
+static __inline unsigned long long
+sh_media_MMUL_W (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v4hi __attribute__ ((mode(V4HI)));
+
+ return (unsigned long long) __builtin_mulv4hi3 ((v4hi) mm, (v4hi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MMULFX_L (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v2si __attribute__ ((mode(V2SI)));
+
+ return (unsigned long long) __builtin_sh_media_MMULFX_L ((v2si) mm,
+ (v2si) mn);
+}
+
+static __inline unsigned long long
+sh_media_MMULFX_W (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v4hi __attribute__ ((mode(V4HI)));
+
+ return (unsigned long long) __builtin_sh_media_MMULFX_W ((v4hi) mm,
+ (v4hi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MMULFXRP_W (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v4hi __attribute__ ((mode(V4HI)));
+
+ return (unsigned long long) __builtin_sh_media_MMULFXRP_W ((v4hi) mm,
+ (v4hi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MMULHI_WL (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v4hi __attribute__ ((mode(V4HI)));
+
+ return (unsigned long long) __builtin_sh_media_MMULHI_WL ((v4hi) mm,
+ (v4hi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MMULLO_WL (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v4hi __attribute__ ((mode(V4HI)));
+
+ return (unsigned long long) __builtin_sh_media_MMULLO_WL ((v4hi) mm,
+ (v4hi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MMULSUM_WQ (unsigned long long mm, unsigned long long mn,
+ unsigned long long mw)
+{
+ typedef unsigned int uv4hi __attribute__ ((mode(V4HI)));
+
+ return __builtin_sh_media_MMULSUM_WQ ((uv4hi) mm, (uv4hi) mn, mw);
+}
+
+static __inline unsigned long long
+sh_media_MPERM_W (unsigned long long mm, unsigned int mn)
+{
+ typedef float v4hi __attribute__ ((mode(V4HI)));
+
+ return (unsigned long long) __builtin_sh_media_MPERM_W ((v4hi) mm, mn);
+}
+
+static __inline unsigned long long
+sh_media_MSAD_UBQ (unsigned long long mm, unsigned long long mn,
+ unsigned long long mw)
+{
+ typedef unsigned int uv8qi __attribute__ ((mode(V8QI)));
+
+ return __builtin_sh_media_MSAD_UBQ ((uv8qi) mm, (uv8qi) mn, mw);
+}
+
+static __inline unsigned long long
+sh_media_MSHALDS_L (unsigned long long mm, unsigned int mn)
+{
+ typedef float v2si __attribute__ ((mode(V2SI)));
+
+ return (unsigned long long) __builtin_sh_media_MSHALDS_L ((v2si) mm, mn);
+}
+
+static __inline unsigned long long
+sh_media_MSHALDS_W (unsigned long long mm, unsigned int mn)
+{
+ typedef float v4hi __attribute__ ((mode(V4HI)));
+
+ return (unsigned long long) __builtin_sh_media_MSHALDS_W ((v4hi) mm, mn);
+}
+
+static __inline unsigned long long
+sh_media_MSHARD_L (unsigned long long mm, unsigned int mn)
+{
+ typedef float v2si __attribute__ ((mode(V2SI)));
+
+ return (unsigned long long) __builtin_ashrv2si3 ((v2si) mm, mn);
+}
+
+static __inline unsigned long long
+sh_media_MSHARD_W (unsigned long long mm, unsigned int mn)
+{
+ typedef float v4hi __attribute__ ((mode(V4HI)));
+
+ return (unsigned long long) __builtin_ashrv4hi3 ((v4hi) mm, mn);
+}
+
+#define sh_media_MSHARDS_Q __builtin_sh_media_MSHARDS_Q
+
+static __inline unsigned long long
+sh_media_MSHFHI_B (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v8qi __attribute__ ((mode(V8QI)));
+
+ return (unsigned long long) __builtin_sh_media_MSHFHI_B ((v8qi) mm,
+ (v8qi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MSHFHI_L (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v2si __attribute__ ((mode(V2SI)));
+
+ return (unsigned long long) __builtin_sh_media_MSHFHI_L ((v2si) mm,
+ (v2si) mn);
+}
+
+static __inline unsigned long long
+sh_media_MSHFHI_W (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v4hi __attribute__ ((mode(V4HI)));
+
+ return (unsigned long long) __builtin_sh_media_MSHFHI_W ((v4hi) mm,
+ (v4hi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MSHFLO_B (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v8qi __attribute__ ((mode(V8QI)));
+
+ return (unsigned long long) __builtin_sh_media_MSHFLO_B ((v8qi) mm,
+ (v8qi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MSHFLO_L (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v2si __attribute__ ((mode(V2SI)));
+
+ return (unsigned long long) __builtin_sh_media_MSHFLO_L ((v2si) mm,
+ (v2si) mn);
+}
+
+static __inline unsigned long long
+sh_media_MSHFLO_W (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v4hi __attribute__ ((mode(V4HI)));
+
+ return (unsigned long long) __builtin_sh_media_MSHFLO_W ((v4hi) mm,
+ (v4hi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MSHLLD_L (unsigned long long mm, unsigned int mn)
+{
+ typedef float v2si __attribute__ ((mode(V2SI)));
+
+ return (unsigned long long) __builtin_ashlv2si3 ((v2si) mm, mn);
+}
+
+static __inline unsigned long long
+sh_media_MSHLLD_W (unsigned long long mm, unsigned int mn)
+{
+ typedef float v4hi __attribute__ ((mode(V4HI)));
+
+ return (unsigned long long) __builtin_ashlv4hi3 ((v4hi) mm, mn);
+}
+
+static __inline unsigned long long
+sh_media_MSHLRD_L (unsigned long long mm, unsigned int mn)
+{
+ typedef float v2si __attribute__ ((mode(V2SI)));
+
+ return (unsigned long long) __builtin_lshrv2si3 ((v2si) mm, mn);
+}
+
+static __inline unsigned long long
+sh_media_MSHLRD_W (unsigned long long mm, unsigned int mn)
+{
+ typedef float v4hi __attribute__ ((mode(V4HI)));
+
+ return (unsigned long long) __builtin_lshrv4hi3 ((v4hi) mm, mn);
+}
+
+static __inline unsigned long long
+sh_media_MSUB_L (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v2si __attribute__ ((mode(V2SI)));
+
+ return (unsigned long long) __builtin_subv2si3 ((v2si) mm, (v2si) mn);
+}
+
+static __inline unsigned long long
+sh_media_MSUB_W (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v4hi __attribute__ ((mode(V4HI)));
+
+ return (unsigned long long) __builtin_subv4hi3 ((v4hi) mm, (v4hi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MSUBS_L (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v2si __attribute__ ((mode(V2SI)));
+
+ return (unsigned long long) __builtin_sssubv2si3 ((v2si) mm, (v2si) mn);
+}
+
+static __inline unsigned long long
+sh_media_MSUBS_UB (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v8qi __attribute__ ((mode(V8QI)));
+
+ return (unsigned long long) __builtin_ussubv8qi3 ((v8qi) mm, (v8qi) mn);
+}
+
+static __inline unsigned long long
+sh_media_MSUBS_W (unsigned long long mm, unsigned long long mn)
+{
+ typedef float v4hi __attribute__ ((mode(V4HI)));
+
+ return (unsigned long long) __builtin_sssubv4hi3 ((v4hi) mm, (v4hi) mn);
+}
+
+#if ! __SH4_NOFPU__
+/* Floating-point Intrinsics */
+
+#define sh_media_FABS_D __builtin_fabs
+#define sh_media_FABS_S __builtin_fabsf
+#define sh_media_FCMPUN_D __builtin_isunordered
+#define sh_media_FCMPUN_S __builtin_isunordered
+
+static __inline float sh_media_FCOSA_S (float fg)
+{
+ union { int i; float f; } u;
+
+ u.f = fg;
+ return __builtin_sh_media_FCOSA_S (u.i);
+}
+
+static __inline float
+sh_media_FGETSCR (void)
+{
+ float f;
+
+ __asm volatile ("fgetscr %0" : "=f" (f));
+ return f;
+}
+
+static __inline float
+sh_media_FIPR_S (const void *fvg, const void *fvh)
+{
+ typedef float v4sf __attribute__ ((mode(V4SF)));
+ v4sf vg = *(v4sf*) fvg;
+ v4sf vh = *(v4sf*) fvh;
+
+ return __builtin_sh_media_FIPR_S (vg, vh);
+}
+
+#if 0
+/* This gives different results for -O0 */
+static __inline float
+sh_media_FMAC_S (float fg, float fh, float fq)
+{
+ return fg * fh + fq;
+}
+#else
+
+#define sh_media_FMAC_S __builtin_sh_media_FMAC_S
+#endif
+
+static __inline long long
+sh_media_FMOV_DQ (double dg)
+{
+ union { long long l; double d; } u;
+
+ u.d = dg;
+ return u.l;
+}
+
+static __inline float
+sh_media_FMOV_LS (int mm)
+{
+ union { int i; float f; } u;
+
+ u.i = mm;
+ return u.f;
+}
+
+static __inline double
+sh_media_FMOV_QD (long long mm)
+{
+ union { long long l; double d; } u;
+
+ u.l = mm;
+ return u.d;
+}
+
+static __inline int
+sh_media_FMOV_SL (float fg)
+{
+ union { int i; float f; } u;
+
+ u.f = fg;
+ return u.i;
+}
+
+static __inline void
+sh_media_FPUTSCR (float fg)
+{
+ __asm volatile ("fputscr %0" : : "f" (fg));
+}
+
+static __inline float sh_media_FSINA_S (float fg)
+{
+ union { int i; float f; } u;
+
+ u.f = fg;
+ return __builtin_sh_media_FSINA_S (u.i);
+}
+
+/* Can't use __builtin_sqrt / __builtin_sqrtf because they still implement
+ error handling unless -ffast-math is used. */
+#define sh_media_FSQRT_D __builtin_sh_media_FSQRT_D
+#define sh_media_FSQRT_S __builtin_sh_media_FSQRT_S
+#define sh_media_FSRRA_S __builtin_sh_media_FSRRA_S
+
+static __inline void
+sh_media_FTRV_S (const void *mtrxg, const void *fvh, void *fvf)
+{
+ typedef float v16sf __attribute__ ((mode(V16SF)));
+ typedef float v4sf __attribute__ ((mode(V4SF)));
+ v16sf mtrx = *(v16sf*) mtrxg;
+ v4sf vh = *(v4sf*) fvh;
+
+ *(v4sf*) fvf = __builtin_sh_media_FTRV_S (mtrx, vh);
+}
+#endif /* ! __SH4_NOFPU__ */
+
+/* Not implemented here: Control and Configuration intrinsics. */
+/* Misaligned Access Support intrinsics */
+
+static __inline unsigned long long
+sh_media_LDHI_L (void *p, int s)
+{
+ return __builtin_sh_media_LDHI_L ((char *)p + s);
+}
+
+static __inline unsigned long long
+sh_media_LDHI_Q (void *p, int s)
+{
+ return __builtin_sh_media_LDHI_Q ((char *)p + s);
+}
+
+static __inline unsigned long long
+sh_media_LDLO_L (void *p, int s)
+{
+ return __builtin_sh_media_LDLO_L ((char *)p + s);
+}
+
+static __inline unsigned long long
+sh_media_LDLO_Q (void *p, int s)
+{
+ return __builtin_sh_media_LDLO_Q ((char *)p + s);
+}
+
+static __inline void
+sh_media_STHI_L (void *p, int s, unsigned int mw)
+{
+ __builtin_sh_media_STHI_L ((char*)p + s, mw);
+}
+
+static __inline void
+sh_media_STHI_Q (void *p, int s, unsigned long long mw)
+{
+ __builtin_sh_media_STHI_Q ((char*)p + s, mw);
+}
+
+static __inline void
+sh_media_STLO_L (void *p, int s, unsigned int mw)
+{
+ __builtin_sh_media_STLO_L ((char*)p + s, mw);
+}
+
+static __inline void
+sh_media_STLO_Q (void *p, int s, unsigned long long mw)
+{
+ __builtin_sh_media_STLO_Q ((char*)p + s, mw);
+}
+
+/* Miscellaneous intrinsics */
+
+#define sh_media_NSB __builtin_sh_media_NSB
+
+static __inline unsigned long long
+sh_media_BYTEREV (unsigned long long mm)
+{
+ typedef float v8qi __attribute__ ((mode(V8QI)));
+
+ return (unsigned long long) __builtin_sh_media_BYTEREV ((v8qi) mm);
+}
+
+__inline__ static unsigned long long
+sh_media_CMVEQ (unsigned long long mm, unsigned long long mn, unsigned long long mw) __attribute__ ((always_inline));
+
+__inline__ static unsigned long long
+sh_media_CMVEQ (unsigned long long mm, unsigned long long mn, unsigned long long mw)
+{
+ return mm == 0 ? mn : mw;
+}
+
+__inline__ static unsigned long long
+sh_media_CMVNE (unsigned long long mm, unsigned long long mn, unsigned long long mw) __attribute__ ((always_inline));
+
+__inline__ static unsigned long long
+sh_media_CMVNE (unsigned long long mm, unsigned long long mn, unsigned long long mw)
+{
+ return mm != 0 ? mn : mw;
+}
+
+static __inline long long
+sh_media_ADDZ_L (unsigned int mm, unsigned int mn)
+{
+ return mm + mn;
+}
+
+/* NOP and Synchronization intrinsics not implemented here. */
+
+static __inline__ void sh_media_PREFO(void *mm, int s)
+{
+ __builtin_sh_media_PREFO (mm + s, 0, 0);
+}
+
+/* Event Handling intrinsics not implemented here. */
+
+/* Old asm stuff */
+
+static __inline__
+void
+sh_media_NOP (void)
+{
+ __asm__ ("nop" : :);
+}
+
+__inline__ static
+unsigned long long
+sh_media_SWAP_Q (void *mm, long long mn, unsigned long long mw)
+{
+ unsigned long long res;
+ unsigned long long *addr = (unsigned long long *)((char *)mm + mn);
+ __asm__ ("swap.q %m1, %0" : "=r" (res), "+o" (*addr) : "0" (mw));
+ return res;
+}
+
+__inline__ static
+void
+sh_media_SYNCI (void)
+{
+ __asm__ __volatile__ ("synci");
+}
+
+__inline__ static
+void
+sh_media_SYNCO (void)
+{
+ __asm__ __volatile__ ("synco");
+}
+
+__inline__ static
+void
+sh_media_ALLOCO (void *mm, int s)
+{
+ __builtin_sh_media_ALLOCO (mm + s);
+}
+
+__inline__ static
+void
+sh_media_ICBI (void *mm, int s)
+{
+ __asm__ __volatile__ ("icbi %m0" : : "o" (((char*)mm)[s]));
+}
+
+__inline__ static
+void
+sh_media_OCBI (void *mm, int s)
+{
+ __asm__ __volatile__ ("ocbi %m0" : : "o" (((char*)mm)[s]));
+}
+
+__inline__ static
+void
+sh_media_OCBP (void *mm, int s)
+{
+ __asm__ __volatile__ ("ocbp %m0" : : "o" (((char*)mm)[s]));
+}
+
+__inline__ static
+void
+sh_media_OCBWB (void *mm, int s)
+{
+ __asm__ __volatile__ ("ocbwb %m0" : : "o" (((char*)mm)[s]));
+}
+
+__inline__ static
+void
+sh_media_PREFI (void *mm, int s)
+{
+ __asm__ __volatile__ ("prefi %m0" : : "o" (((char*)mm)[s]));
+}
+
+__inline__ static
+void
+sh_media_BRK (void)
+{
+ __asm__ __volatile__ ("brk");
+}
+
+__inline__ static
+void
+sh_media_TRAPA (unsigned long long mm)
+{
+ __asm__ __volatile__ ("trapa %%0" : : "r" (mm));
+}
+
+__inline__ static
+short
+sh_media_unaligned_LD_W (void *p)
+{
+#if __LITTLE_ENDIAN__
+ return (((unsigned char *)p)[0]
+ | (((short)((__signed__ char *)p)[1]) << 8));
+#else
+ return ((((short)((__signed__ char *)p)[0]) << 8)
+ | ((unsigned char *)p)[1]);
+#endif
+}
+
+__inline__ static
+unsigned short
+sh_media_unaligned_LD_UW (void *p)
+{
+ unsigned char *addr = p;
+#if __LITTLE_ENDIAN__
+ return sh_media_MSHFLO_B (addr[0], addr[1]);
+#else
+ return sh_media_MSHFLO_B (addr[1], addr[0]);
+#endif
+}
+
+/* We don't use the sh_media_LD* functions here because that turned out
+ to impede constant propagation of the offsets into the ldhi / ldlo
+ instructions. */
+__inline__ static
+int
+sh_media_unaligned_LD_L (void *p)
+{
+#if __LITTLE_ENDIAN__
+ return (__builtin_sh_media_LDHI_L ((char *)p + 3)
+ | __builtin_sh_media_LDLO_L (p));
+#else
+ return (__builtin_sh_media_LDLO_L ((char *)p + 3)
+ | __builtin_sh_media_LDHI_L (p));
+#endif
+}
+
+__inline__ static
+long long
+sh_media_unaligned_LD_Q (void *p)
+{
+#if __LITTLE_ENDIAN__
+ return (__builtin_sh_media_LDHI_Q ((char *)p + 7)
+ | __builtin_sh_media_LDLO_Q (p));
+#else
+ return (__builtin_sh_media_LDLO_Q ((char *)p + 7)
+ | __builtin_sh_media_LDHI_Q (p));
+#endif
+}
+
+__inline__ static
+void
+sh_media_unaligned_ST_W (void *p, unsigned int k)
+{
+ char *addr = p;
+#if __LITTLE_ENDIAN__
+ addr[0] = k;
+ addr[1] = k >> 8;
+#else
+ addr[1] = k;
+ addr[0] = k >> 8;
+#endif
+}
+
+/* We don't use the sh_media_ST* functions here because that turned out
+ to impede constant propagation of the offsets into the ldhi / ldlo
+ instructions. */
+__inline__ static
+void
+sh_media_unaligned_ST_L (void *p, unsigned int k)
+{
+#if __LITTLE_ENDIAN__
+ __builtin_sh_media_STHI_L (p + 3, k);
+ __builtin_sh_media_STLO_L (p, k);
+#else
+ __builtin_sh_media_STLO_L (p + 3, k);
+ __builtin_sh_media_STHI_L (p, k);
+#endif
+}
+
+__inline__ static
+void
+sh_media_unaligned_ST_Q (void *p, unsigned long long k)
+{
+#if __LITTLE_ENDIAN__
+ __builtin_sh_media_STHI_Q (p + 7, k);
+ __builtin_sh_media_STLO_Q (p, k);
+#else
+ __builtin_sh_media_STLO_Q (p + 7, k);
+ __builtin_sh_media_STHI_Q (p, k);
+#endif
+}
+
+#if ! __SH4_NOFPU__
+__inline__ static
+void
+sh_media_FVCOPY_S (const void *fvg, void *fvf)
+{
+ const __GCC_FV *g = fvg;
+ __GCC_FV *f = fvf;
+ *f = *g;
+}
+
+__inline__ static
+void
+sh_media_FVADD_S (const void *fvg, const void *fvh, void *fvf)
+{
+ const float *g = fvg, *h = fvh;
+ float *f = fvf;
+#if 1
+ int i;
+
+ for (i = 0; i < 4; i++)
+ f[i] = g[i] + h[i];
+#else
+ f[0] = g[0] + h[0];
+ f[1] = g[1] + h[1];
+ f[2] = g[2] + h[2];
+ f[3] = g[3] + h[3];
+#endif
+}
+
+__inline__ static
+void
+sh_media_FVSUB_S (const void *fvg, const void *fvh, void *fvf)
+{
+ const float *g = fvg, *h = fvh;
+ float *f = fvf;
+#if 1
+ int i;
+
+ for (i = 0; i < 4; i++)
+ f[i] = g[i] - h[i];
+#else
+ f[0] = g[0] - h[0];
+ f[1] = g[1] - h[1];
+ f[2] = g[2] - h[2];
+ f[3] = g[3] - h[3];
+#endif
+}
+
+__inline__ static
+void
+sh_media_FMTRXCOPY_S (const void *mtrxg, void *mtrxf)
+{
+ const __GCC_MTRX *g = mtrxg;
+ __GCC_MTRX *f = mtrxf;
+ *f = *g;
+}
+
+__inline__ static
+void
+sh_media_FMTRXADD_S (const void *mtrxg, const void *mtrxh, void *mtrxf)
+{
+ const __GCC_FV *g = mtrxg, *h = mtrxh;
+ __GCC_FV *f = mtrxf;
+#if 1
+ int i;
+
+ for (i = 0; i < 4; i++)
+ sh_media_FVADD_S (&g[i], &h[i], &f[i]);
+#else
+ sh_media_FVADD_S (&g[0], &h[0], &f[0]);
+ sh_media_FVADD_S (&g[1], &h[1], &f[1]);
+ sh_media_FVADD_S (&g[2], &h[2], &f[2]);
+ sh_media_FVADD_S (&g[3], &h[3], &f[3]);
+#endif
+}
+
+__inline__ static
+void
+sh_media_FMTRXSUB_S (const void *mtrxg, const void *mtrxh, void *mtrxf)
+{
+ const __GCC_FV *g = mtrxg, *h = mtrxh;
+ __GCC_FV *f = mtrxf;
+#if 1
+ int i;
+
+ for (i = 0; i < 4; i++)
+ sh_media_FVSUB_S (&g[i], &h[i], &f[i]);
+#else
+ sh_media_FVSUB_S (&g[0], &h[0], &f[0]);
+ sh_media_FVSUB_S (&g[1], &h[1], &f[1]);
+ sh_media_FVSUB_S (&g[2], &h[2], &f[2]);
+ sh_media_FVSUB_S (&g[3], &h[3], &f[3]);
+#endif
+}
+
+__inline__ static
+void
+sh_media_FTRVADD_S (const void *mtrxg, const void *fvh, const void *fvi, void *fvf)
+{
+ sh_media_FTRV_S (mtrxg, fvh, fvf);
+ sh_media_FVADD_S (fvf, fvi, fvf);
+}
+
+__inline__ static
+void
+sh_media_FTRVSUB_S (const void *mtrxg, const void *fvh, const void *fvi, void *fvf)
+{
+ sh_media_FTRV_S (mtrxg, fvh, fvf);
+ sh_media_FVSUB_S (fvf, fvi, fvf);
+}
+
+__inline__ static
+void
+sh_media_FMTRXMUL_S (const void *mtrxg, const void *mtrxh, void *mtrxf)
+{
+ const __GCC_FV *g = mtrxg;
+ __GCC_FV *f = mtrxf;
+#if 1
+ int j;
+
+ for (j = 0; j < 4; j++)
+ sh_media_FTRV_S (mtrxh, &g[j], &f[j]);
+#else
+ sh_media_FTRV_S (mtrxh, &g[0], &f[0]);
+ sh_media_FTRV_S (mtrxh, &g[1], &f[1]);
+ sh_media_FTRV_S (mtrxh, &g[2], &f[2]);
+ sh_media_FTRV_S (mtrxh, &g[3], &f[3]);
+#endif
+}
+
+__inline__ static
+void
+sh_media_FMTRXMULADD_S (const void *mtrxg, const void *mtrxh, const void *mtrxi, void *mtrxf)
+{
+ const __GCC_FV *g = mtrxg, *i = mtrxi;
+ __GCC_FV *f = mtrxf;
+#if 1
+ int j;
+
+ for (j = 0; j < 4; j++)
+ sh_media_FTRVADD_S (mtrxh, &g[j], &i[j], &f[j]);
+#else
+ sh_media_FTRVADD_S (mtrxh, &g[0], &i[0], &f[0]);
+ sh_media_FTRVADD_S (mtrxh, &g[1], &i[1], &f[1]);
+ sh_media_FTRVADD_S (mtrxh, &g[2], &i[2], &f[2]);
+ sh_media_FTRVADD_S (mtrxh, &g[3], &i[3], &f[3]);
+#endif
+}
+
+__inline__ static
+void
+sh_media_FMTRXMULSUB_S (const void *mtrxg, const void *mtrxh, const void *mtrxi, void *mtrxf)
+{
+ const __GCC_FV *g = mtrxg, *i = mtrxi;
+ __GCC_FV *f = mtrxf;
+#if 1
+ int j;
+
+ for (j = 0; j < 4; j++)
+ sh_media_FTRVSUB_S (mtrxh, &g[j], &i[j], &f[j]);
+#else
+ sh_media_FTRVSUB_S (mtrxh, &g[0], &i[0], &f[0]);
+ sh_media_FTRVSUB_S (mtrxh, &g[1], &i[1], &f[1]);
+ sh_media_FTRVSUB_S (mtrxh, &g[2], &i[2], &f[2]);
+ sh_media_FTRVSUB_S (mtrxh, &g[3], &i[3], &f[3]);
+#endif
+}
+#endif /* ! __SH4_NOFPU__ */
+
+#endif /* __SHMEDIA__ */
+
+#endif /* _USHMEDIA_H */
diff --git a/gcc/config/sh/vxworks.h b/gcc/config/sh/vxworks.h
new file mode 100644
index 000000000..3276979e4
--- /dev/null
+++ b/gcc/config/sh/vxworks.h
@@ -0,0 +1,69 @@
+/* Definitions of target machine for GCC,
+ for SuperH with targeting the VXWorks run time environment.
+ Copyright (C) 2003, 2004, 2007 Free Software Foundation, Inc.
+ Contributed by CodeSourcery, LLC.
+
+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
+<http://www.gnu.org/licenses/>. */
+
+
+#define TARGET_OS_CPP_BUILTINS() \
+ do \
+ { \
+ builtin_define ("CPU=SH7000"); \
+ VXWORKS_OS_CPP_BUILTINS (); \
+ } \
+ while (0)
+
+#undef SUBTARGET_OVERRIDE_OPTIONS
+#define SUBTARGET_OVERRIDE_OPTIONS \
+ do \
+ { \
+ VXWORKS_OVERRIDE_OPTIONS; \
+ /* The kernel loader cannot handle the relaxation \
+ relocations, so it cannot load kernel modules \
+ (which are ET_REL) or RTP executables (which are \
+ linked with --emit-relocs). No relaxation relocations \
+ appear in shared libraries, so relaxation is OK \
+ for RTP PIC. */ \
+ if (TARGET_RELAX && !(TARGET_VXWORKS_RTP && flag_pic)) \
+ error ("-mrelax is only supported for RTP PIC"); \
+ } \
+ while (0)
+
+#undef SUBTARGET_CPP_SPEC
+#define SUBTARGET_CPP_SPEC VXWORKS_ADDITIONAL_CPP_SPEC
+
+#undef SUBTARGET_LINK_EMUL_SUFFIX
+#define SUBTARGET_LINK_EMUL_SUFFIX "_vxworks"
+
+#undef LIB_SPEC
+#define LIB_SPEC VXWORKS_LIB_SPEC
+#undef LINK_SPEC
+#define LINK_SPEC VXWORKS_LINK_SPEC " " SH_LINK_SPEC
+#undef STARTFILE_SPEC
+#define STARTFILE_SPEC VXWORKS_STARTFILE_SPEC
+#undef ENDFILE_SPEC
+#define ENDFILE_SPEC VXWORKS_ENDFILE_SPEC
+
+#undef TARGET_VERSION
+#define TARGET_VERSION fputs (" (SH/VxWorks)", stderr);
+
+/* There is no default multilib. */
+#undef MULTILIB_DEFAULTS
+
+#undef FUNCTION_PROFILER
+#define FUNCTION_PROFILER VXWORKS_FUNCTION_PROFILER