;; GCC machine description for Intel X86.
;; Copyright (C) 1988, 1994, 1995, 1996 Free Software Foundation, Inc.
;; Mostly by William Schelter.

;; 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.

;; Macro #define NOTICE_UPDATE_CC in file i386.h handles condition code
;; updates for most instructions.

;; Macro REG_CLASS_FROM_LETTER in file i386.h defines the register
;; constraint letters.

;; the special asm out single letter directives following a '%' are:
;; 'z' mov%z1 would be movl, movw, or movb depending on the mode of
;;     operands[1].
;; 'L' Print the opcode suffix for a 32-bit integer opcode.
;; 'W' Print the opcode suffix for a 16-bit integer opcode.
;; 'B' Print the opcode suffix for an 8-bit integer opcode.
;; 'S' Print the opcode suffix for a 32-bit float opcode.
;; 'Q' Print the opcode suffix for a 64-bit float opcode.
;; 'T' Print the opcode suffix for an 80-bit extended real XFmode float opcode.
;; 'J' Print the appropriate jump operand.

;; 'b' Print the QImode name of the register for the indicated operand.
;;     %b0 would print %al if operands[0] is reg 0.
;; 'w' Likewise, print the HImode name of the register.
;; 'k' Likewise, print the SImode name of the register.
;; 'h' Print the QImode name for a "high" register, either ah, bh, ch or dh.
;; 'y' Print "st(0)" instead of "st" as a register.

;; UNSPEC usage:
;; 0  This is a `scas' operation.  The mode of the UNSPEC is always SImode.
;;    operand 0 is the memory address to scan.
;;    operand 1 is a register containing the value to scan for.  The mode
;;       of the scas opcode will be the same as the mode of this operand.
;;    operand 2 is the known alignment of operand 0.
;; 1  This is a `sin' operation.  The mode of the UNSPEC is MODE_FLOAT.
;;    operand 0 is the argument for `sin'.
;; 2  This is a `cos' operation.  The mode of the UNSPEC is MODE_FLOAT.
;;    operand 0 is the argument for `cos'.

;; This shadows the processor_type enumeration, so changes must be made
;; to i386.h at the same time.

(define_attr "type" "integer,fld,fpop,fpdiv"
  (const_string "integer"))

;; Functional units

;; Floating point

(define_function_unit "fp" 1 0
 (and (eq_attr "type" "fpop") (eq_attr "cpu" "!pentium"))
 5 5)

(define_function_unit "fp" 1 0
 (and (eq_attr "type" "fpop") (eq_attr "cpu" "pentium")) 
 3 0)

(define_function_unit "fp" 1 0
 (eq_attr "type" "fpdiv") 
 10 10)

(define_function_unit "fp" 1 0
 (eq_attr "type" "fld") 
 1 0)

(define_function_unit "integer" 1 0
  (and (eq_attr "type" "integer") (eq_attr "cpu" "!i386"))
 2 0)


;; "movl MEM,REG / testl REG,REG" is faster on a 486 than "cmpl $0,MEM".
;; But restricting MEM here would mean that gcc could not remove a redundant
;; test in cases like "incl MEM / je TARGET".
;;
;; We don't want to allow a constant operand for test insns because
;; (set (cc0) (const_int foo)) has no mode information.  Such insns will
;; be folded while optimizing anyway.

;; All test insns have expanders that save the operands away without
;; actually generating RTL.  The bCOND or sCOND (emitted immediately
;; after the tstM or cmp) will actually emit the tstM or cmpM.

;; Processor type -- this attribute must exactly match the processor_type
;; enumeration in rs6000.h.

(define_attr "cpu" "i386,i486,pentium"
  (const (symbol_ref "ix86_cpu")))

(define_insn "tstsi_1"
  [(set (cc0)
	(match_operand:SI 0 "nonimmediate_operand" "rm"))]
  ""
  "*
{
  if (REG_P (operands[0]))
    return AS2 (test%L0,%0,%0);

  operands[1] = const0_rtx;
  return AS2 (cmp%L0,%1,%0);
}")

(define_expand "tstsi"
  [(set (cc0)
	(match_operand:SI 0 "nonimmediate_operand" ""))]
  ""
  "
{
  i386_compare_gen = gen_tstsi_1;
  i386_compare_op0 = operands[0];
  DONE;
}")

(define_insn "tsthi_1"
  [(set (cc0)
	(match_operand:HI 0 "nonimmediate_operand" "rm"))]
  ""
  "*
{
  if (REG_P (operands[0]))
    return AS2 (test%W0,%0,%0);

  operands[1] = const0_rtx;
  return AS2 (cmp%W0,%1,%0);
}")

(define_expand "tsthi"
  [(set (cc0)
	(match_operand:HI 0 "nonimmediate_operand" ""))]
  ""
  "
{
  i386_compare_gen = gen_tsthi_1;
  i386_compare_op0 = operands[0];
  DONE;
}")

(define_insn "tstqi_1"
  [(set (cc0)
	(match_operand:QI 0 "nonimmediate_operand" "qm"))]
  ""
  "*
{
  if (REG_P (operands[0]))
    return AS2 (test%B0,%0,%0);

  operands[1] = const0_rtx;
  return AS2 (cmp%B0,%1,%0);
}")

(define_expand "tstqi"
  [(set (cc0)
	(match_operand:QI 0 "nonimmediate_operand" ""))]
  ""
  "
{
  i386_compare_gen = gen_tstqi_1;
  i386_compare_op0 = operands[0];
  DONE;
}")

(define_insn "tstsf_cc"
  [(set (cc0)
	(match_operand:SF 0 "register_operand" "f"))
   (clobber (match_scratch:HI 1 "=a"))]
  "TARGET_80387 && ! TARGET_IEEE_FP"
  "*
{
  if (! STACK_TOP_P (operands[0]))
    abort ();

  output_asm_insn (\"ftst\", operands);

  if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
    output_asm_insn (AS1 (fstp,%y0), operands);

  return output_fp_cc0_set (insn);
}")

;; Don't generate tstsf if generating IEEE code, since the `ftst' opcode
;; isn't IEEE compliant.

