;;- Machine description for Renesas / SuperH SH.
;; Copyright (C) 1993-2015 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.
;; 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)
;; Virtual FPSCR - bits that are used by FP ops.
(FPSCR_MODES_REG 154)
;; Virtual FPSCR - bits that are updated by FP ops.
(FPSCR_STAT_REG 155)
(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)
(FPSCR_PR 524288) ;; 1 << 19
(FPSCR_SZ 1048576) ;; 1 << 20
(FPSCR_FR 2097152) ;; 1 << 21
])
(define_c_enum "unspec" [
;; These are used with unspec.
UNSPEC_COMPACT_ARGS
UNSPEC_MOVA
UNSPEC_CASESI
UNSPEC_DATALABEL
UNSPEC_BBR
UNSPEC_SFUNC
UNSPEC_PIC
UNSPEC_GOT
UNSPEC_GOTOFF
UNSPEC_PLT
UNSPEC_CALLER
UNSPEC_GOTPLT
UNSPEC_PCREL
UNSPEC_ICACHE
UNSPEC_INIT_TRAMP
UNSPEC_FCOSA
UNSPEC_FSRRA
UNSPEC_FSINA
UNSPEC_NSB
UNSPEC_ALLOCO
UNSPEC_TLSGD
UNSPEC_TLSLDM
UNSPEC_TLSIE
UNSPEC_DTPOFF
UNSPEC_GOTTPOFF
UNSPEC_TPOFF
UNSPEC_RA
UNSPEC_DIV_INV_M0
UNSPEC_DIV_INV_M1
UNSPEC_DIV_INV_M2
UNSPEC_DIV_INV_M3
UNSPEC_DIV_INV20
UNSPEC_DIV_INV_TABLE
UNSPEC_ASHIFTRT
UNSPEC_THUNK
UNSPEC_CHKADD
UNSPEC_SP_SET
UNSPEC_SP_TEST
UNSPEC_MOVUA
;; (unspec [VAL SHIFT] UNSPEC_EXTRACT_S16) computes (short) (VAL >> SHIFT).
;; UNSPEC_EXTRACT_U16 is the unsigned equivalent.
UNSPEC_EXTRACT_S16
UNSPEC_EXTRACT_U16
;; (unspec [TARGET ANCHOR] UNSPEC_SYMOFF) == TARGET - ANCHOR.
UNSPEC_SYMOFF
;; (unspec [OFFSET ANCHOR] UNSPEC_PCREL_SYMOFF) == OFFSET - (ANCHOR - .).
UNSPEC_PCREL_SYMOFF
;; Misc builtins
UNSPEC_BUILTIN_STRLEN
])
(define_c_enum "unspecv" [
;; These are used with unspec_volatile.
UNSPECV_BLOCKAGE
UNSPECV_ALIGN
UNSPECV_CONST2
UNSPECV_CONST4
UNSPECV_CONST8
UNSPECV_WINDOW_END
UNSPECV_CONST_END
UNSPECV_EH_RETURN
UNSPECV_GBR
UNSPECV_SP_SWITCH_B
UNSPECV_SP_SWITCH_E
UNSPECV_FPSCR_MODES
UNSPECV_FPSCR_STAT
])
;; -------------------------------------------------------------------------
;; 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 [(match_test "mdep_reorg_phase <= SH_FIXUP_PCLOAD")
(const_string "no")
(leu (plus (minus (match_dup 0) (pc)) (const_int 252)) (const_int 506))
(const_string "yes")
(match_test "NEXT_INSN (PREV_INSN (insn)) != insn")
(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")
(match_test "mdep_reorg_phase <= SH_FIXUP_PCLOAD")
(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")
(match_test "mdep_reorg_phase <= SH_FIXUP_PCLOAD")
(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 [(match_test "! TARGET_SH2")
(const_string "no")
(leu (plus (minus (match_dup 0) (pc)) (const_int 10330))
(const_int 20660))
(const_string "yes")
(match_test "mdep_reorg_phase <= SH_FIXUP_PCLOAD")
(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 [(match_test "! TARGET_SH2")
(const_string "no")
(leu (plus (minus (match_dup 0) (pc)) (const_int 10328))
(const_int 20658))
(const_string "yes")
(match_test "mdep_reorg_phase <= SH_FIXUP_PCLOAD")
(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)
(match_test "flag_pic")
(const_int 24)
] (const_int 16))
(eq_attr "type" "jump")
(cond [(eq_attr "med_branch_p" "yes")
(const_int 2)
(and (match_test "prev_nonnote_insn (insn)")
(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)
(not (match_test "flag_pic"))
(const_int 10)
(match_test "TARGET_SH2")
(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)
(match_test "flag_pic")
(const_int 22)
] (const_int 14))
(eq_attr "type" "pt_media")
(if_then_else (match_test "TARGET_SHMEDIA64")
(const_int 20) (const_int 12))
(and (eq_attr "type" "jump_media")
(match_test "TARGET_SH5_CUT2_WORKAROUND"))
(const_int 8)
] (if_then_else (match_test "TARGET_SHMEDIA")
(const_int 4)
(const_int 2))))
;; DFA descriptions for the pipelines
(include "sh1.md")
(include "shmedia.md")
(include "sh4.md")
(include "iterators.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 [(match_test "sh_loads_bankedreg_p (insn)")
(const_string "yes")]
(const_string "no")))
;; ??? This should be (nil) instead of (const_int 0)
(define_attr "hit_stack" "yes,no"
(cond [(not (match_test "find_regno_note (insn, REG_INC, SP_REG)"))
(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 "type" "fpscr_toggle") (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)])
;; Since a normal return (rts) implicitly uses the PR register,
;; we can't allow PR register loads in an rts delay slot.
;; On the SH1* 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* and SH4*, the rte instruction does not use the stack, so a
;; pop instruction can go in the delay slot, unless it references a banked
;; register (the register bank is switched by rte).
(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 (match_test "TARGET_SH3") (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)])
;; Conditional branches with delay slots are available starting with SH2.
(define_delay
(and (eq_attr "type" "cbranch") (match_test "TARGET_SH2"))
[(eq_attr "cond_delay_slot" "yes") (nil) (nil)])
;; -------------------------------------------------------------------------
;; SImode signed integer comparisons
;; -------------------------------------------------------------------------
;; Patterns to generate the tst instruction which are usually formed by
;; the combine pass.
;; The canonical form here being used is (eq (and (op) (op)) 0).
;; For some bit patterns, such as contiguous bits, we also must accept
;; zero_extract forms. Single bit tests are also handled via zero_extract
;; patterns in the 'bit field extract patterns' section. All variants
;; are eventually converted to the 'tstsi_t' insn.
;; As long as pseudos can be created (before RA), 'tstsi_t' will also accept
;; constants that won't fit into 8 bits. After having captured the constant
;; we can decide better whether/how to load it into a register and do other
;; post-combine optimizations such as bypassing sign/zero extensions.
(define_insn_and_split "tstsi_t"
[(set (reg:SI T_REG)
(eq:SI (and:SI (match_operand:SI 0 "arith_reg_operand" "%z,r")
(match_operand:SI 1 "arith_or_int_operand" "K08,?r"))
(const_int 0)))]
"TARGET_SH1
&& (can_create_pseudo_p () || arith_reg_operand (operands[1], SImode)
|| satisfies_constraint_K08 (operands[1]))"
"tst %1,%0"
"TARGET_SH1 && can_create_pseudo_p () && CONST_INT_P (operands[1])
&& !sh_in_recog_treg_set_expr ()"
[(const_int 0)]
{
gcc_assert (CONST_INT_P (operands[1]));
HOST_WIDE_INT op1val = INTVAL (operands[1]);
bool op0_dead_after_this =
sh_reg_dead_or_unused_after_insn (curr_insn, REGNO (operands[0]));
if (optimize)
{
if (dump_file)
fprintf (dump_file,
"tstsi_t: trying to optimize const_int 0x%08x\n",
(uint32_t)op1val);
/* See if we can convert a test with a reg and a constant into
something simpler, if the reg is known to be zero or sign
extended. */
sh_extending_set_of_reg eop0 = sh_find_extending_set_of_reg (operands[0],
curr_insn);
if (eop0.ext_code != UNKNOWN)
{
/* Adjust the constant, trying to eliminate bits that are not
contributing to the result. */
if (eop0.from_mode == QImode)
op1val = (op1val
| (eop0.ext_code == SIGN_EXTEND && (op1val & 0xFFFFFF80)
? 0x80 : 0)) & 0xFF;
else if (eop0.from_mode == HImode)
op1val = (op1val
| (eop0.ext_code == SIGN_EXTEND && (op1val & 0xFFFF8000)
? 0x8000 : 0)) & 0xFFFF;
if (dump_file)
fprintf (dump_file, "tstsi_t: using effective const_int: 0x%08x\n",
(uint32_t)op1val);
/* Try to bypass the sign/zero extension first if op0 dies after
this insn. */
if (op0_dead_after_this && eop0.can_use_as_unextended_reg ())
{
if (dump_file)
fprintf (dump_file, "tstsi_t: bypassing sign/zero extension\n");
operands[0] = eop0.use_as_unextended_reg (curr_insn);
}
else if ((eop0.from_mode == QImode && op1val == 0xFF)
|| (eop0.from_mode == HImode && op1val == 0xFFFF))
{
if (dump_file)
fprintf (dump_file, "tstsi_t: converting to cmpeqsi_t\n");
emit_insn (gen_cmpeqsi_t (eop0.use_as_extended_reg (curr_insn),
const0_rtx));
DONE;
}
else if (eop0.ext_code == SIGN_EXTEND
&& ((eop0.from_mode == QImode && op1val == 0x80)
|| (eop0.from_mode == HImode && op1val == 0x8000)))
{
if (dump_file)
fprintf (dump_file, "tstsi_t: converting to cmpgesi_t\n");
emit_insn (gen_cmpgesi_t (eop0.use_as_extended_reg (curr_insn),
const0_rtx));
DONE;
}
else if (!CONST_OK_FOR_K08 (op1val))
{
if (dump_file)
fprintf (dump_file, "tstsi_t: converting const_int to signed "
"value\n");
/* If here we haven't done anything yet. Convert the constant
to a signed value to reduce the constant pool size. */
operands[0] = eop0.use_as_extended_reg (curr_insn);
if (eop0.from_mode == QImode)
op1val |= (op1val & 0x80) ? 0xFFFFFFFFFFFFFF00LL : 0;
else if (eop0.from_mode == HImode)
op1val |= (op1val & 0x8000) ? 0xFFFFFFFFFFFF0000LL : 0;
}
else
operands[0] = eop0.use_as_extended_reg (curr_insn);
}
}
if (dump_file)
fprintf (dump_file, "tstsi_t: using const_int 0x%08x\n",
(uint32_t)op1val);
/* Try to fit the constant into 8 bits by shuffling the value in the
register operand.
Doing that usually results in smaller code as the constants in the
pools are avoided (32 bit constant = load + constant = 6 bytes).
However, if the constant load (LS insn) can be hoisted insn dependencies
can be avoided and chances for parallel execution increase. The common
usage pattern is:
- load reg from mem
- test bits
- conditional branch
FIXME: For now we do that only when optimizing for size until there is
a better heuristic.
FIXME: If there are multiple tst insns in the block with the same
constant, avoid the #imm variant to avoid R0 loads. Use the 'tst Rn,Rm'
variant instead and load the constant into a reg. For that we'd need
to do some analysis. */
if (CONST_OK_FOR_K08 (op1val))
{
/* Do nothing. */
}
else if ((op1val & 0xFFFF) == 0
&& CONST_OK_FOR_K08 (op1val >> 16) && optimize_size)
{
/* Use a swap.w insn to do a shift + reg copy (to R0) in one insn. */
op1val = op1val >> 16;
rtx r = gen_reg_rtx (SImode);
emit_insn (gen_rotlsi3_16 (r, operands[0]));
operands[0] = r;
}
else if ((op1val & 0xFF) == 0
&& CONST_OK_FOR_K08 (op1val >> 8) && optimize_size)
{
/* Use a swap.b insn to do a shift + reg copy (to R0) in one insn. */
op1val = op1val >> 8;
rtx r = gen_reg_rtx (SImode);
emit_insn (gen_swapbsi2 (r, operands[0]));
operands[0] = r;
}
else if ((op1val & 3) == 0
&& CONST_OK_FOR_K08 (op1val >> 2) && optimize_size)
{
op1val = op1val >> 2;
rtx r = gen_reg_rtx (SImode);
emit_insn (gen_lshrsi3_k (r, operands[0], GEN_INT (2)));
operands[0] = r;
}
else if ((op1val & 1) == 0
&& CONST_OK_FOR_K08 (op1val >> 1) && optimize_size)
{
op1val = op1val >> 1;
rtx r = gen_reg_rtx (SImode);
emit_insn (gen_shlr (r, operands[0]));
operands[0] = r;
}
operands[1] = GEN_INT (op1val);
if (!satisfies_constraint_K08 (operands[1]))
operands[1] = force_reg (SImode, operands[1]);
emit_insn (gen_tstsi_t (operands[0], operands[1]));
DONE;
}
[(set_attr "type" "mt_group")])
;; This pattern is used by combine when testing QI/HImode subregs with a
;; negative constant. Ignore high bits by masking them out in the constant.
(define_insn_and_split "*tst<mode>_t"
[(set (reg:SI T_REG)
(eq:SI (subreg:SI
(and:QIHI (match_operand:QIHI 0 "arith_reg_operand")
(match_operand 1 "const_int_operand")) 0)
(const_int 0)))]
"TARGET_SH1 && can_create_pseudo_p ()"
"#"
"&& 1"
[(set (reg:SI T_REG)
(eq:SI (and:SI (match_dup 0) (match_dup 1)) (const_int 0)))]
{
operands[0] = simplify_gen_subreg (SImode, operands[0], <MODE>mode, 0);
operands[1] = GEN_INT (INTVAL (operands[1])
& (<MODE>mode == HImode ? 0xFFFF : 0xFF));
})
;; This pattern might be risky because it also tests the upper bits and not
;; only the subreg. We have to check whether the operands have been sign
;; or zero extended. In the worst case, a zero extension has to be inserted
;; to mask out the unwanted bits.
(define_insn_and_split "*tst<mode>_t_subregs"
[(set (reg:SI T_REG)
(eq:SI
(subreg:QIHI
(and:SI (match_operand:SI 0 "arith_reg_operand")
(match_operand:SI 1 "arith_reg_operand")) <lowpart_le>)
(const_int 0)))]
"TARGET_SH1 && TARGET_LITTLE_ENDIAN && can_create_pseudo_p ()"
"#"
"&& !sh_in_recog_treg_set_expr ()"
[(const_int 0)]
{
sh_split_tst_subregs (curr_insn, <MODE>mode, <lowpart_le>, operands);
DONE;
})
(define_insn_and_split "*tst<mode>_t_subregs"
[(set (reg:SI T_REG)
(eq:SI
(subreg:QIHI
(and:SI (match_operand:SI 0 "arith_reg_operand")
(match_operand:SI 1 "arith_reg_operand")) <lowpart_be>)
(const_int 0)))]
"TARGET_SH1 && TARGET_BIG_ENDIAN && can_create_pseudo_p ()"
"#"
"&& !sh_in_recog_treg_set_expr ()"
[(const_int 0)]
{
sh_split_tst_subregs (curr_insn, <MODE>mode, <lowpart_be>, operands);
DONE;
})
;; Extract contiguous bits and compare them against zero.
;; Notice that this will not be used for single bits. Special single bit
;; extraction patterns are in the 'bit field extract patterns' section.
(define_insn_and_split "*tst<mode>_t_zero_extract"
[(set (reg:SI T_REG)
(eq:SI (zero_extract:SI (match_operand:QIHISI 0 "arith_reg_operand")
(match_operand 1 "const_int_operand")
(match_operand 2 "const_int_operand"))
(const_int 0)))]
"TARGET_SH1 && can_create_pseudo_p ()"
"#"
"&& 1"
[(set (reg:SI T_REG)
(eq:SI (and:SI (match_dup 0) (match_dup 1)) (const_int 0)))]
{
operands[1] = GEN_INT (ZERO_EXTRACT_ANDMASK (operands[1], operands[2]));
if (GET_MODE (operands[0]) != SImode)
operands[0] = simplify_gen_subreg (SImode, operands[0], <MODE>mode, 0);
})
;; Convert '(reg << shift) & mask' into 'reg & (mask >> shift)'.
;; The shifted-out bits in the mask will always be zero, since the
;; shifted-in bits in the reg will also be always zero.
(define_insn_and_split "*tstsi_t_shift_mask"
[(set (reg:SI T_REG)
(eq:SI (and:SI (ashift:SI (match_operand:SI 0 "arith_reg_operand")
(match_operand 1 "const_int_operand"))
(match_operand 2 "const_int_operand"))
(const_int 0)))]
"TARGET_SH1 && can_create_pseudo_p ()"
"#"
"&& 1"
[(set (reg:SI T_REG)
(eq:SI (and:SI (match_dup 0) (match_dup 2)) (const_int 0)))]
{
operands[2] = GEN_INT (INTVAL (operands[2]) >> INTVAL (operands[1]));
})
(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")])
;; Sometimes combine fails to form the (eq (and (op) (op)) 0) tst insn.
;; Try to fix that in the split1 pass by looking for the previous set
;; of the tested op. Also see if there is a preceeding sign/zero
;; extension that can be avoided.
(define_split
[(set (reg:SI T_REG)
(eq:SI (match_operand:SI 0 "arith_reg_operand") (const_int 0)))]
"TARGET_SH1 && can_create_pseudo_p () && optimize
&& !sh_in_recog_treg_set_expr ()"
[(set (reg:SI T_REG) (eq:SI (match_dup 0) (const_int 0)))]
{
if (dump_file)
fprintf (dump_file, "cmpeqsi_t: trying to optimize const_int 0\n");
/* If the tested reg is not dead after this insn, it's probably used by
something else after the comparison. It's probably better to leave
it as it is. */
if (find_regno_note (curr_insn, REG_DEAD, REGNO (operands[0])) == NULL_RTX)
FAIL;
/* FIXME: Maybe also search the predecessor basic blocks to catch
more cases. */
set_of_reg op = sh_find_set_of_reg (operands[0], curr_insn,
prev_nonnote_insn_bb);
if (op.set_src != NULL && GET_CODE (op.set_src) == AND
&& !sh_insn_operands_modified_between_p (op.insn, op.insn, curr_insn))
{
if (dump_file)
fprintf (dump_file, "cmpeqsi_t: found preceeding and in insn %d\n",
INSN_UID (op.insn));
if (!(arith_reg_operand (XEXP (op.set_src, 0), SImode)
&& (arith_reg_operand (XEXP (op.set_src, 1), SImode)
|| CONST_INT_P (XEXP (op.set_src, 1)))))
FAIL;
/* Assume that the operands of the andsi insn are compatible with the
operands of the tstsi_t insn, which is generally the case. */
if (dump_file)
fprintf (dump_file, "cmpeqsi_t: replacing with tstsi_t\n");
emit_insn (gen_tstsi_t (XEXP (op.set_src, 0), XEXP (op.set_src, 1)));
DONE;
}
/* Converting HImode into tests against 0xFFFF tends to increase the code
size, as it will create constant pool entries. Disable it for now. */
const bool enable_himode = false;
/* FIXME: try to keep the (eq (reg) (const_int 0)). Even if the zero
extended reg is used after this insn, if we know that _before_ the zero
extension the value was loaded via sign extending mem load, we can just
use the value of the mem load directly. */
sh_extending_set_of_reg eop = sh_find_extending_set_of_reg (operands[0],
curr_insn);
if (eop.ext_code != UNKNOWN
&& (eop.from_mode == QImode || (eop.from_mode == HImode && enable_himode))
&& eop.can_use_as_unextended_reg ()
&& !reg_used_between_p (operands[0], eop.insn, curr_insn))
{
/* Bypass the sign/zero extension and test against the bit mask, but
only if it's the only use of the sign/zero extracted value.
Otherwise we'd be introducing new constants in the pool. */
if (dump_file)
fprintf (dump_file, "cmpeqsi_t: bypassing sign/zero extension in "
"insn %d and using tstsi_t\n", INSN_UID (op.insn));
emit_insn (gen_tstsi_t (
eop.use_as_unextended_reg (curr_insn),
GEN_INT (eop.from_mode == QImode ? 0xFF : 0xFFFF)));
DONE;
}
if (dump_file)
fprintf (dump_file, "cmpeqsi_t: nothing optimized\n");
FAIL;
})
;; FIXME: For some reason, on SH4A and SH2A combine fails to simplify this
;; pattern by itself. What this actually does is:
;; x == 0: (1 >> 0-0) & 1 = 1
;; x != 0: (1 >> 0-x) & 1 = 0
;; Without this the test pr51244-8.c fails on SH2A and SH4A.
(define_insn_and_split "*cmpeqsi_t"
[(set (reg:SI T_REG)
(and:SI (lshiftrt:SI
(const_int 1)
(neg:SI (match_operand:SI 0 "arith_reg_operand" "r")))
(const_int 1)))]
"TARGET_SH1"
"#"
"&& 1"
[(set (reg:SI T_REG) (eq:SI (match_dup 0) (const_int 0)))])
(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" "N,r")))]
"TARGET_SH1"
"@
cmp/pl %0
cmp/gt %1,%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" "N,r")))]
"TARGET_SH1"
"@
cmp/pz %0
cmp/ge %1,%0"
[(set_attr "type" "mt_group")])
;; Recombine a cmp/pz followed by a nott into a shll.
;; On non-SH2A recombine a cmp/pz followed by a movrt into shll-movt.
;; On SH2A cmp/pz-movrt is slightly better, as it does not mutate the input.
(define_split
[(set (reg:SI T_REG)
(ge:SI (match_operand:SI 0 "arith_reg_operand") (const_int 0)))]
"TARGET_SH1 && can_create_pseudo_p () && optimize
&& !sh_in_recog_treg_set_expr ()"
[(const_int 0)]
{
if (dump_file)
fprintf (dump_file, "cmpgesi_t: trying to optimize for const_int 0\n");
rtx_insn* i = next_nonnote_insn_bb (curr_insn);
if (dump_file)
{
fprintf (dump_file, "cmpgesi_t: following insn is \n");
print_rtl_single (dump_file, i);
fprintf (dump_file, "\n");
}
if (sh_is_nott_insn (i))
{
if (dump_file)
fprintf (dump_file,
"cmpgesi_t: replacing (cmp/pz, nott) with (shll)\n");
emit_insn (gen_shll (gen_reg_rtx (SImode), operands[0]));
set_insn_deleted (i);
DONE;
}
/* On non-SH2A negc is used as movrt replacement, which sets T = 1.
Thus we can remove it only if T is marked as dead afterwards. */
if (rtx dest_reg = !TARGET_SH2A
&& sh_reg_dead_or_unused_after_insn (i, T_REG)
? sh_movrt_set_dest (i) : NULL)
{
if (dump_file)
fprintf (dump_file,
"cmpgesi_t: replacing (cmp/pz, movrt) with (shll, movt)\n");
emit_insn (gen_shll (gen_reg_rtx (SImode), operands[0]));
add_reg_note (emit_insn (gen_movt (dest_reg, get_t_reg_rtx ())),
REG_DEAD, get_t_reg_rtx ());
set_insn_deleted (i);
DONE;
}
if (dump_file)
fprintf (dump_file, "cmpgesi_t: nothing optimized\n");
FAIL;
})
;; FIXME: This is actually wrong. There is no way to literally move a
;; general reg to t reg. Luckily, it seems that this pattern will be only
;; used when the general reg is known be either '0' or '1' during combine.
;; What we actually need is reg != 0 -> T, but we have only reg == 0 -> T.
;; Due to interactions with other patterns, combine fails to pick the latter
;; and invert the dependent logic.
(define_insn "*negtstsi"
[(set (reg:SI T_REG) (match_operand:SI 0 "arith_reg_operand" "r"))]
"TARGET_SH1 && !sh_in_recog_treg_set_expr ()"
"cmp/pl %0"
[(set_attr "type" "mt_group")])
;; Some integer sign comparison patterns can be realized with the div0s insn.
;; div0s Rm,Rn T = (Rm >> 31) ^ (Rn >> 31)
;;
;; The 'cmp_div0s' pattern is our canonical form, into which all the other
;; variations are converted. The negative forms will split into a trailing
;; nott sequence, which will be eliminated either by the
;; 'any_treg_expr_to_reg' pattern, or by the 'sh_treg_combine' pass.
(define_insn "cmp_div0s"
[(set (reg:SI T_REG)
(lshiftrt:SI (xor:SI (match_operand:SI 0 "arith_reg_operand" "%r")
(match_operand:SI 1 "arith_reg_operand" "r"))
(const_int 31)))]
"TARGET_SH1"
"div0s %0,%1"
[(set_attr "type" "arith")])
(define_insn_and_split "*cmp_div0s_1"
[(set (reg:SI T_REG)
(xor:SI (ge:SI (match_operand:SI 0 "arith_reg_operand")
(const_int 0))
(ge:SI (match_operand:SI 1 "arith_reg_operand")
(const_int 0))))]
"TARGET_SH1 && can_create_pseudo_p ()"
"#"
"&& 1"
[(set (reg:SI T_REG)
(lshiftrt:SI (xor:SI (match_dup 0) (match_dup 1)) (const_int 31)))])
(define_insn_and_split "*cmp_div0s_2"
[(set (reg:SI T_REG)
(eq:SI (lshiftrt:SI (match_operand:SI 0 "arith_reg_operand")
(const_int 31))
(ge:SI (match_operand:SI 1 "arith_reg_operand")
(const_int 0))))]
"TARGET_SH1 && can_create_pseudo_p ()"
"#"
"&& 1"
[(set (reg:SI T_REG)
(lshiftrt:SI (xor:SI (match_dup 0) (match_dup 1)) (const_int 31)))])
(define_insn_and_split "*cmp_div0s_3"
[(set (reg:SI T_REG)
(eq:SI (ge:SI (match_operand:SI 0 "arith_reg_operand")
(const_int 0))
(ge:SI (match_operand:SI 1 "arith_reg_operand")
(const_int 0))))]
"TARGET_SH1 && can_create_pseudo_p ()"
"#"
"&& 1"
[(set (reg:SI T_REG)
(lshiftrt:SI (xor:SI (match_dup 0) (match_dup 1)) (const_int 31)))
(set (reg:SI T_REG) (xor:SI (reg:SI T_REG) (const_int 1)))])
(define_insn_and_split "*cmp_div0s_4"
[(set (reg:SI T_REG)
(ge:SI (xor:SI (match_operand:SI 0 "arith_reg_operand")
(match_operand:SI 1 "arith_reg_operand"))
(const_int 0)))]
"TARGET_SH1 && can_create_pseudo_p ()"
"#"
"&& 1"
[(set (reg:SI T_REG)
(lshiftrt:SI (xor:SI (match_dup 0) (match_dup 1)) (const_int 31)))
(set (reg:SI T_REG) (xor:SI (reg:SI T_REG) (const_int 1)))])
(define_insn_and_split "*cmp_div0s_5"
[(set (reg:SI T_REG)
(xor:SI (lshiftrt:SI (match_operand:SI 0 "arith_reg_operand")
(const_int 31))
(ge:SI (match_operand:SI 1 "arith_reg_operand")
(const_int 0))))]
"TARGET_SH1 && can_create_pseudo_p ()"
"#"
"&& 1"
[(set (reg:SI T_REG)
(lshiftrt:SI (xor:SI (match_dup 0) (match_dup 1)) (const_int 31)))
(set (reg:SI T_REG) (xor:SI (reg:SI T_REG) (const_int 1)))])
(define_insn_and_split "*cmp_div0s_6"
[(set (reg:SI T_REG)
(eq:SI (lshiftrt:SI (match_operand:SI 0 "arith_reg_operand")
(const_int 31))
(lshiftrt:SI (match_operand:SI 1 "arith_reg_operand")
(const_int 31))))]
"TARGET_SH1 && can_create_pseudo_p ()"
"#"
"&& 1"
[(set (reg:SI T_REG)
(lshiftrt:SI (xor:SI (match_dup 0) (match_dup 1)) (const_int 31)))
(set (reg:SI T_REG) (xor:SI (reg:SI T_REG) (const_int 1)))])
;; -------------------------------------------------------------------------
;; 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
expand_cbranchsi4 (operands, LAST_AND_UNUSED_RTX_CODE, -1);
DONE;
})
;; Combine patterns to invert compare and branch operations for which we
;; don't have actual comparison insns. These patterns are used in cases
;; which appear after the initial cbranchsi expansion, which also does
;; some condition inversion.
(define_split
[(set (pc)
(if_then_else (ne (match_operand:SI 0 "arith_reg_operand" "")
(match_operand:SI 1 "arith_reg_or_0_operand" ""))
(label_ref (match_operand 2))
(pc)))
(clobber (reg:SI T_REG))]
"TARGET_SH1"
[(set (reg:SI T_REG) (eq:SI (match_dup 0) (match_dup 1)))
(set (pc) (if_then_else (eq (reg:SI T_REG) (const_int 0))
(label_ref (match_dup 2))
(pc)))])
;; FIXME: Similar to the *cmpeqsi_t pattern above, for some reason, on SH4A
;; and SH2A combine fails to simplify this pattern by itself.
;; What this actually does is:
;; x == 0: (1 >> 0-0) & 1 = 1
;; x != 0: (1 >> 0-x) & 1 = 0
;; Without this the test pr51244-8.c fails on SH2A and SH4A.
(define_split
[(set (pc)
(if_then_else
(eq (and:SI (lshiftrt:SI
(const_int 1)
(neg:SI (match_operand:SI 0 "arith_reg_operand" "")))
(const_int 1))
(const_int 0))
(label_ref (match_operand 2))
(pc)))
(clobber (reg:SI T_REG))]
"TARGET_SH1"
[(set (reg:SI T_REG) (eq:SI (match_dup 0) (const_int 0)))
(set (pc) (if_then_else (eq (reg:SI T_REG) (const_int 0))
(label_ref (match_dup 2))
(pc)))])
;; FIXME: These could probably use code iterators for the compare op.
(define_split
[(set (pc)
(if_then_else (le (match_operand:SI 0 "arith_reg_operand" "")
(match_operand:SI 1 "arith_reg_or_0_operand" ""))
(label_ref (match_operand 2))
(pc)))
(clobber (reg:SI T_REG))]
"TARGET_SH1"
[(set (reg:SI T_REG) (gt:SI (match_dup 0) (match_dup 1)))
(set (pc) (if_then_else (eq (reg:SI T_REG) (const_int 0))
(label_ref (match_dup 2))
(pc)))])
(define_split
[(set (pc)
(if_then_else (lt (match_operand:SI 0 "arith_reg_operand" "")
(match_operand:SI 1 "arith_reg_or_0_operand" ""))
(label_ref (match_operand 2))
(pc)))
(clobber (reg:SI T_REG))]
"TARGET_SH1"
[(set (reg:SI T_REG) (ge:SI (match_dup 0) (match_dup 1)))
(set (pc) (if_then_else (eq (reg:SI T_REG) (const_int 0))
(label_ref (match_dup 2))
(pc)))])
(define_split
[(set (pc)
(if_then_else (leu (match_operand:SI 0 "arith_reg_operand" "")
(match_operand:SI 1 "arith_reg_operand" ""))
(label_ref (match_operand 2))
(pc)))
(clobber (reg:SI T_REG))]
"TARGET_SH1"
[(set (reg:SI T_REG) (gtu:SI (match_dup 0) (match_dup 1)))
(set (pc) (if_then_else (eq (reg:SI T_REG) (const_int 0))
(label_ref (match_dup 2))
(pc)))])
(define_split
[(set (pc)
(if_then_else (ltu (match_operand:SI 0 "arith_reg_operand" "")
(match_operand:SI 1 "arith_reg_operand" ""))
(label_ref (match_operand 2))
(pc)))
(clobber (reg:SI T_REG))]
"TARGET_SH1"
[(set (reg:SI T_REG) (geu:SI (match_dup 0) (match_dup 1)))
(set (pc) (if_then_else (eq (reg:SI T_REG) (const_int 0))
(label_ref (match_dup 2))
(pc)))])
;; -------------------------------------------------------------------------
;; SImode unsigned integer comparisons
;; -------------------------------------------------------------------------
;; Usually comparisons of 'unsigned int >= 0' are optimized away completely.
;; However, especially when optimizations are off (e.g. -O0) such comparisons
;; might remain and we have to handle them. If the '>= 0' case wasn't
;; handled here, something else would just load a '0' into the second operand
;; and do the comparison. We can do slightly better by just setting the
;; T bit to '1'.
(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" "r")))]
"TARGET_SH1"
"cmp/hs %1,%0"
"&& satisfies_constraint_Z (operands[1])"
[(set (reg:SI T_REG) (const_int 1))]
""
[(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"
{
static const char* alt[] =
{
"tst %S0,%S0" "\n"
" bf 0f" "\n"
" tst %R0,%R0" "\n"
"0:",
"cmp/eq %S1,%S0" "\n"
" bf 0f" "\n"
" cmp/eq %R1,%R0" "\n"
"0:"
};
return alt[which_alternative];
}
[(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_highpart (SImode, operands[0]);
operands[3] = operands[1] == const0_rtx
? const0_rtx
: gen_highpart (SImode, operands[1]);
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"
{
static const char* alt[] =
{
"cmp/eq %S1,%S0" "\n"
" bf{.|/}s 0f" "\n"
" cmp/gt %S1,%S0" "\n"
" cmp/hi %R1,%R0" "\n"
"0:",
"tst %S0,%S0" "\n"
" bf{.|/}s 0f" "\n"
" cmp/pl %S0" "\n"
" cmp/hi %S0,%R0" "\n"
"0:"
};
return alt[which_alternative];
}
[(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"
{
static const char* alt[] =
{
"cmp/eq %S1,%S0" "\n"
" bf{.|/}s 0f" "\n"
" cmp/ge %S1,%S0" "\n"
" cmp/hs %R1,%R0" "\n"
"0:",
"cmp/pz %S0"
};
return alt[which_alternative];
}
[(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"
{
return "cmp/eq %S1,%S0" "\n"
" bf{.|/}s 0f" "\n"
" cmp/hs %S1,%S0" "\n"
" cmp/hs %R1,%R0" "\n"
"0:";
}
[(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"
{
return "cmp/eq %S1,%S0" "\n"
" bf{.|/}s 0f" "\n"
" cmp/hi %S1,%S0" "\n"
" cmp/hi %R1,%R0" "\n"
"0:";
}
[(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")])
; This pattern is 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]);
})
;; 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])))"
{
return "bt 0f" "\n"
" mov %1,%0" "\n"
"0:";
}
[(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])))"
{
return "bf 0f" "\n"
" mov %1,%0" "\n"
"0:";
}
[(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)
{
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_and_split "adddi3_compact"
[(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"
"#"
"&& can_create_pseudo_p ()"
[(const_int 0)]
{
emit_insn (gen_clrt ());
emit_insn (gen_addc (gen_lowpart (SImode, operands[0]),
gen_lowpart (SImode, operands[1]),
gen_lowpart (SImode, operands[2])));
emit_insn (gen_addc (gen_highpart (SImode, operands[0]),
gen_highpart (SImode, operands[1]),
gen_highpart (SImode, operands[2])));
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")])
;; A simplified version of the addc insn, where the exact value of the
;; T bit doesn't matter. This is easier for combine to pick up.
;; We allow a reg or 0 for one of the operands in order to be able to
;; do 'reg + T' sequences.
(define_insn_and_split "*addc"
[(set (match_operand:SI 0 "arith_reg_dest")
(plus:SI (plus:SI (match_operand:SI 1 "arith_reg_operand")
(match_operand:SI 2 "arith_reg_or_0_operand"))
(match_operand 3 "treg_set_expr")))
(clobber (reg:SI T_REG))]
"TARGET_SH1 && can_create_pseudo_p ()"
"#"
"&& 1"
[(const_int 0)]
{
sh_treg_insns ti = sh_split_treg_set_expr (operands[3], curr_insn);
if (ti.has_trailing_nott ())
{
if (operands[2] == const0_rtx)
{
/* op1 + 0 + (1 - T) = op1 + 1 - T = op1 - (-1) - T */
remove_insn (ti.trailing_nott ());
emit_insn (gen_subc (operands[0], operands[1],
force_reg (SImode, GEN_INT (-1))));
DONE;
}
else if (!TARGET_SH2A)
{
/* op1 + op2 + (1 - T) = op1 - (0 - op2 - 1) - T = op1 - ~op2 - T
On SH2A keep the nott insn, because nott-addc sequence doesn't
mutate the inputs. */
remove_insn (ti.trailing_nott ());
rtx tmp = gen_reg_rtx (SImode);
emit_insn (gen_one_cmplsi2 (tmp, operands[2]));
emit_insn (gen_subc (operands[0], operands[1], tmp));
DONE;
}
}
emit_insn (gen_addc (operands[0], operands[1],
force_reg (SImode, operands[2])));
DONE;
})
(define_insn_and_split "*addc"
[(set (match_operand:SI 0 "arith_reg_dest")
(plus:SI (plus:SI (match_operand 1 "treg_set_expr")
(match_operand:SI 2 "arith_reg_operand"))
(match_operand:SI 3 "arith_reg_operand")))
(clobber (reg:SI T_REG))]
"TARGET_SH1 && can_create_pseudo_p ()"
"#"
"&& 1"
[(parallel [(set (match_dup 0) (plus:SI (plus:SI (match_dup 2) (match_dup 3))
(match_dup 1)))
(clobber (reg:SI T_REG))])])
(define_insn_and_split "*addc"
[(set (match_operand:SI 0 "arith_reg_dest")
(plus:SI (match_operand 1 "treg_set_expr")
(plus:SI (match_operand:SI 2 "arith_reg_operand")
(match_operand:SI 3 "arith_reg_operand"))))
(clobber (reg:SI T_REG))]
"TARGET_SH1 && can_create_pseudo_p ()"
"#"
"&& 1"
[(parallel [(set (match_dup 0) (plus:SI (plus:SI (match_dup 2) (match_dup 3))
(match_dup 1)))
(clobber (reg:SI T_REG))])])
;; Sometimes combine will try to do 'reg + (0-reg) + 1' if the *addc pattern
;; matched. Split this up into a simple sub add sequence, as this will save
;; us one sett insn.
(define_insn_and_split "*minus_plus_one"
[(set (match_operand:SI 0 "arith_reg_dest" "")
(plus:SI (minus:SI (match_operand:SI 1 "arith_reg_operand" "")
(match_operand:SI 2 "arith_reg_operand" ""))
(const_int 1)))]
"TARGET_SH1"
"#"
"&& 1"
[(set (match_dup 0) (minus:SI (match_dup 1) (match_dup 2)))
(set (match_dup 0) (plus:SI (match_dup 0) (const_int 1)))])
;; The tree optimiziers canonicalize
;; reg + (reg & 1)
;; into
;; (reg + 1) & -2
;;
;; On SH2A an add-bclr sequence will be used to handle this.
;; On non-SH2A re-emit the add-and sequence to improve register utilization.
(define_insn_and_split "*round_int_even"
|