;; Machine description for FT32
;; Copyright (C) 2015-2017 Free Software Foundation, Inc.
;; Contributed by FTDI <support@ftdi.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/>.

;; -------------------------------------------------------------------------
;; FT32 specific constraints, predicates and attributes
;; -------------------------------------------------------------------------

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

(define_constants [
  (FP_REG 0)
  (SP_REG 1)
  (CC_REG 35)
])

(define_c_enum "unspec"
  [UNSPEC_STRLEN
   UNSPEC_MOVMEM
   UNSPEC_SETMEM
   UNSPEC_STPCPY
   UNSPEC_INDEX_JMP
   UNSPEC_LPM
   UNSPEC_FMUL
   UNSPEC_FMULS
   UNSPEC_FMULSU
   UNSPEC_COPYSIGN
   UNSPEC_IDENTITY
   UNSPEC_INSERT_BITS
   UNSPEC_JMP_EPILOG
   UNSPEC_JMP_EPILOG24
   UNSPEC_JMP_PROLOG
   UNSPEC_XCHG
   ])

;; -------------------------------------------------------------------------
;; nop instruction
;; -------------------------------------------------------------------------

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

;; -------------------------------------------------------------------------
;; Arithmetic instructions
;; -------------------------------------------------------------------------

(define_insn "addsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
          (plus:SI
           (match_operand:SI 1 "register_operand" "r,r")
           (match_operand:SI 2 "ft32_rimm_operand" "KA,r")))
   ]
  ""
  "add.l  %0,%1,%2")

(define_insn "subsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
          (minus:SI
           (match_operand:SI 1 "register_operand" "r,r")
           (match_operand:SI 2 "ft32_rimm_operand" "KA,r")))]
  ""
  "sub.l  %0,%1,%2")

(define_insn "mulsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
          (mult:SI
           (match_operand:SI 1 "register_operand" "r,r")
           (match_operand:SI 2 "ft32_rimm_operand" "KA,r")))]
  ""
  "mul.l  %0,%1,%2")

(define_insn "umulsidi3"
  [(set (match_operand:DI 0 "register_operand" "=r,r")
    (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "r,r"))
         (zero_extend:DI (match_operand:SI 2 "ft32_rimm_operand" "r,KA"))))
   (clobber (reg:CC CC_REG))]
  ""
  "mul.l  $cc,%1,%2\;muluh.l %h0,%1,%2\;move.l   %0,$cc")

(define_insn "divsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
          (div:SI
           (match_operand:SI 1 "register_operand" "r,r")
           (match_operand:SI 2 "ft32_rimm_operand" "r,KA")))]
  "!TARGET_NODIV"
  "div.l  %0,%1,%2")

(define_insn "modsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
          (mod:SI
           (match_operand:SI 1 "register_operand" "r,r")
           (match_operand:SI 2 "ft32_rimm_operand" "r,KA")))]
  "!TARGET_NODIV"
  "mod.l  %0,%1,%2")

(define_insn "udivsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
          (udiv:SI
           (match_operand:SI 1 "register_operand" "r,r")
           (match_operand:SI 2 "ft32_rimm_operand" "r,KA")))]
  "!TARGET_NODIV"
  "udiv.l %0,%1,%2")

(define_insn "umodsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
          (umod:SI
           (match_operand:SI 1 "register_operand" "r,r")
           (match_operand:SI 2 "register_operand" "r,KA")))]
  "!TARGET_NODIV"
  "umod.l %0,%1,%2")

(define_insn "extvsi"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (sign_extract:SI (match_operand:SI 1 "register_operand" "r")
                      (match_operand:SI 2 "ft32_bwidth_operand" "b")
                      (match_operand:SI 3 "const_int_operand" "i")))]
  ""
  "bexts.l %0,%1,((15 & %2) << 5) | (%3)")

(define_insn "extzvsi"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (zero_extract:SI (match_operand:SI 1 "register_operand" "r")
                      (match_operand:SI 2 "ft32_bwidth_operand" "b")
                      (match_operand:SI 3 "const_int_operand" "i")))]
  ""
  "bextu.l %0,%1,((15 & %2) << 5) | (%3)")

