;; Machine description for Tilera TILEPro chip for GCC.
;; Copyright (C) 2011, 2012
;; 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_ADDLIS                  1)
  (UNSPEC_INSN_AULI                    2)
  (UNSPEC_INSN_AVGB_U                  3)
  (UNSPEC_INSN_AVGH                    4)
  (UNSPEC_INSN_BITX                    5)
  (UNSPEC_INSN_CRC32_32                6)
  (UNSPEC_INSN_CRC32_8                 7)
  (UNSPEC_INSN_DRAIN                   8)
  (UNSPEC_INSN_DTLBPR                  9)
  (UNSPEC_INSN_DWORD_ALIGN             10)
  (UNSPEC_INSN_FINV                    11)
  (UNSPEC_INSN_FLUSH                   12)
  (UNSPEC_INSN_FNOP                    13)
  (UNSPEC_INSN_ICOH                    14)
  (UNSPEC_INSN_ILL                     15)
  (UNSPEC_INSN_INFO                    16)
  (UNSPEC_INSN_INFOL                   17)
  (UNSPEC_INSN_INV                     18)
  (UNSPEC_INSN_LNK                     19)
  (UNSPEC_INSN_MFSPR                   20)
  (UNSPEC_INSN_MNZB                    21)
  (UNSPEC_INSN_MNZH                    22)
  (UNSPEC_INSN_MOVELIS                 23)
  (UNSPEC_INSN_MTSPR                   24)
  (UNSPEC_INSN_MZB                     25)
  (UNSPEC_INSN_MZH                     26)
  (UNSPEC_INSN_NAP                     27)
  (UNSPEC_INSN_PACKBS_U                28)
  (UNSPEC_INSN_PACKHB                  29)
  (UNSPEC_INSN_PACKHS                  30)
  (UNSPEC_INSN_PACKLB                  31)
  (UNSPEC_INSN_PREFETCH_L1             32)
  (UNSPEC_INSN_TBLIDXB0                33)
  (UNSPEC_INSN_TBLIDXB1                34)
  (UNSPEC_INSN_TBLIDXB2                35)
  (UNSPEC_INSN_TBLIDXB3                36)
  (UNSPEC_INSN_WH64                    37)

  ;; 2 cycles
  (UNSPEC_INSN_ADIFFB_U                100)
  (UNSPEC_INSN_ADIFFH                  101)
  (UNSPEC_INSN_MULHHA_SS               102)
  (UNSPEC_INSN_MULHHA_SU               103)
  (UNSPEC_INSN_MULHHA_UU               104)
  (UNSPEC_INSN_MULHHSA_UU              105)
  (UNSPEC_INSN_MULHH_SS                106)
  (UNSPEC_INSN_MULHH_SU                107)
  (UNSPEC_INSN_MULHH_UU                108)
  (UNSPEC_INSN_MULHLA_SS               109)
  (UNSPEC_INSN_MULHLA_SU               110)
  (UNSPEC_INSN_MULHLA_US               111)
  (UNSPEC_INSN_MULHLA_UU               112)
  (UNSPEC_INSN_MULHLSA_UU              113)
  (UNSPEC_INSN_MULHL_SS                114)
  (UNSPEC_INSN_MULHL_SU                115)
  (UNSPEC_INSN_MULHL_US                116)
  (UNSPEC_INSN_MULHL_UU                117)
  (UNSPEC_INSN_MULLLA_SS               118)
  (UNSPEC_INSN_MULLLA_SU               119)
  (UNSPEC_INSN_MULLLA_UU               120)
  (UNSPEC_INSN_MULLLSA_UU              121)
  (UNSPEC_INSN_MULLL_SU                122)
  (UNSPEC_INSN_MULLL_SS                123)
  (UNSPEC_INSN_MULLL_UU                124)
  (UNSPEC_INSN_SADAB_U                 125)
  (UNSPEC_INSN_SADAH                   126)
  (UNSPEC_INSN_SADAH_U                 127)
  (UNSPEC_INSN_SADB_U                  128)
  (UNSPEC_INSN_SADH                    129)
  (UNSPEC_INSN_SADH_U                  130)

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

  ;; Blockage
  (UNSPEC_BLOCKAGE                     200)

  ;; Latency specifying loads.
  (UNSPEC_LATENCY_L2                   201)
  (UNSPEC_LATENCY_MISS                 202)

  ;; Lnk and its label
  (UNSPEC_LNK_AND_LABEL                203)

  ;; Memory fence
  (UNSPEC_MF                           204)

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

  ;; Operations that access network registers.
  (UNSPEC_NETWORK_RECEIVE              206)
  (UNSPEC_NETWORK_SEND                 207)

  ;; Stack protector operations
  (UNSPEC_SP_SET                       208)
  (UNSPEC_SP_TEST                      209)

  ;; A call to __tls_get_addr
  (UNSPEC_TLS_GD_CALL                  210)

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

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

  ;;
  ;; The following are operands.
  ;;
  (UNSPEC_PCREL_SYM                    300)
  (UNSPEC_GOT16_SYM                    301)
  (UNSPEC_GOT32_SYM                    302)
  (UNSPEC_TLS_GD                       303)
  (UNSPEC_TLS_IE                       304)
  (UNSPEC_TLS_LE                       305)
])

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

;; Constants for network registers.
(define_constants [
  (TILEPRO_NETREG_IDN0 0)
  (TILEPRO_NETREG_IDN1 1)
  (TILEPRO_NETREG_SN   2)
  (TILEPRO_NETREG_UDN0 3)
  (TILEPRO_NETREG_UDN1 4)
  (TILEPRO_NETREG_UDN2 5)
  (TILEPRO_NETREG_UDN3 6)
])

;; Constants for special purpose registers.
(define_constants [
  (TILEPRO_NETORDER_REG 66)])


;; Operand and operator predicates and constraints

(include "predicates.md")
(include "constraints.md")
(include "tilepro-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_miss,X01,Y0,Y0_2cycle,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 iterators.
(define_mode_iterator I48MODE [SI DI])
(define_mode_iterator I12MODE [QI HI])

(define_code_iterator binop_u5bit [ashift ashiftrt lshiftrt rotate])
(define_code_iterator binop_with_imm
  [ashift lshiftrt ashiftrt rotate eq lt and ior xor])
(define_code_iterator unop [bswap clz ctz popcount])

(define_mode_attr load [(QI "lb") (HI "lh") (SI "lw")])
(define_mode_attr store [(QI "sb") (HI "sh") (SI "sw")])

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

;; <insn> expands to the name of the insn that implements a particular
;; code.
(define_code_attr insn [(ashift "shl")
			(ashiftrt "sra")
			(lshiftrt "shr")
			(eq "seq")
			(ne "sne")
			(lt "slt")
			(ltu "slt")
			(le "slte")
			(leu "slte")
			(minus "sub")
			(plus "add")
			(rotate "rl")
			(smax "max")
			(smin "min")
			(umax "max")
			(umin "min")
			(ss_minus "sub")
			(ss_plus "add")
			(us_minus "sub")
			(us_plus "add")
			(and "and")
			(ior "or")
			(xor "xor")
			(bswap "bytex")
			(clz "clz")
			(ctz "ctz")
			(popcount "pcnt")])

;; <u> expands to the suffix of the insn that implements a particular
;; code.
(define_code_attr u [(ashift "")
		     (ashiftrt "")
		     (lshiftrt "")
		     (eq "")
		     (ne "")
		     (lt "")
		     (ltu "_u")
		     (le "")
		     (leu "_u")
		     (minus "")
		     (plus "")
		     (rotate "")
		     (smax "")
		     (smin "")
		     (umax "_u")
		     (umin "_u")
		     (ss_minus "s")
		     (ss_plus "s")
		     (us_minus "s_u")
		     (us_plus "s_u")
		     (and "")
		     (ior "")
		     (xor "")])

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

(define_mode_iterator VEC [V4QI V2HI])

;; 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_plus ne le leu minus us_minus])

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

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

;; <pack_u> expands to the suffix of the insn that implements a
;; particular vector packing code.
(define_code_attr pack_u [(truncate "")
			  (us_truncate "s_u")
			  (ss_truncate "s")])


;;
;; The basic data move insns.
;;

(define_expand "movqi"
  [(set (match_operand:QI 0 "nonimmediate_operand" "")
	(match_operand:QI 1 "nonautoinc_operand" ""))]
  ""
{
  if (tilepro_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
   lb_u\t%0, %1
   lbadd_u\t%0, %I1, %i1
   sb\t%0, %r1
   sbadd\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 (tilepro_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,J,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, %1
   lh_u\t%0, %1
   lhadd_u\t%0, %I1, %i1
   sh\t%0, %r1
   shadd\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 (tilepro_expand_mov (SImode, operands))
    DONE;
})

(define_insn "*movsi_high_insn"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(high:SI (match_operand:SI 1 "symbolic_operand" "in")))]
  ""
  "auli\t%0, zero, ha16(%1)"
  [(set_attr "type" "X01")])

(define_insn "*movsi_insn"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r,r,r,r,r,r,U,m")
	(match_operand:SI 1 "move_operand"         "r,I,J,K,N,P,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, %1
   auli\t%0, zero, %h1
   addib\t%0, zero, %j1
   addih\t%0, zero, %h1
   lw\t%0, %1
   lwadd\t%0, %I1, %i1
   sw\t%0, %r1
   swadd\t%I0, %r1, %i0"
  [(set_attr "type" "*,*,X01,X01,X01,X01,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"))]
  ""
  "mm\t%r0, %r1, %r0, 0, 7"
  [(set_attr "type" "X01")])
  
(define_insn "movstricthi"
  [(set (strict_low_part (match_operand:HI 0 "register_operand" "+r"))
	(match_operand:HI 1 "reg_or_0_operand" "rO"))]
  ""
  "mm\t%r0, %r1, %r0, 0, 15"
  [(set_attr "type" "X01")])
  
(define_expand "movmisalign<mode>"
  [(set (match_operand:VEC 0 "nonautoincmem_nonimmediate_operand" "")
        (match_operand:VEC 1 "nonautoincmem_general_operand" ""))]
  ""
{
  tilepro_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
   lw\t%0, %1
   lwadd\t%0, %I1, %i1
   sw\t%0, %r1
   swadd\t%I0, %r1, %i0"
  [(set_attr "type" "*,Y2_2cycle,X1_2cycle,Y2,X1")])

(define_expand "mov<mode>"
  [(set (match_operand:VEC 0 "nonimmediate_operand" "")
        (match_operand:VEC 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], <MODE>mode)
      && operands[1] != const0_rtx
      && (register_operand (operands[0], <MODE>mode)
          || (!reload_in_progress && !reload_completed)))
    {
      emit_insn (gen_movsi (gen_lowpart (SImode, operands[0]),
                            gen_lowpart (SImode, operands[1])));
      DONE;
    }
})

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


;;
;; Bit-field extracts
;;

(define_expand "extv"
  [(set (match_operand:SI 0 "register_operand" "")
	(sign_extract:SI
	 (match_operand:QI 1 "nonautoincmem_operand" "")
	 (match_operand:SI 2 "immediate_operand" "")
	 (match_operand:SI 3 "immediate_operand" "")))]
  ""
{
  HOST_WIDE_INT bit_offset, bit_width;
  HOST_WIDE_INT first_byte_offset, last_byte_offset;

  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 4 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 > 3)
    FAIL;

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

  DONE;
})

(define_expand "extzv"
  [(set (match_operand:SI 0 "register_operand" "")
	(zero_extract:SI
	 (match_operand:QI 1 "nonautoincmem_operand" "")
	 (match_operand:SI 2 "immediate_operand" "")
	 (match_operand:SI 3 "immediate_operand" "")))]
  ""
{
  HOST_WIDE_INT bit_offset, bit_width;
  HOST_WIDE_INT first_byte_offset, last_byte_offset;

  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 4 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 > 3)
    FAIL;

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

  DONE;
})


;;
;; Arithmetic ops
;;

(define_insn "*s123a_insn"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (plus:SI (mult:SI (match_operand:SI 1 "reg_or_0_operand" "rO")
                          (match_operand:SI 2 "cint_248_operand" "I"))
                 (match_operand:SI 3 "reg_or_0_operand" "rO")))]
  ""
  "s%t2a\t%0, %r1, %r3")

(define_expand "addsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(plus:SI (match_operand:SI 1 "register_operand" "")
		 (match_operand:SI 2 "reg_or_cint_operand" "")))]
  ""
  "
    if (tilepro_expand_addsi (operands[0], operands[1], operands[2]))
      DONE;
  ")

(define_insn "*addsi_high_insn"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (plus:SI
         (match_operand:SI 1 "reg_or_0_operand" "%rO")
         (high:SI (match_operand:SI 2 "const_symbolic_operand" "T"))))]
  ""
  "auli\t%0, %r1, %H2"
  [(set_attr "type" "X01")])

(define_insn "*addsi_lo_sum_insn"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (lo_sum:SI
         (match_operand:SI 1 "reg_or_0_operand" "%rO")
         (match_operand:SI 2 "const_symbolic_operand" "T")))]
  ""
  "addli\t%0, %r1, %L2"
  [(set_attr "type" "X01")])

