On Thu, 21 Apr 2022, Richard Biener wrote: > On Wed, 20 Apr 2022, Segher Boessenkool wrote: > > > Hi! > > > > This looks great :-) > > > > On Wed, Apr 20, 2022 at 03:52:33PM +0200, Richard Biener wrote: > > > The following mitigates a problem in combine distribute_notes which > > > places an original REG_EH_REGION based on only may_trap_p which is > > > good to test whether a non-call insn can possibly throw but not if > > > actually it does or we care. That's something we decided at RTL > > > expansion time where we possibly still know the insn evaluates > > > to a constant. > > > > > > In fact, the REG_EH_REGION note with lp > 0 can only come from the > > > original i3 and an assert is added to that effect. That means we only > > > need to retain the note on i3 or, if that cannot trap, drop it but we > > > should never move it to i2. > > > > > > For REG_EH_REGION corresponding to must-not-throw regions or > > > nothrow marking try_combine gets new code ensuring we can merge > > > and distribute notes which means placing must-not-throw notes > > > on all result insns, and dropping nothrow notes or preserve > > > them on i3 for calls. > > > > > * combine.cc (distribute_notes): Assert that a REG_EH_REGION > > > with landing pad > 0 is from i3 and only keep it there or drop > > > it if the insn can not trap. Throw away REG_EH_REGION with > > > landing pad = 0 or INT_MIN if it does not originate from a > > > call i3. Distribute must-not-throw REG_EH_REGION to all > > > resulting instructions. > > > (try_combine): Ensure that we can merge REG_EH_REGION notes. > > > > > --- a/gcc/combine.cc > > > +++ b/gcc/combine.cc > > > @@ -2951,6 +2951,45 @@ try_combine (rtx_insn *i3, rtx_insn *i2, rtx_insn > > > *i1, rtx_insn *i0, > > > return 0; > > > } > > > > > > + /* When i3 transfers to an EH handler we cannot combine if any of the > > > + sources are within a must-not-throw region. Else we can throw away > > > + any nothrow, pick a random must-not-throw region or preserve the EH > > > + transfer on i3. Since we want to preserve nothrow notes on calls > > > + we have to avoid combining from must-not-throw stmts there as well. > > > + This has to be kept in sync with distribute_note. */ > > > + if (rtx i3_eh = find_reg_note (i3, REG_EH_REGION, NULL_RTX)) > > > + { > > > + int i3_lp_nr = INTVAL (XEXP (i3_eh, 0)); > > > + if (i3_lp_nr > 0 > > > + || ((i3_lp_nr == 0 || i3_lp_nr == INT_MIN) && CALL_P (i3))) > > > + { > > > + rtx eh; > > > + int eh_lp; > > > + if (((eh = find_reg_note (i2, REG_EH_REGION, NULL_RTX)) > > > + && (eh_lp = INTVAL (XEXP (eh, 0))) < 0 > > > + && eh_lp != INT_MIN) > > > + || (i2 > > > + && (eh = find_reg_note (i2, REG_EH_REGION, NULL_RTX)) > > > + && (eh_lp = INTVAL (XEXP (eh, 0))) < 0 > > > + && eh_lp != INT_MIN) > > > + || (i1 > > > + && (eh = find_reg_note (i1, REG_EH_REGION, NULL_RTX)) > > > + && (eh_lp = INTVAL (XEXP (eh, 0))) < 0 > > > + && eh_lp != INT_MIN) > > > + || (i0 > > > + && (eh = find_reg_note (i0, REG_EH_REGION, NULL_RTX)) > > > + && (eh_lp = INTVAL (XEXP (eh, 0))) < 0 > > > + && eh_lp != INT_MIN)) > > > + { > > > + if (dump_file && (dump_flags & TDF_DETAILS)) > > > + fprintf (dump_file, "Can't combine insn in must-not-throw " > > > + "EH region into i3 which can throws\n"); > > > + undo_all (); > > > + return 0; > > > + } > > > + } > > > + } > > > > The assignments in the conditionals make this hard to read, and harder > > to change, btw. A utility function wouldn't hurt? The problem of > > course would be thinking of a good name for it :-) > > I've added insn_must_not_throw_p to except.{cc,h}. > > > > case REG_EH_REGION: > > > - /* These notes must remain with the call or trapping instruction. */ > > > - if (CALL_P (i3)) > > > - place = i3; > > > - else if (i2 && CALL_P (i2)) > > > - place = i2; > > > - else > > > - { > > > - gcc_assert (cfun->can_throw_non_call_exceptions); > > > - if (may_trap_p (i3)) > > > - place = i3; > > > - else if (i2 && may_trap_p (i2)) > > > - place = i2; > > > - /* ??? Otherwise assume we've combined things such that we > > > - can now prove that the instructions can't trap. Drop the > > > - note in this case. */ > > > - } > > > - break; > > > + { > > > + /* This handling needs to be kept in sync with the > > > + prerequesite checking in try_combine. */ > > > > (prerequisite) > > Fixed. > > > > + int lp_nr = INTVAL (XEXP (note, 0)); > > > + /* A REG_EH_REGION note transfering control can only ever come > > > + from i3 and it has to stay there. */ > > > + if (lp_nr > 0) > > > + { > > > + gcc_assert (from_insn == i3); > > > + if (CALL_P (i3)) > > > + place = i3; > > > + else > > > + { > > > + gcc_assert (cfun->can_throw_non_call_exceptions); > > > + /* If i3 can still trap preserve the note, otherwise we've > > > + combined things such that we can now prove that the > > > + instructions can't trap. Drop the note in this case. */ > > > + if (may_trap_p (i3)) > > > + place = i3; > > > + } > > > + } > > > > It isn't immediately clear to me that we cannot have moved a trapping > > thing to i2? Do we guarantee that somewhere? Can we / should we assert > > that here? gcc_assert (!(i2 && may_trap_p (i2))) or something like > > that. > > IMHO asserts for catching things we don't handle are bad. I suppose > I could add sth like > > @@ -3724,7 +3712,12 @@ try_combine (rtx_insn *i3, rtx_insn *i2, rtx_insn > *i1, rt > x_insn *i0, > || !modified_between_p (*split, i2, i3)) > /* We can't overwrite I2DEST if its value is still used by > NEWPAT. */ > - && ! reg_referenced_p (i2dest, newpat)) > + && ! reg_referenced_p (i2dest, newpat) > + /* We should not split a possibly trapping part of a throwing > + insn. */ > + && (!cfun->can_throw_non_call_exceptions > + || !can_throw_internal (i3) > + || !may_trap_p (*split))) > { > rtx newdest = i2dest; > enum rtx_code split_code = GET_CODE (*split); > > but then without a testcase it's hard to see if this is to the point. > It of course also assumes we're never going to split the call part > from i3 to i2. > > It should be at least safe and with that we could add your assert, > sligthly altered to > > gcc_assert (!(cfun->can_throw_non_call_exceptions > && i2 && may_trap_p (i2))); > > because we have to guard against spltting say a trapping memory > load out from a CALL_P, but only with non-call EH (the call > might not throw but a (pre-existing!) memory load could trap).
So that doesn't work, it ICEs FAIL: c52103k FAIL: c52103p FAIL: c52104p FAIL: c95089a FAIL: cxa4034 that means the /* If we can split it and use I2DEST, go ahead and see if that helps things be recognized. Verify that none of the registers are set between I2 and I3. */ if (insn_code_number < 0 && (split = find_split_point (&newpat, i3, false)) != 0 ... case isn't the only place we can split i2 (as seen in distribute_notes) from i3? This is for example Trying 901, 902 -> 903: 901: r556:DI=0x9 902: {r555:DI=r556:DI-r132:DI;clobber flags:CC;} REG_DEAD r556:DI REG_DEAD r132:DI REG_UNUSED flags:CC REG_EQUAL 0x9-r132:DI 903: r256:QI=[r234:DI+r555:DI] REG_DEAD r555:DI REG_DEAD r234:DI REG_EH_REGION 0x9 Successfully matched this instruction: (set (reg:DI 555) (minus:DI (reg/f:DI 234 [ arrx52.69 ]) (reg:DI 132 [ _58 ]))) Successfully matched this instruction: (set (reg:QI 256 [ _320 ]) (mem/j:QI (plus:DI (reg:DI 555) (const_int 9 [0x9])) [11 (*arrx52.69_259)[9]{lb: _58 sz: 1}+0 S1 A8])) where it's probably overzealeous, and just because we have at this point (insn 902 901 903 134 (parallel [ (set (reg:DI 555) (minus:DI (reg/f:DI 234 [ arrx52.69 ]) (reg:DI 132 [ _58 ]))) (clobber (reg:CC 17 flags)) ]) "c52104p.adb":273:44 303 {*subdi_1} (expr_list:REG_DEAD (reg/f:DI 234 [ arrx52.69 ]) (nil))) and may_trap_p does case EXPR_LIST: /* An EXPR_LIST is used to represent a function call. This certainly may trap. */ return 1; so in this case for -fnon-call-exceptions we are going to reject a lot of splits - but we actually don't (because the may_trap_p check on the split point fails). I suppose for the assert we'd rather want to check may_trap_p (PATTERN (i2)) then? Do we want to do this for the other (non-call?) cases as well? Changing the assert to gcc_assert (!(cfun->can_throw_non_call_exceptions && i2 && may_trap_p (PATTERN (i2)))); fixes the ICE (just checked one of the testcases). I'm going to re-check with this, but given may_trap_p is looking to err on the trapping side too easily maybe an assert isn't a good idea. Richard.