;; -*- Mode: Scheme -*-
;; GCC machine description for Ubicom IP2022 Communications Controller.
;; Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc.
;; Contributed by Red Hat, Inc and Ubicom, Inc.
;;
;; This file is part of GCC.
;;
;; GCC is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;;
;; GCC is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GCC; see the file COPYING.  If not, write to
;; the Free Software Foundation, 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.  */

;; Default all instruction lengths to two bytes (one 16-bit instruction).
;;
(define_attr "length" "" (const_int 2))

;; Define if we can "skip" an insn or not
(define_attr "skip" "no,yes" (const_string "no"))

;; Define an insn clobbers WREG or not
(define_attr "clobberw" "no,yes" (const_string "yes"))

;; Performance Issues:
;;
;; With the IP2k only having one really useful pointer register we have to
;; make most of our instruction patterns only match one offsettable address
;; before addressing becomes strict whereas afterwards of course we can use
;; any register details that have become fixed.  As we've already committed
;; any reloads at this point of course we're a little late so we have to use
;; a number of peephole2 optimizations to remerge viable patterns.  We can
;; do a bit more tidying up in the machine-dependent reorg pass to try and
;; make things better still.  None of this is ideal, but it's *much* better
;; than nothing.

;; Constraints:
;;
;; I - -255..-1 - all other literal values have to be loaded
;; J - 0..7 - valid bit number in a register
;; K - 0..127 - valid offset for addressing mode
;; L - 1..127 - positive count suitable for shift.
;; M - -1 as a literal value
;; N - +1 as a literal value
;; O - zero
;; P - 0..255
;;
;; a - DP or IP registers (general address)
;; f - IP register
;; j - IPL register
;; k - IPH register
;; b - DP register
;; y - DPH register
;; z - DPL register
;; q - SP register
;; c - DP or SP registers (offsettable address)
;; d - non-pointer registers (not SP, DP, IP)
;; u - non-SP registers (everything except SP)
;;
;; R - Indirect thru IP - Avoid this except for QI mode, since we
;;     can't access extra bytes.
;; S - Short (stack/dp address). Pointer with 0..127 displacement
;;     Note that 0(SP) has undefined contents due to post-decrement push
;; T - data-section immediate value.  A CONST_INT or SYMBOL_REF into .data

;; Special assembly-language format effectors:
;;
;; ABCD -
;;     Reference up to 4 big-endian registers - %A0 is Rn+0, while %D0 is Rn+3
;; STUVWXYZ -
;;     Reference up to 8 big-endian registers - %S0 is Rn+0, while %Z0 is Rn+7
;;
;; H - High part of 16 bit address or literal %hi8data(v) or %hi8insn(v)
;; L - Low part of 16 bit address or literal  %lo8data(v) or %lo8insn(v)
;; b - print a literal value with no punctuation (typically bit selector)
;; e - print 1 << v ('exponent')
;; n - print negative number
;; x - print 16 bit hex number
;; < - interior stack push; adjust any stack-relative operands accordingly
;; > - interior stack pop; clear adjustment.

;;
;; Basic operations to move data in and out of fr's.  Also extended to
;; cover the loading of w with immediates
;;

(define_insn "*movqi_w_gen"
  [(set (reg:QI 10)
        (match_operand:QI 0 "general_operand" "rSi"))]
  "(ip2k_reorg_split_qimode)"
  "mov\\tw,%0"
  [(set_attr "skip" "yes")])

(define_insn "*movqi_fr_w"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=rS")
        (reg:QI 10))]
  "(ip2k_reorg_split_qimode)"
  "mov\\t%0,w"
  [(set_attr "skip" "yes")
   (set_attr "clobberw" "no")])


;; Handle the cases where we get back to back redundant mov patterns issued.  
;; This of course sounds somewhat absurd but is actually reasonably common
;; because we aren't able to match certain patterns before registers are
;; chosen.  This is particularly true of memory to memory operations where
;; we can't provide patterns that will guarantee to match every time because
;; this would require reloads in the middle of instructions.  If we
;; discover a case that doesn't need a reload of course then this combiner
;; operation will tidy up for us.
;;
;; Warning!  Whilst it would be nice to match operand 0 as a general operand
;; we mustn't do so because this doesn't work with the REG_DEAD check.
;;
(define_peephole2
  [(set (match_operand 0 "ip2k_nonsp_reg_operand" "")
	(match_operand 1 "ip2k_gen_operand" ""))
   (set (match_operand 2 "ip2k_split_dest_operand" "")
        (match_dup 0))]
  "(peep2_reg_dead_p (2, operands[0])
    && ! (REG_P (operands[2]) && REGNO (operands[2]) == REG_SP)
    && (REG_P (operands[2])
        || ip2k_xexp_not_uses_reg_p (operands[2], REGNO (operands[0]),
                                     GET_MODE_SIZE (GET_MODE (operands[0])))))"
  [(set (match_dup 2)
	(match_dup 1))]
  "")

(define_peephole2
  [(set (match_operand 0 "ip2k_nonsp_reg_operand" "")
	(match_operand 1 "immediate_operand" ""))
   (set (match_operand 2 "ip2k_gen_operand" "")
        (match_dup 0))]
  "(peep2_reg_dead_p (2, operands[0])
    && ! (REG_P (operands[2]) && REGNO (operands[2]) == REG_SP)
    && ip2k_xexp_not_uses_reg_p (operands[2], REGNO (operands[0]),
                                 GET_MODE_SIZE (GET_MODE (operands[0]))))"
  [(set (match_dup 2)
	(match_dup 1))]
  "")

;;
;; Move 8-bit integers.
;;

(define_expand "movqi"
  [(set (match_operand:QI 0 "" "")
	(match_operand:QI 1 "" ""))]
  ""
  "")

(define_insn "*pushqi"
  [(set (match_operand:QI 0 "push_operand"   "=<")
	(match_operand:QI 1 "general_operand" "g"))]
  ""
  "push\\t%1"
  [(set_attr "skip" "yes")
   (set_attr "clobberw" "no")])

;; IP isn't offsettable but we can fake this behavior here and win if we would
;; otherwise use DP and require a reload from IP.  This instruction is only
;; matched by peephole2 operations.
;;
(define_insn "*movqi_to_ip_plus_offs"
  [(set (mem:QI (plus:HI (reg:HI 4)
			 (match_operand 0 "const_int_operand" "P,P")))
	(match_operand:QI 1 "general_operand"                 "O,g"))]
  "reload_completed && (INTVAL (operands[0]) < 0x100)"
  "*{
    if (INTVAL (operands[0]) == 1)
      OUT_AS1 (inc, ipl);
    else
      {
        OUT_AS2 (mov, w, %0);
        OUT_AS2 (add, ipl, w);
      }

    switch (which_alternative)
      {
      case 0:
        OUT_AS1 (clr, (IP));
	break;

      case 1:
        OUT_AS1 (push, %1%<);
        OUT_AS1 (pop, (IP)%>);
	break;
      }

    if (!find_regno_note (insn, REG_DEAD, REG_IP))
      {
        if (INTVAL (operands[0]) == 1)
          OUT_AS1 (dec, ipl);
        else
          OUT_AS2 (sub, ipl, w);
      }
    return \"\";
  }")

;; IP isn't offsettable but we can fake this behavior here and win if we would
;; otherwise use DP and require a reload from IP.  This instruction is only
;; matched by peephole2 operations.
;;
(define_insn "*movqi_from_ip_plus_offs"
  [(set (match_operand:QI 0 "nonimmediate_operand"           "=g")
        (mem:QI (plus:HI (reg:HI 4)
			 (match_operand 1 "const_int_operand" "P"))))]
  "reload_completed && (INTVAL (operands[1]) < 0x100)"
  "*{
    if (INTVAL (operands[1]) == 1)
      OUT_AS1 (inc, ipl);
    else
      {
        OUT_AS2 (mov, w, %1);
        OUT_AS2 (add, ipl, w);
      }
    OUT_AS1 (push, (IP)%<);
    OUT_AS1 (pop, %0%>);
    if (!find_regno_note (insn, REG_DEAD, REG_IP)
        && ip2k_xexp_not_uses_reg_p (operands[0], REG_IP, 2))
      {
        if (INTVAL (operands[1]) == 1)
          OUT_AS1 (dec, ipl);
        else
          OUT_AS2 (sub, ipl, w);
      }
    return \"\";
  }")

(define_insn_and_split "*movqi"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=roR,roR,r,  rS,roR")
	(match_operand:QI 1 "general_operand"       "  O, ri,o,rioR,rSi"))]
  ""
  "@
   clr\\t%0
   #
   #
   #
   #"
  "(ip2k_reorg_split_qimode
    && (GET_CODE (operands[1]) != CONST_INT
        || INTVAL (operands[1]) != 0))"
  [(set (reg:QI 10) (match_dup 1))
   (set (match_dup 0) (reg:QI 10))]
  ""
  [(set_attr "skip" "yes,no,no,no,no")
   (set_attr "clobberw" "no,yes,yes,yes,yes")])

(define_peephole2
  [(set (reg:HI 12)
  	(reg:HI 4))
   (set (match_operand:QI 0 "nonimmediate_operand" "")
   	(mem:QI (plus:HI (reg:HI 12)
		         (match_operand 1 "const_int_operand" ""))))]
  "((ip2k_reorg_in_progress || ip2k_reorg_completed)
    && peep2_regno_dead_p (2, REG_DP)
    && ip2k_xexp_not_uses_reg_p (operands[0], REG_DP, 2)
    && (INTVAL (operands[1]) < 0x100))"
  [(set (match_dup 0)
	(mem:QI (plus:HI (reg:HI 4)
			 (match_dup 1))))]
  "")

(define_peephole2
  [(set (reg:HI 12)
  	(reg:HI 4))
   (set (mem:QI (plus:HI (reg:HI 12)
		         (match_operand 0 "const_int_operand" "")))
	(match_operand:QI 1 "general_operand" ""))]
  "((ip2k_reorg_in_progress || ip2k_reorg_completed)
    && peep2_regno_dead_p (2, REG_DP)
    && ip2k_xexp_not_uses_reg_p (operands[0], REG_DP, 2)
    && (INTVAL (operands[0]) < 0x100))"
  [(set (mem:QI (plus:HI (reg:HI 4)
			 (match_dup 0)))
	(match_dup 1))]
  "")

(define_peephole2
  [(set (match_operand:QI 0 "register_operand" "")
   	(mem:QI (plus:HI (reg:HI 4)
			 (match_operand 1 "const_int_operand" ""))))
   (set (match_operand:QI 2 "nonimmediate_operand" "")
        (match_dup 0))]
  "((ip2k_reorg_in_progress || ip2k_reorg_completed)
    && peep2_reg_dead_p (2, operands[0]))"
  [(set (match_dup 2)
	(mem:QI (plus:HI (reg:HI 4)
			 (match_dup 1))))]
  "")

;; We sometimes want to copy a value twice, usually when we copy a value into
;; both a structure slot and into a temporary register.  We can win here
;; because gcc doesn't know about ways of reusing w while we're copying.
;;
(define_insn_and_split "*movqi_twice"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=g")
        (match_operand:QI 1 "general_operand"       "g"))
   (set (match_operand:QI 2 "nonimmediate_operand" "=g")
        (match_dup 1))]
  "ip2k_reorg_merge_qimode"
  "mov\\tw,%1\;mov\\t%0,w\;mov\\t%2,w"
  "(ip2k_reorg_split_qimode)"
  [(set (reg:QI 10) (match_dup 1))
   (set (match_dup 0) (reg:QI 10))
   (set (match_dup 2) (reg:QI 10))]
  "")

;; Don't try to match until we've removed redundant reloads.  Often this
;; simplification will remove the need to do two moves!
;;
(define_peephole2
  [(set (match_operand:QI 0 "nonimmediate_operand" "")
        (match_operand:QI 1 "general_operand" ""))
   (set (match_operand:QI 2 "nonimmediate_operand" "")
        (match_dup 0))]
  "(ip2k_reorg_merge_qimode
    && (GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) != 0))"
  [(parallel [(set (match_dup 0)
  		   (match_dup 1))
	      (set (match_dup 2)
	      	   (match_dup 1))])]
  "")

;; Don't try to match until we've removed redundant reloads.  Often this
;; simplification will remove the need to do two moves!
;;
(define_peephole2
  [(set (match_operand:QI 0 "nonimmediate_operand" "")
        (match_operand:QI 1 "general_operand" ""))
   (set (match_operand:QI 2 "nonimmediate_operand" "")
        (match_dup 1))]
  "(ip2k_reorg_merge_qimode
    && (GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) != 0))"
  [(parallel [(set (match_dup 0)
  		   (match_dup 1))
	      (set (match_dup 2)
	      	   (match_dup 1))])]
  "")

;;
;; Move 16-bit integers.
;;

(define_expand "movhi"
  [(set (match_operand:HI 0 "" "")
	(match_operand:HI 1 "" ""))]
  ""
  "")

(define_insn "*pushhi_ip"
  [(set (match_operand:HI 0 "push_operand"      "=<")
	(mem:HI (reg:HI 4)))]
  "reload_completed"
  "inc\\tipl\;push\\t(IP)\;dec\\tipl\;push\\t(IP)"
  [(set_attr "clobberw" "no")])

(define_insn "*movhi_to_ip"
  [(set (mem:HI (reg:HI 4))
  	(match_operand:HI 0 "general_operand" "O,roi"))]
  "reload_completed"
  "*{
    switch (which_alternative)
      {
      case 0:
        OUT_AS1 (clr, (IP));
	OUT_AS1 (inc, ipl);
	OUT_AS1 (clr, (IP));
        if (!find_regno_note (insn, REG_DEAD, REG_IP))
	  OUT_AS1 (dec, ipl);
	return \"\";

      case 1:
        OUT_AS2 (mov, w, %H0);
	OUT_AS2 (mov, (IP), w);
	OUT_AS2 (mov, w, %L0);
	OUT_AS1 (inc, ipl);
	OUT_AS2 (mov, (IP), w);
        if (!find_regno_note (insn, REG_DEAD, REG_IP))
	  OUT_AS1 (dec, ipl);
	return \"\";
      default:
        abort ();
      }
  }")

(define_insn "*movhi_from_ip"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=f,bqdo")
        (mem:HI (reg:HI 4)))]
  "reload_completed"
  "*{
    switch (which_alternative)
      {
      case 0:
        OUT_AS1 (push, (IP));
	OUT_AS1 (inc, ipl);
	OUT_AS2 (mov, w, (IP));
	OUT_AS2 (mov, ipl, w);
	OUT_AS1 (pop, iph);
	return \"\";

      case 1:
        OUT_AS2 (mov, w, (IP));
	OUT_AS2 (mov, %H0, w);
	OUT_AS1 (inc, ipl);
	OUT_AS2 (mov, w, (IP));
	OUT_AS2 (mov, %L0, w);
        if (!find_regno_note (insn, REG_DEAD, REG_IP))
	  OUT_AS1 (dec, ipl);
	return \"\";
      default:
        abort ();
      }
  }")

(define_insn "*movhi_from_ip_plus_offs"
  [(set (match_operand:HI 0 "nonimmediate_operand"           "=f,bqdo")
        (mem:HI (plus:HI (reg:HI 4)
			 (match_operand 1 "const_int_operand" "P,   P"))))]
  "reload_completed && (INTVAL (operands[1]) < 0x100)"
  "*{
    switch (which_alternative)
      {
      case 0:
        OUT_AS2 (mov, w, %1);
        OUT_AS2 (add, ipl, w);
        OUT_AS1 (push, (IP));
        OUT_AS1 (inc, ipl);
        OUT_AS2 (mov, w, (IP));
        OUT_AS2 (mov, ipl, w);
        OUT_AS1 (pop, iph);
        return \"\";

      case 1:
        if (INTVAL (operands[1]) == 1)
          OUT_AS1 (inc, ipl);
        else
          {
            OUT_AS2 (mov, w, %1);
            OUT_AS2 (add, ipl, w);
          }
        OUT_AS1 (push, (IP)%<);
	OUT_AS1 (pop, %H0%>);
        OUT_AS1 (inc, ipl);
        OUT_AS1 (push, (IP)%<);
        OUT_AS1 (pop, %L0%>);
        if (!find_regno_note (insn, REG_DEAD, REG_IP))
	  {
	    OUT_AS1 (dec, ipl);
            if (INTVAL (operands[1]) == 1)
              OUT_AS1 (dec, ipl);
            else
              OUT_AS2 (sub, ipl, w);
	  }
        return \"\";
      default:
        abort ();
      }
  }")

(define_insn_and_split "*movhi"
  [(set
    (match_operand:HI 0 "ip2k_split_dest_operand" "=<,<,uo,b, uS,uo,uo, q,u")
    (match_operand:HI 1 "general_operand"        "ron,i, n,T,uoi,uS,ui,ui,q"))]
  ""
  "@
   push\\t%L1%<\;push\\t%H1%>
   push\\t%L1%<\;push\\t%H1%>
   mov\\tw,%H1\;mov\\t%H0,w\;mov\\tw,%L1\;mov\\t%L0,w
   loadl\\t%x1\;loadh\\t%x1
   mov\\tw,%H1\;push\\t%L1%<\;pop\\t%L0%>\;mov\\t%H0,w
   mov\\tw,%H1\;push\\t%L1%<\;pop\\t%L0%>\;mov\\t%H0,w
   mov\\tw,%H1\;push\\t%L1%<\;pop\\t%L0%>\;mov\\t%H0,w
   mov\\tw,%H1\;mov\\t%H0,w\;mov\\tw,%L1\;mov\\t%L0,w
   mov\\tw,%H1\;mov\\t%H0,w\;mov\\tw,%L1\;mov\\t%L0,w"
  "(ip2k_reorg_split_himode
    && (GET_CODE (operands[1]) == CONST_INT
        || (push_operand (operands[0], HImode)
	    && GET_CODE (operands[1]) == REG)
	|| (register_operand (operands[0], HImode)
	    && REGNO (operands[0]) >= 0x80
	    && ip2k_gen_operand (operands[1], HImode))))"
  [(set (match_dup 2) (match_dup 3))
   (set (match_dup 4) (match_dup 5))]
  "{
     ip2k_split_words (QImode, HImode, operands);  /* Split into 2=3,4=5 */
  }"
  [(set_attr "clobberw" "no,no,yes,no,yes,yes,yes,yes,yes")])

;; We don't generally use IP for HImode indirections because it's not
;; offsettable, however if we're accessing something that's already pointed
;; to by IP and would otherwise require a reload of DP then we can win by
;; simulating HImode accesses via IP instead.

(define_peephole2
  [(set (reg:HI 12)
  	(reg:HI 4))
   (set (mem:HI (reg:HI 12))
   	(match_operand:HI 0 "general_operand" ""))]
  "((ip2k_reorg_in_progress || ip2k_reorg_completed)
    && ip2k_xexp_not_uses_reg_p (operands[0], REG_DP, 2)
    && peep2_regno_dead_p (2, REG_DP))"
  [(set (mem:HI (reg:HI 4))
  	(match_dup 0))]
  "")

(define_peephole2
  [(set (reg:HI 12)
  	(reg:HI 4))
   (set (match_operand:HI 0 "nonimmediate_operand" "")
   	(mem:HI (reg:HI 12)))]
  "((ip2k_reorg_in_progress || ip2k_reorg_completed)
    && ip2k_xexp_not_uses_reg_p (operands[0], REG_DP, 2)
    && peep2_regno_dead_p (2, REG_DP))"
  [(set (match_dup 0)
	(mem:HI (reg:HI 4)))]
  "")

(define_peephole2
  [(set (reg:HI 12)
  	(reg:HI 4))
   (set (match_operand:HI 0 "nonimmediate_operand" "")
   	(mem:HI (plus:HI (reg:HI 12)
		         (match_operand 1 "const_int_operand" ""))))]
   ;
   ; We only match here if IP and DP both go dead because emulating
   ; offsets in conjunction with IP doesn't win unless IP goes
   ; dead too.
   ;
   "((ip2k_reorg_in_progress || ip2k_reorg_completed)
    && peep2_regno_dead_p (2, REG_DP)
    && peep2_regno_dead_p (2, REG_IP)
    && (INTVAL (operands[1]) < 0x100))"
  [(set (match_dup 0)
	(mem:HI (plus:HI (reg:HI 4)
			 (match_dup 1))))]
  "")

(define_peephole2
  [(set (reg:HI 12)
  	(reg:HI 4))
   (set (reg:HI 4)
   	(mem:HI (plus:HI (reg:HI 12)
		         (match_operand 0 "const_int_operand" ""))))]
  "((ip2k_reorg_in_progress || ip2k_reorg_completed)
    && peep2_regno_dead_p (2, REG_DP)
    && (INTVAL (operands[0]) < 0x100))"
  [(set (reg:HI 4)
	(mem:HI (plus:HI (reg:HI 4)
			 (match_dup 0))))]
  "")

(define_peephole2
  [(set (match_operand:HI 0 "register_operand" "")
   	(mem:HI (reg:HI 4)))
   (set (match_operand:HI 2 "nonimmediate_operand" "")
        (match_dup 0))]
  "((ip2k_reorg_in_progress || ip2k_reorg_completed)
    && peep2_reg_dead_p (2, operands[0]))"
  [(set (match_dup 2)
	(mem:HI (reg:HI 4)))]
  "")

(define_peephole2
  [(set (match_operand:HI 0 "register_operand" "")
   	(mem:HI (plus:HI (reg:HI 4)
		         (match_operand 1 "const_int_operand" ""))))
   (set (match_operand:HI 2 "nonimmediate_operand" "")
        (match_dup 0))]
  "((ip2k_reorg_in_progress || ip2k_reorg_completed)
    && peep2_reg_dead_p (2, operands[0])
    && (INTVAL (operands[1]) < 0x100))"
  [(set (match_dup 2)
	(mem:HI (plus:HI (reg:HI 4)
			 (match_dup 1))))]
  "")

(define_peephole2
  [(set (match_operand:HI 0 "ip2k_nonsp_reg_operand" "")
        (match_operand:HI 1 "ip2k_short_operand" ""))
   (set (reg:HI 12)
        (reg:HI 4))
   (set (mem:HI (reg:HI 12))
        (match_dup 0))]
  "(peep2_reg_dead_p (3, operands[0])
    && ip2k_xexp_not_uses_reg_p (operands[0], REG_DP, 2)
    && peep2_regno_dead_p (3, REG_DP))"
  [(set (mem:HI (reg:HI 4))
        (match_dup 1))]
  "")

;; We sometimes want to copy a value twice, usually when we copy a value into
;; both a structure slot and into a temporary register.  We can win here
;; because gcc doesn't know about ways of reusing w while we're copying.
;;
(define_insn "*movhi_twice"
  [(set (match_operand:HI 0 "ip2k_gen_operand" "=&uS,uS")
        (match_operand:HI 1 "ip2k_gen_operand"   "uS,uS"))
   (set (match_operand:HI 2 "ip2k_gen_operand" "=&uS,uS")
        (match_dup 1))]
  "ip2k_reorg_split_simode"
  "*{
    switch (which_alternative)
      {
      case 0:
        return AS2 (mov, w, %L1) CR_TAB
	       AS2 (mov, %L0, w) CR_TAB
	       AS2 (mov, %L2, w) CR_TAB
	       AS2 (mov, w, %H1) CR_TAB
	       AS2 (mov, %H0, w) CR_TAB
	       AS2 (mov, %H2, w);

      case 1:
        return AS2 (mov, w, %L1) CR_TAB
	       AS1 (push, %H1%<) CR_TAB
	       AS1 (push, %H1%<) CR_TAB
	       AS1 (pop, %H0%>) CR_TAB
	       AS2 (mov, %L0, w) CR_TAB
	       AS1 (pop, %H2%>) CR_TAB
	       AS2 (mov, %L2, w);
      default:
        abort ();
      }
  }")

;; We have to be *very* careful with this one to use predicates that do not
;; allow this to match if there are any register dependencies between the
;; operands.
;; Don't try to match until we've removed redundant reloads.  Often this
;; simplification will remove the need to do two moves!
;;
(define_peephole2
  [(set (match_operand:HI 0 "ip2k_gen_operand" "")
        (match_operand:HI 1 "ip2k_gen_operand" ""))
   (set (match_operand:HI 2 "ip2k_gen_operand" "")
        (match_dup 0))]
  "(ip2k_reorg_split_simode)"
  [(parallel [(set (match_dup 0)
  		   (match_dup 1))
	      (set (match_dup 2)
	      	   (match_dup 1))])]
  "")

;; We have to be *very* careful with this one to use predicates that do not
;; allow this to match if there are any register dependencies between the
;; operands.
;; Don't try to match until we've removed redundant reloads.  Often this
;; simplification will remove the need to do two moves!
;;
(define_peephole2
  [(set (match_operand:HI 0 "ip2k_gen_operand" "")
        (match_operand:HI 1 "ip2k_gen_operand" ""))
   (set (match_operand:HI 2 "ip2k_gen_operand" "")
        (match_dup 1))]
  "(ip2k_reorg_split_simode
    && (!REG_P (operands[0])
        || ip2k_xexp_not_uses_reg_p (operands[1], REGNO (operands[0]), 2)))"
  [(parallel [(set (match_dup 0)
  		   (match_dup 1))
	      (set (match_dup 2)
	      	   (match_dup 1))])]
  "")

;;
;; Move 32-bit integers.
;;

(define_expand "movsi"
  [(set (match_operand:SI 0 "" "")
	(match_operand:SI 1 "" ""))]
  ""
  "")

(define_insn_and_split "*movsi"
  [(set (match_operand:SI 0 "ip2k_split_dest_operand" "=<, ro,  S")
	(match_operand:SI 1 "general_operand"	    "roSi,rSi,roi"))]
  ""
  "#"
  "ip2k_reorg_split_simode"
  [(set (match_dup 2) (match_dup 3))
   (set (match_dup 4) (match_dup 5))]
  "{
     ip2k_split_words (HImode, SImode, operands);  /* Split into 2=3,4=5 */
  }")

;; We sometimes want to copy a value twice, usually when we copy a value into
;; both a structure slot and into a temporary register.  We can win here
;; because gcc doesn't know about ways of reusing w while we're copying.
;;
(define_insn "*movsi_twice"
  [(set (match_operand:SI 0 "ip2k_gen_operand" "=&uS,uS")
        (match_operand:SI 1 "ip2k_gen_operand"   "uS,uS"))
   (set (match_operand:SI 2 "ip2k_gen_operand" "=&uS,uS")
        (match_dup 1))]
  "ip2k_reorg_split_dimode"
  "*{
    switch (which_alternative)
      {
      case 0:
        return AS2 (mov, w, %A1) CR_TAB
	       AS2 (mov, %A0, w) CR_TAB
	       AS2 (mov, %A2, w) CR_TAB
	       AS2 (mov, w, %B1) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS2 (mov, %B2, w) CR_TAB
	       AS2 (mov, w, %C1) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS2 (mov, %C2, w) CR_TAB
	       AS2 (mov, w, %D1) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
	       AS2 (mov, %D2, w);

      case 1:
        return AS2 (mov, w, %D1) CR_TAB
               AS1 (push, %C1%<) CR_TAB
	       AS1 (push, %B1%<) CR_TAB
	       AS1 (push, %A1%<) CR_TAB
	       AS1 (push, %C1%<) CR_TAB
	       AS1 (push, %B1%<) CR_TAB
	       AS1 (push, %A1%<) CR_TAB
	       AS1 (pop, %A0%>) CR_TAB
	       AS1 (pop, %B0%>) CR_TAB
	       AS1 (pop, %C0%>) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
	       AS1 (pop, %A2%>) CR_TAB
	       AS1 (pop, %B2%>) CR_TAB
	       AS1 (pop, %C2%>) CR_TAB
	       AS2 (mov, %D2, w);
      default:
        abort ();
     }
  }")

;; We have to be *very* careful with this one to use predicates that do not 
;; allow this to match if there are any register dependencies between the
;; operands.
;; Don't try to match until we've removed redundant reloads.  Often this
;; simplification will remove the need to do two moves!
;;
(define_peephole2
  [(set (match_operand:SI 0 "ip2k_gen_operand" "")
        (match_operand:SI 1 "ip2k_gen_operand" ""))
   (set (match_operand:SI 2 "ip2k_gen_operand" "")
        (match_dup 0))]
  "(ip2k_reorg_split_dimode
    && (!REG_P (operands[0])
        || (ip2k_xexp_not_uses_reg_p (operands[1], REGNO (operands[0]), 4)
	    && ip2k_xexp_not_uses_reg_p (operands[2], REGNO (operands[0]), 4)))
    && (!REG_P (operands[1])
        || (ip2k_xexp_not_uses_reg_p (operands[0], REGNO (operands[1]), 4)
	    && ip2k_xexp_not_uses_reg_p (operands[2], REGNO (operands[1]), 4)))
    && (!REG_P (operands[2])
        || (ip2k_xexp_not_uses_reg_p (operands[0], REGNO (operands[2]), 4)
	    && ip2k_xexp_not_uses_reg_p (operands[1],
                                         REGNO (operands[2]), 4))))"
  [(parallel [(set (match_dup 0)
  		   (match_dup 1))
	      (set (match_dup 2)
	      	   (match_dup 1))])]
  "")

;; We have to be *very* careful with this one to use predicates that do not
;; allow this to match if there are any register dependencies between the
;; operands.
;; Don't try to match until we've removed redundant reloads.  Often this
;; simplification will remove the need to do two moves!
;;
(define_peephole2
  [(set (match_operand:SI 0 "ip2k_gen_operand" "")
        (match_operand:SI 1 "ip2k_gen_operand" ""))
   (set (match_operand:SI 2 "ip2k_gen_operand" "")
        (match_dup 1))]
  "(ip2k_reorg_split_dimode
    && (!REG_P (operands[0])
        || (ip2k_xexp_not_uses_reg_p (operands[1], REGNO (operands[0]), 4)
	    && ip2k_xexp_not_uses_reg_p (operands[2], REGNO (operands[0]), 4)))
    && (!REG_P (operands[1])
        || (ip2k_xexp_not_uses_reg_p (operands[0], REGNO (operands[1]), 4)
	    && ip2k_xexp_not_uses_reg_p (operands[2], REGNO (operands[1]), 4)))
    && (!REG_P (operands[2])
        || (ip2k_xexp_not_uses_reg_p (operands[0], REGNO (operands[2]), 4)
	    && ip2k_xexp_not_uses_reg_p (operands[1],
                                         REGNO (operands[2]), 4))))"
  [(parallel [(set (match_dup 0)
  		   (match_dup 1))
	      (set (match_dup 2)
	      	   (match_dup 1))])]
  "")

;;
;; Move 64-bit integers.
;;

(define_expand "movdi"
  [(set (match_operand:DI 0 "" "")
	(match_operand:DI 1 "" ""))]
  ""
  "")

(define_insn_and_split "*movdi"
  [(set (match_operand:DI 0 "ip2k_split_dest_operand" "=<, ro,  S")
	(match_operand:DI 1 "general_operand"	    "roSi,rSi,roi"))]
  ""
  "#"
  "ip2k_reorg_split_dimode"
  [(set (match_dup 2) (match_dup 3))
   (set (match_dup 4) (match_dup 5))]
  "{
     ip2k_split_words (SImode, DImode, operands);  /* Split into 2=3,4=5 */
  }")

;;
;; Move 32-bit floating point values.
;;

(define_expand "movsf"
  [(set (match_operand:SF 0 "" "")
	(match_operand:SF 1 "" ""))]
  ""
  "if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
     operands[1] = copy_to_mode_reg (SFmode, operands[1]);
  ")

(define_insn_and_split "*movsf"
  [(set (match_operand:SF 0 "ip2k_split_dest_operand" "=r<, o")
	(match_operand:SF 1 "general_operand"	      "roi,ri"))]
  "(ip2k_short_operand (operands[0], SFmode)
    && ip2k_short_operand (operands[1], SFmode))
   || ! (memory_operand (operands[0], SFmode)
         && memory_operand (operands[1], SFmode))"
  "#"
  "(reload_completed || reload_in_progress)"
  [(set (match_dup 2) (match_dup 3))
   (set (match_dup 4) (match_dup 5))
   (set (match_dup 6) (match_dup 7))
   (set (match_dup 8) (match_dup 9))]
  "{
     /* Split into 2=3,4=5 */
     ip2k_split_words (HImode, SImode, operands);
     /* Split 4=5 into 6=7,8=9 */		
     ip2k_split_words (QImode, HImode, &operands[4]); 
     operands[0] = operands[2];
     operands[1] = operands[3];
     ip2k_split_words (QImode, HImode, operands);
  }")

;;
;; Move 64-bit floating point values.
;;

;;
;; Block move operations.
;;

;; Copy a block of bytes (memcpy()).  We expand the definition to convert
;; our memory operand into a register pointer operand instead.
;;
(define_expand "movstrhi"
  [(use (match_operand:BLK 0 "memory_operand" ""))
   (use (match_operand:BLK 1 "memory_operand" ""))
   (use (match_operand:HI 2 "general_operand" ""))
   (use (match_operand 3 "const_int_operand" ""))]
  ""
  "{
    rtx addr0, addr1, count;

    addr0 = copy_to_mode_reg (Pmode, XEXP (operands[0], 0));
    addr1 = copy_to_mode_reg (Pmode, XEXP (operands[1], 0));

    if (GET_CODE (operands[2]) == CONST_INT)
      count = gen_int_mode (INTVAL (operands[2]) & 0xffff, HImode);
    else
      count = operands[2];

    emit_insn (gen_movstrhi_expanded (addr0, count, addr1));
    DONE;
  }")

