/* * 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) (*(unsigned int*)(p) = (unsigned int)(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; }