(define_insn "*addsi3_insn"
  [(set (match_operand:SI 0 "register_operand" "=r,r,r,r")
	(plus:SI (match_operand:SI 1 "reg_or_0_operand" "%rO,rO,rO,rO")
		 (match_operand:SI 2 "add_operand" "r,I,J,K")))]
  ""
  "@
   add\t%0, %r1, %r2
   addi\t%0, %r1, %2
   addli\t%0, %r1, %2
   auli\t%0, %r1, %h2"
  [(set_attr "type" "*,*,X01,X01")])

(define_insn "subsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(minus:SI (match_operand:SI 1 "reg_or_0_operand" "rO")
                  (match_operand:SI 2 "reg_or_0_operand" "rO")))]
  ""
  "sub\t%0, %r1, %r2")

(define_insn "negsi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(neg:SI (match_operand:SI 1 "reg_or_0_operand" "rO")))]
  ""
  "sub\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")))]
  ""
  "adds\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")))]
  ""
  "subs\t%0, %r1, %r2"
  [(set_attr "type" "X01")])

;;
;; Shifts
;;

;; ashift, ashiftrt, lshiftrt, rotate.
(define_insn "<optab>si3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(binop_u5bit:SI (match_operand:SI 1 "reg_or_0_operand" "rO,rO")
			(match_operand:SI 2 "reg_or_u5bit_operand" "I,rO")))]
  ""
  "@
  <insn>i\t%0, %r1, %2
  <insn>\t%0, %r1, %r2")


;;
;; Compares
;;

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

(define_insn "insn_seq"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(eq:SI (match_operand:SI 1 "reg_or_0_operand" "%rO,rO")
               (match_operand:SI 2 "reg_or_cint_operand" "I,rO")))]
  ""
  "@
   seqi\t%0, %r1, %2
   seq\t%0, %r1, %r2")

(define_insn "insn_sne"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ne:SI (match_operand:SI 1 "reg_or_0_operand" "rO")
               (match_operand:SI 2 "reg_or_cint_operand" "rO")))]
  ""
  "sne\t%0, %r1, %r2")

(define_insn "insn_slt"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(lt:SI (match_operand:SI 1 "reg_or_0_operand" "rO,rO")
               (match_operand:SI 2 "reg_or_cint_operand" "I,rO")))]
  ""
  "@
   slti\t%0, %r1, %2
   slt\t%0, %r1, %r2")

(define_insn "insn_slte"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(le:SI (match_operand:SI 1 "reg_or_0_operand" "rO,rO")
               (match_operand:SI 2 "reg_or_cint_operand" "L,rO")))]
  ""
  "@
   slti\t%0, %r1, %P2
   slte\t%0, %r1, %r2")

(define_insn "insn_slt_u"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(ltu:SI (match_operand:SI 1 "reg_or_0_operand" "rO,rO")
                (match_operand:SI 2 "reg_or_cint_operand" "I,rO")))]
  ""
  "@
   slti_u\t%0, %r1, %2
   slt_u\t%0, %r1, %r2")

(define_insn "insn_slte_u"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(leu:SI (match_operand:SI 1 "reg_or_0_operand" "rO,rO")
                (match_operand:SI 2 "reg_or_cint_operand" "Q,rO")))]
  ""
  "@
   slti_u\t%0, %r1, %P2
   slte_u\t%0, %r1, %r2")


;;
;; Logical ops
;;

(define_insn "andsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r,r")
	(and:SI (match_operand:SI 1 "reg_or_0_operand" "%rO,rO,rO")
                (match_operand:SI 2 "and_operand" "I,M,rO")))]
  ""
  "@
   andi\t%0, %r1, %2
   mm\t%0, %r1, zero, %M2
   and\t%0, %r1, %r2"
  [(set_attr "type" "*,X01,*")])
  
(define_insn "iorsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(ior:SI (match_operand:SI 1 "reg_or_0_operand" "%rO,rO")
                (match_operand:SI 2 "reg_or_s8bit_operand" "I,rO")))]
  ""
  "@
   ori\t%0, %r1, %2
   or\t%0, %r1, %r2")
  
(define_insn "xorsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(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")])
  
;; bswap, clz, ctz, popcount
(define_insn "<optab>si2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(unop:SI (match_operand:SI 1 "reg_or_0_operand" "rO")))]
  ""
  "<insn>\t%0, %r1"
  [(set_attr "type" "Y0")])

(define_expand "ctzdi2"
  [(set (match_operand:DI 0 "register_operand" "")
	(ctz:DI (match_operand:DI 1 "reg_or_0_operand" "")))]
  ""
{
  rtx lo, hi, ctz_lo, ctz_hi, ctz_hi_plus_32, result;

  split_di (&operands[1], 1, &lo, &hi);
  lo = force_reg (SImode, lo);
  hi = force_reg (SImode, hi);

  ctz_lo = gen_reg_rtx (SImode);
  emit_insn (gen_ctzsi2 (ctz_lo, lo));

  ctz_hi = gen_reg_rtx (SImode);
  emit_insn (gen_ctzsi2 (ctz_hi, hi));

  ctz_hi_plus_32 = gen_reg_rtx (SImode);
  emit_insn (gen_addsi3 (ctz_hi_plus_32, ctz_hi, GEN_INT (32)));

  result = gen_reg_rtx (SImode);
  emit_insn (gen_insn_mvz (result, ctz_lo, lo, ctz_hi_plus_32));

  emit_move_insn (operands[0], convert_to_mode (DImode, result, 1));

  DONE;
})

(define_expand "clzdi2"
  [(set (match_operand:DI 0 "register_operand" "")
	(clz:DI (match_operand:DI 1 "reg_or_0_operand" "")))]
  ""
{
  rtx lo, hi, clz_lo, clz_hi, clz_lo_plus_32, result;

  split_di (&operands[1], 1, &lo, &hi);
  lo = force_reg (SImode, lo);
  hi = force_reg (SImode, hi);

  clz_lo = gen_reg_rtx (SImode);
  emit_insn (gen_clzsi2 (clz_lo, lo));

  clz_hi = gen_reg_rtx (SImode);
  emit_insn (gen_clzsi2 (clz_hi, hi));

  clz_lo_plus_32 = gen_reg_rtx (SImode);
  emit_insn (gen_addsi3 (clz_lo_plus_32, clz_lo, GEN_INT (32)));

  result = gen_reg_rtx (SImode);
  emit_insn (gen_insn_mvz (result, clz_hi, hi, clz_lo_plus_32));

  emit_move_insn (operands[0], convert_to_mode (DImode, result, 1));

  DONE;
})

(define_expand "ffsdi2"
  [(set (match_operand:DI 0 "register_operand" "")
	(ffs:DI (match_operand:DI 1 "reg_or_0_operand" "")))]
  ""
{
  rtx lo, hi, ctz_lo, ctz_hi, ctz_hi_plus_32, ctz, ctz_plus_1,ctz_cond;
  rtx result;

  split_di (&operands[1], 1, &lo, &hi);
  lo = force_reg (SImode, lo);
  hi = force_reg (SImode, hi);

  ctz_lo = gen_reg_rtx (SImode);
  emit_insn (gen_ctzsi2 (ctz_lo, lo));

  ctz_hi = gen_reg_rtx (SImode);
  emit_insn (gen_ctzsi2 (ctz_hi, hi));

  ctz_hi_plus_32 = gen_reg_rtx (SImode);
  emit_insn (gen_addsi3 (ctz_hi_plus_32, ctz_hi, GEN_INT (32)));

  ctz = gen_reg_rtx (SImode);
  emit_insn (gen_insn_mvz (ctz, ctz_lo, lo, ctz_hi_plus_32));

  ctz_plus_1 = gen_reg_rtx (SImode);
  emit_insn (gen_addsi3 (ctz_plus_1, ctz, GEN_INT (1)));

  ctz_cond = gen_reg_rtx (SImode);
  emit_insn (gen_iorsi3 (ctz_cond, lo, hi));

  result = gen_reg_rtx (SImode);
  emit_insn (gen_insn_mvz (result, ctz_plus_1, ctz_cond, const0_rtx));

  emit_move_insn (operands[0], convert_to_mode (DImode, result, 1));

  DONE;
})

(define_expand "popcountdi2"
  [(set (match_operand:DI 0 "register_operand" "")
	(popcount:DI (match_operand:DI 1 "nonmemory_operand" "")))]
  ""
{
  rtx lo, hi, popcount_lo, popcount_hi, result;

  split_di (&operands[1], 1, &lo, &hi);
  lo = force_reg (SImode, lo);
  hi = force_reg (SImode, hi);

  popcount_lo = gen_reg_rtx (SImode);
  emit_insn (gen_popcountsi2 (popcount_lo, lo));

  popcount_hi = gen_reg_rtx (SImode);
  emit_insn (gen_popcountsi2 (popcount_hi, hi));

  result = gen_reg_rtx (SImode);
  emit_insn (gen_addsi3 (result, popcount_lo, popcount_hi));

  emit_move_insn (operands[0], convert_to_mode (DImode, result, 1));

  DONE;
})

(define_expand "paritysi2"
  [(set (match_operand:SI 0 "register_operand" "")
	(parity:SI (match_operand:SI 1 "reg_or_0_operand" "")))]
  ""
  {
    operands[2] = gen_reg_rtx (SImode);
    emit_insn (gen_popcountsi2 (operands[2], operands[1]));
    emit_insn (gen_andsi3 (operands[0], operands[2], const1_rtx));
    DONE;
  })

(define_expand "paritydi2"
  [(set (match_operand:DI 0 "register_operand" "")
	(parity:DI (match_operand:DI 1 "nonmemory_operand" "")))]
  ""
{
  rtx lo, hi, xor_lohi, result;

  split_di (&operands[1], 1, &lo, &hi);
  lo = force_reg (SImode, lo);
  hi = force_reg (SImode, hi);

  xor_lohi = gen_reg_rtx (SImode);
  emit_insn (gen_xorsi3 (xor_lohi, lo, hi));

  result = gen_reg_rtx (SImode);
  emit_insn (gen_paritysi2 (result, xor_lohi));

  emit_move_insn (operands[0], convert_to_mode (DImode, result, 1));

  DONE;
})

(define_insn "one_cmplsi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(not:SI (match_operand:SI 1 "reg_or_0_operand" "rO")))]
  ""
  "nor\t%0, %r1, zero")


;;
;; Conditional moves.
;;

(define_expand "movsicc"
  [(set (match_operand:SI 0 "register_operand" "")
	(if_then_else:SI (match_operand 1 "comparison_operator" "")
			 (match_operand:SI 2 "reg_or_0_operand" "")
			 (match_operand:SI 3 "reg_or_0_operand" "")))]
  ""
  { operands[1] = tilepro_emit_conditional_move (operands[1]); })

(define_insn "movcc_insn"
  [(set (match_operand:SI 0 "register_operand" "=r,r,r,r")
	(if_then_else:SI
	 (match_operator 4 "eqne_operator"
	  [(match_operand:SI 1 "reg_or_0_operand" "rO,rO,rO,rO")
	   (const_int 0)])
	 (match_operand:SI 2 "reg_or_0_operand"	"rO,O,rO,0")
	 (match_operand:SI 3 "reg_or_0_operand"	"O,rO,0,rO")))]
  ""
  "@
   m%c4\t%0, %r1, %r2
   m%C4\t%0, %r1, %r3
   mv%c4\t%0, %r1, %r2
   mv%C4\t%0, %r1, %r3"
  [(set_attr "type" "*,*,Y0,Y0")])

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

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

(define_expand "insn_mvz"
  [(set (match_operand:SI 0 "register_operand" "")
	(if_then_else:SI
         (eq (match_operand:SI 2 "reg_or_0_operand" "")
             (const_int 0))
         (match_operand:SI 3 "reg_or_0_operand" "")
         (match_operand:SI 1 "reg_or_0_operand" "")))])
   
(define_expand "insn_mvnz"
  [(set (match_operand:SI 0 "register_operand" "")
	(if_then_else:SI
         (ne (match_operand:SI 2 "reg_or_0_operand" "")
             (const_int 0))
         (match_operand:SI 3 "reg_or_0_operand" "")
         (match_operand:SI 1 "reg_or_0_operand" "")))])
   

;;
;; Conversions
;;

(define_insn "zero_extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "=r,r,r")
	(zero_extend:SI (match_operand:QI 1 "move_operand" "rO,U,m")))]
  ""
  "@
   mm\t%0, %r1, zero, 0, 7
   lb_u\t%0, %1
   lbadd_u\t%0, %I1, %i1"
  [(set_attr "type" "X01,Y2_2cycle,X1_2cycle")])
  
(define_insn "zero_extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "=r,r,r")
	(zero_extend:SI (match_operand:HI 1 "move_operand" "rO,U,m")))]
  ""
  "@
   mm\t%0, %r1, zero, 0, 15
   lh_u\t%0, %1
   lhadd_u\t%0, %I1, %i1"
  [(set_attr "type" "X01,Y2_2cycle,X1_2cycle")])

(define_expand "extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "")
	(sign_extend:SI (match_operand:HI 1 "move_operand" "")))]
  ""
{
  if (!memory_operand (operands[1], HImode))
  {
    operands[1] = gen_lowpart (SImode, operands[1]);
    operands[2] = can_create_pseudo_p () ? gen_reg_rtx (SImode) : operands[0];

    emit_move_insn (operands[2], gen_rtx_ASHIFT (SImode, operands[1],
					         GEN_INT (16)));
    emit_move_insn (operands[0], gen_rtx_ASHIFTRT (SImode, operands[2],
					           GEN_INT (16)));
    DONE;
  }
})

