;; Machine Description for Altera Nios II.
;; Copyright (C) 2012-2015 Free Software Foundation, Inc.
;; Contributed by Jonah Graham (jgraham@altera.com) and 
;; Will Reece (wreece@altera.com).
;; Contributed by Mentor Graphics, Inc.
;;
;; 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/>.

;; Register numbers
(define_constants
  [
   (FIRST_RETVAL_REGNO     2)	; Return value registers
   (LAST_RETVAL_REGNO      3)	;
   (FIRST_ARG_REGNO        4)	; Argument registers
   (LAST_ARG_REGNO         7)	;

   (TP_REGNO              23)	; Thread pointer register
   (GP_REGNO	          26)	; Global pointer register
   (FP_REGNO	          28)	; Frame pointer register
   (EA_REGNO	          29)	; Exception return address register
   (RA_REGNO              31)	; Return address register
   (LAST_GP_REG           31)	; Last general purpose register

   ;; Target register definitions
   (STATIC_CHAIN_REGNUM        12)
   (STACK_POINTER_REGNUM       27)
   (HARD_FRAME_POINTER_REGNUM  28)
   (PC_REGNUM                  37)
   (FRAME_POINTER_REGNUM       38)
   (ARG_POINTER_REGNUM         39)
   (FIRST_PSEUDO_REGISTER      40)
  ]
)

;; Enumeration of UNSPECs

(define_c_enum "unspecv" [
  UNSPECV_BLOCKAGE
  UNSPECV_WRCTL
  UNSPECV_RDCTL
  UNSPECV_FWRX
  UNSPECV_FWRY
  UNSPECV_FRDXLO
  UNSPECV_FRDXHI
  UNSPECV_FRDY
  UNSPECV_CUSTOM_NXX
  UNSPECV_CUSTOM_XNXX
  UNSPECV_LDXIO
  UNSPECV_STXIO
])

(define_c_enum "unspec" [
  UNSPEC_FCOS
  UNSPEC_FSIN
  UNSPEC_FTAN
  UNSPEC_FATAN
  UNSPEC_FEXP
  UNSPEC_FLOG
  UNSPEC_ROUND
  UNSPEC_LOAD_GOT_REGISTER
  UNSPEC_PIC_SYM
  UNSPEC_PIC_CALL_SYM
  UNSPEC_PIC_GOTOFF_SYM
  UNSPEC_LOAD_TLS_IE
  UNSPEC_ADD_TLS_LE
  UNSPEC_ADD_TLS_GD
  UNSPEC_ADD_TLS_LDM
  UNSPEC_ADD_TLS_LDO
  UNSPEC_EH_RETURN
  UNSPEC_SYNC
])


;;  Instruction scheduler

; No schedule info is currently available, using an assumption that no
; instruction can use the results of the previous instruction without
; incuring a stall.

; length of an instruction (in bytes)
(define_attr "length" "" (const_int 4))
(define_attr "type" 
  "unknown,complex,control,alu,cond_alu,st,ld,shift,mul,div,custom" 
  (const_string "complex"))

(define_asm_attributes
 [(set_attr "length" "4")
  (set_attr "type" "complex")])

(define_automaton "nios2")
(automata_option "v")
;(automata_option "no-minimization")
(automata_option "ndfa")

; The nios2 pipeline is fairly straightforward for the fast model.
; Every alu operation is pipelined so that an instruction can
; be issued every cycle.  However, there are still potential
; stalls which this description tries to deal with.

(define_cpu_unit "cpu" "nios2")

(define_insn_reservation "complex" 1
  (eq_attr "type" "complex")
  "cpu")

(define_insn_reservation "control" 1
  (eq_attr "type" "control")
  "cpu")

(define_insn_reservation "alu" 1
  (eq_attr "type" "alu")
  "cpu")

(define_insn_reservation "cond_alu" 1
  (eq_attr "type" "cond_alu")
  "cpu")

(define_insn_reservation "st" 1
  (eq_attr "type" "st")
  "cpu")
  
(define_insn_reservation "custom" 1
  (eq_attr "type" "custom")
  "cpu")

; shifts, muls and lds have three cycle latency
(define_insn_reservation "ld" 3
  (eq_attr "type" "ld")
  "cpu")

(define_insn_reservation "shift" 3
  (eq_attr "type" "shift")
  "cpu")

(define_insn_reservation "mul" 3
  (eq_attr "type" "mul")
  "cpu")

(define_insn_reservation "div" 1
  (eq_attr "type" "div")
  "cpu")

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


;; Move instructions

(define_mode_iterator M [QI HI SI])

(define_expand "mov<mode>"
  [(set (match_operand:M 0 "nonimmediate_operand" "")
        (match_operand:M 1 "general_operand" ""))]
  ""
{
  if (nios2_emit_move_sequence (operands, <MODE>mode))
    DONE;
})

