;; GCC machine description for CR16.
;; Copyright (C) 2012-2021 Free Software Foundation, Inc.
;; Contributed by KPIT Cummins Infosystems Limited.

;; 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
  [(SP_REGNUM 15); Stack pointer
   (RA_REGNUM 14); Return address
  ]
)

;; Predicates & Constraints
(include "predicates.md")
(include "constraints.md")

;; UNSPEC usage
(define_constants
  [(UNSPEC_PIC_ADDR             0)
   (UNSPEC_PIC_LOAD_ADDR        1)
   (UNSPEC_LIBRARY_OFFSET       2)
   (UNSPEC_SH_LIB_PUSH_R12      3)
   (UNSPEC_SH_LIB_POP_R12       4)
   (UNSPEC_RETURN_ADDR          5)
  ]
)

;; Attributes
(define_attr "length" "" (const_int 2))

(define_asm_attributes
  [(set_attr "length" "2")]
)

;;  Mode Macro Definitions
(define_mode_iterator CR16IM [QI HI SI])
(define_mode_iterator LONG   [SI SF])
(define_mode_iterator ALLMTD [QI HI SI SF DI DF])
(define_mode_iterator DOUBLE [DI DF])
(define_mode_iterator SHORT  [QI HI])
(define_mode_attr tIsa       [(QI "b") (HI "w") (SI "d") (SF "d")])
(define_mode_attr lImmArith  [(QI "4") (HI "4") (SI "6") (SF "6")])
(define_mode_attr lImmArithD [(QI "4") (HI "4") (SI "6") (SF "6") (DI "12") (DF "12")])
(define_mode_attr iF         [(QI "i") (HI "i") (SI "i") (SF "F")])
(define_mode_attr iFD        [(DI "i") (DF "F")])
(define_mode_attr LL         [(QI "L") (HI "L")])
(define_mode_attr shImmBits  [(QI "3") (HI "4") (SI "5")])

; In QI mode we push 2 bytes instead of 1 byte.
(define_mode_attr pushCnstr [(QI "X") (HI "<") (SI "<") (SF "<") (DI "<") (DF "<")])

; tpush will be used to generate the 'number of registers to push' in the 
; push instruction.
(define_mode_attr tpush [(QI "1") (HI "1") (SI "2") (SF "2") (DI "4") (DF "4")])

;;  Code Macro Definitions
(define_code_attr  sIsa    [(sign_extend "")  (zero_extend "u")])
(define_code_attr  sPat    [(sign_extend "s") (zero_extend "u")])
(define_code_attr  szPat   [(sign_extend "")  (zero_extend "zero_")])
(define_code_attr  szIsa   [(sign_extend "x") (zero_extend "z")])

(define_code_iterator sz_xtnd    [ sign_extend       zero_extend])
(define_code_iterator any_cond   [eq ne gt gtu lt ltu ge geu le leu])
(define_code_iterator plusminus  [plus minus])

(define_code_attr plusminus_insn [(plus "add") (minus "sub")])
(define_code_attr plusminus_flag [(plus "PLUS") (minus "MINUS")])
(define_code_attr comm 		 [(plus "%") (minus "")])

(define_code_iterator any_logic  [and ior xor])
(define_code_attr logic 	 [(and "and") (ior "or") (xor "xor")])
(define_code_attr any_logic_insn [(and "and") (ior "ior") (xor "xor")])
(define_code_attr any_logic_flag [(and "AND") (ior "IOR") (xor "XOR")])

(define_mode_iterator QH 	 [QI HI])
(define_mode_attr qh 		 [(QI "qi") (HI "hi")])
(define_mode_attr QHsz 		 [(QI "2,2,2") (HI "2,2,4")])
(define_mode_attr QHsuffix 	 [(QI "b") (HI "w")])


;;  Function Prologue and Epilogue
(define_expand "prologue"
  [(const_int 0)]
  ""
  {
    cr16_expand_prologue ();
    DONE;
  }
)

(define_insn "push_for_prologue"
  [(set (reg:SI SP_REGNUM)
	(minus:SI (reg:SI SP_REGNUM)
		  (match_operand:SI 0 "immediate_operand" "i")))]
  "reload_completed"
  {
    return cr16_prepare_push_pop_string (0);
  }
  [(set_attr "length" "4")]
)

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

(define_insn "pop_and_popret_return"
  [(set (reg:SI SP_REGNUM)
	(plus:SI (reg:SI SP_REGNUM)
		 (match_operand:SI 0 "immediate_operand" "i")))
   (use (reg:SI RA_REGNUM))
   (return)]
  "reload_completed"
  {
    return cr16_prepare_push_pop_string (1);
  }
  [(set_attr "length" "4")]
)

(define_insn "popret_RA_return"
  [(use (reg:SI RA_REGNUM))
   (return)]
  "reload_completed"
  "popret\tra"
  [(set_attr "length" "2")]
)

;; Arithmetic Instruction  Patterns

