aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDaniel Schemmel <daniel@schemmel.net>2023-05-19 22:56:55 +0000
committerCristian Cadar <c.cadar@imperial.ac.uk>2023-05-26 21:01:54 +0100
commit8600ab9be42443487145b6476e0d6be49d055bd2 (patch)
tree29dd79291c03e5bdc7c7a00d79ef92ce30a70c45
parent2833e66c38a951c6663bf8644c0942f339b622c4 (diff)
downloadklee-8600ab9be42443487145b6476e0d6be49d055bd2.tar.gz
Improve LOH deallocation scheme
-rw-r--r--include/klee/KDAlloc/allocator.h2
-rw-r--r--include/klee/KDAlloc/suballocators/loh.h58
-rw-r--r--include/klee/KDAlloc/suballocators/sized_regions.h159
3 files changed, 60 insertions, 159 deletions
diff --git a/include/klee/KDAlloc/allocator.h b/include/klee/KDAlloc/allocator.h
index 45d90536..1e0d305a 100644
--- a/include/klee/KDAlloc/allocator.h
+++ b/include/klee/KDAlloc/allocator.h
@@ -153,7 +153,7 @@ public:
if (bin < static_cast<int>(sizedBins.size())) {
return sizedBins[bin].deallocate(control->sizedBins[bin], ptr);
} else {
- return largeObjectBin.deallocate(control->largeObjectBin, ptr, size);
+ return largeObjectBin.deallocate(control->largeObjectBin, ptr);
}
}
diff --git a/include/klee/KDAlloc/suballocators/loh.h b/include/klee/KDAlloc/suballocators/loh.h
index d52306fc..16baf911 100644
--- a/include/klee/KDAlloc/suballocators/loh.h
+++ b/include/klee/KDAlloc/suballocators/loh.h
@@ -43,17 +43,17 @@ class LargeObjectAllocator final : public TaggedLogger<LargeObjectAllocator> {
/// over sizes
SizedRegions regions;
- static_assert(
- sizeof(void *) >= sizeof(std::size_t),
- "The quarantine elements are alternating `void*` and `std::size_t`");
- static_assert(
- alignof(void *) >= alignof(std::size_t),
- "The quarantine elements are alternating `void*` and `std::size_t`");
+ static_assert(sizeof(void *) >= sizeof(std::size_t),
+ "The quarantine structure contains a `std::size_t` followed "
+ "by many `void*`s");
+ static_assert(alignof(void *) >= alignof(std::size_t),
+ "The quarantine structure contains a `std::size_t` followed "
+ "by many `void*`s");
using QuarantineElement =
std::aligned_storage_t<sizeof(void *), alignof(void *)>;
/// The quarantine position followed by the quarantine ring buffer
- /// Structure: [pos, ptr1, size1, ptr2, size2, ...]
+ /// Structure: [pos, ptr1, ptr2, ...]
QuarantineElement quarantine[];
Data() = default;
@@ -90,7 +90,7 @@ public:
this->quarantineSize = 0;
this->unlimitedQuarantine = true;
} else {
- this->quarantineSize = quarantineSize == 0 ? 0 : quarantineSize * 2 + 1;
+ this->quarantineSize = quarantineSize == 0 ? 0 : quarantineSize + 1;
this->unlimitedQuarantine = false;
}
@@ -140,32 +140,27 @@ private:
assert(data->referenceCount == 1);
}
- std::pair<void *, std::size_t>
- quarantine(Control const &control, void *const ptr, std::size_t const size) {
+ void *quarantine(Control const &control, void *const ptr) {
assert(!!data &&
"Deallocations can only happen if allocations happened beforehand");
assert(!control.unlimitedQuarantine);
if (control.quarantineSize == 0) {
- return {ptr, size};
+ return ptr;
}
assert(data->referenceCount == 1 &&
"Must hold CoW ownership to quarantine a new pointer+size pair");
auto const pos = reinterpret_cast<std::size_t &>(data->quarantine[0]);
- if (pos + 2 == control.quarantineSize) {
+ if (pos + 1 == control.quarantineSize) {
reinterpret_cast<std::size_t &>(data->quarantine[0]) = 1;
} else {
- reinterpret_cast<std::size_t &>(data->quarantine[0]) = pos + 2;
+ reinterpret_cast<std::size_t &>(data->quarantine[0]) = pos + 1;
}
- auto quarantinedPtr = std::exchange(
- reinterpret_cast<void *&>(data->quarantine[pos]), std::move(ptr));
- auto quarantinedSize = std::exchange(
- reinterpret_cast<std::size_t &>(data->quarantine[pos + 1]),
- std::move(size));
- return {quarantinedPtr, quarantinedSize};
+ return std::exchange(reinterpret_cast<void *&>(data->quarantine[pos]),
+ std::move(ptr));
}
public:
@@ -293,39 +288,30 @@ public:
return result;
}
- void deallocate(Control const &control, void *ptr, std::size_t size) {
+ void deallocate(Control const &control, void *ptr) {
assert(!!data &&
"Deallocations can only happen if allocations happened beforehand");
if (control.unlimitedQuarantine) {
traceLine("Quarantining ", ptr, " for ever");
} else {
- auto quantizedSize = roundUpToMultipleOf4096(size);
- traceLine("Quarantining ", ptr, " with size ", size, " (", quantizedSize,
- ") for ", (control.quarantineSize - 1) / 2, " deallocations");
- assert(size > 0);
- assert(quantizedSize % 4096 == 0);
+ traceLine("Quarantining ", ptr, " for ", control.quarantineSize - 1,
+ " deallocations");
acquireData(control); // we will need quarantine and/or region ownership
- std::tie(ptr, size) = quarantine(control, ptr, size);
+ ptr = quarantine(control, ptr);
if (!ptr) {
return;
}
- quantizedSize = roundUpToMultipleOf4096(size);
- traceLine("Deallocating ", ptr, " with size ", size, " (", quantizedSize,
- ")");
+ traceLine("Deallocating ", ptr);
assert(data->referenceCount == 1);
- assert(size > 0);
- assert(quantizedSize % 4096 == 0);
- size = quantizedSize;
traceContents(control);
- traceLine("Merging region at ",
- static_cast<void *>(static_cast<char *>(ptr) + size),
- " with the preceding region");
+ traceLine("Merging regions around ",
+ static_cast<void *>(static_cast<char *>(ptr)));
- data->regions.mergeRegionWithPrevious(static_cast<char *>(ptr) + size);
+ data->regions.mergeAroundAddress(static_cast<char *>(ptr));
}
}
diff --git a/include/klee/KDAlloc/suballocators/sized_regions.h b/include/klee/KDAlloc/suballocators/sized_regions.h
index 7236a619..2fb12bf5 100644
--- a/include/klee/KDAlloc/suballocators/sized_regions.h
+++ b/include/klee/KDAlloc/suballocators/sized_regions.h
@@ -412,136 +412,51 @@ public:
}
}
-private:
- static CoWPtr<Node> mergeRegionWithPreviousRec(CoWPtr<Node> &treap,
- char *const baseAddress) {
- assert(treap && "cannot extract region that is not part of the treap");
- if (baseAddress < treap->getBaseAddress()) {
- auto &node = treap.acquire();
- if (auto greater = mergeRegionWithPreviousRec(node.lhs, baseAddress)) {
- if (node.getBaseAddress() < greater->getBaseAddress()) {
- auto newSize = static_cast<std::size_t>(greater->getBaseAddress() -
- node.getBaseAddress() +
- greater->getSize());
- assert(
- newSize > node.getSize() &&
- "Sizes only get greater by adding `greater - smaller` to them");
- node.setSize(newSize);
- return {};
+ /// This function merges the region after the given address, with the region
+ /// immediately preceding it. There must be a region before and a region after
+ /// `address`.
+ void mergeAroundAddress(char const *const address) noexcept {
+ assert(root && "An empty treap holds no regions to merge");
+
+ CoWPtr<Node> *currentNode = &root;
+ CoWPtr<Node> *closestPredecessor = nullptr;
+ CoWPtr<Node> *closestSuccessor = nullptr;
+ for (;;) {
+ if (address < (*currentNode)->getBaseAddress()) {
+ assert(!closestSuccessor ||
+ (*currentNode)->getBaseAddress() <
+ (*closestSuccessor)->getBaseAddress());
+ closestSuccessor = currentNode;
+ if ((*currentNode)->lhs) {
+ currentNode = &currentNode->acquire().lhs;
} else {
- return greater;
+ break;
}
} else {
- if (node.lhs->getSize() > node.getSize() ||
- (node.lhs->getSize() == node.getSize() &&
- node.lhs->hash() > node.hash())) {
- auto temp = std::move(node.lhs);
- auto &nodeTemp = temp.getOwned();
- node.lhs = std::move(nodeTemp.rhs);
- nodeTemp.rhs = std::move(treap);
- treap = std::move(temp);
- }
- return {};
- }
- } else if (treap->getBaseAddress() < baseAddress) {
- auto &node = treap.acquire();
- if (auto greater = mergeRegionWithPreviousRec(node.rhs, baseAddress)) {
- if (node.getBaseAddress() < greater->getBaseAddress()) {
- auto newSize = static_cast<std::size_t>(greater->getBaseAddress() -
- node.getBaseAddress() +
- greater->getSize());
- assert(
- newSize > node.getSize() &&
- "Sizes only get greater by adding `greater - smaller` to them");
- node.setSize(newSize);
- return {};
+ assert(!closestPredecessor ||
+ (*currentNode)->getBaseAddress() >
+ (*closestPredecessor)->getBaseAddress());
+ closestPredecessor = currentNode;
+ if ((*currentNode)->rhs) {
+ currentNode = &currentNode->acquire().rhs;
} else {
- return greater;
- }
- } else {
- if (node.rhs->getSize() > node.getSize() ||
- (node.rhs->getSize() == node.getSize() &&
- node.rhs->hash() > node.hash())) {
- auto temp = std::move(node.rhs);
- auto &nodeTemp = temp.getOwned();
- node.rhs = std::move(nodeTemp.lhs);
- nodeTemp.lhs = std::move(treap);
- treap = std::move(temp);
+ break;
}
- return greater;
- }
- } else {
- assert(treap->getBaseAddress() == baseAddress);
- // target is now the greater (w.r.t. the tree key) of the two regions we
- // are looking for
- if (treap->lhs) {
- CoWPtr<Node> lhs, rhs;
- if (treap.isOwned()) {
- auto &node = treap.acquire();
- lhs = std::move(node.lhs);
- rhs = std::move(node.rhs);
- } else {
- lhs = treap->lhs;
- rhs = treap->rhs;
- }
- auto const greaterBaseAddress = treap->getBaseAddress();
- auto const greaterSize = treap->getSize();
-
- auto *target = &lhs;
- while ((*target)->rhs) {
- target = &target->acquire().rhs;
- }
- treap = std::move(*target);
- auto &node = treap.acquire();
- *target = std::move(node.lhs);
-
- assert(greaterBaseAddress > node.getBaseAddress());
- auto newSize = static_cast<std::size_t>(
- greaterBaseAddress - node.getBaseAddress() + greaterSize);
- assert(newSize > node.getSize() &&
- "Sizes only get greater by adding `greater - smaller` to them");
- node.setSize(newSize);
-
- assert(!node.rhs && "We looked for a node that has no rhs");
- assert((!rhs || node.getBaseAddress() < rhs->getBaseAddress()) &&
- "`lesser` comes from the left subtree of `greater`, while "
- "rhs is the right subtree of `greater`");
- assert((!rhs || node.getSize() > rhs->getSize()) &&
- "The old size of `greater` was >= that of its right subtree, "
- "so the new size (of `lesser`) is strictly greater");
- node.rhs = std::move(rhs);
-
- assert(!node.lhs && "The lhs of this node has already been removed");
- assert((!lhs || lhs->getBaseAddress() < node.getBaseAddress()) &&
- "`lesser` is the greatest child of the `lhs` subtree");
- assert((!lhs || node.getSize() > lhs->getSize()) &&
- "The old size of `greater` was >= that of its left subtree, "
- "so the new size (of `lesser`) is strictly greater");
- node.lhs = std::move(lhs);
- return {};
- } else {
- auto greater = std::move(treap);
- if (greater->rhs) {
- if (greater.isOwned()) {
- treap = std::move(greater.acquire().rhs);
- } else {
- treap = greater->rhs;
- }
- }
- return greater; // no move to enable copy elision
}
}
- }
-public:
- /// This function merges the region starting at the given address, with the
- /// region immediately preceding it. Both regions must exist. The resulting
- /// region has the same base address as the lesser region with its size large
- /// enough to cover the space to the end of the greater region (filling any
- /// holes in the process).
- inline void mergeRegionWithPrevious(char *const baseAddress) {
- assert(root && "An empty treap holds no regions to merge");
- mergeRegionWithPreviousRec(root, baseAddress);
+ assert(closestPredecessor && closestSuccessor &&
+ "address must be in between two regions");
+
+ closestPredecessor->acquire().setSize(
+ (*closestSuccessor)->getBaseAddress() + (*closestSuccessor)->getSize() -
+ (*closestPredecessor)->getBaseAddress());
+ CoWPtr<Node> lhs = (*closestSuccessor)->lhs;
+ CoWPtr<Node> rhs = (*closestSuccessor)->rhs;
+ closestSuccessor->release();
+ if (lhs || rhs) {
+ mergeTreaps(closestSuccessor, lhs, rhs);
+ }
}
private: