;; Machine description for the TMS320C[34]x for GNU C compiler
;; Copyright (C) 1994, 1995, 1996, 1997, 1998,
;; 1999, 2000, 2002 Free Software Foundation, Inc.

;; Contributed by Michael Hayes (m.hayes@elec.canterbury.ac.nz)
;;            and Herman Ten Brugge (Haj.Ten.Brugge@net.HCC.nl)

;; This file is part of GNU CC.

;; GNU CC is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; GNU CC is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU CC; see the file COPYING.  If not, write to
;; the Free Software Foundation, 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.

;
; TODO :
;        Try using PQImode again for addresses since C30 only uses
;        24-bit addresses.   Ideally GCC would emit different insns
;        for QImode and Pmode, whether Pmode was QImode or PQImode.
;        For addresses we wouldn't have to have a clobber of the CC
;        associated with each insn and we could use MPYI in address
;        calculations without having to synthesise a proper 32 bit multiply.

; Additional C30/C40 instructions not coded:
; CALLcond, IACK, IDLE, LDE, LDFI, LDII, LDM, NORM, RETIcond
; ROLC, RORC, SIGI, STFI, STII, SUBC, SWI

; Additional C40 instructions not coded:
; LDEP, LDPE, LWRct, LAJcond, RETIcondD

;
; C4x MODES
;
; QImode                char, short, int, long (32-bits)
; HImode                long long              (64-bits)
; QFmode                float, double          (32-bits)
; HFmode                long double            (40-bits)
; CCmode		
; CC_NOOVmode		

;
; C4x PREDICATES:
;
; comparison_operator   LT, GT, LE, GE, LTU, GTU, LEU, GEU, EQ, NE
; memory_operand        memory                                     [m]
; immediate_operand     immediate constant                         [IKN]
; register_operand      register                                   [rf]
; general_operand       register, memory, constant                 [rfmI]

; addr_reg_operand      AR0-AR7, pseudo reg                        [a]
; sp_reg_operand        SP                                         [b]
; std_reg_operand       AR0-AR7, IR0-IR1, RC, RS, RE, SP, pseudo   [c]
; ext_reg_operand       R0-R11, pseudo reg                         [f]
; ext_low_reg_operand   R0-R7, pseudo reg                          [q]
; index_reg_operand     IR0-IR1, pseudo reg                        [x]
; st_reg_operand        ST                                         [y]
; dp_reg_operand        DP                                         [z]
; stik_const_operand    5-bit const                                [K]
; src_operand           general operand                            [rfHmI]
; par_ind_operand       indirect S mode (ARx + 0, 1, IRx)          [S<>]
; parallel_operand      par_ind_operand or ext_low_reg_operand
; symbolic_address_operand
; call_address_operand

; ADDI src2, src1, dst  three operand op
; ADDI src, dst         two operand op

;  Note that the predicates are only used when selecting a pattern
;  to determine if an operand is valid.

;  The constraints then select which of the possible valid operands
;  is present (and guide register selection). The actual assembly
;  instruction is then selected on the basis of the constraints.

;  The extra constraint (valid_operands) is used to determine if
;  the combination of operands is legitimate for the pattern.

;
; C4x CONSTRAINTS:
;
; a   address reg          AR0-AR7
; b   stack pointer        SP
; c   other int reg        AR0-AR7, IR0-IR1, RC, RS, RE
; d   fp reg               R0-R11 (sets CC when dst) 
; e
; f   fp reg               R0-R11 (sets CC when dst)
; g   general reg, memory, constant
; h   fp reg (HFmode)      R0-R11 (sets CC when dst) 
; i   immediate int constant
; j
; k   block count          BK
; l
; m   memory
; n   immediate int constant with known numeric value
; o   offsettable memory
; p   memory address
; q   low fp reg           R0-R7  (sets CC when dst)
; r   general reg          R0-R11, AR0-AR7, IR0-IR1, RC, RS, RE
; s   immediate int constant (value not explicit)
; t                        R0-R1
; u                        R2-R3
; v   repeat count reg     RC
; w
; x   index reg            IR0-IR1
; y   status (CC) reg      ST
; z   data pointer         DP

; G   fp zero
; H   fp 16-bit constant
; I   signed 16-bit
; J   signed 8-bit    (C4x only)
; K   signed 5-bit    (C4x only)
; L   unsigned 16-bit
; M   unsigned 8-bit  (C4x only)
; N   ones complement of unsigned 16-bit
; O   16 bit high constant
; Q   ARx + 9-bit signed disp
; R   ARx + 5-bit unsigned disp  (C4x only)
; S   ARx + 0, 1, IRx disp
; T   direct memory operand
; V   non offsettable memory
; X   any operand
; <   memory operand with autodecrement addressing
; >   memory operand with autoincrement addressing
; {   memory operand with pre-modify addressing
; }   memory operand with post-modify addressing

;  Note that the 'd', 'f', and 'h' constraints are equivalent.
;  The m constraint is equivalent to 'QT<>{}'

;  Note we cannot use the 'g' constraint with Pmode (i.e, QImode)
;  operations since LEGITIMATE_CONSTANT_P accepts SYMBOL_REF.
;  So instead we use 'rIm' for signed operands or 'rLm' for unsigned operands.

;  Note that the constraints are used to select the operands
;  for a chosen pattern.  The constraint that requires the fewest
;  instructions to load an operand is chosen.

;  Note that the 'r' constraint is mostly only used for src integer register 
;  operands,  while 'c' and 'd' constraints are generally only used for dst
;  integer register operands (the 'r' constraint is the union of the 'c' and
;  'd' constraints).  When a register satisfying the 'd' constraint
;  is used as a dst operand, the CC gets clobbered (except for LDIcond)---but 
;  not for 'c'.

;  The 'f' constraint is only for float register operands---when 
;  a register satisying the 'f' constraint is used as a dst operand,
;  the CC gets clobbered (except for LDFcond).

;  The ! in front of the 'b' constaint says to GCC to disparage the
;  use of this constraint.  The 'b' constraint applies only to the SP.

;  Note that we deal with the condition code CC like some of the RISC
;  architectures (arm, sh, sparc) where it is stored in a general register,
;  in this case the hard register ST (21).  Unlike these other architectures
;  that do not set the CC with many instructions, the C[34]x architectures
;  sets the CC for many instructions when the destination register is
;  an extended precision register.  While it would have been easier
;  to use the generic cc0 register to store the CC, as with most of
;  the other ported architectures, this constrains the setting and testing
;  of the CC to be consecutive insns.  Thus we would reduce the benefit
;  of scheduling instructions to avoid pipeline conflicts and filling of
;  delayed branch slots.

;  Since the C[34]x has many instructions that set the CC, we pay the
;  price of having to explicity define which insns clobber the CC
;  (rather than using the macro NOTICE_UPDATE_CC). 

;  Note that many patterns say that the CC is clobbered when in fact
;  that it may not be (depending on the destination register).
;  We have to cover ourselves if an extended precision register
;  is allocated to the destination register.
;  Unfortunately, it is not easy to tell GCC that the clobbering of CC
;  is register dependent.  If we could tolerate the ST register being
;  copied about, then we could store the CC in a pseudo register and
;  use constructs such as (clobber (match_scratch:CC N "&y,X")) to
;  indicate that the 'y' class (ST register) is clobbered for the
;  first combination of operands, but not with the second.
;  I tried this approach for a while but reload got unhappy since I
;  didn't allow it to move the CC around.

;  Note that fundamental operations, such as moves, must not clobber the
;  CC.  Thus movqi choses a move instruction that doesn't clobber the CC.
;  If GCC wants to combine a move with a compare, it is smart enough to
;  chose the move instruction that sets the CC.

;  Unfortunately, the C[34]x instruction set does not have arithmetic or
;  logical operations that never touch the CC.  We thus have to assume
;  that the CC may be clobbered at all times.  If we define patterns
;  such as addqi without the clobber of CC, then GCC will be forced
;  to use registers such as the auxiliary registers which can cause
;  horrible pipeline conflicts.  The tradeoff is that GCC can't now
;  sneak in an add instruction between setting and testing of the CC.

;  Most of the C[34]x instructions require operands of the following formats,
;  where imm represents an immediate constant, dir a direct memory reference,
;  ind an indirect memory reference, and reg a register:

;        src2 (op2)             src1 (op1)      dst (op0)
; imm  dir  ind  reg  |  imm  dir  ind  reg  |  reg      Notes
;---------------------+----------------------+------
; ILH   T   Q<>   r   |   -    -    -    0   |   r       2 operand
;  -    -   S<>   r   |   -    -   S<>   r   |   r       
;  J    -    R    -   |   -    -    R    r   |   r       C4x

;  Arithmetic operations use the I, J constraints for immediate constants,
;  while logical operations use the L, J constraints.  Floating point
;  operations use the H constraint for immediate constants.

;  With most instructions the src2 and src1 operands are commutative
;  (except for SUB, SUBR, ANDN).  The assembler considers
;  ADDI 10, R0, R1 and ADDI R0, 10, R1 to be equivalent.
;  We thus match src2 and src1 with the src_operand predicate and
;  use valid_operands as the extra constraint to reject invalid
;  operand combinations.  For example, ADDI @foo, @bar, R0.

;  Note that we use the ? modifier so that reload doesn't preferentially
;  try the alternative where three registers are acceptable as
;  operands (whenever an operand requires reloading).  Instead it will try
;  the 2 operand form which will produce better code since it won't require
;  a new spill register.

;  Note that the floating point representation of 0.0 on the C4x
;  is 0x80000000 (-2147483648).  This value produces an warning
;  message on 32-bit machines about the decimal constant being so large
;  that it is unsigned.

;  With two operand instructions patterns having two sets,
;  the compare set must come first to keep the combiner happy.
;  While the combiner seems to cope most of the time with the
;  compare set coming second, it's best to have it first.

;
; C4x CONSTANT attributes
;
(define_attr "cpu" "c4x,c3x"
 (const
  (cond [(symbol_ref "TARGET_C3X") (const_string "c3x")]
         (const_string "c4x"))))

;
; C4x INSN ATTRIBUTES:
;
; lda           load address, non-clobber CC
; store         memory store, non-clobber CC
; load_load     parallel memory loads, non-clobber CC
; load_store    parallel memory load and store, non-clobber CC
; store_load    parallel memory store and load, non-clobber CC
; store_store   parallel memory stores, non-clobber CC
; unary         two operand arithmetic, non-clobber CC
; unarycc       two operand arithmetic, clobber CC
; binary        three operand arithmetic, non-clobber CC
; binarycc      three operand arithmetic, clobber CC
; compare       compare, clobber CC
; call          function call
; rets          return from subroutine
; jump          unconditional branch
; jmpc          conditional branch
; db            decrement and branch (unconditional)
; dbc           decrement and branch (conditional)
; ldp           load DP
; push		stack push
; pop		stack pop
; repeat        block repeat
; repeat_top    block repeat top
; laj           link and jump
; multi         multiple instruction
; misc          nop		(default)

;  The only real instructions that affect things are the ones that modify
;  address registers and ones that call or jump.  Note that the number
;  of operands refers to the RTL insn pattern, not the number of explicit
;  operands in the machine instruction.
;
(define_attr "type" "lda,store,unary,unarycc,binary,binarycc,compare,call,rets,jump,jmpc,db,dbc,misc,ldp,repeat,repeat_top,laj,load_load,load_store,store_load,store_store,push,pop,multi"
             (const_string "misc"))


; Some instructions operate on unsigned data constants, some on signed data
; constants, or the ones complement of unsigned constants.
; This differentiates them.  Default to signed.  This attribute
; is used by the macro SMALL_CONST () (defined in c4x.h) to determine
; whether an immediate integer constant will fit within the instruction,
; or will have to be loaded using direct addressing from memory.
; Note that logical operations assume unsigned integers whereas
; arithmetic operations assume signed integers.  Note that the C4x
; small immediate constant (J) used as src2 in three operand instructions
; is always signed.  not_uint16 refers to a number that fits into 16-bits
; when one's complemented.
;
(define_attr "data" "int16,uint16,high_16,not_uint16" (const_string "int16"))

(define_asm_attributes
  [(set_attr "type" "multi")])

;
; C4x DELAY SLOTS
;
; Define delay slot scheduling for branch and call instructions.
; The C[34]x has three delay slots. Note that none of the three instructions
; that follow a delayed branch can be a Bcond, BcondD, BR, BRD, DBcond,
; DBcondD, CALL, CALLcond, TRAPcond, RETIcond, RETScond, RPTB, RPTS, or IDLE.
;
; Annulled branches are a bit difficult because the next instructions
; are preprocessed.
; The table below shows what phase of the c4x is executed.
;        BccA[TF] label
;        op1             fetch, decode and read executed
;        op2             fetch and decode executed
;        op3             fetch executed
; This means that we can allow any instruction in the last delay slot
; and only instructions which modify registers in the first two. 
; lda can not be executed in the first delay slot 
; and ldpk can not be executed in the first two delay slots.

(define_attr "onlyreg" "false,true"
       (cond [(eq_attr "type" "unary,unarycc")
                       (if_then_else (and (match_operand 0 "reg_imm_operand" "")
                                          (match_operand 1 "reg_imm_operand" ""))
                                     (const_string "true") (const_string "false"))
              (eq_attr "type" "binary,binarycc")
                       (if_then_else (and (match_operand 0 "reg_imm_operand" "")
                                          (and (match_operand 1 "reg_imm_operand" "")
                                               (match_operand 2 "reg_imm_operand" "")))
                                     (const_string "true") (const_string "false"))]
             (const_string "false")))

(define_attr "onlyreg_nomod" "false,true"
       (cond [(eq_attr "type" "unary,unarycc,compare,lda,store")
                       (if_then_else (and (match_operand 0 "not_modify_reg" "")
                                          (match_operand 1 "not_modify_reg" ""))
                                     (const_string "true") (const_string "false"))
              (eq_attr "type" "binary,binarycc")
                       (if_then_else (and (match_operand 0 "not_modify_reg" "")
                                          (and (match_operand 1 "not_modify_reg" "")
                                               (match_operand 2 "not_modify_reg" "")))
                                     (const_string "true") (const_string "false"))]
             (const_string "false")))

(define_attr "not_repeat_reg" "false,true"
       (cond [(eq_attr "type" "unary,unarycc,compare,lda,ldp,store")
                       (if_then_else (and (match_operand 0 "not_rc_reg" "")
                                          (match_operand 1 "not_rc_reg" ""))
                                     (const_string "true") (const_string "false"))
              (eq_attr "type" "binary,binarycc")
                       (if_then_else (and (match_operand 0 "not_rc_reg" "")
                                          (and (match_operand 1 "not_rc_reg" "")
                                               (match_operand 2 "not_rc_reg" "")))
                                     (const_string "true") (const_string "false"))]
             (const_string "false")))

/* Disable compare because the c4x contains a bug. The cmpi insn sets the CC
   in the read phase of the pipeline instead of the execution phase when
   two registers are compared.  */
(define_attr "in_annul_slot_1" "false,true"
  (if_then_else (and (and (eq_attr "cpu" "c4x")
		          (eq_attr "type" "!jump,call,rets,jmpc,compare,db,dbc,repeat,repeat_top,laj,push,pop,lda,ldp,multi"))
		     (eq_attr "onlyreg" "true"))
		(const_string "true")
		(const_string "false")))

(define_attr "in_annul_slot_2" "false,true"
  (if_then_else (and (and (eq_attr "cpu" "c4x")
		          (eq_attr "type" "!jump,call,rets,jmpc,db,dbc,repeat,repeat_top,laj,push,pop,ldp,multi"))
		     (eq_attr "onlyreg_nomod" "true"))
		(const_string "true")
		(const_string "false")))

/* Disable ldp because the c4x contains a bug. The ldp insn modifies
   the dp register when the insn is anulled or not.
   Also disable autoincrement insns because of a silicon bug.  */
(define_attr "in_annul_slot_3" "false,true"
  (if_then_else (and (and (eq_attr "cpu" "c4x")
		          (eq_attr "type" "!jump,call,rets,jmpc,db,dbc,repeat,repeat_top,laj,push,pop,ldp,multi"))
		     (eq_attr "onlyreg_nomod" "true"))
		(const_string "true")
		(const_string "false")))

(define_attr "in_delay_slot" "false,true"
  (if_then_else (eq_attr "type" "!jump,call,rets,jmpc,db,dbc,repeat,repeat_top,laj,multi")
		(const_string "true")
		(const_string "false")))

(define_attr "in_repeat_slot" "false,true"
  (if_then_else (and (eq_attr "cpu" "c4x")
		     (and (eq_attr "type" "!jump,call,rets,jmpc,db,dbc,repeat,repeat_top,laj,multi")
		          (eq_attr "not_repeat_reg" "true")))
		(const_string "true")
		(const_string "false")))

(define_attr "in_dbc_slot" "false,true"
  (if_then_else (eq_attr "type" "!jump,call,rets,jmpc,unarycc,binarycc,compare,db,dbc,repeat,repeat_top,laj,multi")
		(const_string "true")
		(const_string "false")))

(define_delay (eq_attr "type" "jmpc")
              [(eq_attr "in_delay_slot" "true")
               (eq_attr "in_annul_slot_1" "true")
               (eq_attr "in_annul_slot_1" "true")

               (eq_attr "in_delay_slot" "true")
               (eq_attr "in_annul_slot_2" "true")
               (eq_attr "in_annul_slot_2" "true")

               (eq_attr "in_delay_slot" "true")
               (eq_attr "in_annul_slot_3" "true")
               (eq_attr "in_annul_slot_3" "true") ])


(define_delay (eq_attr "type" "repeat_top")
              [(eq_attr "in_repeat_slot" "true") (nil) (nil)
               (eq_attr "in_repeat_slot" "true") (nil) (nil)
               (eq_attr "in_repeat_slot" "true") (nil) (nil)])

(define_delay (eq_attr "type" "jump,db")
              [(eq_attr "in_delay_slot" "true") (nil) (nil)
               (eq_attr "in_delay_slot" "true") (nil) (nil)
               (eq_attr "in_delay_slot" "true") (nil) (nil)])


; Decrement and branch conditional instructions cannot modify the
; condition codes for the cycles in the delay slots.
;
(define_delay (eq_attr "type" "dbc")
              [(eq_attr "in_dbc_slot" "true") (nil) (nil)
               (eq_attr "in_dbc_slot" "true") (nil) (nil)
               (eq_attr "in_dbc_slot" "true") (nil) (nil)])

; The LAJ instruction has three delay slots but the last slot is
; used for pushing the return address.  Thus we can only use two slots.
;
(define_delay (eq_attr "type" "laj")
              [(eq_attr "in_delay_slot" "true") (nil) (nil)
               (eq_attr "in_delay_slot" "true") (nil) (nil)])

;
; C4x UNSPEC NUMBERS
;
;  1 BU/BUD
;  2 RPTS
;  3 LSH
;  4 cmphi
;  5 RCPF
;  6 RND
;  7 repeat block filler
;  8 loadhf_int
;  9 storehf_int
; 10 RSQRF
; 11 loadqf_int
; 12 storeqf_int
; 13 Conditional load on overflow
; 14 push_st
; 15 pop_st
; 16 push_dp
; 17 pop_dp
; 18 popqi_unspec
; 19 popqf_unspec
; 20 andn_st
; 22 rptb_init
; 23 toieee
; 24 frieee

;
; C4x FUNCTIONAL UNITS
;
; Define functional units for instruction scheduling to minimise
; pipeline conflicts.
;
; With the C3x, an external memory write (with no wait states) takes
; two cycles and an external memory read (with no wait states) takes
; one cycle.  However, an external read following an external write
; takes two cycles.  With internal memory, reads and writes take
; half a cycle.
;
; When a C4x address register is loaded it will not be available for
; an extra machine cycle.  Calculating with a C4x address register
; makes it unavailable for 2 machine cycles.  To notify GCC of these
; pipeline delays, each of the auxiliary and index registers are declared
; as separate functional units.
;
; (define_function_unit NAME MULTIPLICITY SIMULTANEITY
;			TEST READY-DELAY ISSUE-DELAY [CONFLICT-LIST])
;
; MULTIPLICITY 1 (C4x has no independent identical function units)
; SIMULTANEITY 0 (C4x is pipelined)
; READY_DELAY  1 (Results usually ready after every cyle)
; ISSUE_DELAY  1 (Can issue insns every cycle)

; Just some dummy definitions. The real work is done in c4x_adjust_cost.
; These are needed so the min/max READY_DELAY is known.

(define_function_unit "dummy" 1 0 (const_int 0) 1 1)
(define_function_unit "dummy" 1 0 (const_int 0) 2 1)
(define_function_unit "dummy" 1 0 (const_int 0) 3 1)

