aboutsummaryrefslogtreecommitdiff
path: root/gcc/rtl-ssa/access-utils.h
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/rtl-ssa/access-utils.h')
-rw-r--r--gcc/rtl-ssa/access-utils.h553
1 files changed, 553 insertions, 0 deletions
diff --git a/gcc/rtl-ssa/access-utils.h b/gcc/rtl-ssa/access-utils.h
new file mode 100644
index 0000000..634f99b
--- /dev/null
+++ b/gcc/rtl-ssa/access-utils.h
@@ -0,0 +1,553 @@
+// Access-related utilities for RTL SSA -*- C++ -*-
+// Copyright (C) 2020 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));
+}
+
+}