(define_expand "tstsf"
  [(parallel [(set (cc0)
		   (match_operand:SF 0 "register_operand" ""))
	      (clobber (match_scratch:HI 1 ""))])]
  "TARGET_80387 && ! TARGET_IEEE_FP"
  "
{
  i386_compare_gen = gen_tstsf_cc;
  i386_compare_op0 = operands[0];
  DONE;
}")

(define_insn "tstdf_cc"
  [(set (cc0)
	(match_operand:DF 0 "register_operand" "f"))
   (clobber (match_scratch:HI 1 "=a"))]
  "TARGET_80387 && ! TARGET_IEEE_FP"
  "*
{
  if (! STACK_TOP_P (operands[0]))
    abort ();

  output_asm_insn (\"ftst\", operands);

  if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
    output_asm_insn (AS1 (fstp,%y0), operands);

  return output_fp_cc0_set (insn);
}")

;; Don't generate tstdf if generating IEEE code, since the `ftst' opcode
;; isn't IEEE compliant.

(define_expand "tstdf"
  [(parallel [(set (cc0)
		   (match_operand:DF 0 "register_operand" ""))
	      (clobber (match_scratch:HI 1 ""))])]
  "TARGET_80387 && ! TARGET_IEEE_FP"
  "
{
  i386_compare_gen = gen_tstdf_cc;
  i386_compare_op0 = operands[0];
  DONE;
}")

(define_insn "tstxf_cc"
  [(set (cc0)
	(match_operand:XF 0 "register_operand" "f"))
   (clobber (match_scratch:HI 1 "=a"))]
  "TARGET_80387 && ! TARGET_IEEE_FP"
  "*
{
  if (! STACK_TOP_P (operands[0]))
    abort ();

  output_asm_insn (\"ftst\", operands);

  if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
    output_asm_insn (AS1 (fstp,%y0), operands);

  return output_fp_cc0_set (insn);
}")

;; Don't generate tstxf if generating IEEE code, since the `ftst' opcode
;; isn't IEEE compliant.

(define_expand "tstxf"
  [(parallel [(set (cc0)
		   (match_operand:XF 0 "register_operand" ""))
	      (clobber (match_scratch:HI 1 ""))])]
  "TARGET_80387 && ! TARGET_IEEE_FP"
  "
{
  i386_compare_gen = gen_tstxf_cc;
  i386_compare_op0 = operands[0];
  DONE;
}")

;;- compare instructions.  See comments above tstM patterns about
;;  expansion of these insns.

(define_insn "cmpsi_1"
  [(set (cc0)
	(compare (match_operand:SI 0 "nonimmediate_operand" "mr,r")
		 (match_operand:SI 1 "general_operand" "ri,mr")))]
  "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
  "*
{
  if (CONSTANT_P (operands[0]) || GET_CODE (operands[1]) == MEM)
    {
      cc_status.flags |= CC_REVERSED;
      return AS2 (cmp%L0,%0,%1);
    }
  return AS2 (cmp%L0,%1,%0);
}")

(define_expand "cmpsi"
  [(set (cc0)
	(compare (match_operand:SI 0 "nonimmediate_operand" "")
		 (match_operand:SI 1 "general_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
    operands[0] = force_reg (SImode, operands[0]);

  i386_compare_gen = gen_cmpsi_1;
  i386_compare_op0 = operands[0];
  i386_compare_op1 = operands[1];
  DONE;
}")

(define_insn "cmphi_1"
  [(set (cc0)
	(compare (match_operand:HI 0 "nonimmediate_operand" "mr,r")
		 (match_operand:HI 1 "general_operand" "ri,mr")))]
  "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
  "*
{
  if (CONSTANT_P (operands[0]) || GET_CODE (operands[1]) == MEM)
    {
      cc_status.flags |= CC_REVERSED;
      return AS2 (cmp%W0,%0,%1);
    }
  return AS2 (cmp%W0,%1,%0);
}")

(define_expand "cmphi"
  [(set (cc0)
	(compare (match_operand:HI 0 "nonimmediate_operand" "")
		 (match_operand:HI 1 "general_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
    operands[0] = force_reg (HImode, operands[0]);

  i386_compare_gen = gen_cmphi_1;
  i386_compare_op0 = operands[0];
  i386_compare_op1 = operands[1];
  DONE;
}")

(define_insn "cmpqi_1"
  [(set (cc0)
	(compare (match_operand:QI 0 "nonimmediate_operand" "q,mq")
		 (match_operand:QI 1 "general_operand" "qm,nq")))]
  "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
  "*
{
  if (CONSTANT_P (operands[0]) || GET_CODE (operands[1]) == MEM)
    {
      cc_status.flags |= CC_REVERSED;
      return AS2 (cmp%B0,%0,%1);
    }
  return AS2 (cmp%B0,%1,%0);
}")

(define_expand "cmpqi"
  [(set (cc0)
	(compare (match_operand:QI 0 "nonimmediate_operand" "")
		 (match_operand:QI 1 "general_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
    operands[0] = force_reg (QImode, operands[0]);

  i386_compare_gen = gen_cmpqi_1;
  i386_compare_op0 = operands[0];
  i386_compare_op1 = operands[1];
  DONE;
}")

;; These implement float point compares.  For each of DFmode and
;; SFmode, there is the normal insn, and an insn where the second operand
;; is converted to the desired mode.

(define_insn ""
  [(set (cc0)
	(match_operator 2 "VOIDmode_compare_op"
			[(match_operand:XF 0 "register_operand" "f")
			 (match_operand:XF 1 "register_operand" "f")]))
   (clobber (match_scratch:HI 3 "=a"))]
  "TARGET_80387"
  "* return output_float_compare (insn, operands);")

(define_insn ""
  [(set (cc0)
	(match_operator 2 "VOIDmode_compare_op"
			[(match_operand:XF 0 "register_operand" "f")
			 (float:XF
			  (match_operand:SI 1 "nonimmediate_operand" "rm"))]))
   (clobber (match_scratch:HI 3 "=a"))]
  "TARGET_80387"
  "* return output_float_compare (insn, operands);")

(define_insn ""
  [(set (cc0)
	(match_operator 2 "VOIDmode_compare_op"
			[(float:XF
			  (match_operand:SI 0 "nonimmediate_operand" "rm"))
			 (match_operand:XF 1 "register_operand" "f")]))
   (clobber (match_scratch:HI 3 "=a"))]
  "TARGET_80387"
  "* return output_float_compare (insn, operands);")

(define_insn ""
  [(set (cc0)
	(match_operator 2 "VOIDmode_compare_op"
			[(match_operand:XF 0 "register_operand" "f")
			 (float_extend:XF
			  (match_operand:DF 1 "nonimmediate_operand" "fm"))]))
   (clobber (match_scratch:HI 3 "=a"))]
  "TARGET_80387"
  "* return output_float_compare (insn, operands);")

(define_insn ""
  [(set (cc0)
	(match_operator 2 "VOIDmode_compare_op"
			[(match_operand:XF 0 "register_operand" "f")
			 (float_extend:XF
			  (match_operand:SF 1 "nonimmediate_operand" "fm"))]))
   (clobber (match_scratch:HI 3 "=a"))]
  "TARGET_80387"
  "* return output_float_compare (insn, operands);")

(define_insn ""
  [(set (cc0)
	(compare:CCFPEQ (match_operand:XF 0 "register_operand" "f")
			(match_operand:XF 1 "register_operand" "f")))
   (clobber (match_scratch:HI 2 "=a"))]
  "TARGET_80387"
  "* return output_float_compare (insn, operands);")

(define_insn ""
  [(set (cc0)
	(match_operator 2 "VOIDmode_compare_op"
			[(match_operand:DF 0 "nonimmediate_operand" "f,fm")
			 (match_operand:DF 1 "nonimmediate_operand" "fm,f")]))
   (clobber (match_scratch:HI 3 "=a,a"))]
  "TARGET_80387
   && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)"
  "* return output_float_compare (insn, operands);")

(define_insn ""
  [(set (cc0)
	(match_operator 2 "VOIDmode_compare_op"
			[(match_operand:DF 0 "register_operand" "f")
			 (float:DF
			  (match_operand:SI 1 "nonimmediate_operand" "rm"))]))
   (clobber (match_scratch:HI 3 "=a"))]
  "TARGET_80387"
  "* return output_float_compare (insn, operands);")

(define_insn ""
  [(set (cc0)
	(match_operator 2 "VOIDmode_compare_op"
			[(float:DF
			  (match_operand:SI 0 "nonimmediate_operand" "rm"))
			 (match_operand:DF 1 "register_operand" "f")]))
   (clobber (match_scratch:HI 3 "=a"))]
  "TARGET_80387"
  "* return output_float_compare (insn, operands);")

(define_insn ""
  [(set (cc0)
	(match_operator 2 "VOIDmode_compare_op"
			[(match_operand:DF 0 "register_operand" "f")
			 (float_extend:DF
			  (match_operand:SF 1 "nonimmediate_operand" "fm"))]))
   (clobber (match_scratch:HI 3 "=a"))]
  "TARGET_80387"
  "* return output_float_compare (insn, operands);")

(define_insn ""
  [(set (cc0)
	(match_operator 2 "VOIDmode_compare_op"
			[(float_extend:DF
			  (match_operand:SF 0 "nonimmediate_operand" "fm"))
			 (match_operand:DF 1 "register_operand" "f")]))
   (clobber (match_scratch:HI 3 "=a"))]
  "TARGET_80387"
  "* return output_float_compare (insn, operands);")

(define_insn ""
  [(set (cc0)
	(match_operator 2 "VOIDmode_compare_op"
			[(float_extend:DF
			  (match_operand:SF 0 "register_operand" "f"))
			 (match_operand:DF 1 "nonimmediate_operand" "fm")]))
   (clobber (match_scratch:HI 3 "=a"))]
  "TARGET_80387"
  "* return output_float_compare (insn, operands);")

(define_insn ""
  [(set (cc0)
	(compare:CCFPEQ (match_operand:DF 0 "register_operand" "f")
			(match_operand:DF 1 "register_operand" "f")))
   (clobber (match_scratch:HI 2 "=a"))]
  "TARGET_80387"
  "* return output_float_compare (insn, operands);")

;; These two insns will never be generated by combine due to the mode of
;; the COMPARE.
;(define_insn ""
;  [(set (cc0)
;	(compare:CCFPEQ (match_operand:DF 0 "register_operand" "f")
;			(float_extend:DF
;			 (match_operand:SF 1 "register_operand" "f"))))
;   (clobber (match_scratch:HI 2 "=a"))]
;  "TARGET_80387"
;  "* return output_float_compare (insn, operands);")
;
;(define_insn ""
;  [(set (cc0)
;	(compare:CCFPEQ (float_extend:DF
;			 (match_operand:SF 0 "register_operand" "f"))
;			(match_operand:DF 1 "register_operand" "f")))
;   (clobber (match_scratch:HI 2 "=a"))]
;  "TARGET_80387"
;  "* return output_float_compare (insn, operands);")

(define_insn "cmpsf_cc_1"
  [(set (cc0)
	(match_operator 2 "VOIDmode_compare_op"
			[(match_operand:SF 0 "nonimmediate_operand" "f,fm")
			 (match_operand:SF 1 "nonimmediate_operand" "fm,f")]))
   (clobber (match_scratch:HI 3 "=a,a"))]
  "TARGET_80387
   && (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)"
  "* return output_float_compare (insn, operands);")

(define_insn ""
  [(set (cc0)
	(match_operator 2 "VOIDmode_compare_op"
			[(match_operand:SF 0 "register_operand" "f")
			 (float:SF
			  (match_operand:SI 1 "nonimmediate_operand" "rm"))]))
   (clobber (match_scratch:HI 3 "=a"))]
  "TARGET_80387"
  "* return output_float_compare (insn, operands);")

(define_insn ""
  [(set (cc0)
	(match_operator 2 "VOIDmode_compare_op"
			[(float:SF
			  (match_operand:SI 0 "nonimmediate_operand" "rm"))
			 (match_operand:SF 1 "register_operand" "f")]))
   (clobber (match_scratch:HI 3 "=a"))]
  "TARGET_80387"
  "* return output_float_compare (insn, operands);")

(define_insn ""
  [(set (cc0)
	(compare:CCFPEQ (match_operand:SF 0 "register_operand" "f")
			(match_operand:SF 1 "register_operand" "f")))
   (clobber (match_scratch:HI 2 "=a"))]
  "TARGET_80387"
  "* return output_float_compare (insn, operands);")

(define_expand "cmpxf"
  [(set (cc0)
	(compare (match_operand:XF 0 "register_operand" "")
		 (match_operand:XF 1 "register_operand" "")))]
  "TARGET_80387"
  "
{
  i386_compare_gen = gen_cmpxf_cc;
  i386_compare_gen_eq = gen_cmpxf_ccfpeq;
  i386_compare_op0 = operands[0];
  i386_compare_op1 = operands[1];
  DONE;
}")

(define_expand "cmpdf"
  [(set (cc0)
	(compare (match_operand:DF 0 "register_operand" "")
		 (match_operand:DF 1 "general_operand" "")))]
  "TARGET_80387"
  "
{
  i386_compare_gen = gen_cmpdf_cc;
  i386_compare_gen_eq = gen_cmpdf_ccfpeq;
  i386_compare_op0 = operands[0];
  i386_compare_op1 = (immediate_operand (operands[1], DFmode))
			? copy_to_mode_reg (DFmode, operands[1]) : operands[1];
  DONE;
}")

(define_expand "cmpsf"
  [(set (cc0)
	(compare (match_operand:SF 0 "register_operand" "")
		 (match_operand:SF 1 "general_operand" "")))]
  "TARGET_80387"
  "
{
  i386_compare_gen = gen_cmpsf_cc;
  i386_compare_gen_eq = gen_cmpsf_ccfpeq;
  i386_compare_op0 = operands[0];
  i386_compare_op1 = (immediate_operand (operands[1], SFmode))
			? copy_to_mode_reg (SFmode, operands[1]) : operands[1];
  DONE;
}")

(define_expand "cmpxf_cc"
  [(parallel [(set (cc0)
		   (compare (match_operand:XF 0 "register_operand" "")
			    (match_operand:XF 1 "register_operand" "")))
	      (clobber (match_scratch:HI 2 ""))])]
  "TARGET_80387"
  "")

(define_expand "cmpxf_ccfpeq"
  [(parallel [(set (cc0)
		   (compare:CCFPEQ (match_operand:XF 0 "register_operand" "")
				   (match_operand:XF 1 "register_operand" "")))
	      (clobber (match_scratch:HI 2 ""))])]
  "TARGET_80387"
  "")

(define_expand "cmpdf_cc"
  [(parallel [(set (cc0)
		   (compare (match_operand:DF 0 "register_operand" "")
			    (match_operand:DF 1 "register_operand" "")))
	      (clobber (match_scratch:HI 2 ""))])]
  "TARGET_80387"
  "")

(define_expand "cmpdf_ccfpeq"
  [(parallel [(set (cc0)
		   (compare:CCFPEQ (match_operand:DF 0 "register_operand" "")
				   (match_operand:DF 1 "register_operand" "")))
	      (clobber (match_scratch:HI 2 ""))])]
  "TARGET_80387"
  "
{
  if (! register_operand (operands[1], DFmode))
    operands[1] = copy_to_mode_reg (DFmode, operands[1]);
}")

(define_expand "cmpsf_cc"
  [(parallel [(set (cc0)
		   (compare (match_operand:SF 0 "register_operand" "")
			    (match_operand:SF 1 "register_operand" "")))
	      (clobber (match_scratch:HI 2 ""))])]
  "TARGET_80387"
  "")

(define_expand "cmpsf_ccfpeq"
  [(parallel [(set (cc0)
		   (compare:CCFPEQ (match_operand:SF 0 "register_operand" "")
				   (match_operand:SF 1 "register_operand" "")))
	      (clobber (match_scratch:HI 2 ""))])]
  "TARGET_80387"
  "
{
  if (! register_operand (operands[1], SFmode))
    operands[1] = copy_to_mode_reg (SFmode, operands[1]);
}")

;; logical compare

(define_insn ""
  [(set (cc0)
	(and:SI (match_operand:SI 0 "general_operand" "%ro")
		(match_operand:SI 1 "nonmemory_operand" "ri")))]
  ""
  "*
{
  /* For small integers, we may actually use testb. */
  if (GET_CODE (operands[1]) == CONST_INT
      && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))
      && (! REG_P (operands[0]) || QI_REG_P (operands[0])))
    {
      /* We may set the sign bit spuriously.  */

      if ((INTVAL (operands[1]) & ~0xff) == 0)
        {
	  cc_status.flags |= CC_NOT_NEGATIVE;
	  return AS2 (test%B0,%1,%b0);
	}

      if ((INTVAL (operands[1]) & ~0xff00) == 0)
        {
	  cc_status.flags |= CC_NOT_NEGATIVE;
	  operands[1] = GEN_INT (INTVAL (operands[1]) >> 8);

	  if (QI_REG_P (operands[0]))
	    return AS2 (test%B0,%1,%h0);
	  else
	    {
	      operands[0] = adj_offsettable_operand (operands[0], 1);
	      return AS2 (test%B0,%1,%b0);
	    }
	}

      if (GET_CODE (operands[0]) == MEM
	  && (INTVAL (operands[1]) & ~0xff0000) == 0)
        {
	  cc_status.flags |= CC_NOT_NEGATIVE;
	  operands[1] = GEN_INT (INTVAL (operands[1]) >> 16);
	  operands[0] = adj_offsettable_operand (operands[0], 2);
	  return AS2 (test%B0,%1,%b0);
	}

      if (GET_CODE (operands[0]) == MEM
	  && (INTVAL (operands[1]) & ~0xff000000) == 0)
        {
	  operands[1] = GEN_INT ((INTVAL (operands[1]) >> 24) & 0xff);
	  operands[0] = adj_offsettable_operand (operands[0], 3);
	  return AS2 (test%B0,%1,%b0);
	}
    }

  if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM)
    return AS2 (test%L0,%1,%0);

  return AS2 (test%L1,%0,%1);
}")

(define_insn ""
  [(set (cc0)
	(and:HI (match_operand:HI 0 "general_operand" "%ro")
		(match_operand:HI 1 "nonmemory_operand" "ri")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == CONST_INT
      && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))
      && (! REG_P (operands[0]) || QI_REG_P (operands[0])))
    {
      if ((INTVAL (operands[1]) & 0xff00) == 0)
	{
	  /* ??? This might not be necessary. */
	  if (INTVAL (operands[1]) & 0xffff0000)
	    operands[1] = GEN_INT (INTVAL (operands[1]) & 0xff);

	  /* We may set the sign bit spuriously.  */
	  cc_status.flags |= CC_NOT_NEGATIVE;
	  return AS2 (test%B0,%1,%b0);
	}

      if ((INTVAL (operands[1]) & 0xff) == 0)
        {
	  operands[1] = GEN_INT ((INTVAL (operands[1]) >> 8) & 0xff);

	  if (QI_REG_P (operands[0]))
	    return AS2 (test%B0,%1,%h0);
	  else
	    {
	      operands[0] = adj_offsettable_operand (operands[0], 1);
	      return AS2 (test%B0,%1,%b0);
	    }
	}
    }

  if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM)
    return AS2 (test%W0,%1,%0);

  return AS2 (test%W1,%0,%1);
}")

(define_insn ""
  [(set (cc0)
	(and:QI (match_operand:QI 0 "nonimmediate_operand" "%qm")
		(match_operand:QI 1 "nonmemory_operand" "qi")))]
  ""
  "*
{
  if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM)
    return AS2 (test%B0,%1,%0);

  return AS2 (test%B1,%0,%1);
}")

;; move instructions.
;; There is one for each machine mode,
;; and each is preceded by a corresponding push-insn pattern
;; (since pushes are not general_operands on the 386).

(define_insn ""
  [(set (match_operand:SI 0 "push_operand" "=<")
	(match_operand:SI 1 "general_operand" "g"))]
  "TARGET_PUSH_MEMORY"
  "push%L0 %1")

;; If not a 386, it is faster to move MEM to a REG and then push, rather than
;; push MEM directly.

(define_insn ""
  [(set (match_operand:SI 0 "push_operand" "=<")
	(match_operand:SI 1 "nonmemory_operand" "ri"))]
  "!TARGET_PUSH_MEMORY && TARGET_MOVE"
  "push%L0 %1")

(define_insn ""
  [(set (match_operand:SI 0 "push_operand" "=<")
	(match_operand:SI 1 "nonmemory_operand" "ri"))]
  "!TARGET_PUSH_MEMORY && !TARGET_MOVE"
  "push%L0 %1")

;; General case of fullword move.

;; If generating PIC code and operands[1] is a symbolic CONST, emit a
;; move to get the address of the symbolic object from the GOT.

(define_expand "movsi"
  [(set (match_operand:SI 0 "general_operand" "")
	(match_operand:SI 1 "general_operand" ""))]
  ""
  "
{
  extern int flag_pic;

  if (flag_pic && SYMBOLIC_CONST (operands[1]))
    emit_pic_move (operands, SImode);

  /* Don't generate memory->memory moves, go through a register */
  else if (TARGET_MOVE
	   && (reload_in_progress | reload_completed) == 0
	   && GET_CODE (operands[0]) == MEM
	   && GET_CODE (operands[1]) == MEM)
    {
      operands[1] = force_reg (SImode, operands[1]);
    }
}")

;; On i486, incl reg is faster than movl $1,reg.

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=g,r")
	(match_operand:SI 1 "general_operand" "ri,m"))]
  "(!TARGET_MOVE || GET_CODE (operands[0]) != MEM) || (GET_CODE (operands[1]) != MEM)"
  "*
{
  rtx link;
  if (operands[1] == const0_rtx && REG_P (operands[0]))
    return AS2 (xor%L0,%0,%0);

  if (operands[1] == const1_rtx
      && (link = find_reg_note (insn, REG_WAS_0, 0))
      /* Make sure the insn that stored the 0 is still present.  */
      && ! INSN_DELETED_P (XEXP (link, 0))
      && GET_CODE (XEXP (link, 0)) != NOTE
      /* Make sure cross jumping didn't happen here.  */
      && no_labels_between_p (XEXP (link, 0), insn)
      /* Make sure the reg hasn't been clobbered.  */
      && ! reg_set_between_p (operands[0], XEXP (link, 0), insn))
    /* Fastest way to change a 0 to a 1.  */
    return AS1 (inc%L0,%0);

  if (flag_pic && SYMBOLIC_CONST (operands[1]))
    return AS2 (lea%L0,%a1,%0);

  return AS2 (mov%L0,%1,%0);
}")

(define_insn ""
  [(set (match_operand:HI 0 "push_operand" "=<")
	(match_operand:HI 1 "general_operand" "g"))]
  "TARGET_PUSH_MEMORY"
  "push%W0 %1")

(define_insn ""
  [(set (match_operand:HI 0 "push_operand" "=<")
	(match_operand:HI 1 "nonmemory_operand" "ri"))]
  "!TARGET_PUSH_MEMORY && TARGET_MOVE"
  "push%W0 %1")

(define_insn ""
  [(set (match_operand:HI 0 "push_operand" "=<")
	(match_operand:HI 1 "nonmemory_operand" "ri"))]
  "!TARGET_PUSH_MEMORY && !TARGET_MOVE"
  "push%W0 %1")

;; On i486, an incl and movl are both faster than incw and movw.

(define_expand "movhi"
  [(set (match_operand:HI 0 "general_operand" "")
	(match_operand:HI 1 "general_operand" ""))]
  ""
  "
{
  /* Don't generate memory->memory moves, go through a register */
  if (TARGET_MOVE
      && (reload_in_progress | reload_completed) == 0
      && GET_CODE (operands[0]) == MEM
      && GET_CODE (operands[1]) == MEM)
    {
      operands[1] = force_reg (HImode, operands[1]);
    }
}")

(define_insn ""
  [(set (match_operand:HI 0 "general_operand" "=g,r")
	(match_operand:HI 1 "general_operand" "ri,m"))]
  "(!TARGET_MOVE || GET_CODE (operands[0]) != MEM) || (GET_CODE (operands[1]) != MEM)"
  "*
{
  rtx link;
  if (REG_P (operands[0]) && operands[1] == const0_rtx)
    return AS2 (xor%L0,%k0,%k0);

  if (REG_P (operands[0]) && operands[1] == const1_rtx 
      && (link = find_reg_note (insn, REG_WAS_0, 0))
      /* Make sure the insn that stored the 0 is still present.  */
      && ! INSN_DELETED_P (XEXP (link, 0))
      && GET_CODE (XEXP (link, 0)) != NOTE
      /* Make sure cross jumping didn't happen here.  */
      && no_labels_between_p (XEXP (link, 0), insn)
      /* Make sure the reg hasn't been clobbered.  */
      && ! reg_set_between_p (operands[0], XEXP (link, 0), insn))
    /* Fastest way to change a 0 to a 1.  */
    return AS1 (inc%L0,%k0);

  if (REG_P (operands[0]))
    {
      if (REG_P (operands[1]))
	return AS2 (mov%L0,%k1,%k0);
      else if (CONSTANT_P (operands[1]))
	return AS2 (mov%L0,%1,%k0);
    }

  return AS2 (mov%W0,%1,%0);
}")

(define_expand "movstricthi"
  [(set (strict_low_part (match_operand:HI 0 "general_operand" ""))
	(match_operand:HI 1 "general_operand" ""))]
  ""
  "
{
  /* Don't generate memory->memory moves, go through a register */
  if (TARGET_MOVE
      && (reload_in_progress | reload_completed) == 0
      && GET_CODE (operands[0]) == MEM
      && GET_CODE (operands[1]) == MEM)
    {
      operands[1] = force_reg (HImode, operands[1]);
    }
}")

(define_insn ""
  [(set (strict_low_part (match_operand:HI 0 "general_operand" "+g,r"))
	(match_operand:HI 1 "general_operand" "ri,m"))]
  "(!TARGET_MOVE || GET_CODE (operands[0]) != MEM) || (GET_CODE (operands[1]) != MEM)"
  "*
{
  rtx link;
  if (operands[1] == const0_rtx && REG_P (operands[0]))
    return AS2 (xor%W0,%0,%0);

  if (operands[1] == const1_rtx
      && (link = find_reg_note (insn, REG_WAS_0, 0))
      /* Make sure the insn that stored the 0 is still present.  */
      && ! INSN_DELETED_P (XEXP (link, 0))
      && GET_CODE (XEXP (link, 0)) != NOTE
      /* Make sure cross jumping didn't happen here.  */
      && no_labels_between_p (XEXP (link, 0), insn)
      /* Make sure the reg hasn't been clobbered.  */
      && ! reg_set_between_p (operands[0], XEXP (link, 0), insn))
    /* Fastest way to change a 0 to a 1.  */
    return AS1 (inc%W0,%0);

  return AS2 (mov%W0,%1,%0);
}")

;; emit_push_insn when it calls move_by_pieces
;; requires an insn to "push a byte".
;; But actually we use pushw, which has the effect of rounding
;; the amount pushed up to a halfword.
(define_insn ""
  [(set (match_operand:QI 0 "push_operand" "=<")
	(match_operand:QI 1 "const_int_operand" "n"))]
  ""
  "* return AS1 (push%W0,%1);")

(define_insn ""
  [(set (match_operand:QI 0 "push_operand" "=<")
	(match_operand:QI 1 "register_operand" "q"))]
  "!TARGET_MOVE"
  "*
{
  operands[1] = gen_rtx (REG, HImode, REGNO (operands[1]));
  return AS1 (push%W0,%1);
}")

(define_insn ""
  [(set (match_operand:QI 0 "push_operand" "=<")
	(match_operand:QI 1 "register_operand" "q"))]
  "TARGET_MOVE"
  "*
{
  operands[1] = gen_rtx (REG, HImode, REGNO (operands[1]));
  return AS1 (push%W0,%1);
}")

;; On i486, incb reg is faster than movb $1,reg.

;; ??? Do a recognizer for zero_extract that looks just like this, but reads
;; or writes %ah, %bh, %ch, %dh.

(define_expand "movqi"
  [(set (match_operand:QI 0 "general_operand" "")
	(match_operand:QI 1 "general_operand" ""))]
  ""
  "
{
  /* Don't generate memory->memory moves, go through a register */
  if (TARGET_MOVE
      && (reload_in_progress | reload_completed) == 0
      && GET_CODE (operands[0]) == MEM
      && GET_CODE (operands[1]) == MEM)
    {
      operands[1] = force_reg (QImode, operands[1]);
    }
}")

(define_insn ""
  [(set (match_operand:QI 0 "nonimmediate_operand" "=q,*r,qm")
	(match_operand:QI 1 "general_operand" "*g,q,qn"))]
  "(!TARGET_MOVE || GET_CODE (operands[0]) != MEM) || (GET_CODE (operands[1]) != MEM)"
  "*
{
  rtx link;
  if (operands[1] == const0_rtx && REG_P (operands[0]))
    return AS2 (xor%B0,%0,%0);

  if (operands[1] == const1_rtx
      && (link = find_reg_note (insn, REG_WAS_0, 0))
      /* Make sure the insn that stored the 0 is still present.  */
      && ! INSN_DELETED_P (XEXP (link, 0))
      && GET_CODE (XEXP (link, 0)) != NOTE
      /* Make sure cross jumping didn't happen here.  */
      && no_labels_between_p (XEXP (link, 0), insn)
      /* Make sure the reg hasn't been clobbered.  */
      && ! reg_set_between_p (operands[0], XEXP (link, 0), insn))
    /* Fastest way to change a 0 to a 1.  */
    return AS1 (inc%B0,%0);

  /* If mov%B0 isn't allowed for one of these regs, use mov%L0.  */
  if (NON_QI_REG_P (operands[0]) || NON_QI_REG_P (operands[1]))
    return (AS2 (mov%L0,%k1,%k0));

  return (AS2 (mov%B0,%1,%0));
}")

;; If it becomes necessary to support movstrictqi into %esi or %edi,
;; use the insn sequence:
;;
;;	shrdl $8,srcreg,dstreg
;;	rorl $24,dstreg
;;
;; If operands[1] is a constant, then an andl/orl sequence would be
;; faster.

(define_expand "movstrictqi"
  [(set (strict_low_part (match_operand:QI 0 "general_operand" ""))
	(match_operand:QI 1 "general_operand" ""))]
  ""
  "
{
  /* Don't generate memory->memory moves, go through a register */
  if (TARGET_MOVE
      && (reload_in_progress | reload_completed) == 0
      && GET_CODE (operands[0]) == MEM
      && GET_CODE (operands[1]) == MEM)
    {
      operands[1] = force_reg (QImode, operands[1]);
    }
}")

(define_insn ""
  [(set (strict_low_part (match_operand:QI 0 "nonimmediate_operand" "+qm,q"))
	(match_operand:QI 1 "general_operand" "*qn,m"))]
  "(!TARGET_MOVE || GET_CODE (operands[0]) != MEM) || (GET_CODE (operands[1]) != MEM)"
  "*
{
  rtx link;
  if (operands[1] == const0_rtx && REG_P (operands[0]))
    return AS2 (xor%B0,%0,%0);

  if (operands[1] == const1_rtx
      && (link = find_reg_note (insn, REG_WAS_0, 0))
      /* Make sure the insn that stored the 0 is still present.  */
      && ! INSN_DELETED_P (XEXP (link, 0))
      && GET_CODE (XEXP (link, 0)) != NOTE
      /* Make sure cross jumping didn't happen here.  */
      && no_labels_between_p (XEXP (link, 0), insn)
      /* Make sure the reg hasn't been clobbered.  */
      && ! reg_set_between_p (operands[0], XEXP (link, 0), insn))
    /* Fastest way to change a 0 to a 1.  */
    return AS1 (inc%B0,%0);

  /* If mov%B0 isn't allowed for one of these regs, use mov%L0.  */
  if (NON_QI_REG_P (operands[0]) || NON_QI_REG_P (operands[1]))
    {
      abort ();
      return (AS2 (mov%L0,%k1,%k0));
    }

  return AS2 (mov%B0,%1,%0);
}")

(define_expand "movsf"
  [(set (match_operand:SF 0 "general_operand" "")
	(match_operand:SF 1 "general_operand" ""))]
  ""
  "
{
  /* Special case memory->memory moves and pushes */
  if (TARGET_MOVE
      && (reload_in_progress | reload_completed) == 0
      && GET_CODE (operands[0]) == MEM
      && (GET_CODE (operands[1]) == MEM || push_operand (operands[0], SFmode)))
    {
      rtx (*genfunc) PROTO((rtx, rtx)) = (push_operand (operands[0], SFmode))
						? gen_movsf_push
						: gen_movsf_mem;

      emit_insn ((*genfunc) (operands[0], operands[1]));
      DONE;
    }

  /* If we are loading a floating point constant that isn't 0 or 1
     into a register, indicate we need the pic register loaded.  This could
     be optimized into stores of constants if the target eventually moves
     to memory, but better safe than sorry.  */
  if ((reload_in_progress | reload_completed) == 0
      && GET_CODE (operands[0]) != MEM
      && GET_CODE (operands[1]) == CONST_DOUBLE
      && !standard_80387_constant_p (operands[1]))
    {
      rtx insn, note, fp_const;

      fp_const = force_const_mem (SFmode, operands[1]);
      if (flag_pic)
	current_function_uses_pic_offset_table = 1;

      insn = emit_insn (gen_rtx (SET, SFmode, operands[0], fp_const));
      note = find_reg_note (insn, REG_EQUAL, NULL_RTX);

      if (note)
	XEXP (note, 0) = operands[1];
      else
	REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUAL, operands[1], REG_NOTES (insn));
    }
}")

(define_insn "movsf_push_nomove"
  [(set (match_operand:SF 0 "push_operand" "=<,<")
	(match_operand:SF 1 "general_operand" "gF,f"))]
  "!TARGET_MOVE"
  "*
{
  if (STACK_REG_P (operands[1]))
    {
      rtx xops[3];

      if (! STACK_TOP_P (operands[1]))
        abort ();

      xops[0] = AT_SP (SFmode);
      xops[1] = GEN_INT (4);
      xops[2] = stack_pointer_rtx;

      output_asm_insn (AS2 (sub%L2,%1,%2), xops);

      if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
        output_asm_insn (AS1 (fstp%S0,%0), xops);
      else
        output_asm_insn (AS1 (fst%S0,%0), xops);
      RET;
    }
  return AS1 (push%L1,%1);
}")

(define_insn "movsf_push"
  [(set (match_operand:SF 0 "push_operand" "=<,<,<,<")
	(match_operand:SF 1 "general_operand" "rF,f,m,m"))
   (clobber (match_scratch:SI 2 "=X,X,r,X"))]
  ""
  "*
{
  if (STACK_REG_P (operands[1]))
    {
      rtx xops[3];

      if (! STACK_TOP_P (operands[1]))
        abort ();

      xops[0] = AT_SP (SFmode);
      xops[1] = GEN_INT (4);
      xops[2] = stack_pointer_rtx;

      output_asm_insn (AS2 (sub%L2,%1,%2), xops);

      if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
        output_asm_insn (AS1 (fstp%S0,%0), xops);
      else
        output_asm_insn (AS1 (fst%S0,%0), xops);
      RET;
    }

  else if (GET_CODE (operands[1]) != MEM || GET_CODE (operands[2]) != REG)
    return AS1 (push%L1,%1);

  else
    {
      output_asm_insn (AS2 (mov%L2,%1,%2), operands);
      return AS1 (push%L2,%2);
    }
}")

;; Special memory<->memory pattern that combine will recreate from the
;; moves to pseudos.
(define_insn "movsf_mem"
  [(set (match_operand:SF 0 "memory_operand" "=m")
	(match_operand:SF 1 "memory_operand" "m"))
   (clobber (match_scratch:SI 2 "=&r"))]
  ""
  "*
{
  output_asm_insn (AS2 (mov%L2,%1,%2), operands);
  return AS2 (mov%L0,%2,%0);
}")

;; For the purposes of regclass, prefer FLOAT_REGS.
(define_insn "movsf_normal"
  [(set (match_operand:SF 0 "nonimmediate_operand" "=*rfm,*rf,f,!*rm")
	(match_operand:SF 1 "general_operand" "*rf,*rfm,fG,fF"))]
  "(!TARGET_MOVE || GET_CODE (operands[0]) != MEM) || (GET_CODE (operands[1]) != MEM)"
  "*
{
  int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;

  /* First handle a `pop' insn or a `fld %st(0)' */

  if (STACK_TOP_P (operands[0]) && STACK_TOP_P (operands[1]))
    {
      if (stack_top_dies)
	return AS1 (fstp,%y0);
      else
        return AS1 (fld,%y0);
    }

  /* Handle a transfer between the 387 and a 386 register */

  if (STACK_TOP_P (operands[0]) && NON_STACK_REG_P (operands[1]))
    {
      output_op_from_reg (operands[1], AS1 (fld%z0,%y1));
      RET;
    }

  if (STACK_TOP_P (operands[1]) && NON_STACK_REG_P (operands[0]))
    {
      output_to_reg (operands[0], stack_top_dies);
      RET;
    }

  /* Handle other kinds of writes from the 387 */

  if (STACK_TOP_P (operands[1]))
    {
      if (stack_top_dies)
	return AS1 (fstp%z0,%y0);
      else
        return AS1 (fst%z0,%y0);
    }

  /* Handle other kinds of reads to the 387 */

  if (STACK_TOP_P (operands[0]) && GET_CODE (operands[1]) == CONST_DOUBLE)
    return output_move_const_single (operands);

  if (STACK_TOP_P (operands[0]))
    return AS1 (fld%z1,%y1);

  /* Handle all SFmode moves not involving the 387 */

  return singlemove_string (operands);
}"
  [(set_attr "type" "fld")])


(define_insn "swapsf"
  [(set (match_operand:SF 0 "register_operand" "f")
	(match_operand:SF 1 "register_operand" "f"))
   (set (match_dup 1)
	(match_dup 0))]
  ""
  "*
{
  if (STACK_TOP_P (operands[0]))
    return AS1 (fxch,%1);
  else
    return AS1 (fxch,%0);
}")

(define_expand "movdf"
  [(set (match_operand:DF 0 "general_operand" "")
	(match_operand:DF 1 "general_operand" ""))]
  ""
  "
{
  /* Special case memory->memory moves and pushes */
  if (TARGET_MOVE
      && (reload_in_progress | reload_completed) == 0
      && GET_CODE (operands[0]) == MEM
      && (GET_CODE (operands[1]) == MEM || push_operand (operands[0], DFmode)))
    {
      rtx (*genfunc) PROTO((rtx, rtx)) = (push_operand (operands[0], DFmode))
						? gen_movdf_push
						: gen_movdf_mem;

      emit_insn ((*genfunc) (operands[0], operands[1]));
      DONE;
    }

  /* If we are loading a floating point constant that isn't 0 or 1 into a
     register, indicate we need the pic register loaded.  This could be
     optimized into stores of constants if the target eventually moves to
     memory, but better safe than sorry.  */
  if ((reload_in_progress | reload_completed) == 0
      && GET_CODE (operands[0]) != MEM
      && GET_CODE (operands[1]) == CONST_DOUBLE
      && !standard_80387_constant_p (operands[1]))
    {
      rtx insn, note, fp_const;

      fp_const = force_const_mem (DFmode, operands[1]);
      if (flag_pic)
	current_function_uses_pic_offset_table = 1;

      insn = emit_insn (gen_rtx (SET, DFmode, operands[0], fp_const));
      note = find_reg_note (insn, REG_EQUAL, NULL_RTX);

      if (note)
	XEXP (note, 0) = operands[1];
      else
	REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUAL, operands[1], REG_NOTES (insn));
    }
}")

(define_insn "movdf_push_nomove"
  [(set (match_operand:DF 0 "push_operand" "=<,<")
	(match_operand:DF 1 "general_operand" "gF,f"))]
  "!TARGET_MOVE"
  "*
{
  if (STACK_REG_P (operands[1]))
    {
      rtx xops[3];

      xops[0] = AT_SP (SFmode);
      xops[1] = GEN_INT (8);
      xops[2] = stack_pointer_rtx;

      output_asm_insn (AS2 (sub%L2,%1,%2), xops);

      if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
        output_asm_insn (AS1 (fstp%Q0,%0), xops);
      else
        output_asm_insn (AS1 (fst%Q0,%0), xops);

      RET;
    }
  else
    return output_move_double (operands);
}")

(define_insn "movdf_push"
  [(set (match_operand:DF 0 "push_operand" "=<,<,<,<,<")
	(match_operand:DF 1 "general_operand" "rF,f,o,o,o"))
   (clobber (match_scratch:SI 2 "=X,X,&r,&r,X"))
   (clobber (match_scratch:SI 3 "=X,X,&r,X,X"))]
  ""
  "*
{
  if (STACK_REG_P (operands[1]))
    {
      rtx xops[3];

      xops[0] = AT_SP (SFmode);
      xops[1] = GEN_INT (8);
      xops[2] = stack_pointer_rtx;

      output_asm_insn (AS2 (sub%L2,%1,%2), xops);

      if (find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
        output_asm_insn (AS1 (fstp%Q0,%0), xops);
      else
        output_asm_insn (AS1 (fst%Q0,%0), xops);

      RET;
    }

  else if (GET_CODE (operands[1]) != MEM)
    return output_move_double (operands);

  else
    return output_move_pushmem (operands, insn, GET_MODE_SIZE (DFmode), 2, 4);
}")

(define_insn "movdf_mem"
  [(set (match_operand:DF 0 "memory_operand" "=o,o")
	(match_operand:DF 1 "memory_operand" "o,o"))
   (clobber (match_scratch:SI 2 "=&r,&r"))
   (clobber (match_scratch:SI 3 "=&r,X"))]
  ""
  "* return output_move_memory (operands, insn, GET_MODE_SIZE (DFmode), 2, 4);")

;; For the purposes of regclass, prefer FLOAT_REGS.
(define_insn ""
  [(set (match_operand:DF 0 "nonimmediate_operand" "=f,fm,!*rf,!*rm")
	(match_operand:DF 1 "general_operand" "fmG,f,*rfm,*rfF"))]
  "(!TARGET_MOVE || GET_CODE (operands[0]) != MEM)
   || (GET_CODE (operands[1]) != MEM)"
  "*
{
  int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;

  /* First handle a `pop' insn or a `fld %st(0)' */

  if (STACK_TOP_P (operands[0]) && STACK_TOP_P (operands[1]))
    {
      if (stack_top_dies)
	return AS1 (fstp,%y0);
      else
        return AS1 (fld,%y0);
    }

  /* Handle a transfer between the 387 and a 386 register */

  if (STACK_TOP_P (operands[0]) && NON_STACK_REG_P (operands[1]))
    {
      output_op_from_reg (operands[1], AS1 (fld%z0,%y1));
      RET;
    }

  if (STACK_TOP_P (operands[1]) && NON_STACK_REG_P (operands[0]))
    {
      output_to_reg (operands[0], stack_top_dies);
      RET;
    }

  /* Handle other kinds of writes from the 387 */

  if (STACK_TOP_P (operands[1]))
    {
      if (stack_top_dies)
	return AS1 (fstp%z0,%y0);
      else
        return AS1 (fst%z0,%y0);
    }

  /* Handle other kinds of reads to the 387 */

  if (STACK_TOP_P (operands[0]) && GET_CODE (operands[1]) == CONST_DOUBLE)
    return output_move_const_single (operands);

  if (STACK_TOP_P (operands[0]))
    return AS1 (fld%z1,%y1);

  /* Handle all DFmode moves not involving the 387 */

  return output_move_double (operands);
}"
[(set_attr "type" "fld")])



(define_insn "swapdf"
  [(set (match_operand:DF 0 "register_operand" "f")
	(match_operand:DF 1 "register_operand" "f"))
   (set (match_dup 1)
	(match_dup 0))]
  ""
  "*
{
  if (STACK_TOP_P (operands[0]))
    return AS1 (fxch,%1);
  else
    return AS1 (fxch,%0);
}")

(define_expand "movxf"
  [(set (match_operand:XF 0 "general_operand" "")
	(match_operand:XF 1 "general_operand" ""))]
  ""
  "
{
  /* Special case memory->memory moves and pushes */
  if (TARGET_MOVE
      && (reload_in_progress | reload_completed) == 0
      && GET_CODE (operands[0]) == MEM
      && (GET_CODE (operands[1]) == MEM || push_operand (operands[0], XFmode)))
    {
      rtx (*genfunc) PROTO((rtx, rtx)) = (push_operand (operands[0], XFmode))
						? gen_movxf_push
						: gen_movxf_mem;

      emit_insn ((*genfunc) (operands[0], operands[1]));
      DONE;
    }

  /* If we are loading a floating point constant that isn't 0 or 1
     into a register, indicate we need the pic register loaded.  This could
     be optimized into stores of constants if the target eventually moves
     to memory, but better safe than sorry.  */
  if ((reload_in_progress | reload_completed) == 0
      && GET_CODE (operands[0]) != MEM
      && GET_CODE (operands[1]) == CONST_DOUBLE
      && !standard_80387_constant_p (operands[1]))
    {
      rtx insn, note, fp_const;

      fp_const = force_const_mem (XFmode, operands[1]);
      if (flag_pic)
	current_function_uses_pic_offset_table = 1;

      insn = emit_insn (gen_rtx (SET, XFmode, operands[0], fp_const));
      note = find_reg_note (insn, REG_EQUAL, NULL_RTX);

      if (note)
	XEXP (note, 0) = operands[1];
      else
	REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUAL, operands[1], REG_NOTES (insn));
    }
}")


(define_insn "movxf_push_nomove"
  [(set (match_operand:XF 0 "push_operand" "=<,<")
 	(match_operand:XF 1 "general_operand" "gF,f"))]
  "!TARGET_MOVE"
  "*
{
  if (STACK_REG_P (operands[1]))
    {
      rtx xops[3];

      xops[0] = AT_SP (SFmode);
      xops[1] = GEN_INT (12);
      xops[2] = stack_pointer_rtx;

      output_asm_insn (AS2 (sub%L2,%1,%2), xops);
      output_asm_insn (AS1 (fstp%T0,%0), xops);
      if (! find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
	output_asm_insn (AS1 (fld%T0,%0), xops);

      RET;
    }
  else
    return output_move_double (operands);
 }")

(define_insn "movxf_push"
  [(set (match_operand:XF 0 "push_operand" "=<,<,<,<,<")
 	(match_operand:XF 1 "general_operand" "rF,f,o,o,o"))
   (clobber (match_scratch:SI 2 "=X,X,&r,&r,X"))
   (clobber (match_scratch:SI 3 "=X,X,&r,X,X"))]
  ""
  "*
{
  if (STACK_REG_P (operands[1]))
    {
      rtx xops[3];

      xops[0] = AT_SP (SFmode);
      xops[1] = GEN_INT (12);
      xops[2] = stack_pointer_rtx;

      output_asm_insn (AS2 (sub%L2,%1,%2), xops);
      output_asm_insn (AS1 (fstp%T0,%0), xops);
      if (! find_regno_note (insn, REG_DEAD, FIRST_STACK_REG))
	output_asm_insn (AS1 (fld%T0,%0), xops);

      RET;
    }

  else if (GET_CODE (operands[1]) != MEM
	   || GET_CODE (operands[2]) != REG)
    return output_move_double (operands);

  else
    return output_move_pushmem (operands, insn, GET_MODE_SIZE (XFmode), 2, 4);
}")

(define_insn "movxf_mem"
  [(set (match_operand:XF 0 "memory_operand" "=o,o")
	(match_operand:XF 1 "memory_operand" "o,o"))
   (clobber (match_scratch:SI 2 "=&r,&r"))
   (clobber (match_scratch:SI 3 "=&r,X"))]
  ""
  "* return output_move_memory (operands, insn, GET_MODE_SIZE (XFmode), 2, 4);")

(define_insn ""
  [(set (match_operand:XF 0 "nonimmediate_operand" "=f,fm,!*rf,!*rm")
	(match_operand:XF 1 "general_operand" "fmG,f,*rfm,*rfF"))]
  "(!TARGET_MOVE || GET_CODE (operands[0]) != MEM)
   || (GET_CODE (operands[1]) != MEM)"
  "*
{
  int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;

  /* First handle a `pop' insn or a `fld %st(0)' */

  if (STACK_TOP_P (operands[0]) && STACK_TOP_P (operands[1]))
    {
      if (stack_top_dies)
	return AS1 (fstp,%y0);
      else
        return AS1 (fld,%y0);
    }

  /* Handle a transfer between the 387 and a 386 register */

  if (STACK_TOP_P (operands[0]) && NON_STACK_REG_P (operands[1]))
    {
      output_op_from_reg (operands[1], AS1 (fld%z0,%y1));
      RET;
    }

  if (STACK_TOP_P (operands[1]) && NON_STACK_REG_P (operands[0]))
    {
      output_to_reg (operands[0], stack_top_dies);
      RET;
    }

  /* Handle other kinds of writes from the 387 */

  if (STACK_TOP_P (operands[1]))
    {
      output_asm_insn (AS1 (fstp%z0,%y0), operands);
      if (! stack_top_dies)
	return AS1 (fld%z0,%y0);

      RET;
    }

  /* Handle other kinds of reads to the 387 */

  if (STACK_TOP_P (operands[0]) && GET_CODE (operands[1]) == CONST_DOUBLE)
    return output_move_const_single (operands);

  if (STACK_TOP_P (operands[0]))
       return AS1 (fld%z1,%y1);

  /* Handle all XFmode moves not involving the 387 */

  return output_move_double (operands);
}")

(define_insn "swapxf" 
  [(set (match_operand:XF 0 "register_operand" "f")
	(match_operand:XF 1 "register_operand" "f"))
   (set (match_dup 1)
	(match_dup 0))]
  ""
  "*
{
  if (STACK_TOP_P (operands[0]))
    return AS1 (fxch,%1);
  else
    return AS1 (fxch,%0);
}")

(define_insn ""
  [(set (match_operand:DI 0 "push_operand" "=<,<,<,<")
	(match_operand:DI 1 "general_operand" "riF,o,o,o"))
   (clobber (match_scratch:SI 2 "=X,&r,&r,X"))
   (clobber (match_scratch:SI 3 "=X,&r,X,X"))]
  ""
  "*
{
  if (GET_CODE (operands[1]) != MEM)
    return output_move_double (operands);

  else
    return output_move_pushmem (operands, insn, GET_MODE_SIZE (DImode), 2, 4);
}")

(define_insn "movdi"
  [(set (match_operand:DI 0 "general_operand" "=o,o,r,rm")
	(match_operand:DI 1 "general_operand" "o,o,m,riF"))
   (clobber (match_scratch:SI 2 "=&r,&r,X,X"))
   (clobber (match_scratch:SI 3 "=&r,X,X,X"))]
  ""
  "*
{
  rtx low[2], high[2], xop[6];

  if (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)
    return output_move_double (operands);
  else
    return output_move_memory (operands, insn, GET_MODE_SIZE (DImode), 2, 4);
}")


;;- conversion instructions
;;- NONE

;;- zero extension instructions
;; See comments by `andsi' for when andl is faster than movzx.

(define_insn "zero_extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "=r,&r,?r")
	(zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "0,rm,rm")))]
  ""
  "*
  {
  rtx xops[2];

  if ((TARGET_ZERO_EXTEND_WITH_AND || REGNO (operands[0]) == 0) 
      && REG_P (operands[1]) && REGNO (operands[0]) == REGNO (operands[1]))
    {
      xops[0] = operands[0];
      xops[1] = GEN_INT (0xffff);
      output_asm_insn (AS2 (and%L0,%1,%k0), xops);
      RET;
    }
  if (TARGET_ZERO_EXTEND_WITH_AND && !reg_overlap_mentioned_p (operands[0], operands[1]))
    {
      output_asm_insn (AS2 (xor%L0,%0,%0),operands);
      output_asm_insn (AS2 (mov%W0,%1,%w0),operands);
      RET;
    }

  if (TARGET_ZERO_EXTEND_WITH_AND)
    {
      xops[0] = operands[0];
      xops[1] = gen_rtx (CONST_INT, VOIDmode, 0xffff);
      output_asm_insn (AS2 (mov%W0,%1,%w0),operands);
      output_asm_insn (AS2 (and%L0,%1,%k0), xops);
      RET;
    }

#ifdef INTEL_SYNTAX
  return AS2 (movzx,%1,%0);
#else
  return AS2 (movz%W0%L0,%1,%0);
#endif
}")

(define_split
  [(set (match_operand:SI 0 "register_operand" "")
	(zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "")))]
 "reload_completed && TARGET_ZERO_EXTEND_WITH_AND && !reg_overlap_mentioned_p (operands[0], operands[1])"
 [(set (match_dup 0)
       (const_int 0))
  (set (strict_low_part (match_dup 2))
       (match_dup 1))]
 "operands[2] = gen_rtx (REG, HImode, REGNO (operands[0]));")


(define_split
  [(set (match_operand:SI 0 "register_operand" "")
	(zero_extend:SI (match_operand:HI 1 "memory_operand" "")))]
 "reload_completed && TARGET_ZERO_EXTEND_WITH_AND && reg_overlap_mentioned_p (operands[0], operands[1])"
 [(set (strict_low_part (match_dup 2))
       (match_dup 1))
  (set (match_dup 0)
       (and:SI (match_dup 0)
	       (const_int 65535)))]
  "operands[2] = gen_rtx (REG, HImode, REGNO (operands[0]));")

(define_insn "zero_extendqihi2"
  [(set (match_operand:HI 0 "register_operand" "=q,&q,?r")
	(zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "0,qm,qm")))]
  ""
  "*
  {
  rtx xops[2];

  if ((TARGET_ZERO_EXTEND_WITH_AND || REGNO (operands[0]) == 0)
      && REG_P (operands[1]) 
      && REGNO (operands[0]) == REGNO (operands[1]))
    {
      xops[0] = operands[0];
      xops[1] = GEN_INT (0xff);
      output_asm_insn (AS2 (and%L0,%1,%k0), xops);
      RET;
    }
  if (TARGET_ZERO_EXTEND_WITH_AND && QI_REG_P (operands[0]))
    {
      if(!reg_overlap_mentioned_p(operands[0],operands[1]))
	{
	  output_asm_insn (AS2 (xor%L0,%k0,%k0), operands);
	  output_asm_insn (AS2 (mov%B0,%1,%b0), operands);
	}
      else
	{
	  xops[0] = operands[0];
	  xops[1] = gen_rtx (CONST_INT, VOIDmode, 0xff);
	  output_asm_insn (AS2 (mov%B0,%1,%b0),operands);
	  output_asm_insn (AS2 (and%L0,%1,%k0), xops);
	}
      RET;
    }
  
#ifdef INTEL_SYNTAX
  return AS2 (movzx,%1,%0);
#else
  return AS2 (movz%B0%W0,%1,%0);
#endif
}")

(define_split
  [(set (match_operand:HI 0 "register_operand" "")
	(zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "")))]
 "reload_completed && QI_REG_P (operands[0]) && TARGET_ZERO_EXTEND_WITH_AND
  && !reg_overlap_mentioned_p (operands[0], operands[1])"
 [(set (match_dup 0)
       (const_int 0))
  (set (strict_low_part (match_dup 2))
       (match_dup 1))]
 "operands[2] = gen_rtx (REG, QImode, REGNO (operands[0]));")


(define_split
  [(set (match_operand:HI 0 "register_operand" "")
	(zero_extend:HI (match_operand:QI 1 "memory_operand" "")))]
 "reload_completed && QI_REG_P (operands[0]) && TARGET_ZERO_EXTEND_WITH_AND
  && reg_overlap_mentioned_p (operands[0], operands[1])"
 [(set (strict_low_part (match_dup 2))
       (match_dup 1))
  (set (match_dup 0)
       (and:HI (match_dup 0)
	       (const_int 255)))]
 "operands[2] = gen_rtx (REG, QImode, REGNO (operands[0]));")

(define_split
  [(set (match_operand:HI 0 "register_operand" "")
	(zero_extend:HI (match_operand:QI 1 "register_operand" "")))]
 "reload_completed && TARGET_ZERO_EXTEND_WITH_AND && REGNO (operands[0]) != REGNO (operands[1])"
 [(set (match_dup 0)
       (match_dup 2))
  (set (match_dup 0)
       (and:HI (match_dup 0)
	       (const_int 255)))]
 "operands[2] = gen_rtx (REG, HImode, REGNO (operands[1]));")

(define_insn "zero_extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "=q,&q,?r")
	(zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "0,qm,qm")))]
  ""
  "*
  {
  rtx xops[2];

  if ((TARGET_ZERO_EXTEND_WITH_AND || REGNO (operands[0]) == 0)
      && REG_P (operands[1]) 
      && REGNO (operands[0]) == REGNO (operands[1]))
    {
      xops[0] = operands[0];
      xops[1] = GEN_INT (0xff);
      output_asm_insn (AS2 (and%L0,%1,%k0), xops);
      RET;
    }
  if (TARGET_ZERO_EXTEND_WITH_AND && QI_REG_P (operands[0]))
    {
      if(!reg_overlap_mentioned_p (operands[0], operands[1]))
	{
	  output_asm_insn (AS2 (xor%L0,%0,%0),operands);
	  output_asm_insn (AS2 (mov%B0,%1,%b0),operands);
	}
      else
	{
	  xops[0] = operands[0];
	  xops[1] = gen_rtx (CONST_INT, VOIDmode, 0xff);
	  output_asm_insn (AS2 (mov%B0,%1,%b0), operands);
	  output_asm_insn (AS2 (and%L0,%1,%k0), xops);
	}
      RET;
    }

  if (TARGET_ZERO_EXTEND_WITH_AND && GET_CODE (operands[1]) == REG)
    {
      xops[0] = operands[0];
      xops[1] = gen_rtx (CONST_INT, VOIDmode, 0xff);
      operands[1] = gen_rtx (REG, SImode, REGNO (operands[1]));
      output_asm_insn (AS2 (mov%L0,%1,%0), operands);
      output_asm_insn (AS2 (and%L0,%1,%k0), xops);
      RET;
    }

#ifdef INTEL_SYNTAX
  return AS2 (movzx,%1,%0);
#else
  return AS2 (movz%B0%L0,%1,%0);
#endif
}")

(define_split
  [(set (match_operand:SI 0 "register_operand" "")
	(zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))]
 "reload_completed && QI_REG_P (operands[0]) && TARGET_ZERO_EXTEND_WITH_AND
  && !reg_overlap_mentioned_p (operands[0], operands[1])"
 [(set (match_dup 0)
       (const_int 0))
  (set (strict_low_part (match_dup 2))
       (match_dup 1))]
 "operands[2] = gen_rtx (REG, QImode, REGNO (operands[0]));")


(define_split
  [(set (match_operand:SI 0 "register_operand" "")
	(zero_extend:SI (match_operand:QI 1 "memory_operand" "")))]
 "reload_completed && QI_REG_P (operands[0]) && TARGET_ZERO_EXTEND_WITH_AND
  && reg_overlap_mentioned_p (operands[0], operands[1])"
 [(set (strict_low_part (match_dup 2))
       (match_dup 1))
  (set (match_dup 0)
       (and:SI (match_dup 0)
	       (const_int 255)))]
 "operands[2] = gen_rtx (REG, QImode, REGNO (operands[0]));")

(define_split
  [(set (match_operand:SI 0 "register_operand" "")
	(zero_extend:SI (match_operand:QI 1 "register_operand" "")))]
 "reload_completed && TARGET_ZERO_EXTEND_WITH_AND && REGNO (operands[0]) != REGNO (operands[1])"
 [(set (match_dup 0)
       (match_dup 2))
  (set (match_dup 0)
       (and:SI (match_dup 0)
	       (const_int 255)))]
 "operands[2] = gen_rtx (REG, SImode, REGNO (operands[1]));")

(define_insn "zero_extendsidi2"
  [(set (match_operand:DI 0 "nonimmediate_operand" "=r,?r,?m")
	(zero_extend:DI (match_operand:SI 1 "register_operand" "0,rm,r")))]
  ""
  "*
  {
  rtx high[2], low[2], xops[4];

  if (REG_P (operands[0]) && REG_P (operands[1])
      && REGNO (operands[0]) == REGNO (operands[1]))
    {
      operands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
      return AS2 (xor%L0,%0,%0);
    }

  split_di (operands, 1, low, high);
  xops[0] = low[0];
  xops[1] = operands[1];
  xops[2] = high[0];
  xops[3] = const0_rtx;

  output_asm_insn (AS2 (mov%L0,%1,%0), xops);
  if (GET_CODE (low[0]) == MEM)
    output_asm_insn (AS2 (mov%L2,%3,%2), xops);
  else
    output_asm_insn (AS2 (xor%L2,%2,%2), xops);

  RET;
}")

;;- sign extension instructions

(define_insn "extendsidi2"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(sign_extend:DI (match_operand:SI 1 "register_operand" "0")))]
  ""
  "*
{
  if (REGNO (operands[0]) == 0)
    {
      /* This used to be cwtl, but that extends HI to SI somehow.  */
#ifdef INTEL_SYNTAX
      return \"cdq\";
#else
      return \"cltd\";
#endif
    }

  operands[1] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
  output_asm_insn (AS2 (mov%L0,%0,%1), operands);

  operands[0] = GEN_INT (31);
  return AS2 (sar%L1,%0,%1);
}")

;; Note that the i386 programmers' manual says that the opcodes
;; are named movsx..., but the assembler on Unix does not accept that.
;; We use what the Unix assembler expects.

(define_insn "extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "rm")))]
  ""
  "*
{
  if (REGNO (operands[0]) == 0
      && REG_P (operands[1]) && REGNO (operands[1]) == 0)
#ifdef INTEL_SYNTAX
    return \"cwde\";
#else
    return \"cwtl\";
#endif

#ifdef INTEL_SYNTAX
  return AS2 (movsx,%1,%0);
#else
  return AS2 (movs%W0%L0,%1,%0);
#endif
}")

(define_insn "extendqihi2"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(sign_extend:HI (match_operand:QI 1 "nonimmediate_operand" "qm")))]
  ""
  "*
{
  if (REGNO (operands[0]) == 0
      && REG_P (operands[1]) && REGNO (operands[1]) == 0)
    return \"cbtw\";

#ifdef INTEL_SYNTAX
  return AS2 (movsx,%1,%0);
#else
  return AS2 (movs%B0%W0,%1,%0);
#endif
}")

(define_insn "extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "qm")))]
  ""
  "*
{
#ifdef INTEL_SYNTAX
  return AS2 (movsx,%1,%0);
#else
  return AS2 (movs%B0%L0,%1,%0);
#endif
}")


;; Truncation of long long -> 32 bit

(define_expand "truncdisi2"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=r,m")
	(truncate:SI (match_operand:DI 1 "nonimmediate_operand" "ro,r")))]
  ""
  "
{
  /* Don't generate memory->memory moves, go through a register */
  if (TARGET_MOVE
      && (reload_in_progress | reload_completed) == 0
      && GET_CODE (operands[0]) == MEM
      && GET_CODE (operands[1]) == MEM)
    {
      rtx target = gen_reg_rtx (SImode);
      emit_insn (gen_truncdisi2 (target, operands[1]));
      emit_move_insn (operands[0], target);
      DONE;
    }
}")

(define_insn ""
  [(set (match_operand:SI 0 "nonimmediate_operand" "=r,m")
	(truncate:SI (match_operand:DI 1 "nonimmediate_operand" "ro,r")))]
  "(!TARGET_MOVE || GET_CODE (operands[0]) != MEM) || (GET_CODE (operands[1]) != MEM)"
  "*
{
  rtx low[2], high[2], xops[2];

  split_di (&operands[1], 1, low, high);
  xops[0] = operands[0];
  xops[1] = low[0];
  if (!rtx_equal_p (xops[0], xops[1]))
    output_asm_insn (AS2 (mov%L0,%1,%0), xops);

  RET;
}")

(define_insn ""
  [(set (match_operand:SI 0 "nonimmediate_operand" "=r,m")
	(truncate:SI (lshiftrt:DI (match_operand:DI 1 "nonimmediate_operand" "ro,r")
				  (const_int 32))))]
  "(!TARGET_MOVE || GET_CODE (operands[0]) != MEM) || (GET_CODE (operands[1]) != MEM)"
  "*
{
  rtx low[2], high[2], xops[2];

  split_di (&operands[1], 1, low, high);
  xops[0] = operands[0];
  xops[1] = high[0];
  if (!rtx_equal_p (xops[0], xops[1]))
    output_asm_insn (AS2 (mov%L0,%1,%0), xops);

  RET;
}")



;; Conversions between float and double.

(define_insn "extendsfdf2"
  [(set (match_operand:DF 0 "nonimmediate_operand" "=fm,f")
	(float_extend:DF
	 (match_operand:SF 1 "nonimmediate_operand" "f,fm")))]
  "TARGET_80387"
  "*
{
  int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;

  if (NON_STACK_REG_P (operands[1]))
    {
      output_op_from_reg (operands[1], AS1 (fld%z0,%y1));
      RET;
    }

  if (NON_STACK_REG_P (operands[0]))
    {
      output_to_reg (operands[0], stack_top_dies);
      RET;
    }

  if (STACK_TOP_P (operands[0]))
    return AS1 (fld%z1,%y1);

  if (GET_CODE (operands[0]) == MEM)
    {
      if (stack_top_dies)
	return AS1 (fstp%z0,%y0);
      else
        return AS1 (fst%z0,%y0);
    }

  abort ();
}")

(define_insn "extenddfxf2"
  [(set (match_operand:XF 0 "nonimmediate_operand" "=fm,f,f,!*r")
	(float_extend:XF
	 (match_operand:DF 1 "nonimmediate_operand" "f,fm,!*r,f")))]
  "TARGET_80387"
  "*
{
  int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;

  if (NON_STACK_REG_P (operands[1]))
    {
      output_op_from_reg (operands[1], AS1 (fld%z0,%y1));
      RET;
    }

  if (NON_STACK_REG_P (operands[0]))
    {
      output_to_reg (operands[0], stack_top_dies);
      RET;
    }

  if (STACK_TOP_P (operands[0]))
    return AS1 (fld%z1,%y1);

  if (GET_CODE (operands[0]) == MEM)
    {
      output_asm_insn (AS1 (fstp%z0,%y0), operands);
      if (! stack_top_dies)
	return AS1 (fld%z0,%y0);
      RET;
    }

  abort ();
}")

(define_insn "extendsfxf2"
  [(set (match_operand:XF 0 "nonimmediate_operand" "=fm,f,f,!*r")
	(float_extend:XF
	 (match_operand:SF 1 "nonimmediate_operand" "f,fm,!*r,f")))]
  "TARGET_80387"
  "*
{
  int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;

  if (NON_STACK_REG_P (operands[1]))
    {
      output_op_from_reg (operands[1], AS1 (fld%z0,%y1));
      RET;
    }

  if (NON_STACK_REG_P (operands[0]))
    {
      output_to_reg (operands[0], stack_top_dies);
      RET;
    }

  if (STACK_TOP_P (operands[0]))
    return AS1 (fld%z1,%y1);

  if (GET_CODE (operands[0]) == MEM)
    {
      output_asm_insn (AS1 (fstp%z0,%y0), operands);
      if (! stack_top_dies)
	return AS1 (fld%z0,%y0);
      RET;
    }

  abort ();
}")

(define_expand "truncdfsf2"
  [(parallel [(set (match_operand:SF 0 "nonimmediate_operand" "")
		   (float_truncate:SF
		    (match_operand:DF 1 "register_operand" "")))
	      (clobber (match_dup 2))])]
  "TARGET_80387"
  "
{
  operands[2] = (rtx) assign_386_stack_local (SFmode, 0);
}")

;; This cannot output into an f-reg because there is no way to be sure
;; of truncating in that case.  Otherwise this is just like a simple move
;; insn.  So we pretend we can output to a reg in order to get better
;; register preferencing, but we really use a stack slot.

(define_insn ""
  [(set (match_operand:SF 0 "nonimmediate_operand" "=f,m")
	(float_truncate:SF
	 (match_operand:DF 1 "register_operand" "0,f")))
   (clobber (match_operand:SF 2 "memory_operand" "m,m"))]
  "TARGET_80387"
  "*
{
  int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;

  if (GET_CODE (operands[0]) == MEM)
    {
      if (stack_top_dies)
	return AS1 (fstp%z0,%0);
      else
        return AS1 (fst%z0,%0);
    }
  else if (STACK_TOP_P (operands[0]))
    {
      output_asm_insn (AS1 (fstp%z2,%y2), operands);
      return AS1 (fld%z2,%y2);
    }
  else
    abort ();
}")

(define_insn "truncxfsf2"
  [(set (match_operand:SF 0 "nonimmediate_operand" "=m,!*r")
	(float_truncate:SF
	 (match_operand:XF 1 "register_operand" "f,f")))]
  "TARGET_80387"
  "*
{
  int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;

  if (NON_STACK_REG_P (operands[0]))
    {
      if (stack_top_dies == 0)
	{
	  output_asm_insn (AS1 (fld,%y1), operands);
	  stack_top_dies = 1;
	}
      output_to_reg (operands[0], stack_top_dies);
      RET;
    }
  else if (GET_CODE (operands[0]) == MEM)
    {
      if (stack_top_dies)
	return AS1 (fstp%z0,%0);
      else
	{
	  output_asm_insn (AS1 (fld,%y1), operands);
	  return AS1 (fstp%z0,%0);
	}
    }
  else
    abort ();
}")

(define_insn "truncxfdf2"
  [(set (match_operand:DF 0 "nonimmediate_operand" "=m,!*r")
	(float_truncate:DF
	 (match_operand:XF 1 "register_operand" "f,f")))]
  "TARGET_80387"
  "*
{
  int stack_top_dies = find_regno_note (insn, REG_DEAD, FIRST_STACK_REG) != 0;

  if (NON_STACK_REG_P (operands[0]))
    {
      if (stack_top_dies == 0)
	{
	  output_asm_insn (AS1 (fld,%y1), operands);
	  stack_top_dies = 1;
	}
      output_to_reg (operands[0], stack_top_dies);
      RET;
    }
  else if (GET_CODE (operands[0]) == MEM)
    {
      if (stack_top_dies)
	return AS1 (fstp%z0,%0);
      else
	{
	  output_asm_insn (AS1 (fld,%y1), operands);
	  return AS1 (fstp%z0,%0);
	}
    }
  else
    abort ();
}")


;; The 387 requires that the stack top dies after converting to DImode.

;; Represent an unsigned conversion from SImode to MODE_FLOAT by first
;; doing a signed conversion to DImode, and then taking just the low
;; part.

(define_expand "fixuns_truncxfsi2"
  [(set (match_dup 4)
	(match_operand:XF 1 "register_operand" ""))
   (parallel [(set (match_dup 2)
		   (fix:DI (fix:XF (match_dup 4))))
	      (clobber (match_dup 4))
	      (clobber (match_dup 5))
	      (clobber (match_dup 6))
	      (clobber (match_scratch:SI 7 ""))])
   (set (match_operand:SI 0 "general_operand" "")
	(match_dup 3))]
  "TARGET_80387"
  "
{
  operands[2] = gen_reg_rtx (DImode);
  operands[3] = gen_lowpart (SImode, operands[2]);
  operands[4] = gen_reg_rtx (XFmode);
  operands[5] = (rtx) assign_386_stack_local (SImode, 0);
  operands[6] = (rtx) assign_386_stack_local (SImode, 1);
}")

(define_expand "fixuns_truncdfsi2"
  [(set (match_dup 4)
	(match_operand:DF 1 "register_operand" ""))
   (parallel [(set (match_dup 2)
		   (fix:DI (fix:DF (match_dup 4))))
	      (clobber (match_dup 4))
	      (clobber (match_dup 5))
	      (clobber (match_dup 6))
	      (clobber (match_scratch:SI 7 ""))])
   (set (match_operand:SI 0 "general_operand" "")
	(match_dup 3))]
  "TARGET_80387"
  "
{
  operands[2] = gen_reg_rtx (DImode);
  operands[3] = gen_lowpart (SImode, operands[2]);
  operands[4] = gen_reg_rtx (DFmode);
  operands[5] = (rtx) assign_386_stack_local (SImode, 0);
  operands[6] = (rtx) assign_386_stack_local (SImode, 1);
}")

(define_expand "fixuns_truncsfsi2"
  [(set (match_dup 4)
	(match_operand:SF 1 "register_operand" ""))
   (parallel [(set (match_dup 2)
		   (fix:DI (fix:SF (match_dup 4))))
	      (clobber (match_dup 4))
	      (clobber (match_dup 5))
	      (clobber (match_dup 6))
	      (clobber (match_scratch:SI 7 ""))])
   (set (match_operand:SI 0 "general_operand" "")
	(match_dup 3))]
  "TARGET_80387"
  "
{
  operands[2] = gen_reg_rtx (DImode);
  operands[3] = gen_lowpart (SImode, operands[2]);
  operands[4] = gen_reg_rtx (SFmode);
  operands[5] = (rtx) assign_386_stack_local (SImode, 0);
  operands[6] = (rtx) assign_386_stack_local (SImode, 1);
}")

;; Signed conversion to DImode.

(define_expand "fix_truncxfdi2"
  [(set (match_dup 2)
	(match_operand:XF 1 "register_operand" ""))
   (parallel [(set (match_operand:DI 0 "general_operand" "")
		   (fix:DI (fix:XF (match_dup 2))))
	      (clobber (match_dup 2))
	      (clobber (match_dup 3))
	      (clobber (match_dup 4))
	      (clobber (match_scratch:SI 5 ""))])]
  "TARGET_80387"
  "
{
  operands[1] = copy_to_mode_reg (XFmode, operands[1]);
  operands[2] = gen_reg_rtx (XFmode);
  operands[3] = (rtx) assign_386_stack_local (SImode, 0);
  operands[4] = (rtx) assign_386_stack_local (SImode, 1);
}")

(define_expand "fix_truncdfdi2"
  [(set (match_dup 2)
	(match_operand:DF 1 "register_operand" ""))
   (parallel [(set (match_operand:DI 0 "general_operand" "")
		   (fix:DI (fix:DF (match_dup 2))))
	      (clobber (match_dup 2))
	      (clobber (match_dup 3))
	      (clobber (match_dup 4))
	      (clobber (match_scratch:SI 5 ""))])]
  "TARGET_80387"
  "
{
  operands[1] = copy_to_mode_reg (DFmode, operands[1]);
  operands[2] = gen_reg_rtx (DFmode);
  operands[3] = (rtx) assign_386_stack_local (SImode, 0);
  operands[4] = (rtx) assign_386_stack_local (SImode, 1);
}")

(define_expand "fix_truncsfdi2"
  [(set (match_dup 2)
	(match_operand:SF 1 "register_operand" ""))
   (parallel [(set (match_operand:DI 0 "general_operand" "")
		   (fix:DI (fix:SF (match_dup 2))))
	      (clobber (match_dup 2))
	      (clobber (match_dup 3))
	      (clobber (match_dup 4))
	      (clobber (match_scratch:SI 5 ""))])]
  "TARGET_80387"
  "
{
  operands[1] = copy_to_mode_reg (SFmode, operands[1]);
  operands[2] = gen_reg_rtx (SFmode);
  operands[3] = (rtx) assign_386_stack_local (SImode, 0);
  operands[4] = (rtx) assign_386_stack_local (SImode, 1);
}")

;; These match a signed conversion of either DFmode or SFmode to DImode.

(define_insn ""
  [(set (match_operand:DI 0 "nonimmediate_operand" "=rm")
	(fix:DI (fix:XF (match_operand:XF 1 "register_operand" "f"))))
   (clobber (match_dup 1))
   (clobber (match_operand:SI 2 "memory_operand" "m"))
   (clobber (match_operand:SI 3 "memory_operand" "m"))
   (clobber (match_scratch:SI 4 "=&q"))]
  "TARGET_80387"
  "* return output_fix_trunc (insn, operands);")

(define_insn ""
  [(set (match_operand:DI 0 "nonimmediate_operand" "=rm")
	(fix:DI (fix:DF (match_operand:DF 1 "register_operand" "f"))))
   (clobber (match_dup 1))
   (clobber (match_operand:SI 2 "memory_operand" "m"))
   (clobber (match_operand:SI 3 "memory_operand" "m"))
   (clobber (match_scratch:SI 4 "=&q"))]
  "TARGET_80387"
  "* return output_fix_trunc (insn, operands);")

(define_insn ""
  [(set (match_operand:DI 0 "nonimmediate_operand" "=rm")
	(fix:DI (fix:SF (match_operand:SF 1 "register_operand" "f"))))
   (clobber (match_dup 1))
   (clobber (match_operand:SI 2 "memory_operand" "m"))
   (clobber (match_operand:SI 3 "memory_operand" "m"))
   (clobber (match_scratch:SI 4 "=&q"))]
  "TARGET_80387"
  "* return output_fix_trunc (insn, operands);")

;; Signed MODE_FLOAT conversion to SImode.

(define_expand "fix_truncxfsi2"
  [(parallel [(set (match_operand:SI 0 "general_operand" "")
		   (fix:SI
		    (fix:XF (match_operand:XF 1 "register_operand" ""))))
	      (clobber (match_dup 2))
	      (clobber (match_dup 3))
	      (clobber (match_scratch:SI 4 ""))])]
  "TARGET_80387"
  "
{
  operands[2] = (rtx) assign_386_stack_local (SImode, 0);
  operands[3] = (rtx) assign_386_stack_local (SImode, 1);
}")

(define_expand "fix_truncdfsi2"
  [(parallel [(set (match_operand:SI 0 "general_operand" "")
		   (fix:SI
		    (fix:DF (match_operand:DF 1 "register_operand" ""))))
	      (clobber (match_dup 2))
	      (clobber (match_dup 3))
	      (clobber (match_scratch:SI 4 ""))])]
  "TARGET_80387"
  "
{
  operands[2] = (rtx) assign_386_stack_local (SImode, 0);
  operands[3] = (rtx) assign_386_stack_local (SImode, 1);
}")

(define_expand "fix_truncsfsi2"
  [(parallel [(set (match_operand:SI 0 "general_operand" "")
		   (fix:SI
		    (fix:SF (match_operand:SF 1 "register_operand" ""))))
	      (clobber (match_dup 2))
	      (clobber (match_dup 3))
	      (clobber (match_scratch:SI 4 ""))])]
  "TARGET_80387"
  "
{
  operands[2] = (rtx) assign_386_stack_local (SImode, 0);
  operands[3] = (rtx) assign_386_stack_local (SImode, 1);
}")

(define_insn ""
  [(set (match_operand:SI 0 "nonimmediate_operand" "=rm")
	(fix:SI (fix:XF (match_operand:XF 1 "register_operand" "f"))))
   (clobber (match_operand:SI 2 "memory_operand" "m"))
   (clobber (match_operand:SI 3 "memory_operand" "m"))
   (clobber (match_scratch:SI 4 "=&q"))]
  "TARGET_80387"
  "* return output_fix_trunc (insn, operands);")

(define_insn ""
  [(set (match_operand:SI 0 "nonimmediate_operand" "=rm")
	(fix:SI (fix:DF (match_operand:DF 1 "register_operand" "f"))))
   (clobber (match_operand:SI 2 "memory_operand" "m"))
   (clobber (match_operand:SI 3 "memory_operand" "m"))
   (clobber (match_scratch:SI 4 "=&q"))]
  "TARGET_80387"
  "* return output_fix_trunc (insn, operands);")

(define_insn ""
  [(set (match_operand:SI 0 "nonimmediate_operand" "=rm")
	(fix:SI (fix:SF (match_operand:SF 1 "register_operand" "f"))))
   (clobber (match_operand:SI 2 "memory_operand" "m"))
   (clobber (match_operand:SI 3 "memory_operand" "m"))
   (clobber (match_scratch:SI 4 "=&q"))]
  "TARGET_80387"
  "* return output_fix_trunc (insn, operands);")

;; Conversion between fixed point and floating point.
;; The actual pattern that matches these is at the end of this file.

;; ??? Possibly represent floatunssidf2 here in gcc2.

(define_expand "floatsisf2"
  [(set (match_operand:SF 0 "register_operand" "")
	(float:SF (match_operand:SI 1 "nonimmediate_operand" "")))]
  "TARGET_80387"
  "")

(define_expand "floatdisf2"
  [(set (match_operand:SF 0 "register_operand" "")
	(float:SF (match_operand:DI 1 "nonimmediate_operand" "")))]
  "TARGET_80387"
  "")

(define_expand "floatsidf2"
  [(set (match_operand:DF 0 "register_operand" "")
	(float:DF (match_operand:SI 1 "nonimmediate_operand" "")))]
  "TARGET_80387"
  "")

(define_expand "floatdidf2"
  [(set (match_operand:DF 0 "register_operand" "")
	(float:DF (match_operand:DI 1 "nonimmediate_operand" "")))]
  "TARGET_80387"
  "")

(define_expand "floatsixf2"
  [(set (match_operand:XF 0 "register_operand" "")
	(float:XF (match_operand:SI 1 "nonimmediate_operand" "")))]
  "TARGET_80387"
  "")

(define_expand "floatdixf2"
  [(set (match_operand:XF 0 "register_operand" "")
	(float:XF (match_operand:DI 1 "nonimmediate_operand" "")))]
  "TARGET_80387"
  "")

;; This will convert from SImode or DImode to MODE_FLOAT.

(define_insn ""
  [(set (match_operand:XF 0 "register_operand" "=f")
	(float:XF (match_operand:DI 1 "nonimmediate_operand" "rm")))]
  "TARGET_80387"
  "*
{
  if (NON_STACK_REG_P (operands[1]))
    {
      output_op_from_reg (operands[1], AS1 (fild%z0,%1));
      RET;
    }
  else if (GET_CODE (operands[1]) == MEM)
    return AS1 (fild%z1,%1);
  else
    abort ();
}")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=f")
	(float:DF (match_operand:DI 1 "nonimmediate_operand" "rm")))]
  "TARGET_80387"
  "*
{
  if (NON_STACK_REG_P (operands[1]))
    {
      output_op_from_reg (operands[1], AS1 (fild%z0,%1));
      RET;
    }
  else if (GET_CODE (operands[1]) == MEM)
    return AS1 (fild%z1,%1);
  else
    abort ();
}")

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=f")
	(float:SF (match_operand:DI 1 "nonimmediate_operand" "rm")))]
  "TARGET_80387"
  "*
{
  if (NON_STACK_REG_P (operands[1]))
    {
      output_op_from_reg (operands[1], AS1 (fild%z0,%1));
      RET;
    }
  else if (GET_CODE (operands[1]) == MEM)
    return AS1 (fild%z1,%1);
  else
    abort ();
}")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=f")
	(float:DF (match_operand:SI 1 "nonimmediate_operand" "rm")))]
  "TARGET_80387"
  "*
{
  if (NON_STACK_REG_P (operands[1]))
    {
      output_op_from_reg (operands[1], AS1 (fild%z0,%1));
      RET;
    }
  else if (GET_CODE (operands[1]) == MEM)
    return AS1 (fild%z1,%1);
  else
    abort ();
}")

(define_insn ""
  [(set (match_operand:XF 0 "register_operand" "=f,f")
	(float:XF (match_operand:SI 1 "nonimmediate_operand" "m,!*r")))]
  "TARGET_80387"
  "*
{
  if (NON_STACK_REG_P (operands[1]))
    {
      output_op_from_reg (operands[1], AS1 (fild%z0,%1));
      RET;
    }
  else if (GET_CODE (operands[1]) == MEM)
    return AS1 (fild%z1,%1);
  else
    abort ();
}")

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=f")
	(float:SF (match_operand:SI 1 "nonimmediate_operand" "rm")))]
  "TARGET_80387"
  "*
{
  if (NON_STACK_REG_P (operands[1]))
    {
      output_op_from_reg (operands[1], AS1 (fild%z0,%1));
      RET;
    }
  else if (GET_CODE (operands[1]) == MEM)
    return AS1 (fild%z1,%1);
  else
    abort ();
}")

;;- add instructions

(define_insn "addsidi3_1"
  [(set (match_operand:DI 0 "nonimmediate_operand" "=&r,r,o,&r,r,o,o")
	(plus:DI (match_operand:DI 1 "general_operand" "0,0,0,o,riF,riF,o")
		 (zero_extend:DI (match_operand:SI 2 "general_operand" "o,ri,ri,roi,roi,ri,ri"))))
   (clobber (match_scratch:SI 3 "=X,X,X,X,X,X,&r"))]
  ""
  "*
{
  rtx low[3], high[3], xops[7], temp;

  CC_STATUS_INIT;

  split_di (operands, 2, low, high);
  high[2] = const0_rtx;
  low[2]  = operands[2];

  if (!rtx_equal_p (operands[0], operands[1]))
    {
      xops[0] = high[0];
      xops[1] = low[0];
      xops[2] = high[1];
      xops[3] = low[1];

      if (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)
	{
	  output_asm_insn (AS2 (mov%L1,%3,%1), xops);
	  output_asm_insn (AS2 (mov%L0,%2,%0), xops);
	}
      else
	{
	  xops[4] = high[2];
	  xops[5] = low[2];
	  xops[6] = operands[3];
	  output_asm_insn (AS2 (mov%L6,%3,%6), xops);
	  output_asm_insn (AS2 (add%L6,%5,%6), xops);
	  output_asm_insn (AS2 (mov%L1,%6,%1), xops);
	  output_asm_insn (AS2 (mov%L6,%2,%6), xops);
	  output_asm_insn (AS2 (adc%L6,%4,%6), xops);
	  output_asm_insn (AS2 (mov%L0,%6,%0), xops);
	  RET;
	}
    }

  output_asm_insn (AS2 (add%L0,%2,%0), low);
  output_asm_insn (AS2 (adc%L0,%2,%0), high);
  RET;
}")

(define_insn "addsidi3_2"
  [(set (match_operand:DI 0 "nonimmediate_operand" "=&r,r,o,&r,r,o,o")
	(plus:DI (zero_extend:DI (match_operand:SI 2 "general_operand" "o,ri,ri,o,ri,ri,ri"))
		 (match_operand:DI 1 "general_operand" "0,0,0,roiF,roiF,riF,o")))
   (clobber (match_scratch:SI 3 "=X,X,X,X,X,X,&r"))]
  ""
  "*
{
  rtx low[3], high[3], xops[7], temp;

  CC_STATUS_INIT;

  split_di (operands, 2, low, high);
  high[2] = const0_rtx;
  low[2]  = operands[2];

  if (!rtx_equal_p (operands[0], operands[1]))
    {
      xops[0] = high[0];
      xops[1] = low[0];
      xops[2] = high[1];
      xops[3] = low[1];

      if (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)
	{
	  output_asm_insn (AS2 (mov%L1,%3,%1), xops);
	  output_asm_insn (AS2 (mov%L0,%2,%0), xops);
	}
      else
	{
	  xops[4] = high[2];
	  xops[5] = low[2];
	  xops[6] = operands[3];
	  output_asm_insn (AS2 (mov%L6,%3,%6), xops);
	  output_asm_insn (AS2 (add%L6,%5,%6), xops);
	  output_asm_insn (AS2 (mov%L1,%6,%1), xops);
	  output_asm_insn (AS2 (mov%L6,%2,%6), xops);
	  output_asm_insn (AS2 (adc%L6,%4,%6), xops);
	  output_asm_insn (AS2 (mov%L0,%6,%0), xops);
	  RET;
	}
    }

  output_asm_insn (AS2 (add%L0,%2,%0), low);
  output_asm_insn (AS2 (adc%L0,%2,%0), high);
  RET;
}")

(define_insn "adddi3"
  [(set (match_operand:DI 0 "general_operand" "=&r,ro,o,&r,ro,o,&r,o,o,o")
	(plus:DI (match_operand:DI 1 "general_operand" "%0,0,0,o,riF,o,or,riF,riF,o")
		 (match_operand:DI 2 "general_operand" "o,riF,o,0,0,0,oriF,riF,o,o")))
   (clobber (match_scratch:SI 3 "=X,X,&r,X,&r,&r,X,&r,&r,&r"))]
  ""
  "*
{
  rtx low[3], high[3], xops[7], temp;

  CC_STATUS_INIT;

  if (rtx_equal_p (operands[0], operands[2]))
    {
      temp = operands[1];
      operands[1] = operands[2];
      operands[2] = temp;
    }

  split_di (operands, 3, low, high);
  if (!rtx_equal_p (operands[0], operands[1]))
    {
      xops[0] = high[0];
      xops[1] = low[0];
      xops[2] = high[1];
      xops[3] = low[1];

      if (GET_CODE (operands[0]) != MEM)
	{
	  output_asm_insn (AS2 (mov%L1,%3,%1), xops);
	  output_asm_insn (AS2 (mov%L0,%2,%0), xops);
	}
      else
	{
	  xops[4] = high[2];
	  xops[5] = low[2];
	  xops[6] = operands[3];
	  output_asm_insn (AS2 (mov%L6,%3,%6), xops);
	  output_asm_insn (AS2 (add%L6,%5,%6), xops);
	  output_asm_insn (AS2 (mov%L1,%6,%1), xops);
	  output_asm_insn (AS2 (mov%L6,%2,%6), xops);
	  output_asm_insn (AS2 (adc%L6,%4,%6), xops);
	  output_asm_insn (AS2 (mov%L0,%6,%0), xops);
	  RET;
	}
    }

  if (GET_CODE (operands[3]) == REG && GET_CODE (operands[2]) != REG)
    {
      xops[0] = high[0];
      xops[1] = low[0];
      xops[2] = high[2];
      xops[3] = low[2];
      xops[4] = operands[3];

      output_asm_insn (AS2 (mov%L4,%3,%4), xops);
      output_asm_insn (AS2 (add%L1,%4,%1), xops);
      output_asm_insn (AS2 (mov%L4,%2,%4), xops);
      output_asm_insn (AS2 (adc%L0,%4,%0), xops);
    }

  else if (GET_CODE (low[2]) != CONST_INT || INTVAL (low[2]) != 0)
    {
      output_asm_insn (AS2 (add%L0,%2,%0), low);
      output_asm_insn (AS2 (adc%L0,%2,%0), high);
    }

  else
    output_asm_insn (AS2 (add%L0,%2,%0), high);

  RET;
}")

;; On a 486, it is faster to do movl/addl than to do a single leal if
;; operands[1] and operands[2] are both registers.

(define_expand "addsi3"
  [(set (match_operand:SI 0 "nonimmediate_operand" "")
	(plus:SI (match_operand:SI 1 "nonimmediate_operand" "")
		 (match_operand:SI 2 "general_operand" "")))]
  ""
  "IX86_EXPAND_BINARY_OPERATOR (PLUS, SImode, operands);")

(define_insn ""
  [(set (match_operand:SI 0 "nonimmediate_operand" "=r,rm,r")
	(plus:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,r")
		 (match_operand:SI 2 "general_operand" "rmi,ri,ri")))]
  "ix86_binary_operator_ok (PLUS, SImode, operands)"
  "*
{
  if (REG_P (operands[0]) && REG_P (operands[1])
      && (REG_P (operands[2]) || GET_CODE (operands[2]) == CONST_INT)
      && REGNO (operands[0]) != REGNO (operands[1]))
    {
      if (REG_P (operands[2]) && REGNO (operands[0]) == REGNO (operands[2]))
	return AS2 (add%L0,%1,%0);

      if (operands[2] == stack_pointer_rtx)
	{
	  rtx temp;

	  temp = operands[1];
	  operands[1] = operands[2];
	  operands[2] = temp;
	}

      if (operands[2] != stack_pointer_rtx)
	{
	  CC_STATUS_INIT;
	  operands[1] = SET_SRC (PATTERN (insn));
	  return AS2 (lea%L0,%a1,%0);
	}
    }

  if (!rtx_equal_p (operands[0], operands[1]))
    output_asm_insn (AS2 (mov%L0,%1,%0), operands);

  if (operands[2] == const1_rtx)
    return AS1 (inc%L0,%0);

  if (operands[2] == constm1_rtx)
    return AS1 (dec%L0,%0);

  return AS2 (add%L0,%2,%0);
}")

;; addsi3 is faster, so put this after.

(define_insn "movsi_lea"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (match_operand:QI 1 "address_operand" "p"))]
  ""
  "*
{
  /* Adding a constant to a register is faster with an add.  */
  /* ??? can this ever happen? */
  if (GET_CODE (operands[1]) == PLUS
      && GET_CODE (XEXP (operands[1], 1)) == CONST_INT
      && rtx_equal_p (operands[0], XEXP (operands[1], 0)))
    {
      operands[1] = XEXP (operands[1], 1);

      if (operands[1] == const1_rtx)
        return AS1 (inc%L0,%0);

      if (operands[1] == constm1_rtx)
        return AS1 (dec%L0,%0);

      return AS2 (add%L0,%1,%0);
    }

  CC_STATUS_INIT;
  return AS2 (lea%L0,%a1,%0);
}")

;; ??? `lea' here, for three operand add?  If leaw is used, only %bx,
;; %si and %di can appear in SET_SRC, and output_asm_insn might not be
;; able to handle the operand.  But leal always works?

(define_expand "addhi3"
  [(set (match_operand:HI 0 "general_operand" "")
	(plus:HI (match_operand:HI 1 "nonimmediate_operand" "")
		 (match_operand:HI 2 "general_operand" "")))]
  ""
  "IX86_EXPAND_BINARY_OPERATOR (PLUS, HImode, operands);")

(define_insn ""
  [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,r")
	(plus:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0")
		 (match_operand:HI 2 "general_operand" "ri,rm")))]
  "ix86_binary_operator_ok (PLUS, HImode, operands)"
  "*
{
  /* ??? what about offsettable memory references? */
  if (QI_REG_P (operands[0])
      && GET_CODE (operands[2]) == CONST_INT
      && (INTVAL (operands[2]) & 0xff) == 0)
    {
      int byteval = (INTVAL (operands[2]) >> 8) & 0xff;
      CC_STATUS_INIT;

      if (byteval == 1)
	return AS1 (inc%B0,%h0);
      else if (byteval == 255)
	return AS1 (dec%B0,%h0);

      operands[2] = GEN_INT (byteval);
      return AS2 (add%B0,%2,%h0);
    }

  if (operands[2] == const1_rtx)
    return AS1 (inc%W0,%0);

  if (operands[2] == constm1_rtx
      || (GET_CODE (operands[2]) == CONST_INT
	  && INTVAL (operands[2]) == 65535))
    return AS1 (dec%W0,%0);

  return AS2 (add%W0,%2,%0);
}")

(define_expand "addqi3"
  [(set (match_operand:QI 0 "general_operand" "")
	(plus:QI (match_operand:QI 1 "general_operand" "")
		 (match_operand:QI 2 "general_operand" "")))]
  ""
  "IX86_EXPAND_BINARY_OPERATOR (PLUS, QImode, operands);")

(define_insn ""
  [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,q")
	(plus:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0")
		 (match_operand:QI 2 "general_operand" "qn,qmn")))]
  "ix86_binary_operator_ok (PLUS, QImode, operands)"
  "*
{
  if (operands[2] == const1_rtx)
    return AS1 (inc%B0,%0);

  if (operands[2] == constm1_rtx
      || (GET_CODE (operands[2]) == CONST_INT
	  && INTVAL (operands[2]) == 255))
    return AS1 (dec%B0,%0);

  return AS2 (add%B0,%2,%0);
}")

;Lennart Augustsson <augustss@cs.chalmers.se>
;says this pattern just makes slower code:
;	pushl	%ebp
;	addl	$-80,(%esp)
;instead of
;	leal	-80(%ebp),%eax
;	pushl	%eax
;
;(define_insn ""
;  [(set (match_operand:SI 0 "push_operand" "=<")
;	(plus:SI (match_operand:SI 1 "register_operand" "%r")
;		 (match_operand:SI 2 "nonmemory_operand" "ri")))]
;  ""
;  "*
;{
;  rtx xops[4];
;  xops[0] = operands[0];
;  xops[1] = operands[1];
;  xops[2] = operands[2];
;  xops[3] = gen_rtx (MEM, SImode, stack_pointer_rtx);
;  output_asm_insn (\"push%z1 %1\", xops);
;  output_asm_insn (AS2 (add%z3,%2,%3), xops);
;  RET;
;}")

;; The patterns that match these are at the end of this file.

(define_expand "addxf3"
  [(set (match_operand:XF 0 "register_operand" "")
	(plus:XF (match_operand:XF 1 "register_operand" "")
		 (match_operand:XF 2 "register_operand" "")))]
  "TARGET_80387"
  "")

(define_expand "adddf3"
  [(set (match_operand:DF 0 "register_operand" "")
	(plus:DF (match_operand:DF 1 "nonimmediate_operand" "")
		 (match_operand:DF 2 "nonimmediate_operand" "")))]
  "TARGET_80387"
  "")

(define_expand "addsf3"
  [(set (match_operand:SF 0 "register_operand" "")
	(plus:SF (match_operand:SF 1 "nonimmediate_operand" "")
		 (match_operand:SF 2 "nonimmediate_operand" "")))]
  "TARGET_80387"
  "")

;;- subtract instructions

(define_insn "subsidi3"
  [(set (match_operand:DI 0 "general_operand" "=&r,ro,&r,o,o")
	(minus:DI (match_operand:DI 1 "general_operand" "0,0,roiF,riF,o")
		  (zero_extend:DI (match_operand:SI 2 "general_operand" "o,ri,roi,ri,ri"))))
   (clobber (match_scratch:SI 3 "=X,X,X,X,&r"))]
  ""
  "*
{
  rtx low[3], high[3], xops[7];

  CC_STATUS_INIT;

  split_di (operands, 2, low, high);
  high[2] = const0_rtx;
  low[2]  = operands[2];

  if (!rtx_equal_p (operands[0], operands[1]))
    {
      xops[0] = high[0];
      xops[1] = low[0];
      xops[2] = high[1];
      xops[3] = low[1];

      if (GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM)
	{
	  output_asm_insn (AS2 (mov%L1,%3,%1), xops);
	  output_asm_insn (AS2 (mov%L0,%2,%0), xops);
	}
      else
	{
	  xops[4] = high[2];
	  xops[5] = low[2];
	  xops[6] = operands[3];
	  output_asm_insn (AS2 (mov%L6,%3,%6), xops);
	  output_asm_insn (AS2 (sub%L6,%5,%6), xops);
	  output_asm_insn (AS2 (mov%L1,%6,%1), xops);
	  output_asm_insn (AS2 (mov%L6,%2,%6), xops);
	  output_asm_insn (AS2 (sbb%L6,%4,%6), xops);
	  output_asm_insn (AS2 (mov%L0,%6,%0), xops);
	  RET;
	}
    }

  output_asm_insn (AS2 (sub%L0,%2,%0), low);
  output_asm_insn (AS2 (sbb%L0,%2,%0), high);
  RET;
}")

(define_insn "subdi3"
  [(set (match_operand:DI 0 "general_operand" "=&r,ro,&r,o,o")
	(minus:DI (match_operand:DI 1 "general_operand" "0,0,roiF,riF,o")
		  (match_operand:DI 2 "general_operand" "o,riF,roiF,riF,o")))
   (clobber (match_scratch:SI 3 "=X,X,X,&r,&r"))]
  ""
  "*
{
  rtx low[3], high[3], xops[7];

  CC_STATUS_INIT;

  split_di (operands, 3, low, high);

  if (!rtx_equal_p (operands[0], operands[1]))
    {
      xops[0] = high[0];
      xops[1] = low[0];
      xops[2] = high[1];
      xops[3] = low[1];

      if (GET_CODE (operands[0]) != MEM)
	{
	  output_asm_insn (AS2 (mov%L1,%3,%1), xops);
	  output_asm_insn (AS2 (mov%L0,%2,%0), xops);
	}
      else
	{
	  xops[4] = high[2];
	  xops[5] = low[2];
	  xops[6] = operands[3];
	  output_asm_insn (AS2 (mov%L6,%3,%6), xops);
	  output_asm_insn (AS2 (sub%L6,%5,%6), xops);
	  output_asm_insn (AS2 (mov%L1,%6,%1), xops);
	  output_asm_insn (AS2 (mov%L6,%2,%6), xops);
	  output_asm_insn (AS2 (sbb%L6,%4,%6), xops);
	  output_asm_insn (AS2 (mov%L0,%6,%0), xops);
	  RET;
	}
    }

  if (GET_CODE (operands[3]) == REG)
    {
      xops[0] = high[0];
      xops[1] = low[0];
      xops[2] = high[2];
      xops[3] = low[2];
      xops[4] = operands[3];

      output_asm_insn (AS2 (mov%L4,%3,%4), xops);
      output_asm_insn (AS2 (sub%L1,%4,%1), xops);
      output_asm_insn (AS2 (mov%L4,%2,%4), xops);
      output_asm_insn (AS2 (sbb%L0,%4,%0), xops);
    }

  else if (GET_CODE (low[2]) != CONST_INT || INTVAL (low[2]) != 0)
    {
      output_asm_insn (AS2 (sub%L0,%2,%0), low);
      output_asm_insn (AS2 (sbb%L0,%2,%0), high);
    }

  else
    output_asm_insn (AS2 (sub%L0,%2,%0), high);

  RET;
}")

(define_expand "subsi3"
  [(set (match_operand:SI 0 "nonimmediate_operand" "")
	(minus:SI (match_operand:SI 1 "nonimmediate_operand" "")
		  (match_operand:SI 2 "general_operand" "")))]
  ""
  "IX86_EXPAND_BINARY_OPERATOR (MINUS, SImode, operands);")

(define_insn ""
  [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,r")
	(minus:SI (match_operand:SI 1 "nonimmediate_operand" "0,0")
		  (match_operand:SI 2 "general_operand" "ri,rm")))]
  "ix86_binary_operator_ok (MINUS, SImode, operands)"
  "* return AS2 (sub%L0,%2,%0);")

(define_expand "subhi3"
  [(set (match_operand:HI 0 "general_operand" "")
	(minus:HI (match_operand:HI 1 "nonimmediate_operand" "")
		  (match_operand:HI 2 "general_operand" "")))]
  ""
  "IX86_EXPAND_BINARY_OPERATOR (MINUS, HImode, operands);")

(define_insn ""
  [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,r")
	(minus:HI (match_operand:HI 1 "nonimmediate_operand" "0,0")
		  (match_operand:HI 2 "general_operand" "ri,rm")))]
  "ix86_binary_operator_ok (MINUS, HImode, operands)"
  "* return AS2 (sub%W0,%2,%0);")

(define_expand "subqi3"
  [(set (match_operand:QI 0 "general_operand" "")
	(minus:QI (match_operand:QI 1 "general_operand" "")
		  (match_operand:QI 2 "general_operand" "")))]
  ""
  "IX86_EXPAND_BINARY_OPERATOR (MINUS, QImode, operands);")

(define_insn ""
  [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,q")
	(minus:QI (match_operand:QI 1 "nonimmediate_operand" "0,0")
		  (match_operand:QI 2 "general_operand" "qn,qmn")))]
  "ix86_binary_operator_ok (MINUS, QImode, operands)"
  "* return AS2 (sub%B0,%2,%0);")

;; The patterns that match these are at the end of this file.

(define_expand "subxf3"
  [(set (match_operand:XF 0 "register_operand" "")
	(minus:XF (match_operand:XF 1 "register_operand" "")
		  (match_operand:XF 2 "register_operand" "")))]
  "TARGET_80387"
  "")

(define_expand "subdf3"
  [(set (match_operand:DF 0 "register_operand" "")
	(minus:DF (match_operand:DF 1 "nonimmediate_operand" "")
		  (match_operand:DF 2 "nonimmediate_operand" "")))]
  "TARGET_80387"
  "")

(define_expand "subsf3"
  [(set (match_operand:SF 0 "register_operand" "")
	(minus:SF (match_operand:SF 1 "nonimmediate_operand" "")
		  (match_operand:SF 2 "nonimmediate_operand" "")))]
  "TARGET_80387"
  "")

;;- multiply instructions

;(define_insn "mulqi3"
;  [(set (match_operand:QI 0 "register_operand" "=a")
;	(mult:QI (match_operand:QI 1 "register_operand" "%0")
;		 (match_operand:QI 2 "nonimmediate_operand" "qm")))]
;  ""
;  "imul%B0 %2,%0")

(define_insn "mulhi3"
  [(set (match_operand:HI 0 "register_operand" "=r,r")
	(mult:HI (match_operand:HI 1 "nonimmediate_operand" "%0,rm")
		 (match_operand:HI 2 "general_operand" "g,i")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == REG
      && REGNO (operands[1]) == REGNO (operands[0])
      && (GET_CODE (operands[2]) == MEM || GET_CODE (operands[2]) == REG))
    /* Assembler has weird restrictions.  */
    return AS2 (imul%W0,%2,%0);
  return AS3 (imul%W0,%2,%1,%0);
}")

(define_insn "mulsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(mult:SI (match_operand:SI 1 "nonimmediate_operand" "%0,rm")
		 (match_operand:SI 2 "general_operand" "g,i")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == REG
      && REGNO (operands[1]) == REGNO (operands[0])
      && (GET_CODE (operands[2]) == MEM || GET_CODE (operands[2]) == REG))
    /* Assembler has weird restrictions.  */
    return AS2 (imul%L0,%2,%0);
  return AS3 (imul%L0,%2,%1,%0);
}")

(define_insn "umulqihi3"
  [(set (match_operand:HI 0 "register_operand" "=a")
	(mult:HI (zero_extend:HI (match_operand:QI 1 "register_operand" "%0"))
		 (zero_extend:HI (match_operand:QI 2 "nonimmediate_operand" "qm"))))]
  ""
  "mul%B0 %2")

(define_insn "mulqihi3"
  [(set (match_operand:HI 0 "register_operand" "=a")
	(mult:HI (sign_extend:HI (match_operand:QI 1 "register_operand" "%0"))
		 (sign_extend:HI (match_operand:QI 2 "nonimmediate_operand" "qm"))))]
  ""
  "imul%B0 %2")

(define_insn "umulsidi3"
  [(set (match_operand:DI 0 "register_operand" "=A")
	(mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "%0"))
		 (zero_extend:DI (match_operand:SI 2 "nonimmediate_operand" "rm"))))]
  "TARGET_WIDE_MULTIPLY"
  "mul%L0 %2")

(define_insn "mulsidi3"
  [(set (match_operand:DI 0 "register_operand" "=A")
	(mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "%0"))
		 (sign_extend:DI (match_operand:SI 2 "nonimmediate_operand" "rm"))))]
  "TARGET_WIDE_MULTIPLY"
  "imul%L0 %2")

(define_insn "umulsi3_highpart"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(truncate:SI (lshiftrt:DI (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "%a"))
					   (zero_extend:DI (match_operand:SI 2 "nonimmediate_operand" "rm")))
				  (const_int 32))))
   (clobber (match_scratch:SI 3 "=a"))]
  "TARGET_WIDE_MULTIPLY"
  "mul%L0 %2")

(define_insn "smulsi3_highpart"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(truncate:SI (lshiftrt:DI (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "%a"))
					   (sign_extend:DI (match_operand:SI 2 "nonimmediate_operand" "rm")))
				  (const_int 32))))
   (clobber (match_scratch:SI 3 "=a"))]
  "TARGET_WIDE_MULTIPLY"
  "imul%L0 %2")

;; The patterns that match these are at the end of this file.

(define_expand "mulxf3"
  [(set (match_operand:XF 0 "register_operand" "")
	(mult:XF (match_operand:XF 1 "register_operand" "")
		 (match_operand:XF 2 "register_operand" "")))]
  "TARGET_80387"
  "")

(define_expand "muldf3"
  [(set (match_operand:DF 0 "register_operand" "")
	(mult:DF (match_operand:DF 1 "register_operand" "")
		 (match_operand:DF 2 "nonimmediate_operand" "")))]
  "TARGET_80387"
  "")

(define_expand "mulsf3"
  [(set (match_operand:SF 0 "register_operand" "")
	(mult:SF (match_operand:SF 1 "register_operand" "")
		 (match_operand:SF 2 "nonimmediate_operand" "")))]
  "TARGET_80387"
  "")

;;- divide instructions

(define_insn "divqi3"
  [(set (match_operand:QI 0 "register_operand" "=a")
	(div:QI (match_operand:HI 1 "register_operand" "0")
		(match_operand:QI 2 "nonimmediate_operand" "qm")))]
  ""
  "idiv%B0 %2")

(define_insn "udivqi3"
  [(set (match_operand:QI 0 "register_operand" "=a")
	(udiv:QI (match_operand:HI 1 "register_operand" "0")
		 (match_operand:QI 2 "nonimmediate_operand" "qm")))]
  ""
  "div%B0 %2")

;; The patterns that match these are at the end of this file.

(define_expand "divxf3"
  [(set (match_operand:XF 0 "register_operand" "")
	(div:XF (match_operand:XF 1 "register_operand" "")
		(match_operand:XF 2 "register_operand" "")))]
  "TARGET_80387"
  "")

(define_expand "divsf3"
  [(set (match_operand:SF 0 "register_operand" "")
	(div:SF (match_operand:SF 1 "register_operand" "")
		(match_operand:SF 2 "nonimmediate_operand" "")))]
  "TARGET_80387"
  "")

;; Remainder instructions.

(define_insn "divmodsi4"
  [(set (match_operand:SI 0 "register_operand" "=a")
	(div:SI (match_operand:SI 1 "register_operand" "0")
		(match_operand:SI 2 "nonimmediate_operand" "rm")))
   (set (match_operand:SI 3 "register_operand" "=&d")
	(mod:SI (match_dup 1) (match_dup 2)))]
  ""
  "*
{
#ifdef INTEL_SYNTAX
  output_asm_insn (\"cdq\", operands);
#else
  output_asm_insn (\"cltd\", operands);
#endif
  return AS1 (idiv%L0,%2);
}")

(define_insn "divmodhi4"
  [(set (match_operand:HI 0 "register_operand" "=a")
	(div:HI (match_operand:HI 1 "register_operand" "0")
		(match_operand:HI 2 "nonimmediate_operand" "rm")))
   (set (match_operand:HI 3 "register_operand" "=&d")
	(mod:HI (match_dup 1) (match_dup 2)))]
  ""
  "cwtd\;idiv%W0 %2")

;; ??? Can we make gcc zero extend operand[0]?
(define_insn "udivmodsi4"
  [(set (match_operand:SI 0 "register_operand" "=a")
	(udiv:SI (match_operand:SI 1 "register_operand" "0")
		 (match_operand:SI 2 "nonimmediate_operand" "rm")))
   (set (match_operand:SI 3 "register_operand" "=&d")
	(umod:SI (match_dup 1) (match_dup 2)))]
  ""
  "*
{
  output_asm_insn (AS2 (xor%L3,%3,%3), operands);
  return AS1 (div%L0,%2);
}")

;; ??? Can we make gcc zero extend operand[0]?
(define_insn "udivmodhi4"
  [(set (match_operand:HI 0 "register_operand" "=a")
	(udiv:HI (match_operand:HI 1 "register_operand" "0")
		 (match_operand:HI 2 "nonimmediate_operand" "rm")))
   (set (match_operand:HI 3 "register_operand" "=&d")
	(umod:HI (match_dup 1) (match_dup 2)))]
  ""
  "*
{
  output_asm_insn (AS2 (xor%W0,%3,%3), operands);
  return AS1 (div%W0,%2);
}")

/*
;;this should be a valid double division which we may want to add

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=a")
	(udiv:DI (match_operand:DI 1 "register_operand" "a")
		 (match_operand:SI 2 "nonimmediate_operand" "rm")))
   (set (match_operand:SI 3 "register_operand" "=d")
	(umod:SI (match_dup 1) (match_dup 2)))]
  ""
  "div%L0 %2,%0")
*/

;;- and instructions

;; On i386,
;;			movzbl %bl,%ebx
;; is faster than
;;			andl $255,%ebx
;;
;; but if the reg is %eax, then the "andl" is faster.
;;
;; On i486, the "andl" is always faster than the "movzbl".
;;
;; On both i386 and i486, a three operand AND is as fast with movzbl or
;; movzwl as with andl, if operands[0] != operands[1].

;; The `r' in `rm' for operand 3 looks redundant, but it causes
;; optional reloads to be generated if op 3 is a pseudo in a stack slot.

;; ??? What if we only change one byte of an offsettable memory reference?
(define_insn "andsi3"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,r")
	(and:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0")
		(match_operand:SI 2 "general_operand" "ri,rm")))]
  ""
  "*
{
  if (!rtx_equal_p (operands[0], operands[1])
      && rtx_equal_p (operands[0], operands[2]))
    {
      rtx tmp;
      tmp = operands[1];
      operands[1] = operands[2];
      operands[2] = tmp;
    }
  if (GET_CODE (operands[2]) == CONST_INT
      && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))
    {
      if (INTVAL (operands[2]) == 0xffff && REG_P (operands[0])
	  && (! REG_P (operands[1])
	      || REGNO (operands[0]) != 0 || REGNO (operands[1]) != 0)
	  && (!TARGET_ZERO_EXTEND_WITH_AND || ! rtx_equal_p (operands[0], operands[1])))
	{
	  /* ??? tege: Should forget CC_STATUS only if we clobber a
	     remembered operand.  Fix that later.  */
	  CC_STATUS_INIT;
#ifdef INTEL_SYNTAX
	  return AS2 (movzx,%w1,%0);
#else
	  return AS2 (movz%W0%L0,%w1,%0);
#endif
	}

      if (INTVAL (operands[2]) == 0xff && REG_P (operands[0])
	  && !(REG_P (operands[1]) && NON_QI_REG_P (operands[1]))
	  && (! REG_P (operands[1])
	      || REGNO (operands[0]) != 0 || REGNO (operands[1]) != 0)
	  && (!TARGET_ZERO_EXTEND_WITH_AND || ! rtx_equal_p (operands[0], operands[1])))
	{
	  /* ??? tege: Should forget CC_STATUS only if we clobber a
	     remembered operand.  Fix that later.  */
	  CC_STATUS_INIT;
#ifdef INTEL_SYNTAX
	  return AS2 (movzx,%b1,%0);
#else
	  return AS2 (movz%B0%L0,%b1,%0);
#endif
	}

      if (QI_REG_P (operands[0]) && ~(INTVAL (operands[2]) | 0xff) == 0)
	{
	  CC_STATUS_INIT;

	  if (INTVAL (operands[2]) == 0xffffff00)
	    {
	      operands[2] = const0_rtx;
	      return AS2 (mov%B0,%2,%b0);
	    }

	  operands[2] = GEN_INT (INTVAL (operands[2]) & 0xff);
	  return AS2 (and%B0,%2,%b0);
	}

      if (QI_REG_P (operands[0]) && ~(INTVAL (operands[2]) | 0xff00) == 0)
	{
	  CC_STATUS_INIT;

	  if (INTVAL (operands[2]) == 0xffff00ff)
	    {
	      operands[2] = const0_rtx;
	      return AS2 (mov%B0,%2,%h0);
	    }

	  operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff);
	  return AS2 (and%B0,%2,%h0);
	}

      if (GET_CODE (operands[0]) == MEM && INTVAL (operands[2]) == 0xffff0000)
        {
	  operands[2] = const0_rtx;
	  return AS2 (mov%W0,%2,%w0);
	}
    }

  return AS2 (and%L0,%2,%0);
}")

(define_insn "andhi3"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,r")
	(and:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0")
		(match_operand:HI 2 "general_operand" "ri,rm")))]
  ""
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT
      && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))
    {
      /* Can we ignore the upper byte? */
      if ((! REG_P (operands[0]) || QI_REG_P (operands[0]))
	  && (INTVAL (operands[2]) & 0xff00) == 0xff00)
	{
	  CC_STATUS_INIT;

	  if ((INTVAL (operands[2]) & 0xff) == 0)
	    {
	      operands[2] = const0_rtx;
	      return AS2 (mov%B0,%2,%b0);
	    }

	  operands[2] = GEN_INT (INTVAL (operands[2]) & 0xff);
	  return AS2 (and%B0,%2,%b0);
	}

      /* Can we ignore the lower byte? */
      /* ??? what about offsettable memory references? */
      if (QI_REG_P (operands[0]) && (INTVAL (operands[2]) & 0xff) == 0xff)
	{
	  CC_STATUS_INIT;

	  if ((INTVAL (operands[2]) & 0xff00) == 0)
	    {
	      operands[2] = const0_rtx;
	      return AS2 (mov%B0,%2,%h0);
	    }

	  operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff);
	  return AS2 (and%B0,%2,%h0);
	}
    }

  return AS2 (and%W0,%2,%0);
}")

(define_insn "andqi3"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,q")
	(and:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0")
		(match_operand:QI 2 "general_operand" "qn,qmn")))]
  ""
  "* return AS2 (and%B0,%2,%0);")

/* I am nervous about these two.. add them later..
;I presume this means that we have something in say op0= eax which is small
;and we want to and it with memory so we can do this by just an
;andb m,%al  and have success.
(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=r")
	(and:SI (zero_extend:SI
		 (match_operand:HI 1 "nonimmediate_operand" "rm"))
		(match_operand:SI 2 "general_operand" "0")))]
  "GET_CODE (operands[2]) == CONST_INT
   && (unsigned int) INTVAL (operands[2]) < (1 << GET_MODE_BITSIZE (HImode))"
  "and%W0 %1,%0")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=q")
	(and:SI
	 (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "qm"))
		(match_operand:SI 2 "register_operand" "0")))]
  "GET_CODE (operands[2]) == CONST_INT
   && (unsigned int) INTVAL (operands[2]) < (1 << GET_MODE_BITSIZE (QImode))"
  "and%L0 %1,%0")

*/

;;- Bit set (inclusive or) instructions

;; ??? What if we only change one byte of an offsettable memory reference?
(define_insn "iorsi3"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,r")
	(ior:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0")
		(match_operand:SI 2 "general_operand" "ri,rm")))]
  ""
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT
      && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))
    {
      if ((! REG_P (operands[0]) || QI_REG_P (operands[0]))
	  && (INTVAL (operands[2]) & ~0xff) == 0)
	{
	  CC_STATUS_INIT;

	  if (INTVAL (operands[2]) == 0xff)
	    return AS2 (mov%B0,%2,%b0);

	  return AS2 (or%B0,%2,%b0);
	}

      if (QI_REG_P (operands[0]) && (INTVAL (operands[2]) & ~0xff00) == 0)
	{
	  CC_STATUS_INIT;
	  operands[2] = GEN_INT (INTVAL (operands[2]) >> 8);

	  if (INTVAL (operands[2]) == 0xff)
	    return AS2 (mov%B0,%2,%h0);

	  return AS2 (or%B0,%2,%h0);
	}
    }

  return AS2 (or%L0,%2,%0);
}")

(define_insn "iorhi3"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,r")
	(ior:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0")
		(match_operand:HI 2 "general_operand" "ri,rm")))]
  ""
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT
      && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))
    {
      /* Can we ignore the upper byte? */
      if ((! REG_P (operands[0]) || QI_REG_P (operands[0]))
	  && (INTVAL (operands[2]) & 0xff00) == 0)
	{
	  CC_STATUS_INIT;
	  if (INTVAL (operands[2]) & 0xffff0000)
	    operands[2] = GEN_INT (INTVAL (operands[2]) & 0xffff);

	  if (INTVAL (operands[2]) == 0xff)
	    return AS2 (mov%B0,%2,%b0);

	  return AS2 (or%B0,%2,%b0);
	}

      /* Can we ignore the lower byte? */
      /* ??? what about offsettable memory references? */
      if (QI_REG_P (operands[0])
	  && (INTVAL (operands[2]) & 0xff) == 0)
	{
	  CC_STATUS_INIT;
	  operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff);

	  if (INTVAL (operands[2]) == 0xff)
	    return AS2 (mov%B0,%2,%h0);

	  return AS2 (or%B0,%2,%h0);
	}
    }

  return AS2 (or%W0,%2,%0);
}")

(define_insn "iorqi3"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,q")
	(ior:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0")
		(match_operand:QI 2 "general_operand" "qn,qmn")))]
  ""
  "* return AS2 (or%B0,%2,%0);")

;;- xor instructions

;; ??? What if we only change one byte of an offsettable memory reference?
(define_insn "xorsi3"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=rm,r")
	(xor:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0")
		(match_operand:SI 2 "general_operand" "ri,rm")))]
  ""
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT
      && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))
    {
      if ((! REG_P (operands[0]) || QI_REG_P (operands[0]))
	  && (INTVAL (operands[2]) & ~0xff) == 0)
	{
	  CC_STATUS_INIT;

	  if (INTVAL (operands[2]) == 0xff)
	    return AS1 (not%B0,%b0);

	  return AS2 (xor%B0,%2,%b0);
	}

      if (QI_REG_P (operands[0]) && (INTVAL (operands[2]) & ~0xff00) == 0)
	{
	  CC_STATUS_INIT;
	  operands[2] = GEN_INT (INTVAL (operands[2]) >> 8);

	  if (INTVAL (operands[2]) == 0xff)
	    return AS1 (not%B0,%h0);

	  return AS2 (xor%B0,%2,%h0);
	}
    }

  return AS2 (xor%L0,%2,%0);
}")

(define_insn "xorhi3"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=rm,r")
	(xor:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0")
		(match_operand:HI 2 "general_operand" "ri,rm")))]
  ""
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT
      && ! (GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))
    {
      /* Can we ignore the upper byte? */
      if ((! REG_P (operands[0]) || QI_REG_P (operands[0]))
	  && (INTVAL (operands[2]) & 0xff00) == 0)
	{
	  CC_STATUS_INIT;
	  if (INTVAL (operands[2]) & 0xffff0000)
	    operands[2] = GEN_INT (INTVAL (operands[2]) & 0xffff);

	  if (INTVAL (operands[2]) == 0xff)
	    return AS1 (not%B0,%b0);

	  return AS2 (xor%B0,%2,%b0);
	}

      /* Can we ignore the lower byte? */
      /* ??? what about offsettable memory references? */
      if (QI_REG_P (operands[0])
	  && (INTVAL (operands[2]) & 0xff) == 0)
	{
	  CC_STATUS_INIT;
	  operands[2] = GEN_INT ((INTVAL (operands[2]) >> 8) & 0xff);

	  if (INTVAL (operands[2]) == 0xff)
	    return AS1 (not%B0,%h0);

	  return AS2 (xor%B0,%2,%h0);
	}
    }

  return AS2 (xor%W0,%2,%0);
}")

(define_insn "xorqi3"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=qm,q")
	(xor:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0")
		(match_operand:QI 2 "general_operand" "qn,qm")))]
  ""
  "* return AS2 (xor%B0,%2,%0);")

;;- negation instructions

(define_insn "negdi2"
  [(set (match_operand:DI 0 "general_operand" "=&ro")
	(neg:DI (match_operand:DI 1 "general_operand" "0")))]
  ""
  "*
{
  rtx xops[2], low[1], high[1];

  CC_STATUS_INIT;

  split_di (operands, 1, low, high);
  xops[0] = const0_rtx;
  xops[1] = high[0];

  output_asm_insn (AS1 (neg%L0,%0), low);
  output_asm_insn (AS2 (adc%L1,%0,%1), xops);
  output_asm_insn (AS1 (neg%L0,%0), high);
  RET;
}")

(define_insn "negsi2"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=rm")
	(neg:SI (match_operand:SI 1 "nonimmediate_operand" "0")))]
  ""
  "neg%L0 %0")

(define_insn "neghi2"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=rm")
	(neg:HI (match_operand:HI 1 "nonimmediate_operand" "0")))]
  ""
  "neg%W0 %0")

(define_insn "negqi2"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=qm")
	(neg:QI (match_operand:QI 1 "nonimmediate_operand" "0")))]
  ""
  "neg%B0 %0")

(define_insn "negsf2"
  [(set (match_operand:SF 0 "register_operand" "=f")
	(neg:SF (match_operand:SF 1 "register_operand" "0")))]
  "TARGET_80387"
  "fchs")

(define_insn "negdf2"
  [(set (match_operand:DF 0 "register_operand" "=f")
	(neg:DF (match_operand:DF 1 "register_operand" "0")))]
  "TARGET_80387"
  "fchs")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=f")
	(neg:DF (float_extend:DF (match_operand:SF 1 "register_operand" "0"))))]
  "TARGET_80387"
  "fchs")

(define_insn "negxf2"
  [(set (match_operand:XF 0 "register_operand" "=f")
	(neg:XF (match_operand:XF 1 "register_operand" "0")))]
  "TARGET_80387"
  "fchs")

(define_insn ""
  [(set (match_operand:XF 0 "register_operand" "=f")
	(neg:XF (float_extend:XF (match_operand:DF 1 "register_operand" "0"))))]
  "TARGET_80387"
  "fchs")

;; Absolute value instructions

(define_insn "abssf2"
  [(set (match_operand:SF 0 "register_operand" "=f")
	(abs:SF (match_operand:SF 1 "register_operand" "0")))]
  "TARGET_80387"
  "fabs"
  [(set_attr "type" "fpop")])

(define_insn "absdf2"
  [(set (match_operand:DF 0 "register_operand" "=f")
	(abs:DF (match_operand:DF 1 "register_operand" "0")))]
  "TARGET_80387"
  "fabs"
  [(set_attr "type" "fpop")])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=f")
	(abs:DF (float_extend:DF (match_operand:SF 1 "register_operand" "0"))))]
  "TARGET_80387"
  "fabs"
  [(set_attr "type" "fpop")])

(define_insn "absxf2"
  [(set (match_operand:XF 0 "register_operand" "=f")
	(abs:XF (match_operand:XF 1 "register_operand" "0")))]
  "TARGET_80387"
  "fabs"
  [(set_attr "type" "fpop")])

(define_insn ""
  [(set (match_operand:XF 0 "register_operand" "=f")
	(abs:XF (float_extend:XF (match_operand:DF 1 "register_operand" "0"))))]
  "TARGET_80387"
  "fabs"
  [(set_attr "type" "fpop")])

(define_insn "sqrtsf2"
  [(set (match_operand:SF 0 "register_operand" "=f")
	(sqrt:SF (match_operand:SF 1 "register_operand" "0")))]
  "! TARGET_NO_FANCY_MATH_387 && TARGET_80387"
  "fsqrt")

(define_insn "sqrtdf2"
  [(set (match_operand:DF 0 "register_operand" "=f")
	(sqrt:DF (match_operand:DF 1 "register_operand" "0")))]
  "! TARGET_NO_FANCY_MATH_387 && TARGET_80387
   && (TARGET_IEEE_FP || flag_fast_math) "
  "fsqrt")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=f")
	(sqrt:DF (float_extend:DF
		  (match_operand:SF 1 "register_operand" "0"))))]
  "! TARGET_NO_FANCY_MATH_387 && TARGET_80387"
  "fsqrt")