;; Block copy instruction.  We handle this by calling one of two functions in
;; libgcc.  The first of these is a special case (faster) routine that handles
;; constant block sizes under 256 bytes.  This one is particularly common
;; because we use it when copying data structures.  The second routine handles
;; the general case where we have either a variable block size or one that is
;; greater than 255 bytes.
;;
(define_insn "movstrhi_expanded"
  [(set
    (mem:BLK
     (match_operand:HI 0 "nonimmediate_operand" "rS,ro,rS, rS, ro, rS"))
    (mem:BLK
     (match_operand:HI 2 "nonimmediate_operand" "ro,rS,rS, ro, rS, rS")))
   (use
    (match_operand:HI 1 "general_operand"	 "P, P, P,rSi,rSi,roi"))]
  ""
  "@
   push\\t%L1%<\;push\\t%L2%<\;push\\t%H2%<\;push\\t%L0%<\;push\\t%H0%>%>%>%>\;page\\t__movstrhi_countqi\;call\\t__movstrhi_countqi
   push\\t%L1%<\;push\\t%L2%<\;push\\t%H2%<\;push\\t%L0%<\;push\\t%H0%>%>%>%>\;page\\t__movstrhi_countqi\;call\\t__movstrhi_countqi
   push\\t%L1%<\;push\\t%L2%<\;push\\t%H2%<\;push\\t%L0%<\;push\\t%H0%>%>%>%>\;page\\t__movstrhi_countqi\;call\\t__movstrhi_countqi
   push\\t%L1%<\;push\\t%H1%<\;push\\t%L2%<\;push\\t%H2%<\;push\\t%L0%<\;push\\t%H0%>%>%>%>%>\;page\\t__movstrhi_counthi\;call\\t__movstrhi_counthi
   push\\t%L1%<\;push\\t%H1%<\;push\\t%L2%<\;push\\t%H2%<\;push\\t%L0%<\;push\\t%H0%>%>%>%>%>\;page\\t__movstrhi_counthi\;call\\t__movstrhi_counthi
   push\\t%L1%<\;push\\t%H1%<\;push\\t%L2%<\;push\\t%H2%<\;push\\t%L0%<\;push\\t%H0%>%>%>%>%>\;page\\t__movstrhi_counthi\;call\\t__movstrhi_counthi")


;; Bit insert
;;
(define_expand "insv"
  [(set (zero_extract:QI (match_operand:QI 0 "nonimmediate_operand" "")
			 (match_operand 1 "immediate_operand" "")  ;size
			 (match_operand 2 "immediate_operand" "")) ;pos
	(match_operand:QI 3 "general_operand" ""))]
  ""
  "{
    if (! CONST_OK_FOR_LETTER_P (INTVAL (operands[1]), 'J')
	|| ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J'))
      FAIL;
  }")

(define_insn "*insv"
  [(set (zero_extract:QI
	 (match_operand:QI
	  0 "nonimmediate_operand" "+roR,roR,roR,roR,&roR,&roR,&r")
	 (match_operand
	  1 "immediate_operand"       "N,  N,  J,  J,   N,   J, J") ;sz
	 (match_operand
	  2 "immediate_operand"       "J,  J,  J,  J,   J,   J, J"));pos
	(match_operand:QI
	 3 "general_operand"	     "MN,  O,  M,  O, roR,  rn,oR"))]
  ""
  "*{
    unsigned int pos = INTVAL (operands[2]),
		 siz = INTVAL (operands[1]),
		 mask = (1 << (pos + siz)) - (1 << pos);

    switch (which_alternative)
      {
      case 0:
        return \"setb\\t%0,%b1\";

      case 1:
        return \"clrb\\t%0,%b1\";

      case 2:
        operands[3] = gen_int_mode (mask & 0xff, QImode);
        return AS2 (mov, w, %3) CR_TAB
	       AS2 (or, %0, w);

      case 3:
        operands[3] = gen_int_mode (0xff & ~mask, QImode);
        return AS2 (mov, w, %3) CR_TAB
	       AS2 (and, %0, w);

      case 4:
        return AS2 (clrb, %0,%b2) CR_TAB
	       AS2 (snb, %3, 0) CR_TAB
	       AS2 (setb, %0, %b2);

      case 5:
      case 6:
        {
	  static char buff[256];
	  char *p = buff;

	  /* Clear the destination field */

	  p += sprintf (buff, \"mov\\tw,#$%2.2x\;and\\t%%0,w\;\",
		        0xff & ~mask);

	  if (CONSTANT_P (operands[3]))
	  /* Constant can just be or-ed in. */
	    {
	      p += sprintf (p, \"mov\\tw,#$%2.2x\;or\\t%%0,w\",
		            (int) (INTVAL (operands[3]) << pos) & mask & 0xff);
	      return buff;
	    }

	  p += sprintf (p, \"mov\\tw,%%3\;\"); /* Value to deposit */

	  /* Shift and mask the value before OR-ing into the destination. */

          if (pos != 0)
	    p += sprintf (p, \"mulu\\tw,#%d\;\", 1<<pos);

	  p += sprintf (p, \"\;and\\tw,#$%2.2x\;or\\t%%0,w\", mask);
	  return buff;
        }
      default:
        abort ();
      }
  }"
  [(set_attr "skip" "yes,yes,no,no,no,no,no")
   (set_attr "clobberw" "no,no,yes,yes,no,yes,yes")])

;;
;; Add bytes
;;

(define_expand "addqi3"
  [(set (match_operand:QI 0 "nonimmediate_operand" "")
	(plus:QI (match_operand:QI 1 "general_operand" "")
		 (match_operand:QI 2 "general_operand" "")))]
  ""
  "")

(define_insn "*push_addqi3"
  [(set (match_operand:QI 0 "push_operand"                  "=<,<,<")
        (plus:QI (match_operand:QI 1 "nonimmediate_operand" "%g,g,g")
		 (match_operand:QI 2 "general_operand"       "N,M,g")))]
  ""
  "@
   push\\t%1\;inc\\t1(SP)
   push\\t%1\;dec\\t1(SP)
   mov\\tw,%2\;add\\tw,%1\;push\\twreg"
  [(set_attr "clobberw" "no,no,yes")])

(define_insn "*addqi3_w"
  [(set
    (reg:QI 10)
    (plus:QI
     (match_operand:QI 0 "nonimmediate_operand" "%rS, g,rS, g, rS,  g,rS")
     (match_operand:QI 1 "general_operand"        "N, N, M, M,rSi,rSi, g")))]
  "(ip2k_reorg_split_qimode)"
  "@
   inc\\tw,%0
   inc\\tw,%0
   dec\\tw,%0
   dec\\tw,%0
   mov\\tw,%1\;add\\tw,%0
   mov\\tw,%1\;add\\tw,%0
   mov\\tw,%1\;add\\tw,%0"
  [(set_attr "skip" "no,no,no,no,no,no,no")])

(define_insn_and_split "*addqi3"
  [(set (match_operand:QI 0 "nonimmediate_operand"          "=k,k,z,z,djyoR,djyoR,djyoR,djyS, g,rS, g,rS,  g, rS,rS")
	(plus:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,0,0,    0,    0,    0,   0,rS, g,rS, g, rS,  g,rS")
		 (match_operand:QI 2 "general_operand"       "N,g,N,g,    N,    M,  rSi,   g, N, N, M, M,rSi,rSi, g")))]
  ""
  "@
   incsnz\\t%0\;dec\\tiph
   mov\\tw,%2\;add\\t%0,w
   incsnz\\t%0\;dec\\tdph
   mov\\tw,%2\;add\\t%0,w
   inc\\t%0
   dec\\t%0
   mov\\tw,%2\;add\\t%0,w
   mov\\tw,%2\;add\\t%0,w
   #
   #
   #
   #
   #
   #
   #"
  "(ip2k_reorg_split_qimode
    && ! rtx_equal_p (operands[0], operands[1]))"
  [(set (reg:QI 10)
  	(plus:QI (match_dup 1)
		 (match_dup 2)))
   (set (match_dup 0)
   	(reg:QI 10))]
  ""
  [(set_attr "skip" "no,no,no,no,yes,yes,no,no,no,no,no,no,no,no,no")
   (set_attr
    "clobberw" "no,yes,no,yes,no,no,yes,yes,yes,yes,yes,yes,yes,yes,yes")])

;;
;; Add 16-bit integers.
;;

(define_expand "addhi3"
  [(set (match_operand:HI 0 "nonimmediate_operand" "")
	(plus:HI (match_operand:HI 1 "general_operand" "")
		 (match_operand:HI 2 "general_operand" "")))]
  ""
  "if (rtx_equal_p (operands[1], operands[2]))
     {
       /* It is not impossible to wind up with two constants here.
          If we simply emit the ashl, we'll generate unrecognizable
	  instructions. */
       if (! nonimmediate_operand (operands[1], HImode))
         operands[1] = copy_to_mode_reg (HImode, operands[1]);
       emit_insn (gen_ashlhi3 (operands[0], operands[1], GEN_INT (1)));
       DONE;
     }
  ")

(define_insn "*push_addhi3" ;		           0 1  2  3   4   5 6 7
  [(set
    (match_operand:HI 0 "push_operand"           "=<,<, <, <,  <,  <,<,<")
    (plus:HI
     (match_operand:HI 1 "nonimmediate_operand" "%uo,q,uo,bf, uo, uS,q,q")
     (match_operand:HI 2 "general_operand"        "N,N, M, P,uSi,uoi,u,n")))]
  ""
  "*{
    switch (which_alternative) {
    case 0:
      return AS1 (push, %L1%<) CR_TAB
      	     AS1 (push, %H1%>) CR_TAB
	     AS1 (incsnz, 2(SP)) CR_TAB
	     AS1 (inc, 1(SP));

    case 1:
      return AS2 (mov, w, %H1) CR_TAB
      	     AS1 (push, %L1) CR_TAB
	     AS1 (push, wreg) CR_TAB
	     AS1 (incsnz, 2(SP)) CR_TAB
	     AS1 (inc, 1(SP));

    case 2:
      return AS1 (push, %L1%<) CR_TAB
      	     AS1 (push, %H1%>) CR_TAB
	     AS2 (mov, w, #-1) CR_TAB
	     AS2 (add, 2(SP), w) CR_TAB
	     AS2 (addc, 1(SP), w);

    case 3:
      OUT_AS2 (mov, w, %L2);
      OUT_AS2 (add, %L1, w);
      OUT_AS1 (push, %L1);
      OUT_AS1 (push, %H1);
      if (!find_regno_note (insn, REG_DEAD, REGNO (operands[1])))
        OUT_AS2 (sub, %L1, w);
      return \"\";

    case 4:
    case 5:
      return AS2 (mov, w, %L2) CR_TAB
      	     AS2 (add, w, %L1) CR_TAB
	     AS1 (push, wreg%<) CR_TAB
	     AS2 (mov, w, %H2) CR_TAB
	     AS2 (addc, w, %H1) CR_TAB
	     AS1 (push, wreg%>);

    case 6:
      return AS2 (mov, w, %H1) CR_TAB
      	     AS1 (push, %L1) CR_TAB
	     AS1 (push, wreg) CR_TAB
	     AS2 (mov, w, %L2) CR_TAB
	     AS2 (add, 2(SP), w) CR_TAB
	     AS2 (mov, w, %H2) CR_TAB
	     AS2 (addc, 1(SP), w);

    case 7:
      {
        operands[3] = GEN_INT (INTVAL (operands[2]) + 2);
	return AS1 (push, %L3) CR_TAB
	       AS1 (push, %H3) CR_TAB
	       AS2 (mov, w, %L1) CR_TAB
	       AS2 (add, 2(SP), w) CR_TAB
	       AS2 (mov, w, %H1) CR_TAB
	       AS2 (addc, 1(SP), w);
      }
    default:
      abort ();
    }
  }"
  [(set_attr "clobberw" "no,yes,yes,yes,yes,yes,yes,yes")])

(define_insn "*push_addhi3_zero_ext" ;		     0    1    2   3
  [(set (match_operand:HI 0 "push_operand"         "=<,   <,   <,  <")
        (plus:HI
	 (zero_extend:HI
	  (match_operand:QI 1 "general_operand" "%roRi,roRi,roRi,rSi"))
	 (match_operand:HI 2 "general_operand"      "N,   P, rSi,roi")))]
  ""
  "@
   inc\\tw,%L2\;push\\twreg\;push\\t#0\;rl\\t1(SP)
   mov\\tw,%L2\;add\\tw,%1\;push\\twreg\;push\\t#0\;rl\\t1(SP)
   mov\\tw,%L2\;add\\tw,%1\;push\\twreg%<\;mov\\tw,%H2\;addc\\tw,$ff\;push\\twreg%>
   mov\\tw,%L2\;add\\tw,%1\;push\\twreg%<\;mov\\tw,%H2\;addc\\tw,$ff\;push\\twreg%>")

(define_insn "*addhi3_imm_zero_ext_w"
  [(set
    (match_operand:HI 0 "nonimmediate_operand"  "=rS,o,a,b,a,a,rS,o,rS,o")
    (plus:HI (zero_extend:HI (reg:QI 10))
	     (match_operand 1 "immediate_operand" "O,O,M,i,P,I, P,P, i,i")))]
  ""
  "@
   mov\\t%L0,w\;clr\\t%H0
   mov\\t%L0,w\;clr\\t%H0
   mov\\t%L0,w\;clr\\t%H0\;dec\\t%L0
   loadh\\t%x1\;loadl\\t%x1\;add\\t%L0,w
   mov\\t%L0,w\;clr\\t%H0\;mov\\tw,%1\;add\\t%L0,w
   mov\\t%L0,w\;clr\\t%H0\;mov\\tw,#%n1\;sub\\t%L0,w
   add\\tw,%L1\;mov\\t%L0,w\;clr\\t%H0\;rl\\t%H0
   add\\tw,%L1\;mov\\t%L0,w\;clr\\t%H0\;rl\\t%H0
   add\\tw,%L1\;mov\\t%L0,w\;clr\\t%H0\;mov\\tw,%H1\;addc\\t%H0,w
   add\\tw,%L1\;mov\\t%L0,w\;clr\\t%H0\;mov\\tw,%H1\;addc\\t%H0,w")

(define_insn_and_split "*addhi3_imm_zero_ext"
  [(set
    (match_operand:HI
     0 "nonimmediate_operand" "=rS, o, rS, o,  a,  b,  a,  a, rS, o, rS, o")
    (plus:HI
     (zero_extend:HI
      (match_operand:QI
       1 "general_operand"   "%roR,rS,roR,rS,roR,roR,roR,roR,roR,rS,roR,rS"))
     (match_operand
      2 "immediate_operand"    " O, O,  N, N,  M,  i,  P,  I,  P, P,  i, i")))]
  ""
  "@
   #
   #
   clr\\t%H0\;incsnz\\tw,%1\;inc\\t%H0\;mov\\t%L0,w
   clr\\t%H0\;incsnz\\tw,%1\;inc\\t%H0\;mov\\t%L0,w
   #
   #
   #
   #
   #
   #
   #
   #"
  "(ip2k_reorg_split_qimode
    && (GET_CODE (operands[1]) != CONST_INT
        || INTVAL (operands[1]) != 1))"
  [(set (reg:QI 10)
  	(match_dup 1))
   (set (match_dup 0)
   	(plus:HI (zero_extend:HI (reg:QI 10))
		 (match_dup 2)))])

(define_insn "*addhi3_immediate" ;			 0  1 2  3 4 5 6  7   8   9  a  b  c   d   e  f
  [(set (match_operand:HI 0 "nonimmediate_operand"     "=a,do,a,do,a,a,a,do,&uo,&uS,bf,bf,bf,&uS,&uo, u")
        (plus:HI (match_operand:HI 1 "general_operand" "%0, 0,0, 0,0,0,0, 0, rS, ro,uo,uo,uo, ro, rS,uo")
	         (match_operand 2 "immediate_operand"   "N, N,M, M,P,I,i, i,  N,  N, M, P, I,  i,  i, i")))]
  ""
  "@
   inc\\t%L0
   incsnz\\t%L0\;inc\\t%H0
   dec\\t%L0
   mov\\tw,#-1\;add\\t%L0,w\;addc\\t%H0,w
   mov\\tw,%2\;add\\t%L0,w
   mov\\tw,#%n2\;sub\\t%L0,w
   mov\\tw,%L2\;add\\t%L0,w\;mov\\tw,%H2\;add\\t%H0,w
   mov\\tw,%L2\;add\\t%L0,w\;mov\\tw,%H2\;addc\\t%H0,w
   mov\\tw,%H1\;mov\\t%H0,w\;incsnz\\tw,%L1\;inc\\t%H0\;mov\\t%L0,w
   mov\\tw,%H1\;mov\\t%H0,w\;incsnz\\tw,%L1\;inc\\t%H0\;mov\\t%L0,w
   mov\\tw,%H1\;push\\t%L1%<\;pop\\t%L0%>\;mov\\t%H0,w\;dec\\t%L0
   mov\\tw,%H1\;push\\t%L1%<\;pop\\t%L0%>\;mov\\t%H0,w\;mov\\tw,%2\;add\\t%L0,w
   mov\\tw,%H1\;push\\t%L1%<\;pop\\t%L0%>\;mov\\t%H0,w\;mov\\tw,#%n2\;sub\\t%L0,w
   mov\\tw,%L2\;add\\tw,%L1\;mov\\t%L0,w\;mov\\tw,%H2\;addc\\tw,%H1\;mov\\t%H0,w
   mov\\tw,%L2\;add\\tw,%L1\;mov\\t%L0,w\;mov\\tw,%H2\;addc\\tw,%H1\;mov\\t%H0,w
   mov\\tw,%L2\;add\\tw,%L1\;push\\twreg%<\;mov\\tw,%H2\;addc\\tw,%H1\;mov\\t%H0,w\;pop\\t%L0%>"
  [(set_attr "skip" "yes,no,yes,no,no,no,no,no,no,no,no,no,no,no,no,no")
   (set_attr "clobberw" "no,no,no,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes,yes")])

(define_insn "*addhi3_nonimmediate" ;			      0  1  2    3 4   5   6  7
  [(set (match_operand:HI 0 "nonimmediate_operand"	  "=&bf,bf,&dS,&do,d,&rS,&rS, o")
	(plus:HI (match_operand:HI 1 "general_operand"      "%0, 0,  0,  0,0, ro, rS,rS")
		 (match_operand:HI 2 "nonimmediate_operand" "ro,uo, ro, rS,r, rS, ro,rS")))]
  ""
  "@
   mov\\tw,%L2\;add\\t%L0,w\;mov\\tw,%H2\;add\\t%H0,w
   mov\\tw,%L2\;push\\t%H2%<\;add\\t%L0,w\;pop\\twreg%>\;add\\t%H0,w
   mov\\tw,%L2\;add\\t%L0,w\;mov\\tw,%H2\;addc\\t%H0,w
   mov\\tw,%L2\;add\\t%L0,w\;mov\\tw,%H2\;addc\\t%H0,w
   mov\\tw,%L2\;push\\t%H2%<\;add\\t%L0,w\;pop\\twreg%>\;addc\\t%H0,w
   mov\\tw,%L2\;add\\tw,%L1\;mov\\t%L0,w\;mov\\tw,%H2\;addc\\tw,%H1\;mov\\t%H0,w
   mov\\tw,%L2\;add\\tw,%L1\;mov\\t%L0,w\;mov\\tw,%H2\;addc\\tw,%H1\;mov\\t%H0,w
   mov\\tw,%L2\;add\\tw,%L1\;mov\\t%L0,w\;mov\\tw,%H2\;addc\\tw,%H1\;mov\\t%H0,w")

(define_insn "*addhi3_nonimm_zero_extend_w"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=a,ro,&ro,&rS,&rS, u")
	(plus:HI
	 (zero_extend:HI (reg:QI 10))
	 (match_operand:HI 1 "nonimmediate_operand" "0, 0, rS, rS, ro,uo")))]
  ""
  "@
   add\\t%L0,w
   add\\t%L0,w\;clr\\twreg\;addc\\t%H0,w
   add\\tw,%L1\;mov\\t%L0,w\;clr\\twreg\;addc\\tw,%H1\;mov\\t%H0,w
   add\\tw,%L1\;mov\\t%L0,w\;clr\\twreg\;addc\\tw,%H1\;mov\\t%H0,w
   add\\tw,%L1\;mov\\t%L0,w\;clr\\twreg\;addc\\tw,%H1\;mov\\t%H0,w
   add\\tw,%L1\;push\\twreg%<\;clr\\twreg\;addc\\tw,%H1\;mov\\t%H0,w\;pop\\t%L0%>"
  [(set_attr "skip" "yes,no,no,no,no,no")
   (set_attr "clobberw" "no,yes,yes,yes,yes,yes")])

(define_insn_and_split "*addhi3_nonimm_zero_extend"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=a, ro,&ro,&rS,&rS, u")
	(plus:HI
	 (zero_extend:HI
	  (match_operand:QI 1 "general_operand"   "roR,roR, rS,roR, rS,rS"))
	 (match_operand:HI 2 "nonimmediate_operand" "0,  0, rS, rS, ro,uo")))]
  ""
  "#"
  "(ip2k_reorg_split_qimode)"
  [(set (reg:QI 10)
  	(match_dup 1))
   (set (match_dup 0)
   	(plus:HI (zero_extend:HI (reg:QI 10))
		 (match_dup 2)))])
  

;;
;; Add 32-bit integers.
;;

(define_expand "addsi3"
  [(set (match_operand:SI 0 "nonimmediate_operand" "")
	(plus:SI (match_operand:SI 1 "general_operand" "")
		 (match_operand:SI 2 "general_operand" "")))]
  ""
  "")

(define_insn "*push_addsi3"
  [(set (match_operand:SI 0 "push_operand"                  "=<,<,  <")
        (plus:SI (match_operand:SI 1 "nonimmediate_operand" "%g,g,  g")
		 (match_operand:SI 2 "general_operand"       "N,M,rSi")))]
  ""
  "*{
    switch (which_alternative) {
    case 0:
      OUT_AS1 (push, %D1%<);
      OUT_AS1 (push, %C1%<);
      OUT_AS1 (push, %B1%<);
      OUT_AS1 (push, %A1%>%>%>);
      OUT_AS1 (incsnz, 4(SP));
      OUT_AS1 (incsz, 3(SP));
      OUT_AS1 (page, 1f);
      OUT_AS1 (jmp, 1f);
      OUT_AS1 (incsnz, 2(SP));
      OUT_AS1 (inc, 1(SP));
      OUT_AS1 (1:,);
      return \"\";

    case 1:
      OUT_AS1 (push, %D1%<);
      OUT_AS1 (push, %C1%<);
      OUT_AS1 (push, %B1%<);
      OUT_AS1 (push, %A1%>%>%>);
      OUT_AS2 (mov, w, #-1);
      OUT_AS2 (add, 4(SP), w);
      OUT_AS2 (addc, 3(SP), w);
      OUT_AS2 (addc, 2(SP), w);
      OUT_AS2 (addc, 1(SP), w);
      return \"\";

    case 2:
      OUT_AS2 (mov, w, %D2);
      OUT_AS2 (add, w, %D1);
      OUT_AS1 (push, wreg%<);
      OUT_AS2 (mov, w, %C2);
      OUT_AS2 (addc, w, %C1);
      OUT_AS1 (push, wreg%<);
      OUT_AS2 (mov, w, %B2);
      OUT_AS2 (addc, w, %B1);
      OUT_AS1 (push, wreg%<);
      OUT_AS2 (mov, w, %A2);
      OUT_AS2 (addc, w, %A1);
      OUT_AS1 (push, wreg%>%>%>);
      return \"\";

    default:
      abort();
    }
  }"
  [(set_attr "clobberw" "no,yes,yes")])

(define_insn "*addsi3" ;				      0  1   2   3   4   5   6
  [(set
    (match_operand:SI 0 "nonimmediate_operand" "=ro,ro, ro, rS,&ro,&rS,&rS")
    (plus:SI
     (match_operand:SI 1 "nonimmediate_operand" "%0, 0,  0,  0, rS, ro, rS")
     (match_operand:SI 2 "general_operand"	 "N, M,rSi,roi,rSi,rSi,roi")))]
  ""
  "@
   incsnz\\t%D0\;incsz\\t%C0\;page\\t1f\;jmp\\t1f\;incsnz\\t%B0\;inc\\t%A0\;1:
   mov\\tw,#-1\;add\\t%D0,w\;addc\\t%C0,w\;addc\\t%B0,w\;addc\\t%A0,w
   mov\\tw,%D2\;add\\t%D0,w\;mov\\tw,%C2\;addc\\t%C0,w\;mov\\tw,%B2\;addc\\t%B0,w\;mov\\tw,%A2\;addc\\t%A0,w
   mov\\tw,%D2\;add\\t%D0,w\;mov\\tw,%C2\;addc\\t%C0,w\;mov\\tw,%B2\;addc\\t%B0,w\;mov\\tw,%A2\;addc\\t%A0,w
   mov\\tw,%D2\;add\\tw,%D1\;mov\\t%D0,w\;mov\\tw,%C2\;addc\\tw,%C1\;mov\\t%C0,w\;mov\\tw,%B2\;addc\\tw,%B1\;mov\\t%B0,w\;mov\\tw,%A2\;addc\\tw,%A1\;mov\\t%A0,w
   mov\\tw,%D2\;add\\tw,%D1\;mov\\t%D0,w\;mov\\tw,%C2\;addc\\tw,%C1\;mov\\t%C0,w\;mov\\tw,%B2\;addc\\tw,%B1\;mov\\t%B0,w\;mov\\tw,%A2\;addc\\tw,%A1\;mov\\t%A0,w
   mov\\tw,%D2\;add\\tw,%D1\;mov\\t%D0,w\;mov\\tw,%C2\;addc\\tw,%C1\;mov\\t%C0,w\;mov\\tw,%B2\;addc\\tw,%B1\;mov\\t%B0,w\;mov\\tw,%A2\;addc\\tw,%A1\;mov\\t%A0,w"
  [(set_attr "clobberw" "no,yes,yes,yes,yes,yes,yes")])

(define_insn "*push_addsi3_zero_extendqi" ;		      0   1
  [(set (match_operand:SI 0 "push_operand"                  "=<,  <")
        (plus:SI (zero_extend:SI
	           (match_operand:QI 1 "general_operand" "%roRi,rSi"))
	         (match_operand:SI 2 "general_operand"     "rSi,roi")))]
  ""
  "@
   mov\\tw,%D2\;add\\tw,%1\;push\\twreg%<\;mov\\tw,%C2\;addc\\tw,$ff\;push\\twreg%<\;mov\\tw,%B2\;addc\\tw,$ff\;push\\twreg%<\;mov\\tw,%A2\;addc\\tw,$ff\;push\\twreg%>%>%>
   mov\\tw,%D2\;add\\tw,%1\;push\\twreg%<\;mov\\tw,%C2\;addc\\tw,$ff\;push\\twreg%<\;mov\\tw,%B2\;addc\\tw,$ff\;push\\twreg%<\;mov\\tw,%A2\;addc\\tw,$ff\;push\\twreg%>%>%>")

(define_insn "*addsi3_zero_extendqi" ;
  [(set (match_operand:SI 0 "nonimmediate_operand"  "=ro, rS,&ro,&rS,&rS")
        (plus:SI
	 (zero_extend:SI
	  (match_operand:QI 1 "nonimmediate_operand" "rS,roR, rS,roR, rS"))
	 (match_operand:SI 2 "general_operand"        "0,  0,rSi,rSi,roi")))]
  ""
  "@
   mov\\tw,%1\;add\\t%D0,w\;clr\\twreg\;addc\\t%C0,w\;addc\\t%B0,w\;addc\\t%A0,w
   mov\\tw,%1\;add\\t%D0,w\;clr\\twreg\;addc\\t%C0,w\;addc\\t%B0,w\;addc\\t%A0,w
   mov\\tw,%1\;add\\tw,%D2\;mov\\t%D0,w\;mov\\tw,%C2\;addc\\tw,$ff\;mov\\t%C0,w\;mov\\tw,%B2\;addc\\tw,$ff\;mov\\t%B0,w\;mov\\tw,%A2\;addc\\tw,$ff\;mov\\t%A0,w
   mov\\tw,%1\;add\\tw,%D2\;mov\\t%D0,w\;mov\\tw,%C2\;addc\\tw,$ff\;mov\\t%C0,w\;mov\\tw,%B2\;addc\\tw,$ff\;mov\\t%B0,w\;mov\\tw,%A2\;addc\\tw,$ff\;mov\\t%A0,w
   mov\\tw,%1\;add\\tw,%D2\;mov\\t%D0,w\;mov\\tw,%C2\;addc\\tw,$ff\;mov\\t%C0,w\;mov\\tw,%B2\;addc\\tw,$ff\;mov\\t%B0,w\;mov\\tw,%A2\;addc\\tw,$ff\;mov\\t%A0,w")

(define_insn "*push_addsi3_zero_extendhi" ;		          0   1
  [(set (match_operand:SI 0 "push_operand"                      "=<,  <")
        (plus:SI (zero_extend:SI
	           (match_operand:HI 1 "nonimmediate_operand" "%roR,rSR"))
	         (match_operand:SI 2 "general_operand"         "rSi,roi")))]
  ""
  "@
   mov\\tw,%D2\;add\\tw,%L1\;push\\twreg%<\;mov\\tw,%C2\;addc\\tw,%H1\;push\\twreg%<\;mov\\tw,%B2\;addc\\tw,$ff\;push\\twreg%<\;mov\\tw,%A2\;addc\\tw,$ff\;push\\twreg%>%>%>
   mov\\tw,%D2\;add\\tw,%L1\;push\\twreg%<\;mov\\tw,%C2\;addc\\tw,%H1\;push\\twreg%<\;mov\\tw,%B2\;addc\\tw,$ff\;push\\twreg%<\;mov\\tw,%A2\;addc\\tw,$ff\;push\\twreg%>%>%>")

(define_insn "*addsi3_zero_extendhi" ;
  [(set (match_operand:SI 0 "nonimmediate_operand"  "=ro,rS,&ro,&rS,&rS")
        (plus:SI
	 (zero_extend:SI
	  (match_operand:HI 1 "nonimmediate_operand" "rS,ro, rS, ro, rS"))
	 (match_operand:SI 2 "general_operand"        "0, 0,rSi,rSi,roi")))]
  ""
  "@
   mov\\tw,%L1\;add\\t%D0,w\;mov\\tw,%H1\;addc\\t%C0,w\;clr\\twreg\;addc\\t%B0,w\;addc\\t%A0,w
   mov\\tw,%L1\;add\\t%D0,w\;mov\\tw,%H1\;addc\\t%C0,w\;clr\\twreg\;addc\\t%B0,w\;addc\\t%A0,w
   mov\\tw,%L1\;add\\tw,%D2\;mov\\t%D0,w\;mov\\tw,%C2\;addc\\tw,%H1\;mov\\t%C0,w\;mov\\tw,%B2\;addc\\tw,$ff\;mov\\t%B0,w\;mov\\tw,%A2\;addc\\tw,$ff\;mov\\t%A0,w
   mov\\tw,%L1\;add\\tw,%D2\;mov\\t%D0,w\;mov\\tw,%C2\;addc\\tw,%H1\;mov\\t%C0,w\;mov\\tw,%B2\;addc\\tw,$ff\;mov\\t%B0,w\;mov\\tw,%A2\;addc\\tw,$ff\;mov\\t%A0,w
   mov\\tw,%L1\;add\\tw,%D2\;mov\\t%D0,w\;mov\\tw,%C2\;addc\\tw,%H1\;mov\\t%C0,w\;mov\\tw,%B2\;addc\\tw,$ff\;mov\\t%B0,w\;mov\\tw,%A2\;addc\\tw,$ff\;mov\\t%A0,w")

;;
;; Add 64-bit integers.
;;

(define_expand "adddi3"
  [(set (match_operand:DI 0 "nonimmediate_operand" "")
	(plus:DI (match_operand:DI 1 "general_operand" "")
		 (match_operand:DI 2 "general_operand" "")))]
  ""
  "")

(define_insn "*push_adddi3"
  [(set (match_operand:DI 0 "push_operand"                  "=<,<,  <")
        (plus:DI (match_operand:DI 1 "nonimmediate_operand" "%g,g,  g")
		 (match_operand:DI 2 "general_operand"       "N,M,rSi")))]
  ""
  "*{
    switch (which_alternative)
      {
        case 0:
          OUT_AS1 (push, %Z1%<);
          OUT_AS1 (push, %Y1%<);
          OUT_AS1 (push, %X1%<);
          OUT_AS1 (push, %W1%<);
          OUT_AS1 (push, %V1%<);
          OUT_AS1 (push, %U1%<);
          OUT_AS1 (push, %T1%<);
          OUT_AS1 (push, %S1%>%>%>%>%>%>%>);
          OUT_AS1 (incsnz, 8(SP));
          OUT_AS1 (incsz, 7(SP));
          OUT_AS1 (page, 1f);
          OUT_AS1 (jmp, 1f);
          OUT_AS1 (incsnz, 6(SP));
          OUT_AS1 (incsz, 5(SP));
          OUT_AS1 (page, 1f);
          OUT_AS1 (jmp, 1f);
          OUT_AS1 (incsnz, 4(SP));
          OUT_AS1 (incsz, 3(SP));
          OUT_AS1 (page, 1f);
          OUT_AS1 (jmp, 1f);
          OUT_AS1 (incsnz, 2(SP));
          OUT_AS1 (inc, 1(SP));
          OUT_AS1 (1:,);
          return \"\";

        case 1:
          OUT_AS1 (push, %Z1%<);
          OUT_AS1 (push, %Y1%<);
          OUT_AS1 (push, %X1%<);
          OUT_AS1 (push, %W1%<);
          OUT_AS1 (push, %V1%<);
          OUT_AS1 (push, %U1%<);
          OUT_AS1 (push, %T1%<);
          OUT_AS1 (push, %S1%>%>%>%>%>%>%>);
          OUT_AS2 (mov, w, #-1);
          OUT_AS2 (add, 8(SP), w);
          OUT_AS2 (addc, 7(SP), w);
          OUT_AS2 (addc, 6(SP), w);
          OUT_AS2 (addc, 5(SP), w);
          OUT_AS2 (addc, 4(SP), w);
          OUT_AS2 (addc, 3(SP), w);
          OUT_AS2 (addc, 2(SP), w);
          OUT_AS2 (addc, 1(SP), w);
          return \"\";

        case 2:
          OUT_AS2 (mov, w, %Z2);
          OUT_AS2 (add, w, %Z1);
          OUT_AS1 (push, wreg%<);
          OUT_AS2 (mov, w, %Y2);
          OUT_AS2 (addc, w, %Y1);
          OUT_AS1 (push, wreg%<);
          OUT_AS2 (mov, w, %X2);
          OUT_AS2 (addc, w, %X1);
          OUT_AS1 (push, wreg%<);
          OUT_AS2 (mov, w, %W2);
          OUT_AS2 (addc, w, %W1);
          OUT_AS1 (push, wreg%<);
          OUT_AS2 (mov, w, %V2);
          OUT_AS2 (addc, w, %V1);
          OUT_AS1 (push, wreg%<);
          OUT_AS2 (mov, w, %U2);
          OUT_AS2 (addc, w, %U1);
          OUT_AS1 (push, wreg%<);
          OUT_AS2 (mov, w, %T2);
          OUT_AS2 (addc, w, %T1);
          OUT_AS1 (push, wreg%<);
          OUT_AS2 (mov, w, %S2);
          OUT_AS2 (addc, w, %S1);
          OUT_AS1 (push, wreg%>%>%>%>%>%>%>);
          return \"\";

        default:
          abort();
      }
  }"
  [(set_attr "clobberw" "no,yes,yes")])

(define_insn "*adddi3" ;			 0  1   2   3   4   5   6
  [(set
    (match_operand:DI 0 "nonimmediate_operand" "=ro,ro, ro, rS,&ro,&rS,&rS")
    (plus:DI
     (match_operand:DI 1 "nonimmediate_operand" "%0, 0,  0,  0, rS, ro, rS")
     (match_operand:DI 2 "general_operand"	 "N, M,rSi,roi,rSi,rSi,roi")))]
  ""
  "*{
    switch (which_alternative)
      {
        case 0:
          OUT_AS1 (incsnz, %Z0);
          OUT_AS1 (incsz, %Y0);
          OUT_AS1 (page, 1f);
          OUT_AS1 (jmp, 1f);
          OUT_AS1 (incsnz, %X0);
          OUT_AS1 (incsz, %W0);
          OUT_AS1 (page, 1f);
          OUT_AS1 (jmp, 1f);
          OUT_AS1 (incsnz, %V0);
          OUT_AS1 (incsz, %U0);
          OUT_AS1 (page, 1f);
          OUT_AS1 (jmp, 1f);
          OUT_AS1 (incsnz, %T0);
          OUT_AS1 (inc, %S0);
          OUT_AS1 (1:, );
          return \"\";

        case 1:
          OUT_AS2 (mov, w, #-1);
          OUT_AS2 (add, %Z0, w);
          OUT_AS2 (addc, %Y0, w);
          OUT_AS2 (addc, %X0, w);
          OUT_AS2 (addc, %W0, w);
          OUT_AS2 (addc, %V0, w);
          OUT_AS2 (addc, %U0, w);
          OUT_AS2 (addc, %T0, w);
          OUT_AS2 (addc, %S0, w);
          return \"\";

        case 2:
        case 3:
          OUT_AS2 (mov, w, %Z2);
          OUT_AS2 (add, %Z0, w);
          OUT_AS2 (mov, w, %Y2);
          OUT_AS2 (addc, %Y0, w);
          OUT_AS2 (mov, w, %X2);
          OUT_AS2 (addc, %X0, w);
          OUT_AS2 (mov, w, %W2);
          OUT_AS2 (addc, %W0, w);
          OUT_AS2 (mov, w, %V2);
          OUT_AS2 (addc, %V0, w);
          OUT_AS2 (mov, w, %U2);
          OUT_AS2 (addc, %U0, w);
          OUT_AS2 (mov, w, %T2);
          OUT_AS2 (addc, %T0, w);
          OUT_AS2 (mov, w, %S2);
          OUT_AS2 (addc, %S0, w);
          return \"\";

        case 4:
        case 5:
        case 6:
          OUT_AS2 (mov, w, %Z2);
          OUT_AS2 (add, w, %Z1);
          OUT_AS2 (mov, %Z0, w);
          OUT_AS2 (mov, w, %Y2);
          OUT_AS2 (addc, w, %Y1);
          OUT_AS2 (mov, %Y0, w);
          OUT_AS2 (mov, w, %X2);
          OUT_AS2 (addc, w, %X1);
          OUT_AS2 (mov, %X0, w);
          OUT_AS2 (mov, w, %W2);
          OUT_AS2 (addc, w, %W1);
          OUT_AS2 (mov, %W0, w);
          OUT_AS2 (mov, w, %V2);
          OUT_AS2 (addc, w, %V1);
          OUT_AS2 (mov, %V0, w);
          OUT_AS2 (mov, w, %U2);
          OUT_AS2 (addc, w, %U1);
          OUT_AS2 (mov, %U0, w);
          OUT_AS2 (mov, w, %T2);
          OUT_AS2 (addc, w, %T1);
          OUT_AS2 (mov, %T0, w);
          OUT_AS2 (mov, w, %S2);
          OUT_AS2 (addc, w, %S1);
          OUT_AS2 (mov, %S0, w);
          return \"\";

        default:
          abort();
      }
  }"
  [(set_attr "clobberw" "no,yes,yes,yes,yes,yes,yes")])

(define_insn "*adddi3_zero_extendqi" ;		       0   1   2   3   4
  [(set (match_operand:DI 0 "nonimmediate_operand"  "=ro, rS,&ro,&rS,&rS")
        (plus:DI
	 (zero_extend:DI
	  (match_operand:QI 1 "nonimmediate_operand" "rS,roR, rS,roR, rS"))
	 (match_operand:DI 2 "general_operand"        "0,  0,rSi,rSi,roi")))]
  ""
  "*{
    switch (which_alternative)
      {
        case 0:
        case 1:
	  OUT_AS2 (mov, w, %1);
	  OUT_AS2 (add, %Z0, w);
	  OUT_AS1 (clr, wreg);
	  OUT_AS2 (addc, %Y0, w);
	  OUT_AS2 (addc, %X0, w);
	  OUT_AS2 (addc, %W0, w);
	  OUT_AS2 (addc, %V0, w);
	  OUT_AS2 (addc, %U0, w);
	  OUT_AS2 (addc, %T0, w);
	  OUT_AS2 (addc, %S0, w);
          return \"\";

        case 2:
        case 3:
        case 4:
	  OUT_AS2 (mov, w, %1);
	  OUT_AS2 (add, w, %Z2);
	  OUT_AS2 (mov, %Z0, w);
	  OUT_AS2 (mov, w, %Y2);
	  OUT_AS2 (addc, w, $ff);
	  OUT_AS2 (mov, %Y0, w);
	  OUT_AS2 (mov, w, %X2);
	  OUT_AS2 (addc, w, $ff);
	  OUT_AS2 (mov, %X0, w);
	  OUT_AS2 (mov, w, %W2);
	  OUT_AS2 (addc, w, $ff);
	  OUT_AS2 (mov, %W0, w);
	  OUT_AS2 (mov, w, %V2);
	  OUT_AS2 (addc, w, $ff);
	  OUT_AS2 (mov, %V0, w);
	  OUT_AS2 (mov, w, %U2);
	  OUT_AS2 (addc, w, $ff);
	  OUT_AS2 (mov, %U0, w);
	  OUT_AS2 (mov, w, %T2);
	  OUT_AS2 (addc, w, $ff);
	  OUT_AS2 (mov, %T0, w);
	  OUT_AS2 (mov, w, %S2);
	  OUT_AS2 (addc, w, $ff);
	  OUT_AS2 (mov, %S0, w);
          return \"\";

        default:
          abort();
      }
  }")

(define_insn "*adddi3_zero_extendhi" ;		      0  1   2   3   4
  [(set (match_operand:DI 0 "nonimmediate_operand"  "=ro,rS,&ro,&rS,&rS")
        (plus:DI
	 (zero_extend:DI
	  (match_operand:HI 1 "nonimmediate_operand" "rS,ro, rS, ro, rS"))
	 (match_operand:DI 2 "general_operand"        "0, 0,rSi,rSi,roi")))]
  ""
  "*{
    switch (which_alternative)
      {
        case 0:
        case 1:
          OUT_AS2 (mov, w, %L1);
	  OUT_AS2 (add, %Z0, w);
	  OUT_AS2 (mov, w, %H1);
	  OUT_AS2 (addc, %Y0, w);
	  OUT_AS1 (clr, wreg);
	  OUT_AS2 (addc, %X0, w);
	  OUT_AS2 (addc, %W0, w);
	  OUT_AS2 (addc, %V0, w);
	  OUT_AS2 (addc, %U0, w);
	  OUT_AS2 (addc, %T0, w);
	  OUT_AS2 (addc, %S0, w);
	  return \"\";

        case 2:
        case 3:
        case 4:
	  OUT_AS2 (mov, w, %L1);
	  OUT_AS2 (add, w, %Z2);
	  OUT_AS2 (mov, %Z0, w);
	  OUT_AS2 (mov, w, %Y2);
	  OUT_AS2 (addc, w, %H1);
	  OUT_AS2 (mov, %Y0, w);
	  OUT_AS2 (mov, w, %X2);
	  OUT_AS2 (addc, w, $ff);
	  OUT_AS2 (mov, %X0, w);
	  OUT_AS2 (mov, w, %W2);
	  OUT_AS2 (addc, w, $ff);
	  OUT_AS2 (mov, %W0, w);
	  OUT_AS2 (mov, w, %V2);
	  OUT_AS2 (addc, w, $ff);
	  OUT_AS2 (mov, %V0, w);
	  OUT_AS2 (mov, w, %U2);
	  OUT_AS2 (addc, w, $ff);
	  OUT_AS2 (mov, %U0, w);
	  OUT_AS2 (mov, w, %T2);
	  OUT_AS2 (addc, w, $ff);
	  OUT_AS2 (mov, %T0, w);
	  OUT_AS2 (mov, w, %S2);
	  OUT_AS2 (addc, w, $ff);
	  OUT_AS2 (mov, %S0, w);
          return \"\";

        default:
          abort();
      }
  }")

(define_insn "*adddi3_zero_extendsi" ;		       0  1   2   3   4
  [(set (match_operand:DI 0 "nonimmediate_operand"  "=ro,rS,&ro,&rS,&rS")
        (plus:DI
	 (zero_extend:DI
	  (match_operand:SI 1 "nonimmediate_operand" "rS,ro, rS, ro, rS"))
	 (match_operand:DI 2 "general_operand"        "0, 0,rSi,rSi,roi")))]
  ""
  "*{
    switch (which_alternative)
      {
        case 0:
        case 1:
          OUT_AS2 (mov, w, %D1);
	  OUT_AS2 (add, %Z0, w);
	  OUT_AS2 (mov, w, %C1);
	  OUT_AS2 (addc, %Y0, w);
	  OUT_AS2 (mov, w, %B1);
	  OUT_AS2 (addc, %X0, w);
	  OUT_AS2 (mov, w, %A1);
	  OUT_AS2 (addc, %W0, w);
	  OUT_AS1 (clr, wreg);
	  OUT_AS2 (addc, %V0, w);
	  OUT_AS2 (addc, %U0, w);
	  OUT_AS2 (addc, %T0, w);
	  OUT_AS2 (addc, %S0, w);
          return \"\";

        case 2:
        case 3:
        case 4:
	  OUT_AS2 (mov, w, %D1);
	  OUT_AS2 (add, w, %Z2);
	  OUT_AS2 (mov, %Z0, w);
	  OUT_AS2 (mov, w, %Y2);
	  OUT_AS2 (addc, w, %C1);
	  OUT_AS2 (mov, %Y0, w);
	  OUT_AS2 (mov, w, %X2);
	  OUT_AS2 (addc, w, %B1);
	  OUT_AS2 (mov, %X0, w);
	  OUT_AS2 (mov, w, %W2);
	  OUT_AS2 (addc, w, %A1);
	  OUT_AS2 (mov, %W0, w);
	  OUT_AS2 (mov, w, %V2);
	  OUT_AS2 (addc, w, $ff);
	  OUT_AS2 (mov, %V0, w);
	  OUT_AS2 (mov, w, %U2);
	  OUT_AS2 (addc, w, $ff);
	  OUT_AS2 (mov, %U0, w);
	  OUT_AS2 (mov, w, %T2);
	  OUT_AS2 (addc, w, $ff);
	  OUT_AS2 (mov, %T0, w);
	  OUT_AS2 (mov, w, %S2);
	  OUT_AS2 (addc, w, $ff);
	  OUT_AS2 (mov, %S0, w);
          return \"\";

        default:
          abort();
      }
  }")

;;
;; Subtract bytes.
;;

(define_expand "subqi3"
  [(set (match_operand:QI 0 "nonimmediate_operand" "")
        (minus:QI (match_operand:QI 1 "general_operand" "")
		  (match_operand:QI 2 "general_operand" "")))]
  ""
  "if (GET_CODE (operands[2]) == CONST_INT)
     {
       emit_insn (gen_addqi3 (operands[0], operands[1],
                              gen_int_mode (-INTVAL (operands[2]), QImode)));
       DONE;
     }
  ")

(define_insn "*push_subqi3"
  [(set (match_operand:QI 0 "push_operand"               "=<,  <")
  	(minus:QI (match_operand:QI 1 "general_operand"   "g,rSn")
		  (match_operand:QI 2 "general_operand" "rSn,  g")))]
  ""
  "@
   push\\t%1%<\;mov\\tw,%2\;sub\\t1(SP),w%>
   push\\t%1%<\;mov\\tw,%2\;sub\\t1(SP),w%>")

(define_insn "*subqi3_w"
  [(set (reg:QI 10)
	(minus:QI (match_operand:QI 0 "general_operand"  "rS,rSi,  g,rSi")
		  (match_operand:QI 1 "general_operand" "rSi, rS,rSi,  g")))]
  "(ip2k_reorg_split_qimode)"
  "@
   mov\\tw,%1\;sub\\tw,%0
   mov\\tw,%1\;sub\\tw,%0
   mov\\tw,%1\;sub\\tw,%0
   mov\\tw,%1\;sub\\tw,%0")

(define_insn_and_split "*subqi3"
  [(set
    (match_operand:QI
     0 "nonimmediate_operand" "=k,k,z,z,djyoR,djyoR,djyS,djyoR,  g,  g, rS, rS")
    (minus:QI
     (match_operand:QI
      1 "general_operand"     "0,0,0,0,    0,    0,   0,    0, rS,rSi,  g,rSi")
     (match_operand:QI
      2 "general_operand"     "M,g,M,g,    M,    N,   g,  rSi,rSi, rS,rSi,  g")))]
  ""
  "@
   incsnz\\t%0\;dec\\tiph
   mov\\tw,%2\;sub\\t%0,w
   incsnz\\t%0\;dec\\tdph
   mov\\tw,%2\;sub\\t%0,w
   inc\\t%0
   dec\\t%0
   mov\\tw,%2\;sub\\t%0,w
   mov\\tw,%2\;sub\\t%0,w
   #
   #
   #
   #"
  "(ip2k_reorg_split_qimode
    && ! rtx_equal_p (operands[0], operands[1]))"
  [(set (reg:QI 10)
  	(minus:QI (match_dup 1)
		  (match_dup 2)))
   (set (match_dup 0)
   	(reg:QI 10))]
  ""
  [(set_attr "skip" "no,no,no,no,yes,yes,no,no,no,no,no,no")
   (set_attr "clobberw" "no,yes,no,yes,no,no,yes,yes,yes,yes,yes,yes")])

;;
;; Subtract 16-bit integers.
;;

(define_expand "subhi3"
  [(set (match_operand:HI 0 "nonimmediate_operand" "")
        (minus:HI (match_operand:HI 1 "general_operand" "")
		  (match_operand:HI 2 "general_operand" "")))]
  ""
  "if (GET_CODE (operands[2]) == CONST_INT)
     {
       emit_insn (gen_addhi3 (operands[0], operands[1],
                              gen_int_mode (-INTVAL (operands[2]), HImode)));
       DONE;
     }
  ")

(define_insn "*push_subhi3"
  [(set (match_operand:HI 0 "push_operand"               "=<,  <")
  	(minus:HI (match_operand:HI 1 "general_operand" "ron,rSn")
		  (match_operand:HI 2 "general_operand" "rSn,ron")))]
  ""
  "@
   push\\t%L1%<\;mov\\tw,%L2\;sub\\t1(SP),w\;push\\t%H1%<\;mov\\tw,%H2\;subc\\t1(SP),w%>%>
   push\\t%L1%<\;mov\\tw,%L2\;sub\\t1(SP),w\;push\\t%H1%<\;mov\\tw,%H2\;subc\\t1(SP),w%>%>")

(define_insn "*subhi3_imm"
  [(set
    (match_operand:HI 0 "nonimmediate_operand"     "=a,a,a,a,do,&r,&ro,&rS")
    (minus:HI (match_operand:HI 1 "general_operand" "0,0,0,0, 0,ro, rS, ro")
	      (match_operand 2 "immediate_operand"  "N,M,P,i, i, O,  i,  i")))]
  ""
  "@
   dec\\t%L0
   inc\\t%L0
   mov\\tw,%2\;sub\\t%L0,w
   mov\\tw,%L2\;sub\\t%L0,w\;mov\\tw,%H2\;sub\\t%H0,w
   mov\\tw,%L2\;sub\\t%L0,w\;mov\\tw,%H2\;subc\\t%H0,w
   mov\\tw,%L1\;mov\\t%L0,w\;mov\\tw,%H1\;mov\\t%H0,w
   mov\\tw,%L2\;sub\\tw,%L1\;mov\\t%L0,w\;mov\\tw,%H2\;subc\\tw,%H1\;mov\\t%H0,w
   mov\\tw,%L2\;sub\\tw,%L1\;mov\\t%L0,w\;mov\\tw,%H2\;subc\\tw,%H1\;mov\\t%H0,w"
  [(set_attr "skip" "yes,yes,no,no,no,no,no,no")
   (set_attr "clobberw" "no,no,yes,yes,yes,yes,yes,yes")])

(define_insn "*subhi3_ximm_zero_extend"
  [(set (match_operand:HI 0 "nonimmediate_operand"            "=ro, rS")
  	(minus:HI (match_operand:HI 1 "immediate_operand"       "i,  i")
		  (zero_extend:HI
		    (match_operand:QI 2 "nonimmediate_operand" "rS,roR"))))]
  ""
  "@
   mov\\tw,%2\;sub\\tw,%L1\;mov\\t%L0,w\;mov\\tw,%H1\;mov\\t%H0,w\;clr\\twreg\;subc\\t%H0,w
   mov\\tw,%2\;sub\\tw,%L1\;mov\\t%L0,w\;mov\\tw,%H1\;mov\\t%H0,w\;clr\\twreg\;subc\\t%H0,w")

(define_insn "*subhi3_ximm"
  [(set (match_operand:HI 0 "nonimmediate_operand"         "=&uo,&ro,&rS")
  	(minus:HI (match_operand:HI 1 "immediate_operand"     "i,  i,  i")
		  (match_operand:HI 2 "nonimmediate_operand"  "0, rS, ro")))]
  ""
  "@
   mov\\tw,%L2\;sub\\tw,%L1\;mov\\t%L0,w\;push\\t%H2%<\;mov\\tw,%H1\;mov\\t%H0,w\;pop\\twreg%>\;subc\\t%H0,w
   mov\\tw,%L2\;sub\\tw,%L1\;mov\\t%L0,w\;mov\\tw,%H1\;mov\\t%H0,w\;mov\\tw,%H2\;subc\\t%H0,w
   mov\\tw,%L2\;sub\\tw,%L1\;mov\\t%L0,w\;mov\\tw,%H1\;mov\\t%H0,w\;mov\\tw,%H2\;subc\\t%H0,w")

(define_insn "*subhi3_nonimm_zero_extend"
  [(set
    (match_operand:HI 0 "nonimmediate_operand" "=a,ro, rS,&ro,&rS,&rS")
    (minus:HI
     (match_operand:HI 1 "nonimmediate_operand" "0, 0,  0, rS, ro, rS")
     (zero_extend:HI
      (match_operand:QI 2 "general_operand"   "roR,rS,roR, rS, rS,roR"))))]
  ""
  "@
   mov\\tw,%2\;sub\\t%L0,w
   mov\\tw,%2\;sub\\t%L0,w\;clr\\twreg\;subc\\t%H0,w
   mov\\tw,%2\;sub\\t%L0,w\;clr\\twreg\;subc\\t%H0,w
   mov\\tw,%2\;sub\\tw,%L1\;mov\\t%L0,w\;clr\\twreg\;subc\\tw,%H1\;mov\\t%H0,w
   mov\\tw,%2\;sub\\tw,%L1\;mov\\t%L0,w\;clr\\twreg\;subc\\tw,%H1\;mov\\t%H0,w
   mov\\tw,%2\;sub\\tw,%L1\;mov\\t%L0,w\;clr\\twreg\;subc\\tw,%H1\;mov\\t%H0,w")

(define_insn "*subhi3_nonimm" ;		          0  1  2   3   4   5  6
  [(set
    (match_operand:HI 0 "nonimmediate_operand"  "=a,dS, o,&rS,&rS,&rS, o")
    (minus:HI
     (match_operand:HI 1 "nonimmediate_operand"  "0, 0, 0, ro, ro, rS,rS")
     (match_operand:HI 2 "nonimmediate_operand" "ro,ro,rS,  0, rS, ro,rS")))]
  ""
  "@
   mov\\tw,%L2\;sub\\t%L0,w\;mov\\tw,%H2\;sub\\t%H0,w
   mov\\tw,%L2\;sub\\t%L0,w\;mov\\tw,%H2\;subc\\t%H0,w
   mov\\tw,%L2\;sub\\t%L0,w\;mov\\tw,%H2\;subc\\t%H0,w
   mov\\tw,%L2\;sub\\tw,%L1\;mov\\t%L0,w\;mov\\tw,%H2\;subc\\tw,%H1\;mov\\t%H0,w
   mov\\tw,%L2\;sub\\tw,%L1\;mov\\t%L0,w\;mov\\tw,%H2\;subc\\tw,%H1\;mov\\t%H0,w
   mov\\tw,%L2\;sub\\tw,%L1\;mov\\t%L0,w\;mov\\tw,%H2\;subc\\tw,%H1\;mov\\t%H0,w
   mov\\tw,%L2\;sub\\tw,%L1\;mov\\t%L0,w\;mov\\tw,%H2\;subc\\tw,%H1\;mov\\t%H0,w")

;;
;; Subtract 32-bit integers.
;;

(define_insn "subsi3" ;	        0  1   2   3   4   5   6   7   8   9   a   b
  [(set
    (match_operand:SI
     0 "nonimmediate_operand" "=ro,ro, ro, rS,&ro,&rS,&ro,&rS,&rS,&ro,&ro,&rS")
    (minus:SI
     (match_operand:SI
      1 "general_operand"    "0, 0,  0,  0,  i, ro, rS, rS, ro, rS,  i,  i")
     (match_operand:SI
      2 "general_operand"    "M, N,rSi,roi,  0,  0,  0,roi,rSi,rSi, rS, ro")))]
  ""
  "*{
    switch (which_alternative) {
    case 0:
      return AS2 (mov, w, #1) CR_TAB
             AS2 (add, %D0, w) CR_TAB
	     AS1 (clr, wreg) CR_TAB
	     AS2 (addc, %C0, w) CR_TAB
	     AS2 (addc, %B0, w) CR_TAB
	     AS2 (addc, %A0, w);
      
    case 1:
      return AS2 (mov, w, #-1) CR_TAB
             AS2 (sub, %D0, w) CR_TAB
	     AS2 (subc, %C0, w) CR_TAB
	     AS2 (subc, %B0, w) CR_TAB
	     AS2 (subc, %A0, w);
      
    case 2:
    case 3:
      return AS2 (mov, w, %D2) CR_TAB
             AS2 (sub, %D0, w) CR_TAB
	     AS2 (mov, w, %C2) CR_TAB
	     AS2 (subc, %C0, w) CR_TAB
	     AS2 (mov, w, %B2) CR_TAB
	     AS2 (subc, %B0, w) CR_TAB
	     AS2 (mov, w, %A2) CR_TAB
	     AS2 (subc, %A0, w);

    case 4:
      return AS2 (mov, w, %D2) CR_TAB
             AS2 (sub, w, %D1) CR_TAB
	     AS2 (mov, %D0, w) CR_TAB
	     AS1 (push, %C2%<) CR_TAB
	     AS2 (mov, w, %C1) CR_TAB
	     AS2 (mov, %C0, w) CR_TAB
	     AS1 (pop, wreg%>) CR_TAB
	     AS2 (subc, %C0, w) CR_TAB
	     AS1 (push, %B2%<) CR_TAB
	     AS2 (mov, w, %B1) CR_TAB
	     AS2 (mov, %B0, w) CR_TAB
	     AS1 (pop, wreg%>) CR_TAB
	     AS2 (subc, %B0, w) CR_TAB
	     AS1 (push, %A2%<) CR_TAB
	     AS2 (mov, w, %A1) CR_TAB
	     AS2 (mov, %A0, w) CR_TAB
	     AS1 (pop, wreg%>) CR_TAB
	     AS2 (subc, %A0, w);

    case 5:
    case 6:
    case 7:
    case 8:
    case 9:
      return AS2 (mov, w, %D2) CR_TAB
             AS2 (sub, w, %D1) CR_TAB
	     AS2 (mov, %D0, w) CR_TAB
	     AS2 (mov, w, %C2) CR_TAB
	     AS2 (subc, w, %C1) CR_TAB
	     AS2 (mov, %C0, w) CR_TAB
	     AS2 (mov, w, %B2) CR_TAB
	     AS2 (subc, w, %B1) CR_TAB
	     AS2 (mov, %B0, w) CR_TAB
	     AS2 (mov, w, %A2) CR_TAB
	     AS2 (subc, w, %A1) CR_TAB
	     AS2 (mov, %A0, w);

    case 10:
    case 11:
      return AS2 (mov, w, %D2) CR_TAB
             AS2 (sub, w, %D1) CR_TAB
	     AS2 (mov, %D0, w) CR_TAB
	     AS2 (mov, w, %C1) CR_TAB
	     AS2 (mov, %C0, w) CR_TAB
	     AS2 (mov, w, %C2) CR_TAB
	     AS2 (subc, %C0, w) CR_TAB
	     AS2 (mov, w, %B1) CR_TAB
	     AS2 (mov, %B0, w) CR_TAB
	     AS2 (mov, w, %B2) CR_TAB
	     AS2 (subc, %B0, w) CR_TAB
	     AS2 (mov, w, %A1) CR_TAB
	     AS2 (mov, %A0, w) CR_TAB
	     AS2 (mov, w, %A2) CR_TAB
	     AS2 (subc, %A0, w);
    default:
      abort ();
    }
  }")

;;
;; Subtract 64-bit integers.
;;

(define_insn "subdi3" ;	        0  1   2   3  4   5   6   7   8   9   a   b
  [(set
    (match_operand:DI
     0 "nonimmediate_operand" "=ro,ro, ro, rS,ro,&rS,&ro,&rS,&rS,&ro,&ro,&rS")
    (minus:DI
     (match_operand:DI
      1 "general_operand"      "0, 0,  0,  0, i, ro, rS, rS, ro, rS,  i,  i")
     (match_operand:DI
      2 "general_operand"     "M, N,rSi,roi, 0,  0,  0,roi,rSi,rSi, rS, ro")))]
  ""
  "*{
    switch (which_alternative) {
    case 0:
      return AS2 (mov, w, #1) CR_TAB
             AS2 (add, %Z0, w) CR_TAB
	     AS1 (clr, wreg) CR_TAB
	     AS2 (addc, %Y0, w) CR_TAB
	     AS2 (addc, %X0, w) CR_TAB
	     AS2 (addc, %W0, w) CR_TAB
	     AS2 (addc, %V0, w) CR_TAB
	     AS2 (addc, %U0, w) CR_TAB
	     AS2 (addc, %T0, w) CR_TAB
	     AS2 (addc, %S0, w);
      
    case 1:
      return AS2 (mov, w, #-1) CR_TAB
             AS2 (sub, %Z0, w) CR_TAB
	     AS2 (subc, %Y0, w) CR_TAB
	     AS2 (subc, %X0, w) CR_TAB
	     AS2 (subc, %W0, w) CR_TAB
	     AS2 (subc, %V0, w) CR_TAB
	     AS2 (subc, %U0, w) CR_TAB
	     AS2 (subc, %T0, w) CR_TAB
	     AS2 (subc, %S0, w);
      
    case 2:
    case 3:
      return AS2 (mov, w, %Z2) CR_TAB
             AS2 (sub, %Z0, w) CR_TAB
	     AS2 (mov, w, %Y2) CR_TAB
	     AS2 (subc, %Y0, w) CR_TAB
	     AS2 (mov, w, %X2) CR_TAB
	     AS2 (subc, %X0, w) CR_TAB
	     AS2 (mov, w, %W2) CR_TAB
	     AS2 (subc, %W0, w) CR_TAB
	     AS2 (mov, w, %V2) CR_TAB
	     AS2 (subc, %V0, w) CR_TAB
	     AS2 (mov, w, %U2) CR_TAB
	     AS2 (subc, %U0, w) CR_TAB
	     AS2 (mov, w, %T2) CR_TAB
	     AS2 (subc, %T0, w) CR_TAB
	     AS2 (mov, w, %S2) CR_TAB
	     AS2 (subc, %S0, w);

    case 4:
      return AS2 (mov, w, %Z2) CR_TAB
             AS2 (sub, w, %Z1) CR_TAB
	     AS2 (mov, %Z0, w) CR_TAB
	     AS1 (push, %Y2%<) CR_TAB
	     AS2 (mov, w, %Y1) CR_TAB
	     AS2 (mov, %Y0, w) CR_TAB
	     AS1 (pop, wreg%>) CR_TAB
	     AS2 (subc, %Y0, w) CR_TAB
	     AS1 (push, %X2%<) CR_TAB
	     AS2 (mov, w, %X1) CR_TAB
	     AS2 (mov, %X0, w) CR_TAB
	     AS1 (pop, wreg%>) CR_TAB
	     AS2 (subc, %X0, w) CR_TAB
	     AS1 (push, %W2%<) CR_TAB
	     AS2 (mov, w, %W1) CR_TAB
	     AS2 (mov, %W0, w) CR_TAB
	     AS1 (pop, wreg%>) CR_TAB
	     AS2 (subc, %W0, w) CR_TAB
	     AS1 (push, %V2%<) CR_TAB
	     AS2 (mov, w, %V1) CR_TAB
	     AS2 (mov, %V0, w) CR_TAB
	     AS1 (pop, wreg%>) CR_TAB
	     AS2 (subc, %V0, w) CR_TAB
	     AS1 (push, %U2%<) CR_TAB
	     AS2 (mov, w, %U1) CR_TAB
	     AS2 (mov, %U0, w) CR_TAB
	     AS1 (pop, wreg%>) CR_TAB
	     AS2 (subc, %U0, w) CR_TAB
	     AS1 (push, %T2%<) CR_TAB
	     AS2 (mov, w, %T1) CR_TAB
	     AS2 (mov, %T0, w) CR_TAB
	     AS1 (pop, wreg%>) CR_TAB
	     AS2 (subc, %T0, w) CR_TAB
	     AS1 (push, %S2%<) CR_TAB
	     AS2 (mov, w, %S1) CR_TAB
	     AS2 (mov, %S0, w) CR_TAB
	     AS1 (pop, wreg%>) CR_TAB
	     AS2 (subc, %S0, w);

    case 5:
    case 6:
    case 7:
    case 8:
    case 9:
      return AS2 (mov, w, %Z2) CR_TAB
             AS2 (sub, w, %Z1) CR_TAB
	     AS2 (mov, %Z0, w) CR_TAB
	     AS2 (mov, w, %Y2) CR_TAB
	     AS2 (subc, w, %Y1) CR_TAB
	     AS2 (mov, %Y0, w) CR_TAB
	     AS2 (mov, w, %X2) CR_TAB
	     AS2 (subc, w, %X1) CR_TAB
	     AS2 (mov, %X0, w) CR_TAB
	     AS2 (mov, w, %W2) CR_TAB
	     AS2 (subc, w, %W1) CR_TAB
	     AS2 (mov, %W0, w) CR_TAB
	     AS2 (mov, w, %V2) CR_TAB
	     AS2 (subc, w, %V1) CR_TAB
	     AS2 (mov, %V0, w) CR_TAB
	     AS2 (mov, w, %U2) CR_TAB
	     AS2 (subc, w, %U1) CR_TAB
	     AS2 (mov, %U0, w) CR_TAB
	     AS2 (mov, w, %T2) CR_TAB
	     AS2 (subc, w, %T1) CR_TAB
	     AS2 (mov, %T0, w) CR_TAB
	     AS2 (mov, w, %S2) CR_TAB
	     AS2 (subc, w, %S1) CR_TAB
	     AS2 (mov, %S0, w);

    case 10:
    case 11:
      return AS2 (mov, w, %Z2) CR_TAB
             AS2 (sub, w, %Z1) CR_TAB
	     AS2 (mov, %Z0, w) CR_TAB
	     AS2 (mov, w, %Y1) CR_TAB
	     AS2 (mov, %Y0, w) CR_TAB
	     AS2 (mov, w, %Y2) CR_TAB
	     AS2 (subc, %Y0, w) CR_TAB
	     AS2 (mov, w, %X1) CR_TAB
	     AS2 (mov, %X0, w) CR_TAB
	     AS2 (mov, w, %X2) CR_TAB
	     AS2 (subc, %X0, w) CR_TAB
	     AS2 (mov, w, %W1) CR_TAB
	     AS2 (mov, %W0, w) CR_TAB
	     AS2 (mov, w, %W2) CR_TAB
	     AS2 (subc, %W0, w) CR_TAB
	     AS2 (mov, w, %V1) CR_TAB
	     AS2 (mov, %V0, w) CR_TAB
	     AS2 (mov, w, %V2) CR_TAB
	     AS2 (subc, %V0, w) CR_TAB
	     AS2 (mov, w, %U1) CR_TAB
	     AS2 (mov, %U0, w) CR_TAB
	     AS2 (mov, w, %U2) CR_TAB
	     AS2 (subc, %U0, w) CR_TAB
	     AS2 (mov, w, %T1) CR_TAB
	     AS2 (mov, %T0, w) CR_TAB
	     AS2 (mov, w, %T2) CR_TAB
	     AS2 (subc, %T0, w) CR_TAB
	     AS2 (mov, w, %S1) CR_TAB
	     AS2 (mov, %S0, w) CR_TAB
	     AS2 (mov, w, %S2) CR_TAB
	     AS2 (subc, %S0, w);
    default:
      abort ();
    }
  }")

;;
;; Bitwise and instructions.
;;

(define_expand "andqi3"
  [(set (match_operand:QI 0 "nonimmediate_operand" "")
  	(and:QI (match_operand:QI 1 "nonimmediate_operand" "")
		(match_operand:QI 2 "general_operand" "")))]
  ""
  "")

(define_insn "*andqi3_bit"
  [(set (match_operand:QI 0 "nonimmediate_operand"       "=roR")
        (and:QI (match_dup 0)
                (match_operand:QI 1 "const_int_operand"     "n")))]
  "(find_one_clear_bit_p (INTVAL (operands[1]) | 0xffffff00UL) != -1)"
  "*{
    operands[2] = GEN_INT (find_one_clear_bit_p (INTVAL (operands[1])
                                                 | 0xffffff00UL));
    return AS2 (clrb, %0, %b2);
  }"
  [(set_attr "skip" "yes")
   (set_attr "clobberw" "no")])

(define_insn "*andqi3_w_fr"
  [(set (reg:QI 10)
        (and:QI (match_operand:QI 0 "general_operand" "roRn")
		(reg:QI 10)))]
  "(ip2k_reorg_split_qimode)"
  "and\\tw,%0"
  [(set_attr "skip" "yes")])

(define_insn_and_split "*andqi3_fr_w"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=roR,rS,roR, rS,rS")
        (and:QI
	 (match_operand:QI 1 "nonimmediate_operand"  "%0, 0, rS,roR,rS")
	 (reg:QI 10)))]
  "(ip2k_reorg_split_qimode)"
  "@
   and\\t%0,w
   and\\t%0,w
   # 
   #
   #"
  "(ip2k_reorg_split_qimode
    && ! rtx_equal_p (operands[0], operands[1]))"
  [(set (reg:QI 10)
   	(and:QI (match_dup 1)
		(reg:QI 10)))
   (set (match_dup 0)
   	(reg:QI 10))]
  ""
  [(set_attr "skip" "yes,yes,no,no,no")
   (set_attr "clobberw" "no,no,yes,yes,yes")])

(define_insn_and_split "*andqi3" ;		      0    1   2   3    4
  [(set (match_operand:QI 0 "nonimmediate_operand" "=roR,  rS,roR, rS,  rS")
        (and:QI
	 (match_operand:QI 1 "nonimmediate_operand"  "%0,   0, rS,roR,  rS")
	 (match_operand:QI 2 "general_operand"      "rSn,roRn,rSn,rSn,roRn")))]
  ""
  "@
   mov\\tw,%2\;and\\t%0,w
   #
   #
   #
   #"
  "(ip2k_reorg_split_qimode
    && (! rtx_equal_p (operands[0], operands[1])
        || GET_CODE (operands[2]) != CONST_INT
        || find_one_clear_bit_p (INTVAL (operands[2]) | 0xffffff00UL) == -1))"
  [(set (reg:QI 10)
  	(match_dup 2))
   (set (match_dup 0)
   	(and:QI (match_dup 1)
		(reg:QI 10)))])

(define_insn_and_split "andhi3" ;		       0   1   2   3   4
  [(set (match_operand:HI 0 "nonimmediate_operand"  "=uo, uS,&dS,&do,&dS")
        (and:HI
	 (match_operand:HI 1 "nonimmediate_operand"  "%0,  0, ro, rS, rS")
	 (match_operand:HI 2 "general_operand"      "rSn,ron,rSn,rSn,ron")))]
  ""
  "#"
  "(ip2k_reorg_split_himode)"
  [(set (match_dup 3)
	(and:QI (match_dup 4)
		(match_dup 5)))
   (set (match_dup 6)
   	(and:QI (match_dup 7)
		(match_dup 8)))]
  "{
    operands[3] = ip2k_get_high_half (operands[0], QImode);
    operands[4] = ip2k_get_high_half (operands[1], QImode);
    operands[5] = ip2k_get_high_half (operands[2], QImode);
    operands[6] = ip2k_get_low_half (operands[0], QImode);
    operands[7] = ip2k_get_low_half (operands[1], QImode);
    operands[8] = ip2k_get_low_half (operands[2], QImode);
  }")

(define_insn_and_split "andsi3" ;		     0   1   2   3   4
  [(set (match_operand:SI 0 "nonimmediate_operand" "=uo, uS,&dS,&do,&dS")
        (and:SI
	 (match_operand:SI 1 "nonimmediate_operand" "%0,  0, ro, rS, rS")
	 (match_operand:SI 2 "general_operand"      "rSn,ron,rSn,rSn,ron")))]
  ""
  "#"
  "(ip2k_reorg_split_simode)"
  [(set (match_dup 3)
	(and:HI (match_dup 4)
		(match_dup 5)))
   (set (match_dup 6)
   	(and:HI (match_dup 7)
		(match_dup 8)))]
  "{
    operands[3] = ip2k_get_high_half (operands[0], HImode);
    operands[4] = ip2k_get_high_half (operands[1], HImode);
    operands[5] = ip2k_get_high_half (operands[2], HImode);
    operands[6] = ip2k_get_low_half (operands[0], HImode);
    operands[7] = ip2k_get_low_half (operands[1], HImode);
    operands[8] = ip2k_get_low_half (operands[2], HImode);
  }")

(define_insn_and_split "anddi3" ;		      0   1   2   3   4
  [(set (match_operand:DI 0 "nonimmediate_operand"  "=uo, uS,&dS,&do,&dS")
        (and:DI
	 (match_operand:DI 1 "nonimmediate_operand"  "%0,  0, ro, rS, rS")
	 (match_operand:DI 2 "general_operand"      "rSn,ron,rSn,rSn,ron")))]
  ""
  "#"
  "(ip2k_reorg_split_dimode)"
  [(set (match_dup 3)
	(and:SI (match_dup 4)
		(match_dup 5)))
   (set (match_dup 6)
   	(and:SI (match_dup 7)
		(match_dup 8)))]
  "{
    operands[3] = ip2k_get_high_half (operands[0], SImode);
    operands[4] = ip2k_get_high_half (operands[1], SImode);
    operands[5] = ip2k_get_high_half (operands[2], SImode);
    operands[6] = ip2k_get_low_half (operands[0], SImode);
    operands[7] = ip2k_get_low_half (operands[1], SImode);
    operands[8] = ip2k_get_low_half (operands[2], SImode);
  }")

;;
;; Bitwise or instructions.
;;

(define_expand "iorqi3"
  [(set (match_operand:QI 0 "nonimmediate_operand" "")
 	(ior:QI (match_operand:QI 1 "nonimmediate_operand" "")
		(match_operand:QI 2 "general_operand" "")))]
  ""
  "")

(define_insn "*iorqi3_bit"
  [(set (match_operand:QI 0 "nonimmediate_operand"       "=roR")
        (ior:QI (match_dup 0)
                (match_operand:QI 1 "const_int_operand"     "n")))]
  "(find_one_set_bit_p (INTVAL (operands[1]) & 0xff) != -1)"
  "*{
    operands[2] = GEN_INT (find_one_set_bit_p (INTVAL (operands[1]) & 0xff));
    return AS2 (setb, %0, %b2);
  }"
  [(set_attr "skip" "yes")
   (set_attr "clobberw" "no")])

(define_insn "*iorqi3" ;			      0    1   2   3    4
  [(set (match_operand:QI 0 "nonimmediate_operand" "=roR,  rS,roR, rS,  rS")
        (ior:QI
	 (match_operand:QI 1 "nonimmediate_operand"  "%0,   0, rS,roR,  rS")
	 (match_operand:QI 2 "general_operand"      "rSi,roRi,rSi,rSi,roRi")))]
  ""
  "@
   mov\\tw,%2\;or\\t%0,w
   mov\\tw,%2\;or\\t%0,w
   mov\\tw,%2\;or\\tw,%1\;mov\\t%0,w
   mov\\tw,%2\;or\\tw,%1\;mov\\t%0,w
   mov\\tw,%2\;or\\tw,%1\;mov\\t%0,w")

(define_insn_and_split "iorhi3" ;		     0   1   2   3   4
  [(set (match_operand:HI 0 "nonimmediate_operand" "=uo, uS,&dS,&do,&dS")
        (ior:HI
	 (match_operand:HI 1 "nonimmediate_operand" "%0,  0, ro, rS, rS")
	 (match_operand:HI 2 "general_operand"     "rSn,ron,rSn,rSn,ron")))]
  ""
  "#"
  "(ip2k_reorg_split_himode)"
  [(set (match_dup 3)
	(ior:QI (match_dup 4)
		(match_dup 5)))
   (set (match_dup 6)
   	(ior:QI (match_dup 7)
		(match_dup 8)))]
  "{
    operands[3] = ip2k_get_high_half (operands[0], QImode);
    operands[4] = ip2k_get_high_half (operands[1], QImode);
    operands[5] = ip2k_get_high_half (operands[2], QImode);
    operands[6] = ip2k_get_low_half (operands[0], QImode);
    operands[7] = ip2k_get_low_half (operands[1], QImode);
    operands[8] = ip2k_get_low_half (operands[2], QImode);
  }")

(define_insn_and_split "iorsi3" ;		      0   1   2   3   4
  [(set (match_operand:SI 0 "nonimmediate_operand"  "=uo, uS,&dS,&do,&dS")
        (ior:SI
	 (match_operand:SI 1 "nonimmediate_operand"  "%0,  0, ro, rS, rS")
	 (match_operand:SI 2 "general_operand"      "rSn,ron,rSn,rSn,ron")))]
  ""
  "#"
  "(ip2k_reorg_split_simode)"
  [(set (match_dup 3)
	(ior:HI (match_dup 4)
		(match_dup 5)))
   (set (match_dup 6)
   	(ior:HI (match_dup 7)
		(match_dup 8)))]
  "{
    operands[3] = ip2k_get_high_half (operands[0], HImode);
    operands[4] = ip2k_get_high_half (operands[1], HImode);
    operands[5] = ip2k_get_high_half (operands[2], HImode);
    operands[6] = ip2k_get_low_half (operands[0], HImode);
    operands[7] = ip2k_get_low_half (operands[1], HImode);
    operands[8] = ip2k_get_low_half (operands[2], HImode);
  }")

(define_insn_and_split "iordi3" ;		      0   1   2   3   4
  [(set (match_operand:DI 0 "nonimmediate_operand"  "=uo, uS,&dS,&do,&dS")
        (ior:DI
	 (match_operand:DI 1 "nonimmediate_operand"  "%0,  0, ro, rS, rS")
	 (match_operand:DI 2 "general_operand"      "rSn,ron,rSn,rSn,ron")))]
  ""
  "#"
  "(ip2k_reorg_split_dimode)"
  [(set (match_dup 3)
	(ior:SI (match_dup 4)
		(match_dup 5)))
   (set (match_dup 6)
   	(ior:SI (match_dup 7)
		(match_dup 8)))]
  "{
    operands[3] = ip2k_get_high_half (operands[0], SImode);
    operands[4] = ip2k_get_high_half (operands[1], SImode);
    operands[5] = ip2k_get_high_half (operands[2], SImode);
    operands[6] = ip2k_get_low_half (operands[0], SImode);
    operands[7] = ip2k_get_low_half (operands[1], SImode);
    operands[8] = ip2k_get_low_half (operands[2], SImode);
  }")

;;
;; Bitwise xor instructions
;;
;; TODO: xor ops can also use "not w, fr"!
;;

(define_insn "xorqi3"
  [(set
    (match_operand:QI 0 "nonimmediate_operand"  "=roR,roR,  rS,roR, rS,  rS")
    (xor:QI (match_operand:QI 1 "general_operand" "%0,  0,   0, rS,roR,  rS")
	    (match_operand:QI 2 "general_operand" "M,rSi,roRi,rSi,rSi,roRi")))]
  ""
  "@
   not\\t%0
   mov\\tw,%2\;xor\\t%0,w
   mov\\tw,%2\;xor\\t%0,w
   mov\\tw,%1\;xor\\tw,%2\;mov\\t%0,w
   mov\\tw,%1\;xor\\tw,%2\;mov\\t%0,w
   mov\\tw,%1\;xor\\tw,%2\;mov\\t%0,w"
  [(set_attr "clobberw" "no,yes,yes,yes,yes,yes")])

(define_insn_and_split "xorhi3" ;		     0   1   2   3   4
  [(set (match_operand:HI 0 "nonimmediate_operand" "=uo, uS,&dS,&do,&dS")
        (xor:HI
	 (match_operand:HI 1 "nonimmediate_operand" "%0,  0, ro, rS, rS")
	 (match_operand:HI 2 "general_operand"     "rSn,ron,rSn,rSn,ron")))]
  ""
  "#"
  "(ip2k_reorg_split_himode)"
  [(set (match_dup 3)
	(xor:QI (match_dup 4)
		(match_dup 5)))
   (set (match_dup 6)
   	(xor:QI (match_dup 7)
		(match_dup 8)))]
  "{
    operands[3] = ip2k_get_high_half (operands[0], QImode);
    operands[4] = ip2k_get_high_half (operands[1], QImode);
    operands[5] = ip2k_get_high_half (operands[2], QImode);
    operands[6] = ip2k_get_low_half (operands[0], QImode);
    operands[7] = ip2k_get_low_half (operands[1], QImode);
    operands[8] = ip2k_get_low_half (operands[2], QImode);
  }")

(define_insn_and_split "xorsi3" ;		     0   1   2   3   4
  [(set (match_operand:SI 0 "nonimmediate_operand" "=uo, uS,&dS,&do,&dS")
        (xor:SI
	 (match_operand:SI 1 "nonimmediate_operand" "%0,  0, ro, rS, rS")
	 (match_operand:SI 2 "general_operand"     "rSn,ron,rSn,rSn,ron")))]
  ""
  "#"
  "(ip2k_reorg_split_simode)"
  [(set (match_dup 3)
	(xor:HI (match_dup 4)
		(match_dup 5)))
   (set (match_dup 6)
   	(xor:HI (match_dup 7)
		(match_dup 8)))]
  "{
    operands[3] = ip2k_get_high_half (operands[0], HImode);
    operands[4] = ip2k_get_high_half (operands[1], HImode);
    operands[5] = ip2k_get_high_half (operands[2], HImode);
    operands[6] = ip2k_get_low_half (operands[0], HImode);
    operands[7] = ip2k_get_low_half (operands[1], HImode);
    operands[8] = ip2k_get_low_half (operands[2], HImode);
  }")

(define_insn_and_split "xordi3" ;		     0   1   2   3   4
  [(set (match_operand:DI 0 "nonimmediate_operand" "=uo, uS,&dS,&do,&dS")
        (xor:DI
	 (match_operand:DI 1 "nonimmediate_operand" "%0,  0, ro, rS, rS")
	 (match_operand:DI 2 "general_operand"     "rSn,ron,rSn,rSn,ron")))]
  ""
  "#"
  "(ip2k_reorg_split_dimode)"
  [(set (match_dup 3)
	(xor:SI (match_dup 4)
		(match_dup 5)))
   (set (match_dup 6)
   	(xor:SI (match_dup 7)
		(match_dup 8)))]
  "{
    operands[3] = ip2k_get_high_half (operands[0], SImode);
    operands[4] = ip2k_get_high_half (operands[1], SImode);
    operands[5] = ip2k_get_high_half (operands[2], SImode);
    operands[6] = ip2k_get_low_half (operands[0], SImode);
    operands[7] = ip2k_get_low_half (operands[1], SImode);
    operands[8] = ip2k_get_low_half (operands[2], SImode);
  }")

;;
;; Multiply instructions.
;;

(define_insn "umulqi3"
  [(set (match_operand:QI 0 "nonimmediate_operand"          "=ro, rS,  rS")
  	(mult:QI (match_operand:QI 1 "nonimmediate_operand" "%rS,roR,  rS")
		 (match_operand:QI 2 "general_operand"      "rSi,rSi,roRi")))]
  ""
  "mov\\tw,%1\;mulu\\tw,%2\;mov\\t%0,w")

(define_insn "mulqihi3"
  [(set (match_operand:HI 0 "nonimmediate_operand"   "=ro, rS,  rS")
	(mult:HI
	 (sign_extend:HI
	  (match_operand:QI 1 "nonimmediate_operand" "%rS,roR,  rS"))
	 (sign_extend:HI
	  (match_operand:QI 2 "general_operand"      "rSi,rSi,roRi"))))]
  ""
  "@
   mov\\tw,%1\;muls\\tw,%2\;mov\\t%L0,w\;mov\\tw,mulh\;mov\\t%H0,w
   mov\\tw,%1\;muls\\tw,%2\;mov\\t%L0,w\;mov\\tw,mulh\;mov\\t%H0,w
   mov\\tw,%1\;muls\\tw,%2\;mov\\t%L0,w\;mov\\tw,mulh\;mov\\t%H0,w")

(define_insn_and_split "umulqihi3"
  [(set (match_operand:HI 0 "nonimmediate_operand"  	    "=ro, rS,  rS")
	(mult:HI (zero_extend:HI
		  (match_operand:QI 1 "nonimmediate_operand" "%rS,roR,  rS"))
		 (zero_extend:HI
		  (match_operand:QI 2 "general_operand"     "rSi,rSi,roRi"))))]
  ""
  "#"
  "ip2k_reorg_split_qimode"
  [(set (match_dup 3)
        (mult:QI (match_dup 1)
		 (match_dup 2)))
   (set (reg:QI 10)
   	(reg:QI 15))
   (set (match_dup 4)
        (reg:QI 10))]
  "{
    operands[3] = ip2k_get_low_half (operands[0], QImode);
    operands[4] = ip2k_get_high_half (operands[0], QImode);
  }")

(define_insn "*mulhi3_by2"
  [(set (match_operand:HI 0 "nonimmediate_operand"         "=ro,&ro,&rS")
        (mult:HI (match_operand:HI 1 "nonimmediate_operand" "0, rS, ro")
		 (zero_extend:HI (const_int 2))))]
  ""
  "@
   clrb\\tSTATUS,0\;rl\\t%L0\;rl\\t%H0
   clrb\\tSTATUS,0\;rl\\tw,%L1\;mov\\t%L0,w\;rl\\tw,%H1\;mov\\t%H0,w
   clrb\\tSTATUS,0\;rl\\tw,%L1\;mov\\t%L0,w\;rl\\tw,%H1\;mov\\t%H0,w"
  [(set_attr "clobberw" "no,yes,yes")])

(define_insn "*mulhi3_byqi"
  [(set (match_operand:HI
	 0 "nonimmediate_operand"  		      "=ro,&ro,&rS, &rS")
	(mult:HI (match_operand:HI
		  1 "nonimmediate_operand"              "0, rS, ro,  rS")
		 (zero_extend:HI (match_operand:QI
				  2 "general_operand" "rSi,rSi,rSi,roRi"))))]
  ""
  "@
   mov\\tw,%L1\;mulu\\tw,%2\;mov\\t%L0,w\;push\\tmulh%<\;mov\\tw,%H1\;mulu\\tw,%2\;pop\\t%H0%>\;add\\t%H0,w
   mov\\tw,%L1\;mulu\\tw,%2\;mov\\t%L0,w\;mov\\tw,mulh\;mov\\t%H0,w\;mov\\tw,%H1\;mulu\\tw,%2\;add\\t%H0,w
   mov\\tw,%L1\;mulu\\tw,%2\;mov\\t%L0,w\;mov\\tw,mulh\;mov\\t%H0,w\;mov\\tw,%H1\;mulu\\tw,%2\;add\\t%H0,w
   mov\\tw,%L1\;mulu\\tw,%2\;mov\\t%L0,w\;mov\\tw,mulh\;mov\\t%H0,w\;mov\\tw,%H1\;mulu\\tw,%2\;add\\t%H0,w")

(define_insn "smulqi_highpart"
  [(set (match_operand:QI 0 "nonimmediate_operand"      "=roR, rS,  rS")
	(truncate:QI
	  (lshiftrt:HI
	    (mult:HI
	     (sign_extend:HI
	      (match_operand:QI 1 "nonimmediate_operand" "%rS,roR,  rS"))
	     (sign_extend:HI
	      (match_operand:QI 2 "general_operand"      "rSi,rSi,roRi")))
	    (const_int 8))))]
  ""
  "@
   mov\\tw,%1\;muls\\tw,%2\;mov\\tw,mulh\;mov %0,w
   mov\\tw,%1\;muls\\tw,%2\;mov\\tw,mulh\;mov %0,w
   mov\\tw,%1\;muls\\tw,%2\;mov\\tw,mulh\;mov %0,w")

(define_insn "umulqi_highpart"
  [(set (match_operand:QI 0 "nonimmediate_operand"      "=roR, rS,  rS")
	(truncate:QI
	  (lshiftrt:HI
	    (mult:HI
	     (zero_extend:HI
	      (match_operand:QI 1 "nonimmediate_operand" "%rS,roR,  rS"))
	     (zero_extend:HI
	      (match_operand:QI 2 "general_operand"      "rSi,rSi,roRi")))
	    (const_int 8))))]
  ""
  "@
   mov\\tw,%1\;mulu\\tw,%2\;mov\\tw,mulh\;mov %0,w
   mov\\tw,%1\;mulu\\tw,%2\;mov\\tw,mulh\;mov %0,w
   mov\\tw,%1\;mulu\\tw,%2\;mov\\tw,mulh\;mov %0,w")

(define_insn "mulhi3"
  [(set (match_operand:HI 0 "nonimmediate_operand"          "=uo, uS, uS")
  	(mult:HI (match_operand:HI 1 "nonimmediate_operand" "%rS, ro, rS")
		 (match_operand:HI 2 "general_operand"      "rSi,rSi,roi")))]
  ""
  "push\\t%L2%<\;push\\t%H2%<\;push\\t%L1%<\;push\\t%H1%>\;page\\t__mulhi3\;call\\t__mulhi3\;pop\\t%H0%>\;pop\\t%L0%>")

;; If we find that we're multiplying by a constant that's less than 256 we
;; can replace a full "mulhi3" with one of the lighter weight variants
;; that multiplies an HImode value by a QImode one.
;;
(define_split
  [(set (match_operand:HI 0 "nonimmediate_operand"         "=ro,rS")
  	(mult:HI (match_operand:HI 1 "nonimmediate_operand" "rS,ro")
		 (match_operand:HI 2 "const_int_operand"     "P, P")))]
  "(INTVAL (operands[2]) < 0x100)"
  [(set (match_dup 0)
  	(mult:HI (match_dup 1)
		 (zero_extend:HI (match_dup 3))))]
  "operands[3] = gen_int_mode (INTVAL (operands[2]), QImode);")

;;
;; Divide/Modulus functions.
;;

(define_expand "udivmodhi4"
  [(parallel [(set (reg:HI 128)
		   (udiv:HI (match_operand:HI 1 "general_operand" "")
			    (match_operand:HI 2 "general_operand" "")))
	      (set (reg:HI 130)
		   (umod:HI (match_dup 1) (match_dup 2)))
	      (clobber (reg:QI 132))
	      (clobber (reg:QI 133))])
   (set (match_operand:HI 0 "general_operand" "") (reg:HI 128))
   (set (match_operand:HI 3 "general_operand" "") (reg:HI 130))]
  ""
  "")

(define_insn "*udivmodhi4_call"
  [(set (reg:HI 128)
	(udiv:HI (match_operand:HI 0 "general_operand" "uSi,uoi")
		 (match_operand:HI 1 "general_operand" "uoi,uSi")))
   (set (reg:HI 130)
	(umod:HI (match_dup 0) (match_dup 1)))
   (clobber (reg:QI 132))
   (clobber (reg:QI 133))]
  ""
  "push\\t%L1%<\;push\\t%H1%<\;push\\t%L0%<\;push\\t%H0%>%>%>\;page\\t__udivmodhi4\;call\\t__udivmodhi4")

(define_expand "divmodhi4"
  [(parallel [(set (reg:HI 128)
		   (div:HI (match_operand:HI 1 "general_operand" "")
			   (match_operand:HI 2 "general_operand" "")))
	      (set (reg:HI 130)
		   (mod:HI (match_dup 1)
		   	   (match_dup 2)))
	      (clobber (reg:QI 132))
	      (clobber (reg:QI 133))
	      (clobber (reg:QI 134))
	      (clobber (reg:QI 135))])
   (set (match_operand:HI 0 "general_operand" "") (reg:HI 128))
   (set (match_operand:HI 3 "general_operand" "") (reg:HI 130))]
  ""
  "")

(define_insn "*divmodhi4_call"
  [(set (reg:HI 128)
	(div:HI (match_operand:HI 0 "general_operand" "uSi,uoi")
		(match_operand:HI 1 "general_operand" "uoi,uSi")))
   (set (reg:HI 130)
	(mod:HI (match_dup 0) (match_dup 1)))
   (clobber (reg:QI 132))
   (clobber (reg:QI 133))
   (clobber (reg:QI 134))
   (clobber (reg:QI 135))]
  ""
  "push\\t%L1%<\;push\\t%H1%<\;push\\t%L0%<\;push\\t%H0%>%>%>\;page\\t__divmodhi4\;call\\t__divmodhi4")

(define_expand "udivmodsi4"
  [(parallel [(set (reg:SI 128)
		   (udiv:SI (match_operand:SI 1 "general_operand" "")
			    (match_operand:SI 2 "general_operand" "")))
	      (set (reg:SI 132)
		   (umod:SI (match_dup 1)
		   	    (match_dup 2)))
	      (clobber (reg:QI 136))
	      (clobber (reg:QI 137))
	      (clobber (reg:QI 138))
	      (clobber (reg:QI 139))])
   (set (match_operand:SI 0 "general_operand" "") (reg:SI 128))
   (set (match_operand:SI 3 "general_operand" "") (reg:SI 132))]
  ""
  "")

(define_insn "*udivmodsi4_call"
  [(set (reg:SI 128)
	(udiv:SI (match_operand:SI 0 "general_operand" "rSi,roi")
		 (match_operand:SI 1 "general_operand" "roi,rSi")))
   (set (reg:SI 132)
	(umod:SI (match_dup 0)
		 (match_dup 1)))
   (clobber (reg:QI 136))
   (clobber (reg:QI 137))
   (clobber (reg:QI 138))
   (clobber (reg:QI 139))]
  ""
  "push\\t%D1%<\;push\\t%C1%<\;push\\t%B1%<\;push\\t%A1%<\;push\\t%D0%<\;push\\t%C0%<\;push\\t%B0%<\;push\\t%A0%>%>%>%>%>%>%>\;page\\t__udivmodsi4\;call\\t__udivmodsi4")

(define_expand "divmodsi4"
  [(parallel [(set (reg:SI 128)
		   (div:SI (match_operand:SI 1 "general_operand" "")
			   (match_operand:SI 2 "general_operand" "")))
	      (set (reg:SI 132)
		   (mod:SI (match_dup 1)
		   	   (match_dup 2)))
	      (clobber (reg:QI 136))
	      (clobber (reg:QI 137))
	      (clobber (reg:QI 138))
	      (clobber (reg:QI 139))
	      (clobber (reg:QI 140))
	      (clobber (reg:QI 141))])
   (set (match_operand:SI 0 "general_operand" "") (reg:SI 128))
   (set (match_operand:SI 3 "general_operand" "") (reg:SI 132))]
  ""
  "")

(define_insn "*divmodsi4_call"
  [(set (reg:SI 128)
	(div:SI (match_operand:SI 0 "general_operand" "rSn,ron")
		(match_operand:SI 1 "general_operand" "ron,rSn")))
   (set (reg:SI 132)
	(mod:SI (match_dup 0)
		(match_dup 1)))
   (clobber (reg:QI 136))
   (clobber (reg:QI 137))
   (clobber (reg:QI 138))
   (clobber (reg:QI 139))
   (clobber (reg:QI 140))
   (clobber (reg:QI 141))]
  ""
  "push\\t%D1%<\;push\\t%C1%<\;push\\t%B1%<\;push\\t%A1%<\;push\\t%D0%<\;push\\t%C0%<\;push\\t%B0%<\;push\\t%A0%>%>%>%>%>%>%>\;page\\t__divmodsi4\;call\\t__divmodsi4")

(define_expand "udivmoddi4"
  [(parallel [(set (reg:DI 128)
		   (udiv:DI (match_operand:DI 1 "general_operand" "")
			    (match_operand:DI 2 "general_operand" "")))
	      (set (reg:DI 136)
		   (umod:DI (match_dup 1)
		   	    (match_dup 2)))
	      (clobber (reg:QI 144))
	      (clobber (reg:QI 145))
	      (clobber (reg:QI 146))
	      (clobber (reg:QI 147))
	      (clobber (reg:QI 148))
	      (clobber (reg:QI 149))
	      (clobber (reg:QI 150))
	      (clobber (reg:QI 151))])
   (set (match_operand:DI 0 "general_operand" "") (reg:DI 128))
   (set (match_operand:DI 3 "general_operand" "") (reg:DI 136))]
  ""
  "")

(define_insn "*udivmoddi4_call"
  [(set (reg:DI 128)
	(udiv:DI (match_operand:DI 0 "general_operand" "rSi,roi")
		 (match_operand:DI 1 "general_operand" "roi,rSi")))
   (set (reg:DI 136)
	(umod:DI (match_dup 0)
		 (match_dup 1)))
   (clobber (reg:QI 144))
   (clobber (reg:QI 145))
   (clobber (reg:QI 146))
   (clobber (reg:QI 147))
   (clobber (reg:QI 148))
   (clobber (reg:QI 149))
   (clobber (reg:QI 150))
   (clobber (reg:QI 151))]
  ""
  "push\\t%Z1%<\;push\\t%Y1%<\;push\\t%X1%<\;push\\t%W1%<\;push\\t%V1%<\;push\\t%U1%<\;push\\t%T1%<\;push\\t%S1%<\;push\\t%Z0%<\;push\\t%Y0%<\;push\\t%X0%<\;push\\t%W0%<\;push\\t%V0%<\;push\\t%U0%<\;push\\t%T0%<\;push\\t%S00%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>\;page\\t__udivmoddi4\;call\\t__udivmoddi4")

(define_expand "divmoddi4"
  [(parallel [(set (reg:DI 128)
		   (div:DI (match_operand:DI 1 "general_operand" "")
			   (match_operand:DI 2 "general_operand" "")))
	      (set (reg:DI 136)
		   (mod:DI (match_dup 1)
		   	   (match_dup 2)))
	      (clobber (reg:QI 144))
	      (clobber (reg:QI 145))
	      (clobber (reg:QI 146))
	      (clobber (reg:QI 147))
	      (clobber (reg:QI 148))
	      (clobber (reg:QI 149))
	      (clobber (reg:QI 150))
	      (clobber (reg:QI 151))])
   (set (match_operand:DI 0 "general_operand" "") (reg:DI 128))
   (set (match_operand:DI 3 "general_operand" "") (reg:DI 136))]
  ""
  "")

(define_insn "*divmoddi4_call"
  [(set (reg:DI 128)
	(div:DI (match_operand:DI 0 "general_operand" "rSn,ron")
		(match_operand:DI 1 "general_operand" "ron,rSn")))
   (set (reg:DI 136)
	(mod:DI (match_dup 0)
		(match_dup 1)))
   (clobber (reg:QI 144))
   (clobber (reg:QI 145))
   (clobber (reg:QI 146))
   (clobber (reg:QI 147))
   (clobber (reg:QI 148))
   (clobber (reg:QI 149))
   (clobber (reg:QI 150))
   (clobber (reg:QI 151))]
  ""
  "push\\t%Z1%<\;push\\t%Y1%<\;push\\t%X1%<\;push\\t%W1%<\;push\\t%V1%<\;push\\t%U1%<\;push\\t%T1%<\;push\\t%S1%<\;push\\t%Z0%<\;push\\t%Y0%<\;push\\t%X0%<\;push\\t%W0%<\;push\\t%V0%<\;push\\t%U0%<\;push\\t%T0%<\;push\\t%S00%>%>%>%>%>%>%>%>%>%>%>%>%>%>%>\;page\\t__divmoddi4\;call\\t__divmoddi4")

;;
;; Arithmetic shift left instructions.
;;

(define_insn "ashlqi3"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=roR,roR, rS,roR, rS")
	(ashift:QI
	 (match_operand:QI 1 "nonimmediate_operand"   "0, rS,roR,  0,  0")
	 (match_operand:QI 2 "general_operand"        "N,  L,  L, rS,roR")))]
  ""
  "@
   clrb	status,0\;rl\\t%0
   mov\\tw,%e2\;mulu\\tw,%1\;mov\\t%0,w
   mov\\tw,%e2\;mulu\\tw,%1\;mov\\t%0,w
   mov\\tw,%2\;snz\;page\\t1f\;jmp\\t1f\;2:clrb\\tstatus,0\;rl\\t%0\;decsz\\twreg\;page\\t2b\;jmp\\t2b\;1:
   mov\\tw,%2\;snz\;page\\t1f\;jmp\\t1f\;2:clrb\\tstatus,0\;rl\\t%0\;decsz\\twreg\;page\\t2b\;jmp\\t2b\;1:"
  [(set_attr "clobberw" "no,yes,yes,yes,yes")])

;; Convert simple fixed-size shift of a zero-extended QImode value into a
;; multiply as our multiplier is much faster.  We also do this so that the
;;  multiply can possibly be merged into a much faster multiply-and-accumulate
;; operation.
;;
(define_split
  [(set (match_operand:HI 0 "nonimmediate_operand"              "=ro, rS")
  	(ashift:HI (zero_extend:HI
	              (match_operand:QI 1 "nonimmediate_operand" "rS,roR"))
		   (match_operand:QI 2 "const_int_operand"        "J,  J")))]
  "(INTVAL (operands[2]) < 8)"
  [(set (match_dup 0)
  	(mult:HI (zero_extend:HI (match_dup 1))
		 (zero_extend:HI (match_dup 3))))]
  "operands[3] = gen_int_mode (1 << INTVAL (operands[2]), QImode);")

(define_insn_and_split "*ashlhi3_by8_zero_extend"
  [(set (match_operand:HI 0 "nonimmediate_operand"              "=ro, rS")
  	(ashift:HI (zero_extend:HI
	              (match_operand:QI 1 "nonimmediate_operand" "rS,roR"))
		   (const_int 8)))]
  ""
  "#"
  "reload_completed"
  [(set (match_dup 2) (match_dup 1))
   (set (match_dup 3) (const_int 0))]
  "{
    operands[2] = ip2k_get_high_half (operands[0], QImode);
    operands[3] = ip2k_get_low_half (operands[0], QImode);
  }")

(define_insn "*ashlhi3_zero_extend" ;				   0   1
  [(set (match_operand:HI 0 "nonimmediate_operand"              "=ro, rS")
  	(ashift:HI (zero_extend:HI
	              (match_operand:QI 1 "nonimmediate_operand" "rS,roR"))
		   (match_operand:QI 2 "const_int_operand"        "n,  n")))]
  ""
  "*{
    if (INTVAL (operands[2]) < 8)
      return AS2 (mov, w, %1) CR_TAB
	     AS2 (mulu, w, %e2) CR_TAB
	     AS2 (mov, %L0, w) CR_TAB
	     AS2 (mov, w, MULH) CR_TAB
	     AS2 (mov, %H0, w);
    else
      {
        operands[3] = GEN_INT (INTVAL (operands[2]) - 8);
        return AS2 (mov, w, %1) CR_TAB
	       AS2 (mulu, w, %e3) CR_TAB
	       AS2 (mov, %H0, w) CR_TAB
	       AS1 (clr, %L0);
      }
  }")

;; Convert simple fixed-size shift of a HImode value into a multiply as
;; our multiplier is much faster.  We also do this so that the multiply can
;; possibly be merged into a much faster multiply-and-accumulate operation.
;;
(define_split
  [(set (match_operand:HI 0 "nonimmediate_operand"           "=ro,rS")
  	(ashift:HI (match_operand:HI 1 "nonimmediate_operand" "rS,ro")
		   (match_operand:QI 2 "const_int_operand"     "J, J")))]
  "(INTVAL (operands[2]) < 8)"
  [(set (match_dup 0)
  	(mult:HI (match_dup 1)
		 (zero_extend:HI (match_dup 3))))]
  "operands[3] = gen_int_mode (1 << INTVAL (operands[2]), QImode);")

(define_insn_and_split "ashlhi3_split"
  [(set (match_operand:HI 0 "nonimmediate_operand"           "=ro,rS")
  	(ashift:HI (match_operand:HI 1 "nonimmediate_operand" "rS,ro")
		   (match_operand:QI 2 "const_int_operand"     "n, n")))]
  "(INTVAL (operands[2]) >= 8)"
  "#"
  "&& ip2k_reorg_split_himode"
  [(set (match_dup 4) (const_int 0))]
  "{
    operands[3] = ip2k_get_high_half (operands[0], QImode);
    operands[4] = ip2k_get_low_half (operands[0], QImode);
    operands[5] = ip2k_get_low_half (operands[1], QImode);

    if (INTVAL (operands[2]) == 8)
      emit_insn (gen_movqi (operands[3], operands[5]));
    else
      {
        operands[6] = gen_int_mode (INTVAL (operands[2]) - 8, QImode);
	emit_insn (gen_ashlqi3 (operands[3], operands[5], operands[6]));
      }
  }")

(define_insn "ashlhi3" ;			      0   1   2  3   4
  [(set (match_operand:HI 0 "nonimmediate_operand" "=ro,&rS,&ro,ro, rS")
        (ashift:HI
	 (match_operand:HI 1 "nonimmediate_operand"  "0, ro, rS, 0,  0")
	 (match_operand:QI 2 "general_operand"       "L,  L,  L,rS,roR")))]
  ""
  "*{
    switch (which_alternative)
      {
      case 0:
        switch (INTVAL (operands[2]))
	  {
          case 1:
            return AS2 (clrb, status, 0) CR_TAB
                   AS1 (rl, %L0) CR_TAB
	           AS1 (rl, %H0);

          case 2:
            return AS2 (clrb, status, 0) CR_TAB
	           AS1 (rl, %L0) CR_TAB
	           AS1 (rl, %H0) CR_TAB
	           AS2 (clrb, status, 0) CR_TAB
	           AS1 (rl, %L0) CR_TAB
	           AS1 (rl, %H0);

          case 3:
          case 4:
          case 5:
          case 6:
            return AS2 (mov, w, %L1) CR_TAB
	           AS2 (mulu, w, %e2) CR_TAB
	           AS2 (mov, %L0, w) CR_TAB
	           AS1 (push, MULH%<) CR_TAB
	           AS2 (mov, w, %H1) CR_TAB
	           AS2 (mulu, w, %e2) CR_TAB
	           AS2 (or, 1(SP), w) CR_TAB
	           AS1 (pop, %H0%>);

          case 7:
            return AS1 (rr, %H0) CR_TAB
	           AS2 (mov, w, %L0) CR_TAB
	           AS1 (clr, %L0) CR_TAB
	           AS2 (mov, %H0, w) CR_TAB
	           AS1 (rr, %H0) CR_TAB
	           AS1 (rr, %L0);

          default:
	    /* Should be caught by a different insn pattern */
	    abort ();
          }

      case 1:
      case 2:
        switch (INTVAL (operands[2]))
	  {
          case 1:
            return AS2 (clrb, status, 0) CR_TAB
	           AS2 (rl, w, %L1) CR_TAB
	           AS2 (mov, %L0, w) CR_TAB
	           AS2 (rl, w, %H1) CR_TAB
	           AS2 (mov, %H0, w);

          case 2:
            return AS2 (clrb, status, 0) CR_TAB
	           AS2 (rl, w, %L1) CR_TAB
	           AS2 (mov, %L0, w) CR_TAB
	           AS2 (rl, w, %H1) CR_TAB
	           AS2 (mov, %H0, w) CR_TAB
	           AS2 (clrb, status, 0) CR_TAB
	           AS1 (rl, %L0) CR_TAB
	           AS1 (rl, %H0);

          case 3:
          case 4:
          case 5:
          case 6:
	    return AS2 (mov, w, %L1) CR_TAB
	           AS2 (mulu, w, %e2) CR_TAB
	           AS2 (mov, %L0, w) CR_TAB
	           AS1 (push, MULH%<) CR_TAB
	           AS2 (mov, w, %H1) CR_TAB
	           AS2 (mulu, w, %e2) CR_TAB
	           AS2 (or, 1(SP), w) CR_TAB
	           AS1 (pop, %H0%>);

          case 7:
            return AS2 (rr, w, %H1) CR_TAB
                   AS2 (mov, w, %L1) CR_TAB
	           AS1 (clr, %L0) CR_TAB
	           AS2 (mov, %H0, w) CR_TAB
	           AS1 (rr, %H0) CR_TAB
	           AS1 (rr, %L0);

          default:
	    /* Should be caught by a different insn pattern */
	    abort ();
          }

      case 3:
      case 4:
        return AS2 (mov, w, %2) CR_TAB
               AS1 (snz,) CR_TAB
	       AS1 (page, 2f) CR_TAB
	       AS1 (jmp, 2f) CR_TAB
	       AS1 (1:,) CR_TAB
	       AS2 (clrb, status, 0) CR_TAB
               AS1 (rl, %L0) CR_TAB
	       AS1 (rl, %H0) CR_TAB
	       AS1 (decsz, wreg) CR_TAB
	       AS1 (page, 1b) CR_TAB
	       AS1 (jmp, 1b) CR_TAB
	       AS1 (2:,);

      default:
        abort();
      }
  }")

(define_insn_and_split "*ashlsi3_by16_zero_extend"
  [(set (match_operand:SI 0 "nonimmediate_operand"              "=ro,rS")
  	(ashift:SI (zero_extend:SI
	              (match_operand:HI 1 "nonimmediate_operand" "rS,ro"))
		   (const_int 16)))]
  ""
  "#"
  "reload_completed"
  [(set (match_dup 2) (match_dup 1))
   (set (match_dup 3) (const_int 0))]
  "{
    operands[2] = ip2k_get_high_half (operands[0], HImode);
    operands[3] = ip2k_get_low_half (operands[0], HImode);
  }")

(define_insn_and_split "ashlsi3_split"
  [(set (match_operand:SI 0 "nonimmediate_operand"          "=ro,&ro,&rS")
  	(ashift:SI (match_operand:SI 1 "nonimmediate_operand" "0, rS, ro")
		   (match_operand:QI 2 "const_int_operand"    "n,  n,  n")))]
  "(INTVAL (operands[2]) >= 16)"
  "#"
  "&& ip2k_reorg_split_simode"
  [(const_int 0)]
  "{
    operands[3] = ip2k_get_high_half (operands[0], HImode);
    operands[4] = ip2k_get_low_half (operands[0], HImode);
    operands[5] = ip2k_get_low_half (operands[1], HImode);

    if (INTVAL (operands[2]) == 16)
      {
        emit_insn (gen_movhi (operands[3], operands[5]));
        emit_insn (gen_movhi (operands[4], GEN_INT (0)));
      }
    else
      {
        operands[6] = GEN_INT (INTVAL (operands[2]) - 16);
	emit_insn (gen_ashlhi3 (operands[3], operands[5], operands[6]));
        emit_insn (gen_movhi (operands[4], GEN_INT (0)));
      }
  }")

(define_insn "ashlsi3"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=ro, rS,ro,&ro,&rS")
	(ashift:SI
	 (match_operand:SI 1 "nonimmediate_operand"  "0,  0, 0, rS, ro")
	 (match_operand:QI 2 "general_operand"       "L,roR,rS,  L,  L")))]
  ""
  "*{
    switch (which_alternative) {
    case 0:
      switch (INTVAL (operands[2])) {
      case 1:
        return AS2 (clrb, status, 0) CR_TAB
      	       AS1 (rl, %D0) CR_TAB
	       AS1 (rl, %C0) CR_TAB
	       AS1 (rl, %B0) CR_TAB
	       AS1 (rl, %A0);

      case 2:
        return AS2 (clrb, status, 0) CR_TAB
      	       AS1 (rl, %D0) CR_TAB
	       AS1 (rl, %C0) CR_TAB
	       AS1 (rl, %B0) CR_TAB
	       AS1 (rl, %A0) CR_TAB
	       AS2 (clrb, status, 0) CR_TAB
      	       AS1 (rl, %D0) CR_TAB
	       AS1 (rl, %C0) CR_TAB
	       AS1 (rl, %B0) CR_TAB
	       AS1 (rl, %A0);

      case 8:
        return AS2 (mov, w, %B0) CR_TAB
	       AS2 (mov, %A0, w) CR_TAB
	       AS2 (mov, w, %C0) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS2 (mov, w, %D0) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS1 (clr, %D0);

      case 16:
        return AS2 (mov, w, %C0) CR_TAB
	       AS2 (mov, %A0, w) CR_TAB
	       AS2 (mov, w, %D0) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS1 (clr, %C0) CR_TAB
	       AS1 (clr, %D0);

      case 23:
        return AS2 (rr, w, %C0)	CR_TAB
	       AS2 (mov, w, %D0) CR_TAB
	       AS2 (mov, %A0, w) CR_TAB
	       AS1 (clr, %B0) CR_TAB
	       AS1 (clr, %C0) CR_TAB
	       AS1 (clr, %D0) CR_TAB
	       AS1 (rr, %A0) CR_TAB
	       AS1 (rr, %B0);

      case 24:
        return AS2 (mov, w, %D0) CR_TAB
	       AS2 (mov, %A0, w) CR_TAB
	       AS1 (clr, %B0) CR_TAB
	       AS1 (clr, %C0) CR_TAB
	       AS1 (clr, %D0);

      case 31:
        return AS2 (rr, w, %D0)	CR_TAB
	       AS1 (clr, %A0) CR_TAB
	       AS1 (clr, %B0) CR_TAB
	       AS1 (clr, %C0) CR_TAB
	       AS1 (clr, %D0) CR_TAB
	       AS1 (rr, %A0);

      default:
        return AS2 (mov, w, %2) CR_TAB
      	       AS1 (1:,) CR_TAB
      	       AS2 (clrb, status, 0) CR_TAB
      	       AS1 (rl, %D0) CR_TAB
	       AS1 (rl, %C0) CR_TAB
	       AS1 (rl, %B0) CR_TAB
	       AS1 (rl, %A0) CR_TAB
	       AS1 (decsz, wreg) CR_TAB
	       AS1 (page, 1b) CR_TAB
	       AS1 (jmp, 1b);
      }

    case 1:
    case 2:
      return AS2 (mov, w, %2) CR_TAB
      	     AS1 (snz,) CR_TAB
	     AS1 (page, 2f) CR_TAB
	     AS1 (jmp, 2f) CR_TAB
      	     AS1 (1:,) CR_TAB
      	     AS2 (clrb, status, 0) CR_TAB
      	     AS1 (rl, %D0) CR_TAB
	     AS1 (rl, %C0) CR_TAB
	     AS1 (rl, %B0) CR_TAB
	     AS1 (rl, %A0) CR_TAB
	     AS1 (decsz, wreg) CR_TAB
	     AS1 (page, 1b) CR_TAB
	     AS1 (jmp, 1b) CR_TAB
	     AS1 (2:,);

    case 3:
    case 4:
      switch (INTVAL (operands[2])) {
      case 1:
        return AS2 (clrb, status, 0) CR_TAB
      	       AS2 (rl, w, %D1) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
	       AS2 (rl, w, %C1) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS2 (rl, w, %B1) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS2 (rl, w, %A1) CR_TAB
	       AS2 (mov, %A0, w);

      case 2:
        return AS2 (clrb, status, 0) CR_TAB
      	       AS2 (rl, w, %D1) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
	       AS2 (rl, w, %C1) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS2 (rl, w, %B1) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS2 (rl, w, %A1) CR_TAB
	       AS2 (mov, %A0, w) CR_TAB
      	       AS2 (clrb, status, 0) CR_TAB
      	       AS1 (rl, %D0) CR_TAB
	       AS1 (rl, %C0) CR_TAB
	       AS1 (rl, %B0) CR_TAB
	       AS1 (rl, %A0);

      case 8:
        return AS2 (mov, w, %B1) CR_TAB
	       AS2 (mov, %A0, w) CR_TAB
	       AS2 (mov, w, %C1) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS2 (mov, w, %D1) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS1 (clr, %D0);

      case 16:
        return AS2 (mov, w, %C1) CR_TAB
	       AS2 (mov, %A0, w) CR_TAB
	       AS2 (mov, w, %D1) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS1 (clr, %C0) CR_TAB
	       AS1 (clr, %D0);

      case 23:
        return AS2 (rr, w, %C1)	CR_TAB
	       AS2 (mov, w, %D1) CR_TAB
	       AS2 (mov, %A0, w) CR_TAB
	       AS1 (clr, %B0) CR_TAB
	       AS1 (clr, %C0) CR_TAB
	       AS1 (clr, %D0) CR_TAB
	       AS1 (rr, %A0) CR_TAB
	       AS1 (rr, %B0);

      case 24:
        return AS2 (mov, w, %D1) CR_TAB
	       AS2 (mov, %A0, w) CR_TAB
	       AS1 (clr, %B0) CR_TAB
	       AS1 (clr, %C0) CR_TAB
	       AS1 (clr, %D0);

      case 31:
        return AS2 (rr, w, %D1)	CR_TAB
	       AS1 (clr, %A0) CR_TAB
	       AS1 (clr, %B0) CR_TAB
	       AS1 (clr, %C0) CR_TAB
	       AS1 (clr, %D0) CR_TAB
	       AS1 (rr, %A0);

      default:
        return AS2 (mov, w, %A1) CR_TAB
	       AS2 (mov, %A0, w) CR_TAB
	       AS2 (mov, w, %B1) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS2 (mov, w, %C1) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS2 (mov, w, %D1) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
               AS2 (mov, w, %2) CR_TAB
      	       AS1 (1:,) CR_TAB
      	       AS2 (clrb, status, 0) CR_TAB
      	       AS1 (rl, %D0) CR_TAB
	       AS1 (rl, %C0) CR_TAB
	       AS1 (rl, %B0) CR_TAB
	       AS1 (rl, %A0) CR_TAB
	       AS1 (decsz, wreg) CR_TAB
	       AS1 (page, 1b) CR_TAB
	       AS1 (jmp, 1b);
      }
    default:
      abort ();
    }
  }")

(define_insn_and_split "ashldi3_split"
  [(set (match_operand:DI 0 "nonimmediate_operand"          "=ro,&ro,&rS")
  	(ashift:DI (match_operand:DI 1 "nonimmediate_operand" "0, rS, ro")
		   (match_operand:QI 2 "const_int_operand"    "n,  n,  n")))]
  "((INTVAL (operands[2]) >= 32) || (INTVAL (operands[2]) == 16))"
  "#"
  "&& ip2k_reorg_split_dimode"
  [(const_int 0)]
  "{
    operands[3] = ip2k_get_high_half (operands[0], SImode);
    operands[4] = ip2k_get_low_half (operands[0], SImode);
    operands[5] = ip2k_get_low_half (operands[1], SImode);

    if (INTVAL (operands[2]) == 16)
      {
        operands[6] = ip2k_get_high_half (operands[1], SImode);
	operands[7] = ip2k_get_high_half (operands[3], HImode);
	operands[8] = ip2k_get_low_half (operands[3], HImode);
	operands[9] = ip2k_get_high_half (operands[4], HImode);
	operands[10] = ip2k_get_low_half (operands[4], HImode);
	operands[11] = ip2k_get_low_half (operands[6], HImode);
	operands[12] = ip2k_get_high_half (operands[5], HImode);
	operands[13] = ip2k_get_low_half (operands[5], HImode);
	emit_insn (gen_movhi (operands[7], operands[11]));
	emit_insn (gen_movhi (operands[8], operands[12]));
	emit_insn (gen_movhi (operands[9], operands[13]));
	emit_insn (gen_movhi (operands[10], GEN_INT (0)));
      }
    else if (INTVAL (operands[2]) == 32)
      {
        emit_insn (gen_movsi (operands[3], operands[5]));
        emit_insn (gen_movsi (operands[4], GEN_INT (0)));
      }
    else
      {
        operands[6] = GEN_INT (INTVAL (operands[2]) - 32);
	emit_insn (gen_ashlsi3 (operands[3], operands[5], operands[6]));
        emit_insn (gen_movsi (operands[4], GEN_INT (0)));
      }
  }")

;;
;; Arithmetic shift right instructions.
;;

(define_expand "ashrqi3"
  [(set (match_operand:QI 0 "nonimmediate_operand" "")
	(ashiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "")
		     (match_operand:QI 2 "general_operand" "")))]
  ""
  "if (operands[2] == const0_rtx)
     {
       emit_move_insn (operands[0], operands[1]);
       DONE;
     }
  ")

(define_insn "*ashrqi3"
  [(set
    (match_operand:QI 0 "nonimmediate_operand" "=roR,roR, rS,roR, rS,roR, rS")
    (ashiftrt:QI
     (match_operand:QI 1 "nonimmediate_operand" "0,  0,  0, rS,roR, rS,roR")
     (match_operand:QI 2 "general_operand"      "N, rS,roR,  N,  N,  L,  L")))]
  ""
  "*{
    switch (which_alternative)
      {
      case 0:
        return AS2 (rl, w, %0) CR_TAB
	       AS1 (rr, %0);

      case 3:
      case 4:
        return AS2 (rl, w, %1) CR_TAB /* dup the sign bit */
	       AS2 (rr, w, %1) CR_TAB
	       AS2 (mov, %0, w);

      case 5:
      case 6:
	/* Do >> by left-shifting partially into MULH. */
	operands[2] = GEN_INT (8 - INTVAL (operands[2]));
	return AS2 (mov, w, %1) CR_TAB
       	       AS2 (muls, w, %e2) CR_TAB
	       AS2 (mov, w, mulh) CR_TAB
	       AS2 (mov, %0, w);

      case 1:
      case 2:
      default:
	return AS2 (mov, w, %2) CR_TAB
	       AS1 (snz,) CR_TAB
	       AS1 (page, 2f) CR_TAB
	       AS1 (jmp, 2f) CR_TAB
	       AS1 (1:,) CR_TAB
	       AS2 (setb, status, 0) CR_TAB
	       AS2 (sb, %0, 7) CR_TAB
	       AS2 (clrb, status, 0) CR_TAB
	       AS1 (rr, %0) CR_TAB
	       AS1 (decsz, wreg) CR_TAB
	       AS1 (page, 1b) CR_TAB
	       AS1 (jmp, 1b) CR_TAB
	       AS1 (2:,);
      }
  }")

(define_insn "ashrhi3" ;		             0   1   2  3   4
  [(set (match_operand:HI 0 "nonimmediate_operand" "=ro,&rS,&ro,ro, rS")
        (ashiftrt:HI
	 (match_operand:HI 1 "nonimmediate_operand"  "0, ro, rS, 0,  0")
	 (match_operand:QI 2 "general_operand"       "L,  L,  L,rS,roR")))]
  ""
  "*{
    switch (which_alternative) {
    case 0:
      switch (INTVAL (operands[2])) {
      case 1:
        return AS2 (rl, w, %H0) CR_TAB
	       AS1 (rr, %H0) CR_TAB
	       AS1 (rr, %L0);

      case 2:
        return AS2 (rl, w, %H0) CR_TAB
	       AS1 (rr, %H0) CR_TAB
	       AS1 (rr, %L0) CR_TAB
	       AS2 (rl, w, %H0) CR_TAB
	       AS1 (rr, %H0) CR_TAB
	       AS1 (rr, %L0);

      case 8:
	return AS2 (mov, w, %H0) CR_TAB
	       AS2 (mov, %L0, w) CR_TAB
	       AS1 (clr, %H0) CR_TAB
	       AS2 (snb, %L0, 7) CR_TAB
	       AS1 (not, %H0);

      default:
	return AS2 (mov, w, %2) CR_TAB
	       AS1 (1:,) CR_TAB
	       AS2 (setb, status, 0) CR_TAB
	       AS2 (sb, %H0, 7) CR_TAB
	       AS2 (clrb, status, 0) CR_TAB
	       AS1 (rr, %H0) CR_TAB
	       AS1 (rr, %L0) CR_TAB
	       AS1 (decsz, wreg) CR_TAB
	       AS1 (page, 1b) CR_TAB
	       AS1 (jmp, 1b);
      }

    case 1:
    case 2:
      switch (INTVAL (operands[2])) {
      case 1:
        return AS2 (rl, w, %H1) CR_TAB
	       AS2 (rr, w, %H1) CR_TAB
	       AS2 (mov, %H0, w) CR_TAB
	       AS2 (rr, w, %L1) CR_TAB
	       AS2 (mov, %L0, w);

      case 2:
        return AS2 (rl, w, %H1) CR_TAB
	       AS2 (rr, w, %H1) CR_TAB
	       AS2 (mov, %H0, w) CR_TAB
	       AS2 (rr, w, %L1) CR_TAB
	       AS2 (mov, %L0, w) CR_TAB
	       AS2 (rl, w, %H0) CR_TAB
	       AS1 (rr, %H0) CR_TAB
	       AS1 (rr, %L0);

      case 8:
	return AS2 (mov, w, %H1) CR_TAB
	       AS2 (mov, %L0, w) CR_TAB
	       AS1 (clr, %H0) CR_TAB
	       AS2 (snb, %L0, 7) CR_TAB
	       AS1 (not, %H0);

      default:
	return AS2 (mov, w, %L1) CR_TAB
	       AS2 (mov, %L0, w) CR_TAB
	       AS2 (mov, w, %H1) CR_TAB
	       AS2 (mov, %H0, w) CR_TAB
	       AS2 (mov, w, %2) CR_TAB
	       AS1 (1:,) CR_TAB
	       AS2 (setb, status, 0) CR_TAB
	       AS2 (sb, %H0, 7) CR_TAB
	       AS2 (clrb, status, 0) CR_TAB
	       AS1 (rr, %H0) CR_TAB
	       AS1 (rr, %L0) CR_TAB
	       AS1 (decsz, wreg) CR_TAB
	       AS1 (page, 1b) CR_TAB
	       AS1 (jmp, 1b);
      }

    case 3:
    case 4:
      return AS2 (mov, w, %2) CR_TAB
             AS1 (snz,) CR_TAB
	     AS1 (page, 2f) CR_TAB
	     AS1 (jmp, 2f) CR_TAB
	     AS1 (1:,) CR_TAB
	     AS2 (setb, status, 0) CR_TAB
	     AS2 (sb, %H0, 7) CR_TAB
	     AS2 (clrb, status, 0) CR_TAB
             AS1 (rr, %H0) CR_TAB
	     AS1 (rr, %L0) CR_TAB
	     AS1 (decsz, wreg) CR_TAB
	     AS1 (page, 1b) CR_TAB
	     AS1 (jmp, 1b) CR_TAB
	     AS1 (2:,);

    default:
      abort();
    }
  }")

(define_insn "ashrsi3"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=ro, rS,ro,&ro,&rS")
	(ashiftrt:SI
	 (match_operand:SI 1 "nonimmediate_operand"  "0,  0, 0, rS, ro")
	 (match_operand:QI 2 "general_operand"       "L,roR,rS,  L,  L")))]
  ""
  "*{
    switch (which_alternative) {
    case 0:
      switch (INTVAL (operands[2])) {
      case 1:
        return AS2 (rl, w, %A0) CR_TAB /* dup the sign bit */
	       AS1 (rr, %A0) CR_TAB
	       AS1 (rr, %B0) CR_TAB
	       AS1 (rr, %C0) CR_TAB
	       AS1 (rr, %D0);

      case 2:
        return AS2 (rl, w, %A0) CR_TAB /* dup the sign bit */
	       AS1 (rr, %A0) CR_TAB
	       AS1 (rr, %B0) CR_TAB
	       AS1 (rr, %C0) CR_TAB
	       AS1 (rr, %D0) CR_TAB
               AS2 (rl, w, %A0) CR_TAB
	       AS1 (rr, %A0) CR_TAB
	       AS1 (rr, %B0) CR_TAB
	       AS1 (rr, %C0) CR_TAB
	       AS1 (rr, %D0);

      case 8:
        return AS2 (mov, w, %C0) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
	       AS2 (mov, w, %B0) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS2 (mov, w, %A0) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS1 (clr, %A0) CR_TAB
	       AS2 (snb, %B0, 7) CR_TAB
	       AS1 (not, %A0);

      case 16:
        return AS2 (mov, w, %B0) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
	       AS2 (mov, w, %A0) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS1 (clr, WREG) CR_TAB
	       AS2 (snb, %C0, 7) CR_TAB
	       AS1 (not, WREG) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS2 (mov, %A0, w);

      case 23:
        return AS2 (rl, w, %B0)	CR_TAB
	       AS2 (mov, w, %A0) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
	       AS1 (clr, WREG) CR_TAB
	       AS2 (snb, %D0, 7) CR_TAB
	       AS1 (not, WREG) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS2 (mov, %A0, w) CR_TAB
	       AS1 (rl, %D0) CR_TAB
	       AS1 (rl, %C0);

      case 24:
        return AS2 (mov, w, %A0) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
	       AS1 (clr, WREG) CR_TAB
	       AS2 (snb, %D0, 7) CR_TAB
	       AS1 (not, WREG) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS2 (mov, %A0, w);

      case 31:
        return AS2 (rl, w, %A0)	CR_TAB
	       AS1 (clr, WREG) CR_TAB
	       AS2 (snb, %A0, 7) CR_TAB
	       AS1 (not, WREG) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS2 (mov, %A0, w) CR_TAB
	       AS1 (rl, %D0);

      default:
        return AS2 (mov, w, %2) CR_TAB
      	       AS1 (1:,) CR_TAB
	       AS2 (setb, status, 0) CR_TAB
	       AS2 (sb, %A0, 7) CR_TAB
      	       AS2 (clrb, status, 0) CR_TAB
      	       AS1 (rr, %A0) CR_TAB
	       AS1 (rr, %B0) CR_TAB
	       AS1 (rr, %C0) CR_TAB
	       AS1 (rr, %D0) CR_TAB
	       AS1 (decsz, WREG) CR_TAB
	       AS1 (page, 1b) CR_TAB
	       AS1 (jmp, 1b);
      }

    case 1:
    case 2:
      return AS2 (mov, w, %2) CR_TAB
      	     AS1 (snz,) CR_TAB
	     AS1 (page, 2f) CR_TAB
	     AS1 (jmp, 2f) CR_TAB
      	     AS1 (1:,) CR_TAB
	     AS2 (setb, status, 0) CR_TAB
	     AS2 (sb, %A0, 7) CR_TAB
      	     AS2 (clrb, status, 0) CR_TAB
      	     AS1 (rr, %A0) CR_TAB
	     AS1 (rr, %B0) CR_TAB
	     AS1 (rr, %C0) CR_TAB
	     AS1 (rr, %D0) CR_TAB
	     AS1 (decsz, WREG) CR_TAB
	     AS1 (page, 1b) CR_TAB
	     AS1 (jmp, 1b) CR_TAB
	     AS1 (2:,);

    case 3:
    case 4:
      switch (INTVAL (operands[2])) {
      case 1:
        return AS2 (rl, w, %A1) CR_TAB /* dup the sign bit */
	       AS2 (rr, w, %A1) CR_TAB
	       AS2 (mov, %A0, w) CR_TAB
	       AS2 (rr, w, %B1) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS2 (rr, w, %C1) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS2 (rr, w, %D1) CR_TAB
	       AS2 (mov, %D0, w);

      case 2:
        return AS2 (rl, w, %A1) CR_TAB /* dup the sign bit */
	       AS2 (rr, w, %A1) CR_TAB
	       AS2 (mov, %A0, w) CR_TAB
	       AS2 (rr, w, %B1) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS2 (rr, w, %C1) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS2 (rr, w, %D1) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
	       AS2 (rl, w, %A0) CR_TAB
               AS1 (rr, %A0) CR_TAB
	       AS1 (rr, %B0) CR_TAB
	       AS1 (rr, %C0) CR_TAB
	       AS1 (rr, %D0);

      case 8:
        return AS2 (mov, w, %C1) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
	       AS2 (mov, w, %B1) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS2 (mov, w, %A1) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS1 (clr, %A0) CR_TAB
	       AS2 (snb, %B0, 7) CR_TAB
	       AS1 (not, %A0);

      case 16:
        return AS2 (mov, w, %B1) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
	       AS2 (mov, w, %A1) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS1 (clr, WREG) CR_TAB
	       AS2 (snb, %C0, 7) CR_TAB
	       AS1 (not, WREG) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS2 (mov, %A0, w);

      case 23:
        return AS2 (rl, w, %B1)	CR_TAB
	       AS2 (mov, w, %A1) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
	       AS1 (clr, WREG) CR_TAB
	       AS2 (snb, %D0, 7) CR_TAB
	       AS1 (not, WREG) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS2 (mov, %A0, w) CR_TAB
	       AS1 (rl, %D0) CR_TAB
	       AS1 (rl, %C0);

      case 24:
        return AS2 (mov, w, %A1) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
	       AS1 (clr, WREG) CR_TAB
	       AS2 (snb, %D0, 7) CR_TAB
	       AS1 (not, WREG) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS2 (mov, %A0, w);

      case 31:
        return AS2 (rl, w, %A1)	CR_TAB
	       AS1 (clr, WREG) CR_TAB
	       AS2 (snb, %A1, 7) CR_TAB
	       AS1 (not, WREG) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS2 (mov, %A0, w) CR_TAB
	       AS1 (rl, %D0);

      default:
        return AS2 (mov, w, %A1) CR_TAB
	       AS2 (mov, %A0, w) CR_TAB
	       AS2 (mov, w, %B1) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS2 (mov, w, %C1) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS2 (mov, w, %D1) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
               AS2 (mov, w, %2) CR_TAB
      	       AS1 (1:,) CR_TAB
	       AS2 (setb, status, 0) CR_TAB
	       AS2 (sb, %A0, 7) CR_TAB
      	       AS2 (clrb, status, 0) CR_TAB
      	       AS1 (rr, %A0) CR_TAB
	       AS1 (rr, %B0) CR_TAB
	       AS1 (rr, %C0) CR_TAB
	       AS1 (rr, %D0) CR_TAB
	       AS1 (decsz, wreg) CR_TAB
	       AS1 (page, 1b) CR_TAB
	       AS1 (jmp, 1b);
      }
    default:
      abort ();
    }
  }")

;;
;; Logical shift right instructions.
;;

(define_insn "lshrqi3"
  [(set (match_operand:QI
	 0 "nonimmediate_operand" "=roR, rS,roR,roR, rS,&roR,roR, rS")
	(lshiftrt:QI
	 (match_operand:QI
	  1 "nonimmediate_operand"   "0,  0,  0, rS,roR,  rS, rS,roR")
	 (match_operand:QI
	  2 "general_operand"        "N,roR, rS,  N,  N,  rS,  L,  L")))]
  ""
  "*{
    switch (which_alternative)
      {
      case 0:
        return AS2 (clrb, status, 0) CR_TAB
	       AS1 (rr, %0);

      case 1:
      case 2:
        return AS2 (mov, w, %2) CR_TAB
	       AS1 (snz,) CR_TAB
	       AS1 (page, 2f) CR_TAB
	       AS1 (jmp, 2f) CR_TAB
	       AS1 (1:,) CR_TAB
	       AS2 (clrb, status, 0) CR_TAB
	       AS1 (rr, %0) CR_TAB
	       AS1 (decsz, wreg) CR_TAB
	       AS1 (page, 1b) CR_TAB
	       AS1 (jmp, 1b) CR_TAB
	       AS1 (2:,);

      case 3:
      case 4:
        return AS2 (clrb, status, 0) CR_TAB
	       AS2 (rr, w, %1) CR_TAB
	       AS2 (mov, %0, w);

      case 5:
        return AS2 (mov, w, %1) CR_TAB
	       AS2 (mov, %0, w) CR_TAB
	       AS2 (mov, w, %2) CR_TAB
	       AS1 (snz,) CR_TAB
	       AS1 (page, 2f) CR_TAB
	       AS1 (jmp, 2f) CR_TAB
	       AS1 (1:,)
	       AS2 (clrb, status, 0) CR_TAB
	       AS1 (rr, %0) CR_TAB
	       AS1 (decsz, wreg) CR_TAB
	       AS1 (page, 1b) CR_TAB
	       AS1 (jmp, 1b) CR_TAB
	       AS1 (2:,);

      case 6:
      case 7:
	/* Do >> by left-shifting partially into MULH. */
	operands[2] = GEN_INT (8 - INTVAL (operands[2]));
	return AS2 (mov, w, %1) CR_TAB
       	       AS2 (mulu, w, %e2) CR_TAB
	       AS2 (mov, w, mulh) CR_TAB
	       AS2 (mov, %0, w);
      default:
        abort ();
      }
  }")

(define_insn_and_split "lshrhi3_split"
  [(set (match_operand:HI 0 "nonimmediate_operand"             "=ro,rS")
  	(lshiftrt:HI (match_operand:HI 1 "nonimmediate_operand" "rS,ro")
		   (match_operand:QI 2 "const_int_operand"       "n, n")))]
  "(INTVAL (operands[2]) >= 8)"
  "#"
  "&& ip2k_reorg_split_himode"
  [(const_int 0)]
  "{
    operands[3] = ip2k_get_high_half (operands[0], QImode);
    operands[4] = ip2k_get_low_half (operands[0], QImode);
    operands[5] = ip2k_get_high_half (operands[1], QImode);

    if (INTVAL (operands[2]) == 8)
      emit_insn (gen_movqi (operands[4], operands[5]));
    else
      {
        operands[6] = GEN_INT (INTVAL (operands[2]) - 8);
	emit_insn (gen_lshrqi3 (operands[4], operands[5], operands[6]));
      }
    emit_insn (gen_movqi (operands[3], GEN_INT (0)));
  }")

(define_insn "lshrhi3" ;			      0   1   2  3   4
  [(set (match_operand:HI 0 "nonimmediate_operand" "=ro,&rS,&ro,ro, rS")
        (lshiftrt:HI
	 (match_operand:HI 1 "nonimmediate_operand" " 0, ro, rS, 0,  0")
	 (match_operand:QI 2 "general_operand"       "L,  L,  L,rS,roR")))]
  ""
  "*{
    switch (which_alternative)
      {
      case 0:
        switch (INTVAL (operands[2]))
	  {
          case 1:
      	    return AS2 (clrb, status, 0) CR_TAB
	           AS1 (rr, %H0) CR_TAB
	           AS1 (rr, %L0);

          case 2:
            return AS2 (clrb, status, 0) CR_TAB
	           AS1 (rr, %H0) CR_TAB
	           AS1 (rr, %L0) CR_TAB
      	           AS2 (clrb, status, 0) CR_TAB
	           AS1 (rr, %H0) CR_TAB
	           AS1 (rr, %L0);

	  case 3:
	  case 4:
	  case 5:
	  case 6:
	  case 7:
	    operands[2] = GEN_INT (8 - INTVAL (operands[2]));
	    return AS2 (mov, w, %L0) CR_TAB
       	           AS2 (mulu, w, %e2) CR_TAB
	           AS2 (mov, w, MULH) CR_TAB
		   AS2 (mov, %L0, w) CR_TAB
		   AS2 (mov, w, %H0) CR_TAB
		   AS2 (mulu, w, %e2) CR_TAB
		   AS2 (or, %L0, w) CR_TAB
		   AS2 (mov, w, MULH) CR_TAB
		   AS2 (mov, %H0, w);

          default:
	    /* Should be caught by a different insn pattern */
	    abort ();
          }

      case 1:
      case 2:
        switch (INTVAL (operands[2]))
	  {
          case 1:
            return AS2 (clrb, status, 0) CR_TAB
	           AS2 (rr, w, %H1) CR_TAB
	           AS2 (mov, %H0, w) CR_TAB
	           AS2 (rr, w, %L1) CR_TAB
	           AS2 (mov, %L0, w);

          case 2:
      	    return AS2 (clrb, status, 0) CR_TAB
	           AS2 (rr, w, %H1) CR_TAB
	           AS2 (mov, %H0, w) CR_TAB
	           AS2 (rr, w, %L1) CR_TAB
	           AS2 (mov, %L0, w) CR_TAB
      	           AS2 (clrb, status, 0) CR_TAB
	           AS1 (rr, %H0) CR_TAB
	           AS1 (rr, %L0);

	  case 3:
	  case 4:
	  case 5:
	  case 6:
	  case 7:
	    operands[2] = GEN_INT (8 - INTVAL (operands[2]));
	    return AS2 (mov, w, %L1) CR_TAB
       	           AS2 (mulu, w, %e2) CR_TAB
	           AS2 (mov, w, MULH) CR_TAB
		   AS2 (mov, %L0, w) CR_TAB
		   AS2 (mov, w, %H1) CR_TAB
		   AS2 (mulu, w, %e2) CR_TAB
		   AS2 (or, %L0, w) CR_TAB
		   AS2 (mov, w, MULH) CR_TAB
		   AS2 (mov, %H0, w);

          default:
	    /* Should be caught by a different insn pattern */
	    abort ();
          }

      case 3:
      case 4:
        return AS2 (mov, w, %2) CR_TAB
               AS1 (snz,) CR_TAB
	       AS1 (page, 2f) CR_TAB
	       AS1 (jmp, 2f) CR_TAB
	       AS1 (1:,) CR_TAB
	       AS2 (clrb, status, 0) CR_TAB
               AS1 (rr, %H0) CR_TAB
	       AS1 (rr, %L0) CR_TAB
	       AS1 (decsz, wreg) CR_TAB
	       AS1 (page, 1b) CR_TAB
	       AS1 (jmp, 1b) CR_TAB
	       AS1 (2:,);

      default:
        abort();
      }
  }")

(define_insn_and_split "lshrsi3_split"
  [(set (match_operand:SI 0 "nonimmediate_operand"            "=ro,&ro,&rS")
  	(lshiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "0, rS, ro")
		   (match_operand:QI 2 "const_int_operand"      "n,  n,  n")))]
  "(INTVAL (operands[2]) >= 16)"
  "#"
  "&& ip2k_reorg_split_simode"
  [(const_int 0)]
  "{
    operands[3] = ip2k_get_high_half (operands[0], HImode);
    operands[4] = ip2k_get_low_half (operands[0], HImode);
    operands[5] = ip2k_get_high_half (operands[1], HImode);

    if (INTVAL (operands[2]) == 16)
      emit_insn (gen_movhi (operands[4], operands[5]));
    else
      {
        operands[6] = GEN_INT (INTVAL (operands[2]) - 16);
	emit_insn (gen_lshrhi3 (operands[4], operands[5], operands[6]));
      }
    emit_insn (gen_movhi (operands[3], GEN_INT (0)));
  }")

;; This occurs frequently in supporting FP among other things,
;; and out-of-line is almost as big as inline, so....
;;
(define_insn "lshrsi3"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=ro, rS,ro,&ro,&rS")
	(lshiftrt:SI
	 (match_operand:SI 1 "nonimmediate_operand"  "0,  0, 0, rS, ro")
	 (match_operand:QI 2 "general_operand"       "L,roR,rS,  L,  L")))]

  ""
  "*{
    switch (which_alternative) {
    case 0:
      switch (INTVAL (operands[2])) {
      case 1:
        return AS2 (clrb, status, 0) CR_TAB
	       AS1 (rr, %A0) CR_TAB
	       AS1 (rr, %B0) CR_TAB
	       AS1 (rr, %C0) CR_TAB
	       AS1 (rr, %D0);

      case 2:
        return AS2 (clrb, status, 0) CR_TAB
	       AS1 (rr, %A0) CR_TAB
	       AS1 (rr, %B0) CR_TAB
	       AS1 (rr, %C0) CR_TAB
	       AS1 (rr, %D0) CR_TAB
               AS2 (clrb, status, 0) CR_TAB
	       AS1 (rr, %A0) CR_TAB
	       AS1 (rr, %B0) CR_TAB
	       AS1 (rr, %C0) CR_TAB
	       AS1 (rr, %D0);

      case 8:
        return AS2 (mov, w, %C0) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
	       AS2 (mov, w, %B0) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS2 (mov, w, %A0) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS1 (clr, %A0);

      case 16:
        return AS2 (mov, w, %B0) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
	       AS2 (mov, w, %A0) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS1 (clr, %B0) CR_TAB
	       AS1 (clr, %A0);

      case 23:
        return AS2 (rl, w, %B0)	CR_TAB
	       AS2 (mov, w, %A0) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
	       AS1 (clr, %C0) CR_TAB
	       AS1 (clr, %B0) CR_TAB
	       AS1 (clr, %A0) CR_TAB
	       AS1 (rl, %D0) CR_TAB
	       AS1 (rl, %C0);

      case 24:
        return AS2 (mov, w, %A0) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
	       AS1 (clr, %C0) CR_TAB
	       AS1 (clr, %B0) CR_TAB
	       AS1 (clr, %A0);

      case 31:
        return AS2 (rl, w, %A0)	CR_TAB
	       AS1 (clr, %D0) CR_TAB
	       AS1 (clr, %C0) CR_TAB
	       AS1 (clr, %B0) CR_TAB
	       AS1 (clr, %A0) CR_TAB
	       AS1 (rl, %D0);

      default:
        return AS2 (mov, w, %2) CR_TAB
      	       AS1 (1:,) CR_TAB
      	       AS2 (clrb, status, 0) CR_TAB
      	       AS1 (rr, %A0) CR_TAB
	       AS1 (rr, %B0) CR_TAB
	       AS1 (rr, %C0) CR_TAB
	       AS1 (rr, %D0) CR_TAB
	       AS1 (decsz, wreg) CR_TAB
	       AS1 (page, 1b) CR_TAB
	       AS1 (jmp, 1b);
      }

    case 1:
    case 2:
      return AS2 (mov, w, %2) CR_TAB
      	     AS1 (snz,) CR_TAB
	     AS1 (page, 2f) CR_TAB
	     AS1 (jmp, 2f) CR_TAB
      	     AS1 (1:,) CR_TAB
      	     AS2 (clrb, status, 0) CR_TAB
      	     AS1 (rr, %A0) CR_TAB
	     AS1 (rr, %B0) CR_TAB
	     AS1 (rr, %C0) CR_TAB
	     AS1 (rr, %D0) CR_TAB
	     AS1 (decsz, wreg) CR_TAB
	     AS1 (page, 1b) CR_TAB
	     AS1 (jmp, 1b) CR_TAB
	     AS1 (2:,);

    case 3:
    case 4:
      switch (INTVAL (operands[2])) {
      case 1:
        return AS2 (clrb, status, 0) CR_TAB
	       AS2 (rr, w, %A1) CR_TAB
	       AS2 (mov, %A0, w) CR_TAB
	       AS2 (rr, w, %B1) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS2 (rr, w, %C1) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS2 (rr, w, %D1) CR_TAB
	       AS2 (mov, %D0, w);

      case 2:
        return AS2 (clrb, status, 0) CR_TAB
	       AS2 (rr, w, %A1) CR_TAB
	       AS2 (mov, %A0, w) CR_TAB
	       AS2 (rr, w, %B1) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS2 (rr, w, %C1) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS2 (rr, w, %D1) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
      	       AS2 (clrb, status, 0) CR_TAB
      	       AS1 (rr, %A0) CR_TAB
	       AS1 (rr, %B0) CR_TAB
	       AS1 (rr, %C0) CR_TAB
	       AS1 (rr, %D0);

      case 8:
        return AS2 (mov, w, %C1) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
	       AS2 (mov, w, %B1) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS2 (mov, w, %A1) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS1 (clr, %A0);

      case 16:
        return AS2 (mov, w, %B1) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
	       AS2 (mov, w, %A1) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS1 (clr, %B0) CR_TAB
	       AS1 (clr, %A0);

      case 23:
        return AS2 (rl, w, %B1)	CR_TAB
	       AS2 (mov, w, %A1) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
	       AS1 (clr, %C0) CR_TAB
	       AS1 (clr, %B0) CR_TAB
	       AS1 (clr, %A0) CR_TAB
	       AS1 (rl, %D0) CR_TAB
	       AS1 (rl, %C0);

      case 24:
        return AS2 (mov, w, %A1) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
	       AS1 (clr, %C0) CR_TAB
	       AS1 (clr, %B0) CR_TAB
	       AS1 (clr, %A0);

      case 31:
        return AS2 (rl, w, %A1)	CR_TAB
	       AS1 (clr, %D0) CR_TAB
	       AS1 (clr, %C0) CR_TAB
	       AS1 (clr, %B0) CR_TAB
	       AS1 (clr, %A0) CR_TAB
	       AS1 (rl, %D0);

      default:
        return AS2 (mov, w, %A1) CR_TAB
	       AS2 (mov, %A0, w) CR_TAB
	       AS2 (mov, w, %B1) CR_TAB
	       AS2 (mov, %B0, w) CR_TAB
	       AS2 (mov, w, %C1) CR_TAB
	       AS2 (mov, %C0, w) CR_TAB
	       AS2 (mov, w, %D1) CR_TAB
	       AS2 (mov, %D0, w) CR_TAB
               AS2 (mov, w, %2) CR_TAB
      	       AS1 (1:,) CR_TAB
      	       AS2 (clrb, status, 0) CR_TAB
      	       AS1 (rr, %A0) CR_TAB
	       AS1 (rr, %B0) CR_TAB
	       AS1 (rr, %C0) CR_TAB
	       AS1 (rr, %D0) CR_TAB
	       AS1 (decsz, wreg) CR_TAB
	       AS1 (page, 1b) CR_TAB
	       AS1 (jmp, 1b);
      }
    default:
      abort ();
    }
  }")

(define_insn_and_split "lshrdi3_split"
  [(set (match_operand:DI 0 "nonimmediate_operand"            "=ro,&ro,&rS")
  	(lshiftrt:DI (match_operand:DI 1 "nonimmediate_operand" "0, rS, ro")
		   (match_operand:QI 2 "const_int_operand"      "n,  n,  n")))]
  "((INTVAL (operands[2]) >= 32) || (INTVAL (operands[2]) == 16))"
  "#"
  "&& ip2k_reorg_split_dimode"
  [(const_int 0)]
  "{
    operands[3] = ip2k_get_high_half (operands[0], SImode);
    operands[4] = ip2k_get_low_half (operands[0], SImode);
    operands[5] = ip2k_get_high_half (operands[1], SImode);

    if (INTVAL (operands[2]) == 16)
      {
        operands[6] = ip2k_get_low_half (operands[1], SImode);
	operands[7] = ip2k_get_high_half (operands[3], HImode);
	operands[8] = ip2k_get_low_half (operands[3], HImode);
	operands[9] = ip2k_get_high_half (operands[4], HImode);
	operands[10] = ip2k_get_low_half (operands[4], HImode);
	operands[11] = ip2k_get_high_half (operands[6], HImode);
	operands[12] = ip2k_get_low_half (operands[5], HImode);
	operands[13] = ip2k_get_high_half (operands[5], HImode);
	emit_insn (gen_movhi (operands[10], operands[11]));
	emit_insn (gen_movhi (operands[9], operands[12]));
	emit_insn (gen_movhi (operands[8], operands[13]));
	emit_insn (gen_movhi (operands[7], GEN_INT(0)));
      }
    else if (INTVAL (operands[2]) == 32)
      {
        emit_insn (gen_movsi (operands[4], operands[5]));
        emit_insn (gen_movsi (operands[3], GEN_INT (0)));
      }
    else
      {
        operands[6] = GEN_INT (INTVAL (operands[2]) - 32);
	emit_insn (gen_lshrsi3 (operands[4], operands[5], operands[6]));
        emit_insn (gen_movsi (operands[3], GEN_INT (0)));
      }
  }")

;;
;; Absolute value conversion instructions.
;;

(define_insn "absqi2"
  [(set (match_operand:QI 0 "nonimmediate_operand"	  "=g")
        (abs:QI (match_operand:QI 1 "nonimmediate_operand" "g")))]
  ""
  "mov\\tw,%1\;snb\\twreg,7\;sub\\tw,#0\;mov\\t%0,w")

(define_insn "abssf2"
  [(set (match_operand:SF 0 "nonimmediate_operand"       "=ro")
        (abs:SF (match_operand:SF 1 "nonimmediate_operand" "0")))]
  ""
  "clrb	%A0,7"
  [(set_attr "clobberw" "no")])

;;
;; Negate (X = 0 - Y) instructions.
;;

(define_insn_and_split "negqi2"
  [(set (match_operand:QI 0 "nonimmediate_operand"       "=ro,&ro,&rS")
	(neg:QI (match_operand:QI 1 "nonimmediate_operand" "0, rS, ro")))]
  ""
  "#"
  ""
  [(set (match_dup 0)
	(not:QI (match_dup 1)))
   (set (match_dup 0)
	(plus:QI (match_dup 0)
		 (const_int 1)))]
  "")

(define_insn_and_split "neghi2"
  [(set (match_operand:HI 0 "nonimmediate_operand"       "=ro,&ro,&rS")
	(neg:HI (match_operand:HI 1 "nonimmediate_operand" "0, rS, ro")))]
  ""
  "#"
  ""
  [(set (match_dup 0)
	(not:HI (match_dup 1)))
   (set (match_dup 0)
	(plus:HI (match_dup 0)
		 (const_int 1)))]
  "")

(define_insn_and_split "negsi2"
  [(set (match_operand:SI 0 "nonimmediate_operand"       "=ro,&ro,&rS")
	(neg:SI (match_operand:SI 1 "nonimmediate_operand" "0, rS, ro")))]
  ""
  "#"
  ""
  [(set (match_dup 0)
	(not:SI (match_dup 1)))
   (set (match_dup 0)
	(plus:SI (match_dup 0)
		 (const_int 1)))]
  "")

(define_insn_and_split "negdi2"
  [(set (match_operand:DI 0 "nonimmediate_operand"       "=ro,&ro,&rS")
	(neg:DI (match_operand:DI 1 "nonimmediate_operand" "0, rS, ro")))]
  ""
  "#"
  ""
  [(set (match_dup 0)
	(not:DI (match_dup 1)))
   (set (match_dup 0)
	(plus:DI (match_dup 0)
		 (const_int 1)))]
  "")

;;
;; Bitwise not (one's complement) instructions.
;;

(define_insn "one_cmplqi2"
  [(set (match_operand:QI 0 "nonimmediate_operand"   "=g,roR, rS")
        (not:QI (match_operand:QI 1 "general_operand" "0, rS,roR")))]
  ""
  "@
   not\\t%0
   not\\tw,%1\;mov\\t%0,w
   not\\tw,%1\;mov\\t%0,w"
  [(set_attr "skip" "yes,no,no")
   (set_attr "clobberw" "no,yes,yes")])

(define_insn_and_split "one_cmplhi2"
  [(set (match_operand:HI 0 "nonimmediate_operand"   "=ro,&ro,&rS")
        (not:HI (match_operand:HI 1 "general_operand"  "0, rS, ro")))]
  ""
  "#"
  "(ip2k_reorg_split_himode)"
  [(set (match_dup 3)
	(not:QI (match_dup 4)))
   (set (match_dup 5)
   	(not:QI (match_dup 6)))]
  "{
    operands[3] = ip2k_get_high_half (operands[0], QImode);
    operands[4] = ip2k_get_high_half (operands[1], QImode);
    operands[5] = ip2k_get_low_half (operands[0], QImode);
    operands[6] = ip2k_get_low_half (operands[1], QImode);
  }")

(define_insn_and_split "one_cmplsi2"
  [(set (match_operand:SI 0 "nonimmediate_operand"   "=ro,&ro,&rS")
        (not:SI (match_operand:SI 1 "general_operand"  "0, rS, ro")))]
  ""
  "#"
  "(ip2k_reorg_split_simode)"
  [(set (match_dup 3)
	(not:HI (match_dup 4)))
   (set (match_dup 5)
   	(not:HI (match_dup 6)))]
  "{
    operands[3] = ip2k_get_high_half (operands[0], HImode);
    operands[4] = ip2k_get_high_half (operands[1], HImode);
    operands[5] = ip2k_get_low_half (operands[0], HImode);
    operands[6] = ip2k_get_low_half (operands[1], HImode);
  }")

(define_insn_and_split "one_cmpldi2"
  [(set (match_operand:DI 0 "nonimmediate_operand"   "=ro,&ro,&rS")
        (not:DI (match_operand:DI 1 "general_operand"  "0, rS, ro")))]
  ""
  "#"
  "(ip2k_reorg_split_dimode)"
  [(set (match_dup 3)
	(not:SI (match_dup 4)))
   (set (match_dup 5)
   	(not:SI (match_dup 6)))]
  "{
    operands[3] = ip2k_get_high_half (operands[0], SImode);
    operands[4] = ip2k_get_high_half (operands[1], SImode);
    operands[5] = ip2k_get_low_half (operands[0], SImode);
    operands[6] = ip2k_get_low_half (operands[1], SImode);
  }")

;;
;; Sign extension instructions.
;;

(define_insn "*push_extendqihi2"
  [(set (match_operand:HI 0 "push_operand"                     "=<,<")
        (sign_extend:HI (match_operand:QI 1 "general_operand" "roR,n")))]
  ""
  "@
   push\\t%1%<\;push\\t#0%<\;snb\\t%1,7\;not\\t1(SP)%>%>
   push\\t%L1\;push\\t%H1"
  [(set_attr "clobberw" "no,no")])

(define_insn "extendqihi2"
  [(set (match_operand:HI 0 "nonimmediate_operand"            "=rS,ro,ro")
        (sign_extend:HI (match_operand:QI 1 "general_operand" "roR,rS, n")))]
  ""
  "*{
    switch (which_alternative)
      {
      case 0:
      case 1:
        if (register_operand (operands[0], HImode)
	    && register_operand (operands[1], QImode)
	    && REGNO (operands[0]) == (REGNO (operands[1]) - 1))
	  return AS1 (clr, %H0) CR_TAB
	  	 AS2 (snb, %1, 7) CR_TAB
		 AS1 (not, %H0);
	else
	  return AS2 (mov, w, %1) CR_TAB
	  	 AS2 (mov, %L0, w) CR_TAB
		 AS1 (clr, %H0) CR_TAB
		 AS2 (snb, wreg, 7) CR_TAB
		 AS1 (not, %H0);

      case 2:
	return AS2 (mov, w, %L1) CR_TAB
	       AS2 (mov, %L0, w) CR_TAB
	       AS2 (mov, w, %H1) CR_TAB
	       AS2 (mov, %H0, w);
      default:
        abort ();
      }
  }")

(define_insn "*push_extendhisi2"
  [(set (match_operand:SI 0 "push_operand"                     "=<,<,<")
        (sign_extend:SI (match_operand:HI 1 "general_operand" "roS,n,s")))]
  ""
  "@
   push\\t%L1%<\;push\\t%H1%<\;clr\\twreg\;snb\\t%H1,7\;not\\twreg\;push\\twreg\;push\\twreg%>%>
   push\\t%D1\;push\\t%C1\;push\\t%B1\;push\\t%A1
   push\\t%L1\;push\\t%H1\;push\\t#0\;push\\t#0"
  [(set_attr "clobberw" "yes,no,no")])

(define_insn "extendhisi2"
  [(set (match_operand:SI 0 "nonimmediate_operand"           "=ro,rS,ro,ro")
        (sign_extend:SI (match_operand:HI 1 "general_operand" "rS,ro, n, s")))]
  ""
  "@
   mov\\tw,%L1\;push\\t%H1%<\;pop\\t%C0%>\;mov\\t%D0,w\;clr\\twreg\;snb\\t%C0,7\;not\\twreg\;mov\\t%B0,w\;mov\\t%A0,w
   mov\\tw,%L1\;push\\t%H1%<\;pop\\t%C0%>\;mov\\t%D0,w\;clr\\twreg\;snb\\t%C0,7\;not\\twreg\;mov\\t%B0,w\;mov\\t%A0,w
   mov\\tw,%D1\;mov\\t%D0,w\;mov\\tw,%C1\;mov\\t%C0,w\;mov\\tw,%B1\;mov\\t%B0,w\;mov\\tw,%A1\;mov\\t%A0,w
   mov\\tw,%L1\;push\\t%H1%<\;pop\\t%C0%>\;mov\\t%D0,w\;clr\\t%B0\;clr\\t%A0")

(define_insn "*push_extendqisi2"
  [(set (match_operand:SI 0 "push_operand"                     "=<,<")
        (sign_extend:SI (match_operand:QI 1 "general_operand" "roR,n")))]
  ""
  "@
   push\\t%1%<\;clr\\twreg\;snb\\t%1,7\;not\\twreg\;push\\twreg\;push\\twreg\;push\\twreg%>
   push\\t%D1\;push\\t%C1\;push\\t%B1\;push\\t%A1"
  [(set_attr "clobberw" "yes,no")])

(define_insn "extendqisi2"
  [(set (match_operand:SI 0 "nonimmediate_operand"           "=ro, rS,ro")
        (sign_extend:SI (match_operand:QI 1 "general_operand" "rS,roR, n")))]
  ""
  "@
   mov\\tw,%1\;mov\\t%D0,w\;clr\\twreg\;snb\\t%1,7\;not\\twreg\;mov\\t%C0,w\;mov\\t%B0,w\;mov\\t%A0,w
   mov\\tw,%1\;mov\\t%D0,w\;clr\\twreg\;snb\\t%1,7\;not\\twreg\;mov\\t%C0,w\;mov\\t%B0,w\;mov\\t%A0,w
   mov\\tw,%D1\;mov\\t%D0,w\;mov\\tw,%C1\;mov\\t%C0,w\;mov\\tw,%B1\;mov\\t%B0,w\;mov\\tw,%A1\;mov\\t%A0,w")

;;
;; Zero extension instructions.
;;

(define_insn "*push_zero_extendqihi2"
  [(set (match_operand:HI 0 "push_operand"                      "=<")
        (zero_extend:HI (match_operand:QI 1 "general_operand" "roRi")))]
  ""
  "push\\t%1\;push\\t#0"
  [(set_attr "clobberw" "no")])

(define_insn_and_split "zero_extendqihi2"
  [(set (match_operand:HI 0 "nonimmediate_operand"            "=ro,  rS")
        (zero_extend:HI (match_operand:QI 1 "general_operand" "rSi,roRi")))]
  ""
  "#"
  "ip2k_reorg_completed"
  [(set (match_dup 3) (match_dup 1))
   (set (match_dup 2) (const_int 0))]
  "{
    operands[2] = ip2k_get_high_half (operands[0], QImode);
    operands[3] = ip2k_get_low_half (operands[0], QImode);
  }")

(define_insn "*push_zero_extendhisi2"
  [(set (match_operand:SI 0 "push_operand"                      "=<")
        (zero_extend:SI (match_operand:HI 1 "general_operand" "roSi")))]
  ""
  "push\\t%L1%<\;push\\t%H1%>\;push\\t#0\;push\\t#0")

(define_insn_and_split "zero_extendhisi2"
  [(set (match_operand:SI 0 "nonimmediate_operand"            "=ro, rS")
        (zero_extend:SI (match_operand:HI 1 "general_operand" "rSi,roi")))]
  ""
  "#"
  "ip2k_reorg_completed"
  [(set (match_dup 3) (match_dup 1))
   (set (match_dup 2) (const_int 0))]
  "{
    operands[2] = ip2k_get_high_half (operands[0], HImode);
    operands[3] = ip2k_get_low_half (operands[0], HImode);
  }")

(define_insn "*push_zero_extendqisi2"
  [(set (match_operand:SI 0 "push_operand"                      "=<")
        (zero_extend:SI (match_operand:QI 1 "general_operand" "roRi")))]
  ""
  "push\\t%1\;push\\t#0\;push\\t#0\;push\\t#0"
  [(set_attr "clobberw" "no")])

(define_insn_and_split "zero_extendqisi2"
  [(set (match_operand:SI 0 "nonimmediate_operand"            "=ro,  rS")
        (zero_extend:SI (match_operand:QI 1 "general_operand" "rSi,roRi")))]
  ""
  "#"
  "ip2k_reorg_completed"
  [(set (match_dup 3) (zero_extend:HI (match_dup 1)))
   (set (match_dup 2) (const_int 0))]
  "{
    operands[2] = ip2k_get_high_half (operands[0], HImode);
    operands[3] = ip2k_get_low_half (operands[0], HImode);
  }")

(define_insn "*push_zero_extendsidi2"
  [(set (match_operand:DI 0 "push_operand"                      "=<")
        (zero_extend:DI (match_operand:SI 1 "general_operand" "roSi")))]
  ""
  "push\\t%D1%<\;push\\t%C1%<\;push\\t%B1%<\;push\\t%A1%>%>%>\;push\\t#0\;push\\t#0\;push\\t#0\;push\\t#0")

(define_insn_and_split "zero_extendsidi2"
  [(set (match_operand:DI 0 "nonimmediate_operand"            "=ro, rS")
        (zero_extend:DI (match_operand:SI 1 "general_operand" "rSi,roi")))]
  ""
  "#"
  "ip2k_reorg_completed"
  [(set (match_dup 3) (match_dup 1))
   (set (match_dup 2) (const_int 0))]
  "{
    operands[2] = ip2k_get_high_half (operands[0], SImode);
    operands[3] = ip2k_get_low_half (operands[0], SImode);
  }")

(define_insn "*push_zero_extendhidi2"
  [(set (match_operand:DI 0 "push_operand"                      "=<")
        (zero_extend:DI (match_operand:HI 1 "general_operand" "roSi")))]
  ""
  "push\\t%L1%<\;push\\t%H1%>\;push\\t#0\;push\\t#0\;push\\t#0\;push\\t#0\;push\\t#0\;push\\t#0"
  [(set_attr "clobberw" "no")])

(define_insn_and_split "zero_extendhidi2"
  [(set (match_operand:DI 0 "nonimmediate_operand"            "=ro, rS")
        (zero_extend:DI (match_operand:HI 1 "general_operand" "rSi,roi")))]
  ""
  "#"
  "ip2k_reorg_completed"
  [(set (match_dup 3) (zero_extend:SI (match_dup 1)))
   (set (match_dup 2) (const_int 0))]
  "{
    operands[2] = ip2k_get_high_half (operands[0], SImode);
    operands[3] = ip2k_get_low_half (operands[0], SImode);
  }")

(define_insn "*push_zero_extendqidi2"
  [(set (match_operand:DI 0 "push_operand"                      "=<")
        (zero_extend:DI (match_operand:QI 1 "general_operand" "roRi")))]
  ""
  "push\\t%1\;push\\t#0\;push\\t#0\;push\\t#0\;push\\t#0\;push\\t#0\;push\\t#0\;push\\t#0"
  [(set_attr "clobberw" "no")])

(define_insn_and_split "zero_extendqidi2"
  [(set (match_operand:DI 0 "nonimmediate_operand"            "=ro,  rS")
        (zero_extend:DI (match_operand:QI 1 "general_operand" "rSi,roRi")))]
  ""
  "#"
  "ip2k_reorg_completed"
  [(set (match_dup 3) (zero_extend:SI (match_dup 1)))
   (set (match_dup 2) (const_int 0))]
  "{
    operands[2] = ip2k_get_high_half (operands[0], SImode);
    operands[3] = ip2k_get_low_half (operands[0], SImode);
  }")

;;
;; Truncation instructions.
;;

(define_insn "truncsihi2"
  [(set (match_operand:HI 0 "nonimmediate_operand"         "=rS, ro")
	(truncate:HI (match_operand:SI 1 "general_operand" "roi,rSi")))]
  ""
  "@
   mov\\tw,%D1\;push\\t%C1%<\;pop\\t%H0%>\;mov\\t%L0,w
   mov\\tw,%D1\;push\\t%C1%<\;pop\\t%H0%>\;mov\\t%L0,w")

(define_insn "truncsiqi2"
  [(set (match_operand:QI 0 "nonimmediate_operand"         "=rS, ro")
	(truncate:QI (match_operand:SI 1 "general_operand" "roi,rSi")))]
  ""
  "@
   mov\\tw,%D1\;mov\\t%0,w
   mov\\tw,%D1\;mov\\t%0,w")

(define_insn "trunchiqi2"
  [(set (match_operand:QI 0 "nonimmediate_operand"         "=rS, ro")
	(truncate:QI (match_operand:HI 1 "general_operand" "roi,rSi")))]
  ""
  "@
   mov\\tw,%L1\;mov\\t%0,w
   mov\\tw,%L1\;mov\\t%0,w")

;;
;; Compare with zero (test) instructions.
;;
;; As we don't have a particularly good set of condition codes we simply
;; tagging our comparison operands for use later within our "compare
;; and branch" instructions.
;;

(define_insn "tstqi"
  [(set (cc0)
        (match_operand:QI 0 "nonimmediate_operand" "roR"))]
  ""
  "* return ip2k_set_compare (operands[0], const0_rtx);")

(define_insn "tsthi"
  [(set (cc0)
	(match_operand:HI 0 "nonimmediate_operand" "roS"))]
  ""
  "* return ip2k_set_compare (operands[0], const0_rtx);")

(define_insn "tstsi"
  [(set (cc0)
	(match_operand:SI 0 "nonimmediate_operand" "roS"))]
  ""
  "* return ip2k_set_compare (operands[0], const0_rtx);")

(define_insn "tstdi"
  [(set (cc0)
	(match_operand:DI 0 "nonimmediate_operand" "roS"))]
  ""
  "* return ip2k_set_compare (operands[0], const0_rtx);")

;;
;; General value comparison instructions.
;;
;; As we don't have a particularly good set of condition codes we simply
;; tagging our comparison operands for use later within our "compare
;; and branch" instructions.
;;

(define_insn "cmpqi"
  [(set (cc0)
        (compare (match_operand:QI 0 "nonimmediate_operand" "roR,  rS")
		 (match_operand:QI 1 "general_operand"      "rSn,roRn")))]
  ""
  "* return ip2k_set_compare (operands[0], operands[1]);")

(define_insn "cmphi"
  [(set (cc0)
        (compare (match_operand:HI 0 "nonimmediate_operand" "ro, rS")
		 (match_operand:HI 1 "general_operand"     "rSn,ron")))]
  ""
  "* return ip2k_set_compare (operands[0], operands[1]);")

(define_insn "cmpsi"
  [(set (cc0)
        (compare (match_operand:SI 0 "nonimmediate_operand" "ro, rS")
		 (match_operand:SI 1 "general_operand"     "rSn,ron")))]
  ""
  "* return ip2k_set_compare (operands[0], operands[1]);")

(define_insn "cmpdi"
  [(set (cc0)
  	(compare (match_operand:DI 0 "nonimmediate_operand" "ro, rS")
		 (match_operand:DI 1 "general_operand"     "rSn,ron")))]
  ""
  "* return ip2k_set_compare (operands[0], operands[1]);")

;;
;; Conditional jump instructions.
;;

(define_expand "beq"
  [(set (pc)
        (if_then_else (eq (cc0) (const_int 0))
                      (label_ref (match_operand 0 "" ""))
                      (pc)))]
  ""
  "")

(define_expand "bne"
  [(set (pc)
        (if_then_else (ne (cc0) (const_int 0))
                      (label_ref (match_operand 0 "" ""))
                      (pc)))]
  ""
  "")

(define_expand "bge"
  [(set (pc)
        (if_then_else (ge (cc0) (const_int 0))
                      (label_ref (match_operand 0 "" ""))
                      (pc)))]
  ""
  "")

(define_expand "bgeu"
  [(set (pc)
        (if_then_else (geu (cc0) (const_int 0))
                      (label_ref (match_operand 0 "" ""))
                      (pc)))]
  ""
  "")

(define_expand "blt"
  [(set (pc)
        (if_then_else (lt (cc0) (const_int 0))
                      (label_ref (match_operand 0 "" ""))
                      (pc)))]
  ""
  "")

(define_expand "bltu"
  [(set (pc)
        (if_then_else (ltu (cc0) (const_int 0))
                      (label_ref (match_operand 0 "" ""))
                      (pc)))]
  ""
  "")


(define_expand "ble"
  [(set (pc)
        (if_then_else (le (cc0) (const_int 0))
                      (label_ref (match_operand 0 "" ""))
                      (pc)))]
  ""
  "")

(define_expand "bleu"
  [(set (pc)
        (if_then_else (leu (cc0) (const_int 0))
                      (label_ref (match_operand 0 "" ""))
                      (pc)))]
  ""
  "")

(define_expand "bgt"
  [(set (pc)
        (if_then_else (gt (cc0) (const_int 0))
                      (label_ref (match_operand 0 "" ""))
                      (pc)))]
  ""
  "")

(define_expand "bgtu"
  [(set (pc)
        (if_then_else (gtu (cc0) (const_int 0))
                      (label_ref (match_operand 0 "" ""))
                      (pc)))]
  ""
  "")


;; Implementation of conditional jumps.
;;
;; The assumption is that a previous test or compare instruction will have
;; provided the arguments to be compared to form cc0 and then we perform
;; a compare and branch operation here.
;;
(define_insn "*unsigned_cmp_branch"
  [(set (pc)
        (if_then_else (match_operator 1 "ip2k_unsigned_comparison_operator"
                        [(cc0)
                         (const_int 0)])
                      (label_ref (match_operand 0 "" ""))
                      (pc)))]
  ""
  "* return ip2k_gen_unsigned_comp_branch (insn, GET_CODE (operands[1]),
                                           operands[0]);")

;; Signed branches use Z, N or synthesized V.
;; result is generated as 0 (LT), 1 (EQ), 2 (GT)
;;
(define_insn "*signed_cmp_branch"
  [(set (pc)
        (if_then_else (match_operator 1 "ip2k_signed_comparison_operator"
                        [(cc0)
                         (const_int 0)])
                      (label_ref (match_operand 0 "" ""))
                      (pc)))]
  ""
  "* return ip2k_gen_signed_comp_branch (insn, GET_CODE (operands[1]),
                                         operands[0]);")

;; Reverse branch - reverse our comparison condition so that we can
;; branch in the opposite sense.
;;
(define_insn_and_split "*rvbranch"
  [(set (pc)
        (if_then_else (match_operator 1 "comparison_operator" [(cc0)
                                                               (const_int 0)])
                      (pc)
                      (label_ref (match_operand 0 "" ""))))]
  ""
  "#"
  "reload_completed"
  [(set (pc)
	(if_then_else (match_dup 2)
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  "{
    operands[2] = gen_rtx (reverse_condition (GET_CODE (operands[1])),
	 		   GET_MODE (operands[1]),
			   cc0_rtx, const0_rtx);
   }")

;; This is a bit test and jump sequence.
;;
(define_insn "*bit_cmpqi_branch"
  [(set (pc)
        (if_then_else (match_operator 0 "comparison_operator"
			[(zero_extract
			   (match_operand:QI 1 "nonimmediate_operand" "roR")
			   (const_int 1)
			   (match_operand 2 "immediate_operand" "i"))
			 (const_int 0)])
	              (label_ref (match_operand 3 "" ""))
	              (pc)))]
  "(GET_CODE (operands[0]) == EQ || GET_CODE (operands[0]) == NE)"
  "*{
    if (GET_CODE (operands[0]) == EQ)
      OUT_AS2 (sb, %1, %b2);
    else
      OUT_AS2 (snb, %1, %b2);
    return AS1 (page, %3) CR_TAB
           AS1 (jmp, %3);
  }"
  [(set_attr "clobberw" "no")])

;; This is a bit test and jump sequence but for 16-bit operands.  It's pretty
;; certain that there must be a way to do this using a zero_extract operation,
;; but this didn't seem to want to work so we use a bitwise and instead.  This
;; is exactly as efficient but the combiner handles this OK - the implementation
;; here isn't quite as nice though.
;;
(define_insn "*bit_cmphi_branch"
  [(set
    (pc)
    (if_then_else
     (match_operator 0 "comparison_operator"
		     [(and:HI (match_operand:HI 1 "nonimmediate_operand" "roS")
			      (match_operand    2 "const_int_operand"    "n"))
		      (const_int 0)])
     (label_ref (match_operand 3 "" ""))
     (pc)))]
  "((GET_CODE (operands[0]) == EQ || GET_CODE (operands[0]) == NE)
    && find_one_set_bit_p (INTVAL (operands[2])) != -1)"
  "*{
    int bp = find_one_set_bit_p (INTVAL (operands[2]));
    if (INTVAL (operands[2]) >= 8)
      operands[4] = GEN_INT (bp - 8);
    else
      operands[4] = GEN_INT (bp);

    if (GET_CODE (operands[0]) == EQ)
      {
        if (INTVAL (operands[2]) >= 8)
          OUT_AS2 (sb, %H1, %b4);
        else
          OUT_AS2 (sb, %L1, %b4);
      }
    else
      {
        if (INTVAL (operands[2]) >= 8)
          OUT_AS2 (snb, %H1, %b4);
	else
          OUT_AS2 (snb, %L1, %b4);
      }
    return AS1 (page, %3) CR_TAB
           AS1 (jmp, %3);
  }"
  [(set_attr "clobberw" "no")])

;; Add two operands, compare with a third and branch if equal or not-equal.
;;
(define_insn "*add_and_comp_branch"
  [(set
    (pc)
    (if_then_else
     (match_operator 0 "comparison_operator"
		     [(plus:HI
		       (match_operand:HI 1 "nonimmediate_operand" "ro, rS, rS")
		       (match_operand:HI 2 "general_operand"    "rSn,ron,rSn"))
		      (match_operand:HI 3 "general_operand"    "rSn,rSn,ron")])
     (label_ref (match_operand 4 "" ""))
     (pc)))]
  "(GET_CODE (operands[0]) == EQ || GET_CODE (operands[0]) == NE)"
  "*{
    OUT_AS2 (mov, w, %L2);
    OUT_AS2 (add, w, %L1);
    OUT_AS2 (cse, w, %L3);
    if (GET_CODE (operands[0]) == EQ)
      {
        OUT_AS1 (page, 1f);
        OUT_AS1 (jmp, 1f);
      }
    else
      {
        OUT_AS1 (page, %4);
        OUT_AS1 (jmp, %4);
      }
    OUT_AS2 (mov, w, %H2);
    OUT_AS2 (addc, w, %H1);
    if (GET_CODE (operands[0]) == EQ)
      OUT_AS2 (csne, w, %H3);
    else
      OUT_AS2 (cse, w, %H3);
    OUT_AS1 (page, %4);
    OUT_AS1 (jmp, %4);
    return AS1 (1:, );
  }")

;; Unconditional jump
;;
(define_insn "jump"
  [(set (pc)
        (label_ref (match_operand 0 "" "")))]
  ""
  "page\\t%0\;jmp\\t%0"
  [(set_attr "clobberw" "no")])

;; Indirect jump
;;
(define_insn "indirect_jump"
  [(set (pc) (match_operand:HI 0 "nonimmediate_operand" "ro"))]
  ""
  "page\\t1f\;call\\t1f\;1:mov\\tw,%H0\;mov\\tcallh,w\;mov\\tw,%L0\;mov\\tcalll,w\;ret")

;;
;; Function call instructions.
;;

(define_expand "call"
  [(call (match_operand 0 "" "")
	 (match_operand:HI 1 "" ""))]
  ""
  "")

(define_insn "*call"
  [(call (mem:HI (match_operand:HI 0 "general_operand" "i,roS"))
	 (match_operand:HI 1 "" ""))]
  ""
  "@
   page\\t%b0\;call\\t%b0
   push\\t%L0%<\;push\\t%H0%>\;page\\t__indcall\;call\\t__indcall")

(define_expand "call_pop"
  [(parallel [(call (match_operand 0 "" "")
		    (match_operand:HI 1 "" ""))
	      (set (reg:HI 6)
		   (plus:HI (reg:HI 6)
			    (match_operand:HI 3 "immediate_operand" "")))])]
  ""
  "")

(define_insn "*call_pop"
  [(call (mem:HI (match_operand:HI 0 "general_operand" "i,roS"))
	 (match_operand:HI 1 "" ""))
   (set (reg:HI 6)
        (plus:HI (reg:HI 6)
		 (match_operand:HI 2 "immediate_operand" "")))]
  ""
  "@
   page\\t%b0\;call\\t%b0
   push\\t%L0%<\;push\\t%H0%>\;page\\t__indcall\;call\\t__indcall")

;; Undo any splitting of operands that lead to redundant movhi3 instructions.
;;
(define_peephole2
  [(set (match_operand 0 "ip2k_nonsp_reg_operand" "")
        (match_operand 1 "nonimmediate_operand" ""))
   (parallel [(call (mem:HI (match_dup 0))
   		    (match_operand:HI 2 "" ""))
	      (set (reg:HI 6)
	       	   (plus:HI (reg:HI 6)
		    	    (match_operand:HI 3 "immediate_operand" "")))])]
  ""
  [(parallel [(call (mem:HI (match_dup 1))
   		    (match_dup 2))
	      (set (reg:HI 6)
	           (plus:HI (reg:HI 6)
		   	    (match_dup 3)))])]
  "")

(define_expand "call_value"
  [(set (match_operand 0 "" "")
        (call (match_operand 1 "" "")
	      (match_operand:HI 2 "" "")))]
  ""
  "")

(define_insn "*call_value"
  [(set (match_operand 0 "" "")
        (call (mem:HI (match_operand:HI 1 "general_operand" "i,roS"))
	      (match_operand:HI 2 "" "")))]
  ""
  "@
   page\\t%b1\;call\\t%b1
   push\\t%L1%<\;push\\t%H1%>\;page\\t__indcall\;call\\t__indcall")

(define_expand "call_value_pop"
  [(parallel [(set (match_operand 0 "" "")
		   (call (match_operand 1 "" "")
			 (match_operand:HI 2 "" "")))
	      (set (reg:HI 6)
		   (plus:HI (reg:HI 6)
			    (match_operand:HI 4 "immediate_operand" "")))])]
  ""
  "")

(define_insn "*call_value_pop"
  [(set (match_operand 0 "" "")
	(call (mem:HI (match_operand:HI 1 "general_operand" "i,roS"))
	      (match_operand:HI 2 "" "")))
   (set (reg:HI 6)
	(plus:HI (reg:HI 6)
		 (match_operand:HI 3 "immediate_operand" "")))]
  ""
  "@
   page\\t%b1\;call\\t%b1
   push\\t%L1%<\;push\\t%H1%>\;page\\t__indcall\;call\\t__indcall")

;; Undo any splitting of operands that lead to redundant movhi3 instructions.
;;
(define_peephole2
  [(set (match_operand 0 "ip2k_nonsp_reg_operand" "")
        (match_operand 1 "nonimmediate_operand" ""))
   (parallel [(set (match_operand 2 "" "")
   		   (call (mem:HI (match_dup 0))
   		         (match_operand:HI 3 "" "")))
	      (set (reg:HI 6)
	       	   (plus:HI (reg:HI 6)
		    	    (match_operand:HI 4 "immediate_operand" "")))])]
  ""
  [(parallel [(set (match_dup 2)
  		   (call (mem:HI (match_dup 1))
   		         (match_dup 3)))
	      (set (reg:HI 6)
	           (plus:HI (reg:HI 6)
		   	    (match_dup 4)))])]
  "")

;; Nop instruction.
;;
;; We don't really want nops to appear in our code so just insert a comment.
;;
(define_insn "nop"
  [(const_int 0)]
  ""
  "; nop")


;; SEQ instruction
;;
(define_insn "seq"
  [(set (match_operand:QI 0 "register_operand" "=r")
	(eq:QI (cc0) (const_int 0)))]
  ""
  "* return ip2k_gen_sCOND (insn, EQ, operands[0]);")

;; Tweak SEQ if we can adjust the output operand.  Note that we have to do
;; this via a peephole because we need to ensure that any reloads have taken
;; place before we try to do this.  If there's a reload in order to get our
;; actual result operand then this peephole won't match.
;;
(define_peephole
  [(set (match_operand:QI 0 "register_operand" "")
  	(eq:QI (cc0) (const_int 0)))
   (set (reg:QI 10)
        (match_dup 0))
   (set (match_operand:QI 1 "nonimmediate_operand" "")
   	(reg:QI 10))]
  "find_regno_note (insn, REG_DEAD, REGNO (operands[0]))"
  "* return ip2k_gen_sCOND (insn, EQ, operands[1]);")

;; Another peephole match handles the same merge as above but for cases where
;; we're emulating memory accesses via IP and an offset.
;;
(define_peephole
  [(set (match_operand:QI 0 "register_operand" "")
  	(eq:QI (cc0) (const_int 0)))
   (set (reg:QI 10)
        (match_dup 0))
   (set (mem:QI (plus:HI (reg:HI 4)
			 (match_operand:QI 1 "const_int_operand" "")))
   	(reg:QI 10))]
  "(find_regno_note (insn, REG_DEAD, REGNO (operands[0]))
    && (INTVAL (operands[1]) < 0x100))"
  "*{
      if (INTVAL (operands[1]) == 1)
        OUT_AS1 (inc, ipl);
      else
        {
	  OUT_AS2 (mov, w, %1);
	  OUT_AS2 (add, ipl, w);
	}
      ip2k_gen_sCOND (insn, EQ,
                      gen_rtx_MEM (QImode, gen_rtx_REG (HImode, REG_IP)));
      if (find_regno_note (insn, REG_DEAD, REG_IP))
        {
          if (INTVAL (operands[1]) == 1)
            OUT_AS1 (dec, ipl);
          else
            {
	      OUT_AS2 (mov, w, %1);
	      OUT_AS2 (sub, ipl, w);
	    }
        }
      return \"\";
  }")

;; SNE instruction
;;
(define_insn "sne"
  [(set (match_operand:QI 0 "register_operand" "=r")
	(ne:QI (cc0) (const_int 0)))]
  ""
  "* return ip2k_gen_sCOND (insn, NE, operands[0]);")

;; Tweak SNE if we can adjust the output operand.  Note that we have to do
;; this via a peephole because we need to ensure that any reloads have taken
;; place before we try to do this.  If there's a reload in order to get our
;; actual result operand then this peephole won't match.
;;
(define_peephole
  [(set (match_operand:QI 0 "register_operand" "")
  	(ne:QI (cc0) (const_int 0)))
   (set (reg:QI 10)
        (match_dup 0))
   (set (match_operand:QI 1 "nonimmediate_operand" "")
   	(reg:QI 10))]
  "find_regno_note (PREV_INSN (insn), REG_DEAD, REGNO (operands[0]))"
  "* return ip2k_gen_sCOND (insn, NE, operands[1]);")

;; Another peephole match handles the same merge as above but for cases where
;; we're emulating memory accesses via IP and an offset.
;;
(define_peephole
  [(set (match_operand:QI 0 "register_operand" "")
  	(ne:QI (cc0) (const_int 0)))
   (set (reg:QI 10)
        (match_dup 0))
   (set (mem:QI (plus:HI (reg:HI 4)
			 (match_operand:QI 1 "const_int_operand" "")))
   	(reg:QI 10))]
  "(find_regno_note (PREV_INSN (insn), REG_DEAD, REGNO (operands[0]))
    && (INTVAL (operands[1]) < 0x100))"
  "*{
      if (INTVAL (operands[1]) == 1)
        OUT_AS1 (inc, ipl);
      else
        {
	  OUT_AS2 (mov, w, %1);
	  OUT_AS2 (add, ipl, w);
	}
      ip2k_gen_sCOND (insn, NE,
                      gen_rtx_MEM (QImode, gen_rtx_REG (HImode, REG_IP)));
      if (find_regno_note (insn, REG_DEAD, REG_IP))
        {
          if (INTVAL (operands[1]) == 1)
            OUT_AS1 (dec, ipl);
          else
            {
	      OUT_AS2 (mov, w, %1);
	      OUT_AS2 (sub, ipl, w);
	    }
        }
      return \"\";
  }")



;; Case Dispatch Table Support.
;;
;; Called with 5 arguments:
;;
;; 0. case index
;; 1. lower bound (const_int)
;; 2. range (const_int)
;; 3. label before dispatch table
;; 4. out-of-bounds label
;;
;; With the IP2k we actually really want to do a caseqi but that
;; doesn't exist so we cheat and make it look (to the core of gcc)
;; like we're going to do the SImode stuff but then truncate it
;; away when it's no longer looking :-)
;;
(define_expand "casesi"
  [(set (match_dup 5)
        (truncate:QI (match_operand:SI 0 "general_operand" "g")))
   (set (match_dup 5)
        (minus:QI (match_dup 5)
                  (match_operand 1 "const_int_operand" "n")))
   (set (cc0)
        (compare (match_dup 5)
                 (match_operand 2 "const_int_operand" "n")))
   (set (pc)
        (if_then_else (gtu (cc0)
	                   (const_int 0))
                      (label_ref (match_operand 4 "" ""))
                      (pc)))
   (parallel [(set (pc)
		   (plus:HI (pc)
			    (zero_extend:HI (match_dup 5))))
	      (use (label_ref (match_operand 3 "" "")))
	      (use (match_dup 2))])]
  ""
  "{
    operands[5] = gen_reg_rtx (QImode);
  }")

;; There are TWO instructions per dispatch entry (page & jump), so we
;; multiply by two even though our RTL only indicates a simple addition.
;; Subsequent linker relaxation may well restore this back to what the
;; RTL says though!
;;
;; Note that we handle tables with 128 or more entries differently!
;;
(define_insn "*casedispatch"
  [(set (pc)
	(plus:HI (pc) (zero_extend:HI
		       (match_operand:QI 2 "nonimmediate_operand" "roR,roR"))))
   (use (label_ref (match_operand 0 "" "")))
   (use (match_operand 1 "const_int_operand"                        "K,  n"))]
  ""
  "@
   mov\\tw,%2\;add\\tw,wreg\;add\\tpcl,w
   mov\\tw,%2\;push\\t%0%<\;push\\t#0%<\;add\\tw,wreg\;snc\;inc\\t1(SP)\;add\\t2(SP),w\;snc\;inc\\t1(SP)\;page\\t__indcall\;jmp\\t__indcall%>%>")

;; Handle cleaning up the switch statement stuff.  We can eliminate some
;; register moves in some cases.  Note that our pattern is slightly different
;; to the casesi pattern because our minus has become a plus!
;;
;; Note that as of 07-FEB-2002 we must have this pattern as it is because
;; linker relaxation will not work any other way.
;;
(define_peephole
  [(set (reg:QI 10)
        (plus:QI (match_operand 5 "nonimmediate_operand" "rS,rS,rS,rS")
                  (match_operand 1 "const_int_operand"    "M, n, M, n")))
   (set (match_operand:QI 0 "register_operand"           "+r, r, r, r")
        (reg:QI 10))
   (set (cc0)
        (compare (match_dup 0)
                 (match_operand 2 "const_int_operand"     "K, K, n, n")))
   (set (pc)
        (if_then_else (gtu (cc0)
	                   (const_int 0))
                      (label_ref (match_operand 4 "" ""))
                      (pc)))
   (parallel [(set (pc)
		   (plus:HI (pc)
			    (zero_extend:HI (match_dup 0))))
	      (use (label_ref (match_operand 3 "" "")))
	      (use (match_dup 2))])]
  "(INTVAL (operands[1]) != 0
    && find_regno_note (insn, REG_DEAD, REGNO (operands[0])))"
  "*{
    switch (which_alternative)
      {
      case 0:
      case 2:
        OUT_AS2 (dec, w, %5);
	break;

      case 1:
      case 3:
        OUT_AS2 (mov, w, %1);
	OUT_AS2 (add, w, %5);
	break;
      default:
        abort ();
      }

    OUT_AS2 (cmp, w, %2);
    OUT_AS1 (sc, );
    OUT_AS1 (page, %4);
    OUT_AS1 (jmp, %4);

    switch (which_alternative)
      {
      case 0:
      case 1:
	OUT_AS2 (add, w, WREG);
	OUT_AS2 (add, pcl, w);
	return \"\";

      case 2:
      case 3:
	OUT_AS1 (push, %0%<);
	OUT_AS1 (push, #0%<);
	OUT_AS2 (add, w, WREG);
	OUT_AS1 (snc, );
	OUT_AS1 (inc, 1(SP));
	OUT_AS2 (add, 2(SP), w);
	OUT_AS1 (snc, );
	OUT_AS1 (inc, 1(SP));
	OUT_AS1 (page, __indcall);
	OUT_AS1 (jmp, __indcall%>%>);
	return \"\";
      default:
        abort ();
      }
  }")

(define_peephole
  [(set (cc0)
        (compare (match_operand:QI 0 "nonimmediate_operand" "rS,rS")
                 (match_operand 1 "const_int_operand"        "K, n")))
   (set (pc)
        (if_then_else (gtu (cc0)
	                   (const_int 0))
                      (label_ref (match_operand 2 "" ""))
                      (pc)))
   (parallel [(set (pc)
		   (plus:HI (pc)
			    (zero_extend:HI (match_dup 0))))
	      (use (label_ref (match_operand 3 "" "")))
	      (use (match_dup 1))])]
  ""
  "@
   mov\\tw,%0\;cmp\\tw,%1\;sc\;page\\t%2\;jmp\\t%2\;add\\tw,wreg\;add\\tpcl,w
   mov\\tw,%0\;cmp\\tw,%1\;sc\;page\\t%2\;jmp\\t%2\;push\\t%0%<\;push\\t#0%<\;add\\tw,wreg\;snc\;inc\\t1(SP)\;add\\t2(SP),w\;snc\;inc\\t1(SP)\;page\\t__indcall\;jmp\\t__indcall%>%>")

(define_peephole
  [(set (match_operand:HI 0 "nonimmediate_operand" "+roR")
        (plus:HI (match_dup 0)
                 (const_int -1)))
   (set (cc0)
        (compare (match_dup 0)
		 (match_operand 3 "const_int_operand" "n")))
   (set (pc)
	(if_then_else (match_operator 2 "comparison_operator"
	                [(cc0) (const_int 0)])
		      (label_ref (match_operand 1 "" ""))
		      (pc)))]
  "((GET_CODE (operands[2]) == EQ || GET_CODE (operands[2]) == NE)
    && ((INTVAL (operands[3]) == -1) || (INTVAL (operands[3]) == 65535)))"
  "*{
    OUT_AS2 (mov, w, #255);
    OUT_AS2 (add, %L0, w);
    if ((GET_CODE (operands[0]) == REG)
        && ((REGNO (operands[0]) == REG_DP)
	    || (REGNO (operands[0]) == REG_IP)
	    || (REGNO (operands[0]) == REG_SP)))
      {
        OUT_AS2 (add, %H0, w);
      }
    else
      {
        OUT_AS2 (addc, %H0, w);
      }
    if (GET_CODE (operands[2]) == EQ)
      OUT_AS1 (sc, );
    else
      OUT_AS1 (snc, );
    return AS1 (page, %1) CR_TAB
           AS1 (jmp, %1);
  }")

(define_peephole
  [(set (match_operand:QI 0 "nonimmediate_operand" "+rS")
        (plus:QI (match_dup 0)
                 (const_int -1)))
   (set (cc0)
        (match_dup 0))
   (set (pc)
	(if_then_else (match_operator 2 "comparison_operator"
	                [(cc0) (const_int 0)])
		      (label_ref (match_operand 1 "" ""))
		      (pc)))]
  "(GET_CODE (operands[2]) == EQ || GET_CODE (operands[2]) == NE)"
  "*{
    if (GET_CODE (operands[2]) == EQ)
      OUT_AS1 (decsnz, %0);
    else
      OUT_AS1 (decsz, %0);
    return AS1 (page, %1) CR_TAB
           AS1 (jmp, %1);
  }")

;; Handle move and compare-with-zero operations - we can reuse w across
;; the two operations.
;;
(define_peephole
  [(set (reg:QI 10)
        (match_operand:QI 1 "nonimmediate_operand"  "rS"))
   (set (match_operand:QI 0 "nonimmediate_operand" "=rS")
        (reg:QI 10))
   (set (cc0)
        (match_operand:QI 2 "nonimmediate_operand"  "rS"))
   (set (pc)
	(if_then_else (match_operator 3 "comparison_operator"
	                [(cc0) (const_int 0)])
		      (label_ref (match_operand 4 "" ""))
		      (pc)))]
  "((GET_CODE (operands[3]) == EQ || GET_CODE (operands[3]) == NE)
    && (rtx_equal_p (operands[0], operands[2])
        || rtx_equal_p (operands[1], operands[2])))"
  "*{
    OUT_AS2 (mov, w, %1);
    OUT_AS2 (mov, %0, w);
    if (GET_CODE (operands[3]) == EQ)
      OUT_AS1 (snz, );
    else
      OUT_AS1 (sz, );
    return AS1 (page, %4) CR_TAB
           AS1 (jmp, %4);
  }")

;; Handle move and compare-with-zero operations - we can reuse w across
;; the two operations.
;;
(define_peephole
  [(set (reg:QI 10)
  	(match_operand:QI 1 "nonimmediate_operand"  "uS"))
   (set (match_operand:QI 0 "nonimmediate_operand" "+uS")
        (reg:QI 10))
   (set (cc0)
        (match_operand:SI 2 "nonimmediate_operand"  "uS"))
   (set (pc)
	(if_then_else (match_operator 3 "comparison_operator"
	                [(cc0) (const_int 0)])
		      (label_ref (match_operand 4 "" ""))
		      (pc)))]
  "((GET_CODE (operands[3]) == EQ || GET_CODE (operands[3]) == NE)
    && (rtx_equal_p (operands[0],
                     ip2k_get_high_half (ip2k_get_high_half (operands[2],
                                                             HImode), QImode))
        || rtx_equal_p (operands[1],
                        ip2k_get_high_half (ip2k_get_high_half (operands[2],
                                                                HImode),
                                            QImode))))"
  "*{
    OUT_AS2 (mov, w, %1);
    OUT_AS2 (mov, %0, w);
    OUT_AS2 (or, w, %B2);
    OUT_AS2 (or, w, %C2);
    OUT_AS2 (or, w, %D2);
    if (GET_CODE (operands[3]) == EQ)
      OUT_AS1 (snz, );
    else
      OUT_AS1 (sz, );
    return AS1 (page, %4) CR_TAB
           AS1 (jmp, %4);
  }")

;; Handle move and compare-with-zero operations - we can reuse w across
;; the two operations.
;;
(define_peephole
  [(set (reg:QI 10)
  	(match_operand:QI 1 "nonimmediate_operand"  "uS"))
   (set (match_operand:QI 0 "nonimmediate_operand" "+uS")
        (reg:QI 10))
   (set (cc0)
        (match_operand:HI 2 "nonimmediate_operand"  "uS"))
   (set (pc)
	(if_then_else (match_operator 3 "comparison_operator"
	                [(cc0) (const_int 0)])
		      (label_ref (match_operand 4 "" ""))
		      (pc)))]
  "((GET_CODE (operands[3]) == EQ || GET_CODE (operands[3]) == NE)
    && (rtx_equal_p (operands[0], ip2k_get_high_half (operands[2], QImode))
        || rtx_equal_p (operands[1], ip2k_get_high_half (operands[2], QImode))
        || rtx_equal_p (operands[0], ip2k_get_low_half (operands[2], QImode))
        || rtx_equal_p (operands[1], ip2k_get_low_half (operands[2],QImode))))"
  "*{
    OUT_AS2 (mov, w, %1);
    OUT_AS2 (mov, %0, w);
    if (rtx_equal_p (operands[0], ip2k_get_high_half (operands[2], QImode))
        || rtx_equal_p (operands[1], ip2k_get_high_half (operands[2], QImode)))
      OUT_AS2 (or, w, %L2);
    else
      OUT_AS2 (or, w, %H2);
    if (GET_CODE (operands[3]) == EQ)
      OUT_AS1 (snz, );
    else
      OUT_AS1 (sz, );
    return AS1 (page, %4) CR_TAB
           AS1 (jmp, %4);
  }")

;; Handle move and compare-with-zero operations - we can reuse w across
;; the two operations.
;;
(define_peephole
  [(set (match_operand:HI 0 "nonimmediate_operand" "+uo")
        (match_operand:HI 1 "nonimmediate_operand"  "uo"))
   (set (cc0)
        (match_dup 0))
   (set (pc)
	(if_then_else (match_operator 2 "comparison_operator"
	                [(cc0) (const_int 0)])
		      (label_ref (match_operand 3 "" ""))
		      (pc)))]
  "(GET_CODE (operands[2]) == EQ || GET_CODE (operands[2]) == NE)"
  "*{
    OUT_AS2 (mov, w, %H1);
    OUT_AS1 (push, %L1%<);
    OUT_AS1 (pop, %L0%>);
    OUT_AS2 (mov, %H0, w);
    OUT_AS2 (or, w, %L0);
    if (GET_CODE (operands[2]) == EQ)
      OUT_AS1 (snz, );
    else
      OUT_AS1 (sz, );
    return AS1 (page, %3) CR_TAB
           AS1 (jmp, %3);
  }")

;; Handle move and compare-with-zero operations - we can reuse w across
;; the two operations.
;;
(define_peephole
  [(set (match_operand:HI 0 "nonimmediate_operand" "+uo")
        (match_operand:HI 1 "nonimmediate_operand"  "uo"))
   (set (cc0)
        (match_dup 1))
   (set (pc)
	(if_then_else (match_operator 2 "comparison_operator"
	                [(cc0) (const_int 0)])
		      (label_ref (match_operand 3 "" ""))
		      (pc)))]
  "(GET_CODE (operands[2]) == EQ || GET_CODE (operands[2]) == NE)"
  "*{
    OUT_AS2 (mov, w, %H1);
    OUT_AS1 (push, %L1%<);
    OUT_AS1 (pop, %L0%>);
    OUT_AS2 (mov, %H0, w);
    OUT_AS2 (or, w, %L0);
    if (GET_CODE (operands[2]) == EQ)
      OUT_AS1 (snz, );
    else
      OUT_AS1 (sz, );
    return AS1 (page, %3) CR_TAB
           AS1 (jmp, %3);
  }")

(define_peephole
  [(set (match_operand:HI 0 "nonimmediate_operand" "+f,bqdo")
        (mem:HI (reg:HI 4)))
   (set (cc0)
        (match_dup 0))
   (set (pc)
	(if_then_else (match_operator 1 "comparison_operator"
	                [(cc0) (const_int 0)])
		      (label_ref (match_operand 2 "" ""))
		      (pc)))]
  "(GET_CODE (operands[1]) == EQ || GET_CODE (operands[1]) == NE)"
  "*{
    switch (which_alternative)
      {
      case 0:
        OUT_AS1 (push, (IP));
	OUT_AS1 (inc, ipl);
	OUT_AS2 (mov, w, (IP));
	OUT_AS2 (mov, ipl, w);
	OUT_AS1 (pop, iph);
        OUT_AS2 (or, w, iph);
        if (GET_CODE (operands[1]) == EQ)
          OUT_AS1 (snz, );
        else
          OUT_AS1 (sz, );
        return AS1 (page, %2) CR_TAB
               AS1 (jmp, %2);

      case 1:
        OUT_AS2 (mov, w, (IP));
	OUT_AS2 (mov, %H0, w);
	OUT_AS1 (inc, ipl);
	OUT_AS2 (mov, w, (IP));
	OUT_AS2 (mov, %L0, w);
        if (!find_regno_note (insn, REG_DEAD, REG_IP))
	  OUT_AS1 (dec, ipl);
        OUT_AS2 (or, w, %H0);
        if (GET_CODE (operands[1]) == EQ)
          OUT_AS1 (snz, );
        else
          OUT_AS1 (sz, );
        return AS1 (page, %2) CR_TAB
               AS1 (jmp, %2);
      default:
        abort ();
      }
  }")

(define_peephole
  [(set (match_operand:HI 0 "nonimmediate_operand" "+f,bqdo")
        (mem:HI (reg:HI 4)))
   (set (cc0)
        (mem:HI (reg:HI 4)))
   (set (pc)
	(if_then_else (match_operator 1 "comparison_operator"
	                [(cc0) (const_int 0)])
		      (label_ref (match_operand 2 "" ""))
		      (pc)))]
  "(GET_CODE (operands[1]) == EQ || GET_CODE (operands[1]) == NE)"
  "*{
    switch (which_alternative)
      {
      case 0:
        OUT_AS1 (push, (IP));
	OUT_AS1 (inc, ipl);
	OUT_AS2 (mov, w, (IP));
	OUT_AS2 (mov, ipl, w);
	OUT_AS1 (pop, iph);
        OUT_AS2 (or, w, iph);
        if (GET_CODE (operands[1]) == EQ)
          OUT_AS1 (snz, );
        else
          OUT_AS1 (sz, );
        return AS1 (page, %2) CR_TAB
               AS1 (jmp, %2);

      case 1:
        OUT_AS2 (mov, w, (IP));
	OUT_AS2 (mov, %H0, w);
	OUT_AS1 (inc, ipl);
	OUT_AS2 (mov, w, (IP));
	OUT_AS2 (mov, %L0, w);
        if (!find_regno_note (insn, REG_DEAD, REG_IP))
	  OUT_AS1 (dec, ipl);
        OUT_AS2 (or, w, %H0);
        if (GET_CODE (operands[1]) == EQ)
          OUT_AS1 (snz, );
        else
          OUT_AS1 (sz, );
        return AS1 (page, %2) CR_TAB
               AS1 (jmp, %2);
      default:
        abort ();
      }
  }")

;; Handle move-twice and compare-with-zero operations - we can reuse w across
;; the two operations.
;;
(define_peephole
  [(parallel [(set (match_operand:HI 0 "ip2k_gen_operand" "=uS")
                   (match_operand:HI 1 "ip2k_gen_operand"  "uS"))
              (set (match_operand:HI 2 "ip2k_gen_operand" "=uS")
                   (match_dup 1))])
   (set (cc0)
        (match_dup 0))
   (set (pc)
	(if_then_else (match_operator 3 "comparison_operator"
	                [(cc0) (const_int 0)])
		      (label_ref (match_operand 4 "" ""))
		      (pc)))]
  "(GET_CODE (operands[3]) == EQ || GET_CODE (operands[3]) == NE)"
  "*{
    if ((REG_P (operands[0])
         && !(ip2k_xexp_not_uses_reg_p (operands[1], REGNO (operands[0]), 2)
	      && ip2k_xexp_not_uses_reg_p (operands[2],
                                           REGNO (operands[0]), 2)))
        || (REG_P (operands[2])
            && !(ip2k_xexp_not_uses_reg_p (operands[0], REGNO (operands[2]), 2)
	         && ip2k_xexp_not_uses_reg_p (operands[1],
                                              REGNO (operands[2]), 2))))
      {
        OUT_AS2 (mov, w, %L1);
	OUT_AS1 (push, %H1%<);
	OUT_AS1 (push, %H1%<);
	OUT_AS1 (pop, %H0%>);
	OUT_AS2 (mov, %L0, w);
	OUT_AS1 (pop, %H2%>);
	OUT_AS2 (mov, %L2, w);
        OUT_AS2 (or, w, %H2);
        if (GET_CODE (operands[3]) == EQ)
          OUT_AS1 (snz, );
        else
          OUT_AS1 (sz, );
        return AS1 (page, %4) CR_TAB
               AS1 (jmp, %4);
      }
    else
      {
        OUT_AS2 (mov, w, %L1);
	OUT_AS2 (mov, %L0, w);
	OUT_AS2 (mov, %L2, w);
	OUT_AS2 (mov, w, %H1);
	OUT_AS2 (mov, %H0, w);
	OUT_AS2 (mov, %H2, w);
        OUT_AS2 (or, w, %L2);
        if (GET_CODE (operands[3]) == EQ)
          OUT_AS1 (snz, );
        else
          OUT_AS1 (sz, );
        return AS1 (page, %4) CR_TAB
               AS1 (jmp, %4);
      }
  }")

(define_peephole
  [(parallel [(set (match_operand:HI 0 "ip2k_gen_operand" "=uS")
                   (match_operand:HI 1 "ip2k_gen_operand"  "uS"))
              (set (match_operand:HI 2 "ip2k_gen_operand" "=uS")
                   (match_dup 1))])
   (set (cc0)
        (match_dup 2))
   (set (pc)
	(if_then_else (match_operator 3 "comparison_operator"
	                [(cc0) (const_int 0)])
		      (label_ref (match_operand 4 "" ""))
		      (pc)))]
  "(GET_CODE (operands[3]) == EQ || GET_CODE (operands[3]) == NE)"
  "*{
    if ((REG_P (operands[0])
         && !(ip2k_xexp_not_uses_reg_p (operands[1], REGNO (operands[0]), 2)
	      && ip2k_xexp_not_uses_reg_p (operands[2],
                                           REGNO (operands[0]), 2)))
        || (REG_P (operands[2])
            && !(ip2k_xexp_not_uses_reg_p (operands[0], REGNO (operands[2]), 2)
	         && ip2k_xexp_not_uses_reg_p (operands[1],
                                              REGNO (operands[2]), 2))))
      {
        OUT_AS2 (mov, w, %L1);
	OUT_AS1 (push, %H1%<);
	OUT_AS1 (push, %H1%<);
	OUT_AS1 (pop, %H0%>);
	OUT_AS2 (mov, %L0, w);
	OUT_AS1 (pop, %H2%>);
	OUT_AS2 (mov, %L2, w);
        OUT_AS2 (or, w, %H2);
        if (GET_CODE (operands[3]) == EQ)
          OUT_AS1 (snz, );
        else
          OUT_AS1 (sz, );
        return AS1 (page, %4) CR_TAB
               AS1 (jmp, %4);
      }
    else
      {
        OUT_AS2 (mov, w, %L1);
	OUT_AS2 (mov, %L0, w);
	OUT_AS2 (mov, %L2, w);
	OUT_AS2 (mov, w, %H1);
	OUT_AS2 (mov, %H0, w);
	OUT_AS2 (mov, %H2, w);
        OUT_AS2 (or, w, %L2);
        if (GET_CODE (operands[3]) == EQ)
          OUT_AS1 (snz, );
        else
          OUT_AS1 (sz, );
        return AS1 (page, %4) CR_TAB
               AS1 (jmp, %4);
      }
  }")

;; Handle move and compare-with-zero operations - we can reuse w across
;; the two operations.
;;
(define_peephole
  [(set (match_operand:HI 0 "nonimmediate_operand" "+uo")
        (match_operand:HI 1 "nonimmediate_operand"  "uo"))
   (set (cc0)
        (match_operand:SI 2 "nonimmediate_operand"  "uo"))
   (set (pc)
	(if_then_else (match_operator 3 "comparison_operator"
	                [(cc0) (const_int 0)])
		      (label_ref (match_operand 4 "" ""))
		      (pc)))]
  "((GET_CODE (operands[3]) == EQ || GET_CODE (operands[3]) == NE)
    && (rtx_equal_p (operands[0], ip2k_get_high_half (operands[2], HImode))
        || rtx_equal_p (operands[1],
                        ip2k_get_high_half (operands[2], HImode))))"
  "*{
    OUT_AS2 (mov, w, %H1);
    OUT_AS1 (push, %L1%<);
    OUT_AS1 (pop, %L0%>);
    OUT_AS2 (mov, %H0, w);
    OUT_AS2 (or, w, %B0);
    OUT_AS2 (or, w, %C0);
    OUT_AS2 (or, w, %D0);
    if (GET_CODE (operands[3]) == EQ)
      OUT_AS1 (snz, );
    else
      OUT_AS1 (sz, );
    return AS1 (page, %4) CR_TAB
           AS1 (jmp, %4);
  }")

;; Handle bitwise-and and compare-with-zero operations on bytes.
;;
(define_peephole
  [(set (reg:QI 10)
  	(match_operand:QI 2 "general_operand" "        g"))
   (set (reg:QI 10)
        (and:QI (match_operand:QI 1 "general_operand" "g")
		(reg:QI 10)))
   (set (match_operand:QI 0 "register_operand"       "+r")
   	(reg:QI 10))
   (set (cc0)
        (match_dup 0))
   (set (pc)
	(if_then_else (match_operator 3 "comparison_operator"
	                [(cc0) (const_int 0)])
		      (label_ref (match_operand 4 "" ""))
		      (pc)))]
  "(find_regno_note (PREV_INSN (insn), REG_DEAD, REGNO (operands[0]))
    && (GET_CODE (operands[3]) == EQ || GET_CODE (operands[3]) == NE))"
  "*{
    OUT_AS2 (mov, w, %1);
    OUT_AS2 (and, w, %2);
    if (GET_CODE (operands[3]) == EQ)
      OUT_AS1 (snz, );
    else
      OUT_AS1 (sz, );
    return AS1 (page, %4) CR_TAB
           AS1 (jmp, %4);
  }")

;; Handle bitwise-xor and compare-with-zero operations on bytes.
;;
(define_peephole
  [(set (match_operand:QI 0 "register_operand"       "+r")
        (xor:QI (match_operand:QI 1 "general_operand" "g")
		(match_operand:QI 2 "general_operand" "g")))
   (set (cc0)
        (match_dup 0))
   (set (pc)
	(if_then_else (match_operator 3 "comparison_operator"
	                [(cc0) (const_int 0)])
		      (label_ref (match_operand 4 "" ""))
		      (pc)))]
  "(find_regno_note (PREV_INSN (insn), REG_DEAD, REGNO (operands[0]))
    && (GET_CODE (operands[3]) == EQ || GET_CODE (operands[3]) == NE))"
  "*{
    OUT_AS2 (mov, w, %1);
    OUT_AS2 (xor, w, %2);
    if (GET_CODE (operands[3]) == EQ)
      OUT_AS1 (snz, );
    else
      OUT_AS1 (sz, );
    return AS1 (page, %4) CR_TAB
           AS1 (jmp, %4);
  }")

;; Cope with reload's vagaries.
;;

(define_insn "*pushqi_reload_popqi"
  [(set (match_operand:QI 0 "ip2k_nonsp_reg_operand" "=u, u")
        (match_operand:QI 1 "ip2k_short_operand"      "S, S"))
   (set (reg:HI 12)
        (match_operand:HI 2 "general_operand"         "i,ro"))
   (set (match_operand:QI 3 "ip2k_short_operand"     "=S, S")
        (match_dup 0))]
  ""
  "@
   push\\t%1%<\;loadh\\t%x2\;loadl\\t%x2\;pop\\t%3%>
   push\\t%1%<\;mov\\tw,%L2\;push\\t%H2\;pop\\tdph\;mov\\tdpl,w\;pop\\t%3%>"
)

(define_peephole2
  [(set (match_operand:QI 0 "ip2k_nonsp_reg_operand" "")
        (match_operand:QI 1 "ip2k_short_operand" ""))
   (set (reg:HI 12)
        (match_operand:HI 2 "general_operand" ""))
   (set (match_operand:QI 3 "ip2k_short_operand" "")
        (match_dup 0))]
  "(ip2k_reorg_split_himode
    && peep2_reg_dead_p (3, operands[0])
    && ip2k_address_uses_reg_p (operands[1], REG_DP)
    && ip2k_address_uses_reg_p (operands[3], REG_DP)
    && !(ip2k_address_uses_reg_p (operands[2], REG_SP)
         && (GET_CODE (XEXP (operands[2], 0)) == PLUS)
         && (INTVAL (XEXP (XEXP (operands[2], 0), 1)) >= 126))
    && ip2k_xexp_not_uses_reg_p (operands[2], REGNO (operands[0]),
                                 GET_MODE_SIZE (GET_MODE (operands[0]))))"
  [(parallel [(set (match_dup 0)
                   (match_dup 1))
	      (set (reg:HI 12)
 	           (match_dup 2))
	      (set (match_dup 3)
 	           (match_dup 0))])]
  "")

(define_insn "*pushhi_reload_pophi"
  [(set (match_operand:HI 0 "ip2k_nonsp_reg_operand" "=u, u")
        (match_operand:HI 1 "ip2k_short_operand"      "S, S"))
   (set (reg:HI 12)
        (match_operand:HI 2 "general_operand"         "i,ro"))
   (set (match_operand:HI 3 "ip2k_short_operand"     "=S, S")
        (match_dup 0))]
  ""
  "@
   push\\t%L1%<\;push\\t%H1%<\;loadh\\t%x2\;loadl\\t%x2\;pop\\t%H3%>\;pop\\t%L3%>
   push\\t%L1%<\;push\\t%H1%<\;mov\\tw,%L2\;push\\t%H2\;pop\\tdph\;mov\\tdpl,w\;pop\\t%H3%>\;pop\\t%L3%>"
)

(define_peephole2
  [(set (match_operand:HI 0 "ip2k_nonsp_reg_operand" "")
        (match_operand:HI 1 "ip2k_short_operand" ""))
   (set (reg:HI 12)
        (match_operand:HI 2 "general_operand" ""))
   (set (match_operand:HI 3 "ip2k_short_operand" "")
        (match_dup 0))]
  "(ip2k_reorg_split_simode
    && peep2_reg_dead_p (3, operands[0])
    && ip2k_address_uses_reg_p (operands[1], REG_DP)
    && ip2k_address_uses_reg_p (operands[3], REG_DP)
    && !(ip2k_address_uses_reg_p (operands[2], REG_SP)
         && (GET_CODE (XEXP (operands[2], 0)) == PLUS)
         && (INTVAL (XEXP (XEXP (operands[2], 0), 1)) >= 125))
    && ip2k_xexp_not_uses_reg_p (operands[2], REGNO (operands[0]),
                                 GET_MODE_SIZE (GET_MODE (operands[0]))))"
  [(parallel [(set (match_dup 0)
                   (match_dup 1))
	      (set (reg:HI 12)
 	           (match_dup 2))
	      (set (match_dup 3)
 	           (match_dup 0))])]
  "")

(define_insn "*pushsi_reload_popsi"
  [(set (match_operand:SI 0 "ip2k_nonsp_reg_operand" "=u, u")
        (match_operand:SI 1 "ip2k_short_operand"      "S, S"))
   (set (reg:HI 12)
        (match_operand:HI 2 "general_operand"         "i,ro"))
   (set (match_operand:SI 3 "ip2k_short_operand"     "=S, S")
        (match_dup 0))]
  ""
  "@
   push\\t%D1%<\;push\\t%C1%<\;push\\t%B1%<\;push\\t%A1%<\;loadh\\t%x2\;loadl\\t%x2\;pop\\t%A3%>\;pop\\t%B3%>\;pop\\t%C3%>\;pop\\t%D3%>
   push\\t%D1%<\;push\\t%C1%<\;push\\t%B1%<\;push\\t%A1%<\;mov\\tw,%L2\;push\\t%H2\;pop\\tdph\;mov\\tdpl,w\;pop\\t%A3%>\;pop\\t%B3%>\;pop\\t%C3%>\;pop\\t%D3%>"
)

(define_peephole2
  [(set (match_operand:SI 0 "ip2k_nonsp_reg_operand" "")
        (match_operand:SI 1 "ip2k_short_operand" ""))
   (set (reg:HI 12)
        (match_operand:HI 2 "general_operand" ""))
   (set (match_operand:SI 3 "ip2k_short_operand" "")
        (match_dup 0))]
  "(ip2k_reorg_split_dimode
    && peep2_reg_dead_p (3, operands[0])
    && ip2k_address_uses_reg_p (operands[1], REG_DP)
    && ip2k_address_uses_reg_p (operands[3], REG_DP)
    && ! (ip2k_address_uses_reg_p (operands[2], REG_SP)
          && (GET_CODE (XEXP (operands[2], 0)) == PLUS)
          && (INTVAL (XEXP (XEXP (operands[2], 0), 1)) >= 123)))"
  [(parallel [(set (match_dup 0)
                   (match_dup 1))
	      (set (reg:HI 12)
 	           (match_dup 2))
	      (set (match_dup 3)
 	           (match_dup 0))])]
  "")

(define_insn "*pushdi_reload_popdi"
  [(set (match_operand:DI 0 "ip2k_nonsp_reg_operand" "=u, u")
        (match_operand:DI 1 "ip2k_short_operand"      "S, S"))
   (set (reg:HI 12)
        (match_operand:HI 2 "general_operand"         "i,ro"))
   (set (match_operand:DI 3 "ip2k_short_operand"     "=S, S")
        (match_dup 0))]
  ""
  "@
   push\\t%S1%<\;push\\t%T1%<\;push\\t%U1%<\;push\\t%V1%<\;push\\t%W1%<\;push\\t%X1%<\;push\\t%Y1%<\;push\\t%Z1%<\;loadh\\t%x2\;loadl\\t%x2\;pop\\t%Z3%>\;pop\\t%Y3%>\;pop\\t%X3%>\;pop\\t%W3%>\;pop\\t%V3%>\;pop\\t%U3%>\;pop\\t%T3%>\;pop\\t%S3%>
   push\\t%S1%<\;push\\t%T1%<\;push\\t%U1%<\;push\\t%V1%<\;push\\t%W1%<\;push\\t%X1%<\;push\\t%Y1%<\;push\\t%Z1%<\;mov\\tw,%L2\;push\\t%H2\;pop\\tdph\;mov\\tdpl,w\;pop\\t%Z3%>\;pop\\t%Y3%>\;pop\\t%X3%>\;pop\\t%W3%>\;pop\\t%V3%>\;pop\\t%U3%>\;pop\\t%T3%>\;pop\\t%S3%>"
)

(define_peephole2
  [(set (match_operand:DI 0 "ip2k_nonsp_reg_operand" "")
        (match_operand:DI 1 "ip2k_short_operand" ""))
   (set (reg:HI 12)
        (match_operand:HI 2 "general_operand" ""))
   (set (match_operand:DI 3 "ip2k_short_operand" "")
        (match_dup 0))]
  "((ip2k_reorg_in_progress || ip2k_reorg_completed)
    && peep2_reg_dead_p (3, operands[0])
    && ip2k_address_uses_reg_p (operands[1], REG_DP)
    && ip2k_address_uses_reg_p (operands[3], REG_DP)
    && ! (ip2k_address_uses_reg_p (operands[2], REG_SP)
          && (GET_CODE (XEXP (operands[2], 0)) == PLUS)
          && (INTVAL (XEXP (XEXP (operands[2], 0), 1)) >= 119)))"
  [(parallel [(set (match_dup 0)
                   (match_dup 1))
	      (set (reg:HI 12)
 	           (match_dup 2))
	      (set (match_dup 3)
 	           (match_dup 0))])]
  "")

;; FIXME: Disabled because in lshiftrt:SI op1 must match op0
(define_peephole2
  [(set (match_operand 0 "ip2k_nonsp_reg_operand" "")
        (match_operator 3 "ip2k_binary_operator"
			[(match_operand 1 "general_operand" "")
			 (match_operand 2 "general_operand" "")]))
   (set (match_operand 4 "nonimmediate_operand" "")
        (match_dup 0))]
  "0 && (peep2_reg_dead_p (2, operands[0])
         && ip2k_xexp_not_uses_reg_p (operands[4], REGNO (operands[0]),
                                      GET_MODE_SIZE (GET_MODE (operands[0]))))"
  [(set (match_dup 4)
        (match_op_dup 3 [(match_dup 1)
			 (match_dup 2)]))]
  "")

(define_peephole2
  [(set (match_operand 0 "ip2k_nonsp_reg_operand" "")
        (match_operator 3 "ip2k_binary_operator"
			[(zero_extend:HI
			  (match_operand 1 "general_operand" ""))
			 (match_operand 2 "general_operand" "")]))
   (set (match_operand 4 "nonimmediate_operand" "")
        (match_dup 0))]
  "(peep2_reg_dead_p (2, operands[0])
    && ip2k_xexp_not_uses_reg_p (operands[4], REGNO (operands[0]),
                                 GET_MODE_SIZE (GET_MODE (operands[0]))))"
  [(set (match_dup 4)
        (match_op_dup 3 [(zero_extend:HI (match_dup 1))
			 (match_dup 2)]))]
  "")

(define_peephole2
  [(set (match_operand 0 "ip2k_nonsp_reg_operand" "")
        (match_operator 3 "ip2k_binary_operator"
			[(match_operand 1 "general_operand" "")
			 (zero_extend:HI
			  (match_operand 2 "general_operand" ""))]))
   (set (match_operand 4 "nonimmediate_operand" "")
        (match_dup 0))]
  "(peep2_reg_dead_p (2, operands[0])
    && ip2k_xexp_not_uses_reg_p (operands[4], REGNO (operands[0]),
                                 GET_MODE_SIZE (GET_MODE (operands[0]))))"
  [(set (match_dup 4)
        (match_op_dup 3 [(match_dup 1)
			 (zero_extend:HI (match_dup 2))]))]
  "")

(define_peephole2
  [(set (match_operand 0 "ip2k_nonsp_reg_operand" "")
        (match_operator 3 "ip2k_binary_operator"
			[(zero_extend:SI
			  (match_operand 1 "general_operand" ""))
			 (match_operand 2 "general_operand" "")]))
   (set (match_operand 4 "nonimmediate_operand" "")
        (match_dup 0))]
  "(peep2_reg_dead_p (2, operands[0])
    && ip2k_xexp_not_uses_reg_p (operands[4], REGNO (operands[0]),
                                 GET_MODE_SIZE (GET_MODE (operands[0]))))"
  [(set (match_dup 4)
        (match_op_dup 3 [(zero_extend:SI (match_dup 1))
			 (match_dup 2)]))]
  "")

(define_peephole2
  [(set (match_operand 0 "ip2k_nonsp_reg_operand" "")
        (match_operator 3 "ip2k_binary_operator"
			[(match_operand 1 "general_operand" "")
			 (zero_extend:SI
			  (match_operand 2 "general_operand" ""))]))
   (set (match_operand 4 "nonimmediate_operand" "")
        (match_dup 0))]
  "(peep2_reg_dead_p (2, operands[0])
    && ip2k_xexp_not_uses_reg_p (operands[4], REGNO (operands[0]),
                                 GET_MODE_SIZE (GET_MODE (operands[0]))))"
  [(set (match_dup 4)
        (match_op_dup 3 [(match_dup 1)
			 (zero_extend:SI (match_dup 2))]))]
  "")

(define_peephole2
  [(set (match_operand 0 "ip2k_nonsp_reg_operand" "")
        (match_operand 1 "nonimmediate_operand" ""))
   (set (match_operand 2 "nonimmediate_operand" "")
        (match_operator 3 "ip2k_binary_operator"
			[(match_operand 4 "general_operand" "")
			 (match_dup 0)]))]
  "0 && ((peep2_reg_dead_p (2, operands[0])
    || rtx_equal_p (operands[0], operands[2]))
    && ip2k_xexp_not_uses_reg_p (operands[4], REGNO (operands[0]),
                                 GET_MODE_SIZE (GET_MODE (operands[0]))))"
  [(set (match_dup 2)
        (match_op_dup 3 [(match_dup 4)
			 (match_dup 1)]))]
  "")

(define_peephole2
  [(set (match_operand 0 "ip2k_nonsp_reg_operand" "")
        (match_operand 1 "nonimmediate_operand" ""))
   (set (match_operand 2 "nonimmediate_operand" "")
        (match_operator 3 "ip2k_binary_operator"
			[(zero_extend:HI
			  (match_operand 4 "general_operand" ""))
			 (match_dup 0)]))]
  "((peep2_reg_dead_p (2, operands[0])
     || rtx_equal_p (operands[0], operands[2]))
    && ip2k_xexp_not_uses_reg_p (operands[4], REGNO (operands[0]),
                                 GET_MODE_SIZE (GET_MODE (operands[0]))))"
  [(set (match_dup 2)
        (match_op_dup 3 [(zero_extend:HI (match_dup 4))
			 (match_dup 1)]))]
  "")

