2014-07-04 Chung-Ju Wu <jasonwucj@gmail.com>
+ Kito Cheng <kito@0xlab.org>
+ Monk Chiang <sh.chiang04@gmail.com>
+
+ * config/nds32/nds32.c (nds32_byte_to_size): Move to ...
+ (nds32_output_casesi_pc_relative): Move to ...
+ (nds32_output_casesi): Move to ...
+ (nds32_mem_format): Move to ...
+ (nds32_output_16bit_store): Move to ...
+ (nds32_output_16bit_load): Move to ...
+ (nds32_output_32bit_store): Move to ...
+ (nds32_output_32bit_load): Move to ...
+ (nds32_output_32bit_load_s): Move to ...
+ (nds32_output_stack_push): Move to ...
+ (nds32_output_stack_pop): Move to ...
+ * config/nds32/nds32-md-auxiliary.c: ... here.
+
+2014-07-04 Chung-Ju Wu <jasonwucj@gmail.com>
Ling-Hua Tseng <uranus@tinlans.org>
* config/nds32/nds32-pipelines-auxiliary.c: Add comment to describe
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
+
+/* ------------------------------------------------------------------------ */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "stor-layout.h"
+#include "varasm.h"
+#include "calls.h"
+#include "rtl.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "insn-config.h" /* Required by recog.h. */
+#include "conditions.h"
+#include "output.h"
+#include "insn-attr.h" /* For DFA state_t. */
+#include "insn-codes.h" /* For CODE_FOR_xxx. */
+#include "reload.h" /* For push_reload(). */
+#include "flags.h"
+#include "function.h"
+#include "expr.h"
+#include "recog.h"
+#include "diagnostic-core.h"
+#include "df.h"
+#include "tm_p.h"
+#include "tm-constrs.h"
+#include "optabs.h" /* For GEN_FCN. */
+#include "target.h"
+#include "target-def.h"
+#include "langhooks.h" /* For add_builtin_function(). */
+#include "ggc.h"
+#include "builtins.h"
+
+/* ------------------------------------------------------------------------ */
+
+/* A helper function to return character based on byte size. */
+static char
+nds32_byte_to_size (int byte)
+{
+ switch (byte)
+ {
+ case 4:
+ return 'w';
+ case 2:
+ return 'h';
+ case 1:
+ return 'b';
+ default:
+ /* Normally it should not be here. */
+ gcc_unreachable ();
+ }
+}
+
+/* A helper function to return memory format. */
+enum nds32_16bit_address_type
+nds32_mem_format (rtx op)
+{
+ enum machine_mode mode_test;
+ int val;
+ int regno;
+
+ if (!TARGET_16_BIT)
+ return ADDRESS_NOT_16BIT_FORMAT;
+
+ mode_test = GET_MODE (op);
+
+ op = XEXP (op, 0);
+
+ /* 45 format. */
+ if (GET_CODE (op) == REG && (mode_test == SImode))
+ return ADDRESS_REG;
+
+ /* 333 format for QI/HImode. */
+ if (GET_CODE (op) == REG && (REGNO (op) < R8_REGNUM))
+ return ADDRESS_LO_REG_IMM3U;
+
+ /* post_inc 333 format. */
+ if ((GET_CODE (op) == POST_INC) && (mode_test == SImode))
+ {
+ regno = REGNO(XEXP (op, 0));
+
+ if (regno < 8)
+ return ADDRESS_POST_INC_LO_REG_IMM3U;
+ }
+
+ /* post_inc 333 format. */
+ if ((GET_CODE (op) == POST_MODIFY)
+ && (mode_test == SImode)
+ && (REG_P (XEXP (XEXP (op, 1), 0)))
+ && (CONST_INT_P (XEXP (XEXP (op, 1), 1))))
+ {
+ regno = REGNO (XEXP (XEXP (op, 1), 0));
+ val = INTVAL (XEXP (XEXP (op, 1), 1));
+ if (regno < 8 && val < 32)
+ return ADDRESS_POST_INC_LO_REG_IMM3U;
+ }
+
+ if ((GET_CODE (op) == PLUS)
+ && (GET_CODE (XEXP (op, 0)) == REG)
+ && (GET_CODE (XEXP (op, 1)) == CONST_INT))
+ {
+ val = INTVAL (XEXP (op, 1));
+
+ regno = REGNO(XEXP (op, 0));
+
+ if (regno > 7
+ && regno != SP_REGNUM
+ && regno != FP_REGNUM)
+ return ADDRESS_NOT_16BIT_FORMAT;
+
+ switch (mode_test)
+ {
+ case QImode:
+ /* 333 format. */
+ if (val >= 0 && val < 8 && regno < 8)
+ return ADDRESS_LO_REG_IMM3U;
+ break;
+
+ case HImode:
+ /* 333 format. */
+ if (val >= 0 && val < 16 && (val % 2 == 0) && regno < 8)
+ return ADDRESS_LO_REG_IMM3U;
+ break;
+
+ case SImode:
+ case SFmode:
+ case DFmode:
+ /* fp imply 37 format. */
+ if ((regno == FP_REGNUM) &&
+ (val >= 0 && val < 512 && (val % 4 == 0)))
+ return ADDRESS_FP_IMM7U;
+ /* sp imply 37 format. */
+ else if ((regno == SP_REGNUM) &&
+ (val >= 0 && val < 512 && (val % 4 == 0)))
+ return ADDRESS_SP_IMM7U;
+ /* 333 format. */
+ else if (val >= 0 && val < 32 && (val % 4 == 0) && regno < 8)
+ return ADDRESS_LO_REG_IMM3U;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return ADDRESS_NOT_16BIT_FORMAT;
+}
+
+/* Output 16-bit store. */
+const char *
+nds32_output_16bit_store (rtx *operands, int byte)
+{
+ char pattern[100];
+ char size;
+ rtx code = XEXP (operands[0], 0);
+
+ size = nds32_byte_to_size (byte);
+
+ switch (nds32_mem_format (operands[0]))
+ {
+ case ADDRESS_REG:
+ operands[0] = code;
+ output_asm_insn ("swi450\t%1, [%0]", operands);
+ break;
+ case ADDRESS_LO_REG_IMM3U:
+ snprintf (pattern, sizeof (pattern), "s%ci333\t%%1, %%0", size);
+ output_asm_insn (pattern, operands);
+ break;
+ case ADDRESS_POST_INC_LO_REG_IMM3U:
+ snprintf (pattern, sizeof (pattern), "s%ci333.bi\t%%1, %%0", size);
+ output_asm_insn (pattern, operands);
+ break;
+ case ADDRESS_FP_IMM7U:
+ output_asm_insn ("swi37\t%1, %0", operands);
+ break;
+ case ADDRESS_SP_IMM7U:
+ /* Get immediate value and set back to operands[1]. */
+ operands[0] = XEXP (code, 1);
+ output_asm_insn ("swi37.sp\t%1, [ + (%0)]", operands);
+ break;
+ default:
+ break;
+ }
+
+ return "";
+}
+
+/* Output 16-bit load. */
+const char *
+nds32_output_16bit_load (rtx *operands, int byte)
+{
+ char pattern[100];
+ unsigned char size;
+ rtx code = XEXP (operands[1], 0);
+
+ size = nds32_byte_to_size (byte);
+
+ switch (nds32_mem_format (operands[1]))
+ {
+ case ADDRESS_REG:
+ operands[1] = code;
+ output_asm_insn ("lwi450\t%0, [%1]", operands);
+ break;
+ case ADDRESS_LO_REG_IMM3U:
+ snprintf (pattern, sizeof (pattern), "l%ci333\t%%0, %%1", size);
+ output_asm_insn (pattern, operands);
+ break;
+ case ADDRESS_POST_INC_LO_REG_IMM3U:
+ snprintf (pattern, sizeof (pattern), "l%ci333.bi\t%%0, %%1", size);
+ output_asm_insn (pattern, operands);
+ break;
+ case ADDRESS_FP_IMM7U:
+ output_asm_insn ("lwi37\t%0, %1", operands);
+ break;
+ case ADDRESS_SP_IMM7U:
+ /* Get immediate value and set back to operands[0]. */
+ operands[1] = XEXP (code, 1);
+ output_asm_insn ("lwi37.sp\t%0, [ + (%1)]", operands);
+ break;
+ default:
+ break;
+ }
+
+ return "";
+}
+
+/* Output 32-bit store. */
+const char *
+nds32_output_32bit_store (rtx *operands, int byte)
+{
+ char pattern[100];
+ unsigned char size;
+ rtx code = XEXP (operands[0], 0);
+
+ size = nds32_byte_to_size (byte);
+
+ switch (GET_CODE (code))
+ {
+ case REG:
+ /* (mem (reg X))
+ => access location by using register,
+ use "sbi / shi / swi" */
+ snprintf (pattern, sizeof (pattern), "s%ci\t%%1, %%0", size);
+ break;
+
+ case SYMBOL_REF:
+ case CONST:
+ /* (mem (symbol_ref X))
+ (mem (const (...)))
+ => access global variables,
+ use "sbi.gp / shi.gp / swi.gp" */
+ operands[0] = XEXP (operands[0], 0);
+ snprintf (pattern, sizeof (pattern), "s%ci.gp\t%%1, [ + %%0]", size);
+ break;
+
+ case POST_INC:
+ /* (mem (post_inc reg))
+ => access location by using register which will be post increment,
+ use "sbi.bi / shi.bi / swi.bi" */
+ snprintf (pattern, sizeof (pattern),
+ "s%ci.bi\t%%1, %%0, %d", size, byte);
+ break;
+
+ case POST_DEC:
+ /* (mem (post_dec reg))
+ => access location by using register which will be post decrement,
+ use "sbi.bi / shi.bi / swi.bi" */
+ snprintf (pattern, sizeof (pattern),
+ "s%ci.bi\t%%1, %%0, -%d", size, byte);
+ break;
+
+ case POST_MODIFY:
+ switch (GET_CODE (XEXP (XEXP (code, 1), 1)))
+ {
+ case REG:
+ case SUBREG:
+ /* (mem (post_modify (reg) (plus (reg) (reg))))
+ => access location by using register which will be
+ post modified with reg,
+ use "sb.bi/ sh.bi / sw.bi" */
+ snprintf (pattern, sizeof (pattern), "s%c.bi\t%%1, %%0", size);
+ break;
+ case CONST_INT:
+ /* (mem (post_modify (reg) (plus (reg) (const_int))))
+ => access location by using register which will be
+ post modified with const_int,
+ use "sbi.bi/ shi.bi / swi.bi" */
+ snprintf (pattern, sizeof (pattern), "s%ci.bi\t%%1, %%0", size);
+ break;
+ default:
+ abort ();
+ }
+ break;
+
+ case PLUS:
+ switch (GET_CODE (XEXP (code, 1)))
+ {
+ case REG:
+ case SUBREG:
+ /* (mem (plus reg reg)) or (mem (plus (mult reg const_int) reg))
+ => access location by adding two registers,
+ use "sb / sh / sw" */
+ snprintf (pattern, sizeof (pattern), "s%c\t%%1, %%0", size);
+ break;
+ case CONST_INT:
+ /* (mem (plus reg const_int))
+ => access location by adding one register with const_int,
+ use "sbi / shi / swi" */
+ snprintf (pattern, sizeof (pattern), "s%ci\t%%1, %%0", size);
+ break;
+ default:
+ abort ();
+ }
+ break;
+
+ case LO_SUM:
+ operands[2] = XEXP (code, 1);
+ operands[0] = XEXP (code, 0);
+ snprintf (pattern, sizeof (pattern),
+ "s%ci\t%%1, [%%0 + lo12(%%2)]", size);
+ break;
+
+ default:
+ abort ();
+ }
+
+ output_asm_insn (pattern, operands);
+ return "";
+}
+
+/* Output 32-bit load. */
+const char *
+nds32_output_32bit_load (rtx *operands, int byte)
+{
+ char pattern[100];
+ unsigned char size;
+ rtx code;
+
+ code = XEXP (operands[1], 0);
+
+ size = nds32_byte_to_size (byte);
+
+ switch (GET_CODE (code))
+ {
+ case REG:
+ /* (mem (reg X))
+ => access location by using register,
+ use "lbi / lhi / lwi" */
+ snprintf (pattern, sizeof (pattern), "l%ci\t%%0, %%1", size);
+ break;
+
+ case SYMBOL_REF:
+ case CONST:
+ /* (mem (symbol_ref X))
+ (mem (const (...)))
+ => access global variables,
+ use "lbi.gp / lhi.gp / lwi.gp" */
+ operands[1] = XEXP (operands[1], 0);
+ snprintf (pattern, sizeof (pattern), "l%ci.gp\t%%0, [ + %%1]", size);
+ break;
+
+ case POST_INC:
+ /* (mem (post_inc reg))
+ => access location by using register which will be post increment,
+ use "lbi.bi / lhi.bi / lwi.bi" */
+ snprintf (pattern, sizeof (pattern),
+ "l%ci.bi\t%%0, %%1, %d", size, byte);
+ break;
+
+ case POST_DEC:
+ /* (mem (post_dec reg))
+ => access location by using register which will be post decrement,
+ use "lbi.bi / lhi.bi / lwi.bi" */
+ snprintf (pattern, sizeof (pattern),
+ "l%ci.bi\t%%0, %%1, -%d", size, byte);
+ break;
+
+ case POST_MODIFY:
+ switch (GET_CODE (XEXP (XEXP (code, 1), 1)))
+ {
+ case REG:
+ case SUBREG:
+ /* (mem (post_modify (reg) (plus (reg) (reg))))
+ => access location by using register which will be
+ post modified with reg,
+ use "lb.bi/ lh.bi / lw.bi" */
+ snprintf (pattern, sizeof (pattern), "l%c.bi\t%%0, %%1", size);
+ break;
+ case CONST_INT:
+ /* (mem (post_modify (reg) (plus (reg) (const_int))))
+ => access location by using register which will be
+ post modified with const_int,
+ use "lbi.bi/ lhi.bi / lwi.bi" */
+ snprintf (pattern, sizeof (pattern), "l%ci.bi\t%%0, %%1", size);
+ break;
+ default:
+ abort ();
+ }
+ break;
+
+ case PLUS:
+ switch (GET_CODE (XEXP (code, 1)))
+ {
+ case REG:
+ case SUBREG:
+ /* (mem (plus reg reg)) or (mem (plus (mult reg const_int) reg))
+ use "lb / lh / lw" */
+ snprintf (pattern, sizeof (pattern), "l%c\t%%0, %%1", size);
+ break;
+ case CONST_INT:
+ /* (mem (plus reg const_int))
+ => access location by adding one register with const_int,
+ use "lbi / lhi / lwi" */
+ snprintf (pattern, sizeof (pattern), "l%ci\t%%0, %%1", size);
+ break;
+ default:
+ abort ();
+ }
+ break;
+
+ case LO_SUM:
+ operands[2] = XEXP (code, 1);
+ operands[1] = XEXP (code, 0);
+ snprintf (pattern, sizeof (pattern),
+ "l%ci\t%%0, [%%1 + lo12(%%2)]", size);
+ break;
+
+ default:
+ abort ();
+ }
+
+ output_asm_insn (pattern, operands);
+ return "";
+}
+
+/* Output 32-bit load with signed extension. */
+const char *
+nds32_output_32bit_load_s (rtx *operands, int byte)
+{
+ char pattern[100];
+ unsigned char size;
+ rtx code;
+
+ code = XEXP (operands[1], 0);
+
+ size = nds32_byte_to_size (byte);
+
+ switch (GET_CODE (code))
+ {
+ case REG:
+ /* (mem (reg X))
+ => access location by using register,
+ use "lbsi / lhsi" */
+ snprintf (pattern, sizeof (pattern), "l%csi\t%%0, %%1", size);
+ break;
+
+ case SYMBOL_REF:
+ case CONST:
+ /* (mem (symbol_ref X))
+ (mem (const (...)))
+ => access global variables,
+ use "lbsi.gp / lhsi.gp" */
+ operands[1] = XEXP (operands[1], 0);
+ snprintf (pattern, sizeof (pattern), "l%csi.gp\t%%0, [ + %%1]", size);
+ break;
+
+ case POST_INC:
+ /* (mem (post_inc reg))
+ => access location by using register which will be post increment,
+ use "lbsi.bi / lhsi.bi" */
+ snprintf (pattern, sizeof (pattern),
+ "l%csi.bi\t%%0, %%1, %d", size, byte);
+ break;
+
+ case POST_DEC:
+ /* (mem (post_dec reg))
+ => access location by using register which will be post decrement,
+ use "lbsi.bi / lhsi.bi" */
+ snprintf (pattern, sizeof (pattern),
+ "l%csi.bi\t%%0, %%1, -%d", size, byte);
+ break;
+
+ case POST_MODIFY:
+ switch (GET_CODE (XEXP (XEXP (code, 1), 1)))
+ {
+ case REG:
+ case SUBREG:
+ /* (mem (post_modify (reg) (plus (reg) (reg))))
+ => access location by using register which will be
+ post modified with reg,
+ use "lbs.bi/ lhs.bi" */
+ snprintf (pattern, sizeof (pattern), "l%cs.bi\t%%0, %%1", size);
+ break;
+ case CONST_INT:
+ /* (mem (post_modify (reg) (plus (reg) (const_int))))
+ => access location by using register which will be
+ post modified with const_int,
+ use "lbsi.bi/ lhsi.bi" */
+ snprintf (pattern, sizeof (pattern), "l%csi.bi\t%%0, %%1", size);
+ break;
+ default:
+ abort ();
+ }
+ break;
+
+ case PLUS:
+ switch (GET_CODE (XEXP (code, 1)))
+ {
+ case REG:
+ case SUBREG:
+ /* (mem (plus reg reg)) or (mem (plus (mult reg const_int) reg))
+ use "lbs / lhs" */
+ snprintf (pattern, sizeof (pattern), "l%cs\t%%0, %%1", size);
+ break;
+ case CONST_INT:
+ /* (mem (plus reg const_int))
+ => access location by adding one register with const_int,
+ use "lbsi / lhsi" */
+ snprintf (pattern, sizeof (pattern), "l%csi\t%%0, %%1", size);
+ break;
+ default:
+ abort ();
+ }
+ break;
+
+ case LO_SUM:
+ operands[2] = XEXP (code, 1);
+ operands[1] = XEXP (code, 0);
+ snprintf (pattern, sizeof (pattern),
+ "l%csi\t%%0, [%%1 + lo12(%%2)]", size);
+ break;
+
+ default:
+ abort ();
+ }
+
+ output_asm_insn (pattern, operands);
+ return "";
+}
+
+/* Function to output stack push operation.
+ We need to deal with normal stack push multiple or stack v3push. */
+const char *
+nds32_output_stack_push (void)
+{
+ /* A string pattern for output_asm_insn(). */
+ char pattern[100];
+ /* The operands array which will be used in output_asm_insn(). */
+ rtx operands[3];
+ /* Pick up callee-saved first regno and last regno for further use. */
+ int rb_regno = cfun->machine->callee_saved_regs_first_regno;
+ int re_regno = cfun->machine->callee_saved_regs_last_regno;
+
+ if (TARGET_V3PUSH)
+ {
+ /* For stack v3push:
+ operands[0]: Re
+ operands[1]: imm8u */
+
+ /* This variable is to check if 'push25 Re,imm8u' is available. */
+ int sp_adjust;
+
+ /* Set operands[0]. */
+ operands[0] = gen_rtx_REG (SImode, re_regno);
+
+ /* Check if we can generate 'push25 Re,imm8u',
+ otherwise, generate 'push25 Re,0'. */
+ sp_adjust = cfun->machine->local_size
+ + cfun->machine->out_args_size
+ + cfun->machine->callee_saved_area_padding_bytes;
+ if (satisfies_constraint_Iu08 (GEN_INT (sp_adjust))
+ && NDS32_DOUBLE_WORD_ALIGN_P (sp_adjust))
+ operands[1] = GEN_INT (sp_adjust);
+ else
+ operands[1] = GEN_INT (0);
+
+ /* Create assembly code pattern. */
+ snprintf (pattern, sizeof (pattern), "push25\t%%0, %%1");
+ }
+ else
+ {
+ /* For normal stack push multiple:
+ operands[0]: Rb
+ operands[1]: Re
+ operands[2]: En4 */
+
+ /* This variable is used to check if we only need to generate En4 field.
+ As long as Rb==Re=SP_REGNUM, we set this variable to 1. */
+ int push_en4_only_p = 0;
+
+ /* Set operands[0] and operands[1]. */
+ operands[0] = gen_rtx_REG (SImode, rb_regno);
+ operands[1] = gen_rtx_REG (SImode, re_regno);
+
+ /* 'smw.adm $sp,[$sp],$sp,0' means push nothing. */
+ if (!cfun->machine->fp_size
+ && !cfun->machine->gp_size
+ && !cfun->machine->lp_size
+ && REGNO (operands[0]) == SP_REGNUM
+ && REGNO (operands[1]) == SP_REGNUM)
+ {
+ /* No need to generate instruction. */
+ return "";
+ }
+ else
+ {
+ /* If Rb==Re=SP_REGNUM, we only need to generate En4 field. */
+ if (REGNO (operands[0]) == SP_REGNUM
+ && REGNO (operands[1]) == SP_REGNUM)
+ push_en4_only_p = 1;
+
+ /* Create assembly code pattern.
+ We need to handle the form: "Rb, Re, { $fp $gp $lp }". */
+ snprintf (pattern, sizeof (pattern),
+ "push.s\t%s{%s%s%s }",
+ push_en4_only_p ? "" : "%0, %1, ",
+ cfun->machine->fp_size ? " $fp" : "",
+ cfun->machine->gp_size ? " $gp" : "",
+ cfun->machine->lp_size ? " $lp" : "");
+ }
+ }
+
+ /* We use output_asm_insn() to output assembly code by ourself. */
+ output_asm_insn (pattern, operands);
+ return "";
+}
+
+/* Function to output stack pop operation.
+ We need to deal with normal stack pop multiple or stack v3pop. */
+const char *
+nds32_output_stack_pop (void)
+{
+ /* A string pattern for output_asm_insn(). */
+ char pattern[100];
+ /* The operands array which will be used in output_asm_insn(). */
+ rtx operands[3];
+ /* Pick up callee-saved first regno and last regno for further use. */
+ int rb_regno = cfun->machine->callee_saved_regs_first_regno;
+ int re_regno = cfun->machine->callee_saved_regs_last_regno;
+
+ if (TARGET_V3PUSH)
+ {
+ /* For stack v3pop:
+ operands[0]: Re
+ operands[1]: imm8u */
+
+ /* This variable is to check if 'pop25 Re,imm8u' is available. */
+ int sp_adjust;
+
+ /* Set operands[0]. */
+ operands[0] = gen_rtx_REG (SImode, re_regno);
+
+ /* Check if we can generate 'pop25 Re,imm8u',
+ otherwise, generate 'pop25 Re,0'.
+ We have to consider alloca issue as well.
+ If the function does call alloca(), the stack pointer is not fixed.
+ In that case, we cannot use 'pop25 Re,imm8u' directly.
+ We have to caculate stack pointer from frame pointer
+ and then use 'pop25 Re,0'. */
+ sp_adjust = cfun->machine->local_size
+ + cfun->machine->out_args_size
+ + cfun->machine->callee_saved_area_padding_bytes;
+ if (satisfies_constraint_Iu08 (GEN_INT (sp_adjust))
+ && NDS32_DOUBLE_WORD_ALIGN_P (sp_adjust)
+ && !cfun->calls_alloca)
+ operands[1] = GEN_INT (sp_adjust);
+ else
+ operands[1] = GEN_INT (0);
+
+ /* Create assembly code pattern. */
+ snprintf (pattern, sizeof (pattern), "pop25\t%%0, %%1");
+ }
+ else
+ {
+ /* For normal stack pop multiple:
+ operands[0]: Rb
+ operands[1]: Re
+ operands[2]: En4 */
+
+ /* This variable is used to check if we only need to generate En4 field.
+ As long as Rb==Re=SP_REGNUM, we set this variable to 1. */
+ int pop_en4_only_p = 0;
+
+ /* Set operands[0] and operands[1]. */
+ operands[0] = gen_rtx_REG (SImode, rb_regno);
+ operands[1] = gen_rtx_REG (SImode, re_regno);
+
+ /* 'lmw.bim $sp,[$sp],$sp,0' means pop nothing. */
+ if (!cfun->machine->fp_size
+ && !cfun->machine->gp_size
+ && !cfun->machine->lp_size
+ && REGNO (operands[0]) == SP_REGNUM
+ && REGNO (operands[1]) == SP_REGNUM)
+ {
+ /* No need to generate instruction. */
+ return "";
+ }
+ else
+ {
+ /* If Rb==Re=SP_REGNUM, we only need to generate En4 field. */
+ if (REGNO (operands[0]) == SP_REGNUM
+ && REGNO (operands[1]) == SP_REGNUM)
+ pop_en4_only_p = 1;
+
+ /* Create assembly code pattern.
+ We need to handle the form: "Rb, Re, { $fp $gp $lp }". */
+ snprintf (pattern, sizeof (pattern),
+ "pop.s\t%s{%s%s%s }",
+ pop_en4_only_p ? "" : "%0, %1, ",
+ cfun->machine->fp_size ? " $fp" : "",
+ cfun->machine->gp_size ? " $gp" : "",
+ cfun->machine->lp_size ? " $lp" : "");
+ }
+ }
+
+ /* We use output_asm_insn() to output assembly code by ourself. */
+ output_asm_insn (pattern, operands);
+ return "";
+}
+
+/* Function to generate PC relative jump table.
+ Refer to nds32.md for more details.
+
+ The following is the sample for the case that diff value
+ can be presented in '.short' size.
+
+ addi $r1, $r1, -(case_lower_bound)
+ slti $ta, $r1, (case_number)
+ beqz $ta, .L_skip_label
+
+ la $ta, .L35 ! get jump table address
+ lh $r1, [$ta + $r1 << 1] ! load symbol diff from jump table entry
+ addi $ta, $r1, $ta
+ jr5 $ta
+
+ ! jump table entry
+ L35:
+ .short .L25-.L35
+ .short .L26-.L35
+ .short .L27-.L35
+ .short .L28-.L35
+ .short .L29-.L35
+ .short .L30-.L35
+ .short .L31-.L35
+ .short .L32-.L35
+ .short .L33-.L35
+ .short .L34-.L35 */
+const char *
+nds32_output_casesi_pc_relative (rtx *operands)
+{
+ enum machine_mode mode;
+ rtx diff_vec;
+
+ diff_vec = PATTERN (NEXT_INSN (operands[1]));
+
+ gcc_assert (GET_CODE (diff_vec) == ADDR_DIFF_VEC);
+
+ /* Step C: "t <-- operands[1]". */
+ output_asm_insn ("la\t$ta, %l1", operands);
+
+ /* Get the mode of each element in the difference vector. */
+ mode = GET_MODE (diff_vec);
+
+ /* Step D: "z <-- (mem (plus (operands[0] << m) t))",
+ where m is 0, 1, or 2 to load address-diff value from table. */
+ switch (mode)
+ {
+ case QImode:
+ output_asm_insn ("lb\t%2, [$ta + %0 << 0]", operands);
+ break;
+ case HImode:
+ output_asm_insn ("lh\t%2, [$ta + %0 << 1]", operands);
+ break;
+ case SImode:
+ output_asm_insn ("lw\t%2, [$ta + %0 << 2]", operands);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+
+ /* Step E: "t <-- z + t".
+ Add table label_ref with address-diff value to
+ obtain target case address. */
+ output_asm_insn ("add\t$ta, %2, $ta", operands);
+
+ /* Step F: jump to target with register t. */
+ if (TARGET_16_BIT)
+ return "jr5\t$ta";
+ else
+ return "jr\t$ta";
+}
+
+/* Function to generate normal jump table. */
+const char *
+nds32_output_casesi (rtx *operands)
+{
+ /* Step C: "t <-- operands[1]". */
+ output_asm_insn ("la\t$ta, %l1", operands);
+
+ /* Step D: "z <-- (mem (plus (operands[0] << 2) t))". */
+ output_asm_insn ("lw\t%2, [$ta + %0 << 2]", operands);
+
+ /* No need to perform Step E, which is only used for
+ pc relative jump table. */
+
+ /* Step F: jump to target with register z. */
+ if (TARGET_16_BIT)
+ return "jr5\t%2";
+ else
+ return "jr\t%2";
+}
+
+/* ------------------------------------------------------------------------ */
}
}
-/* A helper function to return character based on byte size. */
-static char
-nds32_byte_to_size (int byte)
-{
- switch (byte)
- {
- case 4:
- return 'w';
- case 2:
- return 'h';
- case 1:
- return 'b';
- default:
- /* Normally it should not be here. */
- gcc_unreachable ();
- }
-}
-
/* A helper function to check if this function should contain prologue. */
static int
nds32_have_prologue_p (void)
return 0;
}
-
-/* Function to generate PC relative jump table.
- Refer to nds32.md for more details.
-
- The following is the sample for the case that diff value
- can be presented in '.short' size.
-
- addi $r1, $r1, -(case_lower_bound)
- slti $ta, $r1, (case_number)
- beqz $ta, .L_skip_label
-
- la $ta, .L35 ! get jump table address
- lh $r1, [$ta + $r1 << 1] ! load symbol diff from jump table entry
- addi $ta, $r1, $ta
- jr5 $ta
-
- ! jump table entry
- L35:
- .short .L25-.L35
- .short .L26-.L35
- .short .L27-.L35
- .short .L28-.L35
- .short .L29-.L35
- .short .L30-.L35
- .short .L31-.L35
- .short .L32-.L35
- .short .L33-.L35
- .short .L34-.L35 */
-const char *
-nds32_output_casesi_pc_relative (rtx *operands)
-{
- enum machine_mode mode;
- rtx diff_vec;
-
- diff_vec = PATTERN (NEXT_INSN (operands[1]));
-
- gcc_assert (GET_CODE (diff_vec) == ADDR_DIFF_VEC);
-
- /* Step C: "t <-- operands[1]". */
- output_asm_insn ("la\t$ta, %l1", operands);
-
- /* Get the mode of each element in the difference vector. */
- mode = GET_MODE (diff_vec);
-
- /* Step D: "z <-- (mem (plus (operands[0] << m) t))",
- where m is 0, 1, or 2 to load address-diff value from table. */
- switch (mode)
- {
- case QImode:
- output_asm_insn ("lb\t%2, [$ta + %0 << 0]", operands);
- break;
- case HImode:
- output_asm_insn ("lh\t%2, [$ta + %0 << 1]", operands);
- break;
- case SImode:
- output_asm_insn ("lw\t%2, [$ta + %0 << 2]", operands);
- break;
- default:
- gcc_unreachable ();
- }
-
- /* Step E: "t <-- z + t".
- Add table label_ref with address-diff value to
- obtain target case address. */
- output_asm_insn ("add\t$ta, %2, $ta", operands);
-
- /* Step F: jump to target with register t. */
- if (TARGET_16_BIT)
- return "jr5\t$ta";
- else
- return "jr\t$ta";
-}
-
-/* Function to generate normal jump table. */
-const char *
-nds32_output_casesi (rtx *operands)
-{
- /* Step C: "t <-- operands[1]". */
- output_asm_insn ("la\t$ta, %l1", operands);
-
- /* Step D: "z <-- (mem (plus (operands[0] << 2) t))". */
- output_asm_insn ("lw\t%2, [$ta + %0 << 2]", operands);
-
- /* No need to perform Step E, which is only used for
- pc relative jump table. */
-
- /* Step F: jump to target with register z. */
- if (TARGET_16_BIT)
- return "jr5\t%2";
- else
- return "jr\t%2";
-}
-
-
-/* Function to return memory format. */
-enum nds32_16bit_address_type
-nds32_mem_format (rtx op)
-{
- enum machine_mode mode_test;
- int val;
- int regno;
-
- if (!TARGET_16_BIT)
- return ADDRESS_NOT_16BIT_FORMAT;
-
- mode_test = GET_MODE (op);
-
- op = XEXP (op, 0);
-
- /* 45 format. */
- if (GET_CODE (op) == REG && (mode_test == SImode))
- return ADDRESS_REG;
-
- /* 333 format for QI/HImode. */
- if (GET_CODE (op) == REG && (REGNO (op) < R8_REGNUM))
- return ADDRESS_LO_REG_IMM3U;
-
- /* post_inc 333 format. */
- if ((GET_CODE (op) == POST_INC) && (mode_test == SImode))
- {
- regno = REGNO(XEXP (op, 0));
-
- if (regno < 8)
- return ADDRESS_POST_INC_LO_REG_IMM3U;
- }
-
- /* post_inc 333 format. */
- if ((GET_CODE (op) == POST_MODIFY)
- && (mode_test == SImode)
- && (REG_P (XEXP (XEXP (op, 1), 0)))
- && (CONST_INT_P (XEXP (XEXP (op, 1), 1))))
- {
- regno = REGNO (XEXP (XEXP (op, 1), 0));
- val = INTVAL (XEXP (XEXP (op, 1), 1));
- if (regno < 8 && val < 32)
- return ADDRESS_POST_INC_LO_REG_IMM3U;
- }
-
- if ((GET_CODE (op) == PLUS)
- && (GET_CODE (XEXP (op, 0)) == REG)
- && (GET_CODE (XEXP (op, 1)) == CONST_INT))
- {
- val = INTVAL (XEXP (op, 1));
-
- regno = REGNO(XEXP (op, 0));
-
- if (regno > 7
- && regno != SP_REGNUM
- && regno != FP_REGNUM)
- return ADDRESS_NOT_16BIT_FORMAT;
-
- switch (mode_test)
- {
- case QImode:
- /* 333 format. */
- if (val >= 0 && val < 8 && regno < 8)
- return ADDRESS_LO_REG_IMM3U;
- break;
-
- case HImode:
- /* 333 format. */
- if (val >= 0 && val < 16 && (val % 2 == 0) && regno < 8)
- return ADDRESS_LO_REG_IMM3U;
- break;
-
- case SImode:
- case SFmode:
- case DFmode:
- /* fp imply 37 format. */
- if ((regno == FP_REGNUM) &&
- (val >= 0 && val < 512 && (val % 4 == 0)))
- return ADDRESS_FP_IMM7U;
- /* sp imply 37 format. */
- else if ((regno == SP_REGNUM) &&
- (val >= 0 && val < 512 && (val % 4 == 0)))
- return ADDRESS_SP_IMM7U;
- /* 333 format. */
- else if (val >= 0 && val < 32 && (val % 4 == 0) && regno < 8)
- return ADDRESS_LO_REG_IMM3U;
- break;
-
- default:
- break;
- }
- }
-
- return ADDRESS_NOT_16BIT_FORMAT;
-}
-
-/* Output 16-bit store. */
-const char *
-nds32_output_16bit_store (rtx *operands, int byte)
-{
- char pattern[100];
- char size;
- rtx code = XEXP (operands[0], 0);
-
- size = nds32_byte_to_size (byte);
-
- switch (nds32_mem_format (operands[0]))
- {
- case ADDRESS_REG:
- operands[0] = code;
- output_asm_insn ("swi450\t%1, [%0]", operands);
- break;
- case ADDRESS_LO_REG_IMM3U:
- snprintf (pattern, sizeof (pattern), "s%ci333\t%%1, %%0", size);
- output_asm_insn (pattern, operands);
- break;
- case ADDRESS_POST_INC_LO_REG_IMM3U:
- snprintf (pattern, sizeof (pattern), "s%ci333.bi\t%%1, %%0", size);
- output_asm_insn (pattern, operands);
- break;
- case ADDRESS_FP_IMM7U:
- output_asm_insn ("swi37\t%1, %0", operands);
- break;
- case ADDRESS_SP_IMM7U:
- /* Get immediate value and set back to operands[1]. */
- operands[0] = XEXP (code, 1);
- output_asm_insn ("swi37.sp\t%1, [ + (%0)]", operands);
- break;
- default:
- break;
- }
-
- return "";
-}
-
-/* Output 16-bit load. */
-const char *
-nds32_output_16bit_load (rtx *operands, int byte)
-{
- char pattern[100];
- unsigned char size;
- rtx code = XEXP (operands[1], 0);
-
- size = nds32_byte_to_size (byte);
-
- switch (nds32_mem_format (operands[1]))
- {
- case ADDRESS_REG:
- operands[1] = code;
- output_asm_insn ("lwi450\t%0, [%1]", operands);
- break;
- case ADDRESS_LO_REG_IMM3U:
- snprintf (pattern, sizeof (pattern), "l%ci333\t%%0, %%1", size);
- output_asm_insn (pattern, operands);
- break;
- case ADDRESS_POST_INC_LO_REG_IMM3U:
- snprintf (pattern, sizeof (pattern), "l%ci333.bi\t%%0, %%1", size);
- output_asm_insn (pattern, operands);
- break;
- case ADDRESS_FP_IMM7U:
- output_asm_insn ("lwi37\t%0, %1", operands);
- break;
- case ADDRESS_SP_IMM7U:
- /* Get immediate value and set back to operands[0]. */
- operands[1] = XEXP (code, 1);
- output_asm_insn ("lwi37.sp\t%0, [ + (%1)]", operands);
- break;
- default:
- break;
- }
-
- return "";
-}
-
-/* Output 32-bit store. */
-const char *
-nds32_output_32bit_store (rtx *operands, int byte)
-{
- char pattern[100];
- unsigned char size;
- rtx code = XEXP (operands[0], 0);
-
- size = nds32_byte_to_size (byte);
-
- switch (GET_CODE (code))
- {
- case REG:
- /* (mem (reg X))
- => access location by using register,
- use "sbi / shi / swi" */
- snprintf (pattern, sizeof (pattern), "s%ci\t%%1, %%0", size);
- break;
-
- case SYMBOL_REF:
- case CONST:
- /* (mem (symbol_ref X))
- (mem (const (...)))
- => access global variables,
- use "sbi.gp / shi.gp / swi.gp" */
- operands[0] = XEXP (operands[0], 0);
- snprintf (pattern, sizeof (pattern), "s%ci.gp\t%%1, [ + %%0]", size);
- break;
-
- case POST_INC:
- /* (mem (post_inc reg))
- => access location by using register which will be post increment,
- use "sbi.bi / shi.bi / swi.bi" */
- snprintf (pattern, sizeof (pattern),
- "s%ci.bi\t%%1, %%0, %d", size, byte);
- break;
-
- case POST_DEC:
- /* (mem (post_dec reg))
- => access location by using register which will be post decrement,
- use "sbi.bi / shi.bi / swi.bi" */
- snprintf (pattern, sizeof (pattern),
- "s%ci.bi\t%%1, %%0, -%d", size, byte);
- break;
-
- case POST_MODIFY:
- switch (GET_CODE (XEXP (XEXP (code, 1), 1)))
- {
- case REG:
- case SUBREG:
- /* (mem (post_modify (reg) (plus (reg) (reg))))
- => access location by using register which will be
- post modified with reg,
- use "sb.bi/ sh.bi / sw.bi" */
- snprintf (pattern, sizeof (pattern), "s%c.bi\t%%1, %%0", size);
- break;
- case CONST_INT:
- /* (mem (post_modify (reg) (plus (reg) (const_int))))
- => access location by using register which will be
- post modified with const_int,
- use "sbi.bi/ shi.bi / swi.bi" */
- snprintf (pattern, sizeof (pattern), "s%ci.bi\t%%1, %%0", size);
- break;
- default:
- abort ();
- }
- break;
-
- case PLUS:
- switch (GET_CODE (XEXP (code, 1)))
- {
- case REG:
- case SUBREG:
- /* (mem (plus reg reg)) or (mem (plus (mult reg const_int) reg))
- => access location by adding two registers,
- use "sb / sh / sw" */
- snprintf (pattern, sizeof (pattern), "s%c\t%%1, %%0", size);
- break;
- case CONST_INT:
- /* (mem (plus reg const_int))
- => access location by adding one register with const_int,
- use "sbi / shi / swi" */
- snprintf (pattern, sizeof (pattern), "s%ci\t%%1, %%0", size);
- break;
- default:
- abort ();
- }
- break;
-
- case LO_SUM:
- operands[2] = XEXP (code, 1);
- operands[0] = XEXP (code, 0);
- snprintf (pattern, sizeof (pattern),
- "s%ci\t%%1, [%%0 + lo12(%%2)]", size);
- break;
-
- default:
- abort ();
- }
-
- output_asm_insn (pattern, operands);
- return "";
-}
-
-/* Output 32-bit load. */
-const char *
-nds32_output_32bit_load (rtx *operands, int byte)
-{
- char pattern[100];
- unsigned char size;
- rtx code;
-
- code = XEXP (operands[1], 0);
-
- size = nds32_byte_to_size (byte);
-
- switch (GET_CODE (code))
- {
- case REG:
- /* (mem (reg X))
- => access location by using register,
- use "lbi / lhi / lwi" */
- snprintf (pattern, sizeof (pattern), "l%ci\t%%0, %%1", size);
- break;
-
- case SYMBOL_REF:
- case CONST:
- /* (mem (symbol_ref X))
- (mem (const (...)))
- => access global variables,
- use "lbi.gp / lhi.gp / lwi.gp" */
- operands[1] = XEXP (operands[1], 0);
- snprintf (pattern, sizeof (pattern), "l%ci.gp\t%%0, [ + %%1]", size);
- break;
-
- case POST_INC:
- /* (mem (post_inc reg))
- => access location by using register which will be post increment,
- use "lbi.bi / lhi.bi / lwi.bi" */
- snprintf (pattern, sizeof (pattern),
- "l%ci.bi\t%%0, %%1, %d", size, byte);
- break;
-
- case POST_DEC:
- /* (mem (post_dec reg))
- => access location by using register which will be post decrement,
- use "lbi.bi / lhi.bi / lwi.bi" */
- snprintf (pattern, sizeof (pattern),
- "l%ci.bi\t%%0, %%1, -%d", size, byte);
- break;
-
- case POST_MODIFY:
- switch (GET_CODE (XEXP (XEXP (code, 1), 1)))
- {
- case REG:
- case SUBREG:
- /* (mem (post_modify (reg) (plus (reg) (reg))))
- => access location by using register which will be
- post modified with reg,
- use "lb.bi/ lh.bi / lw.bi" */
- snprintf (pattern, sizeof (pattern), "l%c.bi\t%%0, %%1", size);
- break;
- case CONST_INT:
- /* (mem (post_modify (reg) (plus (reg) (const_int))))
- => access location by using register which will be
- post modified with const_int,
- use "lbi.bi/ lhi.bi / lwi.bi" */
- snprintf (pattern, sizeof (pattern), "l%ci.bi\t%%0, %%1", size);
- break;
- default:
- abort ();
- }
- break;
-
- case PLUS:
- switch (GET_CODE (XEXP (code, 1)))
- {
- case REG:
- case SUBREG:
- /* (mem (plus reg reg)) or (mem (plus (mult reg const_int) reg))
- use "lb / lh / lw" */
- snprintf (pattern, sizeof (pattern), "l%c\t%%0, %%1", size);
- break;
- case CONST_INT:
- /* (mem (plus reg const_int))
- => access location by adding one register with const_int,
- use "lbi / lhi / lwi" */
- snprintf (pattern, sizeof (pattern), "l%ci\t%%0, %%1", size);
- break;
- default:
- abort ();
- }
- break;
-
- case LO_SUM:
- operands[2] = XEXP (code, 1);
- operands[1] = XEXP (code, 0);
- snprintf (pattern, sizeof (pattern),
- "l%ci\t%%0, [%%1 + lo12(%%2)]", size);
- break;
-
- default:
- abort ();
- }
-
- output_asm_insn (pattern, operands);
- return "";
-}
-
-/* Output 32-bit load with signed extension. */
-const char *
-nds32_output_32bit_load_s (rtx *operands, int byte)
-{
- char pattern[100];
- unsigned char size;
- rtx code;
-
- code = XEXP (operands[1], 0);
-
- size = nds32_byte_to_size (byte);
-
- switch (GET_CODE (code))
- {
- case REG:
- /* (mem (reg X))
- => access location by using register,
- use "lbsi / lhsi" */
- snprintf (pattern, sizeof (pattern), "l%csi\t%%0, %%1", size);
- break;
-
- case SYMBOL_REF:
- case CONST:
- /* (mem (symbol_ref X))
- (mem (const (...)))
- => access global variables,
- use "lbsi.gp / lhsi.gp" */
- operands[1] = XEXP (operands[1], 0);
- snprintf (pattern, sizeof (pattern), "l%csi.gp\t%%0, [ + %%1]", size);
- break;
-
- case POST_INC:
- /* (mem (post_inc reg))
- => access location by using register which will be post increment,
- use "lbsi.bi / lhsi.bi" */
- snprintf (pattern, sizeof (pattern),
- "l%csi.bi\t%%0, %%1, %d", size, byte);
- break;
-
- case POST_DEC:
- /* (mem (post_dec reg))
- => access location by using register which will be post decrement,
- use "lbsi.bi / lhsi.bi" */
- snprintf (pattern, sizeof (pattern),
- "l%csi.bi\t%%0, %%1, -%d", size, byte);
- break;
-
- case POST_MODIFY:
- switch (GET_CODE (XEXP (XEXP (code, 1), 1)))
- {
- case REG:
- case SUBREG:
- /* (mem (post_modify (reg) (plus (reg) (reg))))
- => access location by using register which will be
- post modified with reg,
- use "lbs.bi/ lhs.bi" */
- snprintf (pattern, sizeof (pattern), "l%cs.bi\t%%0, %%1", size);
- break;
- case CONST_INT:
- /* (mem (post_modify (reg) (plus (reg) (const_int))))
- => access location by using register which will be
- post modified with const_int,
- use "lbsi.bi/ lhsi.bi" */
- snprintf (pattern, sizeof (pattern), "l%csi.bi\t%%0, %%1", size);
- break;
- default:
- abort ();
- }
- break;
-
- case PLUS:
- switch (GET_CODE (XEXP (code, 1)))
- {
- case REG:
- case SUBREG:
- /* (mem (plus reg reg)) or (mem (plus (mult reg const_int) reg))
- use "lbs / lhs" */
- snprintf (pattern, sizeof (pattern), "l%cs\t%%0, %%1", size);
- break;
- case CONST_INT:
- /* (mem (plus reg const_int))
- => access location by adding one register with const_int,
- use "lbsi / lhsi" */
- snprintf (pattern, sizeof (pattern), "l%csi\t%%0, %%1", size);
- break;
- default:
- abort ();
- }
- break;
-
- case LO_SUM:
- operands[2] = XEXP (code, 1);
- operands[1] = XEXP (code, 0);
- snprintf (pattern, sizeof (pattern),
- "l%csi\t%%0, [%%1 + lo12(%%2)]", size);
- break;
-
- default:
- abort ();
- }
-
- output_asm_insn (pattern, operands);
- return "";
-}
-
-/* Function to output stack push operation.
- We need to deal with normal stack push multiple or stack v3push. */
-const char *
-nds32_output_stack_push (void)
-{
- /* A string pattern for output_asm_insn(). */
- char pattern[100];
- /* The operands array which will be used in output_asm_insn(). */
- rtx operands[3];
- /* Pick up callee-saved first regno and last regno for further use. */
- int rb_regno = cfun->machine->callee_saved_regs_first_regno;
- int re_regno = cfun->machine->callee_saved_regs_last_regno;
-
- if (TARGET_V3PUSH)
- {
- /* For stack v3push:
- operands[0]: Re
- operands[1]: imm8u */
-
- /* This variable is to check if 'push25 Re,imm8u' is available. */
- int sp_adjust;
-
- /* Set operands[0]. */
- operands[0] = gen_rtx_REG (SImode, re_regno);
-
- /* Check if we can generate 'push25 Re,imm8u',
- otherwise, generate 'push25 Re,0'. */
- sp_adjust = cfun->machine->local_size
- + cfun->machine->out_args_size
- + cfun->machine->callee_saved_area_padding_bytes;
- if (satisfies_constraint_Iu08 (GEN_INT (sp_adjust))
- && NDS32_DOUBLE_WORD_ALIGN_P (sp_adjust))
- operands[1] = GEN_INT (sp_adjust);
- else
- operands[1] = GEN_INT (0);
-
- /* Create assembly code pattern. */
- snprintf (pattern, sizeof (pattern), "push25\t%%0, %%1");
- }
- else
- {
- /* For normal stack push multiple:
- operands[0]: Rb
- operands[1]: Re
- operands[2]: En4 */
-
- /* This variable is used to check if we only need to generate En4 field.
- As long as Rb==Re=SP_REGNUM, we set this variable to 1. */
- int push_en4_only_p = 0;
-
- /* Set operands[0] and operands[1]. */
- operands[0] = gen_rtx_REG (SImode, rb_regno);
- operands[1] = gen_rtx_REG (SImode, re_regno);
-
- /* 'smw.adm $sp,[$sp],$sp,0' means push nothing. */
- if (!cfun->machine->fp_size
- && !cfun->machine->gp_size
- && !cfun->machine->lp_size
- && REGNO (operands[0]) == SP_REGNUM
- && REGNO (operands[1]) == SP_REGNUM)
- {
- /* No need to generate instruction. */
- return "";
- }
- else
- {
- /* If Rb==Re=SP_REGNUM, we only need to generate En4 field. */
- if (REGNO (operands[0]) == SP_REGNUM
- && REGNO (operands[1]) == SP_REGNUM)
- push_en4_only_p = 1;
-
- /* Create assembly code pattern.
- We need to handle the form: "Rb, Re, { $fp $gp $lp }". */
- snprintf (pattern, sizeof (pattern),
- "push.s\t%s{%s%s%s }",
- push_en4_only_p ? "" : "%0, %1, ",
- cfun->machine->fp_size ? " $fp" : "",
- cfun->machine->gp_size ? " $gp" : "",
- cfun->machine->lp_size ? " $lp" : "");
- }
- }
-
- /* We use output_asm_insn() to output assembly code by ourself. */
- output_asm_insn (pattern, operands);
- return "";
-}
-
-/* Function to output stack pop operation.
- We need to deal with normal stack pop multiple or stack v3pop. */
-const char *
-nds32_output_stack_pop (void)
-{
- /* A string pattern for output_asm_insn(). */
- char pattern[100];
- /* The operands array which will be used in output_asm_insn(). */
- rtx operands[3];
- /* Pick up callee-saved first regno and last regno for further use. */
- int rb_regno = cfun->machine->callee_saved_regs_first_regno;
- int re_regno = cfun->machine->callee_saved_regs_last_regno;
-
- if (TARGET_V3PUSH)
- {
- /* For stack v3pop:
- operands[0]: Re
- operands[1]: imm8u */
-
- /* This variable is to check if 'pop25 Re,imm8u' is available. */
- int sp_adjust;
-
- /* Set operands[0]. */
- operands[0] = gen_rtx_REG (SImode, re_regno);
-
- /* Check if we can generate 'pop25 Re,imm8u',
- otherwise, generate 'pop25 Re,0'.
- We have to consider alloca issue as well.
- If the function does call alloca(), the stack pointer is not fixed.
- In that case, we cannot use 'pop25 Re,imm8u' directly.
- We have to caculate stack pointer from frame pointer
- and then use 'pop25 Re,0'. */
- sp_adjust = cfun->machine->local_size
- + cfun->machine->out_args_size
- + cfun->machine->callee_saved_area_padding_bytes;
- if (satisfies_constraint_Iu08 (GEN_INT (sp_adjust))
- && NDS32_DOUBLE_WORD_ALIGN_P (sp_adjust)
- && !cfun->calls_alloca)
- operands[1] = GEN_INT (sp_adjust);
- else
- operands[1] = GEN_INT (0);
-
- /* Create assembly code pattern. */
- snprintf (pattern, sizeof (pattern), "pop25\t%%0, %%1");
- }
- else
- {
- /* For normal stack pop multiple:
- operands[0]: Rb
- operands[1]: Re
- operands[2]: En4 */
-
- /* This variable is used to check if we only need to generate En4 field.
- As long as Rb==Re=SP_REGNUM, we set this variable to 1. */
- int pop_en4_only_p = 0;
-
- /* Set operands[0] and operands[1]. */
- operands[0] = gen_rtx_REG (SImode, rb_regno);
- operands[1] = gen_rtx_REG (SImode, re_regno);
-
- /* 'lmw.bim $sp,[$sp],$sp,0' means pop nothing. */
- if (!cfun->machine->fp_size
- && !cfun->machine->gp_size
- && !cfun->machine->lp_size
- && REGNO (operands[0]) == SP_REGNUM
- && REGNO (operands[1]) == SP_REGNUM)
- {
- /* No need to generate instruction. */
- return "";
- }
- else
- {
- /* If Rb==Re=SP_REGNUM, we only need to generate En4 field. */
- if (REGNO (operands[0]) == SP_REGNUM
- && REGNO (operands[1]) == SP_REGNUM)
- pop_en4_only_p = 1;
-
- /* Create assembly code pattern.
- We need to handle the form: "Rb, Re, { $fp $gp $lp }". */
- snprintf (pattern, sizeof (pattern),
- "pop.s\t%s{%s%s%s }",
- pop_en4_only_p ? "" : "%0, %1, ",
- cfun->machine->fp_size ? " $fp" : "",
- cfun->machine->gp_size ? " $gp" : "",
- cfun->machine->lp_size ? " $lp" : "");
- }
- }
-
- /* We use output_asm_insn() to output assembly code by ourself. */
- output_asm_insn (pattern, operands);
- return "";
-}
-
/* Return align 2 (log base 2) if the next instruction of LABEL is 4 byte. */
int
nds32_target_alignment (rtx label)