;; Machine description of the Lattice Mico32 architecture for GNU C compiler.
;; Contributed by Jon Beniston <jon@beniston.com>

;; Copyright (C) 2009-2018 Free Software Foundation, 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/>.  

;; Include predicate and constraint definitions
(include "predicates.md")
(include "constraints.md")


;; Register numbers
(define_constants
  [(RA_REGNUM           29)	; return address register.
  ]
)

;; LM32 specific volatile operations
(define_constants
  [(UNSPECV_BLOCKAGE    1)]     ; prevent scheduling across pro/epilog boundaries
)

;; LM32 specific operations
(define_constants
  [(UNSPEC_GOT          2)
   (UNSPEC_GOTOFF_HI16  3)
   (UNSPEC_GOTOFF_LO16  4)]     
)

;; --------------------------------- 
;;      instruction types
;; ---------------------------------

(define_attr "type"
  "unknown,load,store,arith,compare,shift,multiply,divide,call,icall,ubranch,uibranch,cbranch"
  (const_string "unknown"))
  
;; ---------------------------------
;;      instruction lengths
;; ---------------------------------
  
; All instructions are 4 bytes
; Except for branches that are out of range, and have to be implemented
; as two instructions
(define_attr "length" "" 
        (cond [
                (eq_attr "type" "cbranch")
                (if_then_else
                        (lt (abs (minus (match_dup 2) (pc)))
                                (const_int 32768)
                        )
                        (const_int 4)
                        (const_int 8)               
                )
              ] 
        (const_int 4))
)
                    
;; ---------------------------------
;;           scheduling 
;; ---------------------------------

(define_automaton "lm32")

(define_cpu_unit "x" "lm32")
(define_cpu_unit "m" "lm32")
(define_cpu_unit "w" "lm32")

(define_insn_reservation "singlecycle" 1
  (eq_attr "type" "store,arith,call,icall,ubranch,uibranch,cbranch")
 "x")

(define_insn_reservation "twocycle" 2
  (eq_attr "type" "compare,shift,divide")
 "x,m") 

(define_insn_reservation "threecycle" 3
  (eq_attr "type" "load,multiply")
 "x,m,w")

;; ---------------------------------
;;               mov 
;; ---------------------------------

