}
}
+/* Print a set of registers in the format required by "movm" and "ret".
+ Register K is saved if bit K of MASK is set. The data and address
+ registers can be stored individually, but the extended registers cannot.
+ We assume that the mask alread takes that into account. For instance,
+ bits 14 to 17 must have the same value. */
+
+void
+mn10300_print_reg_list (file, mask)
+ FILE *file;
+ int mask;
+{
+ int need_comma;
+ int i;
+
+ need_comma = 0;
+ fputc ('[', file);
+
+ for (i = 0; i < FIRST_EXTENDED_REGNUM; i++)
+ if ((mask & (1 << i)) != 0)
+ {
+ if (need_comma)
+ fputc (',', file);
+ fputs (reg_names [i], file);
+ need_comma = 1;
+ }
+
+ if ((mask & 0x3c000) != 0)
+ {
+ if ((mask & 0x3c000) != 0x3c000)
+ abort();
+ if (need_comma)
+ fputc (',', file);
+ fputs ("exreg1", file);
+ need_comma = 1;
+ }
+
+ fputc (']', file);
+}
+
int
can_use_return_insn ()
{
&& !frame_pointer_needed);
}
+/* Returns the set of live, callee-saved registers as a bitmask. The
+ callee-saved extended registers cannot be stored individually, so
+ all of them will be included in the mask if any one of them is used. */
+
+int
+mn10300_get_live_callee_saved_regs ()
+{
+ int mask;
+ int i;
+
+ mask = 0;
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (regs_ever_live[i] && ! call_used_regs[i])
+ mask |= (1 << i);
+ if ((mask & 0x3c000) != 0)
+ mask |= 0x3c000;
+
+ return mask;
+}
+
+/* Generate an instruction that pushes several registers onto the stack.
+ Register K will be saved if bit K in MASK is set. The function does
+ nothing if MASK is zero.
+
+ To be compatible with the "movm" instruction, the lowest-numbered
+ register must be stored in the lowest slot. If MASK is the set
+ { R1,...,RN }, where R1...RN are ordered least first, the generated
+ instruction will have the form:
+
+ (parallel
+ (set (reg:SI 9) (plus:SI (reg:SI 9) (const_int -N*4)))
+ (set (mem:SI (plus:SI (reg:SI 9)
+ (const_int -1*4)))
+ (reg:SI RN))
+ ...
+ (set (mem:SI (plus:SI (reg:SI 9)
+ (const_int -N*4)))
+ (reg:SI R1))) */
+
+void
+mn10300_gen_multiple_store (mask)
+ int mask;
+{
+ if (mask != 0)
+ {
+ int i;
+ int count;
+ rtx par;
+ int pari;
+
+ /* Count how many registers need to be saved. */
+ count = 0;
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if ((mask & (1 << i)) != 0)
+ count += 1;
+
+ /* We need one PARALLEL element to update the stack pointer and
+ an additional element for each register that is stored. */
+ par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count + 1));
+
+ /* Create the instruction that updates the stack pointer. */
+ XVECEXP (par, 0, 0)
+ = gen_rtx_SET (SImode,
+ stack_pointer_rtx,
+ gen_rtx_PLUS (SImode,
+ stack_pointer_rtx,
+ GEN_INT (-count * 4)));
+
+ /* Create each store. */
+ pari = 1;
+ for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--)
+ if ((mask & (1 << i)) != 0)
+ {
+ rtx address = gen_rtx_PLUS (SImode,
+ stack_pointer_rtx,
+ GEN_INT (-pari * 4));
+ XVECEXP(par, 0, pari)
+ = gen_rtx_SET (VOIDmode,
+ gen_rtx_MEM (SImode, address),
+ gen_rtx_REG (SImode, i));
+ pari += 1;
+ }
+
+ par = emit_insn (par);
+ RTX_FRAME_RELATED_P (par) = 1;
+ }
+}
+
void
expand_prologue ()
{
gen_rtx_REG (SImode, 1));
}
- /* And now store all the registers onto the stack with a
- single two byte instruction. */
- if (regs_ever_live[2] || regs_ever_live[3]
- || regs_ever_live[6] || regs_ever_live[7]
- || regs_ever_live[14] || regs_ever_live[15]
- || regs_ever_live[16] || regs_ever_live[17]
- || frame_pointer_needed)
- emit_insn (gen_store_movm ());
+ /* If we use any of the callee-saved registers, save them now. */
+ mn10300_gen_multiple_store (mn10300_get_live_callee_saved_regs ());
/* Now put the frame pointer into the frame pointer register. */
if (frame_pointer_needed)
}
}
+/* Recognise the PARALLEL rtx generated by mn10300_gen_multiple_store().
+ This function is for MATCH_PARALLEL and so assumes OP is known to be
+ parallel. If OP is a multiple store, return a mask indicating which
+ registers it saves. Return 0 otherwise. */
+
+int
+store_multiple_operation (op, mode)
+ rtx op;
+ enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+ int count;
+ int mask;
+ int i;
+ unsigned int last;
+ rtx elt;
+
+ count = XVECLEN (op, 0);
+ if (count < 2)
+ return 0;
+
+ /* Check that first instruction has the form (set (sp) (plus A B)) */
+ elt = XVECEXP (op, 0, 0);
+ if (GET_CODE (elt) != SET
+ || GET_CODE (SET_DEST (elt)) != REG
+ || REGNO (SET_DEST (elt)) != STACK_POINTER_REGNUM
+ || GET_CODE (SET_SRC (elt)) != PLUS)
+ return 0;
+
+ /* Check that A is the stack pointer and B is the expected stack size.
+ For OP to match, each subsequent instruction should push a word onto
+ the stack. We therefore expect the first instruction to create
+ COUNT-1 stack slots. */
+ elt = SET_SRC (elt);
+ if (GET_CODE (XEXP (elt, 0)) != REG
+ || REGNO (XEXP (elt, 0)) != STACK_POINTER_REGNUM
+ || GET_CODE (XEXP (elt, 1)) != CONST_INT
+ || INTVAL (XEXP (elt, 1)) != -(count - 1) * 4)
+ return 0;
+
+ /* Now go through the rest of the vector elements. They must be
+ ordered so that the first instruction stores the highest-numbered
+ register to the highest stack slot and that subsequent instructions
+ store a lower-numbered register to the slot below.
+
+ LAST keeps track of the smallest-numbered register stored so far.
+ MASK is the set of stored registers. */
+ last = FIRST_PSEUDO_REGISTER;
+ mask = 0;
+ for (i = 1; i < count; i++)
+ {
+ /* Check that element i is a (set (mem M) R) and that R is valid. */
+ elt = XVECEXP (op, 0, i);
+ if (GET_CODE (elt) != SET
+ || GET_CODE (SET_DEST (elt)) != MEM
+ || GET_CODE (SET_SRC (elt)) != REG
+ || REGNO (SET_SRC (elt)) >= last)
+ return 0;
+
+ /* R was OK, so provisionally add it to MASK. We return 0 in any
+ case if the rest of the instruction has a flaw. */
+ last = REGNO (SET_SRC (elt));
+ mask |= (1 << last);
+
+ /* Check that M has the form (plus (sp) (const_int -I*4)) */
+ elt = XEXP (SET_DEST (elt), 0);
+ if (GET_CODE (elt) != PLUS
+ || GET_CODE (XEXP (elt, 0)) != REG
+ || REGNO (XEXP (elt, 0)) != STACK_POINTER_REGNUM
+ || GET_CODE (XEXP (elt, 1)) != CONST_INT
+ || INTVAL (XEXP (elt, 1)) != -i * 4)
+ return 0;
+ }
+
+ /* All or none of the callee-saved extended registers must be in the set. */
+ if ((mask & 0x3c000) != 0
+ && (mask & 0x3c000) != 0x3c000)
+ return 0;
+
+ return mask;
+}
+
/* Return true if OP is a valid call operand. */
int
""
"*
{
- int need_comma;
-
- need_comma = 0;
- fputs (\"\\tret [\", asm_out_file);
- if (regs_ever_live[2])
- {
- fputs (\"d2\", asm_out_file);
- need_comma = 1;
- }
- if (regs_ever_live[3])
- {
- if (need_comma)
- fputc (',', asm_out_file);
- fputs (\"d3\", asm_out_file);
- need_comma = 1;
- }
- if (regs_ever_live[6])
- {
- if (need_comma)
- fputc (',', asm_out_file);
- fputs (\"a2\", asm_out_file);
- need_comma = 1;
- }
- if (regs_ever_live[7])
- {
- if (need_comma)
- fputc (',', asm_out_file);
- fputs (\"a3\", asm_out_file);
- need_comma = 1;
- }
- if (regs_ever_live[14] || regs_ever_live[15]
- || regs_ever_live[16] || regs_ever_live[17])
- {
- if (need_comma)
- fputc (',', asm_out_file);
- fputs (\"exreg1\", asm_out_file);
- need_comma = 1;
- }
- fprintf (asm_out_file, \"],%d\\n\", INTVAL (operands[0]));
+ fputs (\"\\tret \", asm_out_file);
+ mn10300_print_reg_list (asm_out_file, mn10300_get_live_callee_saved_regs ());
+ fprintf (asm_out_file, \",%d\\n\", (int) INTVAL (operands[0]));
return \"\";
}"
[(set_attr "cc" "clobber")])
+;; This instruction matches one generated by mn10300_gen_multiple_store()
(define_insn "store_movm"
- [(const_int 1)
- (use (reg:SI 2))
- (use (reg:SI 3))
- (use (reg:SI 6))
- (use (reg:SI 7))
- (use (reg:SI 14))
- (use (reg:SI 15))
- (use (reg:SI 16))
- (use (reg:SI 17))
- (clobber (reg:SI 9))]
+ [(match_parallel 0 "store_multiple_operation"
+ [(set (reg:SI 9) (plus:SI (reg:SI 9) (match_operand 1 "" "")))])]
""
"*
{
- int need_comma;
-
- need_comma = 0;
- fputs (\"\\tmovm [\", asm_out_file);
- if (regs_ever_live[2])
- {
- fputs (\"d2\", asm_out_file);
- need_comma = 1;
- }
- if (regs_ever_live[3])
- {
- if (need_comma)
- fputc (',', asm_out_file);
- fputs (\"d3\", asm_out_file);
- need_comma = 1;
- }
- if (regs_ever_live[6])
- {
- if (need_comma)
- fputc (',', asm_out_file);
- fputs (\"a2\", asm_out_file);
- need_comma = 1;
- }
- if (regs_ever_live[7])
- {
- if (need_comma)
- fputc (',', asm_out_file);
- fputs (\"a3\", asm_out_file);
- need_comma = 1;
- }
- if (regs_ever_live[14] || regs_ever_live[15]
- || regs_ever_live[16] || regs_ever_live[17])
- {
- if (need_comma)
- fputc (',', asm_out_file);
- fputs (\"exreg1\", asm_out_file);
- need_comma = 1;
- }
- fputs (\"],(sp)\\n\", asm_out_file);
+ fputs (\"\\tmovm \", asm_out_file);
+ mn10300_print_reg_list (asm_out_file,
+ store_multiple_operation (operands[0], VOIDmode));
+ fprintf (asm_out_file, \",(sp)\\n\");
return \"\";
}"
[(set_attr "cc" "clobber")])
-
+
(define_insn "return"
[(return)]
"can_use_return_insn ()"