;; Machine description for T-Head vendor extensions
;; Copyright (C) 2021-2024 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/>.

;; XTheadBa

(define_insn "*th_addsl<mode>4"
  [(set (match_operand:X 0 "register_operand" "=r")
	(plus:X (ashift:X (match_operand:X 1 "register_operand" "r")
			  (match_operand:QI 2 "imm123_operand" "Ds3"))
		(match_operand:X 3 "register_operand" "r")))]
  "TARGET_XTHEADBA"
  "th.addsl\t%0,%3,%1,%2"
  [(set_attr "type" "bitmanip")
   (set_attr "mode" "<X:MODE>")])

;; XTheadBb

(define_insn "*th_srri<mode>3"
  [(set (match_operand:GPR 0 "register_operand" "=r")
	(rotatert:GPR (match_operand:GPR 1 "register_operand" "r")
		     (match_operand 2 "const_int_operand" "n")))]
  "TARGET_XTHEADBB && (TARGET_64BIT || <MODE>mode == SImode)"
  {
    bool wform = TARGET_64BIT && (<MODE>mode == SImode);
    operands[2] = GEN_INT (INTVAL (operands[2])
                  & (GET_MODE_BITSIZE (<MODE>mode) - 1));
    return wform ? "th.srriw\t%0,%1,%2" : "th.srri\t%0,%1,%2";
  }
  [(set_attr "type" "bitmanip")
   (set_attr "mode" "<GPR:MODE>")])

(define_insn "*th_ext<mode>4"
  [(set (match_operand:GPR 0 "register_operand" "=r")
	(sign_extract:GPR (match_operand:GPR 1 "register_operand" "r")
			(match_operand 2 "const_int_operand")
			(match_operand 3 "const_int_operand")))]
  "TARGET_XTHEADBB"
{
  operands[2] = GEN_INT (INTVAL (operands[2]) + INTVAL (operands[3]) - 1);
  return "th.ext\t%0,%1,%2,%3";
}
  [(set_attr "type" "bitmanip")
   (set_attr "mode" "<GPR:MODE>")])

(define_insn "*extendhi<SUPERQI:mode>2_th_ext"
  [(set (match_operand:SUPERQI 0 "register_operand" "=r,r")
	(sign_extend:SUPERQI
	    (match_operand:HI 1 "nonimmediate_operand" "r,m")))]
  "TARGET_XTHEADBB && !TARGET_XTHEADMEMIDX"
  "@
   th.ext\t%0,%1,15,0
   lh\t%0,%1"
  [(set_attr "type" "bitmanip,load")
   (set_attr "mode" "<SUPERQI:MODE>")])

(define_insn "*extendqi<SUPERQI:mode>2_th_ext"
  [(set (match_operand:SUPERQI 0 "register_operand" "=r,r")
	(sign_extend:SUPERQI
	    (match_operand:QI 1 "nonimmediate_operand" "r,m")))]
  "TARGET_XTHEADBB && !TARGET_XTHEADMEMIDX"
  "@
   th.ext\t%0,%1,7,0
   lb\t%0,%1"
  [(set_attr "type" "bitmanip,load")
   (set_attr "mode" "<SUPERQI:MODE>")])

(define_insn "*th_extu<mode>4"
  [(set (match_operand:GPR 0 "register_operand" "=r")
	(zero_extract:GPR (match_operand:GPR 1 "register_operand" "r")
			(match_operand 2 "const_int_operand")
			(match_operand 3 "const_int_operand")))]
  "TARGET_XTHEADBB
   && (UINTVAL (operands[2]) + UINTVAL (operands[3])
       <= GET_MODE_BITSIZE (<MODE>mode))"
{
  operands[2] = GEN_INT (INTVAL (operands[2]) + INTVAL (operands[3]) - 1);
  return "th.extu\t%0,%1,%2,%3";
}
  [(set_attr "type" "bitmanip")
   (set_attr "mode" "<GPR:MODE>")])

(define_insn "*zero_extendsidi2_th_extu"
  [(set (match_operand:DI 0 "register_operand" "=r,r")
	(zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "r,m")))]
  "TARGET_64BIT && TARGET_XTHEADBB && !TARGET_XTHEADMEMIDX"
  "@
   th.extu\t%0,%1,31,0
   lwu\t%0,%1"
  [(set_attr "type" "bitmanip,load")
   (set_attr "mode" "DI")])

(define_insn "*zero_extendhi<GPR:mode>2_th_extu"
  [(set (match_operand:GPR 0 "register_operand" "=r,r")
	(zero_extend:GPR (match_operand:HI 1 "nonimmediate_operand" "r,m")))]
  "TARGET_XTHEADBB && !TARGET_XTHEADMEMIDX"
  "@
   th.extu\t%0,%1,15,0
   lhu\t%0,%1"
  [(set_attr "type" "bitmanip,load")
   (set_attr "mode" "<GPR:MODE>")])

(define_insn "*th_clz<mode>2"
  [(set (match_operand:X 0 "register_operand" "=r")
	(clz:X (match_operand:X 1 "register_operand" "r")))]
  "TARGET_XTHEADBB"
  "th.ff1\t%0,%1"
  [(set_attr "type" "bitmanip")
   (set_attr "mode" "<X:MODE>")])

(define_insn "th_rev<mode>2"
  [(set (match_operand:GPR 0 "register_operand" "=r")
	(bswap:GPR (match_operand:GPR 1 "register_operand" "r")))]
  "TARGET_XTHEADBB && (TARGET_64BIT || <MODE>mode == SImode)"
  {
    bool wform = TARGET_64BIT && (<MODE>mode == SImode);
    return wform ? "th.revw\t%0,%1" : "th.rev\t%0,%1";
  }
  [(set_attr "type" "bitmanip")
   (set_attr "mode" "<GPR:MODE>")])

(define_insn "th_tstnbz<mode>2"
  [(set (match_operand:X 0 "register_operand" "=r")
	(unspec:X [(match_operand:X 1 "register_operand" "r")] UNSPEC_ORC_B))]
  "TARGET_XTHEADBB"
  "th.tstnbz\t%0,%1"
  [(set_attr "type" "bitmanip")])

;; XTheadBs

