;; GCC machine description for Matsushita MN10200
;; Copyright (C) 1997, 1998 Free Software Foundation, Inc.

;; Contributed by Jeff Law (law@cygnus.com).

;; This file is part of GNU CC.

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

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

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

;; The original PO technology requires these to be ordered by speed,
;; so that assigner will pick the fastest.

;; See file "rtl.def" for documentation on define_insn, match_*, et. al.

;; Condition code settings.
;; none - insn does not affect cc
;; none_0hit - insn does not affect cc but it does modify operand 0
;;	This attribute is used to keep track of when operand 0 changes.
;; 	See the description of NOTICE_UPDATE_CC for more info.
;; set_znv - sets z,n,v to usable values; c is unknown.
;; set_zn  - sets z,n to usable values; v,c is unknown.
;; compare - compare instruction
;; clobber - value of cc is unknown
(define_attr "cc" "none,none_0hit,set_znv,set_zn,compare,clobber"
  (const_string "clobber"))

;; ----------------------------------------------------------------------
;; MOVE INSTRUCTIONS
;; ----------------------------------------------------------------------
;;
;; Some general notes on move instructions.
;;
;; The hardware can't encode nop moves involving data registers, so
;; we catch them and emit a nop instead.
;;
;; Loads/stores to/from address registers must be 16bit aligned,
;; thus we avoid them for QImode.
;;
;; Stores from address registers always store 24bits, so avoid
;; stores from address registers in HImode, SImode, and SFmode.
;;
;; As a result of the various problems using address registers in
;; QImode, HImode, SImode, and SFmode, we discourage their use via
;; '*' in their constraints.  They're still allowed, but they're never
;; the preferred class for insns with those modes.

;; movqi