(define_insn "sqrtxf2"
  [(set (match_operand:XF 0 "register_operand" "=f")
	(sqrt:XF (match_operand:XF 1 "register_operand" "0")))]
  "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 
   && (TARGET_IEEE_FP || flag_fast_math) "
  "fsqrt")

(define_insn ""
  [(set (match_operand:XF 0 "register_operand" "=f")
	(sqrt:XF (float_extend:XF
		  (match_operand:DF 1 "register_operand" "0"))))]
  "! TARGET_NO_FANCY_MATH_387 && TARGET_80387"
  "fsqrt")

(define_insn ""
  [(set (match_operand:XF 0 "register_operand" "=f")
	(sqrt:XF (float_extend:XF
		  (match_operand:SF 1 "register_operand" "0"))))]
  "! TARGET_NO_FANCY_MATH_387 && TARGET_80387"
  "fsqrt")

(define_insn "sindf2"
  [(set (match_operand:DF 0 "register_operand" "=f")
	(unspec:DF [(match_operand:DF 1 "register_operand" "0")] 1))]
  "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_fast_math"
  "fsin")

(define_insn "sinsf2"
  [(set (match_operand:SF 0 "register_operand" "=f")
	(unspec:SF [(match_operand:SF 1 "register_operand" "0")] 1))]
  "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_fast_math"
  "fsin")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=f")
	(unspec:DF [(float_extend:DF
		     (match_operand:SF 1 "register_operand" "0"))] 1))]
  "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_fast_math"
  "fsin")