;; Addition-Subtraction "adddi3/subdi3" insns.
(define_insn "<plusminus_insn>di3"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(plusminus:DI (match_operand:DI 1 "register_operand" "<comm>0")
		      (match_operand:DI 2 "register_operand" "r")))]
  ""
  {
    return cr16_emit_add_sub_di (operands, <plusminus_flag>);
  })

(define_insn "addsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r,r,r,r")
	(plus:SI (match_operand:SI 1 "register_operand" "%0,0,0,0,0")
		 (match_operand:SI 2 "reg_si_int_operand" "r,M,N,O,i")))]
  ""
  "addd\t%2, %0"
  [(set_attr "length" "2,2,4,4,6")]
)

;; Addition-Subtraction "addhi3/subhi3" insns.
(define_insn "<plusminus_insn>hi3"
  [(set (match_operand:HI 0 "register_operand" "=c,c,c")
	(plusminus:HI (match_operand:HI 1 "register_operand" "<comm>0,0,0")
		      (match_operand:HI 2 "reg_hi_int_operand" "c,M,N")))]
  ""
  "<plusminus_insn>w\t%2, %0"
  [(set_attr "length" "2,2,4")]
)

;; Addition-Subtraction "addqi3/subqi3" insns.
(define_insn "<plusminus_insn>qi3"
  [(set (match_operand:QI 0 "register_operand" "=c,c")
	(plusminus:QI (match_operand:QI 1 "register_operand" "<comm>0,0")
		      (match_operand:QI 2 "reg_qi_int_operand" "c,M")))]
  ""
  "<plusminus_insn>b\t%2, %0"
  [(set_attr "length" "2,2")]
)

;;  Subtract Instruction
(define_insn "subsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(minus:SI (match_operand:SI 1 "register_operand" "0,0")
		  (match_operand:SI 2 "reg_si_int_operand" "r,i")))]
  ""
  "subd\t%2, %0"
  [(set_attr "length" "4,6")]
)

;;  Multiply and Accumulate Instructions "smachisi3/umachisi3"
(define_insn "<sPat>maddhisi4"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(plus:SI
	(mult:SI (sz_xtnd:SI (match_operand:HI 1 "register_operand" "r"))
		 (sz_xtnd:SI (match_operand:HI 2 "register_operand" "r")))
	(match_operand:SI 3 "register_operand" "0")))]
  "TARGET_MAC"
  "mac<sPat>w\t%1, %2, %0"
  [(set_attr "length" "2")]
)

;;  Multiply Instructions
(define_insn "mulhi3"
  [(set (match_operand:HI 0 "register_operand" "=c,c,c")
	(mult:HI (match_operand:HI 1 "register_operand" "%0,0,0")
		 (match_operand:HI 2 "reg_or_int_operand" "c,M,N")))]
  ""
  "mulw\t%2, %0"
  [(set_attr "length" "2,2,4")]
)

(define_insn "mulqihi3"
  [(set (match_operand:HI 0 "register_operand" "=c")
	(mult:HI (sign_extend:HI (match_operand:QI 1 "register_operand" "%0"))
		 (sign_extend:HI (match_operand:QI 2 "register_operand" "c"))))]
  ""
  "mulsb\t%2, %0"
  [(set_attr "length" "2")]
)

;;  Bit Set/Clear Instructions
(define_expand "insv"
  [(set (zero_extract (match_operand 0 "memory_operand" "")
		      (match_operand 1 "immediate_operand" "")
		      (match_operand 2 "immediate_operand" ""))
	(match_operand 3 "immediate_operand" ""))]
  "TARGET_BIT_OPS"
  {
    if (INTVAL (operands[1]) != 1)
      FAIL;
    if (INTVAL (operands[2]) < 0 || INTVAL (operands[2]) > 15)
      FAIL;
    if (INTVAL (operands[3]) == 1)
      {
	if (GET_MODE (operands[0]) == QImode)
	  {
	    emit_insn (gen_set_bitqi (operands[0], operands[2]));
	    DONE;
	  }
	else if (GET_MODE (operands[0]) == HImode)
	  {
	    emit_insn (gen_set_bithi (operands[0], operands[2]));
	    DONE;
	  }
      }
    if (INTVAL (operands[3]) == 0)
      {
	if (GET_MODE (operands[0]) == QImode)
	  {
	    emit_insn (gen_clr_bitqi (operands[0], operands[2]));
	    DONE;
	  }
	else if (GET_MODE (operands[0]) == HImode)
	  {
	    emit_insn (gen_clr_bithi (operands[0], operands[2]));
	    DONE;
	  }
      }
  }
)

(define_insn "set_bit<mode>"
  [(set (zero_extract:SHORT (match_operand:SHORT 0 "memory_operand" "+m")
			    (const_int 1)
			    (match_operand 1 "immediate_operand" "i"))
	(const_int 1))]
  "TARGET_BIT_OPS"
  "sbit<tIsa>\t%1,%0"
  [(set_attr "length" "2")]
)