(define_expand "movqi"
  [(set (match_operand:QI 0 "general_operand" "")
        (match_operand:QI 1 "general_operand" ""))]
  ""
  "
{
  if (can_create_pseudo_p ())
    {
      if (GET_CODE (operand0) == MEM)
        {
          /* Source operand for store must be in a register.  */
          operands[1] = force_reg (QImode, operands[1]);
        }
    }
}")

(define_expand "movhi"
  [(set (match_operand:HI 0 "general_operand" "")
        (match_operand:HI 1 "general_operand" ""))]
  ""
  "
{
  if (can_create_pseudo_p ())
    {
      if (GET_CODE (operands[0]) == MEM)
        {
          /* Source operand for store must be in a register.  */
          operands[1] = force_reg (HImode, operands[1]);
        }
    }
}")

(define_expand "movsi"
  [(set (match_operand:SI 0 "general_operand" "")
        (match_operand:SI 1 "general_operand" ""))]
  ""
  "
{
  if (can_create_pseudo_p ())
    {
      if (GET_CODE (operands[0]) == MEM 
	  || (GET_CODE (operands[0]) == SUBREG 
	      && GET_CODE (SUBREG_REG (operands[0])) == MEM))
        {
          /* Source operand for store must be in a register.  */
          operands[1] = force_reg (SImode, operands[1]);
        }
    }

  if (flag_pic && symbolic_operand (operands[1], SImode)) 
    {
      if (GET_CODE (operands[1]) == LABEL_REF
          || (GET_CODE (operands[1]) == SYMBOL_REF 
              && SYMBOL_REF_LOCAL_P (operands[1])
              && !SYMBOL_REF_WEAK (operands[1])))
        {
          emit_insn (gen_movsi_gotoff_hi16 (operands[0], operands[1]));
          emit_insn (gen_addsi3 (operands[0], 
                                 operands[0], 
                                 pic_offset_table_rtx));
          emit_insn (gen_movsi_gotoff_lo16 (operands[0], 
                                            operands[0], 
                                            operands[1]));
        } 
      else 
        emit_insn (gen_movsi_got (operands[0], operands[1]));
      crtl->uses_pic_offset_table = 1;
      DONE;
    }         
  else if (flag_pic && GET_CODE (operands[1]) == CONST) 
    {
      rtx op = XEXP (operands[1], 0);
      if (GET_CODE (op) == PLUS)
        {
          rtx arg0 = XEXP (op, 0);
          rtx arg1 = XEXP (op, 1);
          if (GET_CODE (arg0) == LABEL_REF
              || (GET_CODE (arg0) == SYMBOL_REF 
                  && SYMBOL_REF_LOCAL_P (arg0)
                  && !SYMBOL_REF_WEAK (arg0)))
            {
              emit_insn (gen_movsi_gotoff_hi16 (operands[0], arg0));
              emit_insn (gen_addsi3 (operands[0], 
                                     operands[0], 
                                     pic_offset_table_rtx));
              emit_insn (gen_movsi_gotoff_lo16 (operands[0], 
                                                operands[0], 
                                                arg0));
            } 
          else 
            emit_insn (gen_movsi_got (operands[0], arg0));
          emit_insn (gen_addsi3 (operands[0], operands[0], arg1));
          crtl->uses_pic_offset_table = 1;
          DONE;
        }     
    }
  else if (!flag_pic && reloc_operand (operands[1], GET_MODE (operands[1]))) 
    {
      emit_insn (gen_rtx_SET (operands[0], gen_rtx_HIGH (SImode, operands[1])));
      emit_insn (gen_rtx_SET (operands[0], gen_rtx_LO_SUM (SImode, operands[0],
							   operands[1])));
      DONE;
    }  
  else if (GET_CODE (operands[1]) == CONST_INT)
    {
      if (!(satisfies_constraint_K (operands[1]) 
          || satisfies_constraint_L (operands[1])
          || satisfies_constraint_U (operands[1])))      
        {
          emit_insn (gen_movsi_insn (operands[0], 
                                     GEN_INT (INTVAL (operands[1]) & ~0xffff)));
          emit_insn (gen_iorsi3 (operands[0], 
                                 operands[0], 
                                 GEN_INT (INTVAL (operands[1]) & 0xffff)));
          DONE;
        }
    }    
}")

(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 (!lm32_expand_block_move (operands))
    FAIL;
  DONE;
})

;; ---------------------------------
;;        load/stores/moves 
;; ---------------------------------

(define_insn "movsi_got"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand 1 "" "")] UNSPEC_GOT))]
  "flag_pic"
  "lw       %0, (gp+got(%1))"
  [(set_attr "type" "load")]
)

(define_insn "movsi_gotoff_hi16"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(match_operand 1 "" "")] UNSPEC_GOTOFF_HI16))]
  "flag_pic"
  "orhi     %0, r0, gotoffhi16(%1)"
  [(set_attr "type" "load")]
)

(define_insn "movsi_gotoff_lo16"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (unspec:SI [(plus:SI (match_operand:SI 1 "register_operand" "0")
                             (match_operand 2 "" ""))] UNSPEC_GOTOFF_LO16))]        
  "flag_pic"
  "addi     %0, %1, gotofflo16(%2)"
  [(set_attr "type" "arith")]
)
  
(define_insn "*movsi_lo_sum"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (lo_sum:SI (match_operand:SI 1 "register_operand" "0")
                   (match_operand:SI 2 "reloc_operand" "i")))]
  "!flag_pic"
  "ori      %0, %0, lo(%2)"
  [(set_attr "type" "arith")]
)

(define_insn "*movqi_insn"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=r,r,m,m,r")
        (match_operand:QI 1 "general_operand" "m,r,r,J,n"))]
  "lm32_move_ok (QImode, operands)"
  "@
   lbu      %0, %1
   or       %0, %1, r0
   sb       %0, %1
   sb       %0, r0
   addi     %0, r0, %1"
  [(set_attr "type" "load,arith,store,store,arith")]   
)
   
(define_insn "*movhi_insn"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=r,r,m,m,r,r")
        (match_operand:HI 1 "general_operand" "m,r,r,J,K,L"))]
  "lm32_move_ok (HImode, operands)"
  "@
   lhu      %0, %1
   or       %0, %1, r0
   sh       %0, %1
   sh       %0, r0
   addi     %0, r0, %1
   ori      %0, r0, %1"
  [(set_attr "type" "load,arith,store,store,arith,arith")]   
)