(define_expand "movqi"
  [(set (match_operand:QI 0 "general_operand" "")
	(match_operand:QI 1 "general_operand" ""))]
  ""
  "
{
  /* One of the ops has to be in a register */
  if (!register_operand (operand0, QImode)
      && !register_operand (operand1, QImode))
    operands[1] = copy_to_mode_reg (QImode, operand1);
}")

;; We avoid memory operations involving address registers because we
;; can't be sure they'll be suitably aligned.
;;
;; We also discourage holding QImode values in address registers.
(define_insn ""
  [(set (match_operand:QI 0 "general_operand" "=d,d,*a,d,d,m,d,*a,*a")
	(match_operand:QI 1 "general_operand" "0,I,I,di,m,d,*a,d,i*a"))]
  "register_operand (operands[0], QImode)
   || register_operand (operands[1], QImode)"
  "@
  nop
  sub %0,%0
  sub %0,%0
  mov %S1,%0
  movbu %1,%0
  movb %1,%0
  mov %1,%0
  mov %1,%0
  mov %1,%0"
  [(set_attr "cc" "none,clobber,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")])

;; movhi

(define_expand "movhi"
  [(set (match_operand:HI 0 "general_operand" "")
	(match_operand:HI 1 "general_operand" ""))]
  ""
  "
{
  /* One of the ops has to be in a register */
  if (!register_operand (operand1, HImode)
      && !register_operand (operand0, HImode))
    operands[1] = copy_to_mode_reg (HImode, operand1);
}")

(define_insn ""
  [(set (match_operand:HI 0 "general_operand" "=d,d,*a,d,d,m,d,*a,*a,*a")
	(match_operand:HI 1 "general_operand" "0,I,I,di,m,d,*a,d,i*a,m"))]
  "register_operand (operands[0], HImode)
   || register_operand (operands[1], HImode)"
  "@
  nop
  sub %0,%0
  sub %0,%0
  mov %s1,%0
  mov %1,%0
  mov %1,%0
  mov %1,%0
  mov %1,%0
  mov %1,%0
  mov %A1,%0"
  [(set_attr "cc" "none,clobber,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")])

;; movpsi and helpers

(define_expand "movpsi"
  [(set (match_operand:PSI 0 "general_operand" "")
	(match_operand:PSI 1 "general_operand" ""))]
  ""
  "
{
  /* One of the ops has to be in a register */
  if (!register_operand (operand1, PSImode)
      && !register_operand (operand0, PSImode))
    operands[1] = copy_to_mode_reg (PSImode, operand1);
}")


;; Constant and indexed addresses are not valid addresses for PSImode,
;; therefore they won't be matched by the general movpsi pattern below.
;; ??? We had patterns to handle indexed addresses, but they kept making
;; us run out of regs, so they were eliminated.

(define_insn ""
  [(set (match_operand:PSI 0 "register_operand" "=a")
	(match_operand:PSI 1 "constant_memory_operand" ""))]
  ""
  "mov %A1,%0"
  [(set_attr "cc" "none_0hit")])

(define_insn ""
  [(set (match_operand:PSI 0 "constant_memory_operand" "=X")
	(match_operand:PSI 1 "register_operand" "a"))]
  ""
  "mov %1,%A0"
  [(set_attr "cc" "none_0hit")])

;; We want to prefer address registers here because 24bit moves to/from
;; memory are shorter and faster when done via address registers.
(define_insn ""
  [(set (match_operand:PSI 0 "general_operand" "=d,a?d,?da,a,m,?d,m")
	(match_operand:PSI 1 "general_operand" "0,I,?dai,m,a,m,?d"))]
  "register_operand (operands[0], PSImode)
   || register_operand (operands[1], PSImode)"
  "@
  nop
  sub %0,%0
  mov %1,%0
  mov %A1,%0
  mov %1,%A0
  movx %A1,%0
  movx %1,%A0"
  [(set_attr "cc" "none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")])

(define_expand "movsi"
  [(set (match_operand:SI 0 "general_operand" "")
	(match_operand:SI 1 "general_operand" ""))]
  ""
  "
{
  /* One of the ops has to be in a register */
  if (!register_operand (operand1, SImode)
      && !register_operand (operand0, SImode))
    operands[1] = copy_to_mode_reg (SImode, operand1);
}")

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=d,d,*a,dm,d,d,*a,*a,*a")
	(match_operand:SI 1 "general_operand" "0,I,I,d,dim,*a,d,*a,i"))]
  "register_operand (operands[0], SImode)
   || register_operand (operands[1], SImode)"
  "*
{
  switch (which_alternative)
    {
    case 0:
      return \"nop\";
    case 1:
    case 2:
      return \"sub %H0,%H0\;sub %L0,%L0\";
    case 3:
    case 5:
    case 6:
    case 7:
      return \"mov %H1,%H0\;mov %L1,%L0\";

    /* The next two cases try to optimize cases where one half
       of the constant is all zeros, or when the two halves are
       the same.  */
    case 4:
    case 8:
      if (REG_P (operands[0])
	  && GET_CODE (operands[1]) == CONST_INT
	  && (INTVAL (operands[1]) & 0xffff0000) == 0)
	output_asm_insn (\"sub %H0,%H0\", operands);
      else
	output_asm_insn (\"mov %h1,%H0\", operands);

      if (GET_CODE (operands[1]) == CONST_INT
	  && ((INTVAL (operands[1]) & 0xffff)
	      == ((INTVAL (operands[1]) >> 16) & 0xffff)))
	output_asm_insn (\"mov %H0,%L0\", operands);
      else if (GET_CODE (operands[1]) == CONST_INT
	       && (INTVAL (operands[1]) & 0xffff) == 0)
	output_asm_insn (\"sub %L0,%L0\", operands);
      else
	output_asm_insn (\"mov %o1,%L0\", operands);
      return \"\";
    }
}"
  [(set_attr "cc" "none,clobber,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")])

(define_expand "movsf"
  [(set (match_operand:SF 0 "general_operand" "")
	(match_operand:SF 1 "general_operand" ""))]
  ""
  "
{
  /* One of the ops has to be in a register */
  if (!register_operand (operand1, SFmode)
      && !register_operand (operand0, SFmode))
    operands[1] = copy_to_mode_reg (SFmode, operand1);
}")

(define_insn ""
  [(set (match_operand:SF 0 "general_operand" "=d,d,*a,dm,d,d,*a,*a,*a")
	(match_operand:SF 1 "general_operand" "0,G,G,d,dim,*a,d,*a,i"))]
  "register_operand (operands[0], SFmode)
   || register_operand (operands[1], SFmode)"
  "*
{
  switch (which_alternative)
    {
    case 0:
      return \"nop\";

    case 1:
    case 2:
      return \"sub %H0,%H0\;sub %L0,%L0\";

    default:
      {
        long val;
        REAL_VALUE_TYPE rv;

	if (GET_CODE (operands[1]) == CONST_DOUBLE)
	  {
	    REAL_VALUE_FROM_CONST_DOUBLE (rv, operands[1]);
	    REAL_VALUE_TO_TARGET_SINGLE (rv, val);
	  }

	if (GET_CODE (operands[1]) == CONST_INT)
	  val = INTVAL (operands[1]);

        if ((GET_CODE (operands[1]) == CONST_INT
	     || GET_CODE (operands[1]) == CONST_DOUBLE)
	    && (val & 0xffff0000) == 0)
	  output_asm_insn (\"sub %H0,%H0\", operands);
	else
	  output_asm_insn (\"mov %h1,%H0\", operands);
	
	if (GET_CODE (operands[1]) == CONST_INT
	    && ((INTVAL (operands[1]) & 0xffff)
		 == ((INTVAL (operands[1]) >> 16) & 0xffff)))
	  output_asm_insn (\"mov %H0,%L0\", operands);
        else if ((GET_CODE (operands[1]) == CONST_INT
		  || GET_CODE (operands[1]) == CONST_DOUBLE)
		 && (val & 0x0000ffff) == 0)
	  output_asm_insn (\"sub %L0,%L0\", operands);
	else
	  output_asm_insn (\"mov %o1,%L0\", operands);
	return \"\";
      }
    }
}"
  [(set_attr "cc" "none,clobber,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")])


;; ----------------------------------------------------------------------
;; TEST INSTRUCTIONS
;; ----------------------------------------------------------------------

;; Go ahead and define tsthi and tstpsi so we can eliminate redundant tst insns
;; when we start trying to optimize this port.
(define_insn "tsthi"
  [(set (cc0) (match_operand:HI 0 "nonimmediate_operand" "da"))]
  ""
  "* return output_tst (operands[0], insn);"
  [(set_attr "cc" "set_znv")])

(define_insn "tstpsi"
  [(set (cc0) (match_operand:PSI 0 "nonimmediate_operand" "da"))]
  ""
  "* return output_tst (operands[0], insn);"
  [(set_attr "cc" "set_znv")])

(define_insn ""
  [(set (cc0) (zero_extend:HI (match_operand:QI 0 "memory_operand" "d")))]
  ""
  "* return output_tst (operands[0], insn);"
  [(set_attr "cc" "set_znv")])

(define_insn ""
  [(set (cc0) (zero_extend:PSI (match_operand:QI 0 "memory_operand" "d")))]
  ""
  "* return output_tst (operands[0], insn);"
  [(set_attr "cc" "set_znv")])

(define_insn "cmphi"
  [(set (cc0)
	(compare:HI (match_operand:HI 0 "nonimmediate_operand" "da")
		    (match_operand:HI 1 "general_operand" "dai")))]
  ""
  "cmp %1,%0"
  [(set_attr "cc" "compare")])

(define_insn "cmppsi"
  [(set (cc0)
	(compare:PSI (match_operand:PSI 0 "nonimmediate_operand" "da")
		     (match_operand:PSI 1 "general_operand" "dai")))]
  ""
  "cmp %1,%0"
  [(set_attr "cc" "compare")])

;; ----------------------------------------------------------------------
;; ADD INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_insn "addhi3"
  [(set (match_operand:HI 0 "general_operand" "=d")
	(plus:HI (match_operand:HI 1 "general_operand" "%0")
		 (match_operand:HI 2 "general_operand" "dai")))]
  ""
  "add %2,%0"
  [(set_attr "cc" "set_zn")])

(define_insn "addpsi3"
  [(set (match_operand:PSI 0 "general_operand" "=da")
	(plus:PSI (match_operand:PSI 1 "general_operand" "%0")
		  (match_operand:PSI 2 "general_operand" "dai")))]
  ""
  "add %2,%0"
  [(set_attr "cc" "set_zn")])

;; We want to avoid using explicit registers; reload won't tell us
;; if it has to spill them and may generate incorrect code in such
;; cases.
;;
;; So we call out to a library routine to perform 32bit add or
;; subtract operations.
;;
;; operand2 must be nonmemory_operand so that we will accept CONST_INTs
;; during initial code generation.
(define_expand "addsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(plus:SI (match_operand:SI 1 "register_operand" "")
		 (match_operand:SI 2 "nonmemory_operand" "")))]
  ""
  "
{
  /* If adding a CONST_INT, we are better off generating code ourselves.

     During RTL generation we call out to library routines.

     After RTL generation we can not call the library routines as
     they need to push arguments via virtual_outgoing_args_rtx which
     has already been instantiated.  So, after RTL generation we just
     FAIL and open code the operation.  */
  if (GET_CODE (operands[2]) == CONST_INT)
    {
      if (!rtx_equal_p (operands[0], operands[1]))
	emit_move_insn (operands[0], operands[1]);
      emit_insn (gen_addsi3_const (operands[0], operands[0], operands[2]));
      DONE;
    }
  else if (rtx_equal_function_value_matters)
    {
      rtx ret, insns;

      start_sequence ();
      ret = emit_library_call_value (gen_rtx (SYMBOL_REF, Pmode, \"__addsi3\"),
				     NULL_RTX, 1, SImode, 2, operands[1],
				     SImode, operands[2], SImode);
      insns = get_insns ();
      end_sequence ();
      emit_libcall_block (insns, operands[0], ret,
			  gen_rtx (PLUS, SImode, operands[1], operands[2]));
      DONE;
    }
  else
    FAIL;
}")

(define_insn "addsi3_const"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(plus:SI (match_operand:SI 1 "register_operand" "0")
		 (match_operand:SI 2 "const_int_operand" "i")))
   (clobber (match_scratch:SI 3 "=&d"))]
  ""
  "*
{
  unsigned long value = INTVAL (operands[2]);

  /* If only the high bits are set in the constant, then we only
     need a single add operation.  It might be better to catch this
     at RTL expansion time.  */
  if ((value & 0xffff) == 0)
    return \"add %h2,%H0\";

  value >>= 16;
  value &= 0xffff;

  if (value == 0)
    return \"sub %3,%3\;add %o2,%L0\;addc %3,%H0\";
  else
    return \"mov %h2,%3\;add %o2,%L0\;addc %3,%H0\";
}"
  [(set_attr "cc" "clobber")])

;; ----------------------------------------------------------------------
;; SUBTRACT INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_insn "subhi3"
  [(set (match_operand:HI 0 "general_operand" "=d")
	(minus:HI (match_operand:HI 1 "general_operand" "0")
		  (match_operand:HI 2 "general_operand" "dai")))]
  ""
  "sub %2,%0"
  [(set_attr "cc" "set_zn")])

(define_insn "subpsi3"
  [(set (match_operand:PSI 0 "general_operand" "=da")
	(minus:PSI (match_operand:PSI 1 "general_operand" "0")
		  (match_operand:PSI 2 "general_operand" "dai")))]
  ""
  "sub %2,%0"
  [(set_attr "cc" "set_zn")])

(define_expand "subsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(minus:SI (match_operand:SI 1 "register_operand" "")
		  (match_operand:SI 2 "register_operand" "")))]
  ""
  "
{
  /* During RTL generation we call out to library routines.

     After RTL generation we can not call the library routines as
     they need to push arguments via virtual_outgoing_args_rtx which
     has already been instantiated.  So, after RTL generation we just
     FAIL and open code the operation.  */
  if (rtx_equal_function_value_matters)
    {
      rtx ret, insns;

      start_sequence ();
      ret = emit_library_call_value (gen_rtx (SYMBOL_REF, Pmode, \"__subsi3\"),
				     NULL_RTX, 1, SImode, 2, operands[1],
				     SImode, operands[2], SImode);
      insns = get_insns ();
      end_sequence ();
      emit_libcall_block (insns, operands[0], ret,
			  gen_rtx (MINUS, SImode, operands[1], operands[2]));
      DONE;
    }
  else
    FAIL;
}")

;; There isn't a negate instruction, so we fake it.
;;
;; We used to expand this into patterns, but a single pattern
;; actually generates better overall code.
;; 
;; We could do HImode negations with a "not;add" sequence, but
;; generally it's generated slightly worse code.
;;
;; The second alternative is not strictly necesasry, but helps
;; when the register allocators start running short of registers.
(define_insn "neghi2"
  [(set (match_operand:HI 0 "general_operand" "=&d,d")
        (neg:HI (match_operand:HI 1 "general_operand" "d,0")))]
  ""
  "@
  sub %0,%0\;sub %1,%0
  not %0\;add 1,%0"
  [(set_attr "cc" "set_zn")])

;; The not/and sequence won't work here.  It's not clear if we'll
;; ever need to provide an alternate sequence since this should
;; be used much less frequently than neghi2.
(define_insn "negpsi2"
  [(set (match_operand:PSI 0 "general_operand" "=&d")
        (neg:PSI (match_operand:PSI 1 "general_operand" "d")))]
  ""
  "sub %0,%0\;sub %1,%0"
  [(set_attr "cc" "set_zn")])

;; Using a magic libcall that accepts its arguments in any
;; data register pair has proven to be the most efficient
;; and most compact way to represent negsi2.
(define_insn "negsi2"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(neg:SI (match_operand:SI 1 "register_operand" "0")))]
  ""
  "jsr ___negsi2_%0"
  [(set_attr "cc" "clobber")])
 
;; ----------------------------------------------------------------------
;; MULTIPLY INSTRUCTIONS
;; ----------------------------------------------------------------------
;;
;; The mn10200 has HIxHI->SI widening multiply, but we get _severe_
;; code density regressions if we enable such a pattern.

(define_insn "mulhi3"
  [(set (match_operand:HI 0 "general_operand" "=d")
	(mult:HI (match_operand:HI 1 "general_operand" "%0")
		 (match_operand:HI 2 "general_operand" "d")))]
  ""
  "mul %2,%0"
  [(set_attr "cc" "set_zn")])

(define_insn "udivmodhi4"
  [(set (match_operand:HI 0 "general_operand" "=d")
	(udiv:HI (match_operand:HI 1 "general_operand" "0")
		 (match_operand:HI 2 "general_operand" "d")))
   (set (match_operand:HI 3 "general_operand" "=&d")
	(umod:HI (match_dup 1) (match_dup 2)))]
  ""
  "*
{
  if (zero_dreg)
    output_asm_insn (\"mov %0,mdr\", &zero_dreg);
  else
    output_asm_insn (\"sub %3,%3\;mov %3,mdr\", operands);
    
  if (find_reg_note (insn, REG_UNUSED, operands[3]))
    return \"divu %2,%0\";
  else
    return \"divu %2,%0\;mov mdr,%3\";
}"
  [(set_attr "cc" "set_zn")])


;; ----------------------------------------------------------------------
;; AND INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_insn "andhi3"
  [(set (match_operand:HI 0 "general_operand" "=d,d")
	(and:HI (match_operand:HI 1 "general_operand" "%0,0")
		(match_operand:HI 2 "general_operand" "M,di")))]
  ""
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xff)
    return \"extxbu %0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x7fff)
    return \"add %0,%0\;lsr %0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffe)
    return \"lsr %0\;add %0,%0\";
  return \"and %2,%0\";
}"
  [(set_attr "cc" "none_0hit,set_znv")])

;; This expander + pattern exist only to allow trampolines to be aligned
;; in the stack.
(define_expand "andpsi3"
  [(set (match_operand:PSI 0 "general_operand" "")
	(and:PSI (match_operand:PSI 1 "general_operand" "")
		(match_operand:PSI 2 "const_int_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[2]) != CONST_INT
      || (INTVAL (operands[2]) & 0xff0000) != 0xff0000)
    FAIL;
}")

(define_insn ""
  [(set (match_operand:PSI 0 "general_operand" "=d")
	(and:PSI (match_operand:PSI 1 "general_operand" "%0")
		(match_operand:PSI 2 "const_int_operand" "i")))]
  "GET_CODE (operands[2]) == CONST_INT
   && (INTVAL (operands[2]) & 0xff0000) == 0xff0000"
  "and %2,%0"
  [(set_attr "cc" "clobber")])

;; ----------------------------------------------------------------------
;; OR INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_insn "iorhi3"
  [(set (match_operand:HI 0 "general_operand" "=d")
	(ior:HI (match_operand:HI 1 "general_operand" "%0")
		(match_operand:HI 2 "general_operand" "di")))]
  ""
  "or %2,%0"
  [(set_attr "cc" "set_znv")])

;; ----------------------------------------------------------------------
;; XOR INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_insn "xorhi3"
  [(set (match_operand:HI 0 "general_operand" "=d")
	(xor:HI (match_operand:HI 1 "general_operand" "%0")
		(match_operand:HI 2 "general_operand" "di")))]
  ""
  "xor %2,%0"
  [(set_attr "cc" "set_znv")])

;; ----------------------------------------------------------------------
;; NOT INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_insn "one_cmplhi2"
  [(set (match_operand:HI 0 "general_operand" "=d")
	(not:HI (match_operand:HI 1 "general_operand" "0")))]
  ""
  "not %0"
  [(set_attr "cc" "set_znv")])


;; -----------------------------------------------------------------
;; BIT INSTRUCTIONS
;; -----------------------------------------------------------------

;; These clears a constant set of bits in memory or in a register.
;; We must support register destinations to make reload happy.
(define_insn ""
  [(set (match_operand:QI 0 "general_operand" "+R,d")
	(subreg:QI
	  (and:HI (subreg:HI (match_dup 0) 0)
		  (match_operand 1 "const_int_operand" "")) 0))
   (clobber (match_scratch:HI 2 "=&d,X"))]
  ""
  "@
  mov %N1,%2\;bclr %2,%0
  and %1,%0"
  [(set_attr "cc" "clobber")])

;; This clears a variable set of bits in memory or in a register.
(define_insn ""
  [(set (match_operand:QI 0 "general_operand" "+R,d")
	(subreg:QI
	  (and:HI (subreg:HI (match_dup 0) 0)
		  (not:HI (match_operand:HI 1 "general_operand" "d,d"))) 0))
   (clobber (match_scratch:HI 2 "=X,&d"))]
  ""
  "@
  bclr %1,%0
  mov %1,%2\;not %2\;and %2,%0"
  [(set_attr "cc" "clobber")])

(define_insn ""
  [(set (match_operand:QI 0 "general_operand" "+R,d")
	(subreg:QI
	  (and:HI (not:HI (match_operand:HI 1 "general_operand" "d,d"))
		  (subreg:HI (match_dup 0) 0)) 0))
   (clobber (match_scratch:HI 2 "=X,&d"))]
  ""
  "@
  bclr %1,%0
  mov %1,%2\;not %2\;and %2,%0"
  [(set_attr "cc" "clobber")])

;; These set bits in memory.
(define_insn ""
  [(set (match_operand:QI 0 "general_operand" "+R,d")
	(subreg:QI
	  (ior:HI (subreg:HI (match_dup 0) 0)
		  (match_operand:HI 1 "general_operand" "d,d")) 0))]
  ""
  "@
  bset %1,%0
  or %1,%0"
  [(set_attr "cc" "clobber")])

(define_insn ""
  [(set (match_operand:QI 0 "general_operand" "+R,d")
	(subreg:QI
	  (ior:HI (match_operand:HI 1 "general_operand" "d,d")
		  (subreg:HI (match_dup 0) 0)) 0))]
  ""
  "@
  bset %1,%0
  or %1,%0"
  [(set_attr "cc" "clobber")])

;; Not any shorter/faster than using cmp, but it might save a
;; register if the result of the AND isn't ever used.

(define_insn ""
  [(set (cc0)
     (zero_extract:HI (match_operand:HI 0 "general_operand" "d")
		      (match_operand 1 "const_int_operand" "")
		      (match_operand 2 "const_int_operand" "")))]
  ""
  "*
{
  int len = INTVAL (operands[1]);
  int bit = INTVAL (operands[2]);
  int mask = 0;
  rtx xoperands[2];

  while (len > 0)
    {
      mask |= (1 << bit);
      bit++;
      len--;
    }

  xoperands[0] = operands[0];
  xoperands[1] = GEN_INT (mask);
  output_asm_insn (\"btst %1,%0\", xoperands);
  return \"\";
}"
  [(set_attr "cc" "clobber")])

(define_insn ""
  [(set (cc0) (and:HI (match_operand:HI 0 "general_operand" "d")
		      (match_operand:HI 1 "const_int_operand" "i")))]
  ""
  "btst %1,%0"
  [(set_attr "cc" "clobber")])


;; ----------------------------------------------------------------------
;; JUMP INSTRUCTIONS
;; ----------------------------------------------------------------------

;; Conditional jump instructions

(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 "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 "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)))]
  ""
  "")

