├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── LICENSE.md ├── README.md └── src ├── Allocator.c ├── Allocator.h ├── Block.c ├── Block.h ├── Constants.h ├── GCTypes.h ├── Heap.c ├── Heap.h ├── ImmixGC.c ├── LargeAllocator.c ├── LargeAllocator.h ├── Line.h ├── Log.h ├── Marker.c ├── Marker.h ├── Memory.h ├── Object.c ├── Object.h ├── StackTrace.h ├── StackoverflowHandler.c ├── StackoverflowHandler.h ├── State.c ├── State.h ├── datastructures ├── Bitmap.c ├── Bitmap.h ├── BlockList.c ├── BlockList.h ├── Stack.c └── Stack.h ├── headers ├── BlockHeader.h ├── LineHeader.h └── ObjectHeader.h └── utils └── MathUtils.h /.gitignore: -------------------------------------------------------------------------------- 1 | # CMake 2 | CMakeCache.txt 3 | CMakeFiles 4 | Makefile 5 | cmake_install.cmake 6 | build/ 7 | 8 | 9 | # Compiled Dynamic libraries 10 | *.so 11 | *.dylib 12 | 13 | 14 | # Clion 15 | *.cbp 16 | cmake-build-debug/ 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | language: c 3 | 4 | compiler: 5 | - clang 6 | 7 | before_install: 8 | - sudo find /usr -name "*libunwind*" -delete 9 | - sudo apt-get install -y -qq libunwind8-dev 10 | 11 | before_script: 12 | - if [ "$TRAVIS_PULL_REQUEST" != "false" ] ; then 13 | echo "Incoming pull request from https://github.com/$TRAVIS_REPO_SLUG/pull/$TRAVIS_PULL_REQUEST"; 14 | author=$(curl -u dummy4dummy:dummy2dummy -s "https://api.github.com/repos/$TRAVIS_REPO_SLUG/pulls/$TRAVIS_PULL_REQUEST" | jq -r ".user.login"); 15 | if [ $? -ne 0 ] ; then exit 1; fi; 16 | echo "Pull request submitted by $author"; 17 | signed=$(curl -s https://www.lightbend.com/contribute/cla/scala/check/$author | jq -r ".signed"); 18 | if [ $? -ne 0 ] ; then exit 1; fi; 19 | if [ "$signed" = "true" ] ; then 20 | echo "CLA check for $author successful"; 21 | else 22 | echo "CLA check for $author failed"; 23 | echo "Please sign the Scala CLA to contribute to Scala Native"; 24 | echo "Go to https://www.lightbend.com/contribute/cla/scala and then resubmit this pull request"; 25 | exit 1; 26 | fi; 27 | fi; 28 | - cmake . 29 | 30 | script: make 31 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | PROJECT(Immix) 3 | 4 | 5 | set(CMAKE_CXX_STANDARD 11) 6 | 7 | add_library(Immix src/ 8 | src/datastructures/Bitmap.c 9 | src/datastructures/Bitmap.h 10 | src/datastructures/BlockList.c 11 | src/datastructures/BlockList.h 12 | src/datastructures/Stack.c 13 | src/datastructures/Stack.h 14 | src/headers/BlockHeader.h 15 | src/headers/LineHeader.h 16 | src/headers/ObjectHeader.h 17 | src/utils/MathUtils.h 18 | src/Allocator.c 19 | src/Allocator.h 20 | src/Block.c 21 | src/Block.h 22 | src/Constants.h 23 | src/GCTypes.h 24 | src/Heap.c 25 | src/Heap.h 26 | src/ImmixGC.c 27 | src/LargeAllocator.c 28 | src/LargeAllocator.h 29 | src/Line.h 30 | src/Log.h 31 | src/Marker.c 32 | src/Marker.h 33 | src/Memory.h 34 | src/Object.c 35 | src/Object.h 36 | src/StackoverflowHandler.c 37 | src/StackoverflowHandler.h 38 | src/StackTrace.h 39 | src/State.c 40 | src/State.h) 41 | 42 | set_target_properties(Immix PROPERTIES LINKER_LANGUAGE C) 43 | set_target_properties(Immix PROPERTIES ARCHIVE_OUTPUT_DIRECTORY build) 44 | 45 | 46 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | Our Immix implementation is provided under Scala License: 4 | 5 | ``` 6 | Copyright (c) 2015-2017 EPFL 7 | 8 | All rights reserved. 9 | 10 | Redistribution and use in source and binary forms, with or without modification, 11 | are permitted provided that the following conditions are met: 12 | 13 | * Redistributions of source code must retain the above copyright notice, 14 | this list of conditions and the following disclaimer. 15 | * Redistributions in binary form must reproduce the above copyright notice, 16 | this list of conditions and the following disclaimer in the documentation 17 | and/or other materials provided with the distribution. 18 | * Neither the name of the EPFL nor the names of its contributors 19 | may be used to endorse or promote products derived from this software 20 | without specific prior written permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 25 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 26 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 27 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 28 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 29 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 31 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 32 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | ``` 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Immix 2 | 3 | This is a staging area for pluggable implementation of Immix garbage collector. 4 | 5 | ## Building Immix 6 | 1. Make sure you have [CMake](https://cmake.org/) 3.2 or newer installed: 7 | ```sh 8 | $ cmake -version 9 | ``` 10 | 11 | 2. Clone the [source](https://github.com/scala-native/immix) with git: 12 | ```sh 13 | $ git clone https://github.com/scala-native/immix.git 14 | $ cd immix 15 | ``` 16 | 17 | 3. Build: 18 | ```sh 19 | $ cmake . && make 20 | ``` 21 | -------------------------------------------------------------------------------- /src/Allocator.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Allocator.h" 3 | #include "Line.h" 4 | #include "Block.h" 5 | #include 6 | #include 7 | 8 | BlockHeader *Allocator_getNextBlock(Allocator *allocator); 9 | bool Allocator_getNextLine(Allocator *allocator); 10 | 11 | /** 12 | * 13 | * Allocates the Allocator and initialises it's fields 14 | * 15 | * @param heapStart 16 | * @param blockCount Initial number of blocks in the heap 17 | * @return 18 | */ 19 | Allocator *Allocator_Create(word_t *heapStart, int blockCount) { 20 | Allocator *allocator = malloc(sizeof(Allocator)); 21 | allocator->heapStart = heapStart; 22 | 23 | BlockList_Init(&allocator->recycledBlocks, heapStart); 24 | BlockList_Init(&allocator->freeBlocks, heapStart); 25 | 26 | // Init the free block list 27 | allocator->freeBlocks.first = (BlockHeader *)heapStart; 28 | BlockHeader *lastBlockHeader = 29 | (BlockHeader *)(heapStart + ((blockCount - 1) * WORDS_IN_BLOCK)); 30 | allocator->freeBlocks.last = lastBlockHeader; 31 | lastBlockHeader->header.nextBlock = LAST_BLOCK; 32 | 33 | // Block stats 34 | allocator->blockCount = (uint64_t)blockCount; 35 | allocator->freeBlockCount = (uint64_t)blockCount; 36 | allocator->recycledBlockCount = 0; 37 | 38 | Allocator_InitCursors(allocator); 39 | 40 | return allocator; 41 | } 42 | 43 | /** 44 | * The Allocator needs one free block for overflow allocation and a free or 45 | * recyclable block for normal allocation. 46 | * 47 | * @param allocator 48 | * @return `true` if there are enough block to initialise the cursors, `false` 49 | * otherwise. 50 | */ 51 | bool Allocator_CanInitCursors(Allocator *allocator) { 52 | return allocator->freeBlockCount >= 2 || 53 | (allocator->freeBlockCount == 1 && 54 | allocator->recycledBlockCount > 0); 55 | } 56 | 57 | /** 58 | * 59 | * Used to initialise the cursors of the Allocator after collection 60 | * 61 | * @param allocator 62 | */ 63 | void Allocator_InitCursors(Allocator *allocator) { 64 | 65 | // Init cursor 66 | allocator->block = NULL; 67 | allocator->cursor = NULL; 68 | allocator->limit = NULL; 69 | 70 | Allocator_getNextLine(allocator); 71 | 72 | // Init large cursor 73 | assert(!BlockList_IsEmpty(&allocator->freeBlocks)); 74 | 75 | BlockHeader *largeHeader = 76 | BlockList_RemoveFirstBlock(&allocator->freeBlocks); 77 | allocator->largeBlock = largeHeader; 78 | allocator->largeCursor = Block_GetFirstWord(largeHeader); 79 | allocator->largeLimit = Block_GetBlockEnd(largeHeader); 80 | } 81 | 82 | /** 83 | * Heuristic that tells if the heap should be grown or not. 84 | */ 85 | bool Allocator_ShouldGrow(Allocator *allocator) { 86 | uint64_t unavailableBlockCount = 87 | allocator->blockCount - 88 | (allocator->freeBlockCount + allocator->recycledBlockCount); 89 | 90 | #ifdef DEBUG_PRINT 91 | printf("\n\nBlock count: %llu\n", allocator->blockCount); 92 | printf("Unavailable: %llu\n", unavailableBlockCount); 93 | printf("Free: %llu\n", allocator->freeBlockCount); 94 | printf("Recycled: %llu\n", allocator->recycledBlockCount); 95 | fflush(stdout); 96 | #endif 97 | 98 | return allocator->freeBlockCount < allocator->blockCount / 3 || 99 | 4 * unavailableBlockCount > allocator->blockCount || 100 | allocator->freeMemoryAfterCollection * 2 < 101 | allocator->blockCount * BLOCK_TOTAL_SIZE; 102 | } 103 | 104 | /** 105 | * Overflow allocation uses only free blocks, it is used when the bump limit of 106 | * the fast allocator is too small to fit 107 | * the block to alloc. 108 | */ 109 | word_t *Allocator_overflowAllocation(Allocator *allocator, size_t size) { 110 | word_t *start = allocator->largeCursor; 111 | word_t *end = (word_t *)((uint8_t *)start + size); 112 | 113 | if (end > allocator->largeLimit) { 114 | if (BlockList_IsEmpty(&allocator->freeBlocks)) { 115 | return NULL; 116 | } 117 | BlockHeader *block = BlockList_RemoveFirstBlock(&allocator->freeBlocks); 118 | allocator->largeBlock = block; 119 | allocator->largeCursor = Block_GetFirstWord(block); 120 | allocator->largeLimit = Block_GetBlockEnd(block); 121 | return Allocator_overflowAllocation(allocator, size); 122 | } 123 | 124 | if (end == allocator->largeLimit) { 125 | memset(start, 0, size); 126 | } else { 127 | memset(start, 0, size + WORD_SIZE); 128 | } 129 | 130 | allocator->largeCursor = end; 131 | 132 | Line_Update(allocator->largeBlock, start); 133 | 134 | return start; 135 | } 136 | 137 | /** 138 | * Allocation fast path, uses the cursor and limit. 139 | */ 140 | INLINE word_t *Allocator_Alloc(Allocator *allocator, size_t size) { 141 | word_t *start = allocator->cursor; 142 | word_t *end = (word_t *)((uint8_t *)start + size); 143 | 144 | // Checks if the end of the block overlaps with the limit 145 | if (end > allocator->limit) { 146 | // If it overlaps but the block to allocate is a `medium` sized block, 147 | // use overflow allocation 148 | if (size > LINE_SIZE) { 149 | return Allocator_overflowAllocation(allocator, size); 150 | } else { 151 | // Otherwise try to get a new line. 152 | if (Allocator_getNextLine(allocator)) { 153 | return Allocator_Alloc(allocator, size); 154 | } 155 | 156 | return NULL; 157 | } 158 | } 159 | 160 | if (end == allocator->limit) { 161 | memset(start, 0, size); 162 | } else { 163 | memset(start, 0, size + WORD_SIZE); 164 | } 165 | 166 | allocator->cursor = end; 167 | 168 | Line_Update(allocator->block, start); 169 | 170 | return start; 171 | } 172 | 173 | /** 174 | * Updates the cursor and the limit of the Allocator to point the next line of 175 | * the recycled block 176 | */ 177 | bool Allocator_nextLineRecycled(Allocator *allocator) { 178 | // The cursor can point on first word of next block, thus `- WORD_SIZE` 179 | BlockHeader *block = Block_GetBlockHeader(allocator->cursor - WORD_SIZE); 180 | assert(Block_IsRecyclable(block)); 181 | 182 | int16_t lineIndex = block->header.first; 183 | if (lineIndex == LAST_HOLE) { 184 | allocator->cursor = NULL; 185 | return Allocator_getNextLine(allocator); 186 | } 187 | 188 | word_t *line = Block_GetLineAddress(block, lineIndex); 189 | 190 | allocator->cursor = line; 191 | FreeLineHeader *lineHeader = (FreeLineHeader *)line; 192 | block->header.first = lineHeader->next; 193 | uint16_t size = lineHeader->size; 194 | allocator->limit = line + (size * WORDS_IN_LINE); 195 | 196 | return true; 197 | } 198 | 199 | /** 200 | * Updates the the cursor and the limit of the Allocator to point to the first 201 | * free line of the new block. 202 | */ 203 | void Allocator_firstLineNewBlock(Allocator *allocator, BlockHeader *block) { 204 | allocator->block = block; 205 | 206 | // The block can be free or recycled. 207 | if (Block_IsFree(block)) { 208 | allocator->cursor = Block_GetFirstWord(block); 209 | allocator->limit = Block_GetBlockEnd(block); 210 | } else { 211 | assert(Block_IsRecyclable(block)); 212 | int16_t lineIndex = block->header.first; 213 | assert(lineIndex < LINE_COUNT); 214 | word_t *line = Block_GetLineAddress(block, lineIndex); 215 | 216 | allocator->cursor = line; 217 | FreeLineHeader *lineHeader = (FreeLineHeader *)line; 218 | block->header.first = lineHeader->next; 219 | uint16_t size = lineHeader->size; 220 | assert(size > 0); 221 | allocator->limit = line + (size * WORDS_IN_LINE); 222 | } 223 | } 224 | 225 | bool Allocator_getNextLine(Allocator *allocator) { 226 | // If cursor is null or the block was free, we need a new block 227 | if (allocator->cursor == NULL || 228 | // The cursor can point on first word of next block, thus `- WORD_SIZE` 229 | Block_IsFree(Block_GetBlockHeader(allocator->cursor - WORD_SIZE))) { 230 | // request the new block. 231 | BlockHeader *block = Allocator_getNextBlock(allocator); 232 | // return false if there is no block left. 233 | if (block == NULL) { 234 | return false; 235 | } 236 | 237 | Allocator_firstLineNewBlock(allocator, block); 238 | 239 | return true; 240 | 241 | } else { 242 | // If we have a recycled block 243 | return Allocator_nextLineRecycled(allocator); 244 | } 245 | } 246 | 247 | /** 248 | * Returns a block, first from recycled if available, otherwise from 249 | * chunk_allocator 250 | */ 251 | BlockHeader *Allocator_getNextBlock(Allocator *allocator) { 252 | BlockHeader *block = NULL; 253 | if (!BlockList_IsEmpty(&allocator->recycledBlocks)) { 254 | block = BlockList_RemoveFirstBlock(&allocator->recycledBlocks); 255 | } else if (!BlockList_IsEmpty(&allocator->freeBlocks)) { 256 | block = BlockList_RemoveFirstBlock(&allocator->freeBlocks); 257 | } 258 | return block; 259 | } 260 | -------------------------------------------------------------------------------- /src/Allocator.h: -------------------------------------------------------------------------------- 1 | #ifndef IMMIX_ALLOCATOR_H 2 | #define IMMIX_ALLOCATOR_H 3 | 4 | #include "GCTypes.h" 5 | #include 6 | #include "datastructures/BlockList.h" 7 | 8 | typedef struct { 9 | word_t *heapStart; 10 | uint64_t blockCount; 11 | BlockList recycledBlocks; 12 | uint64_t recycledBlockCount; 13 | BlockList freeBlocks; 14 | uint64_t freeBlockCount; 15 | BlockHeader *block; 16 | word_t *cursor; 17 | word_t *limit; 18 | BlockHeader *largeBlock; 19 | word_t *largeCursor; 20 | word_t *largeLimit; 21 | size_t freeMemoryAfterCollection; 22 | } Allocator; 23 | 24 | Allocator *Allocator_Create(word_t *, int); 25 | bool Allocator_CanInitCursors(Allocator *allocator); 26 | void Allocator_InitCursors(Allocator *allocator); 27 | word_t *Allocator_Alloc(Allocator *allocator, size_t size); 28 | 29 | bool Allocator_ShouldGrow(Allocator *allocator); 30 | 31 | #endif // IMMIX_ALLOCATOR_H 32 | -------------------------------------------------------------------------------- /src/Block.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "Block.h" 4 | #include "Object.h" 5 | #include "Log.h" 6 | #include "Allocator.h" 7 | #include "Marker.h" 8 | 9 | extern int __object_array_id; 10 | 11 | #define NO_RECYCLABLE_LINE -1 12 | 13 | INLINE void Block_recycleUnmarkedBlock(Allocator *allocator, 14 | BlockHeader *blockHeader) { 15 | memset(blockHeader, 0, LINE_SIZE); 16 | BlockList_AddLast(&allocator->freeBlocks, blockHeader); 17 | Block_SetFlag(blockHeader, block_free); 18 | } 19 | 20 | INLINE void Block_recycleMarkedLine(BlockHeader *blockHeader, 21 | LineHeader *lineHeader, int lineIndex) { 22 | Line_Unmark(lineHeader); 23 | // If the line contains an object 24 | if (Line_ContainsObject(lineHeader)) { 25 | // Unmark all objects in line 26 | Object *object = Line_GetFirstObject(lineHeader); 27 | word_t *lineEnd = 28 | Block_GetLineAddress(blockHeader, lineIndex) + WORDS_IN_LINE; 29 | while (object != NULL && (word_t *)object < lineEnd) { 30 | ObjectHeader *objectHeader = &object->header; 31 | if (Object_IsMarked(objectHeader)) { 32 | Object_SetAllocated(objectHeader); 33 | } else { 34 | Object_SetFree(objectHeader); 35 | } 36 | object = Object_NextObject(object); 37 | } 38 | } 39 | } 40 | 41 | /** 42 | * recycles a block and adds it to the allocator 43 | */ 44 | void Block_Recycle(Allocator *allocator, BlockHeader *blockHeader) { 45 | 46 | // If the block is not marked, it means that it's completely free 47 | if (!Block_IsMarked(blockHeader)) { 48 | Block_recycleUnmarkedBlock(allocator, blockHeader); 49 | allocator->freeBlockCount++; 50 | allocator->freeMemoryAfterCollection += BLOCK_TOTAL_SIZE; 51 | } else { 52 | // If the block is marked, we need to recycle line by line 53 | assert(Block_IsMarked(blockHeader)); 54 | Block_Unmark(blockHeader); 55 | int16_t lineIndex = 0; 56 | int lastRecyclable = NO_RECYCLABLE_LINE; 57 | while (lineIndex < LINE_COUNT) { 58 | LineHeader *lineHeader = 59 | Block_GetLineHeader(blockHeader, lineIndex); 60 | // If the line is marked, we need to unmark all objects in the line 61 | if (Line_IsMarked(lineHeader)) { 62 | // Unmark line 63 | Block_recycleMarkedLine(blockHeader, lineHeader, lineIndex); 64 | lineIndex++; 65 | } else { 66 | // If the line is not marked, we need to merge all continuous 67 | // unmarked lines. 68 | 69 | // If it's the first free line, update the block header to point 70 | // to it. 71 | if (lastRecyclable == NO_RECYCLABLE_LINE) { 72 | blockHeader->header.first = lineIndex; 73 | } else { 74 | // Update the last recyclable line to point to the current 75 | // one 76 | Block_GetFreeLineHeader(blockHeader, lastRecyclable)->next = 77 | lineIndex; 78 | } 79 | lastRecyclable = lineIndex; 80 | lineIndex++; 81 | Line_SetEmpty(lineHeader); 82 | allocator->freeMemoryAfterCollection += LINE_SIZE; 83 | uint8_t size = 1; 84 | while (lineIndex < LINE_COUNT && 85 | !Line_IsMarked(lineHeader = Block_GetLineHeader( 86 | blockHeader, lineIndex))) { 87 | size++; 88 | lineIndex++; 89 | Line_SetEmpty(lineHeader); 90 | allocator->freeMemoryAfterCollection += LINE_SIZE; 91 | } 92 | Block_GetFreeLineHeader(blockHeader, lastRecyclable)->size = 93 | size; 94 | } 95 | } 96 | // If there is no recyclable line, the block is unavailable 97 | if (lastRecyclable == NO_RECYCLABLE_LINE) { 98 | Block_SetFlag(blockHeader, block_unavailable); 99 | } else { 100 | Block_GetFreeLineHeader(blockHeader, lastRecyclable)->next = 101 | LAST_HOLE; 102 | Block_SetFlag(blockHeader, block_recyclable); 103 | BlockList_AddLast(&allocator->recycledBlocks, blockHeader); 104 | 105 | assert(blockHeader->header.first != NO_RECYCLABLE_LINE); 106 | allocator->recycledBlockCount++; 107 | } 108 | } 109 | } 110 | 111 | void Block_Print(BlockHeader *block) { 112 | printf("%p ", block); 113 | if (Block_IsFree(block)) { 114 | printf("FREE\n"); 115 | } else if (Block_IsUnavailable(block)) { 116 | printf("UNAVAILABLE\n"); 117 | } else { 118 | int lineIndex = block->header.first; 119 | while (lineIndex != LAST_HOLE) { 120 | FreeLineHeader *freeLineHeader = 121 | Block_GetFreeLineHeader(block, lineIndex); 122 | printf("[index: %d, size: %d] -> ", lineIndex, 123 | freeLineHeader->size); 124 | lineIndex = freeLineHeader->next; 125 | } 126 | printf("\n"); 127 | } 128 | fflush(stdout); 129 | } -------------------------------------------------------------------------------- /src/Block.h: -------------------------------------------------------------------------------- 1 | #ifndef IMMIX_BLOCK_H 2 | #define IMMIX_BLOCK_H 3 | 4 | #include "headers/BlockHeader.h" 5 | #include "Heap.h" 6 | #include "Line.h" 7 | 8 | #define LAST_HOLE -1 9 | 10 | void Block_Recycle(Allocator *, BlockHeader *); 11 | void Block_Print(BlockHeader *block); 12 | #endif // IMMIX_BLOCK_H 13 | -------------------------------------------------------------------------------- /src/Constants.h: -------------------------------------------------------------------------------- 1 | #ifndef IMMIX_CONSTANTS_H 2 | #define IMMIX_CONSTANTS_H 3 | 4 | #define WORD_SIZE_BITS 3 5 | #define WORD_SIZE (1 << WORD_SIZE_BITS) 6 | 7 | #define WORD_INVERSE_MASK (~((word_t)WORD_SIZE - 1)) 8 | 9 | #define BLOCK_SIZE_BITS 15 10 | #define LINE_SIZE_BITS 8 11 | 12 | #define BLOCK_METADATA_SIZE_BITS 3 13 | #define LINE_METADATA_SIZE_BITS 0 14 | 15 | #define BLOCK_TOTAL_SIZE (1 << BLOCK_SIZE_BITS) 16 | #define BLOCK_METADATA_SIZE (1 << BLOCK_METADATA_SIZE_BITS) 17 | #define LINE_SIZE (1UL << LINE_SIZE_BITS) 18 | #define LINE_METADATA_SIZE (1 << LINE_METADATA_SIZE_BITS) 19 | 20 | #define LINE_SIZE_MASK (LINE_SIZE - 1) 21 | 22 | #define OBJECT_HEADER_SIZE 8 23 | #define WORDS_IN_OBJECT_HEADER (OBJECT_HEADER_SIZE / WORD_SIZE) 24 | 25 | #define LINE_COUNT \ 26 | ((BLOCK_TOTAL_SIZE - BLOCK_METADATA_SIZE) / \ 27 | (LINE_SIZE + LINE_METADATA_SIZE)) 28 | 29 | #define TOTAL_BLOCK_METADATA_SIZE \ 30 | (BLOCK_METADATA_SIZE + (LINE_COUNT * LINE_METADATA_SIZE)) 31 | 32 | #define BLOCK_METADATA_ALIGNED_SIZE \ 33 | ((TOTAL_BLOCK_METADATA_SIZE + LINE_SIZE - 1) / LINE_SIZE * LINE_SIZE) 34 | 35 | #define WORDS_IN_LINE (LINE_SIZE / WORD_SIZE) 36 | #define WORDS_IN_BLOCK (BLOCK_TOTAL_SIZE / WORD_SIZE) 37 | 38 | #define BLOCK_SIZE_IN_BYTES_MASK (BLOCK_TOTAL_SIZE - 1) 39 | #define BLOCK_SIZE_IN_BYTES_INVERSE_MASK (~BLOCK_SIZE_IN_BYTES_MASK) 40 | 41 | #define LARGE_BLOCK_SIZE_BITS 13 42 | #define LARGE_BLOCK_SIZE (1 << LARGE_BLOCK_SIZE_BITS) 43 | 44 | #define LARGE_OBJECT_MIN_SIZE_BITS 13 45 | #define LARGE_OBJECT_MAX_SIZE_BITS 34 46 | 47 | #define MIN_BLOCK_SIZE (1UL << LARGE_OBJECT_MIN_SIZE_BITS) 48 | #define MAX_BLOCK_SIZE (1UL << LARGE_OBJECT_MAX_SIZE_BITS) 49 | 50 | #define LARGE_BLOCK_MASK (~((1 << LARGE_OBJECT_MIN_SIZE_BITS) - 1)) 51 | 52 | #define GROWTH_RATE 30 53 | 54 | #endif // IMMIX_CONSTANTS_H 55 | -------------------------------------------------------------------------------- /src/GCTypes.h: -------------------------------------------------------------------------------- 1 | #ifndef IMMIX_GC_TYPES_H 2 | #define IMMIX_GC_TYPES_H 3 | 4 | #include 5 | 6 | #define INLINE __attribute__((always_inline)) 7 | 8 | typedef uintptr_t word_t; 9 | typedef uint8_t ubyte_t; 10 | 11 | #endif // IMMIX_GC_TYPES_H 12 | -------------------------------------------------------------------------------- /src/Heap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "Heap.h" 5 | #include "Block.h" 6 | #include "Log.h" 7 | #include "Allocator.h" 8 | #include "Marker.h" 9 | #include "State.h" 10 | #include "utils/MathUtils.h" 11 | #include "StackTrace.h" 12 | #include "Memory.h" 13 | 14 | // Allow read and write 15 | #define HEAP_MEM_PROT (PROT_READ | PROT_WRITE) 16 | // Map private anonymous memory, and prevent from reserving swap 17 | #define HEAP_MEM_FLAGS (MAP_NORESERVE | MAP_PRIVATE | MAP_ANONYMOUS) 18 | // Map anonymous memory (not a file) 19 | #define HEAP_MEM_FD -1 20 | #define HEAP_MEM_FD_OFFSET 0 21 | 22 | size_t Heap_getMemoryLimit() { return getMemorySize(); } 23 | 24 | /** 25 | * Maps `MAX_SIZE` of memory and returns the first address aligned on 26 | * `alignement` mask 27 | */ 28 | word_t *Heap_mapAndAlign(size_t memoryLimit, size_t alignmentSize) { 29 | word_t *heapStart = mmap(NULL, memoryLimit, HEAP_MEM_PROT, HEAP_MEM_FLAGS, 30 | HEAP_MEM_FD, HEAP_MEM_FD_OFFSET); 31 | 32 | size_t alignmentMask = ~(alignmentSize - 1); 33 | // Heap start not aligned on 34 | if (((word_t)heapStart & alignmentMask) != (word_t)heapStart) { 35 | word_t *previousBlock = 36 | (word_t *)((word_t)heapStart & BLOCK_SIZE_IN_BYTES_INVERSE_MASK); 37 | heapStart = previousBlock + WORDS_IN_BLOCK; 38 | } 39 | return heapStart; 40 | } 41 | 42 | /** 43 | * Allocates the heap struct and initializes it 44 | */ 45 | Heap *Heap_Create(size_t initialSize) { 46 | assert(initialSize >= 2 * BLOCK_TOTAL_SIZE); 47 | assert(initialSize % BLOCK_TOTAL_SIZE == 0); 48 | 49 | Heap *heap = malloc(sizeof(Heap)); 50 | 51 | size_t memoryLimit = Heap_getMemoryLimit(); 52 | heap->memoryLimit = memoryLimit; 53 | 54 | word_t *smallHeapStart = Heap_mapAndAlign(memoryLimit, BLOCK_TOTAL_SIZE); 55 | 56 | // Init heap for small objects 57 | heap->smallHeapSize = initialSize; 58 | heap->heapStart = smallHeapStart; 59 | heap->heapEnd = smallHeapStart + initialSize / WORD_SIZE; 60 | heap->allocator = 61 | Allocator_Create(smallHeapStart, initialSize / BLOCK_TOTAL_SIZE); 62 | 63 | // Init heap for large objects 64 | word_t *largeHeapStart = Heap_mapAndAlign(memoryLimit, MIN_BLOCK_SIZE); 65 | heap->largeHeapSize = initialSize; 66 | heap->largeAllocator = LargeAllocator_Create(largeHeapStart, initialSize); 67 | heap->largeHeapStart = largeHeapStart; 68 | heap->largeHeapEnd = (word_t *)((ubyte_t *)largeHeapStart + initialSize); 69 | 70 | return heap; 71 | } 72 | /** 73 | * Allocates large objects using the `LargeAllocator`. 74 | * If allocation fails, because there is not enough memory available, it will 75 | * trigger a collection of both the small and the large heap. 76 | */ 77 | word_t *Heap_AllocLarge(Heap *heap, uint32_t objectSize, bool isObjectArray) { 78 | 79 | // Add header 80 | uint32_t size = objectSize + OBJECT_HEADER_SIZE; 81 | 82 | assert(objectSize % WORD_SIZE == 0); 83 | assert(size >= MIN_BLOCK_SIZE); 84 | 85 | // Request an object from the `LargeAllocator` 86 | Object *object = LargeAllocator_GetBlock(heap->largeAllocator, size); 87 | // If the object is not NULL, update it's metadata and return it 88 | if (object != NULL) { 89 | ObjectHeader *objectHeader = &object->header; 90 | 91 | Object_SetObjectType(objectHeader, object_large); 92 | Object_SetObjectArray(objectHeader, isObjectArray); 93 | Object_SetSize(objectHeader, size); 94 | return Object_ToMutatorAddress(object); 95 | } else { 96 | // Otherwise collect 97 | Heap_Collect(heap, stack); 98 | 99 | // After collection, try to alloc again, if it fails, grow the heap by 100 | // at least the size of the object we want to alloc 101 | object = LargeAllocator_GetBlock(heap->largeAllocator, size); 102 | if (object != NULL) { 103 | Object_SetObjectType(&object->header, object_large); 104 | Object_SetSize(&object->header, size); 105 | return Object_ToMutatorAddress(object); 106 | } else { 107 | Heap_GrowLarge(heap, size); 108 | 109 | object = LargeAllocator_GetBlock(heap->largeAllocator, size); 110 | ObjectHeader *objectHeader = &object->header; 111 | 112 | Object_SetObjectType(objectHeader, object_large); 113 | Object_SetObjectArray(objectHeader, isObjectArray); 114 | Object_SetSize(objectHeader, size); 115 | return Object_ToMutatorAddress(object); 116 | } 117 | } 118 | } 119 | 120 | word_t *Heap_allocSmallSlow(Heap *heap, uint32_t size, bool isObjectArray) { 121 | 122 | Heap_Collect(heap, stack); 123 | 124 | Object *object = (Object *)Allocator_Alloc(heap->allocator, size); 125 | if (object != NULL) { 126 | ObjectHeader *objectHeader = &object->header; 127 | 128 | Object_SetObjectType(objectHeader, object_standard); 129 | Object_SetObjectArray(objectHeader, isObjectArray); 130 | Object_SetSize(objectHeader, size); 131 | Object_SetAllocated(objectHeader); 132 | } 133 | 134 | if (object == NULL) { 135 | Heap_Grow(heap, size); 136 | 137 | object = (Object *)Allocator_Alloc(heap->allocator, size); 138 | assert(object != NULL); 139 | 140 | ObjectHeader *objectHeader = &object->header; 141 | 142 | Object_SetObjectType(objectHeader, object_standard); 143 | Object_SetSize(objectHeader, size); 144 | Object_SetAllocated(objectHeader); 145 | } 146 | 147 | return Object_ToMutatorAddress(object); 148 | } 149 | 150 | INLINE word_t *Heap_AllocSmall(Heap *heap, uint32_t objectSize, bool isObjectArray) { 151 | // Add header 152 | uint32_t size = objectSize + OBJECT_HEADER_SIZE; 153 | 154 | assert(objectSize % WORD_SIZE == 0); 155 | assert(size < MIN_BLOCK_SIZE); 156 | 157 | Object *object = (Object *)Allocator_Alloc(heap->allocator, size); 158 | if (object != NULL) { 159 | ObjectHeader *objectHeader = &object->header; 160 | Object_SetObjectType(objectHeader, object_standard); 161 | Object_SetObjectArray(objectHeader, isObjectArray); 162 | Object_SetSize(objectHeader, size); 163 | Object_SetAllocated(objectHeader); 164 | 165 | 166 | return Object_ToMutatorAddress(object); 167 | } else { 168 | return Heap_allocSmallSlow(heap, size, isObjectArray); 169 | } 170 | } 171 | 172 | word_t *Heap_Alloc(Heap *heap, uint32_t objectSize, bool isObjectArray) { 173 | assert(objectSize % WORD_SIZE == 0); 174 | 175 | if (objectSize + OBJECT_HEADER_SIZE >= LARGE_BLOCK_SIZE) { 176 | return Heap_AllocLarge(heap, objectSize, isObjectArray); 177 | } else { 178 | return Heap_AllocSmall(heap, objectSize, isObjectArray); 179 | } 180 | } 181 | 182 | void Heap_Collect(Heap *heap, Stack *stack) { 183 | #ifdef DEBUG_PRINT 184 | printf("\nCollect\n"); 185 | fflush(stdout); 186 | #endif 187 | Marker_MarkRoots(heap, stack); 188 | Heap_Recycle(heap); 189 | 190 | #ifdef DEBUG_PRINT 191 | printf("End collect\n"); 192 | fflush(stdout); 193 | #endif 194 | } 195 | 196 | void Heap_Recycle(Heap *heap) { 197 | BlockList_Clear(&heap->allocator->recycledBlocks); 198 | BlockList_Clear(&heap->allocator->freeBlocks); 199 | 200 | heap->allocator->freeBlockCount = 0; 201 | heap->allocator->recycledBlockCount = 0; 202 | 203 | heap->allocator->freeMemoryAfterCollection = 0; 204 | 205 | word_t *current = heap->heapStart; 206 | while (current != heap->heapEnd) { 207 | BlockHeader *blockHeader = (BlockHeader *)current; 208 | Block_Recycle(heap->allocator, blockHeader); 209 | // block_print(blockHeader); 210 | current += WORDS_IN_BLOCK; 211 | } 212 | LargeAllocator_Sweep(heap->largeAllocator); 213 | 214 | if (!Allocator_CanInitCursors(heap->allocator) || 215 | Allocator_ShouldGrow(heap->allocator)) { 216 | size_t increment = heap->smallHeapSize / WORD_SIZE * GROWTH_RATE / 100; 217 | increment = 218 | (increment - 1 + WORDS_IN_BLOCK) / WORDS_IN_BLOCK * WORDS_IN_BLOCK; 219 | Heap_Grow(heap, increment); 220 | } 221 | Allocator_InitCursors(heap->allocator); 222 | } 223 | 224 | void Heap_exitWithOutOfMemory() { 225 | printf("Out of heap space\n"); 226 | StackTrace_PrintStackTrace(); 227 | exit(1); 228 | } 229 | 230 | bool Heap_isGrowingPossible(Heap *heap, size_t increment) { 231 | return heap->smallHeapSize + heap->largeHeapSize + increment * WORD_SIZE <= 232 | heap->memoryLimit; 233 | } 234 | 235 | /** Grows the small heap by at least `increment` words */ 236 | void Heap_Grow(Heap *heap, size_t increment) { 237 | assert(increment % WORDS_IN_BLOCK == 0); 238 | 239 | // If we cannot grow because we reached the memory limit 240 | if (!Heap_isGrowingPossible(heap, increment)) { 241 | // If we can still init the cursors, grow by max possible increment 242 | if (Allocator_CanInitCursors(heap->allocator)) { 243 | // increment = heap->memoryLimit - (heap->smallHeapSize + 244 | // heap->largeHeapSize); 245 | // round down to block size 246 | // increment = increment / BLOCK_TOTAL_SIZE * BLOCK_TOTAL_SIZE; 247 | return; 248 | } else { 249 | // If the cursors cannot be initialised and we cannot grow, throw 250 | // out of memory exception. 251 | Heap_exitWithOutOfMemory(); 252 | } 253 | } 254 | 255 | #ifdef DEBUG_PRINT 256 | printf("Growing small heap by %zu bytes, to %zu bytes\n", 257 | increment * WORD_SIZE, heap->smallHeapSize + increment * WORD_SIZE); 258 | fflush(stdout); 259 | #endif 260 | 261 | word_t *heapEnd = heap->heapEnd; 262 | heap->heapEnd = heapEnd + increment; 263 | heap->smallHeapSize += increment * WORD_SIZE; 264 | 265 | BlockHeader *lastBlock = (BlockHeader *)(heap->heapEnd - WORDS_IN_BLOCK); 266 | BlockList_AddBlocksLast(&heap->allocator->freeBlocks, 267 | (BlockHeader *)heapEnd, lastBlock); 268 | 269 | heap->allocator->blockCount += increment / WORDS_IN_BLOCK; 270 | heap->allocator->freeBlockCount += increment / WORDS_IN_BLOCK; 271 | } 272 | 273 | /** Grows the large heap by at least `increment` words */ 274 | void Heap_GrowLarge(Heap *heap, size_t increment) { 275 | increment = 1UL << MathUtils_Log2Ceil(increment); 276 | 277 | if (heap->smallHeapSize + heap->largeHeapSize + increment * WORD_SIZE > 278 | heap->memoryLimit) { 279 | Heap_exitWithOutOfMemory(); 280 | } 281 | #ifdef DEBUG_PRINT 282 | printf("Growing large heap by %zu bytes, to %zu bytes\n", 283 | increment * WORD_SIZE, heap->largeHeapSize + increment * WORD_SIZE); 284 | fflush(stdout); 285 | #endif 286 | 287 | word_t *heapEnd = heap->largeHeapEnd; 288 | heap->largeHeapEnd += increment; 289 | heap->largeHeapSize += increment * WORD_SIZE; 290 | heap->largeAllocator->size += increment * WORD_SIZE; 291 | 292 | Bitmap_Grow(heap->largeAllocator->bitmap, increment * WORD_SIZE); 293 | 294 | LargeAllocator_AddChunk(heap->largeAllocator, (Chunk *)heapEnd, 295 | increment * WORD_SIZE); 296 | } 297 | -------------------------------------------------------------------------------- /src/Heap.h: -------------------------------------------------------------------------------- 1 | #ifndef IMMIX_HEAP_H 2 | #define IMMIX_HEAP_H 3 | 4 | #include "GCTypes.h" 5 | #include "Allocator.h" 6 | #include "LargeAllocator.h" 7 | #include "datastructures/Stack.h" 8 | 9 | typedef struct { 10 | size_t memoryLimit; 11 | word_t *heapStart; 12 | word_t *heapEnd; 13 | size_t smallHeapSize; 14 | word_t *largeHeapStart; 15 | word_t *largeHeapEnd; 16 | size_t largeHeapSize; 17 | Allocator *allocator; 18 | LargeAllocator *largeAllocator; 19 | } Heap; 20 | 21 | static inline bool Heap_IsWordInLargeHeap(Heap *heap, word_t *word) { 22 | return word != NULL && word >= heap->largeHeapStart && 23 | word < heap->largeHeapEnd; 24 | } 25 | 26 | static inline bool Heap_IsWordInSmallHeap(Heap *heap, word_t *word) { 27 | return word != NULL && word >= heap->heapStart && word < heap->heapEnd; 28 | } 29 | 30 | static inline bool Heap_IsWordInHeap(Heap *heap, word_t *word) { 31 | return Heap_IsWordInSmallHeap(heap, word) || 32 | Heap_IsWordInLargeHeap(heap, word); 33 | } 34 | static inline bool heap_isObjectInHeap(Heap *heap, Object *object) { 35 | return Heap_IsWordInHeap(heap, (word_t *)object); 36 | } 37 | 38 | Heap *Heap_Create(size_t initialHeapSize); 39 | word_t *Heap_Alloc(Heap *heap, uint32_t objectSize, bool isObjectArray); 40 | word_t *Heap_AllocSmall(Heap *heap, uint32_t objectSize, bool isObjectArray); 41 | word_t *Heap_AllocLarge(Heap *heap, uint32_t objectSize, bool isObjectArray); 42 | 43 | void Heap_Collect(Heap *heap, Stack *stack); 44 | 45 | void Heap_Recycle(Heap *heap); 46 | void Heap_Grow(Heap *heap, size_t increment); 47 | void Heap_GrowLarge(Heap *heap, size_t increment); 48 | 49 | #endif // IMMIX_HEAP_H 50 | -------------------------------------------------------------------------------- /src/ImmixGC.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "GCTypes.h" 5 | #include "Heap.h" 6 | #include "datastructures/Stack.h" 7 | #include "Marker.h" 8 | #include "Log.h" 9 | #include "Object.h" 10 | #include "State.h" 11 | #include "utils/MathUtils.h" 12 | 13 | #define INITIAL_HEAP_SIZE (1024 * 1024UL) 14 | 15 | void scalanative_collect(); 16 | 17 | void scalanative_init() { 18 | heap = Heap_Create(INITIAL_HEAP_SIZE); 19 | stack = Stack_Alloc(INITIAL_STACK_SIZE); 20 | } 21 | 22 | void *scalanative_alloc(void *info, size_t size, int isObjectArray) { 23 | size = MathUtils_RoundToNextMultiple(size, WORD_SIZE); 24 | 25 | void **alloc = (void **)Heap_Alloc(heap, size, isObjectArray ? true : false); 26 | *alloc = info; 27 | return (void *)alloc; 28 | } 29 | 30 | void *scalanative_alloc_small(void *info, size_t size) { 31 | size = MathUtils_RoundToNextMultiple(size, WORD_SIZE); 32 | 33 | void **alloc = (void **)Heap_AllocSmall(heap, size, false); 34 | *alloc = info; 35 | return (void *)alloc; 36 | } 37 | 38 | void *scalanative_alloc_large(void *info, size_t size) { 39 | size = MathUtils_RoundToNextMultiple(size, WORD_SIZE); 40 | 41 | void **alloc = (void **)Heap_AllocLarge(heap, size, false); 42 | *alloc = info; 43 | return (void *)alloc; 44 | } 45 | 46 | void *scalanative_alloc_atomic(void *info, size_t size) { 47 | return scalanative_alloc(info, size, false); 48 | } 49 | 50 | void scalanative_collect() { Heap_Collect(heap, stack); } 51 | -------------------------------------------------------------------------------- /src/LargeAllocator.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "LargeAllocator.h" 5 | #include "utils/MathUtils.h" 6 | #include "Object.h" 7 | #include "Log.h" 8 | #include "headers/ObjectHeader.h" 9 | 10 | inline static int LargeAllocator_sizeToLinkedListIndex(size_t size) { 11 | assert(size >= MIN_BLOCK_SIZE); 12 | return log2_floor(size) - LARGE_OBJECT_MIN_SIZE_BITS; 13 | } 14 | 15 | static inline size_t LargeAllocator_getChunkSize(Chunk *chunk) { 16 | return (chunk->header.size << WORD_SIZE_BITS); 17 | } 18 | 19 | static inline void LargeAllocator_setChunkSize(Chunk *chunk, size_t size) { 20 | chunk->header.size = (uint32_t)(size >> WORD_SIZE_BITS); 21 | } 22 | 23 | Chunk *LargeAllocator_chunkAddOffset(Chunk *chunk, size_t words) { 24 | return (Chunk *)((ubyte_t *)chunk + words); 25 | } 26 | 27 | void LargeAllocator_printFreeList(FreeList *list, int i) { 28 | Chunk *current = list->first; 29 | printf("list %d: ", i); 30 | while (current != NULL) { 31 | assert((1 << (i + LARGE_OBJECT_MIN_SIZE_BITS)) == 32 | LargeAllocator_getChunkSize(current)); 33 | printf("[%p %zu] -> ", current, LargeAllocator_getChunkSize(current)); 34 | current = current->next; 35 | } 36 | printf("\n"); 37 | } 38 | 39 | void LargeAllocator_freeListAddBlockLast(FreeList *freeList, Chunk *chunk) { 40 | if (freeList->first == NULL) { 41 | freeList->first = chunk; 42 | } 43 | freeList->last = chunk; 44 | chunk->next = NULL; 45 | } 46 | 47 | Chunk *LargeAllocator_freeListRemoveFirstBlock(FreeList *freeList) { 48 | if (freeList->first == NULL) { 49 | return NULL; 50 | } 51 | Chunk *chunk = freeList->first; 52 | if (freeList->first == freeList->last) { 53 | freeList->last = NULL; 54 | } 55 | 56 | freeList->first = chunk->next; 57 | return chunk; 58 | } 59 | 60 | void LargeAllocator_freeListInit(FreeList *freeList) { 61 | freeList->first = NULL; 62 | freeList->last = NULL; 63 | } 64 | 65 | LargeAllocator *LargeAllocator_Create(word_t *offset, size_t size) { 66 | LargeAllocator *allocator = malloc(sizeof(LargeAllocator)); 67 | allocator->offset = offset; 68 | allocator->size = size; 69 | allocator->bitmap = Bitmap_Alloc(size, offset); 70 | 71 | for (int i = 0; i < FREE_LIST_COUNT; i++) { 72 | LargeAllocator_freeListInit(&allocator->freeLists[i]); 73 | } 74 | 75 | LargeAllocator_AddChunk(allocator, (Chunk *)offset, size); 76 | 77 | return allocator; 78 | } 79 | 80 | void LargeAllocator_AddChunk(LargeAllocator *allocator, Chunk *chunk, 81 | size_t total_block_size) { 82 | assert(total_block_size >= MIN_BLOCK_SIZE); 83 | assert(total_block_size % MIN_BLOCK_SIZE == 0); 84 | 85 | size_t remaining_size = total_block_size; 86 | ubyte_t *current = (ubyte_t *)chunk; 87 | while (remaining_size > 0) { 88 | int log2_f = log2_floor(remaining_size); 89 | size_t chunkSize = 1UL << log2_f; 90 | chunkSize = chunkSize > MAX_BLOCK_SIZE ? MAX_BLOCK_SIZE : chunkSize; 91 | assert(chunkSize >= MIN_BLOCK_SIZE && chunkSize <= MAX_BLOCK_SIZE); 92 | int listIndex = LargeAllocator_sizeToLinkedListIndex(chunkSize); 93 | 94 | Chunk *currentChunk = (Chunk *)current; 95 | LargeAllocator_freeListAddBlockLast(&allocator->freeLists[listIndex], 96 | (Chunk *)current); 97 | LargeAllocator_setChunkSize(currentChunk, chunkSize); 98 | currentChunk->header.type = object_large; 99 | Object_SetFree(&((Object *)currentChunk)->header); 100 | Bitmap_SetBit(allocator->bitmap, current); 101 | 102 | current += chunkSize; 103 | remaining_size -= chunkSize; 104 | } 105 | } 106 | 107 | Object *LargeAllocator_GetBlock(LargeAllocator *allocator, 108 | size_t requestedBlockSize) { 109 | size_t actualBlockSize = 110 | MathUtils_RoundToNextMultiple(requestedBlockSize, MIN_BLOCK_SIZE); 111 | size_t requiredChunkSize = 1UL << MathUtils_Log2Ceil(actualBlockSize); 112 | 113 | int listIndex = LargeAllocator_sizeToLinkedListIndex(requiredChunkSize); 114 | Chunk *chunk = NULL; 115 | while (listIndex <= FREE_LIST_COUNT - 1 && 116 | (chunk = allocator->freeLists[listIndex].first) == NULL) { 117 | ++listIndex; 118 | } 119 | 120 | if (chunk == NULL) { 121 | return NULL; 122 | } 123 | 124 | size_t chunkSize = LargeAllocator_getChunkSize(chunk); 125 | assert(chunkSize >= MIN_BLOCK_SIZE); 126 | 127 | if (chunkSize - MIN_BLOCK_SIZE >= actualBlockSize) { 128 | Chunk *remainingChunk = 129 | LargeAllocator_chunkAddOffset(chunk, actualBlockSize); 130 | LargeAllocator_freeListRemoveFirstBlock( 131 | &allocator->freeLists[listIndex]); 132 | size_t remainingChunkSize = chunkSize - actualBlockSize; 133 | LargeAllocator_AddChunk(allocator, remainingChunk, remainingChunkSize); 134 | } else { 135 | LargeAllocator_freeListRemoveFirstBlock( 136 | &allocator->freeLists[listIndex]); 137 | } 138 | 139 | Bitmap_SetBit(allocator->bitmap, (ubyte_t *)chunk); 140 | Object *object = (Object *)chunk; 141 | Object_SetAllocated(&object->header); 142 | memset(Object_ToMutatorAddress(object), 0, actualBlockSize - WORD_SIZE); 143 | return object; 144 | } 145 | 146 | void LargeAllocator_Print(LargeAllocator *alloc) { 147 | for (int i = 0; i < FREE_LIST_COUNT; i++) { 148 | if (alloc->freeLists[i].first != NULL) { 149 | 150 | LargeAllocator_printFreeList(&alloc->freeLists[i], i); 151 | } 152 | } 153 | } 154 | 155 | void LargeAllocator_clearFreeLists(LargeAllocator *allocator) { 156 | for (int i = 0; i < FREE_LIST_COUNT; i++) { 157 | allocator->freeLists[i].first = NULL; 158 | allocator->freeLists[i].last = NULL; 159 | } 160 | } 161 | 162 | void LargeAllocator_Sweep(LargeAllocator *allocator) { 163 | LargeAllocator_clearFreeLists(allocator); 164 | 165 | Object *current = (Object *)allocator->offset; 166 | void *heapEnd = (ubyte_t *)allocator->offset + allocator->size; 167 | 168 | while (current != heapEnd) { 169 | assert(Bitmap_GetBit(allocator->bitmap, (ubyte_t *)current)); 170 | ObjectHeader *currentHeader = ¤t->header; 171 | if (Object_IsMarked(currentHeader)) { 172 | Object_SetAllocated(currentHeader); 173 | 174 | current = Object_NextLargeObject(current); 175 | } else { 176 | size_t currentSize = Object_ChunkSize(current); 177 | Object *next = Object_NextLargeObject(current); 178 | while (next != heapEnd && !Object_IsMarked(&next->header)) { 179 | currentSize += Object_ChunkSize(next); 180 | Bitmap_ClearBit(allocator->bitmap, (ubyte_t *)next); 181 | next = Object_NextLargeObject(next); 182 | } 183 | LargeAllocator_AddChunk(allocator, (Chunk *)current, currentSize); 184 | current = next; 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/LargeAllocator.h: -------------------------------------------------------------------------------- 1 | #ifndef IMMIX_LARGEALLOCATOR_H 2 | #define IMMIX_LARGEALLOCATOR_H 3 | 4 | #include "datastructures/Bitmap.h" 5 | #include "GCTypes.h" 6 | #include "Constants.h" 7 | #include "headers/ObjectHeader.h" 8 | 9 | #define FREE_LIST_COUNT \ 10 | (LARGE_OBJECT_MAX_SIZE_BITS - LARGE_OBJECT_MIN_SIZE_BITS + 1) 11 | 12 | typedef struct Chunk Chunk; 13 | 14 | struct Chunk { 15 | ObjectHeader header; 16 | Chunk *next; 17 | }; 18 | 19 | typedef struct { 20 | Chunk *first; 21 | Chunk *last; 22 | } FreeList; 23 | 24 | typedef struct { 25 | word_t *offset; 26 | size_t size; 27 | FreeList freeLists[FREE_LIST_COUNT]; 28 | Bitmap *bitmap; 29 | } LargeAllocator; 30 | 31 | LargeAllocator *LargeAllocator_Create(word_t *offset, size_t largeHeapSize); 32 | void LargeAllocator_AddChunk(LargeAllocator *allocator, Chunk *chunk, 33 | size_t total_block_size); 34 | Object *LargeAllocator_GetBlock(LargeAllocator *allocator, 35 | size_t requestedBlockSize); 36 | void LargeAllocator_Sweep(LargeAllocator *allocator); 37 | void LargeAllocator_Print(LargeAllocator *alloc); 38 | 39 | #endif // IMMIX_LARGEALLOCATOR_H 40 | -------------------------------------------------------------------------------- /src/Line.h: -------------------------------------------------------------------------------- 1 | #ifndef IMMIX_LINE_H 2 | #define IMMIX_LINE_H 3 | 4 | #include "headers/ObjectHeader.h" 5 | #include "headers/LineHeader.h" 6 | #include "headers/BlockHeader.h" 7 | 8 | static INLINE Object *Line_GetFirstObject(LineHeader *lineHeader) { 9 | assert(Line_ContainsObject(lineHeader)); 10 | BlockHeader *blockHeader = Block_BlockHeaderFromLineHeader(lineHeader); 11 | uint8_t offset = Line_GetFirstObjectOffset(lineHeader); 12 | 13 | uint32_t lineIndex = 14 | Block_GetLineIndexFromLineHeader(blockHeader, lineHeader); 15 | 16 | return (Object *)Block_GetLineWord(blockHeader, lineIndex, 17 | offset / WORD_SIZE); 18 | } 19 | 20 | static INLINE void Line_Update(BlockHeader *blockHeader, word_t *objectStart) { 21 | 22 | int lineIndex = Block_GetLineIndexFromWord(blockHeader, objectStart); 23 | LineHeader *lineHeader = Block_GetLineHeader(blockHeader, lineIndex); 24 | 25 | if (!Line_ContainsObject(lineHeader)) { 26 | uint8_t offset = (uint8_t)((word_t)objectStart & LINE_SIZE_MASK); 27 | 28 | Line_SetOffset(lineHeader, offset); 29 | } 30 | } 31 | 32 | #endif // IMMIX_LINE_H 33 | -------------------------------------------------------------------------------- /src/Log.h: -------------------------------------------------------------------------------- 1 | #ifndef IMMIX_LOG_H 2 | #define IMMIX_LOG_H 3 | 4 | #define NDEBUG 5 | #include 6 | 7 | //#define PRINT_STACK_OVERFLOW 8 | //#define DEBUG_PRINT 9 | 10 | #endif // IMMIX_LOG_H 11 | -------------------------------------------------------------------------------- /src/Marker.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "Marker.h" 4 | #include "Object.h" 5 | #include "Log.h" 6 | #include "State.h" 7 | #include "datastructures/Stack.h" 8 | #include "headers/ObjectHeader.h" 9 | #include "Block.h" 10 | #include "StackoverflowHandler.h" 11 | 12 | extern word_t *__modules; 13 | extern int __modules_size; 14 | extern word_t **__stack_bottom; 15 | 16 | #define LAST_FIELD_OFFSET -1 17 | 18 | void Marker_Mark(Heap *heap, Stack *stack); 19 | void StackOverflowHandler_largeHeapOverflowHeapScan(Heap *heap, Stack *stack); 20 | bool StackOverflowHandler_smallHeapOverflowHeapScan(Heap *heap, Stack *stack); 21 | 22 | void Marker_markObject(Heap *heap, Stack *stack, Object *object) { 23 | assert(!Object_IsMarked(&object->header)); 24 | assert(Object_Size(&object->header) != 0); 25 | Object_Mark(object); 26 | if (!overflow) { 27 | overflow = Stack_Push(stack, object); 28 | } 29 | } 30 | 31 | void Marker_markConservative(Heap *heap, Stack *stack, word_t *address) { 32 | assert(Heap_IsWordInHeap(heap, address)); 33 | Object *object = NULL; 34 | if (Heap_IsWordInSmallHeap(heap, address)) { 35 | object = Object_GetObject(address); 36 | assert( 37 | object == NULL || 38 | Line_ContainsObject(&Block_GetBlockHeader((word_t *)object) 39 | ->lineHeaders[Block_GetLineIndexFromWord( 40 | Block_GetBlockHeader((word_t *)object), 41 | (word_t *)object)])); 42 | #ifdef DEBUG_PRINT 43 | if (object == NULL) { 44 | printf("Not found: %p\n", address); 45 | } 46 | #endif 47 | } else { 48 | object = Object_GetLargeObject(heap->largeAllocator, address); 49 | } 50 | 51 | if (object != NULL && !Object_IsMarked(&object->header)) { 52 | Marker_markObject(heap, stack, object); 53 | } 54 | } 55 | 56 | void Marker_Mark(Heap *heap, Stack *stack) { 57 | while (!Stack_IsEmpty(stack)) { 58 | Object *object = Stack_Pop(stack); 59 | ObjectHeader *objectHeader = &object->header; 60 | if (Object_IsObjectArray(objectHeader)) { 61 | // remove header and rtti from size 62 | size_t size = 63 | Object_Size(&object->header) - OBJECT_HEADER_SIZE - WORD_SIZE; 64 | size_t nbWords = size / WORD_SIZE; 65 | for (int i = 0; i < nbWords; i++) { 66 | 67 | word_t *field = object->fields[i]; 68 | Object *fieldObject = Object_FromMutatorAddress(field); 69 | if (heap_isObjectInHeap(heap, fieldObject) && 70 | !Object_IsMarked(&fieldObject->header)) { 71 | Marker_markObject(heap, stack, fieldObject); 72 | } 73 | } 74 | } else { 75 | int64_t *ptr_map = object->rtti->refMapStruct; 76 | int i = 0; 77 | while (ptr_map[i] != LAST_FIELD_OFFSET) { 78 | word_t *field = object->fields[ptr_map[i]]; 79 | Object *fieldObject = Object_FromMutatorAddress(field); 80 | if (heap_isObjectInHeap(heap, fieldObject) && 81 | !Object_IsMarked(&fieldObject->header)) { 82 | Marker_markObject(heap, stack, fieldObject); 83 | } 84 | ++i; 85 | } 86 | } 87 | } 88 | StackOverflowHandler_CheckForOverflow(); 89 | } 90 | 91 | void Marker_markProgramStack(Heap *heap, Stack *stack) { 92 | // Dumps registers into 'regs' which is on stack 93 | jmp_buf regs; 94 | setjmp(regs); 95 | word_t *dummy; 96 | 97 | word_t **current = &dummy; 98 | word_t **stackBottom = __stack_bottom; 99 | 100 | while (current <= stackBottom) { 101 | 102 | word_t *stackObject = (*current) - WORDS_IN_OBJECT_HEADER; 103 | if (Heap_IsWordInHeap(heap, stackObject)) { 104 | Marker_markConservative(heap, stack, stackObject); 105 | } 106 | current += 1; 107 | } 108 | } 109 | 110 | void Marker_markModules(Heap *heap, Stack *stack) { 111 | word_t **modules = &__modules; 112 | int nb_modules = __modules_size; 113 | 114 | for (int i = 0; i < nb_modules; i++) { 115 | Object *object = Object_FromMutatorAddress(modules[i]); 116 | if (heap_isObjectInHeap(heap, object) && 117 | !Object_IsMarked(&object->header)) { 118 | Marker_markObject(heap, stack, object); 119 | } 120 | } 121 | } 122 | 123 | void Marker_MarkRoots(Heap *heap, Stack *stack) { 124 | 125 | Marker_markProgramStack(heap, stack); 126 | 127 | Marker_markModules(heap, stack); 128 | 129 | Marker_Mark(heap, stack); 130 | } 131 | -------------------------------------------------------------------------------- /src/Marker.h: -------------------------------------------------------------------------------- 1 | #ifndef IMMIX_MARKER_H 2 | #define IMMIX_MARKER_H 3 | 4 | #include "Heap.h" 5 | #include "datastructures/Stack.h" 6 | 7 | void Marker_MarkRoots(Heap *heap, Stack *stack); 8 | void Marker_Mark(Heap *heap, Stack *stack); 9 | 10 | #endif // IMMIX_MARKER_H 11 | -------------------------------------------------------------------------------- /src/Memory.h: -------------------------------------------------------------------------------- 1 | #ifndef IMMIX_MEMORY_H 2 | #define IMMIX_MEMORY_H 3 | 4 | /* 5 | * Author: David Robert Nadeau 6 | * Site: http://NadeauSoftware.com/ 7 | * License: Creative Commons Attribution 3.0 Unported License 8 | * http://creativecommons.org/licenses/by/3.0/deed.en_US 9 | */ 10 | 11 | #if defined(_WIN32) 12 | #include 13 | 14 | #elif defined(__unix__) || defined(__unix) || defined(unix) || \ 15 | (defined(__APPLE__) && defined(__MACH__)) 16 | #include 17 | #include 18 | #include 19 | #if defined(BSD) 20 | #include 21 | #endif 22 | 23 | #else 24 | #error "Unable to define getMemorySize( ) for an unknown OS." 25 | #endif 26 | 27 | /** 28 | * Returns the size of physical memory (RAM) in bytes. 29 | */ 30 | size_t getMemorySize() { 31 | #if defined(_WIN32) && (defined(__CYGWIN__) || defined(__CYGWIN32__)) 32 | /* Cygwin under Windows. ------------------------------------ */ 33 | /* New 64-bit MEMORYSTATUSEX isn't available. Use old 32.bit */ 34 | MEMORYSTATUS status; 35 | status.dwLength = sizeof(status); 36 | GlobalMemoryStatus(&status); 37 | return (size_t)status.dwTotalPhys; 38 | 39 | #elif defined(_WIN32) 40 | /* Windows. ------------------------------------------------- */ 41 | /* Use new 64-bit MEMORYSTATUSEX, not old 32-bit MEMORYSTATUS */ 42 | MEMORYSTATUSEX status; 43 | status.dwLength = sizeof(status); 44 | GlobalMemoryStatusEx(&status); 45 | return (size_t)status.ullTotalPhys; 46 | 47 | #elif defined(__unix__) || defined(__unix) || defined(unix) || \ 48 | (defined(__APPLE__) && defined(__MACH__)) 49 | /* UNIX variants. ------------------------------------------- */ 50 | /* Prefer sysctl() over sysconf() except sysctl() HW_REALMEM and HW_PHYSMEM */ 51 | 52 | #if defined(CTL_HW) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM64)) 53 | int mib[2]; 54 | mib[0] = CTL_HW; 55 | #if defined(HW_MEMSIZE) 56 | mib[1] = HW_MEMSIZE; /* OSX. --------------------- */ 57 | #elif defined(HW_PHYSMEM64) 58 | mib[1] = HW_PHYSMEM64; /* NetBSD, OpenBSD. --------- */ 59 | #endif 60 | int64_t size = 0; /* 64-bit */ 61 | size_t len = sizeof(size); 62 | if (sysctl(mib, 2, &size, &len, NULL, 0) == 0) 63 | return (size_t)size; 64 | return 0L; /* Failed? */ 65 | 66 | #elif defined(_SC_AIX_REALMEM) 67 | /* AIX. ----------------------------------------------------- */ 68 | return (size_t)sysconf(_SC_AIX_REALMEM) * (size_t)1024L; 69 | 70 | #elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE) 71 | /* FreeBSD, Linux, OpenBSD, and Solaris. -------------------- */ 72 | return (size_t)sysconf(_SC_PHYS_PAGES) * (size_t)sysconf(_SC_PAGESIZE); 73 | 74 | #elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGE_SIZE) 75 | /* Legacy. -------------------------------------------------- */ 76 | return (size_t)sysconf(_SC_PHYS_PAGES) * (size_t)sysconf(_SC_PAGE_SIZE); 77 | 78 | #elif defined(CTL_HW) && (defined(HW_PHYSMEM) || defined(HW_REALMEM)) 79 | /* DragonFly BSD, FreeBSD, NetBSD, OpenBSD, and OSX. -------- */ 80 | int mib[2]; 81 | mib[0] = CTL_HW; 82 | #if defined(HW_REALMEM) 83 | mib[1] = HW_REALMEM; /* FreeBSD. ----------------- */ 84 | #elif defined(HW_PYSMEM) 85 | mib[1] = HW_PHYSMEM; /* Others. ------------------ */ 86 | #endif 87 | unsigned int size = 0; /* 32-bit */ 88 | size_t len = sizeof(size); 89 | if (sysctl(mib, 2, &size, &len, NULL, 0) == 0) 90 | return (size_t)size; 91 | return 0L; /* Failed? */ 92 | #endif /* sysctl and sysconf variants */ 93 | 94 | #else 95 | return 0L; /* Unknown OS. */ 96 | #endif 97 | } 98 | 99 | #endif // IMMIX_MEMORY_H 100 | -------------------------------------------------------------------------------- /src/Object.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "Object.h" 4 | #include "headers/BlockHeader.h" 5 | #include "Line.h" 6 | #include "Log.h" 7 | #include "utils/MathUtils.h" 8 | 9 | Object *Object_NextLargeObject(Object *object) { 10 | size_t size = Object_ChunkSize(object); 11 | assert(size != 0); 12 | return (Object *)((ubyte_t *)object + size); 13 | } 14 | 15 | Object *Object_NextObject(Object *object) { 16 | size_t size = Object_Size(&object->header); 17 | assert(size < LARGE_BLOCK_SIZE); 18 | if (size == 0) { 19 | return NULL; 20 | } 21 | Object *next = (Object *)((ubyte_t *)object + size); 22 | assert(Block_GetBlockHeader((word_t *)next) == 23 | Block_GetBlockHeader((word_t *)object) || 24 | (ubyte_t *)Block_GetBlockHeader((word_t *)next) == 25 | (ubyte_t *)Block_GetBlockHeader((word_t *)object) + 26 | BLOCK_TOTAL_SIZE); 27 | return next; 28 | } 29 | 30 | static inline bool isWordAligned(word_t *word) { 31 | return ((word_t)word & WORD_INVERSE_MASK) == (word_t)word; 32 | } 33 | 34 | Object *Object_getInLine(BlockHeader *blockHeader, int lineIndex, 35 | word_t *word) { 36 | assert(Line_ContainsObject(Block_GetLineHeader(blockHeader, lineIndex))); 37 | 38 | Object *current = 39 | Line_GetFirstObject(Block_GetLineHeader(blockHeader, lineIndex)); 40 | Object *next = Object_NextObject(current); 41 | 42 | word_t *lineEnd = 43 | Block_GetLineAddress(blockHeader, lineIndex) + WORDS_IN_LINE; 44 | 45 | while (next != NULL && (word_t *)next < lineEnd && (word_t *)next <= word) { 46 | current = next; 47 | next = Object_NextObject(next); 48 | } 49 | 50 | if (Object_IsAllocated(¤t->header) && word >= (word_t *)current && 51 | word < (word_t *)next) { 52 | #ifdef DEBUG_PRINT 53 | if ((word_t *)current != word) { 54 | printf("inner pointer: %p object: %p\n", word, current); 55 | fflush(stdout); 56 | } 57 | #endif 58 | return current; 59 | } else { 60 | #ifdef DEBUG_PRINT 61 | printf("ignoring %p\n", word); 62 | fflush(stdout); 63 | #endif 64 | return NULL; 65 | } 66 | } 67 | 68 | Object *Object_GetObject(word_t *word) { 69 | BlockHeader *blockHeader = Block_GetBlockHeader(word); 70 | 71 | // Check if the word points on the block header 72 | if (word < Block_GetFirstWord(blockHeader)) { 73 | #ifdef DEBUG_PRINT 74 | printf("Points on block header %p\n", word); 75 | fflush(stdout); 76 | #endif 77 | return NULL; 78 | } 79 | 80 | if (!isWordAligned(word)) { 81 | #ifdef DEBUG_PRINT 82 | printf("Word not aligned: %p aligning to %p\n", word, 83 | (word_t *)((word_t)word & WORD_INVERSE_MASK)); 84 | fflush(stdout); 85 | #endif 86 | word = (word_t *)((word_t)word & WORD_INVERSE_MASK); 87 | } 88 | 89 | int lineIndex = Block_GetLineIndexFromWord(blockHeader, word); 90 | while (lineIndex > 0 && 91 | !Line_ContainsObject(Block_GetLineHeader(blockHeader, lineIndex))) { 92 | lineIndex--; 93 | } 94 | 95 | if (Line_ContainsObject(Block_GetLineHeader(blockHeader, lineIndex))) { 96 | return Object_getInLine(blockHeader, lineIndex, word); 97 | } else { 98 | #ifdef DEBUG_PRINT 99 | printf("Word points to empty line %p\n", word); 100 | fflush(stdout); 101 | #endif 102 | return NULL; 103 | } 104 | } 105 | 106 | Object *Object_getLargeInnerPointer(LargeAllocator *allocator, word_t *word) { 107 | word_t *current = (word_t *)((word_t)word & LARGE_BLOCK_MASK); 108 | 109 | while (!Bitmap_GetBit(allocator->bitmap, (ubyte_t *)current)) { 110 | current -= LARGE_BLOCK_SIZE / WORD_SIZE; 111 | } 112 | 113 | Object *object = (Object *)current; 114 | if (word < (word_t *)object + Object_ChunkSize(object) / WORD_SIZE && 115 | object->rtti != NULL) { 116 | #ifdef DEBUG_PRINT 117 | printf("large inner pointer: %p, object: %p\n", word, objectHeader); 118 | fflush(stdout); 119 | #endif 120 | return object; 121 | } else { 122 | 123 | return NULL; 124 | } 125 | } 126 | 127 | Object *Object_GetLargeObject(LargeAllocator *allocator, word_t *word) { 128 | if (((word_t)word & LARGE_BLOCK_MASK) != (word_t)word) { 129 | word = (word_t *)((word_t)word & LARGE_BLOCK_MASK); 130 | } 131 | if (Bitmap_GetBit(allocator->bitmap, (ubyte_t *)word) && 132 | Object_IsAllocated(&((Object *)word)->header)) { 133 | return (Object *)word; 134 | } else { 135 | Object *object = Object_getLargeInnerPointer(allocator, word); 136 | assert(object == NULL || 137 | (word >= (word_t *)object && 138 | word < (word_t *)Object_NextLargeObject(object))); 139 | return object; 140 | } 141 | } 142 | 143 | void Object_Mark(Object *object) { 144 | // Mark the object itself 145 | Object_MarkObjectHeader(&object->header); 146 | 147 | if (!Object_IsLargeObject(&object->header)) { 148 | // Mark the block 149 | BlockHeader *blockHeader = Block_GetBlockHeader((word_t *)object); 150 | Block_Mark(blockHeader); 151 | 152 | // Mark all Lines 153 | int startIndex = 154 | Block_GetLineIndexFromWord(blockHeader, (word_t *)object); 155 | word_t *lastWord = (word_t *)Object_NextObject(object) - 1; 156 | int endIndex = Block_GetLineIndexFromWord(blockHeader, lastWord); 157 | assert(startIndex >= 0 && startIndex < LINE_COUNT); 158 | assert(endIndex >= 0 && endIndex < LINE_COUNT); 159 | assert(startIndex <= endIndex); 160 | for (int i = startIndex; i <= endIndex; i++) { 161 | LineHeader *lineHeader = Block_GetLineHeader(blockHeader, i); 162 | Line_Mark(lineHeader); 163 | } 164 | } 165 | } 166 | 167 | size_t Object_ChunkSize(Object *object) { 168 | return MathUtils_RoundToNextMultiple(Object_Size(&object->header), 169 | MIN_BLOCK_SIZE); 170 | } 171 | -------------------------------------------------------------------------------- /src/Object.h: -------------------------------------------------------------------------------- 1 | #ifndef IMMIX_OBJECT_H 2 | #define IMMIX_OBJECT_H 3 | 4 | #include "headers/ObjectHeader.h" 5 | #include "LargeAllocator.h" 6 | 7 | Object *Object_NextLargeObject(Object *objectHeader); 8 | Object *Object_NextObject(Object *objectHeader); 9 | Object *Object_GetObject(word_t *address); 10 | Object *Object_GetLargeObject(LargeAllocator *largeAllocator, word_t *address); 11 | void Object_Mark(Object *objectHeader); 12 | size_t Object_ChunkSize(Object *objectHeader); 13 | 14 | #endif // IMMIX_OBJECT_H 15 | -------------------------------------------------------------------------------- /src/StackTrace.h: -------------------------------------------------------------------------------- 1 | #ifndef IMMIX_STACKTRACE_H 2 | #define IMMIX_STACKTRACE_H 3 | 4 | #include 5 | #include 6 | 7 | void StackTrace_PrintStackTrace() { 8 | unw_cursor_t cursor; 9 | unw_context_t context; 10 | unw_getcontext(&context); 11 | unw_init_local(&cursor, &context); 12 | 13 | while (unw_step(&cursor) > 0) { 14 | unw_word_t offset, pc; 15 | unw_get_reg(&cursor, UNW_REG_IP, &pc); 16 | if (pc == 0) { 17 | break; 18 | } 19 | 20 | char sym[256]; 21 | if (unw_get_proc_name(&cursor, sym, sizeof(sym), &offset) == 0) { 22 | printf("\tat %s\n", sym); 23 | } 24 | } 25 | } 26 | 27 | #endif // IMMIX_STACKTRACE_H 28 | -------------------------------------------------------------------------------- /src/StackoverflowHandler.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "StackoverflowHandler.h" 3 | #include "State.h" 4 | #include "Block.h" 5 | #include "Object.h" 6 | #include "Marker.h" 7 | 8 | extern int __object_array_id; 9 | 10 | #define LAST_FIELD_OFFSET -1 11 | 12 | bool StackOverflowHandler_smallHeapOverflowHeapScan(Heap *heap, Stack *stack); 13 | void StackOverflowHandler_largeHeapOverflowHeapScan(Heap *heap, Stack *stack); 14 | bool StackOverflowHandler_overflowBlockScan(BlockHeader *block, Heap *heap, 15 | Stack *stack, 16 | word_t **currentOverflowAddress); 17 | 18 | void StackOverflowHandler_CheckForOverflow() { 19 | if (overflow) { 20 | // Set overflow address to the first word of the heap 21 | currentOverflowAddress = heap->heapStart; 22 | overflow = false; 23 | Stack_DoubleSize(stack); 24 | 25 | #ifdef PRINT_STACK_OVERFLOW 26 | printf("Stack grew to %zu bytes\n", 27 | stack->nb_words * sizeof(Stack_Type)); 28 | fflush(stdout); 29 | #endif 30 | 31 | word_t *largeHeapEnd = heap->largeHeapEnd; 32 | // Continue while we don' hit the end of the large heap. 33 | while (currentOverflowAddress != largeHeapEnd) { 34 | 35 | // If the current overflow address is in the small heap, scan the 36 | // small heap. 37 | if (Heap_IsWordInSmallHeap(heap, currentOverflowAddress)) { 38 | // If no object was found in the small heap, move on to large 39 | // heap 40 | if (!StackOverflowHandler_smallHeapOverflowHeapScan(heap, 41 | stack)) { 42 | currentOverflowAddress = heap->largeHeapStart; 43 | } 44 | } else { 45 | StackOverflowHandler_largeHeapOverflowHeapScan(heap, stack); 46 | } 47 | 48 | // At every iteration when a object is found, trace it 49 | Marker_Mark(heap, stack); 50 | } 51 | } 52 | } 53 | 54 | bool StackOverflowHandler_smallHeapOverflowHeapScan(Heap *heap, Stack *stack) { 55 | assert(Heap_IsWordInSmallHeap(heap, currentOverflowAddress)); 56 | BlockHeader *currentBlock = Block_GetBlockHeader(currentOverflowAddress); 57 | word_t *heapEnd = heap->heapEnd; 58 | 59 | while ((word_t *)currentBlock != heapEnd) { 60 | if (StackOverflowHandler_overflowBlockScan(currentBlock, heap, stack, 61 | ¤tOverflowAddress)) { 62 | return true; 63 | } 64 | currentBlock = (BlockHeader *)((word_t *)currentBlock + WORDS_IN_BLOCK); 65 | currentOverflowAddress = (word_t *)currentBlock; 66 | } 67 | return false; 68 | } 69 | 70 | bool StackOverflowHandler_overflowMark(Heap *heap, Stack *stack, 71 | Object *object) { 72 | ObjectHeader *objectHeader = &object->header; 73 | if (Object_IsMarked(objectHeader)) { 74 | if (Object_IsObjectArray(objectHeader)) { 75 | size_t size = 76 | Object_Size(&object->header) - OBJECT_HEADER_SIZE - WORD_SIZE; 77 | size_t nbWords = size / WORD_SIZE; 78 | for (int i = 0; i < nbWords; i++) { 79 | word_t *field = object->fields[i]; 80 | Object *fieldObject = Object_FromMutatorAddress(field); 81 | if (heap_isObjectInHeap(heap, fieldObject) && 82 | !Object_IsMarked(&fieldObject->header)) { 83 | Stack_Push(stack, object); 84 | return true; 85 | } 86 | } 87 | } else { 88 | int64_t *ptr_map = object->rtti->refMapStruct; 89 | int i = 0; 90 | while (ptr_map[i] != LAST_FIELD_OFFSET) { 91 | word_t *field = object->fields[ptr_map[i]]; 92 | Object *fieldObject = Object_FromMutatorAddress(field); 93 | if (heap_isObjectInHeap(heap, fieldObject) && 94 | !Object_IsMarked(&fieldObject->header)) { 95 | Stack_Push(stack, object); 96 | return true; 97 | } 98 | ++i; 99 | } 100 | } 101 | } 102 | return false; 103 | } 104 | 105 | /** 106 | * Scans through the large heap to find marked blocks with unmarked children. 107 | * Updates `currentOverflowAddress` while doing so. 108 | */ 109 | void StackOverflowHandler_largeHeapOverflowHeapScan(Heap *heap, Stack *stack) { 110 | assert(Heap_IsWordInLargeHeap(heap, currentOverflowAddress)); 111 | void *heapEnd = heap->largeHeapEnd; 112 | 113 | while (currentOverflowAddress != heapEnd) { 114 | Object *object = (Object *)currentOverflowAddress; 115 | if (StackOverflowHandler_overflowMark(heap, stack, object)) { 116 | return; 117 | } 118 | currentOverflowAddress = (word_t *)Object_NextLargeObject(object); 119 | } 120 | } 121 | 122 | bool overflowScanLine(Heap *heap, Stack *stack, BlockHeader *block, 123 | int lineIndex) { 124 | LineHeader *lineHeader = Block_GetLineHeader(block, lineIndex); 125 | 126 | if (Line_IsMarked(lineHeader) && Line_ContainsObject(lineHeader)) { 127 | Object *object = Line_GetFirstObject(lineHeader); 128 | word_t *lineEnd = 129 | Block_GetLineAddress(block, lineIndex) + WORDS_IN_LINE; 130 | while (object != NULL && (word_t *)object < lineEnd) { 131 | if (StackOverflowHandler_overflowMark(heap, stack, object)) { 132 | return true; 133 | } 134 | object = Object_NextObject(object); 135 | } 136 | } 137 | return false; 138 | } 139 | 140 | /** 141 | * 142 | * This method is used in case of overflow during the marking phase. 143 | * It sweeps through the block starting at `currentOverflowAddress` until it 144 | * finds a marked block with unmarked children. 145 | * It updates the value of `currentOverflowAddress` while sweeping through the 146 | * block 147 | * Once an object is found it adds it to the stack and returns `true`. If no 148 | * object is found it returns `false`. 149 | * 150 | */ 151 | bool StackOverflowHandler_overflowBlockScan(BlockHeader *block, Heap *heap, 152 | Stack *stack, 153 | word_t **currentOverflowAddress) { 154 | word_t *blockEnd = Block_GetBlockEnd(block); 155 | if (!Block_IsMarked(block)) { 156 | *currentOverflowAddress = blockEnd; 157 | return false; 158 | } 159 | 160 | int lineIndex; 161 | 162 | if (*currentOverflowAddress == (word_t *)block) { 163 | lineIndex = 0; 164 | } else { 165 | lineIndex = Block_GetLineIndexFromWord(block, *currentOverflowAddress); 166 | } 167 | while (lineIndex < LINE_COUNT) { 168 | if (overflowScanLine(heap, stack, block, lineIndex)) { 169 | return true; 170 | } 171 | 172 | lineIndex++; 173 | } 174 | *currentOverflowAddress = blockEnd; 175 | return false; 176 | } -------------------------------------------------------------------------------- /src/StackoverflowHandler.h: -------------------------------------------------------------------------------- 1 | #ifndef IMMIX_STACKOVERFLOWHANDLER_H 2 | #define IMMIX_STACKOVERFLOWHANDLER_H 3 | 4 | void StackOverflowHandler_CheckForOverflow(); 5 | 6 | #endif // IMMIX_STACKOVERFLOWHANDLER_H 7 | -------------------------------------------------------------------------------- /src/State.c: -------------------------------------------------------------------------------- 1 | #include "State.h" 2 | 3 | Heap *heap = NULL; 4 | Stack *stack = NULL; 5 | 6 | // For stackoverflow handling 7 | bool overflow = false; 8 | word_t *currentOverflowAddress = NULL; -------------------------------------------------------------------------------- /src/State.h: -------------------------------------------------------------------------------- 1 | #ifndef IMMIX_STATE_H 2 | #define IMMIX_STATE_H 3 | 4 | #include "Heap.h" 5 | 6 | extern Heap *heap; 7 | extern Stack *stack; 8 | 9 | extern bool overflow; 10 | extern word_t *currentOverflowAddress; 11 | 12 | #endif // IMMIX_STATE_H 13 | -------------------------------------------------------------------------------- /src/datastructures/Bitmap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Bitmap.h" 3 | #include "../Constants.h" 4 | #include "../Log.h" 5 | #include "../utils/MathUtils.h" 6 | 7 | Bitmap *Bitmap_Alloc(size_t size, word_t *offset) { 8 | assert(size % BITMAP_GRANULARITY == 0); 9 | 10 | size_t nbBlocks = size / BITMAP_GRANULARITY; 11 | 12 | unsigned long nbWords = MathUtils_DivAndRoundUp(nbBlocks, BITS_PER_WORD); 13 | void *words = calloc(nbWords, WORD_SIZE); 14 | Bitmap *bitmap = malloc(sizeof(Bitmap)); 15 | bitmap->words = words; 16 | bitmap->size = size; 17 | bitmap->offset = (ubyte_t *)offset; 18 | return bitmap; 19 | } 20 | 21 | size_t addressToIndex(ubyte_t *offset, ubyte_t *addr) { 22 | return (addr - offset) / BITMAP_GRANULARITY; 23 | } 24 | 25 | void Bitmap_SetBit(Bitmap *bitmap, ubyte_t *addr) { 26 | assert(addr >= bitmap->offset && 27 | addr < bitmap->offset + bitmap->size * MIN_BLOCK_SIZE); 28 | size_t index = addressToIndex(bitmap->offset, addr); 29 | bitmap->words[WORD_OFFSET(index)] |= (1LLU << BIT_OFFSET(index)); 30 | } 31 | 32 | void Bitmap_ClearBit(Bitmap *bitmap, ubyte_t *addr) { 33 | assert(addr >= bitmap->offset && 34 | addr < bitmap->offset + bitmap->size * MIN_BLOCK_SIZE); 35 | 36 | size_t index = addressToIndex(bitmap->offset, addr); 37 | 38 | bitmap->words[WORD_OFFSET(index)] &= ~(1LLU << BIT_OFFSET(index)); 39 | } 40 | 41 | int Bitmap_GetBit(Bitmap *bitmap, ubyte_t *addr) { 42 | assert(addr >= bitmap->offset && 43 | addr < bitmap->offset + bitmap->size * MIN_BLOCK_SIZE); 44 | 45 | size_t index = addressToIndex(bitmap->offset, addr); 46 | word_t bit = 47 | bitmap->words[WORD_OFFSET(index)] & (1LLU << BIT_OFFSET(index)); 48 | return bit != 0; 49 | } 50 | 51 | // increment in bytes 52 | void Bitmap_Grow(Bitmap *bitmap, size_t increment) { 53 | assert(increment % BITMAP_GRANULARITY == 0); 54 | 55 | size_t nbBlocks = bitmap->size / BITMAP_GRANULARITY; 56 | size_t nbBlockIncrement = increment / BITMAP_GRANULARITY; 57 | 58 | size_t previousNbWords = MathUtils_DivAndRoundUp(nbBlocks, BITS_PER_WORD); 59 | 60 | size_t totalNbWords = 61 | MathUtils_DivAndRoundUp(nbBlocks + nbBlockIncrement, BITS_PER_WORD); 62 | 63 | bitmap->words = realloc(bitmap->words, totalNbWords * WORD_SIZE); 64 | bitmap->size += increment; 65 | 66 | memset(bitmap->words + previousNbWords, 0, 67 | (totalNbWords - previousNbWords) * WORD_SIZE); 68 | } -------------------------------------------------------------------------------- /src/datastructures/Bitmap.h: -------------------------------------------------------------------------------- 1 | #ifndef IMMIX_BITMAP_H 2 | #define IMMIX_BITMAP_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "../GCTypes.h" 8 | 9 | typedef struct { 10 | size_t size; 11 | word_t *words; 12 | ubyte_t *offset; 13 | } Bitmap; 14 | 15 | #define BITS_PER_WORD (sizeof(word_t) * CHAR_BIT) 16 | #define WORD_OFFSET(b) (b / BITS_PER_WORD) 17 | #define BIT_OFFSET(b) (b % BITS_PER_WORD) 18 | 19 | #define BITMAP_GRANULARITY MIN_BLOCK_SIZE 20 | 21 | Bitmap *Bitmap_Alloc(size_t size, word_t *offset); 22 | 23 | void Bitmap_SetBit(Bitmap *bitmap, ubyte_t *addr); 24 | 25 | void Bitmap_ClearBit(Bitmap *bitmap, ubyte_t *addr); 26 | 27 | int Bitmap_GetBit(Bitmap *bitmap, ubyte_t *addr); 28 | 29 | void Bitmap_Grow(Bitmap *bitmap, size_t nb_words); 30 | 31 | #endif // IMMIX_BITMAP_H 32 | -------------------------------------------------------------------------------- /src/datastructures/BlockList.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "BlockList.h" 4 | #include "../Log.h" 5 | #include "../headers/BlockHeader.h" 6 | 7 | int32_t _getBlockIndex(word_t *heapStart, BlockHeader *blockHeader) { 8 | return (uint32_t)((word_t *)blockHeader - heapStart) / WORDS_IN_BLOCK; 9 | } 10 | 11 | BlockHeader *_getBlockFromIndex(word_t *heapStart, int32_t index) { 12 | return (BlockHeader *)(heapStart + (index * WORDS_IN_BLOCK)); 13 | } 14 | 15 | BlockHeader *_getNextBlock(word_t *heapStart, BlockHeader *header) { 16 | int32_t nextBlockId = header->header.nextBlock; 17 | if (nextBlockId == LAST_BLOCK) { 18 | return NULL; 19 | } else if (nextBlockId == 0) { 20 | nextBlockId = _getBlockIndex(heapStart, header) + 1; 21 | } 22 | return _getBlockFromIndex(heapStart, nextBlockId); 23 | } 24 | 25 | void BlockList_Init(BlockList *blockList, word_t *heapStart) { 26 | blockList->heapStart = heapStart; 27 | blockList->first = NULL; 28 | blockList->last = NULL; 29 | } 30 | 31 | inline bool BlockList_IsEmpty(BlockList *blockList) { 32 | return blockList->first == NULL; 33 | } 34 | 35 | BlockHeader *BlockList_RemoveFirstBlock(BlockList *blockList) { 36 | assert(blockList->first != NULL); 37 | BlockHeader *block = blockList->first; 38 | if (block == blockList->last) { 39 | blockList->first = NULL; 40 | } 41 | blockList->first = _getNextBlock(blockList->heapStart, block); 42 | return block; 43 | } 44 | 45 | void BlockList_AddLast(BlockList *blockList, BlockHeader *blockHeader) { 46 | if (blockList->first == NULL) { 47 | blockList->first = blockHeader; 48 | } else { 49 | blockList->last->header.nextBlock = 50 | _getBlockIndex(blockList->heapStart, blockHeader); 51 | } 52 | blockList->last = blockHeader; 53 | blockHeader->header.nextBlock = LAST_BLOCK; 54 | } 55 | 56 | void BlockList_AddBlocksLast(BlockList *blockList, BlockHeader *first, 57 | BlockHeader *last) { 58 | if (blockList->first == NULL) { 59 | blockList->first = first; 60 | } else { 61 | blockList->last->header.nextBlock = 62 | _getBlockIndex(blockList->heapStart, first); 63 | } 64 | blockList->last = last; 65 | last->header.nextBlock = LAST_BLOCK; 66 | } 67 | 68 | void BlockList_Clear(BlockList *blockList) { 69 | blockList->first = NULL; 70 | blockList->last = NULL; 71 | } 72 | 73 | void BlockList_Print(BlockList *blockList) { 74 | printf("BlockList: "); 75 | BlockHeader *current = blockList->first; 76 | while (current != NULL) { 77 | printf("[%p %d] -> ", current, current->header.first); 78 | current = _getNextBlock(blockList->heapStart, current); 79 | } 80 | printf("\n"); 81 | } 82 | -------------------------------------------------------------------------------- /src/datastructures/BlockList.h: -------------------------------------------------------------------------------- 1 | #ifndef IMMIX_BLOCLIST_H 2 | #define IMMIX_BLOCLIST_H 3 | 4 | #include "../headers/BlockHeader.h" 5 | 6 | #define LAST_BLOCK -1 7 | 8 | typedef struct { 9 | word_t *heapStart; 10 | BlockHeader *first; 11 | BlockHeader *last; 12 | } BlockList; 13 | 14 | void BlockList_Init(BlockList *blockList, word_t *offset); 15 | void BlockList_Clear(BlockList *blockList); 16 | bool BlockList_IsEmpty(BlockList *blockList); 17 | BlockHeader *BlockList_RemoveFirstBlock(BlockList *blockList); 18 | void BlockList_AddLast(BlockList *blockList, BlockHeader *block); 19 | void BlockList_AddBlocksLast(BlockList *blockList, BlockHeader *first, 20 | BlockHeader *last); 21 | void BlockList_Print(BlockList *blockList); 22 | 23 | #endif // IMMIX_BLOCLIST_H 24 | -------------------------------------------------------------------------------- /src/datastructures/Stack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "Stack.h" 5 | #include "../Log.h" 6 | 7 | Stack *Stack_Alloc(size_t size) { 8 | assert(size % sizeof(Stack_Type) == 0); 9 | Stack *stack = malloc(sizeof(Stack)); 10 | stack->current = 0; 11 | stack->bottom = malloc(size); 12 | stack->nb_words = size / sizeof(Stack_Type); 13 | return stack; 14 | } 15 | 16 | bool Stack_Push(Stack *stack, Stack_Type word) { 17 | if (stack->current < stack->nb_words) { 18 | stack->bottom[stack->current++] = word; 19 | return false; 20 | } else { 21 | #ifdef PRINT_STACK_OVERFLOW 22 | printf("Overflow !\n"); 23 | #endif 24 | 25 | return true; 26 | } 27 | } 28 | 29 | Stack_Type Stack_Pop(Stack *stack) { 30 | assert(stack->current > 0); 31 | return stack->bottom[--stack->current]; 32 | } 33 | 34 | bool Stack_IsEmpty(Stack *stack) { return stack->current == 0; } 35 | 36 | void Stack_DoubleSize(Stack *stack) { 37 | assert(stack->current == 0); 38 | size_t nb_words = stack->nb_words * 2; 39 | stack->nb_words = nb_words; 40 | free(stack->bottom); 41 | stack->bottom = malloc(nb_words * sizeof(Stack_Type)); 42 | } -------------------------------------------------------------------------------- /src/datastructures/Stack.h: -------------------------------------------------------------------------------- 1 | #ifndef IMMIX_STACK_H 2 | #define IMMIX_STACK_H 3 | 4 | #include "../GCTypes.h" 5 | #include "../headers/ObjectHeader.h" 6 | 7 | #define INITIAL_STACK_SIZE (256 * 1024) 8 | 9 | typedef Object *Stack_Type; 10 | 11 | typedef struct { 12 | Stack_Type *bottom; 13 | size_t nb_words; 14 | int current; 15 | } Stack; 16 | 17 | Stack *Stack_Alloc(size_t size); 18 | 19 | bool Stack_Push(Stack *stack, Stack_Type word); 20 | 21 | Stack_Type Stack_Pop(Stack *stack); 22 | 23 | bool Stack_IsEmpty(Stack *stack); 24 | 25 | void Stack_DoubleSize(Stack *stack); 26 | 27 | #endif // IMMIX_STACK_H 28 | -------------------------------------------------------------------------------- /src/headers/BlockHeader.h: -------------------------------------------------------------------------------- 1 | #ifndef IMMIX_BLOCKHEADER_H 2 | #define IMMIX_BLOCKHEADER_H 3 | 4 | #include 5 | #include "LineHeader.h" 6 | #include "../GCTypes.h" 7 | #include "../Constants.h" 8 | #include "../Log.h" 9 | 10 | typedef enum { 11 | block_free = 0x0, 12 | block_recyclable = 0x1, 13 | block_unavailable = 0x2 14 | } BlockFlag; 15 | 16 | typedef struct { 17 | struct { 18 | uint8_t mark; 19 | uint8_t flags; 20 | int16_t first; 21 | int32_t nextBlock; 22 | } header; 23 | LineHeader lineHeaders[LINE_COUNT]; 24 | } BlockHeader; 25 | 26 | static inline bool Block_IsRecyclable(BlockHeader *blockHeader) { 27 | return blockHeader->header.flags == block_recyclable; 28 | } 29 | static inline bool Block_IsUnavailable(BlockHeader *blockHeader) { 30 | return blockHeader->header.flags == block_unavailable; 31 | } 32 | static inline bool Block_IsFree(BlockHeader *blockHeader) { 33 | return blockHeader->header.flags == block_free; 34 | } 35 | static inline void Block_SetFlag(BlockHeader *blockHeader, 36 | BlockFlag blockFlag) { 37 | blockHeader->header.flags = blockFlag; 38 | } 39 | 40 | static inline bool Block_IsMarked(BlockHeader *blockHeader) { 41 | return blockHeader->header.mark == 1; 42 | } 43 | 44 | static inline void Block_Unmark(BlockHeader *blockHeader) { 45 | blockHeader->header.mark = 0; 46 | } 47 | 48 | static inline void Block_Mark(BlockHeader *blockHeader) { 49 | blockHeader->header.mark = 1; 50 | } 51 | 52 | static inline BlockHeader *Block_GetBlockHeader(word_t *word) { 53 | return (BlockHeader *)((word_t)word & BLOCK_SIZE_IN_BYTES_INVERSE_MASK); 54 | } 55 | 56 | static inline word_t *Block_GetLineAddress(BlockHeader *blockHeader, 57 | int lineIndex) { 58 | assert(lineIndex < LINE_COUNT); 59 | return (word_t *)((ubyte_t *)blockHeader + BLOCK_METADATA_ALIGNED_SIZE + 60 | (lineIndex * LINE_SIZE)); 61 | } 62 | 63 | static inline word_t *Block_GetLineWord(BlockHeader *blockHeader, int lineIndex, 64 | int wordIndex) { 65 | assert(wordIndex < WORDS_IN_LINE); 66 | return &Block_GetLineAddress(blockHeader, lineIndex)[wordIndex]; 67 | } 68 | 69 | static inline FreeLineHeader *Block_GetFreeLineHeader(BlockHeader *blockHeader, 70 | int lineIndex) { 71 | return (FreeLineHeader *)Block_GetLineAddress(blockHeader, lineIndex); 72 | } 73 | 74 | static inline BlockHeader * 75 | Block_BlockHeaderFromLineHeader(LineHeader *lineHeader) { 76 | return Block_GetBlockHeader((word_t *)lineHeader); 77 | } 78 | 79 | static inline word_t *Block_GetFirstWord(BlockHeader *blockHeader) { 80 | return (word_t *)((ubyte_t *)blockHeader + BLOCK_METADATA_ALIGNED_SIZE); 81 | } 82 | 83 | static inline word_t *Block_GetBlockEnd(BlockHeader *blockHeader) { 84 | return Block_GetFirstWord(blockHeader) + (WORDS_IN_LINE * LINE_COUNT); 85 | } 86 | 87 | static inline uint32_t 88 | Block_GetLineIndexFromLineHeader(BlockHeader *blockHeader, 89 | LineHeader *lineHeader) { 90 | return (uint32_t)(lineHeader - blockHeader->lineHeaders); 91 | } 92 | 93 | static inline uint32_t Block_GetLineIndexFromWord(BlockHeader *blockHeader, 94 | word_t *word) { 95 | word_t *firstWord = Block_GetFirstWord(blockHeader); 96 | return (uint32_t)((word_t)word - (word_t)firstWord) >> LINE_SIZE_BITS; 97 | } 98 | 99 | static inline LineHeader *Block_GetLineHeader(BlockHeader *blockHeader, 100 | int lineIndex) { 101 | return &blockHeader->lineHeaders[lineIndex]; 102 | } 103 | 104 | #endif // IMMIX_BLOCKHEADER_H 105 | -------------------------------------------------------------------------------- /src/headers/LineHeader.h: -------------------------------------------------------------------------------- 1 | #ifndef IMMIX_LINEHEADER_H 2 | #define IMMIX_LINEHEADER_H 3 | 4 | #include 5 | #include 6 | 7 | #define FIRST_OBJECT_OFFSET_MASK (uint8_t)0xFC 8 | 9 | typedef struct { 10 | int16_t next; 11 | uint16_t size; 12 | } FreeLineHeader; 13 | 14 | typedef enum { 15 | line_empty = 0x0, 16 | line_marked = 0x1, 17 | line_contains_object_header = 0x2, 18 | } LineFlag; 19 | 20 | /** 21 | * Contains the offset of bytes to the first object. 22 | * The size of a line is 256 bytes, thus 8 bits are enough to store the offset. 23 | * 24 | * The offset is word aligned, meaning that the 3 lower bits can be used of 25 | * flags. 26 | * Bit 0 is for marking 27 | * Bit 1 is for indicating if the line contains an object 28 | * 29 | */ 30 | typedef uint8_t LineHeader; 31 | 32 | static inline bool Line_IsMarked(LineHeader *lineHeader) { 33 | return (line_marked & *lineHeader) != 0; 34 | } 35 | static inline void Line_Mark(LineHeader *lineHeader) { 36 | *lineHeader |= line_marked; 37 | } 38 | static inline void Line_Unmark(LineHeader *lineHeader) { 39 | *lineHeader &= ~line_marked; 40 | } 41 | 42 | static inline void Line_SetEmpty(LineHeader *lineHeader) { 43 | *lineHeader = (uint8_t)line_empty; 44 | } 45 | 46 | static inline bool Line_ContainsObject(LineHeader *lineHeader) { 47 | return (line_contains_object_header & *lineHeader) != 0; 48 | } 49 | 50 | static inline void Line_SetOffset(LineHeader *lineHeader, uint8_t offset) { 51 | *lineHeader = 52 | (offset & FIRST_OBJECT_OFFSET_MASK) | line_contains_object_header; 53 | } 54 | 55 | static inline uint8_t Line_GetFirstObjectOffset(LineHeader *lineHeader) { 56 | return *lineHeader & FIRST_OBJECT_OFFSET_MASK; 57 | } 58 | 59 | #endif // IMMIX_LINEHEADER_H 60 | -------------------------------------------------------------------------------- /src/headers/ObjectHeader.h: -------------------------------------------------------------------------------- 1 | #ifndef IMMIX_OBJECTHEADER_H 2 | #define IMMIX_OBJECTHEADER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "../GCTypes.h" 8 | #include "../Constants.h" 9 | #include "../Log.h" 10 | 11 | typedef enum { 12 | object_standard = 0x1, 13 | object_large = 0x2, 14 | } ObjectType; 15 | 16 | 17 | typedef enum { 18 | object_free = 0x0, 19 | object_allocated = 0x1, 20 | object_marked = 0x2, 21 | } ObjectFlag; 22 | 23 | typedef struct { 24 | uint32_t size; 25 | uint8_t type; 26 | uint8_t flag; 27 | uint8_t isObjectArray; 28 | } ObjectHeader; 29 | 30 | typedef struct { 31 | struct { 32 | int32_t id; 33 | word_t *name; 34 | int8_t kind; 35 | } rt; 36 | int64_t size; 37 | struct { 38 | int32_t from; 39 | int32_t to; 40 | } range; 41 | struct { 42 | int32_t dyn_method_count; 43 | word_t *dyn_method_salt; 44 | word_t *dyn_method_keys; 45 | word_t *dyn_methods; 46 | } dynDispatchTable; 47 | int64_t *refMapStruct; 48 | } Rtti; 49 | 50 | typedef word_t *Field_t; 51 | 52 | typedef struct { 53 | ObjectHeader header; 54 | Rtti *rtti; 55 | Field_t fields[0]; 56 | } Object; 57 | 58 | static inline bool Object_IsMarked(ObjectHeader *objectHeader) { 59 | return objectHeader->flag == object_marked; 60 | } 61 | 62 | static inline void Object_MarkObjectHeader(ObjectHeader *objectHeader) { 63 | objectHeader->flag = object_marked; 64 | } 65 | 66 | static inline void Object_SetAllocated(ObjectHeader *objectHeader) { 67 | objectHeader->flag = object_allocated; 68 | } 69 | 70 | static inline void Object_SetFree(ObjectHeader *objectHeader) { 71 | objectHeader->flag = object_free; 72 | } 73 | 74 | static inline bool Object_IsAllocated(ObjectHeader *objectHeader) { 75 | return objectHeader->flag == object_allocated; 76 | } 77 | 78 | static inline bool Object_IsStandardObject(ObjectHeader *objectHeader) { 79 | return objectHeader->type == object_standard; 80 | } 81 | static inline bool Object_IsLargeObject(ObjectHeader *objectHeader) { 82 | return objectHeader->type == object_large; 83 | } 84 | 85 | static inline void Object_SetObjectType(ObjectHeader *objectHeader, 86 | ObjectType objectType) { 87 | objectHeader->type = objectType; 88 | } 89 | 90 | static inline size_t Object_Size(ObjectHeader *objectHeader) { 91 | uint32_t size = objectHeader->size; 92 | assert((Object_IsStandardObject(objectHeader) && size < LARGE_BLOCK_SIZE) || 93 | !Object_IsStandardObject(objectHeader)); 94 | 95 | return size << WORD_SIZE_BITS; 96 | } 97 | 98 | static inline void Object_SetSize(ObjectHeader *objectHeader, size_t size) { 99 | uint32_t _size = (uint32_t)(size >> WORD_SIZE_BITS); 100 | assert(!Object_IsStandardObject(objectHeader) || 101 | (Object_IsStandardObject(objectHeader) && _size > 0 && 102 | _size < LARGE_BLOCK_SIZE)); 103 | objectHeader->size = _size; 104 | } 105 | 106 | static inline Object *Object_FromMutatorAddress(word_t *address) { 107 | return (Object *)(address - WORDS_IN_OBJECT_HEADER); 108 | } 109 | 110 | static inline word_t *Object_ToMutatorAddress(Object *object) { 111 | return (word_t *)&object->rtti; 112 | } 113 | 114 | static inline bool Object_IsObjectArray(ObjectHeader *objectHeader) { 115 | return objectHeader->isObjectArray != 0; 116 | } 117 | 118 | static inline void Object_SetObjectArray(ObjectHeader *objectHeader, bool isObjectArray) { 119 | objectHeader->isObjectArray = isObjectArray ? 1 : 0; 120 | } 121 | 122 | #endif // IMMIX_OBJECTHEADER_H 123 | -------------------------------------------------------------------------------- /src/utils/MathUtils.h: -------------------------------------------------------------------------------- 1 | #ifndef IMMIX_MATHUTILS_H 2 | #define IMMIX_MATHUTILS_H 3 | 4 | #include 5 | 6 | static const int MultiplyDeBruijnBitPosition[32] = { 7 | 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8 | 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31}; 9 | 10 | static int log2_floor(size_t v) { 11 | 12 | v |= v >> 1; // first round down to one less than a power of 2 13 | v |= v >> 2; 14 | v |= v >> 4; 15 | v |= v >> 8; 16 | v |= v >> 16; 17 | 18 | return MultiplyDeBruijnBitPosition[(uint32_t)(v * 0x07C4ACDDU) >> 27]; 19 | } 20 | 21 | static inline int MathUtils_Log2Ceil(size_t value) { 22 | return log2_floor(2 * value - 1); 23 | } 24 | 25 | static inline size_t MathUtils_RoundToNextMultiple(size_t value, 26 | size_t multiple) { 27 | return (value + multiple - 1) / multiple * multiple; 28 | } 29 | 30 | static inline size_t MathUtils_DivAndRoundUp(size_t value, size_t divider) { 31 | return (value + divider - 1) / divider; 32 | } 33 | 34 | #endif // IMMIX_MATHUTILS_H 35 | --------------------------------------------------------------------------------