(define_insn "clr_bit<mode>"
  [(set (zero_extract:SHORT (match_operand:SHORT 0 "memory_operand" "+m")
			    (const_int 1)
			    (match_operand 1 "immediate_operand" "i"))
	(const_int 0))]
  "TARGET_BIT_OPS"
  "cbit<tIsa>\t%1,%0"
  [(set_attr "length" "2")]
)

(define_insn "set_bit<mode>_mem"
  [(set (match_operand:SHORT 0 "bit_operand" "=m")
	(ior:SHORT (match_dup 0)
		   (match_operand:SHORT 1 "one_bit_operand" "i"))
  )]
  "TARGET_BIT_OPS"
  "sbit<tIsa>\t$%s1,%0"
  [(set_attr "length" "2")]
)

(define_insn "clear_bit<mode>_mem"
  [(set (match_operand:SHORT 0 "bit_operand" "=m")
	(and:SHORT (match_dup 0)
		   (match_operand:SHORT 1 "rev_one_bit_operand" "i"))
  )]
  "TARGET_BIT_OPS"
  "cbit<tIsa>\t$%r1,%0"
  [(set_attr "length" "2")]
)

;;  Logical Instructions - and/ior/xor "anddi3/iordi3/xordi3"
(define_insn "<any_logic_insn>di3"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(any_logic:DI (match_operand:DI 1 "register_operand" "%0")
		      (match_operand:DI 2 "register_operand" "r")))]
  ""
  {
    return cr16_emit_logical_di (operands, <any_logic_flag>);
  })

; Logical and/ior/xor "andsi3/iorsi3/xorsi3"
(define_insn "<any_logic_insn>si3"
  [(set (match_operand:SI 0 "register_operand" "=r,r,r,r")
	(any_logic:SI (match_operand:SI 1 "register_operand" "%0,0,0,0")
		      (match_operand:SI 2 "reg_si_int_operand" "r,M,N,i")))]
  ""
  "<logic>d\t%2, %0"
  [(set_attr "length" "2,2,4,6")]
)

; Logical and/ior/xor in HImode "andhi3/iorhi3/xorhi3"
; Logical and/ior/xor in QImode "andqi3/iorqi3/xorqi3"
(define_insn "<any_logic_insn><qh>3"
  [(set (match_operand:QH 0 "register_operand" "=c,c,c")
	(any_logic:QH (match_operand:QH 1 "register_operand" "%0,0,0")
		      (match_operand:QH 2 "reg_hi_int_operand" "c,M,N")))]
  ""
  "<logic><QHsuffix>\t%2, %0"
  [(set_attr "length" "<QHsz>")]
)

;;  Sign and Zero Extend Instructions
(define_insn "<szPat>extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(sz_xtnd:SI (match_operand:HI 1 "register_operand" "r")))]
  ""
  "mov<szIsa>w\t%1, %0"
  [(set_attr "length" "4")]
)

(define_insn "<szPat>extendqihi2"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(sz_xtnd:HI (match_operand:QI 1 "register_operand" "r")))]
  ""
  "mov<szIsa>b\t%1, %0"
  [(set_attr "length" "4")]
)

;;  One's Complement
(define_insn "one_cmpldi2"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(not:DI (match_operand:DI 1 "register_operand" "0")))]
  ""
  {
    rtx xoperand ;
    int reg0 = REGNO (operands[0]);

    xoperand = gen_rtx_REG (SImode, reg0 + 2);
    output_asm_insn ("xord\t$-1, %0", operands);
    output_asm_insn ("xord\t$-1, %0", &xoperand);
    return "" ;
  }
  [(set_attr "length" "12")]
)

(define_insn "one_cmpl<mode>2"
  [(set (match_operand:CR16IM 0 "register_operand" "=r")
	(not:CR16IM (match_operand:CR16IM 1 "register_operand" "0")))]
  ""
  "xor<tIsa>\t$-1, %0"
  [(set_attr "length" "2")]
)

;;  Arithmetic Left and Right Shift Instructions
(define_insn "ashlqi3"
  [(set (match_operand:QI 0 "register_operand" "=c,c")
	(ashift:QI (match_operand:QI 1 "register_operand" "0,0")
		   (match_operand:QI 2 "nonmemory_operand" "c,I")))]
  ""
  "ashub\t%2, %0"
  [(set_attr "length" "2,2")]
)

(define_insn "ashlhi3"
  [(set (match_operand:HI 0 "register_operand" "=c,c")
	(ashift:HI (match_operand:HI 1 "register_operand" "0,0")
		   (match_operand:QI 2 "nonmemory_operand" "c,J")))]
  ""
  "ashuw\t%2, %0"
  [(set_attr "length" "2,2")]
)

(define_insn "ashlsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(ashift:SI (match_operand:SI 1 "register_operand" "0,0")
		   (match_operand:QI 2 "nonmemory_operand" "r,K")))]
  ""
  "ashud\t%2, %0"
  [(set_attr "length" "2,2")]
)

