On Fri, Apr 16, 2021 at 12:02 PM Richard Sandiford via Gcc-patches <gcc-patches@gcc.gnu.org> wrote: > > This patch fixes a regression introduced by the rtl-ssa patches. > It was seen on HPPA but it might be latent elsewhere. > > The problem is that the traditional way of expanding an untyped_call > is to emit sequences like: > > (call (mem (symbol_ref "foo"))) > (set (reg pseudo1) (reg result1)) > ... > (set (reg pseudon) (reg resultn)) > > The ABI specifies that result1..resultn are clobbered by the call but > nothing in the RTL indicates that result1..resultn are the results > of the call. Normally, using a clobbered value gives undefined results, > but in this case the results are well-defined and matter for correctness. > > This seems like a niche case, so I think it would be better to mark > it explicitly rather than try to detect it heuristically. > > Note that in expand_builtin_apply we already have an rtx_insn *, > so it doesn't matter whether we call emit_call_insn or emit_insn. > Calling emit_insn seems more natural now that the gen_* call > has been split out. It also matches later code in the function. > > Tested on aarch64-linux-gnu, arm-linux-gnueabihf, armeb-eabi and > x86_64-linux-gnu. OK to install?
OK. Does DF handle this correctly? I wonder why we cannot simply do sth like (call_insn (set (parallel [ (reg:...) (reg:...) ] ) (mem (symbol_ref...))) or so? But I guess we'd have to alter all targets for this... Thanks, Richard. > Richard > > > gcc/ > PR rtl-optimization/98689 > * reg-notes.def (UNTYPED_CALL): New note. > * combine.c (distribute_notes): Handle it. > * emit-rtl.c (try_split): Likewise. > * rtlanal.c (rtx_properties::try_to_add_insn): Likewise. Assume > that calls with the note implicitly set all return value registers. > * builtins.c (expand_builtin_apply): Add a REG_UNTYPED_CALL > to untyped_calls. > --- > gcc/builtins.c | 8 ++++++-- > gcc/combine.c | 1 + > gcc/emit-rtl.c | 1 + > gcc/reg-notes.def | 15 +++++++++++++++ > gcc/rtlanal.c | 9 +++++++++ > 5 files changed, 32 insertions(+), 2 deletions(-) > > diff --git a/gcc/builtins.c b/gcc/builtins.c > index 196dda3fa5e..d30c4eb62fc 100644 > --- a/gcc/builtins.c > +++ b/gcc/builtins.c > @@ -2490,8 +2490,12 @@ expand_builtin_apply (rtx function, rtx arguments, rtx > argsize) > if (targetm.have_untyped_call ()) > { > rtx mem = gen_rtx_MEM (FUNCTION_MODE, function); > - emit_call_insn (targetm.gen_untyped_call (mem, result, > - result_vector (1, result))); > + rtx_insn *seq = targetm.gen_untyped_call (mem, result, > + result_vector (1, result)); > + for (rtx_insn *insn = seq; insn; insn = NEXT_INSN (insn)) > + if (CALL_P (insn)) > + add_reg_note (insn, REG_UNTYPED_CALL, NULL_RTX); > + emit_insn (seq); > } > else if (targetm.have_call_value ()) > { > diff --git a/gcc/combine.c b/gcc/combine.c > index 6cb5b0ca102..9063a07bd00 100644 > --- a/gcc/combine.c > +++ b/gcc/combine.c > @@ -14337,6 +14337,7 @@ distribute_notes (rtx notes, rtx_insn *from_insn, > rtx_insn *i3, rtx_insn *i2, > case REG_SETJMP: > case REG_TM: > case REG_CALL_DECL: > + case REG_UNTYPED_CALL: > case REG_CALL_NOCF_CHECK: > /* These notes must remain with the call. It should not be > possible for both I2 and I3 to be a call. */ > diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c > index 1113c58c49e..07e908624a0 100644 > --- a/gcc/emit-rtl.c > +++ b/gcc/emit-rtl.c > @@ -3964,6 +3964,7 @@ try_split (rtx pat, rtx_insn *trial, int last) > break; > > case REG_CALL_DECL: > + case REG_UNTYPED_CALL: > gcc_assert (call_insn != NULL_RTX); > add_reg_note (call_insn, REG_NOTE_KIND (note), XEXP (note, 0)); > break; > diff --git a/gcc/reg-notes.def b/gcc/reg-notes.def > index d7f0bfdab0b..995052ebc28 100644 > --- a/gcc/reg-notes.def > +++ b/gcc/reg-notes.def > @@ -233,6 +233,21 @@ REG_NOTE (STACK_CHECK) > insn. This note is a SYMBOL_REF. */ > REG_NOTE (CALL_DECL) > > +/* Indicates that the call is an untyped_call. These calls are special > + in that they set all of the target ABI's return value registers to a > + defined value without explicitly saying so. For example, a typical > + untyped_call sequence has the form: > + > + (call (mem (symbol_ref "foo"))) > + (set (reg pseudo1) (reg result1)) > + ... > + (set (reg pseudon) (reg resultn)) > + > + The ABI specifies that result1..resultn are clobbered by the call, > + but the RTL does not indicate that result1..resultn are the results > + of the call. */ > +REG_NOTE (UNTYPED_CALL) > + > /* Indicate that a call should not be verified for control-flow consistency. > The target address of the call is assumed as a valid address and no check > to validate a branch to the target address is needed. The call is marked > diff --git a/gcc/rtlanal.c b/gcc/rtlanal.c > index 170420a610b..67a49e65fd8 100644 > --- a/gcc/rtlanal.c > +++ b/gcc/rtlanal.c > @@ -2333,6 +2333,15 @@ rtx_properties::try_to_add_insn (const rtx_insn *insn, > bool include_notes) > *ref_iter++ = rtx_obj_reference (regno, flags, > reg_raw_mode[regno], 0); > } > + /* Untyped calls implicitly set all function value registers. > + Again, we add them first in case the main pattern contains > + a fixed-form clobber. */ > + if (find_reg_note (insn, REG_UNTYPED_CALL, NULL_RTX)) > + for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno) > + if (targetm.calls.function_value_regno_p (regno) > + && ref_iter != ref_end) > + *ref_iter++ = rtx_obj_reference (regno, rtx_obj_flags::IS_WRITE, > + reg_raw_mode[regno], 0); > if (ref_iter != ref_end && !RTL_CONST_CALL_P (insn)) > { > auto mem_flags = rtx_obj_flags::IS_READ;