(define_insn "movsi_insn"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=r,r,m,m,r,r,r,r,r,r")
        (match_operand:SI 1 "general_operand" "m,r,r,J,K,L,U,S,Y,n"))]
  "lm32_move_ok (SImode, operands)"
  "@
   lw       %0, %1
   or       %0, %1, r0
   sw       %0, %1
   sw       %0, r0
   addi     %0, r0, %1
   ori      %0, r0, %1
   orhi     %0, r0, hi(%1)
   mva      %0, gp(%1)
   orhi     %0, r0, hi(%1)
   ori      %0, r0, lo(%1); orhi     %0, %0, hi(%1)"
  [(set_attr "type" "load,arith,store,store,arith,arith,arith,arith,arith,arith")]   
)

;; ---------------------------------
;;      sign and zero extension 
;; ---------------------------------

(define_insn "*extendqihi2"
  [(set (match_operand:HI 0 "register_operand" "=r,r")
        (sign_extend:HI (match_operand:QI 1 "nonimmediate_operand" "m,r")))]
  "TARGET_SIGN_EXTEND_ENABLED || (GET_CODE (operands[1]) != REG)"
  "@
   lb       %0, %1
   sextb    %0, %1"
  [(set_attr "type" "load,arith")]
)

(define_insn "zero_extendqihi2"
  [(set (match_operand:HI 0 "register_operand" "=r,r")
        (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "m,r")))]
  ""
  "@
   lbu      %0, %1
   andi     %0, %1, 0xff"
  [(set_attr "type" "load,arith")]  
)

(define_insn "*extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
        (sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "m,r")))]
  "TARGET_SIGN_EXTEND_ENABLED || (GET_CODE (operands[1]) != REG)"
  "@
   lb       %0, %1
   sextb    %0, %1"
  [(set_attr "type" "load,arith")]
)

(define_insn "zero_extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
        (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "m,r")))]
  ""
  "@
   lbu      %0, %1
   andi     %0, %1, 0xff"
  [(set_attr "type" "load,arith")]  
)

(define_insn "*extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
        (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "m,r")))]
  "TARGET_SIGN_EXTEND_ENABLED || (GET_CODE (operands[1]) != REG)"
  "@
   lh       %0, %1
   sexth    %0, %1"
  [(set_attr "type" "load,arith")]
)

(define_insn "zero_extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
        (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "m,r")))]
  ""
  "@
   lhu      %0, %1
   andi     %0, %1, 0xffff"
  [(set_attr "type" "load,arith")]  
)

;; ---------------------------------
;;             compare 
;; ---------------------------------

(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_or_int_operand")]))]
  ""
{
  lm32_expand_scc (operands);
  DONE;
})

(define_insn "*seq"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
        (eq:SI (match_operand:SI 1 "register_or_zero_operand" "%rJ,rJ")
               (match_operand:SI 2 "register_or_K_operand" "r,K")))]
  ""
  "@
   cmpe     %0, %z1, %2
   cmpei    %0, %z1, %2"
  [(set_attr "type" "compare")]
)

(define_insn "*sne"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
        (ne:SI (match_operand:SI 1 "register_or_zero_operand" "%rJ,rJ")
               (match_operand:SI 2 "register_or_K_operand" "r,K")))]
  ""
  "@
   cmpne    %0, %z1, %2
   cmpnei   %0, %z1, %2"
  [(set_attr "type" "compare")]
)

(define_insn "*sgt"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
        (gt:SI (match_operand:SI 1 "register_or_zero_operand" "rJ,rJ")
               (match_operand:SI 2 "register_or_K_operand" "r,K")))]
  ""
  "@
   cmpg     %0, %z1, %2
   cmpgi    %0, %z1, %2"
  [(set_attr "type" "compare")]
)

(define_insn "*sge"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
        (ge:SI (match_operand:SI 1 "register_or_zero_operand" "rJ,rJ")
               (match_operand:SI 2 "register_or_K_operand" "r,K")))]
  ""
  "@
   cmpge    %0, %z1, %2
   cmpgei   %0, %z1, %2"
  [(set_attr "type" "compare")]
)

