;; Machine description for Tilera TILE-Gx chip for GCC.
;; Copyright (C) 2011-2013 Free Software Foundation, Inc.
;; Contributed by Walter Lee (walt@tilera.com)
;;
;; This file is part of GCC.
;;
;; GCC is free software; you can redistribute it and/or modify it
;; under the terms of the GNU General Public License as published
;; by the Free Software Foundation; either version 3, or (at your
;; option) any later version.
;;
;; GCC is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
;; License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GCC; see the file COPYING3.  If not see
;; <http://www.gnu.org/licenses/>.

(define_constants [
  ;;
  ;; The following represent intrinsic insns, organized by latency.
  ;;

  ;; single cycle
  (UNSPEC_INSN_ADDR_SHL16INSLI         1)
  (UNSPEC_INSN_BFEXTS                  2)
  (UNSPEC_INSN_BFEXTU                  3)
  (UNSPEC_INSN_BFINS                   4)
  (UNSPEC_INSN_CRC32_32                5)
  (UNSPEC_INSN_CRC32_8                 6)
  (UNSPEC_INSN_DBLALIGN                7)
  (UNSPEC_INSN_DBLALIGN2               8)
  (UNSPEC_INSN_DBLALIGN4               9)
  (UNSPEC_INSN_DBLALIGN6               10)
  (UNSPEC_INSN_DRAIN                   11)
  (UNSPEC_INSN_DTLBPR                  12)
  (UNSPEC_INSN_FINV                    13)
  (UNSPEC_INSN_FLUSH                   14)
  (UNSPEC_INSN_FLUSHWB                 15)
  (UNSPEC_INSN_FNOP                    16)
  (UNSPEC_INSN_ICOH                    17)
  (UNSPEC_INSN_ILL                     18)
  (UNSPEC_INSN_INFO                    19)
  (UNSPEC_INSN_INFOL                   20)
  (UNSPEC_INSN_INV                     21)
  (UNSPEC_INSN_LNK                     22)
  (UNSPEC_INSN_MFSPR                   23)
  (UNSPEC_INSN_MM                      24)
  (UNSPEC_INSN_MTSPR                   25)
  (UNSPEC_INSN_NAP                     26)
  (UNSPEC_INSN_PREFETCH_L1_FAULT       27)
  (UNSPEC_INSN_PREFETCH_L2_FAULT       28)
  (UNSPEC_INSN_PREFETCH_L3_FAULT       29)
  (UNSPEC_INSN_REVBITS                 30)
  (UNSPEC_INSN_SHUFFLEBYTES            31)
  (UNSPEC_INSN_TBLIDXB0                32)
  (UNSPEC_INSN_TBLIDXB1                33)
  (UNSPEC_INSN_TBLIDXB2                34)
  (UNSPEC_INSN_TBLIDXB3                35)
  (UNSPEC_INSN_V1AVGU                  36)
  (UNSPEC_INSN_V2AVGS                  37)
  (UNSPEC_INSN_WH64                    38)

  ;; 2 cycles
  (UNSPEC_INSN_CMUL                    100)
  (UNSPEC_INSN_CMULA                   101)
  (UNSPEC_INSN_CMULAF                  102)
  (UNSPEC_INSN_CMULFR                  103)
  (UNSPEC_INSN_CMULHR                  104)
  (UNSPEC_INSN_CMULF                   105)
  (UNSPEC_INSN_CMULH                   106)
  (UNSPEC_INSN_EXCH                    107)
  (UNSPEC_INSN_FDOUBLE_ADDSUB          108)
  (UNSPEC_INSN_FDOUBLE_ADD_FLAGS       109)
  (UNSPEC_INSN_FDOUBLE_MUL_FLAGS       110)
  (UNSPEC_INSN_FDOUBLE_PACK1           111)
  (UNSPEC_INSN_FDOUBLE_PACK2           112)
  (UNSPEC_INSN_FDOUBLE_SUB_FLAGS       113)
  (UNSPEC_INSN_FDOUBLE_UNPACK_MAX      114)
  (UNSPEC_INSN_FDOUBLE_UNPACK_MIN      115)
  (UNSPEC_INSN_FETCHADDGEZ             116)
  (UNSPEC_INSN_FSINGLE_ADD1            117)
  (UNSPEC_INSN_FSINGLE_ADDSUB2         118)
  (UNSPEC_INSN_FSINGLE_MUL1            119)
  (UNSPEC_INSN_FSINGLE_MUL2            120)
  (UNSPEC_INSN_FSINGLE_PACK1           121)
  (UNSPEC_INSN_FSINGLE_PACK2           122)
  (UNSPEC_INSN_FSINGLE_SUB1            123)
  (UNSPEC_INSN_MULAX                   124)
  (UNSPEC_INSN_MULA_HS_HS              125)
  (UNSPEC_INSN_MULA_HS_HU              126)
  (UNSPEC_INSN_MULA_HS_LS              127)
  (UNSPEC_INSN_MULA_HS_LU              128)
  (UNSPEC_INSN_MULA_HU_HU              129)
  (UNSPEC_INSN_MULA_HU_LS              130)
  (UNSPEC_INSN_MULA_HU_LU              131)
  (UNSPEC_INSN_MULA_LS_LS              132)
  (UNSPEC_INSN_MULA_LS_LU              133)
  (UNSPEC_INSN_MULA_LU_LU              134)
  (UNSPEC_INSN_MUL_HS_HS               135)
  (UNSPEC_INSN_MUL_HS_HU               136)
  (UNSPEC_INSN_MUL_HS_LS               137)
  (UNSPEC_INSN_MUL_HS_LU               138)
  (UNSPEC_INSN_MUL_HU_HU               139)
  (UNSPEC_INSN_MUL_HU_LS               140)
  (UNSPEC_INSN_MUL_HU_LU               141)
  (UNSPEC_INSN_MUL_LS_LS               142)
  (UNSPEC_INSN_MUL_LS_LU               143)
  (UNSPEC_INSN_MUL_LU_LU               144)
  (UNSPEC_INSN_V1ADIFFU                145)
  (UNSPEC_INSN_V1DDOTPU                146)
  (UNSPEC_INSN_V1DDOTPUA               147)
  (UNSPEC_INSN_V1DDOTPUS               148)
  (UNSPEC_INSN_V1DDOTPUSA              149)
  (UNSPEC_INSN_V1DOTP                  150)
  (UNSPEC_INSN_V1DOTPA                 151)
  (UNSPEC_INSN_V1DOTPU                 152)
  (UNSPEC_INSN_V1DOTPUA                153)
  (UNSPEC_INSN_V1DOTPUS                154)
  (UNSPEC_INSN_V1DOTPUSA               155)
  (UNSPEC_INSN_V1SADAU                 156)
  (UNSPEC_INSN_V1SADU                  157)
  (UNSPEC_INSN_V2ADIFFS                158)
  (UNSPEC_INSN_V2DOTP                  159)
  (UNSPEC_INSN_V2DOTPA                 160)
  (UNSPEC_INSN_V2MULFSC                161)
  (UNSPEC_INSN_V2SADAS                 162)
  (UNSPEC_INSN_V2SADAU                 163)
  (UNSPEC_INSN_V2SADS                  164)
  (UNSPEC_INSN_V2SADU                  165)

  ;; 11 cycles
  (UNSPEC_INSN_CMPEXCH                 200)

  ;;
  ;; The following are special insns.
  ;;

  ;; Blockage
  (UNSPEC_BLOCKAGE                     201)

  ;; Lnk and its label
  (UNSPEC_LNK_AND_LABEL                202)

  ;; Memory fence
  (UNSPEC_MF		               203)

  ;; Insns generating difference of two labels
  (UNSPEC_MOV_PCREL_STEP3              204)
  (UNSPEC_MOV_LARGE_PCREL_STEP4        205)

  ;; Latency specifying loads.
  (UNSPEC_LATENCY_L2                   206)
  (UNSPEC_LATENCY_MISS                 207)

  ;; A pseudo-op that prevents network operations from being ordered.
  (UNSPEC_NETWORK_BARRIER              208)

  ;; Operations that access network registers.
  (UNSPEC_NETWORK_RECEIVE              209)
  (UNSPEC_NETWORK_SEND                 210)

  ;; Stack protector operations
  (UNSPEC_SP_SET                       211)
  (UNSPEC_SP_TEST                      212)

  ;; This is used to move a value to a SPR.
  (UNSPEC_SPR_MOVE                     213)

  ;; A call to __tls_get_addr
  (UNSPEC_TLS_GD_CALL                  214)

  ;; An opaque TLS "add" operation for TLS general dynamic model
  ;; access.
  (UNSPEC_TLS_GD_ADD                   215)

  ;; An opaque TLS "load" operation for TLS initial exec model access.
  (UNSPEC_TLS_IE_LOAD                  216)

  ;; An opaque TLS "add" operation for TLS access.
  (UNSPEC_TLS_ADD                      217)

  ;; Atomics
  (UNSPEC_ATOMIC                       218)
  (UNSPEC_CMPXCHG                      219)
  (UNSPEC_XCHG                         220)

  ;;
  ;; The following are operands.
  ;;
  (UNSPEC_HW0                          300)
  (UNSPEC_HW1                          301)
  (UNSPEC_HW2                          302)
  (UNSPEC_HW3                          303)
  (UNSPEC_HW0_LAST                     304)
  (UNSPEC_HW1_LAST                     305)
  (UNSPEC_HW2_LAST                     306)

  (UNSPEC_HW0_PCREL                    307)
  (UNSPEC_HW1_PCREL                    308)
  (UNSPEC_HW1_LAST_PCREL               309)
  (UNSPEC_HW2_LAST_PCREL               310)

  (UNSPEC_HW0_GOT                      311)
  (UNSPEC_HW0_LAST_GOT                 312)
  (UNSPEC_HW1_LAST_GOT                 313)

  (UNSPEC_HW0_TLS_GD                   314)
  (UNSPEC_HW1_LAST_TLS_GD              315)

  (UNSPEC_HW0_TLS_IE                   316)
  (UNSPEC_HW1_LAST_TLS_IE              317)

  (UNSPEC_HW0_TLS_LE                   318)
  (UNSPEC_HW1_LAST_TLS_LE              319)

  (UNSPEC_HW0_PLT_PCREL                320)
  (UNSPEC_HW1_PLT_PCREL                321)

  (UNSPEC_HW1_LAST_PLT_PCREL           322)
  (UNSPEC_HW2_LAST_PLT_PCREL           323)

  ;; This is used to wrap around the addresses of non-temporal load/store
  ;; intrinsics.
  (UNSPEC_NON_TEMPORAL                 324)
])

;; Mark the last instruction of various latencies, used to
;; determine the rtx costs of unspec insns.
(define_constants [
  (TILEGX_LAST_LATENCY_1_INSN           99)
  (TILEGX_LAST_LATENCY_2_INSN          199)
  (TILEGX_LAST_LATENCY_INSN            299)
])

(define_constants [
  (TILEGX_NETREG_IDN0 0)
  (TILEGX_NETREG_IDN1 1)
  (TILEGX_NETREG_UDN0 2)
  (TILEGX_NETREG_UDN1 3)
  (TILEGX_NETREG_UDN2 4)
  (TILEGX_NETREG_UDN3 5)
])

(define_constants [
  (TILEGX_CMPEXCH_REG  66)
  (TILEGX_NETORDER_REG 67)
])


;; Operand and operator predicates and constraints

(include "predicates.md")
(include "constraints.md")
(include "tilegx-generic.md")

;; Define an insn type attribute.  This defines what pipes things can go in.
(define_attr "type"
  "X0,X0_2cycle,X1,X1_branch,X1_2cycle,X1_L2,X1_remote,X1_miss,X01,Y0,Y0_2cycle,Y1,Y2,Y2_2cycle,Y2_L2,Y2_miss,Y01,cannot_bundle,cannot_bundle_3cycle,cannot_bundle_4cycle,nothing"
  (const_string "Y01"))

(define_attr "length" ""
   (cond [(eq_attr "type" "X1_branch")
	  (if_then_else
	   (and (le (minus (match_dup 0) (pc)) (const_int 524280))
		(le (minus (pc) (match_dup 0)) (const_int 524288)))
	   (const_int 8)
	   (const_int 16))
	  ]
	 (const_int 8)))


;; Define some iterators.
(define_mode_iterator IVMODE [SI DI V8QI V4HI V2SI])
(define_mode_iterator IVNMODE [SI V8QI V4HI V2SI])
(define_mode_iterator I48MODE [SI DI])
(define_mode_iterator I48MODE2 [SI DI])
(define_mode_iterator I124MODE [QI HI SI])
(define_mode_iterator FI48MODE [SF DF SI DI])
(define_mode_iterator VEC48MODE [V8QI V4HI])
(define_mode_iterator VEC248MODE [V8QI V4HI V2SI])

(define_mode_attr n [(QI "1") (HI "2") (SI "4") (DI "")
		     (V8QI "1") (V4HI "2") (V2SI "4")])
(define_mode_attr x [(SI "x") (DI "")])
(define_mode_attr bitsuffix [(SI "_32bit") (DI "")])
(define_mode_attr four_if_si [(SI "4") (DI "")])
(define_mode_attr four_s_if_si [(SI "4s") (DI "")])
(define_mode_attr nbits [(SI "5") (DI "6")])
(define_mode_attr shift_pipe [(SI "X01") (DI "*")])

;; Code iterator for either extend.
(define_code_iterator any_extend [sign_extend zero_extend])

;; Code iterator for all three shifts.
(define_code_iterator any_shift [ashift ashiftrt lshiftrt])

;; Code iterator for all byte ops without immediate variants.
(define_code_iterator v1op [us_minus us_plus minus ne le leu mult])

;; Code iterator for all 2-byte vector ops without immediate variants.
(define_code_iterator v2op [ss_minus ss_plus minus ne le leu])

;; Code iterator for all 4-byte vector ops without immediate variants.
(define_code_iterator v4op [ss_minus ss_plus minus plus])

;; Code iterator for all byte vector ops with immediate variants.
(define_code_iterator v1op_immed [plus umax umin eq lt ltu])

;; Code iterator for all 2-byte vector ops with immediate variants.
(define_code_iterator v2op_immed [plus smax smin eq lt ltu])

;; Code iterator for all 2-byte vector shifts without immediate variants.
(define_code_iterator v2shift [ss_ashift])

;; Code iterator for all 4-byte vector shifts without immediate variants.
(define_code_iterator v4shift [ashift ashiftrt lshiftrt ss_ashift])

;; <optab> expands to the name of the optab for a particular code.
(define_code_attr optab [(ashift "ashl")
			 (ashiftrt "ashr")
			 (lshiftrt "lshr")
			 (ss_ashift "ssashl")
			 (eq "seq")
			 (ne "sne")
			 (lt "slt")
			 (ltu "sltu")
			 (le "sle")
			 (leu "sleu")
			 (minus "sub")
			 (plus "add")
			 (mult "mul")
			 (smax "smax")
			 (smin "smin")
			 (ss_minus "sssub")
			 (ss_plus "ssadd")
			 (umax "umax")
			 (umin "umin")
			 (us_minus "ussub")
			 (us_plus "usadd")
			 ])

;; <insn> expands to the name of the insn that implements a particular
;; code.
(define_code_attr insn [(ashift "shl")
			(ashiftrt "shrs")
			(lshiftrt "shru")
			(ss_ashift "shlsc")
			(eq "cmpeq")
			(ne "cmpne")
			(lt "cmplts")
			(ltu "cmpltu")
			(le "cmples")
			(leu "cmpleu")
			(minus "sub")
			(plus "add")
			(mult "multu")
			(smax "maxs")
			(smin "mins")
			(umax "maxu")
			(umin "minu")
			(ss_minus "subsc")
			(ss_plus  "addsc")
                        (us_minus "subuc")
                        (us_plus  "adduc")
			])

;; <pipe> expands to the pipeline resource that contains the
;; particular code.
(define_code_attr pipe [(ashift "X01")
			(ashiftrt "X01")
			(lshiftrt "X01")
			(ss_ashift "X01")
			(eq "X01")
			(ne "X01")
			(lt "X01")
			(ltu "X01")
			(le "X01")
			(leu "X01")
			(minus "X01")
			(plus "X01")
			(mult "X0_2cycle")
			(smax "X01")
			(smin "X01")
			(umax "X01")
			(umin "X01")
			(ss_minus "X01")
			(ss_plus  "X01")
                        (us_minus "X01")
                        (us_plus  "X01")
			])

;; <comm> indicates whether a particular code is commutative, using
;; the "%" commutative opterator constraint.
(define_code_attr comm [(ashift "")
			(ashiftrt "")
			(lshiftrt "")
			(ss_ashift "")
			(eq "%")
			(ne "%")
			(lt "")
			(ltu "")
			(le "")
			(leu "")
			(minus "")
			(plus "%")
			(mult "%")
			(smin "%")
			(umin "%")
			(smax "%")
			(umax "%")
			(ss_plus "%")
			(us_plus "%")
			(ss_minus "")
			(us_minus "")
			])

;; <s> is the load/store extension suffix.
(define_code_attr s [(zero_extend "u")
		     (sign_extend "s")])

;; Code for packing two 2-byte vectors.
(define_code_iterator v2pack [truncate us_truncate])

;; <pack_optab> expands to the part of the optab name describing how
;; two vectors are packed.
(define_code_attr pack_optab [(truncate "trunc")
			      (us_truncate "usat")
			      (ss_truncate "ssat")])

;; <pack_insn> expands to the insn that implements a particular vector
;; packing code.
(define_code_attr pack_insn [(truncate "packl")
			     (us_truncate "packuc")
			     (ss_truncate "packsc")])

;;
;; The basic data move insns.
;;

(define_expand "movqi"
  [(set (match_operand:QI 0 "nonimmediate_operand" "")
	(match_operand:QI 1 "nonautoinc_operand" ""))]
  ""
{
  if (tilegx_expand_mov (QImode, operands))
    DONE;
})

(define_insn "*movqi_insn"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=r,r,r,r,U,m")
	(match_operand:QI 1 "move_operand"         "r,I,U,m,rO,rO"))]
  "(register_operand (operands[0], QImode)
    || reg_or_0_operand (operands[1], QImode))"
  "@
   move\t%0, %r1
   movei\t%0, %1
   ld1u\t%0, %1
   ld1u_add\t%0, %I1, %i1
   st1\t%0, %r1
   st1_add\t%I0, %r1, %i0"
  [(set_attr "type" "*,*,Y2_2cycle,X1_2cycle,Y2,X1")])

(define_expand "movhi"
  [(set (match_operand:HI 0 "nonimmediate_operand" "")
	(match_operand:HI 1 "nonautoinc_operand" ""))]
  ""
{
  if (tilegx_expand_mov (HImode, operands))
    DONE;
})

(define_insn "*movhi_insn"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=r,r,r,r,r,U,m")
	(match_operand:HI 1 "move_operand"         "r,I,JT,U,m,rO,rO"))]
  "(register_operand (operands[0], HImode)
    || reg_or_0_operand (operands[1], HImode))"
  "@
   move\t%0, %r1
   movei\t%0, %1
   moveli\t%0, %H1
   ld2u\t%0, %1
   ld2u_add\t%0, %I1, %i1
   st2\t%0, %r1
   st2_add\t%I0, %r1, %i0"
  [(set_attr "type" "*,*,X01,Y2_2cycle,X1_2cycle,Y2,X1")])

(define_expand "movsi"
  [(set (match_operand:SI 0 "nonimmediate_operand" "")
	(match_operand:SI 1 "nonautoinc_operand" ""))]
  ""
{
  if (tilegx_expand_mov (SImode, operands))
    DONE;
})

(define_insn "*movsi_insn"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r,r,r,r,U,m")
	(match_operand:SI 1 "move_operand"         "r,I,JT,K,U,m,rO,rO"))]
  "(register_operand (operands[0], SImode)
    || reg_or_0_operand (operands[1], SImode))"
  "@
   move\t%0, %r1
   movei\t%0, %1
   moveli\t%0, %H1
   shl16insli\t%0, zero, %h1
   ld4s\t%0, %1
   ld4s_add\t%0, %I1, %i1
   st4\t%0, %r1
   st4_add\t%I0, %r1, %i0"
  [(set_attr "type" "*,*,X01,X01,Y2_2cycle,X1_2cycle,Y2,X1")])

(define_expand "movdi"
  [(set (match_operand:DI 0 "nonimmediate_operand" "")
	(match_operand:DI 1 "nonautoinc_operand" ""))]
  ""
{
  if (tilegx_expand_mov (DImode, operands))
    DONE;
})

(define_insn "*movdi_insn"
  [(set (match_operand:DI 0 "nonimmediate_operand" "=r,r,r,r,r,r,r,r,U,m")
	(match_operand:DI 1 "move_operand"         "r,I,JT,K,N,P,U,m,rO,rO"))]
  "(register_operand (operands[0], DImode)
    || reg_or_0_operand (operands[1], DImode))"
  "@
   move\t%0, %r1
   movei\t%0, %1
   moveli\t%0, %H1
   shl16insli\t%0, zero, %h1
   v1addi\t%0, zero, %j1
   v2addi\t%0, zero, %h1
   ld\t%0, %1
   ld_add\t%0, %I1, %i1
   st\t%0, %r1
   st_add\t%I0, %r1, %i0"
  [(set_attr "type" "*,*,X01,X01,X01,X01,Y2_2cycle,X1_2cycle,Y2,X1")])

(define_expand "movmisalign<mode>"
  [(set (match_operand:VEC248MODE 0 "nonautoincmem_nonimmediate_operand" "")
        (match_operand:VEC248MODE 1 "nonautoincmem_general_operand" ""))]
  ""
{
  tilegx_expand_movmisalign (<MODE>mode, operands);
  DONE;
})

(define_expand "movsf"
  [(set (match_operand:SF 0 "nonimmediate_operand" "")
	(match_operand:SF 1 "general_operand" ""))]
  ""
{
  /* Materialize immediates using clever SImode code, but don't
     do this after reload starts, since gen_lowpart will choke
     during reload if given an illegitimate address. */
  if (immediate_operand (operands[1], SFmode)
      && operands[1] != const0_rtx
      && (register_operand (operands[0], SFmode)
          || (!reload_in_progress && !reload_completed)))
    {
      emit_insn (gen_movsi (gen_lowpart (SImode, operands[0]),
                            gen_lowpart (SImode, operands[1])));
      DONE;
    }
})

(define_insn "*movsf"
  [(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,r,U,m")
	(match_operand:SF 1 "general_operand" "rO,U,m,rO,rO"))]
  ""
  "@
   move\t%0, %r1
   ld4s\t%0, %1
   ld4s_add\t%0, %I1, %i1
   st4\t%0, %r1
   st4_add\t%I0, %r1, %i0"
  [(set_attr "type" "*,Y2_2cycle,X1_2cycle,Y2,X1")])

(define_expand "movdf"
  [(set (match_operand:DF 0 "nonimmediate_operand" "")
	(match_operand:DF 1 "general_operand" ""))]
  ""
{
  /* Materialize immediates using clever DImode code, but don't
     do this after reload starts, since gen_lowpart will choke
     during reload if given an illegitimate address. */
  if (immediate_operand (operands[1], DFmode)
      && operands[1] != const0_rtx
      && (register_operand (operands[0], DFmode)
          || (!reload_in_progress && !reload_completed)))
    {
      emit_insn (gen_movdi (gen_lowpart (DImode, operands[0]),
                            gen_lowpart (DImode, operands[1])));
      DONE;
    }
})

(define_insn "*movdf"
  [(set (match_operand:DF 0 "nonimmediate_operand" "=r,r,r,U,m")
	(match_operand:DF 1 "general_operand" "rO,U,m,rO,rO"))]
  ""
  "@
   move\t%0, %r1
   ld\t%0, %1
   ld_add\t%0, %I1, %i1
   st\t%0, %r1
   st_add\t%I0, %r1, %i0"
  [(set_attr "type" "*,Y2_2cycle,X1_2cycle,Y2,X1")])

(define_expand "mov<mode>"
  [(set (match_operand:VEC248MODE 0 "nonimmediate_operand" "")
        (match_operand:VEC248MODE 1 "general_operand" ""))]
  ""
{
  /* Materialize immediates using clever DImode code, but don't
     do this after reload starts, since gen_lowpart will choke
     during reload if given an illegitimate address. */
  if (immediate_operand (operands[1], <MODE>mode)
      && operands[1] != const0_rtx
      && (register_operand (operands[0], <MODE>mode)
          || (!reload_in_progress && !reload_completed)))
    {
      emit_insn (gen_movdi (gen_lowpart (DImode, operands[0]),
                            gen_lowpart (DImode, operands[1])));
      DONE;
    }
})

(define_insn "*mov<mode>"
  [(set (match_operand:VEC248MODE 0 "nonimmediate_operand" "=r,r,r,U,m")
	(match_operand:VEC248MODE 1 "general_operand" "rO,U,m,rO,rO"))]
  ""
  "@
   move\t%0, %r1
   ld\t%0, %1
   ld_add\t%0, %I1, %i1
   st\t%0, %r1
   st_add\t%I0, %r1, %i0"
  [(set_attr "type" "*,Y2_2cycle,X1_2cycle,Y2,X1")])

(define_insn "movstrictqi"
  [(set (strict_low_part (match_operand:QI 0 "register_operand" "+r"))
	(match_operand:QI 1 "reg_or_0_operand" "rO"))]
  ""
  "bfins\t%0, %r1, 0, 7"
  [(set_attr "type" "X0")])
  
(define_insn "movstricthi"
  [(set (strict_low_part (match_operand:HI 0 "register_operand" "+r"))
	(match_operand:HI 1 "reg_or_0_operand" "rO"))]
  ""
  "bfins\t%0, %r1, 0, 15"
  [(set_attr "type" "X0")])
  
(define_insn "movstrictsi"
  [(set (strict_low_part (match_operand:SI 0 "register_operand" "+r"))
	(match_operand:SI 1 "reg_or_0_operand" "rO"))]
  ""
  "bfins\t%0, %r1, 0, 31"
  [(set_attr "type" "X0")])


;;
;; Bit-field extracts/inserts
;;

(define_expand "insv"
  [(set (zero_extract:DI (match_operand:DI 0 "register_operand" "")
			 (match_operand:DI 1 "u6bit_cint_operand" "")
			 (match_operand:DI 2 "u6bit_cint_operand" ""))
	(match_operand:DI 3 "reg_or_cint_operand" ""))]
  ""
{
  rtx first_rtx = operands[2];
  HOST_WIDE_INT first = INTVAL (first_rtx);
  HOST_WIDE_INT width = INTVAL (operands[1]);
  rtx v = operands[3];

  if (CONST_INT_P (v))
    {
      /* Which bits are we affecting? */
      HOST_WIDE_INT mask = ((((HOST_WIDE_INT) 1) << width) - 1) << first;

      /* Extract just the bits we need, sign extending them to make the
         constant easier to materialize in a register. */
      int shift = sizeof(HOST_WIDE_INT) * 8 - width;
      HOST_WIDE_INT n = (INTVAL (v) << shift) >> shift;

      if (n == 0)
        {
	  /* We are setting every bit in the bitfield to zero. Try to use
             andi instead, since that is more efficient. */
	  rtx mask_rtx = GEN_INT (~mask);
	  if (satisfies_constraint_I (mask_rtx))
            {
	      emit_insn (gen_anddi3 (operands[0], operands[0], mask_rtx));
	      DONE;
            }

	  operands[3] = const0_rtx;
	}
      else
        {
	  if (n == -1)
	    {
	      /* We are setting every bit in the bitfield to one. Try to use
	         ori instead, since that is more efficient. */
	      rtx mask_rtx = GEN_INT (mask);
	      if (satisfies_constraint_I (mask_rtx))
	        {
		  emit_insn (gen_iordi3 (operands[0], operands[0], mask_rtx));
		  DONE;
		}
	    }

	  if (!can_create_pseudo_p ())
            FAIL;

	  operands[3] = force_reg (DImode, GEN_INT (n));
	}
    }
})

(define_insn "*insv_tblidxb0"
  [(set (zero_extract:DI
	 (match_operand:DI 0 "register_operand" "+r")
	 (const_int 8)
	 (const_int 2))
	(match_operand:DI 1 "register_operand" "rO"))]
  ""
  "tblidxb0\t%0, %r1"
  [(set_attr "type" "Y0")])

(define_insn "*insv_tblidxb1"
  [(set (zero_extract:DI
	 (match_operand:DI 0 "register_operand" "+r")
	 (const_int 8)
	 (const_int 2))
	(zero_extract:DI
	 (const_int 8)
	 (const_int 8)
	(match_operand:DI 1 "register_operand" "rO")))]
  ""
  "tblidxb1\t%0, %r1"
  [(set_attr "type" "Y0")])

(define_insn "*insv_tblidxb2"
  [(set (zero_extract:DI
	 (match_operand:DI 0 "register_operand" "+r")
	 (const_int 8)
	 (const_int 2))
	(zero_extract:DI
	 (const_int 8)
	 (const_int 16)
	(match_operand:DI 1 "register_operand" "rO")))]
  ""
  "tblidxb2\t%0, %r1"
  [(set_attr "type" "Y0")])

(define_insn "*insv_tblidxb3"
  [(set (zero_extract:DI
	 (match_operand:DI 0 "register_operand" "+r")
	 (const_int 8)
	 (const_int 2))
	(zero_extract:DI
	 (const_int 8)
	 (const_int 24)
	(match_operand:DI 1 "register_operand" "rO")))]
  ""
  "tblidxb3\t%0, %r1"
  [(set_attr "type" "Y0")])

(define_insn "*insv_bfins"
  [(set (zero_extract:DI
	 (match_operand:DI 0 "register_operand" "+r")
	 (match_operand:DI 1 "u6bit_cint_operand" "n")
	 (match_operand:DI 2 "u6bit_cint_operand" "n"))
	(match_operand:DI 3 "reg_or_cint_operand" "rO"))]
  ""
  "bfins\t%0, %r3, %2, %2+%1-1"
  [(set_attr "type" "X0")])

(define_insn "*insv_mm"
  [(set (zero_extract:DI
	 (match_operand:DI 0 "register_operand" "+r")
	 (match_operand:DI 1 "u6bit_cint_operand" "n")
	 (match_operand:DI 2 "u6bit_cint_operand" "n"))
	(zero_extract:DI
	 (match_operand:DI 3 "register_operand" "rO")
	 (match_dup 1)
	 (match_dup 2)))]
  ""
{
  int n;

  operands[1] = GEN_INT (INTVAL (operands[1]) + INTVAL (operands[2]));

  n = INTVAL (operands[2]);
  n = (n == 0) ? 63 : n - 1;
  operands[2] = GEN_INT (n);

  return "mm\t%0, %r3, %1, %2";
}
  [(set_attr "type" "X0")])

(define_expand "extv"
  [(set (match_operand:DI 0 "register_operand" "")
	(sign_extract:DI (match_operand 1 "nonautoincmem_general_operand" "")
			 (match_operand:DI 2 "immediate_operand" "")
			 (match_operand:DI 3 "immediate_operand" "")))]
  ""
{
  if (MEM_P (operands[1]))
    {
      HOST_WIDE_INT bit_offset, bit_width;
      HOST_WIDE_INT first_byte_offset, last_byte_offset;

      if (GET_MODE (operands[1]) != QImode)
        FAIL;

      bit_width = INTVAL (operands[2]);
      bit_offset = INTVAL (operands[3]);

      /* Reject bitfields that can be done with a normal load.  */
      if (MEM_ALIGN (operands[1]) >= bit_offset + bit_width)
        FAIL;

      /* The value in memory cannot span more than 8 bytes.  */
      first_byte_offset = bit_offset / BITS_PER_UNIT;
      last_byte_offset = (bit_offset + bit_width - 1) / BITS_PER_UNIT;
      if (last_byte_offset - first_byte_offset > 7)
        FAIL;

      tilegx_expand_unaligned_load (operands[0], operands[1],
                                    bit_width, bit_offset, 1);

      DONE;
    }

  operands[1] = force_reg (DImode, operands[1]);
})

(define_expand "extzv"
  [(set (match_operand:DI 0 "register_operand" "")
	(zero_extract:DI (match_operand 1 "nonautoincmem_general_operand" "")
			 (match_operand:DI 2 "immediate_operand" "")
			 (match_operand:DI 3 "immediate_operand" "")))]
  ""
{
  HOST_WIDE_INT bit_width = INTVAL (operands[2]);
  HOST_WIDE_INT bit_offset = INTVAL (operands[3]);

  if (MEM_P (operands[1]))
    {
      HOST_WIDE_INT first_byte_offset, last_byte_offset;

      if (GET_MODE (operands[1]) != QImode)
        FAIL;

      /* Reject bitfields that can be done with a normal load.  */
      if (MEM_ALIGN (operands[1]) >= bit_offset + bit_width)
        FAIL;

      /* The value in memory cannot span more than 8 bytes.  */
      first_byte_offset = bit_offset / BITS_PER_UNIT;
      last_byte_offset = (bit_offset + bit_width - 1) / BITS_PER_UNIT;
      if (last_byte_offset - first_byte_offset > 7)
        FAIL;

      tilegx_expand_unaligned_load (operands[0], operands[1],
                                    bit_width, bit_offset, 0);

      DONE;
    }

    operands[1] = force_reg (DImode, operands[1]);

    if (bit_offset == 0)
      {
	 /* Extracting the low bits is just a bitwise AND.  */
	 HOST_WIDE_INT mask = ((HOST_WIDE_INT)1 << bit_width) - 1;
	 emit_insn (gen_anddi3 (operands[0], operands[1], GEN_INT (mask)));
	 DONE;
      }
})


;;
;; Addresses
;;

;; The next three patterns are used to to materialize a position
;; independent address by adding the difference of two labels to a base
;; label in the text segment, assuming that the difference fits in 32
;; signed bits.
(define_expand "mov_address_step1"
  [(set (match_operand:DI 0 "register_operand" "")
	(const:DI (unspec:DI [(match_operand:DI 1 "symbolic_operand" "")]
			     UNSPEC_HW2_LAST)))])

(define_expand "mov_address_step2"
  [(set (match_operand:DI 0 "register_operand" "")
	(unspec:DI
	 [(match_operand:DI 1 "reg_or_0_operand" "")
	  (const:DI (unspec:DI [(match_operand:DI 2 "symbolic_operand" "")]
			       UNSPEC_HW1))]
	 UNSPEC_INSN_ADDR_SHL16INSLI))])
  
(define_expand "mov_address_step3"
  [(set (match_operand:DI 0 "register_operand" "")
	(unspec:DI
	 [(match_operand:DI 1 "reg_or_0_operand" "")
	  (const:DI (unspec:DI [(match_operand:DI 2 "symbolic_operand" "")]
			       UNSPEC_HW0))]
	 UNSPEC_INSN_ADDR_SHL16INSLI))])

;; First step of the 2-insn sequence to materialize a 32-bit symbolic
;; address.
(define_expand "mov_address_32bit_step1"
  [(set (match_operand:SI 0 "register_operand" "")
        (const:SI (unspec:SI [(match_operand:SI 1 "symbolic_operand" "")]
			     UNSPEC_HW1_LAST)))])
  
;; Second step of the 2-insn sequence to materialize a 32-bit symbolic
;; address.
(define_expand "mov_address_32bit_step2"
  [(set (match_operand:SI 0 "register_operand" "")
	(unspec:SI
	 [(match_operand:SI 1 "reg_or_0_operand" "")
	  (const:SI (unspec:SI [(match_operand:SI 2 "symbolic_operand" "")]
			       UNSPEC_HW0))]
	 UNSPEC_INSN_ADDR_SHL16INSLI))])


;;
;; pic related instructions
;;

;; NOTE: We compute the label in this unusual way because if we place
;; the label after the lnk, whether it is at the same address as the
;; lnk will vary depending on whether the optimization level chooses
;; to insert bundling braces.
(define_insn "insn_lnk_and_label<bitsuffix>"
  [(set (match_operand:I48MODE 0 "register_operand" "=r")
        (unspec_volatile:I48MODE
         [(match_operand:I48MODE 1 "symbolic_operand" "")]
         UNSPEC_LNK_AND_LABEL))]
  ""
  "%1 = . + 8\n\tlnk\t%0"
  [(set_attr "type" "Y1")])

;; The next three patterns are used to to materialize a position
;; independent address by adding the difference of two labels to a
;; base label in the text segment, assuming that the difference fits
;; in 32 signed bits.
(define_expand "mov_pcrel_step1<bitsuffix>"
  [(set (match_operand:I48MODE 0 "register_operand" "")
	(const:I48MODE (unspec:I48MODE
			[(match_operand:I48MODE 1 "symbolic_operand" "")
			 (match_operand:I48MODE 2 "symbolic_operand" "")]
                        UNSPEC_HW1_LAST_PCREL)))]
  "flag_pic")
  
(define_expand "mov_pcrel_step2<bitsuffix>"
  [(set (match_operand:I48MODE 0 "register_operand" "")
	(unspec:I48MODE
	 [(match_operand:I48MODE 1 "reg_or_0_operand" "")
	  (const:I48MODE
	   (unspec:I48MODE [(match_operand:I48MODE 2 "symbolic_operand" "")
			    (match_operand:I48MODE 3 "symbolic_operand" "")]
			   UNSPEC_HW0_PCREL))]
	 UNSPEC_INSN_ADDR_SHL16INSLI))]
  "flag_pic")

(define_insn "mov_pcrel_step3<bitsuffix>"
  [(set (match_operand:I48MODE 0 "register_operand" "=r")
        (unspec:I48MODE [(match_operand:I48MODE 1 "reg_or_0_operand" "rO")
			 (match_operand:I48MODE 2 "reg_or_0_operand" "rO")
			 (match_operand:I48MODE 3 "symbolic_operand" "in")
			 (match_operand:I48MODE 4 "symbolic_operand" "in")]
                        UNSPEC_MOV_PCREL_STEP3))]
  "flag_pic"
  "add<x>\t%0, %r1, %r2")

;; The next three patterns are used to to materialize a position
;; independent 64-bit address by adding the difference of two labels to
;; a base label in the text segment, without any limitation on the size
;; of the difference.
(define_expand "mov_large_pcrel_step1"
  [(set (match_operand:DI 0 "register_operand" "")
	(const:DI (unspec:DI
		   [(match_operand:DI 1 "symbolic_operand" "")
		    (match_operand:DI 2 "symbolic_operand" "")]
		   UNSPEC_HW2_LAST_PCREL)))]
  "flag_pic")
  
(define_expand "mov_large_pcrel_step2"
  [(set (match_operand:DI 0 "register_operand" "")
	(unspec:DI
	 [(match_operand:DI 1 "reg_or_0_operand" "")
	  (const:DI
	   (unspec:DI [(match_operand:DI 2 "symbolic_operand" "")
		       (match_operand:DI 3 "symbolic_operand" "")]
		      UNSPEC_HW1_PCREL))]
	 UNSPEC_INSN_ADDR_SHL16INSLI))]
  "flag_pic")

;; Note: step 3 is same as move_pcrel_step2.
(define_expand "mov_large_pcrel_step3"
  [(set (match_operand:DI 0 "register_operand" "")
	(unspec:DI
	 [(match_operand:DI 1 "reg_or_0_operand" "")
	  (const:DI
	   (unspec:DI [(match_operand:DI 2 "symbolic_operand" "")
		       (match_operand:DI 3 "symbolic_operand" "")]
		      UNSPEC_HW0_PCREL))]
	 UNSPEC_INSN_ADDR_SHL16INSLI))]
  "flag_pic")

(define_insn "mov_large_pcrel_step4"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
		    (match_operand:DI 2 "reg_or_0_operand" "rO")
			 (match_operand:DI 3 "symbolic_operand" "in")
			 (match_operand:DI 4 "symbolic_operand" "in")]
		   UNSPEC_MOV_LARGE_PCREL_STEP4))]
  "flag_pic"
  "add\t%0, %r1, %r2")

;; The next three patterns are used to materialize a position
;; independent 64-bit plt address by adding the difference of two
;; labels to a base label in the text segment.
(define_expand "mov_plt_pcrel_step1"
  [(set (match_operand:DI 0 "register_operand" "")
	(const:DI (unspec:DI
			[(match_operand:DI 1 "symbolic_operand" "")
			 (match_operand:DI 2 "symbolic_operand" "")]
                        UNSPEC_HW2_LAST_PLT_PCREL)))]
  "flag_pic")
  
(define_expand "mov_plt_pcrel_step2"
  [(set (match_operand:DI 0 "register_operand" "")
	(unspec:DI
	 [(match_operand:DI 1 "reg_or_0_operand" "")
	  (const:DI
	   (unspec:DI [(match_operand:DI 2 "symbolic_operand" "")
			    (match_operand:DI 3 "symbolic_operand" "")]
		      UNSPEC_HW1_PLT_PCREL))]
	 UNSPEC_INSN_ADDR_SHL16INSLI))]
  "flag_pic")

(define_expand "mov_plt_pcrel_step3"
  [(set (match_operand:DI 0 "register_operand" "")
	(unspec:DI
	 [(match_operand:DI 1 "reg_or_0_operand" "")
	  (const:DI
	   (unspec:DI [(match_operand:DI 2 "symbolic_operand" "")
			    (match_operand:DI 3 "symbolic_operand" "")]
		      UNSPEC_HW0_PLT_PCREL))]
	 UNSPEC_INSN_ADDR_SHL16INSLI))]
  "flag_pic")

;; The next two patterns are used to materialize a position independent
;; 32-bit plt address by adding the difference of two labels to a base
;; label in the text segment.
(define_expand "mov_plt_pcrel_step1_32bit"
  [(set (match_operand:SI 0 "register_operand" "")
	(const:SI (unspec:SI
			[(match_operand:SI 1 "symbolic_operand" "")
			 (match_operand:SI 2 "symbolic_operand" "")]
                        UNSPEC_HW1_LAST_PLT_PCREL)))]
  "flag_pic")
  
(define_expand "mov_plt_pcrel_step2_32bit"
  [(set (match_operand:SI 0 "register_operand" "")
	(unspec:SI
	 [(match_operand:SI 1 "reg_or_0_operand" "")
	  (const:SI
	   (unspec:SI [(match_operand:SI 2 "symbolic_operand" "")
			    (match_operand:SI 3 "symbolic_operand" "")]
		      UNSPEC_HW0_PLT_PCREL))]
	 UNSPEC_INSN_ADDR_SHL16INSLI))]
  "flag_pic")

(define_expand "add_got16<bitsuffix>"
  [(set (match_operand:I48MODE 0 "register_operand" "")
        (plus:I48MODE
	 (match_operand:I48MODE 1 "reg_or_0_operand" "")
	 (const:I48MODE
	  (unspec:I48MODE [(match_operand:I48MODE 2 "symbolic_operand" "")]
			  UNSPEC_HW0_LAST_GOT))))]
  "flag_pic == 1")

(define_expand "mov_got32_step1<bitsuffix>"
  [(set (match_operand:I48MODE 0 "register_operand" "")
	(const:I48MODE
	 (unspec:I48MODE [(match_operand:I48MODE 1 "symbolic_operand" "")]
			 UNSPEC_HW1_LAST_GOT)))]
  "flag_pic == 2")

(define_expand "mov_got32_step2<bitsuffix>"
  [(set (match_operand:I48MODE 0 "register_operand" "")
	(unspec:I48MODE
	 [(match_operand:I48MODE 1 "reg_or_0_operand" "")
	  (const:I48MODE
	   (unspec:I48MODE [(match_operand:I48MODE 2 "symbolic_operand" "")]
			   UNSPEC_HW0_GOT))]
	 UNSPEC_INSN_ADDR_SHL16INSLI))]
  "flag_pic == 2")


;;
;; TLS
;;
 
(define_expand "mov_tls_gd_step1<bitsuffix>"
  [(set (match_operand:I48MODE 0 "register_operand" "")
	(const:I48MODE
	 (unspec:I48MODE [(match_operand:I48MODE 1 "tls_symbolic_operand" "")]
			 UNSPEC_HW1_LAST_TLS_GD)))]
  "HAVE_AS_TLS")

(define_expand "mov_tls_gd_step2<bitsuffix>"
  [(set (match_operand:I48MODE 0 "register_operand" "")
	(unspec:I48MODE
	 [(match_operand:I48MODE 1 "reg_or_0_operand" "")
	  (const:I48MODE
	   (unspec:I48MODE [(match_operand:I48MODE 2 "tls_symbolic_operand" "")]
			   UNSPEC_HW0_TLS_GD))]
	 UNSPEC_INSN_ADDR_SHL16INSLI))]
  "HAVE_AS_TLS")

(define_expand "mov_tls_ie_step1<bitsuffix>"
  [(set (match_operand:I48MODE 0 "register_operand" "")
	(const:I48MODE
	 (unspec:I48MODE [(match_operand:I48MODE 1 "tls_symbolic_operand" "")]
			 UNSPEC_HW1_LAST_TLS_IE)))]
  "HAVE_AS_TLS")

(define_expand "mov_tls_ie_step2<bitsuffix>"
  [(set (match_operand:I48MODE 0 "register_operand" "")
	(unspec:I48MODE
	 [(match_operand:I48MODE 1 "reg_or_0_operand" "")
	  (const:I48MODE
	   (unspec:I48MODE [(match_operand:I48MODE 2 "tls_symbolic_operand" "")]
			   UNSPEC_HW0_TLS_IE))]
	 UNSPEC_INSN_ADDR_SHL16INSLI))]
  "HAVE_AS_TLS")

(define_expand "mov_tls_le_step1<bitsuffix>"
  [(set (match_operand:I48MODE 0 "register_operand" "")
	(const:I48MODE
	 (unspec:I48MODE [(match_operand:I48MODE 1 "tls_symbolic_operand" "")]
			 UNSPEC_HW1_LAST_TLS_LE)))]
  "HAVE_AS_TLS")

(define_expand "mov_tls_le_step2<bitsuffix>"
  [(set (match_operand:I48MODE 0 "register_operand" "")
	(unspec:I48MODE
	 [(match_operand:I48MODE 1 "reg_or_0_operand" "")
	  (const:I48MODE
	   (unspec:I48MODE [(match_operand:I48MODE 2 "tls_symbolic_operand" "")]
			   UNSPEC_HW0_TLS_LE))]
	 UNSPEC_INSN_ADDR_SHL16INSLI))]
  "HAVE_AS_TLS")

(define_expand "tls_gd_call<bitsuffix>"
  [(parallel
    [(set (reg:I48MODE 0)
	  (unspec:I48MODE [(match_operand:I48MODE 0 "tls_symbolic_operand" "")
			   (reg:I48MODE 0)]
			   UNSPEC_TLS_GD_CALL))
     (clobber (reg:I48MODE 25))
     (clobber (reg:I48MODE 26))
     (clobber (reg:I48MODE 27))
     (clobber (reg:I48MODE 28))
     (clobber (reg:I48MODE 29))
     (clobber (reg:I48MODE 55))])]
   ""
{
  cfun->machine->calls_tls_get_addr = true;
})

