> Am 09.08.2024 um 19:19 schrieb Richard Sandiford <richard.sandif...@arm.com>:
>
> This patch is an attempt to gauge opinion on one way of fixing PR30920.
>
> The PR points out that the libiberty splay tree implementation does
> not implement the algorithm described by Sleator and Tarjan and has
> unclear complexity bounds. (It's also somewhat dangerous in that
> splay_tree_min and splay_tree_max walk the tree without splaying,
> meaning that they are fully linear in the worst case, rather than
> amortised logarithmic.) These properties have been carried over
> to typed-splay-tree.h.
>
> We could fix those problems directly in the existing implementations,
> and probably should for libiberty. But when I added rtl-ssa, I also
> added a third(!) splay tree implementation: splay-tree-utils.h.
> In response to Jeff's understandable unease about having three
> implementations, I was supposed to go back during the next stage 1
> and reduce it to no more than two. I never did that. :-(
I wonder if typed_splay_tree could wrap around splay-tree-utils. Though the
typed variant is the least used…
> splay-tree-utils.h is so called because rtl-ssa uses splay trees
> in structures that are relatively small and very size-sensitive.
> I therefore wanted to be able to embed the splay tree links directly
> in the structures, rather than pay the penalty of using separate
> nodes with one-way or two-way links between them. There were also
> operations for which it was convenient to treat the splay tree root
> as an explicitly managed cursor, rather than treating the tree as
> a pure ADT. The interface is therefore a bit more low-level than
> for the other implementations.
>
> I wondered whether the same trade-offs might apply to users of
> the libiberty splay trees. The first one I looked at in detail
> was SCC value numbering, which seemed like it would benefit from
> using splay-tree-utils.h directly.
>
> The patch does that. It also adds a couple of new helper routines
> to splay-tree-utils.h.
>
> I don't expect this approach to be the right one for every use
> of splay trees. E.g. splay tree used for omp gimplification would
> certainly need separate nodes.
>
> Tested on aarch64-linux-gnu & x86_64-linux-gnu. OK to install?
Ok
Thanks,
Richard
> Richard
>
>
> gcc/
> * splay-tree-utils.h (rooted_splay_tree::insert_relative)
> (rooted_splay_tree::lookup_le): New functions.
> (rooted_splay_tree::remove_root_and_splay_next): Likewise.
> * splay-tree-utils.h (rooted_splay_tree::insert_relative): New
> function, extracted from...
> (rooted_splay_tree::insert): ...here.
> (rooted_splay_tree::lookup_le): New function.
> (rooted_splay_tree::remove_root_and_splay_next): Likewise.
> * tree-ssa-sccvn.cc (pd_range::m_children): New member variable.
> (vn_walk_cb_data::vn_walk_cb_data): Initialize first_range.
> (vn_walk_cb_data::known_ranges): Use a default_splay_tree.
> (vn_walk_cb_data::~vn_walk_cb_data): Remove freeing of known_ranges.
> (pd_range_compare, pd_range_alloc, pd_range_dealloc): Delete.
> (vn_walk_cb_data::push_partial_def): Rewrite splay tree operations
> to use splay-tree-utils.h.
> * rtl-ssa/accesses.cc (function_info::add_use): Use insert_relative.
> ---
> gcc/rtl-ssa/accesses.cc | 8 +--
> gcc/splay-tree-utils.h | 29 +++++++++++
> gcc/splay-tree-utils.tcc | 69 ++++++++++++++++++++++---
> gcc/tree-ssa-sccvn.cc | 106 +++++++++++++--------------------------
> 4 files changed, 131 insertions(+), 81 deletions(-)
>
> diff --git a/gcc/rtl-ssa/accesses.cc b/gcc/rtl-ssa/accesses.cc
> index 5e9077545a8..ef99759871a 100644
> --- a/gcc/rtl-ssa/accesses.cc
> +++ b/gcc/rtl-ssa/accesses.cc
> @@ -1232,16 +1232,16 @@ function_info::add_use (use_info *use)
> need_use_splay_tree (def);
> int comparison = lookup_use (def->m_use_tree, insn);
> gcc_checking_assert (comparison != 0);
> - splay_tree_node<use_info *> *neighbor = def->m_use_tree.root ();
> + use_info *neighbor = def->m_use_tree.root ()->value ();
>
> // If USE comes before NEIGHBOR, insert USE to NEIGHBOR's left,
> // otherwise insert USE to NEIGHBOR's right.
> auto *use_node = allocate<splay_tree_node<use_info *>> (use);
> - def->m_use_tree.insert_child (neighbor, comparison > 0, use_node);
> + def->m_use_tree.insert_relative (comparison, use_node);
> if (comparison > 0)
> - insert_use_after (use, neighbor->value ());
> + insert_use_after (use, neighbor);
> else
> - insert_use_before (use, neighbor->value ());
> + insert_use_before (use, neighbor);
> }
>
> void
> diff --git a/gcc/splay-tree-utils.h b/gcc/splay-tree-utils.h
> index 8344808f6d1..9526e0ba336 100644
> --- a/gcc/splay-tree-utils.h
> +++ b/gcc/splay-tree-utils.h
> @@ -185,6 +185,21 @@ public:
> template<typename Comparator>
> bool insert (node_type new_node, Comparator compare);
>
> + // Insert NEW_NODE into the splay tree. If the tree is currently
> non-empty,
> + // COMPARISON is < 0 if NEW_NODE comes immediate before the current root,
> + // or > 0 if NEW_NODE comes immediately after the current root.
> + //
> + // On return, NEW_NODE is the root of the tree.
> + //
> + // For example, this can be used in the construct:
> + //
> + // int comparison = tree.lookup (...);
> + // if (comparison != 0)
> + // tree.insert_relative (comparison, create_new_node ());
> + //
> + // Complexity: O(1)
> + void insert_relative (int comparison, node_type new_node);
> +
> // Insert NEW_NODE into the splay tree, given that NEW_NODE is the
> // maximum node of the new tree. On return, NEW_NODE is also the
> // root of the tree.
> @@ -212,6 +227,14 @@ public:
> // Otherwise amortized O(log N), worst-cast O(N).
> void remove_root ();
>
> + // Remove the root node of the splay tree. If the root node was not
> + // the maximum node, bring the next node to the root and return true.
> + // Return false otherwise.
> + //
> + // Complexity: O(1) if removing the maximum node. Otherwise amortized
> + // O(log N), worst-cast O(N).
> + bool remove_root_and_splay_next ();
> +
> // Split the left child of the current root out into a separate tree
> // and return the new tree.
> rooted_splay_tree split_before_root ();
> @@ -326,6 +349,12 @@ public:
> int lookup (LeftPredicate want_something_smaller,
> RightPredicate want_something_bigger);
>
> + // Like lookup, but always pick a node that is no bigger than the one
> + // being searched for, if such a node exists.
> + template<typename LeftPredicate, typename RightPredicate>
> + int lookup_le (LeftPredicate want_something_smaller,
> + RightPredicate want_something_bigger);
> +
> // Keep the ability to print subtrees.
> using parent::print;
>
> diff --git a/gcc/splay-tree-utils.tcc b/gcc/splay-tree-utils.tcc
> index 5c36bb55f78..6118233a5f6 100644
> --- a/gcc/splay-tree-utils.tcc
> +++ b/gcc/splay-tree-utils.tcc
> @@ -340,15 +340,30 @@ rooted_splay_tree<Accessors>::insert (node_type
> new_node, Comparator compare)
> if (comparison == 0)
> return false;
>
> - // Insert NEW_NODE before M_ROOT if COMPARISON < 0 and after M_ROOT
> - // otherwise.
> - set_child (new_node, comparison < 0, m_root);
> - set_child (new_node, comparison > 0, get_child (m_root, comparison > 0));
> - set_child (m_root, comparison > 0, nullptr);
> - m_root = new_node;
> + insert_relative (comparison, new_node);
> return true;
> }
>
> +// See the comment above the declaration.
> +template<typename Accessors>
> +inline void
> +rooted_splay_tree<Accessors>::insert_relative (int comparison,
> + node_type new_node)
> +{
> + gcc_checking_assert (!get_child (new_node, 0)
> + && !get_child (new_node, 1)
> + && (!m_root || comparison != 0));
> + if (m_root)
> + {
> + // Insert NEW_NODE before M_ROOT if COMPARISON < 0 and after M_ROOT
> + // otherwise.
> + set_child (new_node, comparison < 0, m_root);
> + set_child (new_node, comparison > 0, get_child (m_root, comparison >
> 0));
> + set_child (m_root, comparison > 0, node_type ());
> + }
> + m_root = new_node;
> +}
> +
> // See the comment above the declaration.
> template<typename Accessors>
> inline void
> @@ -398,6 +413,35 @@ rooted_splay_tree<Accessors>::remove_root ()
> set_child (node, 1, node_type ());
> }
>
> +// See the comment above the declaration.
> +template<typename Accessors>
> +inline bool
> +rooted_splay_tree<Accessors>::remove_root_and_splay_next ()
> +{
> + node_type node = m_root;
> + node_type right = get_child (node, 1);
> + if (right)
> + {
> + // Bring the minimum right-hand node to the root.
> + if (get_child (right, 0))
> + {
> + right = parent::template splay_limit<0> (right);
> + gcc_checking_assert (!get_child (right, 0));
> + }
> + set_child (right, 0, get_child (node, 0));
> + m_root = right;
> + }
> + else
> + m_root = get_child (node, 0);
> + if (m_root)
> + set_parent (m_root, node_type ());
> +
> + // Clear the links from NODE. Its parent is already node_type ().
> + set_child (node, 0, node_type ());
> + set_child (node, 1, node_type ());
> + return right;
> +}
> +
> // See the comment above the declaration.
> template<typename Accessors>
> inline rooted_splay_tree<Accessors>
> @@ -730,6 +774,19 @@ rooted_splay_tree<Accessors>::lookup (LeftPredicate
> want_something_smaller,
> return result;
> }
>
> +// See the comment above the declaration.
> +template<typename Accessors>
> +template<typename LeftPredicate, typename RightPredicate>
> +int
> +rooted_splay_tree<Accessors>::lookup_le (LeftPredicate
> want_something_smaller,
> + RightPredicate want_something_bigger)
> +{
> + int comparison = lookup (want_something_smaller, want_something_bigger);
> + if (comparison < 0 && splay_prev_node ())
> + comparison = 1;
> + return comparison;
> +}
> +
> // See the comment above the declaration.
> template<typename Accessors>
> template<typename Printer>
> diff --git a/gcc/tree-ssa-sccvn.cc b/gcc/tree-ssa-sccvn.cc
> index 0639ba426ff..4370d09d9d8 100644
> --- a/gcc/tree-ssa-sccvn.cc
> +++ b/gcc/tree-ssa-sccvn.cc
> @@ -21,7 +21,6 @@ along with GCC; see the file COPYING3. If not see
> #include "config.h"
> #include "system.h"
> #include "coretypes.h"
> -#include "splay-tree.h"
> #include "backend.h"
> #include "rtl.h"
> #include "tree.h"
> @@ -33,6 +32,7 @@ along with GCC; see the file COPYING3. If not see
> #include "emit-rtl.h"
> #include "cgraph.h"
> #include "gimple-pretty-print.h"
> +#include "splay-tree-utils.h"
> #include "alias.h"
> #include "fold-const.h"
> #include "stor-layout.h"
> @@ -1832,6 +1832,7 @@ struct pd_range
> {
> HOST_WIDE_INT offset;
> HOST_WIDE_INT size;
> + pd_range *m_children[2];
> };
>
> struct pd_data
> @@ -1853,8 +1854,8 @@ struct vn_walk_cb_data
> mask (mask_), masked_result (NULL_TREE), same_val (NULL_TREE),
> vn_walk_kind (vn_walk_kind_),
> tbaa_p (tbaa_p_), redundant_store_removal_p
> (redundant_store_removal_p_),
> - saved_operands (vNULL), first_set (-2), first_base_set (-2),
> - known_ranges (NULL)
> + saved_operands (vNULL), first_range (), first_set (-2),
> + first_base_set (-2)
> {
> if (!last_vuse_ptr)
> last_vuse_ptr = &last_vuse;
> @@ -1924,7 +1925,7 @@ struct vn_walk_cb_data
> pd_range first_range;
> alias_set_type first_set;
> alias_set_type first_base_set;
> - splay_tree known_ranges;
> + default_splay_tree<pd_range *> known_ranges;
> obstack ranges_obstack;
> static constexpr HOST_WIDE_INT bufsize = 64;
> };
> @@ -1932,10 +1933,7 @@ struct vn_walk_cb_data
> vn_walk_cb_data::~vn_walk_cb_data ()
> {
> if (known_ranges)
> - {
> - splay_tree_delete (known_ranges);
> - obstack_free (&ranges_obstack, NULL);
> - }
> + obstack_free (&ranges_obstack, NULL);
> saved_operands.release ();
> }
>
> @@ -1961,32 +1959,6 @@ vn_walk_cb_data::finish (alias_set_type set,
> alias_set_type base_set, tree val)
> vr->type, operands, val);
> }
>
> -/* pd_range splay-tree helpers. */
> -
> -static int
> -pd_range_compare (splay_tree_key offset1p, splay_tree_key offset2p)
> -{
> - HOST_WIDE_INT offset1 = *(HOST_WIDE_INT *)offset1p;
> - HOST_WIDE_INT offset2 = *(HOST_WIDE_INT *)offset2p;
> - if (offset1 < offset2)
> - return -1;
> - else if (offset1 > offset2)
> - return 1;
> - return 0;
> -}
> -
> -static void *
> -pd_tree_alloc (int size, void *data_)
> -{
> - vn_walk_cb_data *data = (vn_walk_cb_data *)data_;
> - return obstack_alloc (&data->ranges_obstack, size);
> -}
> -
> -static void
> -pd_tree_dealloc (void *, void *)
> -{
> -}
> -
> /* Push PD to the vector of partial definitions returning a
> value when we are ready to combine things with VUSE, SET and MAXSIZEI,
> NULL when we want to continue looking for partial defs or -1
> @@ -2055,51 +2027,43 @@ vn_walk_cb_data::push_partial_def (pd_data pd,
> /* ??? Optimize the case where the 2nd partial def completes
> things. */
> gcc_obstack_init (&ranges_obstack);
> - known_ranges = splay_tree_new_with_allocator (pd_range_compare, 0, 0,
> - pd_tree_alloc,
> - pd_tree_dealloc, this);
> - splay_tree_insert (known_ranges,
> - (splay_tree_key)&first_range.offset,
> - (splay_tree_value)&first_range);
> - }
> -
> - pd_range newr = { pd.offset, pd.size };
> - splay_tree_node n;
> - /* Lookup the predecessor of offset + 1 and see if we need to merge.
> */
> - HOST_WIDE_INT loffset = newr.offset + 1;
> - if ((n = splay_tree_predecessor (known_ranges,
> (splay_tree_key)&loffset))
> - && ((r = (pd_range *)n->value), true)
> + known_ranges.insert_max_node (&first_range);
> + }
> + /* Lookup the offset and see if we need to merge. */
> + int comparison = known_ranges.lookup_le
> + ([&] (pd_range *r) { return pd.offset < r->offset; },
> + [&] (pd_range *r) { return pd.offset > r->offset; });
> + r = known_ranges.root ();
> + if (comparison >= 0
> && ranges_known_overlap_p (r->offset, r->size + 1,
> - newr.offset, newr.size))
> + pd.offset, pd.size))
> {
> /* Ignore partial defs already covered. Here we also drop shadowed
> clobbers arriving here at the floor. */
> - if (known_subrange_p (newr.offset, newr.size, r->offset, r->size))
> + if (known_subrange_p (pd.offset, pd.size, r->offset, r->size))
> return NULL;
> - r->size
> - = MAX (r->offset + r->size, newr.offset + newr.size) - r->offset;
> + r->size = MAX (r->offset + r->size, pd.offset + pd.size) - r->offset;
> }
> else
> {
> - /* newr.offset wasn't covered yet, insert the range. */
> - r = XOBNEW (&ranges_obstack, pd_range);
> - *r = newr;
> - splay_tree_insert (known_ranges, (splay_tree_key)&r->offset,
> - (splay_tree_value)r);
> - }
> - /* Merge r which now contains newr and is a member of the splay tree
> with
> - adjacent overlapping ranges. */
> - pd_range *rafter;
> - while ((n = splay_tree_successor (known_ranges,
> - (splay_tree_key)&r->offset))
> - && ((rafter = (pd_range *)n->value), true)
> - && ranges_known_overlap_p (r->offset, r->size + 1,
> - rafter->offset, rafter->size))
> - {
> - r->size = MAX (r->offset + r->size,
> - rafter->offset + rafter->size) - r->offset;
> - splay_tree_remove (known_ranges, (splay_tree_key)&rafter->offset);
> + /* pd.offset wasn't covered yet, insert the range. */
> + void *addr = XOBNEW (&ranges_obstack, pd_range);
> + r = new (addr) pd_range { pd.offset, pd.size, {} };
> + known_ranges.insert_relative (comparison, r);
> }
> + /* Merge r which now contains pd's range and is a member of the splay
> + tree with adjacent overlapping ranges. */
> + if (known_ranges.splay_next_node ())
> + do
> + {
> + pd_range *rafter = known_ranges.root ();
> + if (!ranges_known_overlap_p (r->offset, r->size + 1,
> + rafter->offset, rafter->size))
> + break;
> + r->size = MAX (r->offset + r->size,
> + rafter->offset + rafter->size) - r->offset;
> + }
> + while (known_ranges.remove_root_and_splay_next ());
> /* If we get a clobber, fail. */
> if (TREE_CLOBBER_P (pd.rhs))
> return (void *)-1;
> @@ -2109,7 +2073,7 @@ vn_walk_cb_data::push_partial_def (pd_data pd,
> partial_defs.safe_push (pd);
> }
>
> - /* Now we have merged newr into the range tree. When we have covered
> + /* Now we have merged pd's range into the range tree. When we have covered
> [offseti, sizei] then the tree will contain exactly one node which has
> the desired properties and it will be 'r'. */
> if (!known_subrange_p (0, maxsizei, r->offset, r->size))
> --
> 2.25.1
>