;;- Machine description for GNU compiler
;;- Motorola 68000 Version
;;   Copyright (C) 1987, 1988, 1993 Free Software Foundation, Inc.

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


;;- instruction definitions

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

;;- When naming insn's (operand 0 of define_insn) be careful about using
;;- names from other targets machine descriptions.

;;- cpp macro #define NOTICE_UPDATE_CC in file tm.h handles condition code
;;- updates for most instructions.

;;- Operand classes for the register allocator:
;;- 'a' one of the address registers can be used.
;;- 'd' one of the data registers can be used.
;;- 'f' one of the m68881 registers can be used
;;- 'r' either a data or an address register can be used.
;;- 'x' if one of the Sun FPA registers                    
;;- 'y' if one of the Low Sun FPA registers (fpa0-fpa15).

;;- Immediate Floating point operator constraints
;;- 'G' a floating point constant that is *NOT* one of the standard
;;   68881 constant values (to force calling output_move_const_double
;;   to get it from rom if it is a 68881 constant).
;;- 'H' one of the standard FPA constant values
;;
;;   See the functions standard_XXX_constant_p in output-m68k.c for more
;; info.

;;- Immediate integer operand constraints:
;;- 'I'  1 .. 8
;;- 'J'  -32768 .. 32767
;;- 'K'  all integers EXCEPT -128 .. 127
;;- 'L'  -8 .. -1

;;- Assembler specs:
;;- "%."    size separator ("." or "")			move%.l d0,d1
;;- "%#"    immediate separator ("#" or "")		move%.l %#0,d0
;;- "%-"    push operand "sp@-"				move%.l d0,%-
;;- "%+"    pop operand "sp@+"				move%.l d0,%+
;;- "%@"    top of stack "sp@"				move%.l d0,%@
;;- "%!"    fpcr register
;;- "%$"    single-precision fp specifier ("s" or "")	f%$add.x fp0,fp1
;;- "%&"    double-precision fp specifier ("d" or "")	f%&add.x fp0,fp1

;;- Information about 68040 port.

;;- The 68040 executes all 68030 and 68881/2 instructions, but some must
;;- be emulated in software by the OS.  It is faster to avoid these
;;- instructions and issue a library call rather than trapping into
;;- the kernel.  The affected instructions are fintrz and fscale.  The
;;- TARGET_68040 flag turns the use of the opcodes off.

;;- The '040 also implements a set of new floating-point instructions
;;- which specify the rounding precision in the opcode.  This finally
;;- permit the 68k series to be truly IEEE compliant, and solves all
;;- issues of excess precision accumulating in the extended registers.
;;- By default, GCC does not use these instructions, since such code will
;;- not run on an '030.  To use these instructions, use the -m68040-only
;;- switch.  By changing TARGET_DEFAULT to include TARGET_68040_ONLY,
;;- you can make these instructions the default.

;;- These new instructions aren't directly in the md.  They are brought
;;- into play by defining "%$" and "%&" to expand to "s" and "d" rather
;;- than "".


;;- 		FPA port explanation:

;;-		Usage of the Sun FPA and the 68881 together

;;- The current port of gcc to the sun fpa disallows use of the m68881
;;- instructions completely if code is targeted for the fpa.  This is
;;- for the following reasons:

;;- 1) Expressing the preference hierarchy (ie. use the fpa if you
;;- can, the 68881 otherwise, and data registers only if you are
;;- forced to it) is a bitch with the current constraint scheme,
;;- especially since it would have to work for any combination of
;;- -mfpa, -m68881.

;;- 2) There are no instructions to move between the two types of
;;- registers; the stack must be used as an intermediary.

;;- It could indeed be done; I think the best way would be to have
;;- separate patterns for TARGET_FPA (which implies a 68881),
;;- TARGET_68881, and no floating point co-processor.  Use
;;- define_expands for all of the named instruction patterns, and
;;- include code in the FPA instruction to deal with the 68881 with
;;- preferences specifically set to favor the fpa.  Some of this has
;;- already been done:
;;-
;;- 	1) Separation of most of the patterns out into a TARGET_FPA
;;- case and a TARGET_68881 case (the exceptions are the patterns
;;- which would need one define_expand and three define_insn's under
;;- it (with a lot of duplicate code between them) to replace the
;;- current single define_insn.  These are mov{[ds]f,[ds]i} and the
;;- first two patterns in the md.
;;-
;;- Some would still have to be done:
;;-
;;-	1) Add code to the fpa patterns which correspond to 68881
;;- patterns to deal with the 68881 case (including preferences!).
;;- What you might actually do here is combine the fpa and 68881 code
;;- back together into one pattern for those instructions where it's
;;- absolutely necessary and save yourself some duplicate code.  I'm
;;- not completely sure as to whether you could get away with doing
;;- this only for the mov* insns, or if you'd have to do it for all
;;- named insns.
;;- 	2) Add code to the mov{[ds]f,[ds]i} instructions to handle
;;- moving between fpa regs and 68881 regs.

