On Wed, Jan 11, 2012 at 09:16:04AM +0100, Eric Botcazou wrote: > > And for noreturn calls, it doesn't do anything wrong, the problem is that it > > returns the old size for them. > > According to the head comment, that's precisely the problem, since it should > do > something for them, in particular put back the note.
You're right. So how about this patch (untested so far) instead? In the combiner it still needs to handle the case where REG_NORETURN note hasn't been placed yet, because then fixup_args_size_notes doesn't consider it being a noret call. 2012-01-11 Jakub Jelinek <ja...@redhat.com> PR bootstrap/51796 * combine.c (distribute_notes): If i3 is a noreturn call, allow old_size to be equal to args_size and make sure the noreturn call gets REG_ARGS_SIZE note. * expr.c (fixup_args_size_notes): Put REG_ARGS_SIZE notes on noreturn calls even when the delta is 0. --- gcc/combine.c.jj 2011-12-09 15:21:20.000000000 +0100 +++ gcc/combine.c 2012-01-11 09:37:40.199961939 +0100 @@ -13281,8 +13281,30 @@ distribute_notes (rtx notes, rtx from_in if (!noop_move_p (i3)) { int old_size, args_size = INTVAL (XEXP (note, 0)); + bool noret_call = false; old_size = fixup_args_size_notes (PREV_INSN (i3), i3, args_size); - gcc_assert (old_size != args_size); + if (CALL_P (i3) && !ACCUMULATE_OUTGOING_ARGS) + { + if (find_reg_note (i3, REG_NORETURN, NULL_RTX)) + noret_call = true; + else + { + rtx n; + for (n = next_note; n; n = XEXP (n, 1)) + if (REG_NOTE_KIND (n) == REG_NORETURN) + { + noret_call = true; + break; + } + } + } + gcc_assert (old_size != args_size || noret_call); + /* emit_call_1 adds for !ACCUMULATE_OUTGOING_ARGS + REG_ARGS_SIZE note to all noreturn calls, so ensure + the notes stay on the noreturn call. */ + if (noret_call + && find_reg_note (i3, REG_ARGS_SIZE, NULL_RTX) == NULL_RTX) + place = i3; } break; --- gcc/expr.c.jj 2012-01-02 17:36:53.000000000 +0100 +++ gcc/expr.c 2012-01-11 09:30:08.549680295 +0100 @@ -3642,9 +3642,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 @@ -3789,7 +3791,12 @@ fixup_args_size_notes (rtx prev, rtx las this_delta = find_args_size_adjust (insn); if (this_delta == 0) - continue; + { + if (!CALL_P (insn) + || ACCUMULATE_OUTGOING_ARGS + || find_reg_note (insn, REG_NORETURN, NULL_RTX) == NULL_RTX) + continue; + } gcc_assert (!saw_unknown); if (this_delta == HOST_WIDE_INT_MIN) --- gcc/testsuite/gcc.dg/pr51796.c.jj 2012-01-10 16:43:00.494803970 +0100 +++ gcc/testsuite/gcc.dg/pr51796.c 2012-01-10 16:43:00.494803970 +0100 @@ -0,0 +1,15 @@ +/* PR bootstrap/51796 */ +/* { dg-do compile } */ +/* { dg-options "-Os -fno-omit-frame-pointer -fno-tree-dominator-opts -fno-tree-fre -fno-tree-pre" } */ + +typedef void (*entry_func) (void) __attribute__ ((noreturn)); +extern entry_func entry_addr; +static void bsd_boot_entry (void) +{ + stop (); +} +void bsd_boot (void) +{ + entry_addr = (entry_func) bsd_boot_entry; + (*entry_addr) (); +} Jakub