(define_insn "*th_tst<mode>3"
  [(set (match_operand:X 0 "register_operand" "=r")
	(zero_extract:X (match_operand:X 1 "register_operand" "r")
			(const_int 1)
			(match_operand 2 "const_int_operand" "n")))]
  "TARGET_XTHEADBS && UINTVAL (operands[2]) < GET_MODE_BITSIZE (<MODE>mode)"
  "th.tst\t%0,%1,%2"
  [(set_attr "type" "bitmanip")])

;; XTheadCondMov

(define_insn "*th_cond_mov<GPR:mode><GPR2:mode>"
  [(set (match_operand:GPR 0 "register_operand" "=r,r")
	(if_then_else:GPR
	 (match_operator 4 "equality_operator"
		[(match_operand:GPR2 1 "register_operand" "r,r")
		 (const_int 0)])
	 (match_operand:GPR 2 "reg_or_0_operand" "rJ,0")
	 (match_operand:GPR 3 "reg_or_0_operand" "0,rJ")))]
  "TARGET_XTHEADCONDMOV"
{
  if (which_alternative == 0)
    return "th.mv%C4z\t%0,%z2,%1";

  /* Invert the condition and take else-block.  */
  rtx_code code = GET_CODE (operands[4]);
  code = (code == EQ) ? NE : EQ;
  operands[4] = gen_rtx_fmt_ee (code, VOIDmode, operands[1], const0_rtx);
  return "th.mv%C4z\t%0,%z3,%1";
}
  [(set_attr "type" "condmove")
   (set_attr "mode" "<GPR:MODE>")])

;; XTheadFmv

;; In RV32, we lack fmv.x.d and fmv.d.x, but XTheadFmv has instructions
;; that cover this case.

(define_insn "th_fmv_hw_w_x"
  [(set (match_operand:DF 0 "register_operand" "=f")
	(unspec:DF [(match_operand:SI 1 "register_operand" "r")
                (match_operand:SI 2 "register_operand" "r")]
     UNSPEC_XTHEADFMV))]
  "!TARGET_64BIT && TARGET_XTHEADFMV"
  "fmv.w.x\t%0,%2\n\tth.fmv.hw.x\t%0,%1"
  [(set_attr "move_type" "move")
   (set_attr "type" "fmove")
   (set_attr "mode" "DF")])

(define_insn "th_fmv_x_w"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(unspec:SI [(match_operand:DF 1 "register_operand" "f")]
     UNSPEC_XTHEADFMV))]
  "!TARGET_64BIT && TARGET_XTHEADFMV"
  "fmv.x.w\t%0,%1"
  [(set_attr "move_type" "move")
   (set_attr "type" "fmove")
   (set_attr "mode" "DF")])

(define_insn "th_fmv_x_hw"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(unspec:SI [(match_operand:DF 1 "register_operand" "f")]
     UNSPEC_XTHEADFMV_HW))]
  "!TARGET_64BIT && TARGET_XTHEADFMV"
  "th.fmv.x.hw\t%0,%1"
  [(set_attr "move_type" "move")
   (set_attr "type" "fmove")
   (set_attr "mode" "DF")])

;; XTheadInt

(define_constants
  [(T0_REGNUM	5)
   (T1_REGNUM	6)
   (T2_REGNUM	7)
   (A0_REGNUM	10)
   (A1_REGNUM	11)
   (A2_REGNUM	12)
   (A3_REGNUM	13)
   (A4_REGNUM	14)
   (A5_REGNUM	15)
   (A6_REGNUM	16)
   (A7_REGNUM	17)
   (T3_REGNUM	28)
   (T4_REGNUM	29)
   (T5_REGNUM	30)
   (T6_REGNUM	31)
])

(define_insn "th_int_push"
  [(unspec_volatile [(const_int 0)] UNSPECV_XTHEADINT_PUSH)
   (use (reg:SI RETURN_ADDR_REGNUM))
   (use (reg:SI T0_REGNUM))
   (use (reg:SI T1_REGNUM))
   (use (reg:SI T2_REGNUM))
   (use (reg:SI A0_REGNUM))
   (use (reg:SI A1_REGNUM))
   (use (reg:SI A2_REGNUM))
   (use (reg:SI A3_REGNUM))
   (use (reg:SI A4_REGNUM))
   (use (reg:SI A5_REGNUM))
   (use (reg:SI A6_REGNUM))
   (use (reg:SI A7_REGNUM))
   (use (reg:SI T3_REGNUM))
   (use (reg:SI T4_REGNUM))
   (use (reg:SI T5_REGNUM))
   (use (reg:SI T6_REGNUM))]
  "TARGET_XTHEADINT && !TARGET_64BIT"
  "th.ipush"
  [(set_attr "type"	"store")
   (set_attr "mode"	"SI")])

(define_insn "th_int_pop"
  [(unspec_volatile [(const_int 0)] UNSPECV_XTHEADINT_POP)
   (clobber (reg:SI RETURN_ADDR_REGNUM))
   (clobber (reg:SI T0_REGNUM))
   (clobber (reg:SI T1_REGNUM))
   (clobber (reg:SI T2_REGNUM))
   (clobber (reg:SI A0_REGNUM))
   (clobber (reg:SI A1_REGNUM))
   (clobber (reg:SI A2_REGNUM))
   (clobber (reg:SI A3_REGNUM))
   (clobber (reg:SI A4_REGNUM))
   (clobber (reg:SI A5_REGNUM))
   (clobber (reg:SI A6_REGNUM))
   (clobber (reg:SI A7_REGNUM))
   (clobber (reg:SI T3_REGNUM))
   (clobber (reg:SI T4_REGNUM))
   (clobber (reg:SI T5_REGNUM))
   (clobber (reg:SI T6_REGNUM))
   (return)]
  "TARGET_XTHEADINT && !TARGET_64BIT"
  "th.ipop"
  [(set_attr "type"	"ret")
   (set_attr "mode"	"SI")])

;; XTheadMac

(define_insn "*th_mula<mode>"
  [(set (match_operand:X 0 "register_operand" "=r")
	      (plus:X (mult:X (match_operand:X 1 "register_operand" "r")
			      (match_operand:X 2 "register_operand" "r"))
		      (match_operand:X 3 "register_operand" "0")))]
  "TARGET_XTHEADMAC"
  "th.mula\\t%0,%1,%2"
  [(set_attr "type" "imul")
   (set_attr "mode" "<MODE>")]
)

