;; microblaze.md -- Machine description for Xilinx MicroBlaze processors.
;; Copyright (C) 2009-2013 Free Software Foundation, Inc.

;; Contributed by Michael Eager <eager@eagercon.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/>.  */

(include "constraints.md")
(include "predicates.md")

;;----------------------------------------------------
;; Constants
;;----------------------------------------------------
(define_constants [
  (R_SP        1)       ;; Stack pointer reg
  (R_SR       15)       ;; Sub-routine return addr reg
  (R_IR       14)       ;; Interrupt return addr reg
  (R_DR       16)       ;; Debug trap return addr reg
  (R_ER       17)       ;; Exception return addr reg
  (R_TMP      18)       ;; Assembler temporary reg
  (R_GOT      20)       ;; GOT ptr reg
  (MB_PIPE_3   0)       ;; Microblaze 3-stage pipeline 
  (MB_PIPE_5   1)       ;; Microblaze 5-stage pipeline 
  (UNSPEC_SET_GOT       101)    ;;
  (UNSPEC_GOTOFF        102)    ;; GOT offset
  (UNSPEC_PLT           103)    ;; jump table
  (UNSPEC_CMP		104)    ;; signed compare
  (UNSPEC_CMPU		105)    ;; unsigned compare
  (UNSPEC_TLS           106)    ;; jump table
])


;;----------------------------------------------------
;; Instruction Attributes
;;----------------------------------------------------

;; Classification of each insn.
;; branch	conditional branch
;; jump		unconditional jump
;; call		unconditional call
;; load		load instruction(s)
;; store	store instruction(s)
;; move		data movement within same register set
;; arith	integer arithmetic instruction
;; darith	double precision integer arithmetic instructions
;; imul		integer multiply
;; idiv		integer divide
;; icmp		integer compare
;; Xfadd		floating point add/subtract
;; Xfmul		floating point multiply
;; Xfmadd	floating point multiply-add
;; Xfdiv		floating point divide
;; Xfabs		floating point absolute value
;; Xfneg		floating point negation
;; Xfcmp		floating point compare
;; Xfcvt		floating point convert
;; Xfsqrt	floating point square root
;; multi	multiword sequence (or user asm statements)
;; nop		no operation
;; bshift 	Shift operations

(define_attr "type"
  "unknown,branch,jump,call,load,store,move,arith,darith,imul,idiv,icmp,multi,nop,no_delay_arith,no_delay_load,no_delay_store,no_delay_imul,no_delay_move,bshift,fadd,frsub,fmul,fdiv,fcmp,fsl,fsqrt,fcvt"
  (const_string "unknown"))

;; Main data type used by the insn
(define_attr "mode" "unknown,none,QI,HI,SI,DI,SF,DF" (const_string "unknown"))

;; # instructions (4 bytes each)
(define_attr "length" "" (const_int 4))

(define_code_iterator any_return [return simple_return])

;; <optab> expands to the name of the optab for a particular code.
(define_code_attr optab [(return "return")
			 (simple_return "simple_return")])


;;----------------------------------------------------
;; Attribute describing the processor.  
;;----------------------------------------------------

;; Describe a user's asm statement.
(define_asm_attributes
  [(set_attr "type" "multi")])

;; whether or not generating calls to position independent functions
(define_attr "abicalls" "no,yes"
  (const (symbol_ref "microblaze_abicalls_attr")))

;;----------------------------------------------------------------
;; Microblaze DFA Pipeline description
;;----------------------------------------------------------------
                  
;;-----------------------------------------------------------------
/*
   This is description of pipeline hazards based on DFA.  The
   following constructions can be used for this:

   o define_cpu_unit string [string]) describes a cpu functional unit
     (separated by comma).

     1st operand: Names of cpu function units.
     2nd operand: Name of automaton (see comments for
     DEFINE_AUTOMATON).

     All define_reservations and define_cpu_units should have unique
     names which can not be "nothing".

   o (exclusion_set string string) means that each CPU function unit
     in the first string can not be reserved simultaneously with each
     unit whose name is in the second string and vise versa.  CPU
     units in the string are separated by commas. For example, it is
     useful for description CPU with fully pipelined floating point
     functional unit which can execute simultaneously only single
     floating point insns or only double floating point insns.

   o (presence_set string string) means that each CPU function unit in
     the first string can not be reserved unless at least one of units
     whose names are in the second string is reserved.  This is an
     asymmetric relation.  CPU units in the string are separated by
     commas.  For example, it is useful for description that slot1 is
     reserved after slot0 reservation for a VLIW processor.

   o (absence_set string string) means that each CPU function unit in
     the first string can not be reserved only if each unit whose name
     is in the second string is not reserved.  This is an asymmetric
     relation (actually exclusion set is analogous to this one but it
     is symmetric).  CPU units in the string are separated by commas.
     For example, it is useful for description that slot0 can not be
     reserved after slot1 or slot2 reservation for a VLIW processor.

   o (define_bypass number out_insn_names in_insn_names) names bypass with
     given latency (the first number) from insns given by the first
     string (see define_insn_reservation) into insns given by the
     second string.  Insn names in the strings are separated by
     commas.

   o (define_automaton string) describes names of an automaton
     generated and used for pipeline hazards recognition.  The names
     are separated by comma.  Actually it is possibly to generate the
     single automaton but unfortunately it can be very large.  If we
     use more one automata, the summary size of the automata usually
     is less than the single one.  The automaton name is used in
     define_cpu_unit.  All automata should have unique names.

   o (define_reservation string string) names reservation (the first
     string) of cpu functional units (the 2nd string).  Sometimes unit
     reservations for different insns contain common parts.  In such
     case, you describe common part and use one its name (the 1st
     parameter) in regular expression in define_insn_reservation.  All
     define_reservations, define results and define_cpu_units should
     have unique names which can not be "nothing".

   o (define_insn_reservation name default_latency condition regexpr)
     describes reservation of cpu functional units (the 3nd operand)
     for instruction which is selected by the condition (the 2nd
     parameter).  The first parameter is used for output of debugging
     information.  The reservations are described by a regular
     expression according the following syntax:

       regexp = regexp "," oneof
              | oneof

       oneof = oneof "|" allof
             | allof

       allof = allof "+" repeat
             | repeat

       repeat = element "*" number
              | element

       element = cpu_function_name
               | reservation_name
               | result_name
               | "nothing"
               | "(" regexp ")"

       1. "," is used for describing start of the next cycle in
          reservation.

       2. "|" is used for describing the reservation described by the
          first regular expression *or* the reservation described by
          the second regular expression *or* etc.

       3. "+" is used for describing the reservation described by the
          first regular expression *and* the reservation described by
          the second regular expression *and* etc.

       4. "*" is used for convenience and simply means sequence in
          which the regular expression are repeated NUMBER times with
          cycle advancing (see ",").

       5. cpu function unit name which means reservation.

       6. reservation name -- see define_reservation.

       7. string "nothing" means no units reservation.

*/
;;-----------------------------------------------------------------


;;----------------------------------------------------------------
;; Microblaze 5-stage pipeline description (v5.00.a and later)
;;----------------------------------------------------------------                 
                    
(define_automaton   "mbpipe_5")
(define_cpu_unit    "mb_issue,mb_iu,mb_wb,mb_fpu,mb_fpu_2,mb_mul,mb_mul_2,mb_div,mb_div_2,mb_bs,mb_bs_2" "mbpipe_5")

(define_insn_reservation "mb-integer" 1 
  (and (eq_attr "type" "branch,jump,call,arith,darith,icmp,nop,no_delay_arith")
       (eq (symbol_ref  "microblaze_pipe") (const_int MB_PIPE_5)))
  "mb_issue,mb_iu,mb_wb")

(define_insn_reservation "mb-special-move" 2
  (and (eq_attr "type" "move")
       (eq (symbol_ref  "microblaze_pipe") (const_int MB_PIPE_5)))
  "mb_issue,mb_iu*2,mb_wb")

(define_insn_reservation "mb-mem-load" 3
  (and (eq_attr "type" "load,no_delay_load")
       (eq (symbol_ref  "microblaze_pipe") (const_int MB_PIPE_5)))
  "mb_issue,mb_iu,mb_wb")

(define_insn_reservation "mb-mem-store" 1
  (and (eq_attr "type" "store,no_delay_store")
       (eq (symbol_ref  "microblaze_pipe") (const_int MB_PIPE_5)))
  "mb_issue,mb_iu,mb_wb")

(define_insn_reservation "mb-mul" 3
  (and (eq_attr "type" "imul,no_delay_imul")
       (eq (symbol_ref  "microblaze_pipe") (const_int MB_PIPE_5)))
  "mb_issue,mb_mul,mb_mul_2*2,mb_wb")

(define_insn_reservation "mb-div" 34            
  (and (eq_attr "type" "idiv")
       (eq (symbol_ref  "microblaze_pipe") (const_int MB_PIPE_5)))
    "mb_issue,mb_div,mb_div_2*33,mb_wb")

(define_insn_reservation "mb-bs" 2 
  (and (eq_attr "type" "bshift")
       (eq (symbol_ref  "microblaze_pipe") (const_int MB_PIPE_5)))
   "mb_issue,mb_bs,mb_bs_2,mb_wb")

(define_insn_reservation "mb-fpu-add-sub-mul" 6
  (and (eq_attr "type" "fadd,frsub,fmul")
       (eq (symbol_ref  "microblaze_pipe") (const_int MB_PIPE_5)))
  "mb_issue,mb_fpu,mb_fpu_2*5,mb_wb")

(define_insn_reservation "mb-fpu-fcmp" 3
  (and (eq_attr "type" "fcmp")
       (eq (symbol_ref  "microblaze_pipe") (const_int MB_PIPE_5)))
  "mb_issue,mb_fpu,mb_fpu*2,mb_wb")

(define_insn_reservation "mb-fpu-div" 30
  (and (eq_attr "type" "fdiv")
       (eq (symbol_ref  "microblaze_pipe") (const_int MB_PIPE_5)))
  "mb_issue,mb_fpu,mb_fpu_2*29,mb_wb")

(define_insn_reservation "mb-fpu-sqrt" 30
  (and (eq_attr "type" "fsqrt")
       (eq (symbol_ref  "microblaze_pipe") (const_int MB_PIPE_5)))
  "mb_issue,mb_fpu,mb_fpu_2*29,mb_wb")

