Skip to content

Commit c7404a3

Browse files
committed
Add movable allocation system.
This allows calls to `allocate_memory()` while the VM is running, it will then allocate from the GC heap (unless there is a suitable hole among the supervisor allocations), and when the VM exits and the GC heap is freed, the allocation will be moved to the bottom of the former GC heap and transformed into a proper supervisor allocation. Existing movable allocations will also be moved to defragment the supervisor heap and ensure that the next VM run gets as much memory as possible for the GC heap. By itself this breaks terminalio because it violates the assumption that supervisor_display_move_memory() still has access to an undisturbed heap to copy the tilegrid from. It will work in many cases, but if you're unlucky you will get garbled terminal contents after exiting from the vm run that created the display. This will be fixed in the following commit, which is separate to simplify review.
1 parent bd87201 commit c7404a3

File tree

19 files changed

+283
-137
lines changed

19 files changed

+283
-137
lines changed

main.c

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -123,15 +123,15 @@ void start_mp(supervisor_allocation* heap) {
123123
// to recover from limit hit. (Limit is measured in bytes.)
124124
mp_stack_ctrl_init();
125125

126-
if (stack_alloc != NULL) {
127-
mp_stack_set_limit(stack_alloc->length - 1024);
126+
if (stack_get_bottom() != NULL) {
127+
mp_stack_set_limit(stack_get_length() - 1024);
128128
}
129129

130130

131131
#if MICROPY_MAX_STACK_USAGE
132132
// _ezero (same as _ebss) is an int, so start 4 bytes above it.
133-
if (stack_alloc != NULL) {
134-
mp_stack_set_bottom(stack_alloc->ptr);
133+
if (stack_get_bottom() != NULL) {
134+
mp_stack_set_bottom(stack_get_bottom());
135135
mp_stack_fill_with_sentinel();
136136
}
137137
#endif
@@ -148,7 +148,7 @@ void start_mp(supervisor_allocation* heap) {
148148
#endif
149149

150150
#if MICROPY_ENABLE_GC
151-
gc_init(heap->ptr, heap->ptr + heap->length / 4);
151+
gc_init(heap->ptr, heap->ptr + get_allocation_length(heap) / 4);
152152
#endif
153153
mp_init();
154154
mp_obj_list_init(mp_sys_path, 0);
@@ -451,9 +451,6 @@ int __attribute__((used)) main(void) {
451451
// initialise the cpu and peripherals
452452
safe_mode_t safe_mode = port_init();
453453

454-
// Init memory after the port in case the port needs to set aside memory.
455-
memory_init();
456-
457454
// Turn on LEDs
458455
init_status_leds();
459456
rgb_led_status_init();

ports/atmel-samd/supervisor/port.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -390,8 +390,8 @@ void reset_cpu(void) {
390390
reset();
391391
}
392392

393-
supervisor_allocation* port_fixed_stack(void) {
394-
return NULL;
393+
bool port_has_fixed_stack(void) {
394+
return false;
395395
}
396396

397397
uint32_t *port_stack_get_limit(void) {

ports/cxd56/supervisor/port.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,8 @@ void reset_to_bootloader(void) {
9898
}
9999
}
100100

101-
supervisor_allocation _fixed_stack;
102-
103-
supervisor_allocation* port_fixed_stack(void) {
104-
_fixed_stack.ptr = port_stack_get_limit();
105-
_fixed_stack.length = (port_stack_get_top() - port_stack_get_limit()) * sizeof(uint32_t);
106-
return &_fixed_stack;
101+
bool port_has_fixed_stack(void) {
102+
return true;
107103
}
108104

109105
uint32_t *port_stack_get_limit(void) {

ports/esp32s2/supervisor/port.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -193,12 +193,8 @@ uint32_t *port_stack_get_top(void) {
193193
return port_stack_get_limit() + ESP_TASK_MAIN_STACK / (sizeof(uint32_t) / sizeof(StackType_t));
194194
}
195195

196-
supervisor_allocation _fixed_stack;
197-
198-
supervisor_allocation* port_fixed_stack(void) {
199-
_fixed_stack.ptr = port_stack_get_limit();
200-
_fixed_stack.length = (port_stack_get_top() - port_stack_get_limit()) * sizeof(uint32_t);
201-
return &_fixed_stack;
196+
bool port_has_fixed_stack(void) {
197+
return true;
202198
}
203199

204200
// Place the word to save just after our BSS section that gets blanked.

ports/litex/supervisor/port.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ void reset_cpu(void) {
9898
for(;;) {}
9999
}
100100

101-
supervisor_allocation* port_fixed_stack(void) {
102-
return NULL;
101+
bool port_has_fixed_stack(void) {
102+
return false;
103103
}
104104

105105
uint32_t *port_heap_get_bottom(void) {

ports/mimxrt10xx/supervisor/port.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -334,11 +334,8 @@ uint32_t *port_stack_get_top(void) {
334334
return &_ld_stack_top;
335335
}
336336

337-
supervisor_allocation _fixed_stack;
338-
supervisor_allocation* port_fixed_stack(void) {
339-
_fixed_stack.ptr = port_stack_get_limit();
340-
_fixed_stack.length = (port_stack_get_top() - port_stack_get_limit()) * sizeof(uint32_t);
341-
return &_fixed_stack;
337+
bool port_has_fixed_stack(void) {
338+
return true;
342339
}
343340

344341
uint32_t *port_heap_get_bottom(void) {

ports/nrf/supervisor/port.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,8 +251,8 @@ uint32_t *port_heap_get_top(void) {
251251
return port_stack_get_top();
252252
}
253253

254-
supervisor_allocation* port_fixed_stack(void) {
255-
return NULL;
254+
bool port_has_fixed_stack(void) {
255+
return false;
256256
}
257257

258258
uint32_t *port_stack_get_limit(void) {

ports/stm/supervisor/port.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,8 +267,8 @@ uint32_t *port_heap_get_top(void) {
267267
return &_ld_heap_end;
268268
}
269269

270-
supervisor_allocation* port_fixed_stack(void) {
271-
return NULL;
270+
bool port_has_fixed_stack(void) {
271+
return false;
272272
}
273273

274274
uint32_t *port_stack_get_limit(void) {

py/circuitpy_mpconfig.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,9 @@ extern const struct _mp_obj_module_t wifi_module;
858858

859859
#include "supervisor/flash_root_pointers.h"
860860

861+
// From supervisor/memory.c
862+
struct _supervisor_allocation_node;
863+
861864
#define CIRCUITPY_COMMON_ROOT_POINTERS \
862865
const char *readline_hist[8]; \
863866
vstr_t *repl_line; \
@@ -869,6 +872,7 @@ extern const struct _mp_obj_module_t wifi_module;
869872
FLASH_ROOT_POINTERS \
870873
MEMORYMONITOR_ROOT_POINTERS \
871874
NETWORK_ROOT_POINTERS \
875+
struct _supervisor_allocation_node* first_embedded_allocation; \
872876

873877
void supervisor_run_background_tasks_if_tick(void);
874878
#define RUN_BACKGROUND_TASKS (supervisor_run_background_tasks_if_tick())

shared-module/rgbmatrix/RGBMatrix.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ void *common_hal_rgbmatrix_allocator_impl(size_t sz) {
220220
if (gc_alloc_possible()) {
221221
return m_malloc_maybe(sz + sizeof(void*), true);
222222
} else {
223-
supervisor_allocation *allocation = allocate_memory(align32_size(sz), false);
223+
supervisor_allocation *allocation = allocate_memory(align32_size(sz), false, false);
224224
return allocation ? allocation->ptr : NULL;
225225
}
226226
}

shared-module/sharpdisplay/SharpMemoryFramebuffer.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
#define SHARPMEM_BIT_VCOM_LSB (0x40)
4141

4242
static void *hybrid_alloc(size_t sz) {
43-
supervisor_allocation *allocation = allocate_memory(align32_size(sz), false);
43+
supervisor_allocation *allocation = allocate_memory(align32_size(sz), false, false);
4444
if (allocation) {
4545
memset(allocation->ptr, 0, sz);
4646
return allocation->ptr;

shared-module/usb_midi/__init__.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ void usb_midi_init(void) {
4545
uint16_t portout_size = align32_size(sizeof(usb_midi_portout_obj_t));
4646

4747
// For each embedded MIDI Jack in the descriptor we create a Port
48-
usb_midi_allocation = allocate_memory(tuple_size + portin_size + portout_size, false);
48+
usb_midi_allocation = allocate_memory(tuple_size + portin_size + portout_size, false, false);
4949

5050
mp_obj_tuple_t *ports = (mp_obj_tuple_t *) usb_midi_allocation->ptr;
5151
ports->base.type = &mp_type_tuple;

supervisor/memory.h

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,23 +33,36 @@
3333

3434
#include <stdbool.h>
3535
#include <stdint.h>
36+
#include <stddef.h>
3637

3738
typedef struct {
3839
uint32_t* ptr;
39-
uint32_t length; // in bytes
4040
} supervisor_allocation;
4141

4242

4343

44-
void memory_init(void);
4544
void free_memory(supervisor_allocation* allocation);
45+
46+
// Find the allocation with the given ptr, NULL if not found. When called from the context of a
47+
// supervisor_move_memory() callback, finds the allocation that had that ptr *before* the move, but
48+
// the returned allocation already contains the ptr after the move.
49+
// When called with NULL, may return either NULL or an unused allocation whose ptr is NULL (this is
50+
// a feature used internally in allocate_memory to save code size). Passing the return value to
51+
// free_memory() is a permissible no-op in either case.
4652
supervisor_allocation* allocation_from_ptr(void *ptr);
53+
4754
supervisor_allocation* allocate_remaining_memory(void);
4855

4956
// Allocate a piece of a given length in bytes. If high_address is true then it should be allocated
5057
// at a lower address from the top of the stack. Otherwise, addresses will increase starting after
51-
// statically allocated memory.
52-
supervisor_allocation* allocate_memory(uint32_t length, bool high_address);
58+
// statically allocated memory. If movable is false, memory will be taken from outside the GC heap
59+
// and will stay stationary until freed. While the VM is running, this will fail unless a previous
60+
// allocation of exactly matching length has recently been freed. If movable is true, memory will be
61+
// taken from either outside or inside the GC heap, and when the VM exits, will be moved outside.
62+
// The ptr of the returned supervisor_allocation will change at that point. If you need to be
63+
// notified of that, add your own callback function at the designated place near the end of
64+
// supervisor_move_memory().
65+
supervisor_allocation* allocate_memory(uint32_t length, bool high_address, bool movable);
5366

5467
static inline uint16_t align32_size(uint16_t size) {
5568
if (size % 4 != 0) {
@@ -58,7 +71,10 @@ static inline uint16_t align32_size(uint16_t size) {
5871
return size;
5972
}
6073

61-
// Called after the heap is freed in case the supervisor wants to save some values.
74+
size_t get_allocation_length(supervisor_allocation* allocation);
75+
76+
// Called after the GC heap is freed, transfers movable allocations from the GC heap to the
77+
// supervisor heap and compacts the supervisor heap.
6278
void supervisor_move_memory(void);
6379

6480
#endif // MICROPY_INCLUDED_SUPERVISOR_MEMORY_H

supervisor/port.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,15 @@ uint32_t *port_stack_get_limit(void);
6161
// Get stack top address
6262
uint32_t *port_stack_get_top(void);
6363

64-
supervisor_allocation* port_fixed_stack(void);
64+
// True if stack is not located inside heap (at the top)
65+
bool port_has_fixed_stack(void);
6566

6667
// Get heap bottom address
6768
uint32_t *port_heap_get_bottom(void);
6869

6970
// Get heap top address
7071
uint32_t *port_heap_get_top(void);
7172

72-
supervisor_allocation* port_fixed_heap(void);
73-
7473
// Save and retrieve a word from memory that is preserved over reset. Used for safe mode.
7574
void port_set_saved_word(uint32_t);
7675
uint32_t port_get_saved_word(void);

supervisor/shared/display.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) {
8282
uint16_t total_tiles = width_in_tiles * height_in_tiles;
8383

8484
// First try to allocate outside the heap. This will fail when the VM is running.
85-
tilegrid_tiles = allocate_memory(align32_size(total_tiles), false);
85+
tilegrid_tiles = allocate_memory(align32_size(total_tiles), false, false);
8686
uint8_t* tiles;
8787
if (tilegrid_tiles == NULL) {
8888
tiles = m_malloc(total_tiles, true);
@@ -133,7 +133,7 @@ void supervisor_display_move_memory(void) {
133133
grid->tiles == MP_STATE_VM(terminal_tilegrid_tiles)) {
134134
uint16_t total_tiles = grid->width_in_tiles * grid->height_in_tiles;
135135

136-
tilegrid_tiles = allocate_memory(align32_size(total_tiles), false);
136+
tilegrid_tiles = allocate_memory(align32_size(total_tiles), false, false);
137137
if (tilegrid_tiles != NULL) {
138138
memcpy(tilegrid_tiles->ptr, grid->tiles, total_tiles);
139139
grid->tiles = (uint8_t*) tilegrid_tiles->ptr;

supervisor/shared/external_flash/external_flash.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ static bool allocate_ram_cache(void) {
338338

339339
uint32_t table_size = blocks_per_sector * pages_per_block * sizeof(uint32_t);
340340
// Attempt to allocate outside the heap first.
341-
supervisor_cache = allocate_memory(table_size + SPI_FLASH_ERASE_SIZE, false);
341+
supervisor_cache = allocate_memory(table_size + SPI_FLASH_ERASE_SIZE, false, false);
342342
if (supervisor_cache != NULL) {
343343
MP_STATE_VM(flash_ram_cache) = (uint8_t **) supervisor_cache->ptr;
344344
uint8_t* page_start = (uint8_t *) supervisor_cache->ptr + table_size;

0 commit comments

Comments
 (0)