(define_insn "*tls_gd_call<bitsuffix>"
  [(set (reg:I48MODE 0)
	(unspec:I48MODE [(match_operand:I48MODE 0 "tls_symbolic_operand" "")
			 (reg:I48MODE 0)]
			UNSPEC_TLS_GD_CALL))
   (clobber (reg:I48MODE 25))
   (clobber (reg:I48MODE 26))
   (clobber (reg:I48MODE 27))
   (clobber (reg:I48MODE 28))
   (clobber (reg:I48MODE 29))
   (clobber (reg:I48MODE 55))]
  ""
  "jal\ttls_gd_call(%0)"
  [(set_attr "type" "X1")])

(define_insn "tls_gd_add<bitsuffix>"
  [(set (match_operand:I48MODE 0 "register_operand" "=r")
	(unspec:I48MODE [(match_operand:I48MODE 1 "register_operand" "r")
			 (match_operand:I48MODE 2 "tls_symbolic_operand" "")]
			UNSPEC_TLS_GD_ADD))]
  "HAVE_AS_TLS"
  "add<x>i\t%0, %1, tls_gd_add(%2)")

(define_insn "tls_add<bitsuffix>"
  [(set (match_operand:I48MODE 0 "register_operand" "=r")
	(unspec:I48MODE [(match_operand:I48MODE 1 "register_operand" "r")
			 (match_operand:I48MODE 2 "register_operand" "0")
			 (match_operand:I48MODE 3 "tls_symbolic_operand" "")]
			UNSPEC_TLS_ADD))]
  "HAVE_AS_TLS"
  "add<x>i\t%0, %1, tls_add(%3)")

(define_insn "tls_ie_load<bitsuffix>"
  [(set (match_operand:I48MODE 0 "register_operand" "=r")
	(unspec:I48MODE [(match_operand:I48MODE 1 "register_operand" "r")
			 (match_operand:I48MODE 2 "tls_symbolic_operand" "")]
			UNSPEC_TLS_IE_LOAD))]
  "HAVE_AS_TLS"
  "ld<four_s_if_si>_tls\t%0, %1, tls_ie_load(%2)"
  [(set_attr "type" "X1_2cycle")])

(define_insn "*zero_extract<mode>"
  [(set (match_operand:I48MODE 0 "register_operand" "=r")
	(zero_extract:I48MODE
         (match_operand:I48MODE 1 "reg_or_0_operand" "r")
         (match_operand:I48MODE 2 "u6bit_cint_operand" "n")
         (match_operand:I48MODE 3 "u6bit_cint_operand" "n")))]
  ""
  "bfextu\t%0, %r1, %3, %3+%2-1"
  [(set_attr "type" "X0")])

(define_insn "*sign_extract_low32"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(sign_extract:DI
         (match_operand:DI 1 "reg_or_0_operand" "r")
         (match_operand:DI 2 "u6bit_cint_operand" "n")
         (match_operand:DI 3 "u6bit_cint_operand" "n")))]
  "INTVAL (operands[3]) == 0 && INTVAL (operands[2]) == 32"
  "addxi\t%0, %r1, 0")

(define_insn "*sign_extract"
  [(set (match_operand:I48MODE 0 "register_operand" "=r")
	(sign_extract:I48MODE
         (match_operand:I48MODE 1 "reg_or_0_operand" "r")
         (match_operand:I48MODE 2 "u6bit_cint_operand" "n")
         (match_operand:I48MODE 3 "u6bit_cint_operand" "n")))]
  ""
  "bfexts\t%0, %r1, %3, %3+%2-1"
  [(set_attr "type" "X0")])


;;
;; Arithmetic ops
;;

(define_insn "add<mode>3"
  [(set (match_operand:I48MODE 0 "register_operand" "=r,r,r")
	(plus:I48MODE (match_operand:I48MODE 1 "reg_or_0_operand" "%rO,rO,rO")
		      (match_operand:I48MODE 2 "add_operand" "r,I,JT")))]
  ""
  "@
   add<x>\t%0, %r1, %r2
   add<x>i\t%0, %r1, %2
   add<x>li\t%0, %r1, %H2"
  [(set_attr "type" "*,*,X01")])

(define_insn "*addsi3_sext"
  [(set (match_operand:DI 0 "register_operand" "=r,r,r")
	(sign_extend:DI
	 (plus:SI (match_operand:SI 1 "reg_or_0_operand" "%rO,rO,rO")
		  (match_operand:SI 2 "add_operand" "r,I,JT"))))]
  ""
  "@
   addx\t%0, %r1, %r2
   addxi\t%0, %r1, %2
   addxli\t%0, %r1, %H2"
  [(set_attr "type" "*,*,X01")])

(define_insn "sub<mode>3"
  [(set (match_operand:I48MODE 0 "register_operand" "=r")
	(minus:I48MODE (match_operand:I48MODE 1 "reg_or_0_operand" "rO")
                       (match_operand:I48MODE 2 "reg_or_0_operand" "rO")))]
  ""
  "sub<x>\t%0, %r1, %r2")

(define_insn "*subsi3_sext"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(sign_extend:DI
	 (minus:SI (match_operand:SI 1 "reg_or_0_operand" "rO")
		   (match_operand:SI 2 "reg_or_0_operand" "rO"))))]
  ""
  "subx\t%0, %r1, %r2")

(define_insn "neg<mode>2"
  [(set (match_operand:I48MODE 0 "register_operand" "=r")
	(neg:I48MODE (match_operand:I48MODE 1 "reg_or_0_operand" "rO")))]
  ""
  "sub<x>\t%0, zero, %r1")

(define_insn "*negsi2_sext"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(sign_extend:DI
	 (neg:SI (match_operand:SI 1 "reg_or_0_operand" "rO"))))]
  ""
  "subx\t%0, zero, %r1")

(define_insn "ssaddsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ss_plus:SI (match_operand:SI 1 "reg_or_0_operand" "rO")
                    (match_operand:SI 2 "reg_or_0_operand" "rO")))]
  ""
  "addxsc\t%0, %r1, %r2"
  [(set_attr "type" "X01")])

(define_insn "*ssaddsi3_sext"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(sign_extend:DI
	 (ss_plus:SI (match_operand:SI 1 "reg_or_0_operand" "rO")
		     (match_operand:SI 2 "reg_or_0_operand" "rO"))))]
  ""
  "addxsc\t%0, %r1, %r2"
  [(set_attr "type" "X01")])

(define_insn "sssubsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ss_minus:SI (match_operand:SI 1 "reg_or_0_operand" "rO")
                     (match_operand:SI 2 "reg_or_0_operand" "rO")))]
  ""
  "subxsc\t%0, %r1, %r2"
  [(set_attr "type" "X01")])

(define_insn "*sssubsi3_sext"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(sign_extend:DI
	 (ss_minus:SI (match_operand:SI 1 "reg_or_0_operand" "rO")
		      (match_operand:SI 2 "reg_or_0_operand" "rO"))))]
  ""
  "subxsc\t%0, %r1, %r2"
  [(set_attr "type" "X01")])

(define_expand "addsf3"
  [(set (match_operand:SF 0 "register_operand" "")
        (plus:SF (match_operand:SF 1 "register_operand" "")
                 (match_operand:SF 2 "register_operand" "")))]
  ""
{
  rtx result = gen_lowpart (DImode, operands[0]);
  rtx a = gen_lowpart (DImode, operands[1]);
  rtx b = gen_lowpart (DImode, operands[2]);

  rtx tmp = gen_reg_rtx (DImode);
  rtx flags = gen_reg_rtx (DImode);

  emit_insn (gen_insn_fsingle_add1 (tmp, a, b));
  emit_insn (gen_insn_fsingle_addsub2 (tmp, tmp, a, b));
  emit_insn (gen_insn_fsingle_pack1 (flags, tmp));
  emit_insn (gen_insn_fsingle_pack2 (result, tmp, flags));

  DONE;
})

(define_expand "subsf3"
  [(set (match_operand:SF 0 "register_operand" "")
        (minus:SF (match_operand:SF 1 "register_operand" "")
                  (match_operand:SF 2 "register_operand" "")))]
  ""
{
  rtx result = gen_lowpart (DImode, operands[0]);
  rtx a = gen_lowpart (DImode, operands[1]);
  rtx b = gen_lowpart (DImode, operands[2]);

  rtx tmp = gen_reg_rtx (DImode);
  rtx flags = gen_reg_rtx (DImode);

  emit_insn (gen_insn_fsingle_sub1 (tmp, a, b));
  emit_insn (gen_insn_fsingle_addsub2 (tmp, tmp, a, b));
  emit_insn (gen_insn_fsingle_pack1 (flags, tmp));
  emit_insn (gen_insn_fsingle_pack2 (result, tmp, flags));

  DONE;
})

(define_expand "mulsf3"
  [(set (match_operand:SF 0 "register_operand" "")
        (mult:SF (match_operand:SF 1 "register_operand" "")
                 (match_operand:SF 2 "register_operand" "")))]
  ""
{
  rtx result = gen_lowpart (DImode, operands[0]);
  rtx a = gen_lowpart (DImode, operands[1]);
  rtx b = gen_lowpart (DImode, operands[2]);

  rtx tmp1 = gen_reg_rtx (DImode);
  rtx tmp2 = gen_reg_rtx (DImode);
  rtx flags = gen_reg_rtx (DImode);

  emit_insn (gen_insn_fsingle_mul1 (tmp1, a, b));
  emit_insn (gen_insn_fsingle_mul2 (tmp2, tmp1, b));
  emit_insn (gen_insn_fsingle_pack1 (flags, tmp2));
  emit_insn (gen_insn_fsingle_pack2 (result, tmp2, flags));

  DONE;
})

(define_expand "adddf3"
  [(set (match_operand:DF 0 "register_operand" "")
        (plus:DF (match_operand:DF 1 "register_operand" "")
                 (match_operand:DF 2 "register_operand" "")))]
  ""
{
  rtx result = gen_lowpart (DImode, operands[0]);
  rtx a = gen_lowpart (DImode, operands[1]);
  rtx b = gen_lowpart (DImode, operands[2]);

  rtx min = gen_reg_rtx (DImode);
  rtx max = gen_reg_rtx (DImode);
  rtx flags = gen_reg_rtx (DImode);

  emit_insn (gen_insn_fdouble_unpack_min (min, a, b));
  emit_insn (gen_insn_fdouble_unpack_max (max, a, b));
  emit_insn (gen_insn_fdouble_add_flags (flags, a, b));
  emit_insn (gen_insn_fdouble_addsub (max, max, min, flags));
  emit_insn (gen_insn_fdouble_pack1 (result, max, flags));
  emit_insn (gen_insn_fdouble_pack2 (result, result, max, const0_rtx));

  DONE;
})

(define_expand "subdf3"
  [(set (match_operand:DF 0 "register_operand" "")
        (minus:DF (match_operand:DF 1 "register_operand" "")
                  (match_operand:DF 2 "register_operand" "")))]
  ""
{
  rtx result = gen_lowpart (DImode, operands[0]);
  rtx a = gen_lowpart (DImode, operands[1]);
  rtx b = gen_lowpart (DImode, operands[2]);

  rtx min = gen_reg_rtx (DImode);
  rtx max = gen_reg_rtx (DImode);
  rtx flags = gen_reg_rtx (DImode);

  emit_insn (gen_insn_fdouble_unpack_min (min, a, b));
  emit_insn (gen_insn_fdouble_unpack_max (max, a, b));
  emit_insn (gen_insn_fdouble_sub_flags (flags, a, b));
  emit_insn (gen_insn_fdouble_addsub (max, max, min, flags));
  emit_insn (gen_insn_fdouble_pack1 (result, max, flags));
  emit_insn (gen_insn_fdouble_pack2 (result, result, max, const0_rtx));

  DONE;
})

(define_expand "muldf3"
  [(set (match_operand:DF 0 "register_operand" "")
        (mult:DF (match_operand:DF 1 "register_operand" "")
                 (match_operand:DF 2 "register_operand" "")))]
  ""
  ;; TODO: Decide if we should not inline this with -Os.
  ;; "optimize_function_for_speed_p (cfun)"
{
  rtx result = gen_lowpart (DImode, operands[0]);
  rtx a = gen_lowpart (DImode, operands[1]);
  rtx b = gen_lowpart (DImode, operands[2]);

  rtx a_unpacked = gen_reg_rtx (DImode);
  rtx b_unpacked = gen_reg_rtx (DImode);
  rtx flags = gen_reg_rtx (DImode);

  rtx low1 = gen_reg_rtx (DImode);
  rtx low = gen_reg_rtx (DImode);
  rtx low_carry = gen_reg_rtx (DImode);

  rtx mid = gen_reg_rtx (DImode);
  rtx mid_l32 = gen_reg_rtx (DImode);
  rtx mid_r32 = gen_reg_rtx (DImode);

  rtx high1 = gen_reg_rtx (DImode);
  rtx high = gen_reg_rtx (DImode);
  rtx high1_plus_mid_r32 = gen_reg_rtx (DImode);

  /* NOTE: We compute using max(a, 0) and max(b, 0) rather than
     min(a, b) and max(a, b) because for multiply we just need to unpack,
     we don't actually care which is min and which is max. And this
     formulation gives the scheduler more freedom in case one of a or b
     would stall at the start of this code sequence. */
  emit_insn (gen_insn_fdouble_unpack_max (a_unpacked, a, const0_rtx));
  emit_insn (gen_insn_fdouble_unpack_max (b_unpacked, b, const0_rtx));
  emit_insn (gen_insn_fdouble_mul_flags (flags, a, b));

  /* This depends on the fact that the high few bits of the unpacked
     mantissa are zero, so we can't have a carry out from the mid sum. */
  emit_insn (gen_insn_mul_lu_lu (low1, a_unpacked, b_unpacked));
  emit_insn (gen_insn_mul_hu_lu (mid, a_unpacked, b_unpacked));
  emit_insn (gen_insn_mula_hu_lu (mid, mid, b_unpacked, a_unpacked));
  emit_insn (gen_insn_mul_hu_hu (high1, a_unpacked, b_unpacked));

  emit_insn (gen_ashldi3 (mid_l32, mid, GEN_INT (32)));
  emit_insn (gen_lshrdi3 (mid_r32, mid, GEN_INT (32)));

  emit_insn (gen_adddi3 (high1_plus_mid_r32, high1, mid_r32));

  emit_insn (gen_adddi3 (low, low1, mid_l32));
  emit_insn (gen_insn_cmpltu_didi (low_carry, low, mid_l32));

  emit_insn (gen_adddi3 (high, high1_plus_mid_r32, low_carry));

  emit_insn (gen_insn_fdouble_pack1 (result, high, flags));
  emit_insn (gen_insn_fdouble_pack2 (result, result, high, low));

  DONE;
})


;;
;; Shifts
;;

(define_insn "ashl<mode>3"
  [(set (match_operand:I48MODE 0 "register_operand" "=r,r")
	(ashift:I48MODE
	 (match_operand:I48MODE 1 "reg_or_0_operand" "rO,rO")
	 (match_operand:SI 2 "reg_or_u<nbits>bit_operand" "I,rO")))]
  ""
  "@
  shl<x>i\t%0, %r1, %2
  shl<x>\t%0, %r1, %r2"
  [(set_attr "type" "<shift_pipe>,<shift_pipe>")])

(define_insn "*ashlsi3_sext"
  [(set (match_operand:DI 0 "register_operand" "=r,r")
	(sign_extend:DI
	 (ashift:SI
	  (match_operand:SI 1 "reg_or_0_operand" "rO,rO")
	  (match_operand:SI 2 "reg_or_u5bit_operand" "I,rO"))))]
  ""
  "@
  shlxi\t%0, %r1, %2
  shlx\t%0, %r1, %r2"
  [(set_attr "type" "X01,X01")])