(define_insn "sinxf2"
  [(set (match_operand:XF 0 "register_operand" "=f")
	(unspec:XF [(match_operand:XF 1 "register_operand" "0")] 1))]
  "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_fast_math"
  "fsin")

(define_insn "cosdf2"
  [(set (match_operand:DF 0 "register_operand" "=f")
	(unspec:DF [(match_operand:DF 1 "register_operand" "0")] 2))]
  "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_fast_math"
  "fcos")

(define_insn "cossf2"
  [(set (match_operand:SF 0 "register_operand" "=f")
	(unspec:SF [(match_operand:SF 1 "register_operand" "0")] 2))]
  "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_fast_math"
  "fcos")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=f")
	(unspec:DF [(float_extend:DF
		     (match_operand:SF 1 "register_operand" "0"))] 2))]
  "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_fast_math"
  "fcos")

(define_insn "cosxf2"
  [(set (match_operand:XF 0 "register_operand" "=f")
	(unspec:XF [(match_operand:XF 1 "register_operand" "0")] 2))]
  "! TARGET_NO_FANCY_MATH_387 && TARGET_80387 && flag_fast_math"
  "fcos")

;;- one complement instructions

(define_insn "one_cmplsi2"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=rm")
	(not:SI (match_operand:SI 1 "nonimmediate_operand" "0")))]
  ""
  "not%L0 %0")