(define_insn_reservation "mb-fpu-fcvt" 4
  (and (eq_attr "type" "fcvt")
       (eq (symbol_ref  "microblaze_pipe") (const_int MB_PIPE_5)))
  "mb_issue,mb_fpu,mb_fpu_2*3,mb_wb")

;;----------------------------------------------------------------
;; Microblaze 3-stage pipeline description (for v4.00.a and earlier)
;;----------------------------------------------------------------

(define_automaton   "mbpipe_3")
(define_cpu_unit    "mb3_iu" "mbpipe_3")

(define_insn_reservation "mb3-integer" 1 
  (and (eq_attr "type" "branch,jump,call,arith,darith,icmp,nop,no_delay_arith")
       (eq (symbol_ref  "microblaze_pipe") (const_int MB_PIPE_3)))
  "mb3_iu")

(define_insn_reservation "mb3-special-move" 2
  (and (eq_attr "type" "move")
       (eq (symbol_ref  "microblaze_pipe") (const_int MB_PIPE_3)))
  "mb3_iu*2")

(define_insn_reservation "mb3-mem-load" 2
  (and (eq_attr "type" "load,no_delay_load")
       (eq (symbol_ref  "microblaze_pipe") (const_int MB_PIPE_3)))
  "mb3_iu")

(define_insn_reservation "mb3-mem-store" 1
  (and (eq_attr "type" "store,no_delay_store")
       (eq (symbol_ref  "microblaze_pipe") (const_int MB_PIPE_3)))
  "mb3_iu")

(define_insn_reservation "mb3-mul" 3
  (and (eq_attr "type" "imul,no_delay_imul")
       (eq (symbol_ref  "microblaze_pipe") (const_int MB_PIPE_3)))
  "mb3_iu")

(define_insn_reservation "mb3-div" 34            
  (and (eq_attr "type" "idiv")
       (eq (symbol_ref  "microblaze_pipe") (const_int MB_PIPE_3)))
    "mb3_iu")

(define_insn_reservation "mb3-bs" 2 
  (and (eq_attr "type" "bshift")
       (eq (symbol_ref  "microblaze_pipe") (const_int MB_PIPE_3)))
   "mb3_iu")

(define_insn_reservation "mb3-fpu-add-sub-mul" 6
  (and (eq_attr "type" "fadd,frsub,fmul")
       (eq (symbol_ref  "microblaze_pipe") (const_int MB_PIPE_3)))
  "mb3_iu")

(define_insn_reservation "mb3-fpu-fcmp" 3
  (and (eq_attr "type" "fcmp")
       (eq (symbol_ref  "microblaze_pipe") (const_int MB_PIPE_3)))
  "mb3_iu")

(define_insn_reservation "mb3-fpu-div" 30
  (and (eq_attr "type" "fdiv")
       (eq (symbol_ref  "microblaze_pipe") (const_int MB_PIPE_3)))
  "mb3_iu")

(define_insn_reservation "mb3-fpu-sqrt" 30
  (and (eq_attr "type" "fsqrt")
       (eq (symbol_ref  "microblaze_pipe") (const_int MB_PIPE_3)))
  "mb3_iu")

(define_insn_reservation "mb3-fpu-fcvt" 4
  (and (eq_attr "type" "fcvt")
       (eq (symbol_ref  "microblaze_pipe") (const_int MB_PIPE_3)))
  "mb3_iu")

(automata_option "v")
(automata_option "time")
(automata_option "progress")

(define_insn "bswapsi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (bswap:SI (match_operand:SI 1 "register_operand" "r")))]
  "TARGET_REORDER"
  "swapb %0, %1"
)

(define_insn "bswaphi2"
  [(set (match_operand:HI 0 "register_operand" "=r")
        (bswap:HI (match_operand:HI 1 "register_operand" "r")))]
  "TARGET_REORDER"
  "swaph %0, %1"
)

;;----------------------------------------------------------------
;; Microblaze delay slot description
;;----------------------------------------------------------------
(define_delay (eq_attr "type" "branch,call,jump")
  [(and (eq_attr "type" "!branch,call,jump,icmp,multi,no_delay_arith,no_delay_load,no_delay_store,no_delay_imul,no_delay_move,darith") 
        (ior (not (match_test "microblaze_no_unsafe_delay"))
             (eq_attr "type" "!fadd,frsub,fmul,fdiv,fcmp,store,load")
             ))
  (nil) (nil)])


;;----------------------------------------------------------------
;; Microblaze FPU
;;----------------------------------------------------------------

(define_insn "addsf3"
  [(set (match_operand:SF 0 "register_operand" "=d")
        (plus:SF (match_operand:SF 1 "register_operand" "d")
                 (match_operand:SF 2 "register_operand" "d")))]
  "TARGET_HARD_FLOAT"
  "fadd\t%0,%1,%2"
  [(set_attr "type"     "fadd")
  (set_attr "mode"      "SF")
  (set_attr "length"    "4")])

(define_insn "subsf3"
  [(set (match_operand:SF 0 "register_operand" "=d")
        (minus:SF (match_operand:SF 1 "register_operand" "d")
                  (match_operand:SF 2 "register_operand" "d")))]
  "TARGET_HARD_FLOAT"
  "frsub\t%0,%2,%1"
  [(set_attr "type"     "frsub")
  (set_attr "mode"      "SF")
  (set_attr "length"    "4")])

(define_insn "mulsf3"
  [(set (match_operand:SF 0 "register_operand" "=d")
        (mult:SF (match_operand:SF 1 "register_operand" "d")
                 (match_operand:SF 2 "register_operand" "d")))]
  "TARGET_HARD_FLOAT"
  "fmul\t%0,%1,%2"
  [(set_attr "type"     "fmul")
  (set_attr "mode"      "SF")
  (set_attr "length"    "4")])


(define_insn "divsf3"
  [(set (match_operand:SF 0 "register_operand" "=d")
        (div:SF (match_operand:SF 1 "register_operand" "d")
                (match_operand:SF 2 "register_operand" "d")))]
  "TARGET_HARD_FLOAT"
  "fdiv\t%0,%2,%1"
  [(set_attr "type"     "fdiv")
  (set_attr "mode"      "SF")
  (set_attr "length"    "4")])

(define_insn "sqrtsf2"
  [(set (match_operand:SF 0 "register_operand" "=d")
        (sqrt:SF (match_operand:SF 1 "register_operand" "d")))]
  "TARGET_HARD_FLOAT && TARGET_FLOAT_SQRT"
  "fsqrt\t%0,%1"
  [(set_attr "type"     "fsqrt")
  (set_attr "mode"      "SF")
  (set_attr "length"    "4")])

(define_insn "floatsisf2"
  [(set (match_operand:SF 0 "register_operand" "=d")
        (float:SF (match_operand:SI 1 "register_operand" "d")))]
  "TARGET_HARD_FLOAT && TARGET_FLOAT_CONVERT"
  "flt\t%0,%1"
  [(set_attr "type"     "fcvt")
  (set_attr "mode"      "SF")
  (set_attr "length"    "4")])

(define_insn "fix_truncsfsi2"
  [(set (match_operand:SI 0 "register_operand" "=d")
        (fix:SI (match_operand:SF 1 "register_operand" "d")))]
  "TARGET_HARD_FLOAT && TARGET_FLOAT_CONVERT"
  "fint\t%0,%1"
  [(set_attr "type"     "fcvt")
  (set_attr "mode"      "SF")
  (set_attr "length"    "4")])

;;----------------------------------------------------------------
;; Add
;;----------------------------------------------------------------

;; Add 2 SImode integers [ src1 = reg ; src2 = arith ; dest = reg ]
;; Leave carry as is
(define_insn "addsi3"
  [(set (match_operand:SI 0 "register_operand" "=d,d,d")
	(plus:SI (match_operand:SI 1 "reg_or_0_operand" "%dJ,dJ,dJ")
		 (match_operand:SI 2 "arith_plus_operand" "d,I,i")))]
  ""
  "@
   addk\t%0,%z1,%2
   addik\t%0,%z1,%2
   addik\t%0,%z1,%2"
  [(set_attr "type"	"arith,arith,no_delay_arith")
  (set_attr "mode"	"SI,SI,SI")
  (set_attr "length"	"4,4,8")])

;;----------------------------------------------------------------
;; Double Precision Additions
;;----------------------------------------------------------------

;; reg_DI_dest = reg_DI_src1 + DI_src2

;; Adding 2 DI operands in register or reg/imm

(define_insn "adddi3"
  [(set (match_operand:DI 0 "register_operand" "=d,d,d")
	(plus:DI (match_operand:DI 1 "register_operand" "%d,d,d")
		 (match_operand:DI 2 "arith_operand32" "d,P,N")))]
  ""
  "@
  add\t%L0,%L1,%L2\;addc\t%M0,%M1,%M2
  addi\t%L0,%L1,%2\;addc\t%M0,%M1,r0
  addi\t%L0,%L1,%2\;addc\t%M0,%M1,r0\;addi\t%M0,%M0,-1"
  [(set_attr "type"	"darith")
  (set_attr "mode"	"DI")
  (set_attr "length"	"8,8,12")])

;;----------------------------------------------------------------
;; Subtraction
;;----------------------------------------------------------------

(define_insn "subsi3"
  [(set (match_operand:SI 0 "register_operand" "=d,d")
	(minus:SI (match_operand:SI 1 "arith_operand" "d,d")
		  (match_operand:SI 2 "arith_operand" "d,n")))]
  ""
  "@
   rsubk\t%0,%2,%z1
   addik\t%0,%z1,-%2"
  [(set_attr "type"	"arith,no_delay_arith")
  (set_attr "mode"	"SI")
  (set_attr "length"	"4,8")])


;;----------------------------------------------------------------
;; Double Precision Subtraction
;;----------------------------------------------------------------

(define_insn "subdi3"
  [(set (match_operand:DI 0 "register_operand" "=&d")
	(minus:DI (match_operand:DI 1 "register_operand" "d")
		  (match_operand:DI 2 "arith_operand32" "d")))]
  ""
  "@
   rsub\t%L0,%L2,%L1\;rsubc\t%M0,%M2,%M1"
  [(set_attr "type"	"darith")
  (set_attr "mode"	"DI")
  (set_attr "length"	"8")])