(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_insn ""
  [(set (pc)
	(if_then_else (match_operator 1 "comparison_operator"
				      [(cc0) (const_int 0)])
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  if ((cc_status.flags & CC_OVERFLOW_UNUSABLE) != 0
      && (GET_CODE (operands[1]) == GT
          || GET_CODE (operands[1]) == GE
          || GET_CODE (operands[1]) == LE
          || GET_CODE (operands[1]) == LT))
    return 0;

  if (GET_MODE (SET_SRC (PATTERN (PREV_INSN (insn)))) == PSImode)
    return \"b%b1x %0\";
  else
    return \"b%b1 %0\";
}"
 [(set_attr "cc" "none")])

(define_insn ""
  [(set (pc)
	(if_then_else (match_operator 1 "comparison_operator"
				      [(cc0) (const_int 0)])
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
  if ((cc_status.flags & CC_OVERFLOW_UNUSABLE) != 0
      && (GET_CODE (operands[1]) == GT
          || GET_CODE (operands[1]) == GE
          || GET_CODE (operands[1]) == LE
          || GET_CODE (operands[1]) == LT))
    return 0;

  if (GET_MODE (SET_SRC (PATTERN (PREV_INSN (insn)))) == PSImode)
    return \"b%B1x %0\";
  else
    return \"b%B1 %0\";
}"
 [(set_attr "cc" "none")])

(define_insn "jump"
  [(set (pc)
	(label_ref (match_operand 0 "" "")))]
  ""
  "jmp %l0"
 [(set_attr "cc" "none")])

(define_insn "indirect_jump"
  [(set (pc) (match_operand:PSI 0 "general_operand" "a"))]
  ""
  "jmp (%0)"
  [(set_attr "cc" "none")])

(define_insn "tablejump"
  [(set (pc) (match_operand:PSI 0 "general_operand" "a"))
   (use (label_ref (match_operand 1 "" "")))]
  ""
  "jmp  (%0)"
  [(set_attr "cc" "none")])

;; Call subroutine with no return value.

(define_expand "call"
  [(call (match_operand:QI 0 "general_operand" "")
	 (match_operand:HI 1 "general_operand" ""))]
  ""
  "
{
  if (! call_address_operand (XEXP (operands[0], 0)))
    XEXP (operands[0], 0) = force_reg (PSImode, XEXP (operands[0], 0));
  emit_call_insn (gen_call_internal (XEXP (operands[0], 0), operands[1]));
  DONE;
}")

(define_insn "call_internal"
  [(call (mem:QI (match_operand:PSI 0 "call_address_operand" "aS"))
	 (match_operand:HI 1 "general_operand" "g"))]
  ""
  "jsr %C0"
  [(set_attr "cc" "clobber")])

;; Call subroutine, returning value in operand 0
;; (which must be a hard register).

(define_expand "call_value"
  [(set (match_operand 0 "" "")
	(call (match_operand:QI 1 "general_operand" "")
	      (match_operand:HI 2 "general_operand" "")))]
  ""
  "
{
  if (! call_address_operand (XEXP (operands[1], 0)))
    XEXP (operands[1], 0) = force_reg (PSImode, XEXP (operands[1], 0));
  emit_call_insn (gen_call_value_internal (operands[0],
					   XEXP (operands[1], 0),
					   operands[2]));
  DONE;
}")

(define_insn "call_value_internal"
  [(set (match_operand 0 "" "=da")
	(call (mem:QI (match_operand:PSI 1 "call_address_operand" "aS"))
	      (match_operand:HI 2 "general_operand" "g")))]
  ""
  "jsr %C1"
  [(set_attr "cc" "clobber")])

(define_expand "untyped_call"
  [(parallel [(call (match_operand 0 "" "")
                    (const_int 0))
              (match_operand 1 "" "")
              (match_operand 2 "" "")])]
  ""
  "
{
  int i;

  emit_call_insn (gen_call (operands[0], const0_rtx));

  for (i = 0; i < XVECLEN (operands[2], 0); i++)
    {
      rtx set = XVECEXP (operands[2], 0, i);
      emit_move_insn (SET_DEST (set), SET_SRC (set));
    }
  DONE;
}")

(define_insn "nop"
  [(const_int 0)]
  ""
  "nop"
  [(set_attr "cc" "none")])

;; ----------------------------------------------------------------------
;; EXTEND INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_insn "zero_extendqihi2"
  [(set (match_operand:HI 0 "general_operand" "=d,d,d")
	(zero_extend:HI
	 (match_operand:QI 1 "general_operand" "0,di,m")))]
  ""
  "@
  extxbu %0
  mov %1,%0\;extxbu %0
  movbu %1,%0"
  [(set_attr "cc" "none_0hit")])

(define_insn "zero_extendqipsi2"
  [(set (match_operand:PSI 0 "general_operand" "=d,d,d")
	(zero_extend:PSI
	 (match_operand:QI 1 "general_operand" "0,di,m")))]
  ""
  "@
  extxbu %0
  mov %1,%0\;extxbu %0
  movbu %1,%0"
  [(set_attr "cc" "none_0hit")])

(define_insn "zero_extendqisi2"
  [(set (match_operand:SI 0 "general_operand" "=d,d,d")
	(zero_extend:SI
	 (match_operand:QI 1 "general_operand" "0,di,m")))]
  ""
  "@
  extxbu %L0\;sub %H0,%H0
  mov %1,%L0\;extxbu %L0\;sub %H0,%H0
  movbu %1,%L0\;sub %H0,%H0"
  [(set_attr "cc" "clobber")])

(define_insn "zero_extendhipsi2"
  [(set (match_operand:PSI 0 "general_operand" "=d,d,d")
	(zero_extend:PSI
	 (match_operand:HI 1 "general_operand" "0,di,m")))]
  ""
  "@
  extxu %0
  mov %1,%0\;extxu %0
  mov %1,%0\;extxu %0"
  [(set_attr "cc" "none_0hit")])

(define_insn "zero_extendhisi2"
  [(set (match_operand:SI 0 "general_operand" "=d,d")
	(zero_extend:SI
	 (match_operand:HI 1 "general_operand" "0,dim")))]
  ""
  "@
  sub %H0,%H0
  mov %1,%L0\;sub %H0,%H0"
  [(set_attr "cc" "clobber,clobber")])

;; The last alternative is necessary because the second operand might
;; have been the frame pointer.  The frame pointer would get replaced
;; by (plus (stack_pointer) (const_int)).
;;
;; Reload would think that it only needed a PSImode register in
;; push_reload and at the start of allocate_reload_regs.  However,
;; at the end of allocate_reload_reg it would realize that the
;; reload register must also be valid for SImode, and if it was
;; not valid reload would abort.
(define_insn "zero_extendpsisi2"
  [(set (match_operand:SI 0 "register_operand" "=d,?d,?*d,?*d")
	(zero_extend:SI (match_operand:PSI 1 "extendpsi_operand"
						"m,?0,?*dai,Q")))]
  ""
  "@
  mov %L1,%L0\;movbu %H1,%H0
  jsr ___zero_extendpsisi2_%0
  mov %1,%L0\;jsr ___zero_extendpsisi2_%0
  mov a3,%L0\;add %Z1,%L0\;jsr ___zero_extendpsisi2_%0"
  [(set_attr "cc" "clobber")])

;;- sign extension instructions

(define_insn "extendqihi2"
  [(set (match_operand:HI 0 "general_operand" "=d,d,d")
	(sign_extend:HI
	 (match_operand:QI 1 "general_operand" "0,di,m")))]
  ""
  "*
{
  if (which_alternative == 0)
    return \"extxb %0\";
  else if (which_alternative == 1)
    return \"mov %1,%0\;extxb %0\";
  else if (GET_CODE (XEXP (operands[1], 0)) == REG)
    return \"movbu %1,%0\;extxb %0\";
  else
    return \"movb %1,%0\";
}"
  [(set_attr "cc" "none_0hit")])

(define_insn "extendqipsi2"
  [(set (match_operand:PSI 0 "general_operand" "=d,d,d")
	(sign_extend:PSI
	 (match_operand:QI 1 "general_operand" "0,di,m")))]
  ""
  "*
{
  if (which_alternative == 0)
    return \"extxb %0\";
  else if (which_alternative == 1)
    return \"mov %1,%0\;extxb %0\";
  else if (GET_CODE (XEXP (operands[1], 0)) == REG)
    return \"movbu %1,%0\;extxb %0\";
  else
    return \"movb %1,%0\";
}"
  [(set_attr "cc" "none_0hit")])

(define_insn "extendqisi2"
  [(set (match_operand:SI 0 "general_operand" "=d,d,d")
	(sign_extend:SI
	 (match_operand:QI 1 "general_operand" "0,di,m")))]
  ""
  "*
{
  if (which_alternative == 0)
    return \"extxb %L0\;mov %L0,%H0\;add %H0,%H0\;subc %H0,%H0\";
  else if (which_alternative == 1)
    return \"mov %1,%L0\;extxb %L0\;mov %L0,%H0\;add %H0,%H0\;subc %H0,%H0\";
  else if (GET_CODE (XEXP (operands[1], 0)) == REG)
    return \"movbu %1,%L0\;extxb %L0\;mov %L0,%H0\;add %H0,%H0\;subc %H0,%H0\";
  else
    return \"movb %1,%L0\;mov %L0,%H0\;add %H0,%H0\;subc %H0,%H0\";
}"
  [(set_attr "cc" "clobber")])

(define_insn "extendhipsi2"
  [(set (match_operand:PSI 0 "general_operand" "=d,d,d")
	(sign_extend:PSI
	 (match_operand:HI 1 "general_operand" "0,di,m")))]
  ""
  "@
  extx %0
  mov %1,%0\;extx %0
  mov %1,%0"
  [(set_attr "cc" "none_0hit")])

(define_insn "extendhisi2"
  [(set (match_operand:SI 0 "general_operand" "=d,d,d")
	(sign_extend:SI
	 (match_operand:HI 1 "general_operand" "0,di,m")))]
  ""
  "@
  mov %L0,%H0\;add %H0,%H0\;subc %H0,%H0
  mov %1,%L0\;mov %L0,%H0\;add %H0,%H0\;subc %H0,%H0
  mov %1,%L0\;mov %L0,%H0\;add %H0,%H0\;subc %H0,%H0"
  [(set_attr "cc" "clobber")])

;; The last alternative is necessary because the second operand might
;; have been the frame pointer.  The frame pointer would get replaced
;; by (plus (stack_pointer) (const_int)).
;;
;; Reload would think that it only needed a PSImode register in
;; push_reload and at the start of allocate_reload_regs.  However,
;; at the end of allocate_reload_reg it would realize that the
;; reload register must also be valid for SImode, and if it was
;; not valid reload would abort.
(define_insn "extendpsisi2"
  [(set (match_operand:SI 0 "general_operand" "=d,?d,?*d,?*d")
	(sign_extend:SI (match_operand:PSI 1 "extendpsi_operand"
						"m,?0,?*dai,Q")))]
  ""
  "@
  mov %L1,%L0\;movb %H1,%H0
  jsr ___sign_extendpsisi2_%0
  mov %1,%L0\;jsr ___sign_extendpsisi2_%0
  mov a3,%L0\;add %Z1,%L0\;jsr ___sign_extendpsisi2_%0"
  [(set_attr "cc" "clobber")])

(define_insn "truncsipsi2"
  [(set (match_operand:PSI 0 "general_operand" "=a,?d,?*d,da")
	(truncate:PSI (match_operand:SI 1 "general_operand" "m,?m,?*d,i")))]
   ""
   "@
   mov %1,%0
   movx %A1,%0
   jsr ___truncsipsi2_%1_%0
   mov %1,%0"
  [(set_attr "cc" "clobber")])


;; Combine should be simplifying this stuff, but isn't.
;;
(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=d,d,d")
	(sign_extend:SI
	  (zero_extend:HI (match_operand:QI 1 "general_operand" "0,di,m"))))]
  ""
  "@
  extxbu %L0\;sub %H0,%H0
  mov %1,%L0\;extxbu %L0\;sub %H0,%H0
  movbu %1,%L0\;sub %H0,%H0"
  [(set_attr "cc" "clobber")])

(define_insn ""
  [(set (match_operand:PSI 0 "general_operand" "=d,d,d")
        (truncate:PSI
	  (sign_extend:SI (match_operand:QI 1 "general_operand" "0,di,m"))))]
  ""
  "*
{
  if (which_alternative == 0)
    return \"extxb %0\";
  else if (which_alternative == 1)
    return \"mov %1,%0\;extxb %0\";
  else if (GET_CODE (XEXP (operands[1], 0)) == REG)
    return \"movbu %1,%0\;extxb %0\";
  else
    return \"movb %1,%0\";
}"
  [(set_attr "cc" "none_0hit")])

(define_insn ""
  [(set (match_operand:PSI 0 "general_operand" "=d,d,d")
	(truncate:PSI
	  (sign_extend:SI (match_operand:HI 1 "general_operand" "0,di,m"))))]
  ""
  "@
  extx %0
  mov %1,%0\;extx %0
  mov %1,%0"
  [(set_attr "cc" "none_0hit")])

(define_insn ""
  [(set (match_operand:PSI 0 "general_operand" "=d,d,d")
	(truncate:PSI
	  (sign_extend:SI
	    (zero_extend:HI (match_operand:QI 1 "general_operand" "0,di,m")))))]
  ""
  "@
  extxbu %0
  mov %1,%0\;extxbu %0
  movbu %1,%0"
  [(set_attr "cc" "none_0hit")])

(define_insn ""
  [(set (match_operand:PSI 0 "general_operand" "=d,d,d")
	(truncate:PSI
	  (zero_extend:SI (match_operand:HI 1 "general_operand" "0,di,m"))))]
  ""
  "@
  extxu %0
  mov %1,%0\;extxu %0
  mov %1,%0\;extxu %0"
  [(set_attr "cc" "none_0hit")])

(define_insn ""
  [(set (match_operand:PSI 0 "general_operand" "=d,d,d")
        (truncate:PSI
	  (zero_extend:SI (match_operand:QI 1 "general_operand" "0,di,m"))))]
  ""
  "@
  extxbu %0
  mov %1,%0\;extxbu %0
  movbu %1,%0"
  [(set_attr "cc" "none_0hit")])

;; ----------------------------------------------------------------------
;; SHIFTS
;; ----------------------------------------------------------------------

;; If the shift count is small, we expand it into several single bit
;; shift insns.  Otherwise we expand into a generic shift insn which
;; handles larger shift counts, shift by variable amounts, etc.
(define_expand "ashlhi3"
  [(set (match_operand:HI 0 "general_operand" "")
	(ashift:HI (match_operand:HI 1 "general_operand" "")
		   (match_operand:HI 2 "general_operand" "")))]
  ""
  "
{
  /* This is an experiment to see if exposing more of the underlying
     operations results in better code.  */
  if (GET_CODE (operands[2]) == CONST_INT
      && INTVAL (operands[2]) <= 4)
    {
      int count = INTVAL (operands[2]);
      emit_move_insn (operands[0], operands[1]);
      while (count > 0)
	{
	  emit_insn (gen_rtx (SET, HImode, operands[0],
			      gen_rtx (ASHIFT, HImode,
				       operands[0], GEN_INT (1))));
	  count--;
	}
      DONE;
    }
  else
    {
      expand_a_shift (HImode, ASHIFT, operands);
      DONE;
    }
}")

;; ASHIFT one bit.
(define_insn ""
  [(set (match_operand:HI 0 "general_operand" "=d")
	(ashift:HI (match_operand:HI 1 "general_operand" "0")
		   (const_int 1)))]
  ""
  "add %0,%0"
  [(set_attr "cc" "set_zn")])

(define_expand "lshrhi3"
  [(set (match_operand:HI 0 "general_operand" "")
	(lshiftrt:HI (match_operand:HI 1 "general_operand" "")
		     (match_operand:HI 2 "general_operand" "")))]
  ""
  "
{
  /* This is an experiment to see if exposing more of the underlying
     operations results in better code.  */
  if (GET_CODE (operands[2]) == CONST_INT
      && INTVAL (operands[2]) <= 4)
    {
      int count = INTVAL (operands[2]);
      emit_move_insn (operands[0], operands[1]);
      while (count > 0)
	{
	  emit_insn (gen_rtx (SET, HImode, operands[0],
			      gen_rtx (LSHIFTRT, HImode,
				       operands[0], GEN_INT (1))));
	  count--;
	}
      DONE;
    }
  else
    {
      expand_a_shift (HImode, LSHIFTRT, operands);
      DONE;
    }
}")

;; LSHIFTRT one bit.
(define_insn ""
  [(set (match_operand:HI 0 "general_operand" "=d")
	(lshiftrt:HI (match_operand:HI 1 "general_operand" "0")
		     (const_int 1)))]
  ""
  "lsr %0"
  [(set_attr "cc" "set_znv")])

(define_expand "ashrhi3"
  [(set (match_operand:HI 0 "general_operand" "")
	(ashiftrt:HI (match_operand:HI 1 "general_operand" "")
		     (match_operand:HI 2 "general_operand" "")))]
  ""
  "
{
  /* This is an experiment to see if exposing more of the underlying
     operations results in better code.  */
  if (GET_CODE (operands[2]) == CONST_INT
      && INTVAL (operands[2]) <= 4)
    {
      int count = INTVAL (operands[2]);
      emit_move_insn (operands[0], operands[1]);
      while (count > 0)
	{
	  emit_insn (gen_rtx (SET, HImode, operands[0],
			      gen_rtx (ASHIFTRT, HImode,
				       operands[0], GEN_INT (1))));
	  count--;
	}
      DONE;
    }
  else
    {
      expand_a_shift (HImode, ASHIFTRT, operands);
      DONE;
    }
}")

;; ASHIFTRT one bit.
(define_insn ""
  [(set (match_operand:HI 0 "general_operand" "=d")
	(ashiftrt:HI (match_operand:HI 1 "general_operand" "0")
		     (const_int 1)))]
  ""
  "asr %0"
  [(set_attr "cc" "set_znv")])

;; And the general HImode shift pattern.  Handles both shift by constants
;; and shift by variable counts.
(define_insn ""
  [(set (match_operand:HI 0 "general_operand" "=d,d")
	(match_operator:HI 3 "nshift_operator" 
			[ (match_operand:HI 1 "general_operand" "0,0")
			  (match_operand:HI 2 "general_operand" "KL,dan")]))
   (clobber (match_scratch:HI 4 "=X,&d"))]
  ""
  "* return emit_a_shift (insn, operands);"
  [(set_attr "cc" "clobber")])

;; We expect only ASHIFT with constant shift counts to be common for
;; PSImode, so we optimize just that case.  For all other cases we
;; extend the value to SImode and perform the shift in SImode.
(define_expand "ashlpsi3"
  [(set (match_operand:PSI 0 "general_operand" "")
	(ashift:PSI (match_operand:PSI 1 "general_operand" "")
		   (match_operand:HI 2 "general_operand" "")))]
  ""
  "
{
  /* This is an experiment to see if exposing more of the underlying
     operations results in better code.  */
  if (GET_CODE (operands[2]) == CONST_INT
      && INTVAL (operands[2]) <= 7)
    {
      int count = INTVAL (operands[2]);
      emit_move_insn (operands[0], operands[1]);
      while (count > 0)
	{
	  emit_insn (gen_rtx (SET, PSImode, operands[0],
			      gen_rtx (ASHIFT, PSImode,
				       operands[0], GEN_INT (1))));
	  count--;
	}
      DONE;
    }
  else
    {
      expand_a_shift (PSImode, ASHIFT, operands);
      DONE;
    }
}")

;; ASHIFT one bit.
(define_insn ""
  [(set (match_operand:PSI 0 "general_operand" "=d")
	(ashift:PSI (match_operand:PSI 1 "general_operand" "0")
		    (const_int 1)))]
  ""
  "add %0,%0"
  [(set_attr "cc" "set_zn")])

(define_expand "lshrpsi3"
  [(set (match_operand:PSI 0 "general_operand" "")
	(lshiftrt:PSI (match_operand:PSI 1 "general_operand" "")
		     (match_operand:HI 2 "general_operand" "")))]
  ""
  "
{
  rtx reg = gen_reg_rtx (SImode);

  emit_insn (gen_zero_extendpsisi2 (reg, operands[1]));
  reg = expand_binop (SImode, lshr_optab, reg,
		      operands[2], reg, 1, OPTAB_WIDEN);
  emit_insn (gen_truncsipsi2 (operands[0], reg));
  DONE;
}")

(define_expand "ashrpsi3"
  [(set (match_operand:PSI 0 "general_operand" "")
	(ashiftrt:PSI (match_operand:PSI 1 "general_operand" "")
		     (match_operand:HI 2 "general_operand" "")))]
  ""
  "
{
  rtx reg = gen_reg_rtx (SImode);

  emit_insn (gen_extendpsisi2 (reg, operands[1]));
  reg = expand_binop (SImode, ashr_optab, reg,
		      operands[2], reg, 0, OPTAB_WIDEN);
  emit_insn (gen_truncsipsi2 (operands[0], reg));
  DONE;
}")

(define_expand "ashlsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(ashift:SI (match_operand:SI 1 "nonmemory_operand" "")
		   (match_operand:HI 2 "general_operand" "")))]
  ""
  "
{
  /* For small shifts, just emit a series of single bit shifts inline.

     For other constant shift counts smaller than a word or non-constant
     shift counts we call out to a library call during RTL generation time;
     after RTL generation time we allow optabs.c to open code the operation.
     See comments in addsi3/subsi3 expanders.

     Otherwise we allow optabs.c to open code the operation.  */
  if (GET_CODE (operands[2]) == CONST_INT
      && (INTVAL (operands[2]) <= 3))
    {
      int count = INTVAL (operands[2]);
      emit_move_insn (operands[0], operands[1]);
      while (count > 0)
	{
	  emit_insn (gen_rtx (SET, SImode, operands[0],
				   gen_rtx (ASHIFT, SImode,
					    operands[0], GEN_INT (1))));
	  count--;
	}
      DONE;
    }
  else if (rtx_equal_function_value_matters
	   && (GET_CODE (operands[2]) != CONST_INT
	       || INTVAL (operands[2]) <= 15))
    {
      rtx ret, insns;

      start_sequence ();
      ret = emit_library_call_value (gen_rtx (SYMBOL_REF, Pmode, \"__ashlsi3\"),
				     NULL_RTX, 1, SImode, 2, operands[1],
				     SImode, operands[2], HImode);
      insns = get_insns ();
      end_sequence ();
      emit_libcall_block (insns, operands[0], ret,
			  gen_rtx (ASHIFT, SImode, operands[1], operands[2]));
      DONE;
    }
  else
    FAIL;
}")

;; ASHIFT one bit.
(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=d")
	(ashift:SI (match_operand:SI 1 "general_operand" "0")
		     (const_int 1)))]
  ""
  "add %L0,%L0\;addc %H0,%H0"
  [(set_attr "cc" "clobber")])

(define_expand "lshrsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(lshiftrt:SI (match_operand:SI 1 "general_operand" "")
		     (match_operand:HI 2 "general_operand" "")))]
  ""
  "
{
  /* For small shifts, just emit a series of single bit shifts inline.

     For other constant shift counts smaller than a word or non-constant
     shift counts we call out to a library call during RTL generation time;
     after RTL generation time we allow optabs.c to open code the operation.
     See comments in addsi3/subsi3 expanders.

     Otherwise we allow optabs.c to open code the operation.  */
  if (GET_CODE (operands[2]) == CONST_INT
      && (INTVAL (operands[2]) <= 2))
    {
      int count = INTVAL (operands[2]);
      emit_move_insn (operands[0], operands[1]);
      while (count > 0)
	{
	  emit_insn (gen_rtx (SET, SImode, operands[0],
				   gen_rtx (LSHIFTRT, SImode,
					    operands[0], GEN_INT (1))));
	  count--;
	}
      DONE;
    }
  else if (rtx_equal_function_value_matters
	   && (GET_CODE (operands[2]) != CONST_INT
	       || INTVAL (operands[2]) <= 15))
    {
      rtx ret, insns;

      start_sequence ();
      ret = emit_library_call_value (gen_rtx (SYMBOL_REF, Pmode, \"__lshrsi3\"),
				     NULL_RTX, 1, SImode, 2, operands[1],
				     SImode, operands[2], HImode);
      insns = get_insns ();
      end_sequence ();
      emit_libcall_block (insns, operands[0], ret,
			  gen_rtx (LSHIFTRT, SImode, operands[1], operands[2]));
      DONE;
    }
  else
    FAIL;
}")

;; LSHIFTRT one bit.
(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=d")
	(lshiftrt:SI (match_operand:SI 1 "general_operand" "0")
		     (const_int 1)))]
  ""
  "lsr %H0\;ror %L0"
  [(set_attr "cc" "clobber")])

(define_expand "ashrsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(ashiftrt:SI (match_operand:SI 1 "register_operand" "")
		     (match_operand:HI 2 "general_operand" "")))]
  ""
  "
{
  /* For small shifts, just emit a series of single bit shifts inline.

     For other constant shift counts smaller than a word or non-constant
     shift counts we call out to a library call during RTL generation time;
     after RTL generation time we allow optabs.c to open code the operation.
     See comments in addsi3/subsi3 expanders.

     Otherwise we allow optabs.c to open code the operation.  */
  if (GET_CODE (operands[2]) == CONST_INT
      && (INTVAL (operands[2]) <= 2))
    {
      int count = INTVAL (operands[2]);
      emit_move_insn (operands[0], operands[1]);
      while (count > 0)
	{
	  emit_insn (gen_rtx (SET, SImode, operands[0],
				   gen_rtx (ASHIFTRT, SImode,
					    operands[0], GEN_INT (1))));
	  count--;
	}
      DONE;
    }
  else if (rtx_equal_function_value_matters
	   && (GET_CODE (operands[2]) != CONST_INT
	       || INTVAL (operands[2]) <= 15))
    {
      rtx ret, insns;

      start_sequence ();
      ret = emit_library_call_value (gen_rtx (SYMBOL_REF, Pmode, \"__ashrsi3\"),
				     NULL_RTX, 1, SImode, 2, operands[1],
				     SImode, operands[2], HImode);
      insns = get_insns ();
      end_sequence ();
      emit_libcall_block (insns, operands[0], ret,
			  gen_rtx (ASHIFTRT, SImode, operands[1], operands[2]));
      DONE;
    }
  else
    FAIL;
}")

;; ASHIFTRT one bit.
(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=d")
	(ashiftrt:SI (match_operand:SI 1 "general_operand" "0")
		     (const_int 1)))]
  ""
  "asr %H0\;ror %L0"
  [(set_attr "cc" "clobber")])

;; ----------------------------------------------------------------------
;; FP INSTRUCTIONS
;; ----------------------------------------------------------------------
;;
;; The mn102 series does not have floating point instructions, but since
;; FP values are held in integer regs, we can clear the high bit easily
;; which gives us an efficient inline floating point absolute value.
;;
;; Similarly for negation of a FP value.
;;

(define_expand "abssf2"
  [(set (match_operand:SF 0 "register_operand" "")
        (abs:SF (match_operand:SF 1 "register_operand" "")))]
  ""
  "
{
  rtx target, result, insns;

  start_sequence ();
  target = operand_subword (operands[0], 1, 1, SFmode);
  result = expand_binop (HImode, and_optab,
			 operand_subword_force (operands[1], 1, SFmode),
			 GEN_INT(0x7fff), target, 0, OPTAB_WIDEN);

  if (result == 0)
    abort ();

  if (result != target)
    emit_move_insn (result, target);

  emit_move_insn (operand_subword (operands[0], 0, 1, SFmode),
		  operand_subword_force (operands[1], 0, SFmode));

  insns = get_insns ();
  end_sequence ();

  emit_no_conflict_block (insns, operands[0], operands[1], 0, 0);
  DONE;
}")

(define_expand "negsf2"
  [(set (match_operand:SF 0 "register_operand" "")
        (neg:SF (match_operand:SF 1 "register_operand" "")))]
  ""
  "
{
  rtx target, result, insns;

  start_sequence ();
  target = operand_subword (operands[0], 1, 1, SFmode);
  result = expand_binop (HImode, xor_optab,
			 operand_subword_force (operands[1], 1, SFmode),
			 GEN_INT(0x8000), target, 0, OPTAB_WIDEN);

  if (result == 0)
    abort ();

  if (result != target)
    emit_move_insn (result, target);

  emit_move_insn (operand_subword (operands[0], 0, 1, SFmode),
		  operand_subword_force (operands[1], 0, SFmode));

  insns = get_insns ();
  end_sequence ();

  emit_no_conflict_block (insns, operands[0], operands[1], 0, 0);
  DONE;
}")

;; ----------------------------------------------------------------------
;; PROLOGUE/EPILOGUE
;; ----------------------------------------------------------------------
(define_expand "prologue"
  [(const_int 0)]
  ""
  "expand_prologue (); DONE;")

(define_insn "outline_prologue_call"
  [(const_int 1)]
  ""
  "jsr ___prologue"
  [(set_attr "cc" "clobber")])

(define_expand "epilogue"
  [(return)]
  ""
  "
{
  expand_epilogue ();
  DONE;
}")

(define_insn "outline_epilogue_call_a0"
  [(const_int 2)]
  ""
  "jsr ___epilogue_a0"
  [(set_attr "cc" "clobber")])

(define_insn "outline_epilogue_call_d0"
  [(const_int 3)]
  ""
  "jsr ___epilogue_d0"
  [(set_attr "cc" "clobber")])

(define_insn "outline_epilogue_jump"
  [(const_int 4)]
  ""
  "jmp ___epilogue_noreturn"
  [(set_attr "cc" "clobber")])

(define_insn "return"
  [(return)]
  "reload_completed && total_frame_size () == 0
   && !current_function_needs_context"
  "*
{
  rtx next = next_active_insn (insn);

  if (next
      && GET_CODE (next) == JUMP_INSN
      && GET_CODE (PATTERN (next)) == RETURN)
    return \"\";
  return \"rts\";
}"
  [(set_attr "cc" "clobber")])

(define_insn "return_internal"
  [(const_int 0)
   (return)]
  ""
  "rts"
  [(set_attr "cc" "clobber")])

;; These are special combiner patterns to improve array/pointer accesses.
;;
;; A typical sequence involves extending an integer/char, shifting it left
;; a few times, then truncating the value to PSImode.
;;
;; This first pattern combines the shifting & truncation operations, by
;; itself it is a win because the shifts end up occurring in PSImode instead
;; of SImode.  However, it has the secondary effect of giving us the
;; opportunity to match patterns which allow us to remove the initial
;; extension completely, which is a big win.
(define_insn ""
  [(set (match_operand:PSI 0 "general_operand" "=d,d,a,da")
	(truncate:PSI
	  (ashift:SI (match_operand:SI 1 "general_operand" "d,m,m,i")
		     (match_operand:HI 2 "const_int_operand" "i,i,i,i"))))]
  ""
  "*
{
  int count = INTVAL (operands[2]);
  if (which_alternative == 0)
    output_asm_insn (\"jsr ___truncsipsi2_%1_%0\", operands);
  else if (which_alternative == 1)
    output_asm_insn (\"movx %A1,%0\", operands);
  else
    output_asm_insn (\" mov %1,%0\", operands);

  while (count)
    {
      output_asm_insn (\"add %0,%0\", operands);
      count--;
    }
  return \"\";
}"
  [(set_attr "cc" "clobber")])

;; Similarly, except that we also have zero/sign extension of the
;; original operand.  */
(define_insn ""
  [(set (match_operand:PSI 0 "general_operand" "=d,d")
	(truncate:PSI
	  (ashift:SI
	    (zero_extend:SI (match_operand:HI 1 "general_operand" "0,dim"))
	    (match_operand:HI 2 "const_int_operand" "i,i"))))]
  ""
  "*
{
  int count = INTVAL (operands[2]);

  /* First extend operand 1 to PSImode.  */
  if (which_alternative == 0)
    output_asm_insn (\"extxu %0\", operands);
  else
    output_asm_insn (\"mov %1,%0\;extxu %0\", operands);

  /* Now do the shifting.  */
  while (count)
    {
      output_asm_insn (\"add %0,%0\", operands);
      count--;
    }
  return \"\";
}"
  [(set_attr "cc" "clobber")])

(define_insn ""
  [(set (match_operand:PSI 0 "general_operand" "=d,d,d")
	(truncate:PSI
	  (ashift:SI
	    (sign_extend:SI (match_operand:HI 1 "general_operand" "0,di,m"))
	    (match_operand:HI 2 "const_int_operand" "i,i,i"))))]
  ""
  "*
{
  int count = INTVAL (operands[2]);

  /* First extend operand 1 to PSImode.  */
  if (which_alternative == 0)
    output_asm_insn (\"extx %0\", operands);
  else if (which_alternative == 1)
    output_asm_insn (\"mov %1,%0\;extx %0\", operands);
  else
    output_asm_insn (\"mov %1,%0\", operands);

  /* Now do the shifting.  */
  while (count)
    {
      output_asm_insn (\"add %0,%0\", operands);
      count--;
    }
  return \"\";
}"
  [(set_attr "cc" "clobber")])

(define_insn ""
  [(set (match_operand:PSI 0 "general_operand" "=d,d,d")
	(truncate:PSI
	  (ashift:SI
	    (sign_extend:SI
	      (zero_extend:HI (match_operand:QI 1 "general_operand" "0,di,m")))
	    (match_operand:HI 2 "const_int_operand" "i,i,i"))))]
  ""
  "*
{
  int count = INTVAL (operands[2]);

  /* First extend operand 1 to PSImode.  */
  if (which_alternative == 0)
    output_asm_insn (\"extxbu %0\", operands);
  else if (which_alternative == 1)
    output_asm_insn (\"mov %1,%0\;extxbu %0\", operands);
  else
    output_asm_insn (\"movbu %1,%0\", operands);

  /* Now do the shifting.  */
  while (count)
    {
      output_asm_insn (\"add %0,%0\", operands);
      count--;
    }
  return \"\";
}"
  [(set_attr "cc" "clobber")])

(define_insn ""
  [(set (match_operand:PSI 0 "general_operand" "=d,d,d")
	(truncate:PSI
	  (ashift:SI
	    (sign_extend:SI
	      (match_operand:QI 1 "general_operand" "0,di,m"))
	    (match_operand:HI 2 "const_int_operand" "i,i,i"))))]
  ""
  "*
{
  int count = INTVAL (operands[2]);

  /* First extend operand 1 to PSImode.  */
  if (which_alternative == 0)
    output_asm_insn (\"extxb %0\", operands);
  else if (which_alternative == 1)
    output_asm_insn (\"mov %1,%0\;extxb %0\", operands);
  else if (GET_CODE (XEXP (operands[1], 0)) == REG)
    output_asm_insn (\"movbu %1,%0\;extxb %0\", operands);
  else
    output_asm_insn (\"movb %1,%0\", operands);

  /* Now do the shifting.  */
  while (count)
    {
      output_asm_insn (\"add %0,%0\", operands);
      count--;
    }
  return \"\";
}"
  [(set_attr "cc" "clobber")])

;; Try to combine consecutive updates of the stack pointer (or any
;; other register for that matter).
(define_peephole
  [(set (match_operand:PSI 0 "register_operand" "=da")
	(plus:PSI (match_dup 0)
		  (match_operand 1 "const_int_operand" "")))
   (set (match_dup 0)
	(plus:PSI (match_dup 0)
		  (match_operand 2 "const_int_operand" "")))]
  ""
  "*
{
  operands[1] = GEN_INT (INTVAL (operands[2]) + INTVAL (operands[1]));
  return \"add %1,%0\";
}"
  [(set_attr "cc" "clobber")])

;;
;; We had patterns to check eq/ne, but the they don't work because
;; 0x80000000 + 0x80000000 = 0x0 with a carry out.
;;
;; The Z flag and C flag would be set, and we have no way to
;; check for the Z flag set and C flag clear.
;;
;; This will work on the mn10200 because we can check the ZX flag
;; if the comparison is in HImode.
(define_peephole
  [(set (cc0) (match_operand:HI 0 "register_operand" "d"))
   (set (pc) (if_then_else (ge (cc0) (const_int 0))
			   (match_operand 1 "" "")
			   (pc)))]
  "dead_or_set_p (ins1, operands[0]) && REG_OK_FOR_INDEX_P (operands[0])"
  "add %0,%0\;bcc %1"
  [(set_attr "cc" "clobber")])

(define_peephole
  [(set (cc0) (match_operand:HI 0 "register_operand" "d"))
   (set (pc) (if_then_else (lt (cc0) (const_int 0))
			   (match_operand 1 "" "")
			   (pc)))]
  "dead_or_set_p (ins1, operands[0]) && REG_OK_FOR_INDEX_P (operands[0])"
  "add %0,%0\;bcs %1"
  [(set_attr "cc" "clobber")])

(define_peephole
  [(set (cc0) (match_operand:HI 0 "register_operand" "d"))
   (set (pc) (if_then_else (ge (cc0) (const_int 0))
			   (pc)
			   (match_operand 1 "" "")))]
  "dead_or_set_p (ins1, operands[0]) && REG_OK_FOR_INDEX_P (operands[0])"
  "add %0,%0\;bcs %1"
  [(set_attr "cc" "clobber")])

(define_peephole
  [(set (cc0) (match_operand:HI 0 "register_operand" "d"))
   (set (pc) (if_then_else (lt (cc0) (const_int 0))
			   (pc)
			   (match_operand 1 "" "")))]
  "dead_or_set_p (ins1, operands[0]) && REG_OK_FOR_INDEX_P (operands[0])"
  "add %0,%0\;bcc %1"
  [(set_attr "cc" "clobber")])

(define_peephole
  [(set (cc0) (match_operand:PSI 0 "register_operand" "d"))
   (set (pc) (if_then_else (ge (cc0) (const_int 0))
			   (match_operand 1 "" "")
			   (pc)))]
  "dead_or_set_p (ins1, operands[0]) && REG_OK_FOR_INDEX_P (operands[0])"
  "add %0,%0\;bccx %1"
  [(set_attr "cc" "clobber")])

(define_peephole
  [(set (cc0) (match_operand:PSI 0 "register_operand" "d"))
   (set (pc) (if_then_else (lt (cc0) (const_int 0))
			   (match_operand 1 "" "")
			   (pc)))]
  "dead_or_set_p (ins1, operands[0]) && REG_OK_FOR_INDEX_P (operands[0])"
  "add %0,%0\;bcsx %1"
  [(set_attr "cc" "clobber")])

(define_peephole
  [(set (cc0) (match_operand:PSI 0 "register_operand" "d"))
   (set (pc) (if_then_else (ge (cc0) (const_int 0))
			   (pc)
			   (match_operand 1 "" "")))]
  "dead_or_set_p (ins1, operands[0]) && REG_OK_FOR_INDEX_P (operands[0])"
  "add %0,%0\;bcsx %1"
  [(set_attr "cc" "clobber")])

(define_peephole
  [(set (cc0) (match_operand:PSI 0 "register_operand" "d"))
   (set (pc) (if_then_else (lt (cc0) (const_int 0))
			   (pc)
			   (match_operand 1 "" "")))]
  "dead_or_set_p (ins1, operands[0]) && REG_OK_FOR_INDEX_P (operands[0])"
  "add %0,%0\;bccx %1"
  [(set_attr "cc" "clobber")])

;; We call out to library routines to perform 32bit addition and subtraction
;; operations (see addsi3/subsi3 expanders for why).  These peepholes catch
;; the trivial case where the operation could be done with an add;addc or
;; sub;subc sequence.
(define_peephole
  [(set (mem:SI (reg:PSI 7)) (reg:SI 2))
   (set (reg:SI 0) (call (match_operand:QI 1 "general_operand" "")
			 (match_operand:HI 2 "general_operand" "")))]
  "GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF
   && strcmp (XSTR (XEXP (operands[1], 0), 0), \"__addsi3\") == 0"
  "add d2,d0\;addc d3,d1"
  [(set_attr "cc" "clobber")])

(define_peephole
  [(set (mem:SI (reg:PSI 7)) (reg:SI 2))
   (set (reg:SI 0) (call (match_operand:QI 1 "general_operand" "")
			 (match_operand:HI 2 "general_operand" "")))]
  "GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF
   && strcmp (XSTR (XEXP (operands[1], 0), 0), \"__subsi3\") == 0"
  "sub d2,d0\;subc d3,d1"
  [(set_attr "cc" "clobber")])