(define_insn "*sgtu"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
        (gtu:SI (match_operand:SI 1 "register_or_zero_operand" "rJ,rJ")
                (match_operand:SI 2 "register_or_L_operand" "r,L")))]
  ""
  "@
   cmpgu    %0, %z1, %2
   cmpgui   %0, %z1, %2"
  [(set_attr "type" "compare")]
)

(define_insn "*sgeu"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
        (geu:SI (match_operand:SI 1 "register_or_zero_operand" "rJ,rJ")
                (match_operand:SI 2 "register_or_L_operand" "r,L")))]
  ""
  "@
   cmpgeu   %0, %z1, %2
   cmpgeui  %0, %z1, %2"
  [(set_attr "type" "compare")]
)

;; ---------------------------------
;;       unconditional branch
;; ---------------------------------

(define_insn "jump"
  [(set (pc) (label_ref (match_operand 0 "" "")))]
  ""
  "bi       %0"
  [(set_attr "type" "ubranch")]
)

(define_insn "indirect_jump"
  [(set (pc) (match_operand:SI 0 "register_operand" "r"))]
  ""
  "b        %0"
  [(set_attr "type" "uibranch")]
)

;; ---------------------------------
;;        conditional branch
;; ---------------------------------

(define_expand "cbranchsi4"
  [(set (pc)
   (if_then_else (match_operator 0 "comparison_operator" 
                  [(match_operand:SI 1 "register_operand")
		   (match_operand:SI 2 "nonmemory_operand")])
                 (label_ref (match_operand 3 "" ""))
                 (pc)))]
  ""
  "
{   
  lm32_expand_conditional_branch (operands);
  DONE;
}")

(define_insn "*beq"
  [(set (pc)
        (if_then_else (eq:SI (match_operand:SI 0 "register_or_zero_operand" "rJ")
                             (match_operand:SI 1 "register_or_zero_operand" "rJ"))
                      (label_ref (match_operand 2 "" ""))
                      (pc)))]
  ""
{
  return get_attr_length (insn) == 4
        ? "be     %z0,%z1,%2"
        : "bne    %z0,%z1,8\n\tbi     %2";
}  
  [(set_attr "type" "cbranch")])

(define_insn "*bne"
  [(set (pc)
        (if_then_else (ne:SI (match_operand:SI 0 "register_or_zero_operand" "rJ")
                             (match_operand:SI 1 "register_or_zero_operand" "rJ"))
                      (label_ref (match_operand 2 "" ""))
                      (pc)))]
  ""
{
  return get_attr_length (insn) == 4
        ? "bne    %z0,%z1,%2"
        : "be     %z0,%z1,8\n\tbi     %2";
}  
  [(set_attr "type" "cbranch")])

(define_insn "*bgt"
  [(set (pc)
        (if_then_else (gt:SI (match_operand:SI 0 "register_or_zero_operand" "rJ")
                             (match_operand:SI 1 "register_or_zero_operand" "rJ"))
                      (label_ref (match_operand 2 "" ""))
                      (pc)))]
  ""
{
  return get_attr_length (insn) == 4
        ? "bg     %z0,%z1,%2"
        : "bge    %z1,%z0,8\n\tbi     %2";
}  
  [(set_attr "type" "cbranch")])

(define_insn "*bge"
  [(set (pc)
        (if_then_else (ge:SI (match_operand:SI 0 "register_or_zero_operand" "rJ")
                             (match_operand:SI 1 "register_or_zero_operand" "rJ"))
                      (label_ref (match_operand 2 "" ""))
                      (pc)))]
  ""
{
  return get_attr_length (insn) == 4
        ? "bge    %z0,%z1,%2"
        : "bg     %z1,%z0,8\n\tbi     %2";
}  
  [(set_attr "type" "cbranch")])

(define_insn "*bgtu"
  [(set (pc)
        (if_then_else (gtu:SI (match_operand:SI 0 "register_or_zero_operand" "rJ")
                              (match_operand:SI 1 "register_or_zero_operand" "rJ"))
                      (label_ref (match_operand 2 "" ""))
                      (pc)))]
  ""
{
  return get_attr_length (insn) == 4
        ? "bgu    %z0,%z1,%2"
        : "bgeu   %z1,%z0,8\n\tbi     %2";
}  
  [(set_attr "type" "cbranch")])

(define_insn "*bgeu"
  [(set (pc)
        (if_then_else (geu:SI (match_operand:SI 0 "register_or_zero_operand" "rJ")
                              (match_operand:SI 1 "register_or_zero_operand" "rJ"))
                      (label_ref (match_operand 2 "" ""))
                      (pc)))]
  ""
{
  return get_attr_length (insn) == 4
        ? "bgeu   %z0,%z1,%2"
        : "bgu    %z1,%z0,8\n\tbi     %2";
}  
  [(set_attr "type" "cbranch")])

;; ---------------------------------
;;               call 
;; ---------------------------------

(define_expand "call"
  [(parallel [(call (match_operand 0 "" "")
                    (match_operand 1 "" ""))
              (clobber (reg:SI RA_REGNUM))
             ])]
  ""
  "
{
  rtx addr = XEXP (operands[0], 0);
  if (!CONSTANT_ADDRESS_P (addr))
    XEXP (operands[0], 0) = force_reg (Pmode, addr);
}")

(define_insn "*call"
  [(call (mem:SI (match_operand:SI 0 "call_operand" "r,s"))
         (match_operand 1 "" ""))
   (clobber (reg:SI RA_REGNUM))]
  ""
  "@
   call     %0
   calli    %0"
  [(set_attr "type" "call,icall")]  
)

(define_expand "call_value"
  [(parallel [(set (match_operand 0 "" "")
                   (call (match_operand 1 "" "")
                         (match_operand 2 "" "")))
              (clobber (reg:SI RA_REGNUM))
             ])]
  ""
  "
{
  rtx addr = XEXP (operands[1], 0);
  if (!CONSTANT_ADDRESS_P (addr))
    XEXP (operands[1], 0) = force_reg (Pmode, addr); 
}")

(define_insn "*call_value"
  [(set (match_operand 0 "register_operand" "=r,r")
        (call (mem:SI (match_operand:SI 1 "call_operand" "r,s"))
              (match_operand 2 "" "")))
   (clobber (reg:SI RA_REGNUM))]
  ""
  "@
   call     %1
   calli    %1"
  [(set_attr "type" "call,icall")]  
)

(define_insn "return_internal"
  [(use (match_operand:SI 0 "register_operand" "r"))
   (return)]
  ""
  "b        %0"
  [(set_attr "type" "uibranch")]  
)

(define_expand "return"
  [(return)]
  "lm32_can_use_return ()"
  ""
) 

(define_expand "simple_return"
  [(simple_return)]
  ""
  ""
) 

(define_insn "*return"
  [(return)]
  "reload_completed"
  "ret"
  [(set_attr "type" "uibranch")]  
) 

(define_insn "*simple_return"
  [(simple_return)]
  ""
  "ret"
  [(set_attr "type" "uibranch")]  
) 

;; ---------------------------------
;;       switch/case statements 
;; ---------------------------------
  
(define_expand "tablejump"
  [(set (pc) (match_operand 0 "register_operand" ""))
   (use (label_ref (match_operand 1 "" "")))]
  ""
  "
{
  rtx target = operands[0];
  if (flag_pic)
    {
      /* For PIC, the table entry is relative to the start of the table.  */
      rtx label = gen_reg_rtx (SImode);
      target = gen_reg_rtx (SImode);
      emit_move_insn (label, gen_rtx_LABEL_REF (SImode, operands[1]));
      emit_insn (gen_addsi3 (target, operands[0], label));
    }
  emit_jump_insn (gen_tablejumpsi (target, operands[1]));
  DONE;
}")

(define_insn "tablejumpsi"
  [(set (pc) (match_operand:SI 0 "register_operand" "r"))
   (use (label_ref (match_operand 1 "" "")))]
  ""
  "b        %0"
  [(set_attr "type" "ubranch")]  
)

;; ---------------------------------
;;            arithmetic 
;; ---------------------------------

(define_insn "addsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
        (plus:SI (match_operand:SI 1 "register_or_zero_operand" "%rJ,rJ")
                 (match_operand:SI 2 "register_or_K_operand" "r,K")))]
  ""
  "@
   add      %0, %z1, %2
   addi     %0, %z1, %2"
  [(set_attr "type" "arith")]  
)

(define_insn "subsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (minus:SI (match_operand:SI 1 "register_or_zero_operand" "rJ")
                  (match_operand:SI 2 "register_or_zero_operand" "rJ")))]
  ""
  "sub      %0, %z1, %z2"
  [(set_attr "type" "arith")]  
)