(define_insn "movqi_internal"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=m, r,r, r")
        (match_operand:QI 1 "general_operand"       "rM,m,rM,I"))]
  "(register_operand (operands[0], QImode)
    || reg_or_0_operand (operands[1], QImode))"
  "@
    stb%o0\\t%z1, %0
    ldbu%o1\\t%0, %1
    mov\\t%0, %z1
    movi\\t%0, %1"
  [(set_attr "type" "st,ld,alu,alu")])

(define_insn "movhi_internal"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=m, r,r, r")
        (match_operand:HI 1 "general_operand"       "rM,m,rM,I"))]
  "(register_operand (operands[0], HImode)
    || reg_or_0_operand (operands[1], HImode))"
  "@
    sth%o0\\t%z1, %0
    ldhu%o1\\t%0, %1
    mov\\t%0, %z1
    movi\\t%0, %1"
  [(set_attr "type" "st,ld,alu,alu")])

(define_insn "movsi_internal"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=m, r,r, r,r,r,r,r")
        (match_operand:SI 1 "general_operand"       "rM,m,rM,I,J,K,S,i"))]
  "(register_operand (operands[0], SImode)
    || reg_or_0_operand (operands[1], SImode))"
  "@
    stw%o0\\t%z1, %0
    ldw%o1\\t%0, %1
    mov\\t%0, %z1
    movi\\t%0, %1
    movui\\t%0, %1
    movhi\\t%0, %H1
    addi\\t%0, gp, %%gprel(%1)
    movhi\\t%0, %H1\;addi\\t%0, %0, %L1"
  [(set_attr "type" "st,ld,alu,alu,alu,alu,alu,alu")
   (set_attr "length" "4,4,4,4,4,4,4,8")])

(define_mode_iterator BH [QI HI])
(define_mode_iterator BHW [QI HI SI])
(define_mode_attr bh [(QI "b") (HI "h")])
(define_mode_attr bhw [(QI "b") (HI "h") (SI "w")])
(define_mode_attr bhw_uns [(QI "bu") (HI "hu") (SI "w")])

(define_insn "ld<bhw_uns>io"
  [(set (match_operand:BHW 0 "register_operand" "=r")
        (unspec_volatile:BHW
          [(match_operand:BHW 1 "memory_operand" "m")] UNSPECV_LDXIO))]
  ""
  "ld<bhw_uns>io\\t%0, %1"
  [(set_attr "type" "ld")])

(define_expand "ld<bh>io"
  [(set (match_operand:BH 0 "register_operand" "=r")
        (match_operand:BH 1 "memory_operand"    "m"))]
  ""
{
  rtx tmp = gen_reg_rtx (SImode);
  emit_insn (gen_ld<bh>io_signed (tmp, operands[1]));
  emit_insn (gen_mov<mode> (operands[0], gen_lowpart (<MODE>mode, tmp)));
  DONE;
})

(define_insn "ld<bh>io_signed"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (sign_extend:SI
          (unspec_volatile:BH
            [(match_operand:BH 1 "memory_operand" "m")] UNSPECV_LDXIO)))]
  ""
  "ld<bh>io\\t%0, %1"
  [(set_attr "type" "ld")])

(define_insn "st<bhw>io"
  [(set (match_operand:BHW 0 "memory_operand" "=m")
        (unspec_volatile:BHW
          [(match_operand:BHW 1 "reg_or_0_operand" "rM")] UNSPECV_STXIO))]
  ""
  "st<bhw>io\\t%z1, %0"
  [(set_attr "type" "st")])


;; QI to [HI, SI] extension patterns are collected together
(define_mode_iterator QX [HI SI])

;; Zero extension patterns
(define_insn "zero_extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
        (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "r,m")))]
  ""
  "@
    andi\\t%0, %1, 0xffff
    ldhu%o1\\t%0, %1"
  [(set_attr "type"     "alu,ld")])

(define_insn "zero_extendqi<mode>2"
  [(set (match_operand:QX 0 "register_operand" "=r,r")
        (zero_extend:QX (match_operand:QI 1 "nonimmediate_operand" "r,m")))]
  ""
  "@
    andi\\t%0, %1, 0xff
    ldbu%o1\\t%0, %1"
  [(set_attr "type"     "alu,ld")])

;; Sign extension patterns