(define_insn "insvsi"
  [(set (zero_extract:SI (match_operand:SI 0 "register_operand" "+r,r")
                         (match_operand:SI 1 "ft32_bwidth_operand" "b,b")
                         (match_operand:SI 2 "const_int_operand" "i,i"))
        (match_operand:SI 3 "general_operand" "r,O"))
   (clobber (match_scratch:SI 4 "=&r,r"))]
  ""
  {
    if (which_alternative == 0)
      {
        return \"ldl.l %4,%3,((%1&15)<<5)|(%2)\;bins.l %0,%0,%4\";
      }
    else
      {
        if ((INTVAL(operands[3]) == 0) || (INTVAL(operands[1]) == 1))
          return \"bins.l %0,%0,(%3<<9)|((%1&15)<<5)|(%2)\";
        else
          return \"ldk.l %4,(%3<<10)|((%1&15)<<5)|(%2)\;bins.l %0,%0,%4\";
      }
  })

;; -------------------------------------------------------------------------
;; Unary arithmetic instructions
;; -------------------------------------------------------------------------

(define_insn "one_cmplsi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (not:SI (match_operand:SI 1 "register_operand" "r")))]
  ""
  "xor.l    %0,%1,-1")

;; -------------------------------------------------------------------------
;; Logical operators
;; -------------------------------------------------------------------------

(define_insn "andsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r,r")
        (and:SI (match_operand:SI 1 "register_operand" "r,r,r")
                (match_operand:SI 2 "general_operand"  "r,x,KA")))]
  ""
  "@
  and.l  %0,%1,%2
  bins.l %0,%1,%g2
  and.l  %0,%1,%2")

(define_insn "andqi3"
  [(set (match_operand:QI 0 "register_operand" "=r,r,r")
        (and:QI (match_operand:QI 1 "register_operand" "r,r,r")
                (match_operand:QI 2 "general_operand"  "r,x,KA")))]
  ""
  "@
  and.b  %0,%1,%2
  bins.b %0,%1,%g2
  and.b  %0,%1,%2")

(define_insn "xorsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
        (xor:SI (match_operand:SI 1 "register_operand" "r,r")
                (match_operand:SI 2 "ft32_rimm_operand" "r,KA")))]
  ""
{
  return "xor.l  %0,%1,%2";
})

(define_insn "iorsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r,r")
        (ior:SI (match_operand:SI 1 "register_operand" "r,r,r")
                (match_operand:SI 2 "general_operand"  "r,w,KA")))]
  ""
  "@
  or.l   %0,%1,%2
  bins.l %0,%1,%f2
  or.l   %0,%1,%2")

;; -------------------------------------------------------------------------
;; Shifters
;; -------------------------------------------------------------------------

(define_insn "ashlsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
        (ashift:SI (match_operand:SI 1 "register_operand" "r,r")
                   (match_operand:SI 2 "ft32_rimm_operand" "r,KA")))]
  ""
{
  return "ashl.l %0,%1,%2";
})

(define_insn "ashrsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
        (ashiftrt:SI (match_operand:SI 1 "register_operand" "r,r")
                     (match_operand:SI 2 "ft32_rimm_operand" "r,KA")))]
  ""
{
  return "ashr.l %0,%1,%2";
})

(define_insn "lshrsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
        (lshiftrt:SI (match_operand:SI 1 "register_operand" "r,r")
                     (match_operand:SI 2 "ft32_rimm_operand" "r,KA")))]
  ""
{
  return "lshr.l %0,%1,%2";
})

;; -------------------------------------------------------------------------
;; Move instructions
;; -------------------------------------------------------------------------

;; SImode

(define_insn "*sne"
   [(set (match_operand:SI 0 "register_operand" "=r")
         (reg:SI CC_REG))]
   ""
   "bextu.l %0,$cc,32|0\;xor.l %0,%0,-1"
)

;; Push a register onto the stack
(define_insn "movsi_push"
  [(set (mem:SI (pre_dec:SI (reg:SI SP_REG)))
        (match_operand:SI 0 "register_operand" "r"))]
  ""
  "push.l %0")

;; Pop a register from the stack
(define_insn "movsi_pop"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (mem:SI (post_inc:SI (reg:SI SP_REG))))]
  ""
  "pop.l  %0")