(define_insn "mulsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
        (mult:SI (match_operand:SI 1 "register_or_zero_operand" "%rJ,rJ")
                 (match_operand:SI 2 "register_or_K_operand" "r,K")))]
  "TARGET_MULTIPLY_ENABLED"
  "@
   mul      %0, %z1, %2
   muli     %0, %z1, %2"
  [(set_attr "type" "multiply")]
)

(define_insn "udivsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (udiv:SI (match_operand:SI 1 "register_or_zero_operand" "rJ")
                 (match_operand:SI 2 "register_operand" "r")))]
  "TARGET_DIVIDE_ENABLED"
  "divu     %0, %z1, %2"
  [(set_attr "type" "divide")]
)

(define_insn "umodsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (umod:SI (match_operand:SI 1 "register_or_zero_operand" "rJ")
                 (match_operand:SI 2 "register_operand" "r")))]
  "TARGET_DIVIDE_ENABLED"
  "modu     %0, %z1, %2"
  [(set_attr "type" "divide")]
)

;; ---------------------------------
;;      negation and inversion 
;; ---------------------------------
               
(define_insn "negsi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (neg:SI (match_operand:SI 1 "register_or_zero_operand" "rJ")))]
  ""
  "sub      %0, r0, %z1"
  [(set_attr "type" "arith")]
)      