(define_insn "one_cmplhi2"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=rm")
	(not:HI (match_operand:HI 1 "nonimmediate_operand" "0")))]
  ""
  "not%W0 %0")

(define_insn "one_cmplqi2"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=qm")
	(not:QI (match_operand:QI 1 "nonimmediate_operand" "0")))]
  ""
  "not%B0 %0")

;;- arithmetic shift instructions

;; DImode shifts are implemented using the i386 "shift double" opcode,
;; which is written as "sh[lr]d[lw] imm,reg,reg/mem".  If the shift count
;; is variable, then the count is in %cl and the "imm" operand is dropped
;; from the assembler input.

;; This instruction shifts the target reg/mem as usual, but instead of
;; shifting in zeros, bits are shifted in from reg operand.  If the insn
;; is a left shift double, bits are taken from the high order bits of
;; reg, else if the insn is a shift right double, bits are taken from the
;; low order bits of reg.  So if %eax is "1234" and %edx is "5678",
;; "shldl $8,%edx,%eax" leaves %edx unchanged and sets %eax to "2345".

;; Since sh[lr]d does not change the `reg' operand, that is done
;; separately, making all shifts emit pairs of shift double and normal
;; shift.  Since sh[lr]d does not shift more than 31 bits, and we wish to
;; support a 63 bit shift, each shift where the count is in a reg expands
;; to three pairs.  If the overall shift is by N bits, then the first two
;; pairs shift by N / 2 and the last pair by N & 1.

;; If the shift count is a constant, we need never emit more than one
;; shift pair, instead using moves and sign extension for counts greater
;; than 31.

(define_expand "ashldi3"
  [(set (match_operand:DI 0 "register_operand" "")
	(ashift:DI (match_operand:DI 1 "register_operand" "")
		   (match_operand:QI 2 "nonmemory_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[2]) != CONST_INT
      || ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J'))
    {
      operands[2] = copy_to_mode_reg (QImode, operands[2]);
      emit_insn (gen_ashldi3_non_const_int (operands[0], operands[1],
					    operands[2]));
    }
  else
    emit_insn (gen_ashldi3_const_int (operands[0], operands[1], operands[2]));

  DONE;
}")

(define_insn "ashldi3_const_int"
  [(set (match_operand:DI 0 "register_operand" "=&r")
	(ashift:DI (match_operand:DI 1 "register_operand" "0")
		   (match_operand:QI 2 "const_int_operand" "J")))]
  ""
  "*
{
  rtx xops[4], low[1], high[1];

  CC_STATUS_INIT;

  split_di (operands, 1, low, high);
  xops[0] = operands[2];
  xops[1] = const1_rtx;
  xops[2] = low[0];
  xops[3] = high[0];

  if (INTVAL (xops[0]) > 31)
    {
      output_asm_insn (AS2 (mov%L3,%2,%3), xops);	/* Fast shift by 32 */
      output_asm_insn (AS2 (xor%L2,%2,%2), xops);

      if (INTVAL (xops[0]) > 32)
        {
	  xops[0] = GEN_INT (INTVAL (xops[0]) - 32);
	  output_asm_insn (AS2 (sal%L3,%0,%3), xops); /* Remaining shift */
	}
    }
  else
    {
      output_asm_insn (AS3 (shld%L3,%0,%2,%3), xops);
      output_asm_insn (AS2 (sal%L2,%0,%2), xops);
    }
  RET;
}")

(define_insn "ashldi3_non_const_int"
  [(set (match_operand:DI 0 "register_operand" "=&r")
	(ashift:DI (match_operand:DI 1 "register_operand" "0")
		   (match_operand:QI 2 "register_operand" "c")))
   (clobber (match_dup 2))]
  ""
  "*
{
  rtx xops[4], low[1], high[1];

  CC_STATUS_INIT;

  split_di (operands, 1, low, high);
  xops[0] = operands[2];
  xops[1] = const1_rtx;
  xops[2] = low[0];
  xops[3] = high[0];

  output_asm_insn (AS2 (ror%B0,%1,%0), xops);	/* shift count / 2 */

  output_asm_insn (AS3_SHIFT_DOUBLE (shld%L3,%0,%2,%3), xops);
  output_asm_insn (AS2 (sal%L2,%0,%2), xops);
  output_asm_insn (AS3_SHIFT_DOUBLE (shld%L3,%0,%2,%3), xops);
  output_asm_insn (AS2 (sal%L2,%0,%2), xops);

  xops[1] = GEN_INT (7);			/* shift count & 1 */

  output_asm_insn (AS2 (shr%B0,%1,%0), xops);

  output_asm_insn (AS3_SHIFT_DOUBLE (shld%L3,%0,%2,%3), xops);
  output_asm_insn (AS2 (sal%L2,%0,%2), xops);

  RET;
}")

;; On i386 and i486, "addl reg,reg" is faster than "sall $1,reg"
;; On i486, movl/sall appears slightly faster than leal, but the leal
;; is smaller - use leal for now unless the shift count is 1.

(define_insn "ashlsi3"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=r,rm")
	(ashift:SI (match_operand:SI 1 "nonimmediate_operand" "r,0")
		   (match_operand:SI 2 "nonmemory_operand" "M,cI")))]
  ""
  "*
{
  if (REG_P (operands[0]) && REGNO (operands[0]) != REGNO (operands[1]))
    {
      if (TARGET_DOUBLE_WITH_ADD && INTVAL (operands[2]) == 1)
	{
	  output_asm_insn (AS2 (mov%L0,%1,%0), operands);
	  return AS2 (add%L0,%1,%0);
	}
      else
        {
          CC_STATUS_INIT;

	  if (operands[1] == stack_pointer_rtx)
	    {
	      output_asm_insn (AS2 (mov%L0,%1,%0), operands);
	      operands[1] = operands[0];
	    }
          operands[1] = gen_rtx (MULT, SImode, operands[1],
				 GEN_INT (1 << INTVAL (operands[2])));
	  return AS2 (lea%L0,%a1,%0);
	}
    }

  if (REG_P (operands[2]))
    return AS2 (sal%L0,%b2,%0);

  if (REG_P (operands[0]) && operands[2] == const1_rtx)
    return AS2 (add%L0,%0,%0);

  return AS2 (sal%L0,%2,%0);
}")

(define_insn "ashlhi3"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=rm")
	(ashift:HI (match_operand:HI 1 "nonimmediate_operand" "0")
		   (match_operand:HI 2 "nonmemory_operand" "cI")))]
  ""
  "*
{
  if (REG_P (operands[2]))
    return AS2 (sal%W0,%b2,%0);

  if (REG_P (operands[0]) && operands[2] == const1_rtx)
    return AS2 (add%W0,%0,%0);

  return AS2 (sal%W0,%2,%0);
}")

(define_insn "ashlqi3"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=qm")
	(ashift:QI (match_operand:QI 1 "nonimmediate_operand" "0")
		   (match_operand:QI 2 "nonmemory_operand" "cI")))]
  ""
  "*
{
  if (REG_P (operands[2]))
    return AS2 (sal%B0,%b2,%0);

  if (REG_P (operands[0]) && operands[2] == const1_rtx)
    return AS2 (add%B0,%0,%0);

  return AS2 (sal%B0,%2,%0);
}")

;; See comment above `ashldi3' about how this works.