(define_expand "movsi"
   [(set (match_operand:SI 0 "general_operand" "")
        (match_operand:SI 1 "general_operand" ""))]
   ""
{
  /* If this is a store, force the value into a register.  */
  if (!(reload_in_progress || reload_completed))
    {
      if (MEM_P (operands[0]))
        {
          operands[1] = force_reg (SImode, operands[1]);
          if (MEM_P (XEXP (operands[0], 0)))
            operands[0] = gen_rtx_MEM (SImode, force_reg (SImode, XEXP (operands[0], 0)));
        }
      else
        {
          if (MEM_P (operands[1]) && MEM_P (XEXP (operands[1], 0)))
            operands[1] = gen_rtx_MEM (SImode, force_reg (SImode, XEXP (operands[1], 0)));
        }
      /*
      if (MEM_P (operands[0])) {
        rtx o = XEXP (operands[0], 0);
        if (!REG_P(o) &&
            !CONST_INT_P(o) &&
            GET_CODE(o) != SYMBOL_REF &&
            GET_CODE(o) != LABEL_REF) {
          operands[0] = gen_rtx_MEM (SImode, force_reg (SImode, XEXP (operands[0], 0)));
        }
      }
      */
    }
})

(define_insn "*rtestsi"
  [(set (reg:SI CC_REG)
        (match_operand:SI 0 "register_operand" "r"))]
  ""
  "cmp.l  %0,0"
)

(define_insn "*rtestqi"
  [(set (reg:QI CC_REG)
        (match_operand:QI 0 "register_operand" "r"))]
  ""
  "cmp.b  %0,0"
)

(define_insn "*movsi"
  [(set (match_operand:SI 0 "nonimmediate_operand"         "=r,BW,r,r,r,r,A,r,r")
        (match_operand:SI 1 "ft32_general_movsrc_operand"   "r,r,BW,A,S,i,r,e,f"))]
  "register_operand (operands[0], SImode) || register_operand (operands[1], SImode)"
  "@
   move.l %0,%1
   sti.l  %0,%1
   ldi.l  %0,%1
   lda.l  %0,%1
   ldk.l  %0,%1
   *return ft32_load_immediate(operands[0], INTVAL(operands[1]));
   sta.l  %0,%1
   lpm.l  %0,%1
   lpmi.l %0,%1"
)

(define_expand "movqi"
  [(set (match_operand:QI 0 "general_operand" "")
        (match_operand:QI 1 "general_operand" ""))]
  ""
{
  /* If this is a store, force the value into a register.  */
  if (!(reload_in_progress || reload_completed))
    {
      if (MEM_P (operands[0]))
        {
          operands[1] = force_reg (QImode, operands[1]);
          if (MEM_P (XEXP (operands[0], 0)))
            operands[0] = gen_rtx_MEM (QImode, force_reg (SImode, XEXP (operands[0], 0)));
        }
      else
        {
          if (MEM_P (operands[1]) && MEM_P (XEXP (operands[1], 0)))
            operands[1] = gen_rtx_MEM (QImode, force_reg (SImode, XEXP (operands[1], 0)));
        }
      if (MEM_P (operands[0]) && !REG_P(XEXP (operands[0], 0)))
        {
          operands[0] = gen_rtx_MEM (QImode, force_reg (SImode, XEXP (operands[0], 0)));
        }
    }
})

(define_insn "zero_extendqisi2"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r")
    (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "BW,r,f")))]
  ""
  "@
  ldi.b  %0,%1
  and.l  %0,%1,255
  lpmi.b %0,%1"
)

(define_insn "extendqisi2"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=r")
    (sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "r")))]
  ""
  "bexts.l %0,%1,(8<<5)|0"
)

(define_insn "zero_extendhisi2"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,r")
    (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "BW,r,f")))]
  ""
  "@
  ldi.s  %0,%1
  bextu.l %0,%1,(0<<5)|0
  lpmi.s %0,%1"
)

(define_insn "extendhisi2"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=r")
    (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "r")))]
  ""
  "bexts.l %0,%1,(0<<5)|0"
)

(define_insn "*movqi"
  [(set (match_operand:QI 0 "nonimmediate_operand"           "=r,BW,r,r,A,r,r,r")
        (match_operand:QI 1 "ft32_general_movsrc_operand"  "r,r,BW,A,r,I,e,f"))]
  "register_operand (operands[0], QImode)
   || register_operand (operands[1], QImode)"
  "@
   move.b %0,%1
   sti.b  %0,%1
   ldi.b  %0,%1
   lda.b  %0,%1
   sta.b  %0,%1
   ldk.b  %0,%1
   lpm.b  %0,%1
   lpmi.b %0,%1"
)