(define_expand "ashr<mode>3"
  [(set (match_operand:CR16IM 0 "register_operand" "")
	(ashiftrt:CR16IM (match_operand:CR16IM 1 "register_operand" "")
			 (match_operand:QI 2 "nonmemory_operand" "")))]
  ""
  {
    if (GET_CODE (operands[2]) == CONST_INT)
      {
	/* If the constant is not in range, try placing it in a reg */
	if (!UNSIGNED_INT_FITS_N_BITS(INTVAL (operands[2]),<shImmBits>))
	operands[2] = copy_to_mode_reg(QImode, operands[2]);
      }

    if (GET_CODE (operands[2]) != CONST_INT)
      operands[2] = gen_rtx_NEG (QImode, negate_rtx (QImode, operands[2]));
  }
)

(define_insn "ashrqi3_imm_insn"
  [(set (match_operand:QI 0 "register_operand" "=c")
	(ashiftrt:QI (match_operand:QI 1 "register_operand" "0")
		     (match_operand:QI 2 "shift_qi_imm_operand" "i")))]
  ""
  "ashub\t$%n2, %0"
  [(set_attr "length" "2")]
)

(define_insn "ashrhi3_imm_insn"
  [(set (match_operand:HI 0 "register_operand" "=c")
	(ashiftrt:HI (match_operand:HI 1 "register_operand" "0")
		     (match_operand:QI 2 "shift_hi_imm_operand" "i")))]
  ""
  "ashuw\t$%n2, %0"
  [(set_attr "length" "2")]
)

(define_insn "ashrsi3_imm_insn"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ashiftrt:SI (match_operand:SI 1 "register_operand" "0")
		     (match_operand:QI 2 "shift_si_imm_operand" "i")))]
  ""
  "ashud\t$%n2, %0"
  [(set_attr "length" "2")]
)

(define_insn "ashrqi3_neg_insn"
  [(set (match_operand:QI 0 "register_operand" "=c")
	(ashiftrt:QI (match_operand:QI 1 "register_operand" "0")
		     (neg:QI (match_operand:QI 2 "register_operand" "c"))))]
  ""
  "ashub\t%2,%0"
  [(set_attr "length" "2")]
)

(define_insn "ashrhi3_neg_insn"
  [(set (match_operand:HI 0 "register_operand" "=c")
	(ashiftrt:HI (match_operand:HI 1 "register_operand" "0")
		     (neg:QI (match_operand:QI 2 "register_operand" "c"))))]
  ""
  "ashuw\t%2,%0"
  [(set_attr "length" "2")]
)

(define_insn "ashrdi3_neg_insn"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ashiftrt:SI (match_operand:SI 1 "register_operand" "0")
		     (neg:QI (match_operand:QI 2 "register_operand" "r"))))]
  ""
  "ashud\t%2,%0"
  [(set_attr "length" "2")]
)

(define_expand "lshr<mode>3"
  [(set (match_operand:CR16IM 0 "register_operand" "")
	(lshiftrt:CR16IM (match_operand:CR16IM 1 "register_operand" "")
			 (match_operand:QI 2 "reg_or_int_operand" "")))]
  ""
  {
    if (GET_CODE (operands[2]) == CONST_INT)
      {
	/* If the constant is not in range, try placing it in a reg */
	if (!UNSIGNED_INT_FITS_N_BITS(INTVAL (operands[2]),<shImmBits>))
	operands[2] = copy_to_mode_reg(QImode, operands[2]);
      }

	if (GET_CODE (operands[2]) != CONST_INT)
	operands[2] = gen_rtx_NEG (QImode, negate_rtx (QImode, operands[2]));
   }
)

(define_insn "lshrqi3_imm_insn"
  [(set (match_operand:QI 0 "register_operand" "=c")
	(lshiftrt:QI (match_operand:QI 1 "register_operand" "0")
		     (match_operand:QI 2 "shift_qi_operand" "Q")))]
  ""
  "lshb\t$%n2, %0"
  [(set_attr "length" "2")]
)

(define_insn "lshrhi3_imm_insn"
  [(set (match_operand:HI 0 "register_operand" "=c")
	(lshiftrt:HI (match_operand:HI 1 "register_operand" "0")
		     (match_operand:QI 2 "shift_hi_operand" "R")))]
  ""
  "lshw\t$%n2, %0"
  [(set_attr "length" "2")]
)

(define_insn "lshrsi3_imm_insn"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(lshiftrt:SI (match_operand:SI 1 "register_operand" "0")
		     (match_operand:QI 2 "shift_si_operand" "S")))]
  ""
  "lshd\t$%n2, %0"
  [(set_attr "length" "2")]
)

(define_insn "lshrqi3_neg_insn"
  [(set (match_operand:QI 0 "register_operand" "=c")
	(lshiftrt:QI (match_operand:QI 1 "register_operand" "0")
		     (neg:QI (match_operand:QI 2 "register_operand" "c"))))]
  ""
  "lshb\t%2,%0"
  [(set_attr "length" "2")]
)