; The attribute setar0 is set to 1 for insns where ar0 is a dst operand.
; Note that the attributes unarycc and binarycc do not apply
; if ar0 is a dst operand (only loading an ext. prec. reg. sets CC)
(define_attr "setar0" ""
       (cond [(eq_attr "type" "unary,binary")
                       (if_then_else (match_operand 0 "ar0_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "setlda_ar0" ""
       (cond [(eq_attr "type" "lda")
                       (if_then_else (match_operand 0 "ar0_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

; The attribute usear0 is set to 1 for insns where ar0 is used
; for addressing, as a src operand, or as a dst operand.
(define_attr "usear0" ""
       (cond [(eq_attr "type" "compare,store")
                       (if_then_else (match_operand 0 "ar0_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "compare,lda,unary,unarycc,binary,binarycc")
                       (if_then_else (match_operand 1 "ar0_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "binary,binarycc")
                       (if_then_else (match_operand 2 "ar0_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "db,dbc")
                       (if_then_else (match_operand 0 "ar0_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

; The attribute readar0 is set to 1 for insns where ar0 is a src operand.
(define_attr "readar0" ""
       (cond [(eq_attr "type" "compare")
                       (if_then_else (match_operand 0 "ar0_reg_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "compare,store,lda,unary,unarycc,binary,binarycc")
                       (if_then_else (match_operand 1 "ar0_reg_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "binary,binarycc")
                       (if_then_else (match_operand 2 "ar0_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "setar1" ""
       (cond [(eq_attr "type" "unary,binary")
                       (if_then_else (match_operand 0 "ar1_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "setlda_ar1" ""
       (cond [(eq_attr "type" "lda")
                       (if_then_else (match_operand 0 "ar1_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "usear1" ""
       (cond [(eq_attr "type" "compare,store")
                       (if_then_else (match_operand 0 "ar1_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "compare,lda,unary,unarycc,binary,binarycc")
                       (if_then_else (match_operand 1 "ar1_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "binary,binarycc")
                       (if_then_else (match_operand 2 "ar1_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "db,dbc")
                       (if_then_else (match_operand 0 "ar1_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "readar1" ""
       (cond [(eq_attr "type" "compare")
                       (if_then_else (match_operand 0 "ar1_reg_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "compare,store,lda,unary,unarycc,binary,binarycc")
                       (if_then_else (match_operand 1 "ar1_reg_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "binary,binarycc")
                       (if_then_else (match_operand 2 "ar1_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "setar2" ""
       (cond [(eq_attr "type" "unary,binary")
                       (if_then_else (match_operand 0 "ar2_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "setlda_ar2" ""
       (cond [(eq_attr "type" "lda")
                       (if_then_else (match_operand 0 "ar2_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "usear2" ""
       (cond [(eq_attr "type" "compare,store")
                       (if_then_else (match_operand 0 "ar2_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "compare,lda,unary,unarycc,binary,binarycc")
                       (if_then_else (match_operand 1 "ar2_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "binary,binarycc")
                       (if_then_else (match_operand 2 "ar2_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "db,dbc")
                       (if_then_else (match_operand 0 "ar2_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "readar2" ""
       (cond [(eq_attr "type" "compare")
                       (if_then_else (match_operand 0 "ar2_reg_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "compare,store,lda,unary,unarycc,binary,binarycc")
                       (if_then_else (match_operand 1 "ar2_reg_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "binary,binarycc")
                       (if_then_else (match_operand 2 "ar2_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "setar3" ""
       (cond [(eq_attr "type" "unary,binary")
                       (if_then_else (match_operand 0 "ar3_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "setlda_ar3" ""
       (cond [(eq_attr "type" "lda")
                       (if_then_else (match_operand 0 "ar3_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "usear3" ""
       (cond [(eq_attr "type" "compare,store")
                       (if_then_else (match_operand 0 "ar3_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "compare,lda,unary,unarycc,binary,binarycc")
                       (if_then_else (match_operand 1 "ar3_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "binary,binarycc")
                       (if_then_else (match_operand 2 "ar3_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "db,dbc")
                       (if_then_else (match_operand 0 "ar3_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "readar3" ""
       (cond [(eq_attr "type" "compare")
                       (if_then_else (match_operand 0 "ar3_reg_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "compare,store,lda,unary,unarycc,binary,binarycc")
                       (if_then_else (match_operand 1 "ar3_reg_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "binary,binarycc")
                       (if_then_else (match_operand 2 "ar3_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "setar4" ""
       (cond [(eq_attr "type" "unary,binary")
                       (if_then_else (match_operand 0 "ar4_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "setlda_ar4" ""
       (cond [(eq_attr "type" "lda")
                       (if_then_else (match_operand 0 "ar4_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "usear4" ""
       (cond [(eq_attr "type" "compare,store")
                       (if_then_else (match_operand 0 "ar4_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "compare,lda,unary,unarycc,binary,binarycc")
                       (if_then_else (match_operand 1 "ar4_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "binary,binarycc")
                       (if_then_else (match_operand 2 "ar4_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "db,dbc")
                       (if_then_else (match_operand 0 "ar4_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "readar4" ""
       (cond [(eq_attr "type" "compare")
                       (if_then_else (match_operand 0 "ar4_reg_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "compare,store,lda,unary,unarycc,binary,binarycc")
                       (if_then_else (match_operand 1 "ar4_reg_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "binary,binarycc")
                       (if_then_else (match_operand 2 "ar4_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "setar5" ""
       (cond [(eq_attr "type" "unary,binary")
                       (if_then_else (match_operand 0 "ar5_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "setlda_ar5" ""
       (cond [(eq_attr "type" "lda")
                       (if_then_else (match_operand 0 "ar5_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "usear5" ""
       (cond [(eq_attr "type" "compare,store")
                       (if_then_else (match_operand 0 "ar5_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "compare,lda,unary,unarycc,binary,binarycc")
                       (if_then_else (match_operand 1 "ar5_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "binary,binarycc")
                       (if_then_else (match_operand 2 "ar5_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "db,dbc")
                       (if_then_else (match_operand 0 "ar5_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "readar5" ""
       (cond [(eq_attr "type" "compare")
                       (if_then_else (match_operand 0 "ar5_reg_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "compare,store,lda,unary,unarycc,binary,binarycc")
                       (if_then_else (match_operand 1 "ar5_reg_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "binary,binarycc")
                       (if_then_else (match_operand 2 "ar5_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "setar6" ""
       (cond [(eq_attr "type" "unary,binary")
                       (if_then_else (match_operand 0 "ar6_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "setlda_ar6" ""
       (cond [(eq_attr "type" "lda")
                       (if_then_else (match_operand 0 "ar6_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "usear6" ""
       (cond [(eq_attr "type" "compare,store")
                       (if_then_else (match_operand 0 "ar6_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "compare,lda,unary,unarycc,binary,binarycc")
                       (if_then_else (match_operand 1 "ar6_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "binary,binarycc")
                       (if_then_else (match_operand 2 "ar6_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "db,dbc")
                       (if_then_else (match_operand 0 "ar6_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "readar6" ""
       (cond [(eq_attr "type" "compare")
                       (if_then_else (match_operand 0 "ar6_reg_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "compare,store,lda,unary,unarycc,binary,binarycc")
                       (if_then_else (match_operand 1 "ar6_reg_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "binary,binarycc")
                       (if_then_else (match_operand 2 "ar6_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "setar7" ""
       (cond [(eq_attr "type" "unary,binary")
                       (if_then_else (match_operand 0 "ar7_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "setlda_ar7" ""
       (cond [(eq_attr "type" "lda")
                       (if_then_else (match_operand 0 "ar7_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "usear7" ""
       (cond [(eq_attr "type" "compare,store")
                       (if_then_else (match_operand 0 "ar7_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "compare,lda,unary,unarycc,binary,binarycc")
                       (if_then_else (match_operand 1 "ar7_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "binary,binarycc")
                       (if_then_else (match_operand 2 "ar7_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "db,dbc")
                       (if_then_else (match_operand 0 "ar7_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "readar7" ""
       (cond [(eq_attr "type" "compare")
                       (if_then_else (match_operand 0 "ar7_reg_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "compare,store,lda,unary,unarycc,binary,binarycc")
                       (if_then_else (match_operand 1 "ar7_reg_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "binary,binarycc")
                       (if_then_else (match_operand 2 "ar7_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "setir0" ""
       (cond [(eq_attr "type" "unary,binary")
                       (if_then_else (match_operand 0 "ir0_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "setlda_ir0" ""
       (cond [(eq_attr "type" "lda")
                       (if_then_else (match_operand 0 "ir0_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "useir0" ""
       (cond [(eq_attr "type" "compare,store")
                       (if_then_else (match_operand 0 "ir0_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "compare,lda,unary,unarycc,binary,binarycc")
                       (if_then_else (match_operand 1 "ir0_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "binary,binarycc")
                       (if_then_else (match_operand 2 "ir0_mem_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "setir1" ""
       (cond [(eq_attr "type" "unary,binary")
                       (if_then_else (match_operand 0 "ir1_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "setlda_ir1" ""
       (cond [(eq_attr "type" "lda")
                       (if_then_else (match_operand 0 "ir1_reg_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "useir1" ""
       (cond [(eq_attr "type" "compare,store")
                       (if_then_else (match_operand 0 "ir1_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "compare,lda,unary,unarycc,binary,binarycc")
                       (if_then_else (match_operand 1 "ir1_mem_operand" "")
                                     (const_int 1) (const_int 0))
              (eq_attr "type" "binary,binarycc")
                       (if_then_else (match_operand 2 "ir1_mem_operand" "")
                                     (const_int 1) (const_int 0))]
             (const_int 0)))

; With the C3x, things are simpler, but slower, i.e. more pipeline conflicts :(
; There are three functional groups:
; (1) AR0-AR7, IR0-IR1, BK
; (2) DP
; (3) SP
;
; When a register in one of these functional groups is loaded,
; the contents of that or any other register in its group
; will not be available to the next instruction for 2 machine cycles.
; Similarly, when a register in one of the functional groups is read
; excepting (IR0-IR1, BK, DP) the contents of that or any other register
; in its group will not be available to the next instruction for
; 1 machine cycle.
;
; Let's ignore functional groups 2 and 3 for now, since they are not
; so important.

(define_attr "setgroup1" ""
       (cond [(eq_attr "type" "lda,unary,binary")
                  (if_then_else (match_operand 0 "group1_reg_operand" "")
                                (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "usegroup1" ""
       (cond [(eq_attr "type" "compare,store,store_store,store_load")
              (if_then_else (match_operand 0 "group1_mem_operand" "")
                            (const_int 1) (const_int 0))
              (eq_attr "type" "compare,lda,unary,unarycc,binary,binarycc,load_load,load_store")
              (if_then_else (match_operand 1 "group1_mem_operand" "")
                            (const_int 1) (const_int 0))
              (eq_attr "type" "store_store,load_store")
              (if_then_else (match_operand 2 "group1_mem_operand" "")
                            (const_int 1) (const_int 0))
              (eq_attr "type" "load_load,store_load")
              (if_then_else (match_operand 3 "group1_mem_operand" "")
                            (const_int 1) (const_int 0))]
             (const_int 0)))

(define_attr "readarx" ""
       (cond [(eq_attr "type" "compare")
              (if_then_else (match_operand 0 "arx_reg_operand" "")
                            (const_int 1) (const_int 0))
              (eq_attr "type" "compare,store,lda,unary,unarycc,binary,binarycc")
              (if_then_else (match_operand 1 "arx_reg_operand" "")
                            (const_int 1) (const_int 0))
              (eq_attr "type" "binary,binarycc")
              (if_then_else (match_operand 2 "arx_reg_operand" "")
                            (const_int 1) (const_int 0))]
             (const_int 0)))


;
; C4x INSN PATTERNS:
;
; Note that the movMM and addP patterns can be called during reload
; so we need to take special care with theses patterns since
; we cannot blindly clobber CC or generate new pseudo registers.

;
; TWO OPERAND INTEGER INSTRUCTIONS
;

;
; LDP/LDPK
;
(define_insn "set_ldp"
  [(set (match_operand:QI 0 "dp_reg_operand" "=z")
        (high:QI (match_operand:QI 1 "" "")))]
  "! TARGET_SMALL"
  "* return (TARGET_C3X) ? \"ldp\\t%A1\" : \"ldpk\\t%A1\";"
  [(set_attr "type" "ldp")])

(define_insn "set_ldp_prologue"
  [(set (match_operand:QI 0 "dp_reg_operand" "=z")
        (high:QI (match_operand:QI 1 "" "")))]
  "TARGET_SMALL && TARGET_PARANOID"
  "* return (TARGET_C3X) ? \"ldp\\t@data_sec\" : \"ldpk\\t@data_sec\";"
  [(set_attr "type" "ldp")])

(define_insn "set_high"
  [(set (match_operand:QI 0 "std_reg_operand" "=c")
        (high:QI (match_operand:QI 1 "symbolic_address_operand" "")))]
  "! TARGET_C3X && ! TARGET_TI"
  "ldhi\\t^%H1,%0"
  [(set_attr "type" "unary")])

(define_insn "set_lo_sum"
  [(set (match_operand:QI 0 "std_reg_operand" "+c")
        (lo_sum:QI (match_dup 0)
                   (match_operand:QI 1 "symbolic_address_operand" "")))]
  "! TARGET_TI"
  "or\\t#%H1,%0"
  [(set_attr "type" "unary")])

(define_split
  [(set (match_operand:QI 0 "std_reg_operand" "")
        (match_operand:QI 1 "symbolic_address_operand" ""))]
  "reload_completed && ! TARGET_C3X && ! TARGET_TI"
  [(set (match_dup 0) (high:QI (match_dup 1)))
   (set (match_dup 0) (lo_sum:QI (match_dup 0) (match_dup 1)))]
  "")

(define_split
  [(set (match_operand:QI 0 "reg_operand" "")
	(match_operand:QI 1 "const_int_operand" ""))
   (clobber (reg:QI 16))]
  "! TARGET_C3X
   && ! IS_INT16_CONST (INTVAL (operands[1]))
   && ! IS_HIGH_CONST (INTVAL (operands[1]))
   && reload_completed
   && std_reg_operand (operands[0], QImode)"
  [(set (match_dup 0) (match_dup 2))
   (set (match_dup 0) (ior:QI (match_dup 0) (match_dup 3)))]
  "
{
   operands[2] = GEN_INT (INTVAL (operands[1]) & ~0xffff);
   operands[3] = GEN_INT (INTVAL (operands[1]) & 0xffff);
}")

(define_split
  [(set (match_operand:QI 0 "reg_operand" "")
	(match_operand:QI 1 "const_int_operand" ""))]
  "! TARGET_C3X
   && ! IS_INT16_CONST (INTVAL (operands[1]))
   && ! IS_HIGH_CONST (INTVAL (operands[1]))
   && reload_completed
   && std_reg_operand (operands[0], QImode)"
  [(set (match_dup 0) (match_dup 2))
   (set (match_dup 0) (ior:QI (match_dup 0) (match_dup 3)))]
  "
{
   operands[2] = GEN_INT (INTVAL (operands[1]) & ~0xffff);
   operands[3] = GEN_INT (INTVAL (operands[1]) & 0xffff);
}")

(define_split
  [(set (match_operand:QI 0 "reg_operand" "")
	(match_operand:QI 1 "const_int_operand" ""))
   (clobber (reg:QI 16))]
  "TARGET_C3X && ! TARGET_SMALL
   && ! IS_INT16_CONST (INTVAL (operands[1]))
   && reload_completed
   && std_reg_operand (operands[0], QImode)
   && c4x_shiftable_constant (operands[1]) < 0"
  [(set (match_dup 0) (match_dup 2))
   (set (match_dup 0) (ashift:QI (match_dup 0) (match_dup 4)))
   (set (match_dup 0) (ior:QI (match_dup 0) (match_dup 3)))]
  "
{
   /* Generate two's complement value of 16 MSBs.  */
   operands[2] = gen_rtx (CONST_INT, VOIDmode,
			  (((INTVAL (operands[1]) >> 16) & 0xffff)
			   - 0x8000) ^ ~0x7fff);
   operands[3] = GEN_INT (INTVAL (operands[1]) & 0xffff);
   operands[4] = GEN_INT (16);
}")

(define_split
  [(set (match_operand:QI 0 "reg_operand" "")
	(match_operand:QI 1 "const_int_operand" ""))]
  "TARGET_C3X && ! TARGET_SMALL
   && ! IS_INT16_CONST (INTVAL (operands[1]))
   && reload_completed
   && std_reg_operand (operands[0], QImode)
   && c4x_shiftable_constant (operands[1]) < 0"
  [(set (match_dup 0) (match_dup 2))
   (set (match_dup 0) (ashift:QI (match_dup 0) (match_dup 4)))
   (set (match_dup 0) (ior:QI (match_dup 0) (match_dup 3)))]
  "
{
   /* Generate two's complement value of 16 MSBs.  */
   operands[2] = gen_rtx (CONST_INT, VOIDmode,
			  (((INTVAL (operands[1]) >> 16) & 0xffff)
			   - 0x8000) ^ ~0x7fff);
   operands[3] = GEN_INT (INTVAL (operands[1]) & 0xffff);
   operands[4] = GEN_INT (16);
}")

(define_split
  [(set (match_operand:QI 0 "reg_operand" "")
	(match_operand:QI 1 "const_int_operand" ""))
   (clobber (reg:QI 16))]
  "TARGET_C3X
   && ! IS_INT16_CONST (INTVAL (operands[1]))
   && reload_completed
   && std_reg_operand (operands[0], QImode)
   && c4x_shiftable_constant (operands[1]) >= 0"
  [(set (match_dup 0) (match_dup 2))
   (set (match_dup 0) (ashift:QI (match_dup 0) (match_dup 3)))]
  "
{
   /* Generate two's complement value of MSBs.  */
   int shift = c4x_shiftable_constant (operands[1]);

   operands[2] = GEN_INT ((((INTVAL (operands[1]) >> shift) & 0xffff)
			   - 0x8000) ^ ~0x7fff);
   operands[3] = GEN_INT (shift);
}")

(define_split
  [(set (match_operand:QI 0 "reg_operand" "")
	(match_operand:QI 1 "const_int_operand" ""))]
  "TARGET_C3X
   && ! IS_INT16_CONST (INTVAL (operands[1]))
   && reload_completed
   && std_reg_operand (operands[0], QImode)
   && c4x_shiftable_constant (operands[1]) >= 0"
  [(set (match_dup 0) (match_dup 2))
   (set (match_dup 0) (ashift:QI (match_dup 0) (match_dup 3)))]
  "
{
   /* Generate two's complement value of MSBs.  */
   int shift = c4x_shiftable_constant (operands[1]);

   operands[2] = GEN_INT ((((INTVAL (operands[1]) >> shift) & 0xffff)
			    - 0x8000) ^ ~0x7fff);
   operands[3] = GEN_INT (shift);
}")

(define_split
  [(set (match_operand:QI 0 "reg_operand" "")
	(match_operand:QI 1 "const_int_operand" ""))
   (clobber (reg:QI 16))]
  "! TARGET_SMALL
   && ! IS_INT16_CONST (INTVAL (operands[1]))
   && ! IS_HIGH_CONST (INTVAL (operands[1]))
   && reload_completed
   && ! std_reg_operand (operands[0], QImode)"
  [(set (match_dup 2) (high:QI (match_dup 3)))
   (set (match_dup 0) (match_dup 4))
   (use (match_dup 1))]
  "
{
   rtx dp_reg = gen_rtx_REG (Pmode, DP_REGNO);
   operands[2] = dp_reg;
   operands[3] = force_const_mem (Pmode, operands[1]);
   operands[4] = change_address (operands[3], QImode,
			         gen_rtx_LO_SUM (Pmode, dp_reg,
                                                 XEXP (operands[3], 0)));
   operands[3] = XEXP (operands[3], 0);
}")

(define_split
  [(set (match_operand:QI 0 "reg_operand" "")
	(match_operand:QI 1 "const_int_operand" ""))]
  "! TARGET_SMALL
   && ! IS_INT16_CONST (INTVAL (operands[1]))
   && ! IS_HIGH_CONST (INTVAL (operands[1]))
   && reload_completed
   && ! std_reg_operand (operands[0], QImode)"
  [(set (match_dup 2) (high:QI (match_dup 3)))
   (set (match_dup 0) (match_dup 4))
   (use (match_dup 1))]
  "
{
   rtx dp_reg = gen_rtx_REG (Pmode, DP_REGNO);
   operands[2] = dp_reg;
   operands[3] = force_const_mem (Pmode, operands[1]);
   operands[4] = change_address (operands[3], QImode,
			         gen_rtx_LO_SUM (Pmode, dp_reg,
                                                 XEXP (operands[3], 0)));
   operands[3] = XEXP (operands[3], 0);
}")

(define_split
  [(set (match_operand:QI 0 "reg_operand" "")
	(match_operand:QI 1 "const_int_operand" ""))
   (clobber (reg:QI 16))]
  "TARGET_SMALL
   && ! IS_INT16_CONST (INTVAL (operands[1]))
   && ! IS_HIGH_CONST (INTVAL (operands[1]))
   && reload_completed
   && ((TARGET_C3X && c4x_shiftable_constant (operands[1]) < 0)
       || ! std_reg_operand (operands[0], QImode))"
  [(set (match_dup 0) (match_dup 2))
   (use (match_dup 1))]
  "
{
   rtx dp_reg = gen_rtx_REG (Pmode, DP_REGNO);
   operands[2] = force_const_mem (Pmode, operands[1]);
   operands[2] = change_address (operands[2], QImode,
			         gen_rtx_LO_SUM (Pmode, dp_reg,
                                                 XEXP (operands[2], 0)));
}")

(define_split
  [(set (match_operand:QI 0 "reg_operand" "")
	(match_operand:QI 1 "const_int_operand" ""))]
  "TARGET_SMALL
   && ! IS_INT16_CONST (INTVAL (operands[1]))
   && ! IS_HIGH_CONST (INTVAL (operands[1]))
   && reload_completed
   && ((TARGET_C3X && c4x_shiftable_constant (operands[1]) < 0)
       || ! std_reg_operand (operands[0], QImode))"
  [(set (match_dup 0) (match_dup 2))
   (use (match_dup 1))]
  "
{
   rtx dp_reg = gen_rtx_REG (Pmode, DP_REGNO);
   operands[2] = force_const_mem (Pmode, operands[1]);
   operands[2] = change_address (operands[2], QImode,
			         gen_rtx_LO_SUM (Pmode, dp_reg,
                                                 XEXP (operands[2], 0)));
}")

(define_split
  [(set (match_operand:HI 0 "reg_operand" "")
	(match_operand:HI 1 "const_int_operand" ""))
   (clobber (reg:QI 16))]
  "reload_completed"
  [(set (match_dup 2) (match_dup 4))
   (set (match_dup 3) (match_dup 5))]
  "
{
   operands[2] = c4x_operand_subword (operands[0], 0, 1, HImode);
   operands[3] = c4x_operand_subword (operands[0], 1, 1, HImode);
   operands[4] = c4x_operand_subword (operands[1], 0, 1, HImode);
   operands[5] = c4x_operand_subword (operands[1], 1, 1, HImode);
}")


; We need to clobber the DP reg to be safe in case we
; need to load this address from memory
(define_insn "load_immed_address"
  [(set (match_operand:QI 0 "reg_operand" "=a?x?c*r")
        (match_operand:QI 1 "symbolic_address_operand" ""))
   (clobber (reg:QI 16))]
  "TARGET_LOAD_ADDRESS"
  "#"
  [(set_attr "type" "multi")])


(define_split
  [(set (match_operand:QI 0 "std_reg_operand" "")
        (match_operand:QI 1 "symbolic_address_operand" ""))
   (clobber (reg:QI 16))]
  "reload_completed && ! TARGET_C3X && ! TARGET_TI"
  [(set (match_dup 0) (high:QI (match_dup 1)))
   (set (match_dup 0) (lo_sum:QI (match_dup 0) (match_dup 1)))]
  "")

; CC has been selected to load a symbolic address.  We force the address
; into memory and then generate LDP and LDIU insns.
; This is also required for the C30 if we pretend that we can 
; easily load symbolic addresses into a register.
(define_split
  [(set (match_operand:QI 0 "reg_operand" "")
        (match_operand:QI 1 "symbolic_address_operand" ""))
   (clobber (reg:QI 16))]
  "reload_completed
   && ! TARGET_SMALL 
   && (TARGET_C3X || TARGET_TI || ! std_reg_operand (operands[0], QImode))"
  [(set (match_dup 2) (high:QI (match_dup 3)))
   (set (match_dup 0) (match_dup 4))
   (use (match_dup 1))]
  "
{
   rtx dp_reg = gen_rtx_REG (Pmode, DP_REGNO);
   operands[2] = dp_reg;
   operands[3] = force_const_mem (Pmode, operands[1]);
   operands[4] = change_address (operands[3], QImode,
			         gen_rtx_LO_SUM (Pmode, dp_reg,
                                                 XEXP (operands[3], 0)));
   operands[3] = XEXP (operands[3], 0);
}")

; This pattern is similar to the above but does not emit a LDP
; for the small memory model.
(define_split
  [(set (match_operand:QI 0 "reg_operand" "")
        (match_operand:QI 1 "symbolic_address_operand" ""))
   (clobber (reg:QI 16))]
  "reload_completed
   && TARGET_SMALL
   && (TARGET_C3X || TARGET_TI || ! std_reg_operand (operands[0], QImode))"
  [(set (match_dup 0) (match_dup 2))
   (use (match_dup 1))]
  "
{  
   rtx dp_reg = gen_rtx_REG (Pmode, DP_REGNO);
   operands[2] = force_const_mem (Pmode, operands[1]);
   operands[2] = change_address (operands[2], QImode,
			         gen_rtx_LO_SUM (Pmode, dp_reg,
                                                 XEXP (operands[2], 0)));
}")

(define_insn "loadhi_big_constant"
  [(set (match_operand:HI 0 "reg_operand" "=c*d")
        (match_operand:HI 1 "const_int_operand" ""))
   (clobber (reg:QI 16))]
  ""
  "#"
  [(set_attr "type" "multi")])

;
; LDIU/LDA/STI/STIK
;
; The following moves will not set the condition codes register.
;

; This must come before the general case
(define_insn "*movqi_stik"
  [(set (match_operand:QI 0 "memory_operand" "=m")
        (match_operand:QI 1 "stik_const_operand" "K"))]
  "! TARGET_C3X"
  "stik\\t%1,%0"
  [(set_attr "type" "store")])

(define_insn "loadqi_big_constant"
  [(set (match_operand:QI 0 "reg_operand" "=c*d")
        (match_operand:QI 1 "const_int_operand" ""))
   (clobber (reg:QI 16))]
  "! IS_INT16_CONST (INTVAL (operands[1]))
   && ! IS_HIGH_CONST (INTVAL (operands[1]))"
  "#"
  [(set_attr "type" "multi")])

; We must provide an alternative to store to memory in case we have to
; spill a register.
(define_insn "movqi_noclobber"
  [(set (match_operand:QI 0 "dst_operand" "=d,*c,m,r")
        (match_operand:QI 1 "src_hi_operand" "rIm,rIm,r,O"))]
  "(REG_P (operands[0]) || REG_P (operands[1])
    || GET_CODE (operands[0]) == SUBREG
    || GET_CODE (operands[1]) == SUBREG)
    && ! symbolic_address_operand (operands[1], QImode)"
  "*
   if (which_alternative == 2)
     return \"sti\\t%1,%0\";

   if (! TARGET_C3X && which_alternative == 3)
     {
       operands[1] = GEN_INT ((INTVAL (operands[1]) >> 16) & 0xffff);
       return \"ldhi\\t%1,%0\";
     }

   /* The lda instruction cannot use the same register as source
      and destination.  */
   if (! TARGET_C3X && which_alternative == 1
       && (   IS_ADDR_REG (operands[0])
           || IS_INDEX_REG (operands[0])
           || IS_SP_REG (operands[0]))
       && (REGNO (operands[0]) != REGNO (operands[1])))
      return \"lda\\t%1,%0\";
   return \"ldiu\\t%1,%0\";
  "
  [(set_attr "type" "unary,lda,store,unary")
   (set_attr "data" "int16,int16,int16,high_16")])

;
; LDI
;

; We shouldn't need these peepholes, but the combiner seems to miss them...
(define_peephole
  [(set (match_operand:QI 0 "ext_reg_operand" "=d")
        (match_operand:QI 1 "src_operand" "rIm"))
   (set (reg:CC 21)
        (compare:CC (match_dup 0) (const_int 0)))]
  ""
  "ldi\\t%1,%0"
  [(set_attr "type" "unarycc")
   (set_attr "data" "int16")])

(define_insn "*movqi_set"
  [(set (reg:CC 21)
        (compare:CC (match_operand:QI 1 "src_operand" "rIm") 
                    (const_int 0)))
   (set (match_operand:QI 0 "ext_reg_operand" "=d")
        (match_dup 1))]
  ""
  "ldi\\t%1,%0"
  [(set_attr "type" "unarycc")
   (set_attr "data" "int16")])

; This pattern probably gets in the way and requires a scratch register
; when a simple compare with zero will suffice.
;(define_insn "*movqi_test"
; [(set (reg:CC 21)
;       (compare:CC (match_operand:QI 1 "src_operand" "rIm") 
;                   (const_int 0)))
;  (clobber (match_scratch:QI 0 "=d"))]
; ""
; "@
;  ldi\\t%1,%0"
;  [(set_attr "type" "unarycc")
;   (set_attr "data" "int16")])

;  If one of the operands is not a register, then we should
;  emit two insns, using a scratch register.  This will produce
;  better code in loops if the source operand is invariant, since
;  the source reload can be optimised out.  During reload we cannot
;  use change_address or force_reg which will allocate new pseudo regs.

;  Unlike most other insns, the move insns can't be split with
;  different predicates, because register spilling and other parts of
;  the compiler, have memoized the insn number already.

(define_expand "movqi"
  [(set (match_operand:QI 0 "general_operand" "")
        (match_operand:QI 1 "general_operand" ""))]
  ""
  "
{
  if (c4x_emit_move_sequence (operands, QImode))
    DONE;
}")


; As far as GCC is concerned, the moves are performed in parallel
; thus it must be convinced that there is no aliasing.
; It also assumes that the input operands are simultaneously loaded
; and then the output operands are simultaneously stored.
; With the C4x, if there are parallel stores to the same address
; both stores are executed.
; If there is a parallel load and store to the same address,
; the load is performed first.
; The problem with this pattern is that reload can spoil
; the show when it eliminates a reference to the frame pointer.
; This can invalidate the memory addressing mode, i.e., when
; the displacement is greater than 1.
(define_insn "movqi_parallel"
  [(set (match_operand:QI 0 "parallel_operand" "=q,S<>!V,q,S<>!V")
        (match_operand:QI 1 "parallel_operand" "S<>!V,q,S<>!V,q"))
   (set (match_operand:QI 2 "parallel_operand" "=q,S<>!V,S<>!V,q")
        (match_operand:QI 3 "parallel_operand" "S<>!V,q,q,S<>!V"))]
  "TARGET_PARALLEL && valid_parallel_load_store (operands, QImode)"
  "@
   ldi1\\t%1,%0\\n||\\tldi2\\t%3,%2
   sti1\\t%1,%0\\n||\\tsti2\\t%3,%2
   ldi\\t%1,%0\\n||\\tsti\\t%3,%2
   ldi\\t%3,%2\\n||\\tsti\\t%1,%0"
  [(set_attr "type" "load_load,store_store,load_store,store_load")])

;
; PUSH/POP
;
(define_insn "pushqi"
  [(set (mem:QI (pre_inc:QI (reg:QI 20)))
        (match_operand:QI 0 "reg_operand" "r"))]
  ""
  "push\\t%0"
  [(set_attr "type" "push")])

(define_insn "push_st"
  [(set (mem:QI (pre_inc:QI (reg:QI 20))) (unspec:QI [(reg:QI 21)] 14))
   (use (reg:QI 21))]
  ""
  "push\\tst"
  [(set_attr "type" "push")])

(define_insn "push_dp"
  [(set (mem:QI (pre_inc:QI (reg:QI 20))) (unspec:QI [(reg:QI 16)] 16))
   (use (reg:QI 16))]
  ""
  "push\\tdp"
  [(set_attr "type" "push")])

(define_insn "popqi"
  [(set (match_operand:QI 0 "reg_operand" "=r")
        (mem:QI (post_dec:QI (reg:QI 20))))
   (clobber (reg:CC 21))]
  ""
  "pop\\t%0"
  [(set_attr "type" "pop")])

(define_insn "pop_st"
  [(set (unspec:QI [(reg:QI 21)] 15) (mem:QI (post_dec:QI (reg:QI 20))))
   (clobber (reg:CC 21))]
  ""
  "pop\\tst"
  [(set_attr "type" "pop")])

(define_insn "pop_dp"
  [(set (unspec:QI [(reg:QI 16)] 17) (mem:QI (post_dec:QI (reg:QI 20))))
   (clobber (reg:CC 16))]
  ""
  "pop\\tdp"
  [(set_attr "type" "pop")])

(define_insn "popqi_unspec"
  [(set (unspec:QI [(match_operand:QI 0 "reg_operand" "=r")] 18)
        (mem:QI (post_dec:QI (reg:QI 20))))
   (clobber (match_dup 0))
   (clobber (reg:CC 21))]
  ""
  "pop\\t%0"
  [(set_attr "type" "pop")])

;
; ABSI
;
(define_expand "absqi2"
  [(parallel [(set (match_operand:QI 0 "reg_operand" "")
                   (abs:QI (match_operand:QI 1 "src_operand" "")))
              (clobber (reg:CC_NOOV 21))])]
  ""
  "")

(define_insn "*absqi2_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,c")
        (abs:QI (match_operand:QI 1 "src_operand" "rIm,rIm")))
   (clobber (reg:CC_NOOV 21))]
  ""
  "absi\\t%1,%0"
  [(set_attr "type" "unarycc,unary")
   (set_attr "data" "int16,int16")])

(define_insn "*absqi2_noclobber"
  [(set (match_operand:QI 0 "std_reg_operand" "=c")
        (abs:QI (match_operand:QI 1 "src_operand" "rIm")))]
  ""
  "absi\\t%1,%0"
  [(set_attr "type" "unary")
   (set_attr "data" "int16")])

(define_split
  [(set (match_operand:QI 0 "std_reg_operand" "")
        (abs:QI (match_operand:QI 1 "src_operand" "")))
   (clobber (reg:CC_NOOV 21))]
  "reload_completed"
  [(set (match_dup 0)
        (abs:QI (match_dup 1)))]
  "")

(define_insn "*absqi2_test"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (abs:QI (match_operand:QI 1 "src_operand" "rIm"))
                         (const_int 0)))
   (clobber (match_scratch:QI 0 "=d"))]
  ""
  "absi\\t%1,%0"
  [(set_attr "type" "unarycc")
   (set_attr "data" "int16")])

(define_insn "*absqi2_set"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (abs:QI (match_operand:QI 1 "src_operand" "rIm"))
                         (const_int 0)))
   (set (match_operand:QI 0 "ext_reg_operand" "=d")
        (abs:QI (match_dup 1)))]
  ""
  "absi\\t%1,%0"
  [(set_attr "type" "unarycc")
   (set_attr "data" "int16")])        

;
; NEGI
;
(define_expand "negqi2"
  [(parallel [(set (match_operand:QI 0 "reg_operand" "")
                   (neg:QI (match_operand:QI 1 "src_operand" "")))
              (clobber (reg:CC_NOOV 21))])]
""
"")

(define_insn "*negqi2_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,c")
        (neg:QI (match_operand:QI 1 "src_operand" "rIm,rIm")))
   (clobber (reg:CC_NOOV 21))]
  ""
  "negi\\t%1,%0"
  [(set_attr "type" "unarycc,unary")
   (set_attr "data" "int16,int16")])

(define_insn "*negqi2_noclobber"
  [(set (match_operand:QI 0 "std_reg_operand" "=c")
        (neg:QI (match_operand:QI 1 "src_operand" "rIm")))]
  ""
  "negi\\t%1,%0"
  [(set_attr "type" "unary")
   (set_attr "data" "int16")])

(define_split
  [(set (match_operand:QI 0 "std_reg_operand" "")
        (neg:QI (match_operand:QI 1 "src_operand" "")))
   (clobber (reg:CC_NOOV 21))]
  "reload_completed"
  [(set (match_dup 0)
        (neg:QI (match_dup 1)))]
  "")

(define_insn "*negqi2_test"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (neg:QI (match_operand:QI 1 "src_operand" "rIm"))
                         (const_int 0)))
   (clobber (match_scratch:QI 0 "=d"))]
  ""
  "negi\\t%1,%0"
  [(set_attr "type" "unarycc")
   (set_attr "data" "int16")])

(define_insn "*negqi2_set"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (neg:QI (match_operand:QI 1 "src_operand" "rIm"))
                         (const_int 0)))
   (set (match_operand:QI 0 "ext_reg_operand" "=d")
        (neg:QI (match_dup 1)))]
  ""
  "negi\\t%1,%0"
  [(set_attr "type" "unarycc")
   (set_attr "data" "int16")])        

(define_insn "*negbqi2_clobber"
  [(set (match_operand:QI 0 "ext_reg_operand" "=d")
        (neg:QI (match_operand:QI 1 "src_operand" "rIm")))
   (use (reg:CC_NOOV 21))
   (clobber (reg:CC_NOOV 21))]
  ""
  "negb\\t%1,%0"
  [(set_attr "type" "unarycc")
   (set_attr "data" "int16")])        

;
; NOT
;
(define_expand "one_cmplqi2"
  [(parallel [(set (match_operand:QI 0 "reg_operand" "")
                   (not:QI (match_operand:QI 1 "lsrc_operand" "")))
              (clobber (reg:CC 21))])]
  ""
  "")

(define_insn "*one_cmplqi2_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,c")
        (not:QI (match_operand:QI 1 "lsrc_operand" "rLm,rLm")))
   (clobber (reg:CC 21))]
  ""
  "not\\t%1,%0"
  [(set_attr "type" "unarycc,unary")
   (set_attr "data" "uint16,uint16")])

(define_insn "*one_cmplqi2_noclobber"
  [(set (match_operand:QI 0 "std_reg_operand" "=c")
        (not:QI (match_operand:QI 1 "lsrc_operand" "rLm")))]
  ""
  "not\\t%1,%0"
  [(set_attr "type" "unary")
   (set_attr "data" "uint16")])

(define_split
  [(set (match_operand:QI 0 "std_reg_operand" "")
        (not:QI (match_operand:QI 1 "lsrc_operand" "")))
   (clobber (reg:CC 21))]
  "reload_completed"
  [(set (match_dup 0)
        (not:QI (match_dup 1)))]
  "")

(define_insn "*one_cmplqi2_test"
  [(set (reg:CC 21)
        (compare:CC (not:QI (match_operand:QI 1 "lsrc_operand" "rLm"))
                    (const_int 0)))
   (clobber (match_scratch:QI 0 "=d"))]
  ""
  "not\\t%1,%0"
  [(set_attr "type" "unarycc")
   (set_attr "data" "uint16")])

(define_insn "*one_cmplqi2_set"
  [(set (reg:CC 21)
        (compare:CC (not:QI (match_operand:QI 1 "lsrc_operand" "rLm"))
                    (const_int 0)))
   (set (match_operand:QI 0 "ext_reg_operand" "=d")        
        (not:QI (match_dup 1)))]
  ""
  "not\\t%1,%0"
  [(set_attr "type" "unarycc")
   (set_attr "data" "uint16")])        

(define_insn "*one_cmplqi2_const_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,c")
        (match_operand:QI 1 "not_const_operand" "N,N"))
   (clobber (reg:CC 21))]
  ""
  "@
   not\\t%N1,%0
   not\\t%N1,%0"
   [(set_attr "type" "unarycc,unary")
    (set_attr "data" "not_uint16,not_uint16")])

; movqi can use this for loading an integer that can't normally
; fit into a 16-bit signed integer.  The drawback is that it cannot
; go into R0-R11 since that will clobber the CC and movqi shouldn't
; do that.  This can cause additional reloading but in most cases
; this will cause only an additional register move.  With the large
; memory model we require an extra instruction to load DP anyway,
; if we're loading the constant from memory.  The big advantage of
; allowing constants that satisfy not_const_operand in movqi, is that
; it allows andn to be generated more often.
; However, there is a problem if GCC has decided that it wants
; to use R0-R11, since we won't have a matching pattern...
; In interim, we prevent immed_const allowing `N' constants.
(define_insn "*one_cmplqi2_const_noclobber"
  [(set (match_operand:QI 0 "std_reg_operand" "=c")
        (match_operand:QI 1 "not_const_operand" "N"))]
  ""
  "not\\t%N1,%0"
  [(set_attr "type" "unary")
   (set_attr "data" "not_uint16")])

;
; ROL
;
(define_expand "rotlqi3"
  [(parallel [(set (match_operand:QI 0 "reg_operand" "")
                   (rotate:QI (match_operand:QI 1 "reg_operand" "")
                              (match_operand:QI 2 "const_int_operand" "")))
              (clobber (reg:CC 21))])]
  ""
  "if (INTVAL (operands[2]) > 4)
     FAIL; /* Open code as two shifts and an or */
   if (INTVAL (operands[2]) > 1)
     {
        int i;
	rtx tmp;

        /* If we have 4 or fewer shifts, then it is probably faster
           to emit separate ROL instructions.  A C3x requires
           at least 4 instructions (a C4x requires at least 3), to
           perform a rotation by shifts.  */

	tmp = operands[1];
        for (i = 0; i < INTVAL (operands[2]) - 1; i++)
	  {
   	    tmp = gen_reg_rtx (QImode);
            emit_insn (gen_rotl_1_clobber (tmp, operands[1]));
	    operands[1] = tmp;
	  }
        emit_insn (gen_rotl_1_clobber (operands[0], tmp));
        DONE;
     }")

(define_insn "rotl_1_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,c")
        (rotate:QI (match_operand:QI 1 "reg_operand" "0,0")
                   (const_int 1)))
   (clobber (reg:CC 21))]
  ""
  "rol\\t%0"
  [(set_attr "type" "unarycc,unary")])
; Default to int16 data attr.

;
; ROR
;
(define_expand "rotrqi3"
  [(parallel [(set (match_operand:QI 0 "reg_operand" "")
                   (rotatert:QI (match_operand:QI 1 "reg_operand" "")
                                (match_operand:QI 2 "const_int_operand" "")))
              (clobber (reg:CC 21))])]
  ""
  "if (INTVAL (operands[2]) > 4)
     FAIL; /* Open code as two shifts and an or */
   if (INTVAL (operands[2]) > 1)
     {
        int i;
	rtx tmp;
 
        /* If we have 4 or fewer shifts, then it is probably faster
           to emit separate ROL instructions.  A C3x requires
           at least 4 instructions (a C4x requires at least 3), to
           perform a rotation by shifts.  */
 
	tmp = operands[1];
        for (i = 0; i < INTVAL (operands[2]) - 1; i++)
	  {
   	    tmp = gen_reg_rtx (QImode);
            emit_insn (gen_rotr_1_clobber (tmp, operands[1]));
	    operands[1] = tmp;
	  }
        emit_insn (gen_rotr_1_clobber (operands[0], tmp));
        DONE;
     }")

(define_insn "rotr_1_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,c")
        (rotatert:QI (match_operand:QI 1 "reg_operand" "0,0")
                     (const_int 1)))
   (clobber (reg:CC 21))]
  ""
  "ror\\t%0"
  [(set_attr "type" "unarycc,unary")])
; Default to int16 data attr.


;
; THREE OPERAND INTEGER INSTRUCTIONS
;

;
; ADDI
;
; This is used by reload when it calls gen_add2_insn for address arithmetic
; so we must emit the pattern that doesn't clobber CC.
;
(define_expand "addqi3"
  [(parallel [(set (match_operand:QI 0 "std_or_reg_operand" "")
                   (plus:QI (match_operand:QI 1 "src_operand" "")
                            (match_operand:QI 2 "src_operand" "")))
              (clobber (reg:CC_NOOV 21))])]
  ""
  "legitimize_operands (PLUS, operands, QImode);
   if (reload_in_progress
       || (! IS_PSEUDO_REG (operands[0]) 
           && ! IS_EXT_REG (operands[0])))
   {
      emit_insn (gen_addqi3_noclobber (operands[0], operands[1], operands[2]));
      DONE;
   }")

(define_insn "*addqi3_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
        (plus:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>,0,rR,rS<>")
                 (match_operand:QI 2 "src_operand" "rIm,JR,rS<>,rIm,JR,rS<>")))
   (clobber (reg:CC_NOOV 21))]
  "valid_operands (PLUS, operands, QImode)"
  "@
   addi\\t%2,%0
   addi3\\t%2,%1,%0
   addi3\\t%2,%1,%0
   addi\\t%2,%0
   addi3\\t%2,%1,%0
   addi3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")])
; Default to int16 data attr.

(define_split
  [(set (match_operand:QI 0 "std_reg_operand" "")
        (plus:QI (match_operand:QI 1 "src_operand" "")
                 (match_operand:QI 2 "src_operand" "")))
   (clobber (reg:CC_NOOV 21))]
  "reload_completed"
  [(set (match_dup 0)
        (plus:QI (match_dup 1)
                 (match_dup 2)))]
  "")

(define_insn "*addqi3_test"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (plus:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
                                  (match_operand:QI 2 "src_operand" "rIm,JR,rS<>"))
                         (const_int 0)))
   (clobber (match_scratch:QI 0 "=d,d,d"))]
  "valid_operands (PLUS, operands, QImode)"
  "@
   addi\\t%2,%0
   addi3\\t%2,%1,%0
   addi3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc")])
; Default to int16 data attr.

; gcc does this in combine.c we just reverse it here
(define_insn "*cmp_neg"
  [(set (reg:CC_NOOV 21)
	(compare:CC_NOOV (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
		         (neg: QI (match_operand:QI 2 "src_operand" "g,JR,rS<>"))))
   (clobber (match_scratch:QI 0 "=d,d,d"))]
  "valid_operands (PLUS, operands, QImode)"
  "@
   addi\\t%2,%0
   addi3\\t%2,%1,%0
   addi3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc")])
  
(define_peephole
  [(parallel [(set (match_operand:QI 0 "ext_reg_operand" "=d,d,d")
                   (plus:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
                            (match_operand:QI 2 "src_operand" "g,JR,rS<>")))
              (clobber (reg:CC_NOOV 21))])
   (set (reg:CC_NOOV 21)
        (compare:CC_NOOV (match_dup 0) (const_int 0)))]
  "valid_operands (PLUS, operands, QImode)"
  "@
   addi\\t%2,%0
   addi3\\t%2,%1,%0
   addi3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc")])

(define_insn "*addqi3_set"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (plus:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
                                  (match_operand:QI 2 "src_operand" "rIm,JR,rS<>"))
                         (const_int 0)))
   (set (match_operand:QI 0 "ext_reg_operand" "=d,d,d")
        (plus:QI (match_dup 1) (match_dup 2)))]
  "valid_operands (PLUS, operands, QImode)"
  "@
   addi\\t%2,%0
   addi3\\t%2,%1,%0
   addi3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc")])
; Default to int16 data attr.

; This pattern is required primarily for manipulating the stack pointer
; where GCC doesn't expect CC to be clobbered or for calculating
; addresses during reload.
(define_insn "addqi3_noclobber"
  [(set (match_operand:QI 0 "std_reg_operand" "=c,c,c")
        (plus:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
                 (match_operand:QI 2 "src_operand" "rIm,JR,rS<>")))]
  "valid_operands (PLUS, operands, QImode)"
  "@
   addi\\t%2,%0
   addi3\\t%2,%1,%0
   addi3\\t%2,%1,%0"
  [(set_attr "type" "binary,binary,binary")])
; Default to int16 data attr.


; This pattern is required during reload when eliminate_regs_in_insn
; effectively converts a move insn into an add insn when the src
; operand is the frame pointer plus a constant.  Without this
; pattern, gen_addqi3 can be called with a register for operand0
; that can clobber CC.
; For example, we may have (set (mem (reg ar0)) (reg 99))
; with (set (reg 99) (plus (reg ar3) (const_int 8)))
; Now since ar3, the frame pointer, is unchanging within the function,
; (plus (reg ar3) (const_int 8)) is considered a constant.
; eliminate_regs_in_insn substitutes this constant to give
; (set (mem (reg ar0)) (plus (reg ar3) (const_int 8))).
; This is an invalid C4x insn but if we don't provide a pattern
; for it, it will be considered to be a move insn for reloading.
(define_insn "*addqi3_noclobber_reload"
  [(set (match_operand:QI 0 "std_reg_operand" "=c,c,c")
        (plus:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
                 (match_operand:QI 2 "src_operand" "rIm,JR,rS<>")))]
  "reload_in_progress"
  "@
   addi\\t%2,%0
   addi3\\t%2,%1,%0
   addi3\\t%2,%1,%0"
  [(set_attr "type" "binary,binary,binary")])
; Default to int16 data attr.


(define_insn "*addqi3_carry_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
        (plus:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>,0,rR,rS<>")
                 (match_operand:QI 2 "src_operand" "rIm,JR,rS<>,rIm,JR,rS<>")))
   (use (reg:CC_NOOV 21))
   (clobber (reg:CC_NOOV 21))]
  "valid_operands (PLUS, operands, QImode)"
  "@
   addc\\t%2,%0
   addc3\\t%2,%1,%0
   addc3\\t%2,%1,%0
   addc\\t%2,%0
   addc3\\t%2,%1,%0
   addc3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")])
; Default to int16 data attr.


;
; SUBI/SUBRI
;
(define_expand "subqi3"
  [(parallel [(set (match_operand:QI 0 "reg_operand" "")
                   (minus:QI (match_operand:QI 1 "src_operand" "")
                             (match_operand:QI 2 "src_operand" "")))
              (clobber (reg:CC_NOOV 21))])]
  ""
  "legitimize_operands (MINUS, operands, QImode);")

(define_insn "*subqi3_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,d,d,?d,c,c,c,?c")
        (minus:QI (match_operand:QI 1 "src_operand" "0,rIm,rR,rS<>,0,rIm,rR,rS<>")
                  (match_operand:QI 2 "src_operand" "rIm,0,JR,rS<>,rIm,0,JR,rS<>")))
   (clobber (reg:CC_NOOV 21))]
  "valid_operands (MINUS, operands, QImode)"
  "@
   subi\\t%2,%0
   subri\\t%1,%0
   subi3\\t%2,%1,%0
   subi3\\t%2,%1,%0
   subi\\t%2,%0
   subri\\t%1,%0
   subi3\\t%2,%1,%0
   subi3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc,binarycc,binary,binary,binary,binary")])
; Default to int16 data attr.

(define_split
  [(set (match_operand:QI 0 "std_reg_operand" "")
        (minus:QI (match_operand:QI 1 "src_operand" "")
                  (match_operand:QI 2 "src_operand" "")))
   (clobber (reg:CC_NOOV 21))]
  "reload_completed"
  [(set (match_dup 0)
        (minus:QI (match_dup 1)
                 (match_dup 2)))]
  "")

(define_insn "*subqi3_test"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (minus:QI (match_operand:QI 1 "src_operand" "0,rIm,rR,rS<>")
                                   (match_operand:QI 2 "src_operand" "rIm,0,JR,rS<>"))
                         (const_int 0)))
   (clobber (match_scratch:QI 0 "=d,d,d,?d"))]
  "valid_operands (MINUS, operands, QImode)"
  "@
   subi\\t%2,%0
   subri\\t%1,%0
   subi3\\t%2,%1,%0
   subi3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc,binarycc")])
; Default to int16 data attr.

(define_peephole
  [(parallel [(set (match_operand:QI 0 "ext_reg_operand" "=d,d,d,?d")
                   (minus:QI (match_operand:QI 1 "src_operand" "0,rIm,rR,rS<>")
                             (match_operand:QI 2 "src_operand" "rIm,0,JR,rS<>")))
              (clobber (reg:CC_NOOV 21))])
   (set (reg:CC_NOOV 21)
        (compare:CC_NOOV (match_dup 0) (const_int 0)))]
  "valid_operands (MINUS, operands, QImode)"
  "@
   subi\\t%2,%0
   subri\\t%1,%0
   subi3\\t%2,%1,%0
   subi3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc,binarycc")])
  
(define_insn "*subqi3_set"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (minus:QI (match_operand:QI 1 "src_operand" "0,rIm,rR,rS<>")
                                   (match_operand:QI 2 "src_operand" "rIm,0,JR,rS<>"))
                         (const_int 0)))
   (set (match_operand:QI 0 "ext_reg_operand" "=d,d,d,?d")
        (minus:QI (match_dup 1)
                  (match_dup 2)))]
  "valid_operands (MINUS, operands, QImode)"
  "@
   subi\\t%2,%0
   subri\\t%1,%0
   subi3\\t%2,%1,%0
   subi3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc,binarycc")])
; Default to int16 data attr.

(define_insn "*subqi3_noclobber"
  [(set (match_operand:QI 0 "std_reg_operand" "=c,c,c,?c")
        (minus:QI (match_operand:QI 1 "src_operand" "0,rIm,rR,rS<>")
                  (match_operand:QI 2 "src_operand" "rIm,0,JR,rS<>")))]
  "valid_operands (MINUS, operands, QImode)"
  "@
   subi\\t%2,%0
   subri\\t%1,%0
   subi3\\t%2,%1,%0
   subi3\\t%2,%1,%0"
  [(set_attr "type" "binary,binary,binary,binary")])
; Default to int16 data attr.

(define_insn "*subqi3_carry_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,d,d,?d,c,c,c,?c")
        (minus:QI (match_operand:QI 1 "src_operand" "0,rIm,rR,rS<>,0,rIm,rR,rS<>")
                  (match_operand:QI 2 "src_operand" "rIm,0,JR,rS<>,rIm,0,JR,rS<>")))
   (use (reg:CC_NOOV 21))
   (clobber (reg:CC_NOOV 21))]
  "valid_operands (MINUS, operands, QImode)"
  "@
   subb\\t%2,%0
   subrb\\t%1,%0
   subb3\\t%2,%1,%0
   subb3\\t%2,%1,%0
   subb\\t%2,%0
   subrb\\t%1,%0
   subb3\\t%2,%1,%0
   subb3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc,binarycc,binary,binary,binary,binary")])
; Default to int16 data attr.

(define_insn "*subqi3_carry_set"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (minus:QI (match_operand:QI 1 "src_operand" "0,rIm,rR,rS<>")
                                   (match_operand:QI 2 "src_operand" "rIm,0,JR,rS<>"))
                         (const_int 0)))
   (set (match_operand:QI 0 "ext_reg_operand" "=d,d,d,?d")
        (minus:QI (match_dup 1)
                  (match_dup 2)))
   (use (reg:CC_NOOV 21))]
  "valid_operands (MINUS, operands, QImode)"
  "@
   subb\\t%2,%0
   subrb\\t%1,%0
   subb3\\t%2,%1,%0
   subb3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc,binarycc")])
; Default to int16 data attr.

;
; MPYI
;
(define_expand "mulqi3"
  [(parallel [(set (match_operand:QI 0 "reg_operand" "")
                   (mult:QI (match_operand:QI 1 "src_operand" "")
                            (match_operand:QI 2 "src_operand" "")))
              (clobber (reg:CC_NOOV 21))])]
  ""
  "if (TARGET_MPYI || (GET_CODE (operands[2]) == CONST_INT
       && exact_log2 (INTVAL (operands[2])) >= 0))
     legitimize_operands (MULT, operands, QImode);
   else
     {        
       if (GET_CODE (operands[2]) == CONST_INT)
         {
          /* Let GCC try to synthesise the multiplication using shifts
             and adds.  In most cases this will be more profitable than
             using the C3x MPYI.  */
            FAIL;
         }
       if (operands[1] == operands[2])
         {
            /* Do the squaring operation in-line.  */
            emit_insn (gen_sqrqi2_inline (operands[0], operands[1]));
            DONE;
         }
       if (TARGET_INLINE)
         {
            emit_insn (gen_mulqi3_inline (operands[0], operands[1],
                                          operands[2]));
            DONE;
         }
       c4x_emit_libcall3 (smul_optab->handlers[(int) QImode].libfunc,
			  MULT, QImode, operands);
       DONE;
     }
  ")

(define_insn "*mulqi3_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
        (mult:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>,0,rR,rS<>")
                 (match_operand:QI 2 "src_operand" "rIm,JR,rS<>,rIm,JR,rS<>")))
   (clobber (reg:CC_NOOV 21))]
  "valid_operands (MULT, operands, QImode)"
  "*
  if (which_alternative == 0 || which_alternative == 3)
    {
      if (TARGET_C3X
          && GET_CODE (operands[2]) == CONST_INT
          && exact_log2 (INTVAL (operands[2])) >= 0)
        return \"ash\\t%L2,%0\";
      else
        return \"mpyi\\t%2,%0\";
    }
  else
      return \"mpyi3\\t%2,%1,%0\";"
  [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")])
; Default to int16 data attr.

(define_insn "*mulqi3_test"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (mult:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
                                  (match_operand:QI 2 "src_operand" "rIm,JR,rS<>"))
                         (const_int 0)))
   (clobber (match_scratch:QI 0 "=d,d,d"))]
  "valid_operands (MULT, operands, QImode)"
  "*
  if (which_alternative == 0)
    {
      if (TARGET_C3X 
          && GET_CODE (operands[2]) == CONST_INT
          && exact_log2 (INTVAL (operands[2])) >= 0)
        return \"ash\\t%L2,%0\";
      else
        return \"mpyi\\t%2,%0\";
    } 
  else
      return \"mpyi3\\t%2,%1,%0\";"
  [(set_attr "type" "binarycc,binarycc,binarycc")])
; Default to int16 data attr.

(define_insn "*mulqi3_set"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (mult:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
                                  (match_operand:QI 2 "src_operand" "rIm,JR,rS<>"))
                         (const_int 0)))
   (set (match_operand:QI 0 "ext_reg_operand" "=d,d,d")
        (mult:QI (match_dup 1)
                 (match_dup 2)))]
  "valid_operands (MULT, operands, QImode)"
  "*
  if (which_alternative == 0)
    {
      if (TARGET_C3X 
          && GET_CODE (operands[2]) == CONST_INT
          && exact_log2 (INTVAL (operands[2])) >= 0)
        return \"ash\\t%L2,%0\";
      else
        return \"mpyi\\t%2,%0\";
    }
    else
        return \"mpyi3\\t%2,%1,%0\";"
  [(set_attr "type" "binarycc,binarycc,binarycc")])
; Default to int16 data attr.

; The C3x multiply instruction assumes 24-bit signed integer operands
; and the 48-bit result is truncated to 32-bits.
(define_insn "mulqi3_24_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
        (mult:QI
         (sign_extend:QI
          (and:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>,0,rR,rS<>")
                  (const_int 16777215)))
         (sign_extend:QI
          (and:QI (match_operand:QI 2 "src_operand" "rIm,JR,rS<>,rIm,JR,rS<>")
                  (const_int 16777215)))))
   (clobber (reg:CC_NOOV 21))]
  "TARGET_C3X && valid_operands (MULT, operands, QImode)"
  "@
   mpyi\\t%2,%0
   mpyi3\\t%2,%1,%0
   mpyi3\\t%2,%1,%0
   mpyi\\t%2,%0
   mpyi3\\t%2,%1,%0
   mpyi3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")])
; Default to int16 data attr.


; Fast square function for C3x where TARGET_MPYI not asserted
(define_expand "sqrqi2_inline"
  [(set (match_dup 7) (match_operand:QI 1 "src_operand" ""))
   (parallel [(set (match_dup 3)
                   (lshiftrt:QI (match_dup 7) (const_int 16)))
              (clobber (reg:CC 21))])
   (parallel [(set (match_dup 2)
                   (and:QI (match_dup 7) (const_int 65535)))
              (clobber (reg:CC 21))])
   (parallel [(set (match_dup 4)
                   (mult:QI (sign_extend:QI (and:QI (match_dup 2) 
                                                    (const_int 16777215)))
                            (sign_extend:QI (and:QI (match_dup 2) 
                                                    (const_int 16777215)))))
              (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 5)
                   (mult:QI (sign_extend:QI (and:QI (match_dup 2) 
                                                    (const_int 16777215)))
                            (sign_extend:QI (and:QI (match_dup 3) 
                                                    (const_int 16777215)))))
              (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 6)
                   (ashift:QI (match_dup 5) (const_int 17)))
              (clobber (reg:CC 21))])
   (parallel [(set (match_operand:QI 0 "reg_operand" "")
                   (plus:QI (match_dup 4) (match_dup 6)))
              (clobber (reg:CC_NOOV 21))])]
  ""
  "
  operands[2] = gen_reg_rtx (QImode); /* a = val & 0xffff */
  operands[3] = gen_reg_rtx (QImode); /* b = val >> 16 */
  operands[4] = gen_reg_rtx (QImode); /* a * a */
  operands[5] = gen_reg_rtx (QImode); /* a * b */
  operands[6] = gen_reg_rtx (QImode); /* (a * b) << 17 */
  operands[7] = gen_reg_rtx (QImode); /* val */
  ")

; Inlined integer multiply for C3x
(define_expand "mulqi3_inline"
  [(set (match_dup 12) (const_int -16))
   (set (match_dup 13) (match_operand:QI 1 "src_operand" ""))
   (set (match_dup 14) (match_operand:QI 2 "src_operand" ""))
   (parallel [(set (match_dup 4)
                   (lshiftrt:QI (match_dup 13) (neg:QI (match_dup 12))))
              (clobber (reg:CC 21))])
   (parallel [(set (match_dup 6)
                   (lshiftrt:QI (match_dup 14) (neg:QI (match_dup 12))))
              (clobber (reg:CC 21))])
   (parallel [(set (match_dup 3)
                   (and:QI (match_dup 13)
                           (const_int 65535)))
              (clobber (reg:CC 21))])
   (parallel [(set (match_dup 5)
                   (and:QI (match_dup 14) 
                           (const_int 65535)))
              (clobber (reg:CC 21))])
   (parallel [(set (match_dup 7)
                   (mult:QI (sign_extend:QI (and:QI (match_dup 4) 
                                                    (const_int 16777215)))
                            (sign_extend:QI (and:QI (match_dup 5) 
                                                    (const_int 16777215)))))
              (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 8)
                   (mult:QI (sign_extend:QI (and:QI (match_dup 3) 
                                                    (const_int 16777215)))
                            (sign_extend:QI (and:QI (match_dup 5) 
                                                    (const_int 16777215)))))
              (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 9)
                   (mult:QI (sign_extend:QI (and:QI (match_dup 3) 
                                                    (const_int 16777215)))
                            (sign_extend:QI (and:QI (match_dup 6) 
                                                    (const_int 16777215)))))
              (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 10)
                   (plus:QI (match_dup 7) (match_dup 9)))
              (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 11)
                   (ashift:QI (match_dup 10) (const_int 16)))
              (clobber (reg:CC 21))])
   (parallel [(set (match_operand:QI 0 "reg_operand" "")
                   (plus:QI (match_dup 8) (match_dup 11)))
              (clobber (reg:CC_NOOV 21))])]
  "TARGET_C3X"
  "
  operands[3] = gen_reg_rtx (QImode); /* a = arg1 & 0xffff */
  operands[4] = gen_reg_rtx (QImode); /* b = arg1 >> 16 */
  operands[5] = gen_reg_rtx (QImode); /* a = arg2 & 0xffff */
  operands[6] = gen_reg_rtx (QImode); /* b = arg2 >> 16 */
  operands[7] = gen_reg_rtx (QImode); /* b * c */
  operands[8] = gen_reg_rtx (QImode); /* a * c */
  operands[9] = gen_reg_rtx (QImode); /* a * d */
  operands[10] = gen_reg_rtx (QImode); /* b * c + a * d */
  operands[11] = gen_reg_rtx (QImode); /* (b *c + a * d) << 16 */
  operands[12] = gen_reg_rtx (QImode); /* -16 */
  operands[13] = gen_reg_rtx (QImode); /* arg1 */
  operands[14] = gen_reg_rtx (QImode); /* arg2 */
  ")

;
; MPYSHI (C4x only)
;
(define_expand "smulqi3_highpart"
  [(parallel [(set (match_operand:QI 0 "reg_operand" "")
                   (truncate:QI
                    (lshiftrt:HI
                     (mult:HI
                      (sign_extend:HI (match_operand:QI 1 "src_operand" ""))
                      (sign_extend:HI (match_operand:QI 2 "src_operand" "")))
                 (const_int 32))))
              (clobber (reg:CC_NOOV 21))])]
 ""
 "legitimize_operands (MULT, operands, QImode);
  if (TARGET_C3X)
    {
       c4x_emit_libcall_mulhi (smulhi3_libfunc, SIGN_EXTEND, QImode, operands);
       DONE;
    }
 ")

(define_insn "*smulqi3_highpart_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
        (truncate:QI 
         (lshiftrt:HI
          (mult:HI
           (sign_extend:HI (match_operand:QI 1 "src_operand" "%0,rR,rS<>,0,rR,rS<>"))
           (sign_extend:HI (match_operand:QI 2 "src_operand" "rIm,JR,rS<>,rIm,JR,rS<>")))
      (const_int 32))))
   (clobber (reg:CC_NOOV 21))]
  "! TARGET_C3X && valid_operands (MULT, operands, QImode)"
  "@
   mpyshi\\t%2,%0
   mpyshi3\\t%2,%1,%0
   mpyshi3\\t%2,%1,%0
   mpyshi\\t%2,%0
   mpyshi3\\t%2,%1,%0
   mpyshi3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")
   (set_attr "data" "int16,int16,int16,int16,int16,int16")])

(define_insn "*smulqi3_highpart_noclobber"
  [(set (match_operand:QI 0 "std_reg_operand" "=c,c,?c")
        (truncate:QI 
         (lshiftrt:HI
          (mult:HI
           (sign_extend:HI (match_operand:QI 1 "src_operand" "0,rR,rS<>"))
           (sign_extend:HI (match_operand:QI 2 "src_operand" "rIm,JR,rS<>")))
      (const_int 32))))]
  "! TARGET_C3X && valid_operands (MULT, operands, QImode)"
  "@
   mpyshi\\t%2,%0
   mpyshi3\\t%2,%1,%0
   mpyshi3\\t%2,%1,%0"
  [(set_attr "type" "binary,binary,binary")
   (set_attr "data" "int16,int16,int16")])

;
; MPYUHI (C4x only)
;
(define_expand "umulqi3_highpart"
  [(parallel [(set (match_operand:QI 0 "reg_operand" "")
               (truncate:QI
                (lshiftrt:HI
                 (mult:HI
                  (zero_extend:HI (match_operand:QI 1 "src_operand" ""))
                  (zero_extend:HI (match_operand:QI 2 "lsrc_operand" "")))
                 (const_int 32))))
              (clobber (reg:CC_NOOV 21))])]
 ""
 "legitimize_operands (MULT, operands, QImode);
  if (TARGET_C3X) 
    {
      c4x_emit_libcall_mulhi (umulhi3_libfunc, ZERO_EXTEND, QImode, operands);
      DONE;
    }
 ")

(define_insn "*umulqi3_highpart_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
        (truncate:QI
         (lshiftrt:HI
          (mult:HI 
           (zero_extend:HI (match_operand:QI 1 "src_operand" "%0,rR,rS<>,0,rR,rS<>"))
           (zero_extend:HI (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>,rLm,JR,rS<>")))
          (const_int 32))))
   (clobber (reg:CC_NOOV 21))]
  "! TARGET_C3X && valid_operands (MULT, operands, QImode)"
  "@
   mpyuhi\\t%2,%0
   mpyuhi3\\t%2,%1,%0
   mpyuhi3\\t%2,%1,%0
   mpyuhi\\t%2,%0
   mpyuhi3\\t%2,%1,%0
   mpyuhi3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")
   (set_attr "data" "uint16,uint16,uint16,uint16,uint16,uint16")])

(define_insn "*umulqi3_highpart_noclobber"
  [(set (match_operand:QI 0 "std_reg_operand" "=c,c,?c")
        (truncate:QI
         (lshiftrt:HI
          (mult:HI 
           (zero_extend:HI (match_operand:QI 1 "src_operand" "0,rR,rS<>"))
           (zero_extend:HI (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>")))
          (const_int 32))))]
  "! TARGET_C3X && valid_operands (MULT, operands, QImode)"
  "@
   mpyuhi\\t%2,%0
   mpyuhi3\\t%2,%1,%0
   mpyuhi3\\t%2,%1,%0"
  [(set_attr "type" "binary,binary,binary")
   (set_attr "data" "uint16,uint16,uint16")])

;
; AND
;
(define_expand "andqi3"
  [(parallel [(set (match_operand:QI 0 "reg_operand" "")
                   (and:QI (match_operand:QI 1 "src_operand" "")
                           (match_operand:QI 2 "tsrc_operand" "")))
              (clobber (reg:CC 21))])]
 ""
 "legitimize_operands (AND, operands, QImode);")


(define_insn "*andqi3_255_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,c")
        (and:QI (match_operand:QI 1 "src_operand" "mr,mr")
                (const_int 255)))
   (clobber (reg:CC 21))]
 "! TARGET_C3X"
 "lbu0\\t%1,%0"
  [(set_attr "type" "unarycc,unary")])

(define_insn "*andqi3_255_noclobber"
  [(set (match_operand:QI 0 "reg_operand" "=c")
        (and:QI (match_operand:QI 1 "src_operand" "mr")
                (const_int 255)))]
 "! TARGET_C3X"
 "lbu0\\t%1,%0"
  [(set_attr "type" "unary")])


(define_insn "*andqi3_65535_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,c")
        (and:QI (match_operand:QI 1 "src_operand" "mr,mr")
                (const_int 65535)))
   (clobber (reg:CC 21))]
 "! TARGET_C3X"
 "lhu0\\t%1,%0"
  [(set_attr "type" "unarycc,unary")])

(define_insn "*andqi3_65535_noclobber"
  [(set (match_operand:QI 0 "reg_operand" "=c")
        (and:QI (match_operand:QI 1 "src_operand" "mr")
                (const_int 65535)))]
 "! TARGET_C3X"
 "lhu0\\t%1,%0"
  [(set_attr "type" "unary")])

(define_insn "*andqi3_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,d,d,?d,c,c,c,?c")
        (and:QI (match_operand:QI 1 "src_operand" "%0,0,rR,rS<>,0,0,rR,rS<>")
                (match_operand:QI 2 "tsrc_operand" "N,rLm,JR,rS<>,N,rLm,JR,rS<>")))
   (clobber (reg:CC 21))]
  "valid_operands (AND, operands, QImode)"
  "@
   andn\\t%N2,%0
   and\\t%2,%0
   and3\\t%2,%1,%0
   and3\\t%2,%1,%0
   andn\\t%N2,%0
   and\\t%2,%0
   and3\\t%2,%1,%0
   and3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc,binarycc,binary,binary,binary,binary")
   (set_attr "data" "not_uint16,uint16,int16,uint16,not_uint16,uint16,int16,uint16")])

(define_insn "*andqi3_noclobber"
  [(set (match_operand:QI 0 "std_reg_operand" "=c,c,c,?c")
        (and:QI (match_operand:QI 1 "src_operand" "%0,0,rR,rS<>")
                (match_operand:QI 2 "tsrc_operand" "N,rLm,JR,rS<>")))]
  "valid_operands (AND, operands, QImode)"
  "@
   andn\\t%N2,%0
   and\\t%2,%0
   and3\\t%2,%1,%0
   and3\\t%2,%1,%0"
  [(set_attr "type" "binary,binary,binary,binary")
   (set_attr "data" "not_uint16,uint16,int16,uint16")])

(define_insn "andn_st"
  [(set (unspec:QI [(reg:QI 21)] 20)
        (and:QI (unspec:QI [(reg:QI 21)] 20)
                (match_operand:QI 0 "" "N")))
   (use (match_dup 0))
   (use (reg:CC 21))
   (clobber (reg:CC 21))]
  ""
  "andn\\t%N0,st"
  [(set_attr "type" "misc")
   (set_attr "data" "not_uint16")])

(define_split
  [(set (match_operand:QI 0 "std_reg_operand" "")
        (and:QI (match_operand:QI 1 "src_operand" "")
                (match_operand:QI 2 "tsrc_operand" "")))
   (clobber (reg:CC 21))]
  "reload_completed"
  [(set (match_dup 0)
        (and:QI (match_dup 1)
                (match_dup 2)))]
  "")

(define_insn "*andqi3_test"
  [(set (reg:CC 21)
        (compare:CC (and:QI (match_operand:QI 1 "src_operand" "%0,r,rR,rS<>")
                            (match_operand:QI 2 "tsrc_operand" "N,rLm,JR,rS<>"))
                    (const_int 0)))
   (clobber (match_scratch:QI 0 "=d,X,X,?X"))]
  "valid_operands (AND, operands, QImode)"
  "@
   andn\\t%N2,%0
   tstb\\t%2,%1
   tstb3\\t%2,%1
   tstb3\\t%2,%1"
  [(set_attr "type" "binarycc,binarycc,binarycc,binarycc")
   (set_attr "data" "not_uint16,uint16,int16,uint16")])

(define_peephole
  [(parallel [(set (match_operand:QI 0 "ext_reg_operand" "=d,d,d,?d")
                   (and:QI (match_operand:QI 1 "src_operand" "%0,0,rR,rS<>")
                           (match_operand:QI 2 "tsrc_operand" "N,rLm,JR,rS<>")))
              (clobber (reg:CC 21))])
   (set (reg:CC 21)
        (compare:CC (match_dup 0) (const_int 0)))]
  "valid_operands (AND, operands, QImode)"
  "@
   andn\\t%N2,%0
   and\\t%2,%0
   and3\\t%2,%1,%0
   and3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc,binarycc")
   (set_attr "data" "not_uint16,uint16,int16,uint16")])
  
(define_insn "*andqi3_set"
  [(set (reg:CC 21)
        (compare:CC (and:QI (match_operand:QI 1 "src_operand" "%0,0,rR,rS<>")
                            (match_operand:QI 2 "tsrc_operand" "N,rLm,JR,rS<>"))
                    (const_int 0)))
   (set (match_operand:QI 0 "ext_reg_operand" "=d,d,d,?d")
        (and:QI (match_dup 1)
                (match_dup 2)))]
  "valid_operands (AND, operands, QImode)"
  "@
   andn\\t%N2,%0
   and\\t%2,%0
   and3\\t%2,%1,%0
   and3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc,binarycc")
   (set_attr "data" "not_uint16,uint16,int16,uint16")])

;
; ANDN
;
; NB, this insn doesn't have commutative operands, but valid_operands
; assumes that the code AND does.  We might have to kludge this if
; we make valid_operands stricter.
(define_insn "*andnqi3_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
        (and:QI (not:QI (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>,rLm,JR,rS<>"))
                (match_operand:QI 1 "src_operand" "0,rR,rS<>,0,rR,rS<>")))
   (clobber (reg:CC 21))]
  "valid_operands (AND, operands, QImode)"
  "@
   andn\\t%2,%0
   andn3\\t%2,%1,%0
   andn3\\t%2,%1,%0
   andn\\t%2,%0
   andn3\\t%2,%1,%0
   andn3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")
   (set_attr "data" "uint16,int16,uint16,uint16,int16,uint16")])

(define_insn "*andnqi3_noclobber"
  [(set (match_operand:QI 0 "std_reg_operand" "=c,c,?c")
        (and:QI (not:QI (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>"))
                (match_operand:QI 1 "src_operand" "0,rR,rS<>")))]
  "valid_operands (AND, operands, QImode)"
  "@
   andn\\t%2,%0
   andn3\\t%2,%1,%0
   andn3\\t%2,%1,%0"
  [(set_attr "type" "binary,binary,binary")
   (set_attr "data" "uint16,int16,uint16")])

(define_split
  [(set (match_operand:QI 0 "std_reg_operand" "")
        (and:QI (not:QI (match_operand:QI 2 "lsrc_operand" ""))
                (match_operand:QI 1 "src_operand" "")))
   (clobber (reg:CC 21))]
  "reload_completed"
  [(set (match_dup 0)
        (and:QI (not:QI (match_dup 2))
                (match_dup 1)))]
  "")

(define_insn "*andnqi3_test"
  [(set (reg:CC 21)
        (compare:CC (and:QI (not:QI (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>"))
                            (match_operand:QI 1 "src_operand" "0,rR,rS<>"))
                    (const_int 0)))
   (clobber (match_scratch:QI 0 "=d,d,d"))]
  "valid_operands (AND, operands, QImode)"
  "@
   andn\\t%2,%0
   andn3\\t%2,%1,%0
   andn3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc")
   (set_attr "data" "uint16,int16,uint16")])

(define_insn "*andnqi3_set"
  [(set (reg:CC 21)
        (compare:CC (and:QI (not:QI (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>"))
                            (match_operand:QI 1 "src_operand" "0,rR,rS<>"))
                    (const_int 0)))
   (set (match_operand:QI 0 "ext_reg_operand" "=d,d,d")
        (and:QI (not:QI (match_dup 2))
                (match_dup 1)))]
  "valid_operands (AND, operands, QImode)"
  "@
   andn\\t%2,%0
   andn3\\t%2,%1,%0
   andn3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc")
   (set_attr "data" "uint16,int16,uint16")])

;
; OR
;
(define_expand "iorqi3"
  [(parallel [(set (match_operand:QI 0 "reg_operand" "")
                   (ior:QI (match_operand:QI 1 "src_operand" "")
                           (match_operand:QI 2 "lsrc_operand" "")))
              (clobber (reg:CC 21))])]
 ""
 "legitimize_operands (IOR, operands, QImode);")

(define_insn "*iorqi3_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
        (ior:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>,0,rR,rS<>")
                (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>,rLm,JR,rS<>")))
   (clobber (reg:CC 21))]
  "valid_operands (IOR, operands, QImode)"
  "@
   or\\t%2,%0
   or3\\t%2,%1,%0
   or3\\t%2,%1,%0
   or\\t%2,%0
   or3\\t%2,%1,%0
   or3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")
   (set_attr "data" "uint16,int16,uint16,uint16,int16,uint16")])

(define_split
  [(set (match_operand:QI 0 "std_reg_operand" "")
        (ior:QI (match_operand:QI 1 "src_operand" "")
                (match_operand:QI 2 "lsrc_operand" "")))
   (clobber (reg:CC 21))]
  "reload_completed"
  [(set (match_dup 0)
        (ior:QI (match_dup 1)
                (match_dup 2)))]
  "")

(define_insn "*iorqi3_test"
  [(set (reg:CC 21)
        (compare:CC (ior:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
                            (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>"))
                    (const_int 0)))
   (clobber (match_scratch:QI 0 "=d,d,d"))]
  "valid_operands (IOR, operands, QImode)"
  "@
   or\\t%2,%0
   or3\\t%2,%1,%0
   or3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc")
   (set_attr "data" "uint16,int16,uint16")])

(define_peephole
  [(parallel [(set (match_operand:QI 0 "ext_reg_operand" "=d,d,d")
                   (ior:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
                           (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>")))
              (clobber (reg:CC 21))])
   (set (reg:CC 21)
        (compare:CC (match_dup 0) (const_int 0)))]
  "valid_operands (IOR, operands, QImode)"
  "@
   or\\t%2,%0
   or3\\t%2,%1,%0
   or3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc")
   (set_attr "data" "uint16,int16,uint16")])
  
(define_insn "*iorqi3_set"
  [(set (reg:CC 21)
        (compare:CC (ior:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
                            (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>"))
                    (const_int 0)))
   (set (match_operand:QI 0 "ext_reg_operand" "=d,d,d")
        (ior:QI (match_dup 1)
                (match_dup 2)))]
  "valid_operands (IOR, operands, QImode)"
  "@
   or\\t%2,%0
   or3\\t%2,%1,%0
   or3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc")
   (set_attr "data" "uint16,int16,uint16")])

; This pattern is used for loading symbol references in several parts. 
(define_insn "iorqi3_noclobber"
  [(set (match_operand:QI 0 "std_reg_operand" "=c,c,c")
        (ior:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
                (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>")))]
  "valid_operands (IOR, operands, QImode)"
  "@
   or\\t%2,%0
   or3\\t%2,%1,%0
   or3\\t%2,%1,%0"
  [(set_attr "type" "binary,binary,binary")
   (set_attr "data" "uint16,int16,uint16")])

;
; XOR
;
(define_expand "xorqi3"
  [(parallel [(set (match_operand:QI 0 "reg_operand" "")
                   (xor:QI (match_operand:QI 1 "src_operand" "")
                           (match_operand:QI 2 "lsrc_operand" "")))
              (clobber (reg:CC 21))])]
 ""
 "legitimize_operands (XOR, operands, QImode);")

(define_insn "*xorqi3_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
        (xor:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>,0,rR,rS<>")
                (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>,rLm,JR,rS<>")))
   (clobber (reg:CC 21))]
  "valid_operands (XOR, operands, QImode)"
  "@
   xor\\t%2,%0
   xor3\\t%2,%1,%0
   xor3\\t%2,%1,%0
   xor\\t%2,%0
   xor3\\t%2,%1,%0
   xor3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")
   (set_attr "data" "uint16,int16,uint16,uint16,int16,uint16")])

(define_insn "*xorqi3_noclobber"
  [(set (match_operand:QI 0 "std_reg_operand" "=c,c,?c")
        (xor:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
                (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>")))]
  "valid_operands (XOR, operands, QImode)"
  "@
   xor\\t%2,%0
   xor3\\t%2,%1,%0
   xor3\\t%2,%1,%0"
  [(set_attr "type" "binary,binary,binary")
   (set_attr "data" "uint16,int16,uint16")])

(define_split
  [(set (match_operand:QI 0 "std_reg_operand" "")
        (xor:QI (match_operand:QI 1 "src_operand" "")
                (match_operand:QI 2 "lsrc_operand" "")))
   (clobber (reg:CC 21))]
  "reload_completed"
  [(set (match_dup 0)
        (xor:QI (match_dup 1)
                (match_dup 2)))]
  "")

(define_insn "*xorqi3_test"
  [(set (reg:CC 21)
        (compare:CC (xor:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
                            (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>"))
                    (const_int 0)))
   (clobber (match_scratch:QI 0 "=d,d,d"))]
  "valid_operands (XOR, operands, QImode)"
  "@
   xor\\t%2,%0
   xor3\\t%2,%1,%0
   xor3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc")
   (set_attr "data" "uint16,int16,uint16")])

(define_insn "*xorqi3_set"
  [(set (reg:CC 21)
        (compare:CC (xor:QI (match_operand:QI 1 "src_operand" "%0,rR,rS<>")
                            (match_operand:QI 2 "lsrc_operand" "rLm,JR,rS<>"))
                    (const_int 0)))
   (set (match_operand:QI 0 "ext_reg_operand" "=d,d,d")
        (xor:QI (match_dup 1)
                (match_dup 2)))]
  "valid_operands (XOR, operands, QImode)"
  "@
   xor\\t%2,%0
   xor3\\t%2,%1,%0
   xor3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc")
   (set_attr "data" "uint16,int16,uint16")])

;
; LSH/ASH (left)
;
; The C3x and C4x have two shift instructions ASH and LSH
; If the shift count is positive, a left shift is performed
; otherwise a right shift is performed.  The number of bits
; shifted is determined by the seven LSBs of the shift count.
; If the absolute value of the count is 32 or greater, the result
; using the LSH instruction is zero; with the ASH insn the result
; is zero or negative 1.   Note that the ISO C standard allows 
; the result to be machine dependent whenever the shift count
; exceeds the size of the object.
(define_expand "ashlqi3"
  [(parallel [(set (match_operand:QI 0 "reg_operand" "")
                   (ashift:QI (match_operand:QI 1 "src_operand" "")
                              (match_operand:QI 2 "src_operand" "")))
              (clobber (reg:CC 21))])]
 ""
 "legitimize_operands (ASHIFT, operands, QImode);")

(define_insn "*ashlqi3_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
        (ashift:QI (match_operand:QI 1 "src_operand" "0,rR,rS<>,0,rR,rS<>")
                   (match_operand:QI 2 "src_operand" "rIm,JR,rS<>,rIm,JR,rS<>")))
   (clobber (reg:CC 21))]
  "valid_operands (ASHIFT, operands, QImode)"
  "@
   ash\\t%2,%0
   ash3\\t%2,%1,%0
   ash3\\t%2,%1,%0
   ash\\t%2,%0
   ash3\\t%2,%1,%0
   ash3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")])
; Default to int16 data attr.

(define_insn "*ashlqi3_set"
  [(set (reg:CC 21)
        (compare:CC
          (ashift:QI (match_operand:QI 1 "src_operand" "0,rR,rS<>")
                     (match_operand:QI 2 "src_operand" "rIm,JR,rS<>"))
          (const_int 0)))
   (set (match_operand:QI 0 "reg_operand" "=d,d,d")
        (ashift:QI (match_dup 1)
                   (match_dup 2)))]
  "valid_operands (ASHIFT, operands, QImode)"
  "@
   ash\\t%2,%0
   ash3\\t%2,%1,%0
   ash3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc")])
; Default to int16 data attr.

(define_insn "ashlqi3_noclobber"
  [(set (match_operand:QI 0 "std_reg_operand" "=c,c,?c")
        (ashift:QI (match_operand:QI 1 "src_operand" "0,rR,rS<>")
                   (match_operand:QI 2 "src_operand" "rIm,JR,rS<>")))]
  "valid_operands (ASHIFT, operands, QImode)"
  "@
   ash\\t%2,%0
   ash3\\t%2,%1,%0
   ash3\\t%2,%1,%0"
  [(set_attr "type" "binary,binary,binary")])
; Default to int16 data attr.

(define_split
  [(set (match_operand:QI 0 "std_reg_operand" "")
        (ashift:QI (match_operand:QI 1 "src_operand" "")
                   (match_operand:QI 2 "src_operand" "")))
   (clobber (reg:CC 21))]
  "reload_completed"
  [(set (match_dup 0)
        (ashift:QI (match_dup 1)
                   (match_dup 2)))]
  "")

; This is only used by lshrhi3_reg where we need a LSH insn that will
; shift both ways.
(define_insn "*lshlqi3_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
        (ashift:QI (match_operand:QI 1 "src_operand" "0,rR,rS<>,0,rR,rS<>")
                   (unspec:QI [(match_operand:QI 2 "src_operand" "rIm,JR,rS<>,rIm,JR,rS<>")] 3)))
   (clobber (reg:CC 21))]
  "valid_operands (ASHIFT, operands, QImode)"
  "@
   lsh\\t%2,%0
   lsh3\\t%2,%1,%0
   lsh3\\t%2,%1,%0
   lsh\\t%2,%0
   lsh3\\t%2,%1,%0
   lsh3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")])
; Default to int16 data attr.

;
; LSH (right)
;
; Logical right shift on the C[34]x works by negating the shift count,
; then emitting a right shift with the shift count negated.  This means
; that all actual shift counts in the RTL will be positive.
;
(define_expand "lshrqi3"
  [(parallel [(set (match_operand:QI 0 "reg_operand" "")
                   (lshiftrt:QI (match_operand:QI 1 "src_operand" "")
                                (match_operand:QI 2 "src_operand" "")))
              (clobber (reg:CC 21))])]
  ""
  "legitimize_operands (LSHIFTRT, operands, QImode);")


(define_insn "*lshrqi3_24_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,c")
        (lshiftrt:QI (match_operand:QI 1 "src_operand" "mr,mr")
                     (const_int 24)))
   (clobber (reg:CC 21))]
  "! TARGET_C3X"
  "lbu3\\t%1,%0"
  [(set_attr "type" "unarycc")])


(define_insn "*ashrqi3_24_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,c")
        (ashiftrt:QI (match_operand:QI 1 "src_operand" "mr,mr")
                     (const_int 24)))
   (clobber (reg:CC 21))]
  "! TARGET_C3X"
  "lb3\\t%1,%0"
  [(set_attr "type" "unarycc")])


(define_insn "lshrqi3_16_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,c")
        (lshiftrt:QI (match_operand:QI 1 "src_operand" "mr,mr")
                     (const_int 16)))
   (clobber (reg:CC 21))]
  "! TARGET_C3X"
  "lhu1\\t%1,%0"
  [(set_attr "type" "unarycc")])


(define_insn "*ashrqi3_16_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,c")
        (ashiftrt:QI (match_operand:QI 1 "src_operand" "mr,mr")
                     (const_int 16)))
   (clobber (reg:CC 21))]
  "! TARGET_C3X"
  "lh1\\t%1,%0"
  [(set_attr "type" "unarycc")])


; When the shift count is greater than the size of the word
; the result can be implementation specific
(define_insn "*lshrqi3_const_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,c,?d,?c")
        (lshiftrt:QI (match_operand:QI 1 "src_operand" "0,0,r,r")
                     (match_operand:QI 2 "const_int_operand" "n,n,J,J")))
   (clobber (reg:CC 21))]
  "valid_operands (LSHIFTRT, operands, QImode)"
  "@
   lsh\\t%n2,%0
   lsh\\t%n2,%0
   lsh3\\t%n2,%1,%0
   lsh3\\t%n2,%1,%0"
  [(set_attr "type" "binarycc,binary,binarycc,binary")])

(define_insn "*lshrqi3_const_noclobber"
  [(set (match_operand:QI 0 "std_reg_operand" "=c,?c")
        (lshiftrt:QI (match_operand:QI 1 "src_operand" "0,r")
                     (match_operand:QI 2 "const_int_operand" "n,J")))]
  "valid_operands (LSHIFTRT, operands, QImode)"
  "@
   lsh\\t%n2,%0
   lsh3\\t%n2,%1,%0"
  [(set_attr "type" "binary,binary")])

; When the shift count is greater than the size of the word
; the result can be implementation specific
(define_insn "*lshrqi3_const_set"
  [(set (reg:CC 21)
        (compare:CC
          (lshiftrt:QI (match_operand:QI 1 "src_operand" "0,r")
                       (match_operand:QI 2 "const_int_operand" "n,J"))
          (const_int 0)))
   (set (match_operand:QI 0 "reg_operand" "=?d,d")
        (lshiftrt:QI (match_dup 1)
                     (match_dup 2)))]
  "valid_operands (LSHIFTRT, operands, QImode)"
  "@
   lsh\\t%n2,%0
   lsh3\\t%n2,%1,%0"
  [(set_attr "type" "binarycc,binarycc")])

(define_insn "*lshrqi3_nonconst_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
        (lshiftrt:QI (match_operand:QI 1 "src_operand" "0,rR,rS<>,0,rR,rS<>")
                     (neg:QI (match_operand:QI 2 "src_operand" "rm,R,rS<>,rm,R,rS<>"))))
   (clobber (reg:CC 21))]
  "valid_operands (LSHIFTRT, operands, QImode)"
  "@
   lsh\\t%2,%0
   lsh3\\t%2,%1,%0
   lsh3\\t%2,%1,%0
   lsh\\t%2,%0
   lsh3\\t%2,%1,%0
   lsh3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")])
; Default to int16 data attr.

(define_insn "*lshrqi3_nonconst_noclobber"
  [(set (match_operand:QI 0 "std_reg_operand" "=c,c,?c")
        (lshiftrt:QI (match_operand:QI 1 "src_operand" "0,rR,rS<>")
                     (neg:QI (match_operand:QI 2 "src_operand" "rm,R,rS<>"))))]
  "valid_operands (LSHIFTRT, operands, QImode)"
  "@
   lsh\\t%2,%0
   lsh3\\t%2,%1,%0
   lsh3\\t%2,%1,%0"
  [(set_attr "type" "binary,binary,binary")])
; Default to int16 data attr.

;
; ASH (right)
;
; Arithmetic right shift on the C[34]x works by negating the shift count,
; then emitting a right shift with the shift count negated.  This means
; that all actual shift counts in the RTL will be positive.

(define_expand "ashrqi3"
  [(parallel [(set (match_operand:QI 0 "reg_operand" "")
                   (ashiftrt:QI (match_operand:QI 1 "src_operand" "")
                                (match_operand:QI 2 "src_operand" "")))
              (clobber (reg:CC 21))])]
  ""
  "legitimize_operands (ASHIFTRT, operands, QImode);")

; When the shift count is greater than the size of the word
; the result can be implementation specific
(define_insn "*ashrqi3_const_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,c,?d,?c")
        (ashiftrt:QI (match_operand:QI 1 "src_operand" "0,0,r,r")
                     (match_operand:QI 2 "const_int_operand" "n,n,J,J")))
   (clobber (reg:CC 21))]
  "valid_operands (ASHIFTRT, operands, QImode)"
  "@
   ash\\t%n2,%0
   ash\\t%n2,%0
   ash3\\t%n2,%1,%0
   ash3\\t%n2,%1,%0"
  [(set_attr "type" "binarycc,binary,binarycc,binary")])

(define_insn "*ashrqi3_const_noclobber"
  [(set (match_operand:QI 0 "std_reg_operand" "=c,?c")
        (ashiftrt:QI (match_operand:QI 1 "src_operand" "0,r")
                     (match_operand:QI 2 "const_int_operand" "n,J")))]
  "valid_operands (ASHIFTRT, operands, QImode)"
  "@
   ash\\t%n2,%0
   ash3\\t%n2,%1,%0"
  [(set_attr "type" "binarycc,binarycc")])

; When the shift count is greater than the size of the word
; the result can be implementation specific
(define_insn "*ashrqi3_const_set"
  [(set (reg:CC 21)
        (compare:CC
          (ashiftrt:QI (match_operand:QI 1 "src_operand" "0,r")
                       (match_operand:QI 2 "const_int_operand" "n,J"))
          (const_int 0)))
   (set (match_operand:QI 0 "reg_operand" "=?d,d")
        (ashiftrt:QI (match_dup 1)
                     (match_dup 2)))]
  "valid_operands (ASHIFTRT, operands, QImode)"
  "@
   ash\\t%n2,%0
   ash3\\t%n2,%1,%0"
  [(set_attr "type" "binarycc,binarycc")])

(define_insn "*ashrqi3_nonconst_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,d,?d,c,c,?c")
        (ashiftrt:QI (match_operand:QI 1 "src_operand" "0,rR,rS<>,0,rR,rS<>")
                     (neg:QI (match_operand:QI 2 "src_operand" "rm,R,rS<>,rm,R,rS<>"))))
   (clobber (reg:CC 21))]
  "valid_operands (ASHIFTRT, operands, QImode)"
  "@
   ash\\t%2,%0
   ash3\\t%2,%1,%0
   ash3\\t%2,%1,%0
   ash\\t%2,%0
   ash3\\t%2,%1,%0
   ash3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc,binary,binary,binary")])
; Default to int16 data attr.

(define_insn "*ashrqi3_nonconst_noclobber"
  [(set (match_operand:QI 0 "std_reg_operand" "=c,c,?c")
        (ashiftrt:QI (match_operand:QI 1 "src_operand" "0,rR,rS<>")
                     (neg:QI (match_operand:QI 2 "src_operand" "rm,R,rS<>"))))]
  "valid_operands (ASHIFTRT, operands, QImode)"
  "@
   ash\\t%2,%0
   ash3\\t%2,%1,%0
   ash3\\t%2,%1,%0"
  [(set_attr "type" "binary,binary,binary")])
; Default to int16 data attr.

;
; CMPI
;
; Unfortunately the C40 doesn't allow cmpi3 7, *ar0++ so the next best
; thing would be to get the small constant loaded into a register (say r0)
; so that it could be hoisted out of the loop so that we only
; would need to do cmpi3 *ar0++, r0.  Now the loop optimisation pass
; comes before the flow pass (which finds autoincrements) so we're stuck.
; Ideally, GCC requires another loop optimisation pass (preferably after
; reload) so that it can hoist invariants out of loops.
; The current solution modifies legitimize_operands () so that small
; constants are forced into a pseudo register.
; 
(define_expand "cmpqi"
  [(set (reg:CC 21)
        (compare:CC (match_operand:QI 0 "src_operand" "")
                    (match_operand:QI 1 "src_operand" "")))]
  ""
  "legitimize_operands (COMPARE, operands, QImode);
   c4x_compare_op0 = operands[0];
   c4x_compare_op1 = operands[1];
   DONE;")

(define_insn "*cmpqi_test"
  [(set (reg:CC 21)
        (compare:CC (match_operand:QI 0 "src_operand" "r,rR,rS<>")
                    (match_operand:QI 1 "src_operand" "rIm,JR,rS<>")))]
  "valid_operands (COMPARE, operands, QImode)"
  "@
   cmpi\\t%1,%0
   cmpi3\\t%1,%0
   cmpi3\\t%1,%0"
  [(set_attr "type" "compare,compare,compare")])

(define_insn "*cmpqi_test_noov"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (match_operand:QI 0 "src_operand" "r,rR,rS<>")
                         (match_operand:QI 1 "src_operand" "rIm,JR,rS<>")))]
  "valid_operands (COMPARE, operands, QImode)"
  "@
   cmpi\\t%1,%0
   cmpi3\\t%1,%0
   cmpi3\\t%1,%0"
  [(set_attr "type" "compare,compare,compare")])


;
; BIT-FIELD INSTRUCTIONS
;

;
; LBx/LHw (C4x only)
;
(define_expand "extv"
  [(parallel [(set (match_operand:QI 0 "reg_operand" "")
                   (sign_extract:QI (match_operand:QI 1 "src_operand" "")
                                    (match_operand:QI 2 "const_int_operand" "")
                                    (match_operand:QI 3 "const_int_operand" "")))
              (clobber (reg:CC 21))])]
 "! TARGET_C3X"
 "if ((INTVAL (operands[2]) != 8 && INTVAL (operands[2]) != 16)
      || (INTVAL (operands[3]) % INTVAL (operands[2]) != 0))
        FAIL;
 ")

(define_insn "*extv_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,c")
        (sign_extract:QI (match_operand:QI 1 "src_operand" "rLm,rLm")
                         (match_operand:QI 2 "const_int_operand" "n,n")
                         (match_operand:QI 3 "const_int_operand" "n,n")))
   (clobber (reg:CC 21))]
  "! TARGET_C3X
   && (INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 16)
   && (INTVAL (operands[3]) % INTVAL (operands[2]) == 0)"
  "*
   if (INTVAL (operands[2]) == 8)
     {
       operands[3] = GEN_INT (INTVAL (operands[3]) / 8);
       return \"lb%3\\t%1,%0\";
     }
   operands[3] = GEN_INT (INTVAL (operands[3]) / 16);
   return \"lh%3\\t%1,%0\";
  "
  [(set_attr "type" "binarycc,binary")
   (set_attr "data" "int16,int16")])

(define_insn "*extv_clobber_test"
  [(set (reg:CC 21)
        (compare:CC (sign_extract:QI (match_operand:QI 1 "src_operand" "rLm")
                                     (match_operand:QI 2 "const_int_operand" "n")
                                     (match_operand:QI 3 "const_int_operand" "n"))
   		    (const_int 0)))
   (clobber (match_scratch:QI 0 "=d"))]
  "! TARGET_C3X
   && (INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 16)
   && (INTVAL (operands[3]) % INTVAL (operands[2]) == 0)"
  "*
   if (INTVAL (operands[2]) == 8)
     {
       operands[3] = GEN_INT (INTVAL (operands[3]) / 8);
       return \"lb%3\\t%1,%0\";
     }
   operands[3] = GEN_INT (INTVAL (operands[3]) / 16);
   return \"lh%3\\t%1,%0\";
  "
  [(set_attr "type" "binarycc")
   (set_attr "data" "int16")])

(define_insn "*extv_clobber_set"
  [(set (reg:CC 21)
        (compare:CC (sign_extract:QI (match_operand:QI 1 "src_operand" "rLm")
                                     (match_operand:QI 2 "const_int_operand" "n")
                                     (match_operand:QI 3 "const_int_operand" "n"))
   		    (const_int 0)))
   (set (match_operand:QI 0 "reg_operand" "=d")
        (sign_extract:QI (match_dup 1)
                         (match_dup 2)
                         (match_dup 3)))]
  "! TARGET_C3X
   && (INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 16)
   && (INTVAL (operands[3]) % INTVAL (operands[2]) == 0)"
  "*
   if (INTVAL (operands[2]) == 8)
     {
       operands[3] = GEN_INT (INTVAL (operands[3]) / 8);
       return \"lb%3\\t%1,%0\";
     }
   operands[3] = GEN_INT (INTVAL (operands[3]) / 16);
   return \"lh%3\\t%1,%0\";
  "
  [(set_attr "type" "binarycc")
   (set_attr "data" "int16")])

;
; LBUx/LHUw (C4x only)
;
(define_expand "extzv"
  [(parallel [(set (match_operand:QI 0 "reg_operand" "")
                   (zero_extract:QI (match_operand:QI 1 "src_operand" "")
                                    (match_operand:QI 2 "const_int_operand" "")
                                    (match_operand:QI 3 "const_int_operand" "")))
              (clobber (reg:CC 21))])]
 "! TARGET_C3X"
 "if ((INTVAL (operands[2]) != 8 && INTVAL (operands[2]) != 16)
      || (INTVAL (operands[3]) % INTVAL (operands[2]) != 0))
        FAIL;
 ")

(define_insn "*extzv_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,c")
        (zero_extract:QI (match_operand:QI 1 "src_operand" "rLm,rLm")
                         (match_operand:QI 2 "const_int_operand" "n,n")
                         (match_operand:QI 3 "const_int_operand" "n,n")))
   (clobber (reg:CC 21))]
  "! TARGET_C3X
   && (INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 16)
   && (INTVAL (operands[3]) % INTVAL (operands[2]) == 0)"
  "*
   if (INTVAL (operands[2]) == 8)
     {
       operands[3] = GEN_INT (INTVAL (operands[3]) / 8);
       return \"lbu%3\\t%1,%0\";
     }
   operands[3] = GEN_INT (INTVAL (operands[3]) / 16);
   return \"lhu%3\\t%1,%0\";
  "
  [(set_attr "type" "binarycc,binary")
   (set_attr "data" "uint16,uint16")])

(define_insn "*extzv_test"
  [(set (reg:CC 21)
        (compare:CC (zero_extract:QI (match_operand:QI 1 "src_operand" "rLm")
                                     (match_operand:QI 2 "const_int_operand" "n")
                                     (match_operand:QI 3 "const_int_operand" "n"))
   		    (const_int 0)))
   (clobber (match_scratch:QI 0 "=d"))]
  "! TARGET_C3X
   && (INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 16)
   && (INTVAL (operands[3]) % INTVAL (operands[2]) == 0)"
  "*
   if (INTVAL (operands[2]) == 8)
     {
       operands[3] = GEN_INT (INTVAL (operands[3]) / 8);
       return \"lbu%3\\t%1,%0\";
     }
   operands[3] = GEN_INT (INTVAL (operands[3]) / 16);
   return \"lhu%3\\t%1,%0\";
  "
  [(set_attr "type" "binarycc")
   (set_attr "data" "uint16")])

(define_insn "*extzv_set"
  [(set (reg:CC 21)
        (compare:CC (zero_extract:QI (match_operand:QI 1 "src_operand" "rLm")
                                     (match_operand:QI 2 "const_int_operand" "n")
                                     (match_operand:QI 3 "const_int_operand" "n"))
   		    (const_int 0)))
   (set (match_operand:QI 0 "ext_reg_operand" "=d")
        (zero_extract:QI (match_dup 1)
                         (match_dup 2)
                         (match_dup 3)))]
  "! TARGET_C3X
   && (INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 16)
   && (INTVAL (operands[3]) % INTVAL (operands[2]) == 0)"
  "*
   if (INTVAL (operands[2]) == 8)
     {
	/* 8 bit extract.  */
       operands[3] = GEN_INT (INTVAL (operands[3]) / 8);
       return \"lbu%3\\t%1,%0\";
     }
   /* 16 bit extract.  */
   operands[3] = GEN_INT (INTVAL (operands[3]) / 16);
   return \"lhu%3\\t%1,%0\";
  "
  [(set_attr "type" "binarycc")
   (set_attr "data" "uint16")])

;
; MBx/MHw (C4x only)
;
(define_expand "insv"
  [(parallel [(set (zero_extract:QI (match_operand:QI 0 "reg_operand" "")
                                    (match_operand:QI 1 "const_int_operand" "")
                                    (match_operand:QI 2 "const_int_operand" ""))
                   (match_operand:QI 3 "src_operand" ""))
              (clobber (reg:CC 21))])]
 "! TARGET_C3X"
 "if (! (((INTVAL (operands[1]) == 8 || INTVAL (operands[1]) == 16)
         && (INTVAL (operands[2]) % INTVAL (operands[1]) == 0))
        || (INTVAL (operands[1]) == 24 && INTVAL (operands[2]) == 8)))
    FAIL;
 ")

(define_insn "*insv_clobber"
  [(set (zero_extract:QI (match_operand:QI 0 "reg_operand" "+d,c")
                         (match_operand:QI 1 "const_int_operand" "n,n")
                         (match_operand:QI 2 "const_int_operand" "n,n"))
        (match_operand:QI 3 "src_operand" "rLm,rLm"))
   (clobber (reg:CC 21))]
  "! TARGET_C3X
   && (((INTVAL (operands[1]) == 8 || INTVAL (operands[1]) == 16)
        && (INTVAL (operands[2]) % INTVAL (operands[1]) == 0))
       || (INTVAL (operands[1]) == 24 && INTVAL (operands[2]) == 8))"
  "*
   if (INTVAL (operands[1]) == 8)
     {
       /* 8 bit insert.  */
       operands[2] = GEN_INT (INTVAL (operands[2]) / 8);
       return \"mb%2\\t%3,%0\";
     }
   else if (INTVAL (operands[1]) == 16)
     {
       /* 16 bit insert.  */
       operands[2] = GEN_INT (INTVAL (operands[2]) / 16);
       return \"mh%2\\t%3,%0\";
     }
   /* 24 bit insert.  */
   return \"lwl1\\t%3,%0\";
  "
  [(set_attr "type" "binarycc,binary")
   (set_attr "data" "uint16,uint16")])

(define_peephole
  [(parallel [(set (zero_extract:QI (match_operand:QI 0 "ext_reg_operand" "+d")
                                    (match_operand:QI 1 "const_int_operand" "n")
                                    (match_operand:QI 2 "const_int_operand" "n"))
                   (match_operand:QI 3 "src_operand" "rLm"))
	      (clobber (reg:CC 21))])
   (set (reg:CC 21)
        (compare:CC (match_dup 0) (const_int 0)))]
  "! TARGET_C3X
   && (INTVAL (operands[1]) == 8 || INTVAL (operands[1]) == 16)
   && (INTVAL (operands[2]) % INTVAL (operands[1]) == 0)"
  "*
   if (INTVAL (operands[1]) == 8)
     {
       operands[2] = GEN_INT (INTVAL (operands[2]) / 8);
       return \"mb%2\\t%3,%0\";
     }
   operands[2] = GEN_INT (INTVAL (operands[2]) / 16);
   return \"mh%2\\t%3,%0\";
  "
  [(set_attr "type" "binarycc")
   (set_attr "data" "uint16")])


; TWO OPERAND FLOAT INSTRUCTIONS
;

;
; LDF/STF
;
;  If one of the operands is not a register, then we should
;  emit two insns, using a scratch register.  This will produce
;  better code in loops if the source operand is invariant, since
;  the source reload can be optimised out.  During reload we cannot
;  use change_address or force_reg.
(define_expand "movqf"
  [(set (match_operand:QF 0 "src_operand" "")
        (match_operand:QF 1 "src_operand" ""))]
 ""
 "
{
  if (c4x_emit_move_sequence (operands, QFmode))
    DONE;
}")

; This can generate invalid stack slot displacements
(define_split
 [(set (match_operand:QI 0 "reg_operand" "")
       (unspec:QI [(match_operand:QF 1 "reg_operand" "")] 12))]
  "reload_completed"
  [(set (match_dup 3) (match_dup 1))
   (set (match_dup 0) (match_dup 2))]
  "operands[2] = assign_stack_temp (QImode, GET_MODE_SIZE (QImode), 0);
   operands[3] = copy_rtx (operands[2]);
   PUT_MODE (operands[3], QFmode);")


(define_insn "storeqf_int"
 [(set (match_operand:QI 0 "reg_operand" "=r")
       (unspec:QI [(match_operand:QF 1 "reg_operand" "f")] 12))]
 ""
 "#"
  [(set_attr "type" "multi")])

(define_split
 [(parallel [(set (match_operand:QI 0 "reg_operand" "")
                  (unspec:QI [(match_operand:QF 1 "reg_operand" "")] 12))
             (clobber (reg:CC 21))])]
  "reload_completed"
  [(set (mem:QF (pre_inc:QI (reg:QI 20)))
        (match_dup 1))
   (parallel [(set (match_dup 0)
                   (mem:QI (post_dec:QI (reg:QI 20))))
              (clobber (reg:CC 21))])]
  "")


; We need accurate death notes for this...
;(define_peephole
;  [(set (match_operand:QF 0 "reg_operand" "=f")
;        (match_operand:QF 1 "memory_operand" "m"))
;   (set (mem:QF (pre_inc:QI (reg:QI 20)))
;        (match_dup 0))
;   (parallel [(set (match_operand:QI 2 "reg_operand" "r")
;                   (mem:QI (post_dec:QI (reg:QI 20))))
;              (clobber (reg:CC 21))])]
;  ""
;  "ldiu\\t%1,%0")

(define_insn "storeqf_int_clobber"
 [(parallel [(set (match_operand:QI 0 "reg_operand" "=r")
                  (unspec:QI [(match_operand:QF 1 "reg_operand" "f")] 12))
             (clobber (reg:CC 21))])]
 ""
 "#"
  [(set_attr "type" "multi")])


; This can generate invalid stack slot displacements
(define_split
 [(set (match_operand:QF 0 "reg_operand" "")
       (unspec:QF [(match_operand:QI 1 "reg_operand" "")] 11))]
  "reload_completed"
  [(set (match_dup 2) (match_dup 1))
   (set (match_dup 0) (match_dup 3))]
  "operands[2] = assign_stack_temp (QImode, GET_MODE_SIZE (QImode), 0);
   operands[3] = copy_rtx (operands[2]);
   PUT_MODE (operands[3], QFmode);")


(define_insn "loadqf_int"
 [(set (match_operand:QF 0 "reg_operand" "=f")
       (unspec:QF [(match_operand:QI 1 "reg_operand" "r")] 11))]
 ""
 "#"
  [(set_attr "type" "multi")])

(define_split
 [(parallel [(set (match_operand:QF 0 "reg_operand" "")
                  (unspec:QF [(match_operand:QI 1 "reg_operand" "")] 11))
             (clobber (reg:CC 21))])]
  "reload_completed"
  [(set (mem:QI (pre_inc:QI (reg:QI 20)))
        (match_dup 1))
   (parallel [(set (match_dup 0)
                   (mem:QF (post_dec:QI (reg:QI 20))))
              (clobber (reg:CC 21))])]
  "")

(define_insn "loadqf_int_clobber"
 [(parallel [(set (match_operand:QF 0 "reg_operand" "=f")
                  (unspec:QF [(match_operand:QI 1 "reg_operand" "r")] 11))
             (clobber (reg:CC 21))])]
 ""
 "#"
  [(set_attr "type" "multi")])

; We must provide an alternative to store to memory in case we have to
; spill a register.
(define_insn "movqf_noclobber"
 [(set (match_operand:QF 0 "dst_operand" "=f,m")
       (match_operand:QF 1 "src_operand" "fHm,f"))]
 "REG_P (operands[0]) || REG_P (operands[1])"
 "@
  ldfu\\t%1,%0
  stf\\t%1,%0"
  [(set_attr "type" "unary,store")])

;(define_insn "*movqf_clobber"
;  [(set (match_operand:QF 0 "reg_operand" "=f")
;        (match_operand:QF 1 "src_operand" "fHm"))
;   (clobber (reg:CC 21))]
; "0"
; "ldf\\t%1,%0"
;  [(set_attr "type" "unarycc")])

(define_insn "*movqf_test"
  [(set (reg:CC 21)
        (compare:CC (match_operand:QF 1 "src_operand" "fHm")
                    (const_int 0)))
   (clobber (match_scratch:QF 0 "=f"))]
 ""
 "ldf\\t%1,%0"
  [(set_attr "type" "unarycc")])

(define_insn "*movqf_set"
  [(set (reg:CC 21)
        (compare:CC (match_operand:QF 1 "src_operand" "fHm")
                    (match_operand:QF 2 "fp_zero_operand" "G")))
    (set (match_operand:QF 0 "reg_operand" "=f")
         (match_dup 1))]
 ""
 "ldf\\t%1,%0"
  [(set_attr "type" "unarycc")])


(define_insn "*movqf_parallel"
 [(set (match_operand:QF 0 "parallel_operand" "=q,S<>!V,q,S<>!V")
       (match_operand:QF 1 "parallel_operand" "S<>!V,q,S<>!V,q"))
  (set (match_operand:QF 2 "parallel_operand" "=q,S<>!V,S<>!V,q")
       (match_operand:QF 3 "parallel_operand" "S<>!V,q,q,S<>!V"))]
 "TARGET_PARALLEL && valid_parallel_load_store (operands, QFmode)"
 "@
  ldf1\\t%1,%0\\n||\\tldf2\\t%3,%2
  stf1\\t%1,%0\\n||\\tstf2\\t%3,%2
  ldf\\t%1,%0\\n||\\tstf\\t%3,%2
  ldf\\t%3,%2\\n||\\tstf\\t%1,%0"
  [(set_attr "type" "load_load,store_store,load_store,store_load")])


;
; PUSH/POP
;
(define_insn "pushqf"
  [(set (mem:QF (pre_inc:QI (reg:QI 20)))
        (match_operand:QF 0 "reg_operand" "f"))]
 ""
 "pushf\\t%0"
 [(set_attr "type" "push")])

(define_insn "popqf"
  [(set (match_operand:QF 0 "reg_operand" "=f")
        (mem:QF (post_dec:QI (reg:QI 20))))
   (clobber (reg:CC 21))]
 ""
 "popf\\t%0"
 [(set_attr "type" "pop")])

(define_insn "popqf_unspec"
  [(set (unspec:QF [(match_operand:QF 0 "reg_operand" "=f")] 19)
        (mem:QF (post_dec:QI (reg:QI 20))))
   (clobber (match_dup 0))
   (clobber (reg:CC 21))]
 ""
 "popf\\t%0"
 [(set_attr "type" "pop")])

;
; ABSF
;
(define_expand "absqf2"
  [(parallel [(set (match_operand:QF 0 "reg_operand" "")
                   (abs:QF (match_operand:QF 1 "src_operand" "")))
              (clobber (reg:CC_NOOV 21))])]
""
"")

(define_insn "*absqf2_clobber"
  [(set (match_operand:QF 0 "reg_operand" "=f")
        (abs:QF (match_operand:QF 1 "src_operand" "fHm")))
   (clobber (reg:CC_NOOV 21))]
  ""
  "absf\\t%1,%0"
  [(set_attr "type" "unarycc")])

(define_insn "*absqf2_test"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (abs:QF (match_operand:QF 1 "src_operand" "fHm"))
                         (match_operand:QF 2 "fp_zero_operand" "G")))
   (clobber (match_scratch:QF 0 "=f"))]
  ""
  "absf\\t%1,%0"
  [(set_attr "type" "unarycc")])

(define_insn "*absqf2_set"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (abs:QF (match_operand:QF 1 "src_operand" "fHm"))
                         (match_operand:QF 2 "fp_zero_operand" "G")))
   (set (match_operand:QF 0 "reg_operand" "=f")
        (abs:QF (match_dup 1)))]

  ""
  "absf\\t%1,%0"
  [(set_attr "type" "unarycc")])

;
; NEGF
;
(define_expand "negqf2"
  [(parallel [(set (match_operand:QF 0 "reg_operand" "")
                   (neg:QF (match_operand:QF 1 "src_operand" "")))
              (clobber (reg:CC_NOOV 21))])]
""
"")

(define_insn "*negqf2_clobber"
  [(set (match_operand:QF 0 "reg_operand" "=f")
        (neg:QF (match_operand:QF 1 "src_operand" "fHm")))
   (clobber (reg:CC_NOOV 21))]
  ""
  "negf\\t%1,%0"
  [(set_attr "type" "unarycc")])

(define_insn "*negqf2_test"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (neg:QF (match_operand:QF 1 "src_operand" "fHm"))
                         (match_operand:QF 2 "fp_zero_operand" "G")))
   (clobber (match_scratch:QF 0 "=f"))]
  ""
  "negf\\t%1,%0"
  [(set_attr "type" "unarycc")])

(define_insn "*negqf2_set"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (neg:QF (match_operand:QF 1 "src_operand" "fHm"))
                         (match_operand:QF 2 "fp_zero_operand" "G")))
   (set (match_operand:QF 0 "reg_operand" "=f")
        (neg:QF (match_dup 1)))]
  ""
  "negf\\t%1,%0"
  [(set_attr "type" "unarycc")])

;
; FLOAT
;
(define_insn "floatqiqf2"
  [(set (match_operand:QF 0 "reg_operand" "=f")
        (float:QF (match_operand:QI 1 "src_operand" "rIm")))
   (clobber (reg:CC 21))]
 ""
 "float\\t%1,%0"
  [(set_attr "type" "unarycc")])

(define_insn "*floatqiqf2_set"
  [(set (reg:CC 21)
        (compare:CC (float:QF (match_operand:QI 1 "src_operand" "rIm"))
                    (match_operand:QF 2 "fp_zero_operand" "G")))
   (set (match_operand:QF 0 "reg_operand" "=f")
        (float:QF (match_dup 1)))]
 ""
 "float\\t%1,%0"
  [(set_attr "type" "unarycc")])

; Unsigned conversions are a little tricky because we need to
; add the value for the high bit if necessary.
; 
;
(define_expand "floatunsqiqf2"
 [(set (match_dup 2) (match_dup 3))
  (parallel [(set (reg:CC 21)
                  (compare:CC (float:QF (match_operand:QI 1 "src_operand" ""))
                              (match_dup 3)))
             (set (match_dup 4)
                  (float:QF (match_dup 1)))])
  (set (match_dup 6)
       (if_then_else:QF (lt (reg:CC 21) (const_int 0))
                        (match_dup 5)
                        (match_dup 2)))
  (parallel [(set (match_operand:QF 0 "reg_operand" "")
                  (plus:QF (match_dup 6) (match_dup 4)))
             (clobber (reg:CC_NOOV 21))])]
 ""
 "operands[2] = gen_reg_rtx (QFmode);
  operands[3] = CONST0_RTX (QFmode); 
  operands[4] = gen_reg_rtx (QFmode);
  operands[5] = gen_reg_rtx (QFmode);
  operands[6] = gen_reg_rtx (QFmode);
  emit_move_insn (operands[5], 
   immed_real_const_1 (REAL_VALUE_ATOF (\"4294967296.0\", QFmode), QFmode));")

(define_expand "floatunsqihf2"
 [(set (match_dup 2) (match_dup 3))
  (parallel [(set (reg:CC 21)
                  (compare:CC (float:HF (match_operand:QI 1 "src_operand" ""))
                              (match_dup 3)))
             (set (match_dup 4)
                  (float:HF (match_dup 1)))])
  (set (match_dup 6)
       (if_then_else:HF (lt (reg:CC 21) (const_int 0))
                        (match_dup 5)
                        (match_dup 2)))
  (parallel [(set (match_operand:HF 0 "reg_operand" "")
                  (plus:HF (match_dup 6) (match_dup 4)))
             (clobber (reg:CC_NOOV 21))])]
 ""
 "operands[2] = gen_reg_rtx (HFmode);
  operands[3] = CONST0_RTX (HFmode); 
  operands[4] = gen_reg_rtx (HFmode);
  operands[5] = gen_reg_rtx (HFmode);
  operands[6] = gen_reg_rtx (HFmode);
  emit_move_insn (operands[5], 
   immed_real_const_1 (REAL_VALUE_ATOF (\"4294967296.0\", HFmode), HFmode));")

(define_insn "floatqihf2"
  [(set (match_operand:HF 0 "reg_operand" "=h")
        (float:HF (match_operand:QI 1 "src_operand" "rIm")))
   (clobber (reg:CC 21))]
 ""
 "float\\t%1,%0"
  [(set_attr "type" "unarycc")])

(define_insn "*floatqihf2_set"
  [(set (reg:CC 21)
	(compare:CC (float:HF (match_operand:QI 1 "src_operand" "rIm"))
                    (match_operand:QF 2 "fp_zero_operand" "G")))
   (set (match_operand:HF 0 "reg_operand" "=h")
        (float:HF (match_dup 1)))]
 ""
 "float\\t%1,%0"
  [(set_attr "type" "unarycc")])

;
; FIX
;
(define_insn "fixqfqi_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=d,c")
        (fix:QI (match_operand:QF 1 "src_operand" "fHm,fHm")))
   (clobber (reg:CC 21))]
 ""
 "fix\\t%1,%0"
  [(set_attr "type" "unarycc")])

(define_insn "*fixqfqi_set"
  [(set (reg:CC 21)
        (compare:CC (fix:QI (match_operand:QF 1 "src_operand" "fHm"))
                    (const_int 0)))
   (set (match_operand:QI 0 "ext_reg_operand" "=d")
        (fix:QI (match_dup 1)))]
 ""
 "fix\\t%1,%0"
  [(set_attr "type" "unarycc")])

(define_insn "*fixhfqi_set"
  [(set (reg:CC 21)
        (compare:CC (fix:QI (match_operand:HF 1 "src_operand" "fH"))
                    (const_int 0)))
   (set (match_operand:QI 0 "ext_reg_operand" "=d")
        (fix:QI (match_dup 1)))]
 ""
 "fix\\t%1,%0"
  [(set_attr "type" "unarycc")])

;
; The C[34]x fix instruction implements a floor, not a straight trunc,
; so we have to invert the number, fix it, and reinvert it if negative
;
(define_expand "fix_truncqfqi2"
  [(parallel [(set (match_dup 2)
                   (fix:QI (match_operand:QF 1 "src_operand" "")))
              (clobber (reg:CC 21))])
   (parallel [(set (match_dup 3) (neg:QF (match_dup 1)))
              (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 4) (fix:QI (match_dup 3)))
              (clobber (reg:CC 21))])
   (parallel [(set (reg:CC_NOOV 21)
                   (compare:CC_NOOV (neg:QI (match_dup 4)) (const_int 0)))
              (set (match_dup 5) (neg:QI (match_dup 4)))])
   (set (match_dup 2)
        (if_then_else:QI (le (reg:CC 21) (const_int 0))
                         (match_dup 5)
                         (match_dup 2)))
   (set (match_operand:QI 0 "reg_operand" "=r") (match_dup 2))]
 ""
 "if (TARGET_FAST_FIX)
    {
       emit_insn (gen_fixqfqi_clobber (operands[0], operands[1]));
       DONE;
    }
  operands[2] = gen_reg_rtx (QImode);
  operands[3] = gen_reg_rtx (QFmode);
  operands[4] = gen_reg_rtx (QImode);
  operands[5] = gen_reg_rtx (QImode);
 ")

(define_expand "fix_trunchfqi2"
  [(parallel [(set (match_dup 2)
                   (fix:QI (match_operand:HF 1 "src_operand" "")))
              (clobber (reg:CC 21))])
   (parallel [(set (match_dup 3) (neg:HF (match_dup 1)))
              (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 4) (fix:QI (match_dup 3)))
              (clobber (reg:CC 21))])
   (parallel [(set (reg:CC_NOOV 21)
                   (compare:CC_NOOV (neg:QI (match_dup 4)) (const_int 0)))
              (set (match_dup 5) (neg:QI (match_dup 4)))])
   (set (match_dup 2)
        (if_then_else:QI (le (reg:CC 21) (const_int 0))
                         (match_dup 5)
                         (match_dup 2)))
   (set (match_operand:QI 0 "reg_operand" "=r") (match_dup 2))]
 ""
 "if (TARGET_FAST_FIX)
    {
       emit_insn (gen_fixhfqi_clobber (operands[0], operands[1]));
       DONE;
    }
  operands[2] = gen_reg_rtx (QImode);
  operands[3] = gen_reg_rtx (HFmode);
  operands[4] = gen_reg_rtx (QImode);
  operands[5] = gen_reg_rtx (QImode);
 ")

(define_expand "fix_truncqfhi2"
  [(parallel [(set (match_operand:HI 0 "reg_operand" "")
                   (fix:HI (match_operand:QF 1 "src_operand" "")))
              (clobber (reg:CC 21))])]
  ""
  "c4x_emit_libcall (fix_truncqfhi2_libfunc, FIX, HImode, QFmode, 2, operands);
   DONE;")

(define_expand "fixuns_truncqfqi2"
 [(parallel [(set (match_dup 2)
		  (fix:QI (match_operand:QF 1 "src_operand" "fHm")))
	     (clobber (reg:CC 21))])
  (parallel [(set (match_dup 3)
	          (minus:QF (match_dup 1) (match_dup 5)))
	     (clobber (reg:CC_NOOV 21))])
  (parallel [(set (reg:CC 21)
		  (compare:CC (fix:QI (match_dup 3))
		              (const_int 0)))
	     (set (match_dup 4)
		  (fix:QI (match_dup 3)))])
  (parallel [(set (match_dup 4) (unspec:QI [(match_dup 2)] 13))
             (use (reg:CC 21))])
  (set (match_operand:QI 0 "reg_operand" "=r") (match_dup 4))]
 ""
 "operands[2] = gen_reg_rtx (QImode);
  operands[3] = gen_reg_rtx (QFmode);
  operands[4] = gen_reg_rtx (QImode);
  operands[5] = gen_reg_rtx (QFmode);
  emit_move_insn (operands[5],
   immed_real_const_1 (REAL_VALUE_ATOF (\"4294967296.0\", QFmode), QFmode));")

(define_expand "fixuns_trunchfqi2"
 [(parallel [(set (match_dup 2)
		  (fix:QI (match_operand:HF 1 "src_operand" "hH")))
	     (clobber (reg:CC 21))])
  (parallel [(set (match_dup 3)
	          (minus:HF (match_dup 1) (match_dup 5)))
	     (clobber (reg:CC_NOOV 21))])
  (parallel [(set (reg:CC 21)
		  (compare:CC (fix:QI (match_dup 3))
		              (const_int 0)))
	     (set (match_dup 4)
		  (fix:QI (match_dup 3)))])
  (parallel [(set (match_dup 4) (unspec:QI [(match_dup 2)] 13))
             (use (reg:CC 21))])
  (set (match_operand:QI 0 "reg_operand" "=r") (match_dup 4))]
 ""
 "operands[2] = gen_reg_rtx (QImode);
  operands[3] = gen_reg_rtx (HFmode);
  operands[4] = gen_reg_rtx (QImode);
  operands[5] = gen_reg_rtx (HFmode);
  emit_move_insn (operands[5],
   immed_real_const_1 (REAL_VALUE_ATOF (\"4294967296.0\", HFmode), HFmode));")

(define_expand "fixuns_truncqfhi2"
  [(parallel [(set (match_operand:HI 0 "reg_operand" "")
                   (unsigned_fix:HI (match_operand:QF 1 "src_operand" "")))
              (clobber (reg:CC 21))])]
  ""
  "c4x_emit_libcall (fixuns_truncqfhi2_libfunc, UNSIGNED_FIX, 
                     HImode, QFmode, 2, operands);
   DONE;")

;
; RCPF
;
(define_insn "rcpfqf_clobber"
  [(set (match_operand:QF 0 "reg_operand" "=f")
        (unspec:QF [(match_operand:QF 1 "src_operand" "fHm")] 5))
   (clobber (reg:CC_NOOV 21))]
  "! TARGET_C3X"
  "rcpf\\t%1,%0"
  [(set_attr "type" "unarycc")])

;
; RSQRF
;
(define_insn "*rsqrfqf_clobber"
  [(set (match_operand:QF 0 "reg_operand" "=f")
        (unspec:QF [(match_operand:QF 1 "src_operand" "fHm")] 10))
   (clobber (reg:CC_NOOV 21))]
  "! TARGET_C3X"
  "rsqrf\\t%1,%0"
  [(set_attr "type" "unarycc")])

;
; RNDF
;
(define_insn "*rndqf_clobber"
  [(set (match_operand:QF 0 "reg_operand" "=f")
        (unspec:QF [(match_operand:QF 1 "src_operand" "fHm")] 6))
   (clobber (reg:CC_NOOV 21))]
  "! TARGET_C3X"
  "rnd\\t%1,%0"
  [(set_attr "type" "unarycc")])


; Inlined float square root for C4x
(define_expand "sqrtqf2_inline"
  [(parallel [(set (match_dup 2)
	           (unspec:QF [(match_operand:QF 1 "src_operand" "")] 10))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 3) (mult:QF (match_dup 5) (match_dup 1)))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 4) (mult:QF (match_dup 2) (match_dup 3)))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 4) (mult:QF (match_dup 2) (match_dup 4)))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 4) (minus:QF (match_dup 6) (match_dup 4)))
              (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 2) (mult:QF (match_dup 2) (match_dup 4)))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 4) (mult:QF (match_dup 2) (match_dup 3)))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 4) (mult:QF (match_dup 2) (match_dup 4)))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 4) (minus:QF (match_dup 6) (match_dup 4)))
              (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 2) (mult:QF (match_dup 2) (match_dup 4)))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 4) (mult:QF (match_dup 2) (match_dup 1)))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_operand:QF 0 "reg_operand" "")
	           (unspec:QF [(match_dup 4)] 6))
	      (clobber (reg:CC_NOOV 21))])]
  "! TARGET_C3X"
  "if (! reload_in_progress
       && ! reg_operand (operands[1], QFmode))
     operands[1] = force_reg (QFmode, operands[1]);
   operands[2] = gen_reg_rtx (QFmode);
   operands[3] = gen_reg_rtx (QFmode);
   operands[4] = gen_reg_rtx (QFmode);
   operands[5] = immed_real_const_1 (REAL_VALUE_ATOF (\"0.5\", QFmode),
                                     QFmode);
   operands[6] = immed_real_const_1 (REAL_VALUE_ATOF (\"1.5\", QFmode),
                                     QFmode);")

(define_expand "sqrtqf2"
  [(parallel [(set (match_operand:QF 0 "reg_operand" "")
                   (sqrt:QF (match_operand:QF 1 "src_operand" "")))
              (clobber (reg:CC 21))])]
  "! TARGET_C3X && TARGET_INLINE"
  "emit_insn (gen_sqrtqf2_inline (operands[0], operands[1]));
   DONE;")

;
; TOIEEE / FRIEEE
;
(define_insn "toieee"
  [(set (match_operand:QF 0 "reg_operand" "=f")
        (unspec:QF [(match_operand:QF 1 "src_operand" "fHm")] 23))
   (clobber (reg:CC 21))]
 ""
 "toieee\\t%1,%0")

(define_insn "frieee"
  [(set (match_operand:QF 0 "reg_operand" "=f")
        (unspec:QF [(match_operand:QF 1 "memory_operand" "m")] 24))
   (clobber (reg:CC 21))]
 ""
 "frieee\\t%1,%0")

;
; THREE OPERAND FLOAT INSTRUCTIONS
;

;
; ADDF
;
(define_expand "addqf3"
  [(parallel [(set (match_operand:QF 0 "reg_operand" "")
                   (plus:QF (match_operand:QF 1 "src_operand" "")
                            (match_operand:QF 2 "src_operand" "")))
              (clobber (reg:CC_NOOV 21))])]
  ""
  "legitimize_operands (PLUS, operands, QFmode);")

(define_insn "*addqf3_clobber"
  [(set (match_operand:QF 0 "reg_operand" "=f,f,?f")
        (plus:QF (match_operand:QF 1 "src_operand" "%0,fR,fS<>")
                 (match_operand:QF 2 "src_operand" "fHm,R,fS<>")))
   (clobber (reg:CC_NOOV 21))]
  "valid_operands (PLUS, operands, QFmode)"
  "@
   addf\\t%2,%0
   addf3\\t%2,%1,%0
   addf3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc")])

(define_insn "*addqf3_test"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (plus:QF (match_operand:QF 1 "src_operand" "%0,fR,fS<>")
                                  (match_operand:QF 2 "src_operand" "fHm,R,fS<>"))
                         (match_operand:QF 3 "fp_zero_operand" "G,G,G")))
   (clobber (match_scratch:QF 0 "=f,f,?f"))]
  "valid_operands (PLUS, operands, QFmode)"
  "@
   addf\\t%2,%0
   addf3\\t%2,%1,%0
   addf3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc")])

(define_insn "*addqf3_set"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (plus:QF (match_operand:QF 1 "src_operand" "%0,fR,fS<>")
                                  (match_operand:QF 2 "src_operand" "fHm,R,fS<>"))
                         (match_operand:QF 3 "fp_zero_operand" "G,G,G")))
   (set (match_operand:QF 0 "reg_operand" "=f,f,?f")
        (plus:QF (match_dup 1)
                 (match_dup 2)))]
  "valid_operands (PLUS, operands, QFmode)"
  "@
   addf\\t%2,%0
   addf3\\t%2,%1,%0
   addf3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc")])

;
; SUBF/SUBRF
;
(define_expand "subqf3"
  [(parallel [(set (match_operand:QF 0 "reg_operand" "")
                   (minus:QF (match_operand:QF 1 "src_operand" "")
                             (match_operand:QF 2 "src_operand" "")))
              (clobber (reg:CC_NOOV 21))])]
  ""
  "legitimize_operands (MINUS, operands, QFmode);")

(define_insn "*subqf3_clobber"
   [(set (match_operand:QF 0 "reg_operand" "=f,f,f,?f")
         (minus:QF (match_operand:QF 1 "src_operand" "0,fHm,fR,fS<>")
                   (match_operand:QF 2 "src_operand" "fHm,0,R,fS<>")))
   (clobber (reg:CC_NOOV 21))]
  "valid_operands (MINUS, operands, QFmode)"
  "@
   subf\\t%2,%0
   subrf\\t%1,%0
   subf3\\t%2,%1,%0
   subf3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc,binarycc")])

(define_insn "*subqf3_test"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (minus:QF (match_operand:QF 1 "src_operand" "0,fHm,fR,fS<>")
                                   (match_operand:QF 2 "src_operand" "fHm,0,R,fS<>"))
                         (match_operand:QF 3 "fp_zero_operand" "G,G,G,G")))
   (clobber (match_scratch:QF 0 "=f,f,f,?f"))]
  "valid_operands (MINUS, operands, QFmode)"
  "@
   subf\\t%2,%0
   subrf\\t%1,%0
   subf3\\t%2,%1,%0
   subf3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc,binarycc")])

(define_insn "*subqf3_set"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (minus:QF (match_operand:QF 1 "src_operand" "0,fHm,fR,fS<>")
                                   (match_operand:QF 2 "src_operand" "fHm,0,R,fS<>"))
                         (match_operand:QF 3 "fp_zero_operand" "G,G,G,G")))
   (set (match_operand:QF 0 "reg_operand" "=f,f,f,?f")
        (minus:QF (match_dup 1)
                  (match_dup 2)))]
  "valid_operands (MINUS, operands, QFmode)"
  "@
   subf\\t%2,%0
   subrf\\t%1,%0
   subf3\\t%2,%1,%0
   subf3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc,binarycc")])

;
; MPYF
;
(define_expand "mulqf3"
  [(parallel [(set (match_operand:QF 0 "reg_operand" "")
                   (mult:QF (match_operand:QF 1 "src_operand" "")
                            (match_operand:QF 2 "src_operand" "")))
              (clobber (reg:CC_NOOV 21))])]
  ""
  "legitimize_operands (MULT, operands, QFmode);")

(define_insn "*mulqf3_clobber"
  [(set (match_operand:QF 0 "reg_operand" "=f,f,?f")
        (mult:QF (match_operand:QF 1 "src_operand" "%0,fR,fS<>")
                 (match_operand:QF 2 "src_operand" "fHm,R,fS<>")))
   (clobber (reg:CC_NOOV 21))]
  "valid_operands (MULT, operands, QFmode)"
  "@
   mpyf\\t%2,%0
   mpyf3\\t%2,%1,%0
   mpyf3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc")])

(define_insn "*mulqf3_test"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (mult:QF (match_operand:QF 1 "src_operand" "%0,fR,fS<>")
                                  (match_operand:QF 2 "src_operand" "fHm,R,fS<>"))
                         (match_operand:QF 3 "fp_zero_operand" "G,G,G")))
   (clobber (match_scratch:QF 0 "=f,f,?f"))]
  "valid_operands (MULT, operands, QFmode)"
  "@
   mpyf\\t%2,%0
   mpyf3\\t%2,%1,%0
   mpyf3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc")])

(define_insn "*mulqf3_set"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (mult:QF (match_operand:QF 1 "src_operand" "%0,fR,fS<>")
                                  (match_operand:QF 2 "src_operand" "fHm,R,fS<>"))
                         (match_operand:QF 3 "fp_zero_operand" "G,G,G")))
   (set (match_operand:QF 0 "reg_operand" "=f,f,?f")
        (mult:QF (match_dup 1)
                 (match_dup 2)))]
  "valid_operands (MULT, operands, QFmode)"
  "@
   mpyf\\t%2,%0
   mpyf3\\t%2,%1,%0
   mpyf3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc")])

;
; CMPF
;
(define_expand "cmpqf"
  [(set (reg:CC 21)
        (compare:CC (match_operand:QF 0 "src_operand" "")
                    (match_operand:QF 1 "src_operand" "")))]
  ""
  "legitimize_operands (COMPARE, operands, QFmode);
   c4x_compare_op0 = operands[0];
   c4x_compare_op1 = operands[1];
   DONE;")

(define_insn "*cmpqf"
  [(set (reg:CC 21)
        (compare:CC (match_operand:QF 0 "src_operand" "f,fR,fS<>")
                    (match_operand:QF 1 "src_operand" "fHm,R,fS<>")))]
  "valid_operands (COMPARE, operands, QFmode)"
  "@
   cmpf\\t%1,%0
   cmpf3\\t%1,%0
   cmpf3\\t%1,%0"
  [(set_attr "type" "compare,compare,compare")])

(define_insn "*cmpqf_noov"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (match_operand:QF 0 "src_operand" "f,fR,fS<>")
                         (match_operand:QF 1 "src_operand" "fHm,R,fS<>")))]
  "valid_operands (COMPARE, operands, QFmode)"
  "@
   cmpf\\t%1,%0
   cmpf3\\t%1,%0
   cmpf3\\t%1,%0"
  [(set_attr "type" "compare,compare,compare")])

; Inlined float divide for C4x
(define_expand "divqf3_inline"
  [(parallel [(set (match_dup 3)
	           (unspec:QF [(match_operand:QF 2 "src_operand" "")] 5))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 4) (mult:QF (match_dup 2) (match_dup 3)))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 4) (minus:QF (match_dup 5) (match_dup 4)))
              (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 3) (mult:QF (match_dup 3) (match_dup 4)))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 4) (mult:QF (match_dup 2) (match_dup 3)))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 4) (minus:QF (match_dup 5) (match_dup 4)))
              (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 3) (mult:QF (match_dup 3) (match_dup 4)))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 3)
		   (mult:QF (match_operand:QF 1 "src_operand" "")
	         	    (match_dup 3)))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_operand:QF 0 "reg_operand" "")
	           (unspec:QF [(match_dup 3)] 6))
	      (clobber (reg:CC_NOOV 21))])]
  "! TARGET_C3X"
  "if (! reload_in_progress
      && ! reg_operand (operands[2], QFmode))
     operands[2] = force_reg (QFmode, operands[2]);
   operands[3] = gen_reg_rtx (QFmode);
   operands[4] = gen_reg_rtx (QFmode);
   operands[5] = CONST2_RTX (QFmode);")

(define_expand "divqf3"
  [(parallel [(set (match_operand:QF 0 "reg_operand" "")
                   (div:QF (match_operand:QF 1 "src_operand" "")
                            (match_operand:QF 2 "src_operand" "")))
              (clobber (reg:CC 21))])]
  "! TARGET_C3X && TARGET_INLINE"
  "emit_insn (gen_divqf3_inline (operands[0], operands[1], operands[2]));
   DONE;")

;
; CONDITIONAL MOVES
;

; ???  We should make these pattern fail if the src operand combination
; is not valid.  Although reload will fix things up, it will introduce
; extra load instructions that won't be hoisted out of a loop.

(define_insn "*ldi_conditional"
  [(set (match_operand:QI 0 "reg_operand" "=r,r")
        (if_then_else:QI (match_operator 1 "comparison_operator"
                          [(reg:CC 21) (const_int 0)])
                         (match_operand:QI 2 "src_operand" "rIm,0")
                         (match_operand:QI 3 "src_operand" "0,rIm")))]
 ""
 "@
  ldi%1\\t%2,%0
  ldi%I1\\t%3,%0"
 [(set_attr "type" "binary")])

(define_insn "*ldi_conditional_noov"
  [(set (match_operand:QI 0 "reg_operand" "=r,r")
        (if_then_else:QI (match_operator 1 "comparison_operator"
                          [(reg:CC_NOOV 21) (const_int 0)])
                         (match_operand:QI 2 "src_operand" "rIm,0")
                         (match_operand:QI 3 "src_operand" "0,rIm")))]
 "GET_CODE (operands[1]) != LE
  && GET_CODE (operands[1]) != GE
  && GET_CODE (operands[1]) != LT
  && GET_CODE (operands[1]) != GT"
 "@
  ldi%1\\t%2,%0
  ldi%I1\\t%3,%0"
 [(set_attr "type" "binary")])

(define_insn "*ldi_on_overflow"
  [(set (match_operand:QI 0 "reg_operand" "=r")
	(unspec:QI [(match_operand:QI 1 "src_operand" "rIm")] 13))
   (use (reg:CC 21))]
  ""
  "ldiv\\t%1,%0"
  [(set_attr "type" "unary")])

; Move operand 2 to operand 0 if condition (operand 1) is true
; else move operand 3 to operand 0.
; The temporary register is required below because some of the operands
; might be identical (namely 0 and 2). 
;
(define_expand "movqicc"
  [(set (match_operand:QI 0 "reg_operand" "")
        (if_then_else:QI (match_operand 1 "comparison_operator" "")
                         (match_operand:QI 2 "src_operand" "")
                         (match_operand:QI 3 "src_operand" "")))]
 ""
 "{ 
    enum rtx_code code = GET_CODE (operands[1]);
    rtx ccreg = c4x_gen_compare_reg (code, c4x_compare_op0, c4x_compare_op1);
    if (ccreg == NULL_RTX) FAIL;
    emit_insn (gen_rtx_SET (QImode, operands[0],
                            gen_rtx_IF_THEN_ELSE (QImode,
                                 gen_rtx (code, VOIDmode, ccreg, const0_rtx),
                                          operands[2], operands[3])));
    DONE;}")
                      
(define_insn "*ldf_conditional"
  [(set (match_operand:QF 0 "reg_operand" "=f,f")
        (if_then_else:QF (match_operator 1 "comparison_operator"
                          [(reg:CC 21) (const_int 0)])
                         (match_operand:QF 2 "src_operand" "fHm,0")
                         (match_operand:QF 3 "src_operand" "0,fHm")))]
 ""
 "@
  ldf%1\\t%2,%0
  ldf%I1\\t%3,%0"
 [(set_attr "type" "binary")])

(define_insn "*ldf_conditional_noov"
  [(set (match_operand:QF 0 "reg_operand" "=f,f")
        (if_then_else:QF (match_operator 1 "comparison_operator"
                          [(reg:CC_NOOV 21) (const_int 0)])
                         (match_operand:QF 2 "src_operand" "fHm,0")
                         (match_operand:QF 3 "src_operand" "0,fHm")))]
 "GET_CODE (operands[1]) != LE
  && GET_CODE (operands[1]) != GE
  && GET_CODE (operands[1]) != LT
  && GET_CODE (operands[1]) != GT"
 "@
  ldf%1\\t%2,%0
  ldf%I1\\t%3,%0"
 [(set_attr "type" "binary")])

(define_expand "movqfcc"
  [(set (match_operand:QF 0 "reg_operand" "")
        (if_then_else:QF (match_operand 1 "comparison_operator" "")
                         (match_operand:QF 2 "src_operand" "")
                         (match_operand:QF 3 "src_operand" "")))]
 ""
 "{ 
    enum rtx_code code = GET_CODE (operands[1]);
    rtx ccreg = c4x_gen_compare_reg (code, c4x_compare_op0, c4x_compare_op1);
    if (ccreg == NULL_RTX) FAIL;
    emit_insn (gen_rtx_SET (QFmode, operands[0],
                            gen_rtx_IF_THEN_ELSE (QFmode,
                                 gen_rtx (code, VOIDmode, ccreg, const0_rtx),
                                          operands[2], operands[3])));
    DONE;}")

(define_insn "*ldhf_conditional"
  [(set (match_operand:HF 0 "reg_operand" "=h,h")
        (if_then_else:HF (match_operator 1 "comparison_operator"
                          [(reg:CC 21) (const_int 0)])
                         (match_operand:HF 2 "src_operand" "hH,0")
                         (match_operand:HF 3 "src_operand" "0,hH")))]
 ""
 "@
  ldf%1\\t%2,%0
  ldf%I1\\t%3,%0"
 [(set_attr "type" "binary")])

(define_insn "*ldhf_conditional_noov"
  [(set (match_operand:HF 0 "reg_operand" "=h,h")
        (if_then_else:HF (match_operator 1 "comparison_operator"
                          [(reg:CC_NOOV 21) (const_int 0)])
                         (match_operand:HF 2 "src_operand" "hH,0")
                         (match_operand:HF 3 "src_operand" "0,hH")))]
 "GET_CODE (operands[1]) != LE
  && GET_CODE (operands[1]) != GE
  && GET_CODE (operands[1]) != LT
  && GET_CODE (operands[1]) != GT"
 "@
  ldf%1\\t%2,%0
  ldf%I1\\t%3,%0"
 [(set_attr "type" "binary")])

(define_expand "movhfcc"
  [(set (match_operand:HF 0 "reg_operand" "")
        (if_then_else:HF (match_operand 1 "comparison_operator" "")
                         (match_operand:HF 2 "src_operand" "")
                         (match_operand:HF 3 "src_operand" "")))]
 ""
 "{ 
    enum rtx_code code = GET_CODE (operands[1]);
    rtx ccreg = c4x_gen_compare_reg (code, c4x_compare_op0, c4x_compare_op1);
    if (ccreg == NULL_RTX) FAIL;
    emit_insn (gen_rtx_SET (HFmode, operands[0],
                            gen_rtx_IF_THEN_ELSE (HFmode,
                                 gen_rtx (code, VOIDmode, ccreg, const0_rtx),
                                          operands[2], operands[3])));
    DONE;}")

(define_expand "seq"
 [(set (match_operand:QI 0 "reg_operand" "")
       (const_int 0))
  (set (match_dup 0)
       (if_then_else:QI (eq (match_dup 1) (const_int 0))
		        (const_int 1)
		        (match_dup 0)))]
 ""
 "operands[1] = c4x_gen_compare_reg (EQ, c4x_compare_op0, c4x_compare_op1);")

(define_expand "sne"
 [(set (match_operand:QI 0 "reg_operand" "")
       (const_int 0))
  (set (match_dup 0)
       (if_then_else:QI (ne (match_dup 1) (const_int 0))
		        (const_int 1)
		        (match_dup 0)))]
 ""
 "operands[1] = c4x_gen_compare_reg (NE, c4x_compare_op0, c4x_compare_op1);")

(define_expand "slt"
  [(set (match_operand:QI 0 "reg_operand" "")
        (const_int 0))
   (set (match_dup 0)
        (if_then_else:QI (lt (match_dup 1) (const_int 0))
	 	        (const_int 1)
		         (match_dup 0)))]
  ""
  "operands[1] = c4x_gen_compare_reg (LT, c4x_compare_op0, c4x_compare_op1);
   if (operands[1] == NULL_RTX) FAIL;")

(define_expand "sltu"
  [(set (match_operand:QI 0 "reg_operand" "")
        (const_int 0))
   (set (match_dup 0)
        (if_then_else:QI (ltu (match_dup 1) (const_int 0))
	 	        (const_int 1)
		         (match_dup 0)))]
  ""
  "operands[1] = c4x_gen_compare_reg (LTU, c4x_compare_op0, c4x_compare_op1);")

(define_expand "sgt"
  [(set (match_operand:QI 0 "reg_operand" "")
        (const_int 0))
   (set (match_dup 0)
        (if_then_else:QI (gt (match_dup 1) (const_int 0))
	 	        (const_int 1)
		         (match_dup 0)))]
  "" 
  "operands[1] = c4x_gen_compare_reg (GT, c4x_compare_op0, c4x_compare_op1);
   if (operands[1] == NULL_RTX) FAIL;")

(define_expand "sgtu"
  [(set (match_operand:QI 0 "reg_operand" "")
        (const_int 0))
   (set (match_dup 0)
        (if_then_else:QI (gtu (match_dup 1) (const_int 0))
	 	        (const_int 1)
		         (match_dup 0)))]
  ""
  "operands[1] = c4x_gen_compare_reg (GTU, c4x_compare_op0, c4x_compare_op1);")

(define_expand "sle"
  [(set (match_operand:QI 0 "reg_operand" "")
        (const_int 0))
   (set (match_dup 0)
        (if_then_else:QI (le (match_dup 1) (const_int 0))
	 	         (const_int 1)
		         (match_dup 0)))]
  ""
  "operands[1] = c4x_gen_compare_reg (LE, c4x_compare_op0, c4x_compare_op1);
   if (operands[1] == NULL_RTX) FAIL;")

(define_expand "sleu"
  [(set (match_operand:QI 0 "reg_operand" "")
        (const_int 0))
   (set (match_dup 0)
        (if_then_else:QI (leu (match_dup 1) (const_int 0))
	 	         (const_int 1)
		         (match_dup 0)))]
  ""
  "operands[1] = c4x_gen_compare_reg (LEU, c4x_compare_op0, c4x_compare_op1);")

(define_expand "sge"
  [(set (match_operand:QI 0 "reg_operand" "")
        (const_int 0))
   (set (match_dup 0)
        (if_then_else:QI (ge (match_dup 1) (const_int 0))
	 	         (const_int 1)
		         (match_dup 0)))]
  ""
  "operands[1] = c4x_gen_compare_reg (GE, c4x_compare_op0, c4x_compare_op1);
   if (operands[1] == NULL_RTX) FAIL;")

(define_expand "sgeu"
  [(set (match_operand:QI 0 "reg_operand" "")
        (const_int 0))
   (set (match_dup 0)
        (if_then_else:QI (geu (match_dup 1) (const_int 0))
	 	         (const_int 1)
		         (match_dup 0)))]
  ""
  "operands[1] = c4x_gen_compare_reg (GEU, c4x_compare_op0, c4x_compare_op1);")

(define_split
  [(set (match_operand:QI 0 "reg_operand" "")
        (match_operator:QI 1 "comparison_operator" [(reg:CC 21) (const_int 0)]))]
  "reload_completed"
  [(set (match_dup 0) (const_int 0))
   (set (match_dup 0)
        (if_then_else:QI (match_op_dup 1 [(reg:CC 21) (const_int 0)])
	 	        (const_int 1)
		         (match_dup 0)))]
  "")

(define_split
  [(set (match_operand:QI 0 "reg_operand" "")
        (match_operator:QI 1 "comparison_operator" [(reg:CC_NOOV 21) (const_int 0)]))]
  "reload_completed"
  [(set (match_dup 0) (const_int 0))
   (set (match_dup 0)
        (if_then_else:QI (match_op_dup 1 [(reg:CC_NOOV 21) (const_int 0)])
	 	         (const_int 1)
		         (match_dup 0)))]
  "")

(define_insn "*bu"
  [(set (pc)
        (unspec [(match_operand:QI 0 "reg_operand" "r")] 1))]
  ""
  "bu%#\\t%0"
  [(set_attr "type" "jump")])

(define_expand "caseqi"
  [(parallel [(set (match_dup 5)
                   (minus:QI (match_operand:QI 0 "reg_operand" "")
                             (match_operand:QI 1 "src_operand" "")))
              (clobber (reg:CC_NOOV 21))])
   (set (reg:CC 21)
        (compare:CC (match_dup 5)
                    (match_operand:QI 2 "src_operand" "")))
   (set (pc)
        (if_then_else (gtu (reg:CC 21)
                           (const_int 0))
                      (label_ref (match_operand 4 "" ""))
                      (pc)))
   (parallel [(set (match_dup 6)
                   (plus:QI (match_dup 5)
                            (label_ref:QI (match_operand 3 "" ""))))
              (clobber (reg:CC_NOOV 21))])
   (set (match_dup 7)
        (mem:QI (match_dup 6)))
   (set (pc) (match_dup 7))]
  ""
  "operands[5] = gen_reg_rtx (QImode);
   operands[6] = gen_reg_rtx (QImode);
   operands[7] = gen_reg_rtx (QImode);")
                
;
; PARALLEL FLOAT INSTRUCTIONS
;
; This patterns are under development

;
; ABSF/STF
;

(define_insn "*absqf2_movqf_clobber"
  [(set (match_operand:QF 0 "ext_low_reg_operand" "=q")
        (abs:QF (match_operand:QF 1 "par_ind_operand" "S<>")))
   (set (match_operand:QF 2 "par_ind_operand" "=S<>")
        (match_operand:QF 3 "ext_low_reg_operand" "q"))
   (clobber (reg:CC_NOOV 21))]
  "TARGET_PARALLEL && valid_parallel_operands_4 (operands, QFmode)"
  "absf\\t%1,%0\\n||\\tstf\\t%3,%2"
  [(set_attr "type" "binarycc")])

;
; ADDF/STF
;

(define_insn "*addqf3_movqf_clobber"
  [(set (match_operand:QF 0 "ext_low_reg_operand" "=q,q")
        (plus:QF (match_operand:QF 1 "parallel_operand" "%q,S<>")
                 (match_operand:QF 2 "parallel_operand" "S<>,q")))
   (set (match_operand:QF 3 "par_ind_operand" "=S<>,S<>")
        (match_operand:QF 4 "ext_low_reg_operand" "q,q"))
   (clobber (reg:CC 21))]
  "TARGET_PARALLEL && valid_parallel_operands_5 (operands, QFmode)"
  "addf3\\t%2,%1,%0\\n||\\tstf\\t%4,%3"
  [(set_attr "type" "binarycc,binarycc")])

;
; FLOAT/STF
;

(define_insn "*floatqiqf2_movqf_clobber"
  [(set (match_operand:QF 0 "ext_low_reg_operand" "=q")
        (float:QF (match_operand:QI 1 "par_ind_operand" "S<>")))
   (set (match_operand:QF 2 "par_ind_operand" "=S<>")
        (match_operand:QF 3 "ext_low_reg_operand" "q"))
   (clobber (reg:CC 21))]
  "TARGET_PARALLEL && valid_parallel_operands_4 (operands, QFmode)"
  "float\\t%1,%0\\n||\\tstf\\t%3,%2"
  [(set_attr "type" "binarycc")])

;
; MPYF/ADDF
;

(define_insn "*mulqf3_addqf3_clobber"
  [(set (match_operand:QF 0 "r0r1_reg_operand" "=t,t,t,t")
        (mult:QF (match_operand:QF 1 "parallel_operand" "%S<>!V,q,S<>!V,q")
                 (match_operand:QF 2 "parallel_operand" "q,S<>!V,S<>!V,q")))
   (set (match_operand:QF 3 "r2r3_reg_operand" "=u,u,u,u")
        (plus:QF (match_operand:QF 4 "parallel_operand" "%S<>!V,q,q,S<>!V")
                 (match_operand:QF 5 "parallel_operand" "q,S<>!V,q,S<>!V")))
   (clobber (reg:CC_NOOV 21))]
  "TARGET_PARALLEL_MPY && valid_parallel_operands_6 (operands, QFmode)"
  "mpyf3\\t%2,%1,%0\\n||\\taddf3\\t%5,%4,%3"
  [(set_attr "type" "binarycc,binarycc,binarycc,binarycc")])


;
; MPYF/STF
;

(define_insn "*mulqf3_movqf_clobber"
  [(set (match_operand:QF 0 "ext_low_reg_operand" "=q,q")
        (mult:QF (match_operand:QF 1 "parallel_operand" "%q,S<>")
                 (match_operand:QF 2 "parallel_operand" "S<>,q")))
   (set (match_operand:QF 3 "par_ind_operand" "=S<>,S<>")
        (match_operand:QF 4 "ext_low_reg_operand" "q,q"))
   (clobber (reg:CC 21))]
  "TARGET_PARALLEL && valid_parallel_operands_5 (operands, QFmode)"
  "mpyf3\\t%2,%1,%0\\n||\\tstf\\t%4,%3"
  [(set_attr "type" "binarycc,binarycc")])

;
; MPYF/SUBF
;

(define_insn "*mulqf3_subqf3_clobber"
  [(set (match_operand:QF 0 "r0r1_reg_operand" "=t,t")
        (mult:QF (match_operand:QF 1 "parallel_operand" "S<>,q")
                 (match_operand:QF 2 "parallel_operand" "q,S<>")))
   (set (match_operand:QF 3 "r2r3_reg_operand" "=u,u")
        (minus:QF (match_operand:QF 4 "parallel_operand" "S<>,q")
                  (match_operand:QF 5 "parallel_operand" "q,S<>")))
   (clobber (reg:CC 21))]
  "TARGET_PARALLEL_MPY && valid_parallel_operands_6 (operands, QFmode)"
  "mpyf3\\t%2,%1,%0\\n||\\tsubf3\\t%5,%4,%3"
  [(set_attr "type" "binarycc,binarycc")])

;
; MPYF/LDF 0
;

(define_insn "*mulqf3_clrqf_clobber"
  [(set (match_operand:QF 0 "r0r1_reg_operand" "=t")
        (mult:QF (match_operand:QF 1 "par_ind_operand" "%S<>")
                 (match_operand:QF 2 "par_ind_operand" "S<>")))
   (set (match_operand:QF 3 "r2r3_reg_operand" "=u")
        (match_operand:QF 4 "fp_zero_operand" "G"))
   (clobber (reg:CC 21))]
  "TARGET_PARALLEL_MPY"
  "mpyf3\\t%2,%1,%0\\n||\\tsubf3\\t%3,%3,%3"
  [(set_attr "type" "binarycc")])

;
; NEGF/STF
;

(define_insn "*negqf2_movqf_clobber"
  [(set (match_operand:QF 0 "ext_low_reg_operand" "=q")
        (neg:QF (match_operand:QF 1 "par_ind_operand" "S<>")))
   (set (match_operand:QF 2 "par_ind_operand" "=S<>")
        (match_operand:QF 3 "ext_low_reg_operand" "q"))
   (clobber (reg:CC 21))]
  "TARGET_PARALLEL && valid_parallel_operands_4 (operands, QFmode)"
  "negf\\t%1,%0\\n||\\tstf\\t%3,%2"
  [(set_attr "type" "binarycc")])

;
; SUBF/STF
;

(define_insn "*subqf3_movqf_clobber"
  [(set (match_operand:QF 0 "ext_low_reg_operand" "=q")
        (minus:QF (match_operand:QF 1 "ext_low_reg_operand" "q")
                  (match_operand:QF 2 "par_ind_operand" "S<>")))
   (set (match_operand:QF 3 "par_ind_operand" "=S<>")
        (match_operand:QF 4 "ext_low_reg_operand" "q"))
   (clobber (reg:CC 21))]
  "TARGET_PARALLEL && valid_parallel_operands_5 (operands, QFmode)"
  "subf3\\t%2,%1,%0\\n||\\tstf\\t%4,%3"
  [(set_attr "type" "binarycc")])

;
; TOIEEE/STF
;

(define_insn "*toieee_movqf_clobber"
  [(set (match_operand:QF 0 "ext_low_reg_operand" "=q")
	(unspec:QF [(match_operand:QF 1 "par_ind_operand" "S<>")] 23))
   (set (match_operand:QF 2 "par_ind_operand" "=S<>")
        (match_operand:QF 3 "ext_low_reg_operand" "q"))
   (clobber (reg:CC 21))]
  "TARGET_PARALLEL && valid_parallel_operands_4 (operands, QFmode)"
  "toieee\\t%1,%0\\n||\\tstf\\t%3,%2"
  [(set_attr "type" "binarycc")])

;
; FRIEEE/STF
;

(define_insn "*frieee_movqf_clobber"
  [(set (match_operand:QF 0 "ext_low_reg_operand" "=q")
	(unspec:QF [(match_operand:QF 1 "par_ind_operand" "S<>")] 24))
   (set (match_operand:QF 2 "par_ind_operand" "=S<>")
        (match_operand:QF 3 "ext_low_reg_operand" "q"))
   (clobber (reg:CC 21))]
  "TARGET_PARALLEL && valid_parallel_operands_4 (operands, QFmode)"
  "frieee\\t%1,%0\\n||\\tstf\\t%3,%2"
  [(set_attr "type" "binarycc")])

;
; PARALLEL INTEGER INSTRUCTIONS
;

;
; ABSI/STI
;

(define_insn "*absqi2_movqi_clobber"
  [(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
        (abs:QI (match_operand:QI 1 "par_ind_operand" "S<>")))
   (set (match_operand:QI 2 "par_ind_operand" "=S<>")
        (match_operand:QI 3 "ext_low_reg_operand" "q"))
   (clobber (reg:CC_NOOV 21))]
  "TARGET_PARALLEL && valid_parallel_operands_4 (operands, QImode)"
  "absi\\t%1,%0\\n||\\tsti\\t%3,%2"
  [(set_attr "type" "binarycc")])

;
; ADDI/STI
;

(define_insn "*addqi3_movqi_clobber"
  [(set (match_operand:QI 0 "ext_low_reg_operand" "=q,q")
        (plus:QI (match_operand:QI 1 "parallel_operand" "%q,S<>")
                 (match_operand:QI 2 "parallel_operand" "S<>,q")))
   (set (match_operand:QI 3 "par_ind_operand" "=S<>,S<>")
        (match_operand:QI 4 "ext_low_reg_operand" "q,q"))
   (clobber (reg:CC 21))]
  "TARGET_PARALLEL && valid_parallel_operands_5 (operands, QImode)"
  "addi3\\t%2,%1,%0\\n||\\tsti\\t%4,%3"
  [(set_attr "type" "binarycc,binarycc")])

;
; AND/STI
;

(define_insn "*andqi3_movqi_clobber"
  [(set (match_operand:QI 0 "ext_low_reg_operand" "=q,q")
        (and:QI (match_operand:QI 1 "parallel_operand" "%q,S<>")
                (match_operand:QI 2 "parallel_operand" "S<>,q")))
   (set (match_operand:QI 3 "par_ind_operand" "=S<>,S<>")
        (match_operand:QI 4 "ext_low_reg_operand" "q,q"))
   (clobber (reg:CC 21))]
  "TARGET_PARALLEL && valid_parallel_operands_5 (operands, QImode)"
  "and3\\t%2,%1,%0\\n||\\tsti\\t%4,%3"
  [(set_attr "type" "binarycc,binarycc")])

;
; ASH(left)/STI 
;

(define_insn "*ashlqi3_movqi_clobber"
  [(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
        (ashift:QI (match_operand:QI 1 "par_ind_operand" "S<>")
                   (match_operand:QI 2 "ext_low_reg_operand" "q")))
   (set (match_operand:QI 3 "par_ind_operand" "=S<>")
        (match_operand:QI 4 "ext_low_reg_operand" "q"))
   (clobber (reg:CC 21))]
  "TARGET_PARALLEL && valid_parallel_operands_5 (operands, QImode)"
  "ash3\\t%2,%1,%0\\n||\\tsti\\t%4,%3"
  [(set_attr "type" "binarycc")])

;
; ASH(right)/STI 
;

(define_insn "*ashrqi3_movqi_clobber"
  [(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
        (ashiftrt:QI (match_operand:QI 1 "par_ind_operand" "S<>")
                     (neg:QI (match_operand:QI 2 "ext_low_reg_operand" "q"))))
   (set (match_operand:QI 3 "par_ind_operand" "=S<>")
        (match_operand:QI 4 "ext_low_reg_operand" "q"))
   (clobber (reg:CC 21))]
  "TARGET_PARALLEL && valid_parallel_operands_5 (operands, QImode)"
  "ash3\\t%2,%1,%0\\n||\\tsti\\t%4,%3"
  [(set_attr "type" "binarycc")])

;
; FIX/STI
;

(define_insn "*fixqfqi2_movqi_clobber"
  [(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
        (fix:QI (match_operand:QF 1 "par_ind_operand" "S<>")))
   (set (match_operand:QI 2 "par_ind_operand" "=S<>")
        (match_operand:QI 3 "ext_low_reg_operand" "q"))
   (clobber (reg:CC 21))]
  "TARGET_PARALLEL && valid_parallel_operands_4 (operands, QImode)"
  "fix\\t%1,%0\\n||\\tsti\\t%3,%2"
  [(set_attr "type" "binarycc")])

;
; LSH(right)/STI 
;

(define_insn "*lshrqi3_movqi_clobber"
  [(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
        (lshiftrt:QI (match_operand:QI 1 "par_ind_operand" "S<>")
                     (neg:QI (match_operand:QI 2 "ext_low_reg_operand" "q"))))
   (set (match_operand:QI 3 "par_ind_operand" "=S<>")
        (match_operand:QI 4 "ext_low_reg_operand" "q"))
   (clobber (reg:CC 21))]
  "TARGET_PARALLEL && valid_parallel_operands_5 (operands, QImode)"
  "lsh3\\t%2,%1,%0\\n||\\tsti\\t%4,%3"
  [(set_attr "type" "binarycc")])

;
; MPYI/ADDI
;

(define_insn "*mulqi3_addqi3_clobber"
  [(set (match_operand:QI 0 "r0r1_reg_operand" "=t,t,t,t")
        (mult:QI (match_operand:QI 1 "parallel_operand" "%S<>!V,q,S<>!V,q")
                 (match_operand:QI 2 "parallel_operand" "q,S<>!V,S<>!V,q")))
   (set (match_operand:QI 3 "r2r3_reg_operand" "=u,u,u,u")
        (plus:QI (match_operand:QI 4 "parallel_operand" "%S<>!V,q,q,S<>!V")
                 (match_operand:QI 5 "parallel_operand" "q,S<>!V,q,S<>!V")))
   (clobber (reg:CC 21))]
  "TARGET_PARALLEL_MPY && TARGET_MPYI 
   && valid_parallel_operands_6 (operands, QImode)"
  "mpyi3\\t%2,%1,%0\\n||\\taddi3\\t%5,%4,%3"
  [(set_attr "type" "binarycc,binarycc,binarycc,binarycc")])


;
; MPYI/STI
;

(define_insn "*mulqi3_movqi_clobber"
  [(set (match_operand:QI 0 "ext_low_reg_operand" "=q,q")
        (mult:QI (match_operand:QI 1 "parallel_operand" "%q,S<>")
                 (match_operand:QI 2 "parallel_operand" "S<>,q")))
   (set (match_operand:QI 3 "par_ind_operand" "=S<>,S<>")
        (match_operand:QI 4 "ext_low_reg_operand" "q,q"))
   (clobber (reg:CC 21))]
  "TARGET_PARALLEL && TARGET_MPYI
   && valid_parallel_operands_5 (operands, QImode)"
  "mpyi3\\t%2,%1,%0\\n||\\tsti\\t%4,%3"
  [(set_attr "type" "binarycc,binarycc")])

;
; MPYI/SUBI
;

(define_insn "*mulqi3_subqi3_clobber"
  [(set (match_operand:QI 0 "r0r1_reg_operand" "=t,t")
        (mult:QI (match_operand:QI 1 "parallel_operand" "S<>,q")
                 (match_operand:QI 2 "parallel_operand" "q,S<>")))
   (set (match_operand:QI 3 "r2r3_reg_operand" "=u,u")
        (minus:QI (match_operand:QI 4 "parallel_operand" "S<>,q")
                  (match_operand:QI 5 "parallel_operand" "q,S<>")))
   (clobber (reg:CC 21))]
  "TARGET_PARALLEL_MPY && TARGET_MPYI
   && valid_parallel_operands_6 (operands, QImode)"
  "mpyi3\\t%2,%1,%0\\n||\\tsubi3\\t%5,%4,%3"
  [(set_attr "type" "binarycc,binarycc")])

;
; MPYI/LDI 0
;

(define_insn "*mulqi3_clrqi_clobber"
  [(set (match_operand:QI 0 "r0r1_reg_operand" "=t")
        (mult:QI (match_operand:QI 1 "par_ind_operand" "%S<>")
                 (match_operand:QI 2 "par_ind_operand" "S<>")))
   (set (match_operand:QI 3 "r2r3_reg_operand" "=u")
	(const_int 0))
   (clobber (reg:CC 21))]
  "TARGET_PARALLEL_MPY && TARGET_MPYI"
  "mpyi3\\t%2,%1,%0\\n||\\tsubi3\\t%3,%3,%3"
  [(set_attr "type" "binarycc")])

;
; NEGI/STI
;

(define_insn "*negqi2_movqi_clobber"
  [(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
        (neg:QI (match_operand:QI 1 "par_ind_operand" "S<>")))
   (set (match_operand:QI 2 "par_ind_operand" "=S<>")
        (match_operand:QI 3 "ext_low_reg_operand" "q"))
   (clobber (reg:CC 21))]
  "TARGET_PARALLEL && valid_parallel_operands_4 (operands, QImode)"
  "negi\\t%1,%0\\n||\\tsti\\t%3,%2"
  [(set_attr "type" "binarycc")])

;
; NOT/STI
;

(define_insn "*notqi2_movqi_clobber"
  [(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
        (not:QI (match_operand:QI 1 "par_ind_operand" "S<>")))
   (set (match_operand:QI 2 "par_ind_operand" "=S<>")
        (match_operand:QI 3 "ext_low_reg_operand" "q"))
   (clobber (reg:CC 21))]
  "TARGET_PARALLEL && valid_parallel_operands_4 (operands, QImode)"
  "not\\t%1,%0\\n||\\tsti\\t%3,%2"
  [(set_attr "type" "binarycc")])

;
; OR/STI
;

(define_insn "*iorqi3_movqi_clobber"
  [(set (match_operand:QI 0 "ext_low_reg_operand" "=q,q")
        (ior:QI (match_operand:QI 1 "parallel_operand" "%q,S<>")
                (match_operand:QI 2 "parallel_operand" "S<>,q")))
   (set (match_operand:QI 3 "par_ind_operand" "=S<>,S<>")
        (match_operand:QI 4 "ext_low_reg_operand" "q,q"))
   (clobber (reg:CC 21))]
  "TARGET_PARALLEL && valid_parallel_operands_5 (operands, QImode)"
  "or3\\t%2,%1,%0\\n||\\tsti\\t%4,%3"
  [(set_attr "type" "binarycc,binarycc")])

;
; SUBI/STI
;

(define_insn "*subqi3_movqi_clobber"
  [(set (match_operand:QI 0 "ext_low_reg_operand" "=q")
        (minus:QI (match_operand:QI 1 "par_ind_operand" "S<>")
                  (match_operand:QI 2 "ext_low_reg_operand" "q")))
   (set (match_operand:QI 3 "par_ind_operand" "=S<>")
        (match_operand:QI 4 "ext_low_reg_operand" "q"))
   (clobber (reg:CC 21))]
  "TARGET_PARALLEL && valid_parallel_operands_5 (operands, QImode)"
  "subi3\\t%2,%1,%0\\n||\\tsti\\t%4,%3"
  [(set_attr "type" "binarycc")])

;
; XOR/STI
;

(define_insn "*xorqi3_movqi_clobber"
  [(set (match_operand:QI 0 "ext_low_reg_operand" "=q,q")
        (xor:QI (match_operand:QI 1 "parallel_operand" "%q,S<>")
                (match_operand:QI 2 "parallel_operand" "S<>,q")))
   (set (match_operand:QI 3 "par_ind_operand" "=S<>,S<>")
        (match_operand:QI 4 "ext_low_reg_operand" "q,q"))
   (clobber (reg:CC 21))]
  "TARGET_PARALLEL && valid_parallel_operands_5 (operands, QImode)"
  "xor3\\t%2,%1,%0\\n||\\tsti\\t%4,%3"
  [(set_attr "type" "binarycc,binarycc")])

;
; BRANCH/CALL INSTRUCTIONS
;

;
; Branch instructions
;
(define_insn "*b"
  [(set (pc) (if_then_else (match_operator 0 "comparison_operator"
			   [(reg:CC 21) (const_int 0)])
                           (label_ref (match_operand 1 "" ""))
                           (pc)))]
  ""
  "*
   return c4x_output_cbranch (\"b%0\", insn);"
  [(set_attr "type" "jmpc")])

(define_insn "*b_rev"
  [(set (pc) (if_then_else (match_operator 0 "comparison_operator"
			   [(reg:CC 21) (const_int 0)])
                           (pc)
                           (label_ref (match_operand 1 "" ""))))]
  ""
  "*
   return c4x_output_cbranch (\"b%I0\", insn);"
  [(set_attr "type" "jmpc")])

(define_insn "*b_noov"
  [(set (pc) (if_then_else (match_operator 0 "comparison_operator"
			   [(reg:CC_NOOV 21) (const_int 0)])
                           (label_ref (match_operand 1 "" ""))
                           (pc)))]
 "GET_CODE (operands[0]) != LE
  && GET_CODE (operands[0]) != GE
  && GET_CODE (operands[0]) != LT
  && GET_CODE (operands[0]) != GT"
  "*
   return c4x_output_cbranch (\"b%0\", insn);"
  [(set_attr "type" "jmpc")])

(define_insn "*b_noov_rev"
  [(set (pc) (if_then_else (match_operator 0 "comparison_operator"
			   [(reg:CC_NOOV 21) (const_int 0)])
                           (pc)
                           (label_ref (match_operand 1 "" ""))))]
 "GET_CODE (operands[0]) != LE
  && GET_CODE (operands[0]) != GE
  && GET_CODE (operands[0]) != LT
  && GET_CODE (operands[0]) != GT"
  "*
   return c4x_output_cbranch (\"b%I0\", insn);"
  [(set_attr "type" "jmpc")])

(define_expand "beq"
  [(set (pc) (if_then_else (eq (match_dup 1) (const_int 0))
                           (label_ref (match_operand 0 "" ""))
                           (pc)))]
  ""
  "operands[1] = c4x_gen_compare_reg (EQ, c4x_compare_op0, c4x_compare_op1);")

(define_expand "bne"
  [(set (pc) (if_then_else (ne (match_dup 1) (const_int 0))
                           (label_ref (match_operand 0 "" ""))
                           (pc)))]
  ""
  "operands[1] = c4x_gen_compare_reg (NE, c4x_compare_op0, c4x_compare_op1);")

(define_expand "blt"
  [(set (pc) (if_then_else (lt (match_dup 1) (const_int 0))
                           (label_ref (match_operand 0 "" ""))
                           (pc)))]
  ""
  "operands[1] = c4x_gen_compare_reg (LT, c4x_compare_op0, c4x_compare_op1);
   if (operands[1] == NULL_RTX) FAIL;")

(define_expand "bltu"
  [(set (pc) (if_then_else (ltu (match_dup 1) (const_int 0))
                           (label_ref (match_operand 0 "" ""))
                           (pc)))]
  ""
  "operands[1] = c4x_gen_compare_reg (LTU, c4x_compare_op0, c4x_compare_op1);")

(define_expand "bgt"
  [(set (pc) (if_then_else (gt (match_dup 1) (const_int 0))
                           (label_ref (match_operand 0 "" ""))
                           (pc)))]
  ""
  "operands[1] = c4x_gen_compare_reg (GT, c4x_compare_op0, c4x_compare_op1);
   if (operands[1] == NULL_RTX) FAIL;")

(define_expand "bgtu"
  [(set (pc) (if_then_else (gtu (match_dup 1) (const_int 0))
                           (label_ref (match_operand 0 "" ""))
                           (pc)))]
  ""
  "operands[1] = c4x_gen_compare_reg (GTU, c4x_compare_op0, c4x_compare_op1);")

(define_expand "ble"
  [(set (pc) (if_then_else (le (match_dup 1) (const_int 0))
                           (label_ref (match_operand 0 "" ""))
                           (pc)))]
  ""
  "operands[1] = c4x_gen_compare_reg (LE, c4x_compare_op0, c4x_compare_op1);
   if (operands[1] == NULL_RTX) FAIL;")

(define_expand "bleu"
  [(set (pc) (if_then_else (leu (match_dup 1) (const_int 0))
                           (label_ref (match_operand 0 "" ""))
                           (pc)))]
  ""
  "operands[1] = c4x_gen_compare_reg (LEU, c4x_compare_op0, c4x_compare_op1);")

(define_expand "bge"
  [(set (pc) (if_then_else (ge (match_dup 1) (const_int 0))
                           (label_ref (match_operand 0 "" ""))
                           (pc)))]
  ""
  "operands[1] = c4x_gen_compare_reg (GE, c4x_compare_op0, c4x_compare_op1);
   if (operands[1] == NULL_RTX) FAIL;")

(define_expand "bgeu"
  [(set (pc) (if_then_else (geu (match_dup 1) (const_int 0))
                           (label_ref (match_operand 0 "" ""))
                           (pc)))]
  ""
  "operands[1] = c4x_gen_compare_reg (GEU, c4x_compare_op0, c4x_compare_op1);")

(define_insn "*b_reg"
 [(set (pc) (match_operand:QI 0 "reg_operand" "r"))]
 ""
 "bu%#\\t%0"
  [(set_attr "type" "jump")])

(define_expand "indirect_jump"
 [(set (pc) (match_operand:QI 0 "reg_operand" ""))]
 ""
 "")

(define_insn "tablejump"
  [(set (pc) (match_operand:QI 0 "src_operand" "r"))
   (use (label_ref (match_operand 1 "" "")))]
  ""
  "bu%#\\t%0"
  [(set_attr "type" "jump")])

;
; CALL
;
(define_insn "*call_c3x"
 [(call (mem:QI (match_operand:QI 0 "call_address_operand" "Ur"))
        (match_operand:QI 1 "general_operand" ""))
  (clobber (reg:QI 31))]
  ;; Operand 1 not really used on the C4x.  The C30 doesn't have reg 31.

  "TARGET_C3X"
  "call%U0\\t%C0"
  [(set_attr "type" "call")])

; LAJ requires R11 (31) for the return address
(define_insn "*laj"
 [(call (mem:QI (match_operand:QI 0 "call_address_operand" "Ur"))
        (match_operand:QI 1 "general_operand" ""))
  (clobber (reg:QI 31))]
  ;; Operand 1 not really used on the C4x.

  "! TARGET_C3X"
  "*
   if (final_sequence)
     return c4x_check_laj_p (insn)
	 ? \"nop\\n\\tlaj%U0\\t%C0\" : \"laj%U0\\t%C0\";
   else
     return \"call%U0\\t%C0\";"
  [(set_attr "type" "laj")])

(define_expand "call"
 [(parallel [(call (match_operand:QI 0 "" "")
                   (match_operand:QI 1 "general_operand" ""))
             (clobber (reg:QI 31))])]
 ""
 "
{
  if (GET_CODE (operands[0]) == MEM
      && ! call_address_operand (XEXP (operands[0], 0), Pmode))
    operands[0] = gen_rtx_MEM (GET_MODE (operands[0]),
			       force_reg (Pmode, XEXP (operands[0], 0)));
}")

(define_insn "nodb_call"
 [(call (mem:QI (match_operand:QI 0 "call_address_operand" "Ur"))
	(const_int 0))]
  ""
  "call%U0\\t%C0"
  [(set_attr "type" "call")])

(define_insn "*callv_c3x"
 [(set (match_operand 0 "" "=r")
       (call (mem:QI (match_operand:QI 1 "call_address_operand" "Ur"))
             (match_operand:QI 2 "general_operand" "")))
  (clobber (reg:QI 31))]
  ;; Operand 0 and 2 not really used for the C4x. 
  ;; The C30 doesn't have reg 31.

  "TARGET_C3X"
  "call%U1\\t%C1"
  [(set_attr "type" "call")])

; LAJ requires R11 (31) for the return address
(define_insn "*lajv"
 [(set (match_operand 0 "" "=r")
       (call (mem:QI (match_operand:QI 1 "call_address_operand" "Ur"))
             (match_operand:QI 2 "general_operand" "")))
  (clobber (reg:QI 31))]
  ;; Operand 0 and 2 not really used in the C30 instruction.

  "! TARGET_C3X"
  "*
   if (final_sequence)
     return c4x_check_laj_p (insn)
	 ? \"nop\\n\\tlaj%U1\\t%C1\" : \"laj%U1\\t%C1\";
   else
     return \"call%U1\\t%C1\";"
  [(set_attr "type" "laj")])

(define_expand "call_value"
 [(parallel [(set (match_operand 0 "" "")
                  (call (match_operand:QI 1 "" "")
                        (match_operand:QI 2 "general_operand" "")))
             (clobber (reg:QI 31))])]
 ""
 "
{
  if (GET_CODE (operands[0]) == MEM
      && ! call_address_operand (XEXP (operands[1], 0), Pmode))
    operands[0] = gen_rtx_MEM (GET_MODE (operands[1]),
                               force_reg (Pmode, XEXP (operands[1], 0)));
}")

(define_insn "return"
  [(return)]
  "c4x_null_epilogue_p ()"
  "rets"
  [(set_attr "type" "rets")])

(define_insn "return_from_epilogue"
  [(return)]
  "reload_completed && ! c4x_interrupt_function_p ()"
  "rets"
  [(set_attr "type" "rets")])

(define_insn "return_from_interrupt_epilogue"
  [(return)]
  "reload_completed && c4x_interrupt_function_p ()"
  "reti"
  [(set_attr "type" "rets")])

(define_insn "*return_cc"
  [(set (pc)
        (if_then_else (match_operator 0 "comparison_operator"
                      [(reg:CC 21) (const_int 0)])
                      (return)
                       (pc)))]
  "c4x_null_epilogue_p ()"
  "rets%0"
  [(set_attr "type" "rets")])

(define_insn "*return_cc_noov"
  [(set (pc)
        (if_then_else (match_operator 0 "comparison_operator"
                      [(reg:CC_NOOV 21) (const_int 0)])
                      (return)
                       (pc)))]
  "GET_CODE (operands[0]) != LE
   && GET_CODE (operands[0]) != GE
   && GET_CODE (operands[0]) != LT
   && GET_CODE (operands[0]) != GT
   && c4x_null_epilogue_p ()"
  "rets%0"
  [(set_attr "type" "rets")])

(define_insn "*return_cc_inverse"
  [(set (pc)
        (if_then_else (match_operator 0 "comparison_operator"
                      [(reg:CC 21) (const_int 0)])
                       (pc)
                      (return)))]
  "c4x_null_epilogue_p ()"
  "rets%I0"
  [(set_attr "type" "rets")])

(define_insn "*return_cc_noov_inverse"
  [(set (pc)
        (if_then_else (match_operator 0 "comparison_operator"
                      [(reg:CC_NOOV 21) (const_int 0)])
                       (pc)
                      (return)))]
  "GET_CODE (operands[0]) != LE
   && GET_CODE (operands[0]) != GE
   && GET_CODE (operands[0]) != LT
   && GET_CODE (operands[0]) != GT
   && c4x_null_epilogue_p ()"
  "rets%I0"
  [(set_attr "type" "rets")])

(define_insn "jump"
  [(set (pc) (label_ref (match_operand 0 "" "")))]
  ""
  "br%#\\t%l0"
  [(set_attr "type" "jump")])

(define_insn "trap"
  [(trap_if (const_int 1) (const_int 31))]
  ""
  "trapu\\t31"
  [(set_attr "type" "call")])

(define_expand "conditional_trap"
 [(trap_if (match_operand 0 "comparison_operator" "")
	   (match_operand 1 "const_int_operand" ""))]
 ""
 "{
    enum rtx_code code = GET_CODE (operands[1]);
    rtx ccreg = c4x_gen_compare_reg (code, c4x_compare_op0, c4x_compare_op1);
    if (ccreg == NULL_RTX) FAIL;
    if (GET_MODE (ccreg) == CCmode)
      emit_insn (gen_cond_trap_cc (operands[0], operands[1]));
    else 
      emit_insn (gen_cond_trap_cc_noov (operands[0], operands[1]));
    DONE;}")

(define_insn "cond_trap_cc"
  [(trap_if (match_operator 0 "comparison_operator"
            [(reg:CC 21) (const_int 0)])
	    (match_operand 1 "const_int_operand" ""))]
  ""
  "trap%0\\t31"
  [(set_attr "type" "call")])

(define_insn "cond_trap_cc_noov"
  [(trap_if (match_operator 0 "comparison_operator"
            [(reg:CC_NOOV 21) (const_int 0)])
	    (match_operand 1 "const_int_operand" ""))]
  "GET_CODE (operands[0]) != LE
   && GET_CODE (operands[0]) != GE
   && GET_CODE (operands[0]) != LT
   && GET_CODE (operands[0]) != GT"
  "trap%0\\t31"
  [(set_attr "type" "call")])

;
; DBcond
;
; Note we have to emit a dbu instruction if there are no delay slots
; to fill.
; Also note that GCC will try to reverse a loop to see if it can
; utilise this instruction.  However, if there are more than one
; memory reference in the loop, it cannot guarantee that reversing
; the loop will work :(  (see check_dbra_loop() in loop.c)
; Note that the C3x only decrements the 24 LSBs of the address register
; and the 8 MSBs are untouched.  The C4x uses all 32-bits.  We thus
; have an option to disable this instruction.
(define_insn "*db"
  [(set (pc)
        (if_then_else (ne (match_operand:QI 0 "addr_reg_operand" "+a,?*d,??*r,!m")
                          (const_int 0))
                      (label_ref (match_operand 1 "" ""))
                      (pc)))
   (set (match_dup 0)
        (plus:QI (match_dup 0)
                 (const_int -1)))
   (use (reg:QI 20))
   (clobber (reg:CC_NOOV 21))]
  "TARGET_DB && TARGET_LOOP_UNSIGNED"
  "*
  if (which_alternative == 0)
    return \"dbu%#\\t%0,%l1\";
  else if (which_alternative == 1)
    return c4x_output_cbranch (\"subi\\t1,%0\\n\\tbge\", insn);
  else if (which_alternative == 2)
    return c4x_output_cbranch (\"subi\\t1,%0\\n\\tcmpi\\t0,%0\\n\\tbge\", insn);
  else
    return c4x_output_cbranch (\"push\\tr0\\n\\tldi\\t%0,r0\\n\\tsubi\\t1,r0\\n\\tsti\\tr0,%0\\n\\tpop\\tr0\\n\\tbhs\", insn);
  "
  [(set_attr "type" "db,jmpc,jmpc,jmpc")])

(define_insn "*db_noclobber"
  [(set (pc)
        (if_then_else (ne (match_operand:QI 0 "addr_reg_operand" "+a")
                          (const_int 0))
                      (label_ref (match_operand 1 "" ""))
                      (pc)))
   (set (match_dup 0)
        (plus:QI (match_dup 0)
                 (const_int -1)))]
  "reload_completed && TARGET_DB && TARGET_LOOP_UNSIGNED"
  "dbu%#\\t%0,%l1"
  [(set_attr "type" "db")])

(define_split
  [(set (pc)
        (if_then_else (ne (match_operand:QI 0 "addr_reg_operand" "")
			  (const_int 0))
		      (label_ref (match_operand 1 "" ""))
		      (pc)))
   (set (match_dup 0)
        (plus:QI (match_dup 0)
                 (const_int -1)))
   (use (reg:QI 20))
   (clobber (reg:CC_NOOV 21))]
  "reload_completed && TARGET_DB && TARGET_LOOP_UNSIGNED"
  [(parallel [(set (pc)
                   (if_then_else (ne (match_dup 0)
			             (const_int 0))
		                 (label_ref (match_dup 1))
		                 (pc)))
              (set (match_dup 0)
                   (plus:QI (match_dup 0)
                            (const_int -1)))])]
  "")
  

; This insn is used for some loop tests, typically loops reversed when
; strength reduction is used.  It is actually created when the instruction
; combination phase combines the special loop test.  Since this insn
; is both a jump insn and has an output, it must deal with its own
; reloads, hence the `m' constraints. 

; The C4x does the decrement and then compares the result against zero.
; It branches if the result was greater than or equal to zero.
; In the RTL the comparison and decrement are assumed to happen
; at the same time so we bias the iteration counter with by -1
; when we make the test.
(define_insn "decrement_and_branch_until_zero"
  [(set (pc)
        (if_then_else (ge (plus:QI (match_operand:QI 0 "addr_reg_operand" "+a,?*d,??*r,!m")
			           (const_int -1))
			  (const_int 0))
                      (label_ref (match_operand 1 "" ""))
                      (pc)))
   (set (match_dup 0)
        (plus:QI (match_dup 0)
                 (const_int -1)))
   (use (reg:QI 20))
   (clobber (reg:CC_NOOV 21))]
  "TARGET_DB && (find_reg_note (insn, REG_NONNEG, 0) || TARGET_LOOP_UNSIGNED)"
  "*
  if (which_alternative == 0)
    return \"dbu%#\\t%0,%l1\";
  else if (which_alternative == 1)
    return c4x_output_cbranch (\"subi\\t1,%0\\n\\tbge\", insn);
  else if (which_alternative == 2)
    return c4x_output_cbranch (\"subi\\t1,%0\\n\\tcmpi\\t0,%0\\n\\tbge\", insn);
  else
    return c4x_output_cbranch (\"push\\tr0\\n\\tldi\\t%0,r0\\n\\tsubi\\t1,r0\\n\\tsti\\tr0,%0\\n\\tpop\\tr0\\n\\tbhs\", insn);
  "
  [(set_attr "type" "db,jmpc,jmpc,jmpc")])

(define_insn "*decrement_and_branch_until_zero_noclobber"
  [(set (pc)
        (if_then_else (ge (plus:QI (match_operand:QI 0 "addr_reg_operand" "+a")
			           (const_int -1))
			  (const_int 0))
                      (label_ref (match_operand 1 "" ""))
                      (pc)))
   (set (match_dup 0)
        (plus:QI (match_dup 0)
                 (const_int -1)))]
  "reload_completed && TARGET_DB && TARGET_LOOP_UNSIGNED"
  "dbu%#\\t%0,%l1"
  [(set_attr "type" "db")])

(define_split
  [(set (pc)
        (if_then_else (ge (plus:QI (match_operand:QI 0 "addr_reg_operand" "")
			           (const_int -1))
			  (const_int 0))
                      (label_ref (match_operand 1 "" ""))
                      (pc)))
   (set (match_dup 0)
        (plus:QI (match_dup 0)
                 (const_int -1)))
   (use (reg:QI 20))
   (clobber (reg:CC_NOOV 21))]
  "reload_completed && TARGET_DB && TARGET_LOOP_UNSIGNED"
  [(parallel [(set (pc)
        	   (if_then_else (ge (plus:QI (match_dup 0)
			                      (const_int -1))
			             (const_int 0))
                                 (label_ref (match_dup 1))
                                 (pc)))
              (set (match_dup 0)
                   (plus:QI (match_dup 0)
                            (const_int -1)))])]
  "")

;
; MISC INSTRUCTIONS
;

;
; NOP
;
(define_insn "nop"
  [(const_int 0)]
  ""
  "nop")
; Default to misc type attr.

(define_insn "return_indirect_internal"
  [(return)
   (use (match_operand:QI 0 "reg_operand" ""))]
  "reload_completed"                           
  "bu%#\\t%0"
  [(set_attr "type" "jump")])

(define_expand "prologue"
  [(const_int 1)]
  ""                           
  "c4x_expand_prologue (); DONE;")

(define_expand "epilogue"
  [(const_int 1)]
  ""
  "c4x_expand_epilogue (); DONE;")

;
; RPTB
;
(define_insn "rptb_top"
  [(use (label_ref (match_operand 0 "" "")))
   (use (label_ref (match_operand 1 "" "")))
   (clobber (reg:QI 25))
   (clobber (reg:QI 26))]
  ""
  "*
   return ! final_sequence && c4x_rptb_rpts_p (insn, operands[0])
	 ? \"rpts\\trc\" : \"rptb%#\\t%l1-1\";
  "
  [(set_attr "type" "repeat_top")])

(define_insn "rpts_top"
  [(unspec [(use (label_ref (match_operand 0 "" "")))
            (use (label_ref (match_operand 1 "" "")))] 2)
   (clobber (reg:QI 25))
   (clobber (reg:QI 26))]
  ""
  "*
   return ! final_sequence && c4x_rptb_rpts_p (insn, operands[0])
	 ? \"rpts\\trc\" : \"rptb%#\\t%l1-1\";
  "
  [(set_attr "type" "repeat")])

; This pattern needs to be emitted at the start of the loop to
; say that RS and RE are loaded.
(define_insn "rptb_init"
  [(unspec [(match_operand:QI 0 "register_operand" "va")] 22)
   (clobber (reg:QI 25))
   (clobber (reg:QI 26))]
  ""
  ""
  [(set_attr "type" "repeat")])


; 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
(define_expand "doloop_begin"
  [(use (match_operand 0 "register_operand" ""))
   (use (match_operand:QI 1 "const_int_operand" ""))
   (use (match_operand:QI 2 "const_int_operand" ""))
   (use (match_operand:QI 3 "const_int_operand" ""))]
  ""
  "if (INTVAL (operands[3]) > 1 || ! TARGET_RPTB)
     FAIL;
   emit_insn (gen_rptb_init (operands[0]));
   DONE;
  ")


; The RS (25) and RE (26) registers must be unviolate from the top of the loop
; to here.
(define_insn "rptb_end"
  [(set (pc)
        (if_then_else (ge (match_operand:QI 0 "register_operand" "+v,?a,!*d,!*x*k,!m")
                          (const_int 0))
                      (label_ref (match_operand 1 "" ""))
                      (pc)))
   (set (match_dup 0)
        (plus:QI (match_dup 0)
                 (const_int -1)))
   (use (reg:QI 25))
   (use (reg:QI 26))
   (use (reg:QI 20))
   (clobber (reg:CC_NOOV 21))]
  ""
  "*
   if (which_alternative == 0)
     return c4x_rptb_nop_p (insn) ? \"nop\" : \"\";
   else if (which_alternative == 1 && TARGET_DB)
     return \"dbu%#\\t%0,%l1\";
   else if (which_alternative == 2)
     return c4x_output_cbranch (\"subi\\t1,%0\\n\\tbge\", insn);
   else if (which_alternative == 3 || (which_alternative == 1 && ! TARGET_DB))
     return c4x_output_cbranch (\"subi\\t1,%0\\n\\tcmpi\\t0,%0\\n\\tbge\", insn);
   else
     return c4x_output_cbranch (\"push\\tr0\\n\\tldi\\t%0,r0\\n\\tsubi\\t1,r0\\n\\tsti\\tr0,%0\\n\\tpop\\tr0\\n\\tbhs\", insn);
  "
  [(set_attr "type" "repeat,db,jmpc,jmpc,jmpc")])

(define_split
   [(set (pc)
        (if_then_else (ge (match_operand:QI 0 "addr_reg_operand" "")
                          (const_int 0))
                      (label_ref (match_operand 1 "" ""))
                      (pc)))
   (set (match_dup 0)
        (plus:QI (match_dup 0)
                 (const_int -1)))
   (use (match_operand:QI 2 "const_int_operand" ""))
   (use (match_operand:QI 3 "const_int_operand" ""))
   (use (match_operand:QI 4 "const_int_operand" ""))
   (use (reg:QI 25))
   (use (reg:QI 26))
   (use (reg:QI 20))
   (clobber (reg:CC_NOOV 21))]
  "reload_completed"
  [(parallel [(set (pc)
                   (if_then_else (ge (match_dup 0)
			             (const_int 0))
		                 (label_ref (match_dup 1))
		                 (pc)))
              (set (match_dup 0)
                   (plus:QI (match_dup 0)
                            (const_int -1)))])]
  "")

; 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"
  [(use (match_operand 0 "register_operand" ""))
   (use (match_operand:QI 1 "const_int_operand" ""))
   (use (match_operand:QI 2 "const_int_operand" ""))
   (use (match_operand:QI 3 "const_int_operand" ""))
   (use (label_ref (match_operand 4 "" "")))]
  ""
  "if (! TARGET_LOOP_UNSIGNED 
       && (unsigned HOST_WIDE_INT) INTVAL (operands[2]) > ((unsigned) 1 << 31))
     FAIL;
   if (INTVAL (operands[3]) > 1 || ! TARGET_RPTB)
     {
        /* The C30 maximum iteration count for DB is 2^24.  */
	if (! TARGET_DB)
          FAIL;
        emit_jump_insn (gen_decrement_and_branch_until_zero (operands[0],
                                                             operands[4]));
	DONE;
     }
    emit_jump_insn (gen_rptb_end (operands[0], operands[4]));
    DONE;
  ")

; The current low overhead looping code is naff and is not failsafe
; If you want RTPB instructions to be generated, apply the patches
; from www.elec.canterbury.ac.nz/c4x.  This will utilise the
; doloop_begin and doloop_end patterns in this MD.
(define_expand "decrement_and_branch_on_count"
  [(parallel [(set (pc)
                   (if_then_else (ge (match_operand:QI 0 "register_operand" "")
                                     (const_int 0))
                                 (label_ref (match_operand 1 "" ""))
                                 (pc)))
              (set (match_dup 0)
		   (plus:QI (match_dup 0)
			    (const_int -1)))
              (use (reg:QI 25))
              (use (reg:QI 26))
              (clobber (reg:CC_NOOV 21))])]
  "0"
  "")

(define_expand "movstrqi_small2"
  [(parallel [(set (mem:BLK (match_operand:BLK 0 "src_operand" ""))
                   (mem:BLK (match_operand:BLK 1 "src_operand" "")))
              (use (match_operand:QI 2 "immediate_operand" ""))
              (use (match_operand:QI 3 "immediate_operand" ""))
              (clobber (match_operand:QI 4 "ext_low_reg_operand" ""))])]
  ""
  "
 {
    rtx src, dst, tmp;
    rtx src_mem, dst_mem;    
    int len;
    int i;

    dst = operands[0];
    src = operands[1];
    len = INTVAL (operands[2]);
    tmp = operands[4];

    src_mem = gen_rtx_MEM (QImode, src);
    dst_mem = gen_rtx_MEM (QImode, dst);

    if (TARGET_PARALLEL)
      {
        emit_insn (gen_movqi (tmp, src_mem));	
        emit_insn (gen_addqi3_noclobber (src, src, const1_rtx));	
        for (i = 1; i < len; i++)
          {
            emit_insn (gen_movqi_parallel (tmp, src_mem, dst_mem, tmp));
            emit_insn (gen_addqi3_noclobber (src, src, const1_rtx));	
            emit_insn (gen_addqi3_noclobber (dst, dst, const1_rtx));	
          }
        emit_insn (gen_movqi (dst_mem, tmp));	
        emit_insn (gen_addqi3_noclobber (dst, dst, const1_rtx));	
      }
    else
      {
        for (i = 0; i < len; i++)
          {
	    emit_insn (gen_movqi (tmp, src_mem));	
	    emit_insn (gen_movqi (dst_mem, tmp));	
            emit_insn (gen_addqi3_noclobber (src, src, const1_rtx));	
            emit_insn (gen_addqi3_noclobber (dst, dst, const1_rtx));	
          }
      }
    DONE;
  }
  ")


;
; BLOCK MOVE
; We should probably get RC loaded when using RPTB automagically...
; There's probably no need to call _memcpy() if we don't get
; an immediate operand for the size.  We could do a better job here
; than most memcpy() implementations.
; operand 2 is the number of bytes
; operand 3 is the shared alignment
; operand 4 is a scratch register

(define_insn "movstrqi_small"
  [(set (mem:BLK (match_operand:QI 0 "addr_reg_operand" "+a"))
        (mem:BLK (match_operand:QI 1 "addr_reg_operand" "+a")))
   (use (match_operand:QI 2 "immediate_operand" "i"))
   (use (match_operand:QI 3 "immediate_operand" ""))
   (clobber (match_operand:QI 4 "ext_low_reg_operand" "=&q"))
   (clobber (match_dup 0))
   (clobber (match_dup 1))]
  ""
  "*
 {
   int i;
   int len = INTVAL (operands[2]);
   int first = 1;

   for (i = 0; i < len; i++)
    {
      if (first)
        output_asm_insn (\"ldiu\\t*%1++,%4\", operands);
      else
        output_asm_insn (\"|| ldi\\t*%1++,%4\", operands);
      output_asm_insn (\"sti\\t%4,*%0++\", operands);
      first = 0;
    } 
  return \"\";
  }
  "
  [(set_attr "type" "multi")])

(define_insn "movstrqi_large"
  [(set (mem:BLK (match_operand:QI 0 "addr_reg_operand" "+a"))
        (mem:BLK (match_operand:QI 1 "addr_reg_operand" "+a")))
   (use (match_operand:QI 2 "immediate_operand" "i"))
   (use (match_operand:QI 3 "immediate_operand" ""))
   (clobber (match_operand:QI 4 "ext_low_reg_operand" "=&q"))
   (clobber (match_dup 0))
   (clobber (match_dup 1))
   (clobber (reg:QI 25))
   (clobber (reg:QI 26))
   (clobber (reg:QI 27))]
  ""
  "*
 {
   int len = INTVAL (operands[2]);

   output_asm_insn (\"ldiu\\t*%1++,%4\", operands);
   if (TARGET_RPTS_CYCLES (len))
     {
        output_asm_insn (\"rpts\\t%2-2\", operands);  
        output_asm_insn (\"sti\\t%4,*%0++\", operands);
        output_asm_insn (\"|| ldi\\t*%1++,%4\", operands);
        return \"sti\\t%4,*%0++\";
     }
   else
     {
        output_asm_insn (\"ldiu\\t%2-2,rc\", operands);
        output_asm_insn (\"rptb\\t$+1\", operands);  
        output_asm_insn (\"sti\\t%4,*%0++\", operands);
        output_asm_insn (\"|| ldi\\t*%1++,%4\", operands);

        return \"sti\\t%4,*%0++\";
     }
  }
  "
  [(set_attr "type" "multi")])

; Operand 2 is the count, operand 3 is the alignment.
(define_expand "movstrqi"
  [(parallel [(set (mem:BLK (match_operand:BLK 0 "src_operand" ""))
                   (mem:BLK (match_operand:BLK 1 "src_operand" "")))
              (use (match_operand:QI 2 "immediate_operand" ""))
              (use (match_operand:QI 3 "immediate_operand" ""))])]
  ""
  "
 {
   rtx tmp;
   if (GET_CODE (operands[2]) != CONST_INT 
       || INTVAL (operands[2]) > 32767 
       || INTVAL (operands[2]) <= 0)
     {
        FAIL;  /* Try to call _memcpy */
     }

   operands[0] = copy_to_mode_reg (Pmode, XEXP (operands[0], 0));
   operands[1] = copy_to_mode_reg (Pmode, XEXP (operands[1], 0));
   tmp = gen_reg_rtx (QImode);
   if (INTVAL (operands[2]) < 8)
     emit_insn (gen_movstrqi_small2 (operands[0], operands[1], operands[2],
                                    operands[3], tmp));
   else
     {
      emit_insn (gen_movstrqi_large (operands[0], operands[1], operands[2],
                                     operands[3], tmp));
     }
   DONE;
 }")


(define_insn "*cmpstrqi"
  [(set (match_operand:QI 0 "ext_reg_operand" "=d")
        (compare:QI (mem:BLK (match_operand:QI 1 "addr_reg_operand" "+a"))
                    (mem:BLK (match_operand:QI 2 "addr_reg_operand" "+a"))))
   (use (match_operand:QI 3 "immediate_operand" "i"))
   (use (match_operand:QI 4 "immediate_operand" ""))
   (clobber (match_operand:QI 5 "std_reg_operand" "=&c"))
   (clobber (reg:QI 21))]
  ""
  "*
 {
    output_asm_insn (\"ldi\\t%3-1,%5\", operands);
    output_asm_insn (\"$1:\tsubi3\\t*%1++,*%2++,%0\", operands);
    output_asm_insn (\"dbeq\\t%5,$1\", operands);
    return \"\";
 }")

(define_expand "cmpstrqi"
  [(parallel [(set (match_operand:QI 0 "reg_operand" "")
                   (compare:QI (match_operand:BLK 1 "general_operand" "")
                               (match_operand:BLK 2 "general_operand" "")))
              (use (match_operand:QI 3 "immediate_operand" ""))
              (use (match_operand:QI 4 "immediate_operand" ""))
              (clobber (match_dup 5))
              (clobber (reg:QI 21))])]
  ""
  "
{
   if (GET_CODE (operands[3]) != CONST_INT
       || INTVAL (operands[3]) > 32767 
       || INTVAL (operands[3]) <= 0)
     {
        FAIL;
     }
   operands[1] = copy_to_mode_reg (Pmode, XEXP (operands[1], 0));
   operands[2] = copy_to_mode_reg (Pmode, XEXP (operands[2], 0));
   operands[5] = gen_reg_rtx (QImode);
}")

;
; TWO OPERAND LONG DOUBLE INSTRUCTIONS
;

(define_expand "movhf"
  [(set (match_operand:HF 0 "src_operand" "")
        (match_operand:HF 1 "src_operand" ""))]
 ""
 "if (c4x_emit_move_sequence (operands, HFmode))
    DONE;")

(define_insn "*movhf_noclobber_reg"
  [(set (match_operand:HF 0 "reg_operand" "=h")
        (match_operand:HF 1 "src_operand" "Hh"))]
 "GET_CODE (operands[1]) != MEM"
 "ldfu\\t%1,%0"
  [(set_attr "type" "unary")])

(define_insn "*movhf_noclobber"
 [(set (match_operand:HF 0 "dst_operand" "=h,m")
       (match_operand:HF 1 "src_operand" "Hm,h"))]
 "reg_operand (operands[0], HFmode) ^ reg_operand (operands[1], HFmode)"
 "#"
 [(set_attr "type" "multi,multi")])

(define_insn "*movhf_test"
  [(set (reg:CC 21)
        (compare:CC (match_operand:HF 1 "reg_operand" "h")
                    (const_int 0)))
   (clobber (match_scratch:HF 0 "=h"))]
 ""
 "ldf\\t%1,%0"
  [(set_attr "type" "unarycc")])

(define_insn "*movhf_set"
  [(set (reg:CC 21)
        (compare:CC (match_operand:HF 1 "reg_operand" "h")
                    (match_operand:HF 2 "fp_zero_operand" "G")))
    (set (match_operand:HF 0 "reg_operand" "=h")
         (match_dup 1))]
 ""
 "ldf\\t%1,%0"
  [(set_attr "type" "unarycc")])

(define_split
 [(set (match_operand:HF 0 "reg_operand" "")
       (match_operand:HF 1 "memory_operand" ""))]
 "reload_completed"
 [(set (match_dup 0) (float_extend:HF (match_dup 2)))
  (set (match_dup 0) (unspec:HF [(subreg:QI (match_dup 0) 0)
                                            (match_dup 3)] 8))]
 "operands[2] = c4x_operand_subword (operands[1], 0, 1, HFmode);
  operands[3] = c4x_operand_subword (operands[1], 1, 1, HFmode);
  PUT_MODE (operands[2], QFmode);
  PUT_MODE (operands[3], QImode);")

(define_split
 [(set (match_operand:HF 0 "reg_operand" "")
       (match_operand:HF 1 "const_operand" ""))]
 "reload_completed && 0"
 [(set (match_dup 0) (float_extend:HF (match_dup 2)))
  (set (match_dup 0) (unspec:HF [(subreg:QI (match_dup 0) 0)
                                            (match_dup 3)] 8))]
 "operands[2] = c4x_operand_subword (operands[1], 0, 1, HFmode);
  operands[3] = c4x_operand_subword (operands[1], 1, 1, HFmode);
  PUT_MODE (operands[2], QFmode);
  PUT_MODE (operands[3], QImode);")

(define_split
 [(set (match_operand:HF 0 "memory_operand" "")
       (match_operand:HF 1 "reg_operand" ""))]
  "reload_completed"
  [(set (match_dup 2) (float_truncate:QF (match_dup 1)))
   (set (match_dup 3) (unspec:QI [(match_dup 1)] 9))]
 "operands[2] = c4x_operand_subword (operands[0], 0, 1, HFmode);
  operands[3] = c4x_operand_subword (operands[0], 1, 1, HFmode);
  PUT_MODE (operands[2], QFmode);
  PUT_MODE (operands[3], QImode);")

(define_insn "*loadhf_float"
 [(set (match_operand:HF 0 "reg_operand" "=h")
       (float_extend:HF (match_operand:QF 1 "src_operand" "fHm")))]
 ""
 "ldfu\\t%1,%0"
  [(set_attr "type" "unary")])

(define_insn "*loadhf_int"
 [(set (match_operand:HF 0 "reg_operand" "+h")
       (unspec:HF [(subreg:QI (match_dup 0) 0)
                   (match_operand:QI 1 "src_operand" "rIm")] 8))]
 ""
 "ldiu\\t%1,%0"
  [(set_attr "type" "unary")])

(define_insn "*storehf_float"
  [(set (match_operand:QF 0 "memory_operand" "=m")
        (float_truncate:QF (match_operand:HF 1 "reg_operand" "h")))]
  ""
  "stf\\t%1,%0"
  [(set_attr "type" "store")])

(define_insn "*storehf_int"
 [(set (match_operand:QI 0 "memory_operand" "=m")
       (unspec:QI [(match_operand:HF 1 "reg_operand" "h")] 9))]
 ""
 "sti\\t%1,%0"
  [(set_attr "type" "store")])

(define_insn "extendqfhf2"
  [(set (match_operand:HF 0 "reg_operand" "=h")
        (float_extend:HF (match_operand:QF 1 "reg_operand" "h")))]
  ""
  "ldfu\\t%1,%0"
  [(set_attr "type" "unarycc")])

(define_insn "trunchfqf2"
  [(set (match_operand:QF 0 "reg_operand" "=h")
        (float_truncate:QF (match_operand:HF 1 "reg_operand" "0")))
   (clobber (reg:CC 21))]
  ""
  "andn\\t0ffh,%0"
  [(set_attr "type" "unarycc")])

;
; PUSH/POP
;
(define_insn "pushhf"
  [(set (mem:HF (pre_inc:QI (reg:QI 20)))
        (match_operand:HF 0 "reg_operand" "h"))]
 ""
 "#"
 [(set_attr "type" "multi")])

(define_split
 [(set (mem:HF (pre_inc:QI (reg:QI 20)))
        (match_operand:HF 0 "reg_operand" ""))]
  "reload_completed"
  [(set (mem:QF (pre_inc:QI (reg:QI 20)))
        (float_truncate:QF (match_dup 0)))
   (set (mem:QI (pre_inc:QI (reg:QI 20)))
        (unspec:QI [(match_dup 0)] 9))]
 "")

(define_insn "pushhf_trunc"
  [(set (mem:QF (pre_inc:QI (reg:QI 20)))
        (float_truncate:QF (match_operand:HF 0 "reg_operand" "h")))]
 ""
 "pushf\\t%0"
 [(set_attr "type" "push")])

(define_insn "pushhf_int"
  [(set (mem:QI (pre_inc:QI (reg:QI 20)))
        (unspec:QI [(match_operand:HF 0 "reg_operand" "h")] 9))]
 ""
 "push\\t%0"
 [(set_attr "type" "push")])

; we can not use this because the popf will destroy the low 8 bits
;(define_insn "pophf"
;  [(set (match_operand:HF 0 "reg_operand" "=h")
;        (mem:HF (post_dec:QI (reg:QI 20))))
;   (clobber (reg:CC 21))]
; ""
; "#"
; [(set_attr "type" "multi")])

(define_split
 [(set (match_operand:HF 0 "reg_operand" "")
       (mem:HF (post_dec:QI (reg:QI 20))))
   (clobber (reg:CC 21))]
  "reload_completed"
  [(parallel [(set (match_operand:HF 0 "reg_operand" "=h")
                   (float_extend:HF (mem:QF (post_dec:QI (reg:QI 20)))))
              (clobber (reg:CC 21))])
   (parallel [(set (match_dup 0)
                   (unspec:HF [(subreg:QI (match_dup 0) 0)
                   (mem:QI (post_dec:QI (reg:QI 20)))] 8))
              (clobber (reg:CC 21))])]
 "")

(define_insn "*pophf_int"
 [(set (match_operand:HF 0 "reg_operand" "+h")
       (unspec:HF [(subreg:QI (match_dup 0) 0)
                   (mem:QI (post_dec:QI (reg:QI 20)))] 8))
  (clobber (reg:CC 21))]
 ""
 "pop\\t%0"
  [(set_attr "type" "pop")])

(define_insn "*pophf_float"
 [(set (match_operand:HF 0 "reg_operand" "=h")
       (float_extend:HF (mem:QF (post_dec:QI (reg:QI 20)))))
  (clobber (reg:CC 21))]
 ""
 "popf\\t%0"
  [(set_attr "type" "pop")])

;
; FIX
;
(define_insn "fixhfqi_clobber"
  [(set (match_operand:QI 0 "reg_operand" "=dc")
        (fix:QI (match_operand:HF 1 "reg_or_const_operand" "hH")))
   (clobber (reg:CC 21))]
 ""
 "fix\\t%1,%0"
  [(set_attr "type" "unarycc")])

;
; ABSF
;
(define_expand "abshf2"
  [(parallel [(set (match_operand:HF 0 "reg_operand" "")
                   (abs:HF (match_operand:HF 1 "reg_or_const_operand" "")))
              (clobber (reg:CC_NOOV 21))])]
""
"")

(define_insn "*abshf2_clobber"
  [(set (match_operand:HF 0 "reg_operand" "=h")
        (abs:HF (match_operand:HF 1 "reg_or_const_operand" "hH")))
   (clobber (reg:CC_NOOV 21))]
  ""
  "absf\\t%1,%0"
  [(set_attr "type" "unarycc")])

(define_insn "*abshf2_test"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (abs:HF (match_operand:HF 1 "reg_operand" "h"))
                         (match_operand:HF 2 "fp_zero_operand" "G")))
   (clobber (match_scratch:HF 0 "=h"))]
  ""
  "absf\\t%1,%0"
  [(set_attr "type" "unarycc")])

(define_insn "*abshf2_set"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (abs:HF (match_operand:HF 1 "reg_or_const_operand" "hH"))
                         (match_operand:HF 2 "fp_zero_operand" "G")))
   (set (match_operand:HF 0 "reg_operand" "=h")
        (abs:HF (match_dup 1)))]

  ""
  "absf\\t%1,%0"
  [(set_attr "type" "unarycc")])

;
; NEGF
;
(define_expand "neghf2"
  [(parallel [(set (match_operand:HF 0 "reg_operand" "")
                   (neg:HF (match_operand:HF 1 "reg_or_const_operand" "")))
              (clobber (reg:CC_NOOV 21))])]
""
"")

(define_insn "*neghf2_clobber"
  [(set (match_operand:HF 0 "reg_operand" "=h")
        (neg:HF (match_operand:HF 1 "reg_or_const_operand" "hH")))
   (clobber (reg:CC_NOOV 21))]
  ""
  "negf\\t%1,%0"
  [(set_attr "type" "unarycc")])

(define_insn "*neghf2_test"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (neg:HF (match_operand:HF 1 "reg_or_const_operand" "hH"))
                         (match_operand:HF 2 "fp_zero_operand" "G")))
   (clobber (match_scratch:HF 0 "=h"))]
  ""
  "negf\\t%1,%0"
  [(set_attr "type" "unarycc")])

(define_insn "*neghf2_set"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (neg:HF (match_operand:HF 1 "reg_or_const_operand" "hH"))
                         (match_operand:HF 2 "fp_zero_operand" "G")))
   (set (match_operand:HF 0 "reg_operand" "=h")
        (neg:HF (match_dup 1)))]
  ""
  "negf\\t%1,%0"
  [(set_attr "type" "unarycc")])

;
; RCPF
;
(define_insn "*rcpfhf_clobber"
  [(set (match_operand:HF 0 "reg_operand" "=h")
        (unspec:HF [(match_operand:HF 1 "reg_or_const_operand" "hH")] 5))
   (clobber (reg:CC_NOOV 21))]
  "! TARGET_C3X"
  "rcpf\\t%1,%0"
  [(set_attr "type" "unarycc")])

;
; RSQRF
;
(define_insn "*rsqrfhf_clobber"
  [(set (match_operand:HF 0 "reg_operand" "=h")
        (unspec:HF [(match_operand:HF 1 "reg_or_const_operand" "hH")] 10))
   (clobber (reg:CC_NOOV 21))]
  "! TARGET_C3X"
  "rsqrf\\t%1,%0"
  [(set_attr "type" "unarycc")])

;
; RNDF
;
(define_insn "*rndhf_clobber"
  [(set (match_operand:HF 0 "reg_operand" "=h")
        (unspec:HF [(match_operand:HF 1 "reg_or_const_operand" "hH")] 6))
   (clobber (reg:CC_NOOV 21))]
  "! TARGET_C3X"
  "rnd\\t%1,%0"
  [(set_attr "type" "unarycc")])


; Inlined float square root for C4x
(define_expand "sqrthf2_inline"
  [(parallel [(set (match_dup 2)
	           (unspec:HF [(match_operand:HF 1 "reg_operand" "")] 10))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 3) (mult:HF (match_dup 5) (match_dup 1)))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 4) (mult:HF (match_dup 2) (match_dup 3)))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 4) (mult:HF (match_dup 2) (match_dup 4)))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 4) (minus:HF (match_dup 6) (match_dup 4)))
              (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 2) (mult:HF (match_dup 2) (match_dup 4)))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 4) (mult:HF (match_dup 2) (match_dup 3)))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 4) (mult:HF (match_dup 2) (match_dup 4)))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 4) (minus:HF (match_dup 6) (match_dup 4)))
              (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 2) (mult:HF (match_dup 2) (match_dup 4)))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_operand:HF 0 "reg_operand" "")
		   (mult:HF (match_dup 2) (match_dup 1)))
	      (clobber (reg:CC_NOOV 21))])]
  "! TARGET_C3X"
  "
  operands[2] = gen_reg_rtx (HFmode);
  operands[3] = gen_reg_rtx (HFmode);
  operands[4] = gen_reg_rtx (HFmode);
  operands[5] = immed_real_const_1 (REAL_VALUE_ATOF (\"0.5\", HFmode), HFmode);
  operands[6] = immed_real_const_1 (REAL_VALUE_ATOF (\"1.5\", HFmode), HFmode);
  ")


(define_expand "sqrthf2"
  [(parallel [(set (match_operand:HF 0 "reg_operand" "")
                   (sqrt:HF (match_operand:HF 1 "reg_operand" "")))
              (clobber (reg:CC 21))])]
  "! TARGET_C3X && TARGET_INLINE"
  "emit_insn (gen_sqrthf2_inline (operands[0], operands[1]));
   DONE;")


(define_expand "fix_trunchfhi2"
  [(parallel [(set (match_operand:HI 0 "reg_operand" "")
                   (fix:HI (match_operand:HF 1 "reg_operand" "")))
              (clobber (reg:CC 21))])]
  ""
  "c4x_emit_libcall (fix_trunchfhi2_libfunc, FIX, HImode, HFmode, 2, operands);
   DONE;")

(define_expand "fixuns_trunchfhi2"
  [(parallel [(set (match_operand:HI 0 "reg_operand" "")
                   (unsigned_fix:HI (match_operand:HF 1 "reg_operand" "")))
              (clobber (reg:CC 21))])]
  ""
  "c4x_emit_libcall (fixuns_trunchfhi2_libfunc, UNSIGNED_FIX, 
                     HImode, HFmode, 2, operands);
   DONE;")

;
; THREE OPERAND LONG DOUBLE INSTRUCTIONS
;

;
; ADDF
;
(define_insn "addhf3"
  [(set (match_operand:HF 0 "reg_operand" "=h,?h")
        (plus:HF (match_operand:HF 1 "reg_operand" "%0,h")
                 (match_operand:HF 2 "reg_or_const_operand" "H,h")))
   (clobber (reg:CC_NOOV 21))]
  ""
  "@
   addf\\t%2,%0
   addf3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc")])

;
; SUBF
;
(define_insn "subhf3"
  [(set (match_operand:HF 0 "reg_operand" "=h,h,?h")
        (minus:HF (match_operand:HF 1 "reg_or_const_operand" "0,H,h")
                  (match_operand:HF 2 "reg_or_const_operand" "H,0,h")))
   (clobber (reg:CC_NOOV 21))]
  ""
  "@
   subf\\t%2,%0
   subrf\\t%1,%0
   subf3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc,binarycc")])

;
; MULF
;
; The C3x MPYF only uses 24 bit precision while the C4x uses 32 bit precison.
;
(define_expand "mulhf3"
  [(parallel [(set (match_operand:HF 0 "reg_operand" "=h")
                   (mult:HF (match_operand:HF 1 "reg_operand" "h")
                            (match_operand:HF 2 "reg_operand" "h")))
              (clobber (reg:CC_NOOV 21))])]
  "! TARGET_C3X"
  "")

(define_insn "*mulhf3_c40"
  [(set (match_operand:HF 0 "reg_operand" "=h,?h")
        (mult:HF (match_operand:HF 1 "reg_operand" "%0,h")
                 (match_operand:HF 2 "reg_or_const_operand" "hH,h")))
   (clobber (reg:CC_NOOV 21))]
  ""
  "@
   mpyf\\t%2,%0
   mpyf3\\t%2,%1,%0"
  [(set_attr "type" "binarycc,binarycc")])

;
; CMPF
;
(define_expand "cmphf"
  [(set (reg:CC 21)
        (compare:CC (match_operand:HF 0 "reg_operand" "")
                    (match_operand:HF 1 "reg_or_const_operand" "")))]
  ""
  "c4x_compare_op0 = operands[0];
   c4x_compare_op1 = operands[1];
   DONE;")

(define_insn "*cmphf"
  [(set (reg:CC 21)
        (compare:CC (match_operand:HF 0 "reg_operand" "h")
                    (match_operand:HF 1 "reg_or_const_operand" "hH")))]
  ""
  "cmpf\\t%1,%0"
  [(set_attr "type" "compare")])

(define_insn "*cmphf_noov"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (match_operand:HF 0 "reg_operand" "h")
                         (match_operand:HF 1 "reg_or_const_operand" "hH")))]
  ""
  "cmpf\\t%1,%0"
  [(set_attr "type" "compare")])

; Inlined float divide for C4x
(define_expand "divhf3_inline"
  [(parallel [(set (match_dup 3)
	           (unspec:HF [(match_operand:HF 2 "reg_operand" "")] 5))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 4) (mult:HF (match_dup 2) (match_dup 3)))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 4) (minus:HF (match_dup 5) (match_dup 4)))
              (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 3) (mult:HF (match_dup 3) (match_dup 4)))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 4) (mult:HF (match_dup 2) (match_dup 3)))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 4) (minus:HF (match_dup 5) (match_dup 4)))
              (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_dup 3) (mult:HF (match_dup 3) (match_dup 4)))
	      (clobber (reg:CC_NOOV 21))])
   (parallel [(set (match_operand:HF 0 "reg_operand" "")
		   (mult:HF (match_operand:HF 1 "reg_operand" "")
	         	    (match_dup 3)))
	      (clobber (reg:CC_NOOV 21))])]
  "! TARGET_C3X"
  "
  operands[3] = gen_reg_rtx (HFmode);
  operands[4] = gen_reg_rtx (HFmode);
  operands[5] = CONST2_RTX (HFmode);
  ")

(define_expand "divhf3"
  [(parallel [(set (match_operand:HF 0 "reg_operand" "")
                   (div:HF (match_operand:HF 1 "reg_operand" "")
                           (match_operand:HF 2 "reg_operand" "")))
              (clobber (reg:CC 21))])]
  "! TARGET_C3X && TARGET_INLINE"
  "emit_insn (gen_divhf3_inline (operands[0], operands[1], operands[2]));
   DONE;")


;
; TWO OPERAND LONG LONG INSTRUCTIONS
;

(define_insn "*movhi_stik"
  [(set (match_operand:HI 0 "memory_operand" "=m")
        (match_operand:HI 1 "stik_const_operand" "K"))]
  "! TARGET_C3X"
  "#"
  [(set_attr "type" "multi")])

; We could load some constants using define_splits for the C30
; in the large memory model---these would emit shift and or insns.
(define_expand "movhi"
  [(set (match_operand:HI 0 "src_operand" "")
        (match_operand:HI 1 "src_operand" ""))]
 ""
 "if (c4x_emit_move_sequence (operands, HImode))
    DONE;")

; The constraints for movhi must include 'r' if we don't
; restrict HImode regnos to start on an even number, since
; we can get RC, R8 allocated as a pair.  We want more
; votes for FP_REGS so we use dr as the constraints.
(define_insn "*movhi_noclobber"
  [(set (match_operand:HI 0 "dst_operand" "=dr,m")
        (match_operand:HI 1 "src_operand" "drIm,r"))]
  "reg_operand (operands[0], HImode)
   || reg_operand (operands[1], HImode)"
  "#"
  [(set_attr "type" "multi,multi")])

; This will fail miserably if the destination register is used in the 
; source memory address.
; The usual strategy in this case is to swap the order of insns we emit,
; however, this will fail if we have an autoincrement memory address.
; For example:
; ldi *ar0++, ar0
; ldi *ar0++, ar1
;
; We could convert this to
; ldi *ar0(1), ar1
; ldi *ar0, ar0
;
; However, things are likely to be very screwed up if we get this.

(define_split
  [(set (match_operand:HI 0 "dst_operand" "")
	(match_operand:HI 1 "src_operand" ""))]
  "reload_completed
   && (reg_operand (operands[0], HImode)
       || reg_operand (operands[1], HImode)
       || stik_const_operand (operands[1], HImode))"
  [(set (match_dup 2) (match_dup 4))
   (set (match_dup 3) (match_dup 5))]
  "operands[2] = c4x_operand_subword (operands[0], 0, 1, HImode);
   operands[3] = c4x_operand_subword (operands[0], 1, 1, HImode);
   operands[4] = c4x_operand_subword (operands[1], 0, 1, HImode);
   operands[5] = c4x_operand_subword (operands[1], 1, 1, HImode);
   if (reg_overlap_mentioned_p (operands[2], operands[5]))
     {
	/* Swap order of move insns.  */
	rtx tmp;
	tmp = operands[2];
        operands[2] =operands[3];
        operands[3] = tmp;
	tmp = operands[4];
        operands[4] =operands[5];
        operands[5] = tmp;        
     }")


(define_insn "extendqihi2"
  [(set (match_operand:HI 0 "reg_operand" "=dc")
        (sign_extend:HI (match_operand:QI 1 "src_operand" "rIm")))
   (clobber (reg:CC 21))]
  ""
  "#"
  [(set_attr "type" "multi")])

(define_split
  [(set (match_operand:HI 0 "reg_operand" "")
        (sign_extend:HI (match_operand:QI 1 "src_operand" "")))
   (clobber (reg:CC 21))]
  "reload_completed && TARGET_C3X"
  [(set (match_dup 2) (match_dup 1))
   (set (match_dup 3) (match_dup 2))
   (parallel [(set (match_dup 3) (ashiftrt:QI (match_dup 3) (const_int 31)))
              (clobber (reg:CC 21))])]
  "operands[2] = c4x_operand_subword (operands[0], 0, 0, HImode);
   operands[3] = c4x_operand_subword (operands[0], 1, 0, HImode);")

(define_split
  [(set (match_operand:HI 0 "reg_operand" "")
        (sign_extend:HI (match_operand:QI 1 "src_operand" "")))
   (clobber (reg:CC 21))]
  "reload_completed && ! TARGET_C3X"
  [(set (match_dup 2) (match_dup 1))
   (parallel [(set (match_dup 3) (ashiftrt:QI (match_dup 2) (const_int 31)))
              (clobber (reg:CC 21))])]
  "operands[2] = c4x_operand_subword (operands[0], 0, 0, HImode);
   operands[3] = c4x_operand_subword (operands[0], 1, 0, HImode);")

(define_insn "zero_extendqihi2"
  [(set (match_operand:HI 0 "reg_operand" "=?dc")
        (zero_extend:HI (match_operand:QI 1 "src_operand" "rIm")))
   (clobber (reg:CC 21))]
  ""
  "#"
  [(set_attr "type" "multi")])

; If operand0 and operand1 are the same register we don't need
; the first set.
(define_split
  [(set (match_operand:HI 0 "reg_operand" "")
        (zero_extend:HI (match_operand:QI 1 "src_operand" "")))
   (clobber (reg:CC 21))]
  "reload_completed"
  [(set (match_dup 2) (match_dup 1))
   (set (match_dup 3) (const_int 0))]
  "operands[2] = c4x_operand_subword (operands[0], 0, 0, HImode);
   operands[3] = c4x_operand_subword (operands[0], 1, 0, HImode);")

;
; PUSH/POP
;
(define_insn "*pushhi"
  [(set (mem:HI (pre_inc:QI (reg:QI 20)))
        (match_operand:HI 0 "reg_operand" "r"))]
  ""
  "#"
  [(set_attr "type" "multi")])

(define_split
  [(set (mem:HI (pre_inc:QI (reg:QI 20)))
        (match_operand:HI 0 "reg_operand" ""))]
  "reload_completed"
  [(set (mem:QI (pre_inc:QI (reg:QI 20))) (match_dup 2))
   (set (mem:QI (pre_inc:QI (reg:QI 20))) (match_dup 3))]
  "operands[2] = c4x_operand_subword (operands[0], 0, 0, HImode);
   operands[3] = c4x_operand_subword (operands[0], 1, 0, HImode);")

(define_insn "*pophi"
  [(set (match_operand:HI 0 "reg_operand" "=r")
        (mem:HI (post_dec:QI (reg:QI 20))))
   (clobber (reg:CC 21))]
  ""
  "#"
  [(set_attr "type" "multi")])

(define_split
  [(set (match_operand:HI 0 "reg_operand" "")
       (mem:HI (pre_inc:QI (reg:QI 20))))]
  "reload_completed"
  [(set (match_dup 2) (mem:QI (pre_inc:QI (reg:QI 20))))
   (set (match_dup 3) (mem:QI (pre_inc:QI (reg:QI 20))))]
  "operands[2] = c4x_operand_subword (operands[0], 0, 0, HImode);
   operands[3] = c4x_operand_subword (operands[0], 1, 0, HImode);")

;
; NEG
;
(define_insn "neghi2"
  [(set (match_operand:HI 0 "ext_reg_operand" "=d")
        (neg:HI (match_operand:HI 1 "src_operand" "rm")))
   (clobber (reg:CC_NOOV 21))]
  ""
  "#"
  [(set_attr "type" "multi")])

(define_split
  [(set (match_operand:HI 0 "ext_reg_operand" "")
        (neg:HI (match_operand:HI 1 "src_operand" "")))
   (clobber (reg:CC_NOOV 21))]
  "reload_completed"
   [(parallel [(set (reg:CC_NOOV 21)
                    (compare:CC_NOOV (neg:QI (match_dup 3))
                                     (const_int 0)))
               (set (match_dup 2) (neg:QI (match_dup 3)))])
   (parallel [(set (match_dup 4) (neg:QI (match_dup 5)))
              (use (reg:CC_NOOV 21))
              (clobber (reg:CC_NOOV 21))])]
  "operands[2] = c4x_operand_subword (operands[0], 0, 1, HImode);
   operands[3] = c4x_operand_subword (operands[1], 0, 1, HImode);
   operands[4] = c4x_operand_subword (operands[0], 1, 1, HImode);
   operands[5] = c4x_operand_subword (operands[1], 1, 1, HImode);")

(define_insn "one_cmplhi2"
  [(set (match_operand:HI 0 "reg_operand" "=r")
        (not:HI (match_operand:HI 1 "src_operand" "rm")))
   (clobber (reg:CC 21))]
  ""
  "#"
  [(set_attr "type" "multi")])

(define_split
  [(set (match_operand:HI 0 "reg_operand" "")
        (not:HI (match_operand:HI 1 "src_operand" "")))
   (clobber (reg:CC 21))]
  "reload_completed"
   [(parallel [(set (match_dup 2) (not:QI (match_dup 3)))
               (clobber (reg:CC 21))])
    (parallel [(set (match_dup 4) (not:QI (match_dup 5)))
               (clobber (reg:CC 21))])]
  "operands[2] = c4x_operand_subword (operands[0], 0, 1, HImode);
   operands[3] = c4x_operand_subword (operands[1], 0, 1, HImode);
   operands[4] = c4x_operand_subword (operands[0], 1, 1, HImode);
   operands[5] = c4x_operand_subword (operands[1], 1, 1, HImode);")

(define_expand "floathiqf2"
  [(parallel [(set (match_operand:QF 0 "reg_operand" "")
                   (float:QF (match_operand:HI 1 "src_operand" "")))
              (clobber (reg:CC 21))])]
  ""
  "c4x_emit_libcall (floathiqf2_libfunc, FLOAT, QFmode, HImode, 2, operands);
   DONE;")

(define_expand "floatunshiqf2"
  [(parallel [(set (match_operand:QF 0 "reg_operand" "")
                   (unsigned_float:QF (match_operand:HI 1 "src_operand" "")))
              (clobber (reg:CC 21))])]
  ""
  "c4x_emit_libcall (floatunshiqf2_libfunc, UNSIGNED_FLOAT,
                     QFmode, HImode, 2, operands);
   DONE;")

(define_expand "floathihf2"
  [(parallel [(set (match_operand:HF 0 "reg_operand" "")
                   (float:HF (match_operand:HI 1 "src_operand" "")))
              (clobber (reg:CC 21))])]
  ""
  "c4x_emit_libcall (floathihf2_libfunc, FLOAT, HFmode, HImode, 2, operands);
   DONE;")

(define_expand "floatunshihf2"
  [(parallel [(set (match_operand:HF 0 "reg_operand" "")
                   (unsigned_float:HF (match_operand:HI 1 "src_operand" "")))
              (clobber (reg:CC 21))])]
  ""
  "c4x_emit_libcall (floatunshihf2_libfunc, UNSIGNED_FLOAT,
                     HFmode, HImode, 2, operands);
   DONE;")


;
; THREE OPERAND LONG LONG INSTRUCTIONS
;

(define_expand "addhi3"
  [(parallel [(set (match_operand:HI 0 "ext_reg_operand" "")
                   (plus:HI (match_operand:HI 1 "src_operand" "")
                            (match_operand:HI 2 "src_operand" "")))
              (clobber (reg:CC_NOOV 21))])]
  ""
  "legitimize_operands (PLUS, operands, HImode);")

(define_insn "*addhi3_clobber"
  [(set (match_operand:HI 0 "ext_reg_operand" "=d,d,?d")
        (plus:HI (match_operand:HI 1 "src_operand" "%0,rR,rS<>")
                 (match_operand:HI 2 "src_operand" "rm,R,rS<>")))
   (clobber (reg:CC_NOOV 21))]
  "valid_operands (PLUS, operands, HImode)"
  "#"
  [(set_attr "type" "multi,multi,multi")])

(define_split
 [(set (match_operand:HI 0 "ext_reg_operand" "")
       (plus:HI (match_operand:HI 1 "src_operand" "")
                (match_operand:HI 2 "src_operand" "")))
  (clobber (reg:CC_NOOV 21))]
 "reload_completed"
  [(parallel [(set (reg:CC_NOOV 21)
                   (compare:CC_NOOV (plus:QI (match_dup 4) (match_dup 5))
                                    (const_int 0)))
              (set (match_dup 3) (plus:QI (match_dup 4) (match_dup 5)))])
   (parallel [(set (match_dup 6) (plus:QI (match_dup 7) (match_dup 8)))
              (use (reg:CC_NOOV 21))
              (clobber (reg:CC_NOOV 21))])]
  "operands[3] = c4x_operand_subword (operands[0], 0, 1, HImode);
   operands[4] = c4x_operand_subword (operands[1], 0, 1, HImode);
   operands[5] = c4x_operand_subword (operands[2], 0, 1, HImode);
   operands[6] = c4x_operand_subword (operands[0], 1, 1, HImode);
   operands[7] = c4x_operand_subword (operands[1], 1, 1, HImode);
   operands[8] = c4x_operand_subword (operands[2], 1, 1, HImode);")

(define_expand "subhi3"
  [(parallel [(set (match_operand:HI 0 "ext_reg_operand" "")
                   (minus:HI (match_operand:HI 1 "src_operand" "")
                             (match_operand:HI 2 "src_operand" "")))
              (clobber (reg:CC_NOOV 21))])]
  ""
  "legitimize_operands (MINUS, operands, HImode);")


(define_insn "*subhi3_clobber"
  [(set (match_operand:HI 0 "ext_reg_operand" "=d,d,?d")
        (minus:HI (match_operand:HI 1 "src_operand" "0,rR,rS<>")
                  (match_operand:HI 2 "src_operand" "rm,R,rS<>")))
   (clobber (reg:CC_NOOV 21))]
  "valid_operands (MINUS, operands, HImode)"
  "#"
  [(set_attr "type" "multi,multi,multi")])

(define_split
 [(set (match_operand:HI 0 "ext_reg_operand" "")
       (minus:HI (match_operand:HI 1 "src_operand" "")
                 (match_operand:HI 2 "src_operand" "")))
  (clobber (reg:CC_NOOV 21))]
 "reload_completed"
  [(parallel [(set (reg:CC_NOOV 21)
                   (compare:CC_NOOV (minus:QI (match_dup 4) (match_dup 5))
                                    (const_int 0)))
              (set (match_dup 3) (minus:QI (match_dup 4) (match_dup 5)))])
   (parallel [(set (match_dup 6) (minus:QI (match_dup 7) (match_dup 8)))
              (use (reg:CC_NOOV 21))
              (clobber (reg:CC_NOOV 21))])]
  "operands[3] = c4x_operand_subword (operands[0], 0, 1, HImode);
   operands[4] = c4x_operand_subword (operands[1], 0, 1, HImode);
   operands[5] = c4x_operand_subword (operands[2], 0, 1, HImode);
   operands[6] = c4x_operand_subword (operands[0], 1, 1, HImode);
   operands[7] = c4x_operand_subword (operands[1], 1, 1, HImode);
   operands[8] = c4x_operand_subword (operands[2], 1, 1, HImode);")

(define_expand "iorhi3"
  [(parallel [(set (match_operand:HI 0 "reg_operand" "")
                   (ior:HI (match_operand:HI 1 "src_operand" "")
                           (match_operand:HI 2 "src_operand" "")))
              (clobber (reg:CC 21))])]
  ""
  "legitimize_operands (IOR, operands, HImode);")

(define_insn "*iorhi3_clobber"
  [(set (match_operand:HI 0 "reg_operand" "=d,d,?d")
        (ior:HI (match_operand:HI 1 "src_operand" "%0,rR,rS<>")
                (match_operand:HI 2 "src_operand" "rm,R,rS<>")))
   (clobber (reg:CC 21))]
  "valid_operands (IOR, operands, HImode)"
  "#"
  [(set_attr "type" "multi,multi,multi")])

(define_split
 [(set (match_operand:HI 0 "reg_operand" "")
       (ior:HI (match_operand:HI 1 "src_operand" "")
               (match_operand:HI 2 "src_operand" "")))
  (clobber (reg:CC 21))]
 "reload_completed"
  [(parallel [(set (match_dup 3) (ior:QI (match_dup 4) (match_dup 5)))
              (clobber (reg:CC 21))])
   (parallel [(set (match_dup 6) (ior:QI (match_dup 7) (match_dup 8)))
              (clobber (reg:CC 21))])]
  "operands[3] = c4x_operand_subword (operands[0], 0, 1, HImode);
   operands[4] = c4x_operand_subword (operands[1], 0, 1, HImode);
   operands[5] = c4x_operand_subword (operands[2], 0, 1, HImode);
   operands[6] = c4x_operand_subword (operands[0], 1, 1, HImode);
   operands[7] = c4x_operand_subword (operands[1], 1, 1, HImode);
   operands[8] = c4x_operand_subword (operands[2], 1, 1, HImode);")

(define_expand "andhi3"
  [(parallel [(set (match_operand:HI 0 "reg_operand" "")
                   (and:HI (match_operand:HI 1 "src_operand" "")
                           (match_operand:HI 2 "src_operand" "")))
              (clobber (reg:CC 21))])]
  ""
  "legitimize_operands (AND, operands, HImode);")

(define_insn "*andhi3_clobber"
  [(set (match_operand:HI 0 "reg_operand" "=d,d,?d")
        (and:HI (match_operand:HI 1 "src_operand" "%0,rR,rS<>")
                (match_operand:HI 2 "src_operand" "rm,R,rS<>")))
   (clobber (reg:CC 21))]
  "valid_operands (AND, operands, HImode)"
  "#"
  [(set_attr "type" "multi,multi,multi")])

(define_split
 [(set (match_operand:HI 0 "reg_operand" "")
       (and:HI (match_operand:HI 1 "src_operand" "")
                (match_operand:HI 2 "src_operand" "")))
  (clobber (reg:CC 21))]
 "reload_completed"
  [(parallel [(set (match_dup 3) (and:QI (match_dup 4) (match_dup 5)))
              (clobber (reg:CC 21))])
   (parallel [(set (match_dup 6) (and:QI (match_dup 7) (match_dup 8)))
              (clobber (reg:CC 21))])]
  "operands[3] = c4x_operand_subword (operands[0], 0, 1, HImode);
   operands[4] = c4x_operand_subword (operands[1], 0, 1, HImode);
   operands[5] = c4x_operand_subword (operands[2], 0, 1, HImode);
   operands[6] = c4x_operand_subword (operands[0], 1, 1, HImode);
   operands[7] = c4x_operand_subword (operands[1], 1, 1, HImode);
   operands[8] = c4x_operand_subword (operands[2], 1, 1, HImode);")

(define_expand "xorhi3"
  [(parallel [(set (match_operand:HI 0 "reg_operand" "")
                   (xor:HI (match_operand:HI 1 "src_operand" "")
                           (match_operand:HI 2 "src_operand" "")))
              (clobber (reg:CC 21))])]
  ""
  "legitimize_operands (XOR, operands, HImode);")


(define_insn "*xorhi3_clobber"
  [(set (match_operand:HI 0 "reg_operand" "=d,d,?d")
        (xor:HI (match_operand:HI 1 "src_operand" "%0,rR,rS<>")
                (match_operand:HI 2 "src_operand" "rm,R,rS<>")))
   (clobber (reg:CC 21))]
  "valid_operands (XOR, operands, HImode)"
  "#"
  [(set_attr "type" "multi,multi,multi")])

(define_split
 [(set (match_operand:HI 0 "reg_operand" "")
       (xor:HI (match_operand:HI 1 "src_operand" "")
               (match_operand:HI 2 "src_operand" "")))
  (clobber (reg:CC 21))]
 "reload_completed"
  [(parallel [(set (match_dup 3) (xor:QI (match_dup 4) (match_dup 5)))
              (clobber (reg:CC 21))])
   (parallel [(set (match_dup 6) (xor:QI (match_dup 7) (match_dup 8)))
              (clobber (reg:CC 21))])]
  "operands[3] = c4x_operand_subword (operands[0], 0, 1, HImode);
   operands[4] = c4x_operand_subword (operands[1], 0, 1, HImode);
   operands[5] = c4x_operand_subword (operands[2], 0, 1, HImode);
   operands[6] = c4x_operand_subword (operands[0], 1, 1, HImode);
   operands[7] = c4x_operand_subword (operands[1], 1, 1, HImode);
   operands[8] = c4x_operand_subword (operands[2], 1, 1, HImode);")

(define_expand "ashlhi3"
 [(parallel [(set (match_operand:HI 0 "reg_operand" "")
             (ashift:HI (match_operand:HI 1 "src_operand" "")
                        (match_operand:QI 2 "src_operand" "")))
             (clobber (reg:CC 21))])]
 ""
 "if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) >= 32)
    {
       rtx op0hi = operand_subword (operands[0], 1, 0, HImode);
       rtx op0lo = operand_subword (operands[0], 0, 0, HImode);
       rtx op1lo = operand_subword (operands[1], 0, 0, HImode);
       rtx count = GEN_INT ((INTVAL (operands[2]) - 32));

       if (INTVAL (count))
         emit_insn (gen_ashlqi3 (op0hi, op1lo, count));
       else
         emit_insn (gen_movqi (op0hi, op1lo));
       emit_insn (gen_movqi (op0lo, const0_rtx));
       DONE;
    }
  if (! REG_P (operands[1]))
    operands[1] = force_reg (HImode, operands[1]);
  emit_insn (gen_ashlhi3_reg (operands[0], operands[1], operands[2]));
  DONE;
 ")

; %0.lo = %1.lo << %2
; %0.hi = (%1.hi << %2 ) | (%1.lo >> (32 - %2))
; This algorithm should work for shift counts greater than 32
(define_expand "ashlhi3_reg" 
 [(use (match_operand:HI 1 "reg_operand" ""))
  (use (match_operand:HI 0 "reg_operand" ""))
  /* If the shift count is greater than 32 this will give zero.  */
  (parallel [(set (match_dup 7)
                  (ashift:QI (match_dup 3)
                             (match_operand:QI 2 "reg_operand" "")))
             (clobber (reg:CC 21))])
  /* If the shift count is greater than 32 this will give zero.  */
  (parallel [(set (match_dup 8)
                  (ashift:QI (match_dup 4) (match_dup 2)))
             (clobber (reg:CC 21))])
  (parallel [(set (match_dup 10)
                  (plus:QI (match_dup 2) (const_int -32)))
             (clobber (reg:CC_NOOV 21))])
  /* If the shift count is greater than 32 this will do a left shift.  */
  (parallel [(set (match_dup 9)
                  (lshiftrt:QI (match_dup 3) (neg:QI (match_dup 10))))
             (clobber (reg:CC 21))])
  (set (match_dup 5) (match_dup 7))
  (parallel [(set (match_dup 6)
                  (ior:QI (match_dup 8) (match_dup 9)))
             (clobber (reg:CC 21))])]
 ""
 " 
  operands[3] = operand_subword (operands[1], 0, 1, HImode); /* lo */
  operands[4] = operand_subword (operands[1], 1, 1, HImode); /* hi */
  operands[5] = operand_subword (operands[0], 0, 1, HImode); /* lo */
  operands[6] = operand_subword (operands[0], 1, 1, HImode); /* hi */
  operands[7] = gen_reg_rtx (QImode); /* lo << count */
  operands[8] = gen_reg_rtx (QImode); /* hi << count */
  operands[9] = gen_reg_rtx (QImode); /* lo >> (32 - count) */
  operands[10] = gen_reg_rtx (QImode); /* 32 - count */
 ")

; This should do all the dirty work with define_split
(define_expand "lshrhi3"
 [(parallel [(set (match_operand:HI 0 "reg_operand" "")
             (lshiftrt:HI (match_operand:HI 1 "src_operand" "")
                          (match_operand:QI 2 "src_operand" "")))
             (clobber (reg:CC 21))])]
 ""
 "if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) >= 32)
    {
       rtx op0hi = operand_subword (operands[0], 1, 0, HImode);
       rtx op0lo = operand_subword (operands[0], 0, 0, HImode);
       rtx op1hi = operand_subword (operands[1], 1, 0, HImode);
       rtx count = GEN_INT ((INTVAL (operands[2]) - 32));

       if (INTVAL (count))
         emit_insn (gen_lshrqi3 (op0lo, op1hi, count));
       else
         emit_insn (gen_movqi (op0lo, op1hi));
       emit_insn (gen_movqi (op0hi, const0_rtx));
       DONE;
    }
  if (! REG_P (operands[1]))
    operands[1] = force_reg (HImode, operands[1]);
  emit_insn (gen_lshrhi3_reg (operands[0], operands[1], operands[2]));
  DONE;")

; %0.hi = %1.hi >> %2
; %0.lo = (%1.lo >> %2 ) | (%1.hi << (32 - %2))
; This algorithm should work for shift counts greater than 32
(define_expand "lshrhi3_reg" 
 [(use (match_operand:HI 1 "reg_operand" ""))
  (use (match_operand:HI 0 "reg_operand" ""))
  (parallel [(set (match_dup 11)
                  (neg:QI (match_operand:QI 2 "reg_operand" "")))
             (clobber (reg:CC_NOOV 21))])
  /* If the shift count is greater than 32 this will give zero.  */
  (parallel [(set (match_dup 7)
                  (lshiftrt:QI (match_dup 3)
                               (neg:QI (match_dup 11))))
             (clobber (reg:CC 21))])
  /* If the shift count is greater than 32 this will give zero.  */
  (parallel [(set (match_dup 8)
                  (lshiftrt:QI (match_dup 4) 
                               (neg:QI (match_dup 11))))
             (clobber (reg:CC 21))])
  (parallel [(set (match_dup 10)
                  (plus:QI (match_dup 11) (const_int 32)))
             (clobber (reg:CC_NOOV 21))])
  /* If the shift count is greater than 32 this will do an arithmetic
     right shift.  However, we need a logical right shift.  */
  (parallel [(set (match_dup 9)
                  (ashift:QI (match_dup 4) (unspec:QI [(match_dup 10)] 3)))
             (clobber (reg:CC 21))])
  (set (match_dup 6) (match_dup 8))
  (parallel [(set (match_dup 5)
                  (ior:QI (match_dup 7) (match_dup 9)))
             (clobber (reg:CC 21))])]
 ""
 " 
  operands[3] = operand_subword (operands[1], 0, 1, HImode); /* lo */
  operands[4] = operand_subword (operands[1], 1, 1, HImode); /* hi */
  operands[5] = operand_subword (operands[0], 0, 1, HImode); /* lo */
  operands[6] = operand_subword (operands[0], 1, 1, HImode); /* hi */
  operands[7] = gen_reg_rtx (QImode); /* lo >> count */
  operands[8] = gen_reg_rtx (QImode); /* hi >> count */
  operands[9] = gen_reg_rtx (QImode); /* hi << (32 - count) */
  operands[10] = gen_reg_rtx (QImode); /* 32 - count */
  operands[11] = gen_reg_rtx (QImode); /* -count */
 ")

; This should do all the dirty work with define_split
(define_expand "ashrhi3"
  [(parallel [(set (match_operand:HI 0 "reg_operand" "")
              (ashiftrt:HI (match_operand:HI 1 "src_operand" "")
                           (match_operand:QI 2 "src_operand" "")))
              (clobber (reg:CC 21))])]
 ""
 "if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) >= 32)
    {
       rtx op0hi = operand_subword (operands[0], 1, 0, HImode);
       rtx op0lo = operand_subword (operands[0], 0, 0, HImode);
       rtx op1hi = operand_subword (operands[1], 1, 0, HImode);
       rtx count = GEN_INT ((INTVAL (operands[2]) - 32));

       if (INTVAL (count))
         emit_insn (gen_ashrqi3 (op0lo, op1hi, count));
       else
         emit_insn (gen_movqi (op0lo, op1hi));
       emit_insn (gen_ashrqi3 (op0hi, op1hi, GEN_INT (31)));
       DONE;
    }
  if (! REG_P (operands[1]))
    operands[1] = force_reg (HImode, operands[1]);
  emit_insn (gen_ashrhi3_reg (operands[0], operands[1], operands[2]));
  DONE;")

; %0.hi = %1.hi >> %2
; %0.lo = (%1.lo >> %2 ) | (%1.hi << (32 - %2))
; This algorithm should work for shift counts greater than 32
(define_expand "ashrhi3_reg" 
 [(use (match_operand:HI 1 "reg_operand" ""))
  (use (match_operand:HI 0 "reg_operand" ""))
  (parallel [(set (match_dup 11)
                  (neg:QI (match_operand:QI 2 "reg_operand" "")))
             (clobber (reg:CC_NOOV 21))])
  /* If the shift count is greater than 32 this will give zero.  */
  (parallel [(set (match_dup 7)
                  (lshiftrt:QI (match_dup 3)
                               (neg:QI (match_dup 11))))
             (clobber (reg:CC 21))])
  /* If the shift count is greater than 32 this will give zero.  */
  (parallel [(set (match_dup 8)
                  (ashiftrt:QI (match_dup 4) 
                               (neg:QI (match_dup 11))))
             (clobber (reg:CC 21))])
  (parallel [(set (match_dup 10)
                  (plus:QI (match_dup 11) (const_int 32)))
             (clobber (reg:CC_NOOV 21))])
  /* If the shift count is greater than 32 this will do an arithmetic
     right shift.  */
  (parallel [(set (match_dup 9)
                  (ashift:QI (match_dup 4) (match_dup 10)))
             (clobber (reg:CC 21))])
  (set (match_dup 6) (match_dup 8))
  (parallel [(set (match_dup 5)
                  (ior:QI (match_dup 7) (match_dup 9)))
             (clobber (reg:CC 21))])]
 ""
 " 
  operands[3] = operand_subword (operands[1], 0, 1, HImode); /* lo */
  operands[4] = operand_subword (operands[1], 1, 1, HImode); /* hi */
  operands[5] = operand_subword (operands[0], 0, 1, HImode); /* lo */
  operands[6] = operand_subword (operands[0], 1, 1, HImode); /* hi */
  operands[7] = gen_reg_rtx (QImode); /* lo >> count */
  operands[8] = gen_reg_rtx (QImode); /* hi >> count */
  operands[9] = gen_reg_rtx (QImode); /* hi << (32 - count) */
  operands[10] = gen_reg_rtx (QImode); /* 32 - count */
  operands[11] = gen_reg_rtx (QImode); /* -count */
 ")

(define_expand "cmphi"
  [(set (reg:CC 21)
        (compare:CC (match_operand:HI 0 "src_operand" "")
                    (match_operand:HI 1 "src_operand" "")))]
  ""
  "legitimize_operands (COMPARE, operands, HImode);
   c4x_compare_op0 = operands[0];
   c4x_compare_op1 = operands[1];
   DONE;")

(define_insn "*cmphi_cc"
  [(set (reg:CC 21)
        (compare:CC (match_operand:HI 0 "src_operand" "rR,rS<>")
                    (match_operand:HI 1 "src_operand" "R,rS<>")))]
  "valid_operands (COMPARE, operands, HImode)"
  "#"
  [(set_attr "type" "multi")])

(define_insn "*cmphi_cc_noov"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (match_operand:HI 0 "src_operand" "rR,rS<>")
                         (match_operand:HI 1 "src_operand" "R,rS<>")))]
  "valid_operands (COMPARE, operands, HImode)"
  "#"
  [(set_attr "type" "multi")])

; This works only before reload because we need 2 extra registers.
; Use unspec to avoid recursive split.
(define_split
  [(set (reg:CC 21)
        (compare:CC (match_operand:HI 0 "src_operand" "")
                    (match_operand:HI 1 "src_operand" "")))]
  "! reload_completed"
  [(parallel [(set (reg:CC 21)
                   (unspec:CC [(compare:CC (match_dup 0)
                                           (match_dup 1))] 4))
              (clobber (match_scratch:QI 2 ""))
	      (clobber (match_scratch:QI 3 ""))])]
  "")

(define_split
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (match_operand:HI 0 "src_operand" "")
                         (match_operand:HI 1 "src_operand" "")))]
  "! reload_completed"
  [(parallel [(set (reg:CC_NOOV 21)
                   (unspec:CC_NOOV [(compare:CC_NOOV (match_dup 0)
                                                     (match_dup 1))] 4))
              (clobber (match_scratch:QI 2 ""))
	      (clobber (match_scratch:QI 3 ""))])]
  "")

; This is normally not used. The define splits above are used first.
(define_split
  [(set (reg:CC 21)
        (compare:CC (match_operand:HI 0 "src_operand" "")
                    (match_operand:HI 1 "src_operand" "")))]
  "reload_completed"
  [(parallel [(set (reg:CC 21)
                   (compare:CC (match_dup 0) (match_dup 1)))
              (use (reg:QI 20))])]
  "")

(define_split
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (match_operand:HI 0 "src_operand" "")
                         (match_operand:HI 1 "src_operand" "")))]
  "reload_completed"
  [(parallel [(set (reg:CC_NOOV 21)
                   (compare:CC_NOOV (match_dup 0) (match_dup 1)))
              (use (reg:QI 20))])]
  "")

(define_insn "*cmphi"
  [(set (reg:CC 21)
        (compare:CC (match_operand:HI 0 "src_operand" "rR,rS<>")
                    (match_operand:HI 1 "src_operand" "R,rS<>")))
   (use (reg:QI 20))]
  "valid_operands (COMPARE, operands, HImode)"
  "*
   {
     int use_ir1 = (reg_operand (operands[0], HImode)
	            && REG_P (operands[0])
		    && REGNO (operands[0]) == IR1_REGNO)
		    || (reg_operand (operands[1], HImode)
		        && REG_P (operands[1])
		        && REGNO (operands[1]) == IR1_REGNO);

     if (use_ir1)
       output_asm_insn (\"push\\tir1\", operands);
     else
       output_asm_insn (\"push\\tbk\", operands);
     output_asm_insn (\"push\\tr0\", operands);
     output_asm_insn (\"subi3\\t%1,%0,r0\", operands);
     if (use_ir1)
       {
         output_asm_insn (\"ldiu\\tst,ir1\", operands);
         output_asm_insn (\"or\\t07bh,ir1\", operands);
       }
     else
       {
         output_asm_insn (\"ldiu\\tst,bk\", operands);
         output_asm_insn (\"or\\t07bh,bk\", operands);
       }
     output_asm_insn (\"subb3\\t%O1,%O0,r0\", operands);
     if (use_ir1)
       output_asm_insn (\"and3\\tir1,st,ir1\", operands);
     else
       output_asm_insn (\"and3\\tbk,st,bk\", operands);
     output_asm_insn (\"pop\\tr0\", operands);
     if (use_ir1)
       {
         output_asm_insn (\"ldiu\\tir1,st\", operands);
         output_asm_insn (\"pop\\tir1\", operands);
       }
     else
       {
         output_asm_insn (\"ldiu\\tbk,st\", operands);
         output_asm_insn (\"pop\\tbk\", operands);
       }
     return \"\";
   }"
  [(set_attr "type" "multi")])
 
(define_insn "*cmphi_noov"
  [(set (reg:CC_NOOV 21)
        (compare:CC_NOOV (match_operand:HI 0 "src_operand" "rR,rS<>")
                         (match_operand:HI 1 "src_operand" "R,rS<>")))
   (use (reg:QI 20))]
  "valid_operands (COMPARE, operands, HImode)"
  "*
   {
     int use_ir1 = (reg_operand (operands[0], HImode)
	            && REG_P (operands[0])
		    && REGNO (operands[0]) == IR1_REGNO)
		    || (reg_operand (operands[1], HImode)
		        && REG_P (operands[1])
		        && REGNO (operands[1]) == IR1_REGNO);

     if (use_ir1)
       output_asm_insn (\"push\\tir1\", operands);
     else
       output_asm_insn (\"push\\tbk\", operands);
     output_asm_insn (\"push\\tr0\", operands);
     output_asm_insn (\"subi3\\t%1,%0,r0\", operands);
     if (use_ir1)
       {
         output_asm_insn (\"ldiu\\tst,ir1\", operands);
         output_asm_insn (\"or\\t07bh,ir1\", operands);
       }
     else
       {
         output_asm_insn (\"ldiu\\tst,bk\", operands);
         output_asm_insn (\"or\\t07bh,bk\", operands);
       }
     output_asm_insn (\"subb3\\t%O1,%O0,r0\", operands);
     if (use_ir1)
       output_asm_insn (\"and3\\tir1,st,ir1\", operands);
     else
       output_asm_insn (\"and3\\tbk,st,bk\", operands);
     output_asm_insn (\"pop\\tr0\", operands);
     if (use_ir1)
       {
         output_asm_insn (\"ldiu\\tir1,st\", operands);
         output_asm_insn (\"pop\\tir1\", operands);
       }
     else
       {
         output_asm_insn (\"ldiu\\tbk,st\", operands);
         output_asm_insn (\"pop\\tbk\", operands);
       }
     return \"\";
   }"
  [(set_attr "type" "multi")])

 
(define_insn "cmphi_cc"
  [(set (reg:CC 21)
        (unspec:CC [(compare:CC (match_operand:HI 0 "src_operand" "rR,rS<>")
                                (match_operand:HI 1 "src_operand" "R,rS<>"))] 4))
   (clobber (match_scratch:QI 2 "=&d,&d"))
   (clobber (match_scratch:QI 3 "=&c,&c"))]
  "valid_operands (COMPARE, operands, HImode)"
  "*
   output_asm_insn (\"subi3\\t%1,%0,%2\", operands);
   output_asm_insn (\"ldiu\\tst,%3\", operands);
   output_asm_insn (\"or\\t07bh,%3\", operands);
   output_asm_insn (\"subb3\\t%O1,%O0,%2\", operands);
   output_asm_insn (\"and\\t%3,st\", operands);
   return \"\";"
  [(set_attr "type" "multi")])

(define_insn "cmphi_cc_noov"
  [(set (reg:CC_NOOV 21)
        (unspec:CC_NOOV [(compare:CC_NOOV (match_operand:HI 0 "src_operand" "rR,rS<>")
                                     (match_operand:HI 1 "src_operand" "R,rS<>"))] 4))
   (clobber (match_scratch:QI 2 "=&d,&d"))
   (clobber (match_scratch:QI 3 "=&c,&c"))]
  "valid_operands (COMPARE, operands, HImode)"
  "*
   output_asm_insn (\"subi3\\t%1,%0,%2\", operands);
   output_asm_insn (\"ldiu\\tst,%3\", operands);
   output_asm_insn (\"or\\t07bh,%3\", operands);
   output_asm_insn (\"subb3\\t%O1,%O0,%2\", operands);
   output_asm_insn (\"and\\t%3,st\", operands);
   return \"\";"
  [(set_attr "type" "multi")])

(define_expand "mulhi3"
  [(parallel [(set (match_operand:HI 0 "reg_operand" "")
                   (mult:HI (match_operand:HI 1 "src_operand" "")
                            (match_operand:HI 2 "src_operand" "")))
              (clobber (reg:CC 21))])]
  ""
  "c4x_emit_libcall3 (smul_optab->handlers[(int) HImode].libfunc,
		      MULT, HImode, operands);
   DONE;")


;
; PEEPHOLES
;

; dbCC peepholes
;
; Turns
;   loop:
;           [ ... ]
;           bCC label           ; abnormal loop termination
;           dbu aN, loop        ; normal loop termination
;
; Into
;   loop:
;           [ ... ]
;           dbCC aN, loop
;           bCC label
;
; Which moves the bCC condition outside the inner loop for free.
;
(define_peephole
  [(set (pc) (if_then_else (match_operator 3 "comparison_operator"
                           [(reg:CC 21) (const_int 0)])
                           (label_ref (match_operand 2 "" ""))
                           (pc)))
   (parallel
    [(set (pc)
          (if_then_else
            (ge (plus:QI (match_operand:QI 0 "addr_reg_operand" "+a")
                         (const_int -1))
                (const_int 0))
            (label_ref (match_operand 1 "" ""))
            (pc)))
     (set (match_dup 0)
          (plus:QI (match_dup 0)
                   (const_int -1)))
     (use (reg:QI 20))
     (clobber (reg:CC_NOOV 21))])]
  "! c4x_label_conflict (insn, operands[2], operands[1])"
  "db%I3\\t%0,%l1\\n\\tb%3\\t%l2"
  [(set_attr "type" "multi")])

(define_peephole
  [(set (pc) (if_then_else (match_operator 3 "comparison_operator"
                           [(reg:CC 21) (const_int 0)])
                           (label_ref (match_operand 2 "" ""))
                           (pc)))
   (parallel
    [(set (pc)
          (if_then_else
            (ne (match_operand:QI 0 "addr_reg_operand" "+a")
                (const_int 0))
            (label_ref (match_operand 1 "" ""))
            (pc)))
     (set (match_dup 0)
          (plus:QI (match_dup 0)
                   (const_int -1)))])]
  "! c4x_label_conflict (insn, operands[2], operands[1])"
  "db%I3\\t%0,%l1\\n\\tb%3\\t%l2"
  [(set_attr "type" "multi")])

;
; Peepholes to convert 'call label; rets' into jump label
;

(define_peephole
  [(parallel [(call (mem:QI (match_operand:QI 0 "call_address_operand" ""))
                    (match_operand:QI 1 "general_operand" ""))
              (clobber (reg:QI 31))])
   (return)]
  "c4x_null_epilogue_p ()"
  "*
   if (REG_P (operands[0]))
     return \"bu%#\\t%C0\";
   else
     return \"br%#\\t%C0\";"
  [(set_attr "type" "jump")])

(define_peephole
  [(parallel [(set (match_operand 0 "" "")
                   (call (mem:QI (match_operand:QI 1 "call_address_operand" ""))
                         (match_operand:QI 2 "general_operand" "")))
              (clobber (reg:QI 31))])
   (return)]
  "c4x_null_epilogue_p ()"
  "*
   if (REG_P (operands[1]))
     return \"bu%#\\t%C1\";
   else
     return \"br%#\\t%C1\";"
  [(set_attr "type" "jump")])


; This peephole should be unnecessary with my patches to flow.c
; for better autoincrement detection
(define_peephole
 [(set (match_operand:QF 0 "ext_low_reg_operand" "")
       (mem:QF (match_operand:QI 1 "addr_reg_operand" "")))
  (set (match_operand:QF 2 "ext_low_reg_operand" "")
       (mem:QF (plus:QI (match_dup 1) (const_int 1))))
  (parallel [(set (match_dup 1) (plus:QI (match_dup 1) (const_int 2)))
             (clobber (reg:CC_NOOV 21))])]
 ""
 "ldf\\t*%1++,%0\\n\\tldf\\t*%1++,%2")


; This peephole should be unnecessary with my patches to flow.c
; for better autoincrement detection
(define_peephole
 [(set (mem:QF (match_operand:QI 0 "addr_reg_operand" ""))
       (match_operand:QF 1 "ext_low_reg_operand" ""))
  (set (mem:QF (plus:QI (match_dup 0) (const_int 1)))
       (match_operand:QF 2 "ext_low_reg_operand" ""))
  (parallel [(set (match_dup 0) (plus:QI (match_dup 0) (const_int 2)))
             (clobber (reg:CC_NOOV 21))])]
 ""
 "stf\\t%1,*%0++\\n\\tstf\\t%2,*%0++")


; The following two peepholes remove an unecessary load
; often found at the end of a function.  These peepholes
; could be generalised to other binary operators.  They shouldn't
; be required if we run a post reload mop-up pass.
(define_peephole
 [(parallel [(set (match_operand:QF 0 "ext_reg_operand" "")
                  (plus:QF (match_operand:QF 1 "ext_reg_operand" "")
                           (match_operand:QF 2 "ext_reg_operand" "")))
             (clobber (reg:CC_NOOV 21))])
  (set (match_operand:QF 3 "ext_reg_operand" "")
       (match_dup 0))]
 "dead_or_set_p (insn, operands[0])"
 "addf3\\t%2,%1,%3")

(define_peephole
 [(parallel [(set (match_operand:QI 0 "reg_operand" "")
                  (plus:QI (match_operand:QI 1 "reg_operand" "")
                           (match_operand:QI 2 "reg_operand" "")))
             (clobber (reg:CC_NOOV 21))])
  (set (match_operand:QI 3 "reg_operand" "")
       (match_dup 0))]
 "dead_or_set_p (insn, operands[0])"
 "addi3\\t%2,%1,%3")