(define_expand "movhi"
  [(set (match_operand:HI 0 "general_operand" "")
        (match_operand:HI 1 "general_operand" ""))]
  ""
{
  /* If this is a store, force the value into a register.  */
  if (!(reload_in_progress || reload_completed))
    {
      if (MEM_P (operands[0]))
        {
          operands[1] = force_reg (HImode, operands[1]);
          if (MEM_P (XEXP (operands[0], 0)))
            operands[0] = gen_rtx_MEM (HImode, force_reg (SImode, XEXP (operands[0], 0)));
        }
      else
        {
          if (MEM_P (operands[1]) && MEM_P (XEXP (operands[1], 0)))
            operands[1] = gen_rtx_MEM (HImode, force_reg (SImode, XEXP (operands[1], 0)));
        }
      if (MEM_P (operands[0]))
        {
          rtx o = XEXP (operands[0], 0);
          if (!REG_P(o) &&
              !CONST_INT_P(o) &&
              GET_CODE(o) != SYMBOL_REF &&
              GET_CODE(o) != LABEL_REF) {
            operands[0] = gen_rtx_MEM (HImode, force_reg (SImode, XEXP (operands[0], 0)));
          }
        }
    }
})

(define_insn "*movhi"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=r,BW,r,r,A,r,r,r")
        (match_operand:HI 1 "ft32_general_movsrc_operand"  "r,r,BW,A,r,I,e,f"))]
  "(register_operand (operands[0], HImode)
    || register_operand (operands[1], HImode))"
  "@
   move.s %0,%1
   sti.s  %0,%1
   ldi.s  %0,%1
   lda.s  %0,%1
   sta.s  %0,%1
   ldk.s  %0,%1
   lpm.s  %0,%1
   lpmi.s %0,%1"
)

(define_expand "movsf"
  [(set (match_operand:SF 0 "general_operand" "")
        (match_operand:SF 1 "general_operand" ""))]
  ""
{
  /* If this is a store, force the value into a register.  */
  if (MEM_P (operands[0]))
    operands[1] = force_reg (SFmode, operands[1]);
  if (CONST_DOUBLE_P(operands[1]))
    operands[1] = force_const_mem(SFmode, operands[1]);
})

(define_insn "*movsf"
  [(set (match_operand:SF 0 "nonimmediate_operand" "=r,BW,r,r,A,r,r")
        (match_operand:SF 1 "ft32_general_movsrc_operand"  "r,r,BW,A,r,I,f"))]
  "(register_operand (operands[0], SFmode)
    || register_operand (operands[1], SFmode))"
  "@
   move.l %0,%1
   sti.l  %0,%1
   ldi.l  %0,%1
   lda.l  %0,%1
   sta.l  %0,%1
   ldk.l  %0,%1
   lpmi.l %0,%1"
)

;; -------------------------------------------------------------------------
;; Compare instructions
;; -------------------------------------------------------------------------

(define_expand "cbranchsi4"
  [(set (reg:CC CC_REG)
        (compare:CC
         (match_operand:SI 1 "register_operand" "")
         (match_operand:SI 2 "ft32_rimm_operand" "")))
   (set (pc)
        (if_then_else (match_operator 0 "comparison_operator"
                       [(reg:CC CC_REG) (const_int 0)])
                      (label_ref (match_operand 3 "" ""))
                      (pc)))]
  ""
  "")

(define_insn "cmpsi"
  [(set (reg:CC CC_REG)
        (compare:CC
         (match_operand:SI 0 "register_operand" "r,r")
         (match_operand:SI 1 "ft32_rimm_operand" "r,KA")))]
  ""
  "cmp.l  %0,%1")

(define_insn ""
  [(set (pc)
	(if_then_else
	 (ne (zero_extract:SI (match_operand:SI 0 "register_operand" "r")
			      (const_int 1)
			      (match_operand:SI 1 "const_int_operand" "i"))
	     (const_int 0))
	 (label_ref (match_operand 2 "" ""))
	 (pc)))
   (clobber (reg:CC CC_REG))]
  ""
  "btst.l %0,(1<<5)|%1\;jmpc   nz,%l2")

