Skip to content

Commit 0820b2b

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 instead of 0, since the value of `SHSegmentInfo` may write some bytes to non-zero. Differential Revision: D61807366
1 parent ba128e3 commit 0820b2b

File tree

5 files changed

+63
-41
lines changed

5 files changed

+63
-41
lines changed

include/hermes/VM/AlignedHeapSegment.h

+16-6
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,19 @@ 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 segmentInfo->segmentSize;
171+
}
172+
173+
/// Returns the address that is the upper bound of the segment.
174+
/// This is only used in debugging code and computing memory footprint, so
175+
/// just read the segment size from SHSegmentInfo.
176+
char *hiLim() const {
177+
return lowLim_ + storageSize();
178+
}
179+
167180
/// Returns the address at which the first allocation in this segment would
168181
/// occur.
169182
/// Disable UB sanitization because 'this' may be null during the tests.
@@ -274,7 +287,9 @@ class AlignedHeapSegment : public AlignedHeapSegmentBase {
274287
/// Mask for isolating the storage being pointed into by a pointer.
275288
static constexpr size_t kHighMask{~kLowMask};
276289

277-
/// Returns the storage size, in bytes, of an \c AlignedHeapSegment.
290+
/// Returns the storage size, in bytes, of an \c AlignedHeapSegment. This
291+
/// replaces AlignedHeapSegmentBase::storageSize, which reads the size from
292+
/// SHSegmentInfo.
278293
static constexpr size_t storageSize() {
279294
return kSize;
280295
}
@@ -380,11 +395,6 @@ class AlignedHeapSegment : public AlignedHeapSegmentBase {
380395
/// The number of bytes in the segment that are available for allocation.
381396
inline size_t available() const;
382397

383-
/// Returns the address that is the upper bound of the segment.
384-
char *hiLim() const {
385-
return lowLim() + storageSize();
386-
}
387-
388398
/// Returns the first address after the region in which allocations can occur,
389399
/// taking external memory credits into a account (they decrease the effective
390400
/// end).

include/hermes/VM/CardTableNC.h

+28-15
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 storage 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
@@ -91,10 +89,14 @@ class CardTable {
9189
/// Note that the total size of the card table is 2 times kCardTableSize,
9290
/// since the CardTable contains two byte arrays of that size (cards_ and
9391
/// boundaries_).
94-
static constexpr size_t kFirstUsedIndex =
95-
std::max(sizeof(SHSegmentInfo), (2 * kCardTableSize) >> kLogCardSize);
92+
static constexpr size_t kFirstUsedIndex = std::max(
93+
sizeof(SHSegmentInfo),
94+
(2 * kInlineCardTableSize) >> kLogCardSize);
9695

97-
CardTable() = default;
96+
CardTable() {
97+
// Preserve the segment size.
98+
segmentInfo_.segmentSize = kSegmentSize;
99+
}
98100
/// CardTable is not copyable or movable: It must be constructed in-place.
99101
CardTable(const CardTable &) = delete;
100102
CardTable(CardTable &&) = delete;
@@ -185,6 +187,11 @@ class CardTable {
185187
/// is the first object.)
186188
GCCell *firstObjForCard(unsigned index) const;
187189

190+
/// The end index of the card table (all valid indices should be smaller).
191+
size_t getEndIndex() const {
192+
return getSegmentSize() >> kLogCardSize;
193+
}
194+
188195
#ifdef HERMES_EXTRA_DEBUG
189196
/// Temporary debugging hack: yield the numeric value of the boundaries_ array
190197
/// for the given \p index.
@@ -215,10 +222,16 @@ class CardTable {
215222
#endif // HERMES_SLOW_DEBUG
216223

217224
private:
225+
unsigned getSegmentSize() const {
226+
return segmentInfo_.segmentSize;
227+
}
228+
218229
#ifndef NDEBUG
219-
/// Returns the pointer to the end of the storage containing \p ptr
220-
/// (exclusive).
221-
static void *storageEnd(const void *ptr);
230+
/// Returns the pointer to the end of the storage starting at \p lowLim.
231+
void *storageEnd(const void *lowLim) const {
232+
return reinterpret_cast<char *>(
233+
reinterpret_cast<uintptr_t>(lowLim) + getSegmentSize());
234+
}
222235
#endif
223236

224237
enum class CardStatus : char { Clean = 0, Dirty = 1 };
@@ -262,7 +275,7 @@ class CardTable {
262275
SHSegmentInfo segmentInfo_;
263276
/// This needs to be atomic so that the background thread in Hades can
264277
/// safely dirty cards when compacting.
265-
std::array<AtomicIfConcurrentGC<CardStatus>, kCardTableSize> cards_{};
278+
std::array<AtomicIfConcurrentGC<CardStatus>, kInlineCardTableSize> cards_{};
266279
};
267280

268281
/// See the comment at kHeapBytesPerCardByte above to see why this is
@@ -281,7 +294,7 @@ class CardTable {
281294
/// time: If we allocate a large object that crosses many cards, the first
282295
/// crossed cards gets a non-negative value, and each subsequent one uses the
283296
/// maximum exponent that stays within the card range for the object.
284-
int8_t boundaries_[kCardTableSize];
297+
int8_t boundaries_[kInlineCardTableSize];
285298
};
286299

287300
/// Implementations of inlines.
@@ -311,7 +324,7 @@ inline size_t CardTable::addressToIndex(const void *addr) const {
311324
}
312325

313326
inline const char *CardTable::indexToAddress(size_t index) const {
314-
assert(index <= kValidIndices && "index must be within the index range");
327+
assert(index <= getEndIndex() && "index must be within the index range");
315328
const char *res = base() + (index << kLogCardSize);
316329
assert(
317330
base() <= res && res <= storageEnd(base()) &&
@@ -329,7 +342,7 @@ inline bool CardTable::isCardForAddressDirty(const void *addr) const {
329342
}
330343

331344
inline bool CardTable::isCardForIndexDirty(size_t index) const {
332-
assert(index < kValidIndices && "index is required to be in range.");
345+
assert(index < getEndIndex() && "index is required to be in range.");
333346
return cards_[index].load(std::memory_order_relaxed) == CardStatus::Dirty;
334347
}
335348

include/hermes/VM/sh_segment_info.h

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
/// contain segment-specific information.
1313
typedef struct SHSegmentInfo {
1414
unsigned index;
15+
/// The storage size for this segment. We practically don't support segment
16+
/// with size larger than UINT32_MAX.
17+
unsigned segmentSize;
1518
} SHSegmentInfo;
1619

1720
#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)