;;----------------------------------------------------------------
;; Multiplication
;;----------------------------------------------------------------

(define_insn "mulsi3"
  [(set (match_operand:SI 0 "register_operand" "=d,d,d")
	(mult:SI (match_operand:SI 1 "register_operand" "d,d,d")
		 (match_operand:SI 2 "arith_operand" "d,I,i")))]
  "!TARGET_SOFT_MUL"
  "@
  mul\t%0,%1,%2
  muli\t%0,%1,%2
  muli\t%0,%1,%2"
  [(set_attr "type"	"imul,imul,no_delay_imul")
  (set_attr "mode"	"SI")
  (set_attr "length"	"4,4,8")])

(define_insn "mulsidi3"
  [(set (match_operand:DI 0 "register_operand" "=&d")
        (mult:DI
         (sign_extend:DI (match_operand:SI 1 "register_operand" "d"))
         (sign_extend:DI (match_operand:SI 2 "register_operand" "d"))))]
  "!TARGET_SOFT_MUL && TARGET_MULTIPLY_HIGH"
  "mul\t%L0,%1,%2\;mulh\t%M0,%1,%2"
  [(set_attr "type"     "no_delay_arith")
   (set_attr "mode"     "DI")
   (set_attr "length"   "8")])

(define_insn "umulsidi3"
  [(set (match_operand:DI 0 "register_operand" "=&d")
        (mult:DI
         (zero_extend:DI (match_operand:SI 1 "register_operand" "d"))
         (zero_extend:DI (match_operand:SI 2 "register_operand" "d"))))]
  "!TARGET_SOFT_MUL && TARGET_MULTIPLY_HIGH"
  "mul\t%L0,%1,%2\;mulhu\t%M0,%1,%2"
  [(set_attr "type"     "no_delay_arith")
   (set_attr "mode"     "DI")
   (set_attr "length"   "8")])

(define_insn "usmulsidi3"
  [(set (match_operand:DI 0 "register_operand" "=&d")
        (mult:DI
         (zero_extend:DI (match_operand:SI 1 "register_operand" "d"))
         (sign_extend:DI (match_operand:SI 2 "register_operand" "d"))))]
  "!TARGET_SOFT_MUL && TARGET_MULTIPLY_HIGH"
  "mul\t%L0,%1,%2\;mulhsu\t%M0,%2,%1"
  [(set_attr "type"     "no_delay_arith")
   (set_attr "mode"     "DI")
   (set_attr "length"   "8")])

(define_insn "*smulsi3_highpart"
  [(set (match_operand:SI 0 "register_operand" "=d")
        (truncate:SI
         (lshiftrt:DI
          (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand"  "d"))
                   (sign_extend:DI (match_operand:SI 2 "register_operand"  "d")))
          (const_int 32))))]
  "!TARGET_SOFT_MUL && TARGET_MULTIPLY_HIGH"
  "mulh\t%0,%1,%2"
  [(set_attr "type"     "imul")
  (set_attr "mode"      "SI")
  (set_attr "length"    "4")])

(define_insn "*umulsi3_highpart"
  [(set (match_operand:SI 0 "register_operand"                            "=d")
        (truncate:SI
         (lshiftrt:DI
          (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand"  "d"))
                   (zero_extend:DI (match_operand:SI 2 "register_operand"  "d"))
)
          (const_int 32))))]
  "!TARGET_SOFT_MUL && TARGET_MULTIPLY_HIGH"
  "mulhu\t%0,%1,%2"
  [(set_attr "type"     "imul")
  (set_attr "mode"      "SI")
  (set_attr "length"    "4")])

(define_insn "*usmulsi3_highpart"
  [(set (match_operand:SI 0 "register_operand"                            "=d")
        (truncate:SI
         (lshiftrt:DI
          (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand"  "d"))
                   (sign_extend:DI (match_operand:SI 2 "register_operand"  "d"))
)
          (const_int 32))))]
  "!TARGET_SOFT_MUL && TARGET_MULTIPLY_HIGH"
  "mulhsu\t%0,%2,%1"
  [(set_attr "type"     "imul")
  (set_attr "mode"      "SI")
  (set_attr "length"    "4")])


;;----------------------------------------------------------------
;; Division and remainder
;;----------------------------------------------------------------
(define_expand "divsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(div:SI (match_operand:SI 1 "register_operand" "d")
                (match_operand:SI 2 "register_operand" "d")))
  ]
  "(!TARGET_SOFT_DIV) || (TARGET_BARREL_SHIFT && TARGET_SMALL_DIVIDES)"
  {
    if (TARGET_SOFT_DIV && TARGET_BARREL_SHIFT && TARGET_SMALL_DIVIDES) 
      { 
        microblaze_expand_divide (operands);
        DONE;
      } 
    else if (!TARGET_SOFT_DIV) 
      {
        emit_insn (gen_divsi3_internal (operands[0], operands[1], operands[2]));
        DONE;
      }
  }     
)


(define_insn "divsi3_internal"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(div:SI (match_operand:SI 1 "register_operand" "d")
		(match_operand:SI 2 "register_operand" "d")))
  ]
  "!TARGET_SOFT_DIV"
  "idiv\t%0,%2,%1"
  [(set_attr "type"	"idiv")
  (set_attr "mode"	"SI")
  (set_attr "length"	"4")]
)

(define_insn "udivsi3"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(udiv:SI (match_operand:SI 1 "register_operand" "d")
                 (match_operand:SI 2 "register_operand" "d")))
  ]
  "!TARGET_SOFT_DIV"
  "idivu\t%0,%2,%1"
  [(set_attr "type"	"idiv")
  (set_attr "mode"	"SI")
  (set_attr "length"	"4")])


;;----------------------------------------------------------------
;; Negation and one's complement
;;----------------------------------------------------------------

(define_insn "negsi2"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(neg:SI (match_operand:SI 1 "register_operand" "d")))]
  ""
  "rsubk\t%0,%1,r0"
  [(set_attr "type"	"arith")
  (set_attr "mode"	"SI")
  (set_attr "length"	"4")])

(define_insn "negdi2"
  [(set (match_operand:DI 0 "register_operand" "=d")
	(neg:DI (match_operand:DI 1 "register_operand" "d")))]
  ""
  "rsub\t%L0,%L1,r0\;rsubc\t%M0,%M1,r0"
  [(set_attr "type"	"darith")
  (set_attr "mode"	"DI")
  (set_attr "length"	"8")])


(define_insn "one_cmplsi2"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(not:SI (match_operand:SI 1 "register_operand" "d")))]
  ""
  "xori\t%0,%1,-1"
  [(set_attr "type"	"arith")
  (set_attr "mode"	"SI")
  (set_attr "length"	"4")])

(define_insn "*one_cmpldi2"
  [(set (match_operand:DI 0 "register_operand" "=d")
	(not:DI (match_operand:DI 1 "register_operand" "d")))]
  ""
  "nor\t%M0,r0,%M1\;nor\t%L0,r0,%L1"
  [(set_attr "type"	"darith")
  (set_attr "mode"	"DI")
  (set_attr "length"    "8")]
)

(define_split
  [(set (match_operand:DI 0 "register_operand" "")
	(not:DI (match_operand:DI 1 "register_operand" "")))]
  "reload_completed 
   && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
   && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))"

  [(set (subreg:SI (match_dup 0) 0) (not:SI (subreg:SI (match_dup 1) 0)))
  (set (subreg:SI (match_dup 0) 4) (not:SI (subreg:SI (match_dup 1) 4)))]
  "")


;;----------------------------------------------------------------
;; Logical
;;----------------------------------------------------------------

(define_insn "andsi3"
  [(set (match_operand:SI 0 "register_operand" "=d,d,d,d")
	(and:SI (match_operand:SI 1 "arith_operand" "%d,d,d,d")
		(match_operand:SI 2 "arith_operand" "d,I,i,M")))]
  ""
  "@
   and\t%0,%1,%2
   andi\t%0,%1,%2 #and1
   andi\t%0,%1,%2 #and2
   andi\t%0,%1,%2 #and3"
  [(set_attr "type"	"arith,arith,no_delay_arith,no_delay_arith")
  (set_attr "mode"	"SI,SI,SI,SI")
  (set_attr "length"	"4,8,8,8")])


(define_insn "iorsi3"
  [(set (match_operand:SI 0 "register_operand" "=d,d,d,d")
	(ior:SI (match_operand:SI 1 "arith_operand" "%d,d,d,d")
		(match_operand:SI 2 "arith_operand" "d,I,M,i")))]
  ""
  "@
   or\t%0,%1,%2
   ori\t%0,%1,%2
   ori\t%0,%1,%2
   ori\t%0,%1,%2" 
  [(set_attr "type"	"arith,no_delay_arith,no_delay_arith,no_delay_arith")
  (set_attr "mode"	"SI,SI,SI,SI")
  (set_attr "length"	"4,8,8,8")])

(define_insn "xorsi3"
  [(set (match_operand:SI 0 "register_operand" "=d,d,d")
	(xor:SI (match_operand:SI 1 "arith_operand" "%d,d,d")
		(match_operand:SI 2 "arith_operand" "d,I,i")))]
  ""
  "@
   xor\t%0,%1,%2
   xori\t%0,%1,%2
   xori\t%0,%1,%2"
  [(set_attr "type"	"arith,arith,no_delay_arith")
  (set_attr "mode"	"SI,SI,SI")
  (set_attr "length"	"4,8,8")])

;;----------------------------------------------------------------
;; Zero extension
;;----------------------------------------------------------------

(define_insn "zero_extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "=d,d,d")
	(zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "d,R,m")))]
  ""
  "@
  andi\t%0,%1,0xffff
  lhu%i1\t%0,%1
  lhu%i1\t%0,%1"
  [(set_attr "type"	"no_delay_arith,load,no_delay_load")
  (set_attr "mode"	"SI,SI,SI")
  (set_attr "length"	"8,4,8")])

(define_insn "zero_extendqihi2"
  [(set (match_operand:HI 0 "register_operand" "=d,d,d")
	(zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "d,R,m")))]
  ""
  "@
  andi\t%0,%1,0x00ff
  lbu%i1\t%0,%1
  lbu%i1\t%0,%1"
  [(set_attr "type"	"arith,load,no_delay_load")
  (set_attr "mode"	"HI")
  (set_attr "length"	"4,4,8")])
  
