blob: fca5050e79222edeba04685cc2b1c6e3fe16326f [file] [log] [blame]
Damien George04b91472014-05-03 23:27:38 +01001/*
2 * This file is part of the Micro Python project, http://micropython.org/
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (c) 2013, 2014 Damien P. George
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
25 */
26
Paul Sokolovskyc86889d2014-04-20 11:45:16 +030027#include <assert.h>
Damiendcced922013-10-21 23:45:08 +010028#include <stdio.h>
Damiendcced922013-10-21 23:45:08 +010029#include <string.h>
mux4f7e9f52014-04-03 23:55:12 +020030#include <stdbool.h>
Damien George109c1de2014-10-31 21:30:46 +000031#include <stdint.h>
Damiendcced922013-10-21 23:45:08 +010032
Damiend99b0522013-12-21 18:17:45 +000033#include "mpconfig.h"
Paul Sokolovskye807fa82014-04-02 20:36:32 +030034#include "misc.h"
Damiendcced922013-10-21 23:45:08 +010035#include "gc.h"
36
mux4f7e9f52014-04-03 23:55:12 +020037#include "qstr.h"
38#include "obj.h"
muxcc849f72014-04-05 15:49:03 +020039#include "runtime.h"
mux4f7e9f52014-04-03 23:55:12 +020040
Damien Georged3ebe482014-01-07 15:20:33 +000041#if MICROPY_ENABLE_GC
42
stijn9acb5e42014-06-18 12:29:03 +020043#if 0 // print debugging info
Paul Sokolovskyaf19cbd2014-02-10 21:45:54 +020044#define DEBUG_PRINT (1)
Paul Sokolovsky44739e22014-02-16 18:11:42 +020045#define DEBUG_printf DEBUG_printf
Paul Sokolovskyaf19cbd2014-02-10 21:45:54 +020046#else // don't print debugging info
Damien George41eb6082014-02-26 22:40:35 +000047#define DEBUG_printf(...) (void)0
Paul Sokolovskyaf19cbd2014-02-10 21:45:54 +020048#endif
49
Damien George516b09e2014-08-28 23:06:38 +010050// make this 1 to dump the heap each time it changes
51#define EXTENSIVE_HEAP_PROFILING (0)
52
Damiendcced922013-10-21 23:45:08 +010053#define WORDS_PER_BLOCK (4)
54#define BYTES_PER_BLOCK (WORDS_PER_BLOCK * BYTES_PER_WORD)
55#define STACK_SIZE (64) // tunable; minimum is 1
56
Paul Sokolovsky520e2f52014-02-12 18:31:30 +020057STATIC byte *gc_alloc_table_start;
Damien George40f3c022014-07-03 13:25:24 +010058STATIC mp_uint_t gc_alloc_table_byte_len;
Damien George12bab722014-04-05 20:35:48 +010059#if MICROPY_ENABLE_FINALISER
60STATIC byte *gc_finaliser_table_start;
61#endif
Damien George37ada232014-10-16 21:50:39 +010062// We initialise gc_pool_start to a dummy value so it stays out of the bss
63// section. This makes sure we don't trace this pointer in a collect cycle.
64// If we did trace it, it would make the first block of the heap always
65// reachable, and hence we can never free that block.
66STATIC mp_uint_t *gc_pool_start = (void*)4;
Damien George40f3c022014-07-03 13:25:24 +010067STATIC mp_uint_t *gc_pool_end;
Damiendcced922013-10-21 23:45:08 +010068
Paul Sokolovsky520e2f52014-02-12 18:31:30 +020069STATIC int gc_stack_overflow;
Damien George40f3c022014-07-03 13:25:24 +010070STATIC mp_uint_t gc_stack[STACK_SIZE];
71STATIC mp_uint_t *gc_sp;
Damien George109c1de2014-10-31 21:30:46 +000072STATIC uint16_t gc_lock_depth;
73uint16_t gc_auto_collect_enabled;
Damien Georged5e7f6e2014-08-22 18:17:02 +010074STATIC mp_uint_t gc_last_free_atb_index;
Damiendcced922013-10-21 23:45:08 +010075
Damiendcced922013-10-21 23:45:08 +010076// ATB = allocation table byte
77// 0b00 = FREE -- free block
78// 0b01 = HEAD -- head of a chain of blocks
79// 0b10 = TAIL -- in the tail of a chain of blocks
80// 0b11 = MARK -- marked head block
81
82#define AT_FREE (0)
83#define AT_HEAD (1)
84#define AT_TAIL (2)
85#define AT_MARK (3)
86
87#define BLOCKS_PER_ATB (4)
88#define ATB_MASK_0 (0x03)
89#define ATB_MASK_1 (0x0c)
90#define ATB_MASK_2 (0x30)
91#define ATB_MASK_3 (0xc0)
92
93#define ATB_0_IS_FREE(a) (((a) & ATB_MASK_0) == 0)
94#define ATB_1_IS_FREE(a) (((a) & ATB_MASK_1) == 0)
95#define ATB_2_IS_FREE(a) (((a) & ATB_MASK_2) == 0)
96#define ATB_3_IS_FREE(a) (((a) & ATB_MASK_3) == 0)
97
98#define BLOCK_SHIFT(block) (2 * ((block) & (BLOCKS_PER_ATB - 1)))
99#define ATB_GET_KIND(block) ((gc_alloc_table_start[(block) / BLOCKS_PER_ATB] >> BLOCK_SHIFT(block)) & 3)
100#define ATB_ANY_TO_FREE(block) do { gc_alloc_table_start[(block) / BLOCKS_PER_ATB] &= (~(AT_MARK << BLOCK_SHIFT(block))); } while (0)
101#define ATB_FREE_TO_HEAD(block) do { gc_alloc_table_start[(block) / BLOCKS_PER_ATB] |= (AT_HEAD << BLOCK_SHIFT(block)); } while (0)
102#define ATB_FREE_TO_TAIL(block) do { gc_alloc_table_start[(block) / BLOCKS_PER_ATB] |= (AT_TAIL << BLOCK_SHIFT(block)); } while (0)
103#define ATB_HEAD_TO_MARK(block) do { gc_alloc_table_start[(block) / BLOCKS_PER_ATB] |= (AT_MARK << BLOCK_SHIFT(block)); } while (0)
104#define ATB_MARK_TO_HEAD(block) do { gc_alloc_table_start[(block) / BLOCKS_PER_ATB] &= (~(AT_TAIL << BLOCK_SHIFT(block))); } while (0)
105
Damien George40f3c022014-07-03 13:25:24 +0100106#define BLOCK_FROM_PTR(ptr) (((ptr) - (mp_uint_t)gc_pool_start) / BYTES_PER_BLOCK)
107#define PTR_FROM_BLOCK(block) (((block) * BYTES_PER_BLOCK + (mp_uint_t)gc_pool_start))
Damiendcced922013-10-21 23:45:08 +0100108#define ATB_FROM_BLOCK(bl) ((bl) / BLOCKS_PER_ATB)
109
Damien George12bab722014-04-05 20:35:48 +0100110#if MICROPY_ENABLE_FINALISER
111// FTB = finaliser table byte
112// if set, then the corresponding block may have a finaliser
113
114#define BLOCKS_PER_FTB (8)
115
116#define FTB_GET(block) ((gc_finaliser_table_start[(block) / BLOCKS_PER_FTB] >> ((block) & 7)) & 1)
117#define FTB_SET(block) do { gc_finaliser_table_start[(block) / BLOCKS_PER_FTB] |= (1 << ((block) & 7)); } while (0)
118#define FTB_CLEAR(block) do { gc_finaliser_table_start[(block) / BLOCKS_PER_FTB] &= (~(1 << ((block) & 7))); } while (0)
119#endif
120
Damienbb5316b2013-10-22 21:12:29 +0100121// TODO waste less memory; currently requires that all entries in alloc_table have a corresponding block in pool
122void gc_init(void *start, void *end) {
123 // align end pointer on block boundary
Damien George40f3c022014-07-03 13:25:24 +0100124 end = (void*)((mp_uint_t)end & (~(BYTES_PER_BLOCK - 1)));
stijndef10ce2014-06-18 10:20:41 +0200125 DEBUG_printf("Initializing GC heap: %p..%p = " UINT_FMT " bytes\n", start, end, (byte*)end - (byte*)start);
Damienbb5316b2013-10-22 21:12:29 +0100126
Damien George12bab722014-04-05 20:35:48 +0100127 // calculate parameters for GC (T=total, A=alloc table, F=finaliser table, P=pool; all in bytes):
128 // T = A + F + P
129 // F = A * BLOCKS_PER_ATB / BLOCKS_PER_FTB
130 // P = A * BLOCKS_PER_ATB * BYTES_PER_BLOCK
131 // => T = A * (1 + BLOCKS_PER_ATB / BLOCKS_PER_FTB + BLOCKS_PER_ATB * BYTES_PER_BLOCK)
Damien George40f3c022014-07-03 13:25:24 +0100132 mp_uint_t total_byte_len = (byte*)end - (byte*)start;
Damien George12bab722014-04-05 20:35:48 +0100133#if MICROPY_ENABLE_FINALISER
134 gc_alloc_table_byte_len = total_byte_len * BITS_PER_BYTE / (BITS_PER_BYTE + BITS_PER_BYTE * BLOCKS_PER_ATB / BLOCKS_PER_FTB + BITS_PER_BYTE * BLOCKS_PER_ATB * BYTES_PER_BLOCK);
135#else
136 gc_alloc_table_byte_len = total_byte_len / (1 + BITS_PER_BYTE / 2 * BYTES_PER_BLOCK);
137#endif
138
Damienbb5316b2013-10-22 21:12:29 +0100139 gc_alloc_table_start = (byte*)start;
mux4f7e9f52014-04-03 23:55:12 +0200140
Damien George12bab722014-04-05 20:35:48 +0100141#if MICROPY_ENABLE_FINALISER
Damien Georgea1d3ee32014-08-08 12:33:49 +0100142 mp_uint_t gc_finaliser_table_byte_len = (gc_alloc_table_byte_len * BLOCKS_PER_ATB + BLOCKS_PER_FTB - 1) / BLOCKS_PER_FTB;
Damien George12bab722014-04-05 20:35:48 +0100143 gc_finaliser_table_start = gc_alloc_table_start + gc_alloc_table_byte_len;
144#endif
mux4f7e9f52014-04-03 23:55:12 +0200145
Damien George40f3c022014-07-03 13:25:24 +0100146 mp_uint_t gc_pool_block_len = gc_alloc_table_byte_len * BLOCKS_PER_ATB;
147 gc_pool_start = (mp_uint_t*)((byte*)end - gc_pool_block_len * BYTES_PER_BLOCK);
148 gc_pool_end = (mp_uint_t*)end;
Damienbb5316b2013-10-22 21:12:29 +0100149
Damien Georgea1d3ee32014-08-08 12:33:49 +0100150#if MICROPY_ENABLE_FINALISER
151 assert((byte*)gc_pool_start >= gc_finaliser_table_start + gc_finaliser_table_byte_len);
152#endif
153
Damienbb5316b2013-10-22 21:12:29 +0100154 // clear ATBs
155 memset(gc_alloc_table_start, 0, gc_alloc_table_byte_len);
156
Damien George12bab722014-04-05 20:35:48 +0100157#if MICROPY_ENABLE_FINALISER
158 // clear FTBs
159 memset(gc_finaliser_table_start, 0, gc_finaliser_table_byte_len);
160#endif
mux4f7e9f52014-04-03 23:55:12 +0200161
Damien Georged5e7f6e2014-08-22 18:17:02 +0100162 // set last free ATB index to start of heap
163 gc_last_free_atb_index = 0;
164
Damien George12bab722014-04-05 20:35:48 +0100165 // unlock the GC
Damien George443e0182014-04-08 11:31:21 +0000166 gc_lock_depth = 0;
Damien George12bab722014-04-05 20:35:48 +0100167
Damien George109c1de2014-10-31 21:30:46 +0000168 // allow auto collection
169 gc_auto_collect_enabled = 1;
170
Paul Sokolovskyaf19cbd2014-02-10 21:45:54 +0200171 DEBUG_printf("GC layout:\n");
Damien George12bab722014-04-05 20:35:48 +0100172 DEBUG_printf(" alloc table at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", gc_alloc_table_start, gc_alloc_table_byte_len, gc_alloc_table_byte_len * BLOCKS_PER_ATB);
173#if MICROPY_ENABLE_FINALISER
174 DEBUG_printf(" finaliser table at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", gc_finaliser_table_start, gc_finaliser_table_byte_len, gc_finaliser_table_byte_len * BLOCKS_PER_FTB);
175#endif
176 DEBUG_printf(" pool at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", gc_pool_start, gc_pool_block_len * BYTES_PER_BLOCK, gc_pool_block_len);
Damienbb5316b2013-10-22 21:12:29 +0100177}
178
Damien George443e0182014-04-08 11:31:21 +0000179void gc_lock(void) {
180 gc_lock_depth++;
181}
182
183void gc_unlock(void) {
184 gc_lock_depth--;
185}
186
Dave Hylands2fe841d2014-06-30 22:49:21 -0700187bool gc_is_locked(void) {
188 return gc_lock_depth != 0;
189}
190
Damienfd8b6bc2013-10-22 20:26:36 +0100191#define VERIFY_PTR(ptr) ( \
192 (ptr & (BYTES_PER_BLOCK - 1)) == 0 /* must be aligned on a block */ \
Damien George40f3c022014-07-03 13:25:24 +0100193 && ptr >= (mp_uint_t)gc_pool_start /* must be above start of pool */ \
194 && ptr < (mp_uint_t)gc_pool_end /* must be below end of pool */ \
Damienfd8b6bc2013-10-22 20:26:36 +0100195 )
196
Damiendcced922013-10-21 23:45:08 +0100197#define VERIFY_MARK_AND_PUSH(ptr) \
198 do { \
Damienfd8b6bc2013-10-22 20:26:36 +0100199 if (VERIFY_PTR(ptr)) { \
Damien George40f3c022014-07-03 13:25:24 +0100200 mp_uint_t _block = BLOCK_FROM_PTR(ptr); \
Damiendcced922013-10-21 23:45:08 +0100201 if (ATB_GET_KIND(_block) == AT_HEAD) { \
202 /* an unmarked head, mark it, and push it on gc stack */ \
203 ATB_HEAD_TO_MARK(_block); \
204 if (gc_sp < &gc_stack[STACK_SIZE]) { \
205 *gc_sp++ = _block; \
206 } else { \
207 gc_stack_overflow = 1; \
208 } \
209 } \
210 } \
211 } while (0)
212
Paul Sokolovsky520e2f52014-02-12 18:31:30 +0200213STATIC void gc_drain_stack(void) {
Damiendcced922013-10-21 23:45:08 +0100214 while (gc_sp > gc_stack) {
215 // pop the next block off the stack
Damien George40f3c022014-07-03 13:25:24 +0100216 mp_uint_t block = *--gc_sp;
Damiendcced922013-10-21 23:45:08 +0100217
Damieneefcc792013-10-22 15:25:25 +0100218 // work out number of consecutive blocks in the chain starting with this one
Damien George40f3c022014-07-03 13:25:24 +0100219 mp_uint_t n_blocks = 0;
Damiendcced922013-10-21 23:45:08 +0100220 do {
221 n_blocks += 1;
222 } while (ATB_GET_KIND(block + n_blocks) == AT_TAIL);
223
224 // check this block's children
Damien George40f3c022014-07-03 13:25:24 +0100225 mp_uint_t *scan = (mp_uint_t*)PTR_FROM_BLOCK(block);
226 for (mp_uint_t i = n_blocks * WORDS_PER_BLOCK; i > 0; i--, scan++) {
227 mp_uint_t ptr2 = *scan;
Damiendcced922013-10-21 23:45:08 +0100228 VERIFY_MARK_AND_PUSH(ptr2);
229 }
230 }
231}
232
Paul Sokolovsky520e2f52014-02-12 18:31:30 +0200233STATIC void gc_deal_with_stack_overflow(void) {
Damiendcced922013-10-21 23:45:08 +0100234 while (gc_stack_overflow) {
235 gc_stack_overflow = 0;
236 gc_sp = gc_stack;
237
238 // scan entire memory looking for blocks which have been marked but not their children
Damien George40f3c022014-07-03 13:25:24 +0100239 for (mp_uint_t block = 0; block < gc_alloc_table_byte_len * BLOCKS_PER_ATB; block++) {
Damiendcced922013-10-21 23:45:08 +0100240 // trace (again) if mark bit set
241 if (ATB_GET_KIND(block) == AT_MARK) {
242 *gc_sp++ = block;
243 gc_drain_stack();
244 }
245 }
246 }
247}
248
Paul Sokolovsky755a55f2014-06-05 22:48:02 +0300249#if MICROPY_PY_GC_COLLECT_RETVAL
250uint gc_collected;
251#endif
252
Paul Sokolovsky520e2f52014-02-12 18:31:30 +0200253STATIC void gc_sweep(void) {
Paul Sokolovsky755a55f2014-06-05 22:48:02 +0300254 #if MICROPY_PY_GC_COLLECT_RETVAL
255 gc_collected = 0;
256 #endif
Damiendcced922013-10-21 23:45:08 +0100257 // free unmarked heads and their tails
258 int free_tail = 0;
Damien George40f3c022014-07-03 13:25:24 +0100259 for (mp_uint_t block = 0; block < gc_alloc_table_byte_len * BLOCKS_PER_ATB; block++) {
Damiendcced922013-10-21 23:45:08 +0100260 switch (ATB_GET_KIND(block)) {
261 case AT_HEAD:
Damien George12bab722014-04-05 20:35:48 +0100262#if MICROPY_ENABLE_FINALISER
263 if (FTB_GET(block)) {
264 mp_obj_t obj = (mp_obj_t)PTR_FROM_BLOCK(block);
265 if (((mp_obj_base_t*)obj)->type != MP_OBJ_NULL) {
266 // if the object has a type then see if it has a __del__ method
267 mp_obj_t dest[2];
268 mp_load_method_maybe(obj, MP_QSTR___del__, dest);
269 if (dest[0] != MP_OBJ_NULL) {
270 // load_method returned a method
271 mp_call_method_n_kw(0, 0, dest);
272 }
mux4f7e9f52014-04-03 23:55:12 +0200273 }
Damien George12bab722014-04-05 20:35:48 +0100274 // clear finaliser flag
275 FTB_CLEAR(block);
mux4f7e9f52014-04-03 23:55:12 +0200276 }
Damien George12bab722014-04-05 20:35:48 +0100277#endif
Damiendcced922013-10-21 23:45:08 +0100278 free_tail = 1;
Paul Sokolovsky755a55f2014-06-05 22:48:02 +0300279 #if MICROPY_PY_GC_COLLECT_RETVAL
280 gc_collected++;
281 #endif
Damiendcced922013-10-21 23:45:08 +0100282 // fall through to free the head
283
284 case AT_TAIL:
285 if (free_tail) {
stijnbbcea3f2014-06-16 10:44:29 +0200286 DEBUG_printf("gc_sweep(%p)\n",PTR_FROM_BLOCK(block));
Damiendcced922013-10-21 23:45:08 +0100287 ATB_ANY_TO_FREE(block);
288 }
289 break;
290
291 case AT_MARK:
292 ATB_MARK_TO_HEAD(block);
293 free_tail = 0;
294 break;
295 }
296 }
297}
298
Damien8b3a7c22013-10-23 20:20:17 +0100299void gc_collect_start(void) {
Damien George443e0182014-04-08 11:31:21 +0000300 gc_lock();
Damiendcced922013-10-21 23:45:08 +0100301 gc_stack_overflow = 0;
302 gc_sp = gc_stack;
303}
304
Damien George40f3c022014-07-03 13:25:24 +0100305void gc_collect_root(void **ptrs, mp_uint_t len) {
306 for (mp_uint_t i = 0; i < len; i++) {
307 mp_uint_t ptr = (mp_uint_t)ptrs[i];
Damiendcced922013-10-21 23:45:08 +0100308 VERIFY_MARK_AND_PUSH(ptr);
309 gc_drain_stack();
310 }
311}
312
Damien8b3a7c22013-10-23 20:20:17 +0100313void gc_collect_end(void) {
Damiendcced922013-10-21 23:45:08 +0100314 gc_deal_with_stack_overflow();
315 gc_sweep();
Damien Georged5e7f6e2014-08-22 18:17:02 +0100316 gc_last_free_atb_index = 0;
Damien George443e0182014-04-08 11:31:21 +0000317 gc_unlock();
Damieneefcc792013-10-22 15:25:25 +0100318}
Damiendcced922013-10-21 23:45:08 +0100319
Damieneefcc792013-10-22 15:25:25 +0100320void gc_info(gc_info_t *info) {
Damien George40f3c022014-07-03 13:25:24 +0100321 info->total = (gc_pool_end - gc_pool_start) * sizeof(mp_uint_t);
Damieneefcc792013-10-22 15:25:25 +0100322 info->used = 0;
323 info->free = 0;
324 info->num_1block = 0;
325 info->num_2block = 0;
326 info->max_block = 0;
Damien George40f3c022014-07-03 13:25:24 +0100327 for (mp_uint_t block = 0, len = 0; block < gc_alloc_table_byte_len * BLOCKS_PER_ATB; block++) {
328 mp_uint_t kind = ATB_GET_KIND(block);
Damieneefcc792013-10-22 15:25:25 +0100329 if (kind == AT_FREE || kind == AT_HEAD) {
330 if (len == 1) {
331 info->num_1block += 1;
332 } else if (len == 2) {
333 info->num_2block += 1;
334 }
335 if (len > info->max_block) {
336 info->max_block = len;
337 }
338 }
339 switch (kind) {
Damiendcced922013-10-21 23:45:08 +0100340 case AT_FREE:
Damieneefcc792013-10-22 15:25:25 +0100341 info->free += 1;
342 len = 0;
Damiendcced922013-10-21 23:45:08 +0100343 break;
344
345 case AT_HEAD:
Damieneefcc792013-10-22 15:25:25 +0100346 info->used += 1;
347 len = 1;
348 break;
349
Damiendcced922013-10-21 23:45:08 +0100350 case AT_TAIL:
Damieneefcc792013-10-22 15:25:25 +0100351 info->used += 1;
352 len += 1;
Damiendcced922013-10-21 23:45:08 +0100353 break;
354
355 case AT_MARK:
Damieneefcc792013-10-22 15:25:25 +0100356 // shouldn't happen
Damiendcced922013-10-21 23:45:08 +0100357 break;
358 }
359 }
360
Damieneefcc792013-10-22 15:25:25 +0100361 info->used *= BYTES_PER_BLOCK;
362 info->free *= BYTES_PER_BLOCK;
Damiendcced922013-10-21 23:45:08 +0100363}
364
Damien George40f3c022014-07-03 13:25:24 +0100365void *gc_alloc(mp_uint_t n_bytes, bool has_finaliser) {
366 mp_uint_t n_blocks = ((n_bytes + BYTES_PER_BLOCK - 1) & (~(BYTES_PER_BLOCK - 1))) / BYTES_PER_BLOCK;
stijndef10ce2014-06-18 10:20:41 +0200367 DEBUG_printf("gc_alloc(" UINT_FMT " bytes -> " UINT_FMT " blocks)\n", n_bytes, n_blocks);
Damiendcced922013-10-21 23:45:08 +0100368
Damien George443e0182014-04-08 11:31:21 +0000369 // check if GC is locked
370 if (gc_lock_depth > 0) {
371 return NULL;
Damien George12bab722014-04-05 20:35:48 +0100372 }
373
Damiendcced922013-10-21 23:45:08 +0100374 // check for 0 allocation
375 if (n_blocks == 0) {
376 return NULL;
377 }
378
Damien George40f3c022014-07-03 13:25:24 +0100379 mp_uint_t i;
380 mp_uint_t end_block;
381 mp_uint_t start_block;
382 mp_uint_t n_free = 0;
Damien George109c1de2014-10-31 21:30:46 +0000383 int collected = !gc_auto_collect_enabled;
Damiendcced922013-10-21 23:45:08 +0100384 for (;;) {
385
386 // look for a run of n_blocks available blocks
Damien Georged5e7f6e2014-08-22 18:17:02 +0100387 for (i = gc_last_free_atb_index; i < gc_alloc_table_byte_len; i++) {
388 byte a = gc_alloc_table_start[i];
389 if (ATB_0_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 0; goto found; } } else { n_free = 0; }
390 if (ATB_1_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 1; goto found; } } else { n_free = 0; }
391 if (ATB_2_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 2; goto found; } } else { n_free = 0; }
392 if (ATB_3_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 3; goto found; } } else { n_free = 0; }
393 }
Damiendcced922013-10-21 23:45:08 +0100394
395 // nothing found!
396 if (collected) {
397 return NULL;
398 }
Paul Sokolovsky723a6ed2014-02-11 18:01:38 +0200399 DEBUG_printf("gc_alloc(" UINT_FMT "): no free mem, triggering GC\n", n_bytes);
Damiendcced922013-10-21 23:45:08 +0100400 gc_collect();
401 collected = 1;
402 }
403
404 // found, ending at block i inclusive
405found:
406 // get starting and end blocks, both inclusive
407 end_block = i;
408 start_block = i - n_free + 1;
409
Damien Georgeb796e3d2014-08-28 10:18:40 +0100410 // Set last free ATB index to block after last block we found, for start of
411 // next scan. To reduce fragmentation, we only do this if we were looking
412 // for a single free block, which guarantees that there are no free blocks
Damien George516b09e2014-08-28 23:06:38 +0100413 // before this one. Also, whenever we free or shink a block we must check
414 // if this index needs adjusting (see gc_realloc and gc_free).
Damien Georgeb796e3d2014-08-28 10:18:40 +0100415 if (n_free == 1) {
416 gc_last_free_atb_index = (i + 1) / BLOCKS_PER_ATB;
417 }
Damien Georged5e7f6e2014-08-22 18:17:02 +0100418
Damiendcced922013-10-21 23:45:08 +0100419 // mark first block as used head
420 ATB_FREE_TO_HEAD(start_block);
421
422 // mark rest of blocks as used tail
423 // TODO for a run of many blocks can make this more efficient
Damien George40f3c022014-07-03 13:25:24 +0100424 for (mp_uint_t bl = start_block + 1; bl <= end_block; bl++) {
Damiendcced922013-10-21 23:45:08 +0100425 ATB_FREE_TO_TAIL(bl);
426 }
427
Damien George12bab722014-04-05 20:35:48 +0100428 // get pointer to first block
Damien Georgec0376942014-06-13 22:33:31 +0100429 void *ret_ptr = (void*)(gc_pool_start + start_block * WORDS_PER_BLOCK);
stijndef10ce2014-06-18 10:20:41 +0200430 DEBUG_printf("gc_alloc(%p)\n", ret_ptr);
muxcc849f72014-04-05 15:49:03 +0200431
Damien George32bef312014-04-26 22:23:42 +0100432 // zero out the additional bytes of the newly allocated blocks
Damien Georgedaab6512014-04-25 23:37:55 +0100433 // This is needed because the blocks may have previously held pointers
434 // to the heap and will not be set to something else if the caller
435 // doesn't actually use the entire block. As such they will continue
436 // to point to the heap and may prevent other blocks from being reclaimed.
Damien Georgec0376942014-06-13 22:33:31 +0100437 memset((byte*)ret_ptr + n_bytes, 0, (end_block - start_block + 1) * BYTES_PER_BLOCK - n_bytes);
Damien Georgedaab6512014-04-25 23:37:55 +0100438
Damien George12bab722014-04-05 20:35:48 +0100439#if MICROPY_ENABLE_FINALISER
440 if (has_finaliser) {
Damien George32bef312014-04-26 22:23:42 +0100441 // clear type pointer in case it is never set
442 ((mp_obj_base_t*)ret_ptr)->type = MP_OBJ_NULL;
Damien George12bab722014-04-05 20:35:48 +0100443 // set mp_obj flag only if it has a finaliser
444 FTB_SET(start_block);
445 }
446#endif
447
Damien George516b09e2014-08-28 23:06:38 +0100448 #if EXTENSIVE_HEAP_PROFILING
449 gc_dump_alloc_table();
450 #endif
451
Damien George12bab722014-04-05 20:35:48 +0100452 return ret_ptr;
Damiendcced922013-10-21 23:45:08 +0100453}
454
Damien George12bab722014-04-05 20:35:48 +0100455/*
Damien George40f3c022014-07-03 13:25:24 +0100456void *gc_alloc(mp_uint_t n_bytes) {
mux4f7e9f52014-04-03 23:55:12 +0200457 return _gc_alloc(n_bytes, false);
458}
459
Damien George40f3c022014-07-03 13:25:24 +0100460void *gc_alloc_with_finaliser(mp_uint_t n_bytes) {
mux4f7e9f52014-04-03 23:55:12 +0200461 return _gc_alloc(n_bytes, true);
462}
Damien George12bab722014-04-05 20:35:48 +0100463*/
mux4f7e9f52014-04-03 23:55:12 +0200464
Damienfd8b6bc2013-10-22 20:26:36 +0100465// force the freeing of a piece of memory
466void gc_free(void *ptr_in) {
Damien George443e0182014-04-08 11:31:21 +0000467 if (gc_lock_depth > 0) {
468 // TODO how to deal with this error?
469 return;
Damien George12bab722014-04-05 20:35:48 +0100470 }
471
Damien George40f3c022014-07-03 13:25:24 +0100472 mp_uint_t ptr = (mp_uint_t)ptr_in;
stijnbbcea3f2014-06-16 10:44:29 +0200473 DEBUG_printf("gc_free(%p)\n", ptr);
Damienfd8b6bc2013-10-22 20:26:36 +0100474
475 if (VERIFY_PTR(ptr)) {
Damien George40f3c022014-07-03 13:25:24 +0100476 mp_uint_t block = BLOCK_FROM_PTR(ptr);
Damienfd8b6bc2013-10-22 20:26:36 +0100477 if (ATB_GET_KIND(block) == AT_HEAD) {
Damien George516b09e2014-08-28 23:06:38 +0100478 // set the last_free pointer to this block if it's earlier in the heap
479 if (block / BLOCKS_PER_ATB < gc_last_free_atb_index) {
480 gc_last_free_atb_index = block / BLOCKS_PER_ATB;
481 }
482
Damienfd8b6bc2013-10-22 20:26:36 +0100483 // free head and all of its tail blocks
484 do {
485 ATB_ANY_TO_FREE(block);
486 block += 1;
487 } while (ATB_GET_KIND(block) == AT_TAIL);
Damien George516b09e2014-08-28 23:06:38 +0100488
489 #if EXTENSIVE_HEAP_PROFILING
490 gc_dump_alloc_table();
491 #endif
Damien Georgee7bb0442014-10-23 14:13:05 +0100492 } else {
493 assert(!"bad free");
Damienfd8b6bc2013-10-22 20:26:36 +0100494 }
Damien Georgee7bb0442014-10-23 14:13:05 +0100495 } else if (ptr_in != NULL) {
496 assert(!"bad free");
Damienfd8b6bc2013-10-22 20:26:36 +0100497 }
498}
499
Damien George0b13f3e2014-10-24 23:12:25 +0100500mp_uint_t gc_nbytes(const void *ptr_in) {
Damien George40f3c022014-07-03 13:25:24 +0100501 mp_uint_t ptr = (mp_uint_t)ptr_in;
Damiendcced922013-10-21 23:45:08 +0100502
Damienfd8b6bc2013-10-22 20:26:36 +0100503 if (VERIFY_PTR(ptr)) {
Damien George40f3c022014-07-03 13:25:24 +0100504 mp_uint_t block = BLOCK_FROM_PTR(ptr);
Damiendcced922013-10-21 23:45:08 +0100505 if (ATB_GET_KIND(block) == AT_HEAD) {
506 // work out number of consecutive blocks in the chain starting with this on
Damien George40f3c022014-07-03 13:25:24 +0100507 mp_uint_t n_blocks = 0;
Damiendcced922013-10-21 23:45:08 +0100508 do {
509 n_blocks += 1;
510 } while (ATB_GET_KIND(block + n_blocks) == AT_TAIL);
511 return n_blocks * BYTES_PER_BLOCK;
512 }
513 }
514
515 // invalid pointer
516 return 0;
517}
518
mux87826762014-03-12 21:00:23 +0200519#if 0
Damien George443e0182014-04-08 11:31:21 +0000520// old, simple realloc that didn't expand memory in place
Damien George40f3c022014-07-03 13:25:24 +0100521void *gc_realloc(void *ptr, mp_uint_t n_bytes) {
522 mp_uint_t n_existing = gc_nbytes(ptr);
Damien George6fc765c2014-03-07 00:21:51 +0000523 if (n_bytes <= n_existing) {
524 return ptr;
525 } else {
Damien George410f3072014-04-25 11:44:53 +0000526 bool has_finaliser;
527 if (ptr == NULL) {
528 has_finaliser = false;
529 } else {
Paul Sokolovskyed162b52014-04-20 11:43:38 +0300530#if MICROPY_ENABLE_FINALISER
Damien George40f3c022014-07-03 13:25:24 +0100531 has_finaliser = FTB_GET(BLOCK_FROM_PTR((mp_uint_t)ptr));
Paul Sokolovskyed162b52014-04-20 11:43:38 +0300532#else
Damien George410f3072014-04-25 11:44:53 +0000533 has_finaliser = false;
Paul Sokolovskyed162b52014-04-20 11:43:38 +0300534#endif
Damien George410f3072014-04-25 11:44:53 +0000535 }
536 void *ptr2 = gc_alloc(n_bytes, has_finaliser);
Damien George6fc765c2014-03-07 00:21:51 +0000537 if (ptr2 == NULL) {
538 return ptr2;
539 }
540 memcpy(ptr2, ptr, n_existing);
541 gc_free(ptr);
542 return ptr2;
543 }
544}
Paul Sokolovskyed162b52014-04-20 11:43:38 +0300545
546#else // Alternative gc_realloc impl
Damien George443e0182014-04-08 11:31:21 +0000547
Damien George40f3c022014-07-03 13:25:24 +0100548void *gc_realloc(void *ptr_in, mp_uint_t n_bytes) {
Damien George443e0182014-04-08 11:31:21 +0000549 if (gc_lock_depth > 0) {
550 return NULL;
Damien George12bab722014-04-05 20:35:48 +0100551 }
552
Damien Georgedde739d2014-04-20 18:16:25 +0100553 // check for pure allocation
muxfbaa1472014-03-05 23:23:04 +0200554 if (ptr_in == NULL) {
Damien George12bab722014-04-05 20:35:48 +0100555 return gc_alloc(n_bytes, false);
Damiendcced922013-10-21 23:45:08 +0100556 }
muxfbaa1472014-03-05 23:23:04 +0200557
Damien George37378f82014-10-23 12:02:00 +0100558 // check for pure free
559 if (n_bytes == 0) {
560 gc_free(ptr_in);
561 return NULL;
562 }
563
Damien George40f3c022014-07-03 13:25:24 +0100564 mp_uint_t ptr = (mp_uint_t)ptr_in;
Damien Georgedde739d2014-04-20 18:16:25 +0100565
566 // sanity check the ptr
567 if (!VERIFY_PTR(ptr)) {
568 return NULL;
569 }
570
Paul Sokolovskyc86889d2014-04-20 11:45:16 +0300571 // get first block
Damien George40f3c022014-07-03 13:25:24 +0100572 mp_uint_t block = BLOCK_FROM_PTR(ptr);
Paul Sokolovskyc86889d2014-04-20 11:45:16 +0300573
Damien Georgedde739d2014-04-20 18:16:25 +0100574 // sanity check the ptr is pointing to the head of a block
575 if (ATB_GET_KIND(block) != AT_HEAD) {
576 return NULL;
muxfbaa1472014-03-05 23:23:04 +0200577 }
578
Damien Georgedde739d2014-04-20 18:16:25 +0100579 // compute number of new blocks that are requested
Damien George40f3c022014-07-03 13:25:24 +0100580 mp_uint_t new_blocks = (n_bytes + BYTES_PER_BLOCK - 1) / BYTES_PER_BLOCK;
Damien Georgedde739d2014-04-20 18:16:25 +0100581
Damien George9b0b3732014-10-15 18:24:47 +0000582 // Get the total number of consecutive blocks that are already allocated to
583 // this chunk of memory, and then count the number of free blocks following
584 // it. Stop if we reach the end of the heap, or if we find enough extra
585 // free blocks to satisfy the realloc. Note that we need to compute the
586 // total size of the existing memory chunk so we can correctly and
587 // efficiently shrink it (see below for shrinking code).
Damien George40f3c022014-07-03 13:25:24 +0100588 mp_uint_t n_free = 0;
589 mp_uint_t n_blocks = 1; // counting HEAD block
590 mp_uint_t max_block = gc_alloc_table_byte_len * BLOCKS_PER_ATB;
Damien George9b0b3732014-10-15 18:24:47 +0000591 for (mp_uint_t bl = block + n_blocks; bl < max_block; bl++) {
592 byte block_type = ATB_GET_KIND(bl);
593 if (block_type == AT_TAIL) {
594 n_blocks++;
595 continue;
Damien Georgedde739d2014-04-20 18:16:25 +0100596 }
Damien George9b0b3732014-10-15 18:24:47 +0000597 if (block_type == AT_FREE) {
598 n_free++;
599 if (n_blocks + n_free >= new_blocks) {
600 // stop as soon as we find enough blocks for n_bytes
601 break;
602 }
603 continue;
Damien Georgedde739d2014-04-20 18:16:25 +0100604 }
605 break;
606 }
607
608 // return original ptr if it already has the requested number of blocks
609 if (new_blocks == n_blocks) {
610 return ptr_in;
611 }
612
613 // check if we can shrink the allocated area
614 if (new_blocks < n_blocks) {
615 // free unneeded tail blocks
Damien George9b0b3732014-10-15 18:24:47 +0000616 for (mp_uint_t bl = block + new_blocks, count = n_blocks - new_blocks; count > 0; bl++, count--) {
Damien Georgedde739d2014-04-20 18:16:25 +0100617 ATB_ANY_TO_FREE(bl);
618 }
Damien George516b09e2014-08-28 23:06:38 +0100619
620 // set the last_free pointer to end of this block if it's earlier in the heap
621 if ((block + new_blocks) / BLOCKS_PER_ATB < gc_last_free_atb_index) {
622 gc_last_free_atb_index = (block + new_blocks) / BLOCKS_PER_ATB;
623 }
624
625 #if EXTENSIVE_HEAP_PROFILING
626 gc_dump_alloc_table();
627 #endif
628
Damien Georgedde739d2014-04-20 18:16:25 +0100629 return ptr_in;
630 }
631
632 // check if we can expand in place
633 if (new_blocks <= n_blocks + n_free) {
634 // mark few more blocks as used tail
Damien George40f3c022014-07-03 13:25:24 +0100635 for (mp_uint_t bl = block + n_blocks; bl < block + new_blocks; bl++) {
Damien Georgedde739d2014-04-20 18:16:25 +0100636 assert(ATB_GET_KIND(bl) == AT_FREE);
637 ATB_FREE_TO_TAIL(bl);
638 }
Damien Georgedaab6512014-04-25 23:37:55 +0100639
Damien George32bef312014-04-26 22:23:42 +0100640 // zero out the additional bytes of the newly allocated blocks (see comment above in gc_alloc)
stijnf33385f2014-06-12 17:42:20 +0200641 memset((byte*)ptr_in + n_bytes, 0, new_blocks * BYTES_PER_BLOCK - n_bytes);
Damien Georgedaab6512014-04-25 23:37:55 +0100642
Damien George516b09e2014-08-28 23:06:38 +0100643 #if EXTENSIVE_HEAP_PROFILING
644 gc_dump_alloc_table();
645 #endif
646
Damien Georgedde739d2014-04-20 18:16:25 +0100647 return ptr_in;
648 }
649
650 // can't resize inplace; try to find a new contiguous chain
651 void *ptr_out = gc_alloc(n_bytes,
652#if MICROPY_ENABLE_FINALISER
653 FTB_GET(block)
654#else
655 false
656#endif
657 );
658
659 // check that the alloc succeeded
660 if (ptr_out == NULL) {
661 return NULL;
662 }
663
stijnbbcea3f2014-06-16 10:44:29 +0200664 DEBUG_printf("gc_realloc(%p -> %p)\n", ptr_in, ptr_out);
Damien Georgedde739d2014-04-20 18:16:25 +0100665 memcpy(ptr_out, ptr_in, n_blocks * BYTES_PER_BLOCK);
666 gc_free(ptr_in);
667 return ptr_out;
Damiendcced922013-10-21 23:45:08 +0100668}
Paul Sokolovskyed162b52014-04-20 11:43:38 +0300669#endif // Alternative gc_realloc impl
mux87826762014-03-12 21:00:23 +0200670
Paul Sokolovsky723a6ed2014-02-11 18:01:38 +0200671void gc_dump_info() {
672 gc_info_t info;
673 gc_info(&info);
674 printf("GC: total: " UINT_FMT ", used: " UINT_FMT ", free: " UINT_FMT "\n", info.total, info.used, info.free);
675 printf(" No. of 1-blocks: " UINT_FMT ", 2-blocks: " UINT_FMT ", max blk sz: " UINT_FMT "\n",
676 info.num_1block, info.num_2block, info.max_block);
677}
678
Damien Georgece1162a2014-02-26 22:55:59 +0000679void gc_dump_alloc_table(void) {
Damien George516b09e2014-08-28 23:06:38 +0100680 static const mp_uint_t DUMP_BYTES_PER_LINE = 64;
681 #if !EXTENSIVE_HEAP_PROFILING
682 // When comparing heap output we don't want to print the starting
683 // pointer of the heap because it changes from run to run.
Damien Georgedaab6512014-04-25 23:37:55 +0100684 printf("GC memory layout; from %p:", gc_pool_start);
Damien George516b09e2014-08-28 23:06:38 +0100685 #endif
Damien George40f3c022014-07-03 13:25:24 +0100686 for (mp_uint_t bl = 0; bl < gc_alloc_table_byte_len * BLOCKS_PER_ATB; bl++) {
Damien George516b09e2014-08-28 23:06:38 +0100687 if (bl % DUMP_BYTES_PER_LINE == 0) {
688 // a new line of blocks
Damien George516b09e2014-08-28 23:06:38 +0100689 {
690 // check if this line contains only free blocks
Damien George0b13f3e2014-10-24 23:12:25 +0100691 mp_uint_t bl2 = bl;
692 while (bl2 < gc_alloc_table_byte_len * BLOCKS_PER_ATB && ATB_GET_KIND(bl2) == AT_FREE) {
693 bl2++;
694 }
695 if (bl2 - bl >= 2 * DUMP_BYTES_PER_LINE) {
696 // there are at least 2 lines containing only free blocks, so abbreviate their printing
697 printf("\n (" UINT_FMT " lines all free)", (bl2 - bl) / DUMP_BYTES_PER_LINE);
698 bl = bl2 & (~(DUMP_BYTES_PER_LINE - 1));
699 if (bl >= gc_alloc_table_byte_len * BLOCKS_PER_ATB) {
700 // got to end of heap
Damien George516b09e2014-08-28 23:06:38 +0100701 break;
702 }
703 }
Damien George516b09e2014-08-28 23:06:38 +0100704 }
Damien George516b09e2014-08-28 23:06:38 +0100705 // print header for new line of blocks
Damien George0b13f3e2014-10-24 23:12:25 +0100706 #if EXTENSIVE_HEAP_PROFILING
707 printf("\n%05x: ", (uint)(bl * BYTES_PER_BLOCK) & 0xfffff);
708 #else
709 printf("\n%05x: ", (uint)PTR_FROM_BLOCK(bl) & 0xfffff);
710 #endif
Damieneefcc792013-10-22 15:25:25 +0100711 }
Damien Georgece1162a2014-02-26 22:55:59 +0000712 int c = ' ';
713 switch (ATB_GET_KIND(bl)) {
714 case AT_FREE: c = '.'; break;
Damien Georgec30595e2014-10-17 14:12:57 +0000715 /* this prints out if the object is reachable from BSS or STACK (for unix only)
716 case AT_HEAD: {
717 extern char __bss_start, _end;
718 extern char *stack_top;
719 c = 'h';
720 void **ptrs = (void**)&__bss_start;
721 mp_uint_t len = ((mp_uint_t)&_end - (mp_uint_t)&__bss_start) / sizeof(mp_uint_t);
722 for (mp_uint_t i = 0; i < len; i++) {
723 mp_uint_t ptr = (mp_uint_t)ptrs[i];
724 if (VERIFY_PTR(ptr) && BLOCK_FROM_PTR(ptr) == bl) {
725 c = 'B';
726 break;
727 }
728 }
729 if (c == 'h') {
730 ptrs = (void**)&c;
731 len = ((mp_uint_t)stack_top - (mp_uint_t)&c) / sizeof(mp_uint_t);
732 for (mp_uint_t i = 0; i < len; i++) {
733 mp_uint_t ptr = (mp_uint_t)ptrs[i];
734 if (VERIFY_PTR(ptr) && BLOCK_FROM_PTR(ptr) == bl) {
735 c = 'S';
736 break;
737 }
738 }
739 }
740 break;
741 }
742 */
Damien George0b13f3e2014-10-24 23:12:25 +0100743 /* this prints the uPy object type of the head block */
Damien Georgedaab6512014-04-25 23:37:55 +0100744 case AT_HEAD: {
Damien George40f3c022014-07-03 13:25:24 +0100745 mp_uint_t *ptr = gc_pool_start + bl * WORDS_PER_BLOCK;
746 if (*ptr == (mp_uint_t)&mp_type_tuple) { c = 'T'; }
747 else if (*ptr == (mp_uint_t)&mp_type_list) { c = 'L'; }
748 else if (*ptr == (mp_uint_t)&mp_type_dict) { c = 'D'; }
749 else if (*ptr == (mp_uint_t)&mp_type_float) { c = 'F'; }
750 else if (*ptr == (mp_uint_t)&mp_type_fun_bc) { c = 'B'; }
Damien George0b13f3e2014-10-24 23:12:25 +0100751 else if (*ptr == (mp_uint_t)&mp_type_module) { c = 'M'; }
Damien Georgedaab6512014-04-25 23:37:55 +0100752 else { c = 'h'; }
753 break;
754 }
Damien Georgece1162a2014-02-26 22:55:59 +0000755 case AT_TAIL: c = 't'; break;
756 case AT_MARK: c = 'm'; break;
757 }
758 printf("%c", c);
Damieneefcc792013-10-22 15:25:25 +0100759 }
Damien Georgece1162a2014-02-26 22:55:59 +0000760 printf("\n");
Damieneefcc792013-10-22 15:25:25 +0100761}
762
Damien Georgece1162a2014-02-26 22:55:59 +0000763#if DEBUG_PRINT
Paul Sokolovskyaf19cbd2014-02-10 21:45:54 +0200764void gc_test(void) {
Damien George40f3c022014-07-03 13:25:24 +0100765 mp_uint_t len = 500;
766 mp_uint_t *heap = malloc(len);
767 gc_init(heap, heap + len / sizeof(mp_uint_t));
Damiendcced922013-10-21 23:45:08 +0100768 void *ptrs[100];
769 {
Damien George40f3c022014-07-03 13:25:24 +0100770 mp_uint_t **p = gc_alloc(16, false);
Damien George12bab722014-04-05 20:35:48 +0100771 p[0] = gc_alloc(64, false);
772 p[1] = gc_alloc(1, false);
773 p[2] = gc_alloc(1, false);
774 p[3] = gc_alloc(1, false);
Damien George40f3c022014-07-03 13:25:24 +0100775 mp_uint_t ***p2 = gc_alloc(16, false);
Damiendcced922013-10-21 23:45:08 +0100776 p2[0] = p;
777 p2[1] = p;
778 ptrs[0] = p2;
779 }
Paul Sokolovskyaf19cbd2014-02-10 21:45:54 +0200780 for (int i = 0; i < 25; i+=2) {
Damien George40f3c022014-07-03 13:25:24 +0100781 mp_uint_t *p = gc_alloc(i, false);
Damiendcced922013-10-21 23:45:08 +0100782 printf("p=%p\n", p);
783 if (i & 3) {
784 //ptrs[i] = p;
785 }
786 }
787
Paul Sokolovskyaf19cbd2014-02-10 21:45:54 +0200788 printf("Before GC:\n");
Damien Georgece1162a2014-02-26 22:55:59 +0000789 gc_dump_alloc_table();
Paul Sokolovskyaf19cbd2014-02-10 21:45:54 +0200790 printf("Starting GC...\n");
791 gc_collect_start();
792 gc_collect_root(ptrs, sizeof(ptrs) / sizeof(void*));
793 gc_collect_end();
794 printf("After GC:\n");
Damien Georgece1162a2014-02-26 22:55:59 +0000795 gc_dump_alloc_table();
Damiendcced922013-10-21 23:45:08 +0100796}
Paul Sokolovskyaf19cbd2014-02-10 21:45:54 +0200797#endif
Damien Georged3ebe482014-01-07 15:20:33 +0000798
799#endif // MICROPY_ENABLE_GC