(define_insn ""
  [(set (pc)
	(if_then_else
	 (eq (zero_extract:SI (match_operand:SI 0 "register_operand" "r")
			      (const_int 1)
			      (match_operand:SI 1 "const_int_operand" "i"))
	     (const_int 0))
	 (label_ref (match_operand 2 "" ""))
	 (pc)))
   (clobber (reg:CC CC_REG))]
  ""
  "btst.l %0,(1<<5)|%1\;jmpc   z,%l2")

(define_expand "cbranchqi4"
  [(set (reg:CC CC_REG)
        (compare:CC
         (match_operand:QI 1 "register_operand" "")
         (match_operand:QI 2 "ft32_rimm_operand" "")))
   (set (pc)
        (if_then_else (match_operator 0 "comparison_operator"
                       [(reg:CC CC_REG) (const_int 0)])
                      (label_ref (match_operand 3 "" ""))
                      (pc)))]
  ""
  "")

(define_insn "*cmpqi"
  [(set (reg:CC CC_REG)
        (compare:CC
         (match_operand:QI 0 "register_operand" "r,r")
         (match_operand:QI 1 "ft32_rimm_operand" "r,KA")))]
  ""
  "cmp.b  %0,%1")

;; -------------------------------------------------------------------------
;; Branch instructions
;; -------------------------------------------------------------------------

(define_code_iterator cond [ne eq lt ltu gt gtu ge le geu leu])
(define_code_attr CC [(ne "nz") (eq "z") (lt "lt") (ltu "b")
                      (gt "gt") (gtu "a") (ge "gte") (le "lte")
                      (geu "ae") (leu "be") ])
(define_code_attr rCC [(ne "z") (eq "nz") (lt "gte") (ltu "ae")
                       (gt "lte") (gtu "be") (ge "lt") (le "gt")
                       (geu "b") (leu "a") ])

(define_insn "*b<cond:code>"
  [(set (pc)
        (if_then_else (cond (reg:CC CC_REG)
                            (const_int 0))
                      (label_ref (match_operand 0 "" ""))
                      (pc)))]
  ""
{
  return "jmpc   <CC>,%l0";
}
)

(define_expand "cstoresi4"
  [(set (reg:CC CC_REG)
 	(compare:CC (match_operand:SI 2 "register_operand" "r,r")
 		    (match_operand:SI 3 "ft32_rimm_operand" "r,KA")))
   (set (match_operand:SI 0 "register_operand")
	(match_operator:SI 1 "ordered_comparison_operator"
	[(reg:CC CC_REG) (const_int 0)]))]
  ""
{
  rtx test;

  switch (GET_CODE (operands[1])) {
  case NE:
  case GEU:
  case LT:
  case LE:
  case LEU:
    test = gen_rtx_fmt_ee (reverse_condition (GET_CODE (operands[1])),
       		     SImode, operands[2], operands[3]);
    emit_insn(gen_cstoresi4(operands[0], test, operands[2], operands[3]));
    emit_insn(gen_xorsi3(operands[0], operands[0], gen_int_mode(1, SImode)));
    DONE;
  default:
    ;
  }
})

(define_insn "*seq"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (eq:SI (reg CC_REG) (const_int 0)))]
  ""
  "bextu.l %0,$cc,32|0"
)

(define_insn "*sltu"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (ltu:SI (reg CC_REG) (const_int 0)))]
  ""
  "bextu.l %0,$cc,32|1"
)

(define_insn "*sge"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (ge:SI (reg CC_REG) (const_int 0)))]
  ""
  "bextu.l %0,$cc,32|4"
)

(define_insn "*sgt"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (gt:SI (reg CC_REG) (const_int 0)))]
  ""
  "bextu.l %0,$cc,32|5"
)

(define_insn "*sgtu"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (gtu:SI (reg CC_REG) (const_int 0)))]
  ""
  "bextu.l %0,$cc,32|6"
)

;; -------------------------------------------------------------------------
;; Call and Jump instructions
;; -------------------------------------------------------------------------

(define_expand "call"
  [(call (match_operand:QI 0 "memory_operand" "")
                (match_operand 1 "general_operand" ""))]
  ""
{
  gcc_assert (MEM_P (operands[0]));
})

(define_insn "*call"
  [(call (mem:QI (match_operand:SI
                  0 "nonmemory_operand" "i,r"))
         (match_operand 1 "" ""))]
  ""
  "@
   call   %0
   calli  %0"
)

