1// Copyright 2013 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef V8_HYDROGEN_UNIQUE_H_
6#define V8_HYDROGEN_UNIQUE_H_
7
8#include "src/handles-inl.h"  // TODO(everyone): Fix our inl.h crap
9#include "src/objects-inl.h"  // TODO(everyone): Fix our inl.h crap
10#include "src/string-stream.h"
11#include "src/utils.h"
12#include "src/zone.h"
13
14namespace v8 {
15namespace internal {
16
17
18template <typename T>
19class UniqueSet;
20
21
22// Represents a handle to an object on the heap, but with the additional
23// ability of checking for equality and hashing without accessing the heap.
24//
25// Creating a Unique<T> requires first dereferencing the handle to obtain
26// the address of the object, which is used as the hashcode and the basis for
27// comparison. The object can be moved later by the GC, but comparison
28// and hashing use the old address of the object, without dereferencing it.
29//
30// Careful! Comparison of two Uniques is only correct if both were created
31// in the same "era" of GC or if at least one is a non-movable object.
32template <typename T>
33class Unique {
34 public:
35  Unique<T>() : raw_address_(NULL) {}
36
37  // TODO(titzer): make private and introduce a uniqueness scope.
38  explicit Unique(Handle<T> handle) {
39    if (handle.is_null()) {
40      raw_address_ = NULL;
41    } else {
42      // This is a best-effort check to prevent comparing Unique<T>'s created
43      // in different GC eras; we require heap allocation to be disallowed at
44      // creation time.
45      // NOTE: we currently consider maps to be non-movable, so no special
46      // assurance is required for creating a Unique<Map>.
47      // TODO(titzer): other immortable immovable objects are also fine.
48      DCHECK(!AllowHeapAllocation::IsAllowed() || handle->IsMap());
49      raw_address_ = reinterpret_cast<Address>(*handle);
50      DCHECK_NE(raw_address_, NULL);  // Non-null should imply non-zero address.
51    }
52    handle_ = handle;
53  }
54
55  // TODO(titzer): this is a hack to migrate to Unique<T> incrementally.
56  Unique(Address raw_address, Handle<T> handle)
57    : raw_address_(raw_address), handle_(handle) { }
58
59  // Constructor for handling automatic up casting.
60  // Eg. Unique<JSFunction> can be passed when Unique<Object> is expected.
61  template <class S> Unique(Unique<S> uniq) {
62#ifdef DEBUG
63    T* a = NULL;
64    S* b = NULL;
65    a = b;  // Fake assignment to enforce type checks.
66    USE(a);
67#endif
68    raw_address_ = uniq.raw_address_;
69    handle_ = uniq.handle_;
70  }
71
72  template <typename U>
73  inline bool operator==(const Unique<U>& other) const {
74    DCHECK(IsInitialized() && other.IsInitialized());
75    return raw_address_ == other.raw_address_;
76  }
77
78  template <typename U>
79  inline bool operator!=(const Unique<U>& other) const {
80    DCHECK(IsInitialized() && other.IsInitialized());
81    return raw_address_ != other.raw_address_;
82  }
83
84  inline intptr_t Hashcode() const {
85    DCHECK(IsInitialized());
86    return reinterpret_cast<intptr_t>(raw_address_);
87  }
88
89  inline bool IsNull() const {
90    DCHECK(IsInitialized());
91    return raw_address_ == NULL;
92  }
93
94  inline bool IsKnownGlobal(void* global) const {
95    DCHECK(IsInitialized());
96    return raw_address_ == reinterpret_cast<Address>(global);
97  }
98
99  inline Handle<T> handle() const {
100    return handle_;
101  }
102
103  template <class S> static Unique<T> cast(Unique<S> that) {
104    return Unique<T>(that.raw_address_, Handle<T>::cast(that.handle_));
105  }
106
107  inline bool IsInitialized() const {
108    return raw_address_ != NULL || handle_.is_null();
109  }
110
111  // TODO(titzer): this is a hack to migrate to Unique<T> incrementally.
112  static Unique<T> CreateUninitialized(Handle<T> handle) {
113    return Unique<T>(reinterpret_cast<Address>(NULL), handle);
114  }
115
116  static Unique<T> CreateImmovable(Handle<T> handle) {
117    return Unique<T>(reinterpret_cast<Address>(*handle), handle);
118  }
119
120  friend class UniqueSet<T>;  // Uses internal details for speed.
121  template <class U>
122  friend class Unique;  // For comparing raw_address values.
123
124 protected:
125  Address raw_address_;
126  Handle<T> handle_;
127
128  friend class SideEffectsTracker;
129};
130
131
132template <typename T>
133class UniqueSet FINAL : public ZoneObject {
134 public:
135  // Constructor. A new set will be empty.
136  UniqueSet() : size_(0), capacity_(0), array_(NULL) { }
137
138  // Capacity constructor. A new set will be empty.
139  UniqueSet(int capacity, Zone* zone)
140      : size_(0), capacity_(capacity),
141        array_(zone->NewArray<Unique<T> >(capacity)) {
142    DCHECK(capacity <= kMaxCapacity);
143  }
144
145  // Singleton constructor.
146  UniqueSet(Unique<T> uniq, Zone* zone)
147      : size_(1), capacity_(1), array_(zone->NewArray<Unique<T> >(1)) {
148    array_[0] = uniq;
149  }
150
151  // Add a new element to this unique set. Mutates this set. O(|this|).
152  void Add(Unique<T> uniq, Zone* zone) {
153    DCHECK(uniq.IsInitialized());
154    // Keep the set sorted by the {raw_address} of the unique elements.
155    for (int i = 0; i < size_; i++) {
156      if (array_[i] == uniq) return;
157      if (array_[i].raw_address_ > uniq.raw_address_) {
158        // Insert in the middle.
159        Grow(size_ + 1, zone);
160        for (int j = size_ - 1; j >= i; j--) array_[j + 1] = array_[j];
161        array_[i] = uniq;
162        size_++;
163        return;
164      }
165    }
166    // Append the element to the the end.
167    Grow(size_ + 1, zone);
168    array_[size_++] = uniq;
169  }
170
171  // Remove an element from this set. Mutates this set. O(|this|)
172  void Remove(Unique<T> uniq) {
173    for (int i = 0; i < size_; i++) {
174      if (array_[i] == uniq) {
175        while (++i < size_) array_[i - 1] = array_[i];
176        size_--;
177        return;
178      }
179    }
180  }
181
182  // Compare this set against another set. O(|this|).
183  bool Equals(const UniqueSet<T>* that) const {
184    if (that->size_ != this->size_) return false;
185    for (int i = 0; i < this->size_; i++) {
186      if (this->array_[i] != that->array_[i]) return false;
187    }
188    return true;
189  }
190
191  // Check whether this set contains the given element. O(|this|)
192  // TODO(titzer): use binary search for large sets to make this O(log|this|)
193  template <typename U>
194  bool Contains(const Unique<U> elem) const {
195    for (int i = 0; i < this->size_; ++i) {
196      Unique<T> cand = this->array_[i];
197      if (cand.raw_address_ >= elem.raw_address_) {
198        return cand.raw_address_ == elem.raw_address_;
199      }
200    }
201    return false;
202  }
203
204  // Check if this set is a subset of the given set. O(|this| + |that|).
205  bool IsSubset(const UniqueSet<T>* that) const {
206    if (that->size_ < this->size_) return false;
207    int j = 0;
208    for (int i = 0; i < this->size_; i++) {
209      Unique<T> sought = this->array_[i];
210      while (true) {
211        if (sought == that->array_[j++]) break;
212        // Fail whenever there are more elements in {this} than {that}.
213        if ((this->size_ - i) > (that->size_ - j)) return false;
214      }
215    }
216    return true;
217  }
218
219  // Returns a new set representing the intersection of this set and the other.
220  // O(|this| + |that|).
221  UniqueSet<T>* Intersect(const UniqueSet<T>* that, Zone* zone) const {
222    if (that->size_ == 0 || this->size_ == 0) return new(zone) UniqueSet<T>();
223
224    UniqueSet<T>* out = new(zone) UniqueSet<T>(
225        Min(this->size_, that->size_), zone);
226
227    int i = 0, j = 0, k = 0;
228    while (i < this->size_ && j < that->size_) {
229      Unique<T> a = this->array_[i];
230      Unique<T> b = that->array_[j];
231      if (a == b) {
232        out->array_[k++] = a;
233        i++;
234        j++;
235      } else if (a.raw_address_ < b.raw_address_) {
236        i++;
237      } else {
238        j++;
239      }
240    }
241
242    out->size_ = k;
243    return out;
244  }
245
246  // Returns a new set representing the union of this set and the other.
247  // O(|this| + |that|).
248  UniqueSet<T>* Union(const UniqueSet<T>* that, Zone* zone) const {
249    if (that->size_ == 0) return this->Copy(zone);
250    if (this->size_ == 0) return that->Copy(zone);
251
252    UniqueSet<T>* out = new(zone) UniqueSet<T>(
253        this->size_ + that->size_, zone);
254
255    int i = 0, j = 0, k = 0;
256    while (i < this->size_ && j < that->size_) {
257      Unique<T> a = this->array_[i];
258      Unique<T> b = that->array_[j];
259      if (a == b) {
260        out->array_[k++] = a;
261        i++;
262        j++;
263      } else if (a.raw_address_ < b.raw_address_) {
264        out->array_[k++] = a;
265        i++;
266      } else {
267        out->array_[k++] = b;
268        j++;
269      }
270    }
271
272    while (i < this->size_) out->array_[k++] = this->array_[i++];
273    while (j < that->size_) out->array_[k++] = that->array_[j++];
274
275    out->size_ = k;
276    return out;
277  }
278
279  // Returns a new set representing all elements from this set which are not in
280  // that set. O(|this| * |that|).
281  UniqueSet<T>* Subtract(const UniqueSet<T>* that, Zone* zone) const {
282    if (that->size_ == 0) return this->Copy(zone);
283
284    UniqueSet<T>* out = new(zone) UniqueSet<T>(this->size_, zone);
285
286    int i = 0, j = 0;
287    while (i < this->size_) {
288      Unique<T> cand = this->array_[i];
289      if (!that->Contains(cand)) {
290        out->array_[j++] = cand;
291      }
292      i++;
293    }
294
295    out->size_ = j;
296    return out;
297  }
298
299  // Makes an exact copy of this set. O(|this|).
300  UniqueSet<T>* Copy(Zone* zone) const {
301    UniqueSet<T>* copy = new(zone) UniqueSet<T>(this->size_, zone);
302    copy->size_ = this->size_;
303    memcpy(copy->array_, this->array_, this->size_ * sizeof(Unique<T>));
304    return copy;
305  }
306
307  void Clear() {
308    size_ = 0;
309  }
310
311  inline int size() const {
312    return size_;
313  }
314
315  inline Unique<T> at(int index) const {
316    DCHECK(index >= 0 && index < size_);
317    return array_[index];
318  }
319
320 private:
321  // These sets should be small, since operations are implemented with simple
322  // linear algorithms. Enforce a maximum size.
323  static const int kMaxCapacity = 65535;
324
325  uint16_t size_;
326  uint16_t capacity_;
327  Unique<T>* array_;
328
329  // Grow the size of internal storage to be at least {size} elements.
330  void Grow(int size, Zone* zone) {
331    CHECK(size < kMaxCapacity);  // Enforce maximum size.
332    if (capacity_ < size) {
333      int new_capacity = 2 * capacity_ + size;
334      if (new_capacity > kMaxCapacity) new_capacity = kMaxCapacity;
335      Unique<T>* new_array = zone->NewArray<Unique<T> >(new_capacity);
336      if (size_ > 0) {
337        memcpy(new_array, array_, size_ * sizeof(Unique<T>));
338      }
339      capacity_ = new_capacity;
340      array_ = new_array;
341    }
342  }
343};
344
345} }  // namespace v8::internal
346
347#endif  // V8_HYDROGEN_UNIQUE_H_
348