(define_insn "ashr<mode>3"
  [(set (match_operand:I48MODE 0 "register_operand" "=r,r")
	(ashiftrt:I48MODE
	 (match_operand:I48MODE 1 "reg_or_0_operand" "rO,rO")
	 (match_operand:SI 2 "reg_or_u<nbits>bit_operand" "I,rO")))]
  ""
  "@
  shrsi\t%0, %r1, %2
  shrs\t%0, %r1, %r2")

(define_insn "*ashrsi3_sext"
  [(set (match_operand:DI 0 "register_operand" "=r,r")
	(sign_extend:DI
	 (ashiftrt:SI (match_operand:SI 1 "reg_or_0_operand" "rO,rO")
		      (match_operand:SI 2 "reg_or_u5bit_operand" "I,rO"))))]
  ""
  "@
  shrsi\t%0, %r1, %2
  shrs\t%0, %r1, %r2")

(define_insn "lshr<mode>3"
  [(set (match_operand:I48MODE 0 "register_operand" "=r,r")
	(lshiftrt:I48MODE
	 (match_operand:I48MODE 1 "reg_or_0_operand" "rO,rO")
	 (match_operand:SI 2 "reg_or_u<nbits>bit_operand" "I,rO")))]
  ""
  "@
  shru<x>i\t%0, %r1, %2
  shru<x>\t%0, %r1, %r2"
  [(set_attr "type" "<shift_pipe>,<shift_pipe>")])
  
(define_insn "*lshrsi3_sext"
  [(set (match_operand:DI 0 "register_operand" "=r,r")
	(sign_extend:DI
	 (lshiftrt:SI
	  (match_operand:SI 1 "reg_or_0_operand" "rO,rO")
	  (match_operand:SI 2 "reg_or_u5bit_operand" "I,rO"))))]
  ""
  "@
  shruxi\t%0, %r1, %2
  shrux\t%0, %r1, %r2"
  [(set_attr "type" "X01,X01")])
  
(define_insn "rotldi3"
  [(set (match_operand:DI 0 "register_operand" "=r,r")
	(rotate:DI (match_operand:DI 1 "reg_or_0_operand" "rO,rO")
                   (match_operand:SI 2 "reg_or_u6bit_operand" "I,rO")))]
  ""
  "@
  rotli\t%0, %r1, %2
  rotl\t%0, %r1, %r2")

(define_insn "insn_shl16insli"
  [(set (match_operand:DI 0 "register_operand" "=r,r")
        (ior:DI
         (ashift:DI
	  (match_operand:DI 1 "reg_or_0_operand" "rO,rO")
	  (const_int 16))
         (match_operand:DI 2 "u16bit_or_const_symbolic_operand" "O,KT")))]
  ""
  "@
   shli\t%0, %r1, 16
   shl16insli\t%0, %r1, %H2"
  [(set_attr "type" "*,X01")])

(define_insn "insn_addr_shl16insli<bitsuffix>"
  [(set (match_operand:I48MODE 0 "register_operand" "=r")
	(unspec:I48MODE
	 [(match_operand:I48MODE 1 "reg_or_0_operand" "rO")
	  (match_operand:I48MODE 2 "const_symbolic_operand" "T")]
	 UNSPEC_INSN_ADDR_SHL16INSLI))]
  ""
  "shl16insli\t%0, %r1, %H2"
  [(set_attr "type" "X01")])


;;
;; Compares
;;

(define_expand "cstore<mode>4"
  [(set (match_operand:DI 0 "register_operand" "")
      (match_operator:DI 1 "ordered_comparison_operator"
         [(match_operand:FI48MODE 2 "reg_or_cint_operand" "")
          (match_operand:FI48MODE 3 "reg_or_cint_operand" "")]))]
  ""
{
  if (!tilegx_emit_setcc (operands, GET_MODE (operands[2])))
    FAIL;
  else
    DONE;
})

 
(define_insn "insn_cmpne_<I48MODE:mode><I48MODE2:mode>"
  [(set (match_operand:I48MODE2 0 "register_operand" "=r")
	(ne:I48MODE2 (match_operand:I48MODE 1 "reg_or_0_operand" "rO")
		     (match_operand:I48MODE 2 "reg_or_cint_operand" "rO")))]
  ""
  "cmpne\t%0, %r1, %r2")
 
(define_insn "insn_cmpeq_<I48MODE:mode><I48MODE2:mode>"
  [(set (match_operand:I48MODE2 0 "register_operand" "=r,r")
	(eq:I48MODE2 (match_operand:I48MODE 1 "reg_or_0_operand" "%rO,rO")
		     (match_operand:I48MODE 2 "reg_or_cint_operand" "I,rO")))]
  ""
  "@
   cmpeqi\t%0, %r1, %2
   cmpeq\t%0, %r1, %r2")

(define_insn "insn_cmplts_<I48MODE:mode><I48MODE2:mode>"
  [(set (match_operand:I48MODE2 0 "register_operand" "=r,r")
	(lt:I48MODE2 (match_operand:I48MODE 1 "reg_or_0_operand" "rO,rO")
		     (match_operand:I48MODE 2 "reg_or_cint_operand" "I,rO")))]
  ""
  "@
   cmpltsi\t%0, %r1, %2
   cmplts\t%0, %r1, %r2")

(define_insn "insn_cmpltu_<I48MODE:mode><I48MODE2:mode>"
  [(set (match_operand:I48MODE2 0 "register_operand" "=r,r")
	(ltu:I48MODE2 (match_operand:I48MODE 1 "reg_or_0_operand" "rO,rO")
		      (match_operand:I48MODE 2 "reg_or_cint_operand" "I,rO")))]
  ""
  "@
   cmpltui\t%0, %r1, %2
   cmpltu\t%0, %r1, %r2"
  [(set_attr "type" "X01,*")])

(define_insn "insn_cmples_<I48MODE:mode><I48MODE2:mode>"
  [(set (match_operand:I48MODE2 0 "register_operand" "=r,r")
	(le:I48MODE2 (match_operand:I48MODE 1 "reg_or_0_operand" "rO,rO")
		     (match_operand:I48MODE 2 "reg_or_cint_operand" "L,rO")))]
  ""
  "@
   cmpltsi\t%0, %r1, %P2
   cmples\t%0, %r1, %r2")

(define_insn "insn_cmpleu_<I48MODE:mode><I48MODE2:mode>"
  [(set (match_operand:I48MODE2 0 "register_operand" "=r,r")
	(leu:I48MODE2 (match_operand:I48MODE 1 "reg_or_0_operand" "rO,rO")
		      (match_operand:I48MODE 2 "reg_or_cint_operand" "Q,rO")))]
  ""
  "@
   cmpltui\t%0, %r1, %P2
   cmpleu\t%0, %r1, %r2"
  [(set_attr "type" "X01,*")])


;;
;; Logical ops
;;

(define_insn "and<mode>3"
  [(set (match_operand:IVNMODE 0 "register_operand" "=r,r,r,r")
	(and:IVNMODE (match_operand:IVNMODE 1 "reg_or_0_operand" "%rO,rO,0,rO")
		     (match_operand:IVNMODE 2 "and_operand" "I,S,M,rO")))]
  ""
  "@
   andi\t%0, %r1, %2
   bfextu\t%0, %r1, %M2
   bfins\t%0, zero, %m2
   and\t%0, %r1, %r2"
  [(set_attr "type" "*,X0,X0,*")])
  
(define_insn "*andsi3_sext"
  [(set (match_operand:DI 0 "register_operand" "=r,r,r,r")
	(sign_extend:DI
	 (and:SI (match_operand:SI 1 "reg_or_0_operand" "%rO,rO,0,rO")
		 (match_operand:SI 2 "and_operand" "I,S,M,rO"))))]
  ""
  "@
   andi\t%0, %r1, %2
   bfextu\t%0, %r1, %M2
   bfins\t%0, zero, %m2
   and\t%0, %r1, %r2"
  [(set_attr "type" "*,X0,X0,*")])
  
(define_insn "anddi3"
  [(set (match_operand:DI 0 "register_operand" "=r,r,r,r,r,r")
	(and:DI (match_operand:DI 1 "reg_or_0_operand" "%rO,rO,rO,rO,0,rO")
                    (match_operand:DI 2 "and_operand" "I,Z0,Z1,S,M,rO")))]
  ""
  "@
   andi\t%0, %r1, %2
   v4int_l\t%0, zero, %r1
   v4int_h\t%0, %r1, zero
   bfextu\t%0, %r1, %M2
   bfins\t%0, zero, %m2
   and\t%0, %r1, %r2"
  [(set_attr "type" "*,X01,X01,X0,X0,*")])
  
(define_insn "ior<mode>3"
  [(set (match_operand:IVMODE 0 "register_operand" "=r,r")
	(ior:IVMODE (match_operand:IVMODE 1 "reg_or_0_operand" "%rO,rO")
                    (match_operand:IVMODE 2 "reg_or_s8bit_operand" "rO,I")))]
  ""
  "@
   or\t%0, %r1, %r2
   ori\t%0, %r1, %2"
  [(set_attr "type" "*,X01")])
  
(define_insn "*iorsi3_sext"
  [(set (match_operand:DI 0 "register_operand" "=r,r")
	(sign_extend:DI
	 (ior:SI (match_operand:SI 1 "reg_or_0_operand" "%rO,rO")
		 (match_operand:SI 2 "reg_or_s8bit_operand" "rO,I"))))]
  ""
  "@
   or\t%0, %r1, %r2
   ori\t%0, %r1, %2"
  [(set_attr "type" "*,X01")])
  
(define_insn "xor<mode>3"
  [(set (match_operand:IVMODE 0 "register_operand" "=r,r")
	(xor:IVMODE (match_operand:IVMODE 1 "reg_or_0_operand" "%rO,rO")
                    (match_operand:IVMODE 2 "reg_or_s8bit_operand" "rO,I")))]
  ""
  "@
   xor\t%0, %r1, %r2
   xori\t%0, %r1, %2"
  [(set_attr "type" "*,X01")])

(define_insn "*xorsi3_sext"
  [(set (match_operand:DI 0 "register_operand" "=r,r")
	(sign_extend:DI
	 (xor:SI (match_operand:SI 1 "reg_or_0_operand" "%rO,rO")
		 (match_operand:SI 2 "reg_or_s8bit_operand" "rO,I"))))]
  ""
  "@
   xor\t%0, %r1, %r2
   xori\t%0, %r1, %2"
  [(set_attr "type" "*,X01")])

(define_insn "clzdi2"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(clz:DI (match_operand:DI 1 "reg_or_0_operand" "rO")))]
  ""
  "clz\t%0, %r1"
  [(set_attr "type" "Y0")])

(define_expand "clzsi2"
  [(set (match_dup 2)
        (ashift:DI (match_operand:SI 1 "reg_or_0_operand" "")
                   (const_int 32)))
   (set (subreg:DI (match_operand:SI 0 "register_operand" "") 0)
	(clz:DI (match_dup 2)))]
   ""
   {
     operands[1] = simplify_gen_subreg (DImode, operands[1], SImode, 0);
     operands[2] = gen_reg_rtx (DImode);
   })

(define_insn "ctz<mode>2"
  [(set (match_operand:I48MODE 0 "register_operand" "=r")
	(ctz:I48MODE (match_operand:DI 1 "reg_or_0_operand" "rO")))]
  ""
  "ctz\t%0, %r1"
  [(set_attr "type" "Y0")])

(define_insn "popcount<mode>2"
  [(set (match_operand:I48MODE 0 "register_operand" "=r")
	(popcount:I48MODE (match_operand:DI 1 "reg_or_0_operand" "rO")))]
  ""
  "pcnt\t%0, %r1"
  [(set_attr "type" "Y0")])

(define_expand "parity<mode>2"
  [(set (match_operand:I48MODE 0 "register_operand" "")
	(parity:I48MODE (match_operand:DI 1 "reg_or_0_operand" "")))]
  ""
  {
    rtx tmp = gen_reg_rtx (<MODE>mode);
    emit_insn (gen_popcount<mode>2 (tmp, operands[1]));
    emit_insn (gen_and<mode>3 (operands[0], tmp, const1_rtx));
    DONE;
  })

(define_insn "bswapdi2"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(bswap:DI (match_operand:DI 1 "reg_or_0_operand" "rO")))]
  ""
  "revbytes\t%0, %r1"
  [(set_attr "type" "Y0")])

(define_expand "bswapsi2"
  [(set (match_operand:SI 0 "register_operand" "")
	(bswap:SI (match_operand:SI 1 "reg_or_0_operand" "")))]
  ""
  {
    rtx tmp = gen_reg_rtx (DImode);
    emit_insn (gen_bswapdi2 (tmp, gen_lowpart (DImode, operands[1])));
    emit_insn (gen_ashrdi3 (gen_lowpart (DImode, operands[0]),
			    tmp, GEN_INT (32)));
    DONE;
  })

(define_insn "one_cmpl<mode>2"
  [(set (match_operand:IVMODE 0 "register_operand" "=r")
	(not:IVMODE (match_operand:IVMODE 1 "reg_or_0_operand" "rO")))]
  ""
  "nor\t%0, %r1, zero")


;;
;; Conditional moves
;;

(define_expand "mov<mode>cc"
  [(set (match_operand:I48MODE 0 "register_operand" "")
	(if_then_else:I48MODE
         (match_operand 1 "comparison_operator" "")
         (match_operand:I48MODE 2 "reg_or_0_operand" "")
         (match_operand:I48MODE 3 "reg_or_0_operand" "")))]
  ""
  { operands[1] = tilegx_emit_conditional_move (operands[1]); })

(define_insn "movcc_insn_<I48MODE2:mode><I48MODE:mode>"
  [(set (match_operand:I48MODE 0 "register_operand" "=r,r,r,r")
	(if_then_else:I48MODE
	 (match_operator 4 "eqne_operator"
	  [(match_operand:I48MODE2 1 "reg_or_0_operand" "rO,rO,rO,rO")
	   (const_int 0)])
	 (match_operand:I48MODE 2 "reg_or_0_operand"	"rO,O,rO,0")
	 (match_operand:I48MODE 3 "reg_or_0_operand"	"O,rO,0,rO")))]
  ""
  "@
   m%c4\t%0, %r1, %r2
   m%C4\t%0, %r1, %r3
   cmov%d4z\t%0, %r1, %r2
   cmov%D4z\t%0, %r1, %r3"
  [(set_attr "type" "*,*,Y0,Y0")])

(define_expand "insn_mz"
  [(set (match_operand:DI 0 "register_operand" "")
	(if_then_else:DI
         (eq (match_operand:DI 1 "reg_or_0_operand" "")
             (const_int 0))
         (match_operand:DI 2 "reg_or_0_operand" "")
         (const_int 0)))])

(define_expand "insn_mnz"
  [(set (match_operand:DI 0 "register_operand" "")
	(if_then_else:DI
         (ne (match_operand:DI 1 "reg_or_0_operand" "")
             (const_int 0))
         (match_operand:DI 2 "reg_or_0_operand" "")
         (const_int 0)))])

(define_expand "insn_cmoveqz"
  [(set (match_operand:DI 0 "register_operand" "")
	(if_then_else:DI
         (eq (match_operand:DI 2 "reg_or_0_operand" "")
             (const_int 0))
         (match_operand:DI 3 "reg_or_0_operand" "")
         (match_operand:DI 1 "reg_or_0_operand" "")))])

(define_expand "insn_cmovnez"
  [(set (match_operand:DI 0 "register_operand" "")
	(if_then_else:DI
         (ne (match_operand:DI 2 "reg_or_0_operand" "")
             (const_int 0))
         (match_operand:DI 3 "reg_or_0_operand" "")
         (match_operand:DI 1 "reg_or_0_operand" "")))])


;;
;; Conversions
;;

(define_insn "zero_extendqi<mode>2"
  [(set (match_operand:I48MODE 0 "register_operand" "=r,r,r")
	(zero_extend:I48MODE (match_operand:QI 1 "move_operand" "rO,U,m")))]
  ""
  "@
   bfextu\t%0, %r1, 0, 7
   ld1u\t%0, %1
   ld1u_add\t%0, %I1, %i1"
  [(set_attr "type" "X0,Y2_2cycle,X1_2cycle")])
  
(define_insn "zero_extendhi<mode>2"
  [(set (match_operand:I48MODE 0 "register_operand" "=r,r,r")
	(zero_extend:I48MODE (match_operand:HI 1 "move_operand" "rO,U,m")))]
  ""
  "@
   bfextu\t%0, %r1, 0, 15
   ld2u\t%0, %1
   ld2u_add\t%0, %I1, %i1"
  [(set_attr "type" "X0,Y2_2cycle,X1_2cycle")])

(define_insn "zero_extendsidi2"
  [(set (match_operand:DI 0 "register_operand" "=r,r,r")
	(zero_extend:DI (match_operand:SI 1 "move_operand" "rO,U,m")))]
  ""
  "@
   v4int_l\t%0, zero, %r1
   ld4u\t%0, %1
   ld4u_add\t%0, %I1, %i1"
  [(set_attr "type" "X01,Y2_2cycle,X1_2cycle")])

(define_insn "extendqi<mode>2"
  [(set (match_operand:I48MODE 0 "register_operand" "=r,r,r")
	(sign_extend:I48MODE (match_operand:QI 1 "move_operand" "rO,U,m")))]
  ""
  "@
   bfexts\t%0, %r1, 0, 7
   ld1s\t%0, %1
   ld1s_add\t%0, %I1, %i1"
  [(set_attr "type" "X0,Y2_2cycle,X1_2cycle")])

(define_insn "extendhi<mode>2"
  [(set (match_operand:I48MODE 0 "register_operand" "=r,r,r")
	(sign_extend:I48MODE (match_operand:HI 1 "move_operand" "rO,U,m")))]
  ""
  "@
   bfexts\t%0, %r1, 0, 15
   ld2s\t%0, %1
   ld2s_add\t%0, %I1, %i1"
  [(set_attr "type" "X0,Y2_2cycle,X1_2cycle")])

;; All SImode integer registers should already be in sign-extended
;; form (see TRULY_NOOP_TRUNCATION and truncdisi2).  We can therefore
;; get rid of register->register instructions if we constrain the
;; source to be in the same register as the destination.
(define_insn_and_split "extendsidi2"
  [(set (match_operand:DI 0 "register_operand" "=r,r,r")
        (sign_extend:DI (match_operand:SI 1 "nonimmediate_operand" "0,U,m")))]
  ""
  "@
   #
   ld4s\t%0, %1
   ld4s_add\t%0, %I1, %i1"
  "&& reload_completed && register_operand (operands[1], VOIDmode)"
  [(const_int 0)]
{
  emit_note (NOTE_INSN_DELETED);
  DONE;
}
  [(set_attr "type" "*,Y2_2cycle,X1_2cycle")])

;; Integer truncation patterns.  Truncating SImode values to smaller
;; modes is a no-op, as it is for most other GCC ports.  Truncating
;; DImode values to SImode is not a no-op since we
;; need to make sure that the lower 32 bits are properly sign-extended
;; (see TRULY_NOOP_TRUNCATION).  Truncating DImode values into modes
;; smaller than SImode is equivalent to two separate truncations:
;;
;;                        A       B
;;    DI ---> HI  ==  DI ---> SI ---> HI
;;    DI ---> QI  ==  DI ---> SI ---> QI
;;
;; Step A needs a real instruction but step B does not.

(define_insn "truncdisi2"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=r,U,m")
        (truncate:SI (match_operand:DI 1 "reg_or_0_operand" "rO,rO,rO")))]
  ""
  "@
   addxi\t%0, %r1, 0
   st4\t%0, %r1
   st4_add\t%I0, %r1, %i0"
  [(set_attr "type" "Y01,Y2,X1")])

(define_insn "truncdihi2"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=r,U,m")
        (truncate:HI (match_operand:DI 1 "reg_or_0_operand" "rO,rO,rO")))]
  ""
  "@
   addxi\t%0, %r1, 0
   st2\t%0, %r1
   st2_add\t%I0, %r1, %i0"
  [(set_attr "type" "Y01,Y2,X1")])

(define_insn "truncdiqi2"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=r,U,m")
        (truncate:QI (match_operand:DI 1 "reg_or_0_operand" "rO,rO,rO")))]
  ""
  "@
   addxi\t%0, %r1, 0
   st1\t%0, %r1
   st1_add\t%I0, %r1, %i0"
  [(set_attr "type" "Y01,Y2,X1")])

;; Combiner patterns to optimize away unnecessary truncates.

(define_insn "*zero_extendsidi_truncdisi"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(zero_extend:DI
	 (truncate:SI (match_operand:DI 1 "reg_or_0_operand" "rO"))))]
  ""
  "v4int_l\t%0, zero, %r1"
  [(set_attr "type" "X01")])

(define_insn "*addsi_truncdisi"
  [(set (match_operand:SI 0 "register_operand" "=r,r,r")
	(plus:SI
	 (truncate:SI (match_operand:DI 1 "reg_or_0_operand" "%rO,rO,rO"))
	 (match_operand:SI 2 "add_operand" "r,I,JT")))]
  ""
  "@
   addx\t%0, %r1, %r2
   addxi\t%0, %r1, %2
   addxli\t%0, %r1, %H2"
  [(set_attr "type" "*,*,X01")])

(define_insn "*addsi_truncdisi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI
	 (truncate:SI (match_operand:DI 1 "reg_or_0_operand" "rO"))
	 (truncate:SI (match_operand:DI 2 "reg_or_0_operand" "rO"))))]
  ""
  "addx\t%0, %r1, %r2")

(define_insn "*ashldi_truncdisi"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(ashift:DI
	 (match_operand:DI 1 "reg_or_0_operand" "rO")
	 (truncate:SI (match_operand:DI 2 "reg_or_u6bit_operand" "rO"))))]
  ""
  "shl\t%0, %r1, %r2")

(define_insn "*ashlsi_truncdisi"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(ashift:SI
	 (truncate:SI (match_operand:DI 1 "reg_or_0_operand" "rO,rO"))
	 (match_operand:SI 2 "reg_or_u5bit_operand" "I,rO")))]
  ""
  "@
  shlxi\t%0, %r1, %2
  shlx\t%0, %r1, %r2"
  [(set_attr "type" "X01,X01")])

(define_insn "*ashlsi_truncdisi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ashift:SI
	 (truncate:SI (match_operand:DI 1 "reg_or_0_operand" "rO"))
	 (truncate:SI (match_operand:DI 2 "reg_or_0_operand" "rO"))))]
  ""
  "shlx\t%0, %r1, %r2"
  [(set_attr "type" "X01")])

(define_insn "*ashrdi3_truncdisi"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(ashiftrt:DI
	 (match_operand:DI 1 "reg_or_0_operand" "rO")
	 (truncate:SI (match_operand:DI 2 "reg_or_u6bit_operand" "rO"))))]
  ""
  "shrs\t%0, %r1, %r2")

(define_insn "*lshrsi_truncdisi"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(lshiftrt:SI
	 (truncate:SI (match_operand:DI 1 "reg_or_0_operand" "rO,rO"))
	 (match_operand:SI 2 "reg_or_u5bit_operand" "I,rO")))]
  ""
  "@
  shruxi\t%0, %r1, %2
  shrux\t%0, %r1, %r2"
  [(set_attr "type" "X01,X01")])

(define_insn "*lshrsi_truncdisi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(lshiftrt:SI
	 (truncate:SI (match_operand:DI 1 "reg_or_0_operand" "rO"))
	 (truncate:SI (match_operand:DI 2 "reg_or_0_operand" "rO"))))]
  ""
  "shrux\t%0, %r1, %r2"
  [(set_attr "type" "X01")])

(define_insn "*lshrdi_truncdisi"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(lshiftrt:DI
	 (match_operand:DI 1 "reg_or_0_operand" "rO")
	 (truncate:SI (match_operand:DI 2 "reg_or_u6bit_operand" "rO"))))]
  ""
  "shru\t%0, %r1, %r2")

(define_insn "*rotldi_truncdisi"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(rotate:DI
	 (match_operand:DI 1 "reg_or_0_operand" "rO")
	 (truncate:SI (match_operand:DI 2 "reg_or_u6bit_operand" "rO"))))]
  ""
  "rotl\t%0, %r1, %r2")


;;
;; Multiplies
;;

(define_insn "mulsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(mult:SI (match_operand:SI 1 "reg_or_0_operand" "%rO")
                 (match_operand:SI 2 "reg_or_0_operand" "rO")))]
  ""
  "mulx\t%0, %r1, %r2"
  [(set_attr "type" "Y0_2cycle")])

(define_insn "mulsidi3"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(mult:DI (sign_extend:DI
                  (match_operand:SI 1 "reg_or_0_operand" "%rO"))
		 (sign_extend:DI
                  (match_operand:SI 2 "reg_or_0_operand" "rO"))))]
  ""
  "mul_ls_ls\t%0, %r1, %r2"
  [(set_attr "type" "Y0_2cycle")])

(define_insn "umulsidi3"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(mult:DI (zero_extend:DI
                  (match_operand:SI 1 "reg_or_0_operand" "%rO"))
		 (zero_extend:DI
                  (match_operand:SI 2 "reg_or_0_operand" "rO"))))]
  ""
  "mul_lu_lu\t%0, %r1, %r2"
  [(set_attr "type" "Y0_2cycle")])

(define_expand "muldi3"
  [(set (match_operand:DI 0 "register_operand" "")
        (unspec:DI [(match_operand:DI 1 "nonmemory_operand" "")
                    (match_operand:DI 2 "nonmemory_operand" "")]
                   UNSPEC_INSN_MUL_HU_LU))
   (set (match_dup 0)
        (unspec:DI [(match_dup 0) (match_dup 2) (match_dup 1)]
                   UNSPEC_INSN_MULA_HU_LU))
   (set (match_dup 0)
        (ashift:DI (match_dup 0) (const_int 32)))
   (set (match_dup 0)
        (unspec:DI [(match_dup 0) (match_dup 2) (match_dup 1)]
                   UNSPEC_INSN_MULA_LU_LU))]
  ""
  {
    operands[1] = force_reg (DImode, operands[1]);
    operands[1] = make_safe_from (operands[1], operands[0]);

    if (tilegx_expand_muldi (operands[0], operands[1], operands[2]))
      DONE;
    else
      {
        operands[2] = force_reg (DImode, operands[2]);
        operands[2] = make_safe_from (operands[2], operands[0]);
      }
  })

(define_insn "usmulsidi3"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(mult:DI (zero_extend:DI
		  (match_operand:SI 1 "reg_or_0_operand" "rO"))
		 (sign_extend:DI
		  (match_operand:SI 2 "reg_or_0_operand" "rO"))))]
  ""
  "mul_ls_lu\t%0, %r2, %r1"
  [(set_attr "type" "X0_2cycle")])
  
(define_insn "maddsidi4"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (plus:DI
         (mult:DI (sign_extend:DI
                   (match_operand:SI 1 "reg_or_0_operand" "rO"))
                  (sign_extend:DI
                   (match_operand:SI 2 "reg_or_0_operand" "rO")))
         (match_operand:DI 3 "register_operand" "0")))]
  ""
  "mula_ls_ls\t%0, %r1, %r2"
  [(set_attr "type" "Y0_2cycle")])

(define_insn "umaddsidi4"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (plus:DI
         (mult:DI (zero_extend:DI
                   (match_operand:SI 1 "reg_or_0_operand" "rO"))
                  (zero_extend:DI
                   (match_operand:SI 2 "reg_or_0_operand" "rO")))
         (match_operand:DI 3 "register_operand" "0")))]
  ""
  "mula_lu_lu\t%0, %r1, %r2"
  [(set_attr "type" "Y0_2cycle")])

(define_expand "smulsi3_highpart"
  [(set (match_dup 3)
        (mult:DI (sign_extend:DI (match_operand:SI 1 "reg_or_0_operand" ""))
                 (sign_extend:DI (match_operand:SI 2 "reg_or_0_operand" ""))))
   (set (match_dup 4)
        (ashiftrt:DI (match_dup 3) (const_int 32)))
   (set (match_operand:SI 0 "register_operand" "")
	(truncate:SI (match_dup 4)))]
  ""
  {
    operands[3] = gen_reg_rtx (DImode);
    operands[4] = gen_reg_rtx (DImode);
  })

(define_expand "umulsi3_highpart"
  [(set (match_dup 3)
        (mult:DI (zero_extend:DI (match_operand:SI 1 "reg_or_0_operand" ""))
                 (zero_extend:DI (match_operand:SI 2 "reg_or_0_operand" ""))))
   (set (match_dup 4)
        (lshiftrt:DI (match_dup 3) (const_int 32)))
   (set (match_operand:SI 0 "register_operand" "")
	(truncate:SI (match_dup 4)))]
  ""
  {
    operands[3] = gen_reg_rtx (DImode);
    operands[4] = gen_reg_rtx (DImode);
  })

(define_expand "smuldi3_highpart"
  [(set (match_operand:DI 0 "register_operand" "")
        (truncate:DI
         (ashiftrt:TI 
          (mult:TI (sign_extend:TI (match_operand:DI 1 "reg_or_0_operand" ""))
                   (sign_extend:TI (match_operand:DI 2 "reg_or_0_operand" "")))
          (const_int 64))))]
  ""
  {
    tilegx_expand_smuldi3_highpart (operands[0], operands[1], operands[2]);
    DONE;
  })

(define_expand "umuldi3_highpart"
  [(set (match_operand:DI 0 "register_operand" "")
	(truncate:DI
	 (lshiftrt:TI
	  (mult:TI (zero_extend:TI (match_operand:DI 1 "reg_or_0_operand" ""))
		   (zero_extend:TI (match_operand:DI 2 "reg_or_0_operand" "")))
	  (const_int 64))))]
  ""
{
  tilegx_expand_umuldi3_highpart (operands[0], operands[1], operands[2]);
  DONE;
})


;;
;; Divide stubs.  These exist to work around a bug in expmed.c, which
;; will not attempt to convert a divide by constant into a multiply
;; unless there is a pattern for a divide of the same mode.  The end
;; result is a 32-bit divide turns into 64-bit multiply.
;;

(define_expand "divsi3"
  [(set (match_operand:SI 0 "register_operand" "")
        (div:SI (match_operand:SI 1 "reg_or_0_operand" "")
                (match_operand:SI 2 "reg_or_0_operand" "")))]
  ""
{
  FAIL;
})

(define_expand "udivsi3"
  [(set (match_operand:SI 0 "register_operand" "")
        (udiv:SI (match_operand:SI 1 "reg_or_0_operand" "")
                 (match_operand:SI 2 "reg_or_0_operand" "")))]
  ""
{
  FAIL;
})


;;
;; Loops
;;

