summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xmain.c13
-rw-r--r--ports/atmel-samd/supervisor/port.c4
-rw-r--r--ports/cxd56/supervisor/port.c8
-rw-r--r--ports/esp32s2/supervisor/port.c8
-rw-r--r--ports/litex/supervisor/port.c4
-rw-r--r--ports/mimxrt10xx/supervisor/port.c7
-rw-r--r--ports/nrf/supervisor/port.c4
-rw-r--r--ports/stm/supervisor/port.c4
-rw-r--r--py/circuitpy_mpconfig.h4
-rw-r--r--shared-module/rgbmatrix/RGBMatrix.c2
-rw-r--r--shared-module/sharpdisplay/SharpMemoryFramebuffer.c2
-rw-r--r--shared-module/usb_midi/__init__.c2
-rwxr-xr-xsupervisor/memory.h26
-rw-r--r--supervisor/port.h5
-rw-r--r--supervisor/shared/display.c4
-rw-r--r--supervisor/shared/external_flash/external_flash.c2
-rwxr-xr-xsupervisor/shared/memory.c277
-rwxr-xr-xsupervisor/shared/stack.c38
-rwxr-xr-xsupervisor/shared/stack.h6
19 files changed, 283 insertions, 137 deletions
diff --git a/main.c b/main.c
index 80b163f60..1389f524b 100755
--- a/main.c
+++ b/main.c
@@ -123,15 +123,15 @@ void start_mp(supervisor_allocation* heap) {
// to recover from limit hit. (Limit is measured in bytes.)
mp_stack_ctrl_init();
- if (stack_alloc != NULL) {
- mp_stack_set_limit(stack_alloc->length - 1024);
+ if (stack_get_bottom() != NULL) {
+ mp_stack_set_limit(stack_get_length() - 1024);
}
#if MICROPY_MAX_STACK_USAGE
// _ezero (same as _ebss) is an int, so start 4 bytes above it.
- if (stack_alloc != NULL) {
- mp_stack_set_bottom(stack_alloc->ptr);
+ if (stack_get_bottom() != NULL) {
+ mp_stack_set_bottom(stack_get_bottom());
mp_stack_fill_with_sentinel();
}
#endif
@@ -148,7 +148,7 @@ void start_mp(supervisor_allocation* heap) {
#endif
#if MICROPY_ENABLE_GC
- gc_init(heap->ptr, heap->ptr + heap->length / 4);
+ gc_init(heap->ptr, heap->ptr + get_allocation_length(heap) / 4);
#endif
mp_init();
mp_obj_list_init(mp_sys_path, 0);
@@ -451,9 +451,6 @@ int __attribute__((used)) main(void) {
// initialise the cpu and peripherals
safe_mode_t safe_mode = port_init();
- // Init memory after the port in case the port needs to set aside memory.
- memory_init();
-
// Turn on LEDs
init_status_leds();
rgb_led_status_init();
diff --git a/ports/atmel-samd/supervisor/port.c b/ports/atmel-samd/supervisor/port.c
index d65d09825..fc1d1198e 100644
--- a/ports/atmel-samd/supervisor/port.c
+++ b/ports/atmel-samd/supervisor/port.c
@@ -390,8 +390,8 @@ void reset_cpu(void) {
reset();
}
-supervisor_allocation* port_fixed_stack(void) {
- return NULL;
+bool port_has_fixed_stack(void) {
+ return false;
}
uint32_t *port_stack_get_limit(void) {
diff --git a/ports/cxd56/supervisor/port.c b/ports/cxd56/supervisor/port.c
index 086c2d198..d69f35779 100644
--- a/ports/cxd56/supervisor/port.c
+++ b/ports/cxd56/supervisor/port.c
@@ -98,12 +98,8 @@ void reset_to_bootloader(void) {
}
}
-supervisor_allocation _fixed_stack;
-
-supervisor_allocation* port_fixed_stack(void) {
- _fixed_stack.ptr = port_stack_get_limit();
- _fixed_stack.length = (port_stack_get_top() - port_stack_get_limit()) * sizeof(uint32_t);
- return &_fixed_stack;
+bool port_has_fixed_stack(void) {
+ return true;
}
uint32_t *port_stack_get_limit(void) {
diff --git a/ports/esp32s2/supervisor/port.c b/ports/esp32s2/supervisor/port.c
index aff7dbda4..264bdee97 100644
--- a/ports/esp32s2/supervisor/port.c
+++ b/ports/esp32s2/supervisor/port.c
@@ -193,12 +193,8 @@ uint32_t *port_stack_get_top(void) {
return port_stack_get_limit() + ESP_TASK_MAIN_STACK / (sizeof(uint32_t) / sizeof(StackType_t));
}
-supervisor_allocation _fixed_stack;
-
-supervisor_allocation* port_fixed_stack(void) {
- _fixed_stack.ptr = port_stack_get_limit();
- _fixed_stack.length = (port_stack_get_top() - port_stack_get_limit()) * sizeof(uint32_t);
- return &_fixed_stack;
+bool port_has_fixed_stack(void) {
+ return true;
}
// Place the word to save just after our BSS section that gets blanked.
diff --git a/ports/litex/supervisor/port.c b/ports/litex/supervisor/port.c
index 02617b9af..f5c362ea6 100644
--- a/ports/litex/supervisor/port.c
+++ b/ports/litex/supervisor/port.c
@@ -98,8 +98,8 @@ void reset_cpu(void) {
for(;;) {}
}
-supervisor_allocation* port_fixed_stack(void) {
- return NULL;
+bool port_has_fixed_stack(void) {
+ return false;
}
uint32_t *port_heap_get_bottom(void) {
diff --git a/ports/mimxrt10xx/supervisor/port.c b/ports/mimxrt10xx/supervisor/port.c
index e3fef373f..1be2b1039 100644
--- a/ports/mimxrt10xx/supervisor/port.c
+++ b/ports/mimxrt10xx/supervisor/port.c
@@ -334,11 +334,8 @@ uint32_t *port_stack_get_top(void) {
return &_ld_stack_top;
}
-supervisor_allocation _fixed_stack;
-supervisor_allocation* port_fixed_stack(void) {
- _fixed_stack.ptr = port_stack_get_limit();
- _fixed_stack.length = (port_stack_get_top() - port_stack_get_limit()) * sizeof(uint32_t);
- return &_fixed_stack;
+bool port_has_fixed_stack(void) {
+ return true;
}
uint32_t *port_heap_get_bottom(void) {
diff --git a/ports/nrf/supervisor/port.c b/ports/nrf/supervisor/port.c
index 493de43e0..5f1c9f1ba 100644
--- a/ports/nrf/supervisor/port.c
+++ b/ports/nrf/supervisor/port.c
@@ -251,8 +251,8 @@ uint32_t *port_heap_get_top(void) {
return port_stack_get_top();
}
-supervisor_allocation* port_fixed_stack(void) {
- return NULL;
+bool port_has_fixed_stack(void) {
+ return false;
}
uint32_t *port_stack_get_limit(void) {
diff --git a/ports/stm/supervisor/port.c b/ports/stm/supervisor/port.c
index a8aab00ff..dba1cf21e 100644
--- a/ports/stm/supervisor/port.c
+++ b/ports/stm/supervisor/port.c
@@ -267,8 +267,8 @@ uint32_t *port_heap_get_top(void) {
return &_ld_heap_end;
}
-supervisor_allocation* port_fixed_stack(void) {
- return NULL;
+bool port_has_fixed_stack(void) {
+ return false;
}
uint32_t *port_stack_get_limit(void) {
diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h
index 28fd4095c..28fd6e9b0 100644
--- a/py/circuitpy_mpconfig.h
+++ b/py/circuitpy_mpconfig.h
@@ -858,6 +858,9 @@ extern const struct _mp_obj_module_t wifi_module;
#include "supervisor/flash_root_pointers.h"
+// From supervisor/memory.c
+struct _supervisor_allocation_node;
+
#define CIRCUITPY_COMMON_ROOT_POINTERS \
const char *readline_hist[8]; \
vstr_t *repl_line; \
@@ -869,6 +872,7 @@ extern const struct _mp_obj_module_t wifi_module;
FLASH_ROOT_POINTERS \
MEMORYMONITOR_ROOT_POINTERS \
NETWORK_ROOT_POINTERS \
+ struct _supervisor_allocation_node* first_embedded_allocation; \
void supervisor_run_background_tasks_if_tick(void);
#define RUN_BACKGROUND_TASKS (supervisor_run_background_tasks_if_tick())
diff --git a/shared-module/rgbmatrix/RGBMatrix.c b/shared-module/rgbmatrix/RGBMatrix.c
index 94c3eda27..1f144aedb 100644
--- a/shared-module/rgbmatrix/RGBMatrix.c
+++ b/shared-module/rgbmatrix/RGBMatrix.c
@@ -220,7 +220,7 @@ void *common_hal_rgbmatrix_allocator_impl(size_t sz) {
if (gc_alloc_possible()) {
return m_malloc_maybe(sz + sizeof(void*), true);
} else {
- supervisor_allocation *allocation = allocate_memory(align32_size(sz), false);
+ supervisor_allocation *allocation = allocate_memory(align32_size(sz), false, false);
return allocation ? allocation->ptr : NULL;
}
}
diff --git a/shared-module/sharpdisplay/SharpMemoryFramebuffer.c b/shared-module/sharpdisplay/SharpMemoryFramebuffer.c
index b199e98d6..aefb6b18d 100644
--- a/shared-module/sharpdisplay/SharpMemoryFramebuffer.c
+++ b/shared-module/sharpdisplay/SharpMemoryFramebuffer.c
@@ -40,7 +40,7 @@
#define SHARPMEM_BIT_VCOM_LSB (0x40)
static void *hybrid_alloc(size_t sz) {
- supervisor_allocation *allocation = allocate_memory(align32_size(sz), false);
+ supervisor_allocation *allocation = allocate_memory(align32_size(sz), false, false);
if (allocation) {
memset(allocation->ptr, 0, sz);
return allocation->ptr;
diff --git a/shared-module/usb_midi/__init__.c b/shared-module/usb_midi/__init__.c
index 73a314b99..5afdd1821 100644
--- a/shared-module/usb_midi/__init__.c
+++ b/shared-module/usb_midi/__init__.c
@@ -45,7 +45,7 @@ void usb_midi_init(void) {
uint16_t portout_size = align32_size(sizeof(usb_midi_portout_obj_t));
// For each embedded MIDI Jack in the descriptor we create a Port
- usb_midi_allocation = allocate_memory(tuple_size + portin_size + portout_size, false);
+ usb_midi_allocation = allocate_memory(tuple_size + portin_size + portout_size, false, false);
mp_obj_tuple_t *ports = (mp_obj_tuple_t *) usb_midi_allocation->ptr;
ports->base.type = &mp_type_tuple;
diff --git a/supervisor/memory.h b/supervisor/memory.h
index f4359ca46..4307e3f21 100755
--- a/supervisor/memory.h
+++ b/supervisor/memory.h
@@ -33,23 +33,36 @@
#include <stdbool.h>
#include <stdint.h>
+#include <stddef.h>
typedef struct {
uint32_t* ptr;
- uint32_t length; // in bytes
} supervisor_allocation;
-void memory_init(void);
void free_memory(supervisor_allocation* allocation);
+
+// Find the allocation with the given ptr, NULL if not found. When called from the context of a
+// supervisor_move_memory() callback, finds the allocation that had that ptr *before* the move, but
+// the returned allocation already contains the ptr after the move.
+// When called with NULL, may return either NULL or an unused allocation whose ptr is NULL (this is
+// a feature used internally in allocate_memory to save code size). Passing the return value to
+// free_memory() is a permissible no-op in either case.
supervisor_allocation* allocation_from_ptr(void *ptr);
+
supervisor_allocation* allocate_remaining_memory(void);
// Allocate a piece of a given length in bytes. If high_address is true then it should be allocated
// at a lower address from the top of the stack. Otherwise, addresses will increase starting after
-// statically allocated memory.
-supervisor_allocation* allocate_memory(uint32_t length, bool high_address);
+// statically allocated memory. If movable is false, memory will be taken from outside the GC heap
+// and will stay stationary until freed. While the VM is running, this will fail unless a previous
+// allocation of exactly matching length has recently been freed. If movable is true, memory will be
+// taken from either outside or inside the GC heap, and when the VM exits, will be moved outside.
+// The ptr of the returned supervisor_allocation will change at that point. If you need to be
+// notified of that, add your own callback function at the designated place near the end of
+// supervisor_move_memory().
+supervisor_allocation* allocate_memory(uint32_t length, bool high_address, bool movable);
static inline uint16_t align32_size(uint16_t size) {
if (size % 4 != 0) {
@@ -58,7 +71,10 @@ static inline uint16_t align32_size(uint16_t size) {
return size;
}
-// Called after the heap is freed in case the supervisor wants to save some values.
+size_t get_allocation_length(supervisor_allocation* allocation);
+
+// Called after the GC heap is freed, transfers movable allocations from the GC heap to the
+// supervisor heap and compacts the supervisor heap.
void supervisor_move_memory(void);
#endif // MICROPY_INCLUDED_SUPERVISOR_MEMORY_H
diff --git a/supervisor/port.h b/supervisor/port.h
index f5b3c15d1..5bc06bc4e 100644
--- a/supervisor/port.h
+++ b/supervisor/port.h
@@ -61,7 +61,8 @@ uint32_t *port_stack_get_limit(void);
// Get stack top address
uint32_t *port_stack_get_top(void);
-supervisor_allocation* port_fixed_stack(void);
+// True if stack is not located inside heap (at the top)
+bool port_has_fixed_stack(void);
// Get heap bottom address
uint32_t *port_heap_get_bottom(void);
@@ -69,8 +70,6 @@ uint32_t *port_heap_get_bottom(void);
// Get heap top address
uint32_t *port_heap_get_top(void);
-supervisor_allocation* port_fixed_heap(void);
-
// Save and retrieve a word from memory that is preserved over reset. Used for safe mode.
void port_set_saved_word(uint32_t);
uint32_t port_get_saved_word(void);
diff --git a/supervisor/shared/display.c b/supervisor/shared/display.c
index a9ae25884..de45e2672 100644
--- a/supervisor/shared/display.c
+++ b/supervisor/shared/display.c
@@ -82,7 +82,7 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) {
uint16_t total_tiles = width_in_tiles * height_in_tiles;
// First try to allocate outside the heap. This will fail when the VM is running.
- tilegrid_tiles = allocate_memory(align32_size(total_tiles), false);
+ tilegrid_tiles = allocate_memory(align32_size(total_tiles), false, false);
uint8_t* tiles;
if (tilegrid_tiles == NULL) {
tiles = m_malloc(total_tiles, true);
@@ -133,7 +133,7 @@ void supervisor_display_move_memory(void) {
grid->tiles == MP_STATE_VM(terminal_tilegrid_tiles)) {
uint16_t total_tiles = grid->width_in_tiles * grid->height_in_tiles;
- tilegrid_tiles = allocate_memory(align32_size(total_tiles), false);
+ tilegrid_tiles = allocate_memory(align32_size(total_tiles), false, false);
if (tilegrid_tiles != NULL) {
memcpy(tilegrid_tiles->ptr, grid->tiles, total_tiles);
grid->tiles = (uint8_t*) tilegrid_tiles->ptr;
diff --git a/supervisor/shared/external_flash/external_flash.c b/supervisor/shared/external_flash/external_flash.c
index 5bde7fd48..e2d767235 100644
--- a/supervisor/shared/external_flash/external_flash.c
+++ b/supervisor/shared/external_flash/external_flash.c
@@ -338,7 +338,7 @@ static bool allocate_ram_cache(void) {
uint32_t table_size = blocks_per_sector * pages_per_block * sizeof(uint32_t);
// Attempt to allocate outside the heap first.
- supervisor_cache = allocate_memory(table_size + SPI_FLASH_ERASE_SIZE, false);
+ supervisor_cache = allocate_memory(table_size + SPI_FLASH_ERASE_SIZE, false, false);
if (supervisor_cache != NULL) {
MP_STATE_VM(flash_ram_cache) = (uint8_t **) supervisor_cache->ptr;
uint8_t* page_start = (uint8_t *) supervisor_cache->ptr + table_size;
diff --git a/supervisor/shared/memory.c b/supervisor/shared/memory.c
index 0f96ae273..2be3b42d6 100755
--- a/supervisor/shared/memory.c
+++ b/supervisor/shared/memory.c
@@ -27,78 +27,81 @@
#include "supervisor/memory.h"
#include "supervisor/port.h"
-#include <stddef.h>
+#include <string.h>
+#include "py/gc.h"
#include "supervisor/shared/display.h"
#define CIRCUITPY_SUPERVISOR_ALLOC_COUNT (12)
-// Using a zero length to mark an unused allocation makes the code a bit shorter (but makes it
-// impossible to support zero-length allocations).
-#define FREE 0
-
// The lowest two bits of a valid length are always zero, so we can use them to mark an allocation
-// as freed by the client but not yet reclaimed into the FREE middle.
+// as a hole (freed by the client but not yet reclaimed into the free middle) and as movable.
+#define FLAGS 3
#define HOLE 1
+#define MOVABLE 2
static supervisor_allocation allocations[CIRCUITPY_SUPERVISOR_ALLOC_COUNT];
-// We use uint32_t* to ensure word (4 byte) alignment.
-uint32_t* low_address;
-uint32_t* high_address;
+supervisor_allocation* old_allocations;
-void memory_init(void) {
- low_address = port_heap_get_bottom();
- high_address = port_heap_get_top();
-}
+typedef struct _supervisor_allocation_node {
+ struct _supervisor_allocation_node* next;
+ size_t length;
+ // We use uint32_t to ensure word (4 byte) alignment.
+ uint32_t data[];
+} supervisor_allocation_node;
+
+supervisor_allocation_node* low_head;
+supervisor_allocation_node* high_head;
+
+// Intermediate (void*) is to suppress -Wcast-align warning. Alignment will always be correct
+// because this only reverses how (alloc)->ptr was obtained as &(node->data[0]).
+#define ALLOCATION_NODE(alloc) ((supervisor_allocation_node*)(void*)((char*)((alloc)->ptr) - sizeof(supervisor_allocation_node)))
void free_memory(supervisor_allocation* allocation) {
- if (allocation == NULL) {
+ if (allocation == NULL || allocation->ptr == NULL) {
return;
}
- int32_t index = 0;
- bool found = false;
- for (index = 0; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT; index++) {
- found = allocation == &allocations[index];
- if (found) {
- break;
- }
+ supervisor_allocation_node* node = ALLOCATION_NODE(allocation);
+ if (node == low_head) {
+ do {
+ low_head = low_head->next;
+ } while (low_head != NULL && (low_head->length & HOLE));
}
- if (!found) {
- // Bad!
- // TODO(tannewt): Add a way to escape into safe mode on error.
+ else if (node == high_head) {
+ do {
+ high_head = high_head->next;
+ } while (high_head != NULL && (high_head->length & HOLE));
}
- if (allocation->ptr == high_address) {
- high_address += allocation->length / 4;
- allocation->length = FREE;
- for (index++; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT; index++) {
- if (!(allocations[index].length & HOLE)) {
- break;
- }
- // Division automatically shifts out the HOLE bit.
- high_address += allocations[index].length / 4;
- allocations[index].length = FREE;
- }
- } else if (allocation->ptr + allocation->length / 4 == low_address) {
- low_address = allocation->ptr;
- allocation->length = FREE;
- for (index--; index >= 0; index--) {
- if (!(allocations[index].length & HOLE)) {
- break;
+ else {
+ // Check if it's in the list of embedded allocations.
+ supervisor_allocation_node** emb = &MP_STATE_VM(first_embedded_allocation);
+ while (*emb != NULL) {
+ if (*emb == node) {
+ // Found, remove it from the list.
+ *emb = node->next;
+ m_free(node
+#if MICROPY_MALLOC_USES_ALLOCATED_SIZE
+ , sizeof(supervisor_allocation_node) + (node->length & ~FLAGS)
+#endif
+ );
+ goto done;
}
- low_address -= allocations[index].length / 4;
- allocations[index].length = FREE;
+ emb = &((*emb)->next);
}
- } else {
- // Freed memory isn't in the middle so skip updating bounds. The memory will be added to the
- // middle when the memory to the inside is freed. We still need its length, but setting
- // only the lowest bit is nondestructive.
- allocation->length |= HOLE;
+ // Else it must be within the low or high ranges and becomes a hole.
+ node->length = ((node->length & ~FLAGS) | HOLE);
}
+done:
+ allocation->ptr = NULL;
}
supervisor_allocation* allocation_from_ptr(void *ptr) {
+ // When called from the context of supervisor_move_memory() (old_allocations != NULL), search
+ // by old pointer to give clients a way of mapping from old to new pointer. But not if
+ // ptr == NULL, then the caller wants an allocation whose current ptr is NULL.
+ supervisor_allocation* list = (old_allocations && ptr) ? old_allocations : &allocations[0];
for (size_t index = 0; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT; index++) {
- if (allocations[index].ptr == ptr) {
+ if (list[index].ptr == ptr) {
return &allocations[index];
}
}
@@ -106,50 +109,172 @@ supervisor_allocation* allocation_from_ptr(void *ptr) {
}
supervisor_allocation* allocate_remaining_memory(void) {
- if (low_address == high_address) {
- return NULL;
+ uint32_t* low_address = low_head ? low_head->data + low_head->length / 4 : port_heap_get_bottom();
+ uint32_t* high_address = high_head ? (uint32_t*)high_head : port_heap_get_top();
+ return allocate_memory((high_address - low_address) * 4 - sizeof(supervisor_allocation_node), false, false);
+}
+
+static supervisor_allocation_node* find_hole(supervisor_allocation_node* node, size_t length) {
+ for (; node != NULL; node = node->next) {
+ if (node->length == (length | HOLE)) {
+ break;
+ }
}
- return allocate_memory((high_address - low_address) * 4, false);
+ return node;
}
-supervisor_allocation* allocate_memory(uint32_t length, bool high) {
+static supervisor_allocation_node* allocate_memory_node(uint32_t length, bool high, bool movable) {
+ // supervisor_move_memory() currently does not support movable allocations on the high side, it
+ // must be extended first if this is ever needed.
+ assert(!(high && movable));
if (length == 0 || length % 4 != 0) {
return NULL;
}
- uint8_t index = 0;
- int8_t direction = 1;
- if (high) {
- index = CIRCUITPY_SUPERVISOR_ALLOC_COUNT - 1;
- direction = -1;
- }
- supervisor_allocation* alloc;
- for (; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT; index += direction) {
- alloc = &allocations[index];
- if (alloc->length == FREE && (high_address - low_address) * 4 >= (int32_t) length) {
- break;
+ // 1. Matching hole on the requested side?
+ supervisor_allocation_node* node = find_hole(high ? high_head : low_head, length);
+ if (!node) {
+ // 2. Enough free space in the middle?
+ uint32_t* low_address = low_head ? low_head->data + low_head->length / 4 : port_heap_get_bottom();
+ uint32_t* high_address = high_head ? (uint32_t*)high_head : port_heap_get_top();
+ if ((high_address - low_address) * 4 >= (int32_t)(sizeof(supervisor_allocation_node) + length)) {
+ if (high) {
+ high_address -= (sizeof(supervisor_allocation_node) + length) / 4;
+ node = (supervisor_allocation_node*)high_address;
+ node->next = high_head;
+ high_head = node;
+ }
+ else {
+ node = (supervisor_allocation_node*)low_address;
+ node->next = low_head;
+ low_head = node;
+ }
}
- // If a hole matches in length exactly, we can reuse it.
- if (alloc->length == (length | HOLE)) {
- alloc->length = length;
- return alloc;
+ else {
+ // 3. Matching hole on the other side?
+ node = find_hole(high ? low_head : high_head, length);
+ if (!node) {
+ // 4. GC allocation?
+ if (movable && gc_alloc_possible()) {
+ node = m_malloc_maybe(sizeof(supervisor_allocation_node) + length, true);
+ if (node) {
+ node->next = MP_STATE_VM(first_embedded_allocation);
+ MP_STATE_VM(first_embedded_allocation) = node;
+ }
+ }
+ if (!node) {
+ // 5. Give up.
+ return NULL;
+ }
+ }
}
}
- if (index >= CIRCUITPY_SUPERVISOR_ALLOC_COUNT) {
+ node->length = length;
+ if (movable) {
+ node->length |= MOVABLE;
+ }
+ return node;
+}
+
+supervisor_allocation* allocate_memory(uint32_t length, bool high, bool movable) {
+ supervisor_allocation_node* node = allocate_memory_node(length, high, movable);
+ if (!node) {
return NULL;
}
- if (high) {
- high_address -= length / 4;
- alloc->ptr = high_address;
- } else {
- alloc->ptr = low_address;
- low_address += length / 4;
+ // Find the first free allocation.
+ supervisor_allocation* alloc = allocation_from_ptr(NULL);
+ if (!alloc) {
+ // We should free node again to avoid leaking, but something is wrong anyway if clients try
+ // to make more allocations than available, so don't bother.
+ return NULL;
}
- alloc->length = length;
+ alloc->ptr = &(node->data[0]);
return alloc;
}
+size_t get_allocation_length(supervisor_allocation* allocation) {
+ return ALLOCATION_NODE(allocation)->length & ~FLAGS;
+}
+
void supervisor_move_memory(void) {
+ // This must be called exactly after freeing the heap, so that the embedded allocations, if any,
+ // are now in the free region.
+ assert(MP_STATE_VM(first_embedded_allocation) == NULL || (low_head < MP_STATE_VM(first_embedded_allocation) && MP_STATE_VM(first_embedded_allocation) < high_head));
+
+ // Save the old pointers for allocation_from_ptr().
+ supervisor_allocation old_allocations_array[CIRCUITPY_SUPERVISOR_ALLOC_COUNT];
+ memcpy(old_allocations_array, allocations, sizeof(allocations));
+
+ // Compact the low side. Traverse the list repeatedly, finding movable allocations preceded by a
+ // hole and swapping them, until no more are found. This is not the most runtime-efficient way,
+ // but probably the shortest and simplest code.
+ bool acted;
+ do {
+ acted = false;
+ supervisor_allocation_node** nodep = &low_head;
+ while (*nodep != NULL && (*nodep)->next != NULL) {
+ if (((*nodep)->length & MOVABLE) && ((*nodep)->next->length & HOLE)) {
+ supervisor_allocation_node* oldnode = *nodep;
+ supervisor_allocation_node* start = oldnode->next;
+ supervisor_allocation* alloc = allocation_from_ptr(&(oldnode->data[0]));
+ assert(alloc != NULL);
+ alloc->ptr = &(start->data[0]);
+ oldnode->next = start->next;
+ size_t holelength = start->length;
+ size_t size = sizeof(supervisor_allocation_node) + (oldnode->length & ~FLAGS);
+ memmove(start, oldnode, size);
+ supervisor_allocation_node* newhole = (supervisor_allocation_node*)(void*)((char*)start + size);
+ newhole->next = start;
+ newhole->length = holelength;
+ *nodep = newhole;
+ acted = true;
+ }
+ nodep = &((*nodep)->next);
+ }
+ } while (acted);
+ // Any holes bubbled to the top can be absorbed into the free middle.
+ while (low_head != NULL && (low_head->length & HOLE)) {
+ low_head = low_head->next;
+ };
+
+ // Don't bother compacting the high side, there are no movable allocations and no holes there in
+ // current usage.
+
+ // Promote the embedded allocations to top-level ones, compacting them at the beginning of the
+ // now free region (or possibly in matching holes).
+ // The linked list is unordered, but allocations must be processed in order to avoid risking
+ // overwriting each other. To that end, repeatedly find the lowest element of the list, remove
+ // it from the list, and process it. This ad-hoc selection sort results in substantially shorter
+ // code than using the qsort() function from the C library.
+ while (MP_STATE_VM(first_embedded_allocation)) {
+ // First element is first candidate.
+ supervisor_allocation_node** pminnode = &MP_STATE_VM(first_embedded_allocation);
+ // Iterate from second element (if any) on.
+ for (supervisor_allocation_node** pnode = &(MP_STATE_VM(first_embedded_allocation)->next); *pnode != NULL; pnode = &(*pnode)->next) {
+ if (*pnode < *pminnode) {
+ pminnode = pnode;
+ }
+ }
+ // Remove from list.
+ supervisor_allocation_node* node = *pminnode;
+ *pminnode = node->next;
+ // Process.
+ size_t length = (node->length & ~FLAGS);
+ supervisor_allocation* alloc = allocation_from_ptr(&(node->data[0]));
+ assert(alloc != NULL);
+ // This may overwrite the header of node if it happened to be there already, but not the
+ // data.
+ supervisor_allocation_node* new_node = allocate_memory_node(length, false, true);
+ // There must be enough free space.
+ assert(new_node != NULL);
+ memmove(&(new_node->data[0]), &(node->data[0]), length);
+ alloc->ptr = &(new_node->data[0]);
+ }
+
+ // Notify clients that their movable allocations may have moved.
+ old_allocations = &old_allocations_array[0];
#if CIRCUITPY_DISPLAYIO
supervisor_display_move_memory();
#endif
+ // Add calls to further clients here.
+ old_allocations = NULL;
}
diff --git a/supervisor/shared/stack.c b/supervisor/shared/stack.c
index e7aa956b0..afea20401 100755
--- a/supervisor/shared/stack.c
+++ b/supervisor/shared/stack.c
@@ -34,36 +34,42 @@
extern uint32_t _estack;
+// Requested size.
static uint32_t next_stack_size = CIRCUITPY_DEFAULT_STACK_SIZE;
static uint32_t current_stack_size = 0;
-supervisor_allocation* stack_alloc = NULL;
+// Actual location and size, may be larger than requested.
+static uint32_t* stack_limit = NULL;
+static size_t stack_length = 0;
#define EXCEPTION_STACK_SIZE 1024
void allocate_stack(void) {
- if (port_fixed_stack() != NULL) {
- stack_alloc = port_fixed_stack();
- current_stack_size = stack_alloc->length;
+ if (port_has_fixed_stack()) {
+ stack_limit = port_stack_get_limit();
+ stack_length = (port_stack_get_top() - stack_limit)*sizeof(uint32_t);
+ current_stack_size = stack_length;
} else {
mp_uint_t regs[10];
mp_uint_t sp = cpu_get_regs_and_sp(regs);
mp_uint_t c_size = (uint32_t) port_stack_get_top() - sp;
- stack_alloc = allocate_memory(c_size + next_stack_size + EXCEPTION_STACK_SIZE, true);
+ supervisor_allocation* stack_alloc = allocate_memory(c_size + next_stack_size + EXCEPTION_STACK_SIZE, true, false);
if (stack_alloc == NULL) {
- stack_alloc = allocate_memory(c_size + CIRCUITPY_DEFAULT_STACK_SIZE + EXCEPTION_STACK_SIZE, true);
+ stack_alloc = allocate_memory(c_size + CIRCUITPY_DEFAULT_STACK_SIZE + EXCEPTION_STACK_SIZE, true, false);
current_stack_size = CIRCUITPY_DEFAULT_STACK_SIZE;
} else {
current_stack_size = next_stack_size;
}
+ stack_limit = stack_alloc->ptr;
+ stack_length = get_allocation_length(stack_alloc);
}
- *stack_alloc->ptr = STACK_CANARY_VALUE;
+ *stack_limit = STACK_CANARY_VALUE;
}
inline bool stack_ok(void) {
- return stack_alloc == NULL || *stack_alloc->ptr == STACK_CANARY_VALUE;
+ return stack_limit == NULL || *stack_limit == STACK_CANARY_VALUE;
}
inline void assert_heap_ok(void) {
@@ -77,18 +83,26 @@ void stack_init(void) {
}
void stack_resize(void) {
- if (stack_alloc == NULL) {
+ if (stack_limit == NULL) {
return;
}
if (next_stack_size == current_stack_size) {
- *stack_alloc->ptr = STACK_CANARY_VALUE;
+ *stack_limit = STACK_CANARY_VALUE;
return;
}
- free_memory(stack_alloc);
- stack_alloc = NULL;
+ free_memory(allocation_from_ptr(stack_limit));
+ stack_limit = NULL;
allocate_stack();
}
+uint32_t* stack_get_bottom(void) {
+ return stack_limit;
+}
+
+size_t stack_get_length(void) {
+ return stack_length;
+}
+
void set_next_stack_size(uint32_t size) {
next_stack_size = size;
}
diff --git a/supervisor/shared/stack.h b/supervisor/shared/stack.h
index 7096f0b3e..1c75de5f7 100755
--- a/supervisor/shared/stack.h
+++ b/supervisor/shared/stack.h
@@ -31,10 +31,12 @@
#include "supervisor/memory.h"
-extern supervisor_allocation* stack_alloc;
-
void stack_init(void);
void stack_resize(void);
+// Actual stack location and size, may be larger than requested.
+uint32_t* stack_get_bottom(void);
+size_t stack_get_length(void);
+// Next/current requested stack size.
void set_next_stack_size(uint32_t size);
uint32_t get_current_stack_size(void);
bool stack_ok(void);