From 072855a242c1fd0c1a429498fd322b608c7c4f6e Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Thu, 26 Feb 2026 08:54:01 +0900 Subject: [PATCH] gc.c: add contiguous heap region support (mrb_gc_add_region) Allow users to provide contiguous memory buffers for GC heap pages via mrb_gc_add_region(). Region pages are carved from user-owned buffers and never freed by the GC. This is the foundation for bitmap GC on embedded targets with fragmented RAM. Co-authored-by: Claude --- include/mruby/gc.h | 2 + src/gc.c | 93 ++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 87 insertions(+), 8 deletions(-) diff --git a/include/mruby/gc.h b/include/mruby/gc.h index 55f997efe..b9602ef52 100644 --- a/include/mruby/gc.h +++ b/include/mruby/gc.h @@ -42,6 +42,7 @@ typedef struct mrb_gc { struct mrb_heap_page *heaps; /* all heaps pages */ struct mrb_heap_page *free_heaps;/* heaps for allocation */ struct mrb_heap_page *sweeps; /* page where sweep starts */ + struct mrb_heap_region *regions; /* contiguous heap regions */ struct RBasic *gray_stack[MRB_GRAY_STACK_SIZE]; /* stack of gray objects */ size_t gray_stack_top; /* top index of gray stack */ mrb_bool gray_overflow:1; /* gray stack overflowed; needs heap rescan */ @@ -69,6 +70,7 @@ typedef struct mrb_gc { } mrb_gc; MRB_API mrb_bool mrb_object_dead_p(struct mrb_state *mrb, struct RBasic *object); +MRB_API int mrb_gc_add_region(struct mrb_state *mrb, void *start, size_t size); #define MRB_GC_RED 7 diff --git a/src/gc.c b/src/gc.c index 69a3d9d2d..0c7dd4ad4 100644 --- a/src/gc.c +++ b/src/gc.c @@ -166,9 +166,17 @@ typedef struct mrb_heap_page { struct mrb_heap_page *next; struct mrb_heap_page *free_next; mrb_bool old:1; + mrb_bool region:1; /* from contiguous region, not malloc */ RVALUE objects[MRB_HEAP_PAGE_SIZE]; } mrb_heap_page; +typedef struct mrb_heap_region { + struct mrb_heap_region *next; + uint8_t *base; /* start of user buffer */ + size_t size; /* buffer size in bytes */ + uint16_t page_count; /* pages carved from region */ +} mrb_heap_region; + #define GC_STEP_SIZE 1024 /* white: 001 or 010, black: 100, gray: 000, red:111 */ @@ -283,6 +291,17 @@ static mrb_bool heap_p(mrb_gc *gc, const struct RBasic *object) { mrb_heap_page* page; + mrb_heap_region *region; + + /* fast path: check contiguous regions via arithmetic */ + for (region = gc->regions; region; region = region->next) { + uintptr_t addr = (uintptr_t)object; + uintptr_t base = (uintptr_t)region->base; + uintptr_t end = base + (size_t)region->page_count * sizeof(mrb_heap_page); + if (addr >= base && addr < end) { + return TRUE; + } + } page = gc->heaps; while (page) { @@ -306,9 +325,17 @@ mrb_object_dead_p(mrb_state *mrb, struct RBasic *object) } static void -add_heap(mrb_state *mrb, mrb_gc *gc) +link_heap_page(mrb_gc *gc, mrb_heap_page *page) +{ + page->next = gc->heaps; + gc->heaps = page; + page->free_next = gc->free_heaps; + gc->free_heaps = page; +} + +static void +init_heap_page(mrb_heap_page *page) { - mrb_heap_page *page = (mrb_heap_page*)mrb_calloc(mrb, 1, sizeof(mrb_heap_page)); RVALUE *p, *e; RVALUE *prev = NULL; @@ -318,12 +345,50 @@ add_heap(mrb_state *mrb, mrb_gc *gc) prev = p; } page->freelist = prev; +} - page->next = gc->heaps; - gc->heaps = page; +static void +add_heap(mrb_state *mrb, mrb_gc *gc) +{ + mrb_heap_page *page = (mrb_heap_page*)mrb_calloc(mrb, 1, sizeof(mrb_heap_page)); + init_heap_page(page); + link_heap_page(gc, page); +} - page->free_next = gc->free_heaps; - gc->free_heaps = page; +MRB_API int +mrb_gc_add_region(mrb_state *mrb, void *start, size_t size) +{ + mrb_gc *gc = &mrb->gc; + uint8_t *base = (uint8_t*)start; + mrb_heap_region *region; + uint16_t page_count; + uint16_t i; + + /* align base to pointer size */ + uintptr_t align = sizeof(void*); + uintptr_t offset = ((uintptr_t)base + align - 1) & ~(align - 1); + size -= (size_t)(offset - (uintptr_t)base); + base = (uint8_t*)offset; + + page_count = (uint16_t)(size / sizeof(mrb_heap_page)); + if (page_count == 0) return 0; + + region = (mrb_heap_region*)mrb_malloc(mrb, sizeof(mrb_heap_region)); + region->base = base; + region->size = size; + region->page_count = page_count; + region->next = gc->regions; + gc->regions = region; + + /* carve pages from the contiguous buffer */ + for (i = 0; i < page_count; i++) { + mrb_heap_page *page = (mrb_heap_page*)(base + (size_t)i * sizeof(mrb_heap_page)); + memset(page, 0, sizeof(mrb_heap_page)); + page->region = TRUE; + init_heap_page(page); + link_heap_page(gc, page); + } + return page_count; } #define DEFAULT_GC_INTERVAL_RATIO 200 @@ -345,6 +410,7 @@ mrb_gc_init(mrb_state *mrb, mrb_gc *gc) gc->current_white_part = GC_WHITE_A; gc->heaps = NULL; gc->free_heaps = NULL; + gc->regions = NULL; add_heap(mrb, gc); gc->interval_ratio = DEFAULT_GC_INTERVAL_RATIO; gc->step_ratio = DEFAULT_GC_STEP_RATIO; @@ -370,7 +436,9 @@ free_heap(mrb_state *mrb, mrb_gc *gc) if (p->as.free.tt != MRB_TT_FREE) obj_free(mrb, &p->as.basic, TRUE); } - mrb_free(mrb, tmp); + if (!tmp->region) { + mrb_free(mrb, tmp); + } } } @@ -378,6 +446,15 @@ void mrb_gc_destroy(mrb_state *mrb, mrb_gc *gc) { free_heap(mrb, gc); + /* free region descriptors (buffer memory belongs to the caller) */ + { + mrb_heap_region *region = gc->regions; + while (region) { + mrb_heap_region *next = region->next; + mrb_free(mrb, region); + region = next; + } + } #ifndef MRB_GC_FIXED_ARENA mrb_free(mrb, gc->arena); #endif @@ -1120,7 +1197,7 @@ incremental_sweep_phase(mrb_state *mrb, mrb_gc *gc, size_t limit) } /* free dead slot */ - if (dead_slot) { + if (dead_slot && !page->region) { mrb_heap_page *next = page->next; if (prev) prev->next = next;