(define_insn "lshrhi3_neg_insn"
  [(set (match_operand:HI 0 "register_operand" "=c")
	(lshiftrt:HI (match_operand:HI 1 "register_operand" "0")
		     (neg:QI (match_operand:QI 2 "register_operand" "c"))))]
  ""
  "lshw\t%2,%0"
  [(set_attr "length" "2")]
)

(define_insn "lshrsi3_neg_insn"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(lshiftrt:SI (match_operand:SI 1 "register_operand" "0")
		     (neg:QI (match_operand:QI 2 "register_operand" "r"))))]
  ""
  "lshd\t%2,%0"
  [(set_attr "length" "2")]
)

;;  Move Instructions

;; Move any non-immediate operand 0 to a general operand 1.
;; This applies only before starting the reload process
;; Operand 0 is not a register operand of type mode MODE
;; If Operand 0 is a push operand of type mode MODE
;; then, if Operand 1 is a non-SP register
;; then, Operand 1 = copy_to_mode_reg (<MODE>mode, Operand 1)
;; endif
;; else
;; if Operand 1 is either register or 4-bit immediate constant
;; then, Operand 1 = copy_to_mode_reg (<MODE>mode, Operand 1)
;; endif
;; endif
;;
;; What does copy_to_mode_reg (mode, rtx val) do?
;; Copy the value into new temp reg and return the reg where the
;; mode of the new reg is always mode MODE when value is constant
;;
;; Why should copy_to_mode_reg be called?
;; All sorts of move are nor supported by CR16. Therefore, 
;; when unsupported move is encountered, the additional instructions 
;; will be introduced for the purpose.
;;
;; A new move insn is inserted for Op 1 when one of the following
;; conditions is met.
;; Case 1:  Op 0 is push_operand
;;          Op 1 is SP register
;;
;; Case 2:  Op 0 is not push_operand
;;          Op 1 is neither register nor unsigned 4-bit immediate

(define_expand "mov<mode>"
  [(set (match_operand:ALLMTD 0 "nonimmediate_operand" "")
	(match_operand:ALLMTD 1 "general_operand" ""))]
  ""
  {
    if (!(reload_in_progress || reload_completed))
      {
	/* Only if Op0 is a register operand.  */
	if (!register_operand (operands[0], <MODE>mode))
	  {
	    if (push_operand (operands[0], <MODE>mode)) 
	      {
		/* Use copy_to_mode_reg only if the register needs 
		to be pushed is SP as CR16 does not support pushing SP.  */
		if (!nosp_reg_operand (operands[1], <MODE>mode))
		  operands[1] = copy_to_mode_reg (<MODE>mode, operands[1]);
	      }
	    else
	      {
		/* Use copy_to_mode_reg if op1 is not register operand
		   subject to conditions inside.  */
		if (!register_operand (operands[1], <MODE>mode))
		  {
		    /* CR16 does not support moving immediate to SI or SF 
		       type memory.  */
		    if (<MODE>mode == SImode || <MODE>mode == SFmode ||
			<MODE>mode == DImode || <MODE>mode == DFmode)
		      operands[1] = copy_to_mode_reg (<MODE>mode, operands[1]);
		    else
		      /* moving imm4 is supported by CR16 instruction.  */
		      if (!u4bits_operand (operands[1], <MODE>mode))
			operands[1] = copy_to_mode_reg (<MODE>mode, operands[1]);
		  }
	       }
	  }

	  /* If operand-1 is a symbol, convert it into a BRO or GOT Format.  */
	  if (flag_pic && ! legitimate_pic_operand_p (operands[1]))
	    {
	      operands[1] = legitimize_pic_address (operands[1], <MODE>mode, 0);
	    }
      }
  }
)

; ALLMT     : QI,HI,SI,SF
; pushCnstr : Push constraints 
;                QI : X
;             HI,SI,SF,DI,DF : <
; b         : All non-sp registers
; tpush     : Push count
;                QI,HI : 1
;                SI,SF : 2
;                DI,DF : 4
(define_insn "push<mode>_internal"
  [(set (match_operand:ALLMTD 0 "push_operand" "=<pushCnstr>")
	(match_operand:ALLMTD 1 "nosp_reg_operand" "b"))]
  ""
  "push\t$<tpush>,%p1"
  [(set_attr "length" "2")]
)

