1 /* Auxiliary functions for output asm template or expand rtl
2 pattern of Andes NDS32 cpu for GNU compiler
3 Copyright (C) 2012-2017 Free Software Foundation, Inc.
4 Contributed by Andes Technology Corporation.
6 This file is part of GCC.
8 GCC is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published
10 by the Free Software Foundation; either version 3, or (at your
11 option) any later version.
13 GCC is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
16 License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GCC; see the file COPYING3. If not see
20 <http://www.gnu.org/licenses/>. */
22 /* ------------------------------------------------------------------------ */
26 #include "coretypes.h"
33 #include "optabs.h" /* For GEN_FCN. */
36 #include "tm-constrs.h"
38 /* ------------------------------------------------------------------------ */
40 /* A helper function to return character based on byte size. */
42 nds32_byte_to_size (int byte
)
53 /* Normally it should not be here. */
58 /* A helper function to return memory format. */
59 enum nds32_16bit_address_type
60 nds32_mem_format (rtx op
)
62 machine_mode mode_test
;
67 return ADDRESS_NOT_16BIT_FORMAT
;
69 mode_test
= GET_MODE (op
);
74 if (GET_CODE (op
) == REG
&& (mode_test
== SImode
))
77 /* 333 format for QI/HImode. */
78 if (GET_CODE (op
) == REG
&& (REGNO (op
) < R8_REGNUM
))
79 return ADDRESS_LO_REG_IMM3U
;
81 /* post_inc 333 format. */
82 if ((GET_CODE (op
) == POST_INC
) && (mode_test
== SImode
))
84 regno
= REGNO(XEXP (op
, 0));
87 return ADDRESS_POST_INC_LO_REG_IMM3U
;
90 /* post_inc 333 format. */
91 if ((GET_CODE (op
) == POST_MODIFY
)
92 && (mode_test
== SImode
)
93 && (REG_P (XEXP (XEXP (op
, 1), 0)))
94 && (CONST_INT_P (XEXP (XEXP (op
, 1), 1))))
96 regno
= REGNO (XEXP (XEXP (op
, 1), 0));
97 val
= INTVAL (XEXP (XEXP (op
, 1), 1));
98 if (regno
< 8 && val
< 32)
99 return ADDRESS_POST_INC_LO_REG_IMM3U
;
102 if ((GET_CODE (op
) == PLUS
)
103 && (GET_CODE (XEXP (op
, 0)) == REG
)
104 && (GET_CODE (XEXP (op
, 1)) == CONST_INT
))
106 val
= INTVAL (XEXP (op
, 1));
108 regno
= REGNO(XEXP (op
, 0));
111 && regno
!= SP_REGNUM
112 && regno
!= FP_REGNUM
)
113 return ADDRESS_NOT_16BIT_FORMAT
;
119 if (val
>= 0 && val
< 8 && regno
< 8)
120 return ADDRESS_LO_REG_IMM3U
;
125 if (val
>= 0 && val
< 16 && (val
% 2 == 0) && regno
< 8)
126 return ADDRESS_LO_REG_IMM3U
;
132 /* fp imply 37 format. */
133 if ((regno
== FP_REGNUM
) &&
134 (val
>= 0 && val
< 512 && (val
% 4 == 0)))
135 return ADDRESS_FP_IMM7U
;
136 /* sp imply 37 format. */
137 else if ((regno
== SP_REGNUM
) &&
138 (val
>= 0 && val
< 512 && (val
% 4 == 0)))
139 return ADDRESS_SP_IMM7U
;
141 else if (val
>= 0 && val
< 32 && (val
% 4 == 0) && regno
< 8)
142 return ADDRESS_LO_REG_IMM3U
;
150 return ADDRESS_NOT_16BIT_FORMAT
;
153 /* Output 16-bit store. */
155 nds32_output_16bit_store (rtx
*operands
, int byte
)
159 rtx code
= XEXP (operands
[0], 0);
161 size
= nds32_byte_to_size (byte
);
163 switch (nds32_mem_format (operands
[0]))
167 output_asm_insn ("swi450\t%1, [%0]", operands
);
169 case ADDRESS_LO_REG_IMM3U
:
170 snprintf (pattern
, sizeof (pattern
), "s%ci333\t%%1, %%0", size
);
171 output_asm_insn (pattern
, operands
);
173 case ADDRESS_POST_INC_LO_REG_IMM3U
:
174 snprintf (pattern
, sizeof (pattern
), "s%ci333.bi\t%%1, %%0", size
);
175 output_asm_insn (pattern
, operands
);
177 case ADDRESS_FP_IMM7U
:
178 output_asm_insn ("swi37\t%1, %0", operands
);
180 case ADDRESS_SP_IMM7U
:
181 /* Get immediate value and set back to operands[1]. */
182 operands
[0] = XEXP (code
, 1);
183 output_asm_insn ("swi37.sp\t%1, [ + (%0)]", operands
);
192 /* Output 16-bit load. */
194 nds32_output_16bit_load (rtx
*operands
, int byte
)
198 rtx code
= XEXP (operands
[1], 0);
200 size
= nds32_byte_to_size (byte
);
202 switch (nds32_mem_format (operands
[1]))
206 output_asm_insn ("lwi450\t%0, [%1]", operands
);
208 case ADDRESS_LO_REG_IMM3U
:
209 snprintf (pattern
, sizeof (pattern
), "l%ci333\t%%0, %%1", size
);
210 output_asm_insn (pattern
, operands
);
212 case ADDRESS_POST_INC_LO_REG_IMM3U
:
213 snprintf (pattern
, sizeof (pattern
), "l%ci333.bi\t%%0, %%1", size
);
214 output_asm_insn (pattern
, operands
);
216 case ADDRESS_FP_IMM7U
:
217 output_asm_insn ("lwi37\t%0, %1", operands
);
219 case ADDRESS_SP_IMM7U
:
220 /* Get immediate value and set back to operands[0]. */
221 operands
[1] = XEXP (code
, 1);
222 output_asm_insn ("lwi37.sp\t%0, [ + (%1)]", operands
);
231 /* Output 32-bit store. */
233 nds32_output_32bit_store (rtx
*operands
, int byte
)
237 rtx code
= XEXP (operands
[0], 0);
239 size
= nds32_byte_to_size (byte
);
241 switch (GET_CODE (code
))
245 => access location by using register,
246 use "sbi / shi / swi" */
247 snprintf (pattern
, sizeof (pattern
), "s%ci\t%%1, %%0", size
);
252 /* (mem (symbol_ref X))
254 => access global variables,
255 use "sbi.gp / shi.gp / swi.gp" */
256 operands
[0] = XEXP (operands
[0], 0);
257 snprintf (pattern
, sizeof (pattern
), "s%ci.gp\t%%1, [ + %%0]", size
);
261 /* (mem (post_inc reg))
262 => access location by using register which will be post increment,
263 use "sbi.bi / shi.bi / swi.bi" */
264 snprintf (pattern
, sizeof (pattern
),
265 "s%ci.bi\t%%1, %%0, %d", size
, byte
);
269 /* (mem (post_dec reg))
270 => access location by using register which will be post decrement,
271 use "sbi.bi / shi.bi / swi.bi" */
272 snprintf (pattern
, sizeof (pattern
),
273 "s%ci.bi\t%%1, %%0, -%d", size
, byte
);
277 switch (GET_CODE (XEXP (XEXP (code
, 1), 1)))
281 /* (mem (post_modify (reg) (plus (reg) (reg))))
282 => access location by using register which will be
283 post modified with reg,
284 use "sb.bi/ sh.bi / sw.bi" */
285 snprintf (pattern
, sizeof (pattern
), "s%c.bi\t%%1, %%0", size
);
288 /* (mem (post_modify (reg) (plus (reg) (const_int))))
289 => access location by using register which will be
290 post modified with const_int,
291 use "sbi.bi/ shi.bi / swi.bi" */
292 snprintf (pattern
, sizeof (pattern
), "s%ci.bi\t%%1, %%0", size
);
300 switch (GET_CODE (XEXP (code
, 1)))
304 /* (mem (plus reg reg)) or (mem (plus (mult reg const_int) reg))
305 => access location by adding two registers,
306 use "sb / sh / sw" */
307 snprintf (pattern
, sizeof (pattern
), "s%c\t%%1, %%0", size
);
310 /* (mem (plus reg const_int))
311 => access location by adding one register with const_int,
312 use "sbi / shi / swi" */
313 snprintf (pattern
, sizeof (pattern
), "s%ci\t%%1, %%0", size
);
321 operands
[2] = XEXP (code
, 1);
322 operands
[0] = XEXP (code
, 0);
323 snprintf (pattern
, sizeof (pattern
),
324 "s%ci\t%%1, [%%0 + lo12(%%2)]", size
);
331 output_asm_insn (pattern
, operands
);
335 /* Output 32-bit load. */
337 nds32_output_32bit_load (rtx
*operands
, int byte
)
343 code
= XEXP (operands
[1], 0);
345 size
= nds32_byte_to_size (byte
);
347 switch (GET_CODE (code
))
351 => access location by using register,
352 use "lbi / lhi / lwi" */
353 snprintf (pattern
, sizeof (pattern
), "l%ci\t%%0, %%1", size
);
358 /* (mem (symbol_ref X))
360 => access global variables,
361 use "lbi.gp / lhi.gp / lwi.gp" */
362 operands
[1] = XEXP (operands
[1], 0);
363 snprintf (pattern
, sizeof (pattern
), "l%ci.gp\t%%0, [ + %%1]", size
);
367 /* (mem (post_inc reg))
368 => access location by using register which will be post increment,
369 use "lbi.bi / lhi.bi / lwi.bi" */
370 snprintf (pattern
, sizeof (pattern
),
371 "l%ci.bi\t%%0, %%1, %d", size
, byte
);
375 /* (mem (post_dec reg))
376 => access location by using register which will be post decrement,
377 use "lbi.bi / lhi.bi / lwi.bi" */
378 snprintf (pattern
, sizeof (pattern
),
379 "l%ci.bi\t%%0, %%1, -%d", size
, byte
);
383 switch (GET_CODE (XEXP (XEXP (code
, 1), 1)))
387 /* (mem (post_modify (reg) (plus (reg) (reg))))
388 => access location by using register which will be
389 post modified with reg,
390 use "lb.bi/ lh.bi / lw.bi" */
391 snprintf (pattern
, sizeof (pattern
), "l%c.bi\t%%0, %%1", size
);
394 /* (mem (post_modify (reg) (plus (reg) (const_int))))
395 => access location by using register which will be
396 post modified with const_int,
397 use "lbi.bi/ lhi.bi / lwi.bi" */
398 snprintf (pattern
, sizeof (pattern
), "l%ci.bi\t%%0, %%1", size
);
406 switch (GET_CODE (XEXP (code
, 1)))
410 /* (mem (plus reg reg)) or (mem (plus (mult reg const_int) reg))
411 use "lb / lh / lw" */
412 snprintf (pattern
, sizeof (pattern
), "l%c\t%%0, %%1", size
);
415 /* (mem (plus reg const_int))
416 => access location by adding one register with const_int,
417 use "lbi / lhi / lwi" */
418 snprintf (pattern
, sizeof (pattern
), "l%ci\t%%0, %%1", size
);
426 operands
[2] = XEXP (code
, 1);
427 operands
[1] = XEXP (code
, 0);
428 snprintf (pattern
, sizeof (pattern
),
429 "l%ci\t%%0, [%%1 + lo12(%%2)]", size
);
436 output_asm_insn (pattern
, operands
);
440 /* Output 32-bit load with signed extension. */
442 nds32_output_32bit_load_s (rtx
*operands
, int byte
)
448 code
= XEXP (operands
[1], 0);
450 size
= nds32_byte_to_size (byte
);
452 switch (GET_CODE (code
))
456 => access location by using register,
458 snprintf (pattern
, sizeof (pattern
), "l%csi\t%%0, %%1", size
);
463 /* (mem (symbol_ref X))
465 => access global variables,
466 use "lbsi.gp / lhsi.gp" */
467 operands
[1] = XEXP (operands
[1], 0);
468 snprintf (pattern
, sizeof (pattern
), "l%csi.gp\t%%0, [ + %%1]", size
);
472 /* (mem (post_inc reg))
473 => access location by using register which will be post increment,
474 use "lbsi.bi / lhsi.bi" */
475 snprintf (pattern
, sizeof (pattern
),
476 "l%csi.bi\t%%0, %%1, %d", size
, byte
);
480 /* (mem (post_dec reg))
481 => access location by using register which will be post decrement,
482 use "lbsi.bi / lhsi.bi" */
483 snprintf (pattern
, sizeof (pattern
),
484 "l%csi.bi\t%%0, %%1, -%d", size
, byte
);
488 switch (GET_CODE (XEXP (XEXP (code
, 1), 1)))
492 /* (mem (post_modify (reg) (plus (reg) (reg))))
493 => access location by using register which will be
494 post modified with reg,
495 use "lbs.bi/ lhs.bi" */
496 snprintf (pattern
, sizeof (pattern
), "l%cs.bi\t%%0, %%1", size
);
499 /* (mem (post_modify (reg) (plus (reg) (const_int))))
500 => access location by using register which will be
501 post modified with const_int,
502 use "lbsi.bi/ lhsi.bi" */
503 snprintf (pattern
, sizeof (pattern
), "l%csi.bi\t%%0, %%1", size
);
511 switch (GET_CODE (XEXP (code
, 1)))
515 /* (mem (plus reg reg)) or (mem (plus (mult reg const_int) reg))
517 snprintf (pattern
, sizeof (pattern
), "l%cs\t%%0, %%1", size
);
520 /* (mem (plus reg const_int))
521 => access location by adding one register with const_int,
523 snprintf (pattern
, sizeof (pattern
), "l%csi\t%%0, %%1", size
);
531 operands
[2] = XEXP (code
, 1);
532 operands
[1] = XEXP (code
, 0);
533 snprintf (pattern
, sizeof (pattern
),
534 "l%csi\t%%0, [%%1 + lo12(%%2)]", size
);
541 output_asm_insn (pattern
, operands
);
545 /* Function to output stack push operation.
546 We need to deal with normal stack push multiple or stack v3push. */
548 nds32_output_stack_push (rtx par_rtx
)
550 /* A string pattern for output_asm_insn(). */
552 /* The operands array which will be used in output_asm_insn(). */
554 /* Pick up varargs first regno and last regno for further use. */
555 int rb_va_args
= cfun
->machine
->va_args_first_regno
;
556 int re_va_args
= cfun
->machine
->va_args_last_regno
;
557 int last_argument_regno
= NDS32_FIRST_GPR_REGNUM
558 + NDS32_MAX_GPR_REGS_FOR_ARGS
560 /* Pick up callee-saved first regno and last regno for further use. */
561 int rb_callee_saved
= cfun
->machine
->callee_saved_first_gpr_regno
;
562 int re_callee_saved
= cfun
->machine
->callee_saved_last_gpr_regno
;
564 /* First we need to check if we are pushing argument registers not used
565 for the named arguments. If so, we have to create 'smw.adm' (push.s)
567 if (reg_mentioned_p (gen_rtx_REG (SImode
, last_argument_regno
), par_rtx
))
569 /* Set operands[0] and operands[1]. */
570 operands
[0] = gen_rtx_REG (SImode
, rb_va_args
);
571 operands
[1] = gen_rtx_REG (SImode
, re_va_args
);
572 /* Create assembly code pattern: "Rb, Re, { }". */
573 snprintf (pattern
, sizeof (pattern
), "push.s\t%s", "%0, %1, { }");
574 /* We use output_asm_insn() to output assembly code by ourself. */
575 output_asm_insn (pattern
, operands
);
579 /* If we step here, we are going to do v3push or multiple push operation. */
581 /* The v3push/v3pop instruction should only be applied on
582 none-isr and none-variadic function. */
584 && !nds32_isr_function_p (current_function_decl
)
585 && (cfun
->machine
->va_args_size
== 0))
589 operands[1]: imm8u */
591 /* This variable is to check if 'push25 Re,imm8u' is available. */
594 /* Set operands[0]. */
595 operands
[0] = gen_rtx_REG (SImode
, re_callee_saved
);
597 /* Check if we can generate 'push25 Re,imm8u',
598 otherwise, generate 'push25 Re,0'. */
599 sp_adjust
= cfun
->machine
->local_size
600 + cfun
->machine
->out_args_size
601 + cfun
->machine
->callee_saved_area_gpr_padding_bytes
;
602 if (satisfies_constraint_Iu08 (GEN_INT (sp_adjust
))
603 && NDS32_DOUBLE_WORD_ALIGN_P (sp_adjust
))
604 operands
[1] = GEN_INT (sp_adjust
);
606 operands
[1] = GEN_INT (0);
608 /* Create assembly code pattern. */
609 snprintf (pattern
, sizeof (pattern
), "push25\t%%0, %%1");
613 /* For normal stack push multiple:
618 /* This variable is used to check if we only need to generate En4 field.
619 As long as Rb==Re=SP_REGNUM, we set this variable to 1. */
620 int push_en4_only_p
= 0;
622 /* Set operands[0] and operands[1]. */
623 operands
[0] = gen_rtx_REG (SImode
, rb_callee_saved
);
624 operands
[1] = gen_rtx_REG (SImode
, re_callee_saved
);
626 /* 'smw.adm $sp,[$sp],$sp,0' means push nothing. */
627 if (!cfun
->machine
->fp_size
628 && !cfun
->machine
->gp_size
629 && !cfun
->machine
->lp_size
630 && REGNO (operands
[0]) == SP_REGNUM
631 && REGNO (operands
[1]) == SP_REGNUM
)
633 /* No need to generate instruction. */
638 /* If Rb==Re=SP_REGNUM, we only need to generate En4 field. */
639 if (REGNO (operands
[0]) == SP_REGNUM
640 && REGNO (operands
[1]) == SP_REGNUM
)
643 /* Create assembly code pattern.
644 We need to handle the form: "Rb, Re, { $fp $gp $lp }". */
645 snprintf (pattern
, sizeof (pattern
),
646 "push.s\t%s{%s%s%s }",
647 push_en4_only_p
? "" : "%0, %1, ",
648 cfun
->machine
->fp_size
? " $fp" : "",
649 cfun
->machine
->gp_size
? " $gp" : "",
650 cfun
->machine
->lp_size
? " $lp" : "");
654 /* We use output_asm_insn() to output assembly code by ourself. */
655 output_asm_insn (pattern
, operands
);
659 /* Function to output stack pop operation.
660 We need to deal with normal stack pop multiple or stack v3pop. */
662 nds32_output_stack_pop (rtx par_rtx ATTRIBUTE_UNUSED
)
664 /* A string pattern for output_asm_insn(). */
666 /* The operands array which will be used in output_asm_insn(). */
668 /* Pick up callee-saved first regno and last regno for further use. */
669 int rb_callee_saved
= cfun
->machine
->callee_saved_first_gpr_regno
;
670 int re_callee_saved
= cfun
->machine
->callee_saved_last_gpr_regno
;
672 /* If we step here, we are going to do v3pop or multiple pop operation. */
674 /* The v3push/v3pop instruction should only be applied on
675 none-isr and none-variadic function. */
677 && !nds32_isr_function_p (current_function_decl
)
678 && (cfun
->machine
->va_args_size
== 0))
682 operands[1]: imm8u */
684 /* This variable is to check if 'pop25 Re,imm8u' is available. */
687 /* Set operands[0]. */
688 operands
[0] = gen_rtx_REG (SImode
, re_callee_saved
);
690 /* Check if we can generate 'pop25 Re,imm8u',
691 otherwise, generate 'pop25 Re,0'.
692 We have to consider alloca issue as well.
693 If the function does call alloca(), the stack pointer is not fixed.
694 In that case, we cannot use 'pop25 Re,imm8u' directly.
695 We have to caculate stack pointer from frame pointer
696 and then use 'pop25 Re,0'. */
697 sp_adjust
= cfun
->machine
->local_size
698 + cfun
->machine
->out_args_size
699 + cfun
->machine
->callee_saved_area_gpr_padding_bytes
;
700 if (satisfies_constraint_Iu08 (GEN_INT (sp_adjust
))
701 && NDS32_DOUBLE_WORD_ALIGN_P (sp_adjust
)
702 && !cfun
->calls_alloca
)
703 operands
[1] = GEN_INT (sp_adjust
);
705 operands
[1] = GEN_INT (0);
707 /* Create assembly code pattern. */
708 snprintf (pattern
, sizeof (pattern
), "pop25\t%%0, %%1");
712 /* For normal stack pop multiple:
717 /* This variable is used to check if we only need to generate En4 field.
718 As long as Rb==Re=SP_REGNUM, we set this variable to 1. */
719 int pop_en4_only_p
= 0;
721 /* Set operands[0] and operands[1]. */
722 operands
[0] = gen_rtx_REG (SImode
, rb_callee_saved
);
723 operands
[1] = gen_rtx_REG (SImode
, re_callee_saved
);
725 /* 'lmw.bim $sp,[$sp],$sp,0' means pop nothing. */
726 if (!cfun
->machine
->fp_size
727 && !cfun
->machine
->gp_size
728 && !cfun
->machine
->lp_size
729 && REGNO (operands
[0]) == SP_REGNUM
730 && REGNO (operands
[1]) == SP_REGNUM
)
732 /* No need to generate instruction. */
737 /* If Rb==Re=SP_REGNUM, we only need to generate En4 field. */
738 if (REGNO (operands
[0]) == SP_REGNUM
739 && REGNO (operands
[1]) == SP_REGNUM
)
742 /* Create assembly code pattern.
743 We need to handle the form: "Rb, Re, { $fp $gp $lp }". */
744 snprintf (pattern
, sizeof (pattern
),
745 "pop.s\t%s{%s%s%s }",
746 pop_en4_only_p
? "" : "%0, %1, ",
747 cfun
->machine
->fp_size
? " $fp" : "",
748 cfun
->machine
->gp_size
? " $gp" : "",
749 cfun
->machine
->lp_size
? " $lp" : "");
753 /* We use output_asm_insn() to output assembly code by ourself. */
754 output_asm_insn (pattern
, operands
);
758 /* Function to generate PC relative jump table.
759 Refer to nds32.md for more details.
761 The following is the sample for the case that diff value
762 can be presented in '.short' size.
764 addi $r1, $r1, -(case_lower_bound)
765 slti $ta, $r1, (case_number)
766 beqz $ta, .L_skip_label
768 la $ta, .L35 ! get jump table address
769 lh $r1, [$ta + $r1 << 1] ! load symbol diff from jump table entry
786 nds32_output_casesi_pc_relative (rtx
*operands
)
791 diff_vec
= PATTERN (NEXT_INSN (as_a
<rtx_insn
*> (operands
[1])));
793 gcc_assert (GET_CODE (diff_vec
) == ADDR_DIFF_VEC
);
795 /* Step C: "t <-- operands[1]". */
796 output_asm_insn ("la\t$ta, %l1", operands
);
798 /* Get the mode of each element in the difference vector. */
799 mode
= GET_MODE (diff_vec
);
801 /* Step D: "z <-- (mem (plus (operands[0] << m) t))",
802 where m is 0, 1, or 2 to load address-diff value from table. */
806 output_asm_insn ("lb\t%2, [$ta + %0 << 0]", operands
);
809 output_asm_insn ("lh\t%2, [$ta + %0 << 1]", operands
);
812 output_asm_insn ("lw\t%2, [$ta + %0 << 2]", operands
);
818 /* Step E: "t <-- z + t".
819 Add table label_ref with address-diff value to
820 obtain target case address. */
821 output_asm_insn ("add\t$ta, %2, $ta", operands
);
823 /* Step F: jump to target with register t. */
830 /* Function to generate normal jump table. */
832 nds32_output_casesi (rtx
*operands
)
834 /* Step C: "t <-- operands[1]". */
835 output_asm_insn ("la\t$ta, %l1", operands
);
837 /* Step D: "z <-- (mem (plus (operands[0] << 2) t))". */
838 output_asm_insn ("lw\t%2, [$ta + %0 << 2]", operands
);
840 /* No need to perform Step E, which is only used for
841 pc relative jump table. */
843 /* Step F: jump to target with register z. */
850 /* ------------------------------------------------------------------------ */