From patchwork Wed Oct 10 14:35:26 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: [ARM,2/3] Epilogue using LDRD in ARM mode Date: Wed, 10 Oct 2012 04:35:26 -0000 From: Greta Yorsh X-Patchwork-Id: 190656 Message-Id: <000d01cda6f4$7d7da630$7878f290$@yorsh@arm.com> To: "Greta Yorsh" , "GCC Patches" Cc: "Ramana Radhakrishnan" , "Richard Earnshaw" , , Emit epilogue using LDRD in ARM mode when prefer_ldrd_strd is set. ChangeLog gcc/ 2012-10-10 Sameera Deshpande Greta Yorsh * config/arm/arm.c (arm_emit_ldrd_pop): New function. (arm_expand_epilogue): Used here. (arm_emit_multi_reg_pop): Add a special case for load of a single register with writeback. diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c index f3e84f8..b106cd6 100644 --- a/gcc/config/arm/arm.c +++ b/gcc/config/arm/arm.c @@ -16208,6 +16208,17 @@ arm_emit_multi_reg_pop (unsigned long saved_regs_mask) if (saved_regs_mask & (1 << i)) { reg = gen_rtx_REG (SImode, i); + if ((num_regs == 1) && emit_update && !return_in_pc) + { + /* Emit single load with writeback. */ + tmp = gen_frame_mem (SImode, + gen_rtx_POST_INC (Pmode, + stack_pointer_rtx)); + tmp = emit_insn (gen_rtx_SET (VOIDmode, reg, tmp)); + REG_NOTES (tmp) = alloc_reg_note (REG_CFA_RESTORE, reg, dwarf); + return; + } + tmp = gen_rtx_SET (VOIDmode, reg, gen_frame_mem @@ -16439,6 +16450,109 @@ thumb2_emit_ldrd_pop (unsigned long saved_regs_mask) return; } +/* LDRD in ARM mode needs consecutive registers to be stored. This function + keeps accumulating non-consecutive registers until first consecutive register + pair is found. It then generates multi-reg POP for all accumulated + registers, and then generates LDRD with write-back for consecutive register + pair. This process is repeated until all the registers are loaded from + stack. multi register POP takes care of lone registers as well. However, + LDRD cannot be generated for PC, as results are unpredictable. Hence, if PC + is in SAVED_REGS_MASK, generate multi-reg POP with RETURN or LDR with RETURN + depending upon number of registers in REGS_TO_BE_POPPED_MASK. */ +static void +arm_emit_ldrd_pop (unsigned long saved_regs_mask) +{ + int num_regs = 0; + int i, j; + rtx par = NULL_RTX; + rtx insn = NULL_RTX; + rtx dwarf = NULL_RTX; + rtx tmp; + unsigned long regs_to_be_popped_mask = 0; + + for (i = 0; i <= LAST_ARM_REGNUM; i++) + if (saved_regs_mask & (1 << i)) + num_regs++; + + gcc_assert (num_regs && num_regs <= 16); + + for (i = 0, j = 0; i < num_regs; j++) + if (saved_regs_mask & (1 << j)) + { + i++; + if ((j % 2) == 0 + && (saved_regs_mask & (1 << (j + 1))) + && (j + 1) != SP_REGNUM + && (j + 1) != PC_REGNUM + && regs_to_be_popped_mask) + { + /* Current register and next register form register pair for which + LDRD can be generated. Generate POP for accumulated registers + and reset regs_to_be_popped_mask. SP should be handled here as + the results are unpredictable if register being stored is same + as index register (in this case, SP). PC is always the last + register being popped. Hence, we don't have to worry about PC + here. */ + arm_emit_multi_reg_pop (regs_to_be_popped_mask); + regs_to_be_popped_mask = 0; + continue; + } + + regs_to_be_popped_mask |= (1 << j); + + if ((j % 2) == 1 + && (saved_regs_mask & (1 << (j - 1))) + && j != SP_REGNUM + && j != PC_REGNUM) + { + /* Generate an LDRD for register pair R_, R_. The pattern + generated here is equivalent to + [(SET SP, (PLUS SP, 8)) + (SET R_, (MEM SP)) + (SET R_, (MEM (PLUS SP, 4)))]. */ + tmp = gen_rtx_SET (DImode, + gen_rtx_REG (DImode, j - 1), + gen_frame_mem (DImode, + gen_rtx_POST_INC (Pmode, + stack_pointer_rtx))); + RTX_FRAME_RELATED_P (tmp) = 1; + dwarf = alloc_reg_note (REG_CFA_RESTORE, + gen_rtx_REG (SImode, j - 1), + dwarf); + dwarf = alloc_reg_note (REG_CFA_RESTORE, + gen_rtx_REG (SImode, j), + dwarf); + insn = emit_insn (tmp); + REG_NOTES (insn) = dwarf; + regs_to_be_popped_mask = 0; + dwarf = NULL_RTX; + } + } + + if (regs_to_be_popped_mask) + { + /* single PC pop can happen here. Take care of that. */ + if (regs_to_be_popped_mask == (1 << PC_REGNUM)) + { + /* Only PC is to be popped. */ + par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (2)); + XVECEXP (par, 0, 0) = ret_rtx; + tmp = gen_rtx_SET (SImode, + gen_rtx_REG (SImode, PC_REGNUM), + gen_frame_mem (SImode, + gen_rtx_POST_INC (SImode, + stack_pointer_rtx))); + RTX_FRAME_RELATED_P (tmp) = 1; + XVECEXP (par, 0, 1) = tmp; + emit_jump_insn (par); + } + else + arm_emit_multi_reg_pop (regs_to_be_popped_mask); + } + + return; +} + /* Calculate the size of the return value that is passed in registers. */ static unsigned arm_size_return_regs (void) @@ -23402,6 +23516,8 @@ arm_expand_epilogue (bool really_return) { if (TARGET_THUMB2) thumb2_emit_ldrd_pop (saved_regs_mask); + else if (TARGET_ARM) + arm_emit_ldrd_pop (saved_regs_mask); else arm_emit_multi_reg_pop (saved_regs_mask); }