This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] Significantly improve Thumb code quality when the frame pointer is not eliminated
- From: Richard Earnshaw <Richard dot Earnshaw at buzzard dot freeserve dot co dot uk>
- To: gcc-patches at gcc dot gnu dot org
- Cc: Richard dot Earnshaw at buzzard dot freeserve dot co dot uk
- Date: Sat, 20 Aug 2005 11:50:30 +0100
- Subject: [PATCH] Significantly improve Thumb code quality when the frame pointer is not eliminated
Conventionally it seems that a frame pointer will point directly at the
saved registers for a function frame and that any local variables on the
stack will appear below this (assuming a descending stack model). That
means the local variables have a negative offset from the frame pointer.
Unfortunately, Thumb has no memory addressing modes that directly support
negative offsets from a base pointer, and to make matters worse,
generating even small negative constants in registers takes at least two
instructions (load a positive constant and negate it). So the result is
that code quality when we need a frame pointer is truly horrendous.
This patch changes the model that we use when we need a frame pointer in
Thumb code so that we point it at the base of the local variables: these
then have possitive offsets from the frame pointer and can, in general, be
accessed using base+offset addressing. The result is that we can reduce
code size by as much as one third (yes 33%!) in some non-trivial
subroutines.
Tested on arm-elf, arm-eabi and bootstrapped (compiling the compiler in
thumb code) on arm-netbsdelf.
R.
2005-08-20 Richard Earnshaw <richard.earnshaw@arm.com>
* arm.h (arm_stack_offsets): Add locals_base field.
* arm.c (arm_get_frame_offsets): Compute it.
(thumb_compute_initial_elimination offset): Make the Thumb frame
pointer point to the base of the local variables.
(thumb_expand_prologue): Update accordingly.
(thumb_expand_epilogue): Likewise.
* arm.md (thumb_movhi_clobber): Make this insn a define_expand. Change
mode of clobbered scratch to DImode. Handle a case that's known to
need this.
Index: arm.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/arm/arm.c,v
retrieving revision 1.475
diff -p -p -r1.475 arm.c
*** arm.c 19 Aug 2005 13:17:03 -0000 1.475
--- arm.c 20 Aug 2005 10:24:43 -0000
*************** thumb_force_lr_save (void)
*** 9992,9998 ****
| | \
| | local
| | variables
! | | /
--
| | \
| | outgoing
--- 9992,9998 ----
| | \
| | local
| | variables
! locals base pointer -> | | /
--
| | \
| | outgoing
*************** arm_get_frame_offsets (void)
*** 10109,10116 ****
&& (offsets->soft_frame & 7))
offsets->soft_frame += 4;
! offsets->outgoing_args = offsets->soft_frame + frame_size
! + current_function_outgoing_args_size;
if (ARM_DOUBLEWORD_ALIGN)
{
--- 10109,10117 ----
&& (offsets->soft_frame & 7))
offsets->soft_frame += 4;
! offsets->locals_base = offsets->soft_frame + frame_size;
! offsets->outgoing_args = (offsets->locals_base
! + current_function_outgoing_args_size);
if (ARM_DOUBLEWORD_ALIGN)
{
*************** arm_init_expanders (void)
*** 13158,13165 ****
}
! /* Like arm_compute_initial_elimination offset. Simpler because
! THUMB_HARD_FRAME_POINTER isn't actually the ABI specified frame pointer. */
HOST_WIDE_INT
thumb_compute_initial_elimination_offset (unsigned int from, unsigned int to)
--- 13159,13168 ----
}
! /* Like arm_compute_initial_elimination offset. Simpler because there
! isn't an ABI specified frame pointer for Thumb. Instead, we set it
! to point at the base of the local variables after static stack
! space for a function has been allocated. */
HOST_WIDE_INT
thumb_compute_initial_elimination_offset (unsigned int from, unsigned int to)
*************** thumb_compute_initial_elimination_offset
*** 13179,13188 ****
case FRAME_POINTER_REGNUM:
return offsets->soft_frame - offsets->saved_args;
- case THUMB_HARD_FRAME_POINTER_REGNUM:
case ARM_HARD_FRAME_POINTER_REGNUM:
return offsets->saved_regs - offsets->saved_args;
default:
gcc_unreachable ();
}
--- 13182,13193 ----
case FRAME_POINTER_REGNUM:
return offsets->soft_frame - offsets->saved_args;
case ARM_HARD_FRAME_POINTER_REGNUM:
return offsets->saved_regs - offsets->saved_args;
+ case THUMB_HARD_FRAME_POINTER_REGNUM:
+ return offsets->locals_base - offsets->saved_args;
+
default:
gcc_unreachable ();
}
*************** thumb_compute_initial_elimination_offset
*** 13194,13203 ****
case STACK_POINTER_REGNUM:
return offsets->outgoing_args - offsets->soft_frame;
- case THUMB_HARD_FRAME_POINTER_REGNUM:
case ARM_HARD_FRAME_POINTER_REGNUM:
return offsets->saved_regs - offsets->soft_frame;
default:
gcc_unreachable ();
}
--- 13199,13210 ----
case STACK_POINTER_REGNUM:
return offsets->outgoing_args - offsets->soft_frame;
case ARM_HARD_FRAME_POINTER_REGNUM:
return offsets->saved_regs - offsets->soft_frame;
+ case THUMB_HARD_FRAME_POINTER_REGNUM:
+ return offsets->locals_base - offsets->soft_frame;
+
default:
gcc_unreachable ();
}
*************** thumb_expand_prologue (void)
*** 13239,13256 ****
if (flag_pic)
arm_load_pic_register (live_regs_mask);
! offsets = arm_get_frame_offsets ();
!
! if (frame_pointer_needed)
! {
! insn = emit_insn (gen_movsi (hard_frame_pointer_rtx,
! 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)
{
--- 13246,13256 ----
if (flag_pic)
arm_load_pic_register (live_regs_mask);
! if (!frame_pointer_needed && CALLER_INTERWORKING_SLOT_SIZE > 0)
emit_move_insn (gen_rtx_REG (Pmode, ARM_HARD_FRAME_POINTER_REGNUM),
stack_pointer_rtx);
+ offsets = arm_get_frame_offsets ();
amount = offsets->outgoing_args - offsets->saved_regs;
if (amount)
{
*************** thumb_expand_prologue (void)
*** 13336,13347 ****
REG_NOTES (insn));
}
}
! /* If the frame pointer is needed, emit a special barrier that
! will prevent the scheduler from moving stores to the frame
! before the stack adjustment. */
! if (frame_pointer_needed)
! emit_insn (gen_stack_tie (stack_pointer_rtx,
! hard_frame_pointer_rtx));
}
if (current_function_profile || !TARGET_SCHED_PROLOG)
--- 13336,13364 ----
REG_NOTES (insn));
}
}
! }
!
! if (frame_pointer_needed)
! {
! amount = offsets->outgoing_args - offsets->locals_base;
!
! if (amount < 1024)
! insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
! stack_pointer_rtx, GEN_INT (amount)));
! else
! {
! emit_insn (gen_movsi (hard_frame_pointer_rtx, GEN_INT (amount)));
! insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
! hard_frame_pointer_rtx,
! stack_pointer_rtx));
! dwarf = gen_rtx_SET (SImode, hard_frame_pointer_rtx,
! plus_constant (stack_pointer_rtx, amount));
! RTX_FRAME_RELATED_P (dwarf) = 1;
! REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, dwarf,
! REG_NOTES (insn));
! }
!
! RTX_FRAME_RELATED_P (insn) = 1;
}
if (current_function_profile || !TARGET_SCHED_PROLOG)
*************** thumb_expand_epilogue (void)
*** 13373,13380 ****
amount = offsets->outgoing_args - offsets->saved_regs;
if (frame_pointer_needed)
! emit_insn (gen_movsi (stack_pointer_rtx, hard_frame_pointer_rtx));
! else if (amount)
{
if (amount < 512)
emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
--- 13390,13401 ----
amount = offsets->outgoing_args - offsets->saved_regs;
if (frame_pointer_needed)
! {
! emit_insn (gen_movsi (stack_pointer_rtx, hard_frame_pointer_rtx));
! amount = offsets->locals_base - offsets->saved_regs;
! }
!
! if (amount)
{
if (amount < 512)
emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
Index: arm.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/arm/arm.h,v
retrieving revision 1.288
diff -p -p -r1.288 arm.h
*** arm.h 19 Aug 2005 09:20:30 -0000 1.288
--- arm.h 20 Aug 2005 10:24:44 -0000
*************** typedef struct arm_stack_offsets GTY(())
*** 1476,1481 ****
--- 1476,1482 ----
int frame; /* ARM_HARD_FRAME_POINTER_REGNUM. */
int saved_regs;
int soft_frame; /* FRAME_POINTER_REGNUM. */
+ int locals_base; /* THUMB_HARD_FRAME_POINTER_REGNUM. */
int outgoing_args; /* STACK_POINTER_REGNUM. */
}
arm_stack_offsets;
Index: arm.md
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/arm/arm.md,v
retrieving revision 1.204
diff -p -p -r1.204 arm.md
*** arm.md 30 Jul 2005 18:23:51 -0000 1.204
--- arm.md 20 Aug 2005 10:24:46 -0000
***************
*** 4951,4963 ****
[(set_attr "predicable" "yes")]
)
! (define_insn "thumb_movhi_clobber"
! [(set (match_operand:HI 0 "memory_operand" "=m")
! (match_operand:HI 1 "register_operand" "l"))
! (clobber (match_operand:SI 2 "register_operand" "=&l"))]
"TARGET_THUMB"
! "*
! gcc_unreachable ();"
)
;; We use a DImode scratch because we may occasionally need an additional
--- 4951,4971 ----
[(set_attr "predicable" "yes")]
)
! (define_expand "thumb_movhi_clobber"
! [(set (match_operand:HI 0 "memory_operand" "")
! (match_operand:HI 1 "register_operand" ""))
! (clobber (match_operand:DI 2 "register_operand" ""))]
"TARGET_THUMB"
! "
! if (strict_memory_address_p (HImode, XEXP (operands[0], 0))
! && REGNO (operands[1]) <= LAST_LO_REGNUM)
! {
! emit_insn (gen_movhi (operands[0], operands[1]));
! DONE;
! }
! /* XXX Fixme, need to handle other cases here as well. */
! gcc_unreachable ();
! "
)
;; We use a DImode scratch because we may occasionally need an additional