;; GCC machine description for Intel 80386.
;; Copyright (C) 1988 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, 675 Mass Ave, Cambridge, MA 02139, 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.

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

(define_insn "tstsi_cc"
  [(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_cc;
  i386_compare_op0 = operands[0];
  DONE;
}")

(define_insn "tsthi_cc"
  [(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_cc;
  i386_compare_op0 = operands[0];
  DONE;
}")

(define_insn "tstqi_cc"
  [(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_cc;
  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 (char *) 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 (char *) 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;
}")

;;- compare instructions.  See comments above tstM patterns about
;;  expansion of these insns.

(define_insn "cmpsi_cc"
  [(set (cc0)
	(compare:CC (match_operand:SI 0 "nonimmediate_operand" "mr,ri")
		    (match_operand:SI 1 "general_operand" "ri,mr")))]
  ""
  "*
{
  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:CC (match_operand:SI 0 "nonimmediate_operand" "")
		    (match_operand:SI 1 "general_operand" "")))]
  ""
  "
{
  i386_compare_gen = gen_cmpsi_cc;
  i386_compare_op0 = operands[0];
  i386_compare_op1 = operands[1];
  DONE;
}")

(define_insn "cmphi_cc"
  [(set (cc0)
	(compare:CC (match_operand:HI 0 "nonimmediate_operand" "mr,ri")
		    (match_operand:HI 1 "general_operand" "ri,mr")))]
  ""
  "*
{
  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:CC (match_operand:HI 0 "nonimmediate_operand" "")
		    (match_operand:HI 1 "general_operand" "")))]
  ""
  "
{
  i386_compare_gen = gen_cmphi_cc;
  i386_compare_op0 = operands[0];
  i386_compare_op1 = operands[1];
  DONE;
}")

(define_insn "cmpqi_cc"
  [(set (cc0)
	(compare:CC (match_operand:QI 0 "nonimmediate_operand" "qn,mq")
		    (match_operand:QI 1 "general_operand" "qm,nq")))]
  ""
  "*
{
  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:CC (match_operand:QI 0 "nonimmediate_operand" "")
		    (match_operand:QI 1 "general_operand" "")))]
  ""
  "
{
  i386_compare_gen = gen_cmpqi_cc;
  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 "cmpdf_cc"
  [(set (cc0)
	(compare:CC (match_operand:DF 0 "register_operand" "f")
		    (match_operand:DF 1 "nonimmediate_operand" "fm")))
   (clobber (match_scratch:HI 2 "=a"))]
  "TARGET_80387"
  "* return (char *) output_float_compare (insn, operands);")

(define_insn ""
  [(set (cc0)
	(compare:CC (match_operand:DF 0 "register_operand" "f,f")
		    (float:DF
		     (match_operand:SI 1 "nonimmediate_operand" "m,!*r"))))
   (clobber (match_scratch:HI 2 "=a,a"))]
  "TARGET_80387"
  "* return (char *) output_float_compare (insn, operands);")

(define_insn ""
  [(set (cc0)
	(compare:CC (match_operand:DF 0 "register_operand" "f,f")
		    (float_extend:DF
		     (match_operand:SF 1 "nonimmediate_operand" "fm,!*r"))))
   (clobber (match_scratch:HI 2 "=a,a"))]
  "TARGET_80387"
  "* return (char *) 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 (char *) output_float_compare (insn, operands);")

(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 (char *) output_float_compare (insn, operands);")

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

(define_insn ""
  [(set (cc0)
	(compare:CC (match_operand:SF 0 "register_operand" "f,f")
		    (float:SF
		     (match_operand:SI 1 "nonimmediate_operand" "m,!*r"))))
   (clobber (match_scratch:HI 2 "=a,a"))]
  "TARGET_80387"
  "* return (char *) 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 (char *) output_float_compare (insn, operands);")

(define_expand "cmpdf"
  [(set (cc0)
	(compare:CC (match_operand:DF 0 "register_operand" "")
		    (match_operand:DF 1 "nonimmediate_operand" "")))]
  "TARGET_80387"
  "
{
  i386_compare_gen = gen_cmpdf_cc;
  i386_compare_gen_eq = gen_cmpdf_ccfpeq;
  i386_compare_op0 = operands[0];
  i386_compare_op1 = operands[1];
  DONE;
}")

(define_expand "cmpsf"
  [(set (cc0)
	(compare:CC (match_operand:SF 0 "register_operand" "")
		    (match_operand:SF 1 "nonimmediate_operand" "")))]
  "TARGET_80387"
  "
{
  i386_compare_gen = gen_cmpsf_cc;
  i386_compare_gen_eq = gen_cmpsf_ccfpeq;
  i386_compare_op0 = operands[0];
  i386_compare_op1 = operands[1];
  DONE;
}")

(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_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 "general_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 "general_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 "general_operand" "%qm")
		(match_operand:QI 1 "general_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_486"
  "push%L0 %1")

;; On a 486, 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 "general_operand" "ri"))]
  "TARGET_486"
  "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);
  else if (HALF_PIC_P()
	   && GET_CODE (operands[0]) == REG
           && GET_CODE (operands[1]) == SYMBOL_REF
           && HALF_PIC_ADDRESS_P (operands[1]))
    {
      rtx ptr = HALF_PIC_PTR (operands[1]);
      if (XSTR (ptr, 0) != XSTR (operands[1], 0))
	{
	  emit_move_insn (operands[0], gen_rtx (MEM, Pmode, ptr));
	  DONE;
	}
    }
}")

;; 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"))]
  ""
  "*
{
  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.  */
      && ! XEXP (link, 0)->volatil
      && GET_CODE (XEXP (link, 0)) != NOTE
      /* Make sure cross jumping didn't happen here.  */
      && no_labels_between_p (XEXP (link, 0), insn))
    /* Fastest way to change a 0 to a 1.  */
    return AS1 (inc%L0,%0);

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

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

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

(define_insn "movhi"
  [(set (match_operand:HI 0 "general_operand" "=g,r")
	(match_operand:HI 1 "general_operand" "ri,m"))]
  ""
  "*
{
  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.  */
      && ! XEXP (link, 0)->volatil
      && GET_CODE (XEXP (link, 0)) != NOTE
      /* Make sure cross jumping didn't happen here.  */
      && no_labels_between_p (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_insn "movstricthi"
  [(set (strict_low_part (match_operand:HI 0 "general_operand" "+g,r"))
	(match_operand:HI 1 "general_operand" "ri,m"))]
  ""
  "*
{
  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.  */
      && ! XEXP (link, 0)->volatil
      && GET_CODE (XEXP (link, 0)) != NOTE
      /* Make sure cross jumping didn't happen here.  */
      && no_labels_between_p (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 "general_operand" "q"))]
  ""
  "*
{
  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_insn "movqi"
  [(set (match_operand:QI 0 "general_operand" "=q,*r,qm")
	(match_operand:QI 1 "general_operand" "*g,q,qn"))]
  ""
  "*
{
  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.  */
      && ! XEXP (link, 0)->volatil
      && GET_CODE (XEXP (link, 0)) != NOTE
      /* Make sure cross jumping didn't happen here.  */
      && no_labels_between_p (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_insn "movstrictqi"
  [(set (strict_low_part (match_operand:QI 0 "general_operand" "+q,qm"))
	(match_operand:QI 1 "general_operand" "*g,qn"))]
  ""
  "*
{
  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.  */
      && ! XEXP (link, 0)->volatil
      && GET_CODE (XEXP (link, 0)) != NOTE
      /* Make sure cross jumping didn't happen here.  */
      && no_labels_between_p (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%W0.  */
  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_insn ""
  [(set (match_operand:SF 0 "push_operand" "=<,<")
	(match_operand:SF 1 "general_operand" "gF,f"))]
  ""
  "*
{
  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"
  [(set (match_operand:SF 0 "general_operand" "=f,fm,!*rf,!*rm")
	(match_operand:SF 1 "general_operand" "fmG,f,*rfm,*rfF"))]
  ""
  "*
{
  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 (char *) 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 (char *) singlemove_string (operands);
}")

;;should change to handle the memory operands[1] without doing df push..
(define_insn ""
  [(set (match_operand:DF 0 "push_operand" "=<,<")
	(match_operand:DF 1 "general_operand" "gF,f"))]
  ""
  "*
{
  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 (char *) output_move_double (operands);
}")

(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_insn "movdf"
  [(set (match_operand:DF 0 "general_operand" "=f,fm,!*rf,!*rm")
	(match_operand:DF 1 "general_operand" "fmG,f,*rfm,*rfF"))]
  ""
  "*
{
  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 (char *) 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 (char *) output_move_double (operands);
}")

(define_insn ""
  [(set (match_operand:DI 0 "push_operand" "=<")
	(match_operand:DI 1 "general_operand" "roiF"))]
  ""
  "*
{
  return (char *) output_move_double (operands);
}")

(define_insn "movdi"
  [(set (match_operand:DI 0 "general_operand" "=&r,rm")
	(match_operand:DI 1 "general_operand" "m,riF"))]
  ""
  "*
{
  return (char *) output_move_double (operands);
}")

;;- 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 "general_operand" "=r")
	(zero_extend:SI
	 (match_operand:HI 1 "nonimmediate_operand" "rm")))]
  ""
  "*
{
  if ((TARGET_486 || REGNO (operands[0]) == 0)
      && REG_P (operands[1]) && REGNO (operands[0]) == REGNO (operands[1]))
    {
      rtx xops[2];
      xops[0] = operands[0];
      xops[1] = GEN_INT (0xffff);
      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_insn "zero_extendqihi2"
  [(set (match_operand:HI 0 "general_operand" "=r")
	(zero_extend:HI
	 (match_operand:QI 1 "nonimmediate_operand" "qm")))]
  ""
  "*
{
  if ((TARGET_486 || REGNO (operands[0]) == 0)
      && REG_P (operands[1]) && REGNO (operands[0]) == REGNO (operands[1]))
    {
      rtx xops[2];
      xops[0] = operands[0];
      xops[1] = GEN_INT (0xff);
      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_insn "zero_extendqisi2"
  [(set (match_operand:SI 0 "general_operand" "=r")
	(zero_extend:SI
	 (match_operand:QI 1 "nonimmediate_operand" "qm")))]
  ""
  "*
{
  if ((TARGET_486 || REGNO (operands[0]) == 0)
      && REG_P (operands[1]) && REGNO (operands[0]) == REGNO (operands[1]))
    {
      rtx xops[2];
      xops[0] = operands[0];
      xops[1] = GEN_INT (0xff);
      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
}")

;;- sign extension instructions

/*
(define_insn "extendsidi2"
  [(set (match_operand:DI 0 "general_operand" "=a")
	(sign_extend:DI
	 (match_operand:SI 1 "nonimmediate_operand" "a")))]
  ""
  "clq")
*/

;; 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 "general_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 "general_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 "general_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
}")

;; Conversions between float and double.

(define_insn "extendsfdf2"
  [(set (match_operand:DF 0 "general_operand" "=fm,f,f,!*r")
	(float_extend:DF
	 (match_operand:SF 1 "general_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)
    {
      if (stack_top_dies)
	return AS1 (fstp%z0,%y0);
      else
        return AS1 (fst%z0,%y0);
    }

  abort ();
}")

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

(define_insn "truncdfsf2"
  [(set (match_operand:SF 0 "general_operand" "=m,!*r")
	(float_truncate:SF
	 (match_operand:DF 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]))
    {
      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
        return AS1 (fst%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_truncdfsi2"
  [(set (match_dup 5)
	(match_operand:DF 1 "register_operand" ""))
   (parallel [(set (match_dup 3)
		   (fix:DI (fix:DF (match_dup 5))))
	      (clobber (match_scratch:HI 2 ""))
	      (clobber (match_dup 5))])
   (set (match_operand:SI 0 "general_operand" "")
	(match_dup 4))]
  "TARGET_80387"
  "
{
  operands[3] = gen_reg_rtx (DImode);
  operands[4] = gen_lowpart (SImode, operands[3]);
  operands[5] = gen_reg_rtx (DFmode);
}")

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

;; Signed conversion to DImode.

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

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

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

(define_insn ""
  [(set (match_operand:DI 0 "general_operand" "=m,!*r")
	(fix:DI (fix:DF (match_operand:DF 1 "register_operand" "f,f"))))
   (clobber (match_scratch:HI 2 "=&r,&r"))
   (clobber (match_dup 1))]
  "TARGET_80387"
  "* return (char *) output_fix_trunc (insn, operands);")

(define_insn ""
  [(set (match_operand:DI 0 "general_operand" "=m,!*r")
	(fix:DI (fix:SF (match_operand:SF 1 "register_operand" "f,f"))))
   (clobber (match_scratch:HI 2 "=&r,&r"))
   (clobber (match_dup 1))]
  "TARGET_80387"
  "* return (char *) output_fix_trunc (insn, operands);")

;; Signed MODE_FLOAT conversion to SImode.

(define_expand "fix_truncdfsi2"
  [(parallel [(set (match_operand:SI 0 "general_operand" "")
		   (fix:SI
		    (fix:DF (match_operand:DF 1 "register_operand" ""))))
	      (clobber (match_scratch:HI 2 ""))])]
  "TARGET_80387"
  "")

(define_expand "fix_truncsfsi2"
  [(parallel [(set (match_operand:SI 0 "general_operand" "")
		   (fix:SI
		    (fix:SF (match_operand:SF 1 "register_operand" ""))))
	      (clobber (match_scratch:HI 2 ""))])]
  "TARGET_80387"
  "")

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=m,!*r")
	(fix:SI (fix:DF (match_operand:DF 1 "register_operand" "f,f"))))
   (clobber (match_scratch:HI 2 "=&r,&r"))]
  "TARGET_80387"
  "* return (char *) output_fix_trunc (insn, operands);")

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=m,!*r")
	(fix:SI (fix:SF (match_operand:SF 1 "register_operand" "f,f"))))
   (clobber (match_scratch:HI 2 "=&r,&r"))]
  "TARGET_80387"
  "* return (char *) 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 "general_operand" "")))]
  "TARGET_80387"
  "")

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

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

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

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

(define_insn ""
  [(set (match_operand 0 "register_operand" "=f,f")
	(match_operator 2 "float_op"
			[(match_operand:DI 1 "general_operand" "m,!*r")]))]
  "TARGET_80387 && GET_MODE (operands[0]) == GET_MODE (operands[2])
   && GET_MODE_CLASS (GET_MODE (operands[0])) == MODE_FLOAT"
  "*
{
  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 0 "register_operand" "=f,f")
	(match_operator 2 "float_op"
			[(match_operand:SI 1 "general_operand" "m,!*r")]))]
  "TARGET_80387 && GET_MODE (operands[0]) == GET_MODE (operands[2])
   && GET_MODE_CLASS (GET_MODE (operands[0])) == MODE_FLOAT"
  "*
{
  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 "adddi3"
  [(set (match_operand:DI 0 "general_operand" "=&r,ro")
	(plus:DI (match_operand:DI 1 "general_operand" "%0,0")
		 (match_operand:DI 2 "general_operand" "o,riF")))]
  ""
  "*
{
  rtx low[3], high[3];

  CC_STATUS_INIT;

  split_di (operands, 3, low, high);

  output_asm_insn (AS2 (add%L0,%2,%0), low);
  output_asm_insn (AS2 (adc%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_insn "addsi3"
  [(set (match_operand:SI 0 "general_operand" "=?r,rm,r")
	(plus:SI (match_operand:SI 1 "general_operand" "%r,0,0")
		 (match_operand:SI 2 "general_operand" "ri,ri,rm")))]
  ""
  "*
{
  if (REG_P (operands[0]) && REGNO (operands[0]) != REGNO (operands[1]))
    {
      if (REG_P (operands[2]) && REGNO (operands[0]) == REGNO (operands[2]))
	return AS2 (add%L0,%1,%0);

      if (! TARGET_486 || ! REG_P (operands[2]))
        {
	  CC_STATUS_INIT;

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

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

      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);
}")

;; ??? `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_insn "addhi3"
  [(set (match_operand:HI 0 "general_operand" "=rm,r")
	(plus:HI (match_operand:HI 1 "general_operand" "%0,0")
		 (match_operand:HI 2 "general_operand" "ri,rm")))]
  ""
  "*
{
  if (operands[2] == const1_rtx)
    return AS1 (inc%W0,%0);

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

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

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

  if (operands[2] == constm1_rtx)
    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 "general_operand" "%r")
;		 (match_operand:SI 2 "general_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;
;}")

;; addsi3 is faster, so put this after.

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
        (match_operand:QI 1 "address_operand" "p"))]
  ""
  "*
{
  CC_STATUS_INIT;
  /* 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);
    }
  return AS2 (lea%L0,%a1,%0);
}")

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

(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 "subdi3"
  [(set (match_operand:DI 0 "general_operand" "=&r,ro")
	(minus:DI (match_operand:DI 1 "general_operand" "0,0")
		  (match_operand:DI 2 "general_operand" "o,riF")))]
  ""
  "*
{
  rtx low[3], high[3];

  CC_STATUS_INIT;

  split_di (operands, 3, low, high);

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

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

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

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

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

(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 "general_operand" "=a")
;	(mult:QI (match_operand:QI 1 "general_operand" "%0")
;		 (match_operand:QI 2 "general_operand" "qm")))]
;  ""
;  "imul%B0 %2,%0")

(define_insn ""
  [(set (match_operand:HI 0 "general_operand" "=r")
	(mult:SI (match_operand:HI 1 "general_operand" "%0")
		 (match_operand:HI 2 "general_operand" "r")))]
  "GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x80"
  "* return AS2 (imul%W0,%2,%0);")

(define_insn "mulhi3"
  [(set (match_operand:HI 0 "general_operand" "=r,r")
	(mult:SI (match_operand:HI 1 "general_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 ""
  [(set (match_operand:SI 0 "general_operand" "=r")
	(mult:SI (match_operand:SI 1 "general_operand" "%0")
		 (match_operand:SI 2 "general_operand" "r")))]
  "GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x80"
  "* return AS2 (imul%L0,%2,%0);")

(define_insn "mulsi3"
  [(set (match_operand:SI 0 "general_operand" "=r,r")
	(mult:SI (match_operand:SI 1 "general_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 ""
  [(set (match_operand:HI 0 "general_operand" "=a")
	(mult:SI (zero_extend:HI
		  (match_operand:QI 1 "nonimmediate_operand" "%0"))
		 (zero_extend:HI
		  (match_operand:QI 2 "nonimmediate_operand" "qm"))))]
  ""
  "mul%B0 %2")

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

(define_expand "muldf3"
  [(set (match_operand:DF 0 "register_operand" "")
	(mult:DF (match_operand:DF 1 "nonimmediate_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 "nonimmediate_operand" "")
		 (match_operand:SF 2 "nonimmediate_operand" "")))]
  "TARGET_80387"
  "")

;;- divide instructions

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

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

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

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

(define_expand "divsf3"
  [(set (match_operand:SF 0 "register_operand" "")
	(div:SF (match_operand:SF 1 "nonimmediate_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 "general_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 "general_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 "general_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 "general_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 "general_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 "general_operand" "=r,r,rm,r")
	(and:SI (match_operand:SI 1 "general_operand" "%rm,qm,0,0")
		(match_operand:SI 2 "general_operand" "L,K,ri,rm")))]
  ""
  "*
{
  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_486 || ! 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_486 || ! 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);
	  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 "general_operand" "=rm,r")
	(and:HI (match_operand:HI 1 "general_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 "general_operand" "=qm,q")
	(and:QI (match_operand:QI 1 "general_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 "general_operand" "=q")
	(and:SI
	 (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "qm"))
		(match_operand:SI 2 "general_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 "general_operand" "=rm,r")
	(ior:SI (match_operand:SI 1 "general_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 "general_operand" "=rm,r")
	(ior:HI (match_operand:HI 1 "general_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 "general_operand" "=qm,q")
	(ior:QI (match_operand:QI 1 "general_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 "general_operand" "=rm,r")
	(xor:SI (match_operand:SI 1 "general_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 "general_operand" "=rm,r")
	(xor:HI (match_operand:HI 1 "general_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 "general_operand" "=qm,q")
	(xor:QI (match_operand:QI 1 "general_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 "general_operand" "=rm")
	(neg:SI (match_operand:SI 1 "general_operand" "0")))]
  ""
  "neg%L0 %0")

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

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

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

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

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=f")
	(neg:DF (float_extend:DF (match_operand:SF 1 "general_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 "general_operand" "0")))]
  "TARGET_80387"
  "fabs")

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

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

(define_insn "sqrtsf2"
  [(set (match_operand:SF 0 "register_operand" "=f")
	(sqrt:SF (match_operand:SF 1 "general_operand" "0")))]
  "TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)"
  "fsqrt")

(define_insn "sqrtdf2"
  [(set (match_operand:DF 0 "register_operand" "=f")
	(sqrt:DF (match_operand:DF 1 "general_operand" "0")))]
  "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 "general_operand" "0"))))]
  "TARGET_80387 && (TARGET_IEEE_FP || flag_fast_math)"
  "fsqrt")

(define_insn "sindf2"
  [(set (match_operand:DF 0 "register_operand" "=f")
	(unspec:DF [(match_operand:DF 1 "register_operand" "0")] 1))]
  "TARGET_80387 && (TARGET_IEEE_FP || 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_80387 && (TARGET_IEEE_FP || 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_80387 && (TARGET_IEEE_FP || 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_80387 && (TARGET_IEEE_FP || 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_80387 && (TARGET_IEEE_FP || 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_80387 && (TARGET_IEEE_FP || flag_fast_math)"
  "fcos")

;;- one complement instructions

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

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

(define_insn "one_cmplqi2"
  [(set (match_operand:QI 0 "general_operand" "=qm")
	(not:QI (match_operand:QI 1 "general_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 "general_operand" "=r,rm")
	(ashift:SI (match_operand:SI 1 "general_operand" "r,0")
		   (match_operand:SI 2 "nonmemory_operand" "M,cI")))]
  ""
  "*
{
  if (REG_P (operands[0]) && REGNO (operands[0]) != REGNO (operands[1]))
    {
      if (TARGET_486 && 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 "general_operand" "=rm")
	(ashift:HI (match_operand:HI 1 "general_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 "general_operand" "=qm")
	(ashift:QI (match_operand:QI 1 "general_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 "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 "general_operand" "=rm")
	(ashiftrt:SI (match_operand:SI 1 "general_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 "general_operand" "=rm")
	(ashiftrt:HI (match_operand:HI 1 "general_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 "general_operand" "=qm")
	(ashiftrt:QI (match_operand:QI 1 "general_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_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 "general_operand" "=rm")
	(lshiftrt:SI (match_operand:SI 1 "general_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 "general_operand" "=rm")
	(lshiftrt:HI (match_operand:HI 1 "general_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 "general_operand" "=qm")
	(lshiftrt:QI (match_operand:QI 1 "general_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 "general_operand" "=rm")
	(rotate:SI (match_operand:SI 1 "general_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 "general_operand" "=rm")
	(rotate:HI (match_operand:HI 1 "general_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 "general_operand" "=qm")
	(rotate:QI (match_operand:QI 1 "general_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 "general_operand" "=rm")
	(rotatert:SI (match_operand:SI 1 "general_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 "general_operand" "=rm")
	(rotatert:HI (match_operand:HI 1 "general_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 "general_operand" "=qm")
	(rotatert:QI (match_operand:QI 1 "general_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.
(define_insn "insv"
  [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+&r")
			 (match_operand:SI 1 "general_operand" "i")
			 (match_operand:SI 2 "general_operand" "i"))
	(match_operand:SI 3 "general_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 "general_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;
}")

;; ??? Are these constraints right?
(define_insn ""
  [(set (zero_extract:SI (match_operand:SI 0 "general_operand" "+&qo")
			 (const_int 8)
			 (const_int 8))
	(match_operand:QI 1 "general_operand" "qn"))]
  ""
  "*
{
  if (REG_P (operands[0]))
    return AS2 (mov%B0,%1,%h0);

  operands[0] = adj_offsettable_operand (operands[0], 1);
  return AS2 (mov%B0,%1,%0);
}")
*/

;; 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 "general_operand" "+rm")
			 (const_int 1)
			 (match_operand:SI 2 "general_operand" "r"))
	(match_operand:SI 3 "const_int_operand" "n"))]
  "! TARGET_486 && 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 "general_operand" "+rm")
	(xor:SI (ashift:SI (const_int 1)
			   (match_operand:SI 1 "general_operand" "r"))
		(match_dup 0)))]
  "! TARGET_486 && GET_CODE (operands[1]) != CONST_INT"
  "*
{
  CC_STATUS_INIT;

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

/* ??? This works, but that SUBREG looks dangerous.
(define_insn ""
  [(set (match_operand:HI 0 "general_operand" "+rm")
	(xor:HI (subreg:HI
		 (ashift:SI (const_int 1)
			    (sign_extend:SI
			     (match_operand:HI 1 "nonimmediate_operand" "r"))) 0)
		(match_dup 0)))]
  "! TARGET_486"
  "*
{
  CC_STATUS_INIT;

  return AS2 (btc%W0,%1,%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.

;; ??? All bets are off if operand 0 is a volatile MEM reference.

(define_insn ""
  [(set (cc0) (zero_extract (match_operand 0 "general_operand" "rm")
			    (match_operand:SI 1 "const_int_operand" "n")
			    (match_operand:SI 2 "const_int_operand" "n")))]
  "GET_MODE_CLASS (GET_MODE (operands[0])) == MODE_INT
   && GET_MODE_SIZE (GET_MODE (operands[0])) <= 4
   && (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]))
    {
      /* We may set the sign bit spuriously.  */

      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);
}")

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

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

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

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

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

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

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

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

  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 "general_operand" "rm"))]
  ""
  "*
{
  CC_STATUS_INIT;

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

;; 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)
	(minus:SI (match_operand:SI 0 "general_operand" "")
		  (match_operand:SI 1 "general_operand" "")))
   (set (cc0)
	(compare:CC (match_dup 5)
		    (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 5)
					      (const_int 4))
				     (label_ref (match_operand 3 "" ""))))))
     (clobber (match_scratch:SI 6 ""))])]
  "flag_pic"
  "
{
  operands[5] = 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 "general_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" "")))])]
  ""
  "
{
  if (flag_pic)
    current_function_uses_pic_offset_table = 1;
}")

(define_insn ""
  [(call (match_operand:QI 0 "indirect_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")))]
  ""
  "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.
  ""
  "
{
  if (flag_pic)
    current_function_uses_pic_offset_table = 1;
}")

(define_insn ""
  [(call (match_operand:QI 0 "indirect_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.
  ""
  "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" "")))])]
  ""
  "
{
  if (flag_pic)
    current_function_uses_pic_offset_table = 1;
}")

(define_insn ""
  [(set (match_operand 0 "" "=rf")
	(call (match_operand:QI 1 "indirect_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")))]
  ""
  "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.
  ""
  "
{
  if (flag_pic)
    current_function_uses_pic_offset_table = 1;
}")

(define_insn ""
  [(set (match_operand 0 "" "=rf")
	(call (match_operand:QI 1 "indirect_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.
  ""
  "call %P1")

;; 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);
	  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 "cmpstrsi"
  [(parallel [(set (match_operand:QI 0 "general_operand" "")
		   (compare:CC (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:QI 0 "general_operand" "=&q")
	(compare:CC (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[3], label;

  label = gen_label_rtx ();

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

  xops[0] = operands[0];
  xops[1] = gen_rtx (MEM, QImode,
		     gen_rtx (PLUS, SImode, operands[1], constm1_rtx));
  xops[2] = gen_rtx (MEM, QImode,
		     gen_rtx (PLUS, SImode, operands[2], constm1_rtx));

  output_asm_insn (AS2 (mov%B0,%1,%b0), xops);
  output_asm_insn (AS2 (sub%B0,%2,%b0), xops);
  ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\", CODE_LABEL_NUMBER (label));
  RET;
}")

(define_insn ""
  [(set (cc0)
	(compare:CC (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];

  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);")

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=&r")
	(plus:SI (ffs:SI (match_operand:SI 1 "general_operand" "rm"))
		 (const_int -1)))]
  ""
  "*
{
  rtx xops[2];

  xops[0] = operands[0];
  xops[1] = constm1_rtx;
  output_asm_insn (AS2 (mov%L0,%1,%0), xops);
  return AS2 (bsf%L0,%1,%0);
}")

(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 "general_operand" "=&r")
	(plus:HI (ffs:HI (match_operand:SI 1 "general_operand" "rm"))
		 (const_int -1)))]
  ""
  "*
{
  rtx xops[2];

  xops[0] = operands[0];
  xops[1] = constm1_rtx;
  output_asm_insn (AS2 (mov%W0,%1,%0), xops);
  return AS2 (bsf%W0,%1,%0);
}")

;; 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 "general_operand" "0,fm")
			 (match_operand:DF 2 "general_operand" "fm,0")]))]
  "TARGET_80387"
  "* return (char *) output_387_binary_op (insn, operands);")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=f,f")
	(match_operator:DF 3 "binary_387_op"
	   [(float:DF (match_operand:SI 1 "general_operand" "m,!*r"))
	    (match_operand:DF 2 "general_operand" "0,0")]))]
  "TARGET_80387"
  "* return (char *) output_387_binary_op (insn, operands);")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=f,f,f")
	(match_operator:DF 3 "binary_387_op"
	   [(float_extend:DF (match_operand:SF 1 "general_operand" "fm,!*r,0"))
	    (match_operand:DF 2 "general_operand" "0,0,f")]))]
  "TARGET_80387"
  "* return (char *) output_387_binary_op (insn, operands);")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=f,f")
	(match_operator:DF 3 "binary_387_op"
	  [(match_operand:DF 1 "general_operand" "0,0")
	   (float:DF (match_operand:SI 2 "general_operand" "m,!*r"))]))]
  "TARGET_80387"
  "* return (char *) output_387_binary_op (insn, operands);")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=f,f,f")
	(match_operator:DF 3 "binary_387_op"
	  [(match_operand:DF 1 "general_operand" "0,0,f")
	   (float_extend:DF
	    (match_operand:SF 2 "general_operand" "fm,!*r,0"))]))]
  "TARGET_80387"
  "* return (char *) output_387_binary_op (insn, operands);")

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=f,f")
	(match_operator:SF 3 "binary_387_op"
			[(match_operand:SF 1 "general_operand" "0,fm")
			 (match_operand:SF 2 "general_operand" "fm,0")]))]
  "TARGET_80387"
  "* return (char *) output_387_binary_op (insn, operands);")

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=f,f")
	(match_operator:SF 3 "binary_387_op"
	  [(float:SF (match_operand:SI 1 "general_operand" "m,!*r"))
	   (match_operand:SF 2 "general_operand" "0,0")]))]
  "TARGET_80387"
  "* return (char *) output_387_binary_op (insn, operands);")

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=f,f")
	(match_operator:SF 3 "binary_387_op"
	  [(match_operand:SF 1 "general_operand" "0,0")
	   (float:SF (match_operand:SI 2 "general_operand" "m,!*r"))]))]
  "TARGET_80387"
  "* return (char *) output_387_binary_op (insn, operands);")

(define_expand "strlensi"
  [(parallel [(set (match_dup 4)
		   (unspec:SI [(mem:BLK (match_operand:BLK 1 "general_operand" ""))
			       (match_operand:QI 2 "register_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" "")
	(minus:SI (match_dup 5)
		 (const_int 1)))]
  ""
  "
{
  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 & %esi.

(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 "register_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\";
}")

;;- Local variables:
;;- mode:emacs-lisp
;;- comment-start: ";;- "
;;- eval: (set-syntax-table (copy-sequence (syntax-table)))
;;- eval: (modify-syntax-entry ?[ "(]")
;;- eval: (modify-syntax-entry ?] ")[")
;;- eval: (modify-syntax-entry ?{ "(}")
;;- eval: (modify-syntax-entry ?} "){")
;;- End:
