forked from xuos/xiuos
370 lines
10 KiB
C
370 lines
10 KiB
C
/*
|
|
* mm.c - malloc using segregated list
|
|
* KAIST
|
|
* Tony Kim
|
|
*
|
|
* In this approach,
|
|
* Every block has a header and a footer
|
|
* in which header contains reallocation information, size, and allocation info
|
|
* and footer contains size and allocation info.
|
|
* Free list are tagged to the segregated list.
|
|
* Therefore all free block contains pointer to the predecessor and successor.
|
|
* The segregated list headers are organized by 2^k size.
|
|
*
|
|
*/
|
|
|
|
#include "libmem.h"
|
|
#include "usyscall.h"
|
|
|
|
/* single word (4) or double word (8) alignment */
|
|
#define ALIGNMENT 8
|
|
/* rounds up to the nearest multiple of ALIGNMENT */
|
|
#define ALIGN(size) (((size) + (ALIGNMENT - 1)) & ~0x7)
|
|
|
|
// My additional Macros
|
|
#define WSIZE 4 // word and header/footer size (bytes)
|
|
#define DSIZE 8 // double word size (bytes)
|
|
#define INITCHUNKSIZE (1 << 6)
|
|
#define CHUNKSIZE (1 << 12) //+(1<<7)
|
|
|
|
#define LISTLIMIT 20
|
|
|
|
#define MAX(x, y) ((x) > (y) ? (x) : (y))
|
|
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
|
|
|
// Pack a size and allocated bit into a word
|
|
#define PACK(size, alloc) ((size) | (alloc))
|
|
|
|
// Read and write a word at address p
|
|
#define GET(p) (*(unsigned int*)(p))
|
|
#define PUT(p, val) (*(unsigned int*)(p) = (val) | GET_TAG(p))
|
|
#define PUT_NOTAG(p, val) (*(unsigned int*)(p) = (val))
|
|
|
|
// Store predecessor or successor pointer for free blocks
|
|
#define SET_PTR(p, ptr) (*(uintptr_t*)(p) = (uintptr_t)(ptr))
|
|
|
|
// Read the size and allocation bit from address p
|
|
#define GET_SIZE(p) (GET(p) & ~0x7)
|
|
#define GET_ALLOC(p) (GET(p) & 0x1)
|
|
#define GET_TAG(p) (GET(p) & 0x2)
|
|
#define SET_RATAG(p) (GET(p) |= 0x2)
|
|
#define REMOVE_RATAG(p) (GET(p) &= ~0x2)
|
|
|
|
// Address of block's header and footer
|
|
#define HDRP(ptr) ((char*)(ptr)-WSIZE)
|
|
#define FTRP(ptr) ((char*)(ptr) + GET_SIZE(HDRP(ptr)) - DSIZE)
|
|
|
|
// Address of (physically) next and previous blocks
|
|
#define NEXT_BLKP(ptr) ((char*)(ptr) + GET_SIZE((char*)(ptr)-WSIZE))
|
|
#define PREV_BLKP(ptr) ((char*)(ptr)-GET_SIZE((char*)(ptr)-DSIZE))
|
|
|
|
// Address of free block's predecessor and successor entries
|
|
#define PRED_PTR(ptr) ((char*)(ptr))
|
|
#define SUCC_PTR(ptr) ((char*)(ptr) + WSIZE)
|
|
|
|
// Address of free block's predecessor and successor on the segregated list
|
|
#define PRED(ptr) (*(char**)(ptr))
|
|
#define SUCC(ptr) (*(char**)(SUCC_PTR(ptr)))
|
|
|
|
// End of my additional macros
|
|
|
|
// Global var
|
|
void* segregated_free_lists[LISTLIMIT];
|
|
|
|
// Functions
|
|
static void* extend_heap(size_t size);
|
|
static void* coalesce(void* ptr);
|
|
static void* place(void* ptr, size_t asize);
|
|
static void insert_node(void* ptr, size_t size);
|
|
static void delete_node(void* ptr);
|
|
|
|
static uintptr_t userland_heap_base;
|
|
static uintptr_t userland_heap_top;
|
|
static uintptr_t requested_heap_size;
|
|
|
|
static void* mem_sbrk(size_t size)
|
|
{
|
|
uintptr_t userland_heap_size = userland_heap_top - userland_heap_base;
|
|
if (userland_heap_size - requested_heap_size >= size) {
|
|
void* ret_ptr = (void*)(userland_heap_base + requested_heap_size);
|
|
requested_heap_size += size;
|
|
return ret_ptr;
|
|
}
|
|
|
|
uintptr_t size_needed = size - (userland_heap_size - requested_heap_size);
|
|
userland_heap_top = mmap(userland_heap_top, (uintptr_t)NULL, size_needed, false);
|
|
|
|
return mem_sbrk(size);
|
|
}
|
|
|
|
static void* extend_heap(size_t size)
|
|
{
|
|
void* ptr;
|
|
size_t asize; // Adjusted size
|
|
|
|
asize = ALIGN(size);
|
|
|
|
if ((ptr = mem_sbrk(asize)) == (void*)-1)
|
|
return NULL;
|
|
|
|
// Set headers and footer
|
|
PUT_NOTAG(HDRP(ptr), PACK(asize, 0));
|
|
PUT_NOTAG(FTRP(ptr), PACK(asize, 0));
|
|
PUT_NOTAG(HDRP(NEXT_BLKP(ptr)), PACK(0, 1));
|
|
insert_node(ptr, asize);
|
|
|
|
return coalesce(ptr);
|
|
}
|
|
|
|
static void insert_node(void* ptr, size_t size)
|
|
{
|
|
int list = 0;
|
|
void* search_ptr = ptr;
|
|
void* insert_ptr = NULL;
|
|
|
|
// Select segregated list
|
|
while ((list < LISTLIMIT - 1) && (size > 1)) {
|
|
size >>= 1;
|
|
list++;
|
|
}
|
|
|
|
// Keep size ascending order and search
|
|
search_ptr = segregated_free_lists[list];
|
|
while ((search_ptr != NULL) && (size > GET_SIZE(HDRP(search_ptr)))) {
|
|
insert_ptr = search_ptr;
|
|
search_ptr = PRED(search_ptr);
|
|
}
|
|
|
|
// Set predecessor and successor
|
|
if (search_ptr != NULL) {
|
|
if (insert_ptr != NULL) {
|
|
SET_PTR(PRED_PTR(ptr), search_ptr);
|
|
SET_PTR(SUCC_PTR(search_ptr), ptr);
|
|
SET_PTR(SUCC_PTR(ptr), insert_ptr);
|
|
SET_PTR(PRED_PTR(insert_ptr), ptr);
|
|
} else {
|
|
SET_PTR(PRED_PTR(ptr), search_ptr);
|
|
SET_PTR(SUCC_PTR(search_ptr), ptr);
|
|
SET_PTR(SUCC_PTR(ptr), NULL);
|
|
segregated_free_lists[list] = ptr;
|
|
}
|
|
} else {
|
|
if (insert_ptr != NULL) {
|
|
SET_PTR(PRED_PTR(ptr), NULL);
|
|
SET_PTR(SUCC_PTR(ptr), insert_ptr);
|
|
SET_PTR(PRED_PTR(insert_ptr), ptr);
|
|
} else {
|
|
SET_PTR(PRED_PTR(ptr), NULL);
|
|
SET_PTR(SUCC_PTR(ptr), NULL);
|
|
segregated_free_lists[list] = ptr;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void delete_node(void* ptr)
|
|
{
|
|
int list = 0;
|
|
size_t size = GET_SIZE(HDRP(ptr));
|
|
|
|
// Select segregated list
|
|
while ((list < LISTLIMIT - 1) && (size > 1)) {
|
|
size >>= 1;
|
|
list++;
|
|
}
|
|
|
|
if (PRED(ptr) != NULL) {
|
|
if (SUCC(ptr) != NULL) {
|
|
SET_PTR(SUCC_PTR(PRED(ptr)), SUCC(ptr));
|
|
SET_PTR(PRED_PTR(SUCC(ptr)), PRED(ptr));
|
|
} else {
|
|
SET_PTR(SUCC_PTR(PRED(ptr)), NULL);
|
|
segregated_free_lists[list] = PRED(ptr);
|
|
}
|
|
} else {
|
|
if (SUCC(ptr) != NULL) {
|
|
SET_PTR(PRED_PTR(SUCC(ptr)), NULL);
|
|
} else {
|
|
segregated_free_lists[list] = NULL;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void* coalesce(void* ptr)
|
|
{
|
|
size_t prev_alloc = GET_ALLOC(HDRP(PREV_BLKP(ptr)));
|
|
size_t next_alloc = GET_ALLOC(HDRP(NEXT_BLKP(ptr)));
|
|
size_t size = GET_SIZE(HDRP(ptr));
|
|
|
|
// Do not coalesce with previous block if the previous block is tagged with Reallocation tag
|
|
if (GET_TAG(HDRP(PREV_BLKP(ptr))))
|
|
prev_alloc = 1;
|
|
|
|
if (prev_alloc && next_alloc) { // Case 1
|
|
return ptr;
|
|
} else if (prev_alloc && !next_alloc) { // Case 2
|
|
delete_node(ptr);
|
|
delete_node(NEXT_BLKP(ptr));
|
|
size += GET_SIZE(HDRP(NEXT_BLKP(ptr)));
|
|
PUT(HDRP(ptr), PACK(size, 0));
|
|
PUT(FTRP(ptr), PACK(size, 0));
|
|
} else if (!prev_alloc && next_alloc) { // Case 3
|
|
delete_node(ptr);
|
|
delete_node(PREV_BLKP(ptr));
|
|
size += GET_SIZE(HDRP(PREV_BLKP(ptr)));
|
|
PUT(FTRP(ptr), PACK(size, 0));
|
|
PUT(HDRP(PREV_BLKP(ptr)), PACK(size, 0));
|
|
ptr = PREV_BLKP(ptr);
|
|
} else { // Case 4
|
|
delete_node(ptr);
|
|
delete_node(PREV_BLKP(ptr));
|
|
delete_node(NEXT_BLKP(ptr));
|
|
size += GET_SIZE(HDRP(PREV_BLKP(ptr))) + GET_SIZE(HDRP(NEXT_BLKP(ptr)));
|
|
PUT(HDRP(PREV_BLKP(ptr)), PACK(size, 0));
|
|
PUT(FTRP(NEXT_BLKP(ptr)), PACK(size, 0));
|
|
ptr = PREV_BLKP(ptr);
|
|
}
|
|
|
|
insert_node(ptr, size);
|
|
|
|
return ptr;
|
|
}
|
|
|
|
static void* place(void* ptr, size_t asize)
|
|
{
|
|
size_t ptr_size = GET_SIZE(HDRP(ptr));
|
|
size_t remainder = ptr_size - asize;
|
|
|
|
delete_node(ptr);
|
|
|
|
if (remainder <= DSIZE * 2) {
|
|
// Do not split block
|
|
PUT(HDRP(ptr), PACK(ptr_size, 1));
|
|
PUT(FTRP(ptr), PACK(ptr_size, 1));
|
|
}
|
|
|
|
else if (asize >= 100) {
|
|
// Split block
|
|
PUT(HDRP(ptr), PACK(remainder, 0));
|
|
PUT(FTRP(ptr), PACK(remainder, 0));
|
|
PUT_NOTAG(HDRP(NEXT_BLKP(ptr)), PACK(asize, 1));
|
|
PUT_NOTAG(FTRP(NEXT_BLKP(ptr)), PACK(asize, 1));
|
|
insert_node(ptr, remainder);
|
|
return NEXT_BLKP(ptr);
|
|
|
|
}
|
|
|
|
else {
|
|
// Split block
|
|
PUT(HDRP(ptr), PACK(asize, 1));
|
|
PUT(FTRP(ptr), PACK(asize, 1));
|
|
PUT_NOTAG(HDRP(NEXT_BLKP(ptr)), PACK(remainder, 0));
|
|
PUT_NOTAG(FTRP(NEXT_BLKP(ptr)), PACK(remainder, 0));
|
|
insert_node(NEXT_BLKP(ptr), remainder);
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
static bool malloc_inited = false;
|
|
int mm_init(void)
|
|
{
|
|
// init heap
|
|
userland_heap_base = task_heap_base();
|
|
userland_heap_top = userland_heap_base;
|
|
requested_heap_size = 0;
|
|
|
|
int list;
|
|
char* heap_start; // Pointer to beginning of heap
|
|
|
|
// Initialize segregated free lists
|
|
for (list = 0; list < LISTLIMIT; list++) {
|
|
segregated_free_lists[list] = NULL;
|
|
}
|
|
|
|
// Allocate memory for the initial empty heap
|
|
if ((long)(heap_start = mem_sbrk(4 * WSIZE)) == -1)
|
|
return -1;
|
|
|
|
PUT_NOTAG(heap_start, 0); /* Alignment padding */
|
|
PUT_NOTAG(heap_start + (1 * WSIZE), PACK(DSIZE, 1)); /* Prologue header */
|
|
PUT_NOTAG(heap_start + (2 * WSIZE), PACK(DSIZE, 1)); /* Prologue footer */
|
|
PUT_NOTAG(heap_start + (3 * WSIZE), PACK(0, 1)); /* Epilogue header */
|
|
|
|
if (extend_heap(INITCHUNKSIZE) == NULL)
|
|
return -1;
|
|
|
|
malloc_inited = true;
|
|
return 0;
|
|
}
|
|
|
|
void* malloc(size_t size)
|
|
{
|
|
while (!malloc_inited) {
|
|
mm_init();
|
|
}
|
|
|
|
size_t asize; /* Adjusted block size */
|
|
size_t extendsize; /* Amount to extend heap if no fit */
|
|
void* ptr = NULL; /* Pointer */
|
|
|
|
// Ignore size 0 cases
|
|
if (size == 0)
|
|
return NULL;
|
|
|
|
// Align block size
|
|
if (size <= DSIZE) {
|
|
asize = 2 * DSIZE;
|
|
} else {
|
|
asize = ALIGN(size + DSIZE);
|
|
}
|
|
|
|
int list = 0;
|
|
size_t searchsize = asize;
|
|
// Search for free block in segregated list
|
|
while (list < LISTLIMIT) {
|
|
if ((list == LISTLIMIT - 1) || ((searchsize <= 1) && (segregated_free_lists[list] != NULL))) {
|
|
ptr = segregated_free_lists[list];
|
|
// Ignore blocks that are too small or marked with the reallocation bit
|
|
while ((ptr != NULL) && ((asize > GET_SIZE(HDRP(ptr))) || (GET_TAG(HDRP(ptr))))) {
|
|
ptr = PRED(ptr);
|
|
}
|
|
if (ptr != NULL)
|
|
break;
|
|
}
|
|
|
|
searchsize >>= 1;
|
|
list++;
|
|
}
|
|
|
|
// if free block is not found, extend the heap
|
|
if (ptr == NULL) {
|
|
extendsize = MAX(asize, CHUNKSIZE);
|
|
|
|
if ((ptr = extend_heap(extendsize)) == NULL)
|
|
return NULL;
|
|
}
|
|
|
|
// Place and divide block
|
|
ptr = place(ptr, asize);
|
|
|
|
// Return pointer to newly allocated block
|
|
return ptr;
|
|
}
|
|
|
|
void free(void* ptr)
|
|
{
|
|
size_t size = GET_SIZE(HDRP(ptr));
|
|
|
|
REMOVE_RATAG(HDRP(NEXT_BLKP(ptr)));
|
|
PUT(HDRP(ptr), PACK(size, 0));
|
|
PUT(FTRP(ptr), PACK(size, 0));
|
|
|
|
insert_node(ptr, size);
|
|
coalesce(ptr);
|
|
|
|
return;
|
|
}
|