Commit 3a7731fd by Philip Blundell Committed by Phil Blundell

arm.c (use_return_insn): Don't reject interrupt functions.

2002-02-19  Philip Blundell  <philb@gnu.org>

	* config/arm/arm.c (use_return_insn): Don't reject interrupt
	functions.
	(arm_compute_save_reg_mask): Save LR for interrupt functions too.
	(output_return_instruction): Allow interrupt functions to return with
	ldmfd sp!, {... pc}^.  Use LDR to restore any single register.
	(arm_expand_prologue): Subtract 4 before stacking LR in an
	interrupt function.

From-SVN: r49872
parent 14f583b8
2002-02-19 Philip Blundell <philb@gnu.org>
* config/arm/arm.c (use_return_insn): Don't reject interrupt
functions.
(arm_compute_save_reg_mask): Save LR for interrupt functions too.
(output_return_instruction): Allow interrupt functions to return with
ldmfd sp!, {... pc}^. Use LDR to restore any single register.
(arm_expand_prologue): Subtract 4 before stacking LR in an
interrupt function.
2002-02-19 Philip Blundell <pb@nexus.co.uk> 2002-02-19 Philip Blundell <pb@nexus.co.uk>
* config/arm/arm.c (arm_encode_call_attribute): Operate on any * config/arm/arm.c (arm_encode_call_attribute): Operate on any
......
...@@ -899,9 +899,9 @@ use_return_insn (iscond) ...@@ -899,9 +899,9 @@ use_return_insn (iscond)
func_type = arm_current_func_type (); func_type = arm_current_func_type ();
/* Naked functions, volatile functiond and interrupt /* Naked functions and volatile functions need special
functions all need special consideration. */ consideration. */
if (func_type & (ARM_FT_INTERRUPT | ARM_FT_VOLATILE | ARM_FT_NAKED)) if (func_type & (ARM_FT_VOLATILE | ARM_FT_NAKED))
return 0; return 0;
/* As do variadic functions. */ /* As do variadic functions. */
...@@ -7142,11 +7142,10 @@ arm_compute_save_reg_mask () ...@@ -7142,11 +7142,10 @@ arm_compute_save_reg_mask ()
now and then popping it back into the PC. This incurs extra memory now and then popping it back into the PC. This incurs extra memory
accesses though, so we only do it when optimising for size, and only accesses though, so we only do it when optimising for size, and only
if we know that we will not need a fancy return sequence. */ if we know that we will not need a fancy return sequence. */
if (! IS_INTERRUPT (func_type) if (regs_ever_live [LR_REGNUM]
&& (regs_ever_live [LR_REGNUM]
|| (save_reg_mask || (save_reg_mask
&& optimize_size && optimize_size
&& ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL))) && ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL))
save_reg_mask |= 1 << LR_REGNUM; save_reg_mask |= 1 << LR_REGNUM;
if (cfun->machine->lr_save_eliminated) if (cfun->machine->lr_save_eliminated)
...@@ -7212,7 +7211,6 @@ output_return_instruction (operand, really_return, reverse) ...@@ -7212,7 +7211,6 @@ output_return_instruction (operand, really_return, reverse)
(eg interworking, or ISR) then we can load the return address (eg interworking, or ISR) then we can load the return address
directly into the PC. Otherwise we must load it into LR. */ directly into the PC. Otherwise we must load it into LR. */
if (really_return if (really_return
&& ! IS_INTERRUPT (func_type)
&& ! TARGET_INTERWORK) && ! TARGET_INTERWORK)
return_reg = reg_names[PC_REGNUM]; return_reg = reg_names[PC_REGNUM];
else else
...@@ -7229,14 +7227,23 @@ output_return_instruction (operand, really_return, reverse) ...@@ -7229,14 +7227,23 @@ output_return_instruction (operand, really_return, reverse)
live_regs_mask |= (1 << SP_REGNUM); live_regs_mask |= (1 << SP_REGNUM);
} }
/* On some ARM architectures it is faster to use LDR rather than LDM to /* On some ARM architectures it is faster to use LDR rather than
load a single register. On other architectures, the cost is the same. LDM to load a single register. On other architectures, the
In 26 bit mode we have to use LDM in order to be able to restore the cost is the same. In 26 bit mode, or for exception handlers,
CPSR. */ we have to use LDM to load the PC so that the CPSR is also
if ((live_regs_mask == (1 << LR_REGNUM)) restored. */
&& (! really_return || TARGET_APCS_32)) for (reg = 0; reg <= LAST_ARM_REGNUM; reg++)
{ {
sprintf (instr, "ldr%s\t%%|%s, [%%|sp], #4", conditional, return_reg); if (live_regs_mask == (unsigned int)(1 << reg))
break;
}
if (reg <= LAST_ARM_REGNUM
&& (reg != LR_REGNUM
|| ! really_return
|| (TARGET_APCS_32 && ! IS_INTERRUPT (func_type))))
{
sprintf (instr, "ldr%s\t%%|%s, [%%|sp], #4", conditional,
(reg == LR_REGNUM) ? return_reg : reg_names[reg]);
} }
else else
{ {
...@@ -7281,7 +7288,10 @@ output_return_instruction (operand, really_return, reverse) ...@@ -7281,7 +7288,10 @@ output_return_instruction (operand, really_return, reverse)
memcpy (p, "%|", 2); memcpy (p, "%|", 2);
memcpy (p + 2, return_reg, l); memcpy (p + 2, return_reg, l);
strcpy (p + 2 + l, (TARGET_APCS_32 || !really_return) ? "}" : "}^"); strcpy (p + 2 + l, ((TARGET_APCS_32
&& !IS_INTERRUPT (func_type))
|| !really_return)
? "}" : "}^");
} }
else else
strcpy (p, "}"); strcpy (p, "}");
...@@ -7289,26 +7299,15 @@ output_return_instruction (operand, really_return, reverse) ...@@ -7289,26 +7299,15 @@ output_return_instruction (operand, really_return, reverse)
output_asm_insn (instr, & operand); output_asm_insn (instr, & operand);
if (really_return) /* See if we need to generate an extra instruction to
perform the actual function return. */
if (really_return
&& func_type != ARM_FT_INTERWORKED
&& (live_regs_mask & (1 << LR_REGNUM)) != 0)
{ {
/* See if we need to generate an extra instruction to /* The return has already been handled
perform the actual function return. */ by loading the LR into the PC. */
switch ((int) ARM_FUNC_TYPE (func_type)) really_return = 0;
{
case ARM_FT_ISR:
case ARM_FT_FIQ:
case ARM_FT_EXCEPTION:
case ARM_FT_INTERWORKED:
/* A separate return instruction is always needed. */
break;
default:
/* The return may have already been handled
by loading the LR into the PC. */
if ((live_regs_mask & (1 << LR_REGNUM)) != 0)
really_return = 0;
break;
}
} }
} }
...@@ -8245,6 +8244,19 @@ arm_expand_prologue () ...@@ -8245,6 +8244,19 @@ arm_expand_prologue ()
RTX_FRAME_RELATED_P (insn) = 1; RTX_FRAME_RELATED_P (insn) = 1;
} }
/* If this is an interrupt service routine, and the link register is
going to be pushed, subtracting four now will mean that the
function return can be done with a single instruction. */
if ((func_type == ARM_FT_ISR || func_type == ARM_FT_FIQ)
&& (live_regs_mask & (1 << LR_REGNUM)) != 0)
{
emit_insn (gen_rtx_SET (SImode,
gen_rtx_REG (SImode, LR_REGNUM),
gen_rtx_PLUS (SImode,
gen_rtx_REG (SImode, LR_REGNUM),
GEN_INT (-4))));
}
if (live_regs_mask) if (live_regs_mask)
{ {
insn = emit_multi_reg_push (live_regs_mask); insn = emit_multi_reg_push (live_regs_mask);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment