aboutsummaryrefslogtreecommitdiff
path: root/src/heap.cpp
diff options
context:
space:
mode:
authorMichael Dusan <michael.dusan@gmail.com>2020-02-10 23:08:33 -0500
committerGitHub <noreply@github.com>2020-02-10 23:08:33 -0500
commite624c862894ec50998aafb3026d4ed45208acd6d (patch)
treea01d54c8d5ba3178eaed1fa8d0ef9c081d95d9f2 /src/heap.cpp
parent26183660558c43133d862912c602e316f43698c7 (diff)
parentedb210905dcbe666fa5222bceacd2e5bdb16bb89 (diff)
downloadzig-e624c862894ec50998aafb3026d4ed45208acd6d.tar.gz
zig-e624c862894ec50998aafb3026d4ed45208acd6d.zip
Merge pull request #4389 from mikdusan/stage1-mem
stage1: memory/report overhaul
Diffstat (limited to 'src/heap.cpp')
-rw-r--r--src/heap.cpp377
1 files changed, 377 insertions, 0 deletions
diff --git a/src/heap.cpp b/src/heap.cpp
new file mode 100644
index 0000000000..79c44d13dc
--- /dev/null
+++ b/src/heap.cpp
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 2020 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#include <new>
+#include <string.h>
+
+#include "config.h"
+#include "heap.hpp"
+#include "mem_profile.hpp"
+
+namespace heap {
+
+extern mem::Allocator &bootstrap_allocator;
+
+//
+// BootstrapAllocator implementation is identical to CAllocator minus
+// profile profile functionality. Splitting off to a base interface doesn't
+// seem worthwhile.
+//
+
+void BootstrapAllocator::init(const char *name) {}
+void BootstrapAllocator::deinit() {}
+
+void *BootstrapAllocator::internal_allocate(const mem::TypeInfo &info, size_t count) {
+ return mem::os::calloc(count, info.size);
+}
+
+void *BootstrapAllocator::internal_allocate_nonzero(const mem::TypeInfo &info, size_t count) {
+ return mem::os::malloc(count * info.size);
+}
+
+void *BootstrapAllocator::internal_reallocate(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) {
+ auto new_ptr = this->internal_reallocate_nonzero(info, old_ptr, old_count, new_count);
+ if (new_count > old_count)
+ memset(reinterpret_cast<uint8_t *>(new_ptr) + (old_count * info.size), 0, (new_count - old_count) * info.size);
+ return new_ptr;
+}
+
+void *BootstrapAllocator::internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) {
+ return mem::os::realloc(old_ptr, new_count * info.size);
+}
+
+void BootstrapAllocator::internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) {
+ mem::os::free(ptr);
+}
+
+void CAllocator::init(const char *name) {
+#ifdef ZIG_ENABLE_MEM_PROFILE
+ this->profile = bootstrap_allocator.create<mem::Profile>();
+ this->profile->init(name, "CAllocator");
+#endif
+}
+
+void CAllocator::deinit() {
+#ifdef ZIG_ENABLE_MEM_PROFILE
+ assert(this->profile);
+ this->profile->deinit();
+ bootstrap_allocator.destroy(this->profile);
+ this->profile = nullptr;
+#endif
+}
+
+CAllocator *CAllocator::construct(mem::Allocator *allocator, const char *name) {
+ auto p = new(allocator->create<CAllocator>()) CAllocator();
+ p->init(name);
+ return p;
+}
+
+void CAllocator::destruct(mem::Allocator *allocator) {
+ this->deinit();
+ allocator->destroy(this);
+}
+
+#ifdef ZIG_ENABLE_MEM_PROFILE
+void CAllocator::print_report(FILE *file) {
+ this->profile->print_report(file);
+}
+#endif
+
+void *CAllocator::internal_allocate(const mem::TypeInfo &info, size_t count) {
+#ifdef ZIG_ENABLE_MEM_PROFILE
+ this->profile->record_alloc(info, count);
+#endif
+ return mem::os::calloc(count, info.size);
+}
+
+void *CAllocator::internal_allocate_nonzero(const mem::TypeInfo &info, size_t count) {
+#ifdef ZIG_ENABLE_MEM_PROFILE
+ this->profile->record_alloc(info, count);
+#endif
+ return mem::os::malloc(count * info.size);
+}
+
+void *CAllocator::internal_reallocate(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) {
+ auto new_ptr = this->internal_reallocate_nonzero(info, old_ptr, old_count, new_count);
+ if (new_count > old_count)
+ memset(reinterpret_cast<uint8_t *>(new_ptr) + (old_count * info.size), 0, (new_count - old_count) * info.size);
+ return new_ptr;
+}
+
+void *CAllocator::internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) {
+#ifdef ZIG_ENABLE_MEM_PROFILE
+ this->profile->record_dealloc(info, old_count);
+ this->profile->record_alloc(info, new_count);
+#endif
+ return mem::os::realloc(old_ptr, new_count * info.size);
+}
+
+void CAllocator::internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) {
+#ifdef ZIG_ENABLE_MEM_PROFILE
+ this->profile->record_dealloc(info, count);
+#endif
+ mem::os::free(ptr);
+}
+
+struct ArenaAllocator::Impl {
+ Allocator *backing;
+
+ // regular allocations bump through a segment of static size
+ struct Segment {
+ static constexpr size_t size = 65536;
+ static constexpr size_t object_threshold = 4096;
+
+ uint8_t data[size];
+ };
+
+ // active segment
+ Segment *segment;
+ size_t segment_offset;
+
+ // keep track of segments
+ struct SegmentTrack {
+ static constexpr size_t size = (4096 - sizeof(SegmentTrack *)) / sizeof(Segment *);
+
+ // null if first
+ SegmentTrack *prev;
+ Segment *segments[size];
+ };
+ static_assert(sizeof(SegmentTrack) <= 4096, "unwanted struct padding");
+
+ // active segment track
+ SegmentTrack *segment_track;
+ size_t segment_track_remain;
+
+ // individual allocations punted to backing allocator
+ struct Object {
+ uint8_t *ptr;
+ size_t len;
+ };
+
+ // keep track of objects
+ struct ObjectTrack {
+ static constexpr size_t size = (4096 - sizeof(ObjectTrack *)) / sizeof(Object);
+
+ // null if first
+ ObjectTrack *prev;
+ Object objects[size];
+ };
+ static_assert(sizeof(ObjectTrack) <= 4096, "unwanted struct padding");
+
+ // active object track
+ ObjectTrack *object_track;
+ size_t object_track_remain;
+
+ ATTRIBUTE_RETURNS_NOALIAS inline void *allocate(const mem::TypeInfo& info, size_t count);
+ inline void *reallocate(const mem::TypeInfo& info, void *old_ptr, size_t old_count, size_t new_count);
+
+ inline void new_segment();
+ inline void track_segment();
+ inline void track_object(Object object);
+};
+
+void *ArenaAllocator::Impl::allocate(const mem::TypeInfo& info, size_t count) {
+#ifndef NDEBUG
+ // make behavior when size == 0 portable
+ if (info.size == 0 || count == 0)
+ return nullptr;
+#endif
+ const size_t nbytes = info.size * count;
+ this->segment_offset = (this->segment_offset + (info.alignment - 1)) & ~(info.alignment - 1);
+ if (nbytes >= Segment::object_threshold) {
+ auto ptr = this->backing->allocate<uint8_t>(nbytes);
+ this->track_object({ptr, nbytes});
+ return ptr;
+ }
+ if (this->segment_offset + nbytes > Segment::size)
+ this->new_segment();
+ auto ptr = &this->segment->data[this->segment_offset];
+ this->segment_offset += nbytes;
+ return ptr;
+}
+
+void *ArenaAllocator::Impl::reallocate(const mem::TypeInfo& info, void *old_ptr, size_t old_count, size_t new_count) {
+#ifndef NDEBUG
+ // make behavior when size == 0 portable
+ if (info.size == 0 && old_ptr == nullptr)
+ return nullptr;
+#endif
+ const size_t new_nbytes = info.size * new_count;
+ if (new_nbytes <= info.size * old_count)
+ return old_ptr;
+ const size_t old_nbytes = info.size * old_count;
+ this->segment_offset = (this->segment_offset + (info.alignment - 1)) & ~(info.alignment - 1);
+ if (new_nbytes >= Segment::object_threshold) {
+ auto new_ptr = this->backing->allocate<uint8_t>(new_nbytes);
+ this->track_object({new_ptr, new_nbytes});
+ memcpy(new_ptr, old_ptr, old_nbytes);
+ return new_ptr;
+ }
+ if (this->segment_offset + new_nbytes > Segment::size)
+ this->new_segment();
+ auto new_ptr = &this->segment->data[this->segment_offset];
+ this->segment_offset += new_nbytes;
+ memcpy(new_ptr, old_ptr, old_nbytes);
+ return new_ptr;
+}
+
+void ArenaAllocator::Impl::new_segment() {
+ this->segment = this->backing->create<Segment>();
+ this->segment_offset = 0;
+ this->track_segment();
+}
+
+void ArenaAllocator::Impl::track_segment() {
+ assert(this->segment != nullptr);
+ if (this->segment_track_remain < 1) {
+ auto prev = this->segment_track;
+ this->segment_track = this->backing->create<SegmentTrack>();
+ this->segment_track->prev = prev;
+ this->segment_track_remain = SegmentTrack::size;
+ }
+ this->segment_track_remain -= 1;
+ this->segment_track->segments[this->segment_track_remain] = this->segment;
+}
+
+void ArenaAllocator::Impl::track_object(Object object) {
+ if (this->object_track_remain < 1) {
+ auto prev = this->object_track;
+ this->object_track = this->backing->create<ObjectTrack>();
+ this->object_track->prev = prev;
+ this->object_track_remain = ObjectTrack::size;
+ }
+ this->object_track_remain -= 1;
+ this->object_track->objects[this->object_track_remain] = object;
+}
+
+void ArenaAllocator::init(Allocator *backing, const char *name) {
+#ifdef ZIG_ENABLE_MEM_PROFILE
+ this->profile = bootstrap_allocator.create<mem::Profile>();
+ this->profile->init(name, "ArenaAllocator");
+#endif
+ this->impl = bootstrap_allocator.create<Impl>();
+ {
+ auto &r = *this->impl;
+ r.backing = backing;
+ r.segment_offset = Impl::Segment::size;
+ }
+}
+
+void ArenaAllocator::deinit() {
+ auto &backing = *this->impl->backing;
+
+ // segments
+ if (this->impl->segment_track) {
+ // active track is not full and bounded by track_remain
+ auto prev = this->impl->segment_track->prev;
+ {
+ auto t = this->impl->segment_track;
+ for (size_t i = this->impl->segment_track_remain; i < Impl::SegmentTrack::size; ++i)
+ backing.destroy(t->segments[i]);
+ backing.destroy(t);
+ }
+
+ // previous tracks are full
+ for (auto t = prev; t != nullptr;) {
+ for (size_t i = 0; i < Impl::SegmentTrack::size; ++i)
+ backing.destroy(t->segments[i]);
+ prev = t->prev;
+ backing.destroy(t);
+ t = prev;
+ }
+ }
+
+ // objects
+ if (this->impl->object_track) {
+ // active track is not full and bounded by track_remain
+ auto prev = this->impl->object_track->prev;
+ {
+ auto t = this->impl->object_track;
+ for (size_t i = this->impl->object_track_remain; i < Impl::ObjectTrack::size; ++i) {
+ auto &obj = t->objects[i];
+ backing.deallocate(obj.ptr, obj.len);
+ }
+ backing.destroy(t);
+ }
+
+ // previous tracks are full
+ for (auto t = prev; t != nullptr;) {
+ for (size_t i = 0; i < Impl::ObjectTrack::size; ++i) {
+ auto &obj = t->objects[i];
+ backing.deallocate(obj.ptr, obj.len);
+ }
+ prev = t->prev;
+ backing.destroy(t);
+ t = prev;
+ }
+ }
+
+#ifdef ZIG_ENABLE_MEM_PROFILE
+ assert(this->profile);
+ this->profile->deinit();
+ bootstrap_allocator.destroy(this->profile);
+ this->profile = nullptr;
+#endif
+}
+
+ArenaAllocator *ArenaAllocator::construct(mem::Allocator *allocator, mem::Allocator *backing, const char *name) {
+ auto p = new(allocator->create<ArenaAllocator>()) ArenaAllocator;
+ p->init(backing, name);
+ return p;
+}
+
+void ArenaAllocator::destruct(mem::Allocator *allocator) {
+ this->deinit();
+ allocator->destroy(this);
+}
+
+#ifdef ZIG_ENABLE_MEM_PROFILE
+void ArenaAllocator::print_report(FILE *file) {
+ this->profile->print_report(file);
+}
+#endif
+
+void *ArenaAllocator::internal_allocate(const mem::TypeInfo &info, size_t count) {
+#ifdef ZIG_ENABLE_MEM_PROFILE
+ this->profile->record_alloc(info, count);
+#endif
+ return this->impl->allocate(info, count);
+}
+
+void *ArenaAllocator::internal_allocate_nonzero(const mem::TypeInfo &info, size_t count) {
+#ifdef ZIG_ENABLE_MEM_PROFILE
+ this->profile->record_alloc(info, count);
+#endif
+ return this->impl->allocate(info, count);
+}
+
+void *ArenaAllocator::internal_reallocate(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) {
+ return this->internal_reallocate_nonzero(info, old_ptr, old_count, new_count);
+}
+
+void *ArenaAllocator::internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) {
+#ifdef ZIG_ENABLE_MEM_PROFILE
+ this->profile->record_dealloc(info, old_count);
+ this->profile->record_alloc(info, new_count);
+#endif
+ return this->impl->reallocate(info, old_ptr, old_count, new_count);
+}
+
+void ArenaAllocator::internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) {
+#ifdef ZIG_ENABLE_MEM_PROFILE
+ this->profile->record_dealloc(info, count);
+#endif
+ // noop
+}
+
+BootstrapAllocator bootstrap_allocator_state;
+mem::Allocator &bootstrap_allocator = bootstrap_allocator_state;
+
+CAllocator c_allocator_state;
+mem::Allocator &c_allocator = c_allocator_state;
+
+} // namespace heap