; (DI, DF) move
(define_insn "*mov<mode>_double"
  [(set (match_operand:DOUBLE 0 "nonimmediate_operand" "=r, r, r, m")
	(match_operand:DOUBLE 1 "general_operand" "r, <iFD>, m, r"))]
  "register_operand (operands[0], DImode) 
   || register_operand (operands[0], DFmode)
   || register_operand (operands[1], DImode)
   || register_operand (operands[1], DFmode)"
  {
    if (which_alternative == 0) {
      rtx xoperands[2];
      int reg0 = REGNO (operands[0]);
      int reg1 = REGNO (operands[1]);

      xoperands[0] = gen_rtx_REG (SImode, reg0 + 2);
      xoperands[1] = gen_rtx_REG (SImode, reg1 + 2);
      if ((reg1 + 2) != reg0)
	{
	  output_asm_insn ("movd\t%1, %0", operands);
	  output_asm_insn ("movd\t%1, %0", xoperands);
	}
      else
	{
	  output_asm_insn ("movd\t%1, %0", xoperands);
	  output_asm_insn ("movd\t%1, %0", operands);
	}}

    else if (which_alternative == 1) {
      rtx lo_operands[2];
      rtx hi_operands[2];

      lo_operands[0] = gen_rtx_REG (SImode, REGNO (operands[0]));
      hi_operands[0] = gen_rtx_REG (SImode, REGNO (operands[0]) + 2);
      lo_operands[1] = simplify_gen_subreg (SImode, operands[1],
		       VOIDmode == GET_MODE (operands[1])
		       ? DImode  : GET_MODE (operands[1]), 0);
      hi_operands[1] = simplify_gen_subreg (SImode, operands[1],
		       VOIDmode == GET_MODE (operands[1])
		       ? DImode  : GET_MODE (operands[1]), 4);
      output_asm_insn ("movd\t%1, %0", lo_operands);
      output_asm_insn ("movd\t%1, %0", hi_operands);}

    else if (which_alternative == 2) {
      rtx xoperands[2];
      int reg0 = REGNO (operands[0]), reg1 = -2;
      rtx addr;

	if (MEM_P (operands[1]))
	  addr = XEXP (operands[1], 0);
	else
	  addr = NULL_RTX;
	switch (GET_CODE (addr))
	  {
	    case REG:
	    case SUBREG:
	      reg1 = REGNO (addr);
	      break;
	    case PLUS:
	      switch (GET_CODE (XEXP (addr, 0))) {
		case REG:
		case SUBREG:
		  reg1 = REGNO (XEXP (addr, 0));
		  break;
		case PLUS:
		  reg1 = REGNO (XEXP (XEXP (addr, 0), 0));
		  break;
		default:
		  inform (DECL_SOURCE_LOCATION (cfun->decl), "unexpected expression; addr:");
		  debug_rtx (addr);
		  inform (DECL_SOURCE_LOCATION (cfun->decl), "operands[1]:");
		  debug_rtx (operands[1]);
		  inform (DECL_SOURCE_LOCATION (cfun->decl), "generated code might now work\n");
		  break;}
	      break;
	    default:
	      break;
	  }

	xoperands[0] = gen_rtx_REG (SImode, reg0 + 2);
	xoperands[1] = offset_address (operands[1], GEN_INT (4), 2);
	gcc_assert ((reg0 + 1) != reg1);
	if (reg0 != reg1  &&  (reg1 + 1) != reg0)
	  {
	    output_asm_insn ("loadd\t%1, %0", operands);
	    output_asm_insn ("loadd\t%1, %0", xoperands);
	  }
	else
	  {
	    output_asm_insn ("loadd\t%1, %0", xoperands);
	    output_asm_insn ("loadd\t%1, %0", operands);
	  }}
    else
      {
	rtx xoperands[2];
	xoperands[0] = offset_address (operands[0], GEN_INT (4), 2);
	xoperands[1] = gen_rtx_REG (SImode, REGNO (operands[1]) + 2);
	output_asm_insn ("stord\t%1, %0", operands);
   	output_asm_insn ("stord\t%1, %0", xoperands);
      }
    return "";
  }
  [(set_attr "length" "4, <lImmArithD>, <lImmArithD>, <lImmArithD>")]
)

; All long (SI, SF) register move, load and store operations
; The print_operand will take care of printing the register pair 
; when mode is SI/SF and register is in SHORT_REGS
(define_insn "*mov<mode>_long"
  [(set (match_operand:LONG 0 "nonimmediate_operand" "=r, r, r, m")
	(match_operand:LONG 1 "general_operand" "r, <iF>, m, r"))]
  "register_operand (operands[0], <MODE>mode)
   || register_operand (operands[1], <MODE>mode)"
  "@
  mov<tIsa>\t%1, %0
  mov<tIsa>\t%1, %0
  load<tIsa>\t%1, %0
  stor<tIsa>\t%1, %0"
  [(set_attr "length" "2,<lImmArith>,<lImmArith>,<lImmArith>")]
)

;; All short (QI, HI) register move, load and store operations
(define_insn "*mov<mode>_short"
  [(set (match_operand:SHORT 0 "nonimmediate_operand" "=r, r, r, m, m")
	(match_operand:SHORT 1 "general_operand" "r, <iF>, m, r, <LL>"))]
  "(register_operand (operands[0], <MODE>mode))
    || (store_operand (operands[0], <MODE>mode)
	&& (register_operand (operands[1], <MODE>mode)
	    || u4bits_operand (operands[1], <MODE>mode)))"
  "@
  mov<tIsa>\t%1, %0
  mov<tIsa>\t%1, %0
  load<tIsa>\t%1, %0
  stor<tIsa>\t%1, %0
  stor<tIsa>\t%1, %0"
  [(set_attr "length" "2,<lImmArith>,<lImmArith>,<lImmArith>,<lImmArith>")]
)

