On Fri, 11 Oct 2024, Richard Sandiford wrote: > Alex Coplan <alex.cop...@arm.com> writes: > > Hi, > > > > As the PR shows, pair-fusion was tricking memory_modified_in_insn_p into > > returning false when a common base register (in this case, x1) was > > modified between the mem and the store insn. This lead to wrong code as > > the accesses really did alias. > > > > To avoid this sort of problem, this patch avoids invoking RTL alias > > analysis altogether (and assume an alias conflict) if the two insns to > > be compared share a common address register R, and the insns see different > > definitions of R (i.e. it was modified in between). > > > > Bootstrapped/regtested on aarch64-linux-gnu (all languages, both regular > > bootstrap and LTO+PGO bootstrap). OK for trunk? > > Sorry for the slow review. The patch looks good to me, but... > > > @@ -2544,11 +2624,37 @@ pair_fusion_bb_info::try_fuse_pair (bool load_p, > > unsigned access_size, > > && bitmap_bit_p (&m_tombstone_bitmap, insn->uid ()); > > }; > > > > + // Maximum number of distinct regnos we expect to appear in a single > > + // MEM (and thus in a candidate insn). > > + static constexpr int max_mem_regs = 2; > > + auto_vec<access_info *, max_mem_regs> addr_use_vec[2]; > > + use_array addr_uses[2]; > > + > > + // Collect the lists of register uses that occur in the candidate MEMs. > > + for (int i = 0; i < 2; i++) > > + { > > + // N.B. it's safe for us to ignore uses that only occur in notes > > + // here (e.g. in a REG_EQUIV expression) since we only pass the > > + // MEM down to the alias machinery, so it can't see any insn-level > > + // notes. > > + for (auto use : insns[i]->uses ()) > > + if (use->is_reg () > > + && use->includes_address_uses () > > + && !use->only_occurs_in_notes ()) > > + { > > + gcc_checking_assert (addr_use_vec[i].length () < max_mem_regs); > > + addr_use_vec[i].quick_push (use); > > ...if possible, I think it would be better to just use safe_push here, > without the assert. There'd then be no need to split max_mem_regs out; > it could just be hard-coded in the addr_use_vec declaration. > > Or does that not work for some reason? I'm getting a sense of deja vu...
safe_push should work but as I understand the desire is to rely on fully on-stack pre-allocated vectors? > If it doesn't work, an alternative would be to use access_array_builder. > > OK for trunk and backports if using safe_push works. > > Thanks, > Richard > > > + } > > + addr_uses[i] = use_array (addr_use_vec[i]); > > + } > > + > > > > store_walker<false, decltype(tombstone_p)> > > - forward_store_walker (mem_defs[0], cand_mems[0], insns[1], > > tombstone_p); > > + forward_store_walker (mem_defs[0], cand_mems[0], addr_uses[0], > > insns[1], > > + tombstone_p); > > > > store_walker<true, decltype(tombstone_p)> > > - backward_store_walker (mem_defs[1], cand_mems[1], insns[0], > > tombstone_p); > > + backward_store_walker (mem_defs[1], cand_mems[1], addr_uses[1], > > insns[0], > > + tombstone_p); > > > > alias_walker *walkers[4] = {}; > > if (mem_defs[0]) > > @@ -2562,8 +2668,10 @@ pair_fusion_bb_info::try_fuse_pair (bool load_p, > > unsigned access_size, > > { > > // We want to find any loads hanging off the first store. > > mem_defs[0] = memory_access (insns[0]->defs ()); > > - load_walker<false> forward_load_walker (mem_defs[0], insns[0], > > insns[1]); > > - load_walker<true> backward_load_walker (mem_defs[1], insns[1], > > insns[0]); > > + load_walker<false> forward_load_walker (mem_defs[0], insns[0], > > + addr_uses[0], insns[1]); > > + load_walker<true> backward_load_walker (mem_defs[1], insns[1], > > + addr_uses[1], insns[0]); > > walkers[2] = &forward_load_walker; > > walkers[3] = &backward_load_walker; > > m_pass->do_alias_analysis (alias_hazards, walkers, load_p); > > diff --git a/gcc/testsuite/g++.dg/torture/pr116783.C > > b/gcc/testsuite/g++.dg/torture/pr116783.C > > new file mode 100644 > > index 00000000000..6d59159459d > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/torture/pr116783.C > > @@ -0,0 +1,98 @@ > > +// { dg-do run } > > +// { dg-additional-options "-fstack-protector-strong > > -fno-late-combine-instructions" } > > +// { dg-require-effective-target fstack_protector } > > +// { dg-require-effective-target c++11 } > > + > > +struct Private { > > + char data[24]{}; > > + long moved_from : 4; > > + Private() : moved_from (0) {} > > +}; > > + > > +struct QVariant { > > + __attribute__((noipa)) > > + ~QVariant() { > > + if (!d.moved_from && d.data[0] != 42) > > + __builtin_abort (); > > + } > > + __attribute__((noipa)) > > + QVariant() { > > + d.data[0] = 42; > > + } > > + __attribute__((noipa)) > > + QVariant(QVariant &other) : d(other.d) {} > > + QVariant(QVariant &&other) : d(other.d) { > > + other.d = Private(); > > + other.d.moved_from = true; > > + } > > + QVariant &operator=(QVariant); > > + Private d; > > +}; > > + > > +QVariant id (QVariant v) { return v; } > > +QVariant &QVariant::operator=(QVariant other) > > +{ > > + id(other); > > + return *this; > > +} > > + > > +template <typename T> struct QList { > > + T d; > > + struct const_iterator { > > + T *ptr; > > + T &operator*() { return *ptr; } > > + __attribute__((noipa)) > > + bool operator!=(const_iterator other) { return ptr != other.ptr; } > > + void operator++() { ptr++; } > > + }; > > + __attribute__((noipa)) > > + T at() { return d; } > > + const_iterator begin() { return const_iterator { &d }; } > > + const_iterator end() { return const_iterator { &d }; } > > +}; > > +struct QArrayDataPointer { > > + int d; > > + int *ptr; > > + long size; > > +}; > > + > > +QArrayDataPointer null_qadp; > > + > > +struct QString { > > + __attribute__((noipa)) > > + QList<QString> split() const { > > + return QList<QString> {null_qadp}; > > + } > > + __attribute__((noipa)) > > + friend bool operator==(QString, QString) { return true; } > > + > > + __attribute__((noipa)) > > + QString(QArrayDataPointer dp) : d(dp) {} > > + QArrayDataPointer d; > > +}; > > + > > +__attribute__((noipa)) > > +QString as_qstr (QVariant *v) > > +{ > > + return QString (null_qadp); > > +} > > + > > +int *getNode(const QString &tagContent) { > > + auto expr = tagContent.split(); > > + auto blockName = expr.at(); > > + auto loadedBlocksVariant = QVariant (); > > + QList<QVariant> blockVariantList; > > + for (auto &item : blockVariantList) { > > + auto blockNodeName = as_qstr (&item); > > + blockNodeName == blockName; > > + QString q(null_qadp); > > + } > > + loadedBlocksVariant = QVariant(); > > + return nullptr; > > +} > > + > > +int main(void) > > +{ > > + QString foo(null_qadp); > > + getNode(foo); > > +} > -- Richard Biener <rguent...@suse.de> SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg, Germany; GF: Ivo Totev, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)