(define_expand "ashrdi3"
  [(set (match_operand:DI 0 "register_operand" "")
	(ashiftrt:DI (match_operand:DI 1 "register_operand" "")
		     (match_operand:QI 2 "nonmemory_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[2]) != CONST_INT
      || ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J'))
    {
      operands[2] = copy_to_mode_reg (QImode, operands[2]);
      emit_insn (gen_ashrdi3_non_const_int (operands[0], operands[1],
					    operands[2]));
    }
  else
    emit_insn (gen_ashrdi3_const_int (operands[0], operands[1], operands[2]));

  DONE;
}")

(define_insn "ashldi3_32"
  [(set (match_operand:DI 0 "nonimmediate_operand" "=r,m")
	(ashift:DI (match_operand:DI 1 "nonimmediate_operand" "ro,r")
		   (const_int 32)))]
  ""
  "*
{
  rtx low[2], high[2], xops[4];

  split_di (operands, 2, low, high);
  xops[0] = high[0];
  xops[1] = low[1];
  xops[2] = low[0];
  xops[3] = const0_rtx;
  if (!rtx_equal_p (xops[0], xops[1]))
    output_asm_insn (AS2 (mov%L0,%1,%0), xops);

  if (GET_CODE (low[0]) == MEM)
    output_asm_insn (AS2 (mov%L2,%3,%2), xops);
  else
    output_asm_insn (AS2 (xor%L2,%2,%2), xops);

  RET;
}")

(define_insn "ashrdi3_const_int"
  [(set (match_operand:DI 0 "register_operand" "=&r")
	(ashiftrt:DI (match_operand:DI 1 "register_operand" "0")
		     (match_operand:QI 2 "const_int_operand" "J")))]
  ""
  "*
{
  rtx xops[4], low[1], high[1];

  CC_STATUS_INIT;

  split_di (operands, 1, low, high);
  xops[0] = operands[2];
  xops[1] = const1_rtx;
  xops[2] = low[0];
  xops[3] = high[0];

  if (INTVAL (xops[0]) > 31)
    {
      xops[1] = GEN_INT (31);
      output_asm_insn (AS2 (mov%L2,%3,%2), xops);
      output_asm_insn (AS2 (sar%L3,%1,%3), xops);	/* shift by 32 */

      if (INTVAL (xops[0]) > 32)
        {
	  xops[0] = GEN_INT (INTVAL (xops[0]) - 32);
	  output_asm_insn (AS2 (sar%L2,%0,%2), xops); /* Remaining shift */
	}
    }
  else
    {
      output_asm_insn (AS3 (shrd%L2,%0,%3,%2), xops);
      output_asm_insn (AS2 (sar%L3,%0,%3), xops);
    }

  RET;
}")

(define_insn "ashrdi3_non_const_int"
  [(set (match_operand:DI 0 "register_operand" "=&r")
	(ashiftrt:DI (match_operand:DI 1 "register_operand" "0")
		     (match_operand:QI 2 "register_operand" "c")))
   (clobber (match_dup 2))]
  ""
  "*
{
  rtx xops[4], low[1], high[1];

  CC_STATUS_INIT;

  split_di (operands, 1, low, high);
  xops[0] = operands[2];
  xops[1] = const1_rtx;
  xops[2] = low[0];
  xops[3] = high[0];

  output_asm_insn (AS2 (ror%B0,%1,%0), xops);	/* shift count / 2 */

  output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops);
  output_asm_insn (AS2 (sar%L3,%0,%3), xops);
  output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops);
  output_asm_insn (AS2 (sar%L3,%0,%3), xops);

  xops[1] = GEN_INT (7);			/* shift count & 1 */

  output_asm_insn (AS2 (shr%B0,%1,%0), xops);

  output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops);
  output_asm_insn (AS2 (sar%L3,%0,%3), xops);

  RET;
}")

(define_insn "ashrsi3"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=rm")
	(ashiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "0")
		     (match_operand:SI 2 "nonmemory_operand" "cI")))]
  ""
  "*
{
  if (REG_P (operands[2]))
    return AS2 (sar%L0,%b2,%0);
  else
    return AS2 (sar%L0,%2,%0);
}")

(define_insn "ashrhi3"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=rm")
	(ashiftrt:HI (match_operand:HI 1 "nonimmediate_operand" "0")
		     (match_operand:HI 2 "nonmemory_operand" "cI")))]
  ""
  "*
{
  if (REG_P (operands[2]))
    return AS2 (sar%W0,%b2,%0);
  else
    return AS2 (sar%W0,%2,%0);
}")

(define_insn "ashrqi3"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=qm")
	(ashiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "0")
		     (match_operand:QI 2 "nonmemory_operand" "cI")))]
  ""
  "*
{
  if (REG_P (operands[2]))
    return AS2 (sar%B0,%b2,%0);
  else
    return AS2 (sar%B0,%2,%0);
}")

;;- logical shift instructions

;; See comment above `ashldi3' about how this works.

(define_expand "lshrdi3"
  [(set (match_operand:DI 0 "register_operand" "")
	(lshiftrt:DI (match_operand:DI 1 "register_operand" "")
		     (match_operand:QI 2 "nonmemory_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[2]) != CONST_INT
      || ! CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), 'J'))
    {
      operands[2] = copy_to_mode_reg (QImode, operands[2]);
      emit_insn (gen_lshrdi3_non_const_int (operands[0], operands[1],
					    operands[2]));
    }
  else
    emit_insn (gen_lshrdi3_const_int (operands[0], operands[1], operands[2]));

  DONE;
}")

(define_insn "lshrdi3_32"
  [(set (match_operand:DI 0 "nonimmediate_operand" "=r,m")
	(lshiftrt:DI (match_operand:DI 1 "nonimmediate_operand" "ro,r")
		     (const_int 32)))]
  ""
  "*
{
  rtx low[2], high[2], xops[4];

  split_di (operands, 2, low, high);
  xops[0] = low[0];
  xops[1] = high[1];
  xops[2] = high[0];
  xops[3] = const0_rtx;
  if (!rtx_equal_p (xops[0], xops[1]))
    output_asm_insn (AS2 (mov%L0,%1,%0), xops);

  if (GET_CODE (low[0]) == MEM)
    output_asm_insn (AS2 (mov%L2,%3,%2), xops);
  else
    output_asm_insn (AS2 (xor%L2,%2,%2), xops);

  RET;
}")

(define_insn "lshrdi3_const_int"
  [(set (match_operand:DI 0 "register_operand" "=&r")
	(lshiftrt:DI (match_operand:DI 1 "register_operand" "0")
		     (match_operand:QI 2 "const_int_operand" "J")))]
  ""
  "*
{
  rtx xops[4], low[1], high[1];

  CC_STATUS_INIT;

  split_di (operands, 1, low, high);
  xops[0] = operands[2];
  xops[1] = const1_rtx;
  xops[2] = low[0];
  xops[3] = high[0];

  if (INTVAL (xops[0]) > 31)
    {
      output_asm_insn (AS2 (mov%L2,%3,%2), xops);	/* Fast shift by 32 */
      output_asm_insn (AS2 (xor%L3,%3,%3), xops);

      if (INTVAL (xops[0]) > 32)
        {
	  xops[0] = GEN_INT (INTVAL (xops[0]) - 32);
	  output_asm_insn (AS2 (shr%L2,%0,%2), xops); /* Remaining shift */
	}
    }
  else
    {
      output_asm_insn (AS3 (shrd%L2,%0,%3,%2), xops);
      output_asm_insn (AS2 (shr%L3,%0,%3), xops);
    }

  RET;
}")

(define_insn "lshrdi3_non_const_int"
  [(set (match_operand:DI 0 "register_operand" "=&r")
	(lshiftrt:DI (match_operand:DI 1 "register_operand" "0")
		     (match_operand:QI 2 "register_operand" "c")))
   (clobber (match_dup 2))]
  ""
  "*
{
  rtx xops[4], low[1], high[1];

  CC_STATUS_INIT;

  split_di (operands, 1, low, high);
  xops[0] = operands[2];
  xops[1] = const1_rtx;
  xops[2] = low[0];
  xops[3] = high[0];

  output_asm_insn (AS2 (ror%B0,%1,%0), xops);	/* shift count / 2 */

  output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops);
  output_asm_insn (AS2 (shr%L3,%0,%3), xops);
  output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops);
  output_asm_insn (AS2 (shr%L3,%0,%3), xops);

  xops[1] = GEN_INT (7);			/* shift count & 1 */

  output_asm_insn (AS2 (shr%B0,%1,%0), xops);

  output_asm_insn (AS3_SHIFT_DOUBLE (shrd%L2,%0,%3,%2), xops);
  output_asm_insn (AS2 (shr%L3,%0,%3), xops);

  RET;
}")

(define_insn "lshrsi3"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=rm")
	(lshiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "0")
		     (match_operand:SI 2 "nonmemory_operand" "cI")))]
  ""
  "*
{
  if (REG_P (operands[2]))
    return AS2 (shr%L0,%b2,%0);
  else
    return AS2 (shr%L0,%2,%1);
}")

(define_insn "lshrhi3"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=rm")
	(lshiftrt:HI (match_operand:HI 1 "nonimmediate_operand" "0")
		     (match_operand:HI 2 "nonmemory_operand" "cI")))]
  ""
  "*
{
  if (REG_P (operands[2]))
    return AS2 (shr%W0,%b2,%0);
  else
    return AS2 (shr%W0,%2,%0);
}")

(define_insn "lshrqi3"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=qm")
	(lshiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "0")
		     (match_operand:QI 2 "nonmemory_operand" "cI")))]
  ""
  "*
{
  if (REG_P (operands[2]))
    return AS2 (shr%B0,%b2,%0);
  else
    return AS2 (shr%B0,%2,%0);
}")

;;- rotate instructions

(define_insn "rotlsi3"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=rm")
	(rotate:SI (match_operand:SI 1 "nonimmediate_operand" "0")
		   (match_operand:SI 2 "nonmemory_operand" "cI")))]
  ""
  "*
{
  if (REG_P (operands[2]))
    return AS2 (rol%L0,%b2,%0);
  else
    return AS2 (rol%L0,%2,%0);
}")

(define_insn "rotlhi3"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=rm")
	(rotate:HI (match_operand:HI 1 "nonimmediate_operand" "0")
		   (match_operand:HI 2 "nonmemory_operand" "cI")))]
  ""
  "*
{
  if (REG_P (operands[2]))
    return AS2 (rol%W0,%b2,%0);
  else
    return AS2 (rol%W0,%2,%0);
}")

(define_insn "rotlqi3"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=qm")
	(rotate:QI (match_operand:QI 1 "nonimmediate_operand" "0")
		   (match_operand:QI 2 "nonmemory_operand" "cI")))]
  ""
  "*
{
  if (REG_P (operands[2]))
    return AS2 (rol%B0,%b2,%0);
  else
    return AS2 (rol%B0,%2,%0);
}")

(define_insn "rotrsi3"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=rm")
	(rotatert:SI (match_operand:SI 1 "nonimmediate_operand" "0")
		     (match_operand:SI 2 "nonmemory_operand" "cI")))]
  ""
  "*
{
  if (REG_P (operands[2]))
    return AS2 (ror%L0,%b2,%0);
  else
    return AS2 (ror%L0,%2,%0);
}")

(define_insn "rotrhi3"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=rm")
	(rotatert:HI (match_operand:HI 1 "nonimmediate_operand" "0")
		     (match_operand:HI 2 "nonmemory_operand" "cI")))]
  ""
  "*
{
  if (REG_P (operands[2]))
    return AS2 (ror%W0,%b2,%0);
  else
    return AS2 (ror%W0,%2,%0);
}")

(define_insn "rotrqi3"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=qm")
	(rotatert:QI (match_operand:QI 1 "nonimmediate_operand" "0")
		     (match_operand:QI 2 "nonmemory_operand" "cI")))]
  ""
  "*
{
  if (REG_P (operands[2]))
    return AS2 (ror%B0,%b2,%0);
  else
    return AS2 (ror%B0,%2,%0);
}")

/*
;; This usually looses.  But try a define_expand to recognize a few case
;; we can do efficiently, such as accessing the "high" QImode registers,
;; %ah, %bh, %ch, %dh.
;; ??? Note this has a botch on the mode of operand 0, which needs to be
;; fixed if this is ever enabled.
(define_insn "insv"
  [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+&r")
			 (match_operand:SI 1 "immediate_operand" "i")
			 (match_operand:SI 2 "immediate_operand" "i"))
	(match_operand:SI 3 "nonmemory_operand" "ri"))]
  ""
  "*
{
  if (INTVAL (operands[1]) + INTVAL (operands[2]) > GET_MODE_BITSIZE (SImode))
    abort ();
  if (GET_CODE (operands[3]) == CONST_INT)
    {
      unsigned int mask = (1 << INTVAL (operands[1])) - 1; 
      operands[1] = GEN_INT (~(mask << INTVAL (operands[2])));
      output_asm_insn (AS2 (and%L0,%1,%0), operands);
      operands[3] = GEN_INT (INTVAL (operands[3]) << INTVAL (operands[2]));
      output_asm_insn (AS2 (or%L0,%3,%0), operands);
    }
  else
    {
      operands[0] = gen_rtx (REG, SImode, REGNO (operands[0]));
      if (INTVAL (operands[2]))
	output_asm_insn (AS2 (ror%L0,%2,%0), operands);
      output_asm_insn (AS3 (shrd%L0,%1,%3,%0), operands);
      operands[2] = GEN_INT (BITS_PER_WORD
			     - INTVAL (operands[1]) - INTVAL (operands[2]));
      if (INTVAL (operands[2]))
	output_asm_insn (AS2 (ror%L0,%2,%0), operands);
    }
  RET;
}")
*/
/*
;; ??? There are problems with the mode of operand[3].  The point of this
;; is to represent an HImode move to a "high byte" register.

(define_expand "insv"
  [(set (zero_extract:SI (match_operand:SI 0 "general_operand" "")
			 (match_operand:SI 1 "immediate_operand" "")
			 (match_operand:SI 2 "immediate_operand" ""))
	(match_operand:QI 3 "nonmemory_operand" "ri"))]
  ""
  "
{
  if (GET_CODE (operands[1]) != CONST_INT
      || GET_CODE (operands[2]) != CONST_INT)
    FAIL;

  if (! (INTVAL (operands[1]) == 8
	 && (INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 0))
      && ! INTVAL (operands[1]) == 1)
    FAIL;
}")
*/

;; On i386, the register count for a bit operation is *not* truncated,
;; so SHIFT_COUNT_TRUNCATED must not be defined.

;; On i486, the shift & or/and code is faster than bts or btr.  If
;; operands[0] is a MEM, the bt[sr] is half as fast as the normal code.

;; On i386, bts is a little faster if operands[0] is a reg, and a
;; little slower if operands[0] is a MEM, than the shift & or/and code.
;; Use bts & btr, since they reload better.

;; General bit set and clear.
(define_insn ""
  [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+rm")
			 (const_int 1)
			 (match_operand:SI 2 "register_operand" "r"))
	(match_operand:SI 3 "const_int_operand" "n"))]
  "TARGET_USE_BIT_TEST && GET_CODE (operands[2]) != CONST_INT"
  "*
{
  CC_STATUS_INIT;

  if (INTVAL (operands[3]) == 1)
    return AS2 (bts%L0,%2,%0);
  else
    return AS2 (btr%L0,%2,%0);
}")

;; Bit complement.  See comments on previous pattern.
;; ??? Is this really worthwhile?
(define_insn ""
  [(set (match_operand:SI 0 "nonimmediate_operand" "=rm")
	(xor:SI (ashift:SI (const_int 1)
			   (match_operand:SI 1 "register_operand" "r"))
		(match_operand:SI 2 "nonimmediate_operand" "0")))]
  "TARGET_USE_BIT_TEST && GET_CODE (operands[1]) != CONST_INT"
  "*
{
  CC_STATUS_INIT;

  return AS2 (btc%L0,%1,%0);
}")

(define_insn ""
  [(set (match_operand:SI 0 "nonimmediate_operand" "=rm")
	(xor:SI (match_operand:SI 1 "nonimmediate_operand" "0")
		(ashift:SI (const_int 1)
			   (match_operand:SI 2 "register_operand" "r"))))]
  "TARGET_USE_BIT_TEST && GET_CODE (operands[2]) != CONST_INT"
  "*
{
  CC_STATUS_INIT;

  return AS2 (btc%L0,%2,%0);
}")

;; Recognizers for bit-test instructions.

;; The bt opcode allows a MEM in operands[0].  But on both i386 and
;; i486, it is faster to copy a MEM to REG and then use bt, than to use
;; bt on the MEM directly.

;; ??? The first argument of a zero_extract must not be reloaded, so
;; don't allow a MEM in the operand predicate without allowing it in the
;; constraint.

(define_insn ""
  [(set (cc0) (zero_extract (match_operand:SI 0 "register_operand" "r")
			    (const_int 1)
			    (match_operand:SI 1 "register_operand" "r")))]
  "GET_CODE (operands[1]) != CONST_INT"
  "*
{
  cc_status.flags |= CC_Z_IN_NOT_C;
  return AS2 (bt%L0,%1,%0);
}")

(define_insn ""
  [(set (cc0) (zero_extract (match_operand:SI 0 "register_operand" "r")
			    (match_operand:SI 1 "const_int_operand" "n")
			    (match_operand:SI 2 "const_int_operand" "n")))]
  ""
  "*
{
  unsigned int mask;

  mask = ((1 << INTVAL (operands[1])) - 1) << INTVAL (operands[2]);
  operands[1] = GEN_INT (mask);

  if (QI_REG_P (operands[0]))
    {
      if ((mask & ~0xff) == 0)
        {
	  cc_status.flags |= CC_NOT_NEGATIVE;
	  return AS2 (test%B0,%1,%b0);
	}

      if ((mask & ~0xff00) == 0)
        {
	  cc_status.flags |= CC_NOT_NEGATIVE;
	  operands[1] = GEN_INT (mask >> 8);
	  return AS2 (test%B0,%1,%h0);
	}
    }

  return AS2 (test%L0,%1,%0);
}")

;; ??? All bets are off if operand 0 is a volatile MEM reference.
;; The CPU may access unspecified bytes around the actual target byte.

(define_insn ""
  [(set (cc0) (zero_extract (match_operand:QI 0 "memory_operand" "m")
			    (match_operand:SI 1 "const_int_operand" "n")
			    (match_operand:SI 2 "const_int_operand" "n")))]
  "GET_CODE (operands[0]) != MEM || ! MEM_VOLATILE_P (operands[0])"
  "*
{
  unsigned int mask;

  mask = ((1 << INTVAL (operands[1])) - 1) << INTVAL (operands[2]);
  operands[1] = GEN_INT (mask);

  if (! REG_P (operands[0]) || QI_REG_P (operands[0]))
    {
      if ((mask & ~0xff) == 0)
        {
	  cc_status.flags |= CC_NOT_NEGATIVE;
	  return AS2 (test%B0,%1,%b0);
	}

      if ((mask & ~0xff00) == 0)
        {
	  cc_status.flags |= CC_NOT_NEGATIVE;
	  operands[1] = GEN_INT (mask >> 8);

	  if (QI_REG_P (operands[0]))
	    return AS2 (test%B0,%1,%h0);
	  else
	    {
	      operands[0] = adj_offsettable_operand (operands[0], 1);
	      return AS2 (test%B0,%1,%b0);
	    }
	}

      if (GET_CODE (operands[0]) == MEM && (mask & ~0xff0000) == 0)
        {
	  cc_status.flags |= CC_NOT_NEGATIVE;
	  operands[1] = GEN_INT (mask >> 16);
	  operands[0] = adj_offsettable_operand (operands[0], 2);
	  return AS2 (test%B0,%1,%b0);
	}

      if (GET_CODE (operands[0]) == MEM && (mask & ~0xff000000) == 0)
        {
	  cc_status.flags |= CC_NOT_NEGATIVE;
	  operands[1] = GEN_INT (mask >> 24);
	  operands[0] = adj_offsettable_operand (operands[0], 3);
	  return AS2 (test%B0,%1,%b0);
	}
    }

  if (CONSTANT_P (operands[1]) || GET_CODE (operands[0]) == MEM)
    return AS2 (test%L0,%1,%0);

  return AS2 (test%L1,%0,%1);
}")

;; Store-flag instructions.

;; For all sCOND expanders, also expand the compare or test insn that
;; generates cc0.  Generate an equality comparison if `seq' or `sne'.

