• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef COMPILER_OPTIMIZER_ANALYSIS_TYPED_REF_SET_H
17 #define COMPILER_OPTIMIZER_ANALYSIS_TYPED_REF_SET_H
18 
19 #include "libpandabase/utils/bit_vector.h"
20 #include "optimizer/ir/inst.h"
21 
22 namespace ark::compiler {
23 
24 // Unique id of some object. Objects with different IDs are never equal
25 using Ref = uint32_t;
26 constexpr Ref ALL_REF = std::numeric_limits<Ref>::max();
27 
28 ObjectTypeInfo Merge(ObjectTypeInfo lhs, ObjectTypeInfo rhs);
29 
30 void TryImproveTypeInfo(ObjectTypeInfo &lhs, ObjectTypeInfo rhs);
31 
32 /// @brief Set of unique `Ref`s (RefSet) represented as sorted vector
33 template <typename Allocator = ArenaAllocator>
34 class VectorSet {
35 public:
36     using BitVectorT = BitVector<Allocator>;
37 
VectorSet(Allocator * allocator)38     explicit VectorSet(Allocator *allocator) : data_(allocator->Adapter()) {}
39 
MinBitsToStore()40     size_t MinBitsToStore() const
41     {
42         return data_.empty() ? 0 : data_.back() + 1;
43     }
44 
PopCount()45     size_t PopCount() const
46     {
47         return data_.size();
48     }
49 
50     bool operator==(const VectorSet &other) const
51     {
52         return data_ == other.data_;
53     }
54 
GetBit(size_t index)55     bool GetBit(size_t index) const
56     {
57         auto it = std::lower_bound(data_.begin(), data_.end(), index);
58         return it != data_.end() && *it == index;
59     }
60 
SetBit(size_t index)61     void SetBit(size_t index)
62     {
63         auto it = std::lower_bound(data_.begin(), data_.end(), index);
64         if (it != data_.end() && *it == index) {
65             return;
66         }
67         data_.insert(it, index);
68     }
69 
TransitionToBitVector()70     BitVectorT TransitionToBitVector()
71     {
72         auto bitVector = BitVectorT(data_.back(), data_.get_allocator());
73         for (auto ref : data_) {
74             bitVector.SetBit(ref);
75         }
76         return bitVector;
77     }
78 
79     VectorSet &operator|=(const VectorSet &other)
80     {
81         auto size = data_.size();
82         data_.insert(data_.end(), other.data_.begin(), other.data_.end());
83         std::inplace_merge(data_.begin(), data_.begin() + size, data_.end());
84         data_.erase(std::unique(data_.begin(), data_.end()), data_.end());
85         ASSERT(std::is_sorted(data_.begin(), data_.end()));
86         return *this;
87     }
88 
89     friend BitVectorT operator|=(BitVectorT &to, const VectorSet &from)
90     {
91         // ensure capacity in advance
92         if (auto minBits = from.MinBitsToStore(); minBits > to.size()) {
93             to.resize(minBits);
94         }
95         for (auto ref : from.data_) {
96             to.SetBit(ref);
97         }
98         return to;
99     }
100 
GetSingle()101     Ref GetSingle() const
102     {
103         ASSERT(data_.size() == 1);
104         return data_[0];
105     }
106 
GetData()107     const auto &GetData() const
108     {
109         return data_;
110     }
111 
112 private:
113     std::vector<Ref, typename Allocator::template AdapterType<Ref>> data_;
114 };
115 
116 template <typename Allocator, typename Callback>
VisitBitVector(const VectorSet<Allocator> & vectorSet,Callback && cb)117 void VisitBitVector(const VectorSet<Allocator> &vectorSet, Callback &&cb)
118 {
119     for (auto ref : vectorSet.GetData()) {
120         cb(ref);
121     }
122 }
123 
124 template <typename Allocator, typename Callback>
VisitBitVector(const BitVector<Allocator> & bitVector,Callback && cb)125 void VisitBitVector(const BitVector<Allocator> &bitVector, Callback &&cb)
126 {
127     auto span = bitVector.GetContainerDataSpan();
128     for (size_t maskIdx = 0; maskIdx < span.size(); ++maskIdx) {
129         auto mask = span[maskIdx];
130         size_t maskOffset = 0;
131         while (mask != 0) {
132             auto offset = static_cast<size_t>(Ctz(mask));
133             mask = (mask >> offset) >> 1U;
134             maskOffset += offset;
135             size_t oneIdx = maskIdx * sizeof(mask) * BITS_PER_BYTE + maskOffset;
136             ++maskOffset;  // consume current bit
137             cb(oneIdx);
138         }
139     }
140 }
141 
142 class UniverseSet {
143 public:
PopCount()144     size_t PopCount() const
145     {
146         return std::numeric_limits<size_t>::max();
147     }
148 
149     bool operator==([[maybe_unused]] const UniverseSet &other) const
150     {
151         return true;
152     }
153 
GetBit(size_t index)154     bool GetBit([[maybe_unused]] size_t index) const
155     {
156         return true;
157     }
158 
SetBit(size_t index)159     void SetBit([[maybe_unused]] size_t index) {}
160 };
161 
162 /**
163  * @brief Set of unique `Ref`s (RefSet)
164  *
165  * Is represented as sorted vector while small;
166  * transitions to `BitVector` when grows larger then `VECTOR_THRESHOLD`;
167  * transitions to `UniverseSet` when grows larger then `BIT_VECTOR_THRESHOLD`;
168  */
169 template <typename Allocator = StdAllocatorStub>
170 class SmallSet {
171     using VectorSetT = VectorSet<Allocator>;
172     using BitVectorT = BitVector<Allocator>;
173 
174 public:
SmallSet(Allocator * allocator)175     explicit SmallSet(Allocator *allocator) : data_(VectorSetT(allocator)) {}
176     // CC-OFFNXT(G.CLS.03-CPP) clang-tidy: initializer-list constructor shouldn't be explicit
SmallSet(allocator)177     SmallSet(std::initializer_list<Ref> values, Allocator *allocator = Allocator::GetInstance()) : SmallSet(allocator)
178     {
179         for (auto value : values) {
180             SetBit(value);
181         }
182     }
183     ~SmallSet() = default;
184     DEFAULT_COPY_SEMANTIC(SmallSet);
185     DEFAULT_MOVE_SEMANTIC(SmallSet);
186 
187     bool operator==(const SmallSet &other) const
188     {
189         return data_ == other.data_;
190     }
191 
PopCount()192     size_t PopCount() const
193     {
194         return std::visit([](const auto &s) { return s.PopCount(); }, data_);
195     }
196 
GetBit(Ref index)197     bool GetBit(Ref index) const
198     {
199         if (index == ALL_REF) {
200             return std::holds_alternative<UniverseSet>(data_);
201         }
202         return std::visit([index](const auto &s) { return s.GetBit(index); }, data_);
203     }
204 
SetBit(Ref index)205     void SetBit(Ref index)
206     {
207         if (index == ALL_REF) {
208             MakeUniverse();
209             return;
210         }
211         std::visit([index](auto &s) { s.SetBit(index); }, data_);
212         CheckTransition();
213     }
214 
Includes(const SmallSet & other)215     bool Includes(const SmallSet &other) const
216     {
217         return std::visit(
218             [](const auto &to, const auto &from) -> bool {
219                 using To = std::decay_t<decltype(to)>;
220                 using From = std::decay_t<decltype(from)>;
221                 if constexpr (std::is_same_v<To, UniverseSet>) {
222                     return true;
223                 } else if constexpr (std::is_same_v<From, UniverseSet>) {
224                     return false;
225                 } else {
226                     return Includes(to, from);
227                 }
228             },
229             data_, other.data_);
230     }
231 
232     SmallSet &operator|=(const SmallSet &other)
233     {
234         std::visit(
235             [this](auto &to, const auto &from) {
236                 using To = std::decay_t<decltype(to)>;
237                 using From = std::decay_t<decltype(from)>;
238                 if constexpr (std::is_same_v<To, UniverseSet>) {
239                     // do nothing
240                 } else if constexpr (std::is_same_v<From, UniverseSet>) {
241                     MakeUniverse();
242                 } else if constexpr (std::is_same_v<To, VectorSetT>) {
243                     if constexpr (std::is_same_v<From, VectorSetT>) {
244                         to |= from;
245                     } else {
246                         auto newData = from;
247                         newData |= to;
248                         data_ = std::move(newData);
249                     }
250                 } else {
251                     static_assert(std::is_same_v<To, BitVectorT>);
252                     to |= from;
253                 }
254             },
255             data_, other.data_);
256         CheckTransition();
257         return *this;
258     }
259 
GetSingle()260     Ref GetSingle() const
261     {
262         return std::get<VectorSetT>(data_).GetSingle();
263     }
264 
265     template <typename Callback>
Visit(Callback && cb)266     void Visit(Callback &&cb) const
267     {
268         std::visit(
269             [&cb](auto &s) {
270                 using T = std::decay_t<decltype(s)>;
271                 if constexpr (std::is_same_v<T, UniverseSet>) {
272                     cb(UINT_MAX);
273                     UNREACHABLE();
274                 } else {
275                     VisitBitVector(s, cb);
276                 }
277             },
278             data_);
279     }
280 
IsVectorSet()281     bool IsVectorSet() const
282     {
283         return std::holds_alternative<VectorSetT>(data_);
284     }
285 
286 private:
CheckTransition()287     void CheckTransition()
288     {
289         if (std::holds_alternative<VectorSetT>(data_)) {
290             if (PopCount() > VECTOR_THRESHOLD) {
291                 data_ = std::get<VectorSetT>(data_).TransitionToBitVector();
292             }
293         } else if (std::holds_alternative<BitVectorT>(data_)) {
294             if (std::get<BitVectorT>(data_).capacity() > BIT_VECTOR_THRESHOLD) {
295                 MakeUniverse();
296             }
297         } else {
298             ASSERT(std::holds_alternative<UniverseSet>(data_));
299         }
300     }
301 
MakeUniverse()302     void MakeUniverse()
303     {
304         data_ = UniverseSet {};
305     }
306 
307     template <typename T>
Includes(const T & to,const VectorSetT & from)308     static bool Includes(const T &to, const VectorSetT &from)
309     {
310         for (auto i : from.GetData()) {
311             if (!to.GetBit(i)) {
312                 return false;
313             }
314         }
315         return true;
316     }
317 
318     template <typename T>
Includes(const T & to,const UniverseSet & from)319     static bool Includes([[maybe_unused]] const T &to, [[maybe_unused]] const UniverseSet &from)
320     {
321         return std::is_same_v<T, UniverseSet>;
322     }
323 
Includes(const VectorSetT & to,const BitVectorT & from)324     static bool Includes([[maybe_unused]] const VectorSetT &to, [[maybe_unused]] const BitVectorT &from)
325     {
326         // from is definitely larger
327         return false;
328     }
329 
Includes(const BitVectorT & to,const BitVectorT & from)330     static bool Includes(const BitVectorT &to, const BitVectorT &from)
331     {
332         auto toData = to.GetContainerDataSpan();
333         auto fromData = from.GetContainerDataSpan();
334         for (size_t i = 0; i < fromData.size(); i++) {
335             auto toWord = i >= toData.size() ? 0 : toData[i];
336             if ((toWord & fromData[i]) != fromData[i]) {
337                 return false;
338             }
339         }
340         return true;
341     }
342 
343 private:
344     std::variant<VectorSetT, BitVectorT, UniverseSet> data_;
345     constexpr static size_t VECTOR_THRESHOLD = 5;
346     // NOTE: temp, never trigger
347     constexpr static size_t BIT_VECTOR_THRESHOLD = 200'000 * BITS_PER_UINT32;
348 };
349 
350 template <typename Allocator = StdAllocatorStub>
351 class TypedRefSet : private SmallSet<Allocator> {
352     using Base = SmallSet<Allocator>;
353 
354 public:
TypedRefSet(const Base & base,ObjectTypeInfo typeInfo)355     TypedRefSet(const Base &base, ObjectTypeInfo typeInfo) : Base(base), typeInfo_(typeInfo) {}
356     TypedRefSet(Allocator *allocator, ObjectTypeInfo typeInfo) : Base(allocator), typeInfo_(typeInfo) {}
357     ~TypedRefSet() = default;
358 
359     DEFAULT_COPY_SEMANTIC(TypedRefSet);
360     DEFAULT_MOVE_SEMANTIC(TypedRefSet);
361 
362     using Base::GetBit;
363     using Base::GetSingle;
364     using Base::PopCount;
365     using Base::Visit;
366 
367     bool operator==(const TypedRefSet &other) const
368     {
369         return Base::operator==(other) && typeInfo_ == other.typeInfo_;
370     }
371 
372     bool operator!=(const TypedRefSet &other) const
373     {
374         return !(*this == other);
375     }
376 
377     TypedRefSet &operator|=(const TypedRefSet &other)
378     {
379         Base::operator|=(other);
380         typeInfo_ = Merge(typeInfo_, other.typeInfo_);
381         return *this;
382     }
383 
384     void SetBit(Ref index, ObjectTypeInfo typeInfo)
385     {
386         Base::SetBit(index);
387         UpdateTypeInfoOr(typeInfo);
388     }
389 
390     bool Includes(const TypedRefSet &other) const
391     {
392         if (typeInfo_ != Merge(typeInfo_, other.typeInfo_)) {
393             return false;
394         }
395         return Base::Includes(other);
396     }
397 
398     void TryImproveTypeInfo(ObjectTypeInfo otherKnownTypeInfo)
399     {
400         compiler::TryImproveTypeInfo(typeInfo_, otherKnownTypeInfo);
401     }
402 
403     void UpdateTypeInfoOr(ObjectTypeInfo newPossibleTypeInfo)
404     {
405         typeInfo_ = Merge(typeInfo_, newPossibleTypeInfo);
406     }
407 
408     ObjectTypeInfo GetTypeInfo() const
409     {
410         return typeInfo_;
411     }
412 
413     template <typename Alloc>
414     friend std::ostream &operator<<(std::ostream &os, const TypedRefSet<Alloc> &typedRefSet);
415 
416 private:
417     ObjectTypeInfo typeInfo_ {ObjectTypeInfo::INVALID};
418 };
419 
420 template <typename Allocator>
421 std::ostream &operator<<(std::ostream &os, const SmallSet<Allocator> &smallSet)
422 {
423     os << "{";
424     bool first = true;
425     smallSet.Visit([&os, &first](Ref ref) {
426         if (!first) {
427             os << ", ";
428         }
429         first = false;
430         os << ref;
431     });
432     return os << "}";
433 }
434 
435 template <typename Allocator>
436 std::ostream &operator<<(std::ostream &os, const TypedRefSet<Allocator> &typedRefSet)
437 {
438     return os << static_cast<const SmallSet<Allocator> &>(typedRefSet);
439 }
440 
441 using ArenaSmallSet = SmallSet<ArenaAllocator>;
442 using ArenaTypedRefSet = TypedRefSet<ArenaAllocator>;
443 
444 }  // namespace ark::compiler
445 
446 #endif  // COMPILER_OPTIMIZER_ANALYSIS_TYPED_REF_SET_H