+/* Merge any REG_CFA_ADJUST_CFA note from SRC into DST.
+ AFTER is true iff DST follows SRC in the instruction stream. */
+
+static void
+maybe_merge_cfa_adjust (rtx_insn *dst, rtx_insn *src, bool after)
+{
+ rtx snote = NULL, dnote = NULL;
+ rtx sexp, dexp;
+ rtx exp1, exp2;
+
+ if (RTX_FRAME_RELATED_P (src))
+ snote = find_reg_note (src, REG_CFA_ADJUST_CFA, NULL_RTX);
+ if (snote == NULL)
+ return;
+ sexp = XEXP (snote, 0);
+
+ if (RTX_FRAME_RELATED_P (dst))
+ dnote = find_reg_note (dst, REG_CFA_ADJUST_CFA, NULL_RTX);
+ if (dnote == NULL)
+ {
+ add_reg_note (dst, REG_CFA_ADJUST_CFA, sexp);
+ return;
+ }
+ dexp = XEXP (dnote, 0);
+
+ gcc_assert (GET_CODE (sexp) == SET);
+ gcc_assert (GET_CODE (dexp) == SET);
+
+ if (after)
+ exp1 = dexp, exp2 = sexp;
+ else
+ exp1 = sexp, exp2 = dexp;
+
+ SET_SRC (exp1) = simplify_replace_rtx (SET_SRC (exp1), SET_DEST (exp2),
+ SET_SRC (exp2));
+ XEXP (dnote, 0) = exp1;
+}
+
+/* Return the next (or previous) active insn within BB. */
+
+static rtx_insn *
+prev_active_insn_bb (basic_block bb, rtx_insn *insn)
+{
+ for (insn = PREV_INSN (insn);
+ insn != PREV_INSN (BB_HEAD (bb));
+ insn = PREV_INSN (insn))
+ if (active_insn_p (insn))
+ return insn;
+ return NULL;
+}
+
+static rtx_insn *
+next_active_insn_bb (basic_block bb, rtx_insn *insn)
+{
+ for (insn = NEXT_INSN (insn);
+ insn != NEXT_INSN (BB_END (bb));
+ insn = NEXT_INSN (insn))
+ if (active_insn_p (insn))
+ return insn;
+ return NULL;
+}
+
+/* If INSN has a REG_ARGS_SIZE note, if possible move it to PREV. Otherwise
+ search for a nearby candidate within BB where we can stick the note. */
+
+static void
+force_move_args_size_note (basic_block bb, rtx_insn *prev, rtx_insn *insn)
+{
+ rtx note;
+ rtx_insn *test, *next_candidate, *prev_candidate;
+
+ /* If PREV exists, tail-call to the logic in the other function. */
+ if (prev)
+ {
+ maybe_move_args_size_note (prev, insn, false);
+ return;
+ }
+
+ /* First, make sure there's anything that needs doing. */
+ note = find_reg_note (insn, REG_ARGS_SIZE, NULL_RTX);
+ if (note == NULL)
+ return;
+
+ /* We need to find a spot between the previous and next exception points
+ where we can place the note and "properly" deallocate the arguments. */
+ next_candidate = prev_candidate = NULL;
+
+ /* It is often the case that we have insns in the order:
+ call
+ add sp (previous deallocation)
+ sub sp (align for next arglist)
+ push arg
+ and the add/sub cancel. Therefore we begin by searching forward. */
+
+ test = insn;
+ while ((test = next_active_insn_bb (bb, test)) != NULL)
+ {
+ /* Found an existing note: nothing to do. */
+ if (find_reg_note (test, REG_ARGS_SIZE, NULL_RTX))
+ return;
+ /* Found something that affects unwinding. Stop searching. */
+ if (CALL_P (test) || !insn_nothrow_p (test))
+ break;
+ if (next_candidate == NULL)
+ next_candidate = test;
+ }
+
+ test = insn;
+ while ((test = prev_active_insn_bb (bb, test)) != NULL)
+ {
+ rtx tnote;
+ /* Found a place that seems logical to adjust the stack. */
+ tnote = find_reg_note (test, REG_ARGS_SIZE, NULL_RTX);
+ if (tnote)
+ {
+ XEXP (tnote, 0) = XEXP (note, 0);
+ return;
+ }
+ if (prev_candidate == NULL)
+ prev_candidate = test;
+ /* Found something that affects unwinding. Stop searching. */
+ if (CALL_P (test) || !insn_nothrow_p (test))
+ break;
+ }
+
+ if (prev_candidate)
+ test = prev_candidate;
+ else if (next_candidate)
+ test = next_candidate;
+ else
+ {
+ /* ??? We *must* have a place, lest we ICE on the lost adjustment.
+ Options are: dummy clobber insn, nop, or prevent the removal of
+ the sp += 0 insn. */
+ /* TODO: Find another way to indicate to the dwarf2 code that we
+ have not in fact lost an adjustment. */
+ test = emit_insn_before (gen_rtx_CLOBBER (VOIDmode, const0_rtx), insn);
+ }
+ add_reg_note (test, REG_ARGS_SIZE, XEXP (note, 0));
+}
+