(define_peephole2
  [(set (match_operand 0 "ip2k_nonsp_reg_operand" "")
        (match_operand 1 "nonimmediate_operand" ""))
   (set (match_operand 2 "nonimmediate_operand" "")
        (match_operator 3 "ip2k_binary_operator"
			[(zero_extend:SI
			  (match_operand 4 "general_operand" ""))
			 (match_dup 0)]))]
  "((peep2_reg_dead_p (2, operands[0])
     || rtx_equal_p (operands[0], operands[2]))
    && ip2k_xexp_not_uses_reg_p (operands[4], REGNO (operands[0]),
                                 GET_MODE_SIZE (GET_MODE (operands[0]))))"
  [(set (match_dup 2)
        (match_op_dup 3 [(zero_extend:SI (match_dup 4))
			 (match_dup 1)]))]
  "")

(define_peephole2
  [(set (match_operand 0 "ip2k_nonsp_reg_operand" "")
        (match_operand 1 "nonimmediate_operand" ""))
   (set (match_operand 2 "nonimmediate_operand" "")
        (match_operator 3 "ip2k_binary_operator"
			[(match_dup 0)
			 (match_operand 4 "general_operand" "")]))]
  "0 && ((peep2_reg_dead_p (2, operands[0])
    || rtx_equal_p (operands[0], operands[2]))
    && ip2k_xexp_not_uses_reg_p (operands[4], REGNO (operands[0]),
                                 GET_MODE_SIZE (GET_MODE (operands[0]))))"
  [(set (match_dup 2)
        (match_op_dup 3 [(match_dup 1)
			 (match_dup 4)]))]
  "")