(define_insn "zero_extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "=d,d,d")
	(zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "d,R,m")))]
  ""
  "@
  andi\t%0,%1,0x00ff
  lbu%i1\t%0,%1
  lbu%i1\t%0,%1"
  [(set_attr "type"	"arith,load,no_delay_load")
  (set_attr "mode"	"SI,SI,SI")
  (set_attr "length"	"4,4,8")])

;;----------------------------------------------------------------
;; Sign extension
;;----------------------------------------------------------------

;; basic Sign Extend Operations

(define_insn "extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(sign_extend:SI (match_operand:QI 1 "register_operand" "d")))]
  ""
  "sext8\t%0,%1"
  [(set_attr "type"	"arith")
  (set_attr "mode"	"SI")
  (set_attr "length"	"4")])

(define_insn "extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(sign_extend:SI (match_operand:HI 1 "register_operand" "d")))]
  ""
  "sext16\t%0,%1"
  [(set_attr "type"	"arith")
  (set_attr "mode"	"SI")
  (set_attr "length"	"4")])

;; Those for integer source operand are ordered
;; widest source type first.

(define_insn "extendsidi2"
  [(set (match_operand:DI 0 "register_operand" "=d,d,d")
	(sign_extend:DI (match_operand:SI 1 "nonimmediate_operand" "d,R,m")))]
  ""
  { 
     if (which_alternative == 0)
       output_asm_insn ("addk\t%L0,r0,%1", operands);
     else
       output_asm_insn ("lw%i1\t%L0,%1", operands);

     output_asm_insn ("add\t%M0,%L0,%L0", operands);
     output_asm_insn ("addc\t%M0,r0,r0", operands);
     output_asm_insn ("beqi\t%M0,.+8", operands);
     return "addi\t%M0,r0,0xffffffff";
  }
  [(set_attr "type"	"multi,multi,multi")
  (set_attr "mode"	"DI")
  (set_attr "length"	"20,20,20")])

;;----------------------------------------------------------------
;; Data movement
;;----------------------------------------------------------------

;; 64-bit integer moves

;; Unlike most other insns, the move insns can't be split with
;; different predicates, because register spilling and other parts of
;; the compiler, have memoized the insn number already.

(define_expand "movdi"
  [(set (match_operand:DI 0 "nonimmediate_operand" "")
	(match_operand:DI 1 "general_operand" ""))]
  ""
  {
    /* If operands[1] is a constant address illegal for pic, then we need to
       handle it just like microblaze_legitimize_address does.  */
    if (flag_pic && pic_address_needs_scratch (operands[1]))
    {
        rtx temp = force_reg (DImode, XEXP (XEXP (operands[1], 0), 0));
        rtx temp2 = XEXP (XEXP (operands[1], 0), 1);
        emit_move_insn (operands[0], gen_rtx_PLUS (DImode, temp, temp2));
        DONE;
    }


    if ((reload_in_progress | reload_completed) == 0
        && !register_operand (operands[0], DImode)
        && !register_operand (operands[1], DImode)
        && (((GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) != 0)
	       && operands[1] != CONST0_RTX (DImode))))
    {

      rtx temp = force_reg (DImode, operands[1]);
      emit_move_insn (operands[0], temp);
      DONE;
    }
  }
)



(define_insn "*movdi_internal"
  [(set (match_operand:DI 0 "nonimmediate_operand" "=d,d,d,d,d,R,o")
	(match_operand:DI 1 "general_operand"      " d,i,J,R,o,d,d"))]
  ""
  { 
    switch (which_alternative)
    {
      case 0:
        return "addk\t%0,%1\n\taddk\t%D0,%d1";
      case 1:
	return "addik\t%M0,r0,%h1\n\taddik\t%L0,r0,%j1 #li => la";
      case 2:
	  return "addk\t%0,r0,r0\n\taddk\t%D0,r0,r0";
      case 3:
      case 4:
        if (reg_mentioned_p (operands[0], operands[1]))
          return "lwi\t%D0,%o1\n\tlwi\t%0,%1";
	else
	  return "lwi\t%0,%1\n\tlwi\t%D0,%o1";
      case 5:
      case 6:
        return "swi\t%1,%0\n\tswi\t%D1,%o0";
    }
    return "unreachable";
  }
  [(set_attr "type"	"no_delay_move,no_delay_arith,no_delay_arith,no_delay_load,no_delay_load,no_delay_store,no_delay_store")
  (set_attr "mode"	"DI")
  (set_attr "length"   "8,8,8,8,12,8,12")])

(define_split
  [(set (match_operand:DI 0 "register_operand" "")
	(match_operand:DI 1 "register_operand" ""))]
  "reload_completed 
   && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
   && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1])) 
   && (REGNO(operands[0]) == (REGNO(operands[1]) + 1))"

  [(set (subreg:SI (match_dup 0) 4) (subreg:SI (match_dup 1) 4))
  (set (subreg:SI (match_dup 0) 0) (subreg:SI (match_dup 1) 0))]
  "")

(define_split
  [(set (match_operand:DI 0 "register_operand" "")
	(match_operand:DI 1 "register_operand" ""))]
  "reload_completed 
   && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
   && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1])) 
   && (REGNO (operands[0]) != (REGNO (operands[1]) + 1))"

  [(set (subreg:SI (match_dup 0) 0) (subreg:SI (match_dup 1) 0))
  (set (subreg:SI (match_dup 0) 4) (subreg:SI (match_dup 1) 4))]
  "")

;; Unlike most other insns, the move insns can't be split with
;; different predicates, because register spilling and other parts of
;; the compiler, have memoized the insn number already.

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

;; Added for status registers
(define_insn "movsi_status"
  [(set (match_operand:SI 0 "register_operand" "=d,d,z")
        (match_operand:SI 1 "register_operand" "z,d,d"))]
  "microblaze_is_interrupt_variant ()"
  "@
	mfs\t%0,%1  #mfs
	addk\t%0,%1,r0 #add movsi
	mts\t%0,%1  #mts"	
  [(set_attr "type" "move")
  (set_attr "mode" "SI")
  (set_attr "length" "12")])

;; This move will be not be moved to delay slot.	
(define_insn "*movsi_internal3"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,d")
	(match_operand:SI 1 "immediate_operand" "J,I,Mnis"))]
  "(register_operand (operands[0], SImode) && 
           (GET_CODE (operands[1]) == CONST_INT && 
                 (INTVAL (operands[1]) <= 32767 && INTVAL (operands[1]) >= -32768)))"  
  "@
   addk\t%0,r0,r0
   addik\t%0,r0,%1\t# %X1
   addik\t%0,r0,%1\t# %X1"
  [(set_attr "type"	"arith,arith,no_delay_arith")
  (set_attr "mode"	"SI")
  (set_attr "length"	"4")])

;; This move may be used for PLT label operand
(define_insn "*movsi_internal5_pltop"
  [(set (match_operand:SI 0 "register_operand" "=d,d")
	(match_operand:SI 1 "call_insn_operand" ""))]
  "(register_operand (operands[0], Pmode) && 
           PLT_ADDR_P (operands[1]))"
  { 
     gcc_unreachable ();
  }
  [(set_attr "type"	"load")
  (set_attr "mode"	"SI")
  (set_attr "length"	"4")])

(define_insn "*movsi_internal2"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,d,   d,d,R,m")
	(match_operand:SI 1 "move_src_operand"         " d,I,Mnis,R,m,dJ,dJ"))]
  ""
  "@
   addk\t%0,%1,r0
   addik\t%0,r0,%1\t# %X1
   addik\t%0,%a1
   lw%i1\t%0,%1
   lw%i1\t%0,%1
   sw%i0\t%z1,%0
   sw%i0\t%z1,%0"
  [(set_attr "type"	"load,load,no_delay_load,load,no_delay_load,store,no_delay_store")
  (set_attr "mode"	"SI")
  (set_attr "length"	"4,4,8,4,8,4,8")])


;; 16-bit Integer moves

;; Unlike most other insns, the move insns can't be split with
;; different predicates, because register spilling and other parts of
;; the compiler, have memoized the insn number already.
;; Unsigned loads are used because BYTE_LOADS_ZERO_EXTEND is defined

(define_expand "movhi"
  [(set (match_operand:HI 0 "nonimmediate_operand" "")
	(match_operand:HI 1 "general_operand" ""))]
  ""
  {
    if ((reload_in_progress | reload_completed) == 0
        && !register_operand (operands[0], HImode)
        && !register_operand (operands[1], HImode)
        && ((GET_CODE (operands[1]) != CONST_INT
  	    || INTVAL (operands[1]) != 0)))
    {
        rtx temp = force_reg (HImode, operands[1]);
        emit_move_insn (operands[0], temp);
        DONE;
    }
  }
)

(define_insn "*movhi_internal2"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=d,d,d,d,R,m")
	(match_operand:HI 1 "general_operand"       "I,d,R,m,dJ,dJ"))]
  ""
  "@
   addik\t%0,r0,%1\t# %X1
   addk\t%0,%1,r0
   lhui\t%0,%1
   lhui\t%0,%1
   sh%i0\t%z1,%0
   sh%i0\t%z1,%0"
  [(set_attr "type"	"arith,move,load,no_delay_load,store,no_delay_store")
  (set_attr "mode"	"HI")
  (set_attr "length"	"4,4,4,8,8,8")])

;; 8-bit Integer moves

;; Unlike most other insns, the move insns can't be split with
;; different predicates, because register spilling and other parts of
;; the compiler, have memoized the insn number already.
;; Unsigned loads are used because BYTE_LOADS_ZERO_EXTEND is defined

(define_expand "movqi"
  [(set (match_operand:QI 0 "nonimmediate_operand" "")
	(match_operand:QI 1 "general_operand" ""))]
  ""
  {
    if ((reload_in_progress | reload_completed) == 0
        && !register_operand (operands[0], QImode)
        && !register_operand (operands[1], QImode)
        && ((GET_CODE (operands[1]) != CONST_INT
            || INTVAL (operands[1]) != 0)))
    {
        rtx temp = force_reg (QImode, operands[1]);
        emit_move_insn (operands[0], temp);
        DONE;
    }
  }
)

