> I'd find that very fragile. E.g. distribute_notes itself reverses the > order of all notes, and for no REG_NOTES I think we rely on any ordering.
AFAIK distribute_notes is the only routine that redistributes the RTL notes for a fixed insn, so I'm a little skeptical about the purported fragility here. My point is that the complexity should be in fixup_args_size_notes instead of distribute_notes. Tentative patch attached. PR bootstrap/51796 * calls.c (emit_call_1): Make sure REG_NORETURN note comes first. * combine.c (distribute_notes): If i3 is a noreturn call, allow old_size to be equal to args_size if !ACCUMULATE_OUTGOING_ARGS. * expr.c (fixup_args_size_notes): Put REG_ARGS_SIZE notes on noreturn calls even when the delta is 0. * rtlanal.c (find_and_remove_reg_note): New function. * rtl.h (find_and_remove_reg_note): Declare it. -- Eric Botcazou
Index: calls.c =================================================================== --- calls.c (revision 183092) +++ calls.c (working copy) @@ -413,15 +413,6 @@ emit_call_1 (rtx funexp, tree fntree ATT /* Create a nothrow REG_EH_REGION note, if needed. */ make_reg_eh_region_note (call_insn, ecf_flags, 0); - if (ecf_flags & ECF_NORETURN) - add_reg_note (call_insn, REG_NORETURN, const0_rtx); - - if (ecf_flags & ECF_RETURNS_TWICE) - { - add_reg_note (call_insn, REG_SETJMP, const0_rtx); - cfun->calls_setjmp = 1; - } - SIBLING_CALL_P (call_insn) = ((ecf_flags & ECF_SIBCALL) != 0); /* Restore this now, so that we do defer pops for this call's args @@ -451,6 +442,17 @@ emit_call_1 (rtx funexp, tree fntree ATT else if (!ACCUMULATE_OUTGOING_ARGS && (ecf_flags & ECF_NORETURN) != 0) add_reg_note (call_insn, REG_ARGS_SIZE, GEN_INT (stack_pointer_delta)); + if (ecf_flags & ECF_RETURNS_TWICE) + { + add_reg_note (call_insn, REG_SETJMP, const0_rtx); + cfun->calls_setjmp = 1; + } + + /* The REG_NORETURN note must come before the REG_ARGS_SIZE note for the + fixup_args_size_notes machinery to work correctly. */ + if (ecf_flags & ECF_NORETURN) + add_reg_note (call_insn, REG_NORETURN, const0_rtx); + if (!ACCUMULATE_OUTGOING_ARGS) { /* If returning from the subroutine does not automatically pop the args, Index: combine.c =================================================================== --- combine.c (revision 182780) +++ combine.c (working copy) @@ -13282,7 +13282,13 @@ distribute_notes (rtx notes, rtx from_in { int old_size, args_size = INTVAL (XEXP (note, 0)); old_size = fixup_args_size_notes (PREV_INSN (i3), i3, args_size); - gcc_assert (old_size != args_size); + /* emit_call_1 adds for !ACCUMULATE_OUTGOING_ARGS REG_ARGS_SIZE + note to all noreturn calls, but fixup_args_size_notes cannot + recompute the amount on its own, so the size is unchanged. */ + gcc_assert (old_size != args_size + || (CALL_P (i3) + && !ACCUMULATE_OUTGOING_ARGS + && find_reg_note (i3, REG_NORETURN, NULL_RTX))); } break; Index: expr.c =================================================================== --- expr.c (revision 182780) +++ expr.c (working copy) @@ -3645,9 +3645,11 @@ mem_autoinc_base (rtx mem) (1) One or more auto-inc style memory references (aka pushes), (2) One or more addition/subtraction with the SP as destination, (3) A single move insn with the SP as destination, - (4) A call_pop insn. + (4) A call_pop insn, + (5) Noreturn call insns if !ACCUMULATE_OUTGOING_ARGS. - Insns in the sequence that do not modify the SP are ignored. + Insns in the sequence that do not modify the SP are ignored, + except for noreturn calls. The return value is the amount of adjustment that can be trivially verified, via immediate operand or auto-inc. If the adjustment @@ -3786,23 +3788,42 @@ fixup_args_size_notes (rtx prev, rtx las for (insn = last; insn != prev; insn = PREV_INSN (insn)) { HOST_WIDE_INT this_delta; + rtx note = NULL_RTX; if (!NONDEBUG_INSN_P (insn)) continue; this_delta = find_args_size_adjust (insn); if (this_delta == 0) - continue; + { + /* In the noreturn call case, we need to maintain the invariant + that the REG_NORETURN comes first. So remove it here... */ + if (!CALL_P (insn) + || ACCUMULATE_OUTGOING_ARGS + || (note = find_and_remove_reg_note + (insn, REG_NORETURN, NULL_RTX)) == NULL_RTX) + continue; + } gcc_assert (!saw_unknown); if (this_delta == HOST_WIDE_INT_MIN) saw_unknown = true; add_reg_note (insn, REG_ARGS_SIZE, GEN_INT (args_size)); + + if (this_delta == 0) + { + /* ...and add it back here. */ + XEXP (note, 1) = REG_NOTES (insn); + REG_NOTES (insn) = note; + } + else + { #ifdef STACK_GROWS_DOWNWARD - this_delta = -this_delta; + this_delta = -this_delta; #endif - args_size -= this_delta; + args_size -= this_delta; + } } return saw_unknown ? INT_MIN : args_size; Index: rtlanal.c =================================================================== --- rtlanal.c (revision 182780) +++ rtlanal.c (working copy) @@ -1752,6 +1752,17 @@ find_reg_note (const_rtx insn, enum reg_ return 0; } +/* Likewise, but additionally remove the note. */ + +rtx +find_and_remove_reg_note (rtx insn, enum reg_note kind, const_rtx datum) +{ + rtx note = find_reg_note (insn, kind, datum); + if (note) + remove_note (insn, note); + return note; +} + /* Return the reg-note of kind KIND in insn INSN which applies to register number REGNO, if any. Return 0 if there is no such reg-note. Note that the REGNO of this NOTE need not be REGNO if REGNO is a hard register; Index: rtl.h =================================================================== --- rtl.h (revision 182780) +++ rtl.h (working copy) @@ -1951,6 +1951,7 @@ extern void note_uses (rtx *, void (*) ( extern int dead_or_set_p (const_rtx, const_rtx); extern int dead_or_set_regno_p (const_rtx, unsigned int); extern rtx find_reg_note (const_rtx, enum reg_note, const_rtx); +extern rtx find_and_remove_reg_note (rtx, enum reg_note, const_rtx); extern rtx find_regno_note (const_rtx, enum reg_note, unsigned int); extern rtx find_reg_equal_equiv_note (const_rtx); extern rtx find_constant_src (const_rtx);