(define_peephole2
  [(set (match_operand 0 "ip2k_nonsp_reg_operand" "")
        (match_operand 1 "nonimmediate_operand" ""))
   (set (match_operand 2 "nonimmediate_operand" "")
        (match_operator 3 "ip2k_binary_operator"
			[(match_dup 0)
			 (zero_extend:HI
			  (match_operand 4 "general_operand" ""))]))]
  "((peep2_reg_dead_p (2, operands[0])
     || rtx_equal_p (operands[0], operands[2]))
    && ip2k_xexp_not_uses_reg_p (operands[4], REGNO (operands[0]),
                                 GET_MODE_SIZE (GET_MODE (operands[0]))))"
  [(set (match_dup 2)
        (match_op_dup 3 [(match_dup 1)
			 (zero_extend:HI (match_dup 4))]))]
  "")

(define_peephole2
  [(set (match_operand 0 "ip2k_nonsp_reg_operand" "")
        (match_operand 1 "nonimmediate_operand" ""))
   (set (match_operand 2 "nonimmediate_operand" "")
        (match_operator 3 "ip2k_binary_operator"
			[(match_dup 0)
			 (zero_extend:SI
			  (match_operand 4 "general_operand" ""))]))]
  "((peep2_reg_dead_p (2, operands[0])
     || rtx_equal_p (operands[0], operands[2]))
    && ip2k_xexp_not_uses_reg_p (operands[4], REGNO (operands[0]),
                                 GET_MODE_SIZE (GET_MODE (operands[0]))))"
  [(set (match_dup 2)
        (match_op_dup 3 [(match_dup 1)
			 (zero_extend:SI (match_dup 4))]))]
  "")