(define_insn "*th_mulawsi"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(sign_extend:DI
	  (plus:SI (mult:SI (match_operand:SI 1 "register_operand" "r")
			    (match_operand:SI 2 "register_operand" "r"))
		   (match_operand:SI 3 "register_operand" "0"))))]
  "TARGET_XTHEADMAC && TARGET_64BIT"
  "th.mulaw\\t%0,%1,%2"
  [(set_attr "type" "imul")
   (set_attr "mode" "SI")]
)

(define_insn "*th_mulawsi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	      (plus:SI (mult:SI (match_operand:SI 1 "register_operand" "r")
				(match_operand:SI 2 "register_operand" "r"))
		       (match_operand:SI 3 "register_operand" "0")))]
  "TARGET_XTHEADMAC && TARGET_64BIT"
  "th.mulaw\\t%0,%1,%2"
  [(set_attr "type" "imul")
   (set_attr "mode" "SI")]
)

(define_insn "*th_maddhisi4"
  [(set (match_operand:SI 0 "register_operand" "=r")
	  (plus:SI
	    (mult:SI
	      (sign_extend:SI (match_operand:HI 1 "register_operand" " r"))
	      (sign_extend:SI (match_operand:HI 2 "register_operand" " r")))
	    (match_operand:SI 3 "register_operand" " 0")))]
  "TARGET_XTHEADMAC"
  "th.mulah\\t%0,%1,%2"
  [(set_attr "type" "imul")
   (set_attr "mode" "SI")]
)

(define_insn "*th_sextw_maddhisi4"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(sign_extend:DI
	  (plus:SI
	    (mult:SI
	      (sign_extend:SI (match_operand:HI 1 "register_operand" " r"))
	      (sign_extend:SI (match_operand:HI 2 "register_operand" " r")))
	    (match_operand:SI 3 "register_operand" " 0"))))]
  "TARGET_XTHEADMAC && TARGET_64BIT"
  "th.mulah\\t%0,%1,%2"
  [(set_attr "type" "imul")
   (set_attr "mode" "SI")]
)

(define_insn "*th_muls<mode>"
  [(set (match_operand:X 0 "register_operand" "=r")
	      (minus:X (match_operand:X 3 "register_operand" "0")
		       (mult:X (match_operand:X 1 "register_operand" "r")
			       (match_operand:X 2 "register_operand" "r"))))]
  "TARGET_XTHEADMAC"
  "th.muls\\t%0,%1,%2"
  [(set_attr "type" "imul")
   (set_attr "mode" "<MODE>")]
)

(define_insn "*th_mulswsi"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(sign_extend:DI
	  (minus:SI (match_operand:SI 3 "register_operand" "0")
		    (mult:SI (match_operand:SI 1 "register_operand" "r")
			     (match_operand:SI 2 "register_operand" "r")))))]
  "TARGET_XTHEADMAC && TARGET_64BIT"
  "th.mulsw\\t%0,%1,%2"
  [(set_attr "type" "imul")
   (set_attr "mode" "SI")]
)

(define_insn "*th_mulswsi2"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(minus:SI (match_operand:SI 3 "register_operand" "0")
		  (mult:SI (match_operand:SI 1 "register_operand" "r")
			   (match_operand:SI 2 "register_operand" "r"))))]
  "TARGET_XTHEADMAC && TARGET_64BIT"
  "th.mulsw\\t%0,%1,%2"
  [(set_attr "type" "imul")
   (set_attr "mode" "SI")]
)

(define_insn "*th_msubhisi4"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(minus:SI (match_operand:SI 3 "register_operand" " 0")
	  (mult:SI (sign_extend:SI (match_operand:HI 1 "register_operand" " r"))
	    (sign_extend:SI (match_operand:HI 2 "register_operand" " r")))))]
  "TARGET_XTHEADMAC"
  "th.mulsh\\t%0,%1,%2"
  [(set_attr "type" "imul")
   (set_attr "mode" "SI")]
)

(define_insn "*th_sextw_msubhisi4"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(sign_extend:DI
	  (minus:SI (match_operand:SI 3 "register_operand" " 0")
	    (mult:SI
	      (sign_extend:SI (match_operand:HI 1 "register_operand" " r"))
	      (sign_extend:SI (match_operand:HI 2 "register_operand" " r"))))))]
  "TARGET_XTHEADMAC && TARGET_64BIT"
  "th.mulsh\\t%0,%1,%2"
  [(set_attr "type" "imul")
   (set_attr "mode" "SI")]
)

;; XTheadMemPair

;; MEMPAIR load 64/32 bit
(define_insn "*th_mempair_load_<GPR:mode>2"
  [(set (match_operand:GPR 0 "register_operand" "=r")
	(match_operand:GPR 1 "memory_operand" "m"))
   (set (match_operand:GPR 2 "register_operand" "=r")
	(match_operand:GPR 3 "memory_operand" "m"))]
  "TARGET_XTHEADMEMPAIR && reload_completed
   && th_mempair_operands_p (operands, true, <GPR:MODE>mode)"
  { return th_mempair_output_move (operands, true, <GPR:MODE>mode, UNKNOWN); }
  [(set_attr "move_type" "load")
   (set_attr "type" "load")
   (set_attr "mode" "<GPR:MODE>")])

;; MEMPAIR store 64/32 bit
(define_insn "*th_mempair_store_<GPR:mode>2"
  [(set (match_operand:GPR 0 "memory_operand" "=m")
	(match_operand:GPR 1 "register_operand" "r"))
   (set (match_operand:GPR 2 "memory_operand" "=m")
	(match_operand:GPR 3 "register_operand" "r"))]
  "TARGET_XTHEADMEMPAIR && reload_completed
   && th_mempair_operands_p (operands, false, <GPR:MODE>mode)"
  { return th_mempair_output_move (operands, false, <GPR:MODE>mode, UNKNOWN); }
  [(set_attr "move_type" "store")
   (set_attr "type" "store")
   (set_attr "mode" "<GPR:MODE>")])