(define_insn "*lh"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(sign_extend:SI (match_operand:HI 1 "memory_operand" "U,m")))]
  ""
  "@
   lh\t%0, %1
   lhadd\t%0, %I1, %i1"
  [(set_attr "type" "Y2_2cycle,X1_2cycle")])

(define_expand "extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "")
	(sign_extend:SI (match_operand:QI 1 "move_operand" "")))]
  ""
{
  if (!memory_operand (operands[1], QImode))
  {
    operands[1] = gen_lowpart (SImode, operands[1]);
    operands[2] = can_create_pseudo_p () ? gen_reg_rtx (SImode) : operands[0];

    emit_move_insn (operands[2], gen_rtx_ASHIFT (SImode, operands[1],
					         GEN_INT (24)));
    emit_move_insn (operands[0], gen_rtx_ASHIFTRT (SImode, operands[2],
					           GEN_INT (24)));
    DONE;
  }
})

(define_insn "*lb"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(sign_extend:SI (match_operand:QI 1 "memory_operand" "U,m")))]
  ""
  "@
   lb\t%0, %1
   lbadd\t%0, %I1, %i1"
  [(set_attr "type" "Y2_2cycle,X1_2cycle")])

;;
;; insv patterns
;;
(define_expand "insv"
  [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "")
			 (match_operand:SI 1 "u5bit_cint_operand" "")
			 (match_operand:SI 2 "u5bit_cint_operand" ""))
	(match_operand:SI 3 "reg_or_cint_operand" ""))]
  ""
{
  tilepro_expand_insv (operands);
  DONE;
})

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

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

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

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

(define_insn "*insv_mm1"
  [(set (zero_extract:SI
	 (match_operand:SI 0 "register_operand" "+r")
	 (match_operand:SI 1 "u5bit_cint_operand" "n")
	 (const_int 0))
	(match_operand:SI 2 "register_operand" "rO"))]
  ""
  "mm\t%0, %r2, %0, 0, %1-1"
  [(set_attr "type" "X01")])

(define_insn "*insv_mm2"
  [(set (zero_extract:SI
	 (match_operand:SI 0 "register_operand" "+r")
	 (match_operand:SI 1 "u5bit_cint_operand" "n")
	 (match_operand:SI 2 "u5bit_cint_operand" "n"))
	(zero_extract:SI
	 (match_operand:SI 3 "register_operand" "rO")
	 (match_dup 1)
	 (match_dup 2)))]
  ""
  "mm\t%0, %r3, %0, %2, %2+%1-1"
  [(set_attr "type" "X01")])


;;
;; Multiplies
;;

(define_expand "mulsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (mult:SI (zero_extend:SI
                  (subreg:HI (match_operand:SI 1 "nonmemory_operand" "") 0))
                 (zero_extend:SI
                  (subreg:HI (match_operand:SI 2 "nonmemory_operand" "") 0))))
   (set (match_dup 0)
        (unspec:SI [(match_dup 0) (match_dup 1) (match_dup 2)]
                   UNSPEC_INSN_MULHLSA_UU))
   (set (match_dup 0)
        (unspec:SI [(match_dup 0) (match_dup 2) (match_dup 1)]
                   UNSPEC_INSN_MULHLSA_UU))]
  ""
  {
    operands[1] = force_reg (SImode, operands[1]);
    operands[1] = make_safe_from (operands[1], operands[0]);

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

(define_insn "mulhisi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(mult:SI (sign_extend:SI
		  (match_operand:HI 1 "reg_or_0_operand" "rO"))
		 (sign_extend:SI
		  (match_operand:HI 2 "reg_or_0_operand" "rO"))))]
  ""
  "mulll_ss\t%0, %r1, %r2"
  [(set_attr "type" "Y0_2cycle")])
  
(define_insn "umulhisi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(mult:SI (zero_extend:SI
		  (match_operand:HI 1 "reg_or_0_operand" "rO"))
		 (zero_extend:SI
		  (match_operand:HI 2 "reg_or_0_operand" "rO"))))]
  ""
  "mulll_uu\t%0, %r1, %r2"
  [(set_attr "type" "Y0_2cycle")])
  
(define_insn "usmulhisi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(mult:SI (zero_extend:SI
		  (match_operand:HI 1 "reg_or_0_operand" "rO"))
		 (sign_extend:SI
		  (match_operand:HI 2 "reg_or_0_operand" "rO"))))]
  ""
  "mulll_su\t%0, %r2, %r1"
  [(set_attr "type" "X0_2cycle")])
  
(define_insn "maddhisi4"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (plus:SI
         (mult:SI (sign_extend:SI
                   (match_operand:HI 1 "reg_or_0_operand" "rO"))
                  (sign_extend:SI
                   (match_operand:HI 2 "reg_or_0_operand" "rO")))
         (match_operand:SI 3 "register_operand" "0")))]
  ""
  "mullla_ss\t%0, %r1, %r2"
  [(set_attr "type" "Y0_2cycle")])
  
(define_insn "umaddhisi4"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (plus:SI
         (mult:SI (zero_extend:SI
                   (match_operand:HI 1 "reg_or_0_operand" "rO"))
                  (zero_extend:SI
                   (match_operand:HI 2 "reg_or_0_operand" "rO")))
         (match_operand:SI 3 "register_operand" "0")))]
  ""
  "mullla_uu\t%0, %r1, %r2"
  [(set_attr "type" "Y0_2cycle")])
  

(define_insn "mulqihi3"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(mult:HI (sign_extend:HI
		  (match_operand:QI 1 "reg_or_0_operand" "rO"))
		 (sign_extend:HI
		  (match_operand:QI 2 "reg_or_0_operand" "rO"))))]
  ""
  "mulll_ss\t%0, %r1, %r2"
  [(set_attr "type" "Y0_2cycle")])
  
(define_insn "umulqihi3"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(mult:HI (zero_extend:HI
		  (match_operand:QI 1 "reg_or_0_operand" "rO"))
		 (zero_extend:HI
		  (match_operand:QI 2 "reg_or_0_operand" "rO"))))]
  ""
  "mulll_uu\t%0, %r1, %r2"
  [(set_attr "type" "Y0_2cycle")])

(define_expand "smulsi3_highpart"
  [(set (match_operand:SI 0 "register_operand" "")
        (truncate:SI
         (ashiftrt:DI 
          (mult:DI (sign_extend:DI (match_operand:SI 1 "reg_or_0_operand" ""))
                   (sign_extend:DI (match_operand:SI 2 "reg_or_0_operand" "")))
          (const_int 32))))]
  ""
  {
    tilepro_expand_smulsi3_highpart (operands[0], operands[1], operands[2]);
    DONE;
  })

(define_expand "umulsi3_highpart"
  [(set (match_operand:SI 0 "register_operand" "")
	(truncate:SI
	 (lshiftrt:DI
	  (mult:DI (zero_extend:DI (match_operand:SI 1 "reg_or_0_operand" ""))
		   (zero_extend:DI (match_operand:SI 2 "reg_or_0_operand" "")))
	  (const_int 32))))]
  ""
{
  tilepro_expand_umulsi3_highpart (operands[0], operands[1], operands[2]);
  DONE;
})


;;
;; 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
   ""
{
  if (optimize > 0)
  {
     rtx s0;
     rtx bcomp;
     rtx loc_ref;

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

     s0 = operands [0];

     emit_move_insn (s0, gen_rtx_PLUS (SImode, s0, GEN_INT (-1)));
     bcomp = gen_rtx_NE(SImode, 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)]
  ""
{
  tilepro_expand_prologue ();
  DONE;
})

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

(define_expand "sibcall_epilogue"
  [(const_int 0)]
  ""
{
  tilepro_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)))]
  ""
  "tilepro_allocate_stack (operands[0], operands[1]); DONE;")

;;
;; Branches
;;
(define_expand "call"
  [(parallel [(call (match_operand:SI 0 "call_operand" "")
		    (match_operand 1 "" ""))
              (use (reg:SI 54))
	      (clobber (reg:SI 55))])]
  ""
  "")

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

(define_expand "call_value"
  [(parallel [(set (match_operand 0 "register_operand" "")
		   (call (match_operand:SI 1 "call_operand" "")
			 (match_operand 2 "" "")))
              (use (reg:SI 54))
	      (clobber (reg:SI 55))])]
  "")

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

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

(define_insn "*sibcall_insn"
  [(call (mem:SI (match_operand:SI 0 "call_address_operand" "rO,i"))
	 (match_operand 1 "" ""))
   (use (reg:SI 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:SI 1 "call_operand" "")
			 (match_operand:SI 2 "" "")))
	      (use (reg:SI 54))])]
  ""
  "")

(define_insn "*sibcall_value"
  [(set (match_operand 0 "" "")
	(call (mem:SI (match_operand:SI 1 "call_address_operand" "rO,i"))
	      (match_operand:SI 2 "" "")))
   (use (reg:SI 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:SI 0 "register_operand" "rO"))]
  ""
  "jr\t%r0"
  [(set_attr "type" "X1")])

(define_expand "return"
  [(parallel
    [(return)
     (use (reg:SI 55))])]
  "tilepro_can_use_return_insn_p ()"
  "")

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

(define_expand "tablejump"
  [(set (pc) (match_operand:SI 0 "register_operand" ""))
   (use (label_ref (match_operand 1 "" "")))]
  ""
{
  tilepro_expand_tablejump (operands[0], operands[1]);
  DONE;
})

(define_insn "tablejump_aux"
  [(set (pc) (match_operand:SI 0 "register_operand" "r"))
   (use (label_ref (match_operand 1 "" "")))]
  ""
  "jr\t%0"
  [(set_attr "type" "X1")])

;; 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: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,J")))
   (clobber (mem:BLK (scratch)))]
 ""
 "@
  add\t%0, %1, %2
  addi\t%0, %1, %2
  addli\t%0, %1, %2"
 [(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"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (match_operand:SI 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 "cbranchsi4"
  [(set (pc)
	(if_then_else (match_operator 0 "ordered_comparison_operator"
		       [(match_operand:SI 1 "reg_or_cint_operand")
		        (match_operand:SI 2 "reg_or_cint_operand")])
		      (label_ref (match_operand 3 ""))
		      (pc)))]
  ""
  { tilepro_emit_conditional_branch (operands, SImode); DONE; })


(define_expand "cbranchdi4"
  [(set (pc)
	(if_then_else (match_operator 0 "ordered_comparison_operator"
		       [(match_operand:DI 1 "reg_or_cint_operand")
		        (match_operand:DI 2 "reg_or_cint_operand")])
		      (label_ref (match_operand 3 ""))
		      (pc)))]
  ""
  { tilepro_emit_conditional_branch (operands, DImode); DONE; })
  

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

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

;; FIXME: the straight forward versions which do not include the
;; subreg:QI does not match for some unknown reason.
(define_insn "*bbs_normal"
  [(set (pc)
	(if_then_else
	 (ne (zero_extract:SI (subreg:QI 
			       (match_operand:SI 1 "reg_or_0_operand" "rO") 0)
			      (const_int 1)
			      (const_int 0))
	     (const_int 0))
	 (label_ref (match_operand 0 "" ""))
	 (pc)))]
  ""
  { return tilepro_output_cbranch_with_opcode (insn, operands, "bbs", "bbns",
					    1, 0); }
  [(set_attr "type" "X1_branch")])

(define_insn "*bbc_normal"
  [(set (pc)
	(if_then_else
	 (eq (zero_extract:SI (subreg:QI
			       (match_operand:SI 1 "reg_or_0_operand" "rO") 0)
			      (const_int 1)
			      (const_int 0))
	     (const_int 0))
	 (label_ref (match_operand 0 "" ""))
	 (pc)))]
  ""
  { return tilepro_output_cbranch_with_opcode (insn, operands, "bbns", "bbs",
					    1, 0); }
  [(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:SI 0 "address_operand" "rO")
             (match_operand:SI 1 "const_int_operand" "")
             (match_operand:SI 2 "const_int_operand" ""))]
  ""
  "prefetch\t%r0"
  [(set_attr "type" "Y2")])


;;
;; 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 "tilepro_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:SI 0 "nonimmediate_operand" "=r,U,m")
        (unspec_volatile:SI [(match_operand:SI 1 "netreg_operand" "i,i,i")
			     (reg:SI TILEPRO_NETORDER_REG)]
			    UNSPEC_NETWORK_RECEIVE))
   (clobber (reg:SI TILEPRO_NETORDER_REG))]
  ""
  "@
   move\t%0, %N1
   sw\t%0, %N1
   swadd\t%I0, %N1, %i0"
  [(set_attr "type" "*,Y2,X1")])
  
(define_insn "*netreg_send"
  [(unspec_volatile:SI
    [(match_operand:SI 0 "netreg_operand" "i,i,i,i,i,i")
     (match_operand:SI 1 "reg_or_cint_operand" "rO,I,J,K,N,P")
     (reg:SI TILEPRO_NETORDER_REG)]
    UNSPEC_NETWORK_SEND)
   (clobber (reg:SI TILEPRO_NETORDER_REG))]
  ""
  "@
   move\t%N0, %r1
   movei\t%N0, %1
   moveli\t%N0, %1
   auli\t%N0, zero, %h1
   addib\t%N0, zero, %j1
   addih\t%N0, zero, %h1"
  [(set_attr "type" "*,*,X01,X01,X01,X01")])