(define_peephole2
  [(set (match_operand 0 "ip2k_nonsp_reg_operand" "")
        (match_operand 1 "nonimmediate_operand" ""))
   (set (cc0)
        (match_operator 2 "ip2k_binary_operator"
			[(match_operand 3 "general_operand" "")
			 (match_dup 0)]))]
  "0 && (peep2_reg_dead_p (2, operands[0])
    && ip2k_xexp_not_uses_reg_p (operands[3], REGNO (operands[0]),
                                 GET_MODE_SIZE (GET_MODE (operands[0]))))"
  [(set (cc0)
        (match_op_dup 2 [(match_dup 3)
			 (match_dup 1)]))]
  "")

(define_peephole2
  [(set (match_operand 0 "ip2k_nonsp_reg_operand" "")
        (match_operand 1 "nonimmediate_operand" ""))
   (set (cc0)
        (match_operator 2 "ip2k_binary_operator"
			[(zero_extend:HI
			  (match_operand 3 "general_operand" ""))
			 (match_dup 0)]))]
  "(peep2_reg_dead_p (2, operands[0])
    && ip2k_xexp_not_uses_reg_p (operands[3], REGNO (operands[0]),
                                 GET_MODE_SIZE (GET_MODE (operands[0]))))"
  [(set (cc0)
        (match_op_dup 2 [(zero_extend:HI (match_dup 3))
			 (match_dup 1)]))]
  "")

