Skip to content

Commit 08045e6

Browse files
lavenzgfacebook-github-bot
authored andcommitted
Store segment size in SHSegmentInfo (#1506)
Summary: We need the segment size in several places, such as CardTable, heap segment itself and getting sizes for large object GCCell. Add this size into `SHSegmentInfo`. In addition, in CardTableNCTest, when search dirty bits, we should start from kFirstUsedIndex, since the value o `SHSegmentInfo` may write some bytes to non-zero. Differential Revision: D61807366
1 parent dd78ee4 commit 08045e6

File tree

5 files changed

+79
-44
lines changed

5 files changed

+79
-44
lines changed

include/hermes/VM/AlignedHeapSegment.h

+26-8
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,26 @@ class AlignedHeapSegmentBase {
164164
return lowLim_;
165165
}
166166

167+
/// Read storage size from SHSegmentInfo.
168+
size_t storageSize() const {
169+
auto *segmentInfo = reinterpret_cast<const SHSegmentInfo *>(lowLim_);
170+
return (size_t)segmentInfo->shiftedSegmentSize
171+
<< HERMESVM_LOG_HEAP_SEGMENT_SIZE;
172+
}
173+
174+
/// The largest size the allocation region of an aligned heap segment could
175+
/// be.
176+
size_t maxSize() const {
177+
return storageSize() - kOffsetOfAllocRegion;
178+
}
179+
180+
/// Returns the address that is the upper bound of the segment.
181+
/// This is only used in debugging code and computing memory footprint, so
182+
/// just read the segment size from SHSegmentInfo.
183+
char *hiLim() const {
184+
return lowLim_ + storageSize();
185+
}
186+
167187
/// Returns the address at which the first allocation in this segment would
168188
/// occur.
169189
/// Disable UB sanitization because 'this' may be null during the tests.
@@ -285,7 +305,9 @@ class AlignedHeapSegment : public AlignedHeapSegmentBase {
285305
/// Mask for isolating the storage being pointed into by a pointer.
286306
static constexpr size_t kHighMask{~kLowMask};
287307

288-
/// Returns the storage size, in bytes, of an \c AlignedHeapSegment.
308+
/// Returns the storage size, in bytes, of an \c AlignedHeapSegment. This is a
309+
/// static override of AlignedHeapSegmentBase::storageSize, which reads the
310+
/// size from SHSegmentInfo.
289311
static constexpr size_t storageSize() {
290312
return kSize;
291313
}
@@ -379,7 +401,8 @@ class AlignedHeapSegment : public AlignedHeapSegmentBase {
379401
static inline void setCellHead(const GCCell *start, const size_t sz);
380402

381403
/// The largest size the allocation region of an aligned heap segment could
382-
/// be.
404+
/// be. This is a static override of AlignedHeapSegmentBase::maxSize(), which
405+
/// reads the storage size from SHSegmentInfo.
383406
inline static constexpr size_t maxSize();
384407

385408
/// The size of the allocation region in this aligned heap segment.
@@ -391,11 +414,6 @@ class AlignedHeapSegment : public AlignedHeapSegmentBase {
391414
/// The number of bytes in the segment that are available for allocation.
392415
inline size_t available() const;
393416

394-
/// Returns the address that is the upper bound of the segment.
395-
char *hiLim() const {
396-
return lowLim() + storageSize();
397-
}
398-
399417
/// Returns the first address after the region in which allocations can occur,
400418
/// taking external memory credits into a account (they decrease the effective
401419
/// end).
@@ -545,7 +563,7 @@ void AlignedHeapSegment::setCellHead(const GCCell *cellStart, const size_t sz) {
545563
}
546564

547565
/* static */ constexpr size_t AlignedHeapSegment::maxSize() {
548-
return storageSize() - offsetof(Contents, allocRegion_);
566+
return storageSize() - kOffsetOfAllocRegion;
549567
}
550568

551569
size_t AlignedHeapSegment::size() const {

include/hermes/VM/CardTableNC.h

+33-16
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,9 @@ class CardTable {
6363
static constexpr size_t kCardSize = 1 << kLogCardSize; // ==> 512-byte cards.
6464
static constexpr size_t kSegmentSize = 1 << HERMESVM_LOG_HEAP_SEGMENT_SIZE;
6565

66-
/// The number of valid indices into the card table.
67-
static constexpr size_t kValidIndices = kSegmentSize >> kLogCardSize;
68-
69-
/// The size of the card table.
70-
static constexpr size_t kCardTableSize = kValidIndices;
66+
/// The size of the maximum inline card table. CardStatus array and boundary
67+
/// array for larger segment has larger size and is stored separately.
68+
static constexpr size_t kInlineCardTableSize = kSegmentSize >> kLogCardSize;
7169

7270
/// For convenience, this is a conversion factor to determine how many bytes
7371
/// in the heap correspond to a single byte in the card table. This is
@@ -93,10 +91,15 @@ class CardTable {
9391
/// since the CardTable contains two byte arrays of that size (cards_ and
9492
/// boundaries_). And this index must be larger than the size of SHSegmentInfo
9593
/// to avoid corrupting it when clearing/dirtying bits.
96-
static constexpr size_t kFirstUsedIndex =
97-
std::max(sizeof(SHSegmentInfo), (2 * kCardTableSize) >> kLogCardSize);
98-
99-
CardTable() = default;
94+
static constexpr size_t kFirstUsedIndex = std::max(
95+
sizeof(SHSegmentInfo),
96+
(2 * kInlineCardTableSize) >> kLogCardSize);
97+
98+
CardTable() {
99+
// Preserve the segment size.
100+
segmentInfo_.shiftedSegmentSize =
101+
kSegmentSize >> HERMESVM_LOG_HEAP_SEGMENT_SIZE;
102+
}
100103
/// CardTable is not copyable or movable: It must be constructed in-place.
101104
CardTable(const CardTable &) = delete;
102105
CardTable(CardTable &&) = delete;
@@ -187,6 +190,18 @@ class CardTable {
187190
/// is the first object.)
188191
GCCell *firstObjForCard(unsigned index) const;
189192

193+
/// Get the segment size from SHSegmentInfo. This is only used in debug code
194+
/// or when clearing the entire card table.
195+
size_t getSegmentSize() const {
196+
return (size_t)segmentInfo_.shiftedSegmentSize
197+
<< HERMESVM_LOG_HEAP_SEGMENT_SIZE;
198+
}
199+
200+
/// The end index of the card table (all valid indices should be smaller).
201+
size_t getEndIndex() const {
202+
return getSegmentSize() >> kLogCardSize;
203+
}
204+
190205
#ifdef HERMES_EXTRA_DEBUG
191206
/// Temporary debugging hack: yield the numeric value of the boundaries_ array
192207
/// for the given \p index.
@@ -218,9 +233,11 @@ class CardTable {
218233

219234
private:
220235
#ifndef NDEBUG
221-
/// Returns the pointer to the end of the storage containing \p ptr
222-
/// (exclusive).
223-
static void *storageEnd(const void *ptr);
236+
/// Returns the pointer to the end of the storage starting at \p lowLim.
237+
void *storageEnd(const void *lowLim) const {
238+
return reinterpret_cast<char *>(
239+
reinterpret_cast<uintptr_t>(lowLim) + getSegmentSize());
240+
}
224241
#endif
225242

226243
enum class CardStatus : char { Clean = 0, Dirty = 1 };
@@ -264,7 +281,7 @@ class CardTable {
264281
SHSegmentInfo segmentInfo_;
265282
/// This needs to be atomic so that the background thread in Hades can
266283
/// safely dirty cards when compacting.
267-
std::array<AtomicIfConcurrentGC<CardStatus>, kCardTableSize> cards_{};
284+
std::array<AtomicIfConcurrentGC<CardStatus>, kInlineCardTableSize> cards_{};
268285
};
269286

270287
/// See the comment at kHeapBytesPerCardByte above to see why this is
@@ -283,7 +300,7 @@ class CardTable {
283300
/// time: If we allocate a large object that crosses many cards, the first
284301
/// crossed cards gets a non-negative value, and each subsequent one uses the
285302
/// maximum exponent that stays within the card range for the object.
286-
int8_t boundaries_[kCardTableSize];
303+
int8_t boundaries_[kInlineCardTableSize];
287304
};
288305

289306
/// Implementations of inlines.
@@ -313,7 +330,7 @@ inline size_t CardTable::addressToIndex(const void *addr) const {
313330
}
314331

315332
inline const char *CardTable::indexToAddress(size_t index) const {
316-
assert(index <= kValidIndices && "index must be within the index range");
333+
assert(index <= getEndIndex() && "index must be within the index range");
317334
const char *res = base() + (index << kLogCardSize);
318335
assert(
319336
base() <= res && res <= storageEnd(base()) &&
@@ -331,7 +348,7 @@ inline bool CardTable::isCardForAddressDirty(const void *addr) const {
331348
}
332349

333350
inline bool CardTable::isCardForIndexDirty(size_t index) const {
334-
assert(index < kValidIndices && "index is required to be in range.");
351+
assert(index < getEndIndex() && "index is required to be in range.");
335352
return cards_[index].load(std::memory_order_relaxed) == CardStatus::Dirty;
336353
}
337354

include/hermes/VM/sh_segment_info.h

+4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
/// contain segment-specific information.
1313
typedef struct SHSegmentInfo {
1414
unsigned index;
15+
// Every segment is aligned to 1<<HERMESVM_LOG_HEAP_SEGMENT_SIZE, so we only
16+
// need to store the bits after a right shift. A two bytes integer is large
17+
// enough to hold a 4GB segment.
18+
unsigned short shiftedSegmentSize;
1519
} SHSegmentInfo;
1620

1721
#endif

lib/VM/gcs/CardTableNC.cpp

+6-12
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,6 @@
2020
namespace hermes {
2121
namespace vm {
2222

23-
#ifndef NDEBUG
24-
/* static */ void *CardTable::storageEnd(const void *ptr) {
25-
return AlignedHeapSegment::storageEnd(ptr);
26-
}
27-
#endif
28-
2923
void CardTable::dirtyCardsForAddressRange(const void *low, const void *high) {
3024
// If high is in the middle of some card, ensure that we dirty that card.
3125
high = reinterpret_cast<const char *>(high) + kCardSize - 1;
@@ -44,19 +38,19 @@ OptValue<size_t> CardTable::findNextCardWithStatus(
4438
}
4539

4640
void CardTable::clear() {
47-
cleanRange(kFirstUsedIndex, kValidIndices);
41+
cleanRange(kFirstUsedIndex, getEndIndex());
4842
}
4943

5044
void CardTable::updateAfterCompaction(const void *newLevel) {
5145
const char *newLevelPtr = static_cast<const char *>(newLevel);
5246
size_t firstCleanCardIndex = addressToIndex(newLevelPtr + kCardSize - 1);
5347
assert(
54-
firstCleanCardIndex <= kValidIndices &&
48+
firstCleanCardIndex <= getEndIndex() &&
5549
firstCleanCardIndex >= kFirstUsedIndex && "Invalid index.");
5650
// Dirty the occupied cards (below the level), and clean the cards above the
5751
// level.
5852
dirtyRange(kFirstUsedIndex, firstCleanCardIndex);
59-
cleanRange(firstCleanCardIndex, kValidIndices);
53+
cleanRange(firstCleanCardIndex, getEndIndex());
6054
}
6155

6256
void CardTable::cleanRange(size_t from, size_t to) {
@@ -147,20 +141,20 @@ protectBoundaryTableWork(void *table, size_t sz, oscompat::ProtectMode mode) {
147141

148142
void CardTable::protectBoundaryTable() {
149143
protectBoundaryTableWork(
150-
&boundaries_[0], kValidIndices, oscompat::ProtectMode::None);
144+
&boundaries_[0], getEndIndex(), oscompat::ProtectMode::None);
151145
}
152146

153147
void CardTable::unprotectBoundaryTable() {
154148
protectBoundaryTableWork(
155-
&boundaries_[0], kValidIndices, oscompat::ProtectMode::ReadWrite);
149+
&boundaries_[0], getEndIndex(), oscompat::ProtectMode::ReadWrite);
156150
}
157151
#endif // HERMES_EXTRA_DEBUG
158152

159153
#ifdef HERMES_SLOW_DEBUG
160154
void CardTable::verifyBoundaries(char *start, char *level) const {
161155
// Start should be card-aligned.
162156
assert(isCardAligned(start));
163-
for (unsigned index = addressToIndex(start); index < kValidIndices; index++) {
157+
for (unsigned index = addressToIndex(start); index < getEndIndex(); index++) {
164158
const char *boundary = indexToAddress(index);
165159
if (level <= boundary) {
166160
break;

unittests/VMRuntime/CardTableNCTest.cpp

+10-8
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ CardTableNCTest::CardTableNCTest() {
8080
TEST_F(CardTableNCTest, AddressToIndex) {
8181
// Expected indices in the card table corresponding to the probe
8282
// addresses into the storage.
83-
const size_t lastIx = CardTable::kValidIndices - 1;
83+
const size_t lastIx = table->getEndIndex() - 1;
8484
std::vector<size_t> indices{
8585
CardTable::kFirstUsedIndex,
8686
CardTable::kFirstUsedIndex + 1,
@@ -105,13 +105,13 @@ TEST_F(CardTableNCTest, AddressToIndexBoundary) {
105105
// the storage.
106106
ASSERT_EQ(seg.lowLim(), reinterpret_cast<char *>(table));
107107

108-
const size_t hiLim = CardTable::kValidIndices;
108+
const size_t hiLim = table->getEndIndex();
109109
EXPECT_EQ(0, table->addressToIndex(seg.lowLim()));
110110
EXPECT_EQ(hiLim, table->addressToIndex(seg.hiLim()));
111111
}
112112

113113
TEST_F(CardTableNCTest, DirtyAddress) {
114-
const size_t lastIx = CardTable::kValidIndices - 1;
114+
const size_t lastIx = table->getEndIndex() - 1;
115115

116116
for (char *addr : addrs) {
117117
size_t ind = table->addressToIndex(addr);
@@ -138,7 +138,8 @@ TEST_F(CardTableNCTest, DirtyAddress) {
138138
TEST_F(CardTableNCTest, DirtyAddressRangeEmpty) {
139139
char *addr = addrs.at(0);
140140
table->dirtyCardsForAddressRange(addr, addr);
141-
EXPECT_FALSE(table->findNextDirtyCard(0, CardTable::kValidIndices));
141+
EXPECT_FALSE(table->findNextDirtyCard(
142+
CardTable::kFirstUsedIndex, table->getEndIndex()));
142143
}
143144

144145
/// Dirty an address range smaller than a single card.
@@ -215,25 +216,26 @@ TEST_F(CardTableNCTest, NextDirtyCardImmediate) {
215216
size_t ind = table->addressToIndex(addr);
216217

217218
table->dirtyCardForAddress(addr);
218-
auto dirty = table->findNextDirtyCard(ind, CardTable::kValidIndices);
219+
auto dirty = table->findNextDirtyCard(ind, table->getEndIndex());
219220

220221
ASSERT_TRUE(dirty);
221222
EXPECT_EQ(ind, *dirty);
222223
}
223224

224225
TEST_F(CardTableNCTest, NextDirtyCard) {
225226
/// Empty case: No dirty cards
226-
EXPECT_FALSE(table->findNextDirtyCard(0, CardTable::kValidIndices));
227+
EXPECT_FALSE(table->findNextDirtyCard(
228+
CardTable::kFirstUsedIndex, table->getEndIndex()));
227229

228-
size_t from = 0;
230+
size_t from = CardTable::kFirstUsedIndex;
229231
for (char *addr : addrs) {
230232
table->dirtyCardForAddress(addr);
231233

232234
auto ind = table->addressToIndex(addr);
233235
EXPECT_FALSE(table->findNextDirtyCard(from, ind));
234236

235237
auto atEnd = table->findNextDirtyCard(from, ind + 1);
236-
auto inMiddle = table->findNextDirtyCard(from, CardTable::kValidIndices);
238+
auto inMiddle = table->findNextDirtyCard(from, table->getEndIndex());
237239

238240
ASSERT_TRUE(atEnd);
239241
EXPECT_EQ(ind, *atEnd);

0 commit comments

Comments
 (0)