;; MEMPAIR load DI extended signed SI
(define_insn "*th_mempair_load_extendsidi2"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(sign_extend:DI (match_operand:SI 1 "memory_operand" "m")))
   (set (match_operand:DI 2 "register_operand" "=r")
	(sign_extend:DI (match_operand:SI 3 "memory_operand" "m")))]
  "TARGET_XTHEADMEMPAIR && TARGET_64BIT && reload_completed
   && th_mempair_operands_p (operands, true, SImode)"
  { return th_mempair_output_move (operands, true, SImode, SIGN_EXTEND); }
  [(set_attr "move_type" "load")
   (set_attr "type" "load")
   (set_attr "mode" "DI")
   (set_attr "length" "8")])

;; MEMPAIR load DI extended unsigned SI
(define_insn "*th_mempair_load_zero_extendsidi2"
  [(set (match_operand:DI 0 "register_operand" "=r")
	(zero_extend:DI (match_operand:SI 1 "memory_operand" "m")))
   (set (match_operand:DI 2 "register_operand" "=r")
	(zero_extend:DI (match_operand:SI 3 "memory_operand" "m")))]
  "TARGET_XTHEADMEMPAIR && TARGET_64BIT && reload_completed
   && th_mempair_operands_p (operands, true, SImode)"
  { return th_mempair_output_move (operands, true, SImode, ZERO_EXTEND); }
  [(set_attr "move_type" "load")
   (set_attr "type" "load")
   (set_attr "mode" "DI")
   (set_attr "length" "8")])

;; XTheadMemIdx

;; Help reload to add a displacement for the base register.
;; In the case `zext(*(uN*)(base+(zext(rN)<<1)))` LRA splits
;; off two new instructions: a) `new_base = base + disp`, and
;; b) `index = zext(rN)<<1`.  The index calculation has no
;; corresponding instruction pattern and needs this insn_and_split
;; to recover.

(define_insn_and_split "*th_memidx_operand"
  [(set (match_operand:DI 0 "register_operand" "=r")
     (ashift:DI
       (zero_extend:DI (subreg:SI (match_operand:DI 1 "register_operand" "r") 0))
       (match_operand 2 "const_int_operand" "n")))]
  "TARGET_64BIT && TARGET_XTHEADMEMIDX && lra_in_progress"
  "#"
  ""
  [(set (match_dup 0) (zero_extend:DI (subreg:SI (match_dup 1) 0)))
   (set (match_dup 0) (ashift:DI (match_dup 0) (match_dup 2)))]
  ""
  [(set_attr "type" "bitmanip")])

(define_insn "*th_memidx_zero_extendqi<SUPERQI:mode>2"
  [(set (match_operand:SUPERQI 0 "register_operand" "=r,r,r,r,r,r")
	(zero_extend:SUPERQI
	    (match_operand:QI 1 "nonimmediate_operand"
         " r,th_m_mia,th_m_mib,th_m_mir,th_m_miu,m")))]
  "TARGET_XTHEADMEMIDX"
  "@
   andi\t%0,%1,0xff
   th.lbuia\t%0,%1
   th.lbuib\t%0,%1
   th.lrbu\t%0,%1
   th.lurbu\t%0,%1
   lbu\t%0,%1"
  [(set_attr "move_type" "andi,load,load,load,load,load")
   (set_attr "mode" "<SUPERQI:MODE>")])

(define_insn "*th_memidx_extendsidi2"
  [(set (match_operand:DI 0 "register_operand" "=r,r,r,r,r,r")
	(sign_extend:DI
	    (match_operand:SI 1 "nonimmediate_operand"
         " r,th_m_mia,th_m_mib,th_m_mir,th_m_miu,m")))]
  "TARGET_64BIT && TARGET_XTHEADMEMIDX"
  "@
   sext.w\t%0,%1
   th.lwia\t%0,%1
   th.lwib\t%0,%1
   th.lrw\t%0,%1
   th.lurw\t%0,%1
   lw\t%0,%1"
  [(set_attr "move_type" "move,load,load,load,load,load")
   (set_attr "mode" "DI")])

;; XTheadMemIdx (without XTheadBb)

(define_insn_and_split "*th_memidx_zero_extendsidi2"
  [(set (match_operand:DI 0 "register_operand" "=r,r,r,r,r,r")
	(zero_extend:DI
	    (match_operand:SI 1 "nonimmediate_operand"
         " r,th_m_mia,th_m_mib,th_m_mir,th_m_miu,m")))]
  "TARGET_64BIT && TARGET_XTHEADMEMIDX && !TARGET_XTHEADBB"
  "@
   #
   th.lwuia\t%0,%1
   th.lwuib\t%0,%1
   th.lrwu\t%0,%1
   th.lurwu\t%0,%1
   lwu\t%0,%1"
  "&& reload_completed
   && REG_P (operands[1])
   && !paradoxical_subreg_p (operands[0])"
  [(set (match_dup 0)
	(ashift:DI (match_dup 1) (const_int 32)))
   (set (match_dup 0)
	(lshiftrt:DI (match_dup 0) (const_int 32)))]
  { operands[1] = gen_lowpart (DImode, operands[1]); }
  [(set_attr "move_type" "shift_shift,load,load,load,load,load")
   (set_attr "mode" "DI")])

(define_insn_and_split "*th_memidx_zero_extendhi<GPR:mode>2"
  [(set (match_operand:GPR 0 "register_operand" "=r,r,r,r,r,r")
	(zero_extend:GPR
	    (match_operand:HI 1 "nonimmediate_operand"
         " r,th_m_mia,th_m_mib,th_m_mir,th_m_miu,m")))]
  "TARGET_XTHEADMEMIDX && !TARGET_XTHEADBB"
  "@
   #
   th.lhuia\t%0,%1
   th.lhuib\t%0,%1
   th.lrhu\t%0,%1
   th.lurhu\t%0,%1
   lhu\t%0,%1"
  "&& reload_completed
   && REG_P (operands[1])
   && !paradoxical_subreg_p (operands[0])"
  [(set (match_dup 0)
	(ashift:GPR (match_dup 1) (match_dup 2)))
   (set (match_dup 0)
	(lshiftrt:GPR (match_dup 0) (match_dup 2)))]
  {
    operands[1] = gen_lowpart (<GPR:MODE>mode, operands[1]);
    operands[2] = GEN_INT(GET_MODE_BITSIZE(<GPR:MODE>mode) - 16);
  }
  [(set_attr "move_type" "shift_shift,load,load,load,load,load")
   (set_attr "mode" "<GPR:MODE>")])