(define_peephole2
  [(set (match_operand 0 "ip2k_nonsp_reg_operand" "")
        (match_operand 1 "nonimmediate_operand" ""))
   (set (cc0)
        (match_operator 2 "ip2k_binary_operator"
			[(zero_extend:SI
			  (match_operand 3 "general_operand" ""))
			 (match_dup 0)]))]
  "(peep2_reg_dead_p (2, operands[0])
    && ip2k_xexp_not_uses_reg_p (operands[3], REGNO (operands[0]),
                                 GET_MODE_SIZE (GET_MODE (operands[0]))))"
  [(set (cc0)
        (match_op_dup 2 [(zero_extend:SI (match_dup 3))
			 (match_dup 1)]))]
  "")

(define_peephole2
  [(set (match_operand 0 "ip2k_nonsp_reg_operand" "")
        (match_operand 1 "nonimmediate_operand" ""))
   (set (cc0)
        (match_operator 2 "ip2k_binary_operator"
			[(match_dup 0)
			 (match_operand 3 "general_operand" "")]))]
  "(peep2_reg_dead_p (2, operands[0])
    && ip2k_xexp_not_uses_reg_p (operands[3], REGNO (operands[0]),
                                 GET_MODE_SIZE (GET_MODE (operands[0]))))"
  [(set (cc0)
        (match_op_dup 2 [(match_dup 1)
			 (match_dup 3)]))]
  "")

