Viewing file: access-utils.h (17.01 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
// Access-related utilities for RTL SSA -*- C++ -*- // Copyright (C) 2020-2022 Free Software Foundation, Inc. // // This file is part of GCC. // // GCC is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free // Software Foundation; either version 3, or (at your option) any later // version. // // GCC is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // // You should have received a copy of the GNU General Public License // along with GCC; see the file COPYING3. If not see // <http://www.gnu.org/licenses/>.
namespace rtl_ssa {
// Return a referene to the whole of register REGNO. inline resource_info full_register (unsigned int regno) { return { GET_MODE (regno_reg_rtx[regno]), regno }; }
// Return true if sorted array ACCESSES includes an access to hard registers. inline bool accesses_include_hard_registers (const access_array &accesses) { return accesses.size () && HARD_REGISTER_NUM_P (accesses.front ()->regno ()); }
// Return true if sorted array ACCESSES includes an access to memory. inline bool accesses_include_memory (const access_array &accesses) { return accesses.size () && accesses.back ()->is_mem (); }
// If sorted array ACCESSES includes an access to memory, return the access, // otherwise return null. template<typename T> inline auto memory_access (T accesses) -> decltype (accesses[0]) { if (accesses.size () && accesses.back ()->is_mem ()) return accesses.back (); return nullptr; }
// If sorted array ACCESSES includes a reference to REGNO, return the // access, otherwise return null. template<typename T> inline auto find_access (T accesses, unsigned int regno) -> decltype (accesses[0]) { unsigned int start = 0; unsigned int end = accesses.size (); while (start < end) { unsigned int mid = (start + end) / 2; unsigned int found = accesses[mid]->regno (); if (found == regno) return accesses[mid]; if (found < regno) start = mid + 1; else end = mid; } return nullptr; }
// If sorted array ACCESSES includes a reference to REGNO, return the // index of the access, otherwise return -1. inline int find_access_index (access_array accesses, unsigned int regno) { unsigned int start = 0; unsigned int end = accesses.size (); while (start < end) { unsigned int mid = (start + end) / 2; unsigned int found = accesses[mid]->regno (); if (found == regno) return mid; if (found < regno) start = mid + 1; else end = mid; } return -1; }
// If ACCESS is a set whose result is used by at least one instruction, // return the access as a set_info, otherwise return null. inline const set_info * set_with_nondebug_insn_uses (const access_info *access) { if (access->is_set_with_nondebug_insn_uses ()) // No need for as_a; this test is just as definitive. return static_cast<const set_info *> (access); return nullptr; }
// A non-const version of the above. inline set_info * set_with_nondebug_insn_uses (access_info *access) { if (access->is_set_with_nondebug_insn_uses ()) return static_cast<set_info *> (access); return nullptr; }
// Return true if SET is the only set of SET->resource () and if it // dominates all uses (excluding uses of SET->resource () at points // where SET->resource () is always undefined). inline bool is_single_dominating_def (const set_info *set) { return set->is_first_def () && set->is_last_def (); }
// SET is known to be available on entry to BB. Return true if it is // also available on exit from BB. (The value might or might not be live.) inline bool remains_available_on_exit (const set_info *set, bb_info *bb) { return (set->is_last_def () || *set->next_def ()->insn () > *bb->end_insn ()); }
// ACCESS is known to be associated with an instruction rather than // a phi node. Return which instruction that is. inline insn_info * access_insn (const access_info *access) { // In release builds this function reduces to a single pointer reference. if (auto *def = dyn_cast<const def_info *> (access)) return def->insn (); return as_a<const use_info *> (access)->insn (); }
// If ACCESS records a use, return the value that it uses. If ACCESS records // a set, return that set. If ACCESS records a clobber, return null. inline const set_info * access_value (const access_info *access) { if (!access) return nullptr;
if (auto *use = dyn_cast<const use_info *> (access)) return use->def ();
return dyn_cast<const set_info *> (access); }
// A non-const version of the above. inline set_info * access_value (access_info *access) { auto *const_access = const_cast<const access_info *> (access); return const_cast<set_info *> (access_value (const_access)); }
// If ACCESS is a degenerate phi, return the set_info that defines its input, // otherwise return ACCESS itself. template<typename T> inline const T * look_through_degenerate_phi (const T *access) { if (auto *phi = dyn_cast<const phi_info *> (access)) if (phi->is_degenerate ()) return phi->input_value (0); return access; }
// A non-const version of the above. template<typename T> inline T * look_through_degenerate_phi (T *access) { auto *const_access = const_cast<const T *> (access); return const_cast<T *> (look_through_degenerate_phi (const_access)); }
// If CLOBBER is in a group, return the first clobber in the group, // otherwise return CLOBBER itself. inline clobber_info * first_clobber_in_group (clobber_info *clobber) { if (clobber->is_in_group ()) return clobber->group ()->first_clobber (); return clobber; }
// If CLOBBER is in a group, return the last clobber in the group, // otherwise return CLOBBER itself. inline clobber_info * last_clobber_in_group (clobber_info *clobber) { if (clobber->is_in_group ()) return clobber->group ()->last_clobber (); return clobber; }
// If DEF is a clobber in a group, return the containing group, // otherwise return DEF. inline def_mux clobber_group_or_single_def (def_info *def) { if (auto *clobber = dyn_cast<clobber_info *> (def)) if (clobber->is_in_group ()) return clobber->group (); return def; }
// Return the first definition associated with NODE. If NODE holds // a single set, the result is that set. If NODE holds a clobber_group, // the result is the first clobber in the group. inline def_info * first_def (def_node *node) { return node->first_def (); }
// Likewise for something that is either a node or a single definition. inline def_info * first_def (def_mux mux) { return mux.first_def (); }
// Return the last definition associated with NODE. If NODE holds // a single set, the result is that set. If NODE holds a clobber_group, // the result is the last clobber in the group. inline def_info * last_def (def_node *node) { if (auto *group = dyn_cast<clobber_group *> (node)) return group->last_clobber (); return node->first_def (); }
// Likewise for something that is either a node or a single definition. inline def_info * last_def (def_mux mux) { return mux.last_def (); }
int lookup_use (splay_tree<use_info *> &, insn_info *); int lookup_def (def_splay_tree &, insn_info *); int lookup_clobber (clobber_tree &, insn_info *); int lookup_call_clobbers (insn_call_clobbers_tree &, insn_info *);
// Search backwards from immediately before INSN for the first instruction // recorded in TREE, ignoring any instruction I for which IGNORE (I) is true. // Return null if no such instruction exists. template<typename IgnorePredicate> insn_info * prev_call_clobbers_ignoring (insn_call_clobbers_tree &tree, insn_info *insn, IgnorePredicate ignore) { if (!tree) return nullptr;
int comparison = lookup_call_clobbers (tree, insn); while (comparison <= 0 || ignore (tree->insn ())) { if (!tree.splay_prev_node ()) return nullptr;
comparison = 1; } return tree->insn (); }
// Search forwards from immediately after INSN for the first instruction // recorded in TREE, ignoring any instruction I for which IGNORE (I) is true. // Return null if no such instruction exists. template<typename IgnorePredicate> insn_info * next_call_clobbers_ignoring (insn_call_clobbers_tree &tree, insn_info *insn, IgnorePredicate ignore) { if (!tree) return nullptr;
int comparison = lookup_call_clobbers (tree, insn); while (comparison >= 0 || ignore (tree->insn ())) { if (!tree.splay_next_node ()) return nullptr;
comparison = -1; } return tree->insn (); }
// If ACCESS is a set, return the first use of ACCESS by a nondebug insn I // for which IGNORE (I) is false. Return null if ACCESS is not a set or if // no such use exists. template<typename IgnorePredicate> inline use_info * first_nondebug_insn_use_ignoring (const access_info *access, IgnorePredicate ignore) { if (const set_info *set = set_with_nondebug_insn_uses (access)) { // Written this way to emphasize to the compiler that first_use // must be nonnull in this situation. use_info *use = set->first_use (); do { if (!ignore (use->insn ())) return use; use = use->next_nondebug_insn_use (); } while (use); } return nullptr; }
// If ACCESS is a set, return the last use of ACCESS by a nondebug insn I for // which IGNORE (I) is false. Return null if ACCESS is not a set or if no // such use exists. template<typename IgnorePredicate> inline use_info * last_nondebug_insn_use_ignoring (const access_info *access, IgnorePredicate ignore) { if (const set_info *set = set_with_nondebug_insn_uses (access)) { // Written this way to emphasize to the compiler that // last_nondebug_insn_use must be nonnull in this situation. use_info *use = set->last_nondebug_insn_use (); do { if (!ignore (use->insn ())) return use; use = use->prev_use (); } while (use); } return nullptr; }
// If DEF is null, return null. // // Otherwise, search backwards for an access to DEF->resource (), starting at // the end of DEF's live range. Ignore clobbers if IGNORE_CLOBBERS_SETTING // is YES, otherwise treat them like any other access. Also ignore any // access A for which IGNORE (access_insn (A)) is true. // // Thus if DEF is a set that is used by nondebug insns, the first access // that the function considers is the last such use of the set. Otherwise, // the first access that the function considers is DEF itself. // // Return the access found, or null if there is no access that meets // the criteria. // // Note that this function does not consider separately-recorded call clobbers, // although such clobbers are only relevant if IGNORE_CLOBBERS_SETTING is NO. template<typename IgnorePredicate> access_info * last_access_ignoring (def_info *def, ignore_clobbers ignore_clobbers_setting, IgnorePredicate ignore) { while (def) { auto *clobber = dyn_cast<clobber_info *> (def); if (clobber && ignore_clobbers_setting == ignore_clobbers::YES) def = first_clobber_in_group (clobber); else { if (use_info *use = last_nondebug_insn_use_ignoring (def, ignore)) return use;
insn_info *insn = def->insn (); if (!ignore (insn)) return def; } def = def->prev_def (); } return nullptr; }
// Search backwards for an access to DEF->resource (), starting // immediately before the point at which DEF occurs. Ignore clobbers // if IGNORE_CLOBBERS_SETTING is YES, otherwise treat them like any other // access. Also ignore any access A for which IGNORE (access_insn (A)) // is true. // // Thus if DEF->insn () uses DEF->resource (), that use is the first access // that the function considers, since an instruction's uses occur strictly // before its definitions. // // Note that this function does not consider separately-recorded call clobbers, // although such clobbers are only relevant if IGNORE_CLOBBERS_SETTING is NO. template<typename IgnorePredicate> inline access_info * prev_access_ignoring (def_info *def, ignore_clobbers ignore_clobbers_setting, IgnorePredicate ignore) { return last_access_ignoring (def->prev_def (), ignore_clobbers_setting, ignore); }
// If DEF is null, return null. // // Otherwise, search forwards for a definition of DEF->resource (), // starting at DEF itself. Ignore clobbers if IGNORE_CLOBBERS_SETTING // is YES, otherwise treat them like any other access. Also ignore any // definition D for which IGNORE (D->insn ()) is true. // // Return the definition found, or null if there is no access that meets // the criteria. // // Note that this function does not consider separately-recorded call clobbers, // although such clobbers are only relevant if IGNORE_CLOBBERS_SETTING is NO. template<typename IgnorePredicate> def_info * first_def_ignoring (def_info *def, ignore_clobbers ignore_clobbers_setting, IgnorePredicate ignore) { while (def) { auto *clobber = dyn_cast<clobber_info *> (def); if (clobber && ignore_clobbers_setting == ignore_clobbers::YES) def = last_clobber_in_group (clobber); else if (!ignore (def->insn ())) return def;
def = def->next_def (); } return nullptr; }
// Search forwards for the next access to DEF->resource (), // starting immediately after DEF's instruction. Ignore clobbers if // IGNORE_CLOBBERS_SETTING is YES, otherwise treat them like any other access. // Also ignore any access A for which IGNORE (access_insn (A)) is true; // in this context, ignoring a set includes ignoring all uses of the set. // // Thus if DEF is a set with uses by nondebug insns, the first access that the // function considers is the first such use of the set. // // Return the access found, or null if there is no access that meets the // criteria. // // Note that this function does not consider separately-recorded call clobbers, // although such clobbers are only relevant if IGNORE_CLOBBERS_SETTING is NO. template<typename IgnorePredicate> access_info * next_access_ignoring (def_info *def, ignore_clobbers ignore_clobbers_setting, IgnorePredicate ignore) { if (use_info *use = first_nondebug_insn_use_ignoring (def, ignore)) return use;
return first_def_ignoring (def->next_def (), ignore_clobbers_setting, ignore); }
// Return true if ACCESS1 should before ACCESS2 in an access_array. inline bool compare_access_infos (const access_info *access1, const access_info *access2) { gcc_checking_assert (access1 == access2 || access1->regno () != access2->regno ()); return access1->regno () < access2->regno (); }
// Sort [BEGIN, END) into ascending regno order. The sequence must have // at most one access to a given a regno. inline void sort_accesses (access_info **begin, access_info **end) { auto count = end - begin; if (count <= 1) return;
if (count == 2) { gcc_checking_assert (begin[0]->regno () != begin[1]->regno ()); if (begin[0]->regno () > begin[1]->regno ()) std::swap (begin[0], begin[1]); return; }
std::sort (begin, end, compare_access_infos); }
// Sort the accesses in CONTAINER, which contains pointers to access_infos. template<typename T> inline void sort_accesses (T &container) { return sort_accesses (container.begin (), container.end ()); }
// The underlying non-template implementation of merge_access_arrays. access_array merge_access_arrays_base (obstack_watermark &, access_array, access_array); // Merge access arrays ACCESSES1 and ACCESSES2, including the allocation // in the area governed by WATERMARK. Return an invalid access_array if // ACCESSES1 and ACCESSES2 contain conflicting accesses to the same resource. // // T can be an access_array, a def_array or a use_array. template<typename T> inline T merge_access_arrays (obstack_watermark &watermark, T accesses1, T accesses2) { return T (merge_access_arrays_base (watermark, accesses1, accesses2)); }
// The underlying non-template implementation of insert_access. access_array insert_access_base (obstack_watermark &, access_info *, access_array);
// Return a new access_array that contains the result of inserting ACCESS1 // into sorted access array ACCESSES2. Allocate the returned array in the // area governed by WATERMARK. Return an invalid access_array if ACCESSES2 // contains a conflicting access to the same resource as ACCESS1. // // T can be an access_array, a def_array or a use_array. template<typename T> inline T insert_access (obstack_watermark &watermark, typename T::value_type access1, T accesses2) { return T (insert_access_base (watermark, access1, accesses2)); }
// The underlying non-template implementation of remove_note_accesses. access_array remove_note_accesses_base (obstack_watermark &, access_array);
// If ACCESSES contains accesses that only occur in notes, return a new // array without such accesses, allocating it in the area governed by // WATERMARK. Return ACCESSES itself otherwise. // // T can be an access_array, a def_array or a use_array. template<typename T> inline T remove_note_accesses (obstack_watermark &watermark, T accesses) { return T (remove_note_accesses_base (watermark, accesses)); }
}
|