(define_insn "*movqi_internal2"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=d,d,d,d,d,R,m")
	(match_operand:QI 1 "general_operand"       "J,I,d,R,m,dJ,dJ"))]
  ""
  "@
   addk\t%0,r0,%z1
   addik\t%0,r0,%1\t# %X1
   addk\t%0,%1,r0
   lbu%i1\t%0,%1
   lbu%i1\t%0,%1
   sb%i0\t%z1,%0
   sbi\t%z1,%0"
  [(set_attr "type"	"arith,arith,move,load,no_delay_load,store,no_delay_store")
  (set_attr "mode"	"QI")
  (set_attr "length"	"4,4,8,4,8,4,8")])

;; Block moves, see microblaze.c for more details.
;; Argument 0 is the destination
;; Argument 1 is the source
;; Argument 2 is the length
;; Argument 3 is the alignment
 
(define_expand "movmemsi"
  [(parallel [(set (match_operand:BLK 0 "general_operand")
		   (match_operand:BLK 1 "general_operand"))
	      (use (match_operand:SI 2 ""))
	      (use (match_operand:SI 3 "const_int_operand"))])]
  ""
  {
    if (microblaze_expand_block_move (operands[0], operands[1], 
				      operands[2], operands[3]))
        DONE;
    else  
        FAIL;
  }
)

;; 32-bit floating point moves

(define_expand "movsf"
  [(set (match_operand:SF 0 "nonimmediate_operand" "")
        (match_operand:SF 1 "general_operand" ""))]
  ""
  {
    if ((reload_in_progress | reload_completed) == 0
        && !register_operand (operands[0], SFmode)
        && !register_operand (operands[1], SFmode)
        && ( ((GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) != 0)
                 && operands[1] != CONST0_RTX (SFmode))))
    {
        rtx temp = force_reg (SFmode, operands[1]);
        emit_move_insn (operands[0], temp);
        DONE;
    }
  }
)

;; Applies to both TARGET_SOFT_FLOAT and TARGET_HARD_FLOAT
;;
(define_insn "*movsf_internal"
  [(set (match_operand:SF 0 "nonimmediate_operand" "=d,d,d,d,d,R,m")
        (match_operand:SF 1 "general_operand" "G,d,R,F,m,d,d"))]
  "(register_operand (operands[0], SFmode)
       || register_operand (operands[1], SFmode)
       || operands[1] == CONST0_RTX (SFmode))"
  "@
   addk\t%0,r0,r0
   addk\t%0,%1,r0
   lw%i1\t%0,%1
   addik\t%0,r0,%F1
   lw%i1\t%0,%1
   sw%i0\t%z1,%0
   swi\t%z1,%0"
  [(set_attr "type"     "move,no_delay_load,load,no_delay_load,no_delay_load,store,no_delay_store")
  (set_attr "mode"      "SF")
  (set_attr "length"    "4,4,4,4,4,4,4")])

;; 64-bit floating point moves
(define_expand "movdf"
  [(set (match_operand:DF 0 "nonimmediate_operand" "")
        (match_operand:DF 1 "general_operand" ""))]
  ""
  {
    if (flag_pic == 2) {
      if (GET_CODE (operands[1]) == MEM 
          && !microblaze_legitimate_address_p (DFmode, XEXP (operands[1],0), 0))
      {
        rtx ptr_reg;
        rtx result;
        ptr_reg = force_reg (Pmode, XEXP (operands[1],0));
        result = gen_rtx_MEM (DFmode, ptr_reg);
        emit_move_insn (operands[0], result);
        DONE;
      }
    }
    if ((reload_in_progress | reload_completed) == 0
        && !register_operand (operands[0], DFmode)
        && !register_operand (operands[1], DFmode)
        && (((GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) != 0)
                 && operands[1] != CONST0_RTX (DFmode))))
    {
        rtx temp = force_reg (DFmode, operands[1]);
        emit_move_insn (operands[0], temp);
        DONE;
    }
  }
)

;; movdf_internal
;; Applies to both TARGET_SOFT_FLOAT and TARGET_HARD_FLOAT
;;
(define_insn "*movdf_internal"
  [(set (match_operand:DF 0 "nonimmediate_operand" "=d,d,d,d,o")
        (match_operand:DF 1 "general_operand" "dG,o,F,T,d"))]
  ""
  {
    switch (which_alternative)
    {
      case 0:
	return "addk\t%0,r0,r0\n\taddk\t%D0,r0,r0";
      case 1:
      case 3:
	if (reg_mentioned_p (operands[0], operands[1]))
          return "lwi\t%D0,%o1\n\tlwi\t%0,%1";
        else
	  return "lwi\t%0,%1\n\tlwi\t%D0,%o1";
      case 2:
      {
	return "addik\t%0,r0,%h1 \n\taddik\t%D0,r0,%j1 #Xfer Lo";
      }
      case 4:
	return "swi\t%1,%0\n\tswi\t%D1,%o0";
    }
    gcc_unreachable ();
  }
  [(set_attr "type"     "no_delay_move,no_delay_load,no_delay_load,no_delay_load,no_delay_store")
  (set_attr "mode"      "DF")
  (set_attr "length"    "4,8,8,16,8")])

(define_split
  [(set (match_operand:DF 0 "register_operand" "")
        (match_operand:DF 1 "register_operand" ""))]
  "reload_completed
   && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
   && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
   && (REGNO (operands[0]) == (REGNO (operands[1]) + 1))"
  [(set (subreg:SI (match_dup 0) 4) (subreg:SI (match_dup 1) 4))
  (set (subreg:SI (match_dup 0) 0) (subreg:SI (match_dup 1) 0))]
  "")

(define_split
  [(set (match_operand:DF 0 "register_operand" "")
        (match_operand:DF 1 "register_operand" ""))]
  "reload_completed
   && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
   && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
   && (REGNO (operands[0]) != (REGNO (operands[1]) + 1))"
  [(set (subreg:SI (match_dup 0) 0) (subreg:SI (match_dup 1) 0))
  (set (subreg:SI (match_dup 0) 4) (subreg:SI (match_dup 1) 4))]
  "")

;;----------------------------------------------------------------
;; Shifts
;;----------------------------------------------------------------

;;----------------------------------------------------------------
;; 32-bit left shifts
;;----------------------------------------------------------------
(define_expand "ashlsi3"
  [(set (match_operand:SI 0 "register_operand" "=&d")
	(ashift:SI (match_operand:SI 1 "register_operand" "d")
		   (match_operand:SI 2 "arith_operand" "")))]
  ""
  { 
    /* Avoid recursion for trivial cases. */
    if (!((GET_CODE (operands [2]) == CONST_INT) && (INTVAL (operands[2]) == 1)))
      if (microblaze_expand_shift (operands))
        DONE;
  }
)

;; Irrespective of if we have a barrel-shifter or not, we want to match 
;; shifts by 1 with a special pattern. When a barrel shifter is present, 
;; saves a cycle. If not, allows us to annotate the instruction for delay 
;; slot optimization
(define_insn "*ashlsi3_byone"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(ashift:SI (match_operand:SI 1 "register_operand" "d")
                   (match_operand:SI 2 "arith_operand"    "I")))] 
  "(INTVAL (operands[2]) == 1)"
  "addk\t%0,%1,%1"
  [(set_attr "type"	"arith")
   (set_attr "mode"	"SI")
   (set_attr "length"	"4")]
)

;; Barrel shift left
(define_insn "ashlsi3_bshift"
  [(set (match_operand:SI 0 "register_operand" "=d,d")
	(ashift:SI (match_operand:SI 1 "register_operand" "d,d")
                   (match_operand:SI 2 "arith_operand"    "I,d")))]
  "TARGET_BARREL_SHIFT"
  "@
  bslli\t%0,%1,%2
  bsll\t%0,%1,%2"
  [(set_attr "type"	"bshift,bshift")
  (set_attr "mode"	"SI,SI")
  (set_attr "length"	"4,4")]
)

;; The following patterns apply when there is no barrel shifter present

(define_insn "*ashlsi3_with_mul_delay"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(ashift:SI (match_operand:SI 1 "register_operand"  "d")
                   (match_operand:SI 2 "immediate_operand" "I")))] 
  "!TARGET_SOFT_MUL 
   && ((1 << INTVAL (operands[2])) <= 32767 && (1 << INTVAL (operands[2])) >= -32768)"
  "muli\t%0,%1,%m2"
  ;; This MUL will not generate an imm. Can go into a delay slot.
  [(set_attr "type"	"arith")
   (set_attr "mode"	"SI")
   (set_attr "length"	"4")]
)

(define_insn "*ashlsi3_with_mul_nodelay"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(ashift:SI (match_operand:SI 1 "register_operand"  "d")
                   (match_operand:SI 2 "immediate_operand" "I")))] 
  "!TARGET_SOFT_MUL"
  "muli\t%0,%1,%m2"
  ;; This MUL will generate an IMM. Cannot go into a delay slot
  [(set_attr "type"	"no_delay_arith")
   (set_attr "mode"	"SI")
   (set_attr "length"	"8")]
)

(define_insn "*ashlsi3_with_size_opt"
  [(set (match_operand:SI 0 "register_operand" "=&d")
       (ashift:SI (match_operand:SI 1 "register_operand"  "d")
                   (match_operand:SI 2 "immediate_operand" "I")))]
  "(INTVAL (operands[2]) > 5 && optimize_size)"
  {
    operands[3] = gen_rtx_REG (SImode, MB_ABI_ASM_TEMP_REGNUM);

    output_asm_insn ("ori\t%3,r0,%2", operands);
    if (REGNO (operands[0]) != REGNO (operands[1]))
        output_asm_insn ("addk\t%0,%1,r0", operands);

    output_asm_insn ("addik\t%3,%3,-1", operands);
    output_asm_insn ("bneid\t%3,.-4", operands);
    return "addk\t%0,%0,%0";
  }
  [(set_attr "type"    "multi")
   (set_attr "mode"    "SI")
   (set_attr "length"  "20")]
)

(define_insn "*ashlsi3_with_rotate"
  [(set (match_operand:SI 0 "register_operand" "=&d")
       (ashift:SI (match_operand:SI 1 "register_operand"  "d")
                   (match_operand:SI 2 "immediate_operand" "I")))]
  "(INTVAL (operands[2]) > 17 && !optimize_size)"
  {
    int i, nshift;
    
    nshift = INTVAL (operands[2]);
    operands[3] = gen_int_mode (0xFFFFFFFF << nshift, SImode);

    /* We do one extra shift so that the first bit (carry) coming into the MSB
       will be masked out */
    output_asm_insn ("src\t%0,%1", operands);
    for (i = 0; i < (32 - nshift); i++)
       output_asm_insn ("src\t%0,%0", operands);

    return "andi\t%0,%0,%3";
  }
  [(set_attr "type"    "multi")
  (set_attr "mode"     "SI")
  (set_attr "length"   "80")]
)