(define_insn "extendhisi2"
  [(set (match_operand:SI 0 "register_operand"                     "=r,r")
        (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand"  "r,m")))]
  ""
  "@
   #
   ldh%o1\\t%0, %1"
  [(set_attr "type" "alu,ld")])

(define_insn "extendqi<mode>2"
  [(set (match_operand:QX 0 "register_operand"                     "=r,r")
        (sign_extend:QX (match_operand:QI 1 "nonimmediate_operand"  "r,m")))]
  ""
  "@
   #
   ldb%o1\\t%0, %1"
  [(set_attr "type" "alu,ld")])

;; Split patterns for register alternative cases.
(define_split
  [(set (match_operand:SI 0 "register_operand" "")
        (sign_extend:SI (match_operand:HI 1 "register_operand" "")))]
  "reload_completed"
  [(set (match_dup 0)
        (and:SI (match_dup 1) (const_int 65535)))
   (set (match_dup 0)
        (xor:SI (match_dup 0) (const_int 32768)))
   (set (match_dup 0)
        (plus:SI (match_dup 0) (const_int -32768)))]
  "operands[1] = gen_lowpart (SImode, operands[1]);")

(define_split
  [(set (match_operand:QX 0 "register_operand" "")
        (sign_extend:QX (match_operand:QI 1 "register_operand" "")))]
  "reload_completed"
  [(set (match_dup 0)
        (and:SI (match_dup 1) (const_int 255)))
   (set (match_dup 0)
        (xor:SI (match_dup 0) (const_int 128)))
   (set (match_dup 0)
        (plus:SI (match_dup 0) (const_int -128)))]
  "operands[0] = gen_lowpart (SImode, operands[0]);
   operands[1] = gen_lowpart (SImode, operands[1]);")


;; Arithmetic Operations

(define_insn "addsi3"
  [(set (match_operand:SI 0 "register_operand"            "=r")
        (plus:SI (match_operand:SI 1 "register_operand"   "%r")
                 (match_operand:SI 2 "add_regimm_operand" "rIT")))]
  ""
  "add%i2\\t%0, %1, %z2"
  [(set_attr "type" "alu")])

(define_insn "subsi3"
  [(set (match_operand:SI 0 "register_operand"           "=r")
        (minus:SI (match_operand:SI 1 "reg_or_0_operand" "rM")
                  (match_operand:SI 2 "register_operand" "r")))]
  ""
  "sub\\t%0, %z1, %2"
  [(set_attr "type" "alu")])

(define_insn "mulsi3"
  [(set (match_operand:SI 0 "register_operand"          "=r")
        (mult:SI (match_operand:SI 1 "register_operand" "%r")
                 (match_operand:SI 2 "arith_operand"    "rI")))]
  "TARGET_HAS_MUL"
  "mul%i2\\t%0, %1, %z2"
  [(set_attr "type" "mul")])

(define_expand "divsi3"
  [(set (match_operand:SI 0 "register_operand"          "=r")
        (div:SI (match_operand:SI 1 "register_operand"   "r")
                (match_operand:SI 2 "register_operand"   "r")))]
  ""
{
  if (!TARGET_HAS_DIV)
    {
      if (TARGET_FAST_SW_DIV)
        {
          nios2_emit_expensive_div (operands, SImode);
          DONE;
        }
      else
        FAIL;
    }
})

(define_insn "divsi3_insn"
  [(set (match_operand:SI 0 "register_operand"            "=r")
        (div:SI (match_operand:SI 1 "register_operand"     "r")
                (match_operand:SI 2 "register_operand"     "r")))]
  "TARGET_HAS_DIV"
  "div\\t%0, %1, %2"
  [(set_attr "type" "div")])

(define_insn "udivsi3"
  [(set (match_operand:SI 0 "register_operand"            "=r")
        (udiv:SI (match_operand:SI 1 "register_operand"    "r")
                 (match_operand:SI 2 "register_operand"    "r")))]
  "TARGET_HAS_DIV"
  "divu\\t%0, %1, %2"
  [(set_attr "type" "div")])

(define_code_iterator EXTEND [sign_extend zero_extend])
(define_code_attr us [(sign_extend "s") (zero_extend "u")])
(define_code_attr mul [(sign_extend "mul") (zero_extend "umul")])

(define_insn "<us>mulsi3_highpart"
  [(set (match_operand:SI 0 "register_operand"                       "=r")
        (truncate:SI
         (lshiftrt:DI
          (mult:DI (EXTEND:DI (match_operand:SI 1 "register_operand"  "r"))
                   (EXTEND:DI (match_operand:SI 2 "register_operand"  "r")))
          (const_int 32))))]
  "TARGET_HAS_MULX"
  "mulx<us><us>\\t%0, %1, %2"
  [(set_attr "type" "mul")])

(define_expand "<mul>sidi3"
  [(set (match_operand:DI 0 "register_operand" "")
	(mult:DI (EXTEND:DI (match_operand:SI 1 "register_operand" ""))
		 (EXTEND:DI (match_operand:SI 2 "register_operand" ""))))]
  "TARGET_HAS_MULX"
{
  rtx hi = gen_reg_rtx (SImode);
  rtx lo = gen_reg_rtx (SImode);

  emit_insn (gen_<us>mulsi3_highpart (hi, operands[1], operands[2]));
  emit_insn (gen_mulsi3 (lo, operands[1], operands[2]));
  emit_move_insn (gen_lowpart (SImode, operands[0]), lo);
  emit_move_insn (gen_highpart (SImode, operands[0]), hi);
  DONE;
})


;;  Negate and ones complement

(define_insn "negsi2"
  [(set (match_operand:SI 0 "register_operand"        "=r")
        (neg:SI (match_operand:SI 1 "register_operand" "r")))]
  ""
  "sub\\t%0, zero, %1"
  [(set_attr "type" "alu")])

(define_insn "one_cmplsi2"
  [(set (match_operand:SI 0 "register_operand"        "=r")
        (not:SI (match_operand:SI 1 "register_operand" "r")))]
  ""
  "nor\\t%0, zero, %1"
  [(set_attr "type" "alu")])


;;  Integer logical Operations

(define_code_iterator LOGICAL [and ior xor])
(define_code_attr logical_asm [(and "and") (ior "or") (xor "xor")])

(define_insn "<code>si3"
  [(set (match_operand:SI 0 "register_operand"             "=r,r,r")
        (LOGICAL:SI (match_operand:SI 1 "register_operand" "%r,r,r")
                    (match_operand:SI 2 "logical_operand"  "rM,J,K")))]
  ""
  "@
    <logical_asm>\\t%0, %1, %z2
    <logical_asm>%i2\\t%0, %1, %2
    <logical_asm>h%i2\\t%0, %1, %U2"
  [(set_attr "type" "alu")])

(define_insn "*norsi3"
  [(set (match_operand:SI 0 "register_operand"                 "=r")
        (and:SI (not:SI (match_operand:SI 1 "register_operand" "%r"))
                (not:SI (match_operand:SI 2 "register_operand"  "r"))))]
  ""
  "nor\\t%0, %1, %2"
  [(set_attr "type" "alu")])


;;  Shift instructions

(define_code_iterator SHIFT  [ashift ashiftrt lshiftrt rotate])
(define_code_attr shift_op   [(ashift "ashl") (ashiftrt "ashr")
                              (lshiftrt "lshr") (rotate "rotl")])
(define_code_attr shift_asm  [(ashift "sll") (ashiftrt "sra")
                              (lshiftrt "srl") (rotate "rol")])

(define_insn "<shift_op>si3"
  [(set (match_operand:SI 0 "register_operand"          "=r")
        (SHIFT:SI (match_operand:SI 1 "register_operand" "r")
                  (match_operand:SI 2 "shift_operand"    "rL")))]
  ""
  "<shift_asm>%i2\\t%0, %1, %z2"
  [(set_attr "type" "shift")])

(define_insn "rotrsi3"
  [(set (match_operand:SI 0 "register_operand"             "=r")
        (rotatert:SI (match_operand:SI 1 "register_operand" "r")
                     (match_operand:SI 2 "register_operand" "r")))]
  ""
  "ror\\t%0, %1, %2"
  [(set_attr "type" "shift")])


;; Floating point instructions

;; Mode iterator for single/double float
(define_mode_iterator F [SF DF])
(define_mode_attr f [(SF "s") (DF "d")])

;; Basic arithmetic instructions
(define_code_iterator FOP3 [plus minus mult div])
(define_code_attr fop3 [(plus "add") (minus "sub") (mult "mul") (div "div")])

(define_insn "<fop3><mode>3"
  [(set (match_operand:F 0 "register_operand"        "=r")
        (FOP3:F (match_operand:F 1 "register_operand" "r")
                (match_operand:F 2 "register_operand" "r")))]
  "nios2_fpu_insn_enabled (n2fpu_f<fop3><f>)"
  { return nios2_fpu_insn_asm (n2fpu_f<fop3><f>); }
  [(set_attr "type" "custom")])

;; Floating point min/max operations
(define_code_iterator SMINMAX [smin smax])
(define_code_attr minmax [(smin "min") (smax "max")])
(define_insn "<code><mode>3"
  [(set (match_operand:F 0 "register_operand" "=r")
        (SMINMAX:F (match_operand:F 1 "register_operand" "r")
                   (match_operand:F 2 "register_operand" "r")))]
  "nios2_fpu_insn_enabled (n2fpu_f<minmax><f>)"
  { return nios2_fpu_insn_asm (n2fpu_f<minmax><f>); }
  [(set_attr "type" "custom")])

;; These 2-operand FP operations can be collected together
(define_code_iterator FOP2 [abs neg sqrt])
(define_insn "<code><mode>2"
  [(set (match_operand:F 0 "register_operand" "=r")
        (FOP2:F (match_operand:F 1 "register_operand" "r")))]
  "nios2_fpu_insn_enabled (n2fpu_f<code><f>)"
  { return nios2_fpu_insn_asm (n2fpu_f<code><f>); }
  [(set_attr "type" "custom")])

;; X, Y register access instructions
(define_insn "nios2_fwrx"
  [(unspec_volatile [(match_operand:DF 0 "register_operand" "r")] UNSPECV_FWRX)]
  "nios2_fpu_insn_enabled (n2fpu_fwrx)"
  { return nios2_fpu_insn_asm (n2fpu_fwrx); }
  [(set_attr "type" "custom")])

(define_insn "nios2_fwry"
  [(unspec_volatile [(match_operand:SF 0 "register_operand" "r")] UNSPECV_FWRY)]
  "nios2_fpu_insn_enabled (n2fpu_fwry)"
  { return nios2_fpu_insn_asm (n2fpu_fwry); }
  [(set_attr "type" "custom")])

;; The X, Y read insns uses an int iterator
(define_int_iterator UNSPEC_READ_XY [UNSPECV_FRDXLO UNSPECV_FRDXHI
                                     UNSPECV_FRDY])
(define_int_attr read_xy [(UNSPECV_FRDXLO "frdxlo") (UNSPECV_FRDXHI "frdxhi")
                          (UNSPECV_FRDY "frdy")])
(define_insn "nios2_<read_xy>"
  [(set (match_operand:SF 0 "register_operand" "=r")
        (unspec_volatile:SF [(const_int 0)] UNSPEC_READ_XY))]
  "nios2_fpu_insn_enabled (n2fpu_<read_xy>)"
  { return nios2_fpu_insn_asm (n2fpu_<read_xy>); }
  [(set_attr "type" "custom")])

;; Various math functions
(define_int_iterator MATHFUNC
  [UNSPEC_FCOS UNSPEC_FSIN UNSPEC_FTAN UNSPEC_FATAN UNSPEC_FEXP UNSPEC_FLOG])
(define_int_attr mathfunc [(UNSPEC_FCOS "cos") (UNSPEC_FSIN "sin")
                           (UNSPEC_FTAN "tan") (UNSPEC_FATAN "atan")
                           (UNSPEC_FEXP "exp") (UNSPEC_FLOG "log")])

(define_insn "<mathfunc><mode>2"
  [(set (match_operand:F 0 "register_operand" "=r")
        (unspec:F [(match_operand:F 1 "register_operand" "r")] MATHFUNC))]
  "nios2_fpu_insn_enabled (n2fpu_f<mathfunc><f>)"
  { return nios2_fpu_insn_asm (n2fpu_f<mathfunc><f>); }
  [(set_attr "type" "custom")])

;; Converting between floating point and fixed point

(define_code_iterator FLOAT [float unsigned_float])
(define_code_iterator FIX [fix unsigned_fix])

(define_code_attr conv_op [(float "float") (unsigned_float "floatuns")
                           (fix "fix") (unsigned_fix "fixuns")])
(define_code_attr i [(float "i") (unsigned_float "u")
                     (fix "i") (unsigned_fix "u")])

;; Integer to float conversions
(define_insn "<conv_op>si<mode>2"
  [(set (match_operand:F 0 "register_operand" "=r")
        (FLOAT:F (match_operand:SI 1 "register_operand" "r")))]
  "nios2_fpu_insn_enabled (n2fpu_float<i><f>)"
  { return nios2_fpu_insn_asm (n2fpu_float<i><f>); }
  [(set_attr "type" "custom")])

;; Float to integer conversions
(define_insn "<conv_op>_trunc<mode>si2"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (FIX:SI (match_operand:F 1 "general_operand" "r")))]
  "nios2_fpu_insn_enabled (n2fpu_fix<f><i>)"
  { return nios2_fpu_insn_asm (n2fpu_fix<f><i>); }
  [(set_attr "type" "custom")])

(define_insn "lroundsfsi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:SF 1 "general_operand" "r")] UNSPEC_ROUND))]
  "nios2_fpu_insn_enabled (n2fpu_round)"
  { return nios2_fpu_insn_asm (n2fpu_round); }
  [(set_attr "type" "custom")])

(define_insn "extendsfdf2"
  [(set (match_operand:DF 0 "register_operand" "=r")
        (float_extend:DF (match_operand:SF 1 "general_operand" "r")))]
  "nios2_fpu_insn_enabled (n2fpu_fextsd)"
  { return nios2_fpu_insn_asm (n2fpu_fextsd); }
  [(set_attr "type" "custom")])

(define_insn "truncdfsf2"
  [(set (match_operand:SF 0 "register_operand" "=r")
        (float_truncate:SF (match_operand:DF 1 "general_operand" "r")))]
  "nios2_fpu_insn_enabled (n2fpu_ftruncds)"
  { return nios2_fpu_insn_asm (n2fpu_ftruncds); }
  [(set_attr "type" "custom")])



;; Prologue, Epilogue and Return

(define_expand "prologue"
  [(const_int 1)]
  ""
{
  nios2_expand_prologue ();
  DONE;
})

(define_expand "epilogue"
  [(return)]
  ""
{
  nios2_expand_epilogue (false);
  DONE;
})

(define_expand "sibcall_epilogue"
  [(return)]
  ""
{
  nios2_expand_epilogue (true);
  DONE;
})

(define_insn "return"
  [(simple_return)]
  "nios2_can_use_return_insn ()"
  "ret")

(define_insn "simple_return"
  [(simple_return)]
  ""
  "ret")

;; Block any insns from being moved before this point, since the
;; profiling call to mcount can use various registers that aren't
;; saved or used to pass arguments.

(define_insn "blockage"
  [(unspec_volatile [(const_int 0)] UNSPECV_BLOCKAGE)]
  ""
  ""
  [(set_attr "type" "unknown")
   (set_attr "length" "0")])

;; This is used in compiling the unwind routines.
(define_expand "eh_return"
  [(use (match_operand 0 "general_operand"))]
  ""
{
  if (GET_MODE (operands[0]) != Pmode)
    operands[0] = convert_to_mode (Pmode, operands[0], 0);
  emit_insn (gen_eh_set_ra (operands[0]));
  DONE;
})

;; Modify the return address for EH return.  We can't expand this
;; until we know where it will be put in the stack frame.

(define_insn_and_split "eh_set_ra"
  [(unspec [(match_operand:SI 0 "register_operand" "r")] UNSPEC_EH_RETURN)
   (clobber (match_scratch:SI 1 "=&r"))]
  ""
  "#"
  "reload_completed"
  [(const_int 0)]
{
  nios2_set_return_address (operands[0], operands[1]);
  DONE;
})


;;  Jumps and calls

; Note that the assembler fixes up any out-of-range branch instructions not
; caught by the compiler branch shortening code.  The sequence emitted by
; the assembler can be very inefficient, but it is correct for PIC code.
; For non-PIC we are better off converting to an absolute JMPI.
;
; Direct calls and sibcalls use the CALL and JMPI instructions, respectively.
; These instructions have an immediate operand that specifies the low 28 bits
; of the PC, effectively allowing direct calls within a 256MB memory segment.
; Per the Nios II Processor Reference Handbook, the linker is not required to
; check or adjust for overflow.

(define_insn "indirect_jump"
  [(set (pc) (match_operand:SI 0 "register_operand" "r"))]
  ""
  "jmp\\t%0"
  [(set_attr "type" "control")])

(define_insn "jump"
  [(set (pc)
        (label_ref (match_operand 0 "" "")))]
  ""
  {
    if (flag_pic || get_attr_length (insn) == 4)
      return "br\\t%0";
    else
      return "jmpi\\t%0";
  }
  [(set_attr "type" "control")
   (set (attr "length") 
        (if_then_else
	    (and (ge (minus (match_dup 0) (pc)) (const_int -32768))
	         (le (minus (match_dup 0) (pc)) (const_int 32764)))
	    (const_int 4)
	    (const_int 8)))])


(define_expand "call"
  [(parallel [(call (match_operand 0 "" "")
                    (match_operand 1 "" ""))
              (clobber (reg:SI RA_REGNO))])]
  ""
  "nios2_adjust_call_address (&operands[0], NULL_RTX);")

(define_expand "call_value"
  [(parallel [(set (match_operand 0 "" "")
                   (call (match_operand 1 "" "")
                         (match_operand 2 "" "")))
              (clobber (reg:SI RA_REGNO))])]
  ""
  "nios2_adjust_call_address (&operands[1], NULL_RTX);")

(define_insn "*call"
  [(call (mem:QI (match_operand:SI 0 "call_operand" "i,r"))
         (match_operand 1 "" ""))
   (clobber (reg:SI RA_REGNO))]
  ""
  "@
   call\\t%0
   callr\\t%0"
  [(set_attr "type" "control")])

(define_insn "*call_value"
  [(set (match_operand 0 "" "")
        (call (mem:QI (match_operand:SI 1 "call_operand" "i,r"))
              (match_operand 2 "" "")))
   (clobber (reg:SI RA_REGNO))]
  ""
  "@
   call\\t%1
   callr\\t%1"
  [(set_attr "type" "control")])

(define_expand "sibcall"
  [(parallel [(call (match_operand 0 "" "")
                    (match_operand 1 "" ""))
              (return)])]
  ""
  "nios2_adjust_call_address (&operands[0], NULL_RTX);")

(define_expand "sibcall_value"
  [(parallel [(set (match_operand 0 "" "")
                   (call (match_operand 1 "" "")
                         (match_operand 2 "" "")))
              (return)])]
  ""
  "nios2_adjust_call_address (&operands[1], NULL_RTX);")

(define_insn "sibcall_internal"
 [(call (mem:QI (match_operand:SI 0 "call_operand" "i,j"))
        (match_operand 1 "" ""))
  (return)]
  ""
  "@
   jmpi\\t%0
   jmp\\t%0"
  [(set_attr "type" "control")])

(define_insn "sibcall_value_internal"
 [(set (match_operand 0 "register_operand" "")
       (call (mem:QI (match_operand:SI 1 "call_operand" "i,j"))
             (match_operand 2 "" "")))
  (return)]
  ""
  "@
   jmpi\\t%1
   jmp\\t%1"
  [(set_attr "type" "control")])

(define_expand "tablejump"
  [(parallel [(set (pc) (match_operand 0 "register_operand" "r"))
              (use (label_ref (match_operand 1 "" "")))])]
  ""
{
  if (flag_pic)
    {
      /* Hopefully, CSE will eliminate this copy.  */
      rtx reg1 = copy_addr_to_reg (gen_rtx_LABEL_REF (Pmode, operands[1]));
      rtx reg2 = gen_reg_rtx (SImode);

      emit_insn (gen_addsi3 (reg2, operands[0], reg1));
      operands[0] = reg2;
    }
})

(define_insn "*tablejump"
  [(set (pc)
        (match_operand:SI 0 "register_operand" "r"))
   (use (label_ref (match_operand 1 "" "")))]
  ""
  "jmp\\t%0"
  [(set_attr "type" "control")])


;; cstore, cbranch patterns

(define_mode_iterator CM [SI SF DF])

(define_expand "cstore<mode>4"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (match_operator:SI 1 "expandable_comparison_operator"
	  [(match_operand:CM 2 "register_operand")
	   (match_operand:CM 3 "nonmemory_operand")]))]
  ""
{
  if (!nios2_validate_compare (<MODE>mode, &operands[1], &operands[2],
                               &operands[3]))
    FAIL;
})