(define_insn "*netreg_copy"
  [(unspec_volatile:SI
    [(match_operand:SI 0 "netreg_operand" "i")
     (unspec_volatile:SI [(match_operand:SI 1 "netreg_operand" "i")
			  (reg:SI TILEPRO_NETORDER_REG)]
			 UNSPEC_NETWORK_RECEIVE)
     (reg:SI TILEPRO_NETORDER_REG)]
    UNSPEC_NETWORK_SEND)
   (clobber (reg:SI TILEPRO_NETORDER_REG))
   (clobber (reg:SI TILEPRO_NETORDER_REG))]
  ""
  "move %N0, %N1")

(define_expand "tilepro_idn0_receive"
  [(parallel
    [(set (match_operand:SI 0 "register_operand" "")
	  (unspec_volatile:SI [(const_int TILEPRO_NETREG_IDN0)
			       (reg:SI TILEPRO_NETORDER_REG)]
			      UNSPEC_NETWORK_RECEIVE))
     (clobber (reg:SI TILEPRO_NETORDER_REG))])]
  "")

(define_expand "tilepro_idn1_receive"
  [(parallel
    [(set (match_operand:SI 0 "register_operand" "")
	  (unspec_volatile:SI [(const_int TILEPRO_NETREG_IDN1)
			       (reg:SI TILEPRO_NETORDER_REG)]
			      UNSPEC_NETWORK_RECEIVE))
     (clobber (reg:SI TILEPRO_NETORDER_REG))])]
  "")

(define_expand "tilepro_idn_send"
  [(parallel
    [(unspec_volatile:SI [(const_int TILEPRO_NETREG_IDN0)
			  (match_operand:SI 0 "reg_or_cint_operand" "")
			  (reg:SI TILEPRO_NETORDER_REG)]
			 UNSPEC_NETWORK_SEND)
     (clobber (reg:SI TILEPRO_NETORDER_REG))])]
  "")

(define_expand "tilepro_sn_receive"
  [(parallel
    [(set (match_operand:SI 0 "register_operand" "")
	  (unspec_volatile:SI [(const_int TILEPRO_NETREG_SN)
			       (reg:SI TILEPRO_NETORDER_REG)]
			      UNSPEC_NETWORK_RECEIVE))
     (clobber (reg:SI TILEPRO_NETORDER_REG))])]
  "")

(define_expand "tilepro_sn_send"
  [(parallel
    [(unspec_volatile:SI [(const_int TILEPRO_NETREG_SN)
			  (match_operand:SI 0 "reg_or_cint_operand" "")
			  (reg:SI TILEPRO_NETORDER_REG)]
			 UNSPEC_NETWORK_SEND)
     (clobber (reg:SI TILEPRO_NETORDER_REG))])]
  "")

(define_expand "tilepro_udn0_receive"
  [(parallel
    [(set (match_operand:SI 0 "register_operand" "")
	  (unspec_volatile:SI [(const_int TILEPRO_NETREG_UDN0)
			       (reg:SI TILEPRO_NETORDER_REG)]
			      UNSPEC_NETWORK_RECEIVE))
     (clobber (reg:SI TILEPRO_NETORDER_REG))])]
  "")

(define_expand "tilepro_udn1_receive"
  [(parallel
    [(set (match_operand:SI 0 "register_operand" "")
	  (unspec_volatile:SI [(const_int TILEPRO_NETREG_UDN1)
			       (reg:SI TILEPRO_NETORDER_REG)]
			      UNSPEC_NETWORK_RECEIVE))
     (clobber (reg:SI TILEPRO_NETORDER_REG))])]
  "")

(define_expand "tilepro_udn2_receive"
  [(parallel
    [(set (match_operand:SI 0 "register_operand" "")
	  (unspec_volatile:SI [(const_int TILEPRO_NETREG_UDN2)
			       (reg:SI TILEPRO_NETORDER_REG)]
			      UNSPEC_NETWORK_RECEIVE))
     (clobber (reg:SI TILEPRO_NETORDER_REG))])]
  "")

(define_expand "tilepro_udn3_receive"
  [(parallel
    [(set (match_operand:SI 0 "register_operand" "")
	  (unspec_volatile:SI [(const_int TILEPRO_NETREG_UDN3)
			       (reg:SI TILEPRO_NETORDER_REG)]
			      UNSPEC_NETWORK_RECEIVE))
     (clobber (reg:SI TILEPRO_NETORDER_REG))])]
  "")

(define_expand "tilepro_udn_send"
  [(parallel
    [(unspec_volatile:SI [(const_int TILEPRO_NETREG_UDN0)
			  (match_operand:SI 0 "reg_or_cint_operand" "")
			  (reg:SI TILEPRO_NETORDER_REG)]
			 UNSPEC_NETWORK_SEND)
     (clobber (reg:SI TILEPRO_NETORDER_REG))])]
  "")

(define_insn "*netreg_add_to_network"
  [(unspec_volatile:SI
    [(match_operand:SI 0 "netreg_operand" "i,i,i,i")
     (plus:SI (match_operand:SI 1 "reg_or_0_operand" "%rO,rO,rO,rO")
              (match_operand:SI 2 "add_operand" "r,I,J,K"))
     (reg:SI TILEPRO_NETORDER_REG)]
    UNSPEC_NETWORK_SEND)
   (clobber (reg:SI TILEPRO_NETORDER_REG))]
  ""
  "@
   add\t%N0, %r1, %2
   addi\t%N0, %r1, %2
   addli\t%N0, %r1, %2
   auli\t%N0, %r1, %h2"
  [(set_attr "type" "*,*,X01,X01")])

(define_insn "*netreg_add_from_network"
  [(set (match_operand:SI 0 "register_operand" "=r,r,r,r")
	(plus:SI
	 (unspec_volatile:SI [(match_operand:SI 1 "netreg_operand" "i,i,i,i")
			      (reg:SI TILEPRO_NETORDER_REG)]
			     UNSPEC_NETWORK_RECEIVE)
	 (match_operand:SI 2 "add_operand" "rO,I,J,K")))
   (clobber (reg:SI TILEPRO_NETORDER_REG))]
  ""
  "@
   add\t%0, %N1, %r2
   addi\t%0, %N1, %2
   addli\t%0, %N1, %2
   auli\t%0, %N1, %h2"
  [(set_attr "type" "*,*,X01,X01")])

(define_insn "*netreg_add_from_to_network"
  [(unspec_volatile:SI
    [(match_operand:SI 0 "netreg_operand" "i,i,i,i")
     (plus:SI
      (unspec_volatile:SI [(match_operand:SI 1 "netreg_operand" "i,i,i,i")
			   (reg:SI TILEPRO_NETORDER_REG)]
			  UNSPEC_NETWORK_RECEIVE)
      (match_operand:SI 2 "add_operand" "rO,I,J,K"))
     (reg:SI TILEPRO_NETORDER_REG)]
    UNSPEC_NETWORK_SEND)
   (clobber (reg:SI TILEPRO_NETORDER_REG))
   (clobber (reg:SI TILEPRO_NETORDER_REG))]
  ""
  "@
   add\t%N0, %N1, %r2
   addi\t%N0, %N1, %2
   addli\t%N0, %N1, %2
   auli\t%N0, %N1, %h2"
  [(set_attr "type" "*,*,X01,X01")])

(define_code_iterator netreg_binop
  [minus])

(define_insn "*netreg_binop_to_network"
  [(unspec_volatile:SI
    [(match_operand:SI 0 "netreg_operand" "i")
    (netreg_binop:SI (match_operand:SI 1 "reg_or_0_operand" "rO")
		     (match_operand:SI 2 "reg_or_0_operand" "rO"))
    (reg:SI TILEPRO_NETORDER_REG)]
    UNSPEC_NETWORK_SEND)
   (clobber (reg:SI TILEPRO_NETORDER_REG))]
  ""
  "<insn>\t%N0, %r1, %r2")

(define_insn "*netreg_binop_from_network0"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(netreg_binop:SI
	 (unspec_volatile:SI [(match_operand:SI 1 "netreg_operand" "i")
			      (reg:SI TILEPRO_NETORDER_REG)]
			     UNSPEC_NETWORK_RECEIVE)
	 (match_operand:SI 2 "reg_or_0_operand" "rO")))
   (clobber (reg:SI TILEPRO_NETORDER_REG))]
  ""
  "<insn>\t%0, %N1, %r2")

(define_insn "*netreg_binop_from_network1"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(netreg_binop:SI
         (match_operand:SI 1 "reg_or_0_operand" "rO")
	 (unspec_volatile:SI [(match_operand:SI 2 "netreg_operand" "i")
			      (reg:SI TILEPRO_NETORDER_REG)]
			     UNSPEC_NETWORK_RECEIVE)))
   (clobber (reg:SI TILEPRO_NETORDER_REG))]
  ""
  "<insn>\t%0, %r1, %N2")

(define_insn "*netreg_binop_from_to_network0"
  [(unspec_volatile:SI
    [(match_operand:SI 0 "netreg_operand" "i")
     (netreg_binop:SI
      (unspec_volatile:SI [(match_operand:SI 1 "netreg_operand" "i")
			   (reg:SI TILEPRO_NETORDER_REG)]
			  UNSPEC_NETWORK_RECEIVE)
      (match_operand:SI 2 "reg_or_0_operand" "rO"))
     (reg:SI TILEPRO_NETORDER_REG)]
    UNSPEC_NETWORK_SEND)
   (clobber (reg:SI TILEPRO_NETORDER_REG))
   (clobber (reg:SI TILEPRO_NETORDER_REG))]
  ""
  "<insn>\t%N0, %N1, %r2")

(define_insn "*netreg_binop_from_to_network1"
  [(unspec_volatile:SI
    [(match_operand:SI 0 "netreg_operand" "i")
     (netreg_binop:SI
      (match_operand:SI 1 "reg_or_0_operand" "rO")
      (unspec_volatile:SI [(match_operand:SI 2 "netreg_operand" "i")
			   (reg:SI TILEPRO_NETORDER_REG)]
			  UNSPEC_NETWORK_RECEIVE))
     (reg:SI TILEPRO_NETORDER_REG)]
    UNSPEC_NETWORK_SEND)
   (clobber (reg:SI TILEPRO_NETORDER_REG))
   (clobber (reg:SI TILEPRO_NETORDER_REG))]
  ""
  "<insn>\t%N0, %r1, %N2")

(define_insn "*netreg_binop_to_network"
  [(unspec_volatile:SI
    [(match_operand:SI 0 "netreg_operand" "i,i")
     (binop_with_imm:SI (match_operand:SI 1 "reg_or_0_operand" "rO,rO")
			(match_operand:SI 2 "reg_or_cint_operand" "I,rO"))
     (reg:SI TILEPRO_NETORDER_REG)]
    UNSPEC_NETWORK_SEND)
   (clobber (reg:SI TILEPRO_NETORDER_REG))]
  ""
  "@
   <insn>i<u>\t%N0, %r1, %2
   <insn><u>\t%N0, %r1, %r2")

(define_insn "*netreg_binop_from_network"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(binop_with_imm:SI
         (unspec_volatile:SI [(match_operand:SI 1 "netreg_operand" "i,i")
			      (reg:SI TILEPRO_NETORDER_REG)]
			     UNSPEC_NETWORK_RECEIVE)
         (match_operand:SI 2 "reg_or_cint_operand" "I,rO")))
   (clobber (reg:SI TILEPRO_NETORDER_REG))]
  ""
  "@
   <insn>i<u>\t%0, %N1, %2
   <insn><u>\t%0, %N1, %r2")

(define_insn "*netreg_binop_from_to_network"
  [(unspec_volatile:SI
    [(match_operand:SI 0 "netreg_operand" "i,i")
     (binop_with_imm:SI
      (unspec_volatile:SI [(match_operand:SI 1 "netreg_operand" "i,i")
			   (reg:SI TILEPRO_NETORDER_REG)]
			  UNSPEC_NETWORK_RECEIVE)
      (match_operand:SI 2 "reg_or_cint_operand" "I,rO"))
     (reg:SI TILEPRO_NETORDER_REG)]
    UNSPEC_NETWORK_SEND)
   (clobber (reg:SI TILEPRO_NETORDER_REG))
   (clobber (reg:SI TILEPRO_NETORDER_REG))]
  ""
  "@
   <insn>i<u>\t%N0, %N1, %2
   <insn><u>\t%N0, %N1, %r2")

(define_insn "*netreg_unop_to_network"
  [(unspec_volatile:SI [(match_operand:SI 0 "netreg_operand" "i")
			(unop:SI (match_operand:SI 1 "reg_or_0_operand" "rO"))
			(reg:SI TILEPRO_NETORDER_REG)]
		       UNSPEC_NETWORK_SEND)
   (clobber (reg:SI TILEPRO_NETORDER_REG))]
  ""
  "<insn>\t%N0, %r1"
  [(set_attr "type" "Y0")])

(define_insn "*netreg_unop_from_network"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(unop:SI
	 (unspec_volatile:SI [(match_operand:SI 1 "netreg_operand" "i")
			      (reg:SI TILEPRO_NETORDER_REG)]
			     UNSPEC_NETWORK_RECEIVE)))
   (clobber (reg:SI TILEPRO_NETORDER_REG))]
  ""
  "<insn>\t%0, %N1"
  [(set_attr "type" "Y0")])

