Home | History | Annotate | Line # | Download | only in riscv
      1 /* Subroutines used for code generation for RISC-V.
      2    Copyright (C) 2023-2024 Free Software Foundation, Inc.
      3    Contributed by Christoph Mllner (christoph.muellner (at) vrull.eu).
      4 
      5    This file is part of GCC.
      6 
      7    GCC is free software; you can redistribute it and/or modify
      8    it under the terms of the GNU General Public License as published by
      9    the Free Software Foundation; either version 3, or (at your option)
     10    any later version.
     11 
     12    GCC is distributed in the hope that it will be useful,
     13    but WITHOUT ANY WARRANTY; without even the implied warranty of
     14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15    GNU General Public License for more details.
     16 
     17    You should have received a copy of the GNU General Public License
     18    along with GCC; see the file COPYING3.  If not see
     19    <http://www.gnu.org/licenses/>.  */
     20 
     21 #define IN_TARGET_CODE 1
     22 
     23 #include "config.h"
     24 #include "system.h"
     25 #include "coretypes.h"
     26 #include "target.h"
     27 #include "backend.h"
     28 #include "tree.h"
     29 #include "rtl.h"
     30 #include "insn-attr.h"
     31 #include "explow.h"
     32 #include "memmodel.h"
     33 #include "emit-rtl.h"
     34 #include "optabs.h"
     35 #include "poly-int.h"
     36 #include "output.h"
     37 #include "regs.h"
     38 #include "riscv-protos.h"
     39 
     40 /* If X is a PLUS of a CONST_INT, return the two terms in *BASE_PTR
     41    and *OFFSET_PTR.  Return X in *BASE_PTR and 0 in *OFFSET_PTR otherwise.  */
     42 
     43 static void
     44 split_plus (rtx x, rtx *base_ptr, HOST_WIDE_INT *offset_ptr)
     45 {
     46   if (GET_CODE (x) == PLUS && CONST_INT_P (XEXP (x, 1)))
     47     {
     48       *base_ptr = XEXP (x, 0);
     49       *offset_ptr = INTVAL (XEXP (x, 1));
     50     }
     51   else
     52     {
     53       *base_ptr = x;
     54       *offset_ptr = 0;
     55     }
     56 }
     57 
     58 /* Output a mempair instruction with the provided OPERANDS.
     59    LOAD_P is true if a we have a pair of loads (stores otherwise).
     60    MODE is the access mode (DI or SI).
     61    CODE is the extension code (UNKNOWN, SIGN_EXTEND or ZERO_EXTEND).
     62    This instruction does not handle invalid inputs gracefully,
     63    but is full of assertions to ensure that only valid instructions
     64    are emitted.  */
     65 
     66 const char *
     67 th_mempair_output_move (rtx operands[4], bool load_p,
     68 			machine_mode mode, RTX_CODE code)
     69 {
     70   rtx reg1, reg2, mem1, mem2, base1, base2;
     71   HOST_WIDE_INT offset1, offset2;
     72   rtx output_operands[5];
     73   const char* format;
     74 
     75   gcc_assert (mode == SImode || mode == DImode);
     76 
     77   /* Paired 64-bit access instructions have a fixed shift amount of 4.
     78      Paired 32-bit access instructions have a fixed shift amount of 3.  */
     79   unsigned shamt = (mode == DImode) ? 4 : 3;
     80 
     81   if (load_p)
     82     {
     83 	reg1 = copy_rtx (operands[0]);
     84 	reg2 = copy_rtx (operands[2]);
     85 	mem1 = copy_rtx (operands[1]);
     86 	mem2 = copy_rtx (operands[3]);
     87 
     88 	if (mode == SImode)
     89 	  if (code == ZERO_EXTEND)
     90 	    format = "th.lwud\t%0, %1, (%2), %3, %4";
     91 	  else //SIGN_EXTEND or UNKNOWN
     92 	    format = "th.lwd\t%0, %1, (%2), %3, %4";
     93 	else
     94 	  format = "th.ldd\t%0, %1, (%2), %3, %4";
     95     }
     96   else
     97     {
     98 	reg1 = copy_rtx (operands[1]);
     99 	reg2 = copy_rtx (operands[3]);
    100 	mem1 = copy_rtx (operands[0]);
    101 	mem2 = copy_rtx (operands[2]);
    102 
    103 	if (mode == SImode)
    104 	  format = "th.swd\t%z0, %z1, (%2), %3, %4";
    105 	else
    106 	  format = "th.sdd\t%z0, %z1, (%2), %3, %4";
    107     }
    108 
    109   split_plus (XEXP (mem1, 0), &base1, &offset1);
    110   split_plus (XEXP (mem2, 0), &base2, &offset2);
    111   gcc_assert (rtx_equal_p (base1, base2));
    112   auto size1 = MEM_SIZE (mem1);
    113   auto size2 = MEM_SIZE (mem2);
    114   gcc_assert (known_eq (size1, size2));
    115   gcc_assert (known_eq (offset1 + size1, offset2));
    116 
    117   HOST_WIDE_INT imm2 = offset1 >> shamt;
    118 
    119   /* Make sure all mempair instruction constraints are met.  */
    120   gcc_assert (imm2 >= 0 && imm2 < 4);
    121   gcc_assert ((imm2 << shamt) == offset1);
    122   gcc_assert (REG_P (reg1));
    123   gcc_assert (REG_P (reg2));
    124   gcc_assert (REG_P (base1));
    125   if (load_p)
    126     {
    127       gcc_assert (REGNO (reg1) != REGNO (reg2));
    128       gcc_assert (REGNO (reg1) != REGNO (base1));
    129       gcc_assert (REGNO (reg2) != REGNO (base1));
    130     }
    131 
    132   /* Output the mempair instruction.  */
    133   output_operands[0] = copy_rtx (reg1);
    134   output_operands[1] = copy_rtx (reg2);
    135   output_operands[2] = copy_rtx (base1);
    136   output_operands[3] = gen_rtx_CONST_INT (mode, imm2);
    137   output_operands[4] = gen_rtx_CONST_INT (mode, shamt);
    138   output_asm_insn (format, output_operands);
    139 
    140   return "";
    141 }
    142 
    143 /* Analyse if a pair of loads/stores MEM1 and MEM2 with given MODE
    144    are consecutive so they can be merged into a mempair instruction.
    145    RESERVED will be set to true, if a reversal of the accesses is
    146    required (false otherwise). Returns true if the accesses can be
    147    merged (even if reversing is necessary) and false if not.  */
    148 
    149 static bool
    150 th_mempair_check_consecutive_mems (machine_mode mode, rtx *mem1, rtx *mem2,
    151 				   bool *reversed)
    152 {
    153   rtx base1, base2, offset1, offset2;
    154   extract_base_offset_in_addr (*mem1, &base1, &offset1);
    155   extract_base_offset_in_addr (*mem2, &base2, &offset2);
    156 
    157   /* Make sure both mems are in base+offset form.  */
    158   if (!base1 || !base2)
    159     return false;
    160 
    161   /* If both mems use the same base register, just check the offsets.  */
    162   if (rtx_equal_p (base1, base2))
    163     {
    164       auto size = GET_MODE_SIZE (mode);
    165 
    166       if (known_eq (UINTVAL (offset1) + size, UINTVAL (offset2)))
    167 	{
    168 	  *reversed = false;
    169 	  return true;
    170 	}
    171 
    172       if (known_eq (UINTVAL (offset2) + size, UINTVAL (offset1)))
    173 	{
    174 	  *reversed = true;
    175 	  return true;
    176 	}
    177 
    178       return false;
    179     }
    180 
    181   return false;
    182 }
    183 
    184 /* Check if the given MEM can be used to define the address of a mempair
    185    instruction.  */
    186 
    187 static bool
    188 th_mempair_operand_p (rtx mem, machine_mode mode)
    189 {
    190   if (!MEM_SIZE_KNOWN_P (mem))
    191     return false;
    192 
    193   /* Only DI or SI mempair instructions exist.  */
    194   gcc_assert (mode == SImode || mode == DImode);
    195   auto mem_sz = MEM_SIZE (mem);
    196   auto mode_sz = GET_MODE_SIZE (mode);
    197   if (!known_eq (mem_sz, mode_sz))
    198     return false;
    199 
    200   /* Paired 64-bit access instructions have a fixed shift amount of 4.
    201      Paired 32-bit access instructions have a fixed shift amount of 3.  */
    202   machine_mode mem_mode = GET_MODE (mem);
    203   unsigned shamt = (mem_mode == DImode) ? 4 : 3;
    204 
    205   rtx base;
    206   HOST_WIDE_INT offset;
    207   split_plus (XEXP (mem, 0), &base, &offset);
    208   HOST_WIDE_INT imm2 = offset >> shamt;
    209 
    210   if (imm2 < 0 || imm2 >= 4)
    211     return false;
    212 
    213   if ((imm2 << shamt) != offset)
    214     return false;
    215 
    216   return true;
    217 }
    218 
    219 static bool
    220 th_mempair_load_overlap_p (rtx reg1, rtx reg2, rtx mem)
    221 {
    222   if (REGNO (reg1) == REGNO (reg2))
    223     return true;
    224 
    225   if (reg_overlap_mentioned_p (reg1, mem))
    226     return true;
    227 
    228   rtx base;
    229   HOST_WIDE_INT offset;
    230   split_plus (XEXP (mem, 0), &base, &offset);
    231 
    232   if (!REG_P (base))
    233     return true;
    234 
    235   if (REG_P (base))
    236     {
    237       if (REGNO (base) == REGNO (reg1)
    238 	  || REGNO (base) == REGNO (reg2))
    239 	return true;
    240     }
    241 
    242   return false;
    243 }
    244 
    245 /* Given OPERANDS of consecutive load/store, check if we can merge
    246    them into load-pair or store-pair instructions.
    247    LOAD is true if they are load instructions.
    248    MODE is the mode of memory operation.  */
    249 
    250 bool
    251 th_mempair_operands_p (rtx operands[4], bool load_p,
    252 		       machine_mode mode)
    253 {
    254   rtx mem_1, mem_2, reg_1, reg_2;
    255 
    256   if (load_p)
    257     {
    258       reg_1 = operands[0];
    259       mem_1 = operands[1];
    260       reg_2 = operands[2];
    261       mem_2 = operands[3];
    262       if (!REG_P (reg_1) || !REG_P (reg_2))
    263 	return false;
    264       if (th_mempair_load_overlap_p (reg_1, reg_2, mem_1))
    265 	return false;
    266       if (th_mempair_load_overlap_p (reg_1, reg_2, mem_2))
    267 	return false;
    268     }
    269   else
    270     {
    271       mem_1 = operands[0];
    272       reg_1 = operands[1];
    273       mem_2 = operands[2];
    274       reg_2 = operands[3];
    275     }
    276 
    277   /* Check if the registers are GP registers.  */
    278   if (!REG_P (reg_1) || !GP_REG_P (REGNO (reg_1))
    279       || !REG_P (reg_2) || !GP_REG_P (REGNO (reg_2)))
    280     return false;
    281 
    282   /* The mems cannot be volatile.  */
    283   if (!MEM_P (mem_1) || !MEM_P (mem_2))
    284     return false;
    285   if (MEM_VOLATILE_P (mem_1) || MEM_VOLATILE_P (mem_2))
    286     return false;
    287 
    288 
    289   /* Check if the addresses are in the form of [base+offset].  */
    290   bool reversed = false;
    291   if (!th_mempair_check_consecutive_mems (mode, &mem_1, &mem_2, &reversed))
    292     return false;
    293 
    294   /* If necessary, reverse the local copy of the operands to simplify
    295      testing of alignments and mempair operand.  */
    296   if (reversed)
    297     {
    298       std::swap (mem_1, mem_2);
    299       std::swap (reg_1, reg_2);
    300     }
    301 
    302   /* If we have slow unaligned access, we only accept aligned memory.  */
    303   if (riscv_slow_unaligned_access_p
    304       && known_lt (MEM_ALIGN (mem_1), GET_MODE_SIZE (mode) * BITS_PER_UNIT))
    305     return false;
    306 
    307   /* The first memory accesses must be a mempair operand.  */
    308   if (!th_mempair_operand_p (mem_1, mode))
    309     return false;
    310 
    311   /* The operands must be of the same size.  */
    312   gcc_assert (known_eq (GET_MODE_SIZE (GET_MODE (mem_1)),
    313 			GET_MODE_SIZE (GET_MODE (mem_2))));
    314 
    315   return true;
    316 }
    317 
    318 /* Given OPERANDS of consecutive load/store that can be merged,
    319    swap them if they are not in ascending order.  */
    320 
    321 void
    322 th_mempair_order_operands (rtx operands[4], bool load_p, machine_mode mode)
    323 {
    324   int mem_op = load_p ? 1 : 0;
    325   bool reversed = false;
    326   if (!th_mempair_check_consecutive_mems (mode,
    327 					  operands + mem_op,
    328 					  operands + mem_op + 2,
    329 					  &reversed))
    330     gcc_unreachable ();
    331 
    332   if (reversed)
    333     {
    334       /* Irrespective of whether this is a load or a store,
    335 	 we do the same swap.  */
    336       std::swap (operands[0], operands[2]);
    337       std::swap (operands[1], operands[3]);
    338     }
    339 }
    340 
    341 /* Similar like riscv_save_reg, but saves two registers to memory
    342    and marks the resulting instruction as frame-related.  */
    343 
    344 static void
    345 th_mempair_save_regs (rtx operands[4])
    346 {
    347   rtx set1 = gen_rtx_SET (operands[0], operands[1]);
    348   rtx set2 = gen_rtx_SET (operands[2], operands[3]);
    349   rtx dwarf = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (2));
    350   rtx insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set1, set2)));
    351   RTX_FRAME_RELATED_P (insn) = 1;
    352 
    353   XVECEXP (dwarf, 0, 0) = copy_rtx (set1);
    354   XVECEXP (dwarf, 0, 1) = copy_rtx (set2);
    355   RTX_FRAME_RELATED_P (XVECEXP (dwarf, 0, 0)) = 1;
    356   RTX_FRAME_RELATED_P (XVECEXP (dwarf, 0, 1)) = 1;
    357   add_reg_note (insn, REG_FRAME_RELATED_EXPR, dwarf);
    358 }
    359 
    360 /* Similar like riscv_restore_reg, but restores two registers from memory
    361    and marks the instruction frame-related.  */
    362 
    363 static void
    364 th_mempair_restore_regs (rtx operands[4])
    365 {
    366   rtx set1 = gen_rtx_SET (operands[0], operands[1]);
    367   rtx set2 = gen_rtx_SET (operands[2], operands[3]);
    368   rtx insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set1, set2)));
    369   RTX_FRAME_RELATED_P (insn) = 1;
    370   add_reg_note (insn, REG_CFA_RESTORE, operands[0]);
    371   add_reg_note (insn, REG_CFA_RESTORE, operands[2]);
    372 }
    373 
    374 /* Prepare the OPERANDS array to emit a mempair instruction using the
    375    provided information. No checks are performed, the resulting array
    376    should be validated using th_mempair_operands_p().  */
    377 
    378 void
    379 th_mempair_prepare_save_restore_operands (rtx operands[4],
    380 					  bool load_p, machine_mode mode,
    381 					  int regno, HOST_WIDE_INT offset,
    382 					  int regno2, HOST_WIDE_INT offset2)
    383 {
    384   int reg_op = load_p ? 0 : 1;
    385   int mem_op = load_p ? 1 : 0;
    386 
    387   rtx mem1 = plus_constant (mode, stack_pointer_rtx, offset);
    388   mem1 = gen_frame_mem (mode, mem1);
    389   rtx mem2 = plus_constant (mode, stack_pointer_rtx, offset2);
    390   mem2 = gen_frame_mem (mode, mem2);
    391 
    392   operands[reg_op] = gen_rtx_REG (mode, regno);
    393   operands[mem_op] = mem1;
    394   operands[2 + reg_op] = gen_rtx_REG (mode, regno2);
    395   operands[2 + mem_op] = mem2;
    396 }
    397 
    398 /* Emit a mempair instruction to save/restore two registers to/from stack.  */
    399 
    400 void
    401 th_mempair_save_restore_regs (rtx operands[4], bool load_p,
    402 				 machine_mode mode)
    403 {
    404   gcc_assert (th_mempair_operands_p (operands, load_p, mode));
    405 
    406   th_mempair_order_operands (operands, load_p, mode);
    407 
    408   if (load_p)
    409     th_mempair_restore_regs (operands);
    410   else
    411     th_mempair_save_regs (operands);
    412 }
    413 
    414 /* Return true if X can be represented as signed immediate of NBITS bits.
    415    The immediate is assumed to be shifted by LSHAMT bits left.  */
    416 
    417 static bool
    418 valid_signed_immediate (rtx x, unsigned nbits, unsigned lshamt)
    419 {
    420   if (GET_CODE (x) != CONST_INT)
    421     return false;
    422 
    423   HOST_WIDE_INT v = INTVAL (x);
    424 
    425   HOST_WIDE_INT vunshifted = v >> lshamt;
    426 
    427   /* Make sure we did not shift out any bits.  */
    428   if (vunshifted << lshamt != v)
    429     return false;
    430 
    431   unsigned HOST_WIDE_INT imm_reach = 1LL << nbits;
    432   return ((unsigned HOST_WIDE_INT) vunshifted + imm_reach/2 < imm_reach);
    433 }
    434 
    435 /* Return the address RTX of a move to/from memory
    436    instruction.  */
    437 
    438 static rtx
    439 th_get_move_mem_addr (rtx dest, rtx src, bool load)
    440 {
    441   rtx mem;
    442 
    443   if (load)
    444     mem = src;
    445   else
    446     mem = dest;
    447 
    448   gcc_assert (GET_CODE (mem) == MEM);
    449   return XEXP (mem, 0);
    450 }
    451 
    452 /* Return true if X is a valid address for T-Head's memory addressing modes
    453    with pre/post modification for machine mode MODE.
    454    If it is, fill in INFO appropriately (if non-NULL).
    455    If STRICT_P is true then REG_OK_STRICT is in effect.  */
    456 
    457 static bool
    458 th_memidx_classify_address_modify (struct riscv_address_info *info, rtx x,
    459 				   machine_mode mode, bool strict_p)
    460 {
    461   if (!TARGET_XTHEADMEMIDX)
    462     return false;
    463 
    464   if (GET_MODE_CLASS (mode) != MODE_INT
    465       || GET_MODE_SIZE (mode).to_constant () > UNITS_PER_WORD)
    466     return false;
    467 
    468   if (GET_CODE (x) != POST_MODIFY
    469       && GET_CODE (x) != PRE_MODIFY)
    470     return false;
    471 
    472   rtx reg = XEXP (x, 0);
    473   rtx exp = XEXP (x, 1);
    474   rtx expreg = XEXP (exp, 0);
    475   rtx expoff = XEXP (exp, 1);
    476 
    477   if (GET_CODE (exp) != PLUS
    478       || !rtx_equal_p (expreg, reg)
    479       || !CONST_INT_P (expoff)
    480       || !riscv_valid_base_register_p (reg, mode, strict_p))
    481     return false;
    482 
    483   /* The offset is calculated as (sign_extend(imm5) << imm2)  */
    484   const int shamt_bits = 2;
    485   for (int shamt = 0; shamt < (1 << shamt_bits); shamt++)
    486     {
    487       const int nbits = 5;
    488       if (valid_signed_immediate (expoff, nbits, shamt))
    489 	{
    490 	  if (info)
    491 	    {
    492 	      info->type = ADDRESS_REG_WB;
    493 	      info->reg = reg;
    494 	      info->offset = expoff;
    495 	      info->shift = shamt;
    496 	    }
    497 	  return true;
    498 	}
    499     }
    500 
    501   return false;
    502 }
    503 
    504 /* Return TRUE if X is a MEM with a legitimate modify address.  */
    505 
    506 bool
    507 th_memidx_legitimate_modify_p (rtx x)
    508 {
    509   if (!MEM_P (x))
    510     return false;
    511 
    512   /* Get the mode from the MEM and unpack it.  */
    513   machine_mode mode = GET_MODE (x);
    514   x = XEXP (x, 0);
    515 
    516   return th_memidx_classify_address_modify (NULL, x, mode, reload_completed);
    517 }
    518 
    519 /* Return TRUE if X is a MEM with a legitimate modify address
    520    and the address is POST_MODIFY (if POST is true) or a PRE_MODIFY
    521    (otherwise).  */
    522 
    523 bool
    524 th_memidx_legitimate_modify_p (rtx x, bool post)
    525 {
    526   if (!th_memidx_legitimate_modify_p (x))
    527     return false;
    528 
    529   /* Unpack the MEM and check the code.  */
    530   x = XEXP (x, 0);
    531   if (post)
    532     return GET_CODE (x) == POST_MODIFY;
    533   else
    534     return GET_CODE (x) == PRE_MODIFY;
    535 }
    536 
    537 /* Provide a buffer for a th.lXia/th.lXib/th.sXia/th.sXib instruction
    538    for the given MODE. If LOAD is true, a load instruction will be
    539    provided (otherwise, a store instruction). If X is not suitable
    540    return NULL.  */
    541 
    542 static const char *
    543 th_memidx_output_modify (rtx dest, rtx src, machine_mode mode, bool load)
    544 {
    545   char format[24];
    546   rtx output_operands[2];
    547   rtx x = th_get_move_mem_addr (dest, src, load);
    548 
    549   /* Validate x.  */
    550   if (!th_memidx_classify_address_modify (NULL, x, mode, reload_completed))
    551     return NULL;
    552 
    553   int index = exact_log2 (GET_MODE_SIZE (mode).to_constant ());
    554   bool post = GET_CODE (x) == POST_MODIFY;
    555 
    556   const char *const insn[][4] = {
    557     {
    558       "th.sbi%s\t%%z1,%%0",
    559       "th.shi%s\t%%z1,%%0",
    560       "th.swi%s\t%%z1,%%0",
    561       "th.sdi%s\t%%z1,%%0"
    562     },
    563     {
    564       "th.lbui%s\t%%0,%%1",
    565       "th.lhui%s\t%%0,%%1",
    566       "th.lwi%s\t%%0,%%1",
    567       "th.ldi%s\t%%0,%%1"
    568     }
    569   };
    570 
    571   snprintf (format, sizeof (format), insn[load][index], post ? "a" : "b");
    572   output_operands[0] = dest;
    573   output_operands[1] = src;
    574   output_asm_insn (format, output_operands);
    575   return "";
    576 }
    577 
    578 static bool
    579 is_memidx_mode (machine_mode mode)
    580 {
    581   if (mode == QImode || mode == HImode || mode == SImode)
    582     return true;
    583 
    584   if (mode == DImode && TARGET_64BIT)
    585     return true;
    586 
    587   return false;
    588 }
    589 
    590 static bool
    591 is_fmemidx_mode (machine_mode mode)
    592 {
    593   if (mode == SFmode && TARGET_HARD_FLOAT)
    594     return true;
    595 
    596   if (mode == DFmode && TARGET_DOUBLE_FLOAT)
    597     return true;
    598 
    599   return false;
    600 }
    601 
    602 /* Return true if X is a valid address for T-Head's memory addressing modes
    603    with scaled register offsets for machine mode MODE.
    604    If it is, fill in INFO appropriately (if non-NULL).
    605    If STRICT_P is true then REG_OK_STRICT is in effect.  */
    606 
    607 static bool
    608 th_memidx_classify_address_index (struct riscv_address_info *info, rtx x,
    609 				  machine_mode mode, bool strict_p)
    610 {
    611   /* Ensure that the mode is supported.  */
    612   if (!(TARGET_XTHEADMEMIDX && is_memidx_mode (mode))
    613       && !(TARGET_XTHEADMEMIDX
    614 	   && TARGET_XTHEADFMEMIDX && is_fmemidx_mode (mode)))
    615     return false;
    616 
    617   if (GET_CODE (x) != PLUS)
    618     return false;
    619 
    620   rtx reg = XEXP (x, 0);
    621   enum riscv_address_type type;
    622   rtx offset = XEXP (x, 1);
    623   int shift;
    624 
    625   if (!riscv_valid_base_register_p (reg, mode, strict_p))
    626     return false;
    627 
    628   /* (reg:X) */
    629   if (REG_P (offset)
    630       && GET_MODE (offset) == Xmode)
    631     {
    632       type = ADDRESS_REG_REG;
    633       shift = 0;
    634       offset = offset;
    635     }
    636   /* (zero_extend:DI (reg:SI)) */
    637   else if (GET_CODE (offset) == ZERO_EXTEND
    638 	   && GET_MODE (offset) == DImode
    639 	   && GET_MODE (XEXP (offset, 0)) == SImode)
    640     {
    641       type = ADDRESS_REG_UREG;
    642       shift = 0;
    643       offset = XEXP (offset, 0);
    644     }
    645   /* (ashift:X (reg:X) (const_int shift)) */
    646   else if (GET_CODE (offset) == ASHIFT
    647 	   && GET_MODE (offset) == Xmode
    648 	   && REG_P (XEXP (offset, 0))
    649 	   && GET_MODE (XEXP (offset, 0)) == Xmode
    650 	   && CONST_INT_P (XEXP (offset, 1))
    651 	   && IN_RANGE (INTVAL (XEXP (offset, 1)), 0, 3))
    652     {
    653       type = ADDRESS_REG_REG;
    654       shift = INTVAL (XEXP (offset, 1));
    655       offset = XEXP (offset, 0);
    656     }
    657   /* (ashift:DI (zero_extend:DI (reg:SI)) (const_int shift)) */
    658   else if (GET_CODE (offset) == ASHIFT
    659 	   && GET_MODE (offset) == DImode
    660 	   && GET_CODE (XEXP (offset, 0)) == ZERO_EXTEND
    661 	   && GET_MODE (XEXP (offset, 0)) == DImode
    662 	   && GET_MODE (XEXP (XEXP (offset, 0), 0)) == SImode
    663 	   && CONST_INT_P (XEXP (offset, 1))
    664 	   && IN_RANGE(INTVAL (XEXP (offset, 1)), 0, 3))
    665     {
    666       type = ADDRESS_REG_UREG;
    667       shift = INTVAL (XEXP (offset, 1));
    668       offset = XEXP (XEXP (offset, 0), 0);
    669     }
    670   else
    671     return false;
    672 
    673   if (!strict_p && GET_CODE (offset) == SUBREG)
    674     offset = SUBREG_REG (offset);
    675 
    676   if (!REG_P (offset)
    677       || !riscv_regno_mode_ok_for_base_p (REGNO (offset), mode, strict_p))
    678     return false;
    679 
    680   if (info)
    681     {
    682       info->reg = reg;
    683       info->type = type;
    684       info->offset = offset;
    685       info->shift = shift;
    686     }
    687   return true;
    688 }
    689 
    690 /* Return TRUE if X is a MEM with a legitimate indexed address.  */
    691 
    692 bool
    693 th_memidx_legitimate_index_p (rtx x)
    694 {
    695   if (!MEM_P (x))
    696     return false;
    697 
    698   /* Get the mode from the MEM and unpack it.  */
    699   machine_mode mode = GET_MODE (x);
    700   x = XEXP (x, 0);
    701 
    702   return th_memidx_classify_address_index (NULL, x, mode, reload_completed);
    703 }
    704 
    705 /* Return TRUE if X is a MEM with a legitimate indexed address
    706    and the offset register is zero-extended (if UINDEX is true)
    707    or sign-extended (otherwise).  */
    708 
    709 bool
    710 th_memidx_legitimate_index_p (rtx x, bool uindex)
    711 {
    712   if (!MEM_P (x))
    713     return false;
    714 
    715   /* Get the mode from the MEM and unpack it.  */
    716   machine_mode mode = GET_MODE (x);
    717   x = XEXP (x, 0);
    718 
    719   struct riscv_address_info info;
    720   if (!th_memidx_classify_address_index (&info, x, mode, reload_completed))
    721     return false;
    722 
    723   if (uindex)
    724     return info.type == ADDRESS_REG_UREG;
    725   else
    726     return info.type == ADDRESS_REG_REG;
    727 }
    728 
    729 /* Provide a buffer for a th.lrX/th.lurX/th.srX/th.surX instruction
    730    for the given MODE. If LOAD is true, a load instruction will be
    731    provided (otherwise, a store instruction). If X is not suitable
    732    return NULL.  */
    733 
    734 static const char *
    735 th_memidx_output_index (rtx dest, rtx src, machine_mode mode, bool load)
    736 {
    737   struct riscv_address_info info;
    738   char format[24];
    739   rtx output_operands[2];
    740   rtx x = th_get_move_mem_addr (dest, src, load);
    741 
    742   /* Validate x.  */
    743   if (!th_memidx_classify_address_index (&info, x, mode, reload_completed))
    744     return NULL;
    745 
    746   int index = exact_log2 (GET_MODE_SIZE (mode).to_constant ());
    747   bool uindex = info.type == ADDRESS_REG_UREG;
    748 
    749   const char *const insn[][4] = {
    750     {
    751       "th.s%srb\t%%z1,%%0",
    752       "th.s%srh\t%%z1,%%0",
    753       "th.s%srw\t%%z1,%%0",
    754       "th.s%srd\t%%z1,%%0"
    755     },
    756     {
    757       "th.l%srbu\t%%0,%%1",
    758       "th.l%srhu\t%%0,%%1",
    759       "th.l%srw\t%%0,%%1",
    760       "th.l%srd\t%%0,%%1"
    761     }
    762   };
    763 
    764   snprintf (format, sizeof (format), insn[load][index], uindex ? "u" : "");
    765   output_operands[0] = dest;
    766   output_operands[1] = src;
    767   output_asm_insn (format, output_operands);
    768   return "";
    769 }
    770 
    771 /* Provide a buffer for a th.flX/th.fluX/th.fsX/th.fsuX instruction
    772    for the given MODE. If LOAD is true, a load instruction will be
    773    provided (otherwise, a store instruction). If X is not suitable
    774    return NULL.  */
    775 
    776 static const char *
    777 th_fmemidx_output_index (rtx dest, rtx src, machine_mode mode, bool load)
    778 {
    779   struct riscv_address_info info;
    780   char format[24];
    781   rtx output_operands[2];
    782   rtx x = th_get_move_mem_addr (dest, src, load);
    783 
    784   /* Validate x.  */
    785   if (!th_memidx_classify_address_index (&info, x, mode, false))
    786     return NULL;
    787 
    788   int index = exact_log2 (GET_MODE_SIZE (mode).to_constant ()) - 2;
    789   bool uindex = info.type == ADDRESS_REG_UREG;
    790 
    791   const char *const insn[][2] = {
    792     {
    793       "th.fs%srw\t%%z1,%%0",
    794       "th.fs%srd\t%%z1,%%0"
    795     },
    796     {
    797       "th.fl%srw\t%%0,%%1",
    798       "th.fl%srd\t%%0,%%1"
    799     }
    800   };
    801 
    802   snprintf (format, sizeof (format), insn[load][index], uindex ? "u" : "");
    803   output_operands[0] = dest;
    804   output_operands[1] = src;
    805   output_asm_insn (format, output_operands);
    806   return "";
    807 }
    808 
    809 /* Return true if X is a valid address for T-Head's memory addressing modes
    810    for machine mode MODE.  If it is, fill in INFO appropriately (if non-NULL).
    811    If STRICT_P is true then REG_OK_STRICT is in effect.  */
    812 
    813 bool
    814 th_classify_address (struct riscv_address_info *info, rtx x,
    815 		     machine_mode mode, bool strict_p)
    816 {
    817   switch (GET_CODE (x))
    818     {
    819     case PLUS:
    820       if (th_memidx_classify_address_index (info, x, mode, strict_p))
    821 	return true;
    822       break;
    823 
    824     case POST_MODIFY:
    825     case PRE_MODIFY:
    826       if (th_memidx_classify_address_modify (info, x, mode, strict_p))
    827 	return true;
    828       break;
    829 
    830     default:
    831       return false;
    832   }
    833 
    834     return false;
    835 }
    836 
    837 /* Provide a string containing a XTheadMemIdx instruction for the given
    838    MODE from the provided SRC to the provided DEST.
    839    A pointer to a NULL-terminated string containing the instruction will
    840    be returned if a suitable instruction is available. Otherwise, this
    841    function returns NULL.  */
    842 
    843 const char *
    844 th_output_move (rtx dest, rtx src)
    845 {
    846   enum rtx_code dest_code, src_code;
    847   machine_mode mode;
    848   const char *insn = NULL;
    849 
    850   dest_code = GET_CODE (dest);
    851   src_code = GET_CODE (src);
    852   mode = GET_MODE (dest);
    853 
    854   if (!(mode == GET_MODE (src) || src == CONST0_RTX (mode)))
    855     return NULL;
    856 
    857   if (dest_code == REG && src_code == MEM)
    858     {
    859       if (GET_MODE_CLASS (mode) == MODE_INT
    860 	  || (GET_MODE_CLASS (mode) == MODE_FLOAT && GP_REG_P (REGNO (dest))))
    861 	{
    862 	  if ((insn = th_memidx_output_index (dest, src, mode, true)))
    863 	    return insn;
    864 	  if ((insn = th_memidx_output_modify (dest, src, mode, true)))
    865 	    return insn;
    866 	}
    867       else if (GET_MODE_CLASS (mode) == MODE_FLOAT && HARDFP_REG_P (REGNO (dest)))
    868 	{
    869 	  if ((insn = th_fmemidx_output_index (dest, src, mode, true)))
    870 	    return insn;
    871 	}
    872     }
    873   else if (dest_code == MEM && (src_code == REG || src == CONST0_RTX (mode)))
    874     {
    875       if (GET_MODE_CLASS (mode) == MODE_INT
    876 	  || src == CONST0_RTX (mode)
    877 	  || (GET_MODE_CLASS (mode) == MODE_FLOAT && GP_REG_P (REGNO (src))))
    878 	{
    879 	  if ((insn = th_memidx_output_index (dest, src, mode, false)))
    880 	    return insn;
    881 	  if ((insn = th_memidx_output_modify (dest, src, mode, false)))
    882 	    return insn;
    883 	}
    884       else if (GET_MODE_CLASS (mode) == MODE_FLOAT && HARDFP_REG_P (REGNO (src)))
    885 	{
    886 	  if ((insn = th_fmemidx_output_index (dest, src, mode, false)))
    887 	    return insn;
    888 	}
    889     }
    890   return NULL;
    891 }
    892 
    893 /* Define ASM_OUTPUT_OPCODE to do anything special before
    894    emitting an opcode.  */
    895 const char *
    896 th_asm_output_opcode (FILE *asm_out_file, const char *p)
    897 {
    898   /* We need to add th. prefix to all the xtheadvector
    899      instructions here.*/
    900   if (current_output_insn != NULL)
    901     {
    902       if (get_attr_type (current_output_insn) == TYPE_VSETVL)
    903 	{
    904 	  if (strstr (p, "zero"))
    905 	    {
    906 	      if (strstr (p, "zero,zero"))
    907 		return "th.vsetvli\tzero,zero,e%0,%m1";
    908 	      else
    909 		return "th.vsetvli\tzero,%z0,e%1,%m2";
    910 	    }
    911 	  else
    912 	    {
    913 	      return "th.vsetvli\t%z0,%z1,e%2,%m3";
    914 	    }
    915 	}
    916 
    917       if (get_attr_type (current_output_insn) == TYPE_VLDE ||
    918 	  get_attr_type (current_output_insn) == TYPE_VSTE ||
    919 	  get_attr_type (current_output_insn) == TYPE_VLDFF)
    920 	{
    921 	  if (strstr (p, "e8") || strstr (p, "e16") ||
    922 	      strstr (p, "e32") || strstr (p, "e64"))
    923 	    {
    924 	      get_attr_type (current_output_insn) == TYPE_VSTE
    925 				  ? fputs ("th.vse", asm_out_file)
    926 				  : fputs ("th.vle", asm_out_file);
    927 	      if (strstr (p, "e8"))
    928 		return p+4;
    929 	      else
    930 		return p+5;
    931 	    }
    932 	}
    933 
    934       if (get_attr_type (current_output_insn) == TYPE_VLDS ||
    935 	  get_attr_type (current_output_insn) == TYPE_VSTS)
    936 	{
    937 	  if (strstr (p, "vle8") || strstr (p, "vse8") ||
    938 	      strstr (p, "vle16") || strstr (p, "vse16") ||
    939 	      strstr (p, "vle32") || strstr (p, "vse32") ||
    940 	      strstr (p, "vle64") || strstr (p, "vse64"))
    941 	    {
    942 	      get_attr_type (current_output_insn) == TYPE_VSTS
    943 				  ? fputs ("th.vse", asm_out_file)
    944 				  : fputs ("th.vle", asm_out_file);
    945 	      if (strstr (p, "e8"))
    946 		return p+4;
    947 	      else
    948 		return p+5;
    949 	    }
    950 	  else if (strstr (p, "vlse8") || strstr (p, "vsse8") ||
    951 		   strstr (p, "vlse16") || strstr (p, "vsse16") ||
    952 		   strstr (p, "vlse32") || strstr (p, "vsse32") ||
    953 		   strstr (p, "vlse64") || strstr (p, "vsse64"))
    954 	    {
    955 	      get_attr_type (current_output_insn) == TYPE_VSTS
    956 				  ? fputs ("th.vsse", asm_out_file)
    957 				  : fputs ("th.vlse", asm_out_file);
    958 	      if (strstr (p, "e8"))
    959 		return p+5;
    960 	      else
    961 		return p+6;
    962 	    }
    963 	}
    964 
    965       if (get_attr_type (current_output_insn) == TYPE_VLDUX ||
    966 	  get_attr_type (current_output_insn) == TYPE_VLDOX)
    967 	{
    968 	  if (strstr (p, "ei"))
    969 	    {
    970 	      fputs ("th.vlxe", asm_out_file);
    971 	      if (strstr (p, "ei8"))
    972 		return p+7;
    973 	      else
    974 		return p+8;
    975 	    }
    976 	}
    977 
    978       if (get_attr_type (current_output_insn) == TYPE_VSTUX ||
    979 	  get_attr_type (current_output_insn) == TYPE_VSTOX)
    980 	{
    981 	  if (strstr (p, "ei"))
    982 	    {
    983 	      get_attr_type (current_output_insn) == TYPE_VSTUX
    984 				? fputs ("th.vsuxe", asm_out_file)
    985 				: fputs ("th.vsxe", asm_out_file);
    986 	      if (strstr (p, "ei8"))
    987 		return p+7;
    988 	      else
    989 		return p+8;
    990 	    }
    991 	}
    992 
    993       if (get_attr_type (current_output_insn) == TYPE_VLSEGDE ||
    994 	  get_attr_type (current_output_insn) == TYPE_VSSEGTE ||
    995 	  get_attr_type (current_output_insn) == TYPE_VLSEGDFF)
    996 	{
    997 	  get_attr_type (current_output_insn) == TYPE_VSSEGTE
    998 				? fputs ("th.vsseg", asm_out_file)
    999 				: fputs ("th.vlseg", asm_out_file);
   1000 	  asm_fprintf (asm_out_file, "%c", p[5]);
   1001 	  fputs ("e", asm_out_file);
   1002 	  if (strstr (p, "e8"))
   1003 	    return p+8;
   1004 	  else
   1005 	    return p+9;
   1006 	}
   1007 
   1008       if (get_attr_type (current_output_insn) == TYPE_VLSEGDS ||
   1009 	  get_attr_type (current_output_insn) == TYPE_VSSEGTS)
   1010 	{
   1011 	  get_attr_type (current_output_insn) == TYPE_VSSEGTS
   1012 				? fputs ("th.vssseg", asm_out_file)
   1013 				: fputs ("th.vlsseg", asm_out_file);
   1014 	  asm_fprintf (asm_out_file, "%c", p[6]);
   1015 	  fputs ("e", asm_out_file);
   1016 	  if (strstr (p, "e8"))
   1017 	    return p+9;
   1018 	  else
   1019 	    return p+10;
   1020 	}
   1021 
   1022       if (get_attr_type (current_output_insn) == TYPE_VLSEGDUX ||
   1023 	  get_attr_type (current_output_insn) == TYPE_VLSEGDOX)
   1024 	{
   1025 	  fputs ("th.vlxseg", asm_out_file);
   1026 	  asm_fprintf (asm_out_file, "%c", p[7]);
   1027 	  fputs ("e", asm_out_file);
   1028 	  if (strstr (p, "ei8"))
   1029 	    return p+11;
   1030 	  else
   1031 	    return p+12;
   1032 	}
   1033 
   1034       if (get_attr_type (current_output_insn) == TYPE_VSSEGTUX ||
   1035 	  get_attr_type (current_output_insn) == TYPE_VSSEGTOX)
   1036 	{
   1037 	  fputs ("th.vsxseg", asm_out_file);
   1038 	  asm_fprintf (asm_out_file, "%c", p[7]);
   1039 	  fputs ("e", asm_out_file);
   1040 	  if (strstr (p, "ei8"))
   1041 	    return p+11;
   1042 	  else
   1043 	    return p+12;
   1044 	}
   1045 
   1046       if (get_attr_type (current_output_insn) == TYPE_VNSHIFT)
   1047 	{
   1048 	  if (strstr (p, "vncvt"))
   1049 	    {
   1050 	      fputs ("th.vncvt.x.x.v", asm_out_file);
   1051 	      return p+11;
   1052 	    }
   1053 
   1054 	  strstr (p, "vnsrl") ? fputs ("th.vnsrl.v", asm_out_file)
   1055 			      : fputs ("th.vnsra.v", asm_out_file);
   1056 	  return p+7;
   1057 	}
   1058 
   1059       if (get_attr_type (current_output_insn) == TYPE_VNCLIP)
   1060 	{
   1061 	  if (strstr (p, "vnclipu"))
   1062 	    {
   1063 	      fputs ("th.vnclipu.v", asm_out_file);
   1064 	      return p+9;
   1065 	    }
   1066 	  else
   1067 	    {
   1068 	      fputs ("th.vnclip.v", asm_out_file);
   1069 	      return p+8;
   1070 	    }
   1071 	}
   1072 
   1073       if (get_attr_type (current_output_insn) == TYPE_VMPOP)
   1074 	{
   1075 	  fputs ("th.vmpopc", asm_out_file);
   1076 	  return p+5;
   1077 	}
   1078 
   1079       if (get_attr_type (current_output_insn) == TYPE_VMFFS)
   1080 	{
   1081 	  fputs ("th.vmfirst", asm_out_file);
   1082 	  return p+6;
   1083 	}
   1084 
   1085       if (get_attr_type (current_output_insn) == TYPE_VFNCVTFTOI ||
   1086 	  get_attr_type (current_output_insn) == TYPE_VFNCVTITOF)
   1087 	{
   1088 	  if (strstr (p, "xu"))
   1089 	    {
   1090 	      get_attr_type (current_output_insn) == TYPE_VFNCVTFTOI
   1091 			   ? fputs ("th.vfncvt.xu.f.v", asm_out_file)
   1092 			   : fputs ("th.vfncvt.f.xu.v", asm_out_file);
   1093 	      return p+13;
   1094 	}
   1095 	  else
   1096 	    {
   1097 	      get_attr_type (current_output_insn) == TYPE_VFNCVTFTOI
   1098 			   ? fputs ("th.vfncvt.x.f.v", asm_out_file)
   1099 			   : fputs ("th.vfncvt.f.x.v", asm_out_file);
   1100 	      return p+12;
   1101 	    }
   1102 	}
   1103 
   1104       if (get_attr_type (current_output_insn) == TYPE_VFNCVTFTOF)
   1105 	{
   1106 	  fputs ("th.vfncvt.f.f.v", asm_out_file);
   1107 	  return p+12;
   1108 	}
   1109 
   1110       if (get_attr_type (current_output_insn) == TYPE_VFREDU
   1111 	  && strstr (p, "sum"))
   1112 	{
   1113 	  fputs ("th.vfredsum", asm_out_file);
   1114 	  return p+9;
   1115 	}
   1116 
   1117       if (get_attr_type (current_output_insn) == TYPE_VFWREDU
   1118 	  && strstr (p, "sum"))
   1119 	{
   1120 	  fputs ("th.vfwredsum", asm_out_file);
   1121 	  return p+10;
   1122 	}
   1123 
   1124       if (p[0] == 'v')
   1125 	fputs ("th.", asm_out_file);
   1126     }
   1127 
   1128   return p;
   1129 }
   1130 
   1131 /* Implement TARGET_PRINT_OPERAND_ADDRESS for XTheadMemIdx.  */
   1132 
   1133 bool
   1134 th_print_operand_address (FILE *file, machine_mode mode, rtx x)
   1135 {
   1136   struct riscv_address_info addr;
   1137 
   1138   if (!th_classify_address (&addr, x, mode, reload_completed))
   1139     return false;
   1140 
   1141   switch (addr.type)
   1142     {
   1143     case ADDRESS_REG_REG:
   1144     case ADDRESS_REG_UREG:
   1145       fprintf (file, "%s,%s,%u", reg_names[REGNO (addr.reg)],
   1146 	       reg_names[REGNO (addr.offset)], addr.shift);
   1147       return true;
   1148 
   1149     case ADDRESS_REG_WB:
   1150       fprintf (file, "(%s)," HOST_WIDE_INT_PRINT_DEC ",%u",
   1151 	       reg_names[REGNO (addr.reg)],
   1152 	       INTVAL (addr.offset) >> addr.shift, addr.shift);
   1153 	return true;
   1154 
   1155     default:
   1156       gcc_unreachable ();
   1157     }
   1158 
   1159   gcc_unreachable ();
   1160 }
   1161 
   1162 /* Number array of registers X1, X5-X7, X10-X17, X28-X31, to be
   1163    operated on by instruction th.ipush/th.ipop in XTheadInt.  */
   1164 
   1165 int th_int_regs[] ={
   1166   RETURN_ADDR_REGNUM,
   1167   T0_REGNUM, T1_REGNUM, T2_REGNUM,
   1168   A0_REGNUM, A1_REGNUM, A2_REGNUM, A3_REGNUM,
   1169   A4_REGNUM, A5_REGNUM, A6_REGNUM, A7_REGNUM,
   1170   T3_REGNUM, T4_REGNUM, T5_REGNUM, T6_REGNUM,
   1171 };
   1172 
   1173 /* If MASK contains registers X1, X5-X7, X10-X17, X28-X31, then
   1174    return the mask composed of these registers, otherwise return
   1175    zero.  */
   1176 
   1177 unsigned int
   1178 th_int_get_mask (unsigned int mask)
   1179 {
   1180   unsigned int xtheadint_mask = 0;
   1181 
   1182   if (!TARGET_XTHEADINT || TARGET_64BIT)
   1183     return 0;
   1184 
   1185   for (unsigned int i = 0; i < ARRAY_SIZE (th_int_regs); i++)
   1186     {
   1187       if (!BITSET_P (mask, th_int_regs[i]))
   1188 	return 0;
   1189 
   1190       xtheadint_mask |= (1 << th_int_regs[i]);
   1191     }
   1192 
   1193   return xtheadint_mask; /* Usually 0xf003fce2.  */
   1194 }
   1195 
   1196 /* Returns the occupied frame needed to save registers X1, X5-X7,
   1197    X10-X17, X28-X31.  */
   1198 
   1199 unsigned int
   1200 th_int_get_save_adjustment (void)
   1201 {
   1202   gcc_assert (TARGET_XTHEADINT && !TARGET_64BIT);
   1203   return ARRAY_SIZE (th_int_regs) * UNITS_PER_WORD;
   1204 }
   1205 
   1206 rtx
   1207 th_int_adjust_cfi_prologue (unsigned int mask)
   1208 {
   1209   gcc_assert (TARGET_XTHEADINT && !TARGET_64BIT);
   1210 
   1211   rtx dwarf = NULL_RTX;
   1212   rtx adjust_sp_rtx, reg, mem, insn;
   1213   int saved_size = ARRAY_SIZE (th_int_regs) * UNITS_PER_WORD;
   1214   int offset = saved_size;
   1215 
   1216   for (int regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
   1217     if (BITSET_P (mask, regno - GP_REG_FIRST))
   1218       {
   1219 	offset -= UNITS_PER_WORD;
   1220 	reg = gen_rtx_REG (SImode, regno);
   1221 	mem = gen_frame_mem (SImode, plus_constant (Pmode,
   1222 						    stack_pointer_rtx,
   1223 						    offset));
   1224 
   1225 	insn = gen_rtx_SET (mem, reg);
   1226 	dwarf = alloc_reg_note (REG_CFA_OFFSET, insn, dwarf);
   1227       }
   1228 
   1229   /* Debug info for adjust sp.  */
   1230   adjust_sp_rtx =
   1231     gen_rtx_SET (stack_pointer_rtx,
   1232 		 gen_rtx_PLUS (GET_MODE (stack_pointer_rtx),
   1233 			       stack_pointer_rtx, GEN_INT (-saved_size)));
   1234   dwarf = alloc_reg_note (REG_CFA_ADJUST_CFA, adjust_sp_rtx, dwarf);
   1235 
   1236   return dwarf;
   1237 }
   1238