(define_expand "cbranch<mode>4"
  [(set (pc)
     (if_then_else
       (match_operator 0 "expandable_comparison_operator"
         [(match_operand:CM 1 "register_operand")
          (match_operand:CM 2 "nonmemory_operand")])
       (label_ref (match_operand 3 ""))
       (pc)))]
  ""
{
  if (!nios2_validate_compare (<MODE>mode, &operands[0], &operands[1],
                               &operands[2]))
    FAIL;
  if (GET_MODE_CLASS (<MODE>mode) == MODE_FLOAT
      || !reg_or_0_operand (operands[2], <MODE>mode))
    {
      rtx condreg = gen_reg_rtx (SImode);
      emit_insn (gen_cstore<mode>4
                  (condreg, operands[0], operands[1], operands[2]));
      operands[1] = condreg;
      operands[2] = const0_rtx;
      operands[0] = gen_rtx_fmt_ee (NE, VOIDmode, condreg, const0_rtx);
    }
})

(define_insn "nios2_cbranch"
  [(set (pc)
     (if_then_else
       (match_operator 0 "ordered_comparison_operator"
         [(match_operand:SI 1 "reg_or_0_operand" "rM")
          (match_operand:SI 2 "reg_or_0_operand" "rM")])
       (label_ref (match_operand 3 "" ""))
       (pc)))]
  ""
  {
    if (flag_pic || get_attr_length (insn) == 4)
      return "b%0\t%z1, %z2, %l3";
    else
      return "b%R0\t%z1, %z2, .+8;jmpi\t%l3";
  }
  [(set_attr "type" "control")
   (set (attr "length") 
        (if_then_else
	    (and (ge (minus (match_dup 3) (pc)) (const_int -32768))
	         (le (minus (match_dup 3) (pc)) (const_int 32764)))
	    (const_int 4) (const_int 8)))])

