On 29/01/2025 18:46, Richard Sandiford wrote:
> As Andrew says in the bugzilla comments, this PR is about a case where
> we tried to fuse two stores of x0, one in which x0 was defined and one
> in which it was undefined. merge_access_arrays failed on the conflict,
> but the failure wasn't caught.
>
> Normally the hazard detection code would fail if the instructions
> had incompatible uses. However, an undefined use doesn't impose
> many restrictions on movements. I think this is likely to be the
> only case where hazard detection isn't enough.
>
> As Andrew notes in bugzilla, it might be possible to allow uses
> of defined and undefined values to be merged to the defined value.
> But that sounds dangerous in the general case, as an rtl-ssa-level
> decision. We might run the risk of turning conditional UB into
> unconditional UB. And LLVM proves that the definition of "undef"
> isn't simple.
>
> Tested on aarch64-linux-gnu. OK to install?
Thanks for taking care of this. LGTM, but I have a question below, just
for my own understanding ...
>
> Richard
>
>
> gcc/
> PR rtl-optimization/118320
> * pair-fusion.cc (pair_fusion_bb_info::fuse_pair): Commonize
> the merge of input_uses and return early if it fails.
>
> gcc/testsuite/
> PR rtl-optimization/118320
> * g++.dg/torture/pr118320.C: New test.
> ---
> gcc/pair-fusion.cc | 32 ++++++++++++++++---------
> gcc/testsuite/g++.dg/torture/pr118320.C | 15 ++++++++++++
> 2 files changed, 36 insertions(+), 11 deletions(-)
> create mode 100644 gcc/testsuite/g++.dg/torture/pr118320.C
>
> diff --git a/gcc/pair-fusion.cc b/gcc/pair-fusion.cc
> index 602e572ab6c..5708d0f3b67 100644
> --- a/gcc/pair-fusion.cc
> +++ b/gcc/pair-fusion.cc
> @@ -1730,6 +1730,24 @@ pair_fusion_bb_info::fuse_pair (bool load_p,
> input_uses[i] = remove_note_accesses (attempt, input_uses[i]);
> }
>
> + // Get the uses that the pair instruction would have, and punt if
> + // the unpaired instructions use different definitions of the same
> + // register. That would normally be caught as a side-effect of
> + // hazard detection below, but this check also deals with cases
> + // in which one use is undefined and the other isn't.
> + auto new_uses = merge_access_arrays (attempt,
> + drop_memory_access (input_uses[0]),
> + drop_memory_access (input_uses[1]));
> + if (!new_uses.is_valid ())
> + {
> + if (dump_file)
> + fprintf (dump_file,
> + " load pair: i%d and i%d use different definiitions of"
... how do we know that this is a load pair here? Could this not in
theory trigger for stores too?
Thanks,
Alex
> + " the same register\n",
> + insns[0]->uid (), insns[1]->uid ());
> + return false;
> + }
> +
> // Edge case: if the first insn is a writeback load and the
> // second insn is a non-writeback load which transfers into the base
> // register, then we should drop the writeback altogether as the
> @@ -1852,11 +1870,7 @@ pair_fusion_bb_info::fuse_pair (bool load_p,
> input_defs[1]);
> gcc_assert (pair_change->new_defs.is_valid ());
>
> - pair_change->new_uses
> - = merge_access_arrays (attempt,
> - drop_memory_access (input_uses[0]),
> - drop_memory_access (input_uses[1]));
> - gcc_assert (pair_change->new_uses.is_valid ());
> + pair_change->new_uses = new_uses;
> set_pair_pat (pair_change);
> }
> else
> @@ -1877,9 +1891,7 @@ pair_fusion_bb_info::fuse_pair (bool load_p,
> case Action::CHANGE:
> {
> set_pair_pat (change);
> - change->new_uses = merge_access_arrays (attempt,
> - input_uses[0],
> - input_uses[1]);
> + change->new_uses = new_uses;
> auto d1 = drop_memory_access (input_defs[0]);
> auto d2 = drop_memory_access (input_defs[1]);
> change->new_defs = merge_access_arrays (attempt, d1, d2);
> @@ -1907,9 +1919,7 @@ pair_fusion_bb_info::fuse_pair (bool load_p,
> auto new_insn = crtl->ssa->create_insn (attempt, INSN, pair_pat);
> change = make_change (new_insn);
> change->move_range = move_range;
> - change->new_uses = merge_access_arrays (attempt,
> - input_uses[0],
> - input_uses[1]);
> + change->new_uses = new_uses;
> gcc_assert (change->new_uses.is_valid ());
>
> auto d1 = drop_memory_access (input_defs[0]);
> diff --git a/gcc/testsuite/g++.dg/torture/pr118320.C
> b/gcc/testsuite/g++.dg/torture/pr118320.C
> new file mode 100644
> index 00000000000..228d79860b5
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/torture/pr118320.C
> @@ -0,0 +1,15 @@
> +// { dg-additional-options "-fno-tree-sra" } */
> +
> +struct T{
> + long f[2];
> +};
> +void f1(int);
> +void g(T&);
> +void f1()
> +{
> + T a;
> + a.~T(); // To force the clobber
> + a.f[1] = 1;
> + T b = a;
> + g(b);
> +}
> --
> 2.25.1
>