(define_insn "*ashlsi_inline"
  [(set (match_operand:SI 0 "register_operand" "=&d")
       (ashift:SI (match_operand:SI 1 "register_operand"  "d")
                   (match_operand:SI 2 "immediate_operand" "I")))]
  ""
  {
    int i;
    int nshift = INTVAL (operands[2]);
    if (REGNO (operands[0]) != REGNO (operands[1]))
      output_asm_insn ("addk\t%0,r0,%1", operands);
    output_asm_insn ("addk\t%0,%1,%1", operands);
    for (i = 0; i < (nshift - 2); i++)
      output_asm_insn ("addk\t%0,%0,%0", operands);
    return "addk\t%0,%0,%0";
  }
  [(set_attr "type"    "multi")
  (set_attr "mode"     "SI")
  (set_attr "length"   "124")]
)

(define_insn "*ashlsi_reg"
  [(set (match_operand:SI 0 "register_operand" "=&d")
       (ashift:SI (match_operand:SI 1 "register_operand"  "d")
                   (match_operand:SI 2 "register_operand" "d")))]
  ""
  {
    operands[3] = gen_rtx_REG (SImode, MB_ABI_ASM_TEMP_REGNUM);
    output_asm_insn ("andi\t%3,%2,31", operands);
    if (REGNO (operands[0]) != REGNO (operands[1])) 
      output_asm_insn ("addk\t%0,r0,%1", operands);
    /* Exit the loop if zero shift. */
    output_asm_insn ("beqid\t%3,.+20", operands);
    /* Emit the loop.  */
    output_asm_insn ("addk\t%0,%0,r0", operands);
    output_asm_insn ("addik\t%3,%3,-1", operands);
    output_asm_insn ("bneid\t%3,.-4", operands);
    return "addk\t%0,%0,%0";
  }
  [(set_attr "type"    "multi")
  (set_attr "mode"     "SI")
  (set_attr "length"   "28")]
)


;;----------------------------------------------------------------
;; 32-bit right shifts
;;----------------------------------------------------------------
(define_expand "ashrsi3"
  [(set (match_operand:SI 0 "register_operand" "=&d")
	(ashiftrt:SI (match_operand:SI 1 "register_operand" "d")
                     (match_operand:SI 2 "arith_operand" "")))]
  ""
  {
    /* Avoid recursion for trivial cases. */
    if (!((GET_CODE (operands [2]) == CONST_INT) && (INTVAL (operands[2]) == 1)))
      if (microblaze_expand_shift (operands))
        DONE;
  }
)

;; Irrespective of if we have a barrel-shifter or not, we want to match 
;; shifts by 1 with a special pattern. When a barrel shifter is present, 
;; saves a cycle. If not, allows us to annotate the instruction for delay 
;; slot optimization
(define_insn "*ashrsi3_byone"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(ashiftrt:SI (match_operand:SI 1 "register_operand" "d")
                     (match_operand:SI 2 "arith_operand"    "I")))] 
  "(INTVAL (operands[2]) == 1)"
  "sra\t%0,%1"
  [(set_attr "type"	"arith")
   (set_attr "mode"	"SI")
   (set_attr "length"	"4")]
)

;; Barrel shift right logical
(define_insn "*ashrsi3_bshift"
  [(set (match_operand:SI 0 "register_operand" "=d,d")
	(ashiftrt:SI (match_operand:SI 1 "register_operand" "d,d")
                     (match_operand:SI 2 "arith_operand"    "I,d")))]
  "TARGET_BARREL_SHIFT"
  "@
  bsrai\t%0,%1,%2
  bsra\t%0,%1,%2"
  [(set_attr "type"	"bshift,bshift")
  (set_attr "mode"	"SI,SI")
  (set_attr "length"	"4,4")]
)

(define_insn "*ashrsi_inline"
  [(set (match_operand:SI 0 "register_operand" "=&d")
       (ashiftrt:SI (match_operand:SI 1 "register_operand"  "d")
                   (match_operand:SI 2 "immediate_operand" "I")))]
  ""
  {
    int i;
    int nshift = INTVAL (operands[2]);
    if (REGNO (operands[0]) != REGNO (operands[1]))
      output_asm_insn ("addk\t%0,r0,%1", operands);
    output_asm_insn ("sra\t%0,%1", operands);
    for (i = 0; i < (nshift - 2); i++)
      output_asm_insn ("sra\t%0,%0", operands);
    return "sra\t%0,%0";
  }
  [(set_attr "type"    "multi")
  (set_attr "mode"     "SI")
  (set_attr "length"   "124")]
)

(define_insn "*ashlri_reg"
  [(set (match_operand:SI 0 "register_operand" "=&d")
       (ashiftrt:SI (match_operand:SI 1 "register_operand"  "d")
                   (match_operand:SI 2 "register_operand" "d")))]
  ""
  {
    operands[3] = gen_rtx_REG (SImode, MB_ABI_ASM_TEMP_REGNUM);
    output_asm_insn ("andi\t%3,%2,31", operands);
    if (REGNO (operands[0]) != REGNO (operands[1])) 
      output_asm_insn ("addk\t%0,r0,%1", operands);
    /* Exit the loop if zero shift. */
    output_asm_insn ("beqid\t%3,.+20", operands);
    /* Emit the loop.  */
    output_asm_insn ("addk\t%0,%0,r0", operands);
    output_asm_insn ("addik\t%3,%3,-1", operands);
    output_asm_insn ("bneid\t%3,.-4", operands);
    return "sra\t%0,%0";
  }
  [(set_attr "type"    "multi")
  (set_attr "mode"     "SI")
  (set_attr "length"   "28")]
)

;;----------------------------------------------------------------
;; 32-bit right shifts (logical)
;;----------------------------------------------------------------

(define_expand "lshrsi3"
  [(set (match_operand:SI 0 "register_operand" "=&d")
	(lshiftrt:SI (match_operand:SI 1 "register_operand" "d")
                     (match_operand:SI 2 "arith_operand" "")))]
  ""
  {
    /* Avoid recursion for trivial cases. */
    if (!((GET_CODE (operands [2]) == CONST_INT) && (INTVAL (operands[2]) == 1)))
      if (microblaze_expand_shift (operands))
        DONE;
  }
)

;; Irrespective of if we have a barrel-shifter or not, we want to match 
;; shifts by 1 with a special pattern. When a barrel shifter is present, 
;; saves a cycle. If not, allows us to annotate the instruction for delay 
;; slot optimization
(define_insn "*lshrsi3_byone"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(lshiftrt:SI (match_operand:SI 1 "register_operand" "d")
                     (match_operand:SI 2 "arith_operand"    "I")))] 
  "(INTVAL (operands[2]) == 1)"
  "srl\t%0,%1"
  [(set_attr "type"	"arith")
   (set_attr "mode"	"SI")
   (set_attr "length"	"4")]
)

;; Barrel shift right logical
(define_insn "*lshrsi3_bshift"
  [(set (match_operand:SI 0 "register_operand" "=d,d")
	(lshiftrt:SI (match_operand:SI 1 "register_operand" "d,d")
                     (match_operand:SI 2 "arith_operand"    "I,d")))]
  "TARGET_BARREL_SHIFT"
  "@
  bsrli\t%0,%1,%2
  bsrl\t%0,%1,%2"
  [(set_attr "type"	"bshift,bshift")
  (set_attr "mode"	"SI,SI")
  (set_attr "length"	"4,4")]
)

(define_insn "*lshrsi_inline"
  [(set (match_operand:SI 0 "register_operand" "=&d")
       (lshiftrt:SI (match_operand:SI 1 "register_operand"  "d")
                   (match_operand:SI 2 "immediate_operand" "I")))]
  ""
  {
    int i;
    int nshift = INTVAL (operands[2]);
    if (REGNO (operands[0]) != REGNO (operands[1]))
      output_asm_insn ("addk\t%0,r0,%1", operands);
    output_asm_insn ("srl\t%0,%1", operands);
    for (i = 0; i < (nshift - 2); i++)
      output_asm_insn ("srl\t%0,%0", operands);
    return "srl\t%0,%0";
  }
  [(set_attr "type"    "multi")
  (set_attr "mode"     "SI")
  (set_attr "length"   "124")]
)

(define_insn "*lshlri_reg"
  [(set (match_operand:SI 0 "register_operand" "=&d")
       (lshiftrt:SI (match_operand:SI 1 "register_operand"  "d")
                   (match_operand:SI 2 "register_operand" "d")))]
  ""
  {
    operands[3] = gen_rtx_REG (SImode, MB_ABI_ASM_TEMP_REGNUM);
    output_asm_insn ("andi\t%3,%2,31", operands);
    if (REGNO (operands[0]) != REGNO (operands[1])) 
      output_asm_insn ("addk\t%0,r0,%1", operands);
    /* Exit the loop if zero shift. */
    output_asm_insn ("beqid\t%3,.+20", operands);
    /* Emit the loop.  */
    output_asm_insn ("addk\t%0,%0,r0", operands);
    output_asm_insn ("addik\t%3,%3,-1", operands);
    output_asm_insn ("bneid\t%3,.-4", operands);
    return "srl\t%0,%0";
  }
  [(set_attr "type"    "multi")
  (set_attr "mode"     "SI")
  (set_attr "length"   "28")]
)

;;----------------------------------------------------------------
;; Setting a register from an integer comparison. 
;;----------------------------------------------------------------
(define_expand "cstoresi4"
   [(set (match_operand:SI 0 "register_operand")
        (match_operator:SI 1 "ordered_comparison_operator"
	      [(match_operand:SI 2 "register_operand")
	       (match_operand:SI 3 "register_operand")]))]
  "TARGET_PATTERN_COMPARE"
  "if (GET_CODE (operand1) != EQ && GET_CODE (operand1) != NE) 
     FAIL;
  "
)

