+2004-10-14 Richard Sandiford <rsandifo@redhat.com>
+
+ * config/arm/arm.h (CONDITIONAL_REGISTER_USAGE): Make r11 fixed and
+ global for -mcaller-super-interworking.
+ (CALLER_INTERWORKING_SLOT_SIZE): New macro.
+ * config/arm/arm.c (thumb_compute_save_reg_mask): Save r11 if
+ CALLER_INTERWORKING_SLOT_SIZE is nonzero and the function does
+ not need a frame pointer.
+ (arm_get_frame_offsets): Add CALLER_INTERWORKING_SLOT_SIZE bytes to
+ the soft frame pointer offset.
+ (thumb_expand_prologue): Set up r11 for -mcaller-super-interworking.
+ * config/arm/arm.md (*call_reg_thumb, *call_value_reg_thumb): Use
+ _interwork_{r7,r11}_call_via_rN if some arguments are passed on
+ the stack. Use frame_pointer_needed to choose between them.
+ * config/arm/lib1funcs.asm (_arm_return_{r7,r11}): New functions.
+ (interwork_with_frame): New macro.
+ (interwork): Add _interwork_{r7,r11}_call_via_rN().
+
2004-10-14 Ben Elliston <bje@au.ibm.com>
PR other/17900
mask |= (1 << PIC_OFFSET_TABLE_REGNUM);
if (TARGET_SINGLE_PIC_BASE)
mask &= ~(1 << arm_pic_register);
+ /* See if we might need r11 for calls to _interwork_r11_call_via_rN(). */
+ if (!frame_pointer_needed && CALLER_INTERWORKING_SLOT_SIZE > 0)
+ mask |= 1 << ARM_HARD_FRAME_POINTER_REGNUM;
/* lr will also be pushed if any lo regs are pushed. */
if (mask & 0xff || thumb_force_lr_save ())
/* Saved registers include the stack frame. */
offsets->saved_regs = offsets->saved_args + saved;
- offsets->soft_frame = offsets->saved_regs;
+ offsets->soft_frame = offsets->saved_regs + CALLER_INTERWORKING_SLOT_SIZE;
/* A leaf function does not need any stack alignment if it has nothing
on the stack. */
if (leaf && frame_size == 0)
stack_pointer_rtx));
RTX_FRAME_RELATED_P (insn) = 1;
}
+ else if (CALLER_INTERWORKING_SLOT_SIZE > 0)
+ emit_move_insn (gen_rtx_REG (Pmode, ARM_HARD_FRAME_POINTER_REGNUM),
+ stack_pointer_rtx);
amount = offsets->outgoing_args - offsets->saved_regs;
if (amount)
fixed_regs[10] = 1; \
call_used_regs[10] = 1; \
} \
- if (TARGET_APCS_FRAME) \
+ /* -mcaller-super-interworking reserves r11 for calls to \
+ _interwork_r11_call_via_rN(). Making the register global \
+ is an easy way of ensuring that it remains valid for all \
+ calls. */ \
+ if (TARGET_APCS_FRAME || TARGET_CALLER_INTERWORKING) \
{ \
fixed_regs[ARM_HARD_FRAME_POINTER_REGNUM] = 1; \
call_used_regs[ARM_HARD_FRAME_POINTER_REGNUM] = 1; \
+ if (TARGET_CALLER_INTERWORKING) \
+ global_regs[ARM_HARD_FRAME_POINTER_REGNUM] = 1; \
} \
SUBTARGET_CONDITIONAL_REGISTER_USAGE \
}
goes at a more negative offset in the frame. */
#define FRAME_GROWS_DOWNWARD 1
+/* The amount of scratch space needed by _interwork_{r7,r11}_call_via_rN().
+ When present, it is one word in size, and sits at the top of the frame,
+ between the soft frame pointer and either r7 or r11.
+
+ We only need _interwork_rM_call_via_rN() for -mcaller-super-interworking,
+ and only then if some outgoing arguments are passed on the stack. It would
+ be tempting to also check whether the stack arguments are passed by indirect
+ calls, but there seems to be no reason in principle why a post-reload pass
+ couldn't convert a direct call into an indirect one. */
+#define CALLER_INTERWORKING_SLOT_SIZE \
+ (TARGET_CALLER_INTERWORKING \
+ && current_function_outgoing_args_size != 0 \
+ ? UNITS_PER_WORD : 0)
+
/* Offset within stack frame to start allocating local variables at.
If FRAME_GROWS_DOWNWARD, this is the offset to the END of the
first local allocated. Otherwise, it is the offset to the BEGINNING
"TARGET_THUMB && !arm_arch5"
"*
{
- if (TARGET_CALLER_INTERWORKING)
+ if (!TARGET_CALLER_INTERWORKING)
+ return \"bl\\t%__call_via_%0\";
+ else if (operands[1] == const0_rtx)
return \"bl\\t%__interwork_call_via_%0\";
+ else if (frame_pointer_needed)
+ return \"bl\\t%__interwork_r7_call_via_%0\";
else
- return \"bl\\t%__call_via_%0\";
+ return \"bl\\t%__interwork_r11_call_via_%0\";
}"
[(set_attr "type" "call")]
)
"TARGET_THUMB && !arm_arch5"
"*
{
- if (TARGET_CALLER_INTERWORKING)
+ if (!TARGET_CALLER_INTERWORKING)
+ return \"bl\\t%__call_via_%1\";
+ else if (operands[2] == const0_rtx)
return \"bl\\t%__interwork_call_via_%1\";
+ else if (frame_pointer_needed)
+ return \"bl\\t%__interwork_r7_call_via_%1\";
else
- return \"bl\\t%__call_via_%1\";
+ return \"bl\\t%__interwork_r11_call_via_%1\";
}"
[(set_attr "type" "call")]
)
the target code cannot be relied upon to return via a BX instruction, so
instead we have to store the resturn address on the stack and allow the
called function to return here instead. Upon return we recover the real
- return address and use a BX to get back to Thumb mode. */
+ return address and use a BX to get back to Thumb mode.
+
+ There are three variations of this code. The first,
+ _interwork_call_via_rN(), will push the return address onto the
+ stack and pop it in _arm_return(). It should only be used if all
+ arguments are passed in registers.
+
+ The second, _interwork_r7_call_via_rN(), instead stores the return
+ address at [r7, #-4]. It is the caller's responsibility to ensure
+ that this address is valid and contains no useful data.
+
+ The third, _interwork_r11_call_via_rN(), works in the same way but
+ uses r11 instead of r7. It is useful if the caller does not really
+ need a frame pointer. */
.text
.align 0
.globl _arm_return
_arm_return:
RETLDM
- .code 16
+
+ .globl _arm_return_r7
+_arm_return_r7:
+ ldr lr, [r7, #-4]
+ bx lr
+
+ .globl _arm_return_r11
+_arm_return_r11:
+ ldr lr, [r11, #-4]
+ bx lr
+
+.macro interwork_with_frame frame, register, name, return
+ .code 16
+
+ THUMB_FUNC_START \name
+
+ bx pc
+ nop
+
+ .code 32
+ tst \register, #1
+ streq lr, [\frame, #-4]
+ adreq lr, _arm_return_\frame
+ bx \register
+
+ SIZE (\name)
+.endm
.macro interwork register
.code 16
bx \register
SIZE (_interwork_call_via_\register)
+
+ interwork_with_frame r7,\register,_interwork_r7_call_via_\register
+ interwork_with_frame r11,\register,_interwork_r11_call_via_\register
.endm
interwork r0