Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Store segment size in SHSegmentInfo #1506

Open
wants to merge 4 commits into
base: static_h
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
424 changes: 226 additions & 198 deletions include/hermes/VM/AlignedHeapSegment.h

Large diffs are not rendered by default.

87 changes: 56 additions & 31 deletions include/hermes/VM/CardTableNC.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,9 @@ class CardTable {
static constexpr size_t kCardSize = 1 << kLogCardSize; // ==> 512-byte cards.
static constexpr size_t kSegmentSize = 1 << HERMESVM_LOG_HEAP_SEGMENT_SIZE;

/// The number of valid indices into the card table.
static constexpr size_t kValidIndices = kSegmentSize >> kLogCardSize;

/// The size of the card table.
static constexpr size_t kCardTableSize = kValidIndices;
/// The size of the maximum inline card table. CardStatus array and boundary
/// array for larger segment has larger size and is stored separately.
static constexpr size_t kInlineCardTableSize = kSegmentSize >> kLogCardSize;

/// For convenience, this is a conversion factor to determine how many bytes
/// in the heap correspond to a single byte in the card table. This is
Expand All @@ -77,23 +75,31 @@ class CardTable {
/// guaranteed by a static_assert below.
static constexpr size_t kHeapBytesPerCardByte = kCardSize;

/// A prefix of every segment is occupied by auxilary data
/// structures. The card table is the first such data structure.
/// The card table maps to the segment. Only the suffix of the card
/// table that maps to the suffix of entire segment that is used for
/// allocation is ever used; the prefix that maps to the card table
/// itself is not used. (Nor is the portion that of the card table
/// that maps to the other auxiliary data structure, the mark bit
/// array, but we don't attempt to calculate that here.)
/// It is useful to know the size of this unused region of
/// the card table, so it can be used for other purposes.
/// Note that the total size of the card table is 2 times
/// kCardTableSize, since the CardTable contains two byte arrays of
/// that size (cards_ and _boundaries_).
static constexpr size_t kFirstUsedIndex =
(2 * kCardTableSize) >> kLogCardSize;

CardTable() = default;
/// A prefix of every segment is occupied by auxiliary data structures. The
/// card table is the first such data structure. The card table maps to the
/// segment. Only the suffix of the card table that maps to the suffix of
/// entire segment that is used for allocation is ever used; the prefix that
/// maps to the card table itself is not used, nor is the portion of the card
/// table that maps to the other auxiliary data structure: the mark bit array
/// and guard pages. This small space can be used for other purpose, such as
/// storing the SHSegmentInfo (we assert in AlignedHeapSegment that its size
/// won't exceed this unused space). The actual first used index should take
/// into account all these structures. Here we only calculate for CardTable
/// and size of SHSegmentInfo. It's only used as starting index for
/// clearing/dirtying range of bits.
/// Note that the total size of the card table is 2 times kCardTableSize,
/// since the CardTable contains two byte arrays of that size (cards_ and
/// boundaries_). And this index must be larger than the size of SHSegmentInfo
/// to avoid corrupting it when clearing/dirtying bits.
static constexpr size_t kFirstUsedIndex = std::max(
sizeof(SHSegmentInfo),
(2 * kInlineCardTableSize) >> kLogCardSize);

CardTable() {
// Preserve the segment size.
segmentInfo_.shiftedSegmentSize =
kSegmentSize >> HERMESVM_LOG_HEAP_SEGMENT_SIZE;
}
/// CardTable is not copyable or movable: It must be constructed in-place.
CardTable(const CardTable &) = delete;
CardTable(CardTable &&) = delete;
Expand Down Expand Up @@ -184,6 +190,18 @@ class CardTable {
/// is the first object.)
GCCell *firstObjForCard(unsigned index) const;

/// Get the segment size from SHSegmentInfo. This is only used in debug code
/// or when clearing the entire card table.
size_t getSegmentSize() const {
return (size_t)segmentInfo_.shiftedSegmentSize
<< HERMESVM_LOG_HEAP_SEGMENT_SIZE;
}

/// The end index of the card table (all valid indices should be smaller).
size_t getEndIndex() const {
return getSegmentSize() >> kLogCardSize;
}

#ifdef HERMES_EXTRA_DEBUG
/// Temporary debugging hack: yield the numeric value of the boundaries_ array
/// for the given \p index.
Expand Down Expand Up @@ -215,9 +233,11 @@ class CardTable {

private:
#ifndef NDEBUG
/// Returns the pointer to the end of the storage containing \p ptr
/// (exclusive).
static void *storageEnd(const void *ptr);
/// Returns the pointer to the end of the storage starting at \p lowLim.
void *storageEnd(const void *lowLim) const {
return reinterpret_cast<char *>(
reinterpret_cast<uintptr_t>(lowLim) + getSegmentSize());
}
#endif

enum class CardStatus : char { Clean = 0, Dirty = 1 };
Expand Down Expand Up @@ -255,9 +275,14 @@ class CardTable {

void cleanOrDirtyRange(size_t from, size_t to, CardStatus cleanOrDirty);

/// This needs to be atomic so that the background thread in Hades can safely
/// dirty cards when compacting.
std::array<AtomicIfConcurrentGC<CardStatus>, kCardTableSize> cards_{};
union {
/// The bytes occupied by segmentInfo_ are guaranteed to be not override by
/// writes to cards_ array. See static assertions in AlignedHeapSegmentBase.
SHSegmentInfo segmentInfo_;
/// This needs to be atomic so that the background thread in Hades can
/// safely dirty cards when compacting.
std::array<AtomicIfConcurrentGC<CardStatus>, kInlineCardTableSize> cards_{};
};

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

/// Implementations of inlines.
Expand Down Expand Up @@ -305,7 +330,7 @@ inline size_t CardTable::addressToIndex(const void *addr) const {
}

inline const char *CardTable::indexToAddress(size_t index) const {
assert(index <= kValidIndices && "index must be within the index range");
assert(index <= getEndIndex() && "index must be within the index range");
const char *res = base() + (index << kLogCardSize);
assert(
base() <= res && res <= storageEnd(base()) &&
Expand All @@ -323,7 +348,7 @@ inline bool CardTable::isCardForAddressDirty(const void *addr) const {
}

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

Expand Down
9 changes: 5 additions & 4 deletions include/hermes/VM/HeapRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class HeapRuntime {
public:
~HeapRuntime() {
runtime_->~RT();
sp_->deleteStorage(runtime_);
sp_->deleteStorage(runtime_, kHeapRuntimeStorageSize);
}

/// Allocate a segment and create an aliased shared_ptr that points to the
Expand All @@ -36,16 +36,17 @@ class HeapRuntime {

private:
HeapRuntime(std::shared_ptr<StorageProvider> sp) : sp_{std::move(sp)} {
auto ptrOrError = sp_->newStorage("hermes-rt");
auto ptrOrError = sp_->newStorage("hermes-rt", kHeapRuntimeStorageSize);
if (!ptrOrError)
hermes_fatal("Cannot initialize Runtime storage.", ptrOrError.getError());
static_assert(
sizeof(RT) < AlignedHeapSegment::storageSize(), "Segments too small.");
static_assert(sizeof(RT) < kHeapRuntimeStorageSize, "Segments too small.");
runtime_ = static_cast<RT *>(*ptrOrError);
}

std::shared_ptr<StorageProvider> sp_;
RT *runtime_;
static constexpr size_t kHeapRuntimeStorageSize =
AlignedHeapSegment::storageSize();
};
} // namespace vm
} // namespace hermes
Expand Down
4 changes: 2 additions & 2 deletions include/hermes/VM/LimitedStorageProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ class LimitedStorageProvider final : public StorageProvider {
: delegate_(std::move(provider)), limit_(limit) {}

protected:
llvh::ErrorOr<void *> newStorageImpl(const char *name) override;
llvh::ErrorOr<void *> newStorageImpl(const char *name, size_t sz) override;

void deleteStorageImpl(void *storage) override;
void deleteStorageImpl(void *storage, size_t sz) override;
};

} // namespace vm
Expand Down
27 changes: 14 additions & 13 deletions include/hermes/VM/StorageProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,21 @@ class StorageProvider {

/// @}

/// Create a new segment memory space.
llvh::ErrorOr<void *> newStorage() {
return newStorage(nullptr);
/// Create a new segment memory space with given size \p sz.
llvh::ErrorOr<void *> newStorage(size_t sz) {
return newStorage(nullptr, sz);
}
/// Create a new segment memory space and give this memory the name \p name.
/// \return A pointer to a block of memory that has
/// AlignedHeapSegment::storageSize() bytes, and is aligned on
/// AlignedHeapSegment::storageSize().
llvh::ErrorOr<void *> newStorage(const char *name);
/// \return A pointer to a block of memory that has \p sz bytes, and is
/// aligned on AlignedHeapSegmentBase::kSegmentUnitSize. Note that \p sz must
/// be non-zero and equals to a multiple of
/// AlignedHeapSegmentBase::kSegmentUnitSize.
llvh::ErrorOr<void *> newStorage(const char *name, size_t sz);

/// Delete the given segment's memory space, and make it available for re-use.
/// \post Nothing in the range [storage, storage +
/// AlignedHeapSegment::storageSize()) is valid memory to be read or written.
void deleteStorage(void *storage);
/// Note that \p sz must be the same as used to allocating \p storage.
/// \post Nothing in the range [storage, storage + sz) is valid memory to be
/// read or written.
void deleteStorage(void *storage, size_t sz);

/// The number of storages this provider has allocated in its lifetime.
size_t numSucceededAllocs() const;
Expand All @@ -67,8 +68,8 @@ class StorageProvider {
size_t numLiveAllocs() const;

protected:
virtual llvh::ErrorOr<void *> newStorageImpl(const char *name) = 0;
virtual void deleteStorageImpl(void *storage) = 0;
virtual llvh::ErrorOr<void *> newStorageImpl(const char *name, size_t sz) = 0;
virtual void deleteStorageImpl(void *storage, size_t sz) = 0;

private:
size_t numSucceededAllocs_{0};
Expand Down
4 changes: 4 additions & 0 deletions include/hermes/VM/sh_segment_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
/// contain segment-specific information.
typedef struct SHSegmentInfo {
unsigned index;
// Every segment is aligned to 1<<HERMESVM_LOG_HEAP_SEGMENT_SIZE, so we only
// need to store the bits after a right shift. A two bytes integer is large
// enough to hold a 4GB segment.
unsigned short shiftedSegmentSize;
} SHSegmentInfo;

#endif
14 changes: 8 additions & 6 deletions lib/VM/LimitedStorageProvider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,22 @@
namespace hermes {
namespace vm {

llvh::ErrorOr<void *> LimitedStorageProvider::newStorageImpl(const char *name) {
llvh::ErrorOr<void *> LimitedStorageProvider::newStorageImpl(
const char *name,
size_t sz) {
if (limit_ < AlignedHeapSegment::storageSize()) {
return make_error_code(OOMError::TestVMLimitReached);
}
limit_ -= AlignedHeapSegment::storageSize();
return delegate_->newStorage(name);
limit_ -= sz;
return delegate_->newStorage(name, sz);
}

void LimitedStorageProvider::deleteStorageImpl(void *storage) {
void LimitedStorageProvider::deleteStorageImpl(void *storage, size_t sz) {
if (!storage) {
return;
}
delegate_->deleteStorage(storage);
limit_ += AlignedHeapSegment::storageSize();
delegate_->deleteStorage(storage, sz);
limit_ += sz;
}

} // namespace vm
Expand Down
Loading
Loading