(define_expand "call_value"
  [(set (match_operand 0 "" "")
                (call (match_operand:QI 1 "memory_operand" "")
                 (match_operand 2 "" "")))]
  ""
{
  gcc_assert (MEM_P (operands[1]));
})

(define_insn "*call_value"
  [(set (match_operand 0 "register_operand" "=r")
        (call (mem:QI (match_operand:SI
                       1 "immediate_operand" "i"))
              (match_operand 2 "" "")))]
  ""
  "call   %1"
)

(define_insn "*call_value_indirect"
  [(set (match_operand 0 "register_operand" "=r")
        (call (mem:QI (match_operand:SI
                       1 "register_operand" "r"))
              (match_operand 2 "" "")))]
  ""
  "calli  %1"
)

(define_insn "indirect_jump"
  [(set (pc) (match_operand:SI 0 "nonimmediate_operand" "r"))]
  ""
  "jmpi   %0")

(define_insn "jump"
  [(set (pc)
        (label_ref (match_operand 0 "" "")))]
  ""
  "jmp    %l0"
)

(define_insn "call_prolog"
  [(unspec:SI [(match_operand 0 "" "")]
                   UNSPEC_JMP_PROLOG)]
  ""
  "call   __prolog_%0"
)

(define_insn "jump_epilog"
  [(unspec:SI [(match_operand 0 "" "")]
                   UNSPEC_JMP_EPILOG)]
  ""
  "jmp    __epilog_%0"
)

(define_insn "jump_epilog24"
  [(unspec:SI [(match_operand 0 "" "")]
                   UNSPEC_JMP_EPILOG24)]
  ""
  "jmp    __epilog24_%0"
)


;; Subroutines of "casesi".
;; operand 0 is index
;; operand 1 is the minimum bound
;; operand 2 is the maximum bound - minimum bound + 1
;; operand 3 is CODE_LABEL for the table;
;; operand 4 is the CODE_LABEL to go to if index out of range.

