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)
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
25 #include "coretypes.h"
30 #include "insn-attr.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. */
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));
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
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];
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;
83 reg1
= copy_rtx (operands
[0]);
84 reg2
= copy_rtx (operands
[2]);
85 mem1
= copy_rtx (operands
[1]);
86 mem2
= copy_rtx (operands
[3]);
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";
94 format
= "th.ldd\t%0, %1, (%2), %3, %4";
98 reg1
= copy_rtx (operands
[1]);
99 reg2
= copy_rtx (operands
[3]);
100 mem1
= copy_rtx (operands
[0]);
101 mem2
= copy_rtx (operands
[2]);
104 format
= "th.swd\t%z0, %z1, (%2), %3, %4";
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
));
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
);
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. */
150 th_mempair_check_consecutive_mems (machine_mode mode
, rtx
*mem1
, rtx
*mem2
,
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
)
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
)))
172 if (known_eq (UINTVAL (offset2
) + size
, UINTVAL (offset1
)))
184 /* Check if the given MEM can be used to define the address of a mempair
188 th_mempair_operand_p (rtx mem
, machine_mode mode
)
190 if (!MEM_SIZE_KNOWN_P (mem
))
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
))
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;
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)
213 if ((imm2
<< shamt
) != offset
)
220 th_mempair_load_overlap_p (rtx reg1
, rtx reg2
, rtx mem
)
222 if (REGNO (reg1
) == REGNO (reg2
))
225 if (reg_overlap_mentioned_p (reg1
, mem
))
229 HOST_WIDE_INT offset
;
230 split_plus (XEXP (mem
, 0), &base
, &offset
);
237 if (REGNO (base
) == REGNO (reg1
)
238 || REGNO (base
) == REGNO (reg2
))
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. */
251 th_mempair_operands_p (rtx operands
[4], bool load_p
,
254 rtx mem_1
, mem_2
, reg_1
, reg_2
;
262 if (!REG_P (reg_1
) || !REG_P (reg_2
))
264 if (th_mempair_load_overlap_p (reg_1
, reg_2
, mem_1
))
266 if (th_mempair_load_overlap_p (reg_1
, reg_2
, mem_2
))
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
)))
282 /* The mems cannot be volatile. */
283 if (!MEM_P (mem_1
) || !MEM_P (mem_2
))
285 if (MEM_VOLATILE_P (mem_1
) || MEM_VOLATILE_P (mem_2
))
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
))
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
))
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
)))
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
))));
310 /* Given OPERANDS of consecutive load/store that can be merged,
311 swap them if they are not in ascending order. */
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
,
320 operands
+ mem_op
+ 2,
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. */
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. */
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(). */
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. */
393 th_mempair_save_restore_regs (rtx operands
[4], bool load_p
,
396 gcc_assert (th_mempair_operands_p (operands
, load_p
, mode
));
398 th_mempair_order_operands (operands
, load_p
, mode
);
401 th_mempair_restore_regs (operands
);
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. */
410 valid_signed_immediate (rtx x
, unsigned nbits
, unsigned lshamt
)
412 if (GET_CODE (x
) != CONST_INT
)
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
)
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
431 th_get_move_mem_addr (rtx dest
, rtx src
, bool load
)
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. */
450 th_memidx_classify_address_modify (struct riscv_address_info
*info
, rtx x
,
451 machine_mode mode
, bool strict_p
)
453 if (!TARGET_XTHEADMEMIDX
)
456 if (GET_MODE_CLASS (mode
) != MODE_INT
457 || GET_MODE_SIZE (mode
).to_constant () > UNITS_PER_WORD
)
460 if (GET_CODE (x
) != POST_MODIFY
461 && GET_CODE (x
) != PRE_MODIFY
)
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
))
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
++)
480 if (valid_signed_immediate (expoff
, nbits
, shamt
))
484 info
->type
= ADDRESS_REG_WB
;
486 info
->offset
= expoff
;
496 /* Return TRUE if X is a MEM with a legitimate modify address. */
499 th_memidx_legitimate_modify_p (rtx x
)
504 /* Get the mode from the MEM and unpack it. */
505 machine_mode mode
= GET_MODE (x
);
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
516 th_memidx_legitimate_modify_p (rtx x
, bool post
)
518 if (!th_memidx_legitimate_modify_p (x
))
521 /* Unpack the MEM and check the code. */
524 return GET_CODE (x
) == POST_MODIFY
;
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
535 th_memidx_output_modify (rtx dest
, rtx src
, machine_mode mode
, bool load
)
538 rtx output_operands
[2];
539 rtx x
= th_get_move_mem_addr (dest
, src
, load
);
542 if (!th_memidx_classify_address_modify (NULL
, x
, mode
, reload_completed
))
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",
556 "th.lbui%s\t%%0,%%1",
557 "th.lhui%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
);
571 is_memidx_mode (machine_mode mode
)
573 if (mode
== QImode
|| mode
== HImode
|| mode
== SImode
)
576 if (mode
== DImode
&& TARGET_64BIT
)
583 is_fmemidx_mode (machine_mode mode
)
585 if (mode
== SFmode
&& TARGET_HARD_FLOAT
)
588 if (mode
== DFmode
&& TARGET_DOUBLE_FLOAT
)
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. */
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
)))
609 if (GET_CODE (x
) != PLUS
)
612 rtx op0
= XEXP (x
, 0);
613 rtx op1
= XEXP (x
, 1);
614 enum riscv_address_type type
;
619 if (!riscv_valid_base_register_p (reg
, mode
, strict_p
))
623 if (!riscv_valid_base_register_p (reg
, mode
, strict_p
))
628 if ((REG_P (offset
) || SUBREG_P (offset
))
629 && GET_MODE (offset
) == Xmode
)
631 type
= ADDRESS_REG_REG
;
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
;
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);
726 if (!strict_p
&& SUBREG_P (offset
)
727 && GET_MODE (SUBREG_REG (offset
)) == SImode
)
728 offset
= SUBREG_REG (offset
);
731 || !riscv_regno_mode_ok_for_base_p (REGNO (offset
), mode
, strict_p
))
738 info
->offset
= offset
;
744 /* Return TRUE if X is a MEM with a legitimate indexed address. */
747 th_memidx_legitimate_index_p (rtx x
)
752 /* Get the mode from the MEM and unpack it. */
753 machine_mode mode
= GET_MODE (x
);
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). */
764 th_memidx_legitimate_index_p (rtx x
, bool uindex
)
769 /* Get the mode from the MEM and unpack it. */
770 machine_mode mode
= GET_MODE (x
);
773 struct riscv_address_info info
;
774 if (!th_memidx_classify_address_index (&info
, x
, mode
, reload_completed
))
778 return info
.type
== ADDRESS_REG_UREG
;
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
789 th_memidx_output_index (rtx dest
, rtx src
, machine_mode mode
, bool load
)
791 struct riscv_address_info info
;
793 rtx output_operands
[2];
794 rtx x
= th_get_move_mem_addr (dest
, src
, load
);
797 if (!th_memidx_classify_address_index (&info
, x
, mode
, reload_completed
))
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",
811 "th.l%srbu\t%%0,%%1",
812 "th.l%srhu\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
);
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
831 th_fmemidx_output_index (rtx dest
, rtx src
, machine_mode mode
, bool load
)
833 struct riscv_address_info info
;
835 rtx output_operands
[2];
836 rtx x
= th_get_move_mem_addr (dest
, src
, load
);
839 if (!th_memidx_classify_address_index (&info
, x
, mode
, reload_completed
))
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",
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
);
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. */
868 th_classify_address (struct riscv_address_info
*info
, rtx x
,
869 machine_mode mode
, bool strict_p
)
871 switch (GET_CODE (x
))
874 if (th_memidx_classify_address_index (info
, x
, mode
, strict_p
))
880 if (th_memidx_classify_address_modify (info
, x
, mode
, strict_p
))
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. */
898 th_output_move (rtx dest
, rtx src
)
900 enum rtx_code dest_code
, src_code
;
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
)))
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)))
918 if ((insn
= th_memidx_output_modify (dest
, src
, mode
, true)))
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)))
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)))
935 if ((insn
= th_memidx_output_modify (dest
, src
, mode
, false)))
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)))
947 /* Define ASM_OUTPUT_OPCODE to do anything special before
948 emitting an opcode. */
950 th_asm_output_opcode (FILE *asm_out_file
, const char *p
)
952 /* We need to add th. prefix to all the xtheadvector
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";
963 return "th.vsetvli\tzero,%0,e%1,%m2";
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"))
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"))
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"))
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"))
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"))
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"))
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"))
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"))
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"))
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
);
1108 strstr (p
, "vnsrl") ? fputs ("th.vnsrl.v", asm_out_file
)
1109 : fputs ("th.vnsra.v", asm_out_file
);
1113 if (get_attr_type (current_output_insn
) == TYPE_VNCLIP
)
1115 if (strstr (p
, "vnclipu"))
1117 fputs ("th.vnclipu.v", asm_out_file
);
1122 fputs ("th.vnclip.v", asm_out_file
);
1127 if (get_attr_type (current_output_insn
) == TYPE_VMPOP
)
1129 fputs ("th.vmpopc", asm_out_file
);
1133 if (get_attr_type (current_output_insn
) == TYPE_VMFFS
)
1135 fputs ("th.vmfirst", asm_out_file
);
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
);
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
);
1158 if (get_attr_type (current_output_insn
) == TYPE_VFNCVTFTOF
)
1160 fputs ("th.vfncvt.f.f.v", asm_out_file
);
1164 if (get_attr_type (current_output_insn
) == TYPE_VFREDU
1165 && strstr (p
, "sum"))
1167 fputs ("th.vfredsum", asm_out_file
);
1171 if (get_attr_type (current_output_insn
) == TYPE_VFWREDU
1172 && strstr (p
, "sum"))
1174 fputs ("th.vfwredsum", asm_out_file
);
1179 fputs ("th.", asm_out_file
);
1185 /* Implement TARGET_PRINT_OPERAND_ADDRESS for XTheadMemIdx. */
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
))
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
);
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
);
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
[] ={
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
1232 th_int_get_mask (unsigned int mask
)
1234 unsigned int xtheadint_mask
= 0;
1236 if (!TARGET_XTHEADINT
|| TARGET_64BIT
)
1239 for (unsigned int i
= 0; i
< ARRAY_SIZE (th_int_regs
); i
++)
1241 if (!BITSET_P (mask
, th_int_regs
[i
]))
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. */
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
,
1279 insn
= gen_rtx_SET (mem
, reg
);
1280 dwarf
= alloc_reg_note (REG_CFA_OFFSET
, insn
, dwarf
);
1283 /* Debug info for adjust sp. */
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
);