;; Define the subtract-one-and-jump insns so loop.c knows what to
;; generate.
(define_expand "doloop_end"
  [(use (match_operand 0 "" ""))    ;; loop pseudo
   (use (match_operand 1 "" ""))    ;; iterations; zero if unknown
   (use (match_operand 2 "" ""))    ;; max iterations
   (use (match_operand 3 "" ""))    ;; loop level
   (use (match_operand 4 "" ""))    ;; label
   (use (match_operand 5 "" ""))]   ;; flag: 1 if loop entered at top, else 0
   ""
{
  if (optimize > 0 && flag_modulo_sched)
  {
     rtx s0;
     rtx bcomp;
     rtx loc_ref;
     enum machine_mode mode = GET_MODE (operands[0]);

     /* only do inner loop  */
     if (INTVAL (operands[3]) > 1)
       FAIL;
     /* only deal with loop counters in SImode or DImode  */
     if (mode != SImode && mode != DImode)
       FAIL;

     s0 = operands [0];
     emit_move_insn (s0, gen_rtx_PLUS (mode, s0, GEN_INT (-1)));
     bcomp = gen_rtx_NE(mode, s0, const0_rtx);
     loc_ref = gen_rtx_LABEL_REF (VOIDmode, operands [4]);
     emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
                                  gen_rtx_IF_THEN_ELSE (VOIDmode, bcomp,
                                                        loc_ref, pc_rtx)));
     DONE;
  }
  else
     FAIL;

})

;;
;; Prologue/epilogue
;;
(define_expand "prologue"
  [(const_int 0)]
  ""
{
  tilegx_expand_prologue ();
  DONE;
})

(define_expand "epilogue"
  [(const_int 0)]
  ""
{
  tilegx_expand_epilogue (false);
  DONE;
})

(define_expand "sibcall_epilogue"
  [(const_int 0)]
  ""
{
  tilegx_expand_epilogue (true);
  DONE;
})
 
;;
;; Stack manipulations
;;

;; An insn to allocate new stack space for dynamic use (e.g., alloca).
(define_expand "allocate_stack"
  [(set (match_operand 0 "register_operand" "")
	(minus (reg 54) (match_operand 1 "nonmemory_operand" "")))
   (set (reg 54)
	(minus (reg 54) (match_dup 1)))]
  ""
  "tilegx_allocate_stack (operands[0], operands[1]); DONE;")

;;
;; Branches
;;

(define_expand "call"
  [(parallel [(call (match_operand:DI 0 "call_operand" "")
		    (match_operand 1 "" ""))
              (use (reg:DI 54))
	      (clobber (reg:DI 55))])]
  ""
{
  rtx orig_addr = XEXP (operands[0], 0);
  rtx addr;
  if (GET_CODE (orig_addr) == SYMBOL_REF)
    {
      if (tilegx_cmodel == CM_LARGE)
        {
          addr = gen_reg_rtx (Pmode);
          tilegx_expand_set_const64 (addr, orig_addr);
          operands[0] = gen_rtx_MEM (DImode, addr);
        }
      else if (tilegx_cmodel == CM_LARGE_PIC)
        {
          crtl->uses_pic_offset_table = 1;
          addr = gen_reg_rtx (Pmode);
	  if (SYMBOL_REF_LOCAL_P (orig_addr))
	    tilegx_compute_pcrel_address (addr, orig_addr);
	  else
	    tilegx_compute_pcrel_plt_address (addr, orig_addr);
          operands[0] = gen_rtx_MEM (DImode, addr);
        }
    }
})

(define_insn "*call_insn"
  [(call (mem:DI (match_operand:I48MODE 0 "call_address_operand" "rO,i"))
	 (match_operand 1 "" ""))
   (use (reg:DI 54))
   (clobber (reg:DI 55))]
  ""
  "@
   jalr\t%r0
   jal\t%p0"
  [(set_attr "type" "Y1,X1")])

(define_expand "call_value"
  [(parallel [(set (match_operand 0 "register_operand" "")
		   (call (match_operand:DI 1 "call_operand" "")
			 (match_operand 2 "" "")))
              (use (reg:DI 54))
	      (clobber (reg:DI 55))])]
  ""
{
  rtx orig_addr = XEXP (operands[1], 0);
  rtx addr;
  if (GET_CODE (orig_addr) == SYMBOL_REF)
    {
      if (tilegx_cmodel == CM_LARGE)
        {
          addr = gen_reg_rtx (Pmode);
          tilegx_expand_set_const64 (addr, orig_addr);
          operands[1] = gen_rtx_MEM (DImode, addr);
        }
      else if (tilegx_cmodel == CM_LARGE_PIC)
        {
          crtl->uses_pic_offset_table = 1;
          addr = gen_reg_rtx (Pmode);
	  if (SYMBOL_REF_LOCAL_P (orig_addr))
	    tilegx_compute_pcrel_address (addr, orig_addr);
	  else
	    tilegx_compute_pcrel_plt_address (addr, orig_addr);
          operands[1] = gen_rtx_MEM (DImode, addr);
        }
    }
})

(define_insn "*call_value_insn"
  [(set (match_operand 0 "register_operand" "=r,r")
	(call (mem:DI (match_operand:I48MODE 1 "call_address_operand" "rO,i"))
	      (match_operand 2 "" "")))
   (use (reg:DI 54))
   (clobber (reg:DI 55))]
  ""
  "@
   jalr\t%r1
   jal\t%p1"
  [(set_attr "type" "Y1,X1")])

(define_expand "sibcall"
  [(parallel [(call (match_operand:DI 0 "call_operand" "")
		    (match_operand 1 "" ""))
	      (use (reg:DI 54))])]
  ""
  "")

(define_insn "*sibcall_insn"
  [(call (mem:DI (match_operand:I48MODE 0 "call_address_operand" "rO,i"))
	 (match_operand 1 "" ""))
   (use (reg:DI 54))]
  "SIBLING_CALL_P(insn)"
  "@
   jr\t%r0
   j\t%p0"
  [(set_attr "type" "X1,X1")])

(define_expand "sibcall_value"
  [(parallel [(set (match_operand 0 "" "")
		   (call (match_operand:DI 1 "call_operand" "")
			 (match_operand 2 "" "")))
	      (use (reg:DI 54))])]
  ""
  "")

(define_insn "*sibcall_value"
  [(set (match_operand 0 "" "")
	(call (mem:DI (match_operand:I48MODE 1 "call_address_operand" "rO,i"))
	      (match_operand 2 "" "")))
   (use (reg:DI 54))]
  "SIBLING_CALL_P(insn)"
  "@
   jr\t%r1
   j\t%p1"
  [(set_attr "type" "X1,X1")])

(define_insn "jump"
  [(set (pc) (label_ref (match_operand 0 "" "")))]
  ""
  "j\t%l0"
  [(set_attr "type" "X1")])

(define_insn "indirect_jump"
  [(set (pc) (match_operand 0 "pointer_operand" "rO"))]
  ""
  "jr\t%r0"
  [(set_attr "type" "Y1")])

(define_expand "return"
  [(parallel
    [(return)
     (use (reg:DI 55))])]
  "tilegx_can_use_return_insn_p ()"
  "")

(define_insn "_return"
  [(return)
   (use (reg:DI 55))]
  "reload_completed"
  "jrp\tlr"
  [(set_attr "type" "Y1")])

(define_expand "tablejump"
  [(set (pc) (match_operand 0 "pointer_operand" ""))
   (use (label_ref (match_operand 1 "" "")))]
  ""
{
  tilegx_expand_tablejump (operands[0], operands[1]);
  DONE;
})

(define_insn "tablejump_aux"
  [(set (pc) (match_operand 0 "pointer_operand" "rO"))
   (use (label_ref (match_operand 1 "" "")))]
  ""
  "jr\t%r0"
  [(set_attr "type" "Y1")])

;; Call subroutine returning any type.
(define_expand "untyped_call"
  [(parallel [(call (match_operand 0 "" "")
		    (const_int 0))
	      (match_operand 1 "" "")
	      (match_operand 2 "" "")])]
  ""
{
  int i;

  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)] UNSPEC_BLOCKAGE)]
  ""
  "pseudo"
  [(set_attr "type" "nothing")
   (set_attr "length" "0")])

;; Internal expanders to prevent memory ops from moving around frame
;; allocation/deallocation.
;;
;; TODO: really this clobber should just clobber the frame memory.  Is
;; this possibly by clobbering memory @ the sp reg (as alpha does?)
;; or by explicitly setting the alias set to the frame?
(define_insn "sp_adjust"
  [(set (match_operand:DI 0 "register_operand" "=r,r,r")
        (plus:DI
         (match_operand:DI 1 "register_operand" "%r,r,r")
         (match_operand:DI 2 "add_operand" "r,I,JT")))
   (clobber (mem:BLK (scratch)))]
 ""
 "@
  add\t%0, %1, %2
  addi\t%0, %1, %2
  addli\t%0, %1, %H2"
 [(set_attr "type" "*,*,X01")])

(define_insn "sp_adjust_32bit"
  [(set (match_operand:SI 0 "register_operand" "=r,r,r")
        (plus:SI
         (match_operand:SI 1 "register_operand" "%r,r,r")
         (match_operand:SI 2 "add_operand" "r,I,JT")))
   (clobber (mem:BLK (scratch)))]
 ""
 "@
  addx\t%0, %1, %2
  addxi\t%0, %1, %2
  addxli\t%0, %1, %H2"
 [(set_attr "type" "*,*,X01")])

;; Used for move sp, r52, to pop a stack frame.  We need to make sure
;; that stack frame memory operations have been issued before we do
;; this.  TODO: see above TODO.
(define_insn "sp_restore<bitsuffix>"
  [(set (match_operand:I48MODE 0 "register_operand" "=r")
        (match_operand:I48MODE 1 "register_operand" "r"))
   (clobber (mem:BLK (scratch)))]
 ""
 "move\t%0, %1")

(define_insn "nop"
  [(const_int 0)]
  ""
  "nop"
  [(set_attr "type" "Y01")])


;;
;; Conditional branches
;;

(define_expand "cbranch<mode>4"
  [(set (pc)
      (if_then_else (match_operator 0 "ordered_comparison_operator"
                     [(match_operand:FI48MODE 1 "reg_or_cint_operand")
                        (match_operand:FI48MODE 2 "reg_or_cint_operand")])
                      (label_ref (match_operand 3 ""))
                    (pc)))]
   ""
{
  tilegx_emit_conditional_branch (operands, GET_MODE (operands[1]));
  DONE;
})

(define_insn "*bcc_normal<mode>"
  [(set (pc)
	(if_then_else
	 (match_operator 1 "signed_comparison_operator"
			 [(match_operand:I48MODE 2 "reg_or_0_operand" "rO")
			  (const_int 0)])
	 (label_ref (match_operand 0 "" ""))
	 (pc)))]
  ""
  { return tilegx_output_cbranch (insn, operands, false); }
  [(set_attr "type" "X1_branch")])

(define_insn "*bcc_reverse<mode>"
  [(set (pc)
	(if_then_else
	 (match_operator 1 "signed_comparison_operator"
			 [(match_operand:I48MODE 2 "reg_or_0_operand" "rO")
			  (const_int 0)])
	 (pc)
	 (label_ref (match_operand 0 "" ""))))]
  ""
  { return tilegx_output_cbranch (insn, operands, true); }
  [(set_attr "type" "X1_branch")])

(define_insn "*blbs_normal<mode>"
  [(set (pc)
	(if_then_else
	 (ne (zero_extract:I48MODE
              (match_operand:I48MODE 1 "reg_or_0_operand" "rO")
              (const_int 1)
              (const_int 0))
	     (const_int 0))
	 (label_ref (match_operand 0 "" ""))
	 (pc)))]
  ""
  { return tilegx_output_cbranch_with_opcode (insn, operands, "blbs", "blbc",
					      1); }
  [(set_attr "type" "X1_branch")])

(define_insn "*blbc_normal<mode>"
  [(set (pc)
	(if_then_else
	 (eq (zero_extract:I48MODE
              (match_operand:I48MODE 1 "reg_or_0_operand" "rO")
              (const_int 1)
              (const_int 0))
	     (const_int 0))
	 (label_ref (match_operand 0 "" ""))
	 (pc)))]
  ""
  { return tilegx_output_cbranch_with_opcode (insn, operands, "blbc", "blbs",
					      1); }
  [(set_attr "type" "X1_branch")])

;; Note that __insn_mf() expands to this.
(define_expand "memory_barrier"
  [(set (match_dup 0)
	(unspec_volatile:BLK [(match_dup 0)] UNSPEC_MF))]
  ""
{
  operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
  MEM_VOLATILE_P (operands[0]) = 1;
})

(define_insn "*memory_barrier"
  [(set (match_operand:BLK 0 "" "")
	(unspec_volatile:BLK [(match_dup 0)] UNSPEC_MF))]
  ""
  "mf"
  [(set_attr "type" "X1")])

(define_insn "prefetch"
  [(prefetch (match_operand 0 "address_operand" "rO")
             (match_operand 1 "const_int_operand" "")
             (match_operand 2 "const_int_operand" ""))]
  ""
{
  switch (INTVAL (operands[2]))
    {
      case 0:
      case 1: return "prefetch_l3\t%r0";
      case 2: return "prefetch_l2\t%r0";
      case 3: return "prefetch_l1\t%r0";
      default: gcc_unreachable ();
    }
}
  [(set_attr "type" "Y2")])


;;
;; "__insn" Intrinsics (some expand directly to normal patterns above).
;;

(define_insn "insn_bfexts"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "u6bit_cint_operand" "n")
                    (match_operand:DI 3 "u6bit_cint_operand" "n")]
                   UNSPEC_INSN_BFEXTS))]
  ""
  "bfexts\t%0, %r1, %2, %3"
  [(set_attr "type" "X0")])

(define_insn "insn_bfextu"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "u6bit_cint_operand" "n")
                    (match_operand:DI 3 "u6bit_cint_operand" "n")]
                   UNSPEC_INSN_BFEXTU))]
  ""
  "bfextu\t%0, %r1, %2, %3"
  [(set_attr "type" "X0")])

(define_insn "insn_bfins"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "u6bit_cint_operand" "n")
                    (match_operand:DI 4 "u6bit_cint_operand" "n")]
                   UNSPEC_INSN_BFINS))]
   ""
   "bfins\t%0, %r2, %3, %4"
   [(set_attr "type" "X0")])

(define_insn "insn_cmpexch<four_if_si>"
  [(set (match_operand:I48MODE 0 "register_operand" "=r")
        (mem:I48MODE (match_operand 1 "pointer_operand" "rO")))
   (set (mem:I48MODE (match_dup 1))
        (unspec_volatile:I48MODE
	 [(mem:I48MODE (match_dup 1))
	  (reg:I48MODE TILEGX_CMPEXCH_REG)
	  (match_operand:I48MODE 2 "reg_or_0_operand" "rO")]
	 UNSPEC_INSN_CMPEXCH))]
  ""
  "cmpexch<four_if_si>\t%0, %r1, %r2"
  [(set_attr "type" "X1_remote")])

(define_insn "insn_cmul"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_CMUL))]
  ""
  "cmul\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_cmula"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_CMULA))]
  ""
  "cmula\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_cmulaf"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_CMULAF))]
  ""
  "cmulaf\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_cmulf"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_CMULF))]
  ""
  "cmulf\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_cmulfr"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_CMULFR))]
  ""
  "cmulfr\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_cmulh"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_CMULH))]
  ""
  "cmulh\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_cmulhr"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_CMULHR))]
  ""
  "cmulhr\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_crc32_32"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_CRC32_32))]
  ""
  "crc32_32\t%0, %r1, %r2"
  [(set_attr "type" "X0")])

(define_insn "insn_crc32_8"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_CRC32_8))]
  ""
  "crc32_8\t%0, %r1, %r2"
  [(set_attr "type" "X0")])

(define_insn "insn_dblalign"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand 3 "pointer_operand" "rO")]
                   UNSPEC_INSN_DBLALIGN))]
  ""
  "dblalign\t%0, %r2, %r3"
  [(set_attr "type" "X0")])

(define_insn "insn_dblalign2"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_DBLALIGN2))]
  ""
  "dblalign2\t%0, %r1, %r2"
  [(set_attr "type" "X01")])

(define_insn "insn_dblalign4"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_DBLALIGN4))]
  ""
  "dblalign4\t%0, %r1, %r2"
  [(set_attr "type" "X01")])

(define_insn "insn_dblalign6"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_DBLALIGN6))]
  ""
  "dblalign6\t%0, %r1, %r2"
  [(set_attr "type" "X01")])

(define_insn "insn_dtlbpr"
  [(unspec_volatile:VOID [(match_operand:DI 0 "reg_or_0_operand" "rO")]
                         UNSPEC_INSN_DTLBPR)]
  ""
  "dtlbpr\t%r0"
  [(set_attr "type" "X1")])

(define_insn "insn_exch<four_if_si>"
  [(set (match_operand:I48MODE 0 "register_operand" "=r")
        (mem:I48MODE (match_operand 1 "pointer_operand" "rO")))
   (set (mem:I48MODE (match_dup 1))
	(unspec_volatile:I48MODE
	 [(match_operand:I48MODE 2 "reg_or_0_operand" "rO")]
	 UNSPEC_INSN_EXCH))]
  ""
  "exch<four_if_si>\t%0, %r1, %r2"
  [(set_attr "type" "X1_remote")])

(define_insn "insn_fdouble_add_flags"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_FDOUBLE_ADD_FLAGS))]
  ""
  "fdouble_add_flags\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_fdouble_addsub"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_FDOUBLE_ADDSUB))]
  ""
  "fdouble_addsub\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_fdouble_mul_flags"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_FDOUBLE_MUL_FLAGS))]
  ""
  "fdouble_mul_flags\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_fdouble_pack1"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_FDOUBLE_PACK1))]
  ""
  "fdouble_pack1\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_fdouble_pack2"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_FDOUBLE_PACK2))]
  ""
  "fdouble_pack2\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_fdouble_sub_flags"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_FDOUBLE_SUB_FLAGS))]
  ""
  "fdouble_sub_flags\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_fdouble_unpack_max"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_FDOUBLE_UNPACK_MAX))]
  ""
  "fdouble_unpack_max\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_fdouble_unpack_min"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_FDOUBLE_UNPACK_MIN))]
  ""
  "fdouble_unpack_min\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_fetchadd<four_if_si>"
  [(set (match_operand:I48MODE 0 "register_operand" "=r")
        (unspec_volatile:I48MODE
	 [(mem:I48MODE (match_operand 1 "pointer_operand" "rO"))]
	 UNSPEC_ATOMIC))
   (set (mem:I48MODE (match_dup 1))
        (plus:I48MODE (mem:I48MODE (match_dup 1))
                      (match_operand:I48MODE 2 "reg_or_0_operand" "rO")))]
  ""
  "fetchadd<four_if_si>\t%0, %r1, %r2"
  [(set_attr "type" "X1_remote")])

(define_insn "insn_fetchaddgez<four_if_si>"
  [(set (match_operand:I48MODE 0 "register_operand" "=r")
        (unspec_volatile:I48MODE
	 [(mem:I48MODE (match_operand 1 "pointer_operand" "rO"))]
	 UNSPEC_ATOMIC))
   (set (mem:I48MODE (match_dup 1))
        (unspec:I48MODE [(match_operand:I48MODE 2 "reg_or_0_operand" "rO")
                         (mem:I48MODE (match_dup 1))]
                        UNSPEC_INSN_FETCHADDGEZ))]
  ""
  "fetchaddgez<four_if_si>\t%0, %r1, %r2"
  [(set_attr "type" "X1_remote")])

(define_insn "insn_fetchand<four_if_si>"
  [(set (match_operand:I48MODE 0 "register_operand" "=r")
        (unspec_volatile:I48MODE
	 [(mem:I48MODE (match_operand 1 "pointer_operand" "rO"))]
	 UNSPEC_ATOMIC))
   (set (mem:I48MODE (match_dup 1))
        (and:I48MODE (mem:I48MODE (match_dup 1))
                     (match_operand:I48MODE 2 "reg_or_0_operand" "rO")))]
  ""
  "fetchand<four_if_si>\t%0, %r1, %r2"
  [(set_attr "type" "X1_remote")])

(define_insn "insn_fetchor<four_if_si>"
  [(set (match_operand:I48MODE 0 "register_operand" "=r")
        (unspec_volatile:I48MODE
	 [(mem:I48MODE (match_operand 1 "pointer_operand" "rO"))]
	 UNSPEC_ATOMIC))
   (set (mem:I48MODE (match_dup 1))
        (ior:I48MODE (mem:I48MODE (match_dup 1))
                     (match_operand:I48MODE 2 "reg_or_0_operand" "rO")))]
  ""
  "fetchor<four_if_si>\t%0, %r1, %r2"
  [(set_attr "type" "X1_remote")])

(define_insn "insn_finv"
  [(unspec_volatile:VOID [(match_operand 0 "pointer_operand" "rO")]
                         UNSPEC_INSN_FINV)]
  ""
  "finv\t%r0"
  [(set_attr "type" "X1")])

(define_insn "insn_flush"
  [(unspec_volatile:VOID [(match_operand 0 "pointer_operand" "rO")]
                         UNSPEC_INSN_FLUSH)]
  ""
  "flush\t%r0"
  [(set_attr "type" "X1")])

(define_insn "insn_flushwb"
  [(unspec_volatile:VOID [(const_int 0)] UNSPEC_INSN_FLUSHWB)]
  ""
  "flushwb"
  [(set_attr "type" "X1")])

(define_insn "insn_fnop"
  [(unspec_volatile:VOID [(const_int 0)] UNSPEC_INSN_FNOP)]
  ""
  "fnop")

(define_insn "insn_fsingle_add1"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_FSINGLE_ADD1))]
  ""
  "fsingle_add1\t%0, %r1, %r2"
  [(set_attr "type" "X0")])

(define_insn "insn_fsingle_addsub2"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_FSINGLE_ADDSUB2))]
  ""
  "fsingle_addsub2\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_fsingle_mul1"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_FSINGLE_MUL1))]
  ""
  "fsingle_mul1\t%0, %r1, %r2"
  [(set_attr "type" "X0")])

(define_insn "insn_fsingle_mul2"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_FSINGLE_MUL2))]
  ""
  "fsingle_mul2\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_fsingle_pack1"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_FSINGLE_PACK1))]
  ""
  "fsingle_pack1\t%0, %r1"
  [(set_attr "type" "Y0_2cycle")])

(define_insn "insn_fsingle_pack2"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_FSINGLE_PACK2))]
  ""
  "fsingle_pack2\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_fsingle_sub1"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_FSINGLE_SUB1))]
  ""
  "fsingle_sub1\t%0, %r1, %r2"
  [(set_attr "type" "X0")])

(define_insn "insn_drain"
  [(unspec_volatile:VOID [(const_int 0)] UNSPEC_INSN_DRAIN)]
  ""
  "drain"
  [(set_attr "type" "cannot_bundle")])

(define_insn "insn_icoh"
  [(unspec_volatile:VOID [(match_operand 0 "pointer_operand" "rO")] 
                         UNSPEC_INSN_ICOH)]
  ""
  "icoh\t%r0"
  [(set_attr "type" "X1")])

(define_insn "insn_ill"
  [(unspec_volatile:VOID [(const_int 0)] UNSPEC_INSN_ILL)]
  ""
  "ill"
  [(set_attr "type" "cannot_bundle")])

(define_insn "insn_info"
  [(unspec_volatile:VOID [(match_operand:DI 0 "s8bit_cint_operand" "i")]
                         UNSPEC_INSN_INFO)]
  ""
  "info\t%0")

(define_insn "insn_infol"
  [(unspec_volatile:VOID [(match_operand:DI 0 "s16bit_cint_operand" "i")]
                         UNSPEC_INSN_INFOL)]
  ""
  "infol\t%0"
  [(set_attr "type" "X01")])

(define_insn "insn_inv"
  [(unspec_volatile:VOID [(match_operand 0 "pointer_operand" "rO")]
                         UNSPEC_INSN_INV)]
  ""
  "inv\t%r0"
  [(set_attr "type" "X1")])

;; loads