(define_insn "*netreg_unop_from_to_network"
  [(unspec_volatile:SI
    [(match_operand:SI 0 "netreg_operand" "i")
     (unop:SI
      (unspec_volatile:SI [(match_operand:SI 1 "netreg_operand" "i")
			   (reg:SI TILEPRO_NETORDER_REG)]
			  UNSPEC_NETWORK_RECEIVE))
     (reg:SI TILEPRO_NETORDER_REG)]
    UNSPEC_NETWORK_SEND)
   (clobber (reg:SI TILEPRO_NETORDER_REG))
   (clobber (reg:SI TILEPRO_NETORDER_REG))]
  ""
  "<insn>\t%N0, %N1"
  [(set_attr "type" "Y0")])

(define_insn "*netreg_sadh_u_from_network0"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(unspec:SI
	 [(unspec_volatile:SI [(match_operand:SI 1 "netreg_operand" "i")
			       (reg:SI TILEPRO_NETORDER_REG)]
			      UNSPEC_NETWORK_RECEIVE)
	  (match_operand:SI 2 "reg_or_0_operand" "rO")]
	 UNSPEC_INSN_SADH_U))
   (clobber (reg:SI TILEPRO_NETORDER_REG))]
  ""
  "sadh_u\t%0, %N1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "*netreg_sadh_u_from_network1"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(unspec:SI
	 [(match_operand:SI 1 "reg_or_0_operand" "rO")
	  (unspec_volatile:SI [(match_operand:SI 2 "netreg_operand" "i")
			       (reg:SI TILEPRO_NETORDER_REG)]
			      UNSPEC_NETWORK_RECEIVE)]
	 UNSPEC_INSN_SADH_U))
   (clobber (reg:SI TILEPRO_NETORDER_REG))]
  ""
  "sadh_u\t%0, %r1, %N2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "*netreg_sadah_u_from_network0"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(unspec:SI
	 [(match_operand:SI 1 "reg_or_0_operand" "0")
	  (unspec_volatile:SI [(match_operand:SI 2 "netreg_operand" "i")
			       (reg:SI TILEPRO_NETORDER_REG)]
			      UNSPEC_NETWORK_RECEIVE)
	  (match_operand:SI 3 "reg_or_0_operand" "rO")]
	 UNSPEC_INSN_SADAH_U))
   (clobber (reg:SI TILEPRO_NETORDER_REG))]
  ""
  "sadah_u\t%0, %N2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "*netreg_sadah_u_from_network1"
  [(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")
	  (unspec_volatile:SI [(match_operand:SI 3 "netreg_operand" "i")
			       (reg:SI TILEPRO_NETORDER_REG)]
			      UNSPEC_NETWORK_RECEIVE)]
	 UNSPEC_INSN_SADAH_U))
   (clobber (reg:SI TILEPRO_NETORDER_REG))]
  ""
  "sadah_u\t%0, %r2, %N3"
  [(set_attr "type" "X0_2cycle")])

(define_code_iterator mm_combiner [ior xor plus])

;; This doesn't seem to match -- too complex for 'combine'?
;;
;; (define_insn "*netreg_mm_to_network"
;;   [(unspec_volatile:SI
;;     [(match_operand:SI 0 "netreg_operand" "i")
;;      (mm_combiner:SI
;;       (and:SI (match_operand:SI 1 "reg_or_0_operand" "rO")
;; 	         (match_operand:SI 3 "const_int_operand" "n"))
;;       (and:SI (match_operand:SI 2 "reg_or_0_operand" "rO")
;; 	         (match_operand:SI 4 "const_int_operand" "n")))]
;;     UNSPEC_NETWORK_SEND)]
;;   "tilepro_bitfield_operand_p (INTVAL (operands[3]), NULL, NULL)
;;    && INTVAL (operands[3]) == ~INTVAL (operands[4])"
;;   "mm\t%N0, %r1, %r2, %M3"
;;   [(set_attr "type" "X01")])

;; FIXME: the straight forward versions which do not include the
;; subreg:QI does not match for some unknown reason.
(define_insn "*netreg_bbs_normal"
  [(set (pc)
	(if_then_else
	 (ne (zero_extract:SI
	      (subreg:QI 
	       (unspec_volatile:SI [(match_operand:SI 1 "netreg_operand" "i")
				    (reg:SI TILEPRO_NETORDER_REG)]
				   UNSPEC_NETWORK_RECEIVE) 0)
              (const_int 1)
              (const_int 0))
	     (const_int 0))
	 (label_ref (match_operand 0 "" ""))
	 (pc)))
   (clobber (reg:SI TILEPRO_NETORDER_REG))]
  ""
  { return tilepro_output_cbranch_with_opcode (insn, operands, "bbs", "bbns",
					    1, 1); }
  [(set_attr "type" "X1_branch")])

(define_insn "*netreg_bbc_normal"
  [(set (pc)
	(if_then_else
	 (eq (zero_extract:SI
	      (subreg:QI 
	       (unspec_volatile:SI [(match_operand:SI 1 "netreg_operand" "i")
				    (reg:SI TILEPRO_NETORDER_REG)]
				   UNSPEC_NETWORK_RECEIVE) 0)
              (const_int 1)
              (const_int 0))
	     (const_int 0))
	 (label_ref (match_operand 0 "" ""))
	 (pc)))
   (clobber (reg:SI TILEPRO_NETORDER_REG))]
  ""
  { return tilepro_output_cbranch_with_opcode (insn, operands, "bbns", "bbns",
					    1, 1); }
  [(set_attr "type" "X1_branch")])

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

(define_insn "insn_addlis"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec_volatile:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")
                             (match_operand:SI 2 "s16bit_cint_operand" "i")] 
                            UNSPEC_INSN_ADDLIS))]
  ""
  "addlis\t%0, %r1, %2"
  [(set_attr "type" "X01")])

(define_insn "insn_auli"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")
                    (match_operand:SI 2 "s16bit_cint_operand" "i")] 
                   UNSPEC_INSN_AULI))]
  ""
  "auli\t%0, %r1, %2"
  [(set_attr "type" "X01")])

(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:SI 0 "reg_or_0_operand" "rO")] 
                         UNSPEC_INSN_ICOH)]
  ""
  "icoh\t%r0"
  [(set_attr "type" "X1")])


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

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

;; loads

(define_expand "insn_<load>"
  [(set (match_operand:SI 0 "register_operand" "")
	(sign_extend:SI
	 (mem:I12MODE (match_operand:SI 1 "address_operand" ""))))]
  "")

(define_expand "insn_<load>_u"
  [(set (match_operand:SI 0 "register_operand" "")
	(zero_extend:SI
	 (mem:I12MODE (match_operand:SI 1 "address_operand" ""))))]
  "")

(define_insn "insn_<load>add"
  [(set (match_operand:SI 1 "register_operand" "=r")
        (plus:SI (match_operand:SI 3 "register_operand" "1")
                 (match_operand:SI 2 "s8bit_cint_operand" "i")))
   (set (match_operand:SI 0 "register_operand" "=r")
	(sign_extend:SI (mem:I12MODE (match_dup 3))))]
  ""
  "<load>add\t%0, %1, %2"
  [(set_attr "type" "X1_2cycle")])

(define_insn "insn_<load>add_u"
  [(set (match_operand:SI 1 "register_operand" "=r")
        (plus:SI (match_operand:SI 3 "register_operand" "1")
                 (match_operand:SI 2 "s8bit_cint_operand" "i")))
   (set (match_operand:SI 0 "register_operand" "=r")
        (zero_extend:SI (mem:I12MODE (match_dup 3))))]
  ""
  "<load>add_u\t%0, %1, %2"
  [(set_attr "type" "X1_2cycle")])

(define_expand "insn_lw"
  [(set (match_operand:SI 0 "register_operand" "")
	(mem:SI (match_operand:SI 1 "address_operand" "")))]
  "")

(define_insn "insn_lwadd"
  [(set (match_operand:SI 1 "register_operand" "=r")
        (plus:SI (match_operand:SI 3 "register_operand" "1")
                 (match_operand:SI 2 "s8bit_cint_operand" "i")))
   (set (match_operand:SI 0 "register_operand" "=r")
        (mem:SI (match_dup 3)))]
  ""
  "lwadd\t%0, %1, %2"
  [(set_attr "type" "X1_2cycle")])

(define_insn "insn_lwadd_na"
  [(set (match_operand:SI 1 "register_operand" "=r")
        (plus:SI (match_operand:SI 3 "register_operand" "1")
                 (match_operand:SI 2 "s8bit_cint_operand" "i")))
   (set (match_operand:SI 0 "register_operand" "=r")
        (mem:SI (and:SI (match_dup 3) (const_int -4))))]
  ""
  "lwadd_na\t%0, %1, %2"
  [(set_attr "type" "X1_2cycle")])

(define_insn "insn_lw_na"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(mem:SI (and:SI (match_operand:SI 1 "address_operand" "rO")
                        (const_int -4))))]
  ""
  "lw_na\t%0, %r1"
  [(set_attr "type" "X1_2cycle")])

;; L2 hits

(define_insn "insn_<load>_L2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(sign_extend:SI
	 (unspec:I12MODE
	  [(mem:I12MODE (match_operand:SI 1 "address_operand" "rO"))]
	  UNSPEC_LATENCY_L2)))]
  ""
  "<load>\t%0, %r1"
  [(set_attr "type" "Y2_L2")])

(define_insn "insn_<load>_u_L2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(zero_extend:SI
	 (unspec:I12MODE
	  [(mem:I12MODE (match_operand:SI 1 "address_operand" "rO"))]
	  UNSPEC_LATENCY_L2)))]
  ""
  "<load>_u\t%0, %r1"
  [(set_attr "type" "Y2_L2")])

(define_insn "insn_<load>add_L2"
  [(set (match_operand:SI 1 "register_operand" "=r")
        (plus:SI (match_operand:SI 3 "register_operand" "1")
                 (match_operand:SI 2 "s8bit_cint_operand" "i")))
   (set (match_operand:SI 0 "register_operand" "=r")
        (sign_extend:SI (unspec:I12MODE [(mem:I12MODE (match_dup 3))]
					UNSPEC_LATENCY_L2)))]
  ""
  "<load>add\t%0, %1, %2"
  [(set_attr "type" "X1_L2")])

(define_insn "insn_<load>add_u_L2"
  [(set (match_operand:SI 1 "register_operand" "=r")
        (plus:SI (match_operand:SI 3 "register_operand" "1")
                 (match_operand:SI 2 "s8bit_cint_operand" "i")))
   (set (match_operand:SI 0 "register_operand" "=r")
        (zero_extend:SI (unspec:I12MODE [(mem:I12MODE (match_dup 3))]
					UNSPEC_LATENCY_L2)))]
  ""
  "<load>add_u\t%0, %1, %2"
  [(set_attr "type" "X1_L2")])

(define_insn "insn_lwadd_L2"
  [(set (match_operand:SI 1 "register_operand" "=r")
        (plus:SI (match_operand:SI 3 "register_operand" "1")
                 (match_operand:SI 2 "s8bit_cint_operand" "i")))
   (set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(mem:SI (match_dup 3))] UNSPEC_LATENCY_L2))]
  ""
  "lwadd\t%0, %1, %2"
  [(set_attr "type" "X1_L2")])

(define_insn "insn_lwadd_na_L2"
  [(set (match_operand:SI 1 "register_operand" "=r")
        (plus:SI (match_operand:SI 3 "register_operand" "1")
                 (match_operand:SI 2 "s8bit_cint_operand" "i")))
   (set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(mem:SI (and:SI (match_dup 3) (const_int -4)))]
		   UNSPEC_LATENCY_L2))]
  ""
  "lwadd_na\t%0, %1, %2"
  [(set_attr "type" "X1_L2")])

(define_insn "insn_lw_na_L2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(unspec:SI [(mem:SI (and:SI (match_operand:SI 1 "address_operand" "rO")
				    (const_int -4)))]
		   UNSPEC_LATENCY_L2))]
  ""
  "lw_na\t%0, %r1"
  [(set_attr "type" "X1_L2")])

(define_insn "insn_lw_L2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(unspec:SI [(mem:SI (match_operand:SI 1 "address_operand" "rO"))]
		   UNSPEC_LATENCY_L2))]
  ""
  "lw\t%0, %r1"
  [(set_attr "type" "Y2_L2")])

;; L2 miss

(define_insn "insn_<load>_miss"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(sign_extend:SI
	 (unspec:I12MODE
	  [(mem:I12MODE (match_operand:SI 1 "address_operand" "rO"))]
	  UNSPEC_LATENCY_MISS)))]
  ""
  "<load>\t%0, %r1"
  [(set_attr "type" "Y2_miss")])

(define_insn "insn_<load>_u_miss"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(zero_extend:SI
	 (unspec:I12MODE
	  [(mem:I12MODE (match_operand:SI 1 "address_operand" "rO"))]
	  UNSPEC_LATENCY_MISS)))]
  ""
  "<load>_u\t%0, %r1"
  [(set_attr "type" "Y2_miss")])