(define_insn_and_split "*th_memidx_extend<SHORT:mode><SUPERQI:mode>2"
  [(set (match_operand:SUPERQI 0 "register_operand" "=r,r,r,r,r,r")
	(sign_extend:SUPERQI
	    (match_operand:SHORT 1 "nonimmediate_operand"
         " r,th_m_mia,th_m_mib,th_m_mir,th_m_miu,m")))]
  "TARGET_XTHEADMEMIDX && !TARGET_XTHEADBB"
  "@
   #
   th.l<SHORT:size>ia\t%0,%1
   th.l<SHORT:size>ib\t%0,%1
   th.lr<SHORT:size>\t%0,%1
   th.lur<SHORT:size>\t%0,%1
   l<SHORT:size>\t%0,%1"
  "&& reload_completed
   && REG_P (operands[1])
   && !paradoxical_subreg_p (operands[0])"
  [(set (match_dup 0) (ashift:SI (match_dup 1) (match_dup 2)))
   (set (match_dup 0) (ashiftrt:SI (match_dup 0) (match_dup 2)))]
{
  operands[0] = gen_lowpart (SImode, operands[0]);
  operands[1] = gen_lowpart (SImode, operands[1]);
  operands[2] = GEN_INT (GET_MODE_BITSIZE (SImode)
			 - GET_MODE_BITSIZE (<SHORT:MODE>mode));
}
  [(set_attr "move_type" "shift_shift,load,load,load,load,load")
   (set_attr "mode" "SI")])

;; XTheadMemIdx (with XTheadBb)

(define_insn "*th_memidx_bb_zero_extendsidi2"
  [(set (match_operand:DI 0 "register_operand" "=r,r,r,r,r,r")
	(zero_extend:DI
	    (match_operand:SI 1 "nonimmediate_operand"
         " r,th_m_mia,th_m_mib,th_m_mir,th_m_miu,m")))]
  "TARGET_64BIT && TARGET_XTHEADMEMIDX && TARGET_XTHEADBB"
  "@
   th.extu\t%0,%1,31,0
   th.lwuia\t%0,%1
   th.lwuib\t%0,%1
   th.lrwu\t%0,%1
   th.lurwu\t%0,%1
   lwu\t%0,%1"
  [(set_attr "move_type" "shift_shift,load,load,load,load,load")
   (set_attr "mode" "DI")])

(define_insn "*th_memidx_bb_zero_extendhi<GPR:mode>2"
  [(set (match_operand:GPR 0 "register_operand" "=r,r,r,r,r,r")
	(zero_extend:GPR
	    (match_operand:HI 1 "nonimmediate_operand"
         " r,th_m_mia,th_m_mib,th_m_mir,th_m_miu,m")))]
  "TARGET_XTHEADMEMIDX && TARGET_XTHEADBB"
  "@
   th.extu\t%0,%1,15,0
   th.lhuia\t%0,%1
   th.lhuib\t%0,%1
   th.lrhu\t%0,%1
   th.lurhu\t%0,%1
   lhu\t%0,%1"
  [(set_attr "move_type" "shift_shift,load,load,load,load,load")
   (set_attr "mode" "<GPR:MODE>")])

(define_insn "*th_memidx_bb_extendhi<GPR:mode>2"
  [(set (match_operand:GPR 0 "register_operand" "=r,r,r,r,r,r")
	(sign_extend:GPR
	    (match_operand:HI 1 "nonimmediate_operand"
         " r,th_m_mia,th_m_mib,th_m_mir,th_m_miu,m")))]
  "TARGET_XTHEADMEMIDX && TARGET_XTHEADBB"
  "@
   th.ext\t%0,%1,15,0
   th.lhia\t%0,%1
   th.lhib\t%0,%1
   th.lrh\t%0,%1
   th.lurh\t%0,%1
   lh\t%0,%1"
  [(set_attr "move_type" "shift_shift,load,load,load,load,load")
   (set_attr "mode" "<GPR:MODE>")])

(define_insn "*th_memidx_bb_extendqi<SUPERQI:mode>2"
  [(set (match_operand:SUPERQI 0 "register_operand" "=r,r,r,r,r,r")
	(sign_extend:SUPERQI
	    (match_operand:QI 1 "nonimmediate_operand"
         " r,th_m_mia,th_m_mib,th_m_mir,th_m_miu,m")))]
  "TARGET_XTHEADMEMIDX && TARGET_XTHEADBB"
  "@
   th.ext\t%0,%1,7,0
   th.lbia\t%0,%1
   th.lbib\t%0,%1
   th.lrb\t%0,%1
   th.lurb\t%0,%1
   lb\t%0,%1"
  [(set_attr "move_type" "shift_shift,load,load,load,load,load")
   (set_attr "mode" "<SUPERQI:MODE>")])

;; All modes that are supported by XTheadMemIdx
(define_mode_iterator TH_M_ANYI [(QI "TARGET_XTHEADMEMIDX")
                                 (HI "TARGET_XTHEADMEMIDX")
                                 (SI "TARGET_XTHEADMEMIDX")
                                 (DI "TARGET_64BIT && TARGET_XTHEADMEMIDX")])

;; All modes that are supported by XTheadFMemIdx
(define_mode_iterator TH_M_ANYF [(SF "TARGET_HARD_FLOAT && TARGET_XTHEADFMEMIDX")
                                 (DF "TARGET_DOUBLE_FLOAT && TARGET_XTHEADFMEMIDX")])

;; All non-extension modes that are supported by XTheadMemIdx
(define_mode_iterator TH_M_NOEXTI [(SI "!TARGET_64BIT && TARGET_XTHEADMEMIDX")
                                   (DI "TARGET_64BIT && TARGET_XTHEADMEMIDX")])

;; All non-extension modes that are supported by XTheadFMemIdx
(define_mode_iterator TH_M_NOEXTF [(SF "TARGET_HARD_FLOAT && TARGET_XTHEADFMEMIDX")
                                   (DF "TARGET_DOUBLE_FLOAT && TARGET_XTHEADFMEMIDX")])