(define_expand "insn_ld"
  [(set (match_operand:DI 0 "register_operand" "")
	(mem:DI (match_operand 1 "pointer_operand" "")))]
  "")

(define_insn "insn_ld_add<bitsuffix>"
  [(set (match_operand:I48MODE 1 "pointer_operand" "=r")
        (plus:I48MODE (match_operand 3 "pointer_operand" "1")
		      (match_operand 2 "s8bit_cint_operand" "i")))
   (set (match_operand:DI 0 "register_operand" "=r")
        (mem:DI (match_dup 3)))]
  ""
  "ld_add\t%0, %1, %2"
  [(set_attr "type" "X1_2cycle")])

(define_insn "insn_ldna"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(mem:DI (and:DI (match_operand 1 "pointer_operand" "rO")
                        (const_int -8))))]
  ""
  "ldna\t%0, %r1"
  [(set_attr "type" "X1_2cycle")])

(define_insn "insn_ldna_add<bitsuffix>"
  [(set (match_operand:I48MODE 1 "pointer_operand" "=r")
        (plus:I48MODE (match_operand 3 "pointer_operand" "1")
		      (match_operand 2 "s8bit_cint_operand" "i")))
   (set (match_operand:DI 0 "register_operand" "=r")
        (mem:DI (and:DI (match_dup 3) (const_int -8))))]
  ""
  "ldna_add\t%0, %1, %2"
  [(set_attr "type" "X1_2cycle")])

(define_expand "insn_ld<n><s>"
  [(set (match_operand:DI 0 "register_operand" "")
	(any_extend:DI
	 (mem:I124MODE (match_operand 1 "pointer_operand" ""))))]
  "")

(define_insn "insn_ld<I124MODE:n><s>_add<I48MODE:bitsuffix>"
  [(set (match_operand:I48MODE 1 "pointer_operand" "=r")
        (plus:I48MODE (match_operand 3 "pointer_operand" "1")
		      (match_operand 2 "s8bit_cint_operand" "i")))
   (set (match_operand:DI 0 "register_operand" "=r")
        (any_extend:DI (mem:I124MODE (match_dup 3))))]
  ""
  "ld<I124MODE:n><s>_add\t%0, %1, %2"
  [(set_attr "type" "X1_2cycle")])

;; non temporal loads

(define_insn "insn_ldnt"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(unspec:DI [(mem:DI (match_operand 1 "pointer_operand" "rO"))]
                   UNSPEC_NON_TEMPORAL))]
  ""
  "ldnt\t%0, %r1"
  [(set_attr "type" "X1_2cycle")])

(define_insn "insn_ldnt_add<bitsuffix>"
  [(set (match_operand:I48MODE 1 "pointer_operand" "=r")
        (plus:I48MODE (match_operand 3 "pointer_operand" "1")
		      (match_operand 2 "s8bit_cint_operand" "i")))
   (set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(mem:DI (match_dup 3))]
                   UNSPEC_NON_TEMPORAL))]
  ""
  "ldnt_add\t%0, %1, %2"
  [(set_attr "type" "X1_2cycle")])

(define_insn "insn_ldnt<n><s>"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(any_extend:DI
	 (unspec:I124MODE
	  [(mem:I124MODE (match_operand 1 "pointer_operand" "rO"))]
	  UNSPEC_NON_TEMPORAL)))]
  ""
  "ldnt<n><s>\t%0, %r1"
  [(set_attr "type" "X1_2cycle")])

(define_insn "insn_ldnt<I124MODE:n><s>_add<I48MODE:bitsuffix>"
  [(set (match_operand:I48MODE 1 "pointer_operand" "=r")
        (plus:I48MODE (match_operand 3 "pointer_operand" "1")
		      (match_operand 2 "s8bit_cint_operand" "i")))
   (set (match_operand:DI 0 "register_operand" "=r")
        (any_extend:DI (unspec:I124MODE [(mem:I124MODE (match_dup 3))]
					UNSPEC_NON_TEMPORAL)))]
  ""
  "ldnt<I124MODE:n><s>_add\t%0, %1, %2"
  [(set_attr "type" "X1_2cycle")])

;; L2 hits

(define_insn "insn_ld_L2"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(unspec:DI [(mem:DI (match_operand 1 "pointer_operand" "rO"))]
		   UNSPEC_LATENCY_L2))]
  ""
  "ld\t%0, %r1"
  [(set_attr "type" "Y2_L2")])

(define_insn "insn_ld_add_L2<bitsuffix>"
  [(set (match_operand:I48MODE 1 "pointer_operand" "=r")
        (plus:I48MODE (match_operand 3 "pointer_operand" "1")
		      (match_operand 2 "s8bit_cint_operand" "i")))
   (set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(mem:DI (match_dup 3))]
		   UNSPEC_LATENCY_L2))]
  ""
  "ld_add\t%0, %1, %2"
  [(set_attr "type" "X1_L2")])

(define_insn "insn_ldna_L2"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(unspec:DI [(mem:DI (and:DI (match_operand 1 "pointer_operand" "rO")
				    (const_int -8)))]
		   UNSPEC_LATENCY_L2))]
  ""
  "ldna\t%0, %r1"
  [(set_attr "type" "X1_L2")])

(define_insn "insn_ldna_add_L2<bitsuffix>"
  [(set (match_operand:I48MODE 1 "pointer_operand" "=r")
        (plus:I48MODE (match_operand 3 "pointer_operand" "1")
		      (match_operand 2 "s8bit_cint_operand" "i")))
   (set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(mem:DI (and:DI (match_dup 3) (const_int -8)))]
		   UNSPEC_LATENCY_L2))]
  ""
  "ldna_add\t%0, %1, %2"
  [(set_attr "type" "X1_L2")])

(define_insn "insn_ld<n><s>_L2"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(any_extend:DI 
	 (unspec:I124MODE
	  [(mem:I124MODE (match_operand 1 "pointer_operand" "rO"))]
	  UNSPEC_LATENCY_L2)))]
  ""
  "ld<n><s>\t%0, %r1"
  [(set_attr "type" "Y2_L2")])

(define_insn "insn_ld<I124MODE:n><s>_add_L2<I48MODE:bitsuffix>"
  [(set (match_operand:I48MODE 1 "pointer_operand" "=r")
        (plus:I48MODE (match_operand 3 "pointer_operand" "1")
		      (match_operand 2 "s8bit_cint_operand" "i")))
   (set (match_operand:DI 0 "register_operand" "=r")
        (any_extend:DI (unspec:I124MODE [(mem:I124MODE (match_dup 3))]
					UNSPEC_LATENCY_L2)))]
  ""
  "ld<I124MODE:n><s>_add\t%0, %1, %2"
  [(set_attr "type" "X1_L2")])

;; L2 hits, non temporal loads

(define_insn "insn_ldnt_L2"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(unspec:DI [(unspec:DI
                     [(mem:DI (match_operand 1 "pointer_operand" "rO"))]
                     UNSPEC_NON_TEMPORAL)]
                   UNSPEC_LATENCY_L2))]
  ""
  "ldnt\t%0, %r1"
  [(set_attr "type" "X1_L2")])

(define_insn "insn_ldnt_add_L2<bitsuffix>"
  [(set (match_operand:I48MODE 1 "pointer_operand" "=r")
        (plus:I48MODE (match_operand 3 "pointer_operand" "1")
		      (match_operand 2 "s8bit_cint_operand" "i")))
   (set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(unspec:DI
                     [(mem:DI (match_dup 3))]
                     UNSPEC_NON_TEMPORAL)]
                   UNSPEC_LATENCY_L2))]
                   ""
  "ldnt_add\t%0, %1, %2"
  [(set_attr "type" "X1_L2")])

(define_insn "insn_ldnt<n><s>_L2"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(any_extend:DI
	 (unspec:I124MODE
	  [(unspec:I124MODE
	    [(mem:I124MODE (match_operand 1 "pointer_operand" "rO"))]
	    UNSPEC_NON_TEMPORAL)]
	  UNSPEC_LATENCY_L2)))]
  ""
  "ldnt<n><s>\t%0, %r1"
  [(set_attr "type" "X1_L2")])

(define_insn "insn_ldnt<I124MODE:n><s>_add_L2<I48MODE:bitsuffix>"
  [(set (match_operand:I48MODE 1 "pointer_operand" "=r")
        (plus:I48MODE (match_operand 3 "pointer_operand" "1")
		      (match_operand 2 "s8bit_cint_operand" "i")))
   (set (match_operand:DI 0 "register_operand" "=r")
        (any_extend:DI
	 (unspec:I124MODE [(unspec:I124MODE
			    [(mem:I124MODE (match_dup 3))]
			    UNSPEC_NON_TEMPORAL)]
			  UNSPEC_LATENCY_L2)))]
  ""
  "ldnt<I124MODE:n><s>_add\t%0, %1, %2"
  [(set_attr "type" "X1_L2")])

;; L2 miss

(define_insn "insn_ld_miss"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(unspec:DI [(mem:DI (match_operand 1 "pointer_operand" "rO"))]
		   UNSPEC_LATENCY_MISS))]
  ""
  "ld\t%0, %r1"
  [(set_attr "type" "Y2_miss")])

(define_insn "insn_ld_add_miss<bitsuffix>"
  [(set (match_operand:I48MODE 1 "pointer_operand" "=r")
        (plus:I48MODE (match_operand 3 "pointer_operand" "1")
		      (match_operand 2 "s8bit_cint_operand" "i")))
   (set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(mem:DI (match_dup 3))]
		   UNSPEC_LATENCY_MISS))]
  ""
  "ld_add\t%0, %1, %2"
  [(set_attr "type" "X1_miss")])

(define_insn "insn_ldna_miss"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(unspec:DI [(mem:DI (and:DI (match_operand 1 "pointer_operand" "rO")
				    (const_int -8)))]
		   UNSPEC_LATENCY_MISS))]
  ""
  "ldna\t%0, %r1"
  [(set_attr "type" "X1_miss")])

(define_insn "insn_ldna_add_miss<bitsuffix>"
  [(set (match_operand:I48MODE 1 "pointer_operand" "=r")
        (plus:I48MODE (match_operand 3 "pointer_operand" "1")
		      (match_operand 2 "s8bit_cint_operand" "i")))
   (set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(mem:DI (and:DI (match_dup 3) (const_int -8)))]
		   UNSPEC_LATENCY_MISS))]
  ""
  "ldna_add\t%0, %1, %2"
  [(set_attr "type" "X1_miss")])

(define_insn "insn_ld<n><s>_miss"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(any_extend:DI 
	 (unspec:I124MODE
	  [(mem:I124MODE (match_operand 1 "pointer_operand" "rO"))]
	  UNSPEC_LATENCY_MISS)))]
  ""
  "ld<n><s>\t%0, %r1"
  [(set_attr "type" "Y2_miss")])

(define_insn "insn_ld<I124MODE:n><s>_add_miss<I48MODE:bitsuffix>"
  [(set (match_operand:I48MODE 1 "pointer_operand" "=r")
        (plus:I48MODE (match_operand 3 "pointer_operand" "1")
		      (match_operand 2 "s8bit_cint_operand" "i")))
   (set (match_operand:DI 0 "register_operand" "=r")
        (any_extend:DI (unspec:I124MODE [(mem:I124MODE (match_dup 3))]
					UNSPEC_LATENCY_MISS)))]
  ""
  "ld<I124MODE:n><s>_add\t%0, %1, %2"
  [(set_attr "type" "X1_miss")])

;; L2 miss, non temporal loads

(define_insn "insn_ldnt_miss"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(unspec:DI [(unspec:DI
                     [(mem:DI (match_operand 1 "pointer_operand" "rO"))]
                     UNSPEC_NON_TEMPORAL)]
                   UNSPEC_LATENCY_MISS))]
  ""
  "ldnt\t%0, %r1"
  [(set_attr "type" "X1_miss")])

(define_insn "insn_ldnt_add_miss<bitsuffix>"
  [(set (match_operand:I48MODE 1 "pointer_operand" "=r")
        (plus:I48MODE (match_operand 3 "pointer_operand" "1")
		      (match_operand 2 "s8bit_cint_operand" "i")))
   (set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(unspec:DI
                     [(mem:DI (match_dup 3))]
                     UNSPEC_NON_TEMPORAL)]
                   UNSPEC_LATENCY_MISS))]
                   ""
  "ldnt_add\t%0, %1, %2"
  [(set_attr "type" "X1_miss")])

(define_insn "insn_ldnt<n><s>_miss"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(any_extend:DI
	 (unspec:I124MODE
	  [(unspec:I124MODE
	    [(mem:I124MODE (match_operand 1 "pointer_operand" "rO"))]
	    UNSPEC_NON_TEMPORAL)]
	  UNSPEC_LATENCY_MISS)))]
  ""
  "ldnt<n><s>\t%0, %r1"
  [(set_attr "type" "X1_miss")])

(define_insn "insn_ldnt<I124MODE:n><s>_add_miss<I48MODE:bitsuffix>"
  [(set (match_operand:I48MODE 1 "pointer_operand" "=r")
        (plus:I48MODE (match_operand 3 "pointer_operand" "1")
		      (match_operand 2 "s8bit_cint_operand" "i")))
   (set (match_operand:DI 0 "register_operand" "=r")
        (any_extend:DI
	 (unspec:I124MODE [(unspec:I124MODE
                      [(mem:I124MODE (match_dup 3))]
                      UNSPEC_NON_TEMPORAL)]
                    UNSPEC_LATENCY_MISS)))]
  ""
  "ldnt<I124MODE:n><s>_add\t%0, %1, %2"
  [(set_attr "type" "X1_miss")])

;; end loads

(define_insn "insn_lnk"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(const_int 0)] UNSPEC_INSN_LNK))]
  ""
  "lnk\t%0"
  [(set_attr "type" "Y1")])

(define_insn "insn_mfspr"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec_volatile:DI [(match_operand:DI 1 "u14bit_cint_operand" "i")]
                            UNSPEC_INSN_MFSPR))
   (clobber (mem:BLK (const_int 0)))]
  ""
  "mfspr\t%0, %1"
  [(set_attr "type" "X1")])

(define_insn "insn_mtspr"
  [(unspec_volatile:DI [(match_operand:DI 0 "u14bit_cint_operand" "i")
                        (match_operand:DI 1 "reg_or_0_operand" "rO")]
                       UNSPEC_INSN_MTSPR)
   (clobber (mem:BLK (const_int 0)))]
  ""
  "mtspr\t%0, %r1"
  [(set_attr "type" "X1")])

(define_insn "insn_mm"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "u6bit_cint_operand" "i")
                    (match_operand:DI 4 "u6bit_cint_operand" "i")]
                   UNSPEC_INSN_MM))]
  ""
  "mm\t%0, %r2, %3, %4"
  [(set_attr "type" "X0")])

(define_insn "insn_mul_hs_hs"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_MUL_HS_HS))]
  ""
  "mul_hs_hs\t%0, %r1, %r2"
  [(set_attr "type" "Y0_2cycle")])

(define_insn "insn_mul_hs_hu"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_MUL_HS_HU))]
  ""
  "mul_hs_hu\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mul_hs_ls"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_MUL_HS_LS))]
  ""
  "mul_hs_ls\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mul_hs_lu"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_MUL_HS_LU))]
  ""
  "mul_hs_lu\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mul_hu_hu"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_MUL_HU_HU))]
  ""
  "mul_hu_hu\t%0, %r1, %r2"
  [(set_attr "type" "Y0_2cycle")])

(define_insn "insn_mul_hu_ls"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_MUL_HU_LS))]
  ""
  "mul_hu_ls\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mul_hu_lu"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_MUL_HU_LU))]
  ""
  "mul_hu_lu\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mul_ls_ls"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_MUL_LS_LS))]
  ""
  "mul_ls_ls\t%0, %r1, %r2"
  [(set_attr "type" "Y0_2cycle")])

(define_insn "insn_mul_ls_lu"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_MUL_LS_LU))]
  ""
  "mul_ls_lu\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mul_lu_lu"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_MUL_LU_LU))]
  ""
  "mul_lu_lu\t%0, %r1, %r2"
  [(set_attr "type" "Y0_2cycle")])

(define_insn "insn_mula_hs_hs"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_MULA_HS_HS))]
  ""
  "mula_hs_hs\t%0, %r2, %r3"
  [(set_attr "type" "Y0_2cycle")])

(define_insn "insn_mula_hs_hu"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_MULA_HS_HU))]
  ""
  "mula_hs_hu\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mula_hs_ls"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_MULA_HS_LS))]
  ""
  "mula_hs_ls\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mula_hs_lu"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_MULA_HS_LU))]
  ""
  "mula_hs_lu\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mula_hu_hu"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_MULA_HU_HU))]
  ""
  "mula_hu_hu\t%0, %r2, %r3"
  [(set_attr "type" "Y0_2cycle")])

(define_insn "insn_mula_hu_ls"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_MULA_HU_LS))]
  ""
  "mula_hu_ls\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mula_hu_lu"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_MULA_HU_LU))]
  ""
  "mula_hu_lu\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mula_ls_ls"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_MULA_LS_LS))]
  ""
  "mula_ls_ls\t%0, %r2, %r3"
  [(set_attr "type" "Y0_2cycle")])

(define_insn "insn_mula_ls_lu"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_MULA_LS_LU))]
  ""
  "mula_ls_lu\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mula_lu_lu"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_MULA_LU_LU))]
  ""
  "mula_lu_lu\t%0, %r2, %r3"
  [(set_attr "type" "Y0_2cycle")])

(define_insn "insn_mulax"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "0")
                    (match_operand:SI 2 "reg_or_0_operand" "rO")
                    (match_operand:SI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_MULAX))]
  ""
  "mulax\t%0, %r2, %r3"
  [(set_attr "type" "Y0_2cycle")])

(define_insn "insn_nap"
  [(unspec_volatile:VOID [(const_int 0)] UNSPEC_INSN_NAP)]
  ""
  "nap"
  [(set_attr "type" "cannot_bundle")])

(define_insn "insn_nor_<mode>"
  [(set (match_operand:I48MODE 0 "register_operand" "=r")
	(and:I48MODE
         (not:I48MODE (match_operand:I48MODE 1 "reg_or_0_operand" "rO"))
         (not:I48MODE (match_operand:I48MODE 2 "reg_or_0_operand" "rO"))))]
  ""
  "nor\t%0, %r1, %r2")

(define_expand "insn_prefetch_l1"
  [(prefetch (match_operand 0 "pointer_operand" "")
             (const_int 0)
             (const_int 3))]
  "")

(define_expand "insn_prefetch_l2"
  [(prefetch (match_operand 0 "pointer_operand" "")
             (const_int 0)
             (const_int 2))]
  "")

(define_expand "insn_prefetch_l3"
  [(prefetch (match_operand 0 "pointer_operand" "")
             (const_int 0)
             (const_int 1))]
  "")

(define_insn "insn_prefetch_l1_fault"
  [(unspec_volatile:VOID [(match_operand 0 "pointer_operand" "rO")]
                         UNSPEC_INSN_PREFETCH_L1_FAULT)]
  ""
  "prefetch_l1_fault\t%r0"
  [(set_attr "type" "Y2")])

(define_insn "insn_prefetch_l2_fault"
  [(unspec_volatile:VOID [(match_operand 0 "pointer_operand" "rO")]
                         UNSPEC_INSN_PREFETCH_L2_FAULT)]
  ""
  "prefetch_l2_fault\t%r0"
  [(set_attr "type" "Y2")])

(define_insn "insn_prefetch_l3_fault"
  [(unspec_volatile:VOID [(match_operand 0 "pointer_operand" "rO")]
                         UNSPEC_INSN_PREFETCH_L3_FAULT)]
  ""
  "prefetch_l3_fault\t%r0"
  [(set_attr "type" "Y2")])

(define_insn "insn_revbits"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_REVBITS))]
  ""
  "revbits\t%0, %r1"
  [(set_attr "type" "Y0")])

(define_insn "insn_shl1add"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (plus:DI (mult:DI (match_operand:DI 1 "reg_or_0_operand" "rO")
                          (const_int 2))
                 (match_operand:DI 2 "reg_or_0_operand" "rO")))]
  ""
  "shl1add\t%0, %r1, %r2")

(define_insn "insn_shl1addx"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (plus:SI (mult:SI (match_operand:SI 1 "reg_or_0_operand" "rO")
                          (const_int 2))
                 (match_operand:SI 2 "reg_or_0_operand" "rO")))]
  ""
  "shl1addx\t%0, %r1, %r2")

(define_insn "insn_shl2add"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (plus:DI (mult:DI (match_operand:DI 1 "reg_or_0_operand" "rO")
                          (const_int 4))
                 (match_operand:DI 2 "reg_or_0_operand" "rO")))]
  ""
  "shl2add\t%0, %r1, %r2")

(define_insn "insn_shl2addx"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (plus:SI (mult:SI (match_operand:SI 1 "reg_or_0_operand" "rO")
                          (const_int 4))
                 (match_operand:SI 2 "reg_or_0_operand" "rO")))]
  ""
  "shl2addx\t%0, %r1, %r2")

(define_insn "insn_shl3add"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (plus:DI (mult:DI (match_operand:DI 1 "reg_or_0_operand" "rO")
                          (const_int 8))
                 (match_operand:DI 2 "reg_or_0_operand" "rO")))]
  ""
  "shl3add\t%0, %r1, %r2")

(define_insn "insn_shl3addx"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (plus:SI (mult:SI (match_operand:SI 1 "reg_or_0_operand" "rO")
                          (const_int 8))
                 (match_operand:SI 2 "reg_or_0_operand" "rO")))]
  ""
  "shl3addx\t%0, %r1, %r2")

(define_insn "insn_shufflebytes"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_SHUFFLEBYTES))]
  ""
  "shufflebytes\t%0, %r2, %r3"
  [(set_attr "type" "X0")])

;; stores

(define_expand "insn_st"
  [(set (mem:DI (match_operand 0 "pointer_operand" ""))
        (match_operand:DI 1 "reg_or_0_operand" ""))]
  "")

(define_insn "insn_st_add<bitsuffix>"
  [(set (match_operand:I48MODE 0 "pointer_operand" "=r")
        (plus:I48MODE (match_operand 3 "pointer_operand" "0")
		      (match_operand 2 "s8bit_cint_operand" "i")))
   (set (mem:DI (match_dup 3))
        (match_operand:DI 1 "reg_or_0_operand" "rO"))]
  ""
  "st_add\t%0, %r1, %2"
  [(set_attr "type" "X1")])

(define_expand "insn_st<n>"
  [(set (mem:I124MODE (match_operand 0 "pointer_operand" ""))
        (match_operand:DI 1 "reg_or_0_operand" ""))]
  ""
{
  operands[1] = simplify_gen_subreg (<MODE>mode, operands[1], DImode, 0);
})

(define_expand "insn_st<I124MODE:n>_add<I48MODE:bitsuffix>"
  [(parallel
    [(set (match_operand:I48MODE 0 "pointer_operand" "")
	  (plus:I48MODE (match_operand 3 "pointer_operand" "")
			(match_operand 2 "s8bit_cint_operand" "")))
     (set (mem:I124MODE (match_dup 3))
	  (match_operand:DI 1 "reg_or_0_operand" ""))])]
  ""
{
  operands[1] = simplify_gen_subreg (<I124MODE:MODE>mode, operands[1],
				     DImode, 0);
})

(define_insn "*insn_st<I124MODE:n>_add<I48MODE:bitsuffix>"
  [(set (match_operand:I48MODE 0 "pointer_operand" "=r")
        (plus:I48MODE (match_operand 3 "pointer_operand" "0")
		      (match_operand 2 "s8bit_cint_operand" "i")))
   (set (mem:I124MODE (match_dup 3))
        (match_operand:I124MODE 1 "reg_or_0_operand" "rO"))]
  ""
  "st<I124MODE:n>_add\t%0, %r1, %2"
  [(set_attr "type" "X1")])

