Coverage Report

Created: 2025-06-24 06:43

/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