;; Floating point comparisons
(define_code_iterator FCMP [eq ne gt ge le lt])
(define_insn "nios2_s<code><mode>"
  [(set (match_operand:SI 0 "register_operand"        "=r")
        (FCMP:SI (match_operand:F 1 "register_operand" "r")
                 (match_operand:F 2 "register_operand" "r")))]
  "nios2_fpu_insn_enabled (n2fpu_fcmp<code><f>)"
  { return nios2_fpu_insn_asm (n2fpu_fcmp<code><f>); }
  [(set_attr "type" "custom")])

;; Integer comparisons

(define_code_iterator EQNE [eq ne])
(define_insn "nios2_cmp<code>"
  [(set (match_operand:SI 0 "register_operand"           "=r")
        (EQNE:SI (match_operand:SI 1 "register_operand"  "%r")
                 (match_operand:SI 2 "arith_operand"     "rI")))]
  ""
  "cmp<code>%i2\\t%0, %1, %z2"
  [(set_attr "type" "alu")])

(define_code_iterator SCMP [ge lt])
(define_insn "nios2_cmp<code>"
  [(set (match_operand:SI 0 "register_operand"           "=r")
        (SCMP:SI (match_operand:SI 1 "reg_or_0_operand"  "rM")
                 (match_operand:SI 2 "arith_operand"     "rI")))]
  ""
  "cmp<code>%i2\\t%0, %z1, %z2"
  [(set_attr "type" "alu")])