(define_insn "insn_<load>add_miss"
  [(set (match_operand:SI 1 "register_operand" "=r")
        (plus:SI (match_operand:SI 3 "register_operand" "1")
                 (match_operand:SI 2 "s8bit_cint_operand" "i")))
   (set (match_operand:SI 0 "register_operand" "=r")
        (sign_extend:SI (unspec:I12MODE [(mem:I12MODE (match_dup 3))]
					UNSPEC_LATENCY_MISS)))]
  ""
  "<load>add\t%0, %1, %2"
  [(set_attr "type" "X1_miss")])

(define_insn "insn_<load>add_u_miss"
  [(set (match_operand:SI 1 "register_operand" "=r")
        (plus:SI (match_operand:SI 3 "register_operand" "1")
                 (match_operand:SI 2 "s8bit_cint_operand" "i")))
   (set (match_operand:SI 0 "register_operand" "=r")
        (zero_extend:SI (unspec:I12MODE [(mem:I12MODE (match_dup 3))]
					UNSPEC_LATENCY_MISS)))]
  ""
  "<load>add_u\t%0, %1, %2"
  [(set_attr "type" "X1_miss")])

(define_insn "insn_lwadd_miss"
  [(set (match_operand:SI 1 "register_operand" "=r")
        (plus:SI (match_operand:SI 3 "register_operand" "1")
                 (match_operand:SI 2 "s8bit_cint_operand" "i")))
   (set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(mem:SI (match_dup 3))] UNSPEC_LATENCY_MISS))]
  ""
  "lwadd\t%0, %1, %2"
  [(set_attr "type" "X1_miss")])

(define_insn "insn_lwadd_na_miss"
  [(set (match_operand:SI 1 "register_operand" "=r")
        (plus:SI (match_operand:SI 3 "register_operand" "1")
                 (match_operand:SI 2 "s8bit_cint_operand" "i")))
   (set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(mem:SI (and:SI (match_dup 3) (const_int -4)))]
		   UNSPEC_LATENCY_MISS))]
  ""
  "lwadd_na\t%0, %1, %2"
  [(set_attr "type" "X1_miss")])

(define_insn "insn_lw_na_miss"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(unspec:SI [(mem:SI (and:SI (match_operand:SI 1 "address_operand" "rO")
				    (const_int -4)))]
		   UNSPEC_LATENCY_MISS))]
  ""
  "lw_na\t%0, %r1"
  [(set_attr "type" "X1_miss")])

(define_insn "insn_lw_miss"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(unspec:SI [(mem:SI (match_operand:SI 1 "address_operand" "rO"))]
		   UNSPEC_LATENCY_MISS))]
  ""
  "lw\t%0, %r1"
  [(set_attr "type" "Y2_miss")])

;; end loads

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

(define_insn "*mm"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(mm_combiner:SI
	 (and:SI (match_operand:SI 1 "reg_or_0_operand" "rO")
		 (match_operand:SI 3 "const_int_operand" "n"))
	 (and:SI (match_operand:SI 2 "reg_or_0_operand" "rO")
		 (match_operand:SI 4 "const_int_operand" "n"))))]
  "tilepro_bitfield_operand_p (INTVAL (operands[3]), NULL, NULL)
   && INTVAL (operands[3]) == ~INTVAL (operands[4])"
  "mm\t%0, %r1, %r2, %M3"
  [(set_attr "type" "X01")])

(define_expand "insn_mm"
  [(set (match_operand:SI 0 "register_operand" "")
	(ior:SI
	 (and:SI (match_operand:SI 1 "reg_or_cint_operand" "")
		 (match_operand:SI 3 "u5bit_cint_operand" ""))
	 (and:SI (match_operand:SI 2 "reg_or_cint_operand" "")
		 (match_operand:SI 4 "u5bit_cint_operand" ""))))]
  ""
{
  int first, last, i;
  HOST_WIDE_INT mask;

  first = INTVAL (operands[3]) & 31;
  last = INTVAL (operands[4]) & 31;

  if (((last + 1) & 31) == first)
    {
      /* Handle pathological case of a mask that includes only the
         first operand. The reordering code below can't handle this. */
      emit_move_insn (operands[0], operands[1]);
      DONE;
    }

  /* Canonicalize order by putting constant second, if any. */
  if (CONST_INT_P (operands[1]))
    {
      int tmp_first;

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

      /* Invert the bit range. */
      tmp_first = first;
      first = (last + 1) & 31;
      last = (tmp_first - 1) & 31;
    }

  /* Convert the first/last bit range into a bit mask. */
  mask = 0;

  for (i = first; ; i = (i + 1) & 31)
    {
      mask |= ((HOST_WIDE_INT)1) << i;
      if (i == last)
        break;
    }

  mask = trunc_int_for_mode (mask, SImode);

  operands[1] = force_reg (SImode, operands[1]);
  operands[3] = GEN_INT (mask);
  operands[4] = GEN_INT (~mask);

  if (CONST_INT_P (operands[2]))
    {
      HOST_WIDE_INT inserted_bits = INTVAL (operands[2]) & ~mask;

      if (inserted_bits == 0)
        {
	  /* All inserted bits are zero. Use a bitwise AND. */
          emit_insn (gen_andsi3 (operands[0], operands[1], operands[3]));
          DONE;
        }
      else if (inserted_bits == ~mask)
        {
	  /* All inserted bits are ones. Use a bitwise IOR if we can. */
	  if (satisfies_constraint_I (operands[4]))
	    {
              emit_insn (gen_iorsi3 (operands[0], operands[1], operands[4]));
              DONE;
	    }

	  /* Canonicalize to inserting -1 when setting all masked bits
	     to 1, to facilitate CSE. */
	  inserted_bits = -1;
        }

      /* Sign extend the inserted bits to make them easier to materialize
         in a register, but only if the inserted bits (~mask) do not already
	 include the high bits. */
      if ((~mask & 0x80000000) == 0)
        {
          int shift = sizeof (HOST_WIDE_INT) * 8 - first;
          inserted_bits = (inserted_bits << shift) >> shift;
        }

      operands[2] = GEN_INT (inserted_bits);
    }

  operands[2] = force_reg (SImode, operands[2]);
})

(define_insn "insn_movelis"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec_volatile:SI [(match_operand:SI 1 "s16bit_cint_operand" "i")] 
                            UNSPEC_INSN_MOVELIS))]
  ""
  "movelis\t%0, %1"
  [(set_attr "type" "X01")])

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

(define_expand "insn_prefetch"
  [(prefetch (match_operand:SI 0 "address_operand" "")
             (const_int 0)
             (const_int 2))])

(define_expand "insn_prefetch_L1"
  [(use (match_operand:SI 0 "address_operand" ""))]
  ""
{
  /* Generate a volatile byte load to a dummy register. */
  rtx mem = gen_rtx_MEM (QImode, operands[0]);
  MEM_VOLATILE_P (mem) = 1;

  emit_insn (gen_zero_extendqisi2 (gen_reg_rtx (SImode), mem));
  DONE;
})

(define_expand "insn_s1a"
  [(set (match_operand:SI 0 "register_operand" "")
        (plus:SI (mult:SI (match_operand:SI 1 "reg_or_0_operand" "")
                          (const_int 2))
                 (match_operand:SI 2 "reg_or_0_operand" "")))]
  "")

(define_expand "insn_s2a"
  [(set (match_operand:SI 0 "register_operand" "")
        (plus:SI (mult:SI (match_operand:SI 1 "reg_or_0_operand" "")
                          (const_int 4))
                 (match_operand:SI 2 "reg_or_0_operand" "")))]
  "")

(define_expand "insn_s3a"
  [(set (match_operand:SI 0 "register_operand" "")
        (plus:SI (mult:SI (match_operand:SI 1 "reg_or_0_operand" "")
                          (const_int 8))
                 (match_operand:SI 2 "reg_or_0_operand" "")))]
  "")

(define_expand "insn_<store>"
  [(set (mem:I12MODE (match_operand:SI 0 "address_operand" ""))
        (match_operand:SI 1 "reg_or_0_operand" ""))]
  ""
{
  operands[1] = simplify_gen_subreg (<MODE>mode, operands[1], SImode, 0);
})

(define_expand "insn_sw"
  [(set (mem:SI (match_operand:SI 0 "address_operand" ""))
        (match_operand:SI 1 "reg_or_0_operand" ""))]
  "")

(define_expand "insn_<store>add"
  [(parallel
    [(set (match_operand:SI 0 "register_operand" "")
	  (plus:SI (match_operand:SI 3 "register_operand" "")
		   (match_operand:SI 2 "s8bit_cint_operand" "")))
     (set (mem:I12MODE (match_dup 3))
	  (match_operand:SI 1 "reg_or_0_operand" ""))])]
  ""
{
  operands[1] = simplify_gen_subreg (<MODE>mode, operands[1], SImode, 0);
})

(define_insn "*insn_<store>add"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI (match_operand:SI 3 "register_operand" "0")
		 (match_operand:SI 2 "s8bit_cint_operand" "i")))
   (set (mem:I12MODE (match_dup 3))
	(match_operand:I12MODE 1 "reg_or_0_operand" "rO"))]
  ""
  "<store>add\t%0, %r1, %2"
  [(set_attr "type" "X1")])

(define_insn "insn_swadd"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (plus:SI (match_operand:SI 3 "register_operand" "0")
                 (match_operand:SI 2 "s8bit_cint_operand" "i")))
   (set (mem:SI (match_dup 3))
        (match_operand:SI 1 "reg_or_0_operand" "rO"))]
  ""
  "swadd\t%0, %r1, %2"
  [(set_attr "type" "X1")])

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

(define_insn "insn_tns"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (mem:SI (match_operand:SI 1 "reg_or_0_operand" "rO")))
   (set (mem:SI (match_dup 1)) (const_int 1))]
  ""
  "tns\t%0, %1"
  [(set_attr "type" "X1")])

;; insn_addb
;; insn_addib
;; insn_maxb_u
;; insn_maxib_u
;; insn_minb_u
;; insn_minib_u
;; insn_seqb
;; insn_seqib
;; insn_sltb
;; insn_sltib
;; insn_sltb_u
;; insn_sltib_u
(define_insn "<optab>v4qi3"
  [(set (match_operand:V4QI 0 "register_operand" "=r,r")
	(v1op_immed:V4QI
	 (match_operand:V4QI 1 "reg_or_0_operand" "<comm>rO,rO")
	 (match_operand:V4QI 2 "reg_or_v4s8bit_operand" "W,rO")))]
  ""
  "@
   <insn>ib<u>\t%0, %r1, %j2
   <insn>b<u>\t%0, %r1, %r2"
  [(set_attr "type" "X01,X01")])

(define_expand "insn_<insn>b<u>"
  [(set (match_operand:SI 0 "register_operand" "")
	(v1op_immed:V4QI
	 (match_operand:SI 1 "reg_or_0_operand" "")
	 (match_operand:SI 2 "reg_or_0_operand" "")))]
  ""
{
  tilepro_expand_builtin_vector_binop (gen_<optab>v4qi3, V4QImode, operands[0],
				       V4QImode, operands[1], operands[2], true);
  DONE;
})

(define_expand "insn_<insn>ib<u>"
  [(set (match_operand:SI 0 "register_operand" "")
	(v1op_immed:V4QI
	 (match_operand:SI 1 "reg_or_0_operand" "")
	 (match_operand:SI 2 "s8bit_cint_operand" "")))]
  ""
{
  /* Tile out immediate and expand to general case. */
  rtx n = tilepro_simd_int (operands[2], QImode);
  tilepro_expand_builtin_vector_binop (gen_<optab>v4qi3, V4QImode, operands[0],
				       V4QImode, operands[1], n, true);
  DONE;
})

;; insn_shlb
;; insn_shlib
;; insn_shrb
;; insn_shrib
;; insn_srab
;; insn_sraib
(define_insn "<optab>v4qi3"
  [(set (match_operand:V4QI 0 "register_operand" "=r,r")
	(any_shift:V4QI
	 (match_operand:V4QI 1 "reg_or_0_operand" "rO,rO")
	 (match_operand:SI 2 "reg_or_u5bit_operand" "I,rO")))]
  ""
  "@
   <insn>ib<u>\t%0, %r1, %2
   <insn>b<u>\t%0, %r1, %r2"
  [(set_attr "type" "X01,X01")])

(define_expand "insn_<insn>b<u>"
  [(set (match_operand:SI 0 "register_operand" "")
	(any_shift:V4QI
	 (match_operand:SI 1 "reg_or_0_operand" "")
	 (match_operand:SI 2 "reg_or_u5bit_operand" "")))]
  ""
{
  tilepro_expand_builtin_vector_binop (gen_<optab>v4qi3, V4QImode, operands[0],
				    V4QImode, operands[1], operands[2], false);
  DONE;
})

;; insn_addh
;; insn_addih
;; insn_maxh
;; insn_maxih
;; insn_minh
;; insn_minih
;; insn_seqh
;; insn_seqih
;; insn_slth
;; insn_sltih
;; insn_slth_u
;; insn_sltih_u
(define_insn "<optab>v2hi3"
  [(set (match_operand:V2HI 0 "register_operand" "=r,r")
	(v2op_immed:V2HI
	 (match_operand:V2HI 1 "reg_or_0_operand" "<comm>rO,rO")
	 (match_operand:V2HI 2 "reg_or_v2s8bit_operand" "Y,rO")))]
  ""
  "@
   <insn>ih<u>\t%0, %r1, %j2
   <insn>h<u>\t%0, %r1, %r2"
  [(set_attr "type" "X01,X01")])

