xref: /freebsd-src/contrib/llvm-project/llvm/include/llvm/ADT/FoldingSet.h (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
10b57cec5SDimitry Andric //===- llvm/ADT/FoldingSet.h - Uniquing Hash Set ----------------*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
81fd87a68SDimitry Andric ///
91fd87a68SDimitry Andric /// \file
101fd87a68SDimitry Andric /// This file defines a hash set that can be used to remove duplication of nodes
111fd87a68SDimitry Andric /// in a graph.  This code was originally created by Chris Lattner for use with
121fd87a68SDimitry Andric /// SelectionDAGCSEMap, but was isolated to provide use across the llvm code
131fd87a68SDimitry Andric /// set.
140b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
150b57cec5SDimitry Andric 
160b57cec5SDimitry Andric #ifndef LLVM_ADT_FOLDINGSET_H
170b57cec5SDimitry Andric #define LLVM_ADT_FOLDINGSET_H
180b57cec5SDimitry Andric 
1981ad6265SDimitry Andric #include "llvm/ADT/Hashing.h"
205f757f3fSDimitry Andric #include "llvm/ADT/STLForwardCompat.h"
210b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h"
220b57cec5SDimitry Andric #include "llvm/ADT/iterator.h"
230b57cec5SDimitry Andric #include "llvm/Support/Allocator.h"
24*0fca6ea1SDimitry Andric #include "llvm/Support/xxhash.h"
250b57cec5SDimitry Andric #include <cassert>
260b57cec5SDimitry Andric #include <cstddef>
270b57cec5SDimitry Andric #include <cstdint>
2881ad6265SDimitry Andric #include <type_traits>
290b57cec5SDimitry Andric #include <utility>
300b57cec5SDimitry Andric 
310b57cec5SDimitry Andric namespace llvm {
320b57cec5SDimitry Andric 
330b57cec5SDimitry Andric /// This folding set used for two purposes:
340b57cec5SDimitry Andric ///   1. Given information about a node we want to create, look up the unique
350b57cec5SDimitry Andric ///      instance of the node in the set.  If the node already exists, return
360b57cec5SDimitry Andric ///      it, otherwise return the bucket it should be inserted into.
370b57cec5SDimitry Andric ///   2. Given a node that has already been created, remove it from the set.
380b57cec5SDimitry Andric ///
390b57cec5SDimitry Andric /// This class is implemented as a single-link chained hash table, where the
400b57cec5SDimitry Andric /// "buckets" are actually the nodes themselves (the next pointer is in the
410b57cec5SDimitry Andric /// node).  The last node points back to the bucket to simplify node removal.
420b57cec5SDimitry Andric ///
430b57cec5SDimitry Andric /// Any node that is to be included in the folding set must be a subclass of
440b57cec5SDimitry Andric /// FoldingSetNode.  The node class must also define a Profile method used to
450b57cec5SDimitry Andric /// establish the unique bits of data for the node.  The Profile method is
460b57cec5SDimitry Andric /// passed a FoldingSetNodeID object which is used to gather the bits.  Just
470b57cec5SDimitry Andric /// call one of the Add* functions defined in the FoldingSetBase::NodeID class.
480b57cec5SDimitry Andric /// NOTE: That the folding set does not own the nodes and it is the
490b57cec5SDimitry Andric /// responsibility of the user to dispose of the nodes.
500b57cec5SDimitry Andric ///
510b57cec5SDimitry Andric /// Eg.
520b57cec5SDimitry Andric ///    class MyNode : public FoldingSetNode {
530b57cec5SDimitry Andric ///    private:
540b57cec5SDimitry Andric ///      std::string Name;
550b57cec5SDimitry Andric ///      unsigned Value;
560b57cec5SDimitry Andric ///    public:
570b57cec5SDimitry Andric ///      MyNode(const char *N, unsigned V) : Name(N), Value(V) {}
580b57cec5SDimitry Andric ///       ...
590b57cec5SDimitry Andric ///      void Profile(FoldingSetNodeID &ID) const {
600b57cec5SDimitry Andric ///        ID.AddString(Name);
610b57cec5SDimitry Andric ///        ID.AddInteger(Value);
620b57cec5SDimitry Andric ///      }
630b57cec5SDimitry Andric ///      ...
640b57cec5SDimitry Andric ///    };
650b57cec5SDimitry Andric ///
660b57cec5SDimitry Andric /// To define the folding set itself use the FoldingSet template;
670b57cec5SDimitry Andric ///
680b57cec5SDimitry Andric /// Eg.
690b57cec5SDimitry Andric ///    FoldingSet<MyNode> MyFoldingSet;
700b57cec5SDimitry Andric ///
710b57cec5SDimitry Andric /// Four public methods are available to manipulate the folding set;
720b57cec5SDimitry Andric ///
730b57cec5SDimitry Andric /// 1) If you have an existing node that you want add to the set but unsure
740b57cec5SDimitry Andric /// that the node might already exist then call;
750b57cec5SDimitry Andric ///
760b57cec5SDimitry Andric ///    MyNode *M = MyFoldingSet.GetOrInsertNode(N);
770b57cec5SDimitry Andric ///
780b57cec5SDimitry Andric /// If The result is equal to the input then the node has been inserted.
790b57cec5SDimitry Andric /// Otherwise, the result is the node existing in the folding set, and the
800b57cec5SDimitry Andric /// input can be discarded (use the result instead.)
810b57cec5SDimitry Andric ///
820b57cec5SDimitry Andric /// 2) If you are ready to construct a node but want to check if it already
830b57cec5SDimitry Andric /// exists, then call FindNodeOrInsertPos with a FoldingSetNodeID of the bits to
840b57cec5SDimitry Andric /// check;
850b57cec5SDimitry Andric ///
860b57cec5SDimitry Andric ///   FoldingSetNodeID ID;
870b57cec5SDimitry Andric ///   ID.AddString(Name);
880b57cec5SDimitry Andric ///   ID.AddInteger(Value);
890b57cec5SDimitry Andric ///   void *InsertPoint;
900b57cec5SDimitry Andric ///
910b57cec5SDimitry Andric ///    MyNode *M = MyFoldingSet.FindNodeOrInsertPos(ID, InsertPoint);
920b57cec5SDimitry Andric ///
93480093f4SDimitry Andric /// If found then M will be non-NULL, else InsertPoint will point to where it
940b57cec5SDimitry Andric /// should be inserted using InsertNode.
950b57cec5SDimitry Andric ///
96480093f4SDimitry Andric /// 3) If you get a NULL result from FindNodeOrInsertPos then you can insert a
97480093f4SDimitry Andric /// new node with InsertNode;
980b57cec5SDimitry Andric ///
99480093f4SDimitry Andric ///    MyFoldingSet.InsertNode(M, InsertPoint);
1000b57cec5SDimitry Andric ///
1010b57cec5SDimitry Andric /// 4) Finally, if you want to remove a node from the folding set call;
1020b57cec5SDimitry Andric ///
103480093f4SDimitry Andric ///    bool WasRemoved = MyFoldingSet.RemoveNode(M);
1040b57cec5SDimitry Andric ///
1050b57cec5SDimitry Andric /// The result indicates whether the node existed in the folding set.
1060b57cec5SDimitry Andric 
1070b57cec5SDimitry Andric class FoldingSetNodeID;
1080b57cec5SDimitry Andric class StringRef;
1090b57cec5SDimitry Andric 
1100b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
1110b57cec5SDimitry Andric /// FoldingSetBase - Implements the folding set functionality.  The main
1120b57cec5SDimitry Andric /// structure is an array of buckets.  Each bucket is indexed by the hash of
1130b57cec5SDimitry Andric /// the nodes it contains.  The bucket itself points to the nodes contained
1140b57cec5SDimitry Andric /// in the bucket via a singly linked list.  The last node in the list points
1150b57cec5SDimitry Andric /// back to the bucket to facilitate node removal.
1160b57cec5SDimitry Andric ///
1170b57cec5SDimitry Andric class FoldingSetBase {
1180b57cec5SDimitry Andric protected:
1190b57cec5SDimitry Andric   /// Buckets - Array of bucket chains.
1200b57cec5SDimitry Andric   void **Buckets;
1210b57cec5SDimitry Andric 
1220b57cec5SDimitry Andric   /// NumBuckets - Length of the Buckets array.  Always a power of 2.
1230b57cec5SDimitry Andric   unsigned NumBuckets;
1240b57cec5SDimitry Andric 
1250b57cec5SDimitry Andric   /// NumNodes - Number of nodes in the folding set. Growth occurs when NumNodes
1260b57cec5SDimitry Andric   /// is greater than twice the number of buckets.
1270b57cec5SDimitry Andric   unsigned NumNodes;
1280b57cec5SDimitry Andric 
1290b57cec5SDimitry Andric   explicit FoldingSetBase(unsigned Log2InitSize = 6);
1300b57cec5SDimitry Andric   FoldingSetBase(FoldingSetBase &&Arg);
1310b57cec5SDimitry Andric   FoldingSetBase &operator=(FoldingSetBase &&RHS);
1320b57cec5SDimitry Andric   ~FoldingSetBase();
1330b57cec5SDimitry Andric 
1340b57cec5SDimitry Andric public:
1350b57cec5SDimitry Andric   //===--------------------------------------------------------------------===//
1360b57cec5SDimitry Andric   /// Node - This class is used to maintain the singly linked bucket list in
1370b57cec5SDimitry Andric   /// a folding set.
1380b57cec5SDimitry Andric   class Node {
1390b57cec5SDimitry Andric   private:
1400b57cec5SDimitry Andric     // NextInFoldingSetBucket - next link in the bucket list.
1410b57cec5SDimitry Andric     void *NextInFoldingSetBucket = nullptr;
1420b57cec5SDimitry Andric 
1430b57cec5SDimitry Andric   public:
1440b57cec5SDimitry Andric     Node() = default;
1450b57cec5SDimitry Andric 
1460b57cec5SDimitry Andric     // Accessors
1470b57cec5SDimitry Andric     void *getNextInBucket() const { return NextInFoldingSetBucket; }
1480b57cec5SDimitry Andric     void SetNextInBucket(void *N) { NextInFoldingSetBucket = N; }
1490b57cec5SDimitry Andric   };
1500b57cec5SDimitry Andric 
1510b57cec5SDimitry Andric   /// clear - Remove all nodes from the folding set.
1520b57cec5SDimitry Andric   void clear();
1530b57cec5SDimitry Andric 
1540b57cec5SDimitry Andric   /// size - Returns the number of nodes in the folding set.
1550b57cec5SDimitry Andric   unsigned size() const { return NumNodes; }
1560b57cec5SDimitry Andric 
1570b57cec5SDimitry Andric   /// empty - Returns true if there are no nodes in the folding set.
1580b57cec5SDimitry Andric   bool empty() const { return NumNodes == 0; }
1590b57cec5SDimitry Andric 
1600b57cec5SDimitry Andric   /// capacity - Returns the number of nodes permitted in the folding set
1610b57cec5SDimitry Andric   /// before a rebucket operation is performed.
1620b57cec5SDimitry Andric   unsigned capacity() {
1630b57cec5SDimitry Andric     // We allow a load factor of up to 2.0,
1640b57cec5SDimitry Andric     // so that means our capacity is NumBuckets * 2
1650b57cec5SDimitry Andric     return NumBuckets * 2;
1660b57cec5SDimitry Andric   }
1670b57cec5SDimitry Andric 
1685ffd83dbSDimitry Andric protected:
1695ffd83dbSDimitry Andric   /// Functions provided by the derived class to compute folding properties.
1705ffd83dbSDimitry Andric   /// This is effectively a vtable for FoldingSetBase, except that we don't
1715ffd83dbSDimitry Andric   /// actually store a pointer to it in the object.
1725ffd83dbSDimitry Andric   struct FoldingSetInfo {
1735ffd83dbSDimitry Andric     /// GetNodeProfile - Instantiations of the FoldingSet template implement
1745ffd83dbSDimitry Andric     /// this function to gather data bits for the given node.
1755ffd83dbSDimitry Andric     void (*GetNodeProfile)(const FoldingSetBase *Self, Node *N,
1765ffd83dbSDimitry Andric                            FoldingSetNodeID &ID);
1775ffd83dbSDimitry Andric 
1785ffd83dbSDimitry Andric     /// NodeEquals - Instantiations of the FoldingSet template implement
1795ffd83dbSDimitry Andric     /// this function to compare the given node with the given ID.
1805ffd83dbSDimitry Andric     bool (*NodeEquals)(const FoldingSetBase *Self, Node *N,
1815ffd83dbSDimitry Andric                        const FoldingSetNodeID &ID, unsigned IDHash,
1825ffd83dbSDimitry Andric                        FoldingSetNodeID &TempID);
1835ffd83dbSDimitry Andric 
1845ffd83dbSDimitry Andric     /// ComputeNodeHash - Instantiations of the FoldingSet template implement
1855ffd83dbSDimitry Andric     /// this function to compute a hash value for the given node.
1865ffd83dbSDimitry Andric     unsigned (*ComputeNodeHash)(const FoldingSetBase *Self, Node *N,
1875ffd83dbSDimitry Andric                                 FoldingSetNodeID &TempID);
1885ffd83dbSDimitry Andric   };
1895ffd83dbSDimitry Andric 
1900b57cec5SDimitry Andric private:
1910b57cec5SDimitry Andric   /// GrowHashTable - Double the size of the hash table and rehash everything.
1925ffd83dbSDimitry Andric   void GrowHashTable(const FoldingSetInfo &Info);
1930b57cec5SDimitry Andric 
1940b57cec5SDimitry Andric   /// GrowBucketCount - resize the hash table and rehash everything.
1950b57cec5SDimitry Andric   /// NewBucketCount must be a power of two, and must be greater than the old
1960b57cec5SDimitry Andric   /// bucket count.
1975ffd83dbSDimitry Andric   void GrowBucketCount(unsigned NewBucketCount, const FoldingSetInfo &Info);
1980b57cec5SDimitry Andric 
1990b57cec5SDimitry Andric protected:
2000b57cec5SDimitry Andric   // The below methods are protected to encourage subclasses to provide a more
2010b57cec5SDimitry Andric   // type-safe API.
2020b57cec5SDimitry Andric 
2035ffd83dbSDimitry Andric   /// reserve - Increase the number of buckets such that adding the
2045ffd83dbSDimitry Andric   /// EltCount-th node won't cause a rebucket operation. reserve is permitted
2055ffd83dbSDimitry Andric   /// to allocate more space than requested by EltCount.
2065ffd83dbSDimitry Andric   void reserve(unsigned EltCount, const FoldingSetInfo &Info);
2075ffd83dbSDimitry Andric 
2080b57cec5SDimitry Andric   /// RemoveNode - Remove a node from the folding set, returning true if one
2090b57cec5SDimitry Andric   /// was removed or false if the node was not in the folding set.
2100b57cec5SDimitry Andric   bool RemoveNode(Node *N);
2110b57cec5SDimitry Andric 
2120b57cec5SDimitry Andric   /// GetOrInsertNode - If there is an existing simple Node exactly
2130b57cec5SDimitry Andric   /// equal to the specified node, return it.  Otherwise, insert 'N' and return
2140b57cec5SDimitry Andric   /// it instead.
2155ffd83dbSDimitry Andric   Node *GetOrInsertNode(Node *N, const FoldingSetInfo &Info);
2160b57cec5SDimitry Andric 
2170b57cec5SDimitry Andric   /// FindNodeOrInsertPos - Look up the node specified by ID.  If it exists,
2180b57cec5SDimitry Andric   /// return it.  If not, return the insertion token that will make insertion
2190b57cec5SDimitry Andric   /// faster.
2205ffd83dbSDimitry Andric   Node *FindNodeOrInsertPos(const FoldingSetNodeID &ID, void *&InsertPos,
2215ffd83dbSDimitry Andric                             const FoldingSetInfo &Info);
2220b57cec5SDimitry Andric 
2230b57cec5SDimitry Andric   /// InsertNode - Insert the specified node into the folding set, knowing that
2240b57cec5SDimitry Andric   /// it is not already in the folding set.  InsertPos must be obtained from
2250b57cec5SDimitry Andric   /// FindNodeOrInsertPos.
2265ffd83dbSDimitry Andric   void InsertNode(Node *N, void *InsertPos, const FoldingSetInfo &Info);
2270b57cec5SDimitry Andric };
2280b57cec5SDimitry Andric 
2290b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
2300b57cec5SDimitry Andric 
2310b57cec5SDimitry Andric /// DefaultFoldingSetTrait - This class provides default implementations
2320b57cec5SDimitry Andric /// for FoldingSetTrait implementations.
2330b57cec5SDimitry Andric template<typename T> struct DefaultFoldingSetTrait {
2340b57cec5SDimitry Andric   static void Profile(const T &X, FoldingSetNodeID &ID) {
2350b57cec5SDimitry Andric     X.Profile(ID);
2360b57cec5SDimitry Andric   }
2370b57cec5SDimitry Andric   static void Profile(T &X, FoldingSetNodeID &ID) {
2380b57cec5SDimitry Andric     X.Profile(ID);
2390b57cec5SDimitry Andric   }
2400b57cec5SDimitry Andric 
2410b57cec5SDimitry Andric   // Equals - Test if the profile for X would match ID, using TempID
2420b57cec5SDimitry Andric   // to compute a temporary ID if necessary. The default implementation
2430b57cec5SDimitry Andric   // just calls Profile and does a regular comparison. Implementations
2440b57cec5SDimitry Andric   // can override this to provide more efficient implementations.
2450b57cec5SDimitry Andric   static inline bool Equals(T &X, const FoldingSetNodeID &ID, unsigned IDHash,
2460b57cec5SDimitry Andric                             FoldingSetNodeID &TempID);
2470b57cec5SDimitry Andric 
2480b57cec5SDimitry Andric   // ComputeHash - Compute a hash value for X, using TempID to
2490b57cec5SDimitry Andric   // compute a temporary ID if necessary. The default implementation
2500b57cec5SDimitry Andric   // just calls Profile and does a regular hash computation.
2510b57cec5SDimitry Andric   // Implementations can override this to provide more efficient
2520b57cec5SDimitry Andric   // implementations.
2530b57cec5SDimitry Andric   static inline unsigned ComputeHash(T &X, FoldingSetNodeID &TempID);
2540b57cec5SDimitry Andric };
2550b57cec5SDimitry Andric 
2560b57cec5SDimitry Andric /// FoldingSetTrait - This trait class is used to define behavior of how
2570b57cec5SDimitry Andric /// to "profile" (in the FoldingSet parlance) an object of a given type.
2580b57cec5SDimitry Andric /// The default behavior is to invoke a 'Profile' method on an object, but
2590b57cec5SDimitry Andric /// through template specialization the behavior can be tailored for specific
2600b57cec5SDimitry Andric /// types.  Combined with the FoldingSetNodeWrapper class, one can add objects
2610b57cec5SDimitry Andric /// to FoldingSets that were not originally designed to have that behavior.
26281ad6265SDimitry Andric template <typename T, typename Enable = void>
26381ad6265SDimitry Andric struct FoldingSetTrait : public DefaultFoldingSetTrait<T> {};
2640b57cec5SDimitry Andric 
2650b57cec5SDimitry Andric /// DefaultContextualFoldingSetTrait - Like DefaultFoldingSetTrait, but
2660b57cec5SDimitry Andric /// for ContextualFoldingSets.
2670b57cec5SDimitry Andric template<typename T, typename Ctx>
2680b57cec5SDimitry Andric struct DefaultContextualFoldingSetTrait {
2690b57cec5SDimitry Andric   static void Profile(T &X, FoldingSetNodeID &ID, Ctx Context) {
2700b57cec5SDimitry Andric     X.Profile(ID, Context);
2710b57cec5SDimitry Andric   }
2720b57cec5SDimitry Andric 
2730b57cec5SDimitry Andric   static inline bool Equals(T &X, const FoldingSetNodeID &ID, unsigned IDHash,
2740b57cec5SDimitry Andric                             FoldingSetNodeID &TempID, Ctx Context);
2750b57cec5SDimitry Andric   static inline unsigned ComputeHash(T &X, FoldingSetNodeID &TempID,
2760b57cec5SDimitry Andric                                      Ctx Context);
2770b57cec5SDimitry Andric };
2780b57cec5SDimitry Andric 
2790b57cec5SDimitry Andric /// ContextualFoldingSetTrait - Like FoldingSetTrait, but for
2800b57cec5SDimitry Andric /// ContextualFoldingSets.
2810b57cec5SDimitry Andric template<typename T, typename Ctx> struct ContextualFoldingSetTrait
2820b57cec5SDimitry Andric   : public DefaultContextualFoldingSetTrait<T, Ctx> {};
2830b57cec5SDimitry Andric 
2840b57cec5SDimitry Andric //===--------------------------------------------------------------------===//
2850b57cec5SDimitry Andric /// FoldingSetNodeIDRef - This class describes a reference to an interned
2860b57cec5SDimitry Andric /// FoldingSetNodeID, which can be a useful to store node id data rather
2870b57cec5SDimitry Andric /// than using plain FoldingSetNodeIDs, since the 32-element SmallVector
2880b57cec5SDimitry Andric /// is often much larger than necessary, and the possibility of heap
2890b57cec5SDimitry Andric /// allocation means it requires a non-trivial destructor call.
2900b57cec5SDimitry Andric class FoldingSetNodeIDRef {
2910b57cec5SDimitry Andric   const unsigned *Data = nullptr;
2920b57cec5SDimitry Andric   size_t Size = 0;
2930b57cec5SDimitry Andric 
2940b57cec5SDimitry Andric public:
2950b57cec5SDimitry Andric   FoldingSetNodeIDRef() = default;
2960b57cec5SDimitry Andric   FoldingSetNodeIDRef(const unsigned *D, size_t S) : Data(D), Size(S) {}
2970b57cec5SDimitry Andric 
298*0fca6ea1SDimitry Andric   // Compute a strong hash value used to lookup the node in the FoldingSetBase.
299*0fca6ea1SDimitry Andric   // The hash value is not guaranteed to be deterministic across processes.
30081ad6265SDimitry Andric   unsigned ComputeHash() const {
30181ad6265SDimitry Andric     return static_cast<unsigned>(hash_combine_range(Data, Data + Size));
30281ad6265SDimitry Andric   }
3030b57cec5SDimitry Andric 
304*0fca6ea1SDimitry Andric   // Compute a deterministic hash value across processes that is suitable for
305*0fca6ea1SDimitry Andric   // on-disk serialization.
306*0fca6ea1SDimitry Andric   unsigned computeStableHash() const {
307*0fca6ea1SDimitry Andric     return static_cast<unsigned>(xxh3_64bits(ArrayRef(
308*0fca6ea1SDimitry Andric         reinterpret_cast<const uint8_t *>(Data), sizeof(unsigned) * Size)));
309*0fca6ea1SDimitry Andric   }
310*0fca6ea1SDimitry Andric 
3110b57cec5SDimitry Andric   bool operator==(FoldingSetNodeIDRef) const;
3120b57cec5SDimitry Andric 
3130b57cec5SDimitry Andric   bool operator!=(FoldingSetNodeIDRef RHS) const { return !(*this == RHS); }
3140b57cec5SDimitry Andric 
3150b57cec5SDimitry Andric   /// Used to compare the "ordering" of two nodes as defined by the
3160b57cec5SDimitry Andric   /// profiled bits and their ordering defined by memcmp().
3170b57cec5SDimitry Andric   bool operator<(FoldingSetNodeIDRef) const;
3180b57cec5SDimitry Andric 
3190b57cec5SDimitry Andric   const unsigned *getData() const { return Data; }
3200b57cec5SDimitry Andric   size_t getSize() const { return Size; }
3210b57cec5SDimitry Andric };
3220b57cec5SDimitry Andric 
3230b57cec5SDimitry Andric //===--------------------------------------------------------------------===//
3240b57cec5SDimitry Andric /// FoldingSetNodeID - This class is used to gather all the unique data bits of
3250b57cec5SDimitry Andric /// a node.  When all the bits are gathered this class is used to produce a
3260b57cec5SDimitry Andric /// hash value for the node.
3270b57cec5SDimitry Andric class FoldingSetNodeID {
3280b57cec5SDimitry Andric   /// Bits - Vector of all the data bits that make the node unique.
3290b57cec5SDimitry Andric   /// Use a SmallVector to avoid a heap allocation in the common case.
3300b57cec5SDimitry Andric   SmallVector<unsigned, 32> Bits;
3310b57cec5SDimitry Andric 
3320b57cec5SDimitry Andric public:
3330b57cec5SDimitry Andric   FoldingSetNodeID() = default;
3340b57cec5SDimitry Andric 
3350b57cec5SDimitry Andric   FoldingSetNodeID(FoldingSetNodeIDRef Ref)
3360b57cec5SDimitry Andric     : Bits(Ref.getData(), Ref.getData() + Ref.getSize()) {}
3370b57cec5SDimitry Andric 
3380b57cec5SDimitry Andric   /// Add* - Add various data types to Bit data.
33981ad6265SDimitry Andric   void AddPointer(const void *Ptr) {
34081ad6265SDimitry Andric     // Note: this adds pointers to the hash using sizes and endianness that
34181ad6265SDimitry Andric     // depend on the host. It doesn't matter, however, because hashing on
34281ad6265SDimitry Andric     // pointer values is inherently unstable. Nothing should depend on the
34381ad6265SDimitry Andric     // ordering of nodes in the folding set.
34481ad6265SDimitry Andric     static_assert(sizeof(uintptr_t) <= sizeof(unsigned long long),
34581ad6265SDimitry Andric                   "unexpected pointer size");
34681ad6265SDimitry Andric     AddInteger(reinterpret_cast<uintptr_t>(Ptr));
34781ad6265SDimitry Andric   }
34881ad6265SDimitry Andric   void AddInteger(signed I) { Bits.push_back(I); }
34981ad6265SDimitry Andric   void AddInteger(unsigned I) { Bits.push_back(I); }
35081ad6265SDimitry Andric   void AddInteger(long I) { AddInteger((unsigned long)I); }
35181ad6265SDimitry Andric   void AddInteger(unsigned long I) {
35281ad6265SDimitry Andric     if (sizeof(long) == sizeof(int))
35381ad6265SDimitry Andric       AddInteger(unsigned(I));
35481ad6265SDimitry Andric     else if (sizeof(long) == sizeof(long long)) {
35581ad6265SDimitry Andric       AddInteger((unsigned long long)I);
35681ad6265SDimitry Andric     } else {
35781ad6265SDimitry Andric       llvm_unreachable("unexpected sizeof(long)");
35881ad6265SDimitry Andric     }
35981ad6265SDimitry Andric   }
36081ad6265SDimitry Andric   void AddInteger(long long I) { AddInteger((unsigned long long)I); }
36181ad6265SDimitry Andric   void AddInteger(unsigned long long I) {
36281ad6265SDimitry Andric     AddInteger(unsigned(I));
36381ad6265SDimitry Andric     AddInteger(unsigned(I >> 32));
36481ad6265SDimitry Andric   }
36581ad6265SDimitry Andric 
3660b57cec5SDimitry Andric   void AddBoolean(bool B) { AddInteger(B ? 1U : 0U); }
3670b57cec5SDimitry Andric   void AddString(StringRef String);
3680b57cec5SDimitry Andric   void AddNodeID(const FoldingSetNodeID &ID);
3690b57cec5SDimitry Andric 
3700b57cec5SDimitry Andric   template <typename T>
3710b57cec5SDimitry Andric   inline void Add(const T &x) { FoldingSetTrait<T>::Profile(x, *this); }
3720b57cec5SDimitry Andric 
3730b57cec5SDimitry Andric   /// clear - Clear the accumulated profile, allowing this FoldingSetNodeID
3740b57cec5SDimitry Andric   /// object to be used to compute a new profile.
3750b57cec5SDimitry Andric   inline void clear() { Bits.clear(); }
3760b57cec5SDimitry Andric 
377*0fca6ea1SDimitry Andric   // Compute a strong hash value for this FoldingSetNodeID, used to lookup the
378*0fca6ea1SDimitry Andric   // node in the FoldingSetBase. The hash value is not guaranteed to be
379*0fca6ea1SDimitry Andric   // deterministic across processes.
38081ad6265SDimitry Andric   unsigned ComputeHash() const {
38181ad6265SDimitry Andric     return FoldingSetNodeIDRef(Bits.data(), Bits.size()).ComputeHash();
38281ad6265SDimitry Andric   }
3830b57cec5SDimitry Andric 
384*0fca6ea1SDimitry Andric   // Compute a deterministic hash value across processes that is suitable for
385*0fca6ea1SDimitry Andric   // on-disk serialization.
386*0fca6ea1SDimitry Andric   unsigned computeStableHash() const {
387*0fca6ea1SDimitry Andric     return FoldingSetNodeIDRef(Bits.data(), Bits.size()).computeStableHash();
388*0fca6ea1SDimitry Andric   }
389*0fca6ea1SDimitry Andric 
3900b57cec5SDimitry Andric   /// operator== - Used to compare two nodes to each other.
3910b57cec5SDimitry Andric   bool operator==(const FoldingSetNodeID &RHS) const;
3920b57cec5SDimitry Andric   bool operator==(const FoldingSetNodeIDRef RHS) const;
3930b57cec5SDimitry Andric 
3940b57cec5SDimitry Andric   bool operator!=(const FoldingSetNodeID &RHS) const { return !(*this == RHS); }
3950b57cec5SDimitry Andric   bool operator!=(const FoldingSetNodeIDRef RHS) const { return !(*this ==RHS);}
3960b57cec5SDimitry Andric 
3970b57cec5SDimitry Andric   /// Used to compare the "ordering" of two nodes as defined by the
3980b57cec5SDimitry Andric   /// profiled bits and their ordering defined by memcmp().
3990b57cec5SDimitry Andric   bool operator<(const FoldingSetNodeID &RHS) const;
4000b57cec5SDimitry Andric   bool operator<(const FoldingSetNodeIDRef RHS) const;
4010b57cec5SDimitry Andric 
4020b57cec5SDimitry Andric   /// Intern - Copy this node's data to a memory region allocated from the
4030b57cec5SDimitry Andric   /// given allocator and return a FoldingSetNodeIDRef describing the
4040b57cec5SDimitry Andric   /// interned data.
4050b57cec5SDimitry Andric   FoldingSetNodeIDRef Intern(BumpPtrAllocator &Allocator) const;
4060b57cec5SDimitry Andric };
4070b57cec5SDimitry Andric 
4080b57cec5SDimitry Andric // Convenience type to hide the implementation of the folding set.
4090b57cec5SDimitry Andric using FoldingSetNode = FoldingSetBase::Node;
4100b57cec5SDimitry Andric template<class T> class FoldingSetIterator;
4110b57cec5SDimitry Andric template<class T> class FoldingSetBucketIterator;
4120b57cec5SDimitry Andric 
4130b57cec5SDimitry Andric // Definitions of FoldingSetTrait and ContextualFoldingSetTrait functions, which
4140b57cec5SDimitry Andric // require the definition of FoldingSetNodeID.
4150b57cec5SDimitry Andric template<typename T>
4160b57cec5SDimitry Andric inline bool
4170b57cec5SDimitry Andric DefaultFoldingSetTrait<T>::Equals(T &X, const FoldingSetNodeID &ID,
4180b57cec5SDimitry Andric                                   unsigned /*IDHash*/,
4190b57cec5SDimitry Andric                                   FoldingSetNodeID &TempID) {
4200b57cec5SDimitry Andric   FoldingSetTrait<T>::Profile(X, TempID);
4210b57cec5SDimitry Andric   return TempID == ID;
4220b57cec5SDimitry Andric }
4230b57cec5SDimitry Andric template<typename T>
4240b57cec5SDimitry Andric inline unsigned
4250b57cec5SDimitry Andric DefaultFoldingSetTrait<T>::ComputeHash(T &X, FoldingSetNodeID &TempID) {
4260b57cec5SDimitry Andric   FoldingSetTrait<T>::Profile(X, TempID);
4270b57cec5SDimitry Andric   return TempID.ComputeHash();
4280b57cec5SDimitry Andric }
4290b57cec5SDimitry Andric template<typename T, typename Ctx>
4300b57cec5SDimitry Andric inline bool
4310b57cec5SDimitry Andric DefaultContextualFoldingSetTrait<T, Ctx>::Equals(T &X,
4320b57cec5SDimitry Andric                                                  const FoldingSetNodeID &ID,
4330b57cec5SDimitry Andric                                                  unsigned /*IDHash*/,
4340b57cec5SDimitry Andric                                                  FoldingSetNodeID &TempID,
4350b57cec5SDimitry Andric                                                  Ctx Context) {
4360b57cec5SDimitry Andric   ContextualFoldingSetTrait<T, Ctx>::Profile(X, TempID, Context);
4370b57cec5SDimitry Andric   return TempID == ID;
4380b57cec5SDimitry Andric }
4390b57cec5SDimitry Andric template<typename T, typename Ctx>
4400b57cec5SDimitry Andric inline unsigned
4410b57cec5SDimitry Andric DefaultContextualFoldingSetTrait<T, Ctx>::ComputeHash(T &X,
4420b57cec5SDimitry Andric                                                       FoldingSetNodeID &TempID,
4430b57cec5SDimitry Andric                                                       Ctx Context) {
4440b57cec5SDimitry Andric   ContextualFoldingSetTrait<T, Ctx>::Profile(X, TempID, Context);
4450b57cec5SDimitry Andric   return TempID.ComputeHash();
4460b57cec5SDimitry Andric }
4470b57cec5SDimitry Andric 
4480b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
4490b57cec5SDimitry Andric /// FoldingSetImpl - An implementation detail that lets us share code between
4500b57cec5SDimitry Andric /// FoldingSet and ContextualFoldingSet.
4515ffd83dbSDimitry Andric template <class Derived, class T> class FoldingSetImpl : public FoldingSetBase {
4520b57cec5SDimitry Andric protected:
4530b57cec5SDimitry Andric   explicit FoldingSetImpl(unsigned Log2InitSize)
4540b57cec5SDimitry Andric       : FoldingSetBase(Log2InitSize) {}
4550b57cec5SDimitry Andric 
4560b57cec5SDimitry Andric   FoldingSetImpl(FoldingSetImpl &&Arg) = default;
4570b57cec5SDimitry Andric   FoldingSetImpl &operator=(FoldingSetImpl &&RHS) = default;
4580b57cec5SDimitry Andric   ~FoldingSetImpl() = default;
4590b57cec5SDimitry Andric 
4600b57cec5SDimitry Andric public:
4610b57cec5SDimitry Andric   using iterator = FoldingSetIterator<T>;
4620b57cec5SDimitry Andric 
4630b57cec5SDimitry Andric   iterator begin() { return iterator(Buckets); }
4640b57cec5SDimitry Andric   iterator end() { return iterator(Buckets+NumBuckets); }
4650b57cec5SDimitry Andric 
4660b57cec5SDimitry Andric   using const_iterator = FoldingSetIterator<const T>;
4670b57cec5SDimitry Andric 
4680b57cec5SDimitry Andric   const_iterator begin() const { return const_iterator(Buckets); }
4690b57cec5SDimitry Andric   const_iterator end() const { return const_iterator(Buckets+NumBuckets); }
4700b57cec5SDimitry Andric 
4710b57cec5SDimitry Andric   using bucket_iterator = FoldingSetBucketIterator<T>;
4720b57cec5SDimitry Andric 
4730b57cec5SDimitry Andric   bucket_iterator bucket_begin(unsigned hash) {
4740b57cec5SDimitry Andric     return bucket_iterator(Buckets + (hash & (NumBuckets-1)));
4750b57cec5SDimitry Andric   }
4760b57cec5SDimitry Andric 
4770b57cec5SDimitry Andric   bucket_iterator bucket_end(unsigned hash) {
4780b57cec5SDimitry Andric     return bucket_iterator(Buckets + (hash & (NumBuckets-1)), true);
4790b57cec5SDimitry Andric   }
4800b57cec5SDimitry Andric 
4815ffd83dbSDimitry Andric   /// reserve - Increase the number of buckets such that adding the
4825ffd83dbSDimitry Andric   /// EltCount-th node won't cause a rebucket operation. reserve is permitted
4835ffd83dbSDimitry Andric   /// to allocate more space than requested by EltCount.
4845ffd83dbSDimitry Andric   void reserve(unsigned EltCount) {
4855ffd83dbSDimitry Andric     return FoldingSetBase::reserve(EltCount, Derived::getFoldingSetInfo());
4865ffd83dbSDimitry Andric   }
4875ffd83dbSDimitry Andric 
4880b57cec5SDimitry Andric   /// RemoveNode - Remove a node from the folding set, returning true if one
4890b57cec5SDimitry Andric   /// was removed or false if the node was not in the folding set.
4905ffd83dbSDimitry Andric   bool RemoveNode(T *N) {
4915ffd83dbSDimitry Andric     return FoldingSetBase::RemoveNode(N);
4925ffd83dbSDimitry Andric   }
4930b57cec5SDimitry Andric 
4940b57cec5SDimitry Andric   /// GetOrInsertNode - If there is an existing simple Node exactly
4950b57cec5SDimitry Andric   /// equal to the specified node, return it.  Otherwise, insert 'N' and
4960b57cec5SDimitry Andric   /// return it instead.
4970b57cec5SDimitry Andric   T *GetOrInsertNode(T *N) {
4985ffd83dbSDimitry Andric     return static_cast<T *>(
4995ffd83dbSDimitry Andric         FoldingSetBase::GetOrInsertNode(N, Derived::getFoldingSetInfo()));
5000b57cec5SDimitry Andric   }
5010b57cec5SDimitry Andric 
5020b57cec5SDimitry Andric   /// FindNodeOrInsertPos - Look up the node specified by ID.  If it exists,
5030b57cec5SDimitry Andric   /// return it.  If not, return the insertion token that will make insertion
5040b57cec5SDimitry Andric   /// faster.
5050b57cec5SDimitry Andric   T *FindNodeOrInsertPos(const FoldingSetNodeID &ID, void *&InsertPos) {
5065ffd83dbSDimitry Andric     return static_cast<T *>(FoldingSetBase::FindNodeOrInsertPos(
5075ffd83dbSDimitry Andric         ID, InsertPos, Derived::getFoldingSetInfo()));
5080b57cec5SDimitry Andric   }
5090b57cec5SDimitry Andric 
5100b57cec5SDimitry Andric   /// InsertNode - Insert the specified node into the folding set, knowing that
5110b57cec5SDimitry Andric   /// it is not already in the folding set.  InsertPos must be obtained from
5120b57cec5SDimitry Andric   /// FindNodeOrInsertPos.
5130b57cec5SDimitry Andric   void InsertNode(T *N, void *InsertPos) {
5145ffd83dbSDimitry Andric     FoldingSetBase::InsertNode(N, InsertPos, Derived::getFoldingSetInfo());
5150b57cec5SDimitry Andric   }
5160b57cec5SDimitry Andric 
5170b57cec5SDimitry Andric   /// InsertNode - Insert the specified node into the folding set, knowing that
5180b57cec5SDimitry Andric   /// it is not already in the folding set.
5190b57cec5SDimitry Andric   void InsertNode(T *N) {
5200b57cec5SDimitry Andric     T *Inserted = GetOrInsertNode(N);
5210b57cec5SDimitry Andric     (void)Inserted;
5220b57cec5SDimitry Andric     assert(Inserted == N && "Node already inserted!");
5230b57cec5SDimitry Andric   }
5240b57cec5SDimitry Andric };
5250b57cec5SDimitry Andric 
5260b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
5270b57cec5SDimitry Andric /// FoldingSet - This template class is used to instantiate a specialized
5280b57cec5SDimitry Andric /// implementation of the folding set to the node class T.  T must be a
5290b57cec5SDimitry Andric /// subclass of FoldingSetNode and implement a Profile function.
5300b57cec5SDimitry Andric ///
5310b57cec5SDimitry Andric /// Note that this set type is movable and move-assignable. However, its
5320b57cec5SDimitry Andric /// moved-from state is not a valid state for anything other than
5330b57cec5SDimitry Andric /// move-assigning and destroying. This is primarily to enable movable APIs
5340b57cec5SDimitry Andric /// that incorporate these objects.
5355ffd83dbSDimitry Andric template <class T>
5365ffd83dbSDimitry Andric class FoldingSet : public FoldingSetImpl<FoldingSet<T>, T> {
5375ffd83dbSDimitry Andric   using Super = FoldingSetImpl<FoldingSet, T>;
5380b57cec5SDimitry Andric   using Node = typename Super::Node;
5390b57cec5SDimitry Andric 
5405ffd83dbSDimitry Andric   /// GetNodeProfile - Each instantiation of the FoldingSet needs to provide a
5410b57cec5SDimitry Andric   /// way to convert nodes into a unique specifier.
5425ffd83dbSDimitry Andric   static void GetNodeProfile(const FoldingSetBase *, Node *N,
5435ffd83dbSDimitry Andric                              FoldingSetNodeID &ID) {
5440b57cec5SDimitry Andric     T *TN = static_cast<T *>(N);
5450b57cec5SDimitry Andric     FoldingSetTrait<T>::Profile(*TN, ID);
5460b57cec5SDimitry Andric   }
5470b57cec5SDimitry Andric 
5480b57cec5SDimitry Andric   /// NodeEquals - Instantiations may optionally provide a way to compare a
5490b57cec5SDimitry Andric   /// node with a specified ID.
5505ffd83dbSDimitry Andric   static bool NodeEquals(const FoldingSetBase *, Node *N,
5515ffd83dbSDimitry Andric                          const FoldingSetNodeID &ID, unsigned IDHash,
5525ffd83dbSDimitry Andric                          FoldingSetNodeID &TempID) {
5530b57cec5SDimitry Andric     T *TN = static_cast<T *>(N);
5540b57cec5SDimitry Andric     return FoldingSetTrait<T>::Equals(*TN, ID, IDHash, TempID);
5550b57cec5SDimitry Andric   }
5560b57cec5SDimitry Andric 
5570b57cec5SDimitry Andric   /// ComputeNodeHash - Instantiations may optionally provide a way to compute a
5580b57cec5SDimitry Andric   /// hash value directly from a node.
5595ffd83dbSDimitry Andric   static unsigned ComputeNodeHash(const FoldingSetBase *, Node *N,
5605ffd83dbSDimitry Andric                                   FoldingSetNodeID &TempID) {
5610b57cec5SDimitry Andric     T *TN = static_cast<T *>(N);
5620b57cec5SDimitry Andric     return FoldingSetTrait<T>::ComputeHash(*TN, TempID);
5630b57cec5SDimitry Andric   }
5640b57cec5SDimitry Andric 
5655ffd83dbSDimitry Andric   static const FoldingSetBase::FoldingSetInfo &getFoldingSetInfo() {
5665ffd83dbSDimitry Andric     static constexpr FoldingSetBase::FoldingSetInfo Info = {
5675ffd83dbSDimitry Andric         GetNodeProfile, NodeEquals, ComputeNodeHash};
5685ffd83dbSDimitry Andric     return Info;
5695ffd83dbSDimitry Andric   }
5705ffd83dbSDimitry Andric   friend Super;
5715ffd83dbSDimitry Andric 
5720b57cec5SDimitry Andric public:
5730b57cec5SDimitry Andric   explicit FoldingSet(unsigned Log2InitSize = 6) : Super(Log2InitSize) {}
5740b57cec5SDimitry Andric   FoldingSet(FoldingSet &&Arg) = default;
5750b57cec5SDimitry Andric   FoldingSet &operator=(FoldingSet &&RHS) = default;
5760b57cec5SDimitry Andric };
5770b57cec5SDimitry Andric 
5780b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
5790b57cec5SDimitry Andric /// ContextualFoldingSet - This template class is a further refinement
5800b57cec5SDimitry Andric /// of FoldingSet which provides a context argument when calling
5810b57cec5SDimitry Andric /// Profile on its nodes.  Currently, that argument is fixed at
5820b57cec5SDimitry Andric /// initialization time.
5830b57cec5SDimitry Andric ///
5840b57cec5SDimitry Andric /// T must be a subclass of FoldingSetNode and implement a Profile
5850b57cec5SDimitry Andric /// function with signature
5860b57cec5SDimitry Andric ///   void Profile(FoldingSetNodeID &, Ctx);
5870b57cec5SDimitry Andric template <class T, class Ctx>
5885ffd83dbSDimitry Andric class ContextualFoldingSet
5895ffd83dbSDimitry Andric     : public FoldingSetImpl<ContextualFoldingSet<T, Ctx>, T> {
5900b57cec5SDimitry Andric   // Unfortunately, this can't derive from FoldingSet<T> because the
5910b57cec5SDimitry Andric   // construction of the vtable for FoldingSet<T> requires
5920b57cec5SDimitry Andric   // FoldingSet<T>::GetNodeProfile to be instantiated, which in turn
5930b57cec5SDimitry Andric   // requires a single-argument T::Profile().
5940b57cec5SDimitry Andric 
5955ffd83dbSDimitry Andric   using Super = FoldingSetImpl<ContextualFoldingSet, T>;
5960b57cec5SDimitry Andric   using Node = typename Super::Node;
5970b57cec5SDimitry Andric 
5980b57cec5SDimitry Andric   Ctx Context;
5990b57cec5SDimitry Andric 
6005ffd83dbSDimitry Andric   static const Ctx &getContext(const FoldingSetBase *Base) {
6015ffd83dbSDimitry Andric     return static_cast<const ContextualFoldingSet*>(Base)->Context;
6025ffd83dbSDimitry Andric   }
6035ffd83dbSDimitry Andric 
6040b57cec5SDimitry Andric   /// GetNodeProfile - Each instantiatation of the FoldingSet needs to provide a
6050b57cec5SDimitry Andric   /// way to convert nodes into a unique specifier.
6065ffd83dbSDimitry Andric   static void GetNodeProfile(const FoldingSetBase *Base, Node *N,
6075ffd83dbSDimitry Andric                              FoldingSetNodeID &ID) {
6080b57cec5SDimitry Andric     T *TN = static_cast<T *>(N);
6095ffd83dbSDimitry Andric     ContextualFoldingSetTrait<T, Ctx>::Profile(*TN, ID, getContext(Base));
6100b57cec5SDimitry Andric   }
6110b57cec5SDimitry Andric 
6125ffd83dbSDimitry Andric   static bool NodeEquals(const FoldingSetBase *Base, Node *N,
6135ffd83dbSDimitry Andric                          const FoldingSetNodeID &ID, unsigned IDHash,
6145ffd83dbSDimitry Andric                          FoldingSetNodeID &TempID) {
6150b57cec5SDimitry Andric     T *TN = static_cast<T *>(N);
6160b57cec5SDimitry Andric     return ContextualFoldingSetTrait<T, Ctx>::Equals(*TN, ID, IDHash, TempID,
6175ffd83dbSDimitry Andric                                                      getContext(Base));
6180b57cec5SDimitry Andric   }
6190b57cec5SDimitry Andric 
6205ffd83dbSDimitry Andric   static unsigned ComputeNodeHash(const FoldingSetBase *Base, Node *N,
6215ffd83dbSDimitry Andric                                   FoldingSetNodeID &TempID) {
6220b57cec5SDimitry Andric     T *TN = static_cast<T *>(N);
6235ffd83dbSDimitry Andric     return ContextualFoldingSetTrait<T, Ctx>::ComputeHash(*TN, TempID,
6245ffd83dbSDimitry Andric                                                           getContext(Base));
6250b57cec5SDimitry Andric   }
6260b57cec5SDimitry Andric 
6275ffd83dbSDimitry Andric   static const FoldingSetBase::FoldingSetInfo &getFoldingSetInfo() {
6285ffd83dbSDimitry Andric     static constexpr FoldingSetBase::FoldingSetInfo Info = {
6295ffd83dbSDimitry Andric         GetNodeProfile, NodeEquals, ComputeNodeHash};
6305ffd83dbSDimitry Andric     return Info;
6315ffd83dbSDimitry Andric   }
6325ffd83dbSDimitry Andric   friend Super;
6335ffd83dbSDimitry Andric 
6340b57cec5SDimitry Andric public:
6350b57cec5SDimitry Andric   explicit ContextualFoldingSet(Ctx Context, unsigned Log2InitSize = 6)
6360b57cec5SDimitry Andric       : Super(Log2InitSize), Context(Context) {}
6370b57cec5SDimitry Andric 
6380b57cec5SDimitry Andric   Ctx getContext() const { return Context; }
6390b57cec5SDimitry Andric };
6400b57cec5SDimitry Andric 
6410b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
6420b57cec5SDimitry Andric /// FoldingSetVector - This template class combines a FoldingSet and a vector
6430b57cec5SDimitry Andric /// to provide the interface of FoldingSet but with deterministic iteration
6440b57cec5SDimitry Andric /// order based on the insertion order. T must be a subclass of FoldingSetNode
6450b57cec5SDimitry Andric /// and implement a Profile function.
6460b57cec5SDimitry Andric template <class T, class VectorT = SmallVector<T*, 8>>
6470b57cec5SDimitry Andric class FoldingSetVector {
6480b57cec5SDimitry Andric   FoldingSet<T> Set;
6490b57cec5SDimitry Andric   VectorT Vector;
6500b57cec5SDimitry Andric 
6510b57cec5SDimitry Andric public:
6520b57cec5SDimitry Andric   explicit FoldingSetVector(unsigned Log2InitSize = 6) : Set(Log2InitSize) {}
6530b57cec5SDimitry Andric 
6540b57cec5SDimitry Andric   using iterator = pointee_iterator<typename VectorT::iterator>;
6550b57cec5SDimitry Andric 
6560b57cec5SDimitry Andric   iterator begin() { return Vector.begin(); }
6570b57cec5SDimitry Andric   iterator end()   { return Vector.end(); }
6580b57cec5SDimitry Andric 
6590b57cec5SDimitry Andric   using const_iterator = pointee_iterator<typename VectorT::const_iterator>;
6600b57cec5SDimitry Andric 
6610b57cec5SDimitry Andric   const_iterator begin() const { return Vector.begin(); }
6620b57cec5SDimitry Andric   const_iterator end()   const { return Vector.end(); }
6630b57cec5SDimitry Andric 
6640b57cec5SDimitry Andric   /// clear - Remove all nodes from the folding set.
6650b57cec5SDimitry Andric   void clear() { Set.clear(); Vector.clear(); }
6660b57cec5SDimitry Andric 
6670b57cec5SDimitry Andric   /// FindNodeOrInsertPos - Look up the node specified by ID.  If it exists,
6680b57cec5SDimitry Andric   /// return it.  If not, return the insertion token that will make insertion
6690b57cec5SDimitry Andric   /// faster.
6700b57cec5SDimitry Andric   T *FindNodeOrInsertPos(const FoldingSetNodeID &ID, void *&InsertPos) {
6710b57cec5SDimitry Andric     return Set.FindNodeOrInsertPos(ID, InsertPos);
6720b57cec5SDimitry Andric   }
6730b57cec5SDimitry Andric 
6740b57cec5SDimitry Andric   /// GetOrInsertNode - If there is an existing simple Node exactly
6750b57cec5SDimitry Andric   /// equal to the specified node, return it.  Otherwise, insert 'N' and
6760b57cec5SDimitry Andric   /// return it instead.
6770b57cec5SDimitry Andric   T *GetOrInsertNode(T *N) {
6780b57cec5SDimitry Andric     T *Result = Set.GetOrInsertNode(N);
6790b57cec5SDimitry Andric     if (Result == N) Vector.push_back(N);
6800b57cec5SDimitry Andric     return Result;
6810b57cec5SDimitry Andric   }
6820b57cec5SDimitry Andric 
6830b57cec5SDimitry Andric   /// InsertNode - Insert the specified node into the folding set, knowing that
6840b57cec5SDimitry Andric   /// it is not already in the folding set.  InsertPos must be obtained from
6850b57cec5SDimitry Andric   /// FindNodeOrInsertPos.
6860b57cec5SDimitry Andric   void InsertNode(T *N, void *InsertPos) {
6870b57cec5SDimitry Andric     Set.InsertNode(N, InsertPos);
6880b57cec5SDimitry Andric     Vector.push_back(N);
6890b57cec5SDimitry Andric   }
6900b57cec5SDimitry Andric 
6910b57cec5SDimitry Andric   /// InsertNode - Insert the specified node into the folding set, knowing that
6920b57cec5SDimitry Andric   /// it is not already in the folding set.
6930b57cec5SDimitry Andric   void InsertNode(T *N) {
6940b57cec5SDimitry Andric     Set.InsertNode(N);
6950b57cec5SDimitry Andric     Vector.push_back(N);
6960b57cec5SDimitry Andric   }
6970b57cec5SDimitry Andric 
6980b57cec5SDimitry Andric   /// size - Returns the number of nodes in the folding set.
6990b57cec5SDimitry Andric   unsigned size() const { return Set.size(); }
7000b57cec5SDimitry Andric 
7010b57cec5SDimitry Andric   /// empty - Returns true if there are no nodes in the folding set.
7020b57cec5SDimitry Andric   bool empty() const { return Set.empty(); }
7030b57cec5SDimitry Andric };
7040b57cec5SDimitry Andric 
7050b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
7060b57cec5SDimitry Andric /// FoldingSetIteratorImpl - This is the common iterator support shared by all
7070b57cec5SDimitry Andric /// folding sets, which knows how to walk the folding set hash table.
7080b57cec5SDimitry Andric class FoldingSetIteratorImpl {
7090b57cec5SDimitry Andric protected:
7100b57cec5SDimitry Andric   FoldingSetNode *NodePtr;
7110b57cec5SDimitry Andric 
7120b57cec5SDimitry Andric   FoldingSetIteratorImpl(void **Bucket);
7130b57cec5SDimitry Andric 
7140b57cec5SDimitry Andric   void advance();
7150b57cec5SDimitry Andric 
7160b57cec5SDimitry Andric public:
7170b57cec5SDimitry Andric   bool operator==(const FoldingSetIteratorImpl &RHS) const {
7180b57cec5SDimitry Andric     return NodePtr == RHS.NodePtr;
7190b57cec5SDimitry Andric   }
7200b57cec5SDimitry Andric   bool operator!=(const FoldingSetIteratorImpl &RHS) const {
7210b57cec5SDimitry Andric     return NodePtr != RHS.NodePtr;
7220b57cec5SDimitry Andric   }
7230b57cec5SDimitry Andric };
7240b57cec5SDimitry Andric 
7250b57cec5SDimitry Andric template <class T> class FoldingSetIterator : public FoldingSetIteratorImpl {
7260b57cec5SDimitry Andric public:
7270b57cec5SDimitry Andric   explicit FoldingSetIterator(void **Bucket) : FoldingSetIteratorImpl(Bucket) {}
7280b57cec5SDimitry Andric 
7290b57cec5SDimitry Andric   T &operator*() const {
7300b57cec5SDimitry Andric     return *static_cast<T*>(NodePtr);
7310b57cec5SDimitry Andric   }
7320b57cec5SDimitry Andric 
7330b57cec5SDimitry Andric   T *operator->() const {
7340b57cec5SDimitry Andric     return static_cast<T*>(NodePtr);
7350b57cec5SDimitry Andric   }
7360b57cec5SDimitry Andric 
7370b57cec5SDimitry Andric   inline FoldingSetIterator &operator++() {          // Preincrement
7380b57cec5SDimitry Andric     advance();
7390b57cec5SDimitry Andric     return *this;
7400b57cec5SDimitry Andric   }
7410b57cec5SDimitry Andric   FoldingSetIterator operator++(int) {        // Postincrement
7420b57cec5SDimitry Andric     FoldingSetIterator tmp = *this; ++*this; return tmp;
7430b57cec5SDimitry Andric   }
7440b57cec5SDimitry Andric };
7450b57cec5SDimitry Andric 
7460b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
7470b57cec5SDimitry Andric /// FoldingSetBucketIteratorImpl - This is the common bucket iterator support
7480b57cec5SDimitry Andric /// shared by all folding sets, which knows how to walk a particular bucket
7490b57cec5SDimitry Andric /// of a folding set hash table.
7500b57cec5SDimitry Andric class FoldingSetBucketIteratorImpl {
7510b57cec5SDimitry Andric protected:
7520b57cec5SDimitry Andric   void *Ptr;
7530b57cec5SDimitry Andric 
7540b57cec5SDimitry Andric   explicit FoldingSetBucketIteratorImpl(void **Bucket);
7550b57cec5SDimitry Andric 
7560b57cec5SDimitry Andric   FoldingSetBucketIteratorImpl(void **Bucket, bool) : Ptr(Bucket) {}
7570b57cec5SDimitry Andric 
7580b57cec5SDimitry Andric   void advance() {
7590b57cec5SDimitry Andric     void *Probe = static_cast<FoldingSetNode*>(Ptr)->getNextInBucket();
7600b57cec5SDimitry Andric     uintptr_t x = reinterpret_cast<uintptr_t>(Probe) & ~0x1;
7610b57cec5SDimitry Andric     Ptr = reinterpret_cast<void*>(x);
7620b57cec5SDimitry Andric   }
7630b57cec5SDimitry Andric 
7640b57cec5SDimitry Andric public:
7650b57cec5SDimitry Andric   bool operator==(const FoldingSetBucketIteratorImpl &RHS) const {
7660b57cec5SDimitry Andric     return Ptr == RHS.Ptr;
7670b57cec5SDimitry Andric   }
7680b57cec5SDimitry Andric   bool operator!=(const FoldingSetBucketIteratorImpl &RHS) const {
7690b57cec5SDimitry Andric     return Ptr != RHS.Ptr;
7700b57cec5SDimitry Andric   }
7710b57cec5SDimitry Andric };
7720b57cec5SDimitry Andric 
7730b57cec5SDimitry Andric template <class T>
7740b57cec5SDimitry Andric class FoldingSetBucketIterator : public FoldingSetBucketIteratorImpl {
7750b57cec5SDimitry Andric public:
7760b57cec5SDimitry Andric   explicit FoldingSetBucketIterator(void **Bucket) :
7770b57cec5SDimitry Andric     FoldingSetBucketIteratorImpl(Bucket) {}
7780b57cec5SDimitry Andric 
7790b57cec5SDimitry Andric   FoldingSetBucketIterator(void **Bucket, bool) :
7800b57cec5SDimitry Andric     FoldingSetBucketIteratorImpl(Bucket, true) {}
7810b57cec5SDimitry Andric 
7820b57cec5SDimitry Andric   T &operator*() const { return *static_cast<T*>(Ptr); }
7830b57cec5SDimitry Andric   T *operator->() const { return static_cast<T*>(Ptr); }
7840b57cec5SDimitry Andric 
7850b57cec5SDimitry Andric   inline FoldingSetBucketIterator &operator++() { // Preincrement
7860b57cec5SDimitry Andric     advance();
7870b57cec5SDimitry Andric     return *this;
7880b57cec5SDimitry Andric   }
7890b57cec5SDimitry Andric   FoldingSetBucketIterator operator++(int) {      // Postincrement
7900b57cec5SDimitry Andric     FoldingSetBucketIterator tmp = *this; ++*this; return tmp;
7910b57cec5SDimitry Andric   }
7920b57cec5SDimitry Andric };
7930b57cec5SDimitry Andric 
7940b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
7950b57cec5SDimitry Andric /// FoldingSetNodeWrapper - This template class is used to "wrap" arbitrary
7960b57cec5SDimitry Andric /// types in an enclosing object so that they can be inserted into FoldingSets.
7970b57cec5SDimitry Andric template <typename T>
7980b57cec5SDimitry Andric class FoldingSetNodeWrapper : public FoldingSetNode {
7990b57cec5SDimitry Andric   T data;
8000b57cec5SDimitry Andric 
8010b57cec5SDimitry Andric public:
8020b57cec5SDimitry Andric   template <typename... Ts>
8030b57cec5SDimitry Andric   explicit FoldingSetNodeWrapper(Ts &&... Args)
8040b57cec5SDimitry Andric       : data(std::forward<Ts>(Args)...) {}
8050b57cec5SDimitry Andric 
8060b57cec5SDimitry Andric   void Profile(FoldingSetNodeID &ID) { FoldingSetTrait<T>::Profile(data, ID); }
8070b57cec5SDimitry Andric 
8080b57cec5SDimitry Andric   T &getValue() { return data; }
8090b57cec5SDimitry Andric   const T &getValue() const { return data; }
8100b57cec5SDimitry Andric 
8110b57cec5SDimitry Andric   operator T&() { return data; }
8120b57cec5SDimitry Andric   operator const T&() const { return data; }
8130b57cec5SDimitry Andric };
8140b57cec5SDimitry Andric 
8150b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8160b57cec5SDimitry Andric /// FastFoldingSetNode - This is a subclass of FoldingSetNode which stores
8170b57cec5SDimitry Andric /// a FoldingSetNodeID value rather than requiring the node to recompute it
8180b57cec5SDimitry Andric /// each time it is needed. This trades space for speed (which can be
8190b57cec5SDimitry Andric /// significant if the ID is long), and it also permits nodes to drop
8200b57cec5SDimitry Andric /// information that would otherwise only be required for recomputing an ID.
8210b57cec5SDimitry Andric class FastFoldingSetNode : public FoldingSetNode {
8220b57cec5SDimitry Andric   FoldingSetNodeID FastID;
8230b57cec5SDimitry Andric 
8240b57cec5SDimitry Andric protected:
8250b57cec5SDimitry Andric   explicit FastFoldingSetNode(const FoldingSetNodeID &ID) : FastID(ID) {}
8260b57cec5SDimitry Andric 
8270b57cec5SDimitry Andric public:
8280b57cec5SDimitry Andric   void Profile(FoldingSetNodeID &ID) const { ID.AddNodeID(FastID); }
8290b57cec5SDimitry Andric };
8300b57cec5SDimitry Andric 
8310b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8320b57cec5SDimitry Andric // Partial specializations of FoldingSetTrait.
8330b57cec5SDimitry Andric 
8340b57cec5SDimitry Andric template<typename T> struct FoldingSetTrait<T*> {
8350b57cec5SDimitry Andric   static inline void Profile(T *X, FoldingSetNodeID &ID) {
8360b57cec5SDimitry Andric     ID.AddPointer(X);
8370b57cec5SDimitry Andric   }
8380b57cec5SDimitry Andric };
8390b57cec5SDimitry Andric template <typename T1, typename T2>
8400b57cec5SDimitry Andric struct FoldingSetTrait<std::pair<T1, T2>> {
8410b57cec5SDimitry Andric   static inline void Profile(const std::pair<T1, T2> &P,
8420b57cec5SDimitry Andric                              FoldingSetNodeID &ID) {
8430b57cec5SDimitry Andric     ID.Add(P.first);
8440b57cec5SDimitry Andric     ID.Add(P.second);
8450b57cec5SDimitry Andric   }
8460b57cec5SDimitry Andric };
8470b57cec5SDimitry Andric 
84881ad6265SDimitry Andric template <typename T>
849bdd1243dSDimitry Andric struct FoldingSetTrait<T, std::enable_if_t<std::is_enum<T>::value>> {
85081ad6265SDimitry Andric   static void Profile(const T &X, FoldingSetNodeID &ID) {
8515f757f3fSDimitry Andric     ID.AddInteger(llvm::to_underlying(X));
85281ad6265SDimitry Andric   }
85381ad6265SDimitry Andric };
85481ad6265SDimitry Andric 
8550b57cec5SDimitry Andric } // end namespace llvm
8560b57cec5SDimitry Andric 
8570b57cec5SDimitry Andric #endif // LLVM_ADT_FOLDINGSET_H
858