;;  Compare Instructions
; Instruction generated compares the operands in reverse order
; Therefore, while printing the asm, the reverse of the
; compare condition shall be printed.
(define_insn "cbranch<mode>4"
  [(set (pc)
	(if_then_else (match_operator 0 "ordered_comparison_operator"
		      [(match_operand:CR16IM 1 "register_operand" "r,r")
		       (match_operand:CR16IM 2 "nonmemory_operand" "r,n")])
		       (label_ref (match_operand 3 "" ""))
                      (pc)))
   (clobber (cc0))]
  ""
  "cmp<tIsa>\t%2, %1\;b%d0\t%l3"
  [(set_attr "length" "6,6")]
)

(define_expand "cmp<mode>"
  [(parallel [(set (cc0)
    (compare (match_operand:CR16IM 0 "register_operand" "")
	     (match_operand:CR16IM 1 "nonmemory_operand" "")))
    (clobber (match_scratch:HI 2 "=r"))] ) ]
  ""
  "")

;;  Scond Instructions
(define_expand "cstore<mode>4"
  [(set (cc0)
	(compare (match_operand:CR16IM 2 "register_operand" "")
		 (match_operand:CR16IM 3 "nonmemory_operand" "")))
   (set (match_operand:HI 0 "register_operand")
	(match_operator:HI 1 "ordered_comparison_operator"
	[(cc0) (const_int 0)]))]
  ""
  ""
)

(define_insn "*cmp<mode>_insn"
  [(set (cc0)
	(compare (match_operand:CR16IM 0 "register_operand" "r,r")
		 (match_operand:CR16IM 1 "nonmemory_operand" "r,n")))]
  ""
  "cmp<tIsa>\t%1, %0"
  [(set_attr "length" "2,4")]
)

(define_insn "sCOND_internal"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(match_operator:HI 1 "ordered_comparison_operator"
	[(cc0) (const_int 0)]))]
  ""
  "s%d1\t%0"
  [(set_attr "length" "2")]
)

;;  Jumps and Branches
(define_insn "indirect_jump_return"
  [(set (pc)
	  (reg:SI RA_REGNUM))
   (return)]
  "reload_completed"
  "jump\t (ra)"
  [(set_attr "length" "2")]
)

(define_insn "jump_return"
  [(unspec:SI [(const_int 0)] UNSPEC_RETURN_ADDR)
   (return)]
  "reload_completed"
  "jump\t(ra)"
  [(set_attr "length" "2")]
)

(define_insn "indirect_jump"
  [(set (pc)
	(match_operand:SI 0 "reg_or_sym_operand" "r,i"))]
  ""
  "@
  jump\t%0
  br\t%a0"
  [(set_attr "length" "2,6")]
)

(define_insn "interrupt_return"
  [(unspec_volatile [(const_int 0)] 0)
   (return)]
  ""
  {
    return cr16_prepare_push_pop_string (1);
  }
  [(set_attr "length" "14")]
)

(define_insn "jump_to_imm"
  [(set (pc)
	(match_operand 0 "jump_imm_operand" "i"))]
  ""
  "br\t%c0"
  [(set_attr "length" "6")]
)

(define_insn "jump"
  [(set (pc)
	(label_ref (match_operand 0 "" "")))]
  ""
  "br\t%l0"
  [(set_attr "length" "6")]
)

;;  Table Jump
(define_insn "tablejump"
  [(set (pc)
	(match_operand:SI 0 "register_operand" "r"))
   (use (label_ref:SI (match_operand 1 "" "")))]
  "!flag_pic"
  "jump\t%0"
  [(set_attr "length" "2")]
)

;;  Call Instructions
(define_expand "call"
  [(call (match_operand:QI 0 "memory_operand" "")
	 (match_operand 1 "" ""))]
  ""
  {
    if (flag_pic && ! legitimate_pic_operand_p (operands[0]))
      {
	operands[0] = gen_const_mem (QImode,
	legitimize_pic_address (XEXP (operands[0], 0), Pmode, 0));
	emit_call_insn (gen_cr16_call (operands[0], operands[1]));
      }
    else
      emit_call_insn (gen_cr16_call (operands[0], operands[1]));
      DONE;
  }
)

(define_expand "cr16_call"
  [(parallel
    [(call (match_operand:QI 0 "memory_operand" "")
	   (match_operand 1 "" ""))
   (clobber (reg:SI RA_REGNUM))])]
  ""
  ""
)

(define_insn "cr16_call_insn_branch_pic"
  [(call (mem:QI (match_operand:SI 0 "call_imm_operand" "i"))
	 (match_operand 1 "" ""))
   (clobber (match_operand:SI 2 "register_operand" "+r"))]
  "flag_pic == FAR_PIC"
  {
    if (GET_CODE (operands[0]) != CONST_INT)
      return "loadd\t%g0, %2 \n\tjal %2";
    else
      return "jal %2";
  }
  [(set_attr "length" "8")]
)