(define_expand "insn_<insn>h<u>"
  [(set (match_operand:SI 0 "register_operand" "")
	(v2op_immed:V2HI
	 (match_operand:SI 1 "reg_or_0_operand" "")
	 (match_operand:SI 2 "reg_or_0_operand" "")))]
  ""
{
  tilepro_expand_builtin_vector_binop (gen_<optab>v2hi3, V2HImode, operands[0],
				       V2HImode, operands[1], operands[2], true);
  DONE;
})

(define_expand "insn_<insn>ih<u>"
  [(set (match_operand:SI 0 "register_operand" "")
	(v2op_immed:V2HI
	 (match_operand:SI 1 "reg_or_0_operand" "")
	 (match_operand:SI 2 "s8bit_cint_operand" "")))]
  ""
{
  /* Tile out immediate and expand to general case. */
  rtx n = tilepro_simd_int (operands[2], HImode);
  tilepro_expand_builtin_vector_binop (gen_<optab>v2hi3, V2HImode, operands[0],
				       V2HImode, operands[1], n, true);
  DONE;
})

;; insn_shlh
;; insn_shlih
;; insn_shrh
;; insn_shrih
;; insn_srah
;; insn_sraih
(define_insn "<optab>v2hi3"
  [(set (match_operand:V2HI 0 "register_operand" "=r,r")
	(any_shift:V2HI
	 (match_operand:V2HI 1 "reg_or_0_operand" "rO,rO")
	 (match_operand:SI 2 "reg_or_u5bit_operand" "I,rO")))]
  ""
  "@
   <insn>ih<u>\t%0, %r1, %2
   <insn>h<u>\t%0, %r1, %r2"
  [(set_attr "type" "X01,X01")])

(define_expand "insn_<insn>h<u>"
  [(set (match_operand:SI 0 "register_operand" "")
	(any_shift:V2HI
	 (match_operand:SI 1 "reg_or_0_operand" "")
	 (match_operand:SI 2 "reg_or_0_operand" "")))]
  ""
{
  tilepro_expand_builtin_vector_binop (gen_<optab>v2hi3, V2HImode, operands[0],
				       V2HImode, operands[1], operands[2], false);
  DONE;
})

;; insn_addbs_u
;; insn_subbs_u
;; insn_subb
;; insn_slteb
;; insn_slteb_u
;; insn_sneb
(define_insn "<optab>v4qi3"
  [(set (match_operand:V4QI 0 "register_operand" "=r")
	(v1op:V4QI
	 (match_operand:V4QI 1 "reg_or_0_operand" "<comm>rO")
	 (match_operand:V4QI 2 "reg_or_0_operand" "rO")))]
  ""
  "<insn>b<u>\t%0, %r1, %r2"
  [(set_attr "type" "X01")])

(define_expand "insn_<insn>b<u>"
  [(set (match_operand:SI 0 "register_operand" "")
	(v1op:V4QI
	 (match_operand:SI 1 "reg_or_0_operand" "")
	 (match_operand:SI 2 "reg_or_0_operand" "")))]
  ""
{
  tilepro_expand_builtin_vector_binop (gen_<optab>v4qi3, V4QImode, operands[0],
				       V4QImode, operands[1], operands[2], true);
  DONE;
})

;; insn_addhs
;; insn_subhs
;; insn_subh
;; insn_slteh
;; insn_slteh_u
;; insn_sneh
(define_insn "<optab>v2hi3"
  [(set (match_operand:V2HI 0 "register_operand" "=r")
	(v2op:V2HI
	 (match_operand:V2HI 1 "reg_or_0_operand" "<comm>rO")
	 (match_operand:V2HI 2 "reg_or_0_operand" "rO")))]
  ""
  "<insn>h<u>\t%0, %r1, %r2"
  [(set_attr "type" "X01")])

(define_expand "insn_<insn>h<u>"
  [(set (match_operand:SI 0 "register_operand" "")
	(v2op:V2HI
	 (match_operand:SI 1 "reg_or_0_operand" "")
	 (match_operand:SI 2 "reg_or_0_operand" "")))]
  ""
{
  tilepro_expand_builtin_vector_binop (gen_<optab>v2hi3, V2HImode, operands[0],
				       V2HImode, operands[1], operands[2], true);
  DONE;
})

;; insn_inthb

;; Byte ordering of these vectors is endian dependent.  We concat
;; right-to-left for little endian.  We concat and interleave in the
;; opposite way gcc's vector patterns work, so we need to reverse the
;; order of source operands.

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

(define_expand "insn_inthb"
  [(match_operand:SI 0 "register_operand" "")
   (match_operand:SI 1 "reg_or_0_operand" "")
   (match_operand:SI 2 "reg_or_0_operand" "")]
  ""
{
  /* Our instruction interleaves opposite of the way vec_interleave
     works, so we need to reverse the source operands.  */
  tilepro_expand_builtin_vector_binop (gen_vec_interleave_highv4qi, V4QImode,
                                       operands[0], V4QImode, operands[2],
				       operands[1], true);
  DONE;
})

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

(define_expand "insn_intlb"
  [(match_operand:SI 0 "register_operand" "")
   (match_operand:SI 1 "reg_or_0_operand" "")
   (match_operand:SI 2 "reg_or_0_operand" "")]
  ""
{
  /* Our instruction interleaves opposite of the way vec_interleave
     works, so we need to reverse the source operands.  */
  tilepro_expand_builtin_vector_binop (gen_vec_interleave_lowv4qi, V4QImode,
				       operands[0], V4QImode, operands[2],
				       operands[1], true);
  DONE;
})

;; insn_inthh
;;    {B1,B0} {A1,A0}
;; => {A1,A0,B1,B0}
;; => {A1,B1}
(define_insn "vec_interleave_highv2hi"
  [(set (match_operand:V2HI 0 "register_operand" "=r")
	(vec_select:V2HI
	 (vec_concat:V4HI (match_operand:V2HI 1 "reg_or_0_operand" "rO")
			  (match_operand:V2HI 2 "reg_or_0_operand" "rO"))
	 (parallel [(const_int 1) (const_int 3)])))]
  ""
  "inthh\t%0, %r2, %r1"
  [(set_attr "type" "X01")])

(define_expand "insn_inthh"
  [(match_operand:SI 0 "register_operand" "")
   (match_operand:SI 1 "reg_or_0_operand" "")
   (match_operand:SI 2 "reg_or_0_operand" "")]
  ""
{
  /* Our instruction interleaves opposite of the way vec_interleave
     works, so we need to reverse the source operands.  */
  tilepro_expand_builtin_vector_binop (gen_vec_interleave_highv2hi, V2HImode,
                                       operands[0], V2HImode, operands[2],
				       operands[1], true);
  DONE;
})

;; insn_intlh
;;    {B1,B0} {A1,A0}
;; => {A1,A0,B1,B0}
;; => {A0,B0}
(define_insn "vec_interleave_lowv2hi"
  [(set (match_operand:V2HI 0 "register_operand" "=r")
	(vec_select:V2HI
	 (vec_concat:V4HI (match_operand:V2HI 1 "reg_or_0_operand" "rO")
			  (match_operand:V2HI 2 "reg_or_0_operand" "rO"))
	 (parallel [(const_int 0) (const_int 2)])))]
  ""
  "intlh\t%0, %r2, %r1"
  [(set_attr "type" "X01")])

(define_expand "insn_intlh"
  [(match_operand:SI 0 "register_operand" "")
   (match_operand:SI 1 "reg_or_0_operand" "")
   (match_operand:SI 2 "reg_or_0_operand" "")]
  ""
{
  /* Our instruction interleaves opposite of the way vec_interleave
     works, so we need to reverse the source operands.  */
  tilepro_expand_builtin_vector_binop (gen_vec_interleave_lowv2hi, V2HImode,
                                       operands[0], V2HImode, operands[2],
				       operands[1], true);
  DONE;
})

;; insn_packbs_u
;; insn_packlb
;;    {B1,B0} {A1,A0}
;; => {A1,A0,B1,B0}
(define_insn "vec_pack_<pack_optab>_v2hi"
  [(set (match_operand:V4QI 0 "register_operand" "=r")
	(vec_concat:V4QI
	 (v2pack:V2QI (match_operand:V2HI 1 "reg_or_0_operand" "rO"))
	 (v2pack:V2QI (match_operand:V2HI 2 "reg_or_0_operand" "rO"))))]
  ""
  "<pack_insn>b<pack_u>\t%0, %r2, %r1"
  [(set_attr "type" "X01")])

(define_expand "insn_<pack_insn>b<pack_u>"
  [(set (match_operand:SI 0 "register_operand" "")
	(vec_concat:V4QI
	 (v2pack:V2QI (match_operand:SI 1 "reg_or_0_operand" ""))
	 (v2pack:V2QI (match_operand:SI 2 "reg_or_0_operand" ""))))]
  ""
{
  /* Our instruction concats opposite of the way vec_pack works, so we
     need to reverse the source operands.  */
  tilepro_expand_builtin_vector_binop (gen_vec_pack_<pack_optab>_v2hi,
                                       V4QImode, operands[0],
                                       V2HImode, operands[2], operands[1], true);
  DONE;
})

;; insn_packhb
;;    {B1,B0} {A1,A0}
;; => {A1,A0,B1,B0}
(define_insn "vec_pack_hipart_v2hi"
  [(set (match_operand:V4QI 0 "register_operand" "=r")
	(vec_concat:V4QI
	 (truncate:V2QI
	  (ashiftrt:V2HI (match_operand:V2HI 1 "reg_or_0_operand" "rO")
			 (const_int 8)))
	 (truncate:V2QI
	  (ashiftrt:V2HI (match_operand:V2HI 2 "reg_or_0_operand" "rO")
			 (const_int 8)))))]
  ""
  "packhb\t%0, %r2, %r1"
  [(set_attr "type" "X01")])

(define_expand "insn_packhb"
  [(set (match_operand:SI 0 "register_operand" "")
	(vec_concat:V4QI
	 (truncate:V2QI
	  (ashiftrt:V2HI (match_operand:SI 2 "reg_or_0_operand" "")
			 (const_int 8)))
	 (truncate:V2QI
	  (ashiftrt:V2HI (match_operand:SI 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.  */
  tilepro_expand_builtin_vector_binop (gen_vec_pack_hipart_v2hi,
                                       V4QImode, operands[0],
                                       V2HImode, operands[2], operands[1], true);
  DONE;
})

;; insn_packhs
;;    {B0} {A0}
;; => {A0,B0}
(define_insn "vec_pack_ssat_si"
  [(set (match_operand:V2HI 0 "register_operand" "=r")
	(vec_concat:V2HI
	 (ss_truncate:HI (match_operand:SI 1 "reg_or_0_operand" "rO"))
	 (ss_truncate:HI (match_operand:SI 2 "reg_or_0_operand" "rO"))))]
  ""
  "packhs\t%0, %r2, %r1"
  [(set_attr "type" "X01")])

(define_expand "insn_packhs"
  [(set (match_operand:SI 0 "register_operand" "")
	(vec_concat:V2HI
	 (ss_truncate:HI (match_operand:SI 2 "reg_or_0_operand" ""))
	 (ss_truncate:HI (match_operand:SI 1 "reg_or_0_operand" ""))))]
  ""
{
  /* Our instruction concats opposite of the way vec_pack works, so we
     need to reverse the source operands.  */
  tilepro_expand_builtin_vector_binop (gen_vec_pack_ssat_si,
                                       V2HImode, operands[0],
                                       SImode, operands[2], operands[1], true);
  DONE;
})

;; Rest of the intrinsics
(define_insn "insn_adiffb_u"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")
                    (match_operand:SI 2 "reg_or_0_operand" "rO")] 
                   UNSPEC_INSN_ADIFFB_U))]
  ""
  "adiffb_u\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_adiffh"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")
                    (match_operand:SI 2 "reg_or_0_operand" "rO")] 
                   UNSPEC_INSN_ADIFFH))]
  ""
  "adiffh\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_avgb_u"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")
                    (match_operand:SI 2 "reg_or_0_operand" "rO")] 
                   UNSPEC_INSN_AVGB_U))]
  ""
  "avgb_u\t%0, %r1, %r2"
  [(set_attr "type" "X0")])

(define_insn "insn_avgh"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")
                    (match_operand:SI 2 "reg_or_0_operand" "rO")] 
                   UNSPEC_INSN_AVGH))]
  ""
  "avgh\t%0, %r1, %r2"
  [(set_attr "type" "X0")])

(define_insn "insn_bitx"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")]
                    UNSPEC_INSN_BITX))]
  ""
  "bitx\t%0, %r1"
  [(set_attr "type" "Y0")])

(define_insn "insn_crc32_32"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")
                    (match_operand:SI 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:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")
                    (match_operand:SI 2 "reg_or_0_operand" "rO")] 
                   UNSPEC_INSN_CRC32_8))]
  ""
  "crc32_8\t%0, %r1, %r2"
  [(set_attr "type" "X0")])

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

