testsuite: Fix optimize_one.c FAIL on i686-linux
[official-gcc.git] / gcc / config / riscv / thead.cc
blob2f1d83fbbc7f1df1ba799e1aab219f8461b2dc36
1 /* Subroutines used for code generation for RISC-V.
2 Copyright (C) 2023-2024 Free Software Foundation, Inc.
3 Contributed by Christoph Müllner (christoph.muellner@vrull.eu).
5 This file is part of GCC.
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.
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.
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/>. */
21 #define IN_TARGET_CODE 1
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"
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. */
43 static void
44 split_plus (rtx x, rtx *base_ptr, HOST_WIDE_INT *offset_ptr)
46 if (GET_CODE (x) == PLUS && CONST_INT_P (XEXP (x, 1)))
48 *base_ptr = XEXP (x, 0);
49 *offset_ptr = INTVAL (XEXP (x, 1));
51 else
53 *base_ptr = x;
54 *offset_ptr = 0;
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. */
66 const char *
67 th_mempair_output_move (rtx operands[4], bool load_p,
68 machine_mode mode, RTX_CODE code)
70 rtx reg1, reg2, mem1, mem2, base1, base2;
71 HOST_WIDE_INT offset1, offset2;
72 rtx output_operands[5];
73 const char* format;
75 gcc_assert (mode == SImode || mode == DImode);
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;
81 if (load_p)
83 reg1 = copy_rtx (operands[0]);
84 reg2 = copy_rtx (operands[2]);
85 mem1 = copy_rtx (operands[1]);
86 mem2 = copy_rtx (operands[3]);
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";
96 else
98 reg1 = copy_rtx (operands[1]);
99 reg2 = copy_rtx (operands[3]);
100 mem1 = copy_rtx (operands[0]);
101 mem2 = copy_rtx (operands[2]);
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";
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));
117 HOST_WIDE_INT imm2 = offset1 >> shamt;
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)
127 gcc_assert (REGNO (reg1) != REGNO (reg2));
128 gcc_assert (REGNO (reg1) != REGNO (base1));
129 gcc_assert (REGNO (reg2) != REGNO (base1));
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);
140 return "";
143 /* Analyze 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. */
149 static bool
150 th_mempair_check_consecutive_mems (machine_mode mode, rtx *mem1, rtx *mem2,
151 bool *reversed)
153 rtx base1, base2, offset1, offset2;
154 extract_base_offset_in_addr (*mem1, &base1, &offset1);
155 extract_base_offset_in_addr (*mem2, &base2, &offset2);
157 /* Make sure both mems are in base+offset form. */
158 if (!base1 || !base2)
159 return false;
161 /* If both mems use the same base register, just check the offsets. */
162 if (rtx_equal_p (base1, base2))
164 auto size = GET_MODE_SIZE (mode);
166 if (known_eq (UINTVAL (offset1) + size, UINTVAL (offset2)))
168 *reversed = false;
169 return true;
172 if (known_eq (UINTVAL (offset2) + size, UINTVAL (offset1)))
174 *reversed = true;
175 return true;
178 return false;
181 return false;
184 /* Check if the given MEM can be used to define the address of a mempair
185 instruction. */
187 static bool
188 th_mempair_operand_p (rtx mem, machine_mode mode)
190 if (!MEM_SIZE_KNOWN_P (mem))
191 return false;
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;
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;
205 rtx base;
206 HOST_WIDE_INT offset;
207 split_plus (XEXP (mem, 0), &base, &offset);
208 HOST_WIDE_INT imm2 = offset >> shamt;
210 if (imm2 < 0 || imm2 >= 4)
211 return false;
213 if ((imm2 << shamt) != offset)
214 return false;
216 return true;
219 static bool
220 th_mempair_load_overlap_p (rtx reg1, rtx reg2, rtx mem)
222 if (REGNO (reg1) == REGNO (reg2))
223 return true;
225 if (reg_overlap_mentioned_p (reg1, mem))
226 return true;
228 rtx base;
229 HOST_WIDE_INT offset;
230 split_plus (XEXP (mem, 0), &base, &offset);
232 if (!REG_P (base))
233 return true;
235 if (REG_P (base))
237 if (REGNO (base) == REGNO (reg1)
238 || REGNO (base) == REGNO (reg2))
239 return true;
242 return false;
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. */
250 bool
251 th_mempair_operands_p (rtx operands[4], bool load_p,
252 machine_mode mode)
254 rtx mem_1, mem_2, reg_1, reg_2;
256 if (load_p)
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;
269 else
271 mem_1 = operands[0];
272 reg_1 = operands[1];
273 mem_2 = operands[2];
274 reg_2 = operands[3];
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;
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;
288 /* If we have slow unaligned access, we only accept aligned memory. */
289 if (riscv_slow_unaligned_access_p
290 && known_lt (MEM_ALIGN (mem_1), GET_MODE_SIZE (mode) * BITS_PER_UNIT))
291 return false;
293 /* Check if the addresses are in the form of [base+offset]. */
294 bool reversed = false;
295 if (!th_mempair_check_consecutive_mems (mode, &mem_1, &mem_2, &reversed))
296 return false;
298 /* The first memory accesses must be a mempair operand. */
299 if ((!reversed && !th_mempair_operand_p (mem_1, mode))
300 || (reversed && !th_mempair_operand_p (mem_2, mode)))
301 return false;
303 /* The operands must be of the same size. */
304 gcc_assert (known_eq (GET_MODE_SIZE (GET_MODE (mem_1)),
305 GET_MODE_SIZE (GET_MODE (mem_2))));
307 return true;
310 /* Given OPERANDS of consecutive load/store that can be merged,
311 swap them if they are not in ascending order. */
313 void
314 th_mempair_order_operands (rtx operands[4], bool load_p, machine_mode mode)
316 int mem_op = load_p ? 1 : 0;
317 bool reversed = false;
318 if (!th_mempair_check_consecutive_mems (mode,
319 operands + mem_op,
320 operands + mem_op + 2,
321 &reversed))
322 gcc_unreachable ();
324 if (reversed)
326 /* Irrespective of whether this is a load or a store,
327 we do the same swap. */
328 std::swap (operands[0], operands[2]);
329 std::swap (operands[1], operands[3]);
333 /* Similar like riscv_save_reg, but saves two registers to memory
334 and marks the resulting instruction as frame-related. */
336 static void
337 th_mempair_save_regs (rtx operands[4])
339 rtx set1 = gen_rtx_SET (operands[0], operands[1]);
340 rtx set2 = gen_rtx_SET (operands[2], operands[3]);
341 rtx dwarf = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (2));
342 rtx insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set1, set2)));
343 RTX_FRAME_RELATED_P (insn) = 1;
345 XVECEXP (dwarf, 0, 0) = copy_rtx (set1);
346 XVECEXP (dwarf, 0, 1) = copy_rtx (set2);
347 RTX_FRAME_RELATED_P (XVECEXP (dwarf, 0, 0)) = 1;
348 RTX_FRAME_RELATED_P (XVECEXP (dwarf, 0, 1)) = 1;
349 add_reg_note (insn, REG_FRAME_RELATED_EXPR, dwarf);
352 /* Similar like riscv_restore_reg, but restores two registers from memory
353 and marks the instruction frame-related. */
355 static void
356 th_mempair_restore_regs (rtx operands[4])
358 rtx set1 = gen_rtx_SET (operands[0], operands[1]);
359 rtx set2 = gen_rtx_SET (operands[2], operands[3]);
360 rtx insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set1, set2)));
361 RTX_FRAME_RELATED_P (insn) = 1;
362 add_reg_note (insn, REG_CFA_RESTORE, operands[0]);
363 add_reg_note (insn, REG_CFA_RESTORE, operands[2]);
366 /* Prepare the OPERANDS array to emit a mempair instruction using the
367 provided information. No checks are performed, the resulting array
368 should be validated using th_mempair_operands_p(). */
370 void
371 th_mempair_prepare_save_restore_operands (rtx operands[4],
372 bool load_p, machine_mode mode,
373 int regno, HOST_WIDE_INT offset,
374 int regno2, HOST_WIDE_INT offset2)
376 int reg_op = load_p ? 0 : 1;
377 int mem_op = load_p ? 1 : 0;
379 rtx mem1 = plus_constant (mode, stack_pointer_rtx, offset);
380 mem1 = gen_frame_mem (mode, mem1);
381 rtx mem2 = plus_constant (mode, stack_pointer_rtx, offset2);
382 mem2 = gen_frame_mem (mode, mem2);
384 operands[reg_op] = gen_rtx_REG (mode, regno);
385 operands[mem_op] = mem1;
386 operands[2 + reg_op] = gen_rtx_REG (mode, regno2);
387 operands[2 + mem_op] = mem2;
390 /* Emit a mempair instruction to save/restore two registers to/from stack. */
392 void
393 th_mempair_save_restore_regs (rtx operands[4], bool load_p,
394 machine_mode mode)
396 gcc_assert (th_mempair_operands_p (operands, load_p, mode));
398 th_mempair_order_operands (operands, load_p, mode);
400 if (load_p)
401 th_mempair_restore_regs (operands);
402 else
403 th_mempair_save_regs (operands);
406 /* Return true if X can be represented as signed immediate of NBITS bits.
407 The immediate is assumed to be shifted by LSHAMT bits left. */
409 static bool
410 valid_signed_immediate (rtx x, unsigned nbits, unsigned lshamt)
412 if (GET_CODE (x) != CONST_INT)
413 return false;
415 HOST_WIDE_INT v = INTVAL (x);
417 HOST_WIDE_INT vunshifted = v >> lshamt;
419 /* Make sure we did not shift out any bits. */
420 if (vunshifted << lshamt != v)
421 return false;
423 unsigned HOST_WIDE_INT imm_reach = 1LL << nbits;
424 return ((unsigned HOST_WIDE_INT) vunshifted + imm_reach/2 < imm_reach);
427 /* Return the address RTX of a move to/from memory
428 instruction. */
430 static rtx
431 th_get_move_mem_addr (rtx dest, rtx src, bool load)
433 rtx mem;
435 if (load)
436 mem = src;
437 else
438 mem = dest;
440 gcc_assert (GET_CODE (mem) == MEM);
441 return XEXP (mem, 0);
444 /* Return true if X is a valid address for T-Head's memory addressing modes
445 with pre/post modification for machine mode MODE.
446 If it is, fill in INFO appropriately (if non-NULL).
447 If STRICT_P is true then REG_OK_STRICT is in effect. */
449 static bool
450 th_memidx_classify_address_modify (struct riscv_address_info *info, rtx x,
451 machine_mode mode, bool strict_p)
453 if (!TARGET_XTHEADMEMIDX)
454 return false;
456 if (GET_MODE_CLASS (mode) != MODE_INT
457 || GET_MODE_SIZE (mode).to_constant () > UNITS_PER_WORD)
458 return false;
460 if (GET_CODE (x) != POST_MODIFY
461 && GET_CODE (x) != PRE_MODIFY)
462 return false;
464 rtx reg = XEXP (x, 0);
465 rtx exp = XEXP (x, 1);
466 rtx expreg = XEXP (exp, 0);
467 rtx expoff = XEXP (exp, 1);
469 if (GET_CODE (exp) != PLUS
470 || !rtx_equal_p (expreg, reg)
471 || !CONST_INT_P (expoff)
472 || !riscv_valid_base_register_p (reg, mode, strict_p))
473 return false;
475 /* The offset is calculated as (sign_extend(imm5) << imm2) */
476 const int shamt_bits = 2;
477 for (int shamt = 0; shamt < (1 << shamt_bits); shamt++)
479 const int nbits = 5;
480 if (valid_signed_immediate (expoff, nbits, shamt))
482 if (info)
484 info->type = ADDRESS_REG_WB;
485 info->reg = reg;
486 info->offset = expoff;
487 info->shift = shamt;
489 return true;
493 return false;
496 /* Return TRUE if X is a MEM with a legitimate modify address. */
498 bool
499 th_memidx_legitimate_modify_p (rtx x)
501 if (!MEM_P (x))
502 return false;
504 /* Get the mode from the MEM and unpack it. */
505 machine_mode mode = GET_MODE (x);
506 x = XEXP (x, 0);
508 return th_memidx_classify_address_modify (NULL, x, mode, reload_completed);
511 /* Return TRUE if X is a MEM with a legitimate modify address
512 and the address is POST_MODIFY (if POST is true) or a PRE_MODIFY
513 (otherwise). */
515 bool
516 th_memidx_legitimate_modify_p (rtx x, bool post)
518 if (!th_memidx_legitimate_modify_p (x))
519 return false;
521 /* Unpack the MEM and check the code. */
522 x = XEXP (x, 0);
523 if (post)
524 return GET_CODE (x) == POST_MODIFY;
525 else
526 return GET_CODE (x) == PRE_MODIFY;
529 /* Provide a buffer for a th.lXia/th.lXib/th.sXia/th.sXib instruction
530 for the given MODE. If LOAD is true, a load instruction will be
531 provided (otherwise, a store instruction). If X is not suitable
532 return NULL. */
534 static const char *
535 th_memidx_output_modify (rtx dest, rtx src, machine_mode mode, bool load)
537 char format[24];
538 rtx output_operands[2];
539 rtx x = th_get_move_mem_addr (dest, src, load);
541 /* Validate x. */
542 if (!th_memidx_classify_address_modify (NULL, x, mode, reload_completed))
543 return NULL;
545 int index = exact_log2 (GET_MODE_SIZE (mode).to_constant ());
546 bool post = GET_CODE (x) == POST_MODIFY;
548 const char *const insn[][4] = {
550 "th.sbi%s\t%%z1,%%0",
551 "th.shi%s\t%%z1,%%0",
552 "th.swi%s\t%%z1,%%0",
553 "th.sdi%s\t%%z1,%%0"
556 "th.lbui%s\t%%0,%%1",
557 "th.lhui%s\t%%0,%%1",
558 "th.lwi%s\t%%0,%%1",
559 "th.ldi%s\t%%0,%%1"
563 snprintf (format, sizeof (format), insn[load][index], post ? "a" : "b");
564 output_operands[0] = dest;
565 output_operands[1] = src;
566 output_asm_insn (format, output_operands);
567 return "";
570 static bool
571 is_memidx_mode (machine_mode mode)
573 if (mode == QImode || mode == HImode || mode == SImode)
574 return true;
576 if (mode == DImode && TARGET_64BIT)
577 return true;
579 return false;
582 static bool
583 is_fmemidx_mode (machine_mode mode)
585 if (mode == SFmode && TARGET_HARD_FLOAT)
586 return true;
588 if (mode == DFmode && TARGET_DOUBLE_FLOAT)
589 return true;
591 return false;
594 /* Return true if X is a valid address for T-Head's memory addressing modes
595 with scaled register offsets for machine mode MODE.
596 If it is, fill in INFO appropriately (if non-NULL).
597 If STRICT_P is true then REG_OK_STRICT is in effect. */
599 static bool
600 th_memidx_classify_address_index (struct riscv_address_info *info, rtx x,
601 machine_mode mode, bool strict_p)
603 /* Ensure that the mode is supported. */
604 if (!(TARGET_XTHEADMEMIDX && is_memidx_mode (mode))
605 && !(TARGET_XTHEADMEMIDX
606 && TARGET_XTHEADFMEMIDX && is_fmemidx_mode (mode)))
607 return false;
609 if (GET_CODE (x) != PLUS)
610 return false;
612 rtx op0 = XEXP (x, 0);
613 rtx op1 = XEXP (x, 1);
614 enum riscv_address_type type;
615 int shift;
616 rtx reg = op0;
617 rtx offset = op1;
619 if (!riscv_valid_base_register_p (reg, mode, strict_p))
621 reg = op1;
622 offset = op0;
623 if (!riscv_valid_base_register_p (reg, mode, strict_p))
624 return false;
627 /* (reg:X) */
628 if ((REG_P (offset) || SUBREG_P (offset))
629 && GET_MODE (offset) == Xmode)
631 type = ADDRESS_REG_REG;
632 shift = 0;
633 offset = offset;
635 /* (any_extend:DI (reg:SI)) */
636 else if (TARGET_64BIT
637 && (GET_CODE (offset) == SIGN_EXTEND
638 || GET_CODE (offset) == ZERO_EXTEND)
639 && GET_MODE (offset) == DImode
640 && GET_MODE (XEXP (offset, 0)) == SImode)
642 type = (GET_CODE (offset) == SIGN_EXTEND)
643 ? ADDRESS_REG_REG : ADDRESS_REG_UREG;
644 shift = 0;
645 offset = XEXP (offset, 0);
647 /* (mult:X (reg:X) (const_int scale)) */
648 else if (GET_CODE (offset) == MULT
649 && GET_MODE (offset) == Xmode
650 && REG_P (XEXP (offset, 0))
651 && GET_MODE (XEXP (offset, 0)) == Xmode
652 && CONST_INT_P (XEXP (offset, 1))
653 && pow2p_hwi (INTVAL (XEXP (offset, 1)))
654 && IN_RANGE (exact_log2 (INTVAL (XEXP (offset, 1))), 1, 3))
656 type = ADDRESS_REG_REG;
657 shift = exact_log2 (INTVAL (XEXP (offset, 1)));
658 offset = XEXP (offset, 0);
660 /* (mult:DI (any_extend:DI (reg:SI)) (const_int scale)) */
661 else if (TARGET_64BIT
662 && GET_CODE (offset) == MULT
663 && GET_MODE (offset) == DImode
664 && (GET_CODE (XEXP (offset, 0)) == SIGN_EXTEND
665 || GET_CODE (XEXP (offset, 0)) == ZERO_EXTEND)
666 && GET_MODE (XEXP (offset, 0)) == DImode
667 && REG_P (XEXP (XEXP (offset, 0), 0))
668 && GET_MODE (XEXP (XEXP (offset, 0), 0)) == SImode
669 && CONST_INT_P (XEXP (offset, 1)))
671 type = (GET_CODE (XEXP (offset, 0)) == SIGN_EXTEND)
672 ? ADDRESS_REG_REG : ADDRESS_REG_UREG;
673 shift = exact_log2 (INTVAL (XEXP (x, 1)));
674 offset = XEXP (XEXP (x, 0), 0);
676 /* (ashift:X (reg:X) (const_int shift)) */
677 else if (GET_CODE (offset) == ASHIFT
678 && GET_MODE (offset) == Xmode
679 && REG_P (XEXP (offset, 0))
680 && GET_MODE (XEXP (offset, 0)) == Xmode
681 && CONST_INT_P (XEXP (offset, 1))
682 && IN_RANGE (INTVAL (XEXP (offset, 1)), 0, 3))
684 type = ADDRESS_REG_REG;
685 shift = INTVAL (XEXP (offset, 1));
686 offset = XEXP (offset, 0);
688 /* (ashift:DI (any_extend:DI (reg:SI)) (const_int shift)) */
689 else if (TARGET_64BIT
690 && GET_CODE (offset) == ASHIFT
691 && GET_MODE (offset) == DImode
692 && (GET_CODE (XEXP (offset, 0)) == SIGN_EXTEND
693 || GET_CODE (XEXP (offset, 0)) == ZERO_EXTEND)
694 && GET_MODE (XEXP (offset, 0)) == DImode
695 && GET_MODE (XEXP (XEXP (offset, 0), 0)) == SImode
696 && CONST_INT_P (XEXP (offset, 1))
697 && IN_RANGE(INTVAL (XEXP (offset, 1)), 0, 3))
699 type = (GET_CODE (XEXP (offset, 0)) == SIGN_EXTEND)
700 ? ADDRESS_REG_REG : ADDRESS_REG_UREG;
701 shift = INTVAL (XEXP (offset, 1));
702 offset = XEXP (XEXP (offset, 0), 0);
704 /* (and:X (mult:X (reg:X) (const_int scale)) (const_int mask)) */
705 else if (TARGET_64BIT
706 && GET_CODE (offset) == AND
707 && GET_MODE (offset) == DImode
708 && GET_CODE (XEXP (offset, 0)) == MULT
709 && GET_MODE (XEXP (offset, 0)) == DImode
710 && REG_P (XEXP (XEXP (offset, 0), 0))
711 && GET_MODE (XEXP (XEXP (offset, 0), 0)) == DImode
712 && CONST_INT_P (XEXP (XEXP (offset, 0), 1))
713 && pow2p_hwi (INTVAL (XEXP (XEXP (offset, 0), 1)))
714 && IN_RANGE (exact_log2 (INTVAL (XEXP (XEXP (offset, 0), 1))), 1, 3)
715 && CONST_INT_P (XEXP (offset, 1))
716 && INTVAL (XEXP (offset, 1))
717 >> exact_log2 (INTVAL (XEXP (XEXP (offset, 0), 1))) == 0xffffffff)
719 type = ADDRESS_REG_UREG;
720 shift = exact_log2 (INTVAL (XEXP (XEXP (offset, 0), 1)));
721 offset = XEXP (XEXP (offset, 0), 0);
723 else
724 return false;
726 if (!strict_p && SUBREG_P (offset)
727 && GET_MODE (SUBREG_REG (offset)) == SImode)
728 offset = SUBREG_REG (offset);
730 if (!REG_P (offset)
731 || !riscv_regno_mode_ok_for_base_p (REGNO (offset), mode, strict_p))
732 return false;
734 if (info)
736 info->reg = reg;
737 info->type = type;
738 info->offset = offset;
739 info->shift = shift;
741 return true;
744 /* Return TRUE if X is a MEM with a legitimate indexed address. */
746 bool
747 th_memidx_legitimate_index_p (rtx x)
749 if (!MEM_P (x))
750 return false;
752 /* Get the mode from the MEM and unpack it. */
753 machine_mode mode = GET_MODE (x);
754 x = XEXP (x, 0);
756 return th_memidx_classify_address_index (NULL, x, mode, reload_completed);
759 /* Return TRUE if X is a MEM with a legitimate indexed address
760 and the offset register is zero-extended (if UINDEX is true)
761 or sign-extended (otherwise). */
763 bool
764 th_memidx_legitimate_index_p (rtx x, bool uindex)
766 if (!MEM_P (x))
767 return false;
769 /* Get the mode from the MEM and unpack it. */
770 machine_mode mode = GET_MODE (x);
771 x = XEXP (x, 0);
773 struct riscv_address_info info;
774 if (!th_memidx_classify_address_index (&info, x, mode, reload_completed))
775 return false;
777 if (uindex)
778 return info.type == ADDRESS_REG_UREG;
779 else
780 return info.type == ADDRESS_REG_REG;
783 /* Provide a buffer for a th.lrX/th.lurX/th.srX/th.surX instruction
784 for the given MODE. If LOAD is true, a load instruction will be
785 provided (otherwise, a store instruction). If X is not suitable
786 return NULL. */
788 static const char *
789 th_memidx_output_index (rtx dest, rtx src, machine_mode mode, bool load)
791 struct riscv_address_info info;
792 char format[24];
793 rtx output_operands[2];
794 rtx x = th_get_move_mem_addr (dest, src, load);
796 /* Validate x. */
797 if (!th_memidx_classify_address_index (&info, x, mode, reload_completed))
798 return NULL;
800 int index = exact_log2 (GET_MODE_SIZE (mode).to_constant ());
801 bool uindex = info.type == ADDRESS_REG_UREG;
803 const char *const insn[][4] = {
805 "th.s%srb\t%%z1,%%0",
806 "th.s%srh\t%%z1,%%0",
807 "th.s%srw\t%%z1,%%0",
808 "th.s%srd\t%%z1,%%0"
811 "th.l%srbu\t%%0,%%1",
812 "th.l%srhu\t%%0,%%1",
813 "th.l%srw\t%%0,%%1",
814 "th.l%srd\t%%0,%%1"
818 snprintf (format, sizeof (format), insn[load][index], uindex ? "u" : "");
819 output_operands[0] = dest;
820 output_operands[1] = src;
821 output_asm_insn (format, output_operands);
822 return "";
825 /* Provide a buffer for a th.flX/th.fluX/th.fsX/th.fsuX instruction
826 for the given MODE. If LOAD is true, a load instruction will be
827 provided (otherwise, a store instruction). If X is not suitable
828 return NULL. */
830 static const char *
831 th_fmemidx_output_index (rtx dest, rtx src, machine_mode mode, bool load)
833 struct riscv_address_info info;
834 char format[24];
835 rtx output_operands[2];
836 rtx x = th_get_move_mem_addr (dest, src, load);
838 /* Validate x. */
839 if (!th_memidx_classify_address_index (&info, x, mode, reload_completed))
840 return NULL;
842 int index = exact_log2 (GET_MODE_SIZE (mode).to_constant ()) - 2;
843 bool uindex = info.type == ADDRESS_REG_UREG;
845 const char *const insn[][2] = {
847 "th.fs%srw\t%%z1,%%0",
848 "th.fs%srd\t%%z1,%%0"
851 "th.fl%srw\t%%0,%%1",
852 "th.fl%srd\t%%0,%%1"
856 snprintf (format, sizeof (format), insn[load][index], uindex ? "u" : "");
857 output_operands[0] = dest;
858 output_operands[1] = src;
859 output_asm_insn (format, output_operands);
860 return "";
863 /* Return true if X is a valid address for T-Head's memory addressing modes
864 for machine mode MODE. If it is, fill in INFO appropriately (if non-NULL).
865 If STRICT_P is true then REG_OK_STRICT is in effect. */
867 bool
868 th_classify_address (struct riscv_address_info *info, rtx x,
869 machine_mode mode, bool strict_p)
871 switch (GET_CODE (x))
873 case PLUS:
874 if (th_memidx_classify_address_index (info, x, mode, strict_p))
875 return true;
876 break;
878 case POST_MODIFY:
879 case PRE_MODIFY:
880 if (th_memidx_classify_address_modify (info, x, mode, strict_p))
881 return true;
882 break;
884 default:
885 return false;
888 return false;
891 /* Provide a string containing a XTheadMemIdx instruction for the given
892 MODE from the provided SRC to the provided DEST.
893 A pointer to a NULL-terminated string containing the instruction will
894 be returned if a suitable instruction is available. Otherwise, this
895 function returns NULL. */
897 const char *
898 th_output_move (rtx dest, rtx src)
900 enum rtx_code dest_code, src_code;
901 machine_mode mode;
902 const char *insn = NULL;
904 dest_code = GET_CODE (dest);
905 src_code = GET_CODE (src);
906 mode = GET_MODE (dest);
908 if (!(mode == GET_MODE (src) || src == CONST0_RTX (mode)))
909 return NULL;
911 if (dest_code == REG && src_code == MEM)
913 if (GET_MODE_CLASS (mode) == MODE_INT
914 || (GET_MODE_CLASS (mode) == MODE_FLOAT && GP_REG_P (REGNO (dest))))
916 if ((insn = th_memidx_output_index (dest, src, mode, true)))
917 return insn;
918 if ((insn = th_memidx_output_modify (dest, src, mode, true)))
919 return insn;
921 else if (GET_MODE_CLASS (mode) == MODE_FLOAT && HARDFP_REG_P (REGNO (dest)))
923 if ((insn = th_fmemidx_output_index (dest, src, mode, true)))
924 return insn;
927 else if (dest_code == MEM && (src_code == REG || src == CONST0_RTX (mode)))
929 if (GET_MODE_CLASS (mode) == MODE_INT
930 || src == CONST0_RTX (mode)
931 || (GET_MODE_CLASS (mode) == MODE_FLOAT && GP_REG_P (REGNO (src))))
933 if ((insn = th_memidx_output_index (dest, src, mode, false)))
934 return insn;
935 if ((insn = th_memidx_output_modify (dest, src, mode, false)))
936 return insn;
938 else if (GET_MODE_CLASS (mode) == MODE_FLOAT && HARDFP_REG_P (REGNO (src)))
940 if ((insn = th_fmemidx_output_index (dest, src, mode, false)))
941 return insn;
944 return NULL;
947 /* Define ASM_OUTPUT_OPCODE to do anything special before
948 emitting an opcode. */
949 const char *
950 th_asm_output_opcode (FILE *asm_out_file, const char *p)
952 /* We need to add th. prefix to all the xtheadvector
953 instructions here.*/
954 if (current_output_insn != NULL)
956 if (get_attr_type (current_output_insn) == TYPE_VSETVL)
958 if (strstr (p, "zero"))
960 if (strstr (p, "zero,zero"))
961 return "th.vsetvli\tzero,zero,e%0,%m1";
962 else
963 return "th.vsetvli\tzero,%0,e%1,%m2";
965 else
967 return "th.vsetvli\t%0,%1,e%2,%m3";
971 if (get_attr_type (current_output_insn) == TYPE_VLDE ||
972 get_attr_type (current_output_insn) == TYPE_VSTE ||
973 get_attr_type (current_output_insn) == TYPE_VLDFF)
975 if (strstr (p, "e8") || strstr (p, "e16") ||
976 strstr (p, "e32") || strstr (p, "e64"))
978 get_attr_type (current_output_insn) == TYPE_VSTE
979 ? fputs ("th.vse", asm_out_file)
980 : fputs ("th.vle", asm_out_file);
981 if (strstr (p, "e8"))
982 return p+4;
983 else
984 return p+5;
988 if (get_attr_type (current_output_insn) == TYPE_VLDS ||
989 get_attr_type (current_output_insn) == TYPE_VSTS)
991 if (strstr (p, "vle8") || strstr (p, "vse8") ||
992 strstr (p, "vle16") || strstr (p, "vse16") ||
993 strstr (p, "vle32") || strstr (p, "vse32") ||
994 strstr (p, "vle64") || strstr (p, "vse64"))
996 get_attr_type (current_output_insn) == TYPE_VSTS
997 ? fputs ("th.vse", asm_out_file)
998 : fputs ("th.vle", asm_out_file);
999 if (strstr (p, "e8"))
1000 return p+4;
1001 else
1002 return p+5;
1004 else if (strstr (p, "vlse8") || strstr (p, "vsse8") ||
1005 strstr (p, "vlse16") || strstr (p, "vsse16") ||
1006 strstr (p, "vlse32") || strstr (p, "vsse32") ||
1007 strstr (p, "vlse64") || strstr (p, "vsse64"))
1009 get_attr_type (current_output_insn) == TYPE_VSTS
1010 ? fputs ("th.vsse", asm_out_file)
1011 : fputs ("th.vlse", asm_out_file);
1012 if (strstr (p, "e8"))
1013 return p+5;
1014 else
1015 return p+6;
1019 if (get_attr_type (current_output_insn) == TYPE_VLDUX ||
1020 get_attr_type (current_output_insn) == TYPE_VLDOX)
1022 if (strstr (p, "ei"))
1024 fputs ("th.vlxe", asm_out_file);
1025 if (strstr (p, "ei8"))
1026 return p+7;
1027 else
1028 return p+8;
1032 if (get_attr_type (current_output_insn) == TYPE_VSTUX ||
1033 get_attr_type (current_output_insn) == TYPE_VSTOX)
1035 if (strstr (p, "ei"))
1037 get_attr_type (current_output_insn) == TYPE_VSTUX
1038 ? fputs ("th.vsuxe", asm_out_file)
1039 : fputs ("th.vsxe", asm_out_file);
1040 if (strstr (p, "ei8"))
1041 return p+7;
1042 else
1043 return p+8;
1047 if (get_attr_type (current_output_insn) == TYPE_VLSEGDE ||
1048 get_attr_type (current_output_insn) == TYPE_VSSEGTE ||
1049 get_attr_type (current_output_insn) == TYPE_VLSEGDFF)
1051 get_attr_type (current_output_insn) == TYPE_VSSEGTE
1052 ? fputs ("th.vsseg", asm_out_file)
1053 : fputs ("th.vlseg", asm_out_file);
1054 asm_fprintf (asm_out_file, "%c", p[5]);
1055 fputs ("e", asm_out_file);
1056 if (strstr (p, "e8"))
1057 return p+8;
1058 else
1059 return p+9;
1062 if (get_attr_type (current_output_insn) == TYPE_VLSEGDS ||
1063 get_attr_type (current_output_insn) == TYPE_VSSEGTS)
1065 get_attr_type (current_output_insn) == TYPE_VSSEGTS
1066 ? fputs ("th.vssseg", asm_out_file)
1067 : fputs ("th.vlsseg", asm_out_file);
1068 asm_fprintf (asm_out_file, "%c", p[6]);
1069 fputs ("e", asm_out_file);
1070 if (strstr (p, "e8"))
1071 return p+9;
1072 else
1073 return p+10;
1076 if (get_attr_type (current_output_insn) == TYPE_VLSEGDUX ||
1077 get_attr_type (current_output_insn) == TYPE_VLSEGDOX)
1079 fputs ("th.vlxseg", asm_out_file);
1080 asm_fprintf (asm_out_file, "%c", p[7]);
1081 fputs ("e", asm_out_file);
1082 if (strstr (p, "ei8"))
1083 return p+11;
1084 else
1085 return p+12;
1088 if (get_attr_type (current_output_insn) == TYPE_VSSEGTUX ||
1089 get_attr_type (current_output_insn) == TYPE_VSSEGTOX)
1091 fputs ("th.vsxseg", asm_out_file);
1092 asm_fprintf (asm_out_file, "%c", p[7]);
1093 fputs ("e", asm_out_file);
1094 if (strstr (p, "ei8"))
1095 return p+11;
1096 else
1097 return p+12;
1100 if (get_attr_type (current_output_insn) == TYPE_VNSHIFT)
1102 if (strstr (p, "vncvt"))
1104 fputs ("th.vncvt.x.x.v", asm_out_file);
1105 return p+11;
1108 strstr (p, "vnsrl") ? fputs ("th.vnsrl.v", asm_out_file)
1109 : fputs ("th.vnsra.v", asm_out_file);
1110 return p+7;
1113 if (get_attr_type (current_output_insn) == TYPE_VNCLIP)
1115 if (strstr (p, "vnclipu"))
1117 fputs ("th.vnclipu.v", asm_out_file);
1118 return p+9;
1120 else
1122 fputs ("th.vnclip.v", asm_out_file);
1123 return p+8;
1127 if (get_attr_type (current_output_insn) == TYPE_VMPOP)
1129 fputs ("th.vmpopc", asm_out_file);
1130 return p+5;
1133 if (get_attr_type (current_output_insn) == TYPE_VMFFS)
1135 fputs ("th.vmfirst", asm_out_file);
1136 return p+6;
1139 if (get_attr_type (current_output_insn) == TYPE_VFNCVTFTOI ||
1140 get_attr_type (current_output_insn) == TYPE_VFNCVTITOF)
1142 if (strstr (p, "xu"))
1144 get_attr_type (current_output_insn) == TYPE_VFNCVTFTOI
1145 ? fputs ("th.vfncvt.xu.f.v", asm_out_file)
1146 : fputs ("th.vfncvt.f.xu.v", asm_out_file);
1147 return p+13;
1149 else
1151 get_attr_type (current_output_insn) == TYPE_VFNCVTFTOI
1152 ? fputs ("th.vfncvt.x.f.v", asm_out_file)
1153 : fputs ("th.vfncvt.f.x.v", asm_out_file);
1154 return p+12;
1158 if (get_attr_type (current_output_insn) == TYPE_VFNCVTFTOF)
1160 fputs ("th.vfncvt.f.f.v", asm_out_file);
1161 return p+12;
1164 if (get_attr_type (current_output_insn) == TYPE_VFREDU
1165 && strstr (p, "sum"))
1167 fputs ("th.vfredsum", asm_out_file);
1168 return p+9;
1171 if (get_attr_type (current_output_insn) == TYPE_VFWREDU
1172 && strstr (p, "sum"))
1174 fputs ("th.vfwredsum", asm_out_file);
1175 return p+10;
1178 if (p[0] == 'v')
1179 fputs ("th.", asm_out_file);
1182 return p;
1185 /* Implement TARGET_PRINT_OPERAND_ADDRESS for XTheadMemIdx. */
1187 bool
1188 th_print_operand_address (FILE *file, machine_mode mode, rtx x)
1190 struct riscv_address_info addr;
1192 if (!th_classify_address (&addr, x, mode, reload_completed))
1193 return false;
1195 switch (addr.type)
1197 case ADDRESS_REG_REG:
1198 case ADDRESS_REG_UREG:
1199 fprintf (file, "%s,%s,%u", reg_names[REGNO (addr.reg)],
1200 reg_names[REGNO (addr.offset)], addr.shift);
1201 return true;
1203 case ADDRESS_REG_WB:
1204 fprintf (file, "(%s)," HOST_WIDE_INT_PRINT_DEC ",%u",
1205 reg_names[REGNO (addr.reg)],
1206 INTVAL (addr.offset) >> addr.shift, addr.shift);
1207 return true;
1209 default:
1210 gcc_unreachable ();
1213 gcc_unreachable ();
1216 /* Number array of registers X1, X5-X7, X10-X17, X28-X31, to be
1217 operated on by instruction th.ipush/th.ipop in XTheadInt. */
1219 int th_int_regs[] ={
1220 RETURN_ADDR_REGNUM,
1221 T0_REGNUM, T1_REGNUM, T2_REGNUM,
1222 A0_REGNUM, A1_REGNUM, A2_REGNUM, A3_REGNUM,
1223 A4_REGNUM, A5_REGNUM, A6_REGNUM, A7_REGNUM,
1224 T3_REGNUM, T4_REGNUM, T5_REGNUM, T6_REGNUM,
1227 /* If MASK contains registers X1, X5-X7, X10-X17, X28-X31, then
1228 return the mask composed of these registers, otherwise return
1229 zero. */
1231 unsigned int
1232 th_int_get_mask (unsigned int mask)
1234 unsigned int xtheadint_mask = 0;
1236 if (!TARGET_XTHEADINT || TARGET_64BIT)
1237 return 0;
1239 for (unsigned int i = 0; i < ARRAY_SIZE (th_int_regs); i++)
1241 if (!BITSET_P (mask, th_int_regs[i]))
1242 return 0;
1244 xtheadint_mask |= (1 << th_int_regs[i]);
1247 return xtheadint_mask; /* Usually 0xf003fce2. */
1250 /* Returns the occupied frame needed to save registers X1, X5-X7,
1251 X10-X17, X28-X31. */
1253 unsigned int
1254 th_int_get_save_adjustment (void)
1256 gcc_assert (TARGET_XTHEADINT && !TARGET_64BIT);
1257 return ARRAY_SIZE (th_int_regs) * UNITS_PER_WORD;
1261 th_int_adjust_cfi_prologue (unsigned int mask)
1263 gcc_assert (TARGET_XTHEADINT && !TARGET_64BIT);
1265 rtx dwarf = NULL_RTX;
1266 rtx adjust_sp_rtx, reg, mem, insn;
1267 int saved_size = ARRAY_SIZE (th_int_regs) * UNITS_PER_WORD;
1268 int offset = saved_size;
1270 for (int regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
1271 if (BITSET_P (mask, regno - GP_REG_FIRST))
1273 offset -= UNITS_PER_WORD;
1274 reg = gen_rtx_REG (SImode, regno);
1275 mem = gen_frame_mem (SImode, plus_constant (Pmode,
1276 stack_pointer_rtx,
1277 offset));
1279 insn = gen_rtx_SET (mem, reg);
1280 dwarf = alloc_reg_note (REG_CFA_OFFSET, insn, dwarf);
1283 /* Debug info for adjust sp. */
1284 adjust_sp_rtx =
1285 gen_rtx_SET (stack_pointer_rtx,
1286 gen_rtx_PLUS (GET_MODE (stack_pointer_rtx),
1287 stack_pointer_rtx, GEN_INT (-saved_size)));
1288 dwarf = alloc_reg_note (REG_CFA_ADJUST_CFA, adjust_sp_rtx, dwarf);
1290 return dwarf;