(define_expand "casesi"
  [(match_operand:SI 0 "general_operand" "")
   (match_operand:SI 1 "const_int_operand" "")
   (match_operand:SI 2 "const_int_operand" "")
   (match_operand 3 "" "")
   (match_operand 4 "" "")]
  ""
  "
{
  if (GET_CODE (operands[0]) != REG)
    operands[0] = force_reg (SImode, operands[0]);

  if (operands[1] != const0_rtx)
    {
      rtx index = gen_reg_rtx (SImode);
      rtx offset = gen_reg_rtx (SImode);

      emit_insn (gen_movsi (offset, operands[1]));
      emit_insn (gen_subsi3 (index, operands[0], offset));
      operands[0] = index;
    }

  {
    rtx test = gen_rtx_GTU (VOIDmode, operands[0], operands[2]);
    emit_jump_insn (gen_cbranchsi4 (test, operands[0], operands[2], operands[4]));
  }

  emit_jump_insn (gen_casesi0 (operands[0], operands[3]));
  DONE;
}")

(define_insn "casesi0"
  [(set (pc) (mem:SI (plus:SI
		       (mult:SI (match_operand:SI 0 "register_operand" "r")
				(const_int 4))
		       (label_ref (match_operand 1 "" "")))))
   (clobber (match_scratch:SI 2 "=&r"))
  ]
  ""
  "ldk.l\t$cc,%l1\;ashl.l\t%2,%0,2\;add.l\t%2,%2,$cc\;jmpi\t%2"
  )

;; -------------------------------------------------------------------------
;; Atomic exchange instruction
;; -------------------------------------------------------------------------

(define_insn "atomic_exchangesi"
  [(set (match_operand:SI 0 "register_operand" "=&r,r")	;; output
	(match_operand:SI 1 "memory_operand" "+BW,A"))	;; memory
   (set (match_dup 1)
	(unspec:SI
	  [(match_operand:SI 2 "register_operand" "0,0")	;; input
	   (match_operand:SI 3 "const_int_operand")]		;; model
	  UNSPEC_XCHG))]
  ""
  "@
   exi.l  %0,%1
   exa.l  %0,%1")

(define_insn "atomic_exchangehi"
  [(set (match_operand:HI 0 "register_operand" "=&r,r")	;; output
	(match_operand:HI 1 "memory_operand" "+BW,A"))	;; memory
   (set (match_dup 1)
	(unspec:HI
	  [(match_operand:HI 2 "register_operand" "0,0")	;; input
	   (match_operand:HI 3 "const_int_operand")]		;; model
	  UNSPEC_XCHG))]
  ""
  "@
   exi.s  %0,%1
   exa.s  %0,%1")

(define_insn "atomic_exchangeqi"
  [(set (match_operand:QI 0 "register_operand" "=&r,r")	;; output
	(match_operand:QI 1 "memory_operand" "+BW,A"))	;; memory
   (set (match_dup 1)
	(unspec:QI
	  [(match_operand:QI 2 "register_operand" "0,0")	;; input
	   (match_operand:QI 3 "const_int_operand")]		;; model
	  UNSPEC_XCHG))]
  ""
  "@
   exi.b  %0,%1
   exa.b  %0,%1")

;; -------------------------------------------------------------------------
;; String instructions
;; -------------------------------------------------------------------------

(define_insn "cmpstrsi"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
        (compare:SI (match_operand:BLK 1 "memory_operand" "W,BW")
                    (match_operand:BLK 2 "memory_operand" "W,BW")))
   (clobber (match_operand:SI 3))
   ]
  ""
  "strcmp.%d3 %0,%b1,%b2"
)

(define_insn "movstr"
[(set (match_operand:BLK 1 "memory_operand" "=W")
      (match_operand:BLK 2 "memory_operand" "W"))
      (use (match_operand:SI 0))
      (clobber (match_dup 0))
 ]
"0"
"stpcpy %b1,%b2 # %0 %b1 %b2"
)

(define_insn "movmemsi"
  [(set (match_operand:BLK 0 "memory_operand" "=W,BW")
        (match_operand:BLK 1 "memory_operand" "W,BW"))
        (use (match_operand:SI 2 "ft32_imm_operand" "KA,KA"))
        (use (match_operand:SI 3))
   ]
  ""
  "memcpy.%d3 %b0,%b1,%2 "
)

(define_insn "setmemsi"
  [(set (match_operand:BLK 0 "memory_operand" "=BW") (unspec:BLK [
     (use (match_operand:QI 2 "register_operand" "r"))
     (use (match_operand:SI 1 "ft32_imm_operand" "KA"))
   ] UNSPEC_SETMEM))
   (use (match_operand:SI 3))
   ]
  ""
  "memset.%d3 %b0,%2,%1"
)

(define_insn "strlensi"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand:BLK 1 "memory_operand" "W")
                    (match_operand:QI 2 "const_int_operand" "")
                    (match_operand:SI 3 "ft32_rimm_operand" "")]
                   UNSPEC_STRLEN))]
  ""
  "strlen.%d3 %0,%b1 # %2 %3"
)

;; -------------------------------------------------------------------------
;; Prologue & Epilogue
;; -------------------------------------------------------------------------

(define_expand "prologue"
  [(clobber (const_int 0))]
  ""
{
  extern void ft32_expand_prologue();
  ft32_expand_prologue ();
  DONE;
})


(define_expand "epilogue"
  [(return)]
  ""
{
  extern void ft32_expand_epilogue();
  ft32_expand_epilogue ();
  DONE;
})

(define_insn "link"
  [
;;   (set (mem:SI (pre_dec:SI (reg:SI SP_REG)))
;;        (reg:SI FP_REG))
   (set (match_operand:SI 0)
        (reg:SI SP_REG))
   (set (reg:SI SP_REG)
        (plus:SI (reg:SI SP_REG)
                 (match_operand:SI 1 "general_operand" "L")))]
  ""
  "link   %0,%m1"
)

(define_insn "unlink"
  [(set (reg:SI FP_REG)
        (mem:SI (reg:SI FP_REG)))
   (set (reg:SI SP_REG)
        (plus:SI (reg:SI FP_REG)
                 (const_int 4)))]
  ""
  "unlink $r29"
)

(define_insn "returner"
  [(return)]
  "reload_completed"
  "return")

(define_insn "pretend_returner"
  [(set (reg:SI SP_REG)
        (plus:SI (reg:SI SP_REG)
                 (match_operand:SI 0)))
   (return)]
  "reload_completed"
  "pop.l  $cc\;add.l  $sp,$sp,%0\;jmpi   $cc")

(define_insn "returner24"
  [
  (set (reg:SI SP_REG)
          (plus:SI
           (reg:SI SP_REG)
           (const_int 24)))
  (return)]
  ""
  "jmp    __epilog24")