(define_insn "insn_dword_align"
  [(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_DWORD_ALIGN))]
  ""
  "dword_align\t%0, %r2, %r3"
  [(set_attr "type" "X0")])

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

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

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

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

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

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

(define_insn "insn_mnzb"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")
                    (match_operand:SI 2 "reg_or_0_operand" "rO")] 
                   UNSPEC_INSN_MNZB))]
  ""
  "mnzb\t%0, %r1, %r2"
  [(set_attr "type" "X01")])

(define_insn "insn_mnzh"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")
                    (match_operand:SI 2 "reg_or_0_operand" "rO")] 
                   UNSPEC_INSN_MNZH))]
  ""
  "mnzh\t%0, %r1, %r2"
  [(set_attr "type" "X01")])

(define_insn "insn_mulhh_ss"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")
                    (match_operand:SI 2 "reg_or_0_operand" "rO")] 
                   UNSPEC_INSN_MULHH_SS))]
  ""
  "mulhh_ss\t%0, %r1, %r2"
  [(set_attr "type" "Y0_2cycle")])

(define_insn "insn_mulhh_su"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")
                    (match_operand:SI 2 "reg_or_0_operand" "rO")] 
                   UNSPEC_INSN_MULHH_SU))]
  ""
  "mulhh_su\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mulhh_uu"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")
                    (match_operand:SI 2 "reg_or_0_operand" "rO")] 
                   UNSPEC_INSN_MULHH_UU))]
  ""
  "mulhh_uu\t%0, %r1, %r2"
  [(set_attr "type" "Y0_2cycle")])

(define_insn "insn_mulhha_ss"
  [(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_MULHHA_SS))]
  ""
  "mulhha_ss\t%0, %r2, %r3"
  [(set_attr "type" "Y0_2cycle")])

(define_insn "insn_mulhha_su"
  [(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_MULHHA_SU))]
  ""
  "mulhha_su\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mulhha_uu"
  [(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_MULHHA_UU))]
  ""
  "mulhha_uu\t%0, %r2, %r3"
  [(set_attr "type" "Y0_2cycle")])

(define_insn "insn_mulhhsa_uu"
  [(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_MULHHSA_UU))]
  ""
  "mulhhsa_uu\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mulhl_ss"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")
                    (match_operand:SI 2 "reg_or_0_operand" "rO")] 
                   UNSPEC_INSN_MULHL_SS))]
  ""
  "mulhl_ss\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mulhl_su"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")
                    (match_operand:SI 2 "reg_or_0_operand" "rO")] 
                   UNSPEC_INSN_MULHL_SU))]
  ""
  "mulhl_su\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mulhl_us"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")
                    (match_operand:SI 2 "reg_or_0_operand" "rO")] 
                   UNSPEC_INSN_MULHL_US))]
  ""
  "mulhl_us\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mulhl_uu"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")
                    (match_operand:SI 2 "reg_or_0_operand" "rO")] 
                   UNSPEC_INSN_MULHL_UU))]
  ""
  "mulhl_uu\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mulhla_ss"
  [(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_MULHLA_SS))]
  ""
  "mulhla_ss\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mulhla_su"
  [(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_MULHLA_SU))]
  ""
  "mulhla_su\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mulhla_us"
  [(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_MULHLA_US))]
  ""
  "mulhla_us\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mulhla_uu"
  [(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_MULHLA_UU))]
  ""
  "mulhla_uu\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mulhlsa_uu"
  [(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_MULHLSA_UU))]
  ""
  "mulhlsa_uu\t%0, %r2, %r3"
  [(set_attr "type" "Y0_2cycle")])

(define_insn "insn_mulll_ss"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")
                    (match_operand:SI 2 "reg_or_0_operand" "rO")]
                    UNSPEC_INSN_MULLL_SS))]
  ""
  "mulll_ss\t%0, %r1, %r2"
  [(set_attr "type" "Y0_2cycle")])
  
(define_insn "insn_mulll_su"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")
                    (match_operand:SI 2 "reg_or_0_operand" "rO")] 
                   UNSPEC_INSN_MULLL_SU))]
  ""
  "mulll_su\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mulll_uu"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")
                    (match_operand:SI 2 "reg_or_0_operand" "rO")] 
                   UNSPEC_INSN_MULLL_UU))]
  ""
  "mulll_uu\t%0, %r1, %r2"
  [(set_attr "type" "Y0_2cycle")])

(define_insn "insn_mullla_ss"
  [(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_MULLLA_SS))]
  ""
  "mullla_ss\t%0, %r2, %r3"
  [(set_attr "type" "Y0_2cycle")])

(define_insn "insn_mullla_su"
  [(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_MULLLA_SU))]
  ""
  "mullla_su\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mullla_uu"
  [(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_MULLLA_UU))]
  ""
  "mullla_uu\t%0, %r2, %r3"
  [(set_attr "type" "Y0_2cycle")])

(define_insn "insn_mulllsa_uu"
  [(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_MULLLSA_UU))]
  ""
  "mulllsa_uu\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_mzb"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")
                    (match_operand:SI 2 "reg_or_0_operand" "rO")] 
                   UNSPEC_INSN_MZB))]
  ""
  "mzb\t%0, %r1, %r2"
  [(set_attr "type" "X01")])

(define_insn "insn_mzh"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")
                    (match_operand:SI 2 "reg_or_0_operand" "rO")] 
                   UNSPEC_INSN_MZH))]
  ""
  "mzh\t%0, %r1, %r2"
  [(set_attr "type" "X01")])

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

(define_insn "insn_nor"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (and:SI (not:SI (match_operand:SI 1 "reg_or_0_operand" "rO"))
                (not:SI (match_operand:SI 2 "reg_or_0_operand" "rO"))))]
  ""
  "nor\t%0, %r1, %r2")

(define_insn "insn_sadab_u"
  [(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_SADAB_U))]
  ""
  "sadab_u\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_sadah"
  [(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_SADAH))]
  ""
  "sadah\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_sadah_u"
  [(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_SADAH_U))]
  ""
  "sadah_u\t%0, %r2, %r3"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_sadb_u"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")
                    (match_operand:SI 2 "reg_or_0_operand" "rO")] 
                   UNSPEC_INSN_SADB_U))]
  ""
  "sadb_u\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_sadh"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")
                    (match_operand:SI 2 "reg_or_0_operand" "rO")] 
                   UNSPEC_INSN_SADH))]
  ""
  "sadh\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_sadh_u"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SI 1 "reg_or_0_operand" "rO")
                    (match_operand:SI 2 "reg_or_0_operand" "rO")] 
                   UNSPEC_INSN_SADH_U))]
  ""
  "sadh_u\t%0, %r1, %r2"
  [(set_attr "type" "X0_2cycle")])

(define_insn "insn_tblidxb0"
  [(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")] 
                   UNSPEC_INSN_TBLIDXB0))]
  ""
  "tblidxb0\t%0, %r2"
  [(set_attr "type" "Y0")])

(define_insn "insn_tblidxb1"
  [(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")] 
                   UNSPEC_INSN_TBLIDXB1))]
  ""
  "tblidxb1\t%0, %r2"
  [(set_attr "type" "Y0")])

(define_insn "insn_tblidxb2"
  [(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")] 
                   UNSPEC_INSN_TBLIDXB2))]
  ""
  "tblidxb2\t%0, %r2"
  [(set_attr "type" "Y0")])

(define_insn "insn_tblidxb3"
  [(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")] 
                   UNSPEC_INSN_TBLIDXB3))]
  ""
  "tblidxb3\t%0, %r2"
  [(set_attr "type" "Y0")])


;;
;; 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"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec_volatile:SI [(match_operand:SI 1 "symbolic_operand" "")]
                            UNSPEC_LNK_AND_LABEL))]
  ""
  "%1 = . + 8\n\tlnk\t%0"
  [(set_attr "type" "X1")])

(define_expand "addli_pcrel"
  [(set (match_operand:SI 0 "register_operand" "")
        (lo_sum:SI
	 (match_operand:SI 1 "register_operand" "")
	 (const:SI
	  (unspec:SI [(match_operand:SI 2 "symbolic_operand" "")
		      (match_operand:SI 3 "symbolic_operand" "")]
		     UNSPEC_PCREL_SYM))))]
  "flag_pic")

(define_expand "auli_pcrel"
  [(set (match_operand:SI 0 "register_operand" "")
        (plus:SI
         (match_operand:SI 1 "reg_or_0_operand" "")
         (high:SI
	  (const:SI
	   (unspec:SI [(match_operand:SI 2 "symbolic_operand" "")
		       (match_operand:SI 3 "symbolic_operand" "")]
                      UNSPEC_PCREL_SYM)))))]
  "flag_pic")

(define_expand "add_got16"
  [(set (match_operand:SI 0 "register_operand" "")
        (lo_sum:SI
	 (match_operand:SI 1 "reg_or_0_operand" "")
	 (const:SI (unspec:SI [(match_operand:SI 2 "symbolic_operand" "")]
			      UNSPEC_GOT16_SYM))))]
  "flag_pic == 1")

(define_expand "addhi_got32"
  [(set (match_operand:SI 0 "register_operand" "")
	(plus:SI
	 (match_operand:SI 1 "reg_or_0_operand" "")
	 (high:SI
	  (const:SI (unspec:SI [(match_operand:SI 2 "symbolic_operand" "")]
			       UNSPEC_GOT32_SYM)))))]
  "flag_pic == 2")

(define_expand "addlo_got32"
  [(set (match_operand:SI 0 "register_operand" "")
        (lo_sum:SI
	 (match_operand:SI 1 "reg_or_0_operand" "")
	 (const:SI (unspec:SI [(match_operand:SI 2 "symbolic_operand" "")]
			      UNSPEC_GOT32_SYM))))]
  "flag_pic == 2")


;;
;; TLS
;;

(define_expand "tls_gd_addhi"
  [(set (match_operand:SI 0 "register_operand" "")
	(plus:SI
         (match_operand:SI 1 "reg_or_0_operand" "")
         (high:SI
	  (const:SI (unspec:SI [(match_operand 2 "tls_symbolic_operand" "")]
			       UNSPEC_TLS_GD)))))]
  "HAVE_AS_TLS")

(define_expand "tls_gd_addlo"
  [(set (match_operand:SI 0 "register_operand" "")
        (lo_sum:SI
         (match_operand:SI 1 "reg_or_0_operand" "")
         (const:SI (unspec:SI [(match_operand 2 "tls_symbolic_operand" "")]
			      UNSPEC_TLS_GD))))]
  "HAVE_AS_TLS")

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

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

(define_insn "tls_gd_add"
  [(set (match_operand:SI 0 "register_operand" "=r")
       (unspec:SI [(match_operand:SI 1 "register_operand" "r")
                   (match_operand:SI 2 "tls_symbolic_operand" "")]
                  UNSPEC_TLS_GD_ADD))]
  "HAVE_AS_TLS"
  "addi\t%0, %1, tls_gd_add(%2)")

(define_insn "tls_ie_load"
  [(set (match_operand:SI 0 "register_operand" "=r")
       (unspec:SI [(match_operand:SI 1 "register_operand" "r")
                   (match_operand:SI 2 "tls_symbolic_operand" "")]
                  UNSPEC_TLS_IE_LOAD))]
  "HAVE_AS_TLS"
  "lw_tls\t%0, %1, tls_ie_load(%2)"
  [(set_attr "type" "X1_2cycle")])

(define_expand "tls_ie_addhi"
  [(set (match_operand:SI 0 "register_operand" "")
        (plus:SI
         (match_operand:SI 1 "register_operand" "")
         (high:SI
	  (const:SI (unspec:SI [(match_operand 2 "tls_ie_symbolic_operand" "")]
			       UNSPEC_TLS_IE)))))]
  "HAVE_AS_TLS")

(define_expand "tls_ie_addlo"
  [(set (match_operand:SI 0 "register_operand" "")
        (lo_sum:SI
         (match_operand:SI 1 "register_operand" "")
         (const:SI (unspec:SI [(match_operand 2 "tls_ie_symbolic_operand" "")]
			      UNSPEC_TLS_IE))))]
  "HAVE_AS_TLS")

(define_expand "tls_le_addhi"
  [(set (match_operand:SI 0 "register_operand" "")
        (plus:SI
         (match_operand:SI 1 "register_operand" "")
         (high:SI
	  (const:SI (unspec:SI [(match_operand 2 "tls_le_symbolic_operand" "")]
			       UNSPEC_TLS_LE)))))]
  "HAVE_AS_TLS")

(define_expand "tls_le_addlo"
  [(set (match_operand:SI 0 "register_operand" "")
        (lo_sum:SI
         (match_operand:SI 1 "register_operand" "")
         (const:SI (unspec:SI [(match_operand 2 "tls_le_symbolic_operand" "")]
			      UNSPEC_TLS_LE))))]
  "HAVE_AS_TLS")

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

  emit_insn (gen_stack_protect_setsi (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))]
  ""
  "lw\t%2, %1; { sw\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 (SImode);

  emit_insn (gen_stack_protect_testsi (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))]
  ""
  "lw\t%0, %1; lw\t%3, %2; { seq\t%0, %0, %3; move\t%3, zero }"
  [(set_attr "length" "24")
   (set_attr "type" "cannot_bundle_4cycle")])

