/src/hermes/lib/VM/SegmentedArray.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) Meta Platforms, Inc. and affiliates. |
3 | | * |
4 | | * This source code is licensed under the MIT license found in the |
5 | | * LICENSE file in the root directory of this source tree. |
6 | | */ |
7 | | |
8 | | #include "hermes/VM/SegmentedArray.h" |
9 | | |
10 | | #include "hermes/VM/GCPointer-inline.h" |
11 | | #include "hermes/VM/HermesValue-inline.h" |
12 | | |
13 | | namespace hermes { |
14 | | namespace vm { |
15 | | |
16 | | template <typename HVType> |
17 | | const VTable SegmentedArrayBase<HVType>::Segment::vt( |
18 | | getCellKind(), |
19 | | cellSize<SegmentedArrayBase::Segment>(), |
20 | | nullptr, |
21 | | nullptr, |
22 | | nullptr |
23 | | #ifdef HERMES_MEMORY_INSTRUMENTATION |
24 | | , |
25 | | VTable::HeapSnapshotMetadata{ |
26 | | HeapSnapshot::NodeType::Array, |
27 | | nullptr, |
28 | | nullptr, |
29 | | nullptr, |
30 | | nullptr} |
31 | | #endif |
32 | | ); |
33 | | |
34 | 1 | void SegmentBuildMeta(const GCCell *cell, Metadata::Builder &mb) { |
35 | 1 | const auto *self = static_cast<const SegmentedArray::Segment *>(cell); |
36 | 1 | mb.setVTable(&SegmentedArray::Segment::vt); |
37 | 1 | mb.addArray("data", self->data_, &self->length_, sizeof(GCHermesValue)); |
38 | 1 | } |
39 | 1 | void SegmentSmallBuildMeta(const GCCell *cell, Metadata::Builder &mb) { |
40 | 1 | const auto *self = static_cast<const SegmentedArraySmall::Segment *>(cell); |
41 | 1 | mb.setVTable(&SegmentedArraySmall::Segment::vt); |
42 | 1 | mb.addArray("data", self->data_, &self->length_, sizeof(GCSmallHermesValue)); |
43 | 1 | } |
44 | | |
45 | | template <typename HVType> |
46 | | PseudoHandle<typename SegmentedArrayBase<HVType>::Segment> |
47 | 1.58k | SegmentedArrayBase<HVType>::Segment::create(Runtime &runtime) { |
48 | | // NOTE: This needs to live in the cpp file instead of the header because it |
49 | | // uses PseudoHandle, which requires a specialization of IsGCObject for the |
50 | | // type it constructs. |
51 | 1.58k | return createPseudoHandle(runtime.makeAFixed<Segment>()); |
52 | 1.58k | } hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::Segment::create(hermes::vm::Runtime&) Line | Count | Source | 47 | 520 | SegmentedArrayBase<HVType>::Segment::create(Runtime &runtime) { | 48 | | // NOTE: This needs to live in the cpp file instead of the header because it | 49 | | // uses PseudoHandle, which requires a specialization of IsGCObject for the | 50 | | // type it constructs. | 51 | 520 | return createPseudoHandle(runtime.makeAFixed<Segment>()); | 52 | 520 | } |
hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::Segment::create(hermes::vm::Runtime&) Line | Count | Source | 47 | 1.06k | SegmentedArrayBase<HVType>::Segment::create(Runtime &runtime) { | 48 | | // NOTE: This needs to live in the cpp file instead of the header because it | 49 | | // uses PseudoHandle, which requires a specialization of IsGCObject for the | 50 | | // type it constructs. | 51 | 1.06k | return createPseudoHandle(runtime.makeAFixed<Segment>()); | 52 | 1.06k | } |
|
53 | | |
54 | | template <typename HVType> |
55 | | void SegmentedArrayBase<HVType>::Segment::setLength( |
56 | | Runtime &runtime, |
57 | 1.42M | uint32_t newLength) { |
58 | 1.42M | const auto len = length(); |
59 | 1.42M | if (newLength > len) { |
60 | | // Length is increasing, fill with emptys. |
61 | 1.42M | GCHVType::uninitialized_fill( |
62 | 1.42M | data_ + len, |
63 | 1.42M | data_ + newLength, |
64 | 1.42M | HVType::encodeEmptyValue(), |
65 | 1.42M | runtime.getHeap()); |
66 | 1.42M | length_.store(newLength, std::memory_order_release); |
67 | 1.42M | } else if (newLength < len) { |
68 | | // If length is decreasing a write barrier needs to be done. |
69 | 0 | GCHVType::rangeUnreachableWriteBarrier( |
70 | 0 | data_ + newLength, data_ + len, runtime.getHeap()); |
71 | 0 | length_.store(newLength, std::memory_order_release); |
72 | 0 | } |
73 | 1.42M | } hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::Segment::setLength(hermes::vm::Runtime&, unsigned int) Line | Count | Source | 57 | 531k | uint32_t newLength) { | 58 | 531k | const auto len = length(); | 59 | 531k | if (newLength > len) { | 60 | | // Length is increasing, fill with emptys. | 61 | 530k | GCHVType::uninitialized_fill( | 62 | 530k | data_ + len, | 63 | 530k | data_ + newLength, | 64 | 530k | HVType::encodeEmptyValue(), | 65 | 530k | runtime.getHeap()); | 66 | 530k | length_.store(newLength, std::memory_order_release); | 67 | 530k | } else if (newLength < len) { | 68 | | // If length is decreasing a write barrier needs to be done. | 69 | 0 | GCHVType::rangeUnreachableWriteBarrier( | 70 | 0 | data_ + newLength, data_ + len, runtime.getHeap()); | 71 | 0 | length_.store(newLength, std::memory_order_release); | 72 | 0 | } | 73 | 531k | } |
hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::Segment::setLength(hermes::vm::Runtime&, unsigned int) Line | Count | Source | 57 | 891k | uint32_t newLength) { | 58 | 891k | const auto len = length(); | 59 | 891k | if (newLength > len) { | 60 | | // Length is increasing, fill with emptys. | 61 | 890k | GCHVType::uninitialized_fill( | 62 | 890k | data_ + len, | 63 | 890k | data_ + newLength, | 64 | 890k | HVType::encodeEmptyValue(), | 65 | 890k | runtime.getHeap()); | 66 | 890k | length_.store(newLength, std::memory_order_release); | 67 | 890k | } else if (newLength < len) { | 68 | | // If length is decreasing a write barrier needs to be done. | 69 | 0 | GCHVType::rangeUnreachableWriteBarrier( | 70 | 0 | data_ + newLength, data_ + len, runtime.getHeap()); | 71 | 0 | length_.store(newLength, std::memory_order_release); | 72 | 0 | } | 73 | 891k | } |
|
74 | | |
75 | | template <typename HVType> |
76 | | const VTable SegmentedArrayBase<HVType>::vt( |
77 | | getCellKind(), |
78 | | /*variableSize*/ 0, |
79 | | nullptr, |
80 | | nullptr, |
81 | | _trimSizeCallback |
82 | | #ifdef HERMES_MEMORY_INSTRUMENTATION |
83 | | , |
84 | | VTable::HeapSnapshotMetadata{ |
85 | | HeapSnapshot::NodeType::Array, |
86 | | nullptr, |
87 | | nullptr, |
88 | | nullptr, |
89 | | nullptr} |
90 | | #endif |
91 | | ); |
92 | | |
93 | 1 | void SegmentedArrayBuildMeta(const GCCell *cell, Metadata::Builder &mb) { |
94 | 1 | const auto *self = static_cast<const SegmentedArray *>(cell); |
95 | 1 | mb.setVTable(&SegmentedArray::vt); |
96 | 1 | mb.addArray( |
97 | 1 | "slots", |
98 | 1 | self->inlineStorage(), |
99 | 1 | &self->numSlotsUsed_, |
100 | 1 | sizeof(GCHermesValue)); |
101 | 1 | } |
102 | | |
103 | 1 | void SegmentedArraySmallBuildMeta(const GCCell *cell, Metadata::Builder &mb) { |
104 | 1 | const auto *self = static_cast<const SegmentedArraySmall *>(cell); |
105 | 1 | mb.setVTable(&SegmentedArraySmall::vt); |
106 | 1 | mb.addArray( |
107 | 1 | "slots", |
108 | 1 | self->inlineStorage(), |
109 | 1 | &self->numSlotsUsed_, |
110 | 1 | sizeof(GCSmallHermesValue)); |
111 | 1 | } |
112 | | |
113 | | template <typename HVType> |
114 | | CallResult<PseudoHandle<SegmentedArrayBase<HVType>>> |
115 | 86.3k | SegmentedArrayBase<HVType>::create(Runtime &runtime, size_type capacity) { |
116 | 86.3k | if (LLVM_UNLIKELY(capacity > maxElements())) { |
117 | 0 | return throwExcessiveCapacityError(runtime, capacity); |
118 | 0 | } |
119 | | // Leave the segments as null. Whenever the size is changed, the segments |
120 | | // will be allocated. Note that this means the capacity argument won't be |
121 | | // reflected in capacity() if it is larger than the inline storage space. |
122 | | // That is in order to avoid having an extra field to track, and the upper |
123 | | // bound of "size" can be used instead. |
124 | 86.3k | const auto allocSize = allocationSizeForCapacity(capacity); |
125 | 86.3k | return createPseudoHandle( |
126 | 86.3k | runtime.makeAVariable<SegmentedArrayBase>(allocSize)); |
127 | 86.3k | } hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::create(hermes::vm::Runtime&, unsigned int) Line | Count | Source | 115 | 62 | SegmentedArrayBase<HVType>::create(Runtime &runtime, size_type capacity) { | 116 | 62 | if (LLVM_UNLIKELY(capacity > maxElements())) { | 117 | 0 | return throwExcessiveCapacityError(runtime, capacity); | 118 | 0 | } | 119 | | // Leave the segments as null. Whenever the size is changed, the segments | 120 | | // will be allocated. Note that this means the capacity argument won't be | 121 | | // reflected in capacity() if it is larger than the inline storage space. | 122 | | // That is in order to avoid having an extra field to track, and the upper | 123 | | // bound of "size" can be used instead. | 124 | 62 | const auto allocSize = allocationSizeForCapacity(capacity); | 125 | 62 | return createPseudoHandle( | 126 | 62 | runtime.makeAVariable<SegmentedArrayBase>(allocSize)); | 127 | 62 | } |
hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::create(hermes::vm::Runtime&, unsigned int) Line | Count | Source | 115 | 86.3k | SegmentedArrayBase<HVType>::create(Runtime &runtime, size_type capacity) { | 116 | 86.3k | if (LLVM_UNLIKELY(capacity > maxElements())) { | 117 | 0 | return throwExcessiveCapacityError(runtime, capacity); | 118 | 0 | } | 119 | | // Leave the segments as null. Whenever the size is changed, the segments | 120 | | // will be allocated. Note that this means the capacity argument won't be | 121 | | // reflected in capacity() if it is larger than the inline storage space. | 122 | | // That is in order to avoid having an extra field to track, and the upper | 123 | | // bound of "size" can be used instead. | 124 | 86.3k | const auto allocSize = allocationSizeForCapacity(capacity); | 125 | 86.3k | return createPseudoHandle( | 126 | 86.3k | runtime.makeAVariable<SegmentedArrayBase>(allocSize)); | 127 | 86.3k | } |
|
128 | | |
129 | | template <typename HVType> |
130 | | CallResult<PseudoHandle<SegmentedArrayBase<HVType>>> SegmentedArrayBase< |
131 | 0 | HVType>::createLongLived(Runtime &runtime, size_type capacity) { |
132 | 0 | if (LLVM_UNLIKELY(capacity > maxElements())) { |
133 | 0 | return throwExcessiveCapacityError(runtime, capacity); |
134 | 0 | } |
135 | | // Leave the segments as null. Whenever the size is changed, the segments |
136 | | // will be allocated. |
137 | 0 | const auto allocSize = allocationSizeForCapacity(capacity); |
138 | 0 | return createPseudoHandle( |
139 | 0 | runtime |
140 | 0 | .makeAVariable<SegmentedArrayBase, HasFinalizer::No, LongLived::Yes>( |
141 | 0 | allocSize)); |
142 | 0 | } Unexecuted instantiation: hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::createLongLived(hermes::vm::Runtime&, unsigned int) Unexecuted instantiation: hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::createLongLived(hermes::vm::Runtime&, unsigned int) |
143 | | |
144 | | template <typename HVType> |
145 | | CallResult<PseudoHandle<SegmentedArrayBase<HVType>>> SegmentedArrayBase< |
146 | 21 | HVType>::create(Runtime &runtime, size_type capacity, size_type size) { |
147 | 21 | auto arrRes = create(runtime, capacity); |
148 | 21 | if (LLVM_UNLIKELY(arrRes == ExecutionStatus::EXCEPTION)) { |
149 | 0 | return ExecutionStatus::EXCEPTION; |
150 | 0 | } |
151 | 21 | PseudoHandle<SegmentedArrayBase> self = std::move(*arrRes); |
152 | | // TODO T25663446: This is potentially optimizable to iterate over the |
153 | | // inline storage and the segments separately. |
154 | 21 | self = increaseSize(runtime, std::move(self), size); |
155 | 21 | return self; |
156 | 21 | } Unexecuted instantiation: hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::create(hermes::vm::Runtime&, unsigned int, unsigned int) hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::create(hermes::vm::Runtime&, unsigned int, unsigned int) Line | Count | Source | 146 | 21 | HVType>::create(Runtime &runtime, size_type capacity, size_type size) { | 147 | 21 | auto arrRes = create(runtime, capacity); | 148 | 21 | if (LLVM_UNLIKELY(arrRes == ExecutionStatus::EXCEPTION)) { | 149 | 0 | return ExecutionStatus::EXCEPTION; | 150 | 0 | } | 151 | 21 | PseudoHandle<SegmentedArrayBase> self = std::move(*arrRes); | 152 | | // TODO T25663446: This is potentially optimizable to iterate over the | 153 | | // inline storage and the segments separately. | 154 | 21 | self = increaseSize(runtime, std::move(self), size); | 155 | 21 | return self; | 156 | 21 | } |
|
157 | | |
158 | | template <typename HVType> |
159 | | typename SegmentedArrayBase<HVType>::size_type |
160 | 4.30M | SegmentedArrayBase<HVType>::capacity() const { |
161 | 4.30M | const auto numSlotsUsed = numSlotsUsed_.load(std::memory_order_relaxed); |
162 | 4.30M | if (numSlotsUsed <= kValueToSegmentThreshold) { |
163 | | // In the case where the size is less than the number of inline elements, |
164 | | // the capacity is at most slotCapacity, or the segment threshold if slot |
165 | | // capacity goes beyond that. |
166 | 577k | return std::min(slotCapacity(), size_type{kValueToSegmentThreshold}); |
167 | 3.73M | } else { |
168 | | // Any slot after numSlotsUsed_ is guaranteed to be null. |
169 | 3.73M | return kValueToSegmentThreshold + |
170 | 3.73M | (numSlotsUsed - kValueToSegmentThreshold) * Segment::kMaxLength; |
171 | 3.73M | } |
172 | 4.30M | } hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::capacity() const Line | Count | Source | 160 | 1.08M | SegmentedArrayBase<HVType>::capacity() const { | 161 | 1.08M | const auto numSlotsUsed = numSlotsUsed_.load(std::memory_order_relaxed); | 162 | 1.08M | if (numSlotsUsed <= kValueToSegmentThreshold) { | 163 | | // In the case where the size is less than the number of inline elements, | 164 | | // the capacity is at most slotCapacity, or the segment threshold if slot | 165 | | // capacity goes beyond that. | 166 | 24.6k | return std::min(slotCapacity(), size_type{kValueToSegmentThreshold}); | 167 | 1.06M | } else { | 168 | | // Any slot after numSlotsUsed_ is guaranteed to be null. | 169 | 1.06M | return kValueToSegmentThreshold + | 170 | 1.06M | (numSlotsUsed - kValueToSegmentThreshold) * Segment::kMaxLength; | 171 | 1.06M | } | 172 | 1.08M | } |
hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::capacity() const Line | Count | Source | 160 | 3.22M | SegmentedArrayBase<HVType>::capacity() const { | 161 | 3.22M | const auto numSlotsUsed = numSlotsUsed_.load(std::memory_order_relaxed); | 162 | 3.22M | if (numSlotsUsed <= kValueToSegmentThreshold) { | 163 | | // In the case where the size is less than the number of inline elements, | 164 | | // the capacity is at most slotCapacity, or the segment threshold if slot | 165 | | // capacity goes beyond that. | 166 | 553k | return std::min(slotCapacity(), size_type{kValueToSegmentThreshold}); | 167 | 2.67M | } else { | 168 | | // Any slot after numSlotsUsed_ is guaranteed to be null. | 169 | 2.67M | return kValueToSegmentThreshold + | 170 | 2.67M | (numSlotsUsed - kValueToSegmentThreshold) * Segment::kMaxLength; | 171 | 2.67M | } | 172 | 3.22M | } |
|
173 | | |
174 | | template <typename HVType> |
175 | | typename SegmentedArrayBase<HVType>::size_type |
176 | 544k | SegmentedArrayBase<HVType>::totalCapacityOfSpine() const { |
177 | 544k | const auto slotCap = slotCapacity(); |
178 | 544k | if (slotCap <= kValueToSegmentThreshold) { |
179 | 12.3k | return slotCap; |
180 | 531k | } else { |
181 | 531k | return kValueToSegmentThreshold + |
182 | 531k | (slotCap - kValueToSegmentThreshold) * Segment::kMaxLength; |
183 | 531k | } |
184 | 544k | } hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::totalCapacityOfSpine() const Line | Count | Source | 176 | 543k | SegmentedArrayBase<HVType>::totalCapacityOfSpine() const { | 177 | 543k | const auto slotCap = slotCapacity(); | 178 | 543k | if (slotCap <= kValueToSegmentThreshold) { | 179 | 12.3k | return slotCap; | 180 | 530k | } else { | 181 | 530k | return kValueToSegmentThreshold + | 182 | 530k | (slotCap - kValueToSegmentThreshold) * Segment::kMaxLength; | 183 | 530k | } | 184 | 543k | } |
hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::totalCapacityOfSpine() const Line | Count | Source | 176 | 968 | SegmentedArrayBase<HVType>::totalCapacityOfSpine() const { | 177 | 968 | const auto slotCap = slotCapacity(); | 178 | 968 | if (slotCap <= kValueToSegmentThreshold) { | 179 | 88 | return slotCap; | 180 | 880 | } else { | 181 | 880 | return kValueToSegmentThreshold + | 182 | 880 | (slotCap - kValueToSegmentThreshold) * Segment::kMaxLength; | 183 | 880 | } | 184 | 968 | } |
|
185 | | |
186 | | template <typename HVType> |
187 | | ExecutionStatus SegmentedArrayBase<HVType>::push_back( |
188 | | MutableHandle<SegmentedArrayBase> &self, |
189 | | Runtime &runtime, |
190 | 543k | Handle<> value) { |
191 | 543k | auto oldSize = self->size(runtime); |
192 | 543k | if (growRight(self, runtime, 1) == ExecutionStatus::EXCEPTION) { |
193 | 0 | return ExecutionStatus::EXCEPTION; |
194 | 0 | } |
195 | 543k | const auto shv = HVType::encodeHermesValue(*value, runtime); |
196 | 543k | auto &elm = self->atRef(runtime, oldSize); |
197 | 543k | new (&elm) GCHVType(shv, runtime.getHeap()); |
198 | 543k | return ExecutionStatus::RETURNED; |
199 | 543k | } hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::push_back(hermes::vm::MutableHandle<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue> >&, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>) Line | Count | Source | 190 | 543k | Handle<> value) { | 191 | 543k | auto oldSize = self->size(runtime); | 192 | 543k | if (growRight(self, runtime, 1) == ExecutionStatus::EXCEPTION) { | 193 | 0 | return ExecutionStatus::EXCEPTION; | 194 | 0 | } | 195 | 543k | const auto shv = HVType::encodeHermesValue(*value, runtime); | 196 | 543k | auto &elm = self->atRef(runtime, oldSize); | 197 | 543k | new (&elm) GCHVType(shv, runtime.getHeap()); | 198 | 543k | return ExecutionStatus::RETURNED; | 199 | 543k | } |
Unexecuted instantiation: hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::push_back(hermes::vm::MutableHandle<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32> >&, hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>) |
200 | | |
201 | | template <typename HVType> |
202 | | ExecutionStatus SegmentedArrayBase<HVType>::resize( |
203 | | MutableHandle<SegmentedArrayBase> &self, |
204 | | Runtime &runtime, |
205 | 968 | size_type newSize) { |
206 | 968 | if (newSize > self->size(runtime)) { |
207 | 968 | return growRight(self, runtime, newSize - self->size(runtime)); |
208 | 968 | } else if (newSize < self->size(runtime)) { |
209 | 0 | self->shrinkRight(runtime, self->size(runtime) - newSize); |
210 | 0 | } |
211 | 0 | return ExecutionStatus::RETURNED; |
212 | 968 | } Unexecuted instantiation: hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::resize(hermes::vm::MutableHandle<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue> >&, hermes::vm::Runtime&, unsigned int) hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::resize(hermes::vm::MutableHandle<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32> >&, hermes::vm::Runtime&, unsigned int) Line | Count | Source | 205 | 968 | size_type newSize) { | 206 | 968 | if (newSize > self->size(runtime)) { | 207 | 968 | return growRight(self, runtime, newSize - self->size(runtime)); | 208 | 968 | } else if (newSize < self->size(runtime)) { | 209 | 0 | self->shrinkRight(runtime, self->size(runtime) - newSize); | 210 | 0 | } | 211 | 0 | return ExecutionStatus::RETURNED; | 212 | 968 | } |
|
213 | | |
214 | | template <typename HVType> |
215 | | ExecutionStatus SegmentedArrayBase<HVType>::resizeLeft( |
216 | | MutableHandle<SegmentedArrayBase> &self, |
217 | | Runtime &runtime, |
218 | 0 | size_type newSize) { |
219 | 0 | if (newSize == self->size(runtime)) { |
220 | 0 | return ExecutionStatus::RETURNED; |
221 | 0 | } else if (newSize > self->size(runtime)) { |
222 | 0 | return growLeft(self, runtime, newSize - self->size(runtime)); |
223 | 0 | } else { |
224 | 0 | self->shrinkLeft(runtime, self->size(runtime) - newSize); |
225 | 0 | return ExecutionStatus::RETURNED; |
226 | 0 | } |
227 | 0 | } Unexecuted instantiation: hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::resizeLeft(hermes::vm::MutableHandle<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue> >&, hermes::vm::Runtime&, unsigned int) Unexecuted instantiation: hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::resizeLeft(hermes::vm::MutableHandle<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32> >&, hermes::vm::Runtime&, unsigned int) |
228 | | |
229 | | template <typename HVType> |
230 | | void SegmentedArrayBase<HVType>::resizeWithinCapacity( |
231 | | SegmentedArrayBase *self, |
232 | | Runtime &runtime, |
233 | 1.07M | size_type newSize) { |
234 | 1.07M | const size_type currSize = self->size(runtime); |
235 | 1.07M | assert( |
236 | 1.07M | newSize <= self->capacity() && |
237 | 1.07M | "Cannot resizeWithinCapacity to a size not within capacity"); |
238 | 1.07M | if (newSize > currSize) { |
239 | 1.07M | self->increaseSizeWithinCapacity(runtime, newSize - currSize); |
240 | 1.07M | } else if (newSize < currSize) { |
241 | 0 | self->shrinkRight(runtime, currSize - newSize); |
242 | 0 | } |
243 | 1.07M | } Unexecuted instantiation: hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::resizeWithinCapacity(hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>*, hermes::vm::Runtime&, unsigned int) hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::resizeWithinCapacity(hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>*, hermes::vm::Runtime&, unsigned int) Line | Count | Source | 233 | 1.07M | size_type newSize) { | 234 | 1.07M | const size_type currSize = self->size(runtime); | 235 | 1.07M | assert( | 236 | 1.07M | newSize <= self->capacity() && | 237 | 1.07M | "Cannot resizeWithinCapacity to a size not within capacity"); | 238 | 1.07M | if (newSize > currSize) { | 239 | 1.07M | self->increaseSizeWithinCapacity(runtime, newSize - currSize); | 240 | 1.07M | } else if (newSize < currSize) { | 241 | 0 | self->shrinkRight(runtime, currSize - newSize); | 242 | 0 | } | 243 | 1.07M | } |
|
244 | | |
245 | | template <typename HVType> |
246 | | ExecutionStatus SegmentedArrayBase<HVType>::throwExcessiveCapacityError( |
247 | | Runtime &runtime, |
248 | 0 | size_type capacity) { |
249 | 0 | assert( |
250 | 0 | capacity > maxElements() && |
251 | 0 | "Shouldn't call this without first checking that capacity is big"); |
252 | 0 | return runtime.raiseRangeError( |
253 | 0 | TwineChar16( |
254 | 0 | "Requested an array size larger than the max allowable: Requested elements = ") + |
255 | 0 | capacity + ", max elements = " + maxElements()); |
256 | 0 | } Unexecuted instantiation: hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::throwExcessiveCapacityError(hermes::vm::Runtime&, unsigned int) Unexecuted instantiation: hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::throwExcessiveCapacityError(hermes::vm::Runtime&, unsigned int) |
257 | | |
258 | | template <typename HVType> |
259 | | void SegmentedArrayBase<HVType>::allocateSegment( |
260 | | Runtime &runtime, |
261 | | Handle<SegmentedArrayBase> self, |
262 | 1.58k | SegmentNumber segment) { |
263 | 1.58k | assert( |
264 | 1.58k | self->segmentAtPossiblyUnallocated(segment)->isEmpty() && |
265 | 1.58k | "Allocating into a non-empty segment"); |
266 | 1.58k | PseudoHandle<Segment> c = Segment::create(runtime); |
267 | 1.58k | self->segmentAtPossiblyUnallocated(segment)->set( |
268 | 1.58k | HVType::encodeObjectValue(c.get(), runtime), runtime.getHeap()); |
269 | 1.58k | } hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::allocateSegment(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue> >, unsigned int) Line | Count | Source | 262 | 520 | SegmentNumber segment) { | 263 | 520 | assert( | 264 | 520 | self->segmentAtPossiblyUnallocated(segment)->isEmpty() && | 265 | 520 | "Allocating into a non-empty segment"); | 266 | 520 | PseudoHandle<Segment> c = Segment::create(runtime); | 267 | 520 | self->segmentAtPossiblyUnallocated(segment)->set( | 268 | 520 | HVType::encodeObjectValue(c.get(), runtime), runtime.getHeap()); | 269 | 520 | } |
hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::allocateSegment(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32> >, unsigned int) Line | Count | Source | 262 | 1.06k | SegmentNumber segment) { | 263 | 1.06k | assert( | 264 | 1.06k | self->segmentAtPossiblyUnallocated(segment)->isEmpty() && | 265 | 1.06k | "Allocating into a non-empty segment"); | 266 | 1.06k | PseudoHandle<Segment> c = Segment::create(runtime); | 267 | 1.06k | self->segmentAtPossiblyUnallocated(segment)->set( | 268 | 1.06k | HVType::encodeObjectValue(c.get(), runtime), runtime.getHeap()); | 269 | 1.06k | } |
|
270 | | |
271 | | template <typename HVType> |
272 | | ExecutionStatus SegmentedArrayBase<HVType>::growRight( |
273 | | MutableHandle<SegmentedArrayBase> &self, |
274 | | Runtime &runtime, |
275 | 544k | size_type amount) { |
276 | 544k | if (self->size(runtime) + amount <= self->totalCapacityOfSpine()) { |
277 | 543k | increaseSize(runtime, self, amount); |
278 | 543k | return ExecutionStatus::RETURNED; |
279 | 543k | } |
280 | 162 | const auto newSize = self->size(runtime) + amount; |
281 | | // Allocate a new SegmentedArray according to the resize policy. |
282 | 162 | auto arrRes = |
283 | 162 | create(runtime, calculateNewCapacity(self->size(runtime), newSize)); |
284 | 162 | if (arrRes == ExecutionStatus::EXCEPTION) { |
285 | 0 | return ExecutionStatus::EXCEPTION; |
286 | 0 | } |
287 | 162 | PseudoHandle<SegmentedArrayBase> newSegmentedArray = std::move(*arrRes); |
288 | | // Copy inline storage and segments over. |
289 | | // Do this with raw pointers so that the range write barrier occurs. |
290 | 162 | const auto numSlotsUsed = self->numSlotsUsed_.load(std::memory_order_relaxed); |
291 | 162 | GCHVType::uninitialized_copy( |
292 | 162 | self->inlineStorage(), |
293 | 162 | self->inlineStorage() + numSlotsUsed, |
294 | 162 | newSegmentedArray->inlineStorage(), |
295 | 162 | runtime.getHeap()); |
296 | | // Set the size of the new array to be the same as the old array's size. |
297 | 162 | newSegmentedArray->numSlotsUsed_.store( |
298 | 162 | numSlotsUsed, std::memory_order_release); |
299 | 162 | newSegmentedArray = |
300 | 162 | increaseSize(runtime, std::move(newSegmentedArray), amount); |
301 | | // Assign back to self. |
302 | 162 | self = newSegmentedArray.get(); |
303 | 162 | return ExecutionStatus::RETURNED; |
304 | 162 | } hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::growRight(hermes::vm::MutableHandle<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue> >&, hermes::vm::Runtime&, unsigned int) Line | Count | Source | 275 | 543k | size_type amount) { | 276 | 543k | if (self->size(runtime) + amount <= self->totalCapacityOfSpine()) { | 277 | 543k | increaseSize(runtime, self, amount); | 278 | 543k | return ExecutionStatus::RETURNED; | 279 | 543k | } | 280 | 56 | const auto newSize = self->size(runtime) + amount; | 281 | | // Allocate a new SegmentedArray according to the resize policy. | 282 | 56 | auto arrRes = | 283 | 56 | create(runtime, calculateNewCapacity(self->size(runtime), newSize)); | 284 | 56 | if (arrRes == ExecutionStatus::EXCEPTION) { | 285 | 0 | return ExecutionStatus::EXCEPTION; | 286 | 0 | } | 287 | 56 | PseudoHandle<SegmentedArrayBase> newSegmentedArray = std::move(*arrRes); | 288 | | // Copy inline storage and segments over. | 289 | | // Do this with raw pointers so that the range write barrier occurs. | 290 | 56 | const auto numSlotsUsed = self->numSlotsUsed_.load(std::memory_order_relaxed); | 291 | 56 | GCHVType::uninitialized_copy( | 292 | 56 | self->inlineStorage(), | 293 | 56 | self->inlineStorage() + numSlotsUsed, | 294 | 56 | newSegmentedArray->inlineStorage(), | 295 | 56 | runtime.getHeap()); | 296 | | // Set the size of the new array to be the same as the old array's size. | 297 | 56 | newSegmentedArray->numSlotsUsed_.store( | 298 | 56 | numSlotsUsed, std::memory_order_release); | 299 | 56 | newSegmentedArray = | 300 | 56 | increaseSize(runtime, std::move(newSegmentedArray), amount); | 301 | | // Assign back to self. | 302 | 56 | self = newSegmentedArray.get(); | 303 | 56 | return ExecutionStatus::RETURNED; | 304 | 56 | } |
hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::growRight(hermes::vm::MutableHandle<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32> >&, hermes::vm::Runtime&, unsigned int) Line | Count | Source | 275 | 968 | size_type amount) { | 276 | 968 | if (self->size(runtime) + amount <= self->totalCapacityOfSpine()) { | 277 | 862 | increaseSize(runtime, self, amount); | 278 | 862 | return ExecutionStatus::RETURNED; | 279 | 862 | } | 280 | 106 | const auto newSize = self->size(runtime) + amount; | 281 | | // Allocate a new SegmentedArray according to the resize policy. | 282 | 106 | auto arrRes = | 283 | 106 | create(runtime, calculateNewCapacity(self->size(runtime), newSize)); | 284 | 106 | if (arrRes == ExecutionStatus::EXCEPTION) { | 285 | 0 | return ExecutionStatus::EXCEPTION; | 286 | 0 | } | 287 | 106 | PseudoHandle<SegmentedArrayBase> newSegmentedArray = std::move(*arrRes); | 288 | | // Copy inline storage and segments over. | 289 | | // Do this with raw pointers so that the range write barrier occurs. | 290 | 106 | const auto numSlotsUsed = self->numSlotsUsed_.load(std::memory_order_relaxed); | 291 | 106 | GCHVType::uninitialized_copy( | 292 | 106 | self->inlineStorage(), | 293 | 106 | self->inlineStorage() + numSlotsUsed, | 294 | 106 | newSegmentedArray->inlineStorage(), | 295 | 106 | runtime.getHeap()); | 296 | | // Set the size of the new array to be the same as the old array's size. | 297 | 106 | newSegmentedArray->numSlotsUsed_.store( | 298 | 106 | numSlotsUsed, std::memory_order_release); | 299 | 106 | newSegmentedArray = | 300 | 106 | increaseSize(runtime, std::move(newSegmentedArray), amount); | 301 | | // Assign back to self. | 302 | 106 | self = newSegmentedArray.get(); | 303 | 106 | return ExecutionStatus::RETURNED; | 304 | 106 | } |
|
305 | | |
306 | | template <typename HVType> |
307 | | ExecutionStatus SegmentedArrayBase<HVType>::growLeft( |
308 | | MutableHandle<SegmentedArrayBase> &self, |
309 | | Runtime &runtime, |
310 | 0 | size_type amount) { |
311 | 0 | if (self->size(runtime) + amount <= self->totalCapacityOfSpine()) { |
312 | 0 | growLeftWithinCapacity(runtime, self, amount); |
313 | 0 | return ExecutionStatus::RETURNED; |
314 | 0 | } |
315 | 0 | const auto newSize = self->size(runtime) + amount; |
316 | 0 | auto arrRes = create( |
317 | 0 | runtime, calculateNewCapacity(self->size(runtime), newSize), newSize); |
318 | 0 | if (arrRes == ExecutionStatus::EXCEPTION) { |
319 | 0 | return ExecutionStatus::EXCEPTION; |
320 | 0 | } |
321 | 0 | PseudoHandle<SegmentedArrayBase> newSegmentedArray = std::move(*arrRes); |
322 | | // Copy element-by-element, since a shift would need to happen anyway. |
323 | | // Since self and newSegmentedArray are distinct, don't need to worry about |
324 | | // order. |
325 | 0 | GCHVType::copy( |
326 | 0 | self->begin(runtime), |
327 | 0 | self->end(runtime), |
328 | 0 | newSegmentedArray->begin(runtime) + amount, |
329 | 0 | runtime.getHeap()); |
330 | | // Assign back to self. |
331 | 0 | self = newSegmentedArray.get(); |
332 | 0 | return ExecutionStatus::RETURNED; |
333 | 0 | } Unexecuted instantiation: hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::growLeft(hermes::vm::MutableHandle<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue> >&, hermes::vm::Runtime&, unsigned int) Unexecuted instantiation: hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::growLeft(hermes::vm::MutableHandle<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32> >&, hermes::vm::Runtime&, unsigned int) |
334 | | |
335 | | template <typename HVType> |
336 | | void SegmentedArrayBase<HVType>::growLeftWithinCapacity( |
337 | | Runtime &runtime, |
338 | | PseudoHandle<SegmentedArrayBase> self, |
339 | 0 | size_type amount) { |
340 | 0 | assert( |
341 | 0 | self->size(runtime) + amount <= self->totalCapacityOfSpine() && |
342 | 0 | "Cannot grow higher than capacity"); |
343 | | // Fill with empty values at the end to simplify the write barrier. |
344 | 0 | self = increaseSize(runtime, std::move(self), amount); |
345 | | // Copy the range from the beginning to the end. |
346 | 0 | GCHVType::copy_backward( |
347 | 0 | self->begin(runtime), |
348 | 0 | self->end(runtime) - amount, |
349 | 0 | self->end(runtime), |
350 | 0 | runtime.getHeap()); |
351 | | // Fill the beginning with empty values. |
352 | 0 | GCHVType::fill( |
353 | 0 | self->begin(runtime), |
354 | 0 | self->begin(runtime) + amount, |
355 | 0 | HVType::encodeEmptyValue(), |
356 | 0 | runtime.getHeap()); |
357 | 0 | } Unexecuted instantiation: hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::growLeftWithinCapacity(hermes::vm::Runtime&, hermes::vm::PseudoHandle<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue> >, unsigned int) Unexecuted instantiation: hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::growLeftWithinCapacity(hermes::vm::Runtime&, hermes::vm::PseudoHandle<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32> >, unsigned int) |
358 | | |
359 | | template <typename HVType> |
360 | | void SegmentedArrayBase<HVType>::shrinkRight( |
361 | | Runtime &runtime, |
362 | 6 | size_type amount) { |
363 | 6 | decreaseSize(runtime, amount); |
364 | 6 | } hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::shrinkRight(hermes::vm::Runtime&, unsigned int) Line | Count | Source | 362 | 6 | size_type amount) { | 363 | 6 | decreaseSize(runtime, amount); | 364 | 6 | } |
Unexecuted instantiation: hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::shrinkRight(hermes::vm::Runtime&, unsigned int) |
365 | | |
366 | | template <typename HVType> |
367 | | void SegmentedArrayBase<HVType>::shrinkLeft( |
368 | | Runtime &runtime, |
369 | 0 | size_type amount) { |
370 | | // Copy the end values leftwards to the beginning. |
371 | 0 | GCHVType::copy( |
372 | 0 | begin(runtime) + amount, end(runtime), begin(runtime), runtime.getHeap()); |
373 | | // Now that all the values are moved down, fill the end with empty values. |
374 | 0 | decreaseSize(runtime, amount); |
375 | 0 | } Unexecuted instantiation: hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::shrinkLeft(hermes::vm::Runtime&, unsigned int) Unexecuted instantiation: hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::shrinkLeft(hermes::vm::Runtime&, unsigned int) |
376 | | |
377 | | template <typename HVType> |
378 | | void SegmentedArrayBase<HVType>::increaseSizeWithinCapacity( |
379 | | Runtime &runtime, |
380 | 1.61M | size_type amount) { |
381 | | // This function has the same logic as increaseSize, but removes some |
382 | | // complexity from avoiding dealing with alllocations. |
383 | 1.61M | const auto currSize = size(runtime); |
384 | 1.61M | const auto finalSize = currSize + amount; |
385 | 1.61M | assert( |
386 | 1.61M | finalSize <= capacity() && |
387 | 1.61M | "Cannot use increaseSizeWithinCapacity without checking for capacity first"); |
388 | | |
389 | 1.61M | if (finalSize <= kValueToSegmentThreshold) { |
390 | | // currSize and finalSize are inside inline storage, bump and fill. |
391 | 196k | GCHVType::uninitialized_fill( |
392 | 196k | inlineStorage() + currSize, |
393 | 196k | inlineStorage() + finalSize, |
394 | 196k | HVType::encodeEmptyValue(), |
395 | 196k | runtime.getHeap()); |
396 | | // Set the final size. |
397 | 196k | numSlotsUsed_.store(finalSize, std::memory_order_release); |
398 | 196k | return; |
399 | 196k | } |
400 | | // Since this change is within capacity, it is at most filling up a single |
401 | | // segment. |
402 | 1.41M | const SegmentNumber segment = toSegment(finalSize - 1); |
403 | 1.41M | const auto segmentLength = toInterior(finalSize - 1) + 1; |
404 | | // Fill the inline slots if necessary, and the single segment. |
405 | 1.41M | if (currSize < kValueToSegmentThreshold) { |
406 | 0 | GCHVType::uninitialized_fill( |
407 | 0 | inlineStorage() + currSize, |
408 | 0 | inlineStorage() + kValueToSegmentThreshold, |
409 | 0 | HVType::encodeEmptyValue(), |
410 | 0 | runtime.getHeap()); |
411 | 0 | } |
412 | 1.41M | segmentAt(runtime, segment)->setLength(runtime, segmentLength); |
413 | 1.41M | } hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::increaseSizeWithinCapacity(hermes::vm::Runtime&, unsigned int) Line | Count | Source | 380 | 542k | size_type amount) { | 381 | | // This function has the same logic as increaseSize, but removes some | 382 | | // complexity from avoiding dealing with alllocations. | 383 | 542k | const auto currSize = size(runtime); | 384 | 542k | const auto finalSize = currSize + amount; | 385 | 542k | assert( | 386 | 542k | finalSize <= capacity() && | 387 | 542k | "Cannot use increaseSizeWithinCapacity without checking for capacity first"); | 388 | | | 389 | 542k | if (finalSize <= kValueToSegmentThreshold) { | 390 | | // currSize and finalSize are inside inline storage, bump and fill. | 391 | 12.3k | GCHVType::uninitialized_fill( | 392 | 12.3k | inlineStorage() + currSize, | 393 | 12.3k | inlineStorage() + finalSize, | 394 | 12.3k | HVType::encodeEmptyValue(), | 395 | 12.3k | runtime.getHeap()); | 396 | | // Set the final size. | 397 | 12.3k | numSlotsUsed_.store(finalSize, std::memory_order_release); | 398 | 12.3k | return; | 399 | 12.3k | } | 400 | | // Since this change is within capacity, it is at most filling up a single | 401 | | // segment. | 402 | 530k | const SegmentNumber segment = toSegment(finalSize - 1); | 403 | 530k | const auto segmentLength = toInterior(finalSize - 1) + 1; | 404 | | // Fill the inline slots if necessary, and the single segment. | 405 | 530k | if (currSize < kValueToSegmentThreshold) { | 406 | 0 | GCHVType::uninitialized_fill( | 407 | 0 | inlineStorage() + currSize, | 408 | 0 | inlineStorage() + kValueToSegmentThreshold, | 409 | 0 | HVType::encodeEmptyValue(), | 410 | 0 | runtime.getHeap()); | 411 | 0 | } | 412 | 530k | segmentAt(runtime, segment)->setLength(runtime, segmentLength); | 413 | 530k | } |
hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::increaseSizeWithinCapacity(hermes::vm::Runtime&, unsigned int) Line | Count | Source | 380 | 1.07M | size_type amount) { | 381 | | // This function has the same logic as increaseSize, but removes some | 382 | | // complexity from avoiding dealing with alllocations. | 383 | 1.07M | const auto currSize = size(runtime); | 384 | 1.07M | const auto finalSize = currSize + amount; | 385 | 1.07M | assert( | 386 | 1.07M | finalSize <= capacity() && | 387 | 1.07M | "Cannot use increaseSizeWithinCapacity without checking for capacity first"); | 388 | | | 389 | 1.07M | if (finalSize <= kValueToSegmentThreshold) { | 390 | | // currSize and finalSize are inside inline storage, bump and fill. | 391 | 184k | GCHVType::uninitialized_fill( | 392 | 184k | inlineStorage() + currSize, | 393 | 184k | inlineStorage() + finalSize, | 394 | 184k | HVType::encodeEmptyValue(), | 395 | 184k | runtime.getHeap()); | 396 | | // Set the final size. | 397 | 184k | numSlotsUsed_.store(finalSize, std::memory_order_release); | 398 | 184k | return; | 399 | 184k | } | 400 | | // Since this change is within capacity, it is at most filling up a single | 401 | | // segment. | 402 | 889k | const SegmentNumber segment = toSegment(finalSize - 1); | 403 | 889k | const auto segmentLength = toInterior(finalSize - 1) + 1; | 404 | | // Fill the inline slots if necessary, and the single segment. | 405 | 889k | if (currSize < kValueToSegmentThreshold) { | 406 | 0 | GCHVType::uninitialized_fill( | 407 | 0 | inlineStorage() + currSize, | 408 | 0 | inlineStorage() + kValueToSegmentThreshold, | 409 | 0 | HVType::encodeEmptyValue(), | 410 | 0 | runtime.getHeap()); | 411 | 0 | } | 412 | 889k | segmentAt(runtime, segment)->setLength(runtime, segmentLength); | 413 | 889k | } |
|
414 | | |
415 | | template <typename HVType> |
416 | | PseudoHandle<SegmentedArrayBase<HVType>> |
417 | | SegmentedArrayBase<HVType>::increaseSize( |
418 | | Runtime &runtime, |
419 | | PseudoHandle<SegmentedArrayBase> self, |
420 | 544k | size_type amount) { |
421 | 544k | const auto currSize = self->size(runtime); |
422 | 544k | const auto finalSize = currSize + amount; |
423 | | |
424 | 544k | if (finalSize <= self->capacity()) { |
425 | 542k | self->increaseSizeWithinCapacity(runtime, amount); |
426 | 542k | return self; |
427 | 542k | } |
428 | | |
429 | | // Inline slots must be reserved by the caller. Since finalSize is greater |
430 | | // than the capacity, we know that it must require adding segments. |
431 | 1.40k | assert(finalSize > kValueToSegmentThreshold); |
432 | | |
433 | | // currSize might be in inline storage, but finalSize is definitely in |
434 | | // segments. |
435 | | // Allocate missing segments after filling inline storage. |
436 | 1.40k | if (currSize <= kValueToSegmentThreshold) { |
437 | | // Segments will need to be allocated, if the old size didn't have the |
438 | | // inline storage filled up, fill it up now. |
439 | 30 | GCHVType::uninitialized_fill( |
440 | 30 | self->inlineStorage() + currSize, |
441 | 30 | self->inlineStorage() + kValueToSegmentThreshold, |
442 | 30 | HVType::encodeEmptyValue(), |
443 | 30 | runtime.getHeap()); |
444 | | // Set the size to the inline storage threshold. |
445 | 30 | self->numSlotsUsed_.store( |
446 | 30 | kValueToSegmentThreshold, std::memory_order_release); |
447 | 30 | } |
448 | | |
449 | | // NOTE: during this function, allocations can happen. |
450 | | // If one of these allocations triggers a full compacting GC, then the array |
451 | | // currently being increased might have its capacity shrunk to match its |
452 | | // numSlotsUsed. So, increase numSlotsUsed immediately to its final value |
453 | | // before the allocations happen so it isn't shrunk, and also fill with |
454 | | // empty values so that any mark passes don't fail. The segments should all |
455 | | // have length 0 until allocations are finished, so that uninitialized |
456 | | // memory is not scanned inside the segments. Once allocations are finished, |
457 | | // go back and fixup the lengths. |
458 | 1.40k | const SegmentNumber startSegment = |
459 | 1.40k | currSize <= kValueToSegmentThreshold ? 0 : toSegment(currSize - 1); |
460 | 1.40k | const SegmentNumber lastSegment = toSegment(finalSize - 1); |
461 | 1.40k | const auto newNumSlotsUsed = numSlotsForCapacity(finalSize); |
462 | | // Put empty values into all of the added slots so that the memory is not |
463 | | // uninitialized during marking. |
464 | 1.40k | GCHVType::uninitialized_fill( |
465 | 1.40k | self->inlineStorage() + |
466 | 1.40k | self->numSlotsUsed_.load(std::memory_order_relaxed), |
467 | 1.40k | self->inlineStorage() + newNumSlotsUsed, |
468 | 1.40k | HVType::encodeEmptyValue(), |
469 | 1.40k | runtime.getHeap()); |
470 | 1.40k | self->numSlotsUsed_.store(newNumSlotsUsed, std::memory_order_release); |
471 | | |
472 | | // Allocate a handle to track the current array. |
473 | 1.40k | auto selfHandle = runtime.makeHandle(std::move(self)); |
474 | | // Allocate each segment. |
475 | 1.40k | if (startSegment <= lastSegment && |
476 | 1.40k | selfHandle->segmentAtPossiblyUnallocated(startSegment)->isEmpty()) { |
477 | | // The start segment might already be allocated if it was half full when |
478 | | // we increase the size. |
479 | 30 | allocateSegment(runtime, selfHandle, startSegment); |
480 | 30 | } |
481 | 2.96k | for (auto i = startSegment + 1; i <= lastSegment; ++i) { |
482 | | // All segments except the start need to become allocated. |
483 | 1.55k | allocateSegment(runtime, selfHandle, i); |
484 | 1.55k | } |
485 | | |
486 | | // Now that all allocations have occurred, set the lengths inside each |
487 | | // segment, and optionally fill. |
488 | 4.37k | for (auto i = startSegment; i <= lastSegment; ++i) { |
489 | | // If its the last chunk, set to the length required by any leftover |
490 | | // elements. |
491 | 2.96k | const auto segmentLength = |
492 | 2.96k | i == lastSegment ? toInterior(finalSize - 1) + 1 : Segment::kMaxLength; |
493 | 2.96k | selfHandle->segmentAt(runtime, i)->setLength(runtime, segmentLength); |
494 | 2.96k | } |
495 | 1.40k | self = selfHandle; |
496 | 1.40k | return self; |
497 | 1.40k | } hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::increaseSize(hermes::vm::Runtime&, hermes::vm::PseudoHandle<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue> >, unsigned int) Line | Count | Source | 420 | 543k | size_type amount) { | 421 | 543k | const auto currSize = self->size(runtime); | 422 | 543k | const auto finalSize = currSize + amount; | 423 | | | 424 | 543k | if (finalSize <= self->capacity()) { | 425 | 542k | self->increaseSizeWithinCapacity(runtime, amount); | 426 | 542k | return self; | 427 | 542k | } | 428 | | | 429 | | // Inline slots must be reserved by the caller. Since finalSize is greater | 430 | | // than the capacity, we know that it must require adding segments. | 431 | 520 | assert(finalSize > kValueToSegmentThreshold); | 432 | | | 433 | | // currSize might be in inline storage, but finalSize is definitely in | 434 | | // segments. | 435 | | // Allocate missing segments after filling inline storage. | 436 | 520 | if (currSize <= kValueToSegmentThreshold) { | 437 | | // Segments will need to be allocated, if the old size didn't have the | 438 | | // inline storage filled up, fill it up now. | 439 | 3 | GCHVType::uninitialized_fill( | 440 | 3 | self->inlineStorage() + currSize, | 441 | 3 | self->inlineStorage() + kValueToSegmentThreshold, | 442 | 3 | HVType::encodeEmptyValue(), | 443 | 3 | runtime.getHeap()); | 444 | | // Set the size to the inline storage threshold. | 445 | 3 | self->numSlotsUsed_.store( | 446 | 3 | kValueToSegmentThreshold, std::memory_order_release); | 447 | 3 | } | 448 | | | 449 | | // NOTE: during this function, allocations can happen. | 450 | | // If one of these allocations triggers a full compacting GC, then the array | 451 | | // currently being increased might have its capacity shrunk to match its | 452 | | // numSlotsUsed. So, increase numSlotsUsed immediately to its final value | 453 | | // before the allocations happen so it isn't shrunk, and also fill with | 454 | | // empty values so that any mark passes don't fail. The segments should all | 455 | | // have length 0 until allocations are finished, so that uninitialized | 456 | | // memory is not scanned inside the segments. Once allocations are finished, | 457 | | // go back and fixup the lengths. | 458 | 520 | const SegmentNumber startSegment = | 459 | 520 | currSize <= kValueToSegmentThreshold ? 0 : toSegment(currSize - 1); | 460 | 520 | const SegmentNumber lastSegment = toSegment(finalSize - 1); | 461 | 520 | const auto newNumSlotsUsed = numSlotsForCapacity(finalSize); | 462 | | // Put empty values into all of the added slots so that the memory is not | 463 | | // uninitialized during marking. | 464 | 520 | GCHVType::uninitialized_fill( | 465 | 520 | self->inlineStorage() + | 466 | 520 | self->numSlotsUsed_.load(std::memory_order_relaxed), | 467 | 520 | self->inlineStorage() + newNumSlotsUsed, | 468 | 520 | HVType::encodeEmptyValue(), | 469 | 520 | runtime.getHeap()); | 470 | 520 | self->numSlotsUsed_.store(newNumSlotsUsed, std::memory_order_release); | 471 | | | 472 | | // Allocate a handle to track the current array. | 473 | 520 | auto selfHandle = runtime.makeHandle(std::move(self)); | 474 | | // Allocate each segment. | 475 | 520 | if (startSegment <= lastSegment && | 476 | 520 | selfHandle->segmentAtPossiblyUnallocated(startSegment)->isEmpty()) { | 477 | | // The start segment might already be allocated if it was half full when | 478 | | // we increase the size. | 479 | 3 | allocateSegment(runtime, selfHandle, startSegment); | 480 | 3 | } | 481 | 1.03k | for (auto i = startSegment + 1; i <= lastSegment; ++i) { | 482 | | // All segments except the start need to become allocated. | 483 | 517 | allocateSegment(runtime, selfHandle, i); | 484 | 517 | } | 485 | | | 486 | | // Now that all allocations have occurred, set the lengths inside each | 487 | | // segment, and optionally fill. | 488 | 1.55k | for (auto i = startSegment; i <= lastSegment; ++i) { | 489 | | // If its the last chunk, set to the length required by any leftover | 490 | | // elements. | 491 | 1.03k | const auto segmentLength = | 492 | 1.03k | i == lastSegment ? toInterior(finalSize - 1) + 1 : Segment::kMaxLength; | 493 | 1.03k | selfHandle->segmentAt(runtime, i)->setLength(runtime, segmentLength); | 494 | 1.03k | } | 495 | 520 | self = selfHandle; | 496 | 520 | return self; | 497 | 520 | } |
hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::increaseSize(hermes::vm::Runtime&, hermes::vm::PseudoHandle<hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32> >, unsigned int) Line | Count | Source | 420 | 989 | size_type amount) { | 421 | 989 | const auto currSize = self->size(runtime); | 422 | 989 | const auto finalSize = currSize + amount; | 423 | | | 424 | 989 | if (finalSize <= self->capacity()) { | 425 | 101 | self->increaseSizeWithinCapacity(runtime, amount); | 426 | 101 | return self; | 427 | 101 | } | 428 | | | 429 | | // Inline slots must be reserved by the caller. Since finalSize is greater | 430 | | // than the capacity, we know that it must require adding segments. | 431 | 888 | assert(finalSize > kValueToSegmentThreshold); | 432 | | | 433 | | // currSize might be in inline storage, but finalSize is definitely in | 434 | | // segments. | 435 | | // Allocate missing segments after filling inline storage. | 436 | 888 | if (currSize <= kValueToSegmentThreshold) { | 437 | | // Segments will need to be allocated, if the old size didn't have the | 438 | | // inline storage filled up, fill it up now. | 439 | 27 | GCHVType::uninitialized_fill( | 440 | 27 | self->inlineStorage() + currSize, | 441 | 27 | self->inlineStorage() + kValueToSegmentThreshold, | 442 | 27 | HVType::encodeEmptyValue(), | 443 | 27 | runtime.getHeap()); | 444 | | // Set the size to the inline storage threshold. | 445 | 27 | self->numSlotsUsed_.store( | 446 | 27 | kValueToSegmentThreshold, std::memory_order_release); | 447 | 27 | } | 448 | | | 449 | | // NOTE: during this function, allocations can happen. | 450 | | // If one of these allocations triggers a full compacting GC, then the array | 451 | | // currently being increased might have its capacity shrunk to match its | 452 | | // numSlotsUsed. So, increase numSlotsUsed immediately to its final value | 453 | | // before the allocations happen so it isn't shrunk, and also fill with | 454 | | // empty values so that any mark passes don't fail. The segments should all | 455 | | // have length 0 until allocations are finished, so that uninitialized | 456 | | // memory is not scanned inside the segments. Once allocations are finished, | 457 | | // go back and fixup the lengths. | 458 | 888 | const SegmentNumber startSegment = | 459 | 888 | currSize <= kValueToSegmentThreshold ? 0 : toSegment(currSize - 1); | 460 | 888 | const SegmentNumber lastSegment = toSegment(finalSize - 1); | 461 | 888 | const auto newNumSlotsUsed = numSlotsForCapacity(finalSize); | 462 | | // Put empty values into all of the added slots so that the memory is not | 463 | | // uninitialized during marking. | 464 | 888 | GCHVType::uninitialized_fill( | 465 | 888 | self->inlineStorage() + | 466 | 888 | self->numSlotsUsed_.load(std::memory_order_relaxed), | 467 | 888 | self->inlineStorage() + newNumSlotsUsed, | 468 | 888 | HVType::encodeEmptyValue(), | 469 | 888 | runtime.getHeap()); | 470 | 888 | self->numSlotsUsed_.store(newNumSlotsUsed, std::memory_order_release); | 471 | | | 472 | | // Allocate a handle to track the current array. | 473 | 888 | auto selfHandle = runtime.makeHandle(std::move(self)); | 474 | | // Allocate each segment. | 475 | 888 | if (startSegment <= lastSegment && | 476 | 888 | selfHandle->segmentAtPossiblyUnallocated(startSegment)->isEmpty()) { | 477 | | // The start segment might already be allocated if it was half full when | 478 | | // we increase the size. | 479 | 27 | allocateSegment(runtime, selfHandle, startSegment); | 480 | 27 | } | 481 | 1.92k | for (auto i = startSegment + 1; i <= lastSegment; ++i) { | 482 | | // All segments except the start need to become allocated. | 483 | 1.03k | allocateSegment(runtime, selfHandle, i); | 484 | 1.03k | } | 485 | | | 486 | | // Now that all allocations have occurred, set the lengths inside each | 487 | | // segment, and optionally fill. | 488 | 2.81k | for (auto i = startSegment; i <= lastSegment; ++i) { | 489 | | // If its the last chunk, set to the length required by any leftover | 490 | | // elements. | 491 | 1.92k | const auto segmentLength = | 492 | 1.92k | i == lastSegment ? toInterior(finalSize - 1) + 1 : Segment::kMaxLength; | 493 | 1.92k | selfHandle->segmentAt(runtime, i)->setLength(runtime, segmentLength); | 494 | 1.92k | } | 495 | 888 | self = selfHandle; | 496 | 888 | return self; | 497 | 888 | } |
|
498 | | |
499 | | template <typename HVType> |
500 | | void SegmentedArrayBase<HVType>::decreaseSize( |
501 | | Runtime &runtime, |
502 | 6 | size_type amount) { |
503 | 6 | const auto initialSize = size(runtime); |
504 | 6 | const auto initialNumSlots = numSlotsUsed_.load(std::memory_order_relaxed); |
505 | 6 | assert(amount <= initialSize && "Cannot decrease size past zero"); |
506 | 6 | const auto finalSize = initialSize - amount; |
507 | 6 | const auto finalNumSlots = numSlotsForCapacity(finalSize); |
508 | 6 | assert( |
509 | 6 | finalNumSlots <= initialNumSlots && |
510 | 6 | "Should not be increasing the number of slots"); |
511 | 6 | if (finalSize > kValueToSegmentThreshold) { |
512 | | // Set the new last used segment's length to be the leftover. |
513 | 0 | segmentAt(runtime, toSegment(finalSize - 1)) |
514 | 0 | ->setLength(runtime, toInterior(finalSize - 1) + 1); |
515 | 0 | } |
516 | | // Before shrinking, do a snapshot write barrier for the elements being |
517 | | // removed. |
518 | 6 | GCHVType::rangeUnreachableWriteBarrier( |
519 | 6 | inlineStorage() + finalNumSlots, |
520 | 6 | inlineStorage() + initialNumSlots, |
521 | 6 | runtime.getHeap()); |
522 | 6 | numSlotsUsed_.store(finalNumSlots, std::memory_order_release); |
523 | 6 | } hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::decreaseSize(hermes::vm::Runtime&, unsigned int) Line | Count | Source | 502 | 6 | size_type amount) { | 503 | 6 | const auto initialSize = size(runtime); | 504 | 6 | const auto initialNumSlots = numSlotsUsed_.load(std::memory_order_relaxed); | 505 | 6 | assert(amount <= initialSize && "Cannot decrease size past zero"); | 506 | 6 | const auto finalSize = initialSize - amount; | 507 | 6 | const auto finalNumSlots = numSlotsForCapacity(finalSize); | 508 | 6 | assert( | 509 | 6 | finalNumSlots <= initialNumSlots && | 510 | 6 | "Should not be increasing the number of slots"); | 511 | 6 | if (finalSize > kValueToSegmentThreshold) { | 512 | | // Set the new last used segment's length to be the leftover. | 513 | 0 | segmentAt(runtime, toSegment(finalSize - 1)) | 514 | 0 | ->setLength(runtime, toInterior(finalSize - 1) + 1); | 515 | 0 | } | 516 | | // Before shrinking, do a snapshot write barrier for the elements being | 517 | | // removed. | 518 | 6 | GCHVType::rangeUnreachableWriteBarrier( | 519 | 6 | inlineStorage() + finalNumSlots, | 520 | 6 | inlineStorage() + initialNumSlots, | 521 | 6 | runtime.getHeap()); | 522 | 6 | numSlotsUsed_.store(finalNumSlots, std::memory_order_release); | 523 | 6 | } |
Unexecuted instantiation: hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::decreaseSize(hermes::vm::Runtime&, unsigned int) |
524 | | |
525 | | template <typename HVType> |
526 | 2.56k | gcheapsize_t SegmentedArrayBase<HVType>::_trimSizeCallback(const GCCell *cell) { |
527 | 2.56k | const auto *self = reinterpret_cast<const SegmentedArrayBase *>(cell); |
528 | | // This array will shrink so that it has the same slot capacity as the slot |
529 | | // size. |
530 | 2.56k | return allocationSizeForSlots( |
531 | 2.56k | self->numSlotsUsed_.load(std::memory_order_relaxed)); |
532 | 2.56k | } Unexecuted instantiation: hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue>::_trimSizeCallback(hermes::vm::GCCell const*) hermes::vm::SegmentedArrayBase<hermes::vm::HermesValue32>::_trimSizeCallback(hermes::vm::GCCell const*) Line | Count | Source | 526 | 2.56k | gcheapsize_t SegmentedArrayBase<HVType>::_trimSizeCallback(const GCCell *cell) { | 527 | 2.56k | const auto *self = reinterpret_cast<const SegmentedArrayBase *>(cell); | 528 | | // This array will shrink so that it has the same slot capacity as the slot | 529 | | // size. | 530 | 2.56k | return allocationSizeForSlots( | 531 | 2.56k | self->numSlotsUsed_.load(std::memory_order_relaxed)); | 532 | 2.56k | } |
|
533 | | |
534 | | template class SegmentedArrayBase<HermesValue>; |
535 | | template class SegmentedArrayBase<SmallHermesValue>; |
536 | | |
537 | | } // namespace vm |
538 | | } // namespace hermes |