(define_insn "seq_internal_pat" 
  [(set (match_operand:SI 0 "register_operand" "=d")
	(eq:SI 
	       (match_operand:SI 1 "register_operand" "d")
	       (match_operand:SI 2 "register_operand" "d")))]
  "TARGET_PATTERN_COMPARE"
  "pcmpeq\t%0,%1,%2"
  [(set_attr "type"	"arith")
   (set_attr "mode"	"SI")
   (set_attr "length"	"4")]
)              

(define_insn "sne_internal_pat" 
  [(set (match_operand:SI 0 "register_operand" "=d")
	(ne:SI 
	       (match_operand:SI 1 "register_operand" "d")
	       (match_operand:SI 2 "register_operand" "d")))]
  "TARGET_PATTERN_COMPARE"
  "pcmpne\t%0,%1,%2"
  [(set_attr "type"	"arith")
  (set_attr "mode"	"SI")
  (set_attr "length"	"4")]
)              

(define_insn "signed_compare"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(unspec
		[(match_operand:SI 1 "register_operand" "d")
		 (match_operand:SI 2 "register_operand" "d")] UNSPEC_CMP))]
  ""
  "cmp\t%0,%1,%2"
  [(set_attr "type"	"arith")
  (set_attr "mode"	"SI")
  (set_attr "length"	"4")])

(define_insn "unsigned_compare"
  [(set (match_operand:SI 0 "register_operand" "=d")
	(unspec 
		[(match_operand:SI 1 "register_operand" "d")
		 (match_operand:SI 2 "register_operand" "d")] UNSPEC_CMPU))]
  ""
  "cmpu\t%0,%1,%2"
  [(set_attr "type"	"arith")
  (set_attr "mode"	"SI")
  (set_attr "length"	"4")])

;;----------------------------------------------------------------
;; Setting a register from an floating point comparison. 
;;----------------------------------------------------------------
(define_insn "cstoresf4"
   [(set (match_operand:SI 0 "register_operand" "=r")
        (match_operator 1 "comparison_operator"
	      [(match_operand:SF 2 "register_operand" "r")
	       (match_operand:SF 3 "register_operand" "r")]))]
  "TARGET_HARD_FLOAT"
  "fcmp.%C1\t%0,%3,%2"
  [(set_attr "type"     "fcmp")
   (set_attr "mode"      "SF")
   (set_attr "length"    "4")]
)

;;----------------------------------------------------------------
;; Conditional branches
;;----------------------------------------------------------------

(define_expand "cbranchsi4"
  [(set (pc)
	(if_then_else (match_operator 0 "ordered_comparison_operator"
		       [(match_operand:SI 1 "register_operand")
		        (match_operand:SI 2 "arith_operand")])
		      (label_ref (match_operand 3 ""))
		      (pc)))]
  ""
{
  microblaze_expand_conditional_branch (SImode, operands);
  DONE;
})

(define_expand "cbranchsf4"
  [(set (pc)
	(if_then_else (match_operator 0 "comparison_operator"
		       [(match_operand:SF 1 "register_operand")
		        (match_operand:SF 2 "register_operand")])
		      (label_ref (match_operand 3 ""))
		      (pc)))]
  "TARGET_HARD_FLOAT"
{
  microblaze_expand_conditional_branch_sf (operands);
  DONE;

})

;; Used to implement comparison instructions
(define_expand "condjump"
  [(set (pc)
	(if_then_else (match_operand 0)
		      (label_ref (match_operand 1))
		      (pc)))])

(define_insn "branch_zero"
  [(set (pc)
	(if_then_else (match_operator:SI 0 "ordered_comparison_operator"
  				 [(match_operand:SI 1 "register_operand" "d")
                                  (const_int 0)])
                      (match_operand:SI 2 "pc_or_label_operand" "")
                      (match_operand:SI 3 "pc_or_label_operand" "")))
  ]
  ""
  {
    if (operands[3] == pc_rtx) 
      return "b%C0i%?\t%z1,%2";
    else 
      return "b%N0i%?\t%z1,%3";
  }
  [(set_attr "type"	"branch")
   (set_attr "mode"	"none")
   (set_attr "length"	"4")]
)

;;----------------------------------------------------------------
;; Unconditional branches
;;----------------------------------------------------------------
(define_insn "jump"
  [(set (pc)
	(label_ref (match_operand 0 "" "")))]
  ""
  {
    if (GET_CODE (operands[0]) == REG)
        return "br%?\t%0";
    else	
        return "bri%?\t%l0";
  }
  [(set_attr "type"	"jump")
  (set_attr "mode"	"none")
  (set_attr "length"	"4")])

(define_expand "indirect_jump"
  [(set (pc) (match_operand 0 "register_operand" "d"))]
  ""
  {
    rtx dest = operands[0];
    if (GET_CODE (dest) != REG || GET_MODE (dest) != Pmode)
      operands[0] = copy_to_mode_reg (Pmode, dest);

    emit_jump_insn (gen_indirect_jump_internal1 (operands[0]));
    DONE;
  }
)

;; Indirect jumps. Jump to register values. Assuming absolute jumps

(define_insn "indirect_jump_internal1"
  [(set (pc) (match_operand:SI 0 "register_operand" "d"))]
  ""
  "bra%?\t%0"
  [(set_attr "type"	"jump")
  (set_attr "mode"	"none")
  (set_attr "length"	"4")])

(define_expand "tablejump"
  [(set (pc)
	(match_operand 0 "register_operand" "d"))
  (use (label_ref (match_operand 1 "" "")))]
  ""
  {
    gcc_assert (GET_MODE (operands[0]) == Pmode);

    if (!flag_pic)
      emit_jump_insn (gen_tablejump_internal1 (operands[0], operands[1]));
    else
      emit_jump_insn (gen_tablejump_internal3 (operands[0], operands[1]));
    DONE;
  }
)

(define_insn "tablejump_internal1"
  [(set (pc)
	(match_operand:SI 0 "register_operand" "d"))
  (use (label_ref (match_operand 1 "" "")))]
  ""
  "bra%?\t%0 "
  [(set_attr "type"	"jump")
  (set_attr "mode"	"none")
  (set_attr "length"	"4")])

(define_expand "tablejump_internal3"
  [(parallel [(set (pc)
		   (plus:SI (match_operand:SI 0 "register_operand" "d")
			    (label_ref:SI (match_operand:SI 1 "" ""))))
             (use (label_ref:SI (match_dup 1)))])]
  ""
  ""
)

;; need to change for MicroBlaze PIC
(define_insn ""
 [(set (pc)
	(plus:SI (match_operand:SI 0 "register_operand" "d")
		 (label_ref:SI (match_operand 1 "" ""))))
  (use (label_ref:SI (match_dup 1)))]
 "next_active_insn (insn) != 0
  && GET_CODE (PATTERN (next_active_insn (insn))) == ADDR_DIFF_VEC
  && PREV_INSN (next_active_insn (insn)) == operands[1]
  && flag_pic"
  {
    output_asm_insn ("addk\t%0,%0,r20",operands);
    return "bra%?\t%0";
}
 [(set_attr "type"	"jump")
  (set_attr "mode"	"none")
  (set_attr "length"	"4")])

(define_expand "tablejump_internal4"
  [(parallel [(set (pc)
		   (plus:DI (match_operand:DI 0 "register_operand" "d")
			    (label_ref:DI (match_operand:SI 1 "" ""))))
             (use (label_ref:DI (match_dup 1)))])]
  ""
  ""
)

;;----------------------------------------------------------------
;; Function prologue/epilogue and stack allocation
;;----------------------------------------------------------------
(define_expand "prologue"
  [(const_int 1)]
  ""
  {
      microblaze_expand_prologue ();
      DONE;
  }
)

(define_expand "epilogue"
  [(use (const_int 0))]
  ""
  {
      microblaze_expand_epilogue ();
      DONE;
  }
)

;; An insn to allocate new stack space for dynamic use (e.g., alloca).
;; We copy the return address, decrement the stack pointer and save the 
;; return address again at the new stack top 

(define_expand "allocate_stack"
  [(set (match_operand 0 "register_operand" "=r")
	(minus (reg 1) (match_operand 1 "register_operand" "")))
   (set (reg 1)
	(minus (reg 1) (match_dup 1)))]
  ""
  { 
    rtx retaddr = gen_rtx_MEM (Pmode, stack_pointer_rtx);
    rtx rtmp    = gen_rtx_REG (SImode, R_TMP);
    rtx neg_op0;

    emit_move_insn (rtmp, retaddr);
    if (GET_CODE (operands[1]) != CONST_INT)
    {
        neg_op0 = gen_reg_rtx (Pmode);
	emit_insn (gen_negsi2 (neg_op0, operands[1]));
    } else
        neg_op0 = GEN_INT (- INTVAL (operands[1]));

    emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, neg_op0));
    emit_move_insn (gen_rtx_MEM (Pmode, stack_pointer_rtx), rtmp);
    emit_move_insn (operands[0], virtual_stack_dynamic_rtx);
    emit_insn (gen_rtx_CLOBBER (SImode, rtmp));
    DONE;
  }
)

(define_expand "save_stack_block"
  [(match_operand 0 "register_operand" "")
   (match_operand 1 "register_operand" "")]
  ""
  {
    emit_move_insn (operands[0], operands[1]);
    DONE;
  }
)

(define_expand "restore_stack_block"
  [(match_operand 0 "register_operand" "")
   (match_operand 1 "register_operand" "")]
  ""
  {
    rtx retaddr = gen_rtx_MEM (Pmode, stack_pointer_rtx);
    rtx rtmp    = gen_rtx_REG (SImode, R_TMP);

    /* Move the retaddr.  */
    emit_move_insn (rtmp, retaddr);
    emit_move_insn (operands[0], operands[1]);
    emit_move_insn (gen_rtx_MEM (Pmode, operands[0]), rtmp);
    DONE;
  }
)

;; Trivial return.  Make it look like a normal return insn as that
;; allows jump optimizations to work better .
(define_expand "return"
  [(simple_return)]
  "microblaze_can_use_return_insn ()"
  {}
)

(define_expand "simple_return"
  [(simple_return)]
  ""
  {}
)

(define_insn "*<optab>"
  [(any_return)]
  ""
  { 
    if (microblaze_is_interrupt_variant ())
        return "rtid\tr14, 0\;%#";
    else
        return "rtsd\tr15, 8\;%#";
  }
  [(set_attr "type"	"jump")
  (set_attr "mode"	"none")
  (set_attr "length"	"4")]
)