(define_peephole2
  [(set (match_operand 0 "ip2k_nonsp_reg_operand" "")
        (match_operand 1 "nonimmediate_operand" ""))
   (set (cc0)
        (match_operator 2 "ip2k_binary_operator"
			[(match_dup 0)
			 (zero_extend:HI
			  (match_operand 3 "general_operand" ""))]))]
  "(peep2_reg_dead_p (2, operands[0])
    && ip2k_xexp_not_uses_reg_p (operands[3], REGNO (operands[0]),
                                 GET_MODE_SIZE (GET_MODE (operands[0]))))"
  [(set (cc0)
        (match_op_dup 2 [(match_dup 1)
			 (zero_extend:HI (match_dup 3))]))]
  "")

(define_peephole2
  [(set (match_operand 0 "ip2k_nonsp_reg_operand" "")
        (match_operand 1 "nonimmediate_operand" ""))
   (set (cc0)
        (match_operator 2 "ip2k_binary_operator"
			[(match_dup 0)
			 (zero_extend:SI
			  (match_operand 3 "general_operand" ""))]))]
  "(peep2_reg_dead_p (2, operands[0])
    && ip2k_xexp_not_uses_reg_p (operands[3], REGNO (operands[0]),
                                 GET_MODE_SIZE (GET_MODE (operands[0]))))"
  [(set (cc0)
        (match_op_dup 2 [(match_dup 1)
			 (zero_extend:SI (match_dup 3))]))]
  "")

