Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Start port of memory manager to C++.

  • Loading branch information...
commit ae252f0633a2da1a3326228a8c85333f5b122849 1 parent dc2cec1
@nominolo authored
View
6 Makefile
@@ -138,12 +138,16 @@ vm/unittest.o: $(GTEST_A)
-e '/^$$/ d' -e 's/$$/ :/' < $(df).d >> $(df).P; \
rm -f $(df).d
-VM_SRCS = vm/thread.cc
+VM_SRCS = vm/thread.cc vm/capability.cc vm/memorymanager.cc
unittest: vm/unittest.o $(GTEST_A) $(VM_SRCS:.cc=.o)
@echo "LINK $^ => $@"
@$(CCC) -o $@ $^
+.PHONY: test
+test: unittest
+ @./unittest
+
utils/genopcodes: utils/genopcodes.o
@echo "LINK $^ => $@"
@$(CC) -o $@ $^
View
9 vm/capability.cc
@@ -0,0 +1,9 @@
+#include "capability.hh"
+
+using namespace lambdachine;
+
+Capability::Capability() : currentThread_(NULL) {
+}
+
+Capability::~Capability() {
+}
View
11 vm/capability.hh
@@ -2,8 +2,19 @@
#define _CAPABILITY_H_
#include "common.hh"
+#include "vm.hh"
+
+namespace lambdachine {
class Capability {
+public:
+ explicit Capability();
+ ~Capability();
+ inline Thread *currentThread() { return currentThread_; }
+
+private:
+ Thread *currentThread_;
+};
}
View
6 vm/config.hh
@@ -22,4 +22,10 @@
# endif
#endif
+#define DEBUG_MEMORY_MANAGER 0x00000001L
+#define DEBUG_LOADER 0x00000002L
+#define DEBUG_INTERPRETER 0x00000004L
+
+#define DEBUG_COMPONENTS 0xffffffffL
+
#endif
View
157 vm/memorymanager.cc
@@ -0,0 +1,157 @@
+#include "memorymanager.hh"
+#include "utils.hh"
+
+#include <sys/mman.h>
+#include <stdio.h>
+#include <errno.h>
+
+#define DLOG(...) \
+ if (DEBUG_COMPONENTS & DEBUG_MEMORY_MANAGER) { \
+ fprintf(stderr, "MM: " __VA_ARGS__); }
+
+using namespace lambdachine;
+
+#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
+# define MAP_ANONYMOUS MAP_ANON
+#endif
+
+#if LC_ARCH_BITS == 64
+char *const kMMapRegionStart = reinterpret_cast<char*>(0x100000000);
+char *const kMMapRegionEnd = reinterpret_cast<char*>(1UL << 40); // 1 Tbyte
+#elif LC_ARCH_BITS == 32
+char *const kMMapRegionStart = reinterpret_cast<char*>(0x10000);
+char *const kMMapRegionEnd = reinterpret_cast<char*>(1UL << 31); // 2 GB
+#else
+# error "Only 32 bit and 64 bit architectures supported."
+#endif
+
+static void outOfMemory() {
+ fprintf(stderr, "FATAL: Out of memory.\n");
+ fflush(stderr);
+ abort();
+}
+
+const int kMMapProtection = PROT_READ | PROT_WRITE;
+const int kMMapFlags = MAP_PRIVATE | MAP_ANONYMOUS;
+
+Region *Region::newRegion(RegionType regionType) {
+ // TODO: Grab a lock.
+ static char *alloc_hint = alignToRegionBoundary(kMMapRegionStart);
+ size_t size = kRegionSize;
+ char *ptr;
+
+ for (;;) {
+ ptr = static_cast<char*>(mmap(alloc_hint, size, kMMapProtection, kMMapFlags, -1, 0));
+ if (ptr != MAP_FAILED && isAlignedAtPowerOf2(kRegionSize, ptr)) {
+ // Success!
+ alloc_hint += size;
+ break;
+ }
+ if (ptr == MAP_FAILED) {
+ munmap(ptr, size);
+ if (alloc_hint >= kMMapRegionEnd) {
+ outOfMemory();
+ }
+ }
+ }
+
+ DLOG("Allocated region %p-%p\n", ptr, ptr + size);
+
+ Region *region = reinterpret_cast<Region*>(ptr);
+ region->region_info_ = regionType;
+ region->region_link_ = NULL;
+ region->initBlocks();
+
+ return region;
+}
+
+void Region::initBlocks() {
+ // Mark all blocks as free.
+ char *ptr = reinterpret_cast<char*>(this) + sizeof(Region);
+ for (Word i = 0; i < kBlocksPerRegion; i++) {
+ blocks_[i].flags_ = Block::kUninitialized;
+ blocks_[i].start_ = ptr;
+ blocks_[i].free_ = ptr;
+ ptr = alignToBlockBoundary(ptr + 1);
+ blocks_[i].end_ = ptr;
+ blocks_[i].link_ = &blocks_[i+1];
+ }
+ blocks_[kBlocksPerRegion - 1].link_ = NULL; // Overwrite last link
+ next_free_ = &blocks_[0];
+}
+
+static void Region::operator delete(void *p) {
+ // destructor has already been called. Nothing to be done here.
+}
+
+Region::~Region() {
+ char *ptr = reinterpret_cast<char*>(this);
+ DLOG("Freeing region %p-%p\n", ptr, ptr + kRegionSize);
+
+ munmap(ptr, kRegionSize);
+}
+
+Block *Region::grabFreeBlock() {
+ if (next_free_ == NULL) return NULL;
+
+ Block *b = next_free_;
+ next_free_ = b->link_;
+ b->link_ = NULL;
+
+ DLOG("Returning block %p-%p\n", b->start(), b->end());
+
+ return b;
+}
+
+MemoryManager::MemoryManager()
+ : full_(NULL), free_(NULL) {
+ region_ = Region::newRegion(Region::kSmallObjectRegion);
+ info_tables_ = grabFreeBlock(Block::kInfoTables);
+}
+
+MemoryManager::~MemoryManager() {
+ Region *r = region_;
+ while (r != NULL) {
+ Region *next = r->region_link_;
+ delete r;
+ r = next;
+ }
+}
+
+Block *MemoryManager::grabFreeBlock(Block::BlockFlags flags) {
+ // 1. Try to grab a block from the free block list (very likely).
+ Block *b = NULL;
+ if (free_ != NULL) {
+ b = free_;
+ free_ = b->link_;
+ b->link_ = NULL;
+ b->flags_ = static_cast<uint32_t>(flags);
+ return b;
+ }
+
+ // 2. Try to grab the free block from the most recently allocated
+ // region.
+ b = region_->grabFreeBlock();
+
+ while (b == NULL) {
+ // 3. If that failed, request more memory from the OS.
+ Region *r = Region::newRegion(Region::kSmallObjectRegion);
+ r->region_link_ = region_;
+ region_ = r;
+ b = r->grabFreeBlock();
+ }
+
+ b->flags_ = static_cast<uint32_t>(flags);
+ return b;
+}
+
+void *MemoryManager::allocInto(Block **block, size_t bytes) {
+ char *ptr = (*block)->alloc(bytes);
+ while (LC_UNLIKELY(ptr == NULL)) {
+ Block *b = grabFreeBlock((*block)->contents());
+ b->link_ = *block;
+ *block = b;
+ ptr = b->alloc(bytes);
+ }
+ return ptr;
+}
View
146 vm/memorymanager.hh
@@ -0,0 +1,146 @@
+#ifndef _MEMORYMANAGER_H_
+#define _MEMORYMANAGER_H_
+
+#include "common.hh"
+#include "utils.hh"
+
+namespace lambdachine {
+
+// Only one OS thread should allocate to each block.
+
+class Block
+{
+public:
+ // Start of data in this block.
+ inline char *start() { return start_; }
+
+ // First free byte in the block.
+ inline char *free() { return free_; }
+
+ // Points past last free byte.
+ inline char *end() { return end_; }
+
+ // Allocate a number of bytes in this block. Returns NULL
+ // if no room left.
+ inline char *alloc(size_t bytes) {
+ char *ptr = free_;
+ free_ += bytes;
+ if (LC_UNLIKELY(free_ > end_)) {
+ free_ = ptr;
+ return NULL;
+ }
+ return ptr;
+ }
+
+ static const int kBlockSizeLog2 = 15; // 32K
+ static const size_t kBlockSize = ((size_t)1) << kBlockSizeLog2;
+ static const Word kBlockMask = kBlockSize - 1;
+
+ typedef enum {
+ kUninitialized = 0,
+ kClosures = 1,
+ kInfoTables = 2,
+ kStaticClosures = 3,
+ kStrings = 4,
+ kBytecode = 5,
+ kContentsMask = 0xff,
+ kScavenged = 0x100,
+ kFull = 0x200,
+ } BlockFlags;
+
+ inline BlockFlags flags() {
+ return static_cast<BlockFlags>(flags_);
+ }
+
+ inline BlockFlags contents() {
+ return static_cast<BlockFlags>(flags_ & kContentsMask);
+ }
+
+private:
+ friend class Region;
+ friend class MemoryManager;
+ Block() {}; // Hidden
+ ~Block() {};
+ void operator delete(void *p) {}; // Forbid deleting Blocks
+
+ char *start_;
+ char *end_;
+ char *free_;
+ Block *link_;
+ uint32_t flags_;
+#if LC_ARCH_BITS == 64
+ uint32_t padding;
+#endif
+};
+
+
+class Region {
+public:
+ typedef enum {
+ kSmallObjectRegion = 1, // The region is subdivided into blocks.
+ // kLargeObjectRegion, // The region contains large objects.
+ } RegionType;
+
+ static const int kRegionSizeLog2 = 20; /* 1MB */
+ static const size_t kRegionSize = 1UL << kRegionSizeLog2;
+ static const Word kBlocksPerRegion = kRegionSize / Block::kBlockSize;
+
+ // Allocate a new memory region from the OS.
+ static Region *newRegion(RegionType);
+
+ static inline char* alignToRegionBoundary(char *ptr) {
+ Word w = reinterpret_cast<Word>(ptr);
+ return
+ reinterpret_cast<char*>(roundUpToPowerOf2(kRegionSizeLog2, w));
+ };
+
+ static inline char* alignToBlockBoundary(char *ptr) {
+ Word w = reinterpret_cast<Word>(ptr);
+ return
+ reinterpret_cast<char*>
+ (roundUpToPowerOf2(Block::kBlockSizeLog2, w));
+ };
+
+ // Unlink and return a free block from the region.
+ //
+ // Returns NULL if this region has no more free blocks.
+ Block *grabFreeBlock();
+
+ static void operator delete(void *p);
+ ~Region();
+
+private:
+ Region() {} // Hidden
+ void initBlocks();
+
+ Word region_info_;
+ Block blocks_[kBlocksPerRegion];
+ Region *region_link_;
+ Block *next_free_;
+
+ friend class MemoryManager;
+};
+
+class MemoryManager
+{
+ // void *allocInfoTable(Word nwords);
+public:
+ MemoryManager();
+ ~MemoryManager();
+
+ inline void *allocInfoTable(Word nwords) {
+ return allocInto(&info_tables_, nwords * sizeof(Word));
+ }
+private:
+ void *allocInto(Block **, size_t bytes);
+ Block *grabFreeBlock(Block::BlockFlags);
+
+ Region *region_;
+ Block *full_;
+ Block *free_;
+ Block *info_tables_;
+};
+
+}
+
+#endif /* _MEMORYMANAGER_H_ */
View
21 vm/unittest.cc
@@ -1,5 +1,6 @@
#include "gtest/gtest.h"
#include "thread.hh"
+#include "memorymanager.hh"
using namespace lambdachine;
@@ -10,6 +11,26 @@ TEST(ThreadTest, StartStop) {
delete T;
}
+TEST(MMTest, AllocRegion) {
+ Region *region = Region::newRegion(Region::kSmallObjectRegion);
+ ASSERT_TRUE(region != NULL);
+ delete region;
+}
+
+TEST(MMTest, AllocBasic) {
+ MemoryManager m;
+ void *p = m.allocInfoTable(10);
+ ASSERT_TRUE(p != NULL);
+}
+
+TEST(MMTest, AllocBasic2) {
+ MemoryManager m;
+ // Fill up at least one block.
+ for (size_t i = 0; i < (Block::kBlockSize / 10) + 10; i++) {
+ void *p = m.allocInfoTable(10);
+ ASSERT_TRUE(p != NULL);
+ }
+}
int main(int argc, char *argv[]) {
::testing::InitGoogleTest(&argc, argv);
View
39 vm/utils.hh
@@ -1,8 +1,45 @@
#ifndef _UTILS_H_
#define _UTILS_H_
-template <typename A> bool within(A low, A high, A value) {
+#include "common.hh"
+
+namespace lambdachine {
+
+template <typename A> inline bool within(A low, A high, A value) {
return low <= value && value <= high;
};
+inline Word clearLowBits(int num_bits, Word value) {
+ if (num_bits >= LC_ARCH_BITS)
+ return 0;
+ else
+ return value & ~((1UL << num_bits) - 1);
+}
+
+/**
+ * Checks that the pointer is value is a multiple of the given power
+ * of 2.
+ *
+ * In other words, checks that the lowest {@code power} bits of {@code
+ * ptr} are zero.
+ */
+inline bool isAlignedAtPowerOf2(int power, void *ptr) {
+ Word value = reinterpret_cast<Word>(ptr);
+ return (value & ((1UL << power) - 1)) == 0;
+}
+
+/**
+ * Rounds the given value up to the next multiple of the given power
+ * of 2.
+ *
+ * For example, roundUpToPowerOf2(0x7123, 12) == 0x8000 because 0x8000
+ * is the next multiple of 2^12 that is >= 0x7123.
+ */
+inline Word roundUpToPowerOf2(int power, Word value) {
+ return (value + (1UL << power) - 1) & ~((1UL << power) - 1);
+}
+
+
+}
+
#endif /* _UTILS_H_ */
Please sign in to comment.
Something went wrong with that request. Please try again.