;; XTheadMemIdx optimizations
;; All optimizations attempt to improve the operand utilization of
;; XTheadMemIdx instructions, where one sign or zero extended
;; register-index-operand can be shifted left by a 2-bit immediate.
;;
;; The basic idea is the following optimization:
;; (set (reg 0) (op (reg 1) (imm 2)))
;; (set (reg 3) (mem (plus (reg 0) (reg 4)))
;; ==>
;; (set (reg 3) (mem (plus (reg 4) (op2 (reg 1) (imm 2))))
;; This optimization only valid if (reg 0) has no further uses.
;;
;; The three-instruction case is as follows:
;; (set (reg 0) (op1 (reg 1) (imm 2)))
;; (set (reg 3) (op2 (reg 0) (imm 4)))
;; (set (reg 5) (mem (plus (reg 3) (reg 6)))
;; ==>
;; (set (reg 5) (mem (plus (reg 6) (op2 (reg 1) (imm 2/4)))))
;; This optimization is only valid if (reg 0) and (reg 3) have no further uses.
;;
;; The optimization cases are:
;; I) fold 2-bit ashift of register offset into mem-plus RTX
;; US) fold 32-bit zero-extended (shift) offset into mem-plus
;; UZ) fold 32-bit zero-extended (zext) offset into mem-plus
;;
;; The first optimization case is targeting the th.lr<MODE> instructions.
;; The other optimization cases are targeting the th.lur<MODE> instructions
;; and have to consider two forms of zero-extensions:
;; - ashift-32 + lshiftrt-{29..32} if there are no zero-extension instructions.
;;   Left-shift amounts of 29..31 indicate a left-shifted zero-extended value.
;; - zero-extend32 if there are zero-extension instructions (XTheadBb or Zbb).
;;
;; We always have three peephole passes per optimization case:
;; a) no-extended (X) word-load
;; b) any-extend (SUBX) word-load
;; c) store
;;
;; Note, that SHIFTs will be converted to MULTs during combine.

(define_insn_and_split "*th_memidx_I_a"
  [(set (match_operand:TH_M_NOEXTI 0 "register_operand" "=r")
        (mem:TH_M_NOEXTI (plus:X
          (mult:X (match_operand:X 1 "register_operand" "r")
                  (match_operand:QI 2 "immediate_operand" "i"))
          (match_operand:X 3 "register_operand" "r"))))]
  "TARGET_XTHEADMEMIDX
   && CONST_INT_P (operands[2])
   && pow2p_hwi (INTVAL (operands[2]))
   && IN_RANGE (exact_log2 (INTVAL (operands[2])), 1, 3)"
  "#"
  "&& 1"
  [(set (match_dup 0)
        (mem:TH_M_NOEXTI (plus:X
          (match_dup 3)
          (ashift:X (match_dup 1) (match_dup 2)))))]
  { operands[2] = GEN_INT (exact_log2 (INTVAL (operands [2])));
  }
)

(define_insn_and_split "*th_memidx_I_b"
  [(set (match_operand:X 0 "register_operand" "=r")
        (any_extend:X (mem:SUBX (plus:X
          (mult:X (match_operand:X 1 "register_operand" "r")
                  (match_operand:QI 2 "immediate_operand" "i"))
          (match_operand:X 3 "register_operand" "r")))))]
  "TARGET_XTHEADMEMIDX
   && CONST_INT_P (operands[2])
   && pow2p_hwi (INTVAL (operands[2]))
   && IN_RANGE (exact_log2 (INTVAL (operands[2])), 1, 3)"
  "#"
  "&& 1"
  [(set (match_dup 0)
        (any_extend:X (mem:SUBX (plus:X
          (match_dup 3)
          (ashift:X (match_dup 1) (match_dup 2))))))]
  { operands[2] = GEN_INT (exact_log2 (INTVAL (operands [2])));
  }
)

(define_insn_and_split "*th_memidx_I_c"
  [(set (mem:TH_M_ANYI (plus:X
          (mult:X (match_operand:X 1 "register_operand" "r")
                  (match_operand:QI 2 "immediate_operand" "i"))
          (match_operand:X 3 "register_operand" "r")))
        (match_operand:TH_M_ANYI 0 "register_operand" "r"))]
  "TARGET_XTHEADMEMIDX
   && CONST_INT_P (operands[2])
   && pow2p_hwi (INTVAL (operands[2]))
   && IN_RANGE (exact_log2 (INTVAL (operands[2])), 1, 3)"
  "#"
  "&& 1"
  [(set (mem:TH_M_ANYI (plus:X
          (match_dup 3)
          (ashift:X (match_dup 1) (match_dup 2))))
        (match_dup 0))]
  { operands[2] = GEN_INT (exact_log2 (INTVAL (operands [2])));
  }
)

(define_insn_and_split "*th_memidx_US_a"
  [(set (match_operand:TH_M_NOEXTI 0 "register_operand" "=r")
        (mem:TH_M_NOEXTI (plus:DI
          (and:DI
            (mult:DI (match_operand:DI 1 "register_operand" "r")
                     (match_operand:QI 2 "immediate_operand" "i"))
            (match_operand:DI 3 "immediate_operand" "i"))
          (match_operand:DI 4 "register_operand" "r"))))]
  "TARGET_64BIT && TARGET_XTHEADMEMIDX
   && CONST_INT_P (operands[2])
   && pow2p_hwi (INTVAL (operands[2]))
   && IN_RANGE (exact_log2 (INTVAL (operands[2])), 1, 3)
   && CONST_INT_P (operands[3])
   && (INTVAL (operands[3]) >> exact_log2 (INTVAL (operands[2]))) == 0xffffffff"
  "#"
  "&& 1"
  [(set (match_dup 0)
        (mem:TH_M_NOEXTI (plus:DI
          (match_dup 4)
          (ashift:DI (zero_extend:DI (match_dup 1)) (match_dup 2)))))]
  { operands[1] = gen_lowpart (SImode, operands[1]);
    operands[2] = GEN_INT (exact_log2 (INTVAL (operands [2])));
  }
)