(define_insn "one_cmplsi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (not:SI (match_operand:SI 1 "register_or_zero_operand" "rJ")))]
  ""
  "not      %0, %z1"
  [(set_attr "type" "arith")]
)

;; ---------------------------------
;;             logical 
;; ---------------------------------

(define_insn "andsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
        (and:SI (match_operand:SI 1 "register_or_zero_operand" "%rJ,rJ")
                (match_operand:SI 2 "register_or_L_operand" "r,L")))]
  ""
  "@
   and      %0, %z1, %2
   andi     %0, %z1, %2"
  [(set_attr "type" "arith")]
)

(define_insn "iorsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
        (ior:SI (match_operand:SI 1 "register_or_zero_operand" "%rJ,rJ")
                (match_operand:SI 2 "register_or_L_operand" "r,L")))]
  ""
  "@
   or       %0, %z1, %2
   ori      %0, %z1, %2"
  [(set_attr "type" "arith")]
)

(define_insn "xorsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
        (xor:SI (match_operand:SI 1 "register_or_zero_operand" "%rJ,rJ")
                (match_operand:SI 2 "register_or_L_operand" "r,L")))]
  ""
  "@
   xor      %0, %z1, %2
   xori     %0, %z1, %2"
  [(set_attr "type" "arith")]
)

(define_insn "*norsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(not:SI (ior:SI (match_operand:SI 1 "register_or_zero_operand" "%rJ,rJ")
			(match_operand:SI 2 "register_or_L_operand" "r,L"))))]
  ""
  "@ 
   nor      %0, %z1, %2
   nori     %0, %z1, %2"     		
  [(set_attr "type" "arith")]
)                

(define_insn "*xnorsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(not:SI (xor:SI (match_operand:SI 1 "register_or_zero_operand" "%rJ,rJ")
			(match_operand:SI 2 "register_or_L_operand" "r,L"))))]
  ""
  "@
   xnor     %0, %z1, %2
   xnori    %0, %z1, %2"     		
  [(set_attr "type" "arith")]
)                

;; ---------------------------------
;;              shifts 
;; ---------------------------------

(define_expand "ashlsi3"
  [(set (match_operand:SI 0 "register_operand" "")
        (ashift:SI (match_operand:SI 1 "register_or_zero_operand" "")
                   (match_operand:SI 2 "register_or_L_operand" "")))]
  ""
{
  if (!TARGET_BARREL_SHIFT_ENABLED)
    {
      if (!optimize_size 
          && satisfies_constraint_L (operands[2])
          && INTVAL (operands[2]) <= 8)
        {
          int i;
          int shifts = INTVAL (operands[2]);
          
          if (shifts == 0)
            emit_move_insn (operands[0], operands[1]);
          else
            emit_insn (gen_addsi3 (operands[0], operands[1], operands[1]));
          for (i = 1; i < shifts; i++) 
            emit_insn (gen_addsi3 (operands[0], operands[0], operands[0]));
          DONE;                  
        }
      else
        FAIL;
    }
})  