(define_insn "cr16_call_insn_branch"
  [(call (mem:QI (match_operand:SI 0 "call_imm_operand" "i"))
	 (match_operand 1 "" ""))
   (clobber (match_operand:SI 2 "register_operand" "+r"))]
  "flag_pic == 0 || flag_pic == NEAR_PIC"
  {
    /* Print the immediate address for bal 
       'b' is used instead of 'a' to avoid compiler calling
       the GO_IF_LEGITIMATE_ADDRESS which cannot
       perform checks on const_int code addresses as it
       assumes all const_int are data addresses.
    */
    if (GET_CODE (operands[0]) != CONST_INT)
      return "bal (ra), %a0";
    else
      operands[4] = GEN_INT ((INTVAL (operands[0]))>>1);
      return "movd\t%g4,\t(r1,r0)\n\tjal\t(r1,r0)";	
  }
  [(set_attr "length" "6")]
)

(define_insn "cr16_call_insn_jump"
  [(call (mem:QI (match_operand:SI 0 "register_operand" "r"))
	 (match_operand 1 "" ""))
   (clobber (match_operand:SI 2 "register_operand" "+r"))]
  ""
  "jal\t%0"
  [(set_attr "length" "2")]
)

;;  Call Value Instructions

(define_expand "call_value"
  [(set (match_operand 0 "general_operand" "")
	(call (match_operand:QI 1 "memory_operand" "")
	      (match_operand 2 "" "")))]
  ""
  {
    if (flag_pic && !legitimate_pic_operand_p (operands[1]))
      {
	operands[1] = gen_const_mem (QImode,
	legitimize_pic_address (XEXP (operands[1], 0), Pmode, 0));
	emit_call_insn (gen_cr16_call_value (operands[0], operands[1], operands[2]));
      }
    else
	emit_call_insn (gen_cr16_call_value (operands[0], operands[1], operands[2]));
    DONE;
  }
)

(define_expand "cr16_call_value"
  [(parallel
    [(set (match_operand 0 "general_operand" "")
	  (call (match_operand 1 "memory_operand" "")
		(match_operand 2 "" "")))
     (clobber (reg:SI RA_REGNUM))])]
  ""
  ""
)

(define_insn "cr16_call_value_insn_branch_pic"
  [(set (match_operand 0 "" "=g")
	(call (mem:QI (match_operand:SI 1 "call_imm_operand" "i"))
	      (match_operand 2 "" "")))
   (clobber (match_operand:SI 3 "register_operand" "+r"))]
  "flag_pic == FAR_PIC"
  {
    if (GET_CODE (operands[1]) != CONST_INT)
      return "loadd\t%g1, %3 \n\tjal %3";
    else
      return "jal %3";
  }
  [(set_attr "length" "8")]
)

(define_insn "cr16_call_value_insn_branch"
  [(set (match_operand 0 "" "=g")
	(call (mem:QI (match_operand:SI 1 "call_imm_operand" "i"))
	      (match_operand 2 "" "")))
   (clobber (match_operand:SI 3 "register_operand" "+r"))]
  "flag_pic == 0 || flag_pic == NEAR_PIC"
  {
    /* Print the immediate address for bal 
       'b' is used instead of 'a' to avoid compiler calling
       the GO_IF_LEGITIMATE_ADDRESS which cannot
       perform checks on const_int code addresses as it
       assumes all const_int are data addresses.
    */
    if (GET_CODE (operands[1]) != CONST_INT) 
      return "bal (ra), %a1";
    else
      {
	operands[4] = GEN_INT ((INTVAL (operands[1]))>>1);
        return "movd\t%g4,\t(r1,r0)\n\tjal\t(r1,r0)";	
      }
  }
  [(set_attr "length" "6")]
)


(define_insn "cr16_call_value_insn_jump"
  [(set (match_operand 0 "" "=g")
	(call (mem:QI (match_operand:SI 1 "register_operand" "r"))
	      (match_operand 2 "" "")))
   (clobber (match_operand:SI 3 "register_operand" "+r"))]
  ""
  "jal\t%1"
  [(set_attr "length" "2")]
)


;;  Nop
(define_insn "nop"
  [(const_int 0)]
  ""
  "nop\t"
)

;; PIC
/* When generating pic, we need to load the symbol offset into a register.
   So that the optimizer does not confuse this with a normal symbol load
   we use an unspec.  The offset will be loaded from a constant pool entry,
   since that is the only type of relocation we can use.  */
                                                                                                                            
(define_insn "unspec_bro_addr"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(unspec:SI [(match_operand 1 "" "")] UNSPEC_PIC_ADDR))]
  ""
  "movd \t%f1, %0"
  [(set_attr "length" "4")]
)

(define_insn "unspec_got_addr"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(unspec:SI [(match_operand 1 "" "")] UNSPEC_PIC_LOAD_ADDR))]
  ""
  "loadd \t%g1, %0"
  [(set_attr "length" "6")]
)