(define_insn_and_split "*th_memidx_US_b"
  [(set (match_operand:X 0 "register_operand" "=r")
        (any_extend:X (mem:SUBX (plus:DI
          (and:DI
            (mult:DI (match_operand:DI 1 "register_operand" "r")
                     (match_operand:QI 2 "immediate_operand" "i"))
            (match_operand:DI 3 "immediate_operand" "i"))
          (match_operand:DI 4 "register_operand" "r")))))]
  "TARGET_64BIT && TARGET_XTHEADMEMIDX
   && CONST_INT_P (operands[2])
   && pow2p_hwi (INTVAL (operands[2]))
   && IN_RANGE (exact_log2 (INTVAL (operands[2])), 1, 3)
   && CONST_INT_P (operands[3])
   && (INTVAL (operands[3]) >> exact_log2 (INTVAL (operands[2]))) == 0xffffffff"
  "#"
  "&& 1"
  [(set (match_dup 0)
        (any_extend:X (mem:SUBX (plus:DI
          (match_dup 4)
          (ashift:DI (zero_extend:DI (match_dup 1)) (match_dup 2))))))]
  { operands[1] = gen_lowpart (SImode, operands[1]);
    operands[2] = GEN_INT (exact_log2 (INTVAL (operands [2])));
  }
)

(define_insn_and_split "*th_memidx_US_c"
  [(set (mem:TH_M_ANYI (plus:DI
          (and:DI
            (mult:DI (match_operand:DI 1 "register_operand" "r")
                     (match_operand:QI 2 "immediate_operand" "i"))
            (match_operand:DI 3 "immediate_operand" "i"))
          (match_operand:DI 4 "register_operand" "r")))
        (match_operand:TH_M_ANYI 0 "register_operand" "r"))]
  "TARGET_64BIT && TARGET_XTHEADMEMIDX
   && CONST_INT_P (operands[2])
   && pow2p_hwi (INTVAL (operands[2]))
   && IN_RANGE (exact_log2 (INTVAL (operands[2])), 1, 3)
   && CONST_INT_P (operands[3])
   && (INTVAL (operands[3]) >> exact_log2 (INTVAL (operands[2]))) == 0xffffffff"
  "#"
  "&& 1"
  [(set (mem:TH_M_ANYI (plus:DI
          (match_dup 4)
          (ashift:DI (zero_extend:DI (match_dup 1)) (match_dup 2))))
        (match_dup 0))]
  { operands[1] = gen_lowpart (SImode, operands[1]);
    operands[2] = GEN_INT (exact_log2 (INTVAL (operands [2])));
  }
)

(define_insn_and_split "*th_memidx_UZ_a"
  [(set (match_operand:TH_M_NOEXTI 0 "register_operand" "=r")
        (mem:TH_M_NOEXTI (plus:DI
          (zero_extend:DI (match_operand:SI 1 "register_operand" "r"))
          (match_operand:DI 2 "register_operand" "r"))))]
  "TARGET_64BIT && TARGET_XTHEADMEMIDX"
  "#"
  "&& 1"
  [(set (match_dup 0)
        (mem:TH_M_NOEXTI (plus:DI
          (match_dup 2)
          (zero_extend:DI (match_dup 1)))))]
)

(define_insn_and_split "*th_memidx_UZ_b"
  [(set (match_operand:X 0 "register_operand" "=r")
        (any_extend:X (mem:SUBX (plus:DI
          (zero_extend:DI (match_operand:SI 1 "register_operand" "r"))
          (match_operand:DI 2 "register_operand" "r")))))]
  "TARGET_64BIT && TARGET_XTHEADMEMIDX"
  "#"
  "&& 1"
  [(set (match_dup 0)
        (any_extend:X (mem:SUBX (plus:DI
          (match_dup 2)
          (zero_extend:DI (match_dup 1))))))]
)

(define_insn_and_split "*th_memidx_UZ_c"
  [(set (mem:TH_M_ANYI (plus:DI
          (zero_extend:DI (match_operand:SI 1 "register_operand" "r"))
          (match_operand:DI 2 "register_operand" "r")))
        (match_operand:TH_M_ANYI 0 "register_operand" "r"))]
  "TARGET_64BIT && TARGET_XTHEADMEMIDX"
  "#"
  "&& 1"
  [(set (mem:TH_M_ANYI (plus:DI
          (match_dup 2)
          (zero_extend:DI (match_dup 1))))
        (match_dup 0))]
)

;; XTheadFMemIdx
;; Note, that we might get GP registers in FP-mode (reg:DF a2)
;; which cannot be handled by the XTheadFMemIdx instructions.
;; This might even happend after register allocation.
;; We could implement splitters that undo the combiner results
;; if "after_reload && !HARDFP_REG_P (operands[0])", but this
;; raises even more questions (e.g. split into what?).
;; So let's solve this by simply requiring XTheadMemIdx
;; which provides the necessary instructions to cover this case.

(define_insn "*th_fmemidx_movsf_hardfloat"
  [(set (match_operand:SF 0 "nonimmediate_operand" "=f,th_m_mir,f,th_m_miu")
	(match_operand:SF 1 "move_operand"         " th_m_mir,f,th_m_miu,f"))]
  "TARGET_HARD_FLOAT && TARGET_XTHEADFMEMIDX && TARGET_XTHEADMEMIDX
   && (register_operand (operands[0], SFmode)
       || reg_or_0_operand (operands[1], SFmode))"
  { return riscv_output_move (operands[0], operands[1]); }
  [(set_attr "move_type" "fpload,fpstore,fpload,fpstore")
   (set_attr "mode" "SF")])

(define_insn "*th_fmemidx_movdf_hardfloat_rv64"
  [(set (match_operand:DF 0 "nonimmediate_operand" "=f,th_m_mir,f,th_m_miu")
	(match_operand:DF 1 "move_operand"         " th_m_mir,f,th_m_miu,f"))]
  "TARGET_64BIT && TARGET_DOUBLE_FLOAT && TARGET_XTHEADFMEMIDX
   && TARGET_XTHEADMEMIDX
   && (register_operand (operands[0], DFmode)
       || reg_or_0_operand (operands[1], DFmode))"
  { return riscv_output_move (operands[0], operands[1]); }
  [(set_attr "move_type" "fpload,fpstore,fpload,fpstore")
   (set_attr "mode" "DF")])

;; XTheadFMemIdx optimizations
;; Similar like XTheadMemIdx optimizations, but less cases.