;;- Since the fpa is more powerful than the 68881 and also has more
;;- registers, and since I think the resultant md would be medium ugly
;;- (lot's of duplicate code, ugly constraint strings), I elected not
;;- to do this change.

;;- Another reason why someone *might* want to do the change is to
;;- control which register classes are accessed in a slightly cleaner
;;- way than I have.  See the blurb on CONDITIONAL_REGISTER_USAGE in
;;- the internals manual.

;;- Yet another reason why someone might want to do this change is to
;;- allow use of some of the 68881 insns which have no equivalent on
;;- the fpa.  The sqrt instruction comes fairly quickly to mind.

;;- If this is ever done, don't forget to change sun3.h so that
;;- it *will* define __HAVE_68881__ when the FPA is in use.

;;-		Condition code hack

;;- When a floating point compare is done in the fpa, the resulting
;;- condition codes are left in the fpastatus register.  The values in
;;- this register must be moved into the 68000 cc register before any
;;- jump is executed.  Once this has been done, regular jump
;;- instructions are fine (ie. floating point jumps are not necessary.
;;- They are only done if the cc is in the 68881).

;;- The instructions that move the fpastatus register to the 68000
;;- register clobber a data register (the move cannot be done direct).
;;- These instructions might be bundled either with the compare
;;- instruction, or the branch instruction.  If we were using both the
;;- fpa and the 68881 together, we would wish to only mark the
;;- register clobbered if we were doing the compare in the fpa, but I
;;- think that that decision (whether to clobber the register or not)
;;- must be done before register allocation (makes sense) and hence we
;;- can't know if the floating point compare will be done in the fpa
;;- or the fp.  So whenever we are asked for code that uses the fpa,
;;- we will mark a data register as clobbered.  This is reasonable, as
;;- almost all floating point compare operations done with fpa code
;;- enabled will be done in the fpa.  It's even more reasonable since
;;- we decided to make the 68881 and the fpa mutually exclusive.

;;- We place to code to move the fpastatus register inside of a
;;- define_expand so that we can do it conditionally based on whether
;;- we are targeting an fpa or not.

;;- This still leaves us with the question of where we wish to put the
;;- code to move the fpastatus reg.  If we put it in the compare
;;- instruction, we can restrict the clobbering of the register to
;;- floating point compares, but we can't take advantage of floating
;;- point subtracts & etc. that alter the fpastatus register.  If we
;;- put it in the branch instruction, all branches compiled with fpa
;;- code enabled will clobber a data register, but we will be able to
;;- take advantage of fpa subtracts.  This balance favors putting the
;;- code in with the compare instruction.

;;- Note that if some enterprising hacker should decide to switch
;;- this, he'll need to modify the code in NOTICE_UPDATE_CC.

;;-		Usage of the top 16 fpa registers

;;- The only locations which we may transfer fpa registers 16-31 from
;;- or to are the fpa registers 0-15.  (68000 registers and memory
;;- locations are impossible).  This causes problems in gcc, which
;;- assumes that mov?? instructions require no additional registers
;;- (see section 11.7) and since floating point moves *must* be
;;- supported into general registers (see section 12.3 under
;;- HARD_REGNO_OK_FOR_MODE_P) from anywhere.

;;- My solution was to reserve fpa0 for moves into or out of these top
;;- 16 registers and to disparage the choice to reload into or out of
;;- these registers as much as I could.  That alternative is always
;;- last in the list, so it will not be used unless all else fails.  I
;;- will note that according to my current information, sun's compiler
;;- doesn't use these top 16 registers at all.

;;- There is another possible way to do it.  I *believe* that if you
;;- make absolutely sure that the code will not be executed in the
;;- reload pass, you can support the mov?? names with define_expands
;;- which require new registers.  This may be possible by the
;;- appropriate juggling of constraints.  I may come back to this later.

;;- 		Usage of constant RAM

;;- This has been handled correctly (I believe) but the way I've done
;;- it could use a little explanation.  The constant RAM can only be
;;- accessed when the instruction is in "command register" mode.
;;- "command register" mode means that no accessing of memory or the
;;- 68000 registers is being done.  This can be expressed easily in
;;- constraints, so generally the mode of the instruction is
;;- determined by a branch off of which_alternative.  In outputting
;;- instructions, a 'w' means to output an access to the constant ram
;;- (if the arg is CONST_DOUBLE and is one of the available
;;- constants), and 'x' means to output a register pair (if the arg is
;;- a 68000 register) and a 'y' is the combination of the above two
;;- processes.  You use a 'y' in two operand DF instructions where you
;;- *know* the other operand is an fpa register, you use an 'x' in DF
;;- instructions where the arg might be a 68000 register and the
;;- instruction is *not* in "command register" mode, and you use a 'w'
;;- in two situations: 1) The instruction *is* in command register
;;- mode (and hence won't be accessing 68000 registers), or 2) The
;;- instruction is a two operand SF instruction where you know the
;;- other operand is an fpa register.

;;-		Optimization issues

;;- I actually think that I've included all of the fpa instructions
;;- that should be included.  Note that if someone is interested in
;;- doing serious floating point work on the sun fpa, I would advise
;;- the use of the "asm" instruction in gcc to allow you to use the
;;- sin, cos, and exponential functions on the fpa board.

;;- END FPA Explanation Section.


;;- Some of these insn's are composites of several m68000 op codes.
;;- The assembler (or final @@??) insures that the appropriate one is
;;- selected.

(define_insn ""
  [(set (match_operand:DF 0 "push_operand" "=m")
	(match_operand:DF 1 "general_operand" "ro<>fyE"))]
  ""
  "*
{
  if (FP_REG_P (operands[1]))
    return \"fmove%.d %f1,%0\";
  if (FPA_REG_P (operands[1]))
    return \"fpmove%.d %1, %x0\";
  return output_move_double (operands);
}")

(define_insn ""
  [(set (match_operand:DI 0 "push_operand" "=m")
	(match_operand:DI 1 "general_operand" "ro<>Fy"))]
  ""
  "*
{
  return output_move_double (operands);
}")

;; 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.
(define_insn "tstsi"
  [(set (cc0)
	(match_operand:SI 0 "nonimmediate_operand" "rm"))]
  ""
  "*
{
#ifdef ISI_OV
  /* ISI's assembler fails to handle tstl a0.  */
  if (! ADDRESS_REG_P (operands[0]))
#else
  if (TARGET_68020 || ! ADDRESS_REG_P (operands[0]))
#endif
    return \"tst%.l %0\";
  /* If you think that the 68020 does not support tstl a0,
     reread page B-167 of the 68020 manual more carefully.  */
  /* On an address reg, cmpw may replace cmpl.  */
#ifdef SGS_CMP_ORDER
  return \"cmp%.w %0,%#0\";
#else
  return \"cmp%.w %#0,%0\";
#endif
}")

;; This can't use an address register, because comparisons
;; with address registers as second operand always test the whole word.
(define_insn "tsthi"
  [(set (cc0)
	(match_operand:HI 0 "nonimmediate_operand" "dm"))]
  ""
  "tst%.w %0")

(define_insn "tstqi"
  [(set (cc0)
	(match_operand:QI 0 "nonimmediate_operand" "dm"))]
  ""
  "tst%.b %0")
  
(define_expand "tstsf"
  [(set (cc0)
	(match_operand:SF 0 "general_operand" ""))]
  "TARGET_68881 || TARGET_FPA"
  "
{
  if (TARGET_FPA)
    {
      emit_insn (gen_tstsf_fpa (operands[0]));
      DONE;
    }
}")

(define_insn "tstsf_fpa"
  [(set (cc0)
	(match_operand:SF 0 "general_operand" "xmdF"))
   (clobber (match_scratch:SI 1 "=d"))]
  "TARGET_FPA"
  "fptst%.s %x0\;fpmove fpastatus,%1\;movw %1,cc")

(define_insn ""
  [(set (cc0)
	(match_operand:SF 0 "general_operand" "fdm"))]
  "TARGET_68881"
  "*
{
  cc_status.flags = CC_IN_68881;
  if (FP_REG_P (operands[0]))
    return \"ftst%.x %0\";
  return \"ftst%.s %0\";
}")

(define_expand "tstdf"
  [(set (cc0)
	(match_operand:DF 0 "general_operand" ""))]
  "TARGET_68881 || TARGET_FPA"
  "
{
  if (TARGET_FPA)
    {
      emit_insn (gen_tstsf_fpa (operands[0]));
      DONE;
    }
}")

(define_insn "tstdf_fpa"
  [(set (cc0)
	(match_operand:DF 0 "general_operand" "xrmF"))
   (clobber (match_scratch:SI 1 "=d"))]
  "TARGET_FPA"
  "fptst%.d %x0\;fpmove fpastatus,%1\;movw %1,cc")

(define_insn ""
  [(set (cc0)
	(match_operand:DF 0 "general_operand" "fm"))]
  "TARGET_68881"
  "*
{
  cc_status.flags = CC_IN_68881;
  if (FP_REG_P (operands[0]))
    return \"ftst%.x %0\";
  return \"ftst%.d %0\";
}")

;; compare instructions.

;; A composite of the cmp, cmpa, & cmpi m68000 op codes.
(define_insn "cmpsi"
  [(set (cc0)
	(compare (match_operand:SI 0 "nonimmediate_operand" "rKs,mr,>")
		 (match_operand:SI 1 "general_operand" "mr,Ksr,>")))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
#ifdef SGS_CMP_ORDER
    return \"cmpm%.l %0,%1\";
#else
    return \"cmpm%.l %1,%0\";
#endif
  if (REG_P (operands[1])
      || (!REG_P (operands[0]) && GET_CODE (operands[0]) != MEM))
    { cc_status.flags |= CC_REVERSED;
#ifdef SGS_CMP_ORDER
      return \"cmp%.l %d1,%d0\";
#else
      return \"cmp%.l %d0,%d1\";
#endif
    }
#ifdef SGS_CMP_ORDER
  return \"cmp%.l %d0,%d1\";
#else
  return \"cmp%.l %d1,%d0\";
#endif
}")

(define_insn "cmphi"
  [(set (cc0)
	(compare (match_operand:HI 0 "nonimmediate_operand" "rnm,d,n,m,>")
		 (match_operand:HI 1 "general_operand" "d,rnm,m,n,>")))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
#ifdef SGS_CMP_ORDER
    return \"cmpm%.w %0,%1\";
#else
    return \"cmpm%.w %1,%0\";
#endif
  if ((REG_P (operands[1]) && !ADDRESS_REG_P (operands[1]))
      || (!REG_P (operands[0]) && GET_CODE (operands[0]) != MEM))
    { cc_status.flags |= CC_REVERSED;
#ifdef SGS_CMP_ORDER
      return \"cmp%.w %d1,%d0\";
#else
      return \"cmp%.w %d0,%d1\";
#endif
    }
#ifdef SGS_CMP_ORDER
  return \"cmp%.w %d0,%d1\";
#else
  return \"cmp%.w %d1,%d0\";
#endif
}")

(define_insn "cmpqi"
  [(set (cc0)
	(compare (match_operand:QI 0 "nonimmediate_operand" "dn,md,>")
		 (match_operand:QI 1 "general_operand" "dm,nd,>")))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
#ifdef SGS_CMP_ORDER
    return \"cmpm%.b %0,%1\";
#else
    return \"cmpm%.b %1,%0\";
#endif
  if (REG_P (operands[1])
      || (!REG_P (operands[0]) && GET_CODE (operands[0]) != MEM))
    { cc_status.flags |= CC_REVERSED;
#ifdef SGS_CMP_ORDER
      return \"cmp%.b %d1,%d0\";
#else
      return \"cmp%.b %d0,%d1\";
#endif
    }
#ifdef SGS_CMP_ORDER
  return \"cmp%.b %d0,%d1\";
#else
  return \"cmp%.b %d1,%d0\";
#endif
}")

(define_expand "cmpdf"
  [(set (cc0)
	(compare (match_operand:DF 0 "general_operand" "")
		 (match_operand:DF 1 "general_operand" "")))]
  "TARGET_68881 || TARGET_FPA"
  "
{
  if (TARGET_FPA)
    {
      emit_insn (gen_cmpdf_fpa (operands[0], operands[1]));
      DONE;
    }
}")

(define_insn "cmpdf_fpa"
  [(set (cc0)
	(compare (match_operand:DF 0 "general_operand" "x,y")
		 (match_operand:DF 1 "general_operand" "xH,rmF")))
   (clobber (match_scratch:SI 2 "=d,d"))]
  "TARGET_FPA"
  "fpcmp%.d %y1,%0\;fpmove fpastatus,%2\;movw %2,cc")

(define_insn ""
  [(set (cc0)
	(compare (match_operand:DF 0 "general_operand" "f,mG")
		 (match_operand:DF 1 "general_operand" "fmG,f")))]
  "TARGET_68881"
  "*
{
  cc_status.flags = CC_IN_68881;
#ifdef SGS_CMP_ORDER
  if (REG_P (operands[0]))
    {
      if (REG_P (operands[1]))
	return \"fcmp%.x %0,%1\";
      else
        return \"fcmp%.d %0,%f1\";
    }
  cc_status.flags |= CC_REVERSED;
  return \"fcmp%.d %1,%f0\";
#else
  if (REG_P (operands[0]))
    {
      if (REG_P (operands[1]))
	return \"fcmp%.x %1,%0\";
      else
        return \"fcmp%.d %f1,%0\";
    }
  cc_status.flags |= CC_REVERSED;
  return \"fcmp%.d %f0,%1\";
#endif
}")

(define_expand "cmpsf"
 [(set (cc0)
       (compare (match_operand:SF 0 "general_operand" "")
		(match_operand:SF 1 "general_operand" "")))]
 "TARGET_68881 || TARGET_FPA"
 "
{
  if (TARGET_FPA)
    {
      emit_insn (gen_cmpsf_fpa (operands[0], operands[1]));
      DONE;
    }
}")

(define_insn "cmpsf_fpa"
  [(set (cc0)
	(compare (match_operand:SF 0 "general_operand" "x,y")
		 (match_operand:SF 1 "general_operand" "xH,rmF")))
   (clobber (match_scratch:SI 2 "=d,d"))]
  "TARGET_FPA"
  "fpcmp%.s %w1,%x0\;fpmove fpastatus,%2\;movw %2,cc")

(define_insn ""
  [(set (cc0)
	(compare (match_operand:SF 0 "general_operand" "f,mdG")
		 (match_operand:SF 1 "general_operand" "fmdG,f")))]
  "TARGET_68881"
  "*
{
  cc_status.flags = CC_IN_68881;
#ifdef SGS_CMP_ORDER
  if (FP_REG_P (operands[0]))
    {
      if (FP_REG_P (operands[1]))
	return \"fcmp%.x %0,%1\";
      else
        return \"fcmp%.s %0,%f1\";
    }
  cc_status.flags |= CC_REVERSED;
  return \"fcmp%.s %1,%f0\";
#else
  if (FP_REG_P (operands[0]))
    {
      if (FP_REG_P (operands[1]))
	return \"fcmp%.x %1,%0\";
      else
        return \"fcmp%.s %f1,%0\";
    }
  cc_status.flags |= CC_REVERSED;
  return \"fcmp%.s %f0,%1\";
#endif
}")

;; Recognizers for btst instructions.

(define_insn ""
  [(set (cc0) (zero_extract (match_operand:QI 0 "nonimmediate_operand" "do")
			    (const_int 1)
			    (minus:SI (const_int 7)
				      (match_operand:SI 1 "general_operand" "di"))))]
  ""
  "* { return output_btst (operands, operands[1], operands[0], insn, 7); }")

(define_insn ""
  [(set (cc0) (zero_extract (match_operand:SI 0 "nonimmediate_operand" "d")
			    (const_int 1)
			    (minus:SI (const_int 31)
				      (match_operand:SI 1 "general_operand" "di"))))]
  ""
  "* { return output_btst (operands, operands[1], operands[0], insn, 31); }")

;; The following two patterns are like the previous two
;; except that they use the fact that bit-number operands
;; are automatically masked to 3 or 5 bits.

(define_insn ""
  [(set (cc0) (zero_extract (match_operand:QI 0 "nonimmediate_operand" "do")
			    (const_int 1)
			    (minus:SI (const_int 7)
				      (and:SI
				       (match_operand:SI 1 "general_operand" "d")
				       (const_int 7)))))]
  ""
  "* { return output_btst (operands, operands[1], operands[0], insn, 7); }")

(define_insn ""
  [(set (cc0) (zero_extract (match_operand:SI 0 "nonimmediate_operand" "d")
			    (const_int 1)
			    (minus:SI (const_int 31)
				      (and:SI
				       (match_operand:SI 1 "general_operand" "d")
				       (const_int 31)))))]
  ""
  "* { return output_btst (operands, operands[1], operands[0], insn, 31); }")

;; Nonoffsettable mem refs are ok in this one pattern
;; since we don't try to adjust them.
(define_insn ""
  [(set (cc0) (zero_extract (match_operand:QI 0 "nonimmediate_operand" "md")
			    (const_int 1)
			    (match_operand:SI 1 "general_operand" "i")))]
  "GET_CODE (operands[1]) == CONST_INT
   && (unsigned) INTVAL (operands[1]) < 8"
  "*
{
  operands[1] = gen_rtx (CONST_INT, VOIDmode, 7 - INTVAL (operands[1]));
  return output_btst (operands, operands[1], operands[0], insn, 7);
}")

(define_insn ""
  [(set (cc0) (zero_extract (match_operand:SI 0 "nonimmediate_operand" "do")
			    (const_int 1)
			    (match_operand:SI 1 "general_operand" "i")))]
  "GET_CODE (operands[1]) == CONST_INT"
  "*
{
  if (GET_CODE (operands[0]) == MEM)
    {
      operands[0] = adj_offsettable_operand (operands[0],
					     INTVAL (operands[1]) / 8);
      operands[1] = gen_rtx (CONST_INT, VOIDmode, 
			     7 - INTVAL (operands[1]) % 8);
      return output_btst (operands, operands[1], operands[0], insn, 7);
    }
  operands[1] = gen_rtx (CONST_INT, VOIDmode,
			 31 - INTVAL (operands[1]));
  return output_btst (operands, operands[1], operands[0], insn, 31);
}")


;; move instructions

;; A special case in which it is not desirable
;; to reload the constant into a data register.
(define_insn ""
  [(set (match_operand:SI 0 "push_operand" "=m")
	(match_operand:SI 1 "general_operand" "J"))]
  "GET_CODE (operands[1]) == CONST_INT
   && INTVAL (operands[1]) >= -0x8000
   && INTVAL (operands[1]) < 0x8000"
  "*
{
  if (operands[1] == const0_rtx)
    return \"clr%.l %0\";
  return \"pea %a1\";
}")

;This is never used.
;(define_insn "swapsi"
;  [(set (match_operand:SI 0 "general_operand" "+r")
;	(match_operand:SI 1 "general_operand" "+r"))
;   (set (match_dup 1) (match_dup 0))]
;  ""
;  "exg %1,%0")

;; Special case of fullword move when source is zero.
;; The reason this is special is to avoid loading a zero
;; into a data reg with moveq in order to store it elsewhere.
   
(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=g")
	(const_int 0))]
  ;; clr insns on 68000 read before writing.
  ;; This isn't so on the 68010, but we have no alternative for it.
  "(TARGET_68020
    || !(GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))"
  "*
{
  if (ADDRESS_REG_P (operands[0]))
    return \"sub%.l %0,%0\";
  /* moveq is faster on the 68000.  */
  if (DATA_REG_P (operands[0]) && !TARGET_68020)
#if defined(MOTOROLA) && !defined(CRDS)
    return \"moveq%.l %#0,%0\";
#else
    return \"moveq %#0,%0\";
#endif
  return \"clr%.l %0\";
}")

;; General case of fullword move. 
;;
;; This is the main "hook" for PIC code.  When generating
;; PIC, movsi is responsible for determining when the source address
;; needs PIC relocation and appropriately calling legitimize_pic_address
;; to perform the actual relocation.
;;
;; In both the PIC and non-PIC cases the patterns generated will
;; matched by the next define_insn. 
(define_expand "movsi"
  [(set (match_operand:SI 0 "general_operand" "")
	(match_operand:SI 1 "general_operand" ""))]
  ""
  "
{
  if (flag_pic && symbolic_operand (operands[1], SImode)) 
    {
      /* The source is an address which requires PIC relocation.  
         Call legitimize_pic_address with the source, mode, and a relocation
         register (a new pseudo, or the final destination if reload_in_progress
         is set).   Then fall through normally */
      extern rtx legitimize_pic_address();
      rtx temp = reload_in_progress ? operands[0] : gen_reg_rtx (Pmode);
      operands[1] = legitimize_pic_address (operands[1], SImode, temp);
    }
}")

;; General case of fullword move.  The register constraints
;; force integer constants in range for a moveq to be reloaded
;; if they are headed for memory.
(define_insn ""
  ;; Notes: make sure no alternative allows g vs g.
  ;; We don't allow f-regs since fixed point cannot go in them.
  ;; We do allow y and x regs since fixed point is allowed in them.
  [(set (match_operand:SI 0 "general_operand" "=g,da,y,!*x*r*m")
	(match_operand:SI 1 "general_operand" "daymKs,i,g,*x*r*m"))]
  ""
  "*
{
  if (which_alternative == 3)
    return \"fpmove%.l %x1,fpa0\;fpmove%.l fpa0,%x0\";	
  if (FPA_REG_P (operands[1]) || FPA_REG_P (operands[0]))
    return \"fpmove%.l %x1,%x0\";
  if (GET_CODE (operands[1]) == CONST_INT)
    {
      if (operands[1] == const0_rtx
	  && (DATA_REG_P (operands[0])
	      || GET_CODE (operands[0]) == MEM)
	  /* clr insns on 68000 read before writing.
	     This isn't so on the 68010, but we have no alternative for it.  */
	  && (TARGET_68020
	      || !(GET_CODE (operands[0]) == MEM
		   && MEM_VOLATILE_P (operands[0]))))
	return \"clr%.l %0\";
      else if (DATA_REG_P (operands[0])
	       && INTVAL (operands[1]) < 128
	       && INTVAL (operands[1]) >= -128)
        {
#if defined(MOTOROLA) && !defined(CRDS)
          return \"moveq%.l %1,%0\";
#else
	  return \"moveq %1,%0\";
#endif
	}
#ifndef NO_ADDSUB_Q
      else if (DATA_REG_P (operands[0])
	       /* Do this with a moveq #N-8, dreg; addq #8,dreg */
	       && INTVAL (operands[1]) < 136
	       && INTVAL (operands[1]) >= 128)
        {
	  operands[1] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[1]) - 8);
#if defined(MOTOROLA) && !defined(CRDS)
          return \"moveq%.l %1,%0\;addq%.w %#8,%0\";
#else
	  return \"moveq %1,%0\;addq%.w %#8,%0\";
#endif
	}
      else if (DATA_REG_P (operands[0])
	       /* Do this with a moveq #N+8, dreg; subq #8,dreg */
	       && INTVAL (operands[1]) < -128
	       && INTVAL (operands[1]) >= -136)
        {
	  operands[1] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[1]) + 8);
#if defined(MOTOROLA) && !defined(CRDS)
          return \"moveq%.l %1,%0;subq%.w %#8,%0\";
#else
	  return \"moveq %1,%0;subq%.w %#8,%0\";
#endif
	}
#endif
      else if (DATA_REG_P (operands[0])
	       /* If N is in the right range and is even, then use
	          moveq #N/2, dreg; addl dreg,dreg */
	       && INTVAL (operands[1]) > 127
	       && INTVAL (operands[1]) <= 254
	       && INTVAL (operands[1]) % 2 == 0)
        {
	  operands[1] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[1]) / 2);
#if defined(MOTOROLA) && !defined(CRDS)
          return \"moveq%.l %1,%0\;add%.w %0,%0\";
#else
	  return \"moveq %1,%0\;add%.w %0,%0\";
#endif
	}
      else if (ADDRESS_REG_P (operands[0])
	       && INTVAL (operands[1]) < 0x8000
	       && INTVAL (operands[1]) >= -0x8000)
	return \"move%.w %1,%0\";
      else if (push_operand (operands[0], SImode)
	       && INTVAL (operands[1]) < 0x8000
	       && INTVAL (operands[1]) >= -0x8000)
        return \"pea %a1\";
    }
  else if ((GET_CODE (operands[1]) == SYMBOL_REF
	    || GET_CODE (operands[1]) == CONST)
	   && push_operand (operands[0], SImode))
    return \"pea %a1\";
  else if ((GET_CODE (operands[1]) == SYMBOL_REF
	    || GET_CODE (operands[1]) == CONST)
	   && ADDRESS_REG_P (operands[0]))
    return \"lea %a1,%0\";
  return \"move%.l %1,%0\";
}")

(define_insn "movhi"
  [(set (match_operand:HI 0 "general_operand" "=g")
	(match_operand:HI 1 "general_operand" "g"))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == CONST_INT)
    {
      if (operands[1] == const0_rtx
	  && (DATA_REG_P (operands[0])
	      || GET_CODE (operands[0]) == MEM)
	  /* clr insns on 68000 read before writing.
	     This isn't so on the 68010, but we have no alternative for it.  */
	  && (TARGET_68020
	      || !(GET_CODE (operands[0]) == MEM
		   && MEM_VOLATILE_P (operands[0]))))
	return \"clr%.w %0\";
      else if (DATA_REG_P (operands[0])
	       && INTVAL (operands[1]) < 128
	       && INTVAL (operands[1]) >= -128)
        {
#if defined(MOTOROLA) && !defined(CRDS)
          return \"moveq%.l %1,%0\";
#else
	  return \"moveq %1,%0\";
#endif
	}
      else if (INTVAL (operands[1]) < 0x8000
	       && INTVAL (operands[1]) >= -0x8000)
	return \"move%.w %1,%0\";
    }
  else if (CONSTANT_P (operands[1]))
    return \"move%.l %1,%0\";
#ifndef SGS_NO_LI
  /* Recognize the insn before a tablejump, one that refers
     to a table of offsets.  Such an insn will need to refer
     to a label on the insn.  So output one.  Use the label-number
     of the table of offsets to generate this label.  */
  if (GET_CODE (operands[1]) == MEM
      && GET_CODE (XEXP (operands[1], 0)) == PLUS
      && (GET_CODE (XEXP (XEXP (operands[1], 0), 0)) == LABEL_REF
	  || GET_CODE (XEXP (XEXP (operands[1], 0), 1)) == LABEL_REF)
      && GET_CODE (XEXP (XEXP (operands[1], 0), 0)) != PLUS
      && GET_CODE (XEXP (XEXP (operands[1], 0), 1)) != PLUS)
    {
      rtx labelref;
      if (GET_CODE (XEXP (XEXP (operands[1], 0), 0)) == LABEL_REF)
	labelref = XEXP (XEXP (operands[1], 0), 0);
      else
	labelref = XEXP (XEXP (operands[1], 0), 1);
#if defined (MOTOROLA) && !defined (SGS_SWITCH_TABLES)
#ifdef SGS
      asm_fprintf (asm_out_file, \"\\tset %LLI%d,.+2\\n\",
		   CODE_LABEL_NUMBER (XEXP (labelref, 0)));
#else /* not SGS */
      asm_fprintf (asm_out_file, \"\\t.set %LLI%d,.+2\\n\",
	           CODE_LABEL_NUMBER (XEXP (labelref, 0)));
#endif /* not SGS */
#else /* SGS_SWITCH_TABLES or not MOTOROLA */
      ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"LI\",
				 CODE_LABEL_NUMBER (XEXP (labelref, 0)));
#ifdef SGS_SWITCH_TABLES
      /* Set flag saying we need to define the symbol
	 LD%n (with value L%n-LI%n) at the end of the switch table.  */
      switch_table_difference_label_flag = 1;
#endif /* SGS_SWITCH_TABLES */
#endif /* SGS_SWITCH_TABLES or not MOTOROLA */
    }
#endif /* SGS_NO_LI */
  return \"move%.w %1,%0\";
}")

(define_insn "movstricthi"
  [(set (strict_low_part (match_operand:HI 0 "general_operand" "+dm"))
	(match_operand:HI 1 "general_operand" "rmn"))]
  ""
  "*
{
  if (GET_CODE (operands[1]) == CONST_INT)
    {
      if (operands[1] == const0_rtx
	  && (DATA_REG_P (operands[0])
	      || GET_CODE (operands[0]) == MEM)
	  /* clr insns on 68000 read before writing.
	     This isn't so on the 68010, but we have no alternative for it.  */
	  && (TARGET_68020
	      || !(GET_CODE (operands[0]) == MEM
		   && MEM_VOLATILE_P (operands[0]))))
	return \"clr%.w %0\";
    }
  return \"move%.w %1,%0\";
}")

(define_insn "movqi"
  [(set (match_operand:QI 0 "general_operand" "=d,*a,m,m,?*a")
	(match_operand:QI 1 "general_operand" "dmi*a,d*a,dmi,?*a,m"))]
  ""
  "*
{
  rtx xoperands[4];

  /* This is probably useless, since it loses for pushing a struct
     of several bytes a byte at a time.  */
  if (GET_CODE (operands[0]) == MEM
      && GET_CODE (XEXP (operands[0], 0)) == PRE_DEC
      && XEXP (XEXP (operands[0], 0), 0) == stack_pointer_rtx)
    {
      xoperands[1] = operands[1];
      xoperands[2]
        = gen_rtx (MEM, QImode,
		   gen_rtx (PLUS, VOIDmode, stack_pointer_rtx, const1_rtx));
      /* Just pushing a byte puts it in the high byte of the halfword.  */
      /* We must put it in the low-order, high-numbered byte.  */
      output_asm_insn (\"move%.b %1,%-\;move%.b %@,%2\", xoperands);
      return \"\";
    }

  /* Moving a byte into an address register is not possible.  */
  /* Use d0 as an intermediate, but don't clobber its contents.  */
  if (ADDRESS_REG_P (operands[0]) && GET_CODE (operands[1]) == MEM)
    {
      /* ??? For 2.5, don't allow this choice and use secondary reloads
	 instead.

	 See if the address register is used in the address.  If it
	 is, we have to generate a more complex sequence than those below.  */
      if (refers_to_regno_p (REGNO (operands[0]), REGNO (operands[0]) + 1,
			     operands[1], NULL_RTX))
	{
	  /* See if the stack pointer is used in the address.  If it isn't,
	     we can push d0 or d1 (the insn can't use both of them) on
	     the stack, perform our move into d0/d1, copy the byte from d0/1,
	     and pop d0/1.  */
	  if (! reg_mentioned_p (stack_pointer_rtx, operands[1]))
	    {
	      if (refers_to_regno_p (0, 1, operands[1], NULL_RTX))
		return \"move%.l %/d0,%-\;move%.b %1,%/d0\;move%.l %/d0,%0\;move%.l %+,%/d0\";
	      else
		return \"move%.l %/d1,%-\;move%.b %1,%/d1\;move%.l %/d1,%0\;move%.l %+,%/d1\";
	    }
	  else
	    {
	      /* Otherwise, we know that d0 cannot be used in the address
		 (since sp and one address register is).  Assume that sp is
		 being used as a base register and replace the address
		 register that is our operand[0] with d0.  */
	      rtx reg_map[FIRST_PSEUDO_REGISTER];
	      int i;

	      for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
		reg_map[i] = 0;

	      reg_map[REGNO (operands[0])] = gen_rtx (REG, Pmode, 0);
	      operands[1] = copy_rtx (operands[1]);
	      replace_regs (operands[1], reg_map, FIRST_PSEUDO_REGISTER, 0);
	      return \"exg %/d0,%0\;move%.b %1,%/d0\;exg %/d0,%0\";
	    }
	}

      /* If the address of operand 1 uses d0, choose d1 as intermediate.  */
      if (refers_to_regno_p (0, 1, operands[1], NULL_RTX))
	return \"exg %/d1,%0\;move%.b %1,%/d1\;exg %/d1,%0\";
      /* Otherwise d0 is usable.
	 (An effective address on the 68k can't use two d-regs.)  */
      else
	return \"exg %/d0,%0\;move%.b %1,%/d0\;exg %/d0,%0\";
    }
    
  /* Likewise for moving from an address reg.  */
  if (ADDRESS_REG_P (operands[1]) && GET_CODE (operands[0]) == MEM)
    {
      /* ??? For 2.5, don't allow this choice and use secondary reloads
	 instead.

	 See if the address register is used in the address.  If it
	 is, we have to generate a more complex sequence than those below.  */
      if (refers_to_regno_p (REGNO (operands[1]), REGNO (operands[1]) + 1,
			     operands[0], NULL_RTX))
	{
	  /* See if the stack pointer is used in the address.  If it isn't,
	     we can push d0 or d1 (the insn can't use both of them) on
	     the stack, copy the byte to d0/1, perform our move from d0/d1, 
	     and pop d0/1.  */
	  if (! reg_mentioned_p (stack_pointer_rtx, operands[0]))
	    {
	      if (refers_to_regno_p (0, 1, operands[0], NULL_RTX))
		return \"move%.l %/d0,%-\;move%.l %1,%/d0\;move%.b %/d0,%0\;move%.l %+,%/d0\";
	      else
		return \"move%.l %/d1,%-\;move%.l %1,%/d1\;move%.b %/d1,%0\;move%.l %+,%/d1\";
	    }
	  else
	    {
	      /* Otherwise, we know that d0 cannot be used in the address
		 (since sp and one address register is).  Assume that sp is
		 being used as a base register and replace the address
		 register that is our operand[1] with d0.  */
	      rtx reg_map[FIRST_PSEUDO_REGISTER];
	      int i;

	      for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
		reg_map[i] = 0;

	      reg_map[REGNO (operands[1])] = gen_rtx (REG, Pmode, 0);
	      operands[0] = copy_rtx (operands[0]);
	      replace_regs (operands[0], reg_map, FIRST_PSEUDO_REGISTER, 0);
	      return \"exg %/d0,%1\;move%.b %/d0,%0\;exg %/d0,%1\";
	    }
	}

      if (refers_to_regno_p (0, 1, operands[0], NULL_RTX))
        return \"exg %/d1,%1\;move%.b %/d1,%0\;exg %/d1,%1\";
      else
        return \"exg %/d0,%1\;move%.b %/d0,%0\;exg %/d0,%1\";
    }

  /* clr and st insns on 68000 read before writing.
     This isn't so on the 68010, but we have no alternative for it.  */
  if (TARGET_68020
      || !(GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0])))
    {
      if (operands[1] == const0_rtx)
	return \"clr%.b %0\";
      if (GET_CODE (operands[1]) == CONST_INT
	  && INTVAL (operands[1]) == -1)
	{
	  CC_STATUS_INIT;
	  return \"st %0\";
	}
    }
  if (GET_CODE (operands[1]) != CONST_INT && CONSTANT_P (operands[1]))
    return \"move%.l %1,%0\";
  if (ADDRESS_REG_P (operands[0]) || ADDRESS_REG_P (operands[1]))
    return \"move%.w %1,%0\";
  return \"move%.b %1,%0\";
}")

(define_insn "movstrictqi"
  [(set (strict_low_part (match_operand:QI 0 "general_operand" "+dm"))
	(match_operand:QI 1 "general_operand" "dmn"))]
  ""
  "*
{
  if (operands[1] == const0_rtx
      /* clr insns on 68000 read before writing.
         This isn't so on the 68010, but we have no alternative for it.  */
      && (TARGET_68020
          || !(GET_CODE (operands[0]) == MEM && MEM_VOLATILE_P (operands[0]))))
    return \"clr%.b %0\";
  return \"move%.b %1,%0\";
}")

(define_insn "movsf"
  [(set (match_operand:SF 0 "general_operand" "=rmf,x,y,rm,!x,!rm")
	(match_operand:SF 1 "general_operand" "rmfF,xH,rmF,y,rm,x"))]
;  [(set (match_operand:SF 0 "general_operand" "=rmf")
;	(match_operand:SF 1 "general_operand" "rmfF"))]
  ""
  "*
{
  if (which_alternative >= 4)
    return \"fpmove%.s %1,fpa0\;fpmove%.s fpa0,%0\";
  if (FPA_REG_P (operands[0]))
    {
      if (FPA_REG_P (operands[1]))
	return \"fpmove%.s %x1,%x0\";
      else if (GET_CODE (operands[1]) == CONST_DOUBLE)
	return output_move_const_single (operands);
      else if (FP_REG_P (operands[1]))
        return \"fmove%.s %1,sp@-\;fpmove%.d sp@+, %0\";
      return \"fpmove%.s %x1,%x0\";
    }
  if (FPA_REG_P (operands[1]))
    {
      if (FP_REG_P (operands[0]))
	return \"fpmove%.s %x1,sp@-\;fmove%.s sp@+,%0\";
      else
	return \"fpmove%.s %x1,%x0\";
    }
  if (FP_REG_P (operands[0]))
    {
      if (FP_REG_P (operands[1]))
	return \"f%$move%.x %1,%0\";
      else if (ADDRESS_REG_P (operands[1]))
	return \"move%.l %1,%-\;f%$move%.s %+,%0\";
      else if (GET_CODE (operands[1]) == CONST_DOUBLE)
	return output_move_const_single (operands);
      return \"f%$move%.s %f1,%0\";
    }
  if (FP_REG_P (operands[1]))
    {
      if (ADDRESS_REG_P (operands[0]))
	return \"fmove%.s %1,%-\;move%.l %+,%0\";
      return \"fmove%.s %f1,%0\";
    }
  return \"move%.l %1,%0\";
}")

(define_insn "movdf"
  [(set (match_operand:DF 0 "general_operand" "=rm,rf,rf,&rof<>,y,rm,x,!x,!rm")
	(match_operand:DF 1 "general_operand" "rf,m,0,rofE<>,rmE,y,xH,rm,x"))]
;  [(set (match_operand:DF 0 "general_operand" "=rm,&rf,&rof<>")
;	(match_operand:DF 1 "general_operand" "rf,m,rofF<>"))]
  ""
  "*
{
  if (which_alternative == 7)
    return \"fpmove%.d %x1,fpa0\;fpmove%.d fpa0,%x0\";
  if (FPA_REG_P (operands[0]))
    {
      if (GET_CODE (operands[1]) == CONST_DOUBLE)
	return output_move_const_double (operands);
      if (FP_REG_P (operands[1]))
        return \"fmove%.d %1,sp@-\;fpmove%.d sp@+,%x0\";
      return \"fpmove%.d %x1,%x0\";
    }
  else if (FPA_REG_P (operands[1]))
    {
      if (FP_REG_P(operands[0]))
        return \"fpmove%.d %x1,sp@-\;fmoved sp@+,%0\";
      else
        return \"fpmove%.d %x1,%x0\";
    }
  if (FP_REG_P (operands[0]))
    {
      if (FP_REG_P (operands[1]))
	return \"f%&move%.x %1,%0\";
      if (REG_P (operands[1]))
	{
	  rtx xoperands[2];
	  xoperands[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
	  output_asm_insn (\"move%.l %1,%-\", xoperands);
	  output_asm_insn (\"move%.l %1,%-\", operands);
	  return \"f%&move%.d %+,%0\";
	}
      if (GET_CODE (operands[1]) == CONST_DOUBLE)
	return output_move_const_double (operands);
      return \"f%&move%.d %f1,%0\";
    }
  else if (FP_REG_P (operands[1]))
    {
      if (REG_P (operands[0]))
	{
	  output_asm_insn (\"fmove%.d %f1,%-\;move%.l %+,%0\", operands);
	  operands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
	  return \"move%.l %+,%0\";
	}
      else
        return \"fmove%.d %f1,%0\";
    }
  return output_move_double (operands);
}
")

(define_expand "movxf"
  [(set (match_operand:XF 0 "nonimmediate_operand" "")
	(match_operand:XF 1 "general_operand" ""))]
  ""
  "
{
  if (CONSTANT_P (operands[1]))
    {
      operands[1] = force_const_mem (XFmode, operands[1]);
      if (! memory_address_p (XFmode, XEXP (operands[1], 0))
	  && ! reload_in_progress)
	operands[1] = change_address (operands[1], XFmode,
				      XEXP (operands[1], 0));
    }
}")

(define_insn ""
  [(set (match_operand:XF 0 "nonimmediate_operand" "=f,m,f,!r,!f")
	(match_operand:XF 1 "nonimmediate_operand" "m,f,f,f,r"))]
  "TARGET_68881"
  "*
{
  if (FP_REG_P (operands[0]))
    {
      if (FP_REG_P (operands[1]))
	return \"fmove%.x %1,%0\";
      if (REG_P (operands[1]))
	{
	  rtx xoperands[2];
	  xoperands[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 2);
	  output_asm_insn (\"move%.l %1,%-\", xoperands);
	  xoperands[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
	  output_asm_insn (\"move%.l %1,%-\", xoperands);
	  output_asm_insn (\"move%.l %1,%-\", operands);
	  return \"fmove%.x %+,%0\";
	}
      if (GET_CODE (operands[1]) == CONST_DOUBLE)
        return \"fmove%.x %1,%0\";
      return \"fmove%.x %f1,%0\";
    }
  if (REG_P (operands[0]))
    {
      output_asm_insn (\"fmove%.x %f1,%-\;move%.l %+,%0\", operands);
      operands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
      output_asm_insn (\"move%.l %+,%0\", operands);
      operands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
      return \"move%.l %+,%0\";
    }
  return \"fmove%.x %f1,%0\";
}
")

(define_insn ""
  [(set (match_operand:XF 0 "nonimmediate_operand" "=rm,rf,&rof<>")
	(match_operand:XF 1 "nonimmediate_operand" "rf,m,rof<>"))]
  "! TARGET_68881"
  "*
{
  if (FP_REG_P (operands[0]))
    {
      if (FP_REG_P (operands[1]))
	return \"fmove%.x %1,%0\";
      if (REG_P (operands[1]))
	{
	  rtx xoperands[2];
	  xoperands[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 2);
	  output_asm_insn (\"move%.l %1,%-\", xoperands);
	  xoperands[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
	  output_asm_insn (\"move%.l %1,%-\", xoperands);
	  output_asm_insn (\"move%.l %1,%-\", operands);
	  return \"fmove%.x %+,%0\";
	}
      if (GET_CODE (operands[1]) == CONST_DOUBLE)
        return \"fmove%.x %1,%0\";
      return \"fmove%.x %f1,%0\";
    }
  if (FP_REG_P (operands[1]))
    {
      if (REG_P (operands[0]))
        {
          output_asm_insn (\"fmove%.x %f1,%-\;move%.l %+,%0\", operands);
          operands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
          output_asm_insn (\"move%.l %+,%0\", operands);
          operands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
          return \"move%.l %+,%0\";
        }
      else
        return \"fmove%.x %f1,%0\";
    }
  return output_move_double (operands);
}
")

;; movdi can apply to fp regs in some cases
(define_insn "movdi"
  ;; Let's see if it really still needs to handle fp regs, and, if so, why.
  [(set (match_operand:DI 0 "general_operand" "=rm,r,&ro<>,y,rm,!*x,!rm")
	(match_operand:DI 1 "general_operand" "rF,m,roi<>F,rmiF,y,rmF,*x"))]
;  [(set (match_operand:DI 0 "general_operand" "=rm,&r,&ro<>,!&rm,!&f,y,rm,x,!x,!rm")
;	(match_operand:DI 1 "general_operand" "r,m,roi<>,fF,rfmF,rmi,y,rm,x"))]
;  [(set (match_operand:DI 0 "general_operand" "=rm,&rf,&ro<>,!&rm,!&f")
;	(match_operand:DI 1 "general_operand" "r,m,roi<>,fF,rfF"))]
  ""
  "*
{
  if (which_alternative == 8)
    return \"fpmove%.d %x1,fpa0\;fpmove%.d fpa0,%x0\";
  if (FPA_REG_P (operands[0]) || FPA_REG_P (operands[1]))
    return \"fpmove%.d %x1,%x0\";
  if (FP_REG_P (operands[0]))
    {
      if (FP_REG_P (operands[1]))
	return \"fmove%.x %1,%0\";
      if (REG_P (operands[1]))
	{
	  rtx xoperands[2];
	  xoperands[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
	  output_asm_insn (\"move%.l %1,%-\", xoperands);
	  output_asm_insn (\"move%.l %1,%-\", operands);
	  return \"fmove%.d %+,%0\";
	}
      if (GET_CODE (operands[1]) == CONST_DOUBLE)
	return output_move_const_double (operands);
      return \"fmove%.d %f1,%0\";
    }
  else if (FP_REG_P (operands[1]))
    {
      if (REG_P (operands[0]))
	{
	  output_asm_insn (\"fmove%.d %f1,%-\;move%.l %+,%0\", operands);
	  operands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
	  return \"move%.l %+,%0\";
	}
      else
        return \"fmove%.d %f1,%0\";
    }
  return output_move_double (operands);
}
")

;; Thus goes after the move instructions
;; because the move instructions are better (require no spilling)
;; when they can apply.  It goes before the add/sub insns
;; so we will prefer it to them.

(define_insn "pushasi"
  [(set (match_operand:SI 0 "push_operand" "=m")
	(match_operand:SI 1 "address_operand" "p"))]
  ""
  "pea %a1")

;; truncation instructions
(define_insn "truncsiqi2"
  [(set (match_operand:QI 0 "general_operand" "=dm,d")
	(truncate:QI
	 (match_operand:SI 1 "general_operand" "doJ,i")))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == REG)
    {
      /* Must clear condition codes, since the move.l bases them on
	 the entire 32 bits, not just the desired 8 bits.  */
      CC_STATUS_INIT;
      return \"move%.l %1,%0\";
    }
  if (GET_CODE (operands[1]) == MEM)
    operands[1] = adj_offsettable_operand (operands[1], 3);
  return \"move%.b %1,%0\";
}")

(define_insn "trunchiqi2"
  [(set (match_operand:QI 0 "general_operand" "=dm,d")
	(truncate:QI
	 (match_operand:HI 1 "general_operand" "doJ,i")))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == REG
      && (GET_CODE (operands[1]) == MEM
	  || GET_CODE (operands[1]) == CONST_INT))
    {
      /* Must clear condition codes, since the move.w bases them on
	 the entire 16 bits, not just the desired 8 bits.  */
      CC_STATUS_INIT;
      return \"move%.w %1,%0\";
    }
  if (GET_CODE (operands[0]) == REG)
    {
      /* Must clear condition codes, since the move.l bases them on
	 the entire 32 bits, not just the desired 8 bits.  */
      CC_STATUS_INIT;
      return \"move%.l %1,%0\";
    }
  if (GET_CODE (operands[1]) == MEM)
    operands[1] = adj_offsettable_operand (operands[1], 1);
  return \"move%.b %1,%0\";
}")

(define_insn "truncsihi2"
  [(set (match_operand:HI 0 "general_operand" "=dm,d")
	(truncate:HI
	 (match_operand:SI 1 "general_operand" "roJ,i")))]
  ""
  "*
{
  if (GET_CODE (operands[0]) == REG)
    {
      /* Must clear condition codes, since the move.l bases them on
	 the entire 32 bits, not just the desired 8 bits.  */
      CC_STATUS_INIT;
      return \"move%.l %1,%0\";
    }
  if (GET_CODE (operands[1]) == MEM)
    operands[1] = adj_offsettable_operand (operands[1], 2);
  return \"move%.w %1,%0\";
}")

;; zero extension instructions

(define_expand "zero_extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "")
	(const_int 0))
   (set (strict_low_part (match_dup 2))
	(match_operand:HI 1 "general_operand" ""))]
  ""
  "
{
  operands[1] = make_safe_from (operands[1], operands[0]);
  if (GET_CODE (operands[0]) == SUBREG)
    operands[2] = gen_rtx (SUBREG, HImode, SUBREG_REG (operands[0]),
			   SUBREG_WORD (operands[0]));
  else
    operands[2] = gen_rtx (SUBREG, HImode, operands[0], 0);
}")

(define_expand "zero_extendqihi2"
  [(set (match_operand:HI 0 "register_operand" "")
	(const_int 0))
   (set (strict_low_part (match_dup 2))
	(match_operand:QI 1 "general_operand" ""))]
  ""
  "
{
  operands[1] = make_safe_from (operands[1], operands[0]);
  if (GET_CODE (operands[0]) == SUBREG)
    operands[2] = gen_rtx (SUBREG, QImode, SUBREG_REG (operands[0]),
			   SUBREG_WORD (operands[0]));
  else
    operands[2] = gen_rtx (SUBREG, QImode, operands[0], 0);
}")

(define_expand "zero_extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "")
	(const_int 0))
   (set (strict_low_part (match_dup 2))
	(match_operand:QI 1 "general_operand" ""))]
  ""
  "
{
  operands[1] = make_safe_from (operands[1], operands[0]);
  if (GET_CODE (operands[0]) == SUBREG)
    operands[2] = gen_rtx (SUBREG, QImode, SUBREG_REG (operands[0]),
			   SUBREG_WORD (operands[0]));
  else
    operands[2] = gen_rtx (SUBREG, QImode, operands[0], 0);
}")

;; Patterns to recognize zero-extend insns produced by the combiner.
;; We don't allow both operands in memory, because of aliasing problems.
;; Explicitly disallow two memory operands via the condition since reloading
;; of this case will result in worse code than the uncombined patterns.

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=do<>,d<")
	(zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "r,m")))]
  "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
  "*
{
  if (DATA_REG_P (operands[0]))
    {
      if (GET_CODE (operands[1]) == REG
	  && REGNO (operands[0]) == REGNO (operands[1]))
	return \"and%.l %#0xFFFF,%0\";
      if (reg_mentioned_p (operands[0], operands[1]))
        return \"move%.w %1,%0\;and%.l %#0xFFFF,%0\";
      return \"clr%.l %0\;move%.w %1,%0\";
    }
  else if (GET_CODE (operands[0]) == MEM
	   && GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
    return \"move%.w %1,%0\;clr%.w %0\";
  else if (GET_CODE (operands[0]) == MEM
	   && GET_CODE (XEXP (operands[0], 0)) == POST_INC)
    return \"clr%.w %0\;move%.w %1,%0\";
  else
    {
      output_asm_insn (\"clr%.w %0\", operands);
      operands[0] = adj_offsettable_operand (operands[0], 2);
      return \"move%.w %1,%0\";
    }
}")

(define_insn ""
  [(set (match_operand:HI 0 "general_operand" "=do<>,d")
	(zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "d,m")))]
  "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
  "*
{
  if (DATA_REG_P (operands[0]))
    {
      if (GET_CODE (operands[1]) == REG
	  && REGNO (operands[0]) == REGNO (operands[1]))
	return \"and%.w %#0xFF,%0\";
      if (reg_mentioned_p (operands[0], operands[1]))
        return \"move%.b %1,%0\;and%.w %#0xFF,%0\";
      return \"clr%.w %0\;move%.b %1,%0\";
    }
  else if (GET_CODE (operands[0]) == MEM
	   && GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
    {
      if (REGNO (XEXP (XEXP (operands[0], 0), 0))
	  == STACK_POINTER_REGNUM)
	{
	  output_asm_insn (\"clr%.w %-\", operands);
	  operands[0] = gen_rtx (MEM, GET_MODE (operands[0]),
				 plus_constant (stack_pointer_rtx, 1));
	  return \"move%.b %1,%0\";
	}
      else
	return \"move%.b %1,%0\;clr%.b %0\";
    }
  else if (GET_CODE (operands[0]) == MEM
	   && GET_CODE (XEXP (operands[0], 0)) == POST_INC)
    return \"clr%.b %0\;move%.b %1,%0\";
  else
    {
      output_asm_insn (\"clr%.b %0\", operands);
      operands[0] = adj_offsettable_operand (operands[0], 1);
      return \"move%.b %1,%0\";
    }
}")

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=do<>,d")
	(zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "d,m")))]
  "GET_CODE (operands[0]) != MEM || GET_CODE (operands[1]) != MEM"
  "*
{
  if (DATA_REG_P (operands[0]))
    {
      if (GET_CODE (operands[1]) == REG
	  && REGNO (operands[0]) == REGNO (operands[1]))
	return \"and%.l %#0xFF,%0\";
      if (reg_mentioned_p (operands[0], operands[1]))
        return \"move%.b %1,%0\;and%.l %#0xFF,%0\";
      return \"clr%.l %0\;move%.b %1,%0\";
    }
  else if (GET_CODE (operands[0]) == MEM
	   && GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
    {
      operands[0] = XEXP (XEXP (operands[0], 0), 0);
#ifdef MOTOROLA
#ifdef SGS
      return \"clr%.l -(%0)\;move%.b %1,3(%0)\";
#else
      return \"clr%.l -(%0)\;move%.b %1,(3,%0)\";
#endif
#else
      return \"clrl %0@-\;moveb %1,%0@(3)\";
#endif
    }
  else if (GET_CODE (operands[0]) == MEM
	   && GET_CODE (XEXP (operands[0], 0)) == POST_INC)
    {
      operands[0] = XEXP (XEXP (operands[0], 0), 0);
#ifdef MOTOROLA
#ifdef SGS
      return \"clr%.l (%0)+\;move%.b %1,-1(%0)\";
#else
      return \"clr%.l (%0)+\;move%.b %1,(-1,%0)\";
#endif
#else
      return \"clrl %0@+\;moveb %1,%0@(-1)\";
#endif
    }
  else
    {
      output_asm_insn (\"clr%.l %0\", operands);
      operands[0] = adj_offsettable_operand (operands[0], 3);
      return \"move%.b %1,%0\";
    }
}")

;; sign extension instructions

(define_insn "extendhisi2"
  [(set (match_operand:SI 0 "general_operand" "=*d,a")
	(sign_extend:SI
	 (match_operand:HI 1 "nonimmediate_operand" "0,rm")))]
  ""
  "*
{
  if (ADDRESS_REG_P (operands[0]))
    return \"move%.w %1,%0\";
  return \"ext%.l %0\";
}")

(define_insn "extendqihi2"
  [(set (match_operand:HI 0 "general_operand" "=d")
	(sign_extend:HI (match_operand:QI 1 "nonimmediate_operand" "0")))]
  ""
  "ext%.w %0")

(define_insn "extendqisi2"
  [(set (match_operand:SI 0 "general_operand" "=d")
	(sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "0")))]
  "TARGET_68020"
  "extb%.l %0")

;; Conversions between float and double.

(define_expand "extendsfdf2"
  [(set (match_operand:DF 0 "general_operand" "")
	(float_extend:DF
	 (match_operand:SF 1 "general_operand" "")))]
  "TARGET_68881 || TARGET_FPA"
  "")

(define_insn ""
  [(set (match_operand:DF 0 "general_operand" "=x,y")
	(float_extend:DF
	 (match_operand:SF 1 "general_operand" "xH,rmF")))]
  "TARGET_FPA"
  "fpstod %w1,%0")

(define_insn ""
  [(set (match_operand:DF 0 "general_operand" "=*fdm,f")
	(float_extend:DF
	  (match_operand:SF 1 "general_operand" "f,dmF")))]
  "TARGET_68881"
  "*
{
  if (FP_REG_P (operands[0]) && FP_REG_P (operands[1]))
    {
      if (REGNO (operands[0]) == REGNO (operands[1]))
	{
	  /* Extending float to double in an fp-reg is a no-op.
	     NOTICE_UPDATE_CC has already assumed that the
	     cc will be set.  So cancel what it did.  */
	  cc_status = cc_prev_status;
	  return \"\";
	}
      return \"f%&move%.x %1,%0\";
    }
  if (FP_REG_P (operands[0]))
    return \"f%&move%.s %f1,%0\";
  if (DATA_REG_P (operands[0]) && FP_REG_P (operands[1]))
    {
      output_asm_insn (\"fmove%.d %f1,%-\;move%.l %+,%0\", operands);
      operands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
      return \"move%.l %+,%0\";
    }
  return \"fmove%.d %f1,%0\";
}")

;; This cannot output into an f-reg because there is no way to be
;; sure of truncating in that case.
;; But on the Sun FPA, we can be sure.
(define_expand "truncdfsf2"
  [(set (match_operand:SF 0 "general_operand" "")
	(float_truncate:SF
	  (match_operand:DF 1 "general_operand" "")))]
  "TARGET_68881 || TARGET_FPA"
  "")

(define_insn ""
  [(set (match_operand:SF 0 "general_operand" "=x,y")
	(float_truncate:SF
	  (match_operand:DF 1 "general_operand" "xH,rmF")))]
  "TARGET_FPA"
  "fpdtos %y1,%0")

;; On the '040 we can truncate in a register accurately and easily.
(define_insn ""
  [(set (match_operand:SF 0 "general_operand" "=f")
	(float_truncate:SF
	  (match_operand:DF 1 "general_operand" "fmG")))]
  "TARGET_68040_ONLY"
  "*
{
  if (FP_REG_P (operands[1]))
    return \"f%$move%.x %1,%0\";
  return \"f%$move%.d %f1,%0\";
}")

(define_insn ""
  [(set (match_operand:SF 0 "general_operand" "=dm")
	(float_truncate:SF
	  (match_operand:DF 1 "general_operand" "f")))]
  "TARGET_68881"
  "fmove%.s %f1,%0")

;; Conversion between fixed point and floating point.
;; Note that among the fix-to-float insns
;; the ones that start with SImode come first.
;; That is so that an operand that is a CONST_INT
;; (and therefore lacks a specific machine mode).
;; will be recognized as SImode (which is always valid)
;; rather than as QImode or HImode.

(define_expand "floatsisf2"
  [(set (match_operand:SF 0 "general_operand" "")
	(float:SF (match_operand:SI 1 "general_operand" "")))]
  "TARGET_68881 || TARGET_FPA"
  "")

(define_insn ""
  [(set (match_operand:SF 0 "general_operand" "=y,x")
	(float:SF (match_operand:SI 1 "general_operand" "rmi,x")))]
  "TARGET_FPA"
  "fpltos %1,%0")

(define_insn ""
  [(set (match_operand:SF 0 "general_operand" "=f")
	(float:SF (match_operand:SI 1 "general_operand" "dmi")))]
  "TARGET_68881"
  "f%$move%.l %1,%0")

(define_expand "floatsidf2"
  [(set (match_operand:DF 0 "general_operand" "")
	(float:DF (match_operand:SI 1 "general_operand" "")))]
  "TARGET_68881 || TARGET_FPA"
  "")

(define_insn ""
  [(set (match_operand:DF 0 "general_operand" "=y,x")
	(float:DF (match_operand:SI 1 "general_operand" "rmi,x")))]
  "TARGET_FPA"
  "fpltod %1,%0")

(define_insn ""
  [(set (match_operand:DF 0 "general_operand" "=f")
	(float:DF (match_operand:SI 1 "general_operand" "dmi")))]
  "TARGET_68881"
  "f%&move%.l %1,%0")

(define_insn "floathisf2"
  [(set (match_operand:SF 0 "general_operand" "=f")
	(float:SF (match_operand:HI 1 "general_operand" "dmn")))]
  "TARGET_68881"
  "f%$move%.w %1,%0")

(define_insn "floathidf2"
  [(set (match_operand:DF 0 "general_operand" "=f")
	(float:DF (match_operand:HI 1 "general_operand" "dmn")))]
  "TARGET_68881"
  "fmove%.w %1,%0")

(define_insn "floatqisf2"
  [(set (match_operand:SF 0 "general_operand" "=f")
	(float:SF (match_operand:QI 1 "general_operand" "dmn")))]
  "TARGET_68881"
  "fmove%.b %1,%0")

(define_insn "floatqidf2"
  [(set (match_operand:DF 0 "general_operand" "=f")
	(float:DF (match_operand:QI 1 "general_operand" "dmn")))]
  "TARGET_68881"
  "f%&move%.b %1,%0")

;; New routines to convert floating-point values to integers
;; to be used on the '040.  These should be faster than trapping
;; into the kernel to emulate fintrz.  They should also be faster
;; than calling the subroutines fixsfsi or fixdfsi.

(define_insn "fix_truncdfsi2"
  [(set (match_operand:SI 0 "general_operand" "=dm")
	(fix:SI (fix:DF (match_operand:DF 1 "register_operand" "f"))))
   (clobber (match_scratch:SI 2 "=d"))
   (clobber (match_scratch:SI 3 "=d"))]
  "TARGET_68881 && TARGET_68040"
  "*
{
  CC_STATUS_INIT;
  return \"fmovem%.l %!,%2\;moveq %#16,%3\;or%.l %2,%3\;and%.w %#-33,%3\;fmovem%.l %3,%!\;fmove%.l %1,%0\;fmovem%.l %2,%!\";
}")

(define_insn "fix_truncdfhi2"
  [(set (match_operand:HI 0 "general_operand" "=dm")
	(fix:HI (fix:DF (match_operand:DF 1 "register_operand" "f"))))
   (clobber (match_scratch:SI 2 "=d"))
   (clobber (match_scratch:SI 3 "=d"))]
  "TARGET_68881 && TARGET_68040"
  "*
{
  CC_STATUS_INIT;
  return \"fmovem%.l %!,%2\;moveq %#16,%3\;or%.l %2,%3\;and%.w %#-33,%3\;fmovem%.l %3,%!\;fmove%.w %1,%0\;fmovem%.l %2,%!\";
}")

(define_insn "fix_truncdfqi2"
  [(set (match_operand:QI 0 "general_operand" "=dm")
	(fix:QI (fix:DF (match_operand:DF 1 "register_operand" "f"))))
   (clobber (match_scratch:SI 2 "=d"))
   (clobber (match_scratch:SI 3 "=d"))]
  "TARGET_68881 && TARGET_68040"
  "*
{
  CC_STATUS_INIT;
  return \"fmovem%.l %!,%2\;moveq %#16,%3\;or%.l %2,%3\;and%.w %#-33,%3\;fmovem%.l %3,%!\;fmove%.b %1,%0\;fmovem%.l %2,%!\";
}")

;; Convert a float to a float whose value is an integer.
;; This is the first stage of converting it to an integer type.

(define_insn "ftruncdf2"
  [(set (match_operand:DF 0 "general_operand" "=f")
	(fix:DF (match_operand:DF 1 "general_operand" "fFm")))]
  "TARGET_68881 && !TARGET_68040"
  "*
{
  if (FP_REG_P (operands[1]))
    return \"fintrz%.x %f1,%0\";
  return \"fintrz%.d %f1,%0\";
}")

(define_insn "ftruncsf2"
  [(set (match_operand:SF 0 "general_operand" "=f")
	(fix:SF (match_operand:SF 1 "general_operand" "dfFm")))]
  "TARGET_68881 && !TARGET_68040"
  "*
{
  if (FP_REG_P (operands[1]))
    return \"fintrz%.x %f1,%0\";
  return \"fintrz%.s %f1,%0\";
}")

;; Convert a float whose value is an integer
;; to an actual integer.  Second stage of converting float to integer type.
(define_insn "fixsfqi2"
  [(set (match_operand:QI 0 "general_operand" "=dm")
	(fix:QI (match_operand:SF 1 "general_operand" "f")))]
  "TARGET_68881"
  "fmove%.b %1,%0")

(define_insn "fixsfhi2"
  [(set (match_operand:HI 0 "general_operand" "=dm")
	(fix:HI (match_operand:SF 1 "general_operand" "f")))]
  "TARGET_68881"
  "fmove%.w %1,%0")

(define_insn "fixsfsi2"
  [(set (match_operand:SI 0 "general_operand" "=dm")
	(fix:SI (match_operand:SF 1 "general_operand" "f")))]
  "TARGET_68881"
  "fmove%.l %1,%0")

(define_insn "fixdfqi2"
  [(set (match_operand:QI 0 "general_operand" "=dm")
	(fix:QI (match_operand:DF 1 "general_operand" "f")))]
  "TARGET_68881"
  "fmove%.b %1,%0")

(define_insn "fixdfhi2"
  [(set (match_operand:HI 0 "general_operand" "=dm")
	(fix:HI (match_operand:DF 1 "general_operand" "f")))]
  "TARGET_68881"
  "fmove%.w %1,%0")

(define_insn "fixdfsi2"
  [(set (match_operand:SI 0 "general_operand" "=dm")
	(fix:SI (match_operand:DF 1 "general_operand" "f")))]
  "TARGET_68881"
  "fmove%.l %1,%0")

;; Convert a float to an integer.
;; On the Sun FPA, this is done in one step.

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=x,y")
	(fix:SI (fix:SF (match_operand:SF 1 "general_operand" "xH,rmF"))))]
  "TARGET_FPA"
  "fpstol %w1,%0")

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=x,y")
	(fix:SI (fix:DF (match_operand:DF 1 "general_operand" "xH,rmF"))))]
  "TARGET_FPA"
  "fpdtol %y1,%0")

;; add instructions

;; Note that the middle two alternatives are near-duplicates
;; in order to handle insns generated by reload.
;; This is needed since they are not themselves reloaded,
;; so commutativity won't apply to them.
(define_insn "addsi3"
  [(set (match_operand:SI 0 "general_operand" "=m,?a,?a,r")
	(plus:SI (match_operand:SI 1 "general_operand" "%0,a,rJK,0")
		 (match_operand:SI 2 "general_operand" "dIKLs,rJK,a,mrIKLs")))]
  ""
  "*
{
  if (! operands_match_p (operands[0], operands[1]))
    {
      if (!ADDRESS_REG_P (operands[1]))
	{
	  rtx tmp = operands[1];

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

      /* These insns can result from reloads to access
	 stack slots over 64k from the frame pointer.  */
      if (GET_CODE (operands[2]) == CONST_INT
	  && INTVAL (operands[2]) + 0x8000 >= (unsigned) 0x10000)
        return \"move%.l %2,%0\;add%.l %1,%0\";
#ifdef SGS
      if (GET_CODE (operands[2]) == REG)
	return \"lea 0(%1,%2.l),%0\";
      else
	return \"lea %c2(%1),%0\";
#else /* not SGS */
#ifdef MOTOROLA
      if (GET_CODE (operands[2]) == REG)
	return \"lea (%1,%2.l),%0\";
      else
	return \"lea (%c2,%1),%0\";
#else /* not MOTOROLA (MIT syntax) */
      if (GET_CODE (operands[2]) == REG)
	return \"lea %1@(0,%2:l),%0\";
      else
	return \"lea %1@(%c2),%0\";
#endif /* not MOTOROLA */
#endif /* not SGS */
    }
  if (GET_CODE (operands[2]) == CONST_INT)
    {
#ifndef NO_ADDSUB_Q
      if (INTVAL (operands[2]) > 0
	  && INTVAL (operands[2]) <= 8)
	return (ADDRESS_REG_P (operands[0])
		? \"addq%.w %2,%0\"
		: \"addq%.l %2,%0\");
      if (INTVAL (operands[2]) < 0
	  && INTVAL (operands[2]) >= -8)
        {
	  operands[2] = gen_rtx (CONST_INT, VOIDmode,
			         - INTVAL (operands[2]));
	  return (ADDRESS_REG_P (operands[0])
		  ? \"subq%.w %2,%0\"
		  : \"subq%.l %2,%0\");
	}
      /* On everything except the 68000 it is faster to use two
	 addqw instructions to add a small integer (8 < N <= 16)
	 to an address register.  Likewise for subqw.*/
      if (INTVAL (operands[2]) > 8
	  && INTVAL (operands[2]) <= 16
	  && ADDRESS_REG_P (operands[0])
	  && TARGET_68020) 
	{
	  operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) - 8);
	  return \"addq%.w %#8,%0\;addq%.w %2,%0\";
	}
      if (INTVAL (operands[2]) < -8
	  && INTVAL (operands[2]) >= -16
	  && ADDRESS_REG_P (operands[0])
	  && TARGET_68020) 
	{
	  operands[2] = gen_rtx (CONST_INT, VOIDmode, 
				  - INTVAL (operands[2]) - 8);
	  return \"subq%.w %#8,%0\;subq%.w %2,%0\";
	}
#endif
      if (ADDRESS_REG_P (operands[0])
	  && INTVAL (operands[2]) >= -0x8000
	  && INTVAL (operands[2]) < 0x8000)
	return \"add%.w %2,%0\";
    }
  return \"add%.l %2,%0\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=a")
	(plus:SI (match_operand:SI 1 "general_operand" "0")
		 (sign_extend:SI
		  (match_operand:HI 2 "nonimmediate_operand" "rm"))))]
  ""
  "add%.w %2,%0")

(define_insn "addhi3"
  [(set (match_operand:HI 0 "general_operand" "=m,r")
	(plus:HI (match_operand:HI 1 "general_operand" "%0,0")
		 (match_operand:HI 2 "general_operand" "dn,rmn")))]
  ""
  "*
{
#ifndef NO_ADDSUB_Q
  if (GET_CODE (operands[2]) == CONST_INT)
    {
      /* If the constant would be a negative number when interpreted as
	 HImode, make it negative.  This is usually, but not always, done
	 elsewhere in the compiler.  First check for constants out of range,
	 which could confuse us.  */

      if (INTVAL (operands[2]) >= 32768)
	operands[2] = gen_rtx (CONST_INT, VOIDmode,
			       INTVAL (operands[2]) - 65536);

      if (INTVAL (operands[2]) > 0
	  && INTVAL (operands[2]) <= 8)
	return \"addq%.w %2,%0\";
      if (INTVAL (operands[2]) < 0
	  && INTVAL (operands[2]) >= -8)
	{
	  operands[2] = gen_rtx (CONST_INT, VOIDmode,
			         - INTVAL (operands[2]));
	  return \"subq%.w %2,%0\";
	}
      /* On everything except the 68000 it is faster to use two
	 addqw instructions to add a small integer (8 < N <= 16)
	 to an address register.  Likewise for subqw. */
      if (INTVAL (operands[2]) > 8
	  && INTVAL (operands[2]) <= 16
	  && ADDRESS_REG_P (operands[0])
	  && TARGET_68020) 
	{
	  operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) - 8);
	  return \"addq%.w %#8,%0\;addq%.w %2,%0\";
	}
      if (INTVAL (operands[2]) < -8
	  && INTVAL (operands[2]) >= -16
	  && ADDRESS_REG_P (operands[0])
	  && TARGET_68020) 
	{
	  operands[2] = gen_rtx (CONST_INT, VOIDmode, 
				 - INTVAL (operands[2]) - 8);
	  return \"subq%.w %#8,%0\;subq%.w %2,%0\";
	}
    }
#endif
  return \"add%.w %2,%0\";
}")

;; These insns must use MATCH_DUP instead of the more expected
;; use of a matching constraint because the "output" here is also
;; an input, so you can't use the matching constraint.  That also means
;; that you can't use the "%", so you need patterns with the matched
;; operand in both positions.

(define_insn ""
  [(set (strict_low_part (match_operand:HI 0 "general_operand" "+m,d"))
	(plus:HI (match_dup 0)
		 (match_operand:HI 1 "general_operand" "dn,rmn")))]
  ""
  "*
{
#ifndef NO_ADDSUB_Q
  if (GET_CODE (operands[1]) == CONST_INT)
    {
      /* If the constant would be a negative number when interpreted as
	 HImode, make it negative.  This is usually, but not always, done
	 elsewhere in the compiler.  First check for constants out of range,
	 which could confuse us.  */

      if (INTVAL (operands[1]) >= 32768)
	operands[1] = gen_rtx (CONST_INT, VOIDmode,
			       INTVAL (operands[1]) - 65536);

      if (INTVAL (operands[1]) > 0
	  && INTVAL (operands[1]) <= 8)
	return \"addq%.w %1,%0\";
      if (INTVAL (operands[1]) < 0
	  && INTVAL (operands[1]) >= -8)
	{
	  operands[1] = gen_rtx (CONST_INT, VOIDmode,
			         - INTVAL (operands[1]));
	  return \"subq%.w %1,%0\";
	}
      /* On everything except the 68000 it is faster to use two
	 addqw instructions to add a small integer (8 < N <= 16)
	 to an address register.  Likewise for subqw. */
      if (INTVAL (operands[1]) > 8
	  && INTVAL (operands[1]) <= 16
	  && ADDRESS_REG_P (operands[0])
	  && TARGET_68020) 
	{
	  operands[1] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[1]) - 8);
	  return \"addq%.w %#8,%0\;addq%.w %1,%0\";
	}
      if (INTVAL (operands[1]) < -8
	  && INTVAL (operands[1]) >= -16
	  && ADDRESS_REG_P (operands[0])
	  && TARGET_68020) 
	{
	  operands[1] = gen_rtx (CONST_INT, VOIDmode, 
				 - INTVAL (operands[1]) - 8);
	  return \"subq%.w %#8,%0\;subq%.w %1,%0\";
	}
    }
#endif
  return \"add%.w %1,%0\";
}")

(define_insn ""
  [(set (strict_low_part (match_operand:HI 0 "general_operand" "+m,d"))
	(plus:HI (match_operand:HI 1 "general_operand" "dn,rmn")
		 (match_dup 0)))]
  ""
  "*
{
#ifndef NO_ADDSUB_Q
  if (GET_CODE (operands[1]) == CONST_INT)
    {
      /* If the constant would be a negative number when interpreted as
	 HImode, make it negative.  This is usually, but not always, done
	 elsewhere in the compiler.  First check for constants out of range,
	 which could confuse us.  */

      if (INTVAL (operands[1]) >= 32768)
	operands[1] = gen_rtx (CONST_INT, VOIDmode,
			       INTVAL (operands[1]) - 65536);

      if (INTVAL (operands[1]) > 0
	  && INTVAL (operands[1]) <= 8)
	return \"addq%.w %1,%0\";
      if (INTVAL (operands[1]) < 0
	  && INTVAL (operands[1]) >= -8)
	{
	  operands[1] = gen_rtx (CONST_INT, VOIDmode,
			         - INTVAL (operands[1]));
	  return \"subq%.w %1,%0\";
	}
      /* On everything except the 68000 it is faster to use two
	 addqw instructions to add a small integer (8 < N <= 16)
	 to an address register.  Likewise for subqw. */
      if (INTVAL (operands[1]) > 8
	  && INTVAL (operands[1]) <= 16
	  && ADDRESS_REG_P (operands[0])
	  && TARGET_68020) 
	{
	  operands[1] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[1]) - 8);
	  return \"addq%.w %#8,%0\;addq%.w %1,%0\";
	}
      if (INTVAL (operands[1]) < -8
	  && INTVAL (operands[1]) >= -16
	  && ADDRESS_REG_P (operands[0])
	  && TARGET_68020) 
	{
	  operands[1] = gen_rtx (CONST_INT, VOIDmode, 
				 - INTVAL (operands[1]) - 8);
	  return \"subq%.w %#8,%0\;subq%.w %1,%0\";
	}
    }
#endif
  return \"add%.w %1,%0\";
}")

(define_insn "addqi3"
  [(set (match_operand:QI 0 "general_operand" "=m,d")
	(plus:QI (match_operand:QI 1 "general_operand" "%0,0")
		 (match_operand:QI 2 "general_operand" "dn,dmn")))]
  ""
  "*
{
#ifndef NO_ADDSUB_Q
  if (GET_CODE (operands[2]) == CONST_INT)
    {
      if (INTVAL (operands[2]) >= 128)
	operands[2] = gen_rtx (CONST_INT, VOIDmode,
			       INTVAL (operands[2]) - 256);

      if (INTVAL (operands[2]) > 0
	  && INTVAL (operands[2]) <= 8)
	return \"addq%.b %2,%0\";
      if (INTVAL (operands[2]) < 0 && INTVAL (operands[2]) >= -8)
       {
	 operands[2] = gen_rtx (CONST_INT, VOIDmode, - INTVAL (operands[2]));
	 return \"subq%.b %2,%0\";
       }
    }
#endif
  return \"add%.b %2,%0\";
}")

(define_insn ""
  [(set (strict_low_part (match_operand:QI 0 "general_operand" "+m,d"))
	(plus:QI (match_dup 0)
		 (match_operand:QI 1 "general_operand" "dn,dmn")))]
  ""
  "*
{
#ifndef NO_ADDSUB_Q
  if (GET_CODE (operands[1]) == CONST_INT)
    {
      if (INTVAL (operands[1]) >= 128)
	operands[1] = gen_rtx (CONST_INT, VOIDmode,
			       INTVAL (operands[1]) - 256);

      if (INTVAL (operands[1]) > 0
	  && INTVAL (operands[1]) <= 8)
	return \"addq%.b %1,%0\";
      if (INTVAL (operands[1]) < 0 && INTVAL (operands[1]) >= -8)
       {
	 operands[1] = gen_rtx (CONST_INT, VOIDmode, - INTVAL (operands[1]));
	 return \"subq%.b %1,%0\";
       }
    }
#endif
  return \"add%.b %1,%0\";
}")

(define_insn ""
  [(set (strict_low_part (match_operand:QI 0 "general_operand" "+m,d"))
	(plus:QI (match_operand:QI 1 "general_operand" "dn,dmn")
		 (match_dup 0)))]
  ""
  "*
{
#ifndef NO_ADDSUB_Q
  if (GET_CODE (operands[1]) == CONST_INT)
    {
      if (INTVAL (operands[1]) >= 128)
	operands[1] = gen_rtx (CONST_INT, VOIDmode,
			       INTVAL (operands[1]) - 256);

      if (INTVAL (operands[1]) > 0
	  && INTVAL (operands[1]) <= 8)
	return \"addq%.b %1,%0\";
      if (INTVAL (operands[1]) < 0 && INTVAL (operands[1]) >= -8)
       {
	 operands[1] = gen_rtx (CONST_INT, VOIDmode, - INTVAL (operands[1]));
	 return \"subq%.b %1,%0\";
       }
    }
#endif
  return \"add%.b %1,%0\";
}")

(define_expand "adddf3"
  [(set (match_operand:DF 0 "general_operand" "")
	(plus:DF (match_operand:DF 1 "general_operand" "")
		 (match_operand:DF 2 "general_operand" "")))]
  "TARGET_68881 || TARGET_FPA"
  "")

(define_insn ""
  [(set (match_operand:DF 0 "general_operand" "=x,y")
	(plus:DF (match_operand:DF 1 "general_operand" "%xH,y")
		 (match_operand:DF 2 "general_operand" "xH,dmF")))]
  "TARGET_FPA"
  "*
{
  if (rtx_equal_p (operands[0], operands[1]))
    return \"fpadd%.d %y2,%0\";
  if (rtx_equal_p (operands[0], operands[2]))
    return \"fpadd%.d %y1,%0\";
  if (which_alternative == 0)
    return \"fpadd3%.d %w2,%w1,%0\";
  return \"fpadd3%.d %x2,%x1,%0\";
}")

(define_insn ""
  [(set (match_operand:DF 0 "general_operand" "=f")
	(plus:DF (match_operand:DF 1 "general_operand" "%0")
		 (match_operand:DF 2 "general_operand" "fmG")))]
  "TARGET_68881"
  "*
{
  if (REG_P (operands[2]))
    return \"f%&add%.x %2,%0\";
  return \"f%&add%.d %f2,%0\";
}")

(define_expand "addsf3"
  [(set (match_operand:SF 0 "general_operand" "")
	(plus:SF (match_operand:SF 1 "general_operand" "")
		 (match_operand:SF 2 "general_operand" "")))]
  "TARGET_68881 || TARGET_FPA"
  "")

(define_insn ""
  [(set (match_operand:SF 0 "general_operand" "=x,y")
	(plus:SF (match_operand:SF 1 "general_operand" "%xH,y")
		 (match_operand:SF 2 "general_operand" "xH,rmF")))]
  "TARGET_FPA"
  "*
{
  if (rtx_equal_p (operands[0], operands[1]))
    return \"fpadd%.s %w2,%0\";
  if (rtx_equal_p (operands[0], operands[2]))
    return \"fpadd%.s %w1,%0\";
  if (which_alternative == 0)
    return \"fpadd3%.s %w2,%w1,%0\";
  return \"fpadd3%.s %2,%1,%0\";
}")

(define_insn ""
  [(set (match_operand:SF 0 "general_operand" "=f")
	(plus:SF (match_operand:SF 1 "general_operand" "%0")
		 (match_operand:SF 2 "general_operand" "fdmF")))]
  "TARGET_68881"
  "*
{
  if (REG_P (operands[2]) && ! DATA_REG_P (operands[2]))
    return \"f%$add%.x %2,%0\";
  return \"f%$add%.s %f2,%0\";
}")

;; subtract instructions

(define_insn "subsi3"
  [(set (match_operand:SI 0 "general_operand" "=m,r,!a,?d")
	(minus:SI (match_operand:SI 1 "general_operand" "0,0,a,mrIKs")
		  (match_operand:SI 2 "general_operand" "dIKs,mrIKs,J,0")))]
  ""
  "*
{
  if (! operands_match_p (operands[0], operands[1]))
    {
      if (operands_match_p (operands[0], operands[2]))
	{
#ifndef NO_ADDSUB_Q
	  if (GET_CODE (operands[1]) == CONST_INT)
	    {
	      if (INTVAL (operands[1]) > 0
		  && INTVAL (operands[1]) <= 8)
		return \"subq%.l %1,%0\;neg%.l %0\";
	    }
#endif
	  return \"sub%.l %1,%0\;neg%.l %0\";
	}
      /* This case is matched by J, but negating -0x8000
         in an lea would give an invalid displacement.
	 So do this specially.  */
      if (INTVAL (operands[2]) == -0x8000)
	return \"move%.l %1,%0\;sub%.l %2,%0\";
#ifdef SGS
      return \"lea %n2(%1),%0\";
#else
#ifdef MOTOROLA
      return \"lea (%n2,%1),%0\";
#else /* not MOTOROLA (MIT syntax) */
      return \"lea %1@(%n2),%0\";
#endif /* not MOTOROLA */
#endif /* not SGS */
    }
  if (GET_CODE (operands[2]) == CONST_INT)
    {
#ifndef NO_ADDSUB_Q
      if (INTVAL (operands[2]) > 0
	  && INTVAL (operands[2]) <= 8)
	return \"subq%.l %2,%0\";
      /* Using two subqw for 8 < N <= 16 being subtracted from an
	 address register is faster on all but 68000 */
      if (INTVAL (operands[2]) > 8
	  && INTVAL (operands[2]) <= 16
	  && ADDRESS_REG_P (operands[0])
	  && TARGET_68020)
	{
	  operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) - 8);
	  return \"subq%.w %#8,%0\;subq%.w %2,%0\";
	}
#endif
      if (ADDRESS_REG_P (operands[0])
	  && INTVAL (operands[2]) >= -0x8000
	  && INTVAL (operands[2]) < 0x8000)
	return \"sub%.w %2,%0\";
    }
  return \"sub%.l %2,%0\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=a")
	(minus:SI (match_operand:SI 1 "general_operand" "0")
		  (sign_extend:SI
		   (match_operand:HI 2 "nonimmediate_operand" "rm"))))]
  ""
  "sub%.w %2,%0")

(define_insn "subhi3"
  [(set (match_operand:HI 0 "general_operand" "=m,r")
	(minus:HI (match_operand:HI 1 "general_operand" "0,0")
		  (match_operand:HI 2 "general_operand" "dn,rmn")))]
  ""
  "sub%.w %2,%0")

(define_insn ""
  [(set (strict_low_part (match_operand:HI 0 "general_operand" "+m,d"))
	(minus:HI (match_dup 0)
		  (match_operand:HI 1 "general_operand" "dn,rmn")))]
  ""
  "sub%.w %1,%0")

(define_insn "subqi3"
  [(set (match_operand:QI 0 "general_operand" "=m,d")
	(minus:QI (match_operand:QI 1 "general_operand" "0,0")
		  (match_operand:QI 2 "general_operand" "dn,dmn")))]
  ""
  "sub%.b %2,%0")

(define_insn ""
  [(set (strict_low_part (match_operand:QI 0 "general_operand" "+m,d"))
	(minus:QI (match_dup 0)
		  (match_operand:QI 1 "general_operand" "dn,dmn")))]
  ""
  "sub%.b %1,%0")

(define_expand "subdf3"
  [(set (match_operand:DF 0 "general_operand" "")
	(minus:DF (match_operand:DF 1 "general_operand" "")
		  (match_operand:DF 2 "general_operand" "")))]
  "TARGET_68881 || TARGET_FPA"
  "")

(define_insn ""
  [(set (match_operand:DF 0 "general_operand" "=x,y,y")
	(minus:DF (match_operand:DF 1 "general_operand" "xH,y,dmF")
		  (match_operand:DF 2 "general_operand" "xH,dmF,0")))]
  "TARGET_FPA"
  "*
{
  if (rtx_equal_p (operands[0], operands[2]))
    return \"fprsub%.d %y1,%0\";
  if (rtx_equal_p (operands[0], operands[1]))
    return \"fpsub%.d %y2,%0\";
  if (which_alternative == 0)
    return \"fpsub3%.d %w2,%w1,%0\";
  return \"fpsub3%.d %x2,%x1,%0\";
}")

(define_insn ""
  [(set (match_operand:DF 0 "general_operand" "=f")
	(minus:DF (match_operand:DF 1 "general_operand" "0")
		  (match_operand:DF 2 "general_operand" "fmG")))]
  "TARGET_68881"
  "*
{
  if (REG_P (operands[2]))
    return \"f%&sub%.x %2,%0\";
  return \"f%&sub%.d %f2,%0\";
}")

(define_expand "subsf3"
  [(set (match_operand:SF 0 "general_operand" "")
	(minus:SF (match_operand:SF 1 "general_operand" "")
		  (match_operand:SF 2 "general_operand" "")))]
  "TARGET_68881 || TARGET_FPA"
  "")

(define_insn ""
  [(set (match_operand:SF 0 "general_operand" "=x,y,y")
	(minus:SF (match_operand:SF 1 "general_operand" "xH,y,rmF")
		  (match_operand:SF 2 "general_operand" "xH,rmF,0")))]
  "TARGET_FPA"
  "*
{
  if (rtx_equal_p (operands[0], operands[2]))
    return \"fprsub%.s %w1,%0\";
  if (rtx_equal_p (operands[0], operands[1]))
    return \"fpsub%.s %w2,%0\";
  if (which_alternative == 0)
    return \"fpsub3%.s %w2,%w1,%0\";
  return \"fpsub3%.s %2,%1,%0\";
}")

(define_insn ""
  [(set (match_operand:SF 0 "general_operand" "=f")
	(minus:SF (match_operand:SF 1 "general_operand" "0")
		  (match_operand:SF 2 "general_operand" "fdmF")))]
  "TARGET_68881"
  "*
{
  if (REG_P (operands[2]) && ! DATA_REG_P (operands[2]))
    return \"f%$sub%.x %2,%0\";
  return \"f%$sub%.s %f2,%0\";
}")

;; multiply instructions

(define_insn "mulhi3"
  [(set (match_operand:HI 0 "general_operand" "=d")
	(mult:HI (match_operand:HI 1 "general_operand" "%0")
		 (match_operand:HI 2 "general_operand" "dmn")))]
  ""
  "*
{
#if defined(MOTOROLA) && !defined(CRDS)
  return \"muls%.w %2,%0\";
#else
  return \"muls %2,%0\";
#endif
}")

(define_insn "mulhisi3"
  [(set (match_operand:SI 0 "general_operand" "=d")
	(mult:SI (sign_extend:SI
		  (match_operand:HI 1 "nonimmediate_operand" "%0"))
		 (sign_extend:SI
		  (match_operand:HI 2 "nonimmediate_operand" "dm"))))]
  ""
  "*
{
#if defined(MOTOROLA) && !defined(CRDS)
  return \"muls%.w %2,%0\";
#else
  return \"muls %2,%0\";
#endif
}")

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=d")
	(mult:SI (sign_extend:SI
		  (match_operand:HI 1 "nonimmediate_operand" "%0"))
		 (match_operand:SI 2 "const_int_operand" "n")))]
  "INTVAL (operands[2]) >= -0x8000 && INTVAL (operands[2]) <= 0x7fff"
  "*
{
#if defined(MOTOROLA) && !defined(CRDS)
  return \"muls%.w %2,%0\";
#else
  return \"muls %2,%0\";
#endif
}")

(define_insn "mulsi3"
  [(set (match_operand:SI 0 "general_operand" "=d")
	(mult:SI (match_operand:SI 1 "general_operand" "%0")
		 (match_operand:SI 2 "general_operand" "dmsK")))]
  "TARGET_68020"
  "muls%.l %2,%0")

(define_insn "umulhisi3"
  [(set (match_operand:SI 0 "general_operand" "=d")
	(mult:SI (zero_extend:SI
		  (match_operand:HI 1 "nonimmediate_operand" "%0"))
		 (zero_extend:SI
		  (match_operand:HI 2 "nonimmediate_operand" "dm"))))]
  ""
  "*
{
#if defined(MOTOROLA) && !defined(CRDS)
  return \"mulu%.w %2,%0\";
#else
  return \"mulu %2,%0\";
#endif
}")

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=d")
	(mult:SI (zero_extend:SI
		  (match_operand:HI 1 "nonimmediate_operand" "%0"))
		 (match_operand:SI 2 "const_int_operand" "n")))]
  "INTVAL (operands[2]) >= 0 && INTVAL (operands[2]) <= 0xffff"
  "*
{
#if defined(MOTOROLA) && !defined(CRDS)
  return \"mulu%.w %2,%0\";
#else
  return \"mulu %2,%0\";
#endif
}")

;; We need a separate DEFINE_EXPAND for u?mulsidi3 to be able to use the
;; proper matching constraint.  This is because the matching is between
;; the high-numbered word of the DImode operand[0] and operand[1].
(define_expand "umulsidi3"
  [(parallel
    [(set (subreg:SI (match_operand:DI 0 "register_operand" "") 1)
	  (mult:SI (match_operand:SI 1 "register_operand" "")
		   (match_operand:SI 2 "nonimmediate_operand" "")))
     (set (subreg:SI (match_dup 0) 0)
	  (truncate:SI (lshiftrt:DI (mult:DI (zero_extend:DI (match_dup 1))
					     (zero_extend:DI (match_dup 2)))
				    (const_int 32))))])]
  "TARGET_68020"
  "")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(mult:SI (match_operand:SI 1 "register_operand" "%0")
		  (match_operand:SI 2 "nonimmediate_operand" "dm")))
   (set (match_operand:SI 3 "register_operand" "=d")
	(truncate:SI (lshiftrt:DI (mult:DI (zero_extend:DI (match_dup 1))
					   (zero_extend:DI (match_dup 2)))
				  (const_int 32))))]
  "TARGET_68020"
  "mulu%.l %2,%3:%0")

; Match immediate case.  For 2.4 only match things < 2^31.
; It's tricky with larger values in these patterns since we need to match
; values between the two parallel multiplies, between a CONST_DOUBLE and
; a CONST_INT.
(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(mult:SI (match_operand:SI 1 "register_operand" "%0")
		 (match_operand:SI 2 "const_int_operand" "n")))
   (set (match_operand:SI 3 "register_operand" "=d")
	(truncate:SI (lshiftrt:DI (mult:DI (zero_extend:DI (match_dup 1))
					   (match_dup 2))
				  (const_int 32))))]
  "TARGET_68020
   && (unsigned) INTVAL (operands[2]) <= 0x7fffffff"
  "mulu%.l %2,%3:%0")

(define_expand "mulsidi3"
  [(parallel
    [(set (subreg:SI (match_operand:DI 0 "register_operand" "") 1)
	  (mult:SI (match_operand:SI 1 "register_operand" "")
		   (match_operand:SI 2 "nonimmediate_operand" "")))
     (set (subreg:SI (match_dup 0) 0)
	  (truncate:SI (lshiftrt:DI (mult:DI (sign_extend:DI (match_dup 1))
					     (sign_extend:DI (match_dup 2)))
				    (const_int 32))))])]
  "TARGET_68020"
  "")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(mult:SI (match_operand:SI 1 "register_operand" "%0")
		 (match_operand:SI 2 "nonimmediate_operand" "dm")))
   (set (match_operand:SI 3 "register_operand" "=d")
	(truncate:SI (lshiftrt:DI (mult:DI (sign_extend:DI (match_dup 1))
					   (sign_extend:DI (match_dup 2)))
				  (const_int 32))))]
  "TARGET_68020"
  "muls%.l %2,%3:%0")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(mult:SI (match_operand:SI 1 "register_operand" "%0")
		 (match_operand:SI 2 "const_int_operand" "n")))
   (set (match_operand:SI 3 "register_operand" "=d")
	(truncate:SI (lshiftrt:DI (mult:DI (sign_extend:DI (match_dup 1))
					   (match_dup 2))
				  (const_int 32))))]
  "TARGET_68020
   /* This test is a noop on 32 bit machines,
      but important for a cross-compiler hosted on 64-bit machines.  */
   && INTVAL (operands[2]) <= 0x7fffffff
   && INTVAL (operands[2]) >= -0x80000000"
  "muls%.l %2,%3:%0")

(define_expand "muldf3"
  [(set (match_operand:DF 0 "general_operand" "")
	(mult:DF (match_operand:DF 1 "general_operand" "")
		 (match_operand:DF 2 "general_operand" "")))]
  "TARGET_68881 || TARGET_FPA"
  "")

(define_insn ""
  [(set (match_operand:DF 0 "general_operand" "=x,y")
	(mult:DF (match_operand:DF 1 "general_operand" "%xH,y")
		 (match_operand:DF 2 "general_operand" "xH,rmF")))]
  "TARGET_FPA"
  "*
{
  if (rtx_equal_p (operands[1], operands[2]))
    return \"fpsqr%.d %y1,%0\";
  if (rtx_equal_p (operands[0], operands[1]))
    return \"fpmul%.d %y2,%0\";
  if (rtx_equal_p (operands[0], operands[2]))
    return \"fpmul%.d %y1,%0\";
  if (which_alternative == 0)
    return \"fpmul3%.d %w2,%w1,%0\";
  return \"fpmul3%.d %x2,%x1,%0\";
}")

(define_insn ""
  [(set (match_operand:DF 0 "general_operand" "=f")
	(mult:DF (match_operand:DF 1 "general_operand" "%0")
		 (match_operand:DF 2 "general_operand" "fmG")))]
  "TARGET_68881"
  "*
{
  if (GET_CODE (operands[2]) == CONST_DOUBLE
      && floating_exact_log2 (operands[2]) && !TARGET_68040)
    {
      int i = floating_exact_log2 (operands[2]);
      operands[2] = gen_rtx (CONST_INT, VOIDmode, i);
      return \"fscale%.l %2,%0\";
    }
  if (REG_P (operands[2]))
    return \"f%&mul%.x %2,%0\";
  return \"f%&mul%.d %f2,%0\";
}")

(define_expand "mulsf3"
  [(set (match_operand:SF 0 "general_operand" "")
	(mult:SF (match_operand:SF 1 "general_operand" "")
		 (match_operand:SF 2 "general_operand" "")))]
  "TARGET_68881 || TARGET_FPA"
  "")

(define_insn ""
  [(set (match_operand:SF 0 "general_operand" "=x,y")
	(mult:SF (match_operand:SF 1 "general_operand" "%xH,y")
		 (match_operand:SF 2 "general_operand" "xH,rmF")))]
  "TARGET_FPA"
  "*
{
  if (rtx_equal_p (operands[1], operands[2]))
    return \"fpsqr%.s %w1,%0\";
  if (rtx_equal_p (operands[0], operands[1]))
    return \"fpmul%.s %w2,%0\";
  if (rtx_equal_p (operands[0], operands[2]))
    return \"fpmul%.s %w1,%0\";
  if (which_alternative == 0)
    return \"fpmul3%.s %w2,%w1,%0\";
  return \"fpmul3%.s %2,%1,%0\";
}")

(define_insn ""
  [(set (match_operand:SF 0 "general_operand" "=f")
	(mult:SF (match_operand:SF 1 "general_operand" "%0")
		 (match_operand:SF 2 "general_operand" "fdmF")))]
  "TARGET_68881"
  "*
{
#ifdef FSGLMUL_USE_S
  if (REG_P (operands[2]) && ! DATA_REG_P (operands[2]))
    return (TARGET_68040_ONLY
	    ? \"fsmul%.s %2,%0\"
	    : \"fsglmul%.s %2,%0\");
#else
  if (REG_P (operands[2]) && ! DATA_REG_P (operands[2]))
    return (TARGET_68040_ONLY
	    ? \"fsmul%.x %2,%0\"
	    : \"fsglmul%.x %2,%0\");
#endif
  return (TARGET_68040_ONLY
	  ? \"fsmul%.s %f2,%0\"
	  : \"fsglmul%.s %f2,%0\");
}")

;; divide instructions

(define_insn "divhi3"
  [(set (match_operand:HI 0 "general_operand" "=d")
	(div:HI (match_operand:HI 1 "general_operand" "0")
		(match_operand:HI 2 "general_operand" "dmn")))]
  ""
  "*
{
#ifdef MOTOROLA
  return \"ext%.l %0\;divs%.w %2,%0\";
#else
  return \"extl %0\;divs %2,%0\";
#endif
}")

;; These patterns don't work because the divs instruction is undefined if
;; the quotient is more than 16 bits.  This valid C would be miscompiled:
;; int n; short d; unsigned short q; ... q = (unsigned int) (n / d);
;; Imagine what happens when n = 100000 and d = 1.
;;(define_insn "divhisi3"
;;  [(set (match_operand:HI 0 "general_operand" "=d")
;;	(truncate:HI
;;	 (div:SI
;;	  (match_operand:SI 1 "general_operand" "0")
;;	  (sign_extend:SI (match_operand:HI 2 "nonimmediate_operand" "dm")))))]
;;  ""
;;  "*
;;{
;;#ifdef MOTOROLA
;;  return \"divs%.w %2,%0\";
;;#else
;;  return \"divs %2,%0\";
;;#endif
;;}")

;;(define_insn ""
;;  [(set (match_operand:HI 0 "general_operand" "=d")
;;	(truncate:HI (div:SI (match_operand:SI 1 "general_operand" "0")
;;			     (match_operand:SI 2 "const_int_operand" "n"))))]
;;  ""
;;  "*
;;{
;;#ifdef MOTOROLA
;;  return \"divs%.w %2,%0\";
;;#else
;;  return \"divs %2,%0\";
;;#endif
;;}")

(define_insn "udivhi3"
  [(set (match_operand:HI 0 "general_operand" "=d")
	(udiv:HI (match_operand:HI 1 "general_operand" "0")
		 (match_operand:HI 2 "general_operand" "dmn")))]
  ""
  "*
{
#ifdef MOTOROLA
  return \"and%.l %#0xFFFF,%0\;divu%.w %2,%0\";
#else
  return \"andl %#0xFFFF,%0\;divu %2,%0\";
#endif
}")

;; See comment before divhisi3 why these are commented out.
;;(define_insn "udivhisi3"
;;  [(set (match_operand:HI 0 "general_operand" "=d")
;;	(truncate:HI
;;	 (udiv:SI
;;	  (match_operand:SI 1 "general_operand" "0")
;;	  (zero_extend:SI (match_operand:HI 2 "nonimmediate_operand" "dm")))))]
;;  ""
;;  "*
;;{
;;#ifdef MOTOROLA
;;  return \"divu%.w %2,%0\";
;;#else
;;  return \"divu %2,%0\";
;;#endif
;;}")

;;(define_insn ""
;;  [(set (match_operand:HI 0 "general_operand" "=d")
;;	(truncate:HI (udiv:SI (match_operand:SI 1 "general_operand" "0")
;;			      (match_operand:SI 2 "const_int_operand" "n"))))]
;;  ""
;;  "*
;;{
;;#ifdef MOTOROLA
;;  return \"divu%.w %2,%0\";
;;#else
;;  return \"divu %2,%0\";
;;#endif
;;}")

(define_expand "divdf3"
  [(set (match_operand:DF 0 "general_operand" "")
	(div:DF (match_operand:DF 1 "general_operand" "")
		(match_operand:DF 2 "general_operand" "")))]
  "TARGET_68881 || TARGET_FPA"
  "")

(define_insn ""
  [(set (match_operand:DF 0 "general_operand" "=x,y,y")
	(div:DF (match_operand:DF 1 "general_operand" "xH,y,rmF")
		(match_operand:DF 2 "general_operand" "xH,rmF,0")))]
  "TARGET_FPA"
  "*
{
  if (rtx_equal_p (operands[0], operands[2]))
    return \"fprdiv%.d %y1,%0\";
  if (rtx_equal_p (operands[0], operands[1]))
    return \"fpdiv%.d %y2,%0\";
  if (which_alternative == 0)
    return \"fpdiv3%.d %w2,%w1,%0\";
  return \"fpdiv3%.d %x2,%x1,%x0\";
}")

(define_insn ""
  [(set (match_operand:DF 0 "general_operand" "=f")
	(div:DF (match_operand:DF 1 "general_operand" "0")
		(match_operand:DF 2 "general_operand" "fmG")))]
  "TARGET_68881"
  "*
{
  if (REG_P (operands[2]))
    return \"f%&div%.x %2,%0\";
  return \"f%&div%.d %f2,%0\";
}")

(define_expand "divsf3"
  [(set (match_operand:SF 0 "general_operand" "")
	(div:SF (match_operand:SF 1 "general_operand" "")
		(match_operand:SF 2 "general_operand" "")))]
  "TARGET_68881 || TARGET_FPA"
  "")

(define_insn ""
  [(set (match_operand:SF 0 "general_operand" "=x,y,y")
	(div:SF (match_operand:SF 1 "general_operand" "xH,y,rmF")
		(match_operand:SF 2 "general_operand" "xH,rmF,0")))]
  "TARGET_FPA"
  "*
{
  if (rtx_equal_p (operands[0], operands[1]))
    return \"fpdiv%.s %w2,%0\";
  if (rtx_equal_p (operands[0], operands[2]))
    return \"fprdiv%.s %w1,%0\";
  if (which_alternative == 0)
    return \"fpdiv3%.s %w2,%w1,%0\";
  return \"fpdiv3%.s %2,%1,%0\";
}")

(define_insn ""
  [(set (match_operand:SF 0 "general_operand" "=f")
	(div:SF (match_operand:SF 1 "general_operand" "0")
		(match_operand:SF 2 "general_operand" "fdmF")))]
  "TARGET_68881"
  "*
{
#ifdef FSGLDIV_USE_S
  if (REG_P (operands[2]) && ! DATA_REG_P (operands[2]))
    return (TARGET_68040_ONLY
	    ? \"fsdiv%.s %2,%0\"
	    : \"fsgldiv%.s %2,%0\");
#else
  if (REG_P (operands[2]) && ! DATA_REG_P (operands[2]))
    return (TARGET_68040_ONLY
	    ? \"fsdiv%.x %2,%0\"
	    : \"fsgldiv%.x %2,%0\");
#endif
  return (TARGET_68040_ONLY
	  ? \"fsdiv%.s %f2,%0\"
	  : \"fsgldiv%.s %f2,%0\");
}")

;; Remainder instructions.

(define_insn "modhi3"
  [(set (match_operand:HI 0 "general_operand" "=d")
	(mod:HI (match_operand:HI 1 "general_operand" "0")
		(match_operand:HI 2 "general_operand" "dmn")))]
  ""
  "*
{
  /* The swap insn produces cc's that don't correspond to the result.  */
  CC_STATUS_INIT;
#ifdef MOTOROLA
  return \"ext%.l %0\;divs%.w %2,%0\;swap %0\";
#else
  return \"extl %0\;divs %2,%0\;swap %0\";
#endif
}")

;; See comment before divhisi3 why these are commented out.
;;(define_insn "modhisi3"
;;  [(set (match_operand:HI 0 "general_operand" "=d")
;;	(truncate:HI
;;	 (mod:SI
;;	  (match_operand:SI 1 "general_operand" "0")
;;	  (sign_extend:SI (match_operand:HI 2 "nonimmediate_operand" "dm")))))]
;;  ""
;;  "*
;;{
;;  /* The swap insn produces cc's that don't correspond to the result.  */
;;  CC_STATUS_INIT;
;;#ifdef MOTOROLA
;;  return \"divs%.w %2,%0\;swap %0\";
;;#else
;;  return \"divs %2,%0\;swap %0\";
;;#endif
;;}")

;;(define_insn ""
;;  [(set (match_operand:HI 0 "general_operand" "=d")
;;	(truncate:HI (mod:SI (match_operand:SI 1 "general_operand" "0")
;;			     (match_operand:SI 2 "const_int_operand" "n"))))]
;;  ""
;;  "*
;;{
;;  /* The swap insn produces cc's that don't correspond to the result.  */
;;  CC_STATUS_INIT;
;;#ifdef MOTOROLA
;;  return \"divs%.w %2,%0\;swap %0\";
;;#else
;;  return \"divs %2,%0\;swap %0\";
;;#endif
;;}")

(define_insn "umodhi3"
  [(set (match_operand:HI 0 "general_operand" "=d")
	(umod:HI (match_operand:HI 1 "general_operand" "0")
		 (match_operand:HI 2 "general_operand" "dmn")))]
  ""
  "*
{
  /* The swap insn produces cc's that don't correspond to the result.  */
  CC_STATUS_INIT;
#ifdef MOTOROLA
  return \"and%.l %#0xFFFF,%0\;divu%.w %2,%0\;swap %0\";
#else
  return \"andl %#0xFFFF,%0\;divu %2,%0\;swap %0\";
#endif
}")

;; See comment before divhisi3 why these are commented out.
;;(define_insn "umodhisi3"
;;  [(set (match_operand:HI 0 "general_operand" "=d")
;;	(truncate:HI
;;	 (umod:SI
;;	  (match_operand:SI 1 "general_operand" "0")
;;	  (zero_extend:SI (match_operand:HI 2 "nonimmediate_operand" "dm")))))]
;;  ""
;;  "*
;;{
;;  /* The swap insn produces cc's that don't correspond to the result.  */
;;  CC_STATUS_INIT;
;;#ifdef MOTOROLA
;;  return \"divu%.w %2,%0\;swap %0\";
;;#else
;;  return \"divu %2,%0\;swap %0\";
;;#endif
;;}")

;;(define_insn ""
;;  [(set (match_operand:HI 0 "general_operand" "=d")
;;	(truncate:HI (umod:SI (match_operand:SI 1 "general_operand" "0")
;;			      (match_operand:SI 2 "const_int_operand" "n"))))]
;;  ""
;;  "*
;;{
;;  /* The swap insn produces cc's that don't correspond to the result.  */
;;  CC_STATUS_INIT;
;;#ifdef MOTOROLA
;;  return \"divu%.w %2,%0\;swap %0\";
;;#else
;;  return \"divu %2,%0\;swap %0\";
;;#endif
;;}")

(define_insn "divmodsi4"
  [(set (match_operand:SI 0 "general_operand" "=d")
	(div:SI (match_operand:SI 1 "general_operand" "0")
		(match_operand:SI 2 "general_operand" "dmsK")))
   (set (match_operand:SI 3 "general_operand" "=d")
	(mod:SI (match_dup 1) (match_dup 2)))]
  "TARGET_68020"
  "*
{
  if (find_reg_note (insn, REG_UNUSED, operands[3]))
    return \"divs%.l %2,%0\";
  else
    return \"divsl%.l %2,%3:%0\";
}")

(define_insn "udivmodsi4"
  [(set (match_operand:SI 0 "general_operand" "=d")
	(udiv:SI (match_operand:SI 1 "general_operand" "0")
		 (match_operand:SI 2 "general_operand" "dmsK")))
   (set (match_operand:SI 3 "general_operand" "=d")
	(umod:SI (match_dup 1) (match_dup 2)))]
  "TARGET_68020"
  "*
{
  if (find_reg_note (insn, REG_UNUSED, operands[3]))
    return \"divu%.l %2,%0\";
  else
    return \"divul%.l %2,%3:%0\";
}")

;; logical-and instructions

;; Prevent AND from being made with sp.  This doesn't exist in the machine
;; and reload will cause inefficient code.  Since sp is a FIXED_REG, we
;; can't allocate pseudos into it.
(define_insn "andsi3"
  [(set (match_operand:SI 0 "not_sp_operand" "=m,d")
	(and:SI (match_operand:SI 1 "general_operand" "%0,0")
		(match_operand:SI 2 "general_operand" "dKs,dmKs")))]
  ""
  "*
{
  int logval;
  if (GET_CODE (operands[2]) == CONST_INT
      && (INTVAL (operands[2]) | 0xffff) == 0xffffffff
      && (DATA_REG_P (operands[0])
	  || offsettable_memref_p (operands[0])))
    { 
      if (GET_CODE (operands[0]) != REG)
        operands[0] = adj_offsettable_operand (operands[0], 2);
      operands[2] = gen_rtx (CONST_INT, VOIDmode,
			     INTVAL (operands[2]) & 0xffff);
      /* Do not delete a following tstl %0 insn; that would be incorrect.  */
      CC_STATUS_INIT;
      if (operands[2] == const0_rtx)
        return \"clr%.w %0\";
      return \"and%.w %2,%0\";
    }
  if (GET_CODE (operands[2]) == CONST_INT
      && (logval = exact_log2 (~ INTVAL (operands[2]))) >= 0
      && (DATA_REG_P (operands[0])
          || offsettable_memref_p (operands[0])))
    { 
      if (DATA_REG_P (operands[0]))
        {
          operands[1] = gen_rtx (CONST_INT, VOIDmode, logval);
        }
      else
        {
          operands[0] = adj_offsettable_operand (operands[0], 3 - (logval / 8));          operands[1] = gen_rtx (CONST_INT, VOIDmode, logval % 8);
        }
      /* This does not set condition codes in a standard way.  */
      CC_STATUS_INIT;
      return \"bclr %1,%0\";
    }
  return \"and%.l %2,%0\";
}")

(define_insn "andhi3"
  [(set (match_operand:HI 0 "general_operand" "=m,d")
	(and:HI (match_operand:HI 1 "general_operand" "%0,0")
		(match_operand:HI 2 "general_operand" "dn,dmn")))]
  ""
  "and%.w %2,%0")

(define_insn ""
  [(set (strict_low_part (match_operand:HI 0 "general_operand" "+m,d"))
	(and:HI (match_dup 0)
		(match_operand:HI 1 "general_operand" "dn,dmn")))]
  ""
  "and%.w %1,%0")

(define_insn ""
  [(set (strict_low_part (match_operand:HI 0 "general_operand" "+m,d"))
	(and:HI (match_operand:HI 1 "general_operand" "dn,dmn")
		(match_dup 0)))]
  ""
  "and%.w %1,%0")

(define_insn "andqi3"
  [(set (match_operand:QI 0 "general_operand" "=m,d")
	(and:QI (match_operand:QI 1 "general_operand" "%0,0")
		(match_operand:QI 2 "general_operand" "dn,dmn")))]
  ""
  "and%.b %2,%0")

(define_insn ""
  [(set (strict_low_part (match_operand:QI 0 "general_operand" "+m,d"))
	(and:QI (match_dup 0)
		(match_operand:QI 1 "general_operand" "dn,dmn")))]
  ""
  "and%.b %1,%0")

(define_insn ""
  [(set (strict_low_part (match_operand:QI 0 "general_operand" "+m,d"))
	(and:QI (match_operand:QI 1 "general_operand" "dn,dmn")
		(match_dup 0)))]
  ""
  "and%.b %1,%0")

;; inclusive-or instructions

(define_insn "iorsi3"
  [(set (match_operand:SI 0 "general_operand" "=m,d")
	(ior:SI (match_operand:SI 1 "general_operand" "%0,0")
		(match_operand:SI 2 "general_operand" "dKs,dmKs")))]
  ""
  "*
{
  register int logval;
  if (GET_CODE (operands[2]) == CONST_INT
      && INTVAL (operands[2]) >> 16 == 0
      && (DATA_REG_P (operands[0])
	  || offsettable_memref_p (operands[0])))
    { 
      if (GET_CODE (operands[0]) != REG)
        operands[0] = adj_offsettable_operand (operands[0], 2);
      /* Do not delete a following tstl %0 insn; that would be incorrect.  */
      CC_STATUS_INIT;
      return \"or%.w %2,%0\";
    }
  if (GET_CODE (operands[2]) == CONST_INT
      && (logval = exact_log2 (INTVAL (operands[2]))) >= 0
      && (DATA_REG_P (operands[0])
	  || offsettable_memref_p (operands[0])))
    { 
      if (DATA_REG_P (operands[0]))
	{
	  operands[1] = gen_rtx (CONST_INT, VOIDmode, logval);
	}
      else
        {
	  operands[0] = adj_offsettable_operand (operands[0], 3 - (logval / 8));
	  operands[1] = gen_rtx (CONST_INT, VOIDmode, logval % 8);
	}
      CC_STATUS_INIT;
      return \"bset %1,%0\";
    }
  return \"or%.l %2,%0\";
}")

(define_insn "iorhi3"
  [(set (match_operand:HI 0 "general_operand" "=m,d")
	(ior:HI (match_operand:HI 1 "general_operand" "%0,0")
		(match_operand:HI 2 "general_operand" "dn,dmn")))]
  ""
  "or%.w %2,%0")

(define_insn ""
  [(set (strict_low_part (match_operand:HI 0 "general_operand" "+m,d"))
	(ior:HI (match_dup 0)
		(match_operand:HI 1 "general_operand" "dn,dmn")))]
  ""
  "or%.w %1,%0")

(define_insn ""
  [(set (strict_low_part (match_operand:HI 0 "general_operand" "+m,d"))
	(ior:HI (match_operand:HI 1 "general_operand" "dn,dmn")
		(match_dup 0)))]
  ""
  "or%.w %1,%0")

(define_insn "iorqi3"
  [(set (match_operand:QI 0 "general_operand" "=m,d")
	(ior:QI (match_operand:QI 1 "general_operand" "%0,0")
		(match_operand:QI 2 "general_operand" "dn,dmn")))]
  ""
  "or%.b %2,%0")

(define_insn ""
  [(set (strict_low_part (match_operand:QI 0 "general_operand" "+m,d"))
	(ior:QI (match_dup 0)
		(match_operand:QI 1 "general_operand" "dn,dmn")))]
  ""
  "or%.b %1,%0")

(define_insn ""
  [(set (strict_low_part (match_operand:QI 0 "general_operand" "+m,d"))
	(ior:QI (match_operand:QI 1 "general_operand" "dn,dmn")
		(match_dup 0)))]
  ""
  "or%.b %1,%0")

;; xor instructions

(define_insn "xorsi3"
  [(set (match_operand:SI 0 "general_operand" "=do,m")
	(xor:SI (match_operand:SI 1 "general_operand" "%0,0")
		(match_operand:SI 2 "general_operand" "di,dKs")))]
  ""
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT
      && INTVAL (operands[2]) >> 16 == 0
      && (offsettable_memref_p (operands[0]) || DATA_REG_P (operands[0])))
    { 
      if (! DATA_REG_P (operands[0]))
	operands[0] = adj_offsettable_operand (operands[0], 2);
      /* Do not delete a following tstl %0 insn; that would be incorrect.  */
      CC_STATUS_INIT;
      return \"eor%.w %2,%0\";
    }
  return \"eor%.l %2,%0\";
}")

(define_insn "xorhi3"
  [(set (match_operand:HI 0 "general_operand" "=dm")
	(xor:HI (match_operand:HI 1 "general_operand" "%0")
		(match_operand:HI 2 "general_operand" "dn")))]
  ""
  "eor%.w %2,%0")

(define_insn ""
  [(set (strict_low_part (match_operand:HI 0 "general_operand" "+dm"))
	(xor:HI (match_dup 0)
		(match_operand:HI 1 "general_operand" "dn")))]
  ""
  "eor%.w %1,%0")


(define_insn ""
  [(set (strict_low_part (match_operand:HI 0 "general_operand" "+dm"))
	(xor:HI (match_operand:HI 1 "general_operand" "dn")
		(match_dup 0)))]
  ""
  "eor%.w %1,%0")

(define_insn "xorqi3"
  [(set (match_operand:QI 0 "general_operand" "=dm")
	(xor:QI (match_operand:QI 1 "general_operand" "%0")
		(match_operand:QI 2 "general_operand" "dn")))]
  ""
  "eor%.b %2,%0")

(define_insn ""
  [(set (strict_low_part (match_operand:QI 0 "general_operand" "+dm"))
	(xor:QI (match_dup 0)
		(match_operand:QI 1 "general_operand" "dn")))]
  ""
  "eor%.b %1,%0")

(define_insn ""
  [(set (strict_low_part (match_operand:QI 0 "general_operand" "+dm"))
	(xor:QI (match_operand:QI 1 "general_operand" "dn")
		(match_dup 0)))]
  ""
  "eor%.b %1,%0")

;; negation instructions

(define_insn "negsi2"
  [(set (match_operand:SI 0 "general_operand" "=dm")
	(neg:SI (match_operand:SI 1 "general_operand" "0")))]
  ""
  "neg%.l %0")

(define_insn "neghi2"
  [(set (match_operand:HI 0 "general_operand" "=dm")
	(neg:HI (match_operand:HI 1 "general_operand" "0")))]
  ""
  "neg%.w %0")

(define_insn ""
  [(set (strict_low_part (match_operand:HI 0 "general_operand" "+dm"))
	(neg:HI (match_dup 0)))]
  ""
  "neg%.w %0")

(define_insn "negqi2"
  [(set (match_operand:QI 0 "general_operand" "=dm")
	(neg:QI (match_operand:QI 1 "general_operand" "0")))]
  ""
  "neg%.b %0")

(define_insn ""
  [(set (strict_low_part (match_operand:QI 0 "general_operand" "+dm"))
	(neg:QI (match_dup 0)))]
  ""
  "neg%.b %0")

(define_expand "negsf2"
  [(set (match_operand:SF 0 "general_operand" "")
	(neg:SF (match_operand:SF 1 "general_operand" "")))]
  "TARGET_68881 || TARGET_FPA"
  "")

(define_insn ""
  [(set (match_operand:SF 0 "general_operand" "=x,y")
	(neg:SF (match_operand:SF 1 "general_operand" "xH,rmF")))]
  "TARGET_FPA"
  "fpneg%.s %w1,%0")

(define_insn ""
  [(set (match_operand:SF 0 "general_operand" "=f,d")
	(neg:SF (match_operand:SF 1 "general_operand" "fdmF,0")))]
  "TARGET_68881"
  "*
{
  if (DATA_REG_P (operands[0]))
    {
      operands[1] = gen_rtx (CONST_INT, VOIDmode, 31);
      return \"bchg %1,%0\";
    }
  if (REG_P (operands[1]) && ! DATA_REG_P (operands[1]))
    return \"f%$neg%.x %1,%0\";
  return \"f%$neg%.s %f1,%0\";
}")

(define_expand "negdf2"
  [(set (match_operand:DF 0 "general_operand" "")
	(neg:DF (match_operand:DF 1 "general_operand" "")))]
  "TARGET_68881 || TARGET_FPA"
  "")

(define_insn ""
  [(set (match_operand:DF 0 "general_operand" "=x,y")
	(neg:DF (match_operand:DF 1 "general_operand" "xH,rmF")))]
  "TARGET_FPA"
  "fpneg%.d %y1, %0")

(define_insn ""
  [(set (match_operand:DF 0 "general_operand" "=f,d")
	(neg:DF (match_operand:DF 1 "general_operand" "fmF,0")))]
  "TARGET_68881"
  "*
{
  if (DATA_REG_P (operands[0]))
    {
      operands[1] = gen_rtx (CONST_INT, VOIDmode, 31);
      return \"bchg %1,%0\";
    }
  if (REG_P (operands[1]) && ! DATA_REG_P (operands[1]))
    return \"f%&neg%.x %1,%0\";
  return \"f%&neg%.d %f1,%0\";
}")

;; Sqrt instruction for the 68881

(define_insn "sqrtdf2"
  [(set (match_operand:DF 0 "general_operand" "=f")
	(sqrt:DF (match_operand:DF 1 "general_operand" "fm")))]
  "TARGET_68881"
  "*
{
  if (FP_REG_P (operands[1]))
    return \"fsqrt%.x %1,%0\";
  else
    return \"fsqrt%.d %1,%0\";
}")

;; Absolute value instructions

(define_expand "abssf2"
  [(set (match_operand:SF 0 "general_operand" "")
	(abs:SF (match_operand:SF 1 "general_operand" "")))]
  "TARGET_68881 || TARGET_FPA"
  "")

(define_insn ""
  [(set (match_operand:SF 0 "general_operand" "=x,y")
	(abs:SF (match_operand:SF 1 "general_operand" "xH,rmF")))]
  "TARGET_FPA"
  "fpabs%.s %y1,%0")

(define_insn ""
  [(set (match_operand:SF 0 "general_operand" "=f")
	(abs:SF (match_operand:SF 1 "general_operand" "fdmF")))]
  "TARGET_68881"
  "*
{
  if (REG_P (operands[1]) && ! DATA_REG_P (operands[1]))
    return \"f%$abs%.x %1,%0\";
  return \"f%$abs%.s %f1,%0\";
}")

(define_expand "absdf2"
  [(set (match_operand:DF 0 "general_operand" "")
	(abs:DF (match_operand:DF 1 "general_operand" "")))]
  "TARGET_68881 || TARGET_FPA"
  "")

(define_insn ""
  [(set (match_operand:DF 0 "general_operand" "=x,y")
	(abs:DF (match_operand:DF 1 "general_operand" "xH,rmF")))]
  "TARGET_FPA"
  "fpabs%.d %y1,%0")

(define_insn ""
  [(set (match_operand:DF 0 "general_operand" "=f")
	(abs:DF (match_operand:DF 1 "general_operand" "fmF")))]
  "TARGET_68881"
  "*
{
  if (REG_P (operands[1]) && ! DATA_REG_P (operands[1]))
    return \"f%&abs%.x %1,%0\";
  return \"f%&abs%.d %f1,%0\";
}")

;; one complement instructions

(define_insn "one_cmplsi2"
  [(set (match_operand:SI 0 "general_operand" "=dm")
	(not:SI (match_operand:SI 1 "general_operand" "0")))]
  ""
  "not%.l %0")

(define_insn "one_cmplhi2"
  [(set (match_operand:HI 0 "general_operand" "=dm")
	(not:HI (match_operand:HI 1 "general_operand" "0")))]
  ""
  "not%.w %0")

(define_insn ""
  [(set (strict_low_part (match_operand:HI 0 "general_operand" "+dm"))
	(not:HI (match_dup 0)))]
  ""
  "not%.w %0")

(define_insn "one_cmplqi2"
  [(set (match_operand:QI 0 "general_operand" "=dm")
	(not:QI (match_operand:QI 1 "general_operand" "0")))]
  ""
  "not%.b %0")

(define_insn ""
  [(set (strict_low_part (match_operand:QI 0 "general_operand" "+dm"))
	(not:QI (match_dup 0)))]
  ""
  "not%.b %0")

;; arithmetic shift instructions
;; We don't need the shift memory by 1 bit instruction

;; On all 68k models, this makes faster code in a special case.

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(ashift:SI (match_operand:SI 1 "register_operand" "0")
		   (match_operand:SI 2 "immediate_operand" "i")))]
  "(GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 16)"
  "*
{
  CC_STATUS_INIT;
  return \"swap %0\;clr%.w %0\";
}")

;; On the 68000, this makes faster code in a special case.

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(ashift:SI (match_operand:SI 1 "register_operand" "0")
		   (match_operand:SI 2 "immediate_operand" "i")))]
  "(! TARGET_68020 && GET_CODE (operands[2]) == CONST_INT
    && INTVAL (operands[2]) > 16 && INTVAL (operands[2]) <= 24)"
  "*
{
  CC_STATUS_INIT;

  operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) - 16);
  return \"asl%.w %2,%0\;swap %0\;clr%.w %0\";
}")

(define_insn "ashlsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(ashift:SI (match_operand:SI 1 "register_operand" "0")
		   (match_operand:SI 2 "general_operand" "dI")))]
  ""
  "*
{
  if (operands[2] == const1_rtx)
    return \"add%.l %0,%0\";
  return \"asl%.l %2,%0\";
}")

(define_insn "ashlhi3"
  [(set (match_operand:HI 0 "register_operand" "=d")
	(ashift:HI (match_operand:HI 1 "register_operand" "0")
		   (match_operand:HI 2 "general_operand" "dI")))]
  ""
  "asl%.w %2,%0")

(define_insn ""
  [(set (strict_low_part (match_operand:HI 0 "register_operand" "+d"))
	(ashift:HI (match_dup 0)
		   (match_operand:HI 1 "general_operand" "dI")))]
  ""
  "asl%.w %1,%0")

(define_insn "ashlqi3"
  [(set (match_operand:QI 0 "register_operand" "=d")
	(ashift:QI (match_operand:QI 1 "register_operand" "0")
		   (match_operand:QI 2 "general_operand" "dI")))]
  ""
  "asl%.b %2,%0")

(define_insn ""
  [(set (strict_low_part (match_operand:QI 0 "register_operand" "+d"))
	(ashift:QI (match_dup 0)
		   (match_operand:QI 1 "general_operand" "dI")))]
  ""
  "asl%.b %1,%0")

;; On all 68k models, this makes faster code in a special case.

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(ashiftrt:SI (match_operand:SI 1 "register_operand" "0")
		     (match_operand:SI 2 "immediate_operand" "i")))]
  "(GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 16)" 
  "swap %0\;ext%.l %0")

;; On the 68000, this makes faster code in a special case.

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(ashiftrt:SI (match_operand:SI 1 "register_operand" "0")
		     (match_operand:SI 2 "immediate_operand" "i")))]
  "(! TARGET_68020 && GET_CODE (operands[2]) == CONST_INT
    && INTVAL (operands[2]) > 16 && INTVAL (operands[2]) <= 24)"
  "*
{
  operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) - 16);
  return \"swap %0\;asr%.w %2,%0\;ext%.l %0\";
}")

(define_insn "ashrsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(ashiftrt:SI (match_operand:SI 1 "register_operand" "0")
		     (match_operand:SI 2 "general_operand" "dI")))]
  ""
  "*
{
  return \"asr%.l %2,%0\";
}")

(define_insn "ashrhi3"
  [(set (match_operand:HI 0 "register_operand" "=d")
	(ashiftrt:HI (match_operand:HI 1 "register_operand" "0")
		     (match_operand:HI 2 "general_operand" "dI")))]
  ""
  "asr%.w %2,%0")

(define_insn ""
  [(set (strict_low_part (match_operand:HI 0 "register_operand" "+d"))
	(ashiftrt:HI (match_dup 0)
		     (match_operand:HI 1 "general_operand" "dI")))]
  ""
  "asr%.w %1,%0")

(define_insn "ashrqi3"
  [(set (match_operand:QI 0 "register_operand" "=d")
	(ashiftrt:QI (match_operand:QI 1 "register_operand" "0")
		     (match_operand:QI 2 "general_operand" "dI")))]
  ""
  "asr%.b %2,%0")

(define_insn ""
  [(set (strict_low_part (match_operand:QI 0 "register_operand" "+d"))
	(ashiftrt:QI (match_dup 0)
		     (match_operand:QI 1 "general_operand" "dI")))]
  ""
  "asr%.b %1,%0")

;; logical shift instructions

;; On all 68k models, this makes faster code in a special case.

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(lshift:SI (match_operand:SI 1 "register_operand" "0")
		   (match_operand:SI 2 "immediate_operand" "i")))]
  "(GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 16)"
  "*
{
  CC_STATUS_INIT;
  return \"swap %0\;clr%.w %0\";
}")

;; On the 68000, this makes faster code in a special case.

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(lshift:SI (match_operand:SI 1 "register_operand" "0")
		   (match_operand:SI 2 "immediate_operand" "i")))]
  "(! TARGET_68020 && GET_CODE (operands[2]) == CONST_INT
    && INTVAL (operands[2]) > 16 && INTVAL (operands[2]) <= 24)"
  "*
{
  CC_STATUS_INIT;

  operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) - 16);
  return \"lsl%.w %2,%0\;swap %0\;clr%.w %0\";
}")

(define_insn "lshlsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(lshift:SI (match_operand:SI 1 "register_operand" "0")
		   (match_operand:SI 2 "general_operand" "dI")))]
  ""
  "*
{
  if (operands[2] == const1_rtx)
    return \"add%.l %0,%0\";
  return \"lsl%.l %2,%0\";
}")

(define_insn "lshlhi3"
  [(set (match_operand:HI 0 "register_operand" "=d")
	(lshift:HI (match_operand:HI 1 "register_operand" "0")
		   (match_operand:HI 2 "general_operand" "dI")))]
  ""
  "lsl%.w %2,%0")

(define_insn ""
  [(set (strict_low_part (match_operand:HI 0 "register_operand" "+d"))
	(lshift:HI (match_dup 0)
		   (match_operand:HI 1 "general_operand" "dI")))]
  ""
  "lsl%.w %1,%0")

(define_insn "lshlqi3"
  [(set (match_operand:QI 0 "register_operand" "=d")
	(lshift:QI (match_operand:QI 1 "register_operand" "0")
		   (match_operand:QI 2 "general_operand" "dI")))]
  ""
  "lsl%.b %2,%0")

(define_insn ""
  [(set (strict_low_part (match_operand:QI 0 "register_operand" "+d"))
	(lshift:QI (match_dup 0)
		   (match_operand:QI 1 "general_operand" "dI")))]
  ""
  "lsl%.b %1,%0")

;; On all 68k models, this makes faster code in a special case.

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(lshiftrt:SI (match_operand:SI 1 "register_operand" "0")
		     (match_operand:SI 2 "immediate_operand" "i")))]
  "(GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 16)" 
  "*
{
  CC_STATUS_INIT;
  return \"clr%.w %0\;swap %0\";
}")

;; On the 68000, this makes faster code in a special case.

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=d")
	(lshiftrt:SI (match_operand:SI 1 "register_operand" "0")
		     (match_operand:SI 2 "immediate_operand" "i")))]
  "(! TARGET_68020 && GET_CODE (operands[2]) == CONST_INT
    && INTVAL (operands[2]) > 16 && INTVAL (operands[2]) <= 24)"
  "*
{
  /* I think lsr%.w sets the CC properly.  */
  operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) - 16);
  return \"clr%.w %0\;swap %0\;lsr%.w %2,%0\";
}")

(define_insn "lshrsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(lshiftrt:SI (match_operand:SI 1 "register_operand" "0")
		     (match_operand:SI 2 "general_operand" "dI")))]
  ""
  "*
{
  return \"lsr%.l %2,%0\";
}")

(define_insn "lshrhi3"
  [(set (match_operand:HI 0 "register_operand" "=d")
	(lshiftrt:HI (match_operand:HI 1 "register_operand" "0")
		     (match_operand:HI 2 "general_operand" "dI")))]
  ""
  "lsr%.w %2,%0")

(define_insn ""
  [(set (strict_low_part (match_operand:HI 0 "register_operand" "+d"))
	(lshiftrt:HI (match_dup 0)
		     (match_operand:HI 1 "general_operand" "dI")))]
  ""
  "lsr%.w %1,%0")

(define_insn "lshrqi3"
  [(set (match_operand:QI 0 "register_operand" "=d")
	(lshiftrt:QI (match_operand:QI 1 "register_operand" "0")
		     (match_operand:QI 2 "general_operand" "dI")))]
  ""
  "lsr%.b %2,%0")

(define_insn ""
  [(set (strict_low_part (match_operand:QI 0 "register_operand" "+d"))
	(lshiftrt:QI (match_dup 0)
		     (match_operand:QI 1 "general_operand" "dI")))]
  ""
  "lsr%.b %1,%0")

;; rotate instructions

(define_insn "rotlsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(rotate:SI (match_operand:SI 1 "register_operand" "0")
		   (match_operand:SI 2 "general_operand" "dI")))]
  ""
  "rol%.l %2,%0")

(define_insn "rotlhi3"
  [(set (match_operand:HI 0 "register_operand" "=d")
	(rotate:HI (match_operand:HI 1 "register_operand" "0")
		   (match_operand:HI 2 "general_operand" "dI")))]
  ""
  "rol%.w %2,%0")


(define_insn ""
  [(set (strict_low_part (match_operand:HI 0 "register_operand" "+d"))
	(rotate:HI (match_dup 0)
		   (match_operand:HI 1 "general_operand" "dI")))]
  ""
  "rol%.w %1,%0")

(define_insn "rotlqi3"
  [(set (match_operand:QI 0 "register_operand" "=d")
	(rotate:QI (match_operand:QI 1 "register_operand" "0")
		   (match_operand:QI 2 "general_operand" "dI")))]
  ""
  "rol%.b %2,%0")

(define_insn ""
  [(set (strict_low_part (match_operand:QI 0 "register_operand" "+d"))
	(rotate:QI (match_dup 0)
		   (match_operand:QI 1 "general_operand" "dI")))]
  ""
  "rol%.b %1,%0")

(define_insn "rotrsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(rotatert:SI (match_operand:SI 1 "register_operand" "0")
		     (match_operand:SI 2 "general_operand" "dI")))]
  ""
  "ror%.l %2,%0")

(define_insn "rotrhi3"
  [(set (match_operand:HI 0 "register_operand" "=d")
	(rotatert:HI (match_operand:HI 1 "register_operand" "0")
		     (match_operand:HI 2 "general_operand" "dI")))]
  ""
  "ror%.w %2,%0")

(define_insn ""
  [(set (strict_low_part (match_operand:HI 0 "register_operand" "+d"))
	(rotatert:HI (match_dup 0)
		     (match_operand:HI 1 "general_operand" "dI")))]
  ""
  "ror%.w %1,%0")

(define_insn "rotrqi3"
  [(set (match_operand:QI 0 "register_operand" "=d")
	(rotatert:QI (match_operand:QI 1 "register_operand" "0")
		     (match_operand:QI 2 "general_operand" "dI")))]
  ""
  "ror%.b %2,%0")

(define_insn ""
  [(set (strict_low_part (match_operand:QI 0 "register_operand" "+d"))
	(rotatert:QI (match_dup 0)
		     (match_operand:QI 1 "general_operand" "dI")))]
  ""
  "ror%.b %1,%0")

;; Special cases of bit-field insns which we should
;; recognize in preference to the general case.
;; These handle aligned 8-bit and 16-bit fields,
;; which can usually be done with move instructions.

;
; Special case for 32-bit field in memory.  This only occurs when 32-bit
; alignment of structure members is specified.
;
; The move is allowed to be odd byte aligned, because that's still faster
; than an odd byte aligned bit field instruction.
;
(define_insn ""
  [(set (zero_extract:SI (match_operand:QI 0 "nonimmediate_operand" "+o")
			 (match_operand:SI 1 "immediate_operand" "i")
			 (match_operand:SI 2 "immediate_operand" "i"))
	(match_operand:SI 3 "general_operand" "rmi"))]
  "TARGET_68020 && TARGET_BITFIELD
   && GET_CODE (operands[1]) == CONST_INT
   && (INTVAL (operands[1]) == 32)
   && GET_CODE (operands[2]) == CONST_INT
   && (INTVAL (operands[2]) % 8) == 0
   && ! mode_dependent_address_p (XEXP (operands[0], 0))"
  "*
{
  operands[0]
    = adj_offsettable_operand (operands[0], INTVAL (operands[2]) / 8);

  return \"move%.l %3,%0\";
}")

(define_insn ""
  [(set (zero_extract:SI (match_operand:SI 0 "nonimmediate_operand" "+do")
			 (match_operand:SI 1 "immediate_operand" "i")
			 (match_operand:SI 2 "immediate_operand" "i"))
	(match_operand:SI 3 "general_operand" "d"))]
  "TARGET_68020 && TARGET_BITFIELD
   && GET_CODE (operands[1]) == CONST_INT
   && (INTVAL (operands[1]) == 8 || INTVAL (operands[1]) == 16)
   && GET_CODE (operands[2]) == CONST_INT
   && INTVAL (operands[2]) % INTVAL (operands[1]) == 0
   && (GET_CODE (operands[0]) == REG
       || ! mode_dependent_address_p (XEXP (operands[0], 0)))"
  "*
{
  if (REG_P (operands[0]))
    {
      if (INTVAL (operands[1]) + INTVAL (operands[2]) != 32)
        return \"bfins %3,%0{%b2:%b1}\";
    }
  else
    operands[0]
      = adj_offsettable_operand (operands[0], INTVAL (operands[2]) / 8);

  if (GET_CODE (operands[3]) == MEM)
    operands[3] = adj_offsettable_operand (operands[3],
					   (32 - INTVAL (operands[1])) / 8);
  if (INTVAL (operands[1]) == 8)
    return \"move%.b %3,%0\";
  return \"move%.w %3,%0\";
}")


;
; Special case for 32-bit field in memory.  This only occurs when 32-bit
; alignment of structure members is specified.
;
; The move is allowed to be odd byte aligned, because that's still faster
; than an odd byte aligned bit field instruction.
;
(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=rm")
	(zero_extract:SI (match_operand:QI 1 "nonimmediate_operand" "o")
			 (match_operand:SI 2 "immediate_operand" "i")
			 (match_operand:SI 3 "immediate_operand" "i")))]
  "TARGET_68020 && TARGET_BITFIELD
   && GET_CODE (operands[2]) == CONST_INT
   && (INTVAL (operands[2]) == 32)
   && GET_CODE (operands[3]) == CONST_INT
   && (INTVAL (operands[3]) % 8) == 0
   && ! mode_dependent_address_p (XEXP (operands[1], 0))"
  "*
{
  operands[1]
    = adj_offsettable_operand (operands[1], INTVAL (operands[3]) / 8);

  return \"move%.l %1,%0\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=&d")
	(zero_extract:SI (match_operand:SI 1 "nonimmediate_operand" "do")
			 (match_operand:SI 2 "immediate_operand" "i")
			 (match_operand:SI 3 "immediate_operand" "i")))]
  "TARGET_68020 && TARGET_BITFIELD
   && GET_CODE (operands[2]) == CONST_INT
   && (INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 16)
   && GET_CODE (operands[3]) == CONST_INT
   && INTVAL (operands[3]) % INTVAL (operands[2]) == 0
   && (GET_CODE (operands[1]) == REG
       || ! mode_dependent_address_p (XEXP (operands[1], 0)))"
  "*
{
  cc_status.flags |= CC_NOT_NEGATIVE;
  if (REG_P (operands[1]))
    {
      if (INTVAL (operands[2]) + INTVAL (operands[3]) != 32)
	return \"bfextu %1{%b3:%b2},%0\";
    }
  else
    operands[1]
      = adj_offsettable_operand (operands[1], INTVAL (operands[3]) / 8);

  output_asm_insn (\"clr%.l %0\", operands);
  if (GET_CODE (operands[0]) == MEM)
    operands[0] = adj_offsettable_operand (operands[0],
					   (32 - INTVAL (operands[1])) / 8);
  if (INTVAL (operands[2]) == 8)
    return \"move%.b %1,%0\";
  return \"move%.w %1,%0\";
}")

;
; Special case for 32-bit field in memory.  This only occurs when 32-bit
; alignment of structure members is specified.
;
; The move is allowed to be odd byte aligned, because that's still faster
; than an odd byte aligned bit field instruction.
;
(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=rm")
	(sign_extract:SI (match_operand:QI 1 "nonimmediate_operand" "o")
			 (match_operand:SI 2 "immediate_operand" "i")
			 (match_operand:SI 3 "immediate_operand" "i")))]
  "TARGET_68020 && TARGET_BITFIELD
   && GET_CODE (operands[2]) == CONST_INT
   && (INTVAL (operands[2]) == 32)
   && GET_CODE (operands[3]) == CONST_INT
   && (INTVAL (operands[3]) % 8) == 0
   && ! mode_dependent_address_p (XEXP (operands[1], 0))"
  "*
{
  operands[1]
    = adj_offsettable_operand (operands[1], INTVAL (operands[3]) / 8);

  return \"move%.l %1,%0\";
}")

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=d")
	(sign_extract:SI (match_operand:SI 1 "nonimmediate_operand" "do")
			 (match_operand:SI 2 "immediate_operand" "i")
			 (match_operand:SI 3 "immediate_operand" "i")))]
  "TARGET_68020 && TARGET_BITFIELD
   && GET_CODE (operands[2]) == CONST_INT
   && (INTVAL (operands[2]) == 8 || INTVAL (operands[2]) == 16)
   && GET_CODE (operands[3]) == CONST_INT
   && INTVAL (operands[3]) % INTVAL (operands[2]) == 0
   && (GET_CODE (operands[1]) == REG
       || ! mode_dependent_address_p (XEXP (operands[1], 0)))"
  "*
{
  if (REG_P (operands[1]))
    {
      if (INTVAL (operands[2]) + INTVAL (operands[3]) != 32)
	return \"bfexts %1{%b3:%b2},%0\";
    }
  else
    operands[1]
      = adj_offsettable_operand (operands[1], INTVAL (operands[3]) / 8);

  if (INTVAL (operands[2]) == 8)
    return \"move%.b %1,%0\;extb%.l %0\";
  return \"move%.w %1,%0\;ext%.l %0\";
}")

;; Bit field instructions, general cases.
;; "o,d" constraint causes a nonoffsettable memref to match the "o"
;; so that its address is reloaded.

(define_insn "extv"
  [(set (match_operand:SI 0 "general_operand" "=d,d")
	(sign_extract:SI (match_operand:QI 1 "nonimmediate_operand" "o,d")
			 (match_operand:SI 2 "general_operand" "di,di")
			 (match_operand:SI 3 "general_operand" "di,di")))]
  "TARGET_68020 && TARGET_BITFIELD"
  "bfexts %1{%b3:%b2},%0")

(define_insn "extzv"
  [(set (match_operand:SI 0 "general_operand" "=d,d")
	(zero_extract:SI (match_operand:QI 1 "nonimmediate_operand" "o,d")
			 (match_operand:SI 2 "general_operand" "di,di")
			 (match_operand:SI 3 "general_operand" "di,di")))]
  "TARGET_68020 && TARGET_BITFIELD"
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT)
    {
      if (INTVAL (operands[2]) != 32)
	cc_status.flags |= CC_NOT_NEGATIVE;
    }
  else
    {
      CC_STATUS_INIT;
    }
  return \"bfextu %1{%b3:%b2},%0\";
}")

(define_insn ""
  [(set (zero_extract:SI (match_operand:QI 0 "nonimmediate_operand" "+o,d")
			 (match_operand:SI 1 "general_operand" "di,di")
			 (match_operand:SI 2 "general_operand" "di,di"))
        (xor:SI (zero_extract:SI (match_dup 0) (match_dup 1) (match_dup 2))
		(match_operand 3 "immediate_operand" "i,i")))]
  "TARGET_68020 && TARGET_BITFIELD
   && GET_CODE (operands[3]) == CONST_INT
   && (INTVAL (operands[3]) == -1
       || (GET_CODE (operands[1]) == CONST_INT
           && (~ INTVAL (operands[3]) & ((1 << INTVAL (operands[1]))- 1)) == 0))"
  "*
{
  CC_STATUS_INIT;
  return \"bfchg %0{%b2:%b1}\";
}")

(define_insn ""
  [(set (zero_extract:SI (match_operand:QI 0 "nonimmediate_operand" "+o,d")
			 (match_operand:SI 1 "general_operand" "di,di")
			 (match_operand:SI 2 "general_operand" "di,di"))
	(const_int 0))]
  "TARGET_68020 && TARGET_BITFIELD"
  "*
{
  CC_STATUS_INIT;
  return \"bfclr %0{%b2:%b1}\";
}")

(define_insn ""
  [(set (zero_extract:SI (match_operand:QI 0 "nonimmediate_operand" "+o,d")
			 (match_operand:SI 1 "general_operand" "di,di")
			 (match_operand:SI 2 "general_operand" "di,di"))
	(const_int -1))]
  "TARGET_68020 && TARGET_BITFIELD"
  "*
{
  CC_STATUS_INIT;
  return \"bfset %0{%b2:%b1}\";
}")

(define_insn "insv"
  [(set (zero_extract:SI (match_operand:QI 0 "nonimmediate_operand" "+o,d")
			 (match_operand:SI 1 "general_operand" "di,di")
			 (match_operand:SI 2 "general_operand" "di,di"))
	(match_operand:SI 3 "general_operand" "d,d"))]
  "TARGET_68020 && TARGET_BITFIELD"
  "bfins %3,%0{%b2:%b1}")

;; Now recognize bit field insns that operate on registers
;; (or at least were intended to do so).

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=d")
	(sign_extract:SI (match_operand:SI 1 "nonimmediate_operand" "d")
			 (match_operand:SI 2 "general_operand" "di")
			 (match_operand:SI 3 "general_operand" "di")))]
  "TARGET_68020 && TARGET_BITFIELD"
  "bfexts %1{%b3:%b2},%0")

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=d")
	(zero_extract:SI (match_operand:SI 1 "nonimmediate_operand" "d")
			 (match_operand:SI 2 "general_operand" "di")
			 (match_operand:SI 3 "general_operand" "di")))]
  "TARGET_68020 && TARGET_BITFIELD"
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT)
    {
      if (INTVAL (operands[2]) != 32)
	cc_status.flags |= CC_NOT_NEGATIVE;
    }
  else
    {
      CC_STATUS_INIT;
    }
  return \"bfextu %1{%b3:%b2},%0\";
}")

(define_insn ""
  [(set (zero_extract:SI (match_operand:SI 0 "nonimmediate_operand" "+d")
			 (match_operand:SI 1 "general_operand" "di")
			 (match_operand:SI 2 "general_operand" "di"))
	(const_int 0))]
  "TARGET_68020 && TARGET_BITFIELD"
  "*
{
  CC_STATUS_INIT;
  return \"bfclr %0{%b2:%b1}\";
}")

(define_insn ""
  [(set (zero_extract:SI (match_operand:SI 0 "nonimmediate_operand" "+d")
			 (match_operand:SI 1 "general_operand" "di")
			 (match_operand:SI 2 "general_operand" "di"))
	(const_int -1))]
  "TARGET_68020 && TARGET_BITFIELD"
  "*
{
  CC_STATUS_INIT;
  return \"bfset %0{%b2:%b1}\";
}")

(define_insn ""
  [(set (zero_extract:SI (match_operand:SI 0 "nonimmediate_operand" "+d")
			 (match_operand:SI 1 "general_operand" "di")
			 (match_operand:SI 2 "general_operand" "di"))
	(match_operand:SI 3 "general_operand" "d"))]
  "TARGET_68020 && TARGET_BITFIELD"
  "*
{
#if 0
  /* These special cases are now recognized by a specific pattern.  */
  if (GET_CODE (operands[1]) == CONST_INT && GET_CODE (operands[2]) == CONST_INT
      && INTVAL (operands[1]) == 16 && INTVAL (operands[2]) == 16)
    return \"move%.w %3,%0\";
  if (GET_CODE (operands[1]) == CONST_INT && GET_CODE (operands[2]) == CONST_INT
      && INTVAL (operands[1]) == 24 && INTVAL (operands[2]) == 8)
    return \"move%.b %3,%0\";
#endif
  return \"bfins %3,%0{%b2:%b1}\";
}")

;; Special patterns for optimizing bit-field instructions.

(define_insn ""
  [(set (cc0)
	(zero_extract:SI (match_operand:QI 0 "memory_operand" "o")
			 (match_operand:SI 1 "general_operand" "di")
			 (match_operand:SI 2 "general_operand" "di")))]
  "TARGET_68020 && TARGET_BITFIELD
   && GET_CODE (operands[1]) == CONST_INT"
  "*
{
  if (operands[1] == const1_rtx
      && GET_CODE (operands[2]) == CONST_INT)
    {    
      int width = GET_CODE (operands[0]) == REG ? 31 : 7;
      return output_btst (operands,
			  gen_rtx (CONST_INT, VOIDmode,
				   width - INTVAL (operands[2])),
			  operands[0],
			  insn, 1000);
      /* Pass 1000 as SIGNPOS argument so that btst will
         not think we are testing the sign bit for an `and'
	 and assume that nonzero implies a negative result.  */
    }
  if (INTVAL (operands[1]) != 32)
    cc_status.flags = CC_NOT_NEGATIVE;
  return \"bftst %0{%b2:%b1}\";
}")

  
;;; now handle the register cases
(define_insn ""
  [(set (cc0)
	(zero_extract:SI (match_operand:SI 0 "nonimmediate_operand" "d")
			 (match_operand:SI 1 "general_operand" "di")
			 (match_operand:SI 2 "general_operand" "di")))]
  "TARGET_68020 && TARGET_BITFIELD
   && GET_CODE (operands[1]) == CONST_INT"
  "*
{
  if (operands[1] == const1_rtx
      && GET_CODE (operands[2]) == CONST_INT)
    {    
      int width = GET_CODE (operands[0]) == REG ? 31 : 7;
      return output_btst (operands,
			  gen_rtx (CONST_INT, VOIDmode,
				   width - INTVAL (operands[2])),
			  operands[0],
			  insn, 1000);
      /* Pass 1000 as SIGNPOS argument so that btst will
         not think we are testing the sign bit for an `and'
	 and assume that nonzero implies a negative result.  */
    }
  if (INTVAL (operands[1]) != 32)
    cc_status.flags = CC_NOT_NEGATIVE;
  return \"bftst %0{%b2:%b1}\";
}")

(define_insn "seq"
  [(set (match_operand:QI 0 "general_operand" "=d")
	(eq:QI (cc0) (const_int 0)))]
  ""
  "*
  cc_status = cc_prev_status;
  OUTPUT_JUMP (\"seq %0\", \"fseq %0\", \"seq %0\");
")

(define_insn "sne"
  [(set (match_operand:QI 0 "general_operand" "=d")
	(ne:QI (cc0) (const_int 0)))]
  ""
  "*
  cc_status = cc_prev_status;
  OUTPUT_JUMP (\"sne %0\", \"fsne %0\", \"sne %0\");
")

(define_insn "sgt"
  [(set (match_operand:QI 0 "general_operand" "=d")
	(gt:QI (cc0) (const_int 0)))]
  ""
  "*
  cc_status = cc_prev_status;
  OUTPUT_JUMP (\"sgt %0\", \"fsgt %0\", 0);
")

(define_insn "sgtu"
  [(set (match_operand:QI 0 "general_operand" "=d")
	(gtu:QI (cc0) (const_int 0)))]
  ""
  "* cc_status = cc_prev_status;
     return \"shi %0\"; ")

(define_insn "slt"
  [(set (match_operand:QI 0 "general_operand" "=d")
	(lt:QI (cc0) (const_int 0)))]
  ""
  "* cc_status = cc_prev_status;
     OUTPUT_JUMP (\"slt %0\", \"fslt %0\", \"smi %0\"); ")

(define_insn "sltu"
  [(set (match_operand:QI 0 "general_operand" "=d")
	(ltu:QI (cc0) (const_int 0)))]
  ""
  "* cc_status = cc_prev_status;
     return \"scs %0\"; ")

(define_insn "sge"
  [(set (match_operand:QI 0 "general_operand" "=d")
	(ge:QI (cc0) (const_int 0)))]
  ""
  "* cc_status = cc_prev_status;
     OUTPUT_JUMP (\"sge %0\", \"fsge %0\", \"spl %0\"); ")

(define_insn "sgeu"
  [(set (match_operand:QI 0 "general_operand" "=d")
	(geu:QI (cc0) (const_int 0)))]
  ""
  "* cc_status = cc_prev_status;
     return \"scc %0\"; ")

(define_insn "sle"
  [(set (match_operand:QI 0 "general_operand" "=d")
	(le:QI (cc0) (const_int 0)))]
  ""
  "*
  cc_status = cc_prev_status;
  OUTPUT_JUMP (\"sle %0\", \"fsle %0\", 0);
")

(define_insn "sleu"
  [(set (match_operand:QI 0 "general_operand" "=d")
	(leu:QI (cc0) (const_int 0)))]
  ""
  "* cc_status = cc_prev_status;
     return \"sls %0\"; ")

;; Basic conditional jump instructions.

(define_insn "beq"
  [(set (pc)
	(if_then_else (eq (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
#ifdef MOTOROLA
  OUTPUT_JUMP (\"jbeq %l0\", \"fbeq %l0\", \"jbeq %l0\");
#else
  OUTPUT_JUMP (\"jeq %l0\", \"fjeq %l0\", \"jeq %l0\");
#endif
}")

(define_insn "bne"
  [(set (pc)
	(if_then_else (ne (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
#ifdef MOTOROLA
  OUTPUT_JUMP (\"jbne %l0\", \"fbne %l0\", \"jbne %l0\");
#else
  OUTPUT_JUMP (\"jne %l0\", \"fjne %l0\", \"jne %l0\");
#endif
}")

(define_insn "bgt"
  [(set (pc)
	(if_then_else (gt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
#ifdef MOTOROLA
  OUTPUT_JUMP (\"jbgt %l0\", \"fbgt %l0\", 0);
#else
  OUTPUT_JUMP (\"jgt %l0\", \"fjgt %l0\", 0);
#endif
")

(define_insn "bgtu"
  [(set (pc)
	(if_then_else (gtu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
#ifdef MOTOROLA
  return \"jbhi %l0\";
#else
  return \"jhi %l0\";
#endif
")

(define_insn "blt"
  [(set (pc)
	(if_then_else (lt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
#ifdef MOTOROLA
  OUTPUT_JUMP (\"jblt %l0\", \"fblt %l0\", \"jbmi %l0\");
#else
  OUTPUT_JUMP (\"jlt %l0\", \"fjlt %l0\", \"jmi %l0\");
#endif
")

(define_insn "bltu"
  [(set (pc)
	(if_then_else (ltu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
#ifdef MOTOROLA
  return \"jbcs %l0\";
#else
  return \"jcs %l0\";
#endif
")

(define_insn "bge"
  [(set (pc)
	(if_then_else (ge (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
#ifdef MOTOROLA
  OUTPUT_JUMP (\"jbge %l0\", \"fbge %l0\", \"jbpl %l0\");
#else
  OUTPUT_JUMP (\"jge %l0\", \"fjge %l0\", \"jpl %l0\");
#endif
")

(define_insn "bgeu"
  [(set (pc)
	(if_then_else (geu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
#ifdef MOTOROLA
  return \"jbcc %l0\";
#else
  return \"jcc %l0\";
#endif
")

(define_insn "ble"
  [(set (pc)
	(if_then_else (le (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
#ifdef MOTOROLA
  OUTPUT_JUMP (\"jble %l0\", \"fble %l0\", 0);
#else
  OUTPUT_JUMP (\"jle %l0\", \"fjle %l0\", 0);
#endif
")

(define_insn "bleu"
  [(set (pc)
	(if_then_else (leu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
#ifdef MOTOROLA
  return \"jbls %l0\";
#else
  return \"jls %l0\";
#endif
")

;; Negated conditional jump instructions.

(define_insn ""
  [(set (pc)
	(if_then_else (eq (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
#ifdef MOTOROLA
  OUTPUT_JUMP (\"jbne %l0\", \"fbne %l0\", \"jbne %l0\");
#else
  OUTPUT_JUMP (\"jne %l0\", \"fjne %l0\", \"jne %l0\");
#endif
}")

(define_insn ""
  [(set (pc)
	(if_then_else (ne (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
#ifdef MOTOROLA
  OUTPUT_JUMP (\"jbeq %l0\", \"fbeq %l0\", \"jbeq %l0\");
#else
  OUTPUT_JUMP (\"jeq %l0\", \"fjeq %l0\", \"jeq %l0\");
#endif
}")

(define_insn ""
  [(set (pc)
	(if_then_else (gt (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
#ifdef MOTOROLA
  OUTPUT_JUMP (\"jble %l0\", \"fbngt %l0\", 0);
#else
  OUTPUT_JUMP (\"jle %l0\", \"fjngt %l0\", 0);
#endif
")

(define_insn ""
  [(set (pc)
	(if_then_else (gtu (cc0)
			   (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
#ifdef MOTOROLA
  return \"jbls %l0\";
#else
  return \"jls %l0\";
#endif
")

(define_insn ""
  [(set (pc)
	(if_then_else (lt (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
#ifdef MOTOROLA
  OUTPUT_JUMP (\"jbge %l0\", \"fbnlt %l0\", \"jbpl %l0\");
#else
  OUTPUT_JUMP (\"jge %l0\", \"fjnlt %l0\", \"jpl %l0\");
#endif
")

(define_insn ""
  [(set (pc)
	(if_then_else (ltu (cc0)
			   (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
#ifdef MOTOROLA
  return \"jbcc %l0\";
#else
  return \"jcc %l0\";
#endif
")

(define_insn ""
  [(set (pc)
	(if_then_else (ge (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
#ifdef MOTOROLA
  OUTPUT_JUMP (\"jblt %l0\", \"fbnge %l0\", \"jbmi %l0\");
#else
  OUTPUT_JUMP (\"jlt %l0\", \"fjnge %l0\", \"jmi %l0\");
#endif
")

(define_insn ""
  [(set (pc)
	(if_then_else (geu (cc0)
			   (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
#ifdef MOTOROLA
  return \"jbcs %l0\";
#else
  return \"jcs %l0\";
#endif
")

(define_insn ""
  [(set (pc)
	(if_then_else (le (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
#ifdef MOTOROLA
  OUTPUT_JUMP (\"jbgt %l0\", \"fbnle %l0\", 0);
#else
  OUTPUT_JUMP (\"jgt %l0\", \"fjnle %l0\", 0);
#endif
")

(define_insn ""
  [(set (pc)
	(if_then_else (leu (cc0)
			   (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
#ifdef MOTOROLA
  return \"jbhi %l0\";
#else
  return \"jhi %l0\";
#endif
")

;; Unconditional and other jump instructions
(define_insn "jump"
  [(set (pc)
	(label_ref (match_operand 0 "" "")))]
  ""
  "*
#ifdef MOTOROLA
  return \"jbra %l0\";
#else
  return \"jra %l0\";
#endif
")

;; We support two different ways of handling dispatch tables.
;; The NeXT uses absolute tables, and other machines use relative.
;; This define_expand can generate either kind.
(define_expand "tablejump"
  [(parallel [(set (pc) (match_operand 0 "" ""))
	      (use (label_ref (match_operand 1 "" "")))])]
  ""
  "
{
#ifdef CASE_VECTOR_PC_RELATIVE
    operands[0] = gen_rtx (PLUS, SImode, pc_rtx, operands[0]);
#endif
}")

;; Jump to variable address from dispatch table of absolute addresses.
(define_insn ""
  [(set (pc) (match_operand:SI 0 "register_operand" "a"))
   (use (label_ref (match_operand 1 "" "")))]
  ""
  "*
#ifdef MOTOROLA
  return \"jmp (%0)\";
#else
  return \"jmp %0@\";
#endif
")

;; Jump to variable address from dispatch table of relative addresses.
(define_insn ""
  [(set (pc)
	(plus:SI (pc) (match_operand:HI 0 "register_operand" "r")))
   (use (label_ref (match_operand 1 "" "")))]
  ""
  "*
#ifdef ASM_RETURN_CASE_JUMP
 ASM_RETURN_CASE_JUMP;
#else
#ifdef SGS
#ifdef ASM_OUTPUT_CASE_LABEL
  return \"jmp 6(%%pc,%0.w)\";
#else
#ifdef CRDS
  return \"jmp 2(pc,%0.w)\";
#else
  return \"jmp 2(%%pc,%0.w)\";
#endif  /* end !CRDS */
#endif
#else /* not SGS */
#ifdef MOTOROLA
  return \"jmp (2,pc,%0.w)\";
#else
  return \"jmp pc@(2,%0:w)\";
#endif
#endif
#endif
")

;; Decrement-and-branch insns.
(define_insn ""
  [(set (pc)
	(if_then_else
	 (ne (match_operand:HI 0 "general_operand" "+g")
	     (const_int 0))
	 (label_ref (match_operand 1 "" ""))
	 (pc)))
   (set (match_dup 0)
	(plus:HI (match_dup 0)
		 (const_int -1)))]
  ""
  "*
{
  CC_STATUS_INIT;
  if (DATA_REG_P (operands[0]))
    return \"dbra %0,%l1\";
  if (GET_CODE (operands[0]) == MEM)
    {
#ifdef MOTOROLA
#ifdef NO_ADDSUB_Q
      return \"sub%.w %#1,%0\;jbcc %l1\";
#else
      return \"subq%.w %#1,%0\;jbcc %l1\";
#endif
#else /* not MOTOROLA */
      return \"subqw %#1,%0\;jcc %l1\";
#endif
    }
#ifdef MOTOROLA
#ifdef SGS_CMP_ORDER
#ifdef NO_ADDSUB_Q
  return \"sub%.w %#1,%0\;cmp%.w %0,%#-1\;jbne %l1\";
#else
  return \"subq%.w %#1,%0\;cmp%.w %0,%#-1\;jbne %l1\";
#endif
#else /* not SGS_CMP_ORDER */
  return \"subq%.w %#1,%0\;cmp%.w %#-1,%0\;jbne %l1\";
#endif
#else /* not MOTOROLA */
  return \"subqw %#1,%0\;cmpw %#-1,%0\;jne %l1\";
#endif
}")

(define_insn ""
  [(set (pc)
	(if_then_else
	 (ne (match_operand:SI 0 "general_operand" "+g")
	     (const_int 0))
	 (label_ref (match_operand 1 "" ""))
	 (pc)))
   (set (match_dup 0)
	(plus:SI (match_dup 0)
		 (const_int -1)))]
  ""
  "*
{
  CC_STATUS_INIT;
#ifdef MOTOROLA
#ifdef NO_ADDSUB_Q
  if (DATA_REG_P (operands[0]))
    return \"dbra %0,%l1\;clr%.w %0\;sub%.l %#1,%0\;jbcc %l1\";
  if (GET_CODE (operands[0]) == MEM)
    return \"sub%.l %#1,%0\;jbcc %l1\";
#else
  if (DATA_REG_P (operands[0]))
    return \"dbra %0,%l1\;clr%.w %0\;subq%.l %#1,%0\;jbcc %l1\";
  if (GET_CODE (operands[0]) == MEM)
    return \"subq%.l %#1,%0\;jbcc %l1\";
#endif /* NO_ADDSUB_Q */
#ifdef SGS_CMP_ORDER
#ifdef NO_ADDSUB_Q
  return \"sub.l %#1,%0\;cmp.l %0,%#-1\;jbne %l1\";
#else
  return \"subq.l %#1,%0\;cmp.l %0,%#-1\;jbne %l1\";
#endif
#else /* not SGS_CMP_ORDER */
  return \"subq.l %#1,%0\;cmp.l %#-1,%0\;jbne %l1\";
#endif /* not SGS_CMP_ORDER */
#else /* not MOTOROLA */
  if (DATA_REG_P (operands[0]))
    return \"dbra %0,%l1\;clr%.w %0\;subql %#1,%0\;jcc %l1\";
  if (GET_CODE (operands[0]) == MEM)
    return \"subql %#1,%0\;jcc %l1\";
  return \"subql %#1,%0\;cmpl %#-1,%0\;jne %l1\";
#endif /* not MOTOROLA */
}")

;; Two dbra patterns that use REG_NOTES info generated by strength_reduce.

(define_insn ""
  [(set (pc)
	(if_then_else
	  (ge (plus:HI (match_operand:HI 0 "general_operand" "+g")
		       (const_int -1))
	      (const_int 0))
	  (label_ref (match_operand 1 "" ""))
	  (pc)))
   (set (match_dup 0)
	(plus:HI (match_dup 0)
		 (const_int -1)))]
  "find_reg_note (insn, REG_NONNEG, 0)"
  "*
{
  CC_STATUS_INIT;
#ifdef MOTOROLA
#ifdef NO_ADDSUB_Q
  if (DATA_REG_P (operands[0]))
    return \"dbra %0,%l1\";
  if (GET_CODE (operands[0]) == MEM)
    return \"sub%.w %#1,%0\;jbcc %l1\";
#else
  if (DATA_REG_P (operands[0]))
    return \"dbra %0,%l1\";
  if (GET_CODE (operands[0]) == MEM)
    return \"subq%.w %#1,%0\;jbcc %l1\";
#endif
#ifdef SGS_CMP_ORDER
#ifdef NO_ADDSUB_Q
  return \"sub.w %#1,%0\;cmp.w %0,%#-1\;jbne %l1\";
#else
  return \"subq.w %#1,%0\;cmp.w %0,%#-1\;jbne %l1\";
#endif
#else /* not SGS_CMP_ORDER */
  return \"subq.w %#1,%0\;cmp.w %#-1,%0\;jbne %l1\";
#endif /* not SGS_CMP_ORDER */
#else /* not MOTOROLA */
  if (DATA_REG_P (operands[0]))
    return \"dbra %0,%l1\";
  if (GET_CODE (operands[0]) == MEM)
    return \"subqw %#1,%0\;jcc %l1\";
  return \"subqw %#1,%0\;cmpw %#-1,%0\;jne %l1\";
#endif /* not MOTOROLA */
}")

(define_insn "decrement_and_branch_until_zero"
  [(set (pc)
	(if_then_else
	  (ge (plus:SI (match_operand:SI 0 "general_operand" "+g")
		       (const_int -1))
	      (const_int 0))
	  (label_ref (match_operand 1 "" ""))
	  (pc)))
   (set (match_dup 0)
	(plus:SI (match_dup 0)
		 (const_int -1)))]
  "find_reg_note (insn, REG_NONNEG, 0)"
  "*
{
  CC_STATUS_INIT;
#ifdef MOTOROLA
#ifdef NO_ADDSUB_Q
  if (DATA_REG_P (operands[0]))
    return \"dbra %0,%l1\;clr%.w %0\;sub%.l %#1,%0\;jbcc %l1\";
  if (GET_CODE (operands[0]) == MEM)
    return \"sub%.l %#1,%0\;jbcc %l1\";
#else
  if (DATA_REG_P (operands[0]))
    return \"dbra %0,%l1\;clr%.w %0\;subq%.l %#1,%0\;jbcc %l1\";
  if (GET_CODE (operands[0]) == MEM)
    return \"subq%.l %#1,%0\;jbcc %l1\";
#endif
#ifdef SGS_CMP_ORDER
#ifdef NO_ADDSUB_Q
  return \"sub.l %#1,%0\;cmp.l %0,%#-1\;jbne %l1\";
#else
  return \"subq.l %#1,%0\;cmp.l %0,%#-1\;jbne %l1\";
#endif
#else /* not SGS_CMP_ORDER */
  return \"subq.l %#1,%0\;cmp.l %#-1,%0\;jbne %l1\";
#endif /* not SGS_CMP_ORDER */
#else /* not MOTOROLA */
  if (DATA_REG_P (operands[0]))
    return \"dbra %0,%l1\;clr%.w %0\;subql %#1,%0\;jcc %l1\";
  if (GET_CODE (operands[0]) == MEM)
    return \"subql %#1,%0\;jcc %l1\";
  return \"subql %#1,%0\;cmpl %#-1,%0\;jne %l1\";
#endif /* not MOTOROLA */
}")


;; PIC calls are handled by loading the address of the function into a 
;; register (via movsi), then emitting a register indirect call using
;; the "jsr" function call syntax.
;;
;; It is important to note that the "jsr" syntax is always used for 
;; PIC calls, even on machines in which GCC normally uses the "jbsr"
;; syntax for non-PIC calls.  This keeps at least 1 assembler (Sun)
;; from emitting incorrect code for a PIC call.
;;
;; We have different patterns for PIC calls and non-PIC calls.  The
;; different patterns are only used to choose the right syntax
;; ("jsr" vs "jbsr").
;;
;; On svr4 m68k, PIC stuff is done differently. To be able to support
;; dynamic linker LAZY BINDING, all the procedure calls need to go 
;; through the PLT (Procedure Linkage Table) section in PIC mode. The 
;; svr4 m68k assembler recognizes this syntax: `bsr FUNC@PLTPC' and it 
;; will create the correct relocation entry (R_68K_PLT32) for `FUNC', 
;; that tells the linker editor to create an entry for `FUNC' in PLT
;; section at link time. However, all global objects reference are still
;; done by using `OBJ@GOT'. So, the goal here is to output the function 
;; call operand as `FUNC@PLTPC', but output object operand as `OBJ@GOT'. 
;; We need to have a way to differentiate these two different operands.
;;
;; The strategy I use here is to use SYMBOL_REF_FLAG to differentiate 
;; these two different operands. The macro LEGITIMATE_PIC_OPERAND_P needs
;; to be changed to recognize function calls symbol_ref operand as a legal 
;; PIC operand (by checking whether SYMBOL_REF_FLAG is set). This will 
;; avoid the compiler to load this symbol_ref operand into a register. 
;; Remember, the operand "foo@PLTPC" cannot be called via jsr directly 
;; since the value is a PC relative offset, not a real address.
;;
;; All global objects are treated in the similar way as in SUN3. The only 
;; difference is: on m68k svr4, the reference of such global object needs 
;; to end with a suffix "@GOT" so the assembler and linker know to create
;; an entry for it in GOT (Global Offset Table) section. This is done in 
;; m68k.c.

;; Call subroutine with no return value.
(define_expand "call"
  [(call (match_operand:QI 0 "memory_operand" "")
	 (match_operand:SI 1 "general_operand" ""))]
  ;; Operand 1 not really used on the m68000.

  ""
  "
{
  if (flag_pic && GET_CODE (XEXP (operands[0], 0)) == SYMBOL_REF)
#ifdef MOTOROLA
    SYMBOL_REF_FLAG (XEXP (operands[0], 0)) = 1;
#else
    operands[0] = gen_rtx (MEM, GET_MODE (operands[0]),
			   force_reg (Pmode, XEXP (operands[0], 0)));
#endif
}")

;; This is a normal call sequence.
(define_insn ""
  [(call (match_operand:QI 0 "memory_operand" "o")
	 (match_operand:SI 1 "general_operand" "g"))]
  ;; Operand 1 not really used on the m68000.

  "! flag_pic"
  "*
#ifdef MOTOROLA
  return \"jsr %0\";
#else
  return \"jbsr %0\";
#endif
")

;; This is a PIC call sequence.
(define_insn ""
  [(call (match_operand:QI 0 "memory_operand" "o")
	 (match_operand:SI 1 "general_operand" "g"))]
  ;; Operand 1 not really used on the m68000.

  "flag_pic"
  "*
#ifdef HPUX_ASM
  return \"bsr %0\";
#else
#ifdef MOTOROLA
  if (GET_CODE (operands[0]) == MEM 
      && GET_CODE (XEXP (operands[0], 0)) == SYMBOL_REF)
    return \"bsr %0@PLTPC\";
#endif
  return \"jsr %0\";
#endif
")

;; Call subroutine, returning value in operand 0
;; (which must be a hard register).
;; See comments before "call" regarding PIC calls.
(define_expand "call_value"
  [(set (match_operand 0 "" "")
	(call (match_operand:QI 1 "memory_operand" "")
     (match_operand:SI 2 "general_operand" "")))]
  ;; Operand 2 not really used on the m68000.
  ""
  "
{
  if (flag_pic && GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF)
#ifdef MOTOROLA
    SYMBOL_REF_FLAG (XEXP (operands[1], 0)) = 1;
#else
    operands[1] = gen_rtx (MEM, GET_MODE (operands[1]),
			   force_reg (Pmode, XEXP (operands[1], 0)));
#endif
}")

;; This is a normal call_value
(define_insn ""
  [(set (match_operand 0 "" "=rf")
	(call (match_operand:QI 1 "memory_operand" "o")
	      (match_operand:SI 2 "general_operand" "g")))]
  ;; Operand 2 not really used on the m68000.
  "! flag_pic"
  "*
#ifdef MOTOROLA
  return \"jsr %1\";
#else
  return \"jbsr %1\";
#endif
")

;; This is a PIC call_value
(define_insn ""
  [(set (match_operand 0 "" "=rf")
	(call (match_operand:QI 1 "memory_operand" "o")
	      (match_operand:SI 2 "general_operand" "g")))]
  ;; Operand 2 not really used on the m68000.
  "flag_pic"
  "*
#ifdef HPUX_ASM
  return \"bsr %1\";
#else
#ifdef MOTOROLA
  if (GET_CODE (operands[1]) == MEM 
      && GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF)
    return \"bsr %1@PLTPC\";
#endif
  return \"jsr %1\";
#endif
")

;; Call subroutine returning any type.

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

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

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

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

  DONE;
}")

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

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

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

(define_insn "probe"
 [(reg:SI 15)]
 "NEED_PROBE"
 "*
{
  operands[0] = gen_rtx (PLUS, SImode, stack_pointer_rtx,
			 gen_rtx (CONST_INT, VOIDmode, NEED_PROBE));
  return \"tstl %a0\";
}")

;; Used for frameless functions which save no regs and allocate no locals.
(define_insn "return"
  [(return)]
  "USE_RETURN_INSN"
  "*
{
  if (current_function_pops_args == 0)
    return \"rts\";
  operands[0] = gen_rtx (CONST_INT, VOIDmode, current_function_pops_args);
  return \"rtd %0\";
}")

(define_insn "indirect_jump"
  [(set (pc) (match_operand:SI 0 "address_operand" "p"))]
  ""
  "jmp %a0")

;; This should not be used unless the add/sub insns can't be.

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=a")
	(match_operand:QI 1 "address_operand" "p"))]
  ""
  "lea %a1,%0")

;; This is the first machine-dependent peephole optimization.
;; It is useful when a floating value is returned from a function call
;; and then is moved into an FP register.
;; But it is mainly intended to test the support for these optimizations.

(define_peephole
  [(set (reg:SI 15) (plus:SI (reg:SI 15) (const_int 4)))
   (set (match_operand:DF 0 "register_operand" "=f")
	(match_operand:DF 1 "register_operand" "ad"))]
  "FP_REG_P (operands[0]) && ! FP_REG_P (operands[1])"
  "*
{
  rtx xoperands[2];
  xoperands[1] = gen_rtx (REG, SImode, REGNO (operands[1]) + 1);
  output_asm_insn (\"move%.l %1,%@\", xoperands);
  output_asm_insn (\"move%.l %1,%-\", operands);
  return \"fmove%.d %+,%0\";
}
")

;; Optimize a stack-adjust followed by a push of an argument.
;; This is said to happen frequently with -msoft-float
;; when there are consecutive library calls.

(define_peephole
  [(set (reg:SI 15) (plus:SI (reg:SI 15)
			     (match_operand:SI 0 "immediate_operand" "n")))
   (set (match_operand:SF 1 "push_operand" "=m")
	(match_operand:SF 2 "general_operand" "rmfF"))]
  "GET_CODE (operands[0]) == CONST_INT && INTVAL (operands[0]) >= 4
   && ! reg_mentioned_p (stack_pointer_rtx, operands[2])"
  "*
{
  if (INTVAL (operands[0]) > 4)
    {
      rtx xoperands[2];
      xoperands[0] = stack_pointer_rtx;
      xoperands[1] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[0]) - 4);
#ifndef NO_ADDSUB_Q
      if (INTVAL (xoperands[1]) <= 8)
        output_asm_insn (\"addq%.w %1,%0\", xoperands);
      else if (INTVAL (xoperands[1]) <= 16 && TARGET_68020)
	{
	  xoperands[1] = gen_rtx (CONST_INT, VOIDmode, 
				  INTVAL (xoperands[1]) - 8);
	  output_asm_insn (\"addq%.w %#8,%0\;addq%.w %1,%0\", xoperands);
	}
      else
#endif
        if (INTVAL (xoperands[1]) <= 0x7FFF)
          output_asm_insn (\"add%.w %1,%0\", xoperands);
      else
        output_asm_insn (\"add%.l %1,%0\", xoperands);
    }
  if (FP_REG_P (operands[2]))
    return \"fmove%.s %2,%@\";
  return \"move%.l %2,%@\";
}")

;; Speed up stack adjust followed by a fullword fixedpoint push.

(define_peephole
  [(set (reg:SI 15) (plus:SI (reg:SI 15)
			     (match_operand:SI 0 "immediate_operand" "n")))
   (set (match_operand:SI 1 "push_operand" "=m")
	(match_operand:SI 2 "general_operand" "g"))]
  "GET_CODE (operands[0]) == CONST_INT && INTVAL (operands[0]) >= 4
   && ! reg_mentioned_p (stack_pointer_rtx, operands[2])"
  "*
{
  if (INTVAL (operands[0]) > 4)
    {
      rtx xoperands[2];
      xoperands[0] = stack_pointer_rtx;
      xoperands[1] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[0]) - 4);
#ifndef NO_ADDSUB_Q
      if (INTVAL (xoperands[1]) <= 8)
        output_asm_insn (\"addq%.w %1,%0\", xoperands);
      else if (INTVAL (xoperands[1]) <= 16 && TARGET_68020)
	{
	  xoperands[1] = gen_rtx (CONST_INT, VOIDmode, 
				  INTVAL (xoperands[1]) - 8);
	  output_asm_insn (\"addq%.w %#8,%0\;addq%.w %1,%0\", xoperands);
	}
      else
#endif
        if (INTVAL (xoperands[1]) <= 0x7FFF)
          output_asm_insn (\"add%.w %1,%0\", xoperands);
      else
        output_asm_insn (\"add%.l %1,%0\", xoperands);
    }
  if (operands[2] == const0_rtx)
    return \"clr%.l %@\";
  return \"move%.l %2,%@\";
}")

;; Speed up pushing a single byte but leaving four bytes of space.

(define_peephole
  [(set (mem:QI (pre_dec:SI (reg:SI 15)))
	(match_operand:QI 1 "general_operand" "dami"))
   (set (reg:SI 15) (minus:SI (reg:SI 15) (const_int 2)))]
  "! reg_mentioned_p (stack_pointer_rtx, operands[1])"
  "*
{
  rtx xoperands[4];

  if (GET_CODE (operands[1]) == REG)
    return \"move%.l %1,%-\";

  xoperands[1] = operands[1];
  xoperands[2]
    = gen_rtx (MEM, QImode,
	       gen_rtx (PLUS, VOIDmode, stack_pointer_rtx,
			gen_rtx (CONST_INT, VOIDmode, 3)));
  xoperands[3] = stack_pointer_rtx;
  output_asm_insn (\"subq%.w %#4,%3\;move%.b %1,%2\", xoperands);
  return \"\";
}")

(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=d")
	(const_int 0))
   (set (strict_low_part (subreg:HI (match_dup 0) 0))
	(match_operand:HI 1 "general_operand" "rmn"))]
  "strict_low_part_peephole_ok (HImode, prev_nonnote_insn (insn), operands[0])"
  "*
{
  if (GET_CODE (operands[1]) == CONST_INT)
    {
      if (operands[1] == const0_rtx
	  && (DATA_REG_P (operands[0])
	      || GET_CODE (operands[0]) == MEM)
	  /* clr insns on 68000 read before writing.
	     This isn't so on the 68010, but we have no alternative for it.  */
	  && (TARGET_68020
	      || !(GET_CODE (operands[0]) == MEM
		   && MEM_VOLATILE_P (operands[0]))))
	return \"clr%.w %0\";
    }
  return \"move%.w %1,%0\";
}")

;; dbCC peepholes
;;
;; Turns
;;   loop:
;;           [ ... ]
;;           jCC label		; abnormal loop termination
;;           dbra dN, loop	; normal loop termination
;;
;; Into
;;   loop:
;;           [ ... ]
;;           dbCC dN, loop
;;           jCC label
;;
;; Which moves the jCC condition outside the inner loop for free.
;;
(define_peephole
  [(set (pc) (if_then_else (match_operator 3 "valid_dbcc_comparison_p"
                             [(cc0) (const_int 0)])
                           (label_ref (match_operand 2 "" ""))
                           (pc)))
   (parallel
    [(set (pc)
	  (if_then_else
	    (ge (plus:HI (match_operand:HI 0 "register_operand" "+d")
		         (const_int -1))
	        (const_int 0))
	    (label_ref (match_operand 1 "" ""))
	    (pc)))
     (set (match_dup 0)
	  (plus:HI (match_dup 0)
		   (const_int -1)))])]
  "DATA_REG_P (operands[0])"
  "*
{
  CC_STATUS_INIT;
  output_dbcc_and_branch (operands);
  return \"\";
}")

(define_peephole
  [(set (pc) (if_then_else (match_operator 3 "valid_dbcc_comparison_p"
                             [(cc0) (const_int 0)])
                           (label_ref (match_operand 2 "" ""))
                           (pc)))
   (parallel
    [(set (pc)
	  (if_then_else
	    (ge (plus:SI (match_operand:SI 0 "register_operand" "+d")
		         (const_int -1))
	        (const_int 0))
	    (label_ref (match_operand 1 "" ""))
	    (pc)))
     (set (match_dup 0)
	  (plus:SI (match_dup 0)
		   (const_int -1)))])]
  "DATA_REG_P (operands[0])"
  "*
{
  CC_STATUS_INIT;
  output_dbcc_and_branch (operands);
  return \"\";
}")


;; FPA multiply and add.
(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=x,y,y")
	(plus:DF (mult:DF (match_operand:DF 1 "general_operand" "%x,dmF,y")
			  (match_operand:DF 2 "general_operand" "xH,y,y"))
		 (match_operand:DF 3 "general_operand" "xH,y,dmF")))]
   "TARGET_FPA"
   "@
    fpma%.d %1,%w2,%w3,%0
    fpma%.d %x1,%x2,%x3,%0
    fpma%.d %x1,%x2,%x3,%0")

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=x,y,y")
	(plus:SF (mult:SF (match_operand:SF 1 "general_operand" "%x,ydmF,y")
			  (match_operand:SF 2 "general_operand" "xH,y,ydmF"))
		 (match_operand:SF 3 "general_operand" "xH,ydmF,ydmF")))]
   "TARGET_FPA"
   "@
    fpma%.s %1,%w2,%w3,%0
    fpma%.s %1,%2,%3,%0
    fpma%.s %1,%2,%3,%0")

;; FPA Multiply and subtract
(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=x,y,y")
	(minus:DF (match_operand:DF 1 "general_operand" "xH,rmF,y")
		  (mult:DF (match_operand:DF 2 "general_operand" "%xH,y,y")
			   (match_operand:DF 3 "general_operand" "x,y,rmF"))))]
  "TARGET_FPA"
  "@
   fpms%.d %3,%w2,%w1,%0
   fpms%.d %x3,%2,%x1,%0
   fpms%.d %x3,%2,%x1,%0")

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=x,y,y")
	(minus:SF (match_operand:SF 1 "general_operand" "xH,rmF,yrmF")
		  (mult:SF (match_operand:SF 2 "general_operand" "%xH,rmF,y")
			   (match_operand:SF 3 "general_operand" "x,y,yrmF"))))]
  "TARGET_FPA"
  "@
   fpms%.s %3,%w2,%w1,%0
   fpms%.s %3,%2,%1,%0
   fpms%.s %3,%2,%1,%0")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=x,y,y")
	(minus:DF (mult:DF (match_operand:DF 1 "general_operand" "%xH,y,y")
			   (match_operand:DF 2 "general_operand" "x,y,rmF"))
		  (match_operand:DF 3 "general_operand" "xH,rmF,y")))]
  "TARGET_FPA"
  "@
   fpmr%.d %2,%w1,%w3,%0
   fpmr%.d %x2,%1,%x3,%0
   fpmr%.d %x2,%1,%x3,%0")

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=x,y,y")
	(minus:SF (mult:SF (match_operand:SF 1 "general_operand" "%xH,rmF,y")
			   (match_operand:SF 2 "general_operand" "x,y,yrmF"))
		  (match_operand:SF 3 "general_operand" "xH,rmF,yrmF")))]
  "TARGET_FPA"
  "@
   fpmr%.s %2,%w1,%w3,%0
   fpmr%.s %x2,%1,%x3,%0
   fpmr%.s %x2,%1,%x3,%0")

;; FPA Add and multiply
(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=x,y,y")
	(mult:DF (plus:DF (match_operand:DF 1 "general_operand" "%xH,y,y")
			  (match_operand:DF 2 "general_operand" "x,y,rmF"))
		 (match_operand:DF 3 "general_operand" "xH,rmF,y")))]
  "TARGET_FPA"
  "@
   fpam%.d %2,%w1,%w3,%0
   fpam%.d %x2,%1,%x3,%0
   fpam%.d %x2,%1,%x3,%0")

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=x,y,y")
	(mult:SF (plus:SF (match_operand:SF 1 "general_operand" "%xH,rmF,y")
			  (match_operand:SF 2 "general_operand" "x,y,yrmF"))
		 (match_operand:SF 3 "general_operand" "xH,rmF,yrmF")))]
  "TARGET_FPA"
  "@
   fpam%.s %2,%w1,%w3,%0
   fpam%.s %x2,%1,%x3,%0
   fpam%.s %x2,%1,%x3,%0")

;;FPA Subtract and multiply
(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=x,y,y")
	(mult:DF (minus:DF (match_operand:DF 1 "general_operand" "xH,y,y")
			   (match_operand:DF 2 "general_operand" "x,y,rmF"))
		 (match_operand:DF 3 "general_operand" "xH,rmF,y")))]
  "TARGET_FPA"
  "@
   fpsm%.d %2,%w1,%w3,%0
   fpsm%.d %x2,%1,%x3,%0
   fpsm%.d %x2,%1,%x3,%0")

(define_insn ""
  [(set (match_operand:DF 0 "register_operand" "=x,y,y")
	(mult:DF (match_operand:DF 1 "general_operand" "xH,rmF,y")
		 (minus:DF (match_operand:DF 2 "general_operand" "xH,y,y")
			   (match_operand:DF 3 "general_operand" "x,y,rmF"))))]
  "TARGET_FPA"
  "@
   fpsm%.d %3,%w2,%w1,%0
   fpsm%.d %x3,%2,%x1,%0
   fpsm%.d %x3,%2,%x1,%0")

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=x,y,y")
	(mult:SF (minus:SF (match_operand:SF 1 "general_operand" "xH,rmF,y")
			   (match_operand:SF 2 "general_operand" "x,y,yrmF"))
		 (match_operand:SF 3 "general_operand" "xH,rmF,yrmF")))]
  "TARGET_FPA"
  "@
   fpsm%.s %2,%w1,%w3,%0
   fpsm%.s %x2,%1,%x3,%0
   fpsm%.s %x2,%1,%x3,%0")

(define_insn ""
  [(set (match_operand:SF 0 "register_operand" "=x,y,y")
	(mult:SF (match_operand:SF 1 "general_operand" "xH,rmF,yrmF")
		 (minus:SF (match_operand:SF 2 "general_operand" "xH,rmF,y")
			   (match_operand:SF 3 "general_operand" "x,y,yrmF"))))]
  "TARGET_FPA"
  "@
   fpsm%.s %3,%w2,%w1,%0
   fpsm%.s %x3,%2,%x1,%0
   fpsm%.s %x3,%2,%x1,%0")

(define_insn "tstxf"
  [(set (cc0)
	(match_operand:XF 0 "nonimmediate_operand" "fm"))]
  "TARGET_68881"
  "*
{
  cc_status.flags = CC_IN_68881;
  return \"ftst%.x %0\";
}")


(define_expand "cmpxf"
  [(set (cc0)
	(compare (match_operand:XF 0 "general_operand" "f,mG")
		 (match_operand:XF 1 "general_operand" "fmG,f")))]
  "TARGET_68881"
  "
{
  if (CONSTANT_P (operands[0]))
      operands[0] = force_const_mem (XFmode, operands[0]);
  if (CONSTANT_P (operands[1]))
      operands[1] = force_const_mem (XFmode, operands[1]);
}")

(define_insn ""
  [(set (cc0)
	(compare (match_operand:XF 0 "nonimmediate_operand" "f,mG")
		 (match_operand:XF 1 "nonimmediate_operand" "fmG,f")))]
  "TARGET_68881"
  "*
{
  cc_status.flags = CC_IN_68881;
#ifdef SGS_CMP_ORDER
  if (REG_P (operands[0]))
    {
      if (REG_P (operands[1]))
	return \"fcmp%.x %0,%1\";
      else
        return \"fcmp%.x %0,%f1\";
    }
  cc_status.flags |= CC_REVERSED;
  return \"fcmp%.x %1,%f0\";
#else
  if (REG_P (operands[0]))
    {
      if (REG_P (operands[1]))
	return \"fcmp%.x %1,%0\";
      else
        return \"fcmp%.x %f1,%0\";
    }
  cc_status.flags |= CC_REVERSED;
  return \"fcmp%.x %f0,%1\";
#endif
}")

(define_insn "extendsfxf2"
  [(set (match_operand:XF 0 "general_operand" "=fm,f")
	(float_extend:XF (match_operand:SF 1 "general_operand" "f,m")))]
  "TARGET_68881"
  "*
{
  if (FP_REG_P (operands[0]) && FP_REG_P (operands[1]))
    {
      if (REGNO (operands[0]) == REGNO (operands[1]))
	{
	  /* Extending float to double in an fp-reg is a no-op.
	     NOTICE_UPDATE_CC has already assumed that the
	     cc will be set.  So cancel what it did.  */
	  cc_status = cc_prev_status;
	  return \"\";
	}
      return \"f%$move%.x %1,%0\";
    }
  if (FP_REG_P (operands[0]))
    return \"f%$move%.s %f1,%0\";
  return \"fmove%.x %f1,%0\";
}")


(define_insn "extenddfxf2"
  [(set (match_operand:XF 0 "general_operand" "=fm,f")
	(float_extend:XF
          (match_operand:DF 1 "general_operand" "f,m")))]
  "TARGET_68881"
  "*
{
  if (FP_REG_P (operands[0]) && FP_REG_P (operands[1]))
    {
      if (REGNO (operands[0]) == REGNO (operands[1]))
	{
	  /* Extending float to double in an fp-reg is a no-op.
	     NOTICE_UPDATE_CC has already assumed that the
	     cc will be set.  So cancel what it did.  */
	  cc_status = cc_prev_status;
	  return \"\";
	}
      return \"fmove%.x %1,%0\";
    }
  if (FP_REG_P (operands[0]))
    return \"f%&move%.d %f1,%0\";
  return \"fmove%.x %f1,%0\";
}")

(define_insn "truncxfdf2"
  [(set (match_operand:DF 0 "general_operand" "=m,!r")
	(float_truncate:DF
          (match_operand:XF 1 "general_operand" "f,f")))]
  "TARGET_68881"
  "*
{
  if (REG_P (operands[0]))
    {
      output_asm_insn (\"fmove%.d %f1,%-\;move%.l %+,%0\", operands);
      operands[0] = gen_rtx (REG, SImode, REGNO (operands[0]) + 1);
      return \"move%.l %+,%0\";
    }
  return \"fmove%.d %f1,%0\";
}")
 
(define_insn "truncxfsf2"
  [(set (match_operand:SF 0 "general_operand" "=dm")
	(float_truncate:SF
	  (match_operand:XF 1 "general_operand" "f")))]
  "TARGET_68881"
  "fmove%.s %f1,%0")

(define_insn "floatsixf2"
  [(set (match_operand:XF 0 "general_operand" "=f")
	(float:XF (match_operand:SI 1 "general_operand" "dmi")))]
  "TARGET_68881"
  "fmove%.l %1,%0")

(define_insn "floathixf2"
  [(set (match_operand:XF 0 "general_operand" "=f")
	(float:XF (match_operand:HI 1 "general_operand" "dmn")))]
  "TARGET_68881"
  "fmove%.w %1,%0")

(define_insn "floatqixf2"
  [(set (match_operand:XF 0 "general_operand" "=f")
	(float:XF (match_operand:QI 1 "general_operand" "dmn")))]
  "TARGET_68881"
  "fmove%.b %1,%0")

(define_insn "ftruncxf2"
  [(set (match_operand:XF 0 "general_operand" "=f")
	(fix:XF (match_operand:XF 1 "general_operand" "fFm")))]
  "TARGET_68881"
  "*
{
  if (FP_REG_P (operands[1]))
    return \"fintrz%.x %f1,%0\";
  return \"fintrz%.x %f1,%0\";
}")

(define_insn "fixxfqi2"
  [(set (match_operand:QI 0 "general_operand" "=dm")
	(fix:QI (match_operand:XF 1 "general_operand" "f")))]
  "TARGET_68881"
  "fmove%.b %1,%0")

(define_insn "fixxfhi2"
  [(set (match_operand:HI 0 "general_operand" "=dm")
	(fix:HI (match_operand:XF 1 "general_operand" "f")))]
  "TARGET_68881"
  "fmove%.w %1,%0")

(define_insn "fixxfsi2"
  [(set (match_operand:SI 0 "general_operand" "=dm")
	(fix:SI (match_operand:XF 1 "general_operand" "f")))]
  "TARGET_68881"
  "fmove%.l %1,%0")

(define_expand "addxf3"
  [(set (match_operand:XF 0 "general_operand" "")
	(plus:XF (match_operand:XF 1 "general_operand" "")
		 (match_operand:XF 2 "general_operand" "")))]
  "TARGET_68881"
  "
{
  if (CONSTANT_P (operands[1]))
    operands[1] = force_const_mem (XFmode, operands[1]);
  if (CONSTANT_P (operands[2]))
    operands[2] = force_const_mem (XFmode, operands[2]);
}")

(define_insn ""
  [(set (match_operand:XF 0 "general_operand" "=f")
	(plus:XF (match_operand:XF 1 "nonimmediate_operand" "%0")
		 (match_operand:XF 2 "nonimmediate_operand" "fmG")))]
  "TARGET_68881"
  "*
{
  if (REG_P (operands[2]))
    return \"fadd%.x %2,%0\";
  return \"fadd%.x %f2,%0\";
}")

(define_expand "subxf3"
  [(set (match_operand:XF 0 "general_operand" "")
	(minus:XF (match_operand:XF 1 "general_operand" "")
		 (match_operand:XF 2 "general_operand" "")))]
  "TARGET_68881"
  "
{
  if (CONSTANT_P (operands[1]))
    operands[1] = force_const_mem (XFmode, operands[1]);
  if (CONSTANT_P (operands[2]))
    operands[2] = force_const_mem (XFmode, operands[2]);
}")

(define_insn ""
  [(set (match_operand:XF 0 "general_operand" "=f")
	(minus:XF (match_operand:XF 1 "nonimmediate_operand" "0")
		 (match_operand:XF 2 "nonimmediate_operand" "fmG")))]
  "TARGET_68881"
  "*
{
  if (REG_P (operands[2]))
    return \"fsub%.x %2,%0\";
  return \"fsub%.x %f2,%0\";
}")

(define_expand "mulxf3"
  [(set (match_operand:XF 0 "general_operand" "")
	(mult:XF (match_operand:XF 1 "general_operand" "")
		(match_operand:XF 2 "general_operand" "")))]
  "TARGET_68881"
  "
{
  if (CONSTANT_P (operands[1]))
    operands[1] = force_const_mem (XFmode, operands[1]);
  if (CONSTANT_P (operands[2]))
    operands[2] = force_const_mem (XFmode, operands[2]);
}")

(define_insn ""
  [(set (match_operand:XF 0 "general_operand" "=f")
	(mult:XF (match_operand:XF 1 "nonimmediate_operand" "%0")
		(match_operand:XF 2 "nonimmediate_operand" "fmG")))]
  "TARGET_68881"
  "*
{
  if (REG_P (operands[2]))
    return \"fmul%.x %2,%0\";
  return \"fmul%.x %f2,%0\";
}")

(define_expand "divxf3"
  [(set (match_operand:XF 0 "general_operand" "")
	(div:XF (match_operand:XF 1 "general_operand" "")
		(match_operand:XF 2 "general_operand" "")))]
  "TARGET_68881"
  "
{
  if (CONSTANT_P (operands[1]))
    operands[1] = force_const_mem (XFmode, operands[1]);
  if (CONSTANT_P (operands[2]))
    operands[2] = force_const_mem (XFmode, operands[2]);
}")

(define_insn ""
  [(set (match_operand:XF 0 "general_operand" "=f")
	(div:XF (match_operand:XF 1 "nonimmediate_operand" "0")
		(match_operand:XF 2 "nonimmediate_operand" "fmG")))]
  "TARGET_68881"
  "*
{
  if (REG_P (operands[2]))
    return \"fdiv%.x %2,%0\";
  return \"fdiv%.x %f2,%0\";
}")

(define_insn "negxf2"
  [(set (match_operand:XF 0 "general_operand" "=f")
	(neg:XF (match_operand:XF 1 "nonimmediate_operand" "fmF")))]
  "TARGET_68881"
  "*
{
  if (REG_P (operands[1]) && ! DATA_REG_P (operands[1]))
    return \"fneg%.x %1,%0\";
  return \"fneg%.x %f1,%0\";
}")

(define_insn "absxf2"
  [(set (match_operand:XF 0 "general_operand" "=f")
	(abs:XF (match_operand:XF 1 "nonimmediate_operand" "fmF")))]
  "TARGET_68881"
  "*
{
  if (REG_P (operands[1]) && ! DATA_REG_P (operands[1]))
    return \"fabs%.x %1,%0\";
  return \"fabs%.x %f1,%0\";
}")

(define_insn "sqrtxf2"
  [(set (match_operand:XF 0 "general_operand" "=f")
	(sqrt:XF (match_operand:DF 1 "nonimmediate_operand" "fm")))]
  "TARGET_68881"
  "*
{
    return \"fsqrt%.x %1,%0\";
}")