;; non-temporal stores

(define_insn "insn_stnt"
  [(set (mem:DI (unspec [(match_operand 0 "pointer_operand" "rO")]
			UNSPEC_NON_TEMPORAL))
        (match_operand:DI 1 "reg_or_0_operand" "rO"))]
  ""
  "stnt\t%0, %r1"
  [(set_attr "type" "X1")])

(define_insn "insn_stnt_add<bitsuffix>"
  [(set (match_operand:I48MODE 0 "pointer_operand" "=r")
        (plus:I48MODE (match_operand 3 "pointer_operand" "0")
		      (match_operand 2 "s8bit_cint_operand" "i")))
   (set (mem:DI (unspec:I48MODE [(match_dup 3)] UNSPEC_NON_TEMPORAL))
        (match_operand:DI 1 "reg_or_0_operand" "rO"))]
  ""
  "stnt_add\t%0, %r1, %2"
  [(set_attr "type" "X1")])

(define_expand "insn_stnt<n>"
  [(set (mem:I124MODE (unspec [(match_operand 0 "pointer_operand" "")]
			      UNSPEC_NON_TEMPORAL))
        (match_operand:DI 1 "reg_or_0_operand" ""))]
  ""
{
  operands[1] = simplify_gen_subreg (<MODE>mode, operands[1], DImode, 0);
})

(define_insn "*insn_stnt<n>"
  [(set (mem:I124MODE (unspec [(match_operand 0 "pointer_operand" "rO")]
			      UNSPEC_NON_TEMPORAL))
	(match_operand:I124MODE 1 "reg_or_0_operand" "rO"))]
  ""
  "stnt<n>\t%0, %r1"
  [(set_attr "type" "X1")])

(define_expand "insn_stnt<I124MODE:n>_add<I48MODE:bitsuffix>"
  [(parallel
    [(set (match_operand:I48MODE 0 "pointer_operand" "")
	  (plus:I48MODE (match_operand 3 "pointer_operand" "")
			(match_operand 2 "s8bit_cint_operand" "")))
     (set (mem:I124MODE (unspec:I48MODE [(match_dup 3)] UNSPEC_NON_TEMPORAL))
	  (match_operand:DI 1 "reg_or_0_operand" "rO"))])]
  ""
{
  operands[1] = simplify_gen_subreg (<I124MODE:MODE>mode, operands[1],
				     DImode, 0);
})

(define_insn "*insn_stnt<I124MODE:n>_add<I48MODE:bitsuffix>"
  [(set (match_operand:I48MODE 0 "pointer_operand" "=r")
        (plus:I48MODE (match_operand 3 "pointer_operand" "0")
		      (match_operand 2 "s8bit_cint_operand" "i")))
   (set (mem:I124MODE (unspec:I48MODE [(match_dup 3)] UNSPEC_NON_TEMPORAL))
        (match_operand:I124MODE 1 "reg_or_0_operand" "rO"))]
  ""
  "stnt<I124MODE:n>_add\t%0, %r1, %2"
  [(set_attr "type" "X1")])

;; end stores

(define_insn "insn_tblidxb0"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_TBLIDXB0))]
  ""
  "tblidxb0\t%0, %r2"
  [(set_attr "type" "Y0")])

(define_insn "insn_tblidxb1"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_TBLIDXB1))]
  ""
  "tblidxb1\t%0, %r2"
  [(set_attr "type" "Y0")])

(define_insn "insn_tblidxb2"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_TBLIDXB2))]
  ""
  "tblidxb2\t%0, %r2"
  [(set_attr "type" "Y0")])

(define_insn "insn_tblidxb3"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_TBLIDXB3))]
  ""
  "tblidxb3\t%0, %r2"
  [(set_attr "type" "Y0")])

;; insn_v1add
;; insn_v1addi
;; insn_v1cmpeq
;; insn_v1cmpeqi
;; insn_v1cmplts
;; insn_v1cmpltsi
;; insn_v1cmpltu
;; insn_v1cmpltui
;; insn_v1maxu
;; insn_v1maxui
;; insn_v1minu
;; insn_v1minui
(define_insn "<optab>v8qi3"
  [(set (match_operand:V8QI 0 "register_operand" "=r,r")
	(v1op_immed:V8QI
	 (match_operand:V8QI 1 "reg_or_0_operand" "<comm>rO,rO")
	 (match_operand:V8QI 2 "reg_or_v8s8bit_operand" "W,rO")))]
  ""
  "@
   v1<insn>i\t%0, %r1, %j2
   v1<insn>\t%0, %r1, %r2"
  [(set_attr "type" "<pipe>,<pipe>")])

(define_expand "insn_v1<insn>"
  [(set (match_operand:DI 0 "register_operand" "")
	(v1op_immed:V8QI
	 (match_operand:DI 1 "reg_or_0_operand" "")
	 (match_operand:DI 2 "reg_or_0_operand" "")))]
  ""
{
  tilegx_expand_builtin_vector_binop (gen_<optab>v8qi3, V8QImode, operands[0],
				      V8QImode, operands[1], operands[2], true);
  DONE;
})

(define_expand "insn_v1<insn>i"
  [(set (match_operand:DI 0 "register_operand" "")
	(v1op_immed:V8QI
	 (match_operand:DI 1 "reg_or_0_operand" "")
	 (match_operand:DI 2 "s8bit_cint_operand" "")))]
  ""
{
  /* Tile out immediate and expand to general case. */
  rtx n = tilegx_simd_int (operands[2], QImode);
  tilegx_expand_builtin_vector_binop (gen_<optab>v8qi3, V8QImode, operands[0],
				      V8QImode, operands[1], n, true);
  DONE;
})

;; insn_v1shl
;; insn_v1shli
;; insn_v1shrs
;; insn_v1shrsi
;; insn_v1shru
;; insn_v1shrui
(define_insn "<optab>v8qi3"
  [(set (match_operand:V8QI 0 "register_operand" "=r,r")
	(any_shift:V8QI
	 (match_operand:V8QI 1 "reg_or_0_operand" "rO,rO")
	 (match_operand:DI 2 "reg_or_u5bit_operand" "I,rO")))]
  ""
  "@
   v1<insn>i\t%0, %r1, %2
   v1<insn>\t%0, %r1, %r2"
  [(set_attr "type" "<pipe>,<pipe>")])

(define_expand "insn_v1<insn>"
  [(set (match_operand:DI 0 "register_operand" "")
	(any_shift:V8QI
	 (match_operand:DI 1 "reg_or_0_operand" "")
	 (match_operand:DI 2 "reg_or_u5bit_operand" "")))]
  ""
{
  tilegx_expand_builtin_vector_binop (gen_<optab>v8qi3, V8QImode, operands[0],
				      V8QImode, operands[1], operands[2], false);
  DONE;
})

;; insn_v2add
;; insn_v2addi
;; insn_v2maxs
;; insn_v2maxsi
;; insn_v2mins
;; insn_v2minsi
;; insn_v2cmpeq
;; insn_v2cmpeqi
;; insn_v2cmplts
;; insn_v2cmpltsi
;; insn_v2cmpltu
;; insn_v2cmpltui
(define_insn "<optab>v4hi3"
  [(set (match_operand:V4HI 0 "register_operand" "=r,r")
	(v2op_immed:V4HI
	 (match_operand:V4HI 1 "reg_or_0_operand" "<comm>rO,rO")
	 (match_operand:V4HI 2 "reg_or_v4s8bit_operand" "Y,rO")))]
  ""
  "@
   v2<insn>i\t%0, %r1, %j2
   v2<insn>\t%0, %r1, %r2"
  [(set_attr "type" "<pipe>,<pipe>")])

(define_expand "insn_v2<insn>"
  [(set (match_operand:DI 0 "register_operand" "")
	(v2op_immed:V4HI
	 (match_operand:DI 1 "reg_or_0_operand" "")
	 (match_operand:DI 2 "reg_or_0_operand" "")))]
  ""
{
  tilegx_expand_builtin_vector_binop (gen_<optab>v4hi3, V4HImode, operands[0],
				      V4HImode, operands[1], operands[2], true);
  DONE;
})

(define_expand "insn_v2<insn>i"
  [(set (match_operand:DI 0 "register_operand" "")
	(v2op_immed:V4HI
	 (match_operand:DI 1 "reg_or_0_operand" "")
	 (match_operand:DI 2 "s8bit_cint_operand" "")))]
  ""
{
  /* Tile out immediate and expand to general case. */
  rtx n = tilegx_simd_int (operands[2], HImode);
  tilegx_expand_builtin_vector_binop (gen_<optab>v4hi3, V4HImode, operands[0],
				      V4HImode, operands[1], n, true);
  DONE;
})

;; insn_v2shl
;; insn_v2shli
;; insn_v2shrs
;; insn_v2shrsi
;; insn_v2shru
;; insn_v2shrui
(define_insn "<optab>v4hi3"
  [(set (match_operand:V4HI 0 "register_operand" "=r,r")
	(any_shift:V4HI
	 (match_operand:V4HI 1 "reg_or_0_operand" "rO,rO")
	 (match_operand:DI 2 "reg_or_u5bit_operand" "I,rO")))]
  ""
  "@
   v2<insn>i\t%0, %r1, %2
   v2<insn>\t%0, %r1, %r2"
  [(set_attr "type" "<pipe>,<pipe>")])

(define_expand "insn_v2<insn>"
  [(set (match_operand:DI 0 "register_operand" "")
	(any_shift:V4HI
	 (match_operand:DI 1 "reg_or_0_operand" "")
	 (match_operand:DI 2 "reg_or_u5bit_operand" "")))]
  ""
{
  tilegx_expand_builtin_vector_binop (gen_<optab>v4hi3, V4HImode, operands[0],
				      V4HImode, operands[1], operands[2], false);
  DONE;
})

;; insn_v1adduc
;; insn_v1subuc
;; insn_v1sub
;; insn_v1cmpne
;; insn_v1cmples
;; insn_v1cmpleu
;; insn_v1multu
(define_insn "<optab>v8qi3"
  [(set (match_operand:V8QI 0 "register_operand" "=r")
	(v1op:V8QI
	 (match_operand:V8QI 1 "reg_or_0_operand" "<comm>rO")
	 (match_operand:V8QI 2 "reg_or_0_operand" "rO")))]
  ""
  "v1<insn>\t%0, %r1, %r2"
  [(set_attr "type" "<pipe>")])

(define_expand "insn_v1<insn>"
  [(set (match_operand:DI 0 "register_operand" "")
	(v1op:V8QI
	 (match_operand:DI 1 "reg_or_0_operand" "")
	 (match_operand:DI 2 "reg_or_0_operand" "")))]
  ""
{
  tilegx_expand_builtin_vector_binop (gen_<optab>v8qi3, V8QImode, operands[0],
				      V8QImode, operands[1], operands[2], true);
  DONE;
})

;; insn_v2addsc
;; insn_v2subsc
;; insn_v2sub
;; insn_v2cmpne
;; insn_v2cmples
;; insn_v2cmpleu
(define_insn "<optab>v4hi3"
  [(set (match_operand:V4HI 0 "register_operand" "=r")
	(v2op:V4HI
	 (match_operand:V4HI 1 "reg_or_0_operand" "<comm>rO")
	 (match_operand:V4HI 2 "reg_or_0_operand" "rO")))]
  ""
  "v2<insn>\t%0, %r1, %r2"
  [(set_attr "type" "<pipe>")])

(define_expand "insn_v2<insn>"
  [(set (match_operand:DI 0 "register_operand" "")
	(v2op:V4HI
	 (match_operand:DI 1 "reg_or_0_operand" "")
	 (match_operand:DI 2 "reg_or_0_operand" "")))]
  ""
{
  tilegx_expand_builtin_vector_binop (gen_<optab>v4hi3, V4HImode, operands[0],
				      V4HImode, operands[1], operands[2], true);
  DONE;
})

;; insn_v2mults
(define_insn "mulv4hi3"
  [(set (match_operand:V4HI 0 "register_operand" "=r")
	(mult:V4HI
	 (match_operand:V4HI 1 "reg_or_0_operand" "%rO")
	 (match_operand:V4HI 2 "reg_or_0_operand" "rO")))]
  ""
  "v2mults\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_expand "insn_v2mults"
  [(set (match_operand:DI 0 "register_operand" "")
	(mult:V4HI
	 (match_operand:DI 1 "reg_or_0_operand" "")
	 (match_operand:DI 2 "reg_or_0_operand" "")))]
  ""
{
  tilegx_expand_builtin_vector_binop (gen_mulv4hi3, V4HImode, operands[0],
				      V4HImode, operands[1], operands[2], true);
  DONE;
})

;; insn_v2shlsc
(define_insn "<optab>v4hi3"
  [(set (match_operand:V4HI 0 "register_operand" "=r")
	(v2shift:V4HI
	 (match_operand:V4HI 1 "reg_or_0_operand" "rO")
	 (match_operand:DI 2 "reg_or_0_operand" "rO")))]
  ""
  "v2<insn>\t%0, %r1, %r2"
  [(set_attr "type" "<pipe>")])

(define_expand "insn_v2<insn>"
  [(set (match_operand:DI 0 "register_operand" "")
	(v2shift:V4HI
	 (match_operand:DI 1 "reg_or_0_operand" "")
	 (match_operand:DI 2 "reg_or_0_operand" "")))]
  ""
{
  tilegx_expand_builtin_vector_binop (gen_<optab>v4hi3, V4HImode, operands[0],
				      V4HImode, operands[1], operands[2], false);
  DONE;
})

;; insn_v4addsc
;; insn_v4subsc
;; insn_v4add
;; insn_v4sub
(define_insn "<optab>v2si3"
  [(set (match_operand:V2SI 0 "register_operand" "=r")
	(v4op:V2SI
	 (match_operand:V2SI 1 "reg_or_0_operand" "<comm>rO")
	 (match_operand:V2SI 2 "reg_or_0_operand" "rO")))]
  ""
  "v4<insn>\t%0, %r1, %r2"
  [(set_attr "type" "<pipe>")])

(define_expand "insn_v4<insn>"
  [(set (match_operand:DI 0 "register_operand" "")
	(v4op:V2SI
	 (match_operand:DI 1 "reg_or_0_operand" "")
	 (match_operand:DI 2 "reg_or_0_operand" "")))]
  ""
{
  tilegx_expand_builtin_vector_binop (gen_<optab>v2si3, V2SImode, operands[0],
				      V2SImode, operands[1], operands[2], true);
  DONE;
})

;; insn_v4shl
;; insn_v4shrs
;; insn_v4shru
;; insn_v4shlsc
(define_insn "<optab>v2si3"
  [(set (match_operand:V2SI 0 "register_operand" "=r")
	(v4shift:V2SI
	 (match_operand:V2SI 1 "reg_or_0_operand" "rO")
	 (match_operand:DI 2 "reg_or_0_operand" "rO")))]
  ""
  "v4<insn>\t%0, %r1, %r2"
  [(set_attr "type" "<pipe>")])

(define_expand "insn_v4<insn>"
  [(set (match_operand:DI 0 "register_operand" "")
	(v4shift:V2SI
	 (match_operand:DI 1 "reg_or_0_operand" "")
	 (match_operand:DI 2 "reg_or_0_operand" "")))]
  ""
{
  tilegx_expand_builtin_vector_binop (gen_<optab>v2si3, V2SImode, operands[0],
				      V2SImode, operands[1], operands[2], false);
  DONE;
})

;; insn_v1int_h
;;    {B7,B6,B5,B4,B3,B2,B1,B0} {A7,A6,A5,A4,A3,A2,A1,A0}
;; => {A7,A6,A5,A4,A3,A2,A1,A0,B7,B6,B5,B4,B3,B2,B1,B0}
;; => {A7,B7,A6,B6,A5,B5,A4,B4}
(define_insn "vec_interleave_highv8qi"
  [(set (match_operand:V8QI 0 "register_operand" "=r")
	(vec_select:V8QI
	 (vec_concat:V16QI (match_operand:V8QI 1 "reg_or_0_operand" "rO")
			   (match_operand:V8QI 2 "reg_or_0_operand" "rO"))
	 (parallel [(const_int 4) (const_int 12) 
		    (const_int 5) (const_int 13) 
		    (const_int 6) (const_int 14) 
		    (const_int 7) (const_int 15)])))]
  ""
  "v1int_h\t%0, %r2, %r1"
  [(set_attr "type" "X01")])

(define_expand "insn_v1int_h"
  [(match_operand:DI 0 "register_operand" "")
   (match_operand:DI 1 "reg_or_0_operand" "")
   (match_operand:DI 2 "reg_or_0_operand" "")]
  ""
{
  /* Our instruction interleaves opposite of the way vec_interleave
     works, so we need to reverse the source operands.  */
  tilegx_expand_builtin_vector_binop (gen_vec_interleave_highv8qi, V8QImode,
				      operands[0], V8QImode, operands[2],
				      operands[1], true);
  DONE;
})

;; insn_v1int_l
;;    {B7,B6,B5,B4,B3,B2,B1,B0} {A7,A6,A5,A4,A3,A2,A1,A0}
;; => {A7,A6,A5,A4,A3,A2,A1,A0,B7,B6,B5,B4,B3,B2,B1,B0}
;; => {A3,B3,A2,B2,A1,B1,A0,B0}
(define_insn "vec_interleave_lowv8qi"
  [(set (match_operand:V8QI 0 "register_operand" "=r")
	(vec_select:V8QI
	 (vec_concat:V16QI (match_operand:V8QI 1 "reg_or_0_operand" "rO")
			   (match_operand:V8QI 2 "reg_or_0_operand" "rO"))
	 (parallel [(const_int 0) (const_int 8)
		    (const_int 1) (const_int 9)
		    (const_int 2) (const_int 10)
		    (const_int 3) (const_int 11)])))]
  ""
  "v1int_l\t%0, %r2, %r1"
  [(set_attr "type" "X01")])

(define_expand "insn_v1int_l"
  [(match_operand:DI 0 "register_operand" "")
   (match_operand:DI 1 "reg_or_0_operand" "")
   (match_operand:DI 2 "reg_or_0_operand" "")]
  ""
{
  /* Our instruction interleaves opposite of the way vec_interleave
     works, so we need to reverse the source operands.  */
  tilegx_expand_builtin_vector_binop (gen_vec_interleave_lowv8qi, V8QImode,
				      operands[0], V8QImode, operands[2],
				      operands[1], true);
  DONE;
})

;; insn_v2int_h
;;    {B3,B2,B1,B0} {A3,A2,A1,A0}
;; => {A3,A2,A1,A0,B3,B2,B1,B0}
;; => {A3,B3,A2,B2}
(define_insn "vec_interleave_highv4hi"
  [(set (match_operand:V4HI 0 "register_operand" "=r")
	(vec_select:V4HI
	 (vec_concat:V8HI (match_operand:V4HI 1 "reg_or_0_operand" "rO")
			  (match_operand:V4HI 2 "reg_or_0_operand" "rO"))
	 (parallel [(const_int 2) (const_int 6)
		    (const_int 3) (const_int 7)])))]
  ""
  "v2int_h\t%0, %r2, %r1"
  [(set_attr "type" "X01")])

(define_expand "insn_v2int_h"
  [(match_operand:DI 0 "register_operand" "")
   (match_operand:DI 1 "reg_or_0_operand" "")
   (match_operand:DI 2 "reg_or_0_operand" "")]
  ""
{
  /* Our instruction interleaves opposite of the way vec_interleave
     works, so we need to reverse the source operands.  */
  tilegx_expand_builtin_vector_binop (gen_vec_interleave_highv4hi, V4HImode,
                                      operands[0], V4HImode, operands[2],
				      operands[1], true);
  DONE;
})

;; insn_v2int_l
;;    {B3,B2,B1,B0} {A3,A2,A1,A0}
;; => {A3,A2,A1,A0,B3,B2,B1,B0}
;; => {A1,B1,A0,B0}
(define_insn "vec_interleave_lowv4hi"
  [(set (match_operand:V4HI 0 "register_operand" "=r")
	(vec_select:V4HI
	 (vec_concat:V8HI (match_operand:V4HI 1 "reg_or_0_operand" "rO")
			  (match_operand:V4HI 2 "reg_or_0_operand" "rO"))
	 (parallel [(const_int 0) (const_int 4)
		    (const_int 1) (const_int 5)])))]
  ""
  "v2int_l\t%0, %r2, %r1"
  [(set_attr "type" "X01")])

(define_expand "insn_v2int_l"
  [(match_operand:DI 0 "register_operand" "")
   (match_operand:DI 1 "reg_or_0_operand" "")
   (match_operand:DI 2 "reg_or_0_operand" "")]
  ""
{
  tilegx_expand_builtin_vector_binop (gen_vec_interleave_lowv4hi, V4HImode,
                                      operands[0], V4HImode, operands[2],
				      operands[1], true);
  DONE;
})

;; insn_v4int_h
;;    {B1,B0} {A1,A0}
;; => {A1,A0,B1,B0}
;; => {A1,B1}
(define_insn "vec_interleave_highv2si"
  [(set (match_operand:V2SI 0 "register_operand" "=r")
	(vec_select:V2SI
	 (vec_concat:V4SI (match_operand:V2SI 1 "reg_or_0_operand" "rO")
			  (match_operand:V2SI 2 "reg_or_0_operand" "rO"))
	 (parallel [(const_int 1) (const_int 3)])))]
  ""
  "v4int_h\t%0, %r2, %r1"
  [(set_attr "type" "X01")])

(define_expand "insn_v4int_h"
  [(match_operand:DI 0 "register_operand" "")
   (match_operand:DI 1 "reg_or_0_operand" "")
   (match_operand:DI 2 "reg_or_0_operand" "")]
  ""
{
  /* Our instruction interleaves opposite of the way vec_interleave
     works, so we need to reverse the source operands.  */
  tilegx_expand_builtin_vector_binop (gen_vec_interleave_highv2si, V2SImode,
                                      operands[0], V2SImode, operands[2],
				      operands[1], true);
  DONE;
})

;; insn_v4int_l
;;    {B1,B0} {A1,A0}
;; => {A1,A0,B1,B0}
;; => {A0,B0}
(define_insn "vec_interleave_lowv2si"
  [(set (match_operand:V2SI 0 "register_operand" "=r")
	(vec_select:V2SI
	 (vec_concat:V4SI (match_operand:V2SI 1 "reg_or_0_operand" "rO")
			  (match_operand:V2SI 2 "reg_or_0_operand" "rO"))
	 (parallel [(const_int 0) (const_int 2)])))]
  ""
  "v4int_l\t%0, %r2, %r1"
  [(set_attr "type" "X01")])

(define_expand "insn_v4int_l"
  [(match_operand:DI 0 "register_operand" "")
   (match_operand:DI 1 "reg_or_0_operand" "")
   (match_operand:DI 2 "reg_or_0_operand" "")]
  ""
{
  /* Our instruction interleaves opposite of the way vec_interleave
     works, so we need to reverse the source operands.  */
  tilegx_expand_builtin_vector_binop (gen_vec_interleave_lowv2si, V2SImode,
                                      operands[0], V2SImode, operands[2],
				      operands[1], true);
  DONE;
})