(define_insn_and_split "*th_fmemidx_I_a"
  [(set (match_operand:TH_M_NOEXTF 0 "register_operand" "=f")
        (mem:TH_M_NOEXTF (plus:X
          (mult:X (match_operand:X 1 "register_operand" "r")
                  (match_operand:QI 2 "immediate_operand" "i"))
          (match_operand:X 3 "register_operand" "r"))))]
  "TARGET_XTHEADMEMIDX && TARGET_XTHEADFMEMIDX
   && CONST_INT_P (operands[2])
   && pow2p_hwi (INTVAL (operands[2]))
   && IN_RANGE (exact_log2 (INTVAL (operands[2])), 1, 3)"
  "#"
  "&& reload_completed"
  [(set (match_dup 0)
        (mem:TH_M_NOEXTF (plus:X
          (match_dup 3)
          (ashift:X (match_dup 1) (match_dup 2)))))]
  { operands[2] = GEN_INT (exact_log2 (INTVAL (operands [2])));
  }
  [(set_attr "move_type" "fpload")
   (set_attr "mode" "<UNITMODE>")
   (set_attr "type" "fmove")
   (set (attr "length") (const_int 16))])

(define_insn_and_split "*th_fmemidx_I_c"
  [(set (mem:TH_M_ANYF (plus:X
          (mult:X (match_operand:X 1 "register_operand" "r")
                  (match_operand:QI 2 "immediate_operand" "i"))
          (match_operand:X 3 "register_operand" "r")))
        (match_operand:TH_M_ANYF 0 "register_operand" "f"))]
  "TARGET_XTHEADMEMIDX && TARGET_XTHEADFMEMIDX
   && CONST_INT_P (operands[2])
   && pow2p_hwi (INTVAL (operands[2]))
   && IN_RANGE (exact_log2 (INTVAL (operands[2])), 1, 3)"
  "#"
  "&& 1"
  [(set (mem:TH_M_ANYF (plus:X
          (match_dup 3)
          (ashift:X (match_dup 1) (match_dup 2))))
        (match_dup 0))]
  { operands[2] = GEN_INT (exact_log2 (INTVAL (operands [2])));
  }
)

(define_insn_and_split "*th_fmemidx_US_a"
  [(set (match_operand:TH_M_NOEXTF 0 "register_operand" "=f")
        (mem:TH_M_NOEXTF (plus:DI
          (and:DI
            (mult:DI (match_operand:DI 1 "register_operand" "r")
                     (match_operand:QI 2 "immediate_operand" "i"))
            (match_operand:DI 3 "immediate_operand" "i"))
          (match_operand:DI 4 "register_operand" "r"))))]
  "TARGET_64BIT && TARGET_XTHEADMEMIDX && TARGET_XTHEADFMEMIDX
   && CONST_INT_P (operands[2])
   && pow2p_hwi (INTVAL (operands[2]))
   && IN_RANGE (exact_log2 (INTVAL (operands[2])), 1, 3)
   && CONST_INT_P (operands[3])
   && (INTVAL (operands[3]) >> exact_log2 (INTVAL (operands[2]))) == 0xffffffff"
  "#"
  "&& reload_completed"
  [(set (match_dup 0)
        (mem:TH_M_NOEXTF (plus:DI
          (match_dup 4)
          (ashift:DI (zero_extend:DI (match_dup 1)) (match_dup 2)))))]
  { operands[1] = gen_lowpart (SImode, operands[1]);
    operands[2] = GEN_INT (exact_log2 (INTVAL (operands [2])));
  }
  [(set_attr "move_type" "fpload")
   (set_attr "mode" "<UNITMODE>")
   (set_attr "type" "fmove")
   (set (attr "length") (const_int 16))])

(define_insn_and_split "*th_fmemidx_US_c"
  [(set (mem:TH_M_ANYF (plus:DI
          (and:DI
            (mult:DI (match_operand:DI 1 "register_operand" "r")
                     (match_operand:QI 2 "immediate_operand" "i"))
            (match_operand:DI 3 "immediate_operand" "i"))
          (match_operand:DI 4 "register_operand" "r")))
        (match_operand:TH_M_ANYF 0 "register_operand" "f"))]
  "TARGET_64BIT && TARGET_XTHEADMEMIDX && TARGET_XTHEADFMEMIDX
   && CONST_INT_P (operands[2])
   && pow2p_hwi (INTVAL (operands[2]))
   && IN_RANGE (exact_log2 (INTVAL (operands[2])), 1, 3)
   && CONST_INT_P (operands[3])
   && (INTVAL (operands[3]) >> exact_log2 (INTVAL (operands[2]))) == 0xffffffff"
  "#"
  "&& 1"
  [(set (mem:TH_M_ANYF (plus:DI
          (match_dup 4)
          (ashift:DI (zero_extend:DI (match_dup 1)) (match_dup 2))))
        (match_dup 0))]
  { operands[1] = gen_lowpart (SImode, operands[1]);
    operands[2] = GEN_INT (exact_log2 (INTVAL (operands [2])));
  }
)

(define_insn_and_split "*th_fmemidx_UZ_a"
  [(set (match_operand:TH_M_NOEXTF 0 "register_operand" "=f")
        (mem:TH_M_NOEXTF (plus:DI
          (zero_extend:DI (match_operand:SI 1 "register_operand" "r"))
          (match_operand:DI 2 "register_operand" "r"))))]
  "TARGET_64BIT && TARGET_XTHEADMEMIDX && TARGET_XTHEADFMEMIDX
   && (!HARD_REGISTER_NUM_P (REGNO (operands[0])) || HARDFP_REG_P (REGNO (operands[0])))"
  "#"
  "&& reload_completed"
  [(set (match_dup 0)
        (mem:TH_M_NOEXTF (plus:DI
          (match_dup 2)
          (zero_extend:DI (match_dup 1)))))]
  ""
  [(set_attr "move_type" "fpload")
   (set_attr "mode" "<UNITMODE>")
   (set_attr "type" "fmove")
   (set (attr "length") (const_int 16))])

(define_insn_and_split "*th_fmemidx_UZ_c"
  [(set (mem:TH_M_ANYF (plus:DI
          (zero_extend:DI (match_operand:SI 1 "register_operand" "r"))
          (match_operand:DI 2 "register_operand" "r")))
        (match_operand:TH_M_ANYF 0 "register_operand" "f"))]
  "TARGET_64BIT && TARGET_XTHEADMEMIDX && TARGET_XTHEADFMEMIDX"
  "#"
  "&& 1"
  [(set (mem:TH_M_ANYF (plus:DI
          (match_dup 2)
          (zero_extend:DI (match_dup 1))))
        (match_dup 0))]
)

(include "thead-peephole.md")