;; The 386 sCOND opcodes can write to memory.  But a gcc sCOND insn may
;; not have any input reloads.  A MEM write might need an input reload
;; for the address of the MEM.  So don't allow MEM as the SET_DEST.

(define_expand "seq"
  [(match_dup 1)
   (set (match_operand:QI 0 "register_operand" "")
	(eq:QI (cc0) (const_int 0)))]
  ""
  "
{
  if (TARGET_IEEE_FP
      && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT)
    operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1);
  else
    operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
}")

(define_insn ""
  [(set (match_operand:QI 0 "register_operand" "=q")
	(eq:QI (cc0) (const_int 0)))]
  ""
  "*
{
  if (cc_prev_status.flags & CC_Z_IN_NOT_C)
    return AS1 (setnb,%0);
  else
    return AS1 (sete,%0);
}")

(define_expand "sne"
  [(match_dup 1)
   (set (match_operand:QI 0 "register_operand" "")
	(ne:QI (cc0) (const_int 0)))]
  ""
  "
{
  if (TARGET_IEEE_FP
      && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT)
    operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1);
  else
    operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
}")

(define_insn ""
  [(set (match_operand:QI 0 "register_operand" "=q")
	(ne:QI (cc0) (const_int 0)))]
  ""
  "*
{
  if (cc_prev_status.flags & CC_Z_IN_NOT_C)
    return AS1 (setb,%0);
  else
    return AS1 (setne,%0);
}
")

(define_expand "sgt"
  [(match_dup 1)
   (set (match_operand:QI 0 "register_operand" "")
	(gt:QI (cc0) (const_int 0)))]
  ""
  "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")

(define_insn ""
  [(set (match_operand:QI 0 "register_operand" "=q")
	(gt:QI (cc0) (const_int 0)))]
  ""
  "*
{
  if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
    return AS1 (sete,%0);

  OUTPUT_JUMP (\"setg %0\", \"seta %0\", NULL_PTR);
}")

(define_expand "sgtu"
  [(match_dup 1)
   (set (match_operand:QI 0 "register_operand" "")
	(gtu:QI (cc0) (const_int 0)))]
  ""
  "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")

(define_insn ""
  [(set (match_operand:QI 0 "register_operand" "=q")
	(gtu:QI (cc0) (const_int 0)))]
  ""
  "* return \"seta %0\"; ")

(define_expand "slt"
  [(match_dup 1)
   (set (match_operand:QI 0 "register_operand" "")
	(lt:QI (cc0) (const_int 0)))]
  ""
  "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")

(define_insn ""
  [(set (match_operand:QI 0 "register_operand" "=q")
	(lt:QI (cc0) (const_int 0)))]
  ""
  "*
{
  if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
    return AS1 (sete,%0);

  OUTPUT_JUMP (\"setl %0\", \"setb %0\", \"sets %0\");
}")

(define_expand "sltu"
  [(match_dup 1)
   (set (match_operand:QI 0 "register_operand" "")
	(ltu:QI (cc0) (const_int 0)))]
  ""
  "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")

(define_insn ""
  [(set (match_operand:QI 0 "register_operand" "=q")
	(ltu:QI (cc0) (const_int 0)))]
  ""
  "* return \"setb %0\"; ")

(define_expand "sge"
  [(match_dup 1)
   (set (match_operand:QI 0 "register_operand" "")
	(ge:QI (cc0) (const_int 0)))]
  ""
  "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")

(define_insn ""
  [(set (match_operand:QI 0 "register_operand" "=q")
	(ge:QI (cc0) (const_int 0)))]
  ""
  "*
{
  if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
    return AS1 (sete,%0);

  OUTPUT_JUMP (\"setge %0\", \"setae %0\", \"setns %0\");
}")

(define_expand "sgeu"
  [(match_dup 1)
   (set (match_operand:QI 0 "register_operand" "")
	(geu:QI (cc0) (const_int 0)))]
  ""
  "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")

(define_insn ""
  [(set (match_operand:QI 0 "register_operand" "=q")
	(geu:QI (cc0) (const_int 0)))]
  ""
  "* return \"setae %0\"; ")

(define_expand "sle"
  [(match_dup 1)
   (set (match_operand:QI 0 "register_operand" "")
	(le:QI (cc0) (const_int 0)))]
  ""
  "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")

(define_insn ""
  [(set (match_operand:QI 0 "register_operand" "=q")
	(le:QI (cc0) (const_int 0)))]
  ""
  "*
{
  if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
    return AS1 (setb,%0);

  OUTPUT_JUMP (\"setle %0\", \"setbe %0\", NULL_PTR);
}")

(define_expand "sleu"
  [(match_dup 1)
   (set (match_operand:QI 0 "register_operand" "")
	(leu:QI (cc0) (const_int 0)))]
  ""
  "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")

(define_insn ""
  [(set (match_operand:QI 0 "register_operand" "=q")
	(leu:QI (cc0) (const_int 0)))]
  ""
  "* return \"setbe %0\"; ")

;; Basic conditional jump instructions.
;; We ignore the overflow flag for signed branch instructions.

;; For all bCOND expanders, also expand the compare or test insn that
;; generates cc0.  Generate an equality comparison if `beq' or `bne'.

(define_expand "beq"
  [(match_dup 1)
   (set (pc)
	(if_then_else (eq (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  if (TARGET_IEEE_FP
      && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT)
    operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1);
  else
    operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
}")

(define_insn ""
  [(set (pc)
	(if_then_else (eq (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  if (cc_prev_status.flags & CC_Z_IN_NOT_C)
    return \"jnc %l0\";
  else
  if (cc_prev_status.flags & CC_TEST_AX)
    {
      operands[1] = gen_rtx (REG, SImode, 0);
      operands[2] = GEN_INT (0x4000);
      output_asm_insn (AS2 (testl,%2,%1), operands);
      return AS1 (jne,%l0);
    }

    return \"je %l0\";
}")

(define_expand "bne"
  [(match_dup 1)
   (set (pc)
	(if_then_else (ne (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  if (TARGET_IEEE_FP
      && GET_MODE_CLASS (GET_MODE (i386_compare_op0)) == MODE_FLOAT)
    operands[1] = (*i386_compare_gen_eq)(i386_compare_op0, i386_compare_op1);
  else
    operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);
}")

(define_insn ""
  [(set (pc)
	(if_then_else (ne (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  if (cc_prev_status.flags & CC_Z_IN_NOT_C)
    return \"jc %l0\";
  else
  if (cc_prev_status.flags & CC_TEST_AX)
    {
      operands[1] = gen_rtx (REG, SImode, 0);
      operands[2] = GEN_INT (0x4000);
      output_asm_insn (AS2 (testl,%2,%1), operands);
      return AS1 (je,%l0);
    }

    return \"jne %l0\";
}")

(define_expand "bgt"
  [(match_dup 1)
   (set (pc)
	(if_then_else (gt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")

(define_insn ""
  [(set (pc)
	(if_then_else (gt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
    return AS1 (je,%l0);

  if (cc_prev_status.flags & CC_TEST_AX)
    {
      operands[1] = gen_rtx (REG, SImode, 0);
      operands[2] = GEN_INT (0x4100);
      output_asm_insn (AS2 (testl,%2,%1), operands);
      return AS1 (je,%l0);
    }
  OUTPUT_JUMP (\"jg %l0\", \"ja %l0\", NULL_PTR);
}")

(define_expand "bgtu"
  [(match_dup 1)
   (set (pc)
	(if_then_else (gtu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")

(define_insn ""
  [(set (pc)
	(if_then_else (gtu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "ja %l0")

(define_expand "blt"
  [(match_dup 1)
   (set (pc)
	(if_then_else (lt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")

(define_insn ""
  [(set (pc)
	(if_then_else (lt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
    return AS1 (je,%l0);

  if (cc_prev_status.flags & CC_TEST_AX)
    {
      operands[1] = gen_rtx (REG, SImode, 0);
      operands[2] = GEN_INT (0x100);
      output_asm_insn (AS2 (testl,%2,%1), operands);
      return AS1 (jne,%l0);
    }
  OUTPUT_JUMP (\"jl %l0\", \"jb %l0\", \"js %l0\");
}")

(define_expand "bltu"
  [(match_dup 1)
   (set (pc)
	(if_then_else (ltu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")

(define_insn ""
  [(set (pc)
	(if_then_else (ltu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "jb %l0")

(define_expand "bge"
  [(match_dup 1)
   (set (pc)
	(if_then_else (ge (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")

(define_insn ""
  [(set (pc)
	(if_then_else (ge (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
    return AS1 (je,%l0);
  if (cc_prev_status.flags & CC_TEST_AX)
    {
      operands[1] = gen_rtx (REG, SImode, 0);
      operands[2] = GEN_INT (0x100);
      output_asm_insn (AS2 (testl,%2,%1), operands);
      return AS1 (je,%l0);
    }
  OUTPUT_JUMP (\"jge %l0\", \"jae %l0\", \"jns %l0\");
}")

(define_expand "bgeu"
  [(match_dup 1)
   (set (pc)
	(if_then_else (geu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")

(define_insn ""
  [(set (pc)
	(if_then_else (geu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "jae %l0")

(define_expand "ble"
  [(match_dup 1)
   (set (pc)
	(if_then_else (le (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")

(define_insn ""
  [(set (pc)
	(if_then_else (le (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
    return AS1 (jb,%l0);
  if (cc_prev_status.flags & CC_TEST_AX)
    {
      operands[1] = gen_rtx (REG, SImode, 0);
      operands[2] = GEN_INT (0x4100);
      output_asm_insn (AS2 (testl,%2,%1), operands);
      return AS1 (jne,%l0);
    }

  OUTPUT_JUMP (\"jle %l0\", \"jbe %l0\", NULL_PTR);
}")

(define_expand "bleu"
  [(match_dup 1)
   (set (pc)
	(if_then_else (leu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "operands[1] = (*i386_compare_gen)(i386_compare_op0, i386_compare_op1);")

(define_insn ""
  [(set (pc)
	(if_then_else (leu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "jbe %l0")

;; Negated conditional jump instructions.

(define_insn ""
  [(set (pc)
	(if_then_else (eq (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
  if (cc_prev_status.flags & CC_Z_IN_NOT_C)
    return \"jc %l0\";
  else
  if (cc_prev_status.flags & CC_TEST_AX)
    {
      operands[1] = gen_rtx (REG, SImode, 0);
      operands[2] = GEN_INT (0x4000);
      output_asm_insn (AS2 (testl,%2,%1), operands);
      return AS1 (je,%l0);
    }
    return \"jne %l0\";
}")

(define_insn ""
  [(set (pc)
	(if_then_else (ne (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
  if (cc_prev_status.flags & CC_Z_IN_NOT_C)
    return \"jnc %l0\";
  else
  if (cc_prev_status.flags & CC_TEST_AX)
    {
      operands[1] = gen_rtx (REG, SImode, 0);
      operands[2] = GEN_INT (0x4000);
      output_asm_insn (AS2 (testl,%2,%1), operands);
      return AS1 (jne,%l0);
    }
    return \"je %l0\";
}")

(define_insn ""
  [(set (pc)
	(if_then_else (gt (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
  if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
    return AS1 (jne,%l0);
  if (cc_prev_status.flags & CC_TEST_AX)
    {
      operands[1] = gen_rtx (REG, SImode, 0);
      operands[2] = GEN_INT (0x4100);
      output_asm_insn (AS2 (testl,%2,%1), operands);
      return AS1 (jne,%l0);
    }
  OUTPUT_JUMP (\"jle %l0\", \"jbe %l0\", NULL_PTR);
}")

(define_insn ""
  [(set (pc)
	(if_then_else (gtu (cc0)
			   (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "jbe %l0")

(define_insn ""
  [(set (pc)
	(if_then_else (lt (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
  if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
    return AS1 (jne,%l0);
  if (cc_prev_status.flags & CC_TEST_AX)
    {
      operands[1] = gen_rtx (REG, SImode, 0);
      operands[2] = GEN_INT (0x100);
      output_asm_insn (AS2 (testl,%2,%1), operands);
      return AS1 (je,%l0);
    }

  OUTPUT_JUMP (\"jge %l0\", \"jae %l0\", \"jns %l0\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (ltu (cc0)
			   (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "jae %l0")

(define_insn ""
  [(set (pc)
	(if_then_else (ge (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
  if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
    return AS1 (jne,%l0);
  if (cc_prev_status.flags & CC_TEST_AX)
    {
      operands[1] = gen_rtx (REG, SImode, 0);
      operands[2] = GEN_INT (0x100);
      output_asm_insn (AS2 (testl,%2,%1), operands);
      return AS1 (jne,%l0);
    }
  OUTPUT_JUMP (\"jl %l0\", \"jb %l0\", \"js %l0\");
}")

(define_insn ""
  [(set (pc)
	(if_then_else (geu (cc0)
			   (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "jb %l0")

(define_insn ""
  [(set (pc)
	(if_then_else (le (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
  if (TARGET_IEEE_FP && (cc_prev_status.flags & CC_IN_80387))
    return AS1 (jae,%l0);

  if (cc_prev_status.flags & CC_TEST_AX)
    {
      operands[1] = gen_rtx (REG, SImode, 0);
      operands[2] = GEN_INT (0x4100);
      output_asm_insn (AS2 (testl,%2,%1), operands);
      return AS1 (je,%l0);
    }
  OUTPUT_JUMP (\"jg %l0\", \"ja %l0\", NULL_PTR);
}")

(define_insn ""
  [(set (pc)
	(if_then_else (leu (cc0)
			   (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "ja %l0")

;; Unconditional and other jump instructions

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

(define_insn "indirect_jump"
  [(set (pc) (match_operand:SI 0 "nonimmediate_operand" "rm"))]
  ""
  "*
{
  CC_STATUS_INIT;

  return AS1 (jmp,%*%0);
}")

;; ??? could transform while(--i > 0) S; to if (--i > 0) do S; while(--i);
;;     if S does not change i

(define_expand "decrement_and_branch_until_zero"
  [(parallel [(set (pc)
		   (if_then_else (ge (plus:SI (match_operand:SI 0 "general_operand" "")
					      (const_int -1))
				     (const_int 0))
				 (label_ref (match_operand 1 "" ""))
				 (pc)))
	      (set (match_dup 0)
		   (plus:SI (match_dup 0)
			    (const_int -1)))])]
  ""
  "")

(define_insn ""
  [(set (pc)
	(if_then_else (match_operator 0 "arithmetic_comparison_operator"
				      [(plus:SI (match_operand:SI 1 "nonimmediate_operand" "+r,m")
						(match_operand:SI 2 "general_operand" "rmi,ri"))
				       (const_int 0)])
		      (label_ref (match_operand 3 "" ""))
		      (pc)))
   (set (match_dup 1)
	(plus:SI (match_dup 1)
		 (match_dup 2)))]
  ""
  "*
{
  CC_STATUS_INIT;
  if (operands[2] == constm1_rtx)
    output_asm_insn (AS1 (dec%L1,%1), operands);

  else if (operands[1] == const1_rtx)
    output_asm_insn (AS1 (inc%L1,%1), operands);

  else
    output_asm_insn (AS2 (add%L1,%2,%1), operands);

  return AS1 (%J0,%l3);
}")

(define_insn ""
  [(set (pc)
	(if_then_else (match_operator 0 "arithmetic_comparison_operator"
				      [(minus:SI (match_operand:SI 1 "nonimmediate_operand" "+r,m")
						 (match_operand:SI 2 "general_operand" "rmi,ri"))
				       (const_int 0)])
		      (label_ref (match_operand 3 "" ""))
		      (pc)))
   (set (match_dup 1)
	(minus:SI (match_dup 1)
		  (match_dup 2)))]
  ""
  "*
{
  CC_STATUS_INIT;
  if (operands[2] == const1_rtx)
    output_asm_insn (AS1 (dec%L1,%1), operands);

  else if (operands[1] == constm1_rtx)
    output_asm_insn (AS1 (inc%L1,%1), operands);

  else
    output_asm_insn (AS2 (sub%L1,%2,%1), operands);

  return AS1 (%J0,%l3);
}")

;; Implement switch statements when generating PIC code.  Switches are
;; implemented by `tablejump' when not using -fpic.

;; Emit code here to do the range checking and make the index zero based.

(define_expand "casesi"
  [(set (match_dup 5)
	(match_operand:SI 0 "general_operand" ""))
   (set (match_dup 6)
	(minus:SI (match_dup 5)
		  (match_operand:SI 1 "general_operand" "")))
   (set (cc0)
	(compare:CC (match_dup 6)
		    (match_operand:SI 2 "general_operand" "")))
   (set (pc)
	(if_then_else (gtu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 4 "" ""))
		      (pc)))
   (parallel
    [(set (pc)
	  (minus:SI (reg:SI 3)
		    (mem:SI (plus:SI (mult:SI (match_dup 6)
					      (const_int 4))
				     (label_ref (match_operand 3 "" ""))))))
     (clobber (match_scratch:SI 7 ""))])]
  "flag_pic"
  "
{
  operands[5] = gen_reg_rtx (SImode);
  operands[6] = gen_reg_rtx (SImode);
  current_function_uses_pic_offset_table = 1;
}")

;; Implement a casesi insn.

;; Each entry in the "addr_diff_vec" looks like this as the result of the
;; two rules below:
;; 
;; 	.long _GLOBAL_OFFSET_TABLE_+[.-.L2]
;; 
;; 1. An expression involving an external reference may only use the
;;    addition operator, and only with an assembly-time constant.
;;    The example above satisfies this because ".-.L2" is a constant.
;; 
;; 2. The symbol _GLOBAL_OFFSET_TABLE_ is magic, and at link time is
;;    given the value of "GOT - .", where GOT is the actual address of
;;    the Global Offset Table.  Therefore, the .long above actually
;;    stores the value "( GOT - . ) + [ . - .L2 ]", or "GOT - .L2".  The
;;    expression "GOT - .L2" by itself would generate an error from as(1).
;; 
;; The pattern below emits code that looks like this:
;; 
;; 	movl %ebx,reg
;; 	subl TABLE@GOTOFF(%ebx,index,4),reg
;; 	jmp reg
;; 
;; The addr_diff_vec contents may be directly referenced with @GOTOFF, since
;; the addr_diff_vec is known to be part of this module.
;; 
;; The subl above calculates "GOT - (( GOT - . ) + [ . - .L2 ])", which
;; evaluates to just ".L2".

(define_insn ""
  [(set (pc)
	(minus:SI (reg:SI 3)
		  (mem:SI (plus:SI
			   (mult:SI (match_operand:SI 0 "register_operand" "r")
				    (const_int 4))
			   (label_ref (match_operand 1 "" ""))))))
   (clobber (match_scratch:SI 2 "=&r"))]
  ""
  "*
{
  rtx xops[4];

  xops[0] = operands[0];
  xops[1] = operands[1];
  xops[2] = operands[2];
  xops[3] = pic_offset_table_rtx;

  output_asm_insn (AS2 (mov%L2,%3,%2), xops);
  output_asm_insn (\"sub%L2 %l1@GOTOFF(%3,%0,4),%2\", xops);
  output_asm_insn (AS1 (jmp,%*%2), xops);
  ASM_OUTPUT_ALIGN_CODE (asm_out_file);
  RET;
}")

(define_insn "tablejump"
  [(set (pc) (match_operand:SI 0 "nonimmediate_operand" "rm"))
   (use (label_ref (match_operand 1 "" "")))]
  ""
  "*
{
  CC_STATUS_INIT;

  return AS1 (jmp,%*%0);
}")

;; Call insns.

;; If generating PIC code, the predicate indirect_operand will fail
;; for operands[0] containing symbolic references on all of the named
;; call* patterns.  Each named pattern is followed by an unnamed pattern
;; that matches any call to a symbolic CONST (ie, a symbol_ref).  The
;; unnamed patterns are only used while generating PIC code, because
;; otherwise the named patterns match.

;; Call subroutine returning no value.

(define_expand "call_pop"
  [(parallel [(call (match_operand:QI 0 "indirect_operand" "")
		    (match_operand:SI 1 "general_operand" ""))
	      (set (reg:SI 7)
		   (plus:SI (reg:SI 7)
			    (match_operand:SI 3 "immediate_operand" "")))])]
  ""
  "
{
  rtx addr;

  if (flag_pic)
    current_function_uses_pic_offset_table = 1;

  /* With half-pic, force the address into a register.  */
  addr = XEXP (operands[0], 0);
  if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr))
    XEXP (operands[0], 0) = force_reg (Pmode, addr);

  if (! expander_call_insn_operand (operands[0], QImode))
    operands[0]
      = change_address (operands[0], VOIDmode,
			copy_to_mode_reg (Pmode, XEXP (operands[0], 0)));
}")

(define_insn ""
  [(call (match_operand:QI 0 "call_insn_operand" "m")
	 (match_operand:SI 1 "general_operand" "g"))
   (set (reg:SI 7) (plus:SI (reg:SI 7)
			    (match_operand:SI 3 "immediate_operand" "i")))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == MEM
      && ! CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
    {
      operands[0] = XEXP (operands[0], 0);
      return AS1 (call,%*%0);
    }
  else
    return AS1 (call,%P0);
}")

(define_insn ""
  [(call (mem:QI (match_operand:SI 0 "symbolic_operand" ""))
	 (match_operand:SI 1 "general_operand" "g"))
   (set (reg:SI 7) (plus:SI (reg:SI 7)
			    (match_operand:SI 3 "immediate_operand" "i")))]
  "!HALF_PIC_P ()"
  "call %P0")

(define_expand "call"
  [(call (match_operand:QI 0 "indirect_operand" "")
	 (match_operand:SI 1 "general_operand" ""))]
  ;; Operand 1 not used on the i386.
  ""
  "
{
  rtx addr;

  if (flag_pic)
    current_function_uses_pic_offset_table = 1;

  /* With half-pic, force the address into a register.  */
  addr = XEXP (operands[0], 0);
  if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr))
    XEXP (operands[0], 0) = force_reg (Pmode, addr);

  if (! expander_call_insn_operand (operands[0], QImode))
    operands[0]
      = change_address (operands[0], VOIDmode,
			copy_to_mode_reg (Pmode, XEXP (operands[0], 0)));
}")

(define_insn ""
  [(call (match_operand:QI 0 "call_insn_operand" "m")
	 (match_operand:SI 1 "general_operand" "g"))]
  ;; Operand 1 not used on the i386.
  ""
  "*
{
  if (GET_CODE (operands[0]) == MEM
      && ! CONSTANT_ADDRESS_P (XEXP (operands[0], 0)))
    {
      operands[0] = XEXP (operands[0], 0);
      return AS1 (call,%*%0);
    }
  else
    return AS1 (call,%P0);
}")

(define_insn ""
  [(call (mem:QI (match_operand:SI 0 "symbolic_operand" ""))
	 (match_operand:SI 1 "general_operand" "g"))]
  ;; Operand 1 not used on the i386.
  "!HALF_PIC_P ()"
  "call %P0")

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

(define_expand "call_value_pop"
  [(parallel [(set (match_operand 0 "" "")
		   (call (match_operand:QI 1 "indirect_operand" "")
			 (match_operand:SI 2 "general_operand" "")))
	      (set (reg:SI 7)
		   (plus:SI (reg:SI 7)
			    (match_operand:SI 4 "immediate_operand" "")))])]
  ""
  "
{
  rtx addr;

  if (flag_pic)
    current_function_uses_pic_offset_table = 1;

  /* With half-pic, force the address into a register.  */
  addr = XEXP (operands[1], 0);
  if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr))
    XEXP (operands[1], 0) = force_reg (Pmode, addr);

  if (! expander_call_insn_operand (operands[1], QImode))
    operands[1]
      = change_address (operands[1], VOIDmode,
			copy_to_mode_reg (Pmode, XEXP (operands[1], 0)));
}")

(define_insn ""
  [(set (match_operand 0 "" "=rf")
	(call (match_operand:QI 1 "call_insn_operand" "m")
	      (match_operand:SI 2 "general_operand" "g")))
   (set (reg:SI 7) (plus:SI (reg:SI 7)
			    (match_operand:SI 4 "immediate_operand" "i")))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == MEM
      && ! CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
    {
      operands[1] = XEXP (operands[1], 0);
      output_asm_insn (AS1 (call,%*%1), operands);
    }
  else
    output_asm_insn (AS1 (call,%P1), operands);

  RET;
}")

(define_insn ""
  [(set (match_operand 0 "" "=rf")
	(call (mem:QI (match_operand:SI 1 "symbolic_operand" ""))
	      (match_operand:SI 2 "general_operand" "g")))
   (set (reg:SI 7) (plus:SI (reg:SI 7)
			    (match_operand:SI 4 "immediate_operand" "i")))]
  "!HALF_PIC_P ()"
  "call %P1")

(define_expand "call_value"
  [(set (match_operand 0 "" "")
	(call (match_operand:QI 1 "indirect_operand" "")
	      (match_operand:SI 2 "general_operand" "")))]
  ;; Operand 2 not used on the i386.
  ""
  "
{
  rtx addr;

  if (flag_pic)
    current_function_uses_pic_offset_table = 1;

  /* With half-pic, force the address into a register.  */
  addr = XEXP (operands[1], 0);
  if (GET_CODE (addr) != REG && HALF_PIC_P () && !CONSTANT_ADDRESS_P (addr))
    XEXP (operands[1], 0) = force_reg (Pmode, addr);

  if (! expander_call_insn_operand (operands[1], QImode))
    operands[1]
      = change_address (operands[1], VOIDmode,
			copy_to_mode_reg (Pmode, XEXP (operands[1], 0)));
}")

(define_insn ""
  [(set (match_operand 0 "" "=rf")
	(call (match_operand:QI 1 "call_insn_operand" "m")
	      (match_operand:SI 2 "general_operand" "g")))]
  ;; Operand 2 not used on the i386.
  ""
  "*
{
  if (GET_CODE (operands[1]) == MEM
      && ! CONSTANT_ADDRESS_P (XEXP (operands[1], 0)))
    {
      operands[1] = XEXP (operands[1], 0);
      output_asm_insn (AS1 (call,%*%1), operands);
    }
  else
    output_asm_insn (AS1 (call,%P1), operands);

  RET;
}")

(define_insn ""
  [(set (match_operand 0 "" "=rf")
	(call (mem:QI (match_operand:SI 1 "symbolic_operand" ""))
	      (match_operand:SI 2 "general_operand" "g")))]
  ;; Operand 2 not used on the i386.
  "!HALF_PIC_P ()"
  "call %P1")

;; Call subroutine returning any type.

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

  /* In order to give reg-stack an easier job in validating two
     coprocessor registers as containing a possible return value,
     simply pretend the untyped call returns a complex long double
     value.  */
  emit_call_insn (TARGET_80387
                  ? gen_call_value (gen_rtx (REG, XCmode, FIRST_FLOAT_REG),
		                  operands[0], const0_rtx)
                  : 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));
    }

  /* The optimizer does not know that the call sets the function value
     registers we stored in the result block.  We avoid problems by
     claiming that all hard registers are used and clobbered at this
     point.  */
  emit_insn (gen_blockage ());

  DONE;
}")

;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and
;; all of memory.  This blocks insns from being moved across this point.

(define_insn "blockage"
  [(unspec_volatile [(const_int 0)] 0)]
  ""
  "")

;; Insn emitted into the body of a function to return from a function.
;; This is only done if the function's epilogue is known to be simple.
;; See comments for simple_386_epilogue in i386.c.

(define_insn "return"
  [(return)]
  "simple_386_epilogue ()"
  "*
{
  function_epilogue (asm_out_file, get_frame_size ());
  RET;
}")

(define_insn "nop"
  [(const_int 0)]
  ""
  "nop")

(define_expand "movstrsi"
  [(parallel [(set (match_operand:BLK 0 "memory_operand" "")
		   (match_operand:BLK 1 "memory_operand" ""))
	      (use (match_operand:SI 2 "const_int_operand" ""))
	      (use (match_operand:SI 3 "const_int_operand" ""))
	      (clobber (match_scratch:SI 4 ""))
	      (clobber (match_dup 5))
	      (clobber (match_dup 6))])]
  ""
  "
{
  rtx addr0, addr1;

  if (GET_CODE (operands[2]) != CONST_INT)
    FAIL;

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

  operands[5] = addr0;
  operands[6] = addr1;

  operands[0] = gen_rtx (MEM, BLKmode, addr0);
  operands[1] = gen_rtx (MEM, BLKmode, addr1);
}")

;; It might seem that operands 0 & 1 could use predicate register_operand.
;; But strength reduction might offset the MEM expression.  So we let
;; reload put the address into %edi & %esi.

(define_insn ""
  [(set (mem:BLK (match_operand:SI 0 "address_operand" "D"))
	(mem:BLK (match_operand:SI 1 "address_operand" "S")))
   (use (match_operand:SI 2 "const_int_operand" "n"))
   (use (match_operand:SI 3 "immediate_operand" "i"))
   (clobber (match_scratch:SI 4 "=&c"))
   (clobber (match_dup 0))
   (clobber (match_dup 1))]
  ""
  "*
{
  rtx xops[2];

  output_asm_insn (\"cld\", operands);
  if (GET_CODE (operands[2]) == CONST_INT)
    {
      if (INTVAL (operands[2]) & ~0x03)
	{
	  xops[0] = GEN_INT ((INTVAL (operands[2]) >> 2) & 0x3fffffff);
	  xops[1] = operands[4];

	  output_asm_insn (AS2 (mov%L1,%0,%1), xops);
#ifdef INTEL_SYNTAX
	  output_asm_insn (\"rep movsd\", xops);
#else
	  output_asm_insn (\"rep\;movsl\", xops);
#endif
	}
      if (INTVAL (operands[2]) & 0x02)
	output_asm_insn (\"movsw\", operands);
      if (INTVAL (operands[2]) & 0x01)
	output_asm_insn (\"movsb\", operands);
    }
  else
    abort ();
  RET;
}")

(define_expand "clrstrsi"
  [(set (match_dup 3) (const_int 0))
   (parallel [(set (match_operand:BLK 0 "memory_operand" "")
		   (const_int 0))
	      (use (match_operand:SI 1 "const_int_operand" ""))
	      (use (match_operand:SI 2 "const_int_operand" ""))
	      (use (match_dup 3))
	      (clobber (match_scratch:SI 4 ""))
	      (clobber (match_dup 5))])]
  ""
  "
{
  rtx addr0, addr1;

  if (GET_CODE (operands[2]) != CONST_INT)
    FAIL;

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

  operands[3] = gen_reg_rtx (SImode);
  operands[5] = addr0;

  operands[0] = gen_rtx (MEM, BLKmode, addr0);
}")

;; It might seem that operand 0 could use predicate register_operand.
;; But strength reduction might offset the MEM expression.  So we let
;; reload put the address into %edi.

(define_insn ""
  [(set (mem:BLK (match_operand:SI 0 "address_operand" "D"))
	(const_int 0))
   (use (match_operand:SI 1 "const_int_operand" "n"))
   (use (match_operand:SI 2 "immediate_operand" "i"))
   (use (match_operand:SI 3 "register_operand" "a"))
   (clobber (match_scratch:SI 4 "=&c"))
   (clobber (match_dup 0))]
  ""
  "*
{
  rtx xops[2];

  output_asm_insn (\"cld\", operands);
  if (GET_CODE (operands[1]) == CONST_INT)
    {
      if (INTVAL (operands[1]) & ~0x03)
	{
	  xops[0] = GEN_INT ((INTVAL (operands[1]) >> 2) & 0x3fffffff);
	  xops[1] = operands[4];

	  output_asm_insn (AS2 (mov%L1,%0,%1), xops);
#ifdef INTEL_SYNTAX
	  output_asm_insn (\"rep stosd\", xops);
#else
	  output_asm_insn (\"rep\;stosl\", xops);
#endif
	}
      if (INTVAL (operands[1]) & 0x02)
	output_asm_insn (\"stosw\", operands);
      if (INTVAL (operands[1]) & 0x01)
	output_asm_insn (\"stosb\", operands);
    }
  else
    abort ();
  RET;
}")

(define_expand "cmpstrsi"
  [(parallel [(set (match_operand:SI 0 "general_operand" "")
		   (compare:SI (match_operand:BLK 1 "general_operand" "")
			       (match_operand:BLK 2 "general_operand" "")))
	      (use (match_operand:SI 3 "general_operand" ""))
	      (use (match_operand:SI 4 "immediate_operand" ""))
	      (clobber (match_dup 5))
	      (clobber (match_dup 6))
	      (clobber (match_dup 3))])]
  ""
  "
{
  rtx addr1, addr2;

  addr1 = copy_to_mode_reg (Pmode, XEXP (operands[1], 0));
  addr2 = copy_to_mode_reg (Pmode, XEXP (operands[2], 0));
  operands[3] = copy_to_mode_reg (SImode, operands[3]);

  operands[5] = addr1;
  operands[6] = addr2;

  operands[1] = gen_rtx (MEM, BLKmode, addr1);
  operands[2] = gen_rtx (MEM, BLKmode, addr2);

}")

;; memcmp recognizers.  The `cmpsb' opcode does nothing if the count is
;; zero.  Emit extra code to make sure that a zero-length compare is EQ.

;; It might seem that operands 0 & 1 could use predicate register_operand.
;; But strength reduction might offset the MEM expression.  So we let
;; reload put the address into %edi & %esi.

;; ??? Most comparisons have a constant length, and it's therefore
;; possible to know that the length is non-zero, and to avoid the extra
;; code to handle zero-length compares.

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=&r")
	(compare:SI (mem:BLK (match_operand:SI 1 "address_operand" "S"))
		    (mem:BLK (match_operand:SI 2 "address_operand" "D"))))
   (use (match_operand:SI 3 "register_operand" "c"))
   (use (match_operand:SI 4 "immediate_operand" "i"))
   (clobber (match_dup 1))
   (clobber (match_dup 2))
   (clobber (match_dup 3))]
  ""
  "*
{
  rtx xops[2], label;

  label = gen_label_rtx ();

  output_asm_insn (\"cld\", operands);
  output_asm_insn (AS2 (xor%L0,%0,%0), operands);
  output_asm_insn (\"repz\;cmps%B2\", operands);
  output_asm_insn (\"je %l0\", &label);

  xops[0] = operands[0];
  xops[1] = const1_rtx;
  output_asm_insn (AS2 (sbb%L0,%0,%0), xops);
  if (QI_REG_P (xops[0]))
     output_asm_insn (AS2 (or%B0,%1,%b0), xops);
  else
     output_asm_insn (AS2 (or%L0,%1,%0), xops);
	
  ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\", CODE_LABEL_NUMBER (label));
  RET;
}")

(define_insn ""
  [(set (cc0)
	(compare:SI (mem:BLK (match_operand:SI 0 "address_operand" "S"))
		    (mem:BLK (match_operand:SI 1 "address_operand" "D"))))
   (use (match_operand:SI 2 "register_operand" "c"))
   (use (match_operand:SI 3 "immediate_operand" "i"))
   (clobber (match_dup 0))
   (clobber (match_dup 1))
   (clobber (match_dup 2))]
  ""
  "*
{
  rtx xops[2];

  cc_status.flags |= CC_NOT_SIGNED;

  xops[0] = gen_rtx (REG, QImode, 0);
  xops[1] = CONST0_RTX (QImode);

  output_asm_insn (\"cld\", operands);
  output_asm_insn (AS2 (test%B0,%1,%0), xops);
  return \"repz\;cmps%B2\";
}")


(define_expand "ffssi2"
  [(set (match_dup 2)
	(plus:SI (ffs:SI (match_operand:SI 1 "general_operand" ""))
		 (const_int -1)))
   (set (match_operand:SI 0 "general_operand" "")
	(plus:SI (match_dup 2) (const_int 1)))]
  ""
  "operands[2] = gen_reg_rtx (SImode);")

;; Note, you cannot optimize away the branch following the bsfl by assuming
;; that the destination is not modified if the input is 0, since not all
;; x86 implementations do this.

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=&r")
	(plus:SI (ffs:SI (match_operand:SI 1 "nonimmediate_operand" "rm"))
		 (const_int -1)))]
  ""
  "*
{
  rtx xops[3];
  static int ffssi_label_number;
  char buffer[30];

  xops[0] = operands[0];
  xops[1] = operands[1];
  xops[2] = constm1_rtx;
  output_asm_insn (AS2 (bsf%L0,%1,%0), xops);
#ifdef LOCAL_LABEL_PREFIX
  sprintf (buffer, \"jnz %sLFFSSI%d\",
	   LOCAL_LABEL_PREFIX, ffssi_label_number);
#else
  sprintf (buffer, \"jnz %sLFFSSI%d\",
	   \"\", ffssi_label_number);
#endif
  output_asm_insn (buffer, xops);
  output_asm_insn (AS2 (mov%L0,%2,%0), xops);
#ifdef LOCAL_LABEL_PREFIX
  sprintf (buffer, \"%sLFFSSI%d:\",
	   LOCAL_LABEL_PREFIX, ffssi_label_number);
#else
  sprintf (buffer, \"%sLFFSSI%d:\",
	   \"\", ffssi_label_number);
#endif
  output_asm_insn (buffer, xops);

  ffssi_label_number++;
  return \"\";
}")

(define_expand "ffshi2"
  [(set (match_dup 2)
	(plus:HI (ffs:HI (match_operand:HI 1 "general_operand" ""))
		 (const_int -1)))
   (set (match_operand:HI 0 "general_operand" "")
	(plus:HI (match_dup 2) (const_int 1)))]
  ""
  "operands[2] = gen_reg_rtx (HImode);")

(define_insn ""
  [(set (match_operand:HI 0 "register_operand" "=&r")
	(plus:HI (ffs:HI (match_operand:SI 1 "nonimmediate_operand" "rm"))
		 (const_int -1)))]
  ""
  "*
{
  rtx xops[3];
  static int ffshi_label_number;
  char buffer[30];

  xops[0] = operands[0];
  xops[1] = operands[1];
  xops[2] = constm1_rtx;
  output_asm_insn (AS2 (bsf%W0,%1,%0), xops);
#ifdef LOCAL_LABEL_PREFIX
  sprintf (buffer, \"jnz %sLFFSHI%d\",
	   LOCAL_LABEL_PREFIX, ffshi_label_number);
#else
  sprintf (buffer, \"jnz %sLFFSHI%d\",
	   \"\", ffshi_label_number);
#endif
  output_asm_insn (buffer, xops);
  output_asm_insn (AS2 (mov%W0,%2,%0), xops);
#ifdef LOCAL_LABEL_PREFIX
  sprintf (buffer, \"%sLFFSHI%d:\",
	   LOCAL_LABEL_PREFIX, ffshi_label_number);
#else
  sprintf (buffer, \"%sLFFSHI%d:\",
	   \"\", ffshi_label_number);
#endif
  output_asm_insn (buffer, xops);

  ffshi_label_number++;
  return \"\";
}")

;; These patterns match the binary 387 instructions for addM3, subM3,
;; mulM3 and divM3.  There are three patterns for each of DFmode and
;; SFmode.  The first is the normal insn, the second the same insn but
;; with one operand a conversion, and the third the same insn but with
;; the other operand a conversion.  The conversion may be SFmode or
;; SImode if the target mode DFmode, but only SImode if the target mode
;; is SFmode.

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=f,f")
	(match_operator:DF 3 "binary_387_op"
			[(match_operand:DF 1 "nonimmediate_operand" "0,fm")
			 (match_operand:DF 2 "nonimmediate_operand" "fm,0")]))]
  "TARGET_80387"
  "* return output_387_binary_op (insn, operands);"
  [(set (attr "type") 
        (cond [(match_operand:DF 3 "is_mul" "") 
                 (const_string "fpop")
               (match_operand:DF 3 "is_div" "") 
                 (const_string "fpdiv")
              ]
               (const_string "fpop")
        )
  )])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=f")
	(match_operator:DF 3 "binary_387_op"
	   [(float:DF (match_operand:SI 1 "nonimmediate_operand" "rm"))
	    (match_operand:DF 2 "register_operand" "0")]))]
  "TARGET_80387"
  "* return output_387_binary_op (insn, operands);"
  [(set (attr "type") 
        (cond [(match_operand:DF 3 "is_mul" "") 
                 (const_string "fpop")
               (match_operand:DF 3 "is_div" "") 
                 (const_string "fpdiv")
              ]
               (const_string "fpop")
        )
  )])

(define_insn ""
  [(set (match_operand:XF 0 "register_operand" "=f,f")
	(match_operator:XF 3 "binary_387_op"
			[(match_operand:XF 1 "register_operand" "0,f")
			 (match_operand:XF 2 "register_operand" "f,0")]))]
  "TARGET_80387"
  "* return output_387_binary_op (insn, operands);"
  [(set (attr "type") 
        (cond [(match_operand:DF 3 "is_mul" "") 
                 (const_string "fpop")
               (match_operand:DF 3 "is_div" "") 
                 (const_string "fpdiv")
              ]
               (const_string "fpop")
        )
  )])

(define_insn ""
  [(set (match_operand:XF 0 "register_operand" "=f")
	(match_operator:XF 3 "binary_387_op"
	   [(float:XF (match_operand:SI 1 "nonimmediate_operand" "rm"))
	    (match_operand:XF 2 "register_operand" "0")]))]
  "TARGET_80387"
  "* return output_387_binary_op (insn, operands);"
  [(set (attr "type") 
        (cond [(match_operand:DF 3 "is_mul" "") 
                 (const_string "fpop")
               (match_operand:DF 3 "is_div" "") 
                 (const_string "fpdiv")
              ]
               (const_string "fpop")
        )
  )])

(define_insn ""
  [(set (match_operand:XF 0 "register_operand" "=f,f")
	(match_operator:XF 3 "binary_387_op"
	   [(float_extend:XF (match_operand:SF 1 "nonimmediate_operand" "fm,0"))
	    (match_operand:XF 2 "register_operand" "0,f")]))]
  "TARGET_80387"
  "* return output_387_binary_op (insn, operands);"
  [(set (attr "type") 
        (cond [(match_operand:DF 3 "is_mul" "") 
                 (const_string "fpop")
               (match_operand:DF 3 "is_div" "") 
                 (const_string "fpdiv")
              ]
               (const_string "fpop")
        )
  )])

(define_insn ""
  [(set (match_operand:XF 0 "register_operand" "=f")
	(match_operator:XF 3 "binary_387_op"
	  [(match_operand:XF 1 "register_operand" "0")
	   (float:XF (match_operand:SI 2 "nonimmediate_operand" "rm"))]))]
  "TARGET_80387"
  "* return output_387_binary_op (insn, operands);"
  [(set (attr "type") 
        (cond [(match_operand:DF 3 "is_mul" "") 
                 (const_string "fpop")
               (match_operand:DF 3 "is_div" "") 
                 (const_string "fpdiv")
              ]
               (const_string "fpop")
        )
  )])

(define_insn ""
  [(set (match_operand:XF 0 "register_operand" "=f,f")
	(match_operator:XF 3 "binary_387_op"
	  [(match_operand:XF 1 "register_operand" "0,f")
	   (float_extend:XF
	    (match_operand:SF 2 "nonimmediate_operand" "fm,0"))]))]
  "TARGET_80387"
  "* return output_387_binary_op (insn, operands);"
  [(set (attr "type") 
        (cond [(match_operand:DF 3 "is_mul" "") 
                 (const_string "fpop")
               (match_operand:DF 3 "is_div" "") 
                 (const_string "fpdiv")
              ]
               (const_string "fpop")
        )
  )])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=f,f")
	(match_operator:DF 3 "binary_387_op"
	   [(float_extend:DF (match_operand:SF 1 "nonimmediate_operand" "fm,0"))
	    (match_operand:DF 2 "register_operand" "0,f")]))]
  "TARGET_80387"
  "* return output_387_binary_op (insn, operands);"
  [(set (attr "type") 
        (cond [(match_operand:DF 3 "is_mul" "") 
                 (const_string "fpop")
               (match_operand:DF 3 "is_div" "") 
                 (const_string "fpdiv")
              ]
               (const_string "fpop")
        )
  )])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=f")
	(match_operator:DF 3 "binary_387_op"
	  [(match_operand:DF 1 "register_operand" "0")
	   (float:DF (match_operand:SI 2 "nonimmediate_operand" "rm"))]))]
  "TARGET_80387"
  "* return output_387_binary_op (insn, operands);"
  [(set (attr "type") 
        (cond [(match_operand:DF 3 "is_mul" "") 
                 (const_string "fpop")
               (match_operand:DF 3 "is_div" "") 
                 (const_string "fpdiv")
              ]
               (const_string "fpop")
        )
  )])

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=f,f")
	(match_operator:DF 3 "binary_387_op"
	  [(match_operand:DF 1 "register_operand" "0,f")
	   (float_extend:DF
	    (match_operand:SF 2 "nonimmediate_operand" "fm,0"))]))]
  "TARGET_80387"
  "* return output_387_binary_op (insn, operands);"
  [(set (attr "type") 
        (cond [(match_operand:DF 3 "is_mul" "") 
                 (const_string "fpop")
               (match_operand:DF 3 "is_div" "") 
                 (const_string "fpdiv")
              ]
               (const_string "fpop")
        )
  )])

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=f,f")
	(match_operator:SF 3 "binary_387_op"
			[(match_operand:SF 1 "nonimmediate_operand" "0,fm")
			 (match_operand:SF 2 "nonimmediate_operand" "fm,0")]))]
  "TARGET_80387"
  "* return output_387_binary_op (insn, operands);"
  [(set (attr "type") 
        (cond [(match_operand:DF 3 "is_mul" "") 
                 (const_string "fpop")
               (match_operand:DF 3 "is_div" "") 
                 (const_string "fpdiv")
              ]
               (const_string "fpop")
        )
  )])

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=f")
	(match_operator:SF 3 "binary_387_op"
	  [(float:SF (match_operand:SI 1 "nonimmediate_operand" "rm"))
	   (match_operand:SF 2 "register_operand" "0")]))]
  "TARGET_80387"
  "* return output_387_binary_op (insn, operands);"
  [(set (attr "type") 
        (cond [(match_operand:DF 3 "is_mul" "") 
                 (const_string "fpop")
               (match_operand:DF 3 "is_div" "") 
                 (const_string "fpdiv")
              ]
               (const_string "fpop")
        )
  )])

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=f")
	(match_operator:SF 3 "binary_387_op"
	  [(match_operand:SF 1 "register_operand" "0")
	   (float:SF (match_operand:SI 2 "nonimmediate_operand" "rm"))]))]
  "TARGET_80387"
  "* return output_387_binary_op (insn, operands);"
  [(set (attr "type") 
        (cond [(match_operand:DF 3 "is_mul" "") 
                 (const_string "fpop")
               (match_operand:DF 3 "is_div" "") 
                 (const_string "fpdiv")
              ]
               (const_string "fpop")
        )
  )])

(define_expand "strlensi"
  [(parallel [(set (match_dup 4)
		   (unspec:SI [(mem:BLK (match_operand:BLK 1 "general_operand" ""))
			       (match_operand:QI 2 "immediate_operand" "")
			       (match_operand:SI 3 "immediate_operand" "")] 0))
	      (clobber (match_dup 1))])
   (set (match_dup 5)
	(not:SI (match_dup 4)))
   (set (match_operand:SI 0 "register_operand" "")
	(plus:SI (match_dup 5)
	         (const_int -1)))]
  ""
  "
{
  if (TARGET_UNROLL_STRLEN && operands[2] == const0_rtx && optimize > 1)
    {
      rtx address;
      rtx scratch;

	/* well it seems that some optimizer does not combine a call like
	     foo(strlen(bar), strlen(bar));
	   when the move and the subtraction is done here.  It does calculate
	   the length just once when these instructions are done inside of
	   output_strlen_unroll().  But I think since &bar[strlen(bar)] is
	   often used and I use one fewer register for the lifetime of
	   output_strlen_unroll() this is better.  */
      scratch = gen_reg_rtx (SImode);
      address = force_reg (SImode, XEXP (operands[1], 0));

	/* move address to scratch-register
	   this is done here because the i586 can do the following and
	   in the same cycle with the following move.  */
      if (GET_CODE (operands[3]) != CONST_INT || INTVAL (operands[3]) < 4)
	  emit_insn (gen_movsi (scratch, address));

      emit_insn (gen_movsi (operands[0], address));

      if(TARGET_USE_Q_REG)
	emit_insn (gen_strlensi_unroll5 (operands[0],
					operands[3],
					scratch,
					operands[0]));
      else
	emit_insn (gen_strlensi_unroll4 (operands[0],
					operands[3],
					scratch,
					operands[0]));

        /* gen_strlensi_unroll[45] returns the address of the zero
           at the end of the string, like memchr(), so compute the
           length by subtracting the startaddress.  */
      emit_insn (gen_subsi3 (operands[0], operands[0], address));
      DONE;
    }

  operands[1] = copy_to_mode_reg (SImode, XEXP (operands[1], 0));
  operands[4] = gen_reg_rtx (SImode);
  operands[5] = gen_reg_rtx (SImode);
}")

;; It might seem that operands 0 & 1 could use predicate register_operand.
;; But strength reduction might offset the MEM expression.  So we let
;; reload put the address into %edi.

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=&c")
	(unspec:SI [(mem:BLK (match_operand:SI 1 "address_operand" "D"))
		    (match_operand:QI 2 "immediate_operand" "a")
		    (match_operand:SI 3 "immediate_operand" "i")] 0))
   (clobber (match_dup 1))]
  ""
  "*
{
  rtx xops[2];

  xops[0] = operands[0];
  xops[1] = constm1_rtx;
  output_asm_insn (\"cld\", operands);
  output_asm_insn (AS2 (mov%L0,%1,%0), xops);
  return \"repnz\;scas%B2\";
}")

(define_insn "strlensi_unroll"
  [(set (match_operand:SI 0 "register_operand" "=&r,&r")
	(unspec:SI [(mem:BLK (match_operand:SI 1 "address_operand" "r,r"))
		    (match_operand:SI 2 "immediate_operand" "i,i")] 0))
   (clobber (match_scratch:SI 3 "=&q,&r"))]
  "optimize > 1"
  "* return output_strlen_unroll (operands);")

;; the only difference between the following patterns is the register preference
;; on a pentium using a q-register saves one clock cycle per 4 characters

(define_insn "strlensi_unroll4"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
		   (unspec:SI [(mem:BLK (match_operand:SI 3 "register_operand" "0,0"))
			       (match_operand:SI 1 "immediate_operand" "i,i")
			       (match_operand:SI 2 "register_operand" "=&q,&!r")] 0))
   (clobber (match_dup 2))]
  "(TARGET_USE_ANY_REG && optimize > 1)"
  "* return output_strlen_unroll (operands);")

(define_insn "strlensi_unroll5"
  [(set (match_operand:SI 0 "register_operand" "=r")
		   (unspec:SI [(mem:BLK (match_operand:SI 3 "register_operand" "0"))
			       (match_operand:SI 1 "immediate_operand" "i")
			       (match_operand:SI 2 "register_operand" "=&q")] 0))
   (clobber (match_dup 2))]
  "(TARGET_USE_Q_REG && optimize > 1)"
  "* return output_strlen_unroll (operands);")