(define_peephole2
  [(set (match_operand 0 "ip2k_nonsp_reg_operand" "")
        (match_operator 3 "ip2k_unary_operator"
			[(match_operand 1 "general_operand" "")]))
   (set (match_operand 2 "nonimmediate_operand" "")
        (match_dup 0))]
  "(peep2_reg_dead_p (2, operands[0])
    && ip2k_xexp_not_uses_reg_p (operands[2], REGNO (operands[0]),
                                 GET_MODE_SIZE (GET_MODE (operands[0]))))"
  [(set (match_dup 2)
        (match_op_dup 3 [(match_dup 1)]))]
  "")

(define_peephole2
  [(set (match_operand 0 "ip2k_nonsp_reg_operand" "")
        (match_operand 1 "nonimmediate_operand" ""))
   (set (match_operand 2 "nonimmediate_operand" "")
        (match_operator 3 "ip2k_unary_operator" [(match_dup 0)]))]
  "(peep2_reg_dead_p (2, operands[0])
    && ip2k_xexp_not_uses_reg_p (operands[2], REGNO (operands[0]),
                                 GET_MODE_SIZE (GET_MODE (operands[0]))))"
  [(set (match_dup 2)
        (match_op_dup 3 [(match_dup 1)]))]
  "")

(define_peephole2
  [(set (match_operand 0 "ip2k_nonsp_reg_operand" "")
        (match_operand 1 "nonimmediate_operand" ""))
   (set (cc0)
        (match_dup 0))]
  "peep2_reg_dead_p (2, operands[0])"
  [(set (cc0)
        (match_dup 1))]
  "")

;; Look for places where we can shorten a compare operation.
;;
(define_peephole2
  [(set (match_operand:QI 0 "nonimmediate_operand" "")
        (const_int 0))
   (set (cc0)
        (match_operand:HI 1 "nonimmediate_operand" ""))
   (set (pc)
	(if_then_else (match_operator 2 "comparison_operator"
	                [(cc0) (const_int 0)])
		      (label_ref (match_operand 3 "" ""))
		      (pc)))]
  "(rtx_equal_p (ip2k_get_high_half (operands[1], QImode), operands[0]))"
  [(set (match_dup 0)
        (const_int 0))
   (set (cc0)
        (match_dup 4))
   (set (pc)
	(if_then_else (match_op_dup 2
	                [(cc0) (const_int 0)])
		      (label_ref (match_dup 3))
		      (pc)))]
  "{
    operands[4] = ip2k_get_low_half (operands[1], QImode);
  }")

;; Look for places where we can shorten a compare operation.
;;
(define_peephole2
  [(set (match_operand:QI 0 "nonimmediate_operand" "")
        (const_int 0))
   (set (cc0)
        (compare (match_operand:HI 1 "nonimmediate_operand" "")
	         (match_operand 2 "const_int_operand" "")))
   (set (pc)
	(if_then_else (match_operator 3 "comparison_operator"
	                [(cc0) (const_int 0)])
		      (label_ref (match_operand 4 "" ""))
		      (pc)))]
  "(rtx_equal_p (ip2k_get_high_half (operands[1], QImode), operands[0])
    && (abs (INTVAL (operands[2]) <= 127)))"
  [(set (match_dup 0)
        (const_int 0))
   (set (cc0)
        (compare (match_dup 5)
	         (match_dup 6)))
   (set (pc)
	(if_then_else (match_op_dup 3
	                [(cc0) (const_int 0)])
		      (label_ref (match_dup 4))
		      (pc)))]
  "{
    operands[5] = ip2k_get_low_half (operands[1], QImode);
    operands[6] = gen_int_mode (INTVAL (operands[2]) & 0xff, QImode);
  }")

;; This is one of those cases where gcc just can't untangle our wishes.  We
;; want to add some values but get two copies of the result.  In this instance
;; however, the seconds copy can be made more cheaply by combining things.
;;
(define_peephole
  [(set (match_operand:HI 0 "ip2k_nonsp_reg_operand"       "+&u")
        (plus:HI (match_operand:HI 1 "nonimmediate_operand" "rS")
	         (match_operand:HI 2 "general_operand"     "rSi")))
   (set (match_operand:HI 3 "ip2k_gen_operand"            "=&uS")
        (match_dup 0))]
  "(ip2k_xexp_not_uses_reg_p (operands[1], REGNO (operands[0]),
                              GET_MODE_SIZE (GET_MODE (operands[0])))
    && ip2k_xexp_not_uses_reg_p (operands[2], REGNO (operands[0]),
                                 GET_MODE_SIZE (GET_MODE (operands[0])))
    && ip2k_xexp_not_uses_reg_p (operands[3], REGNO (operands[0]),
                                 GET_MODE_SIZE (GET_MODE (operands[0])))
    && (!REG_P (operands[3])
        || (ip2k_xexp_not_uses_reg_p (operands[1], REGNO (operands[3]),
                                      GET_MODE_SIZE (GET_MODE (operands[3])))
            && ip2k_xexp_not_uses_reg_p (operands[2], REGNO (operands[3]),
                                         GET_MODE_SIZE (GET_MODE (operands[3]))))))"
  "mov\\tw,%L2\;add\\tw,%L1\;mov\\t%L0,w\;mov\\t%L3,w\;mov\\tw,%H2\;addc\\tw,%H1\;mov\\t%H0,w\;mov\\t%H3,w")

(define_peephole
  [(set (match_operand:HI 0 "ip2k_short_operand"           "+&S")
        (plus:HI (match_operand:HI 1 "nonimmediate_operand" "rS")
	         (match_operand:HI 2 "general_operand"     "rSi")))
   (set (match_operand:HI 3 "ip2k_nonsp_reg_operand"       "=&u")
        (match_dup 0))]
  "(ip2k_xexp_not_uses_reg_p (operands[0], REGNO (operands[3]),
                              GET_MODE_SIZE (GET_MODE (operands[3])))
    && ip2k_xexp_not_uses_reg_p (operands[1], REGNO (operands[3]),
                                 GET_MODE_SIZE (GET_MODE (operands[3])))
    && ip2k_xexp_not_uses_reg_p (operands[2], REGNO (operands[3]),
                                 GET_MODE_SIZE (GET_MODE (operands[3])))
    && ! rtx_equal_p (operands[0], operands[1])
    && ! rtx_equal_p (operands[0], operands[2]))"
  "mov\\tw,%L2\;add\\tw,%L1\;mov\\t%L0,w\;mov\\t%L3,w\;mov\\tw,%H2\;addc\\tw,%H1\;mov\\t%H0,w\;mov\\t%H3,w")

;; Some splits zero the MSByte of a word that we then use for shifting.  We
;; can therefore replace full shifts with zero-extended ones.  These are
;; cheaper for us.
;;
(define_peephole2
  [(set (match_operand:QI 0 "register_operand" "")
  	(const_int 0))
   (set (match_operand:HI 1 "nonimmediate_operand" "")
   	(ashift:HI (match_operand:HI 2 "register_operand" "")
		   (match_operand 3 "const_int_operand" "")))]
  "(rtx_equal_p (ip2k_get_high_half (operands[2], QImode), operands[0])
    && peep2_reg_dead_p (2, operands[0]))"
  [(set (match_dup 1)
  	(ashift:HI (zero_extend:HI (match_dup 4))
		   (match_dup 3)))]
  "{
    operands[4] = ip2k_get_low_half (operands[2], QImode);
  }")

(define_peephole2
  [(set (match_operand:QI 0 "register_operand" "")
  	(const_int 0))
   (set (match_operand:HI 1 "nonimmediate_operand" "")
   	(ashift:HI (match_operand:HI 2 "register_operand" "")
		   (match_operand 3 "const_int_operand" "")))]
  "(rtx_equal_p (ip2k_get_high_half (operands[2], QImode), operands[0]))"
  [(set (match_dup 0)
  	(const_int 0))
   (set (match_dup 1)
  	(ashift:HI (zero_extend:HI (match_dup 4))
		   (match_dup 3)))]
  "{
    operands[4] = ip2k_get_low_half (operands[2], QImode);
  }")

;; Some splits zero the MSByte of a word that we then use for multiplying.  We
;; can therefore replace the full multiplies with zero-extended ones.
;; These are cheaper for us.
;;
(define_peephole2
  [(set (match_operand:QI 0 "register_operand" "")
  	(const_int 0))
   (set (match_operand:HI 1 "nonimmediate_operand" "")
   	(mult:HI (match_operand:HI 2 "register_operand" "")
		 (zero_extend:HI
		  (match_operand:QI 3 "const_int_operand" ""))))]
  "(rtx_equal_p (ip2k_get_high_half (operands[2], QImode), operands[0])
    && (peep2_reg_dead_p (2, operands[0])
        || rtx_equal_p (operands[1], operands[2])))"
  [(set (match_dup 1)
  	(mult:HI (zero_extend:HI (match_dup 4))
		 (zero_extend:HI (match_dup 3))))]
  "{
    operands[4] = ip2k_get_low_half (operands[2], QImode);
  }")

(define_peephole2
  [(set (match_operand:QI 0 "register_operand" "")
  	(const_int 0))
   (set (match_operand:HI 1 "nonimmediate_operand" "")
   	(mult:HI (match_operand:HI 2 "register_operand" "")
		 (zero_extend:HI
		  (match_operand:QI 3 "const_int_operand" ""))))]
  "(rtx_equal_p (ip2k_get_high_half (operands[2], QImode), operands[0]))"
  [(set (match_dup 0)
  	(const_int 0))
   (set (match_dup 1)
  	(mult:HI (zero_extend:HI (match_dup 4))
		 (zero_extend:HI (match_dup 3))))]
  "{
    operands[4] = ip2k_get_low_half (operands[2], QImode);
  }")

;; Merge in a redundant move before a zero-extended multiply.
;;
(define_peephole2
  [(set (match_operand:QI 0 "register_operand" "")
  	(match_operand:QI 1 "general_operand" ""))
   (set (match_operand:HI 2 "nonimmediate_operand" "")
   	(mult:HI (zero_extend:HI (match_dup 0))
		 (zero_extend:HI
		  (match_operand:QI 3 "const_int_operand" ""))))]
  "(peep2_reg_dead_p (2, operands[0])
    || rtx_equal_p (ip2k_get_high_half (operands[2], QImode), operands[0])
    || rtx_equal_p (ip2k_get_low_half (operands[2], QImode), operands[0]))"
  [(set (match_dup 2)
  	(mult:HI (zero_extend:HI (match_dup 1))
		 (zero_extend:HI (match_dup 3))))]
  "")

;; Pick up redundant clears followed by adds - these can just become moves.
;;
(define_peephole2
  [(set (match_operand 0 "register_operand" "")
        (const_int 0))
   (set (match_operand 2 "nonimmediate_operand" "")
        (plus (match_dup 0)
	      (match_operand 1 "general_operand" "")))]
  "peep2_reg_dead_p (2, operands[0])"
  [(set (match_dup 2)
  	(match_dup 1))]
  "")

(define_peephole2
  [(set (match_operand 0 "register_operand" "")
        (const_int 0))
   (set (match_dup 0)
        (plus (match_dup 0)
	      (match_operand 1 "general_operand" "")))]
  ""
  [(set (match_dup 0)
  	(match_dup 1))]
  "")

;; Clear up an add followed by a push of the result.  The fact that this 
;; isn't picked up consistently within the combiner suggests a bug somewhere.
;;
(define_peephole2
  [(set (match_operand:HI 0 "register_operand" "")
  	(plus:HI (match_operand:HI 1 "nonimmediate_operand" "")
	         (match_operand:HI 2 "general_operand" "")))
   (set (mem:HI (post_dec:HI (reg:HI 6)))
        (match_dup 0))]
  "peep2_reg_dead_p (2, operands[0])"
  [(set (mem:HI (post_dec:HI (reg:HI 6)))
	(plus:HI (match_dup 1)
		 (match_dup 2)))]
  "")

;; Tidy up stack slot addressing where we've eliminated some registers.
;;   This looks like something strange going on though as gcc-2.97 didn't
;; exhibit this behavior, whereas gcc-3.0.4 does.
;;
(define_peephole2
  [(set (match_operand:HI 0 "register_operand" "")
  	(plus:HI (match_operand:HI 1 "nonimmediate_operand" "")
	         (match_operand 2 "const_int_operand" "")))
   (set (mem:HI (post_dec:HI (reg:HI 6)))
        (plus:HI (match_dup 0)
		 (match_operand 3 "const_int_operand" "")))]
  "peep2_reg_dead_p (2, operands[0])"
  [(set (mem:HI (post_dec:HI (reg:HI 6)))
	(plus:HI (match_dup 1)
		 (match_dup 4)))]
  "{
    operands[4] = gen_int_mode (INTVAL (operands[2]) + INTVAL (operands[3]),
                                HImode);
  }")

;; Match duplicate loads of a symbol ref.  This isn't something that we want to
;; do at the peephole2 stage because more often than not we'll make one of the
;; two loads redundant after we run peephole2.  We catch the remaining cases
;; here though
;;
(define_peephole
  [(set (match_operand:HI 0 "nonimmediate_operand" "+uS")
        (match_operand 1 "ip2k_symbol_ref_operand"   "i"))
   (set (match_operand:HI 2 "nonimmediate_operand" "=uS")
   	(match_dup 1))]
  "((!REG_P (operands[0]) || (REGNO (operands[0]) != REG_DP))
    && (!REG_P (operands[2]) || (REGNO (operands[2]) != REG_DP)))"
  "mov\\tw,%L1\;mov\\t%L0,w\;mov\\t%L2,w\;mov\\tw,%H1\;mov\\t%H0,w\;mov\\t%H2,w")

(define_peephole
  [(set (match_operand:HI 0 "nonimmediate_operand" "+&uS")
        (match_operand 1 "ip2k_symbol_ref_operand"   "i"))
   (set (match_operand:HI 2 "nonimmediate_operand" "=&uS")
   	(match_dup 0))]
  ""
  "*{
    if ((REG_P (operands[0])
         && !(ip2k_xexp_not_uses_reg_p (operands[1], REGNO (operands[0]), 2)
	      && ip2k_xexp_not_uses_reg_p (operands[2],
                                           REGNO (operands[0]), 2)))
        || (REG_P (operands[2])
            && !(ip2k_xexp_not_uses_reg_p (operands[0], REGNO (operands[2]), 2)
	         && ip2k_xexp_not_uses_reg_p (operands[1],
                                              REGNO (operands[2]), 2))))
      {
        return AS2 (mov, w, %L1) CR_TAB
	       AS1 (push, %H1%<) CR_TAB
	       AS1 (push, %H1%<) CR_TAB
	       AS1 (pop, %H0%>) CR_TAB
	       AS2 (mov, %L0, w) CR_TAB
	       AS1 (pop, %H2%>) CR_TAB
	       AS2 (mov, %L2, w);
      }
    else
      {
        return AS2 (mov, w, %L1) CR_TAB
	       AS2 (mov, %L0, w) CR_TAB
	       AS2 (mov, %L2, w) CR_TAB
	       AS2 (mov, w, %H1) CR_TAB
	       AS2 (mov, %H0, w) CR_TAB
	       AS2 (mov, %H2, w);
      }
  }")

;; Handle the common array indexing pattern.
;; This is of the form A = X + (Y * C).
;; We use splits earlier in this file to get our interesting cases into the
;; same form (i.e. zero-extended multiply and add).
;;
(define_insn "*mulacchi"
  [(set (match_operand:HI 3 "nonimmediate_operand"                   "=rS")
        (plus:HI (mult:HI (zero_extend:HI
			   (match_operand:QI 1 "nonimmediate_operand" "rS"))
			  (zero_extend:HI
			   (match_operand:QI 2 "const_int_operand"     "n")))
		 (match_operand:HI 0 "general_operand"                "rSi")))]
  ""
  "*{
    if (immediate_operand (operands[0], HImode)
        && REG_P (operands[3])
	&& (REGNO (operands[3]) == REG_DP)
	&& (INTVAL (operands[2]) == 2))
      return AS2 (mov, w, %1) CR_TAB
      	     AS1 (loadl, %x0) CR_TAB
	     AS1 (loadh, %x0) CR_TAB
	     AS2 (add, dpl, w) CR_TAB
	     AS2 (add, dpl, w);
    else
      return AS2 (mov, w, %1) CR_TAB
             AS2 (mulu, w, %2) CR_TAB
             AS2 (add, w, %L0) CR_TAB
	     AS2 (mov, %L3, w) CR_TAB
	     AS2 (mov, w, %H0) CR_TAB
	     AS2 (addc, w, MULH) CR_TAB
	     AS2 (mov, %H3, w);
  }")

(define_peephole2
  [(set (match_operand:HI 0 "register_operand" "")
	(mult:HI (zero_extend:HI
		   (match_operand:QI 1 "nonimmediate_operand" ""))
		 (zero_extend:HI
		   (match_operand 2 "const_int_operand" ""))))
   (set (match_operand:HI 3 "nonimmediate_operand" "")
        (plus:HI (match_dup 0)
		 (match_operand:HI 4 "general_operand" "")))]
  "(((! REG_P (operands[3]))
     || (ip2k_xexp_not_uses_reg_p (operands[4], REGNO (operands[3]),
                                   GET_MODE_SIZE (GET_MODE (operands[3])))
         && ip2k_xexp_not_uses_reg_p (operands[0], REGNO (operands[3]),
                                      GET_MODE_SIZE (GET_MODE (operands[3])))))
    && peep2_reg_dead_p (2, operands[0]))"
  [(set (match_dup 3)
	(plus:HI (mult:HI (zero_extend:HI
		   	    (match_dup 1))
		   	  (zero_extend:HI
			    (match_dup 2)))
		 (match_dup 4)))]
  "")

(define_insn "*mulhi_and_accumulate"
  [(set (match_operand:HI 0 "nonimmediate_operand"           "=rS")
	(mult:HI (zero_extend:HI
		   (match_operand:QI 1 "nonimmediate_operand" "rS"))
		 (zero_extend:HI
		   (match_operand:QI 2 "const_int_operand"     "n"))))
   (set (match_operand:HI 3 "nonimmediate_operand"           "=rS")
        (plus:HI (match_dup 0)
		 (match_operand:HI 4 "general_operand"      "%rSi")))]
  "((! REG_P (operands[3]))
    || (ip2k_xexp_not_uses_reg_p (operands[4], REGNO (operands[3]),
                                  GET_MODE_SIZE (GET_MODE (operands[3])))
        && ip2k_xexp_not_uses_reg_p (operands[0], REGNO (operands[3]),
                                     GET_MODE_SIZE (GET_MODE (operands[3])))))"
  "*{
    return AS2 (mov, w, %1) CR_TAB
	   AS2 (mulu, w, %2) CR_TAB
	   AS2 (mov, %L0, w) CR_TAB
	   AS2 (add, w, %L4) CR_TAB
	   AS2 (mov, %L3, w) CR_TAB
	   AS2 (mov, w, %H4) CR_TAB
	   AS2 (addc, w, MULH) CR_TAB
	   AS2 (mov, %H3, w) CR_TAB
	   AS2 (mov, w, MULH) CR_TAB
	   AS2 (mov, %H0, w);
  }")

(define_peephole2
  [(set (match_operand:HI 0 "nonimmediate_operand" "")
	(mult:HI (zero_extend:HI
		   (match_operand:QI 1 "nonimmediate_operand" ""))
		 (zero_extend:HI
		   (match_operand 2 "const_int_operand" ""))))
   (set (match_operand:HI 3 "nonimmediate_operand" "")
        (plus:HI (match_dup 0)
		 (match_operand:HI 4 "general_operand" "")))]
  "((! REG_P (operands[3]))
    || (ip2k_xexp_not_uses_reg_p (operands[4], REGNO (operands[3]),
                                  GET_MODE_SIZE (GET_MODE (operands[3])))
        && ip2k_xexp_not_uses_reg_p (operands[0], REGNO (operands[3]),
                                     GET_MODE_SIZE (GET_MODE (operands[3])))))"
  [(parallel [(set (match_dup 0)
  		   (mult:HI (zero_extend:HI
		   	      (match_dup 1))
		   	    (zero_extend:HI
			      (match_dup 2))))
  	      (set (match_dup 3)
	      	   (plus:HI (match_dup 0)
		   	    (match_dup 4)))])]
  "")

;; Handle the common array indexing pattern.
;; This is of the form A = X + (Y * C).
;; We use splits earlier in this file to get our interesting cases into the 
;; same form (i.e. multiply and add).
;;
(define_peephole
  [(set (match_operand:HI 0 "register_operand"              "=r")
	(mult:HI (match_operand:HI 1 "nonimmediate_operand" "rS")
		 (zero_extend:HI
		   (match_operand:QI 2 "const_int_operand"   "n"))))
   (set (match_operand:HI 3 "nonimmediate_operand"         "=rS")
        (plus:HI (match_dup 0)
		 (match_operand:HI 4 "general_operand"    "%rSi")))]
  "((!REG_P (operands[3])
     || (ip2k_xexp_not_uses_reg_p (operands[4], REGNO (operands[3]),
                                   GET_MODE_SIZE (GET_MODE (operands[3])))))
    && find_regno_note (insn, REG_DEAD, REGNO (operands[0])))"
  "*{
    if (immediate_operand (operands[4], HImode)
        && REG_P (operands[3])
	&& (REGNO (operands[3]) == REG_DP)
	&& (INTVAL (operands[2]) == 2)
	&& ip2k_xexp_not_uses_reg_p (operands[1], REG_DP,
                                     GET_MODE_SIZE (HImode)))
      return AS2 (clrb, STATUS, 0) CR_TAB
	     AS1 (loadl, %x4) CR_TAB
	     AS1 (loadh, %x4) CR_TAB
	     AS2 (rl, w, %L1) CR_TAB
	     AS2 (add, dpl, w) CR_TAB
	     AS2 (rl, w, %H1) CR_TAB
	     AS2 (add, dph, w);
    else if (!REG_P (operands[3])
             || (ip2k_xexp_not_uses_reg_p (operands[1], REGNO (operands[3]),
                                           GET_MODE_SIZE (GET_MODE (operands[3])))
                 && ip2k_xexp_not_uses_reg_p (operands[2], REGNO (operands[3]),
		 			       GET_MODE_SIZE (GET_MODE (operands[3])))))
      return AS2 (mov, w, %L1) CR_TAB
      	     AS2 (mulu, w, %2) CR_TAB
	     AS2 (add, w, %L4) CR_TAB
	     AS2 (mov, %L3, w) CR_TAB
	     AS2 (mov, w, %H4) CR_TAB
	     AS2 (addc, w, MULH) CR_TAB
	     AS2 (mov, %H3, w) CR_TAB
	     AS2 (mov, w, %H1) CR_TAB
	     AS2 (mulu, w, %2) CR_TAB
	     AS2 (add, %H3, w);
    else
      return AS2 (mov, w, %L1) CR_TAB
      	     AS2 (mulu, w, %2) CR_TAB
	     AS2 (add, w, %L4) CR_TAB
	     AS1 (push, wreg%<) CR_TAB
	     AS2 (mov, w, %H4) CR_TAB
	     AS2 (addc, w, MULH) CR_TAB
	     AS1 (push, wreg%<) CR_TAB
	     AS2 (mov, w, %H1) CR_TAB
	     AS2 (mulu, w, %2) CR_TAB
	     AS1 (pop, %H3%>) CR_TAB
	     AS1 (pop, %L3%>) CR_TAB
	     AS2 (add, %H3, w);
  }")

;; Handle the more complex variant of the preceding multiply and accumulate
;; variant of the preceding multiply-and-add operation.  This one would 
;; otherwise fail to match because the result never goes dead.
;;
(define_peephole
  [(set (match_operand:HI 0 "nonimmediate_operand"         "=rS")
	(mult:HI (match_operand:HI 1 "nonimmediate_operand" "rS")
		 (zero_extend:HI
		   (match_operand:QI 2 "const_int_operand"   "n"))))
   (set (match_dup 0)
        (plus:HI (match_dup 0)
		 (match_operand:HI 3 "general_operand"    "%rSi")))]
  "(!REG_P (operands[0])
    || (ip2k_xexp_not_uses_reg_p (operands[1], REGNO (operands[0]),
                                  GET_MODE_SIZE (GET_MODE (operands[0])))
        && ip2k_xexp_not_uses_reg_p (operands[2], REGNO (operands[0]),
                                     GET_MODE_SIZE (GET_MODE (operands[0])))
        && ip2k_xexp_not_uses_reg_p (operands[3], REGNO (operands[0]),
                                     GET_MODE_SIZE (GET_MODE (operands[0])))))"
  "*{
    if (immediate_operand (operands[3], HImode)
        && REG_P (operands[0])
        && (REGNO (operands[0]) == REG_DP)
	&& (INTVAL (operands[2]) == 2))
      return AS2 (clrb, STATUS, 0) CR_TAB
	     AS1 (loadl, %x3) CR_TAB
	     AS1 (loadh, %x3) CR_TAB
	     AS2 (rl, w, %L1) CR_TAB
	     AS2 (add, dpl, w) CR_TAB
	     AS2 (rl, w, %H1) CR_TAB
	     AS2 (add, dph, w);
    else
      return AS2 (mov, w, %L1) CR_TAB
      	     AS2 (mulu, w, %2) CR_TAB
	     AS2 (add, w, %L3) CR_TAB
	     AS2 (mov, %L0, w) CR_TAB
	     AS2 (mov, w, %H3) CR_TAB
	     AS2 (addc, w, MULH) CR_TAB
	     AS2 (mov, %H0, w) CR_TAB
	     AS2 (mov, w, %H1) CR_TAB
	     AS2 (mulu, w, %2) CR_TAB
	     AS2 (add, %H0, w);
  }")

;; Handle the a complex variant of the preceding multiply and add
;; operations where the intermediate result is also required.
;;
(define_peephole
  [(set (match_operand:HI 0 "nonimmediate_operand"         "=rS")
	(mult:HI (match_operand:HI 1 "nonimmediate_operand" "rS")
		 (zero_extend:HI
		   (match_operand:QI 2 "const_int_operand"   "n"))))
   (set (match_operand:HI 3 "nonimmediate_operand"         "=rS")
        (plus:HI (match_dup 0)
		 (match_operand:HI 4 "general_operand"    "%rSi")))]
  "((!REG_P (operands[3])
     || (ip2k_xexp_not_uses_reg_p (operands[4], REGNO (operands[3]),
                                   GET_MODE_SIZE (GET_MODE (operands[3])))
         && ip2k_xexp_not_uses_reg_p (operands[0], REGNO (operands[3]),
                                      GET_MODE_SIZE (GET_MODE (operands[3])))
         && ip2k_xexp_not_uses_reg_p (operands[1], REGNO (operands[3]),
                                      GET_MODE_SIZE (GET_MODE (operands[3])))
         && ip2k_xexp_not_uses_reg_p (operands[2], REGNO (operands[3]),
                                      GET_MODE_SIZE (GET_MODE (operands[3])))))
    && (INTVAL (operands[2]) != 2))"
  "* return AS2 (mov, w, %H4) CR_TAB
  	    AS2 (mov, %H3, w) CR_TAB
	    AS2 (mov, w, %L1) CR_TAB
      	    AS2 (mulu, w, %2) CR_TAB
	    AS2 (mov, %L0, w) CR_TAB
	    AS2 (add, w, %L4) CR_TAB
	    AS2 (mov, %L3, w) CR_TAB
	    AS2 (mov, w, MULH) CR_TAB
	    AS2 (mov, %H0, w) CR_TAB
	    AS2 (addc, %H3, w) CR_TAB
	    AS2 (mov, w, %H1) CR_TAB
	    AS2 (mulu, w, %2) CR_TAB
	    AS2 (add, %H3, w) CR_TAB
	    AS2 (add, %H0, w);")

;; Byte swapping!
;;
(define_peephole
  [(set (reg:QI 10)
  	(match_operand:QI 1 "nonimmediate_operand" "rS"))
   (set (match_operand:QI 0 "register_operand"     "=r")
  	(reg:QI 10))
   (set (reg:QI 10)
   	(match_operand:QI 2 "nonimmediate_operand" "rS"))
   (set (match_dup 1)
   	(reg:QI 10))
   (set (reg:QI 10)
   	(match_dup 0))
   (set (match_dup 2)
   	(reg:QI 10))]
  "find_regno_note (PREV_INSN (insn), REG_DEAD, REGNO (operands[0]))"
  "push\\t%1%<\;push\\t%2%<\;pop\\t%1%>\;pop\\t%2%>")