(define_insn "*ashlsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
        (ashift:SI (match_operand:SI 1 "register_or_zero_operand" "rJ,rJ")
                   (match_operand:SI 2 "register_or_L_operand" "r,L")))]
  "TARGET_BARREL_SHIFT_ENABLED"
  "@ 
   sl       %0, %z1, %2
   sli      %0, %z1, %2"
  [(set_attr "type" "shift")]
)

(define_expand "ashrsi3"
  [(set (match_operand:SI 0 "register_operand" "")
        (ashiftrt:SI (match_operand:SI 1 "register_or_zero_operand" "")
                     (match_operand:SI 2 "register_or_L_operand" "")))]
  ""
{
  if (!TARGET_BARREL_SHIFT_ENABLED)
    {
      if (!optimize_size 
          && satisfies_constraint_L (operands[2])
          && INTVAL (operands[2]) <= 8)
        {
          int i;
          int shifts = INTVAL (operands[2]);
          rtx one = GEN_INT (1);
          
          if (shifts == 0)
            emit_move_insn (operands[0], operands[1]);
          else
            emit_insn (gen_ashrsi3_1bit (operands[0], operands[1], one));
          for (i = 1; i < shifts; i++) 
            emit_insn (gen_ashrsi3_1bit (operands[0], operands[0], one));
          DONE;                  
        }
      else
        FAIL;
    }
})  
                       
(define_insn "*ashrsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
        (ashiftrt:SI (match_operand:SI 1 "register_or_zero_operand" "rJ,rJ")
                     (match_operand:SI 2 "register_or_L_operand" "r,L")))]
  "TARGET_BARREL_SHIFT_ENABLED"
  "@
   sr       %0, %z1, %2
   sri      %0, %z1, %2"
  [(set_attr "type" "shift")]
)

(define_insn "ashrsi3_1bit"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (ashiftrt:SI (match_operand:SI 1 "register_or_zero_operand" "rJ")
                     (match_operand:SI 2 "constant_M_operand" "M")))]
  "!TARGET_BARREL_SHIFT_ENABLED"
  "sri      %0, %z1, %2"
  [(set_attr "type" "shift")]
)

(define_expand "lshrsi3"
  [(set (match_operand:SI 0 "register_operand" "")
        (lshiftrt:SI (match_operand:SI 1 "register_or_zero_operand" "")
                     (match_operand:SI 2 "register_or_L_operand" "")))]
  ""
{
  if (!TARGET_BARREL_SHIFT_ENABLED)
    {
      if (!optimize_size 
          && satisfies_constraint_L (operands[2])
          && INTVAL (operands[2]) <= 8)
        {
          int i;
          int shifts = INTVAL (operands[2]);
          rtx one = GEN_INT (1);
          
          if (shifts == 0)
            emit_move_insn (operands[0], operands[1]);
          else
            emit_insn (gen_lshrsi3_1bit (operands[0], operands[1], one));
          for (i = 1; i < shifts; i++) 
            emit_insn (gen_lshrsi3_1bit (operands[0], operands[0], one));
          DONE;                  
        }
      else
        FAIL;
    }
})  

(define_insn "*lshrsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
        (lshiftrt:SI (match_operand:SI 1 "register_or_zero_operand" "rJ,rJ")
                     (match_operand:SI 2 "register_or_L_operand" "r,L")))]
  "TARGET_BARREL_SHIFT_ENABLED"
  "@ 
   sru      %0, %z1, %2
   srui     %0, %z1, %2"
  [(set_attr "type" "shift")]   
)

(define_insn "lshrsi3_1bit"
  [(set (match_operand:SI 0 "register_operand" "=r")
        (lshiftrt:SI (match_operand:SI 1 "register_or_zero_operand" "rJ")
                     (match_operand:SI 2 "constant_M_operand" "M")))]
  "!TARGET_BARREL_SHIFT_ENABLED"
  "srui     %0, %z1, %2"
  [(set_attr "type" "shift")]   
)

;; ---------------------------------
;;     function entry / exit 
;; ---------------------------------

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

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

;; ---------------------------------
;;              nop 
;; ---------------------------------

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

;; ---------------------------------
;;             blockage 
;; ---------------------------------

;; used to stop the scheduler from 
;; scheduling code across certain boundaries

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