;; Normal return.

(define_insn "<optab>_internal"
  [(any_return)
   (use (match_operand:SI 0 "register_operand" ""))]
  ""
  {	
    if (microblaze_is_interrupt_variant ())
        return "rtid\tr14,0 \;%#";
    else
        return "rtsd\tr15,8 \;%#";
  }
  [(set_attr "type"	"jump")
  (set_attr "mode"	"none")
  (set_attr "length"	"4")])


;; Block any insns from across this point
;; Useful to group sequences together.
(define_insn "blockage"
  [(unspec_volatile [(const_int 0)] 0)]
  ""
  ""
  [(set_attr "type"	"unknown")
  (set_attr "mode"	"none")
  (set_attr "length"	"0")])

  
;;----------------------------------------------------------------
;; Function calls
;;----------------------------------------------------------------

(define_expand "call"
  [(parallel [(call (match_operand 0 "memory_operand" "m")
		    (match_operand 1 "" "i"))
             (clobber (reg:SI R_SR))
             (use (match_operand 2 "" ""))
             (use (match_operand 3 "" ""))])]
  ""
  {
    rtx addr = XEXP (operands[0], 0);

    if (flag_pic == 2 && GET_CODE (addr) == SYMBOL_REF 
	&& !SYMBOL_REF_LOCAL_P (addr)) 
      {
        rtx temp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_PLT);
        XEXP (operands[0], 0) = temp;
      }
    
    if ((GET_CODE (addr) != REG && !CONSTANT_ADDRESS_P (addr))
	|| !call_insn_operand (addr, VOIDmode))
      XEXP (operands[0], 0) = copy_to_mode_reg (Pmode, addr);

    if (GET_CODE (XEXP (operands[0], 0)) == UNSPEC)
      emit_call_insn (gen_call_internal_plt0 (operands[0], operands[1],
                        gen_rtx_REG (SImode, 
				     GP_REG_FIRST + MB_ABI_SUB_RETURN_ADDR_REGNUM),
                               	     pic_offset_table_rtx));
    else
      emit_call_insn (gen_call_internal0 (operands[0], operands[1],
                        gen_rtx_REG (SImode, 
				     GP_REG_FIRST + MB_ABI_SUB_RETURN_ADDR_REGNUM)));

        DONE;
  }
)

(define_expand "call_internal0"
  [(parallel [(call (match_operand 0 "" "")
		    (match_operand 1 "" ""))
             (clobber (match_operand:SI 2 "" ""))])]
  ""
  {
  }
)
 
(define_expand "call_internal_plt0"
  [(parallel [(call (match_operand 0 "" "")
		    (match_operand 1 "" ""))
             (clobber (match_operand:SI 2 "" ""))
             (use (match_operand:SI 3 "" ""))])]
  ""
  {
  }
)
 
(define_insn "call_internal_plt"
  [(call (mem (match_operand:SI 0 "call_insn_plt_operand" ""))
	 (match_operand:SI 1 "" "i"))
  (clobber (reg:SI R_SR))
  (use (reg:SI R_GOT))]
  "flag_pic"
  {
    register rtx target2 = gen_rtx_REG (Pmode, 
			      GP_REG_FIRST + MB_ABI_SUB_RETURN_ADDR_REGNUM);
    gen_rtx_CLOBBER (VOIDmode, target2);
    return "brlid\tr15,%0\;%#";
  }
  [(set_attr "type"	"call")
  (set_attr "mode"	"none")
  (set_attr "length"	"4")])

(define_insn "call_internal1"
  [(call (mem (match_operand:SI 0 "call_insn_simple_operand" "ri"))
	 (match_operand:SI 1 "" "i"))
  (clobber (reg:SI R_SR))]
  ""
  {
    register rtx target = operands[0];
    register rtx target2 = gen_rtx_REG (Pmode,
			      GP_REG_FIRST + MB_ABI_SUB_RETURN_ADDR_REGNUM);
    if (GET_CODE (target) == SYMBOL_REF) {
        gen_rtx_CLOBBER (VOIDmode, target2);
        return "brlid\tr15,%0\;%#";
    } else if (GET_CODE (target) == CONST_INT)
        return "la\t%@,r0,%0\;brald\tr15,%@\;%#";
    else if (GET_CODE (target) == REG)
        return "brald\tr15,%0\;%#";	
    else {
        fprintf (stderr,"Unsupported call insn\n");
        return NULL;
    }
  }
  [(set_attr "type"	"call")
  (set_attr "mode"	"none")
  (set_attr "length"	"4")])

;; calls.c now passes a fourth argument, make saber happy

(define_expand "call_value"
  [(parallel [(set (match_operand 0 "register_operand" "=d")
		   (call (match_operand 1 "memory_operand" "m")
			 (match_operand 2 "" "i")))
             (clobber (reg:SI R_SR))
             (use (match_operand 3 "" ""))])] ;; next_arg_reg
  ""
  {
    rtx addr = XEXP (operands[1], 0);

    if (flag_pic == 2 && GET_CODE (addr) == SYMBOL_REF
	&& !SYMBOL_REF_LOCAL_P (addr)) 
      {
        rtx temp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_PLT);
        XEXP (operands[1], 0) = temp;
      }

    if ((GET_CODE (addr) != REG && !CONSTANT_ADDRESS_P (addr))
        || !call_insn_operand (addr, VOIDmode))
      XEXP (operands[1], 0) = copy_to_mode_reg (Pmode, addr);

    if (GET_CODE (XEXP (operands[1], 0)) == UNSPEC)
      emit_call_insn (gen_call_value_intern_plt0 (operands[0], operands[1], 
			operands[2],
                        gen_rtx_REG (SImode, 
				     GP_REG_FIRST + MB_ABI_SUB_RETURN_ADDR_REGNUM),
				     pic_offset_table_rtx));
    else
      emit_call_insn (gen_call_value_internal (operands[0], operands[1], 
			operands[2],
                        gen_rtx_REG (SImode, 
				     GP_REG_FIRST + MB_ABI_SUB_RETURN_ADDR_REGNUM)));

    DONE;
  }
)


(define_expand "call_value_internal"
  [(parallel [(set (match_operand 0 "" "")
		   (call (match_operand 1 "" "")
			 (match_operand 2 "" "")))
             (clobber (match_operand:SI 3 "" ""))
             ])]
  ""
  {}
)

(define_expand "call_value_intern_plt0"
  [(parallel[(set (match_operand 0 "" "")
                  (call (match_operand 1 "" "")
                        (match_operand 2 "" "")))
             (clobber (match_operand:SI 3 "" ""))
             (use (match_operand:SI 4 "" ""))])]
  "flag_pic"
  {}
)

(define_insn "call_value_intern_plt"
  [(set (match_operand:VOID 0 "register_operand" "=d")
        (call (mem (match_operand:SI 1 "call_insn_plt_operand" ""))
              (match_operand:SI 2 "" "i")))
   (clobber (match_operand:SI 3 "register_operand" "=d"))
   (use (match_operand:SI 4 "register_operand"))]
  "flag_pic"
  { 
    register rtx target2=gen_rtx_REG (Pmode,GP_REG_FIRST + MB_ABI_SUB_RETURN_ADDR_REGNUM);

    gen_rtx_CLOBBER (VOIDmode,target2);
    return "brlid\tr15,%1\;%#";
  }
  [(set_attr "type"	"call")
  (set_attr "mode"	"none")
  (set_attr "length"	"4")])

(define_insn "call_value_intern"
  [(set (match_operand:VOID 0 "register_operand" "=d")
        (call (mem (match_operand:VOID 1 "call_insn_operand" "ri"))
              (match_operand:SI 2 "" "i")))
   (clobber (match_operand:SI 3 "register_operand" "=d"))]
  ""
  { 
    register rtx target = operands[1];
    register rtx target2=gen_rtx_REG (Pmode,GP_REG_FIRST + MB_ABI_SUB_RETURN_ADDR_REGNUM);

    if (GET_CODE (target) == SYMBOL_REF)
    {
      gen_rtx_CLOBBER (VOIDmode,target2);
      if (SYMBOL_REF_FLAGS (target) & SYMBOL_FLAG_FUNCTION)
        {
	  return "brlid\tr15,%1\;%#";
        }
      else
        {
	  return "bralid\tr15,%1\;%#";
        }
    }
    else if (GET_CODE (target) == CONST_INT)
        return "la\t%@,r0,%1\;brald\tr15,%@\;%#";
    else if (GET_CODE (target) == REG)
        return "brald\tr15,%1\;%#";	
    else 
        return "Unsupported call insn\n";
  }
  [(set_attr "type"	"call")
  (set_attr "mode"	"none")
  (set_attr "length"	"4")])


;; Call subroutine returning any type.
(define_expand "untyped_call"
  [(parallel [(call (match_operand 0 "" "")
		    (const_int 0))
             (match_operand 1 "" "")
             (match_operand 2 "" "")])]
  ""
  {
    if (operands[0])		/* silence statement not reached warnings */
    {
        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));
	}

        emit_insn (gen_blockage ());
        DONE;
      }
  }
)

;;----------------------------------------------------------------
;; Misc.
;;----------------------------------------------------------------

(define_insn "nop"
  [(const_int 0)]
  ""
  "nop"
  [(set_attr "type"	"nop")
  (set_attr "mode"	"none")
  (set_attr "length"	"4")])

;; The insn to set GOT. The hardcoded number "8" accounts for $pc difference
;; between "mfs" and "addik" instructions.
(define_insn "set_got"
  [(set (match_operand:SI 0 "register_operand" "=r")
    (unspec:SI [(const_int 0)] UNSPEC_SET_GOT))]
  ""
  "mfs\t%0,rpc\n\taddik\t%0,%0,_GLOBAL_OFFSET_TABLE_+8"
  [(set_attr "type" "multi")
   (set_attr "length" "12")])

;; This insn gives the count of leading number of zeros for the second
;; operand and stores the result in first operand.
(define_insn "clzsi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (clz:SI (match_operand:SI 1 "register_operand" "r")))]
  "TARGET_HAS_CLZ"
  "clz\t%0,%1"
  [(set_attr "type"     "arith")
  (set_attr "mode"      "SI")
  (set_attr "length"    "4")])
