static void adjust_frame_related_expr (rtx last_sp_set, rtx insn, HOST_WIDE_INT this_adjust) { rtx note = find_reg_note (last_sp_set, REG_FRAME_RELATED_EXPR, NULL_RTX); rtx new_expr = NULL_RTX; if (note == NULL_RTX && RTX_FRAME_RELATED_P (insn)) return; if (note && GET_CODE (XEXP (note, 0)) == SEQUENCE && XVECLEN (XEXP (note, 0), 0) >= 2) { rtx expr = XEXP (note, 0); rtx last = XVECEXP (expr, 0, XVECLEN (expr, 0) - 1); int i; if (GET_CODE (last) == SET && RTX_FRAME_RELATED_P (last) == RTX_FRAME_RELATED_P (insn) && SET_DEST (last) == stack_pointer_rtx && GET_CODE (SET_SRC (last)) == PLUS && XEXP (SET_SRC (last), 0) == stack_pointer_rtx && CONST_INT_P (XEXP (SET_SRC (last), 1))) { XEXP (SET_SRC (last), 1) = GEN_INT (INTVAL (XEXP (SET_SRC (last), 1)) + this_adjust); return; } new_expr = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (XVECLEN (expr, 0) + 1)); for (i = 0; i < XVECLEN (expr, 0); i++) XVECEXP (new_expr, 0, i) = XVECEXP (expr, 0, i); } else { new_expr = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (2)); if (note) XVECEXP (new_expr, 0, 0) = XEXP (note, 0); else { rtx expr = copy_rtx (single_set_for_csa (last_sp_set)); XEXP (SET_SRC (expr), 1) = GEN_INT (INTVAL (XEXP (SET_SRC (expr), 1)) - this_adjust); RTX_FRAME_RELATED_P (expr) = 1; XVECEXP (new_expr, 0, 0) = expr; } } XVECEXP (new_expr, 0, XVECLEN (new_expr, 0) - 1) = copy_rtx (single_set_for_csa (insn)); RTX_FRAME_RELATED_P (XVECEXP (new_expr, 0, XVECLEN (new_expr, 0) - 1)) = RTX_FRAME_RELATED_P (insn); if (note) XEXP (note, 0) = new_expr; else add_reg_note (last_sp_set, REG_FRAME_RELATED_EXPR, new_expr); }
static int try_apply_stack_adjustment (rtx insn, struct csa_memlist *memlist, HOST_WIDE_INT new_adjust, HOST_WIDE_INT delta) { struct csa_memlist *ml; rtx set; set = single_set_for_csa (insn); validate_change (insn, &XEXP (SET_SRC (set), 1), GEN_INT (new_adjust), 1); for (ml = memlist; ml ; ml = ml->next) validate_change (ml->insn, ml->mem, replace_equiv_address_nv (*ml->mem, plus_constant (stack_pointer_rtx, ml->sp_offset - delta)), 1); if (apply_change_group ()) { /* Succeeded. Update our knowledge of the memory references. */ for (ml = memlist; ml ; ml = ml->next) ml->sp_offset -= delta; return 1; } else return 0; }
static int try_apply_stack_adjustment (rtx insn, struct csa_reflist *reflist, HOST_WIDE_INT new_adjust, HOST_WIDE_INT delta) { struct csa_reflist *ml; rtx set; set = single_set_for_csa (insn); if (MEM_P (SET_DEST (set))) validate_change (insn, &SET_DEST (set), replace_equiv_address (SET_DEST (set), stack_pointer_rtx), 1); else validate_change (insn, &XEXP (SET_SRC (set), 1), GEN_INT (new_adjust), 1); for (ml = reflist; ml ; ml = ml->next) { rtx new_addr = plus_constant (Pmode, stack_pointer_rtx, ml->sp_offset - delta); rtx new_val; if (MEM_P (*ml->ref)) new_val = replace_equiv_address_nv (*ml->ref, new_addr); else if (GET_MODE (*ml->ref) == GET_MODE (stack_pointer_rtx)) new_val = new_addr; else new_val = lowpart_subreg (GET_MODE (*ml->ref), new_addr, GET_MODE (new_addr)); validate_change (ml->insn, ml->ref, new_val, 1); } if (apply_change_group ()) { /* Succeeded. Update our knowledge of the stack references. */ for (ml = reflist; ml ; ml = ml->next) ml->sp_offset -= delta; return 1; } else return 0; }
static void combine_stack_adjustments_for_block (basic_block bb) { HOST_WIDE_INT last_sp_adjust = 0; rtx last_sp_set = NULL_RTX; rtx last2_sp_set = NULL_RTX; struct csa_reflist *reflist = NULL; rtx insn, next, set; struct record_stack_refs_data data; bool end_of_block = false; for (insn = BB_HEAD (bb); !end_of_block ; insn = next) { end_of_block = insn == BB_END (bb); next = NEXT_INSN (insn); if (! INSN_P (insn)) continue; set = single_set_for_csa (insn); if (set) { rtx dest = SET_DEST (set); rtx src = SET_SRC (set); /* Find constant additions to the stack pointer. */ if (dest == stack_pointer_rtx && GET_CODE (src) == PLUS && XEXP (src, 0) == stack_pointer_rtx && CONST_INT_P (XEXP (src, 1))) { HOST_WIDE_INT this_adjust = INTVAL (XEXP (src, 1)); /* If we've not seen an adjustment previously, record it now and continue. */ if (! last_sp_set) { last_sp_set = insn; last_sp_adjust = this_adjust; continue; } /* If not all recorded refs can be adjusted, or the adjustment is now too large for a constant addition, we cannot merge the two stack adjustments. Also we need to be careful to not move stack pointer such that we create stack accesses outside the allocated area. We can combine an allocation into the first insn, or a deallocation into the second insn. We can not combine an allocation followed by a deallocation. The only somewhat frequent occurrence of the later is when a function allocates a stack frame but does not use it. For this case, we would need to analyze rtl stream to be sure that allocated area is really unused. This means not only checking the memory references, but also all registers or global memory references possibly containing a stack frame address. Perhaps the best way to address this problem is to teach gcc not to allocate stack for objects never used. */ /* Combine an allocation into the first instruction. */ if (STACK_GROWS_DOWNWARD ? this_adjust <= 0 : this_adjust >= 0) { if (try_apply_stack_adjustment (last_sp_set, reflist, last_sp_adjust + this_adjust, this_adjust)) { /* It worked! */ maybe_move_args_size_note (last_sp_set, insn, false); delete_insn (insn); last_sp_adjust += this_adjust; continue; } } /* Otherwise we have a deallocation. Do not combine with a previous allocation. Combine into the second insn. */ else if (STACK_GROWS_DOWNWARD ? last_sp_adjust >= 0 : last_sp_adjust <= 0) { if (try_apply_stack_adjustment (insn, reflist, last_sp_adjust + this_adjust, -last_sp_adjust)) { /* It worked! */ maybe_move_args_size_note (insn, last_sp_set, true); delete_insn (last_sp_set); last_sp_set = insn; last_sp_adjust += this_adjust; free_csa_reflist (reflist); reflist = NULL; continue; } } /* Combination failed. Restart processing from here. If deallocation+allocation conspired to cancel, we can delete the old deallocation insn. */ if (last_sp_set) { if (last_sp_adjust == 0) { maybe_move_args_size_note (insn, last_sp_set, true); delete_insn (last_sp_set); } else last2_sp_set = last_sp_set; } free_csa_reflist (reflist); reflist = NULL; last_sp_set = insn; last_sp_adjust = this_adjust; continue; } /* Find a store with pre-(dec|inc)rement or pre-modify of exactly the previous adjustment and turn it into a simple store. This is equivalent to anticipating the stack adjustment so this must be an allocation. */ if (MEM_P (dest) && ((STACK_GROWS_DOWNWARD ? (GET_CODE (XEXP (dest, 0)) == PRE_DEC && last_sp_adjust == (HOST_WIDE_INT) GET_MODE_SIZE (GET_MODE (dest))) : (GET_CODE (XEXP (dest, 0)) == PRE_INC && last_sp_adjust == -(HOST_WIDE_INT) GET_MODE_SIZE (GET_MODE (dest)))) || ((STACK_GROWS_DOWNWARD ? last_sp_adjust >= 0 : last_sp_adjust <= 0) && GET_CODE (XEXP (dest, 0)) == PRE_MODIFY && GET_CODE (XEXP (XEXP (dest, 0), 1)) == PLUS && XEXP (XEXP (XEXP (dest, 0), 1), 0) == stack_pointer_rtx && GET_CODE (XEXP (XEXP (XEXP (dest, 0), 1), 1)) == CONST_INT && INTVAL (XEXP (XEXP (XEXP (dest, 0), 1), 1)) == -last_sp_adjust)) && XEXP (XEXP (dest, 0), 0) == stack_pointer_rtx && !reg_mentioned_p (stack_pointer_rtx, src) && memory_address_p (GET_MODE (dest), stack_pointer_rtx) && try_apply_stack_adjustment (insn, reflist, 0, -last_sp_adjust)) { if (last2_sp_set) maybe_move_args_size_note (last2_sp_set, last_sp_set, false); else maybe_move_args_size_note (insn, last_sp_set, true); delete_insn (last_sp_set); free_csa_reflist (reflist); reflist = NULL; last_sp_set = NULL_RTX; last_sp_adjust = 0; continue; } } data.insn = insn; data.reflist = reflist; if (!CALL_P (insn) && last_sp_set && !for_each_rtx (&PATTERN (insn), record_stack_refs, &data)) { reflist = data.reflist; continue; } reflist = data.reflist; /* Otherwise, we were not able to process the instruction. Do not continue collecting data across such a one. */ if (last_sp_set && (CALL_P (insn) || reg_mentioned_p (stack_pointer_rtx, PATTERN (insn)))) { if (last_sp_set && last_sp_adjust == 0) { force_move_args_size_note (bb, last2_sp_set, last_sp_set); delete_insn (last_sp_set); } free_csa_reflist (reflist); reflist = NULL; last2_sp_set = NULL_RTX; last_sp_set = NULL_RTX; last_sp_adjust = 0; } } if (last_sp_set && last_sp_adjust == 0) { force_move_args_size_note (bb, last2_sp_set, last_sp_set); delete_insn (last_sp_set); } if (reflist) free_csa_reflist (reflist); }