;; insn_v1mnz
;; insn_v1mz
;; insn_v2mnz
;; insn_v2mz
(define_insn "insn_mnz_<mode>"
  [(set (match_operand:VEC48MODE 0 "register_operand" "=r")
	(if_then_else:VEC48MODE
         (ne:VEC48MODE
	  (match_operand:VEC48MODE 1 "reg_or_0_operand" "rO")
	  (const_int 0))
         (match_operand:VEC48MODE 2 "reg_or_0_operand" "rO")
         (const_int 0)))]
  ""
  "v<n>mnz\t%0, %r1, %r2"
  [(set_attr "type" "X01")])

(define_expand "insn_v<n>mnz"
  [(set (match_operand:DI 0 "register_operand" "")
	(if_then_else:VEC48MODE
         (ne:VEC48MODE
	  (match_operand:DI 1 "reg_or_0_operand" "")
	  (const_int 0))
         (match_operand:DI 2 "reg_or_0_operand" "")
         (const_int 0)))]
  ""
{
  tilegx_expand_builtin_vector_binop (gen_insn_mnz_<mode>, <MODE>mode,
                                      operands[0], <MODE>mode, operands[1],
				      operands[2], true);
  DONE;
})

(define_insn "insn_mz_<mode>"
  [(set (match_operand:VEC48MODE 0 "register_operand" "=r")
	(if_then_else:VEC48MODE
         (ne:VEC48MODE
	  (match_operand:VEC48MODE 1 "reg_or_0_operand" "rO")
	  (const_int 0))
         (const_int 0)
         (match_operand:VEC48MODE 2 "reg_or_0_operand" "rO")))]
  ""
  "v<n>mz\t%0, %r1, %r2"
  [(set_attr "type" "X01")])
(define_expand "insn_v<n>mz"
  [(set (match_operand:DI 0 "register_operand" "")
	(if_then_else:VEC48MODE
         (ne:VEC48MODE
	  (match_operand:DI 1 "reg_or_0_operand" "")
	  (const_int 0))
         (const_int 0)
         (match_operand:DI 2 "reg_or_0_operand" "")))]
  ""
{
  tilegx_expand_builtin_vector_binop (gen_insn_mz_<mode>, <MODE>mode,
                                      operands[0], <MODE>mode, operands[1],
				      operands[2], true);
  DONE;
})

;; insn_v1mulu
(define_insn "vec_widen_umult_lo_v8qi"
  [(set (match_operand:V4HI 0 "register_operand" "=r")
        (mult:V4HI
	 (zero_extend:V4HI
	  (vec_select:V4QI
	   (match_operand:V8QI 1 "register_operand" "r")
	   (parallel [(const_int 0) (const_int 1)
		      (const_int 2) (const_int 3)])))
	 (zero_extend:V4HI
	  (vec_select:V4QI
	   (match_operand:V8QI 2 "register_operand" "r")
	   (parallel [(const_int 0) (const_int 1)
		      (const_int 2) (const_int 3)])))))]
  ""
  "v1mulu\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_expand "insn_v1mulu"
  [(match_operand:DI 0 "register_operand" "")
   (match_operand:DI 1 "reg_or_0_operand" "")
   (match_operand:DI 2 "reg_or_0_operand" "")]
  ""
{
  tilegx_expand_builtin_vector_binop (gen_vec_widen_umult_lo_v8qi, V4HImode,
                                      operands[0], V8QImode, operands[1],
				      operands[2], true);
  DONE;
})

;; insn_v1mulus
(define_insn "vec_widen_usmult_lo_v8qi"
  [(set (match_operand:V4HI 0 "register_operand" "=r")
        (mult:V4HI
	 (zero_extend:V4HI
	  (vec_select:V4QI
	   (match_operand:V8QI 1 "register_operand" "r")
	   (parallel [(const_int 0) (const_int 1)
		      (const_int 2) (const_int 3)])))
	 (sign_extend:V4HI
	  (vec_select:V4QI
	   (match_operand:V8QI 2 "register_operand" "r")
	   (parallel [(const_int 0) (const_int 1)
		      (const_int 2) (const_int 3)])))))]
  ""
  "v1mulus\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_expand "insn_v1mulus"
  [(match_operand:DI 0 "register_operand" "")
   (match_operand:DI 1 "reg_or_0_operand" "")
   (match_operand:DI 2 "reg_or_0_operand" "")]
  ""
{
  tilegx_expand_builtin_vector_binop (gen_vec_widen_usmult_lo_v8qi, V4HImode,
                                      operands[0], V8QImode, operands[1],
				      operands[2], true);
  DONE;
})

;; insn_v2muls
(define_insn "vec_widen_smult_lo_v4qi"
  [(set (match_operand:V2SI 0 "register_operand" "=r")
        (mult:V2SI
	 (sign_extend:V2SI
	  (vec_select:V2HI
	   (match_operand:V4HI 1 "register_operand" "r")
	   (parallel [(const_int 0) (const_int 1)])))
	 (sign_extend:V2SI
	  (vec_select:V2HI
	   (match_operand:V4HI 2 "register_operand" "r")
	   (parallel [(const_int 0) (const_int 1)])))))]
  ""
  "v2muls\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_expand "insn_v2muls"
  [(match_operand:DI 0 "register_operand" "")
   (match_operand:DI 1 "reg_or_0_operand" "")
   (match_operand:DI 2 "reg_or_0_operand" "")]
  ""
{
  tilegx_expand_builtin_vector_binop (gen_vec_widen_smult_lo_v4qi, V2SImode,
                                      operands[0], V4HImode, operands[1],
				      operands[2], true);
  DONE;
})

;; v2packl
;; v2packuc
;;    {B3,B2,B1,B0} {A3,A2,A1,A0}
;; => {A3,A2,A1,A0,B3,B2,B1,B0}
(define_insn "vec_pack_<pack_optab>_v4hi"
  [(set (match_operand:V8QI 0 "reg_or_0_operand" "=r")
	(vec_concat:V8QI
	 (v2pack:V4QI (match_operand:V4HI 1 "reg_or_0_operand" "rO"))
	 (v2pack:V4QI (match_operand:V4HI 2 "reg_or_0_operand" "rO"))))]
  ""
  "v2<pack_insn>\t%0, %r2, %r1"
  [(set_attr "type" "X01")])

(define_expand "insn_v2<pack_insn>"
  [(set (match_operand:DI 0 "reg_or_0_operand" "")
	(vec_concat:V8QI
	 (v2pack:V4QI (match_operand:DI 2 "reg_or_0_operand" ""))
	 (v2pack:V4QI (match_operand:DI 1 "reg_or_0_operand" ""))))]
  ""
{
  /* Our instruction concats opposite of the way vec_pack works, so we
     need to reverse the source operands.  */
  tilegx_expand_builtin_vector_binop (gen_vec_pack_<pack_optab>_v4hi,
				      V8QImode, operands[0], V4HImode,
				      operands[2], operands[1], true);
  DONE;
})

;; v2packh
;;    {B3,B2,B1,B0} {A3,A2,A1,A0}
;; => {A3_hi,A2_hi,A1_hi,A0_hi,B3_hi,B2_hi,B1_hi,B0_hi}
(define_insn "vec_pack_hipart_v4hi"
  [(set (match_operand:V8QI 0 "reg_or_0_operand" "=r")
	(vec_concat:V8QI
	 (truncate:V4QI
	  (ashiftrt:V4HI (match_operand:V4HI 1 "reg_or_0_operand" "rO")
			 (const_int 8)))
	 (truncate:V4QI
	  (ashiftrt:V4HI (match_operand:V4HI 2 "reg_or_0_operand" "rO")
			 (const_int 8)))))]
  ""
  "v2packh\t%0, %r2, %r1"
  [(set_attr "type" "X01")])

(define_expand "insn_v2packh"
  [(set (match_operand:DI 0 "reg_or_0_operand" "")
	(vec_concat:V8QI
	 (truncate:V4QI
	  (ashiftrt:V4HI (match_operand:DI 2 "reg_or_0_operand" "")
			 (const_int 8)))
	 (truncate:V4QI
	  (ashiftrt:V4HI (match_operand:DI 1 "reg_or_0_operand" "")
			 (const_int 8)))))]
  ""
{
  /* Our instruction concats opposite of the way vec_pack works, so we
     need to reverse the source operands.  */
  tilegx_expand_builtin_vector_binop (gen_vec_pack_hipart_v4hi, V8QImode,
                                      operands[0], V4HImode, operands[2],
				      operands[1], true);
  DONE;
})

;; v4packsc
;;    {B1,B0} {A1,A0}
;; => {A1,A0,B1,B0}
(define_insn "vec_pack_ssat_v2si"
  [(set (match_operand:V4HI 0 "reg_or_0_operand" "=r")
	(vec_concat:V4HI
	 (us_truncate:V2HI (match_operand:V2SI 1 "reg_or_0_operand" "rO"))
	 (us_truncate:V2HI (match_operand:V2SI 2 "reg_or_0_operand" "rO"))))]
  ""
  "v4packsc\t%0, %r2, %r1"
  [(set_attr "type" "X01")])

(define_expand "insn_v4packsc"
  [(set (match_operand:DI 0 "reg_or_0_operand" "")
	(vec_concat:V4HI
	 (us_truncate:V2HI (match_operand:DI 2 "reg_or_0_operand" ""))
	 (us_truncate:V2HI (match_operand:DI 1 "reg_or_0_operand" ""))))]
  ""
{
  /* Our instruction concats opposite of the way vec_pack works, so we
     need to reverse the source operands.  */
  tilegx_expand_builtin_vector_binop (gen_vec_pack_ssat_v2si, V4HImode,
                                      operands[0], V2SImode, operands[2],
				      operands[1], true);
  DONE;
})

;; Rest of the vector intrinsics
(define_insn "insn_v1adiffu"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_V1ADIFFU))]
  ""
  "v1adiffu\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_v1avgu"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_V1AVGU))]
  ""
  "v1avgu\t%0, %r1, %r2"
  [(set_attr "type" "X0")])

(define_insn "insn_v1ddotpu"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_V1DDOTPU))]
  ""
  "v1ddotpu\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_v1ddotpua"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_V1DDOTPUA))]
  ""
  "v1ddotpua\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_v1ddotpus"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_V1DDOTPUS))]
  ""
  "v1ddotpus\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_v1ddotpusa"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_V1DDOTPUSA))]
  ""
  "v1ddotpusa\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_v1dotp"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_V1DOTP))]
  ""
  "v1dotp\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_v1dotpa"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_V1DOTPA))]
  ""
  "v1dotpa\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_v1dotpu"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_V1DOTPU))]
  ""
  "v1dotpu\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_v1dotpua"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_V1DOTPUA))]
  ""
  "v1dotpua\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_v1dotpus"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_V1DOTPUS))]
  ""
  "v1dotpus\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_v1dotpusa"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_V1DOTPUSA))]
  ""
  "v1dotpusa\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_v1sadau"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_V1SADAU))]
  ""
  "v1sadau\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_v1sadu"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_V1SADU))]
  ""
  "v1sadu\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "*insn_v1sadu"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(truncate:SI
	 (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
		     (match_operand:DI 2 "reg_or_0_operand" "rO")]
		    UNSPEC_INSN_V1SADU)))]
   ""
  "v1sadu\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_v2adiffs"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_V2ADIFFS))]
  ""
  "v2adiffs\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_v2avgs"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_V2AVGS))]
  ""
  "v2avgs\t%0, %r1, %r2"
  [(set_attr "type" "X0")])

(define_insn "insn_v2dotp"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_V2DOTP))]
  ""
  "v2dotp\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_v2dotpa"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_V2DOTPA))]
  ""
  "v2dotpa\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_v2mulfsc"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_V2MULFSC))]
  ""
  "v2mulfsc\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_v2sadas"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_V2SADAS))]
  ""
  "v2sadas\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_v2sadau"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "0")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")
                    (match_operand:DI 3 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_V2SADAU))]
  ""
  "v2sadau\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_v2sads"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_V2SADS))]
  ""
  "v2sads\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "*insn_v2sads"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(truncate:SI
	 (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
		     (match_operand:DI 2 "reg_or_0_operand" "rO")]
		    UNSPEC_INSN_V2SADS)))]
  ""
  "v2sads\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_v2sadu"
  [(set (match_operand:DI 0 "register_operand" "=r")
        (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
                    (match_operand:DI 2 "reg_or_0_operand" "rO")]
                   UNSPEC_INSN_V2SADU))]
  ""
  "v2sadu\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "*insn_v2sadu"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(truncate:SI
	 (unspec:DI [(match_operand:DI 1 "reg_or_0_operand" "rO")
		     (match_operand:DI 2 "reg_or_0_operand" "rO")]
		    UNSPEC_INSN_V2SADU)))]
  ""
  "v2sadu\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_wh64"
  [(unspec_volatile:VOID [(match_operand 0 "pointer_operand" "rO")]
                         UNSPEC_INSN_WH64)
   (clobber (mem:BLK (const_int 0)))]
  ""
  "wh64\t%r0"
  [(set_attr "type" "X1")])


;; Network intrinsics

;; Note the "pseudo" text is handled specially by the
;; asm_output_opcode routine.  If the output is an empty string, the
;; instruction would bypass the asm_output_opcode routine, bypassing
;; the bundle handling code.
(define_insn "tilegx_network_barrier"
  [(unspec_volatile:SI [(const_int 0)] UNSPEC_NETWORK_BARRIER)]
  ""
  "pseudo"
  [(set_attr "type" "nothing")
   (set_attr "length" "0")])

(define_insn "*netreg_receive"
  [(set (match_operand:DI 0 "nonimmediate_operand" "=r,U,m")
        (unspec_volatile:DI [(match_operand:DI 1 "netreg_operand" "i,i,i")
			     (reg:DI TILEGX_NETORDER_REG)]
                            UNSPEC_NETWORK_RECEIVE))
   (clobber (reg:DI TILEGX_NETORDER_REG))]

  ""
  "@
   move\t%0, %N1
   st\t%0, %N1
   st_add\t%I0, %N1, %i0"
  [(set_attr "type" "*,Y2,X1")])

(define_insn "*netreg_send"
  [(unspec_volatile:DI
    [(match_operand:DI 0 "netreg_operand"  "i,i,i,i,i,i")
     (match_operand:DI 1 "reg_or_cint_operand" "r,I,J,K,N,P")
     (reg:DI TILEGX_NETORDER_REG)]
     UNSPEC_NETWORK_SEND)
   (clobber (reg:DI TILEGX_NETORDER_REG))]
  ""
  "@
   move\t%N0, %r1
   movei\t%N0, %1
   moveli\t%N0, %1
   shl16insli\t%N0, zero, %h1
   v1addi\t%N0, zero, %j1
   v2addi\t%N0, zero, %h1"
  [(set_attr "type" "*,*,X01,X01,X01,X01")])

(define_expand "tilegx_idn0_receive"
  [(parallel
    [(set (match_operand:DI 0 "register_operand" "")
	  (unspec_volatile:DI [(const_int TILEGX_NETREG_IDN0)
			       (reg:DI TILEGX_NETORDER_REG)]
			      UNSPEC_NETWORK_RECEIVE))
     (clobber (reg:DI TILEGX_NETORDER_REG))])]
  "")

(define_expand "tilegx_idn1_receive"
  [(parallel
    [(set (match_operand:DI 0 "register_operand" "")
	  (unspec_volatile:DI [(const_int TILEGX_NETREG_IDN1)
			       (reg:DI TILEGX_NETORDER_REG)]
			      UNSPEC_NETWORK_RECEIVE))
     (clobber (reg:DI TILEGX_NETORDER_REG))])]
  "")

(define_expand "tilegx_idn_send"
  [(parallel
    [(unspec_volatile:DI [(const_int TILEGX_NETREG_IDN0)
			  (match_operand:DI 0 "reg_or_cint_operand" "")
			  (reg:DI TILEGX_NETORDER_REG)]
			 UNSPEC_NETWORK_SEND)
     (clobber (reg:DI TILEGX_NETORDER_REG))])]
  "")

(define_expand "tilegx_udn0_receive"
  [(parallel
    [(set (match_operand:DI 0 "register_operand" "")
	  (unspec_volatile:DI [(const_int TILEGX_NETREG_UDN0)
			       (reg:DI TILEGX_NETORDER_REG)]
			      UNSPEC_NETWORK_RECEIVE))
     (clobber (reg:DI TILEGX_NETORDER_REG))])]
  "")

(define_expand "tilegx_udn1_receive"
  [(parallel
    [(set (match_operand:DI 0 "register_operand" "")
	  (unspec_volatile:DI [(const_int TILEGX_NETREG_UDN1)
			       (reg:DI TILEGX_NETORDER_REG)]
			      UNSPEC_NETWORK_RECEIVE))
     (clobber (reg:DI TILEGX_NETORDER_REG))])]
  "")

(define_expand "tilegx_udn2_receive"
  [(parallel
    [(set (match_operand:DI 0 "register_operand" "")
	  (unspec_volatile:DI [(const_int TILEGX_NETREG_UDN2)
			       (reg:DI TILEGX_NETORDER_REG)]
			      UNSPEC_NETWORK_RECEIVE))
     (clobber (reg:DI TILEGX_NETORDER_REG))])]
  "")

(define_expand "tilegx_udn3_receive"
  [(parallel
    [(set (match_operand:DI 0 "register_operand" "")
	  (unspec_volatile:DI [(const_int TILEGX_NETREG_UDN3)
			       (reg:DI TILEGX_NETORDER_REG)]
			      UNSPEC_NETWORK_RECEIVE))
     (clobber (reg:DI TILEGX_NETORDER_REG))])]
  "")

(define_expand "tilegx_udn_send"
  [(parallel
    [(unspec_volatile:DI [(const_int TILEGX_NETREG_UDN0)
			  (match_operand:DI 0 "reg_or_cint_operand" "")
			  (reg:DI TILEGX_NETORDER_REG)]
			 UNSPEC_NETWORK_SEND)
    (clobber (reg:DI TILEGX_NETORDER_REG))])]
  "")

(define_insn "*netreg_adddi_to_network"
  [(unspec_volatile:DI
    [(match_operand:DI 0 "netreg_operand" "i,i,i")
     (plus:DI (match_operand:DI 1 "reg_or_0_operand" "%rO,rO,rO")
              (match_operand:DI 2 "add_operand" "r,I,JT"))
     (reg:DI TILEGX_NETORDER_REG)]
    UNSPEC_NETWORK_SEND)
   (clobber (reg:DI TILEGX_NETORDER_REG))]
  ""
  "@
   add\t%N0, %r1, %2
   addi\t%N0, %r1, %2
   addli\t%N0, %r1, %H2"
  [(set_attr "type" "*,*,X01")])

(define_insn "*netreg_adddi_from_network"
  [(set (match_operand:DI 0 "register_operand" "=r,r,r")
	(plus:DI (unspec_volatile:DI
                  [(match_operand:DI 1 "netreg_operand" "%i,i,i")
		   (reg:DI TILEGX_NETORDER_REG)]
                  UNSPEC_NETWORK_RECEIVE)
		 (match_operand:DI 2 "add_operand" "rO,I,JT")))
   (clobber (reg:DI TILEGX_NETORDER_REG))]
  ""
  "@
   add\t%0, %N1, %r2
   addi\t%0, %N1, %2
   addli\t%0, %N1, %H2"
  [(set_attr "type" "*,*,X01")])


;;
;; Stack protector instructions.
;;

(define_expand "stack_protect_set"
  [(set (match_operand 0 "nonautoincmem_operand" "")
	(match_operand 1 "nonautoincmem_operand" ""))]
  ""
{
#ifdef TARGET_THREAD_SSP_OFFSET
  rtx tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM);
  rtx ssp_addr = gen_rtx_PLUS (Pmode, tp, GEN_INT (TARGET_THREAD_SSP_OFFSET));
  rtx ssp = gen_reg_rtx (Pmode);
  
  emit_insn (gen_rtx_SET (VOIDmode, ssp, ssp_addr));

  operands[1] = gen_rtx_MEM (Pmode, ssp);
#endif

  if (TARGET_32BIT)
    emit_insn (gen_stack_protect_setsi (operands[0], operands[1]));
  else
    emit_insn (gen_stack_protect_setdi (operands[0], operands[1]));

  DONE;
})

(define_insn "stack_protect_setsi"
  [(set (match_operand:SI 0 "nonautoincmem_operand" "=U")
        (unspec:SI [(match_operand:SI 1 "nonautoincmem_operand" "U")]
		   UNSPEC_SP_SET))
   (set (match_scratch:SI 2 "=&r") (const_int 0))]
  ""
  "ld4s\t%2, %1; { st4\t%0, %2; move\t%2, zero }"
  [(set_attr "length" "16")
   (set_attr "type" "cannot_bundle_3cycle")])

(define_insn "stack_protect_setdi"
  [(set (match_operand:DI 0 "nonautoincmem_operand" "=U")
        (unspec:DI [(match_operand:DI 1 "nonautoincmem_operand" "U")]
		   UNSPEC_SP_SET))
   (set (match_scratch:DI 2 "=&r") (const_int 0))]
  ""
  "ld\t%2, %1; { st\t%0, %2; move\t%2, zero }"
  [(set_attr "length" "16")
   (set_attr "type" "cannot_bundle_3cycle")])

(define_expand "stack_protect_test"
  [(match_operand 0 "nonautoincmem_operand" "")
   (match_operand 1 "nonautoincmem_operand" "")
   (match_operand 2 "" "")]
  ""
{
  rtx compare_result;
  rtx bcomp, loc_ref;

#ifdef TARGET_THREAD_SSP_OFFSET
  rtx tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM);
  rtx ssp_addr = gen_rtx_PLUS (Pmode, tp, GEN_INT (TARGET_THREAD_SSP_OFFSET));
  rtx ssp = gen_reg_rtx (Pmode);
  
  emit_insn (gen_rtx_SET (VOIDmode, ssp, ssp_addr));

  operands[1] = gen_rtx_MEM (Pmode, ssp);
#endif

  compare_result = gen_reg_rtx (Pmode);

  if (TARGET_32BIT)
    emit_insn (gen_stack_protect_testsi (compare_result, operands[0],
					 operands[1]));
  else
    emit_insn (gen_stack_protect_testdi (compare_result, operands[0],
					 operands[1]));

  bcomp = gen_rtx_NE (SImode, compare_result, const0_rtx);

  loc_ref = gen_rtx_LABEL_REF (VOIDmode, operands[2]);

  emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
			       gen_rtx_IF_THEN_ELSE (VOIDmode, bcomp,
						     loc_ref, pc_rtx)));

  DONE;
})

(define_insn "stack_protect_testsi"
  [(set (match_operand:SI 0 "register_operand" "=&r")
        (unspec:SI [(match_operand:SI 1 "nonautoincmem_operand" "U")
                    (match_operand:SI 2 "nonautoincmem_operand" "U")]
                   UNSPEC_SP_TEST))
   (set (match_scratch:SI 3 "=&r") (const_int 0))]
  ""
  "ld4s\t%0, %1; ld4s\t%3, %2; { cmpeq\t%0, %0, %3; move\t%3, zero }"
  [(set_attr "length" "24")
   (set_attr "type" "cannot_bundle_4cycle")])

(define_insn "stack_protect_testdi"
  [(set (match_operand:DI 0 "register_operand" "=&r")
        (unspec:DI [(match_operand:DI 1 "nonautoincmem_operand" "U")
                    (match_operand:DI 2 "nonautoincmem_operand" "U")]
                   UNSPEC_SP_TEST))
   (set (match_scratch:DI 3 "=&r") (const_int 0))]
  ""
  "ld\t%0, %1; ld\t%3, %2; { cmpeq\t%0, %0, %3; move\t%3, zero }"
  [(set_attr "length" "24")
   (set_attr "type" "cannot_bundle_4cycle")])

(include "sync.md")