(define_code_iterator UCMP [geu ltu])
(define_insn "nios2_cmp<code>"
  [(set (match_operand:SI 0 "register_operand"           "=r")
        (UCMP:SI (match_operand:SI 1 "reg_or_0_operand"  "rM")
                 (match_operand:SI 2 "uns_arith_operand" "rJ")))]
  ""
  "cmp<code>%i2\\t%0, %z1, %z2"
  [(set_attr "type" "alu")])



;; Custom instruction patterns.  The operands are intentionally
;; mode-less, to serve as generic carriers of all Altera defined
;; built-in instruction/function types.

(define_insn "custom_nxx"
  [(unspec_volatile [(match_operand 0 "custom_insn_opcode" "N")
                     (match_operand 1 "reg_or_0_operand"  "rM")
                     (match_operand 2 "reg_or_0_operand"  "rM")]
    UNSPECV_CUSTOM_NXX)]
  ""
  "custom\\t%0, zero, %z1, %z2"
  [(set_attr "type" "custom")])

(define_insn "custom_xnxx"
  [(set (match_operand 0 "register_operand"   "=r")
        (unspec_volatile [(match_operand 1 "custom_insn_opcode" "N")
                          (match_operand 2 "reg_or_0_operand"  "rM")
                          (match_operand 3 "reg_or_0_operand"  "rM")] 
	 UNSPECV_CUSTOM_XNXX))]
  ""
  "custom\\t%1, %0, %z2, %z3"
  [(set_attr "type" "custom")])


;;  Misc. patterns

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

;; Connect 'sync' to 'memory_barrier' standard expand name
(define_expand "memory_barrier"
  [(const_int 0)]
  ""
{
  emit_insn (gen_sync ());
  DONE;
})

;; For the nios2 __builtin_sync built-in function
(define_expand "sync"
  [(set (match_dup 0)
	(unspec:BLK [(match_dup 0)] UNSPEC_SYNC))]
  ""
{
  operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode));
  MEM_VOLATILE_P (operands[0]) = 1;
})

(define_insn "*sync_insn"
  [(set (match_operand:BLK 0 "" "")
	(unspec:BLK [(match_dup 0)] UNSPEC_SYNC))]
  ""
  "sync"
  [(set_attr "type" "control")])

(define_insn "rdctl"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec_volatile:SI [(match_operand:SI 1 "rdwrctl_operand" "O")] 
	 UNSPECV_RDCTL))]
  ""
  "rdctl\\t%0, ctl%1"
  [(set_attr "type" "control")])

(define_insn "wrctl"
  [(unspec_volatile:SI [(match_operand:SI 0 "rdwrctl_operand"  "O")
                        (match_operand:SI 1 "reg_or_0_operand" "rM")] 
    UNSPECV_WRCTL)]
  ""
  "wrctl\\tctl%0, %z1"
  [(set_attr "type" "control")])

;; Trap patterns
(define_insn "trap"
  [(trap_if (const_int 1) (const_int 3))]
  ""
  "break\\t3"
  [(set_attr "type" "control")])

(define_insn "ctrapsi4"
  [(trap_if (match_operator 0 "ordered_comparison_operator"
              [(match_operand:SI 1 "reg_or_0_operand" "rM")
               (match_operand:SI 2 "reg_or_0_operand" "rM")])
            (match_operand 3 "const_int_operand" "i"))]
  ""
  "b%R0\\t%z1, %z2, 1f\;break\\t%3\;1:"
  [(set_attr "type" "control")
   (set_attr "length" "8")])
  
;; Load the GOT register.
(define_insn "load_got_register"
  [(set (match_operand:SI 0 "register_operand" "=&r")
	 (unspec:SI [(const_int 0)] UNSPEC_LOAD_GOT_REGISTER))
   (set (match_operand:SI 1 "register_operand" "=r")
	 (unspec:SI [(const_int 0)] UNSPEC_LOAD_GOT_REGISTER))]
  ""
  "nextpc\\t%0
\\t1:
\\tmovhi\\t%1, %%hiadj(_gp_got - 1b)
\\taddi\\t%1, %1, %%lo(_gp_got - 1b)"
  [(set_attr "length" "12")])

;; Read thread pointer register
(define_expand "get_thread_pointersi"
  [(match_operand:SI 0 "register_operand" "=r")]
  "TARGET_LINUX_ABI"
{
  emit_move_insn (operands[0], gen_rtx_REG (Pmode, TP_REGNO));
  DONE;
})
