From fb0e181a3e98760d86581f5da1a5e6ef78977738 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Mon, 17 Feb 2025 14:57:20 -0700 Subject: [PATCH] [decycler] Reduce stack use further Down to three pointers. Exercise for the reader to prove this is optimal. --- src/hb-decycler.hh | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/hb-decycler.hh b/src/hb-decycler.hh index 710daeb80..364e8c464 100644 --- a/src/hb-decycler.hh +++ b/src/hb-decycler.hh @@ -55,6 +55,13 @@ * That is, it may not detect a cycle until the cycle is fully traversed, * even multiple times. See Floyd's algorithm analysis for details. * + * The implementation saves a pointer storage on the stack by combining + * this->u.decycler and this->u.next into a union. This is possible because + * at any point we only need one of those values. The invariant is that + * after construction, and before destruction, of a node, the u.decycler + * field is always valid. The u.next field is only valid when the node is + * in the traversal path, parent to another node. + * * There are three method's: * * - hb_decycler_node_t() constructor: Creates a new node in the traversal. @@ -89,8 +96,9 @@ struct hb_decycler_t struct hb_decycler_node_t { hb_decycler_node_t (hb_decycler_t &decycler) - : decycler (decycler) { + u.decycler = &decycler; + if (!decycler.tortoise) { // First node. @@ -100,19 +108,21 @@ struct hb_decycler_node_t tortoise_asleep = !decycler.hare->tortoise_asleep; this->prev = decycler.hare; - decycler.hare->next = this; + decycler.hare->u.next = this; decycler.hare = this; if (!tortoise_asleep) - decycler.tortoise = decycler.tortoise->next; // Time to move. + decycler.tortoise = decycler.tortoise->u.next; // Time to move. } ~hb_decycler_node_t () { + hb_decycler_t &decycler = *u.decycler; + assert (decycler.hare == this); decycler.hare = prev; if (prev) - prev->next = nullptr; + prev->u.decycler = &decycler; if (!tortoise_asleep) decycler.tortoise = decycler.tortoise->prev; @@ -122,6 +132,8 @@ struct hb_decycler_node_t { value = value_; + hb_decycler_t &decycler = *u.decycler; + if (decycler.tortoise == this) return true; // First node; not a cycle. @@ -132,8 +144,10 @@ struct hb_decycler_node_t } private: - hb_decycler_t &decycler; - hb_decycler_node_t *next = nullptr; + union { + hb_decycler_t *decycler; + hb_decycler_node_t *next; + } u = {nullptr}; hb_decycler_node_t *prev = nullptr; unsigned value = (unsigned) -1; bool tortoise_asleep = false;