1 //==--- ImmutableList.h - Immutable (functional) list interface --*- C++ -*-==// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 /// 9 /// \file 10 /// This file defines the ImmutableList class. 11 /// 12 //===----------------------------------------------------------------------===// 13 14 #ifndef LLVM_ADT_IMMUTABLELIST_H 15 #define LLVM_ADT_IMMUTABLELIST_H 16 17 #include "llvm/ADT/FoldingSet.h" 18 #include "llvm/Support/Allocator.h" 19 #include <cassert> 20 #include <cstdint> 21 #include <new> 22 23 namespace llvm { 24 25 template <typename T> class ImmutableListFactory; 26 27 template <typename T> 28 class ImmutableListImpl : public FoldingSetNode { 29 friend class ImmutableListFactory<T>; 30 31 T Head; 32 const ImmutableListImpl* Tail; 33 34 template <typename ElemT> 35 ImmutableListImpl(ElemT &&head, const ImmutableListImpl *tail = nullptr) Head(std::forward<ElemT> (head))36 : Head(std::forward<ElemT>(head)), Tail(tail) {} 37 38 public: 39 ImmutableListImpl(const ImmutableListImpl &) = delete; 40 ImmutableListImpl &operator=(const ImmutableListImpl &) = delete; 41 getHead()42 const T& getHead() const { return Head; } getTail()43 const ImmutableListImpl* getTail() const { return Tail; } 44 Profile(FoldingSetNodeID & ID,const T & H,const ImmutableListImpl * L)45 static inline void Profile(FoldingSetNodeID& ID, const T& H, 46 const ImmutableListImpl* L){ 47 ID.AddPointer(L); 48 ID.Add(H); 49 } 50 Profile(FoldingSetNodeID & ID)51 void Profile(FoldingSetNodeID& ID) { 52 Profile(ID, Head, Tail); 53 } 54 }; 55 56 /// ImmutableList - This class represents an immutable (functional) list. 57 /// It is implemented as a smart pointer (wraps ImmutableListImpl), so it 58 /// it is intended to always be copied by value as if it were a pointer. 59 /// This interface matches ImmutableSet and ImmutableMap. ImmutableList 60 /// objects should almost never be created directly, and instead should 61 /// be created by ImmutableListFactory objects that manage the lifetime 62 /// of a group of lists. When the factory object is reclaimed, all lists 63 /// created by that factory are released as well. 64 template <typename T> 65 class ImmutableList { 66 public: 67 using value_type = T; 68 using Factory = ImmutableListFactory<T>; 69 70 static_assert(std::is_trivially_destructible<T>::value, 71 "T must be trivially destructible!"); 72 73 private: 74 const ImmutableListImpl<T>* X; 75 76 public: 77 // This constructor should normally only be called by ImmutableListFactory<T>. 78 // There may be cases, however, when one needs to extract the internal pointer 79 // and reconstruct a list object from that pointer. X(x)80 ImmutableList(const ImmutableListImpl<T>* x = nullptr) : X(x) {} 81 getInternalPointer()82 const ImmutableListImpl<T>* getInternalPointer() const { 83 return X; 84 } 85 86 class iterator { 87 const ImmutableListImpl<T>* L = nullptr; 88 89 public: 90 iterator() = default; iterator(ImmutableList l)91 iterator(ImmutableList l) : L(l.getInternalPointer()) {} 92 93 iterator& operator++() { L = L->getTail(); return *this; } 94 bool operator==(const iterator& I) const { return L == I.L; } 95 bool operator!=(const iterator& I) const { return L != I.L; } 96 const value_type& operator*() const { return L->getHead(); } 97 const std::remove_reference_t<value_type> *operator->() const { 98 return &L->getHead(); 99 } 100 getList()101 ImmutableList getList() const { return L; } 102 }; 103 104 /// begin - Returns an iterator referring to the head of the list, or 105 /// an iterator denoting the end of the list if the list is empty. begin()106 iterator begin() const { return iterator(X); } 107 108 /// end - Returns an iterator denoting the end of the list. This iterator 109 /// does not refer to a valid list element. end()110 iterator end() const { return iterator(); } 111 112 /// isEmpty - Returns true if the list is empty. isEmpty()113 bool isEmpty() const { return !X; } 114 contains(const T & V)115 bool contains(const T& V) const { 116 for (iterator I = begin(), E = end(); I != E; ++I) { 117 if (*I == V) 118 return true; 119 } 120 return false; 121 } 122 123 /// isEqual - Returns true if two lists are equal. Because all lists created 124 /// from the same ImmutableListFactory are uniqued, this has O(1) complexity 125 /// because it the contents of the list do not need to be compared. Note 126 /// that you should only compare two lists created from the same 127 /// ImmutableListFactory. isEqual(const ImmutableList & L)128 bool isEqual(const ImmutableList& L) const { return X == L.X; } 129 130 bool operator==(const ImmutableList& L) const { return isEqual(L); } 131 132 /// getHead - Returns the head of the list. getHead()133 const T& getHead() const { 134 assert(!isEmpty() && "Cannot get the head of an empty list."); 135 return X->getHead(); 136 } 137 138 /// getTail - Returns the tail of the list, which is another (possibly empty) 139 /// ImmutableList. getTail()140 ImmutableList getTail() const { 141 return X ? X->getTail() : nullptr; 142 } 143 Profile(FoldingSetNodeID & ID)144 void Profile(FoldingSetNodeID& ID) const { 145 ID.AddPointer(X); 146 } 147 }; 148 149 template <typename T> 150 class ImmutableListFactory { 151 using ListTy = ImmutableListImpl<T>; 152 using CacheTy = FoldingSet<ListTy>; 153 154 CacheTy Cache; 155 uintptr_t Allocator; 156 ownsAllocator()157 bool ownsAllocator() const { 158 return (Allocator & 0x1) == 0; 159 } 160 getAllocator()161 BumpPtrAllocator& getAllocator() const { 162 return *reinterpret_cast<BumpPtrAllocator*>(Allocator & ~0x1); 163 } 164 165 public: ImmutableListFactory()166 ImmutableListFactory() 167 : Allocator(reinterpret_cast<uintptr_t>(new BumpPtrAllocator())) {} 168 ImmutableListFactory(BumpPtrAllocator & Alloc)169 ImmutableListFactory(BumpPtrAllocator& Alloc) 170 : Allocator(reinterpret_cast<uintptr_t>(&Alloc) | 0x1) {} 171 ~ImmutableListFactory()172 ~ImmutableListFactory() { 173 if (ownsAllocator()) delete &getAllocator(); 174 } 175 176 template <typename ElemT> concat(ElemT && Head,ImmutableList<T> Tail)177 [[nodiscard]] ImmutableList<T> concat(ElemT &&Head, ImmutableList<T> Tail) { 178 // Profile the new list to see if it already exists in our cache. 179 FoldingSetNodeID ID; 180 void* InsertPos; 181 182 const ListTy* TailImpl = Tail.getInternalPointer(); 183 ListTy::Profile(ID, Head, TailImpl); 184 ListTy* L = Cache.FindNodeOrInsertPos(ID, InsertPos); 185 186 if (!L) { 187 // The list does not exist in our cache. Create it. 188 BumpPtrAllocator& A = getAllocator(); 189 L = (ListTy*) A.Allocate<ListTy>(); 190 new (L) ListTy(std::forward<ElemT>(Head), TailImpl); 191 192 // Insert the new list into the cache. 193 Cache.InsertNode(L, InsertPos); 194 } 195 196 return L; 197 } 198 199 template <typename ElemT> add(ElemT && Data,ImmutableList<T> L)200 [[nodiscard]] ImmutableList<T> add(ElemT &&Data, ImmutableList<T> L) { 201 return concat(std::forward<ElemT>(Data), L); 202 } 203 204 template <typename... CtorArgs> emplace(ImmutableList<T> Tail,CtorArgs &&...Args)205 [[nodiscard]] ImmutableList<T> emplace(ImmutableList<T> Tail, 206 CtorArgs &&...Args) { 207 return concat(T(std::forward<CtorArgs>(Args)...), Tail); 208 } 209 getEmptyList()210 ImmutableList<T> getEmptyList() const { 211 return ImmutableList<T>(nullptr); 212 } 213 214 template <typename ElemT> create(ElemT && Data)215 ImmutableList<T> create(ElemT &&Data) { 216 return concat(std::forward<ElemT>(Data), getEmptyList()); 217 } 218 }; 219 220 //===----------------------------------------------------------------------===// 221 // Partially-specialized Traits. 222 //===----------------------------------------------------------------------===// 223 224 template <typename T> struct DenseMapInfo<ImmutableList<T>, void> { 225 static inline ImmutableList<T> getEmptyKey() { 226 return reinterpret_cast<ImmutableListImpl<T>*>(-1); 227 } 228 229 static inline ImmutableList<T> getTombstoneKey() { 230 return reinterpret_cast<ImmutableListImpl<T>*>(-2); 231 } 232 233 static unsigned getHashValue(ImmutableList<T> X) { 234 uintptr_t PtrVal = reinterpret_cast<uintptr_t>(X.getInternalPointer()); 235 return (unsigned((uintptr_t)PtrVal) >> 4) ^ 236 (unsigned((uintptr_t)PtrVal) >> 9); 237 } 238 239 static bool isEqual(ImmutableList<T> X1, ImmutableList<T> X2) { 240 return X1 == X2; 241 } 242 }; 243 244 } // end namespace llvm 245 246 #endif // LLVM_ADT_IMMUTABLELIST_H 247