Alex Coplan <alex.cop...@arm.com> writes: > Hi, > > This is a v2, original patch is here: > https://gcc.gnu.org/pipermail/gcc-patches/2023-November/637606.html > > This addresses review feedback and: > - Fixes a bug in the previous version in > function_info::finalize_new_accesses; we should now correctly handle > the case where properties.refs () has two writes to a resource and we're > adding a new (temporary) set for that resource. > - Drops some handling for new uses which isn't needed now that RTL-SSA can > infer uses of mem (since g:505f1202e3a1a1aecd0df10d0f1620df6fea4ab5). > > Bootstrapped/regtested as a series on aarch64-linux-gnu, OK for trunk?
OK, thanks. Richard > Thanks, > Alex > > -- >8 -- > > The upcoming aarch64 load pair pass needs to form store pairs, and can > re-order stores over loads when alias analysis determines this is safe. > In the case that both mem defs have uses in the RTL-SSA IR, and both > stores require re-ordering over their uses, we represent that as > (tentative) deletion of the original store insns and creation of a new > insn, to prevent requiring repeated re-parenting of uses during the > pass. We then update all mem uses that require re-parenting in one go > at the end of the pass. > > To support this, RTL-SSA needs to handle inserting new insns (rather > than just changing existing ones), so this patch adds support for that. > > New insns (and new accesses) are temporaries, allocated above a temporary > obstack_watermark, such that the user can easily back out of a change without > awkward bookkeeping. > > gcc/ChangeLog: > > * rtl-ssa/accesses.cc (function_info::create_set): New. > * rtl-ssa/accesses.h (access_info::is_temporary): New. > * rtl-ssa/changes.cc (move_insn): Handle new (temporary) insns. > (function_info::finalize_new_accesses): Handle new/temporary > user-created accesses. > (function_info::apply_changes_to_insn): Ensure m_is_temp flag > on new insns gets cleared. > (function_info::change_insns): Handle new/temporary insns. > (function_info::create_insn): New. > * rtl-ssa/changes.h (class insn_change): Make function_info a > friend class. > * rtl-ssa/functions.h (function_info): Declare new entry points: > create_set, create_insn. Declare new change_alloc helper. > * rtl-ssa/insns.cc (insn_info::print_full): Identify temporary insns > in > dump. > * rtl-ssa/insns.h (insn_info): Add new m_is_temp flag and accompanying > is_temporary accessor. > * rtl-ssa/internals.inl (insn_info::insn_info): Initialize m_is_temp > to > false. > * rtl-ssa/member-fns.inl (function_info::change_alloc): New. > * rtl-ssa/movement.h (restrict_movement_for_defs_ignoring): Add > handling for temporary defs. > > diff --git a/gcc/rtl-ssa/accesses.cc b/gcc/rtl-ssa/accesses.cc > index 510545a8bad..76d70fd8bd3 100644 > --- a/gcc/rtl-ssa/accesses.cc > +++ b/gcc/rtl-ssa/accesses.cc > @@ -1456,6 +1456,16 @@ function_info::make_uses_available (obstack_watermark > &watermark, > return use_array (new_uses, num_uses); > } > > +set_info * > +function_info::create_set (obstack_watermark &watermark, > + insn_info *insn, > + resource_info resource) > +{ > + auto set = change_alloc<set_info> (watermark, insn, resource); > + set->m_is_temp = true; > + return set; > +} > + > // Return true if ACCESS1 can represent ACCESS2 and if ACCESS2 can > // represent ACCESS1. > static bool > diff --git a/gcc/rtl-ssa/accesses.h b/gcc/rtl-ssa/accesses.h > index fce31d46717..7e7a90ece97 100644 > --- a/gcc/rtl-ssa/accesses.h > +++ b/gcc/rtl-ssa/accesses.h > @@ -204,6 +204,10 @@ public: > // in the main instruction pattern. > bool only_occurs_in_notes () const { return m_only_occurs_in_notes; } > > + // Return true if this is a temporary access, e.g. one created for > + // an insn that is about to be inserted. > + bool is_temporary () const { return m_is_temp; } > + > protected: > access_info (resource_info, access_kind); > > diff --git a/gcc/rtl-ssa/changes.cc b/gcc/rtl-ssa/changes.cc > index aab532b9f26..2f2d12d5f30 100644 > --- a/gcc/rtl-ssa/changes.cc > +++ b/gcc/rtl-ssa/changes.cc > @@ -394,14 +394,20 @@ move_insn (insn_change &change, insn_info *after) > // At the moment we don't support moving instructions between EBBs, > // but this would be worth adding if it's useful. > insn_info *insn = change.insn (); > - gcc_assert (after->ebb () == insn->ebb ()); > + > bb_info *bb = after->bb (); > basic_block cfg_bb = bb->cfg_bb (); > > - if (insn->bb () != bb) > - // Force DF to mark the old block as dirty. > - df_insn_delete (rtl); > - ::remove_insn (rtl); > + if (!insn->is_temporary ()) > + { > + gcc_assert (after->ebb () == insn->ebb ()); > + > + if (insn->bb () != bb) > + // Force DF to mark the old block as dirty. > + df_insn_delete (rtl); > + ::remove_insn (rtl); > + } > + > ::add_insn_after (rtl, after_rtl, cfg_bb); > } > > @@ -437,12 +443,33 @@ function_info::finalize_new_accesses (insn_change > &change, insn_info *pos) > { > def_info *def = find_access (change.new_defs, ref.regno); > gcc_assert (def); > + > + if (def->m_is_temp && is_a<set_info *> (def) && def->last_def ()) > + { > + // For temporary sets being added with this change, we keep track of > + // the corresponding permanent def using the last_def link. > + // > + // So if we have one of these, follow it to get the permanent def. > + def = def->last_def (); > + gcc_assert (!def->m_is_temp && !def->m_has_been_superceded); > + } > + > if (def->m_is_temp) > { > - // At present, the only temporary instruction definitions we > - // create are clobbers, such as those added during recog. > - gcc_assert (is_a<clobber_info *> (def)); > - def = allocate<clobber_info> (change.insn (), ref.regno); > + if (is_a<clobber_info *> (def)) > + def = allocate<clobber_info> (change.insn (), ref.regno); > + else if (is_a<set_info *> (def)) > + { > + // Install the permanent set in the last_def link of the > + // temporary def. This allows us to find the permanent def > + // later in case we see a second write to the same resource. > + def_info *perm_def = allocate<set_info> (change.insn (), > + def->resource ()); > + def->set_last_def (perm_def); > + def = perm_def; > + } > + else > + gcc_unreachable (); > } > else if (!def->m_has_been_superceded) > { > @@ -645,6 +672,8 @@ function_info::apply_changes_to_insn (insn_change &change) > > insn->set_accesses (builder.finish ().begin (), num_defs, num_uses); > } > + > + insn->m_is_temp = false; > } > > // Add a temporary placeholder instruction after AFTER. > @@ -677,7 +706,8 @@ function_info::change_insns (array_slice<insn_change *> > changes) > if (!change->is_deletion ()) > { > // Remove any notes that are no longer relevant. > - update_notes (change->rtl ()); > + if (!change->insn ()->m_is_temp) > + update_notes (change->rtl ()); > > // Make sure that the placement of this instruction would still > // leave room for previous instructions. > @@ -686,6 +716,17 @@ function_info::change_insns (array_slice<insn_change *> > changes) > // verify_insn_changes is supposed to make sure that this holds. > gcc_unreachable (); > min_insn = later_insn (min_insn, change->move_range.first); > + > + if (change->insn ()->m_is_temp) > + { > + change->m_insn = allocate<insn_info> (change->insn ()->bb (), > + change->rtl (), > + change->insn_uid ()); > + > + // Set the flag again so subsequent logic is aware. > + // It will be cleared later on. > + change->m_insn->m_is_temp = true; > + } > } > } > > @@ -784,7 +825,8 @@ function_info::change_insns (array_slice<insn_change *> > changes) > // Remove the placeholder first so that we have a wider range of > // program points when inserting INSN. > insn_info *after = placeholder->prev_any_insn (); > - remove_insn (insn); > + if (!insn->is_temporary ()) > + remove_insn (insn); > remove_insn (placeholder); > insn->set_bb (after->bb ()); > add_insn_after (insn, after); > @@ -1105,6 +1147,28 @@ function_info::perform_pending_updates () > return changed_cfg; > } > > +insn_info * > +function_info::create_insn (obstack_watermark &watermark, > + rtx_code insn_code, > + rtx pat) > +{ > + rtx_insn *rti = nullptr; > + > + // TODO: extend, move in to emit-rtl.cc. > + switch (insn_code) > + { > + case INSN: > + rti = make_insn_raw (pat); > + break; > + default: > + gcc_unreachable (); > + } > + > + auto insn = change_alloc<insn_info> (watermark, nullptr, rti, INSN_UID > (rti)); > + insn->m_is_temp = true; > + return insn; > +} > + > // Print a description of CHANGE to PP. > void > rtl_ssa::pp_insn_change (pretty_printer *pp, const insn_change &change) > diff --git a/gcc/rtl-ssa/changes.h b/gcc/rtl-ssa/changes.h > index d56e3a646e2..d91cf432afe 100644 > --- a/gcc/rtl-ssa/changes.h > +++ b/gcc/rtl-ssa/changes.h > @@ -32,6 +32,8 @@ namespace rtl_ssa { > // something that we might do. > class insn_change > { > + friend class function_info; > + > public: > enum delete_action { DELETE }; > > diff --git a/gcc/rtl-ssa/functions.h b/gcc/rtl-ssa/functions.h > index ecb40fdaf57..4ffd3fa44e2 100644 > --- a/gcc/rtl-ssa/functions.h > +++ b/gcc/rtl-ssa/functions.h > @@ -68,6 +68,16 @@ public: > // Return the SSA information for CFG_BB. > bb_info *bb (basic_block cfg_bb) const { return m_bbs[cfg_bb->index]; } > > + // Create a temporary def. > + set_info *create_set (obstack_watermark &watermark, > + insn_info *insn, > + resource_info resource); > + > + // Create a temporary insn with code INSN_CODE and pattern PAT. > + insn_info *create_insn (obstack_watermark &watermark, > + rtx_code insn_code, > + rtx pat); > + > // Return a list of all the instructions in the function, in reverse > // postorder. The list includes both real and artificial instructions. > // > @@ -195,6 +205,10 @@ public: > // Print the contents of the function to PP. > void print (pretty_printer *pp) const; > > + // Allocate an object of type T above the obstack watermark WM. > + template<typename T, typename... Ts> > + T *change_alloc (obstack_watermark &wm, Ts... args); > + > private: > class bb_phi_info; > class build_info; > diff --git a/gcc/rtl-ssa/insns.cc b/gcc/rtl-ssa/insns.cc > index 5fde3f2bb4b..2fa48e0dacd 100644 > --- a/gcc/rtl-ssa/insns.cc > +++ b/gcc/rtl-ssa/insns.cc > @@ -192,6 +192,11 @@ insn_info::print_full (pretty_printer *pp) const > pp_newline_and_indent (pp, 0); > pp_string (pp, "has volatile refs"); > } > + if (m_is_temp) > + { > + pp_newline_and_indent (pp, 0); > + pp_string (pp, "temporary"); > + } > } > pp_indentation (pp) -= 2; > } > diff --git a/gcc/rtl-ssa/insns.h b/gcc/rtl-ssa/insns.h > index a604fe295cd..6d0506706ad 100644 > --- a/gcc/rtl-ssa/insns.h > +++ b/gcc/rtl-ssa/insns.h > @@ -306,6 +306,8 @@ public: > // Print a full description of the instruction. > void print_full (pretty_printer *) const; > > + bool is_temporary () const { return m_is_temp; } > + > private: > // The first-order way of representing the order between instructions > // is to assign "program points", with higher point numbers coming > @@ -414,8 +416,11 @@ private: > unsigned int m_has_pre_post_modify : 1; > unsigned int m_has_volatile_refs : 1; > > + // Indicates the insn is a temporary / new user-allocated insn. > + unsigned int m_is_temp : 1; > + > // For future expansion. > - unsigned int m_spare : 27; > + unsigned int m_spare : 26; > > // The program point at which the instruction occurs. > // > diff --git a/gcc/rtl-ssa/internals.inl b/gcc/rtl-ssa/internals.inl > index e49297c12b3..907c4504352 100644 > --- a/gcc/rtl-ssa/internals.inl > +++ b/gcc/rtl-ssa/internals.inl > @@ -415,6 +415,7 @@ inline insn_info::insn_info (bb_info *bb, rtx_insn *rtl, > int cost_or_uid) > m_is_asm (false), > m_has_pre_post_modify (false), > m_has_volatile_refs (false), > + m_is_temp (false), > m_spare (0), > m_point (0), > m_cost_or_uid (cost_or_uid), > diff --git a/gcc/rtl-ssa/member-fns.inl b/gcc/rtl-ssa/member-fns.inl > index ce2db045b78..b8940ca5566 100644 > --- a/gcc/rtl-ssa/member-fns.inl > +++ b/gcc/rtl-ssa/member-fns.inl > @@ -962,4 +962,16 @@ function_info::add_regno_clobber (obstack_watermark > &watermark, > return true; > } > > +template<typename T, typename... Ts> > +inline T * > +function_info::change_alloc (obstack_watermark &wm, Ts... args) > +{ > + static_assert (std::is_trivially_destructible<T>::value, > + "destructor won't be called"); > + static_assert (alignof (T) <= obstack_alignment, > + "too much alignment required"); > + void *addr = XOBNEW (wm, T); > + return new (addr) T (std::forward<Ts> (args)...); > +} > + > } > diff --git a/gcc/rtl-ssa/movement.h b/gcc/rtl-ssa/movement.h > index ec076db406f..41226dd3666 100644 > --- a/gcc/rtl-ssa/movement.h > +++ b/gcc/rtl-ssa/movement.h > @@ -182,6 +182,11 @@ restrict_movement_for_defs_ignoring (insn_range_info > &move_range, > { > for (def_info *def : defs) > { > + // Skip fresh defs that are being inserted, as these shouldn't > + // constrain movement. > + if (def->is_temporary ()) > + continue; > + > // If the definition is a clobber, we can move it with respect > // to other clobbers. > // > @@ -247,7 +252,8 @@ restrict_movement_for_defs_ignoring (insn_range_info > &move_range, > > // Make sure that we don't move stores between basic blocks, since we > // don't have enough information to tell whether it's safe. > - if (def_info *def = memory_access (defs)) > + def_info *def = memory_access (defs); > + if (def && !def->is_temporary ()) > { > move_range = move_later_than (move_range, def->bb ()->head_insn ()); > move_range = move_earlier_than (move_range, def->bb ()->end_insn ());