1 /* Auxiliary functions for output asm template or expand rtl
2 pattern of Andes NDS32 cpu for GNU compiler
3 Copyright (C) 2012-2018 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 /* ------------------------------------------------------------------------ */
24 #define IN_TARGET_CODE 1
28 #include "coretypes.h"
35 #include "optabs.h" /* For GEN_FCN. */
38 #include "tm-constrs.h"
40 /* ------------------------------------------------------------------------ */
42 /* A helper function to return character based on byte size. */
44 nds32_byte_to_size (int byte
)
55 /* Normally it should not be here. */
60 /* A helper function to return memory format. */
61 enum nds32_16bit_address_type
62 nds32_mem_format (rtx op
)
64 machine_mode mode_test
;
69 return ADDRESS_NOT_16BIT_FORMAT
;
71 mode_test
= GET_MODE (op
);
76 if (GET_CODE (op
) == REG
&& (mode_test
== SImode
))
79 /* 333 format for QI/HImode. */
80 if (GET_CODE (op
) == REG
&& (REGNO (op
) < R8_REGNUM
))
81 return ADDRESS_LO_REG_IMM3U
;
83 /* post_inc 333 format. */
84 if ((GET_CODE (op
) == POST_INC
) && (mode_test
== SImode
))
86 regno
= REGNO(XEXP (op
, 0));
89 return ADDRESS_POST_INC_LO_REG_IMM3U
;
92 /* post_inc 333 format. */
93 if ((GET_CODE (op
) == POST_MODIFY
)
94 && (mode_test
== SImode
)
95 && (REG_P (XEXP (XEXP (op
, 1), 0)))
96 && (CONST_INT_P (XEXP (XEXP (op
, 1), 1))))
98 regno
= REGNO (XEXP (XEXP (op
, 1), 0));
99 val
= INTVAL (XEXP (XEXP (op
, 1), 1));
100 if (regno
< 8 && val
< 32)
101 return ADDRESS_POST_INC_LO_REG_IMM3U
;
104 if ((GET_CODE (op
) == PLUS
)
105 && (GET_CODE (XEXP (op
, 0)) == REG
)
106 && (GET_CODE (XEXP (op
, 1)) == CONST_INT
))
108 val
= INTVAL (XEXP (op
, 1));
110 regno
= REGNO(XEXP (op
, 0));
113 && regno
!= SP_REGNUM
114 && regno
!= FP_REGNUM
)
115 return ADDRESS_NOT_16BIT_FORMAT
;
121 if (val
>= 0 && val
< 8 && regno
< 8)
122 return ADDRESS_LO_REG_IMM3U
;
127 if (val
>= 0 && val
< 16 && (val
% 2 == 0) && regno
< 8)
128 return ADDRESS_LO_REG_IMM3U
;
134 /* fp imply 37 format. */
135 if ((regno
== FP_REGNUM
) &&
136 (val
>= 0 && val
< 512 && (val
% 4 == 0)))
137 return ADDRESS_FP_IMM7U
;
138 /* sp imply 37 format. */
139 else if ((regno
== SP_REGNUM
) &&
140 (val
>= 0 && val
< 512 && (val
% 4 == 0)))
141 return ADDRESS_SP_IMM7U
;
143 else if (val
>= 0 && val
< 32 && (val
% 4 == 0) && regno
< 8)
144 return ADDRESS_LO_REG_IMM3U
;
152 return ADDRESS_NOT_16BIT_FORMAT
;
155 /* Output 16-bit store. */
157 nds32_output_16bit_store (rtx
*operands
, int byte
)
161 rtx code
= XEXP (operands
[0], 0);
163 size
= nds32_byte_to_size (byte
);
165 switch (nds32_mem_format (operands
[0]))
169 output_asm_insn ("swi450\t%1, [%0]", operands
);
171 case ADDRESS_LO_REG_IMM3U
:
172 snprintf (pattern
, sizeof (pattern
), "s%ci333\t%%1, %%0", size
);
173 output_asm_insn (pattern
, operands
);
175 case ADDRESS_POST_INC_LO_REG_IMM3U
:
176 snprintf (pattern
, sizeof (pattern
), "s%ci333.bi\t%%1, %%0", size
);
177 output_asm_insn (pattern
, operands
);
179 case ADDRESS_FP_IMM7U
:
180 output_asm_insn ("swi37\t%1, %0", operands
);
182 case ADDRESS_SP_IMM7U
:
183 /* Get immediate value and set back to operands[1]. */
184 operands
[0] = XEXP (code
, 1);
185 output_asm_insn ("swi37.sp\t%1, [ + (%0)]", operands
);
194 /* Output 16-bit load. */
196 nds32_output_16bit_load (rtx
*operands
, int byte
)
200 rtx code
= XEXP (operands
[1], 0);
202 size
= nds32_byte_to_size (byte
);
204 switch (nds32_mem_format (operands
[1]))
208 output_asm_insn ("lwi450\t%0, [%1]", operands
);
210 case ADDRESS_LO_REG_IMM3U
:
211 snprintf (pattern
, sizeof (pattern
), "l%ci333\t%%0, %%1", size
);
212 output_asm_insn (pattern
, operands
);
214 case ADDRESS_POST_INC_LO_REG_IMM3U
:
215 snprintf (pattern
, sizeof (pattern
), "l%ci333.bi\t%%0, %%1", size
);
216 output_asm_insn (pattern
, operands
);
218 case ADDRESS_FP_IMM7U
:
219 output_asm_insn ("lwi37\t%0, %1", operands
);
221 case ADDRESS_SP_IMM7U
:
222 /* Get immediate value and set back to operands[0]. */
223 operands
[1] = XEXP (code
, 1);
224 output_asm_insn ("lwi37.sp\t%0, [ + (%1)]", operands
);
233 /* Output 32-bit store. */
235 nds32_output_32bit_store (rtx
*operands
, int byte
)
239 rtx code
= XEXP (operands
[0], 0);
241 size
= nds32_byte_to_size (byte
);
243 switch (GET_CODE (code
))
247 => access location by using register,
248 use "sbi / shi / swi" */
249 snprintf (pattern
, sizeof (pattern
), "s%ci\t%%1, %%0", size
);
254 /* (mem (symbol_ref X))
256 => access global variables,
257 use "sbi.gp / shi.gp / swi.gp" */
258 operands
[0] = XEXP (operands
[0], 0);
259 snprintf (pattern
, sizeof (pattern
), "s%ci.gp\t%%1, [ + %%0]", size
);
263 /* (mem (post_inc reg))
264 => access location by using register which will be post increment,
265 use "sbi.bi / shi.bi / swi.bi" */
266 snprintf (pattern
, sizeof (pattern
),
267 "s%ci.bi\t%%1, %%0, %d", size
, byte
);
271 /* (mem (post_dec reg))
272 => access location by using register which will be post decrement,
273 use "sbi.bi / shi.bi / swi.bi" */
274 snprintf (pattern
, sizeof (pattern
),
275 "s%ci.bi\t%%1, %%0, -%d", size
, byte
);
279 switch (GET_CODE (XEXP (XEXP (code
, 1), 1)))
283 /* (mem (post_modify (reg) (plus (reg) (reg))))
284 => access location by using register which will be
285 post modified with reg,
286 use "sb.bi/ sh.bi / sw.bi" */
287 snprintf (pattern
, sizeof (pattern
), "s%c.bi\t%%1, %%0", size
);
290 /* (mem (post_modify (reg) (plus (reg) (const_int))))
291 => access location by using register which will be
292 post modified with const_int,
293 use "sbi.bi/ shi.bi / swi.bi" */
294 snprintf (pattern
, sizeof (pattern
), "s%ci.bi\t%%1, %%0", size
);
302 switch (GET_CODE (XEXP (code
, 1)))
306 /* (mem (plus reg reg)) or (mem (plus (mult reg const_int) reg))
307 => access location by adding two registers,
308 use "sb / sh / sw" */
309 snprintf (pattern
, sizeof (pattern
), "s%c\t%%1, %%0", size
);
312 /* (mem (plus reg const_int))
313 => access location by adding one register with const_int,
314 use "sbi / shi / swi" */
315 snprintf (pattern
, sizeof (pattern
), "s%ci\t%%1, %%0", size
);
323 operands
[2] = XEXP (code
, 1);
324 operands
[0] = XEXP (code
, 0);
325 snprintf (pattern
, sizeof (pattern
),
326 "s%ci\t%%1, [%%0 + lo12(%%2)]", size
);
333 output_asm_insn (pattern
, operands
);
337 /* Output 32-bit load. */
339 nds32_output_32bit_load (rtx
*operands
, int byte
)
345 code
= XEXP (operands
[1], 0);
347 size
= nds32_byte_to_size (byte
);
349 switch (GET_CODE (code
))
353 => access location by using register,
354 use "lbi / lhi / lwi" */
355 snprintf (pattern
, sizeof (pattern
), "l%ci\t%%0, %%1", size
);
360 /* (mem (symbol_ref X))
362 => access global variables,
363 use "lbi.gp / lhi.gp / lwi.gp" */
364 operands
[1] = XEXP (operands
[1], 0);
365 snprintf (pattern
, sizeof (pattern
), "l%ci.gp\t%%0, [ + %%1]", size
);
369 /* (mem (post_inc reg))
370 => access location by using register which will be post increment,
371 use "lbi.bi / lhi.bi / lwi.bi" */
372 snprintf (pattern
, sizeof (pattern
),
373 "l%ci.bi\t%%0, %%1, %d", size
, byte
);
377 /* (mem (post_dec reg))
378 => access location by using register which will be post decrement,
379 use "lbi.bi / lhi.bi / lwi.bi" */
380 snprintf (pattern
, sizeof (pattern
),
381 "l%ci.bi\t%%0, %%1, -%d", size
, byte
);
385 switch (GET_CODE (XEXP (XEXP (code
, 1), 1)))
389 /* (mem (post_modify (reg) (plus (reg) (reg))))
390 => access location by using register which will be
391 post modified with reg,
392 use "lb.bi/ lh.bi / lw.bi" */
393 snprintf (pattern
, sizeof (pattern
), "l%c.bi\t%%0, %%1", size
);
396 /* (mem (post_modify (reg) (plus (reg) (const_int))))
397 => access location by using register which will be
398 post modified with const_int,
399 use "lbi.bi/ lhi.bi / lwi.bi" */
400 snprintf (pattern
, sizeof (pattern
), "l%ci.bi\t%%0, %%1", size
);
408 switch (GET_CODE (XEXP (code
, 1)))
412 /* (mem (plus reg reg)) or (mem (plus (mult reg const_int) reg))
413 use "lb / lh / lw" */
414 snprintf (pattern
, sizeof (pattern
), "l%c\t%%0, %%1", size
);
417 /* (mem (plus reg const_int))
418 => access location by adding one register with const_int,
419 use "lbi / lhi / lwi" */
420 snprintf (pattern
, sizeof (pattern
), "l%ci\t%%0, %%1", size
);
428 operands
[2] = XEXP (code
, 1);
429 operands
[1] = XEXP (code
, 0);
430 snprintf (pattern
, sizeof (pattern
),
431 "l%ci\t%%0, [%%1 + lo12(%%2)]", size
);
438 output_asm_insn (pattern
, operands
);
442 /* Output 32-bit load with signed extension. */
444 nds32_output_32bit_load_s (rtx
*operands
, int byte
)
450 code
= XEXP (operands
[1], 0);
452 size
= nds32_byte_to_size (byte
);
454 switch (GET_CODE (code
))
458 => access location by using register,
460 snprintf (pattern
, sizeof (pattern
), "l%csi\t%%0, %%1", size
);
465 /* (mem (symbol_ref X))
467 => access global variables,
468 use "lbsi.gp / lhsi.gp" */
469 operands
[1] = XEXP (operands
[1], 0);
470 snprintf (pattern
, sizeof (pattern
), "l%csi.gp\t%%0, [ + %%1]", size
);
474 /* (mem (post_inc reg))
475 => access location by using register which will be post increment,
476 use "lbsi.bi / lhsi.bi" */
477 snprintf (pattern
, sizeof (pattern
),
478 "l%csi.bi\t%%0, %%1, %d", size
, byte
);
482 /* (mem (post_dec reg))
483 => access location by using register which will be post decrement,
484 use "lbsi.bi / lhsi.bi" */
485 snprintf (pattern
, sizeof (pattern
),
486 "l%csi.bi\t%%0, %%1, -%d", size
, byte
);
490 switch (GET_CODE (XEXP (XEXP (code
, 1), 1)))
494 /* (mem (post_modify (reg) (plus (reg) (reg))))
495 => access location by using register which will be
496 post modified with reg,
497 use "lbs.bi/ lhs.bi" */
498 snprintf (pattern
, sizeof (pattern
), "l%cs.bi\t%%0, %%1", size
);
501 /* (mem (post_modify (reg) (plus (reg) (const_int))))
502 => access location by using register which will be
503 post modified with const_int,
504 use "lbsi.bi/ lhsi.bi" */
505 snprintf (pattern
, sizeof (pattern
), "l%csi.bi\t%%0, %%1", size
);
513 switch (GET_CODE (XEXP (code
, 1)))
517 /* (mem (plus reg reg)) or (mem (plus (mult reg const_int) reg))
519 snprintf (pattern
, sizeof (pattern
), "l%cs\t%%0, %%1", size
);
522 /* (mem (plus reg const_int))
523 => access location by adding one register with const_int,
525 snprintf (pattern
, sizeof (pattern
), "l%csi\t%%0, %%1", size
);
533 operands
[2] = XEXP (code
, 1);
534 operands
[1] = XEXP (code
, 0);
535 snprintf (pattern
, sizeof (pattern
),
536 "l%csi\t%%0, [%%1 + lo12(%%2)]", size
);
543 output_asm_insn (pattern
, operands
);
547 /* Function to output stack push operation.
548 We need to deal with normal stack push multiple or stack v3push. */
550 nds32_output_stack_push (rtx par_rtx
)
552 /* A string pattern for output_asm_insn(). */
554 /* The operands array which will be used in output_asm_insn(). */
556 /* Pick up varargs first regno and last regno for further use. */
557 int rb_va_args
= cfun
->machine
->va_args_first_regno
;
558 int re_va_args
= cfun
->machine
->va_args_last_regno
;
559 int last_argument_regno
= NDS32_FIRST_GPR_REGNUM
560 + NDS32_MAX_GPR_REGS_FOR_ARGS
562 /* Pick up callee-saved first regno and last regno for further use. */
563 int rb_callee_saved
= cfun
->machine
->callee_saved_first_gpr_regno
;
564 int re_callee_saved
= cfun
->machine
->callee_saved_last_gpr_regno
;
566 /* First we need to check if we are pushing argument registers not used
567 for the named arguments. If so, we have to create 'smw.adm' (push.s)
569 if (reg_mentioned_p (gen_rtx_REG (SImode
, last_argument_regno
), par_rtx
))
571 /* Set operands[0] and operands[1]. */
572 operands
[0] = gen_rtx_REG (SImode
, rb_va_args
);
573 operands
[1] = gen_rtx_REG (SImode
, re_va_args
);
574 /* Create assembly code pattern: "Rb, Re, { }". */
575 snprintf (pattern
, sizeof (pattern
), "push.s\t%s", "%0, %1, { }");
576 /* We use output_asm_insn() to output assembly code by ourself. */
577 output_asm_insn (pattern
, operands
);
581 /* If we step here, we are going to do v3push or multiple push operation. */
583 /* The v3push/v3pop instruction should only be applied on
584 none-isr and none-variadic function. */
586 && !nds32_isr_function_p (current_function_decl
)
587 && (cfun
->machine
->va_args_size
== 0))
591 operands[1]: imm8u */
593 /* This variable is to check if 'push25 Re,imm8u' is available. */
596 /* Set operands[0]. */
597 operands
[0] = gen_rtx_REG (SImode
, re_callee_saved
);
599 /* Check if we can generate 'push25 Re,imm8u',
600 otherwise, generate 'push25 Re,0'. */
601 sp_adjust
= cfun
->machine
->local_size
602 + cfun
->machine
->out_args_size
603 + cfun
->machine
->callee_saved_area_gpr_padding_bytes
;
604 if (satisfies_constraint_Iu08 (GEN_INT (sp_adjust
))
605 && NDS32_DOUBLE_WORD_ALIGN_P (sp_adjust
))
606 operands
[1] = GEN_INT (sp_adjust
);
608 operands
[1] = GEN_INT (0);
610 /* Create assembly code pattern. */
611 snprintf (pattern
, sizeof (pattern
), "push25\t%%0, %%1");
615 /* For normal stack push multiple:
620 /* This variable is used to check if we only need to generate En4 field.
621 As long as Rb==Re=SP_REGNUM, we set this variable to 1. */
622 int push_en4_only_p
= 0;
624 /* Set operands[0] and operands[1]. */
625 operands
[0] = gen_rtx_REG (SImode
, rb_callee_saved
);
626 operands
[1] = gen_rtx_REG (SImode
, re_callee_saved
);
628 /* 'smw.adm $sp,[$sp],$sp,0' means push nothing. */
629 if (!cfun
->machine
->fp_size
630 && !cfun
->machine
->gp_size
631 && !cfun
->machine
->lp_size
632 && REGNO (operands
[0]) == SP_REGNUM
633 && REGNO (operands
[1]) == SP_REGNUM
)
635 /* No need to generate instruction. */
640 /* If Rb==Re=SP_REGNUM, we only need to generate En4 field. */
641 if (REGNO (operands
[0]) == SP_REGNUM
642 && REGNO (operands
[1]) == SP_REGNUM
)
645 /* Create assembly code pattern.
646 We need to handle the form: "Rb, Re, { $fp $gp $lp }". */
647 snprintf (pattern
, sizeof (pattern
),
648 "push.s\t%s{%s%s%s }",
649 push_en4_only_p
? "" : "%0, %1, ",
650 cfun
->machine
->fp_size
? " $fp" : "",
651 cfun
->machine
->gp_size
? " $gp" : "",
652 cfun
->machine
->lp_size
? " $lp" : "");
656 /* We use output_asm_insn() to output assembly code by ourself. */
657 output_asm_insn (pattern
, operands
);
661 /* Function to output stack pop operation.
662 We need to deal with normal stack pop multiple or stack v3pop. */
664 nds32_output_stack_pop (rtx par_rtx ATTRIBUTE_UNUSED
)
666 /* A string pattern for output_asm_insn(). */
668 /* The operands array which will be used in output_asm_insn(). */
670 /* Pick up callee-saved first regno and last regno for further use. */
671 int rb_callee_saved
= cfun
->machine
->callee_saved_first_gpr_regno
;
672 int re_callee_saved
= cfun
->machine
->callee_saved_last_gpr_regno
;
674 /* If we step here, we are going to do v3pop or multiple pop operation. */
676 /* The v3push/v3pop instruction should only be applied on
677 none-isr and none-variadic function. */
679 && !nds32_isr_function_p (current_function_decl
)
680 && (cfun
->machine
->va_args_size
== 0))
684 operands[1]: imm8u */
686 /* This variable is to check if 'pop25 Re,imm8u' is available. */
689 /* Set operands[0]. */
690 operands
[0] = gen_rtx_REG (SImode
, re_callee_saved
);
692 /* Check if we can generate 'pop25 Re,imm8u',
693 otherwise, generate 'pop25 Re,0'.
694 We have to consider alloca issue as well.
695 If the function does call alloca(), the stack pointer is not fixed.
696 In that case, we cannot use 'pop25 Re,imm8u' directly.
697 We have to caculate stack pointer from frame pointer
698 and then use 'pop25 Re,0'. */
699 sp_adjust
= cfun
->machine
->local_size
700 + cfun
->machine
->out_args_size
701 + cfun
->machine
->callee_saved_area_gpr_padding_bytes
;
702 if (satisfies_constraint_Iu08 (GEN_INT (sp_adjust
))
703 && NDS32_DOUBLE_WORD_ALIGN_P (sp_adjust
)
704 && !cfun
->calls_alloca
)
705 operands
[1] = GEN_INT (sp_adjust
);
707 operands
[1] = GEN_INT (0);
709 /* Create assembly code pattern. */
710 snprintf (pattern
, sizeof (pattern
), "pop25\t%%0, %%1");
714 /* For normal stack pop multiple:
719 /* This variable is used to check if we only need to generate En4 field.
720 As long as Rb==Re=SP_REGNUM, we set this variable to 1. */
721 int pop_en4_only_p
= 0;
723 /* Set operands[0] and operands[1]. */
724 operands
[0] = gen_rtx_REG (SImode
, rb_callee_saved
);
725 operands
[1] = gen_rtx_REG (SImode
, re_callee_saved
);
727 /* 'lmw.bim $sp,[$sp],$sp,0' means pop nothing. */
728 if (!cfun
->machine
->fp_size
729 && !cfun
->machine
->gp_size
730 && !cfun
->machine
->lp_size
731 && REGNO (operands
[0]) == SP_REGNUM
732 && REGNO (operands
[1]) == SP_REGNUM
)
734 /* No need to generate instruction. */
739 /* If Rb==Re=SP_REGNUM, we only need to generate En4 field. */
740 if (REGNO (operands
[0]) == SP_REGNUM
741 && REGNO (operands
[1]) == SP_REGNUM
)
744 /* Create assembly code pattern.
745 We need to handle the form: "Rb, Re, { $fp $gp $lp }". */
746 snprintf (pattern
, sizeof (pattern
),
747 "pop.s\t%s{%s%s%s }",
748 pop_en4_only_p
? "" : "%0, %1, ",
749 cfun
->machine
->fp_size
? " $fp" : "",
750 cfun
->machine
->gp_size
? " $gp" : "",
751 cfun
->machine
->lp_size
? " $lp" : "");
755 /* We use output_asm_insn() to output assembly code by ourself. */
756 output_asm_insn (pattern
, operands
);
760 /* Function to generate PC relative jump table.
761 Refer to nds32.md for more details.
763 The following is the sample for the case that diff value
764 can be presented in '.short' size.
766 addi $r1, $r1, -(case_lower_bound)
767 slti $ta, $r1, (case_number)
768 beqz $ta, .L_skip_label
770 la $ta, .L35 ! get jump table address
771 lh $r1, [$ta + $r1 << 1] ! load symbol diff from jump table entry
788 nds32_output_casesi_pc_relative (rtx
*operands
)
793 diff_vec
= PATTERN (NEXT_INSN (as_a
<rtx_insn
*> (operands
[1])));
795 gcc_assert (GET_CODE (diff_vec
) == ADDR_DIFF_VEC
);
797 /* Step C: "t <-- operands[1]". */
798 output_asm_insn ("la\t$ta, %l1", operands
);
800 /* Get the mode of each element in the difference vector. */
801 mode
= GET_MODE (diff_vec
);
803 /* Step D: "z <-- (mem (plus (operands[0] << m) t))",
804 where m is 0, 1, or 2 to load address-diff value from table. */
808 output_asm_insn ("lb\t%2, [$ta + %0 << 0]", operands
);
811 output_asm_insn ("lh\t%2, [$ta + %0 << 1]", operands
);
814 output_asm_insn ("lw\t%2, [$ta + %0 << 2]", operands
);
820 /* Step E: "t <-- z + t".
821 Add table label_ref with address-diff value to
822 obtain target case address. */
823 output_asm_insn ("add\t$ta, %2, $ta", operands
);
825 /* Step F: jump to target with register t. */
832 /* Function to generate normal jump table. */
834 nds32_output_casesi (rtx
*operands
)
836 /* Step C: "t <-- operands[1]". */
837 output_asm_insn ("la\t$ta, %l1", operands
);
839 /* Step D: "z <-- (mem (plus (operands[0] << 2) t))". */
840 output_asm_insn ("lw\t%2, [$ta + %0 << 2]", operands
);
842 /* No need to perform Step E, which is only used for
843 pc relative jump table. */
845 /* Step F: jump to target with register z. */
852 /* ------------------------------------------------------------------------ */