Numworks Epsilon  1.4.1
Graphing Calculator Operating System
gc.c
Go to the documentation of this file.
1 /*
2  * This file is part of the MicroPython 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 
27 #include <assert.h>
28 #include <stdio.h>
29 #include <string.h>
30 
31 #include "py/gc.h"
32 #include "py/runtime.h"
33 
34 #if MICROPY_ENABLE_GC
35 
36 #if MICROPY_DEBUG_VERBOSE // print debugging info
37 #define DEBUG_PRINT (1)
38 #define DEBUG_printf DEBUG_printf
39 #else // don't print debugging info
40 #define DEBUG_PRINT (0)
41 #define DEBUG_printf(...) (void)0
42 #endif
43 
44 // make this 1 to dump the heap each time it changes
45 #define EXTENSIVE_HEAP_PROFILING (0)
46 
47 #define WORDS_PER_BLOCK ((MICROPY_BYTES_PER_GC_BLOCK) / BYTES_PER_WORD)
48 #define BYTES_PER_BLOCK (MICROPY_BYTES_PER_GC_BLOCK)
49 
50 // ATB = allocation table byte
51 // 0b00 = FREE -- free block
52 // 0b01 = HEAD -- head of a chain of blocks
53 // 0b10 = TAIL -- in the tail of a chain of blocks
54 // 0b11 = MARK -- marked head block
55 
56 #define AT_FREE (0)
57 #define AT_HEAD (1)
58 #define AT_TAIL (2)
59 #define AT_MARK (3)
60 
61 #define BLOCKS_PER_ATB (4)
62 #define ATB_MASK_0 (0x03)
63 #define ATB_MASK_1 (0x0c)
64 #define ATB_MASK_2 (0x30)
65 #define ATB_MASK_3 (0xc0)
66 
67 #define ATB_0_IS_FREE(a) (((a) & ATB_MASK_0) == 0)
68 #define ATB_1_IS_FREE(a) (((a) & ATB_MASK_1) == 0)
69 #define ATB_2_IS_FREE(a) (((a) & ATB_MASK_2) == 0)
70 #define ATB_3_IS_FREE(a) (((a) & ATB_MASK_3) == 0)
71 
72 #define BLOCK_SHIFT(block) (2 * ((block) & (BLOCKS_PER_ATB - 1)))
73 #define ATB_GET_KIND(block) ((MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] >> BLOCK_SHIFT(block)) & 3)
74 #define ATB_ANY_TO_FREE(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] &= (~(AT_MARK << BLOCK_SHIFT(block))); } while (0)
75 #define ATB_FREE_TO_HEAD(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] |= (AT_HEAD << BLOCK_SHIFT(block)); } while (0)
76 #define ATB_FREE_TO_TAIL(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] |= (AT_TAIL << BLOCK_SHIFT(block)); } while (0)
77 #define ATB_HEAD_TO_MARK(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] |= (AT_MARK << BLOCK_SHIFT(block)); } while (0)
78 #define ATB_MARK_TO_HEAD(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] &= (~(AT_TAIL << BLOCK_SHIFT(block))); } while (0)
79 
80 #define BLOCK_FROM_PTR(ptr) (((byte*)(ptr) - MP_STATE_MEM(gc_pool_start)) / BYTES_PER_BLOCK)
81 #define PTR_FROM_BLOCK(block) (((block) * BYTES_PER_BLOCK + (uintptr_t)MP_STATE_MEM(gc_pool_start)))
82 #define ATB_FROM_BLOCK(bl) ((bl) / BLOCKS_PER_ATB)
83 
84 #if MICROPY_ENABLE_FINALISER
85 // FTB = finaliser table byte
86 // if set, then the corresponding block may have a finaliser
87 
88 #define BLOCKS_PER_FTB (8)
89 
90 #define FTB_GET(block) ((MP_STATE_MEM(gc_finaliser_table_start)[(block) / BLOCKS_PER_FTB] >> ((block) & 7)) & 1)
91 #define FTB_SET(block) do { MP_STATE_MEM(gc_finaliser_table_start)[(block) / BLOCKS_PER_FTB] |= (1 << ((block) & 7)); } while (0)
92 #define FTB_CLEAR(block) do { MP_STATE_MEM(gc_finaliser_table_start)[(block) / BLOCKS_PER_FTB] &= (~(1 << ((block) & 7))); } while (0)
93 #endif
94 
95 #if MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL
96 #define GC_ENTER() mp_thread_mutex_lock(&MP_STATE_MEM(gc_mutex), 1)
97 #define GC_EXIT() mp_thread_mutex_unlock(&MP_STATE_MEM(gc_mutex))
98 #else
99 #define GC_ENTER()
100 #define GC_EXIT()
101 #endif
102 
103 // TODO waste less memory; currently requires that all entries in alloc_table have a corresponding block in pool
104 void gc_init(void *start, void *end) {
105  // align end pointer on block boundary
106  end = (void*)((uintptr_t)end & (~(BYTES_PER_BLOCK - 1)));
107  DEBUG_printf("Initializing GC heap: %p..%p = " UINT_FMT " bytes\n", start, end, (byte*)end - (byte*)start);
108 
109  // calculate parameters for GC (T=total, A=alloc table, F=finaliser table, P=pool; all in bytes):
110  // T = A + F + P
111  // F = A * BLOCKS_PER_ATB / BLOCKS_PER_FTB
112  // P = A * BLOCKS_PER_ATB * BYTES_PER_BLOCK
113  // => T = A * (1 + BLOCKS_PER_ATB / BLOCKS_PER_FTB + BLOCKS_PER_ATB * BYTES_PER_BLOCK)
114  size_t total_byte_len = (byte*)end - (byte*)start;
115 #if MICROPY_ENABLE_FINALISER
116  MP_STATE_MEM(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);
117 #else
118  MP_STATE_MEM(gc_alloc_table_byte_len) = total_byte_len / (1 + BITS_PER_BYTE / 2 * BYTES_PER_BLOCK);
119 #endif
120 
121  MP_STATE_MEM(gc_alloc_table_start) = (byte*)start;
122 
123 #if MICROPY_ENABLE_FINALISER
124  size_t gc_finaliser_table_byte_len = (MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB + BLOCKS_PER_FTB - 1) / BLOCKS_PER_FTB;
125  MP_STATE_MEM(gc_finaliser_table_start) = MP_STATE_MEM(gc_alloc_table_start) + MP_STATE_MEM(gc_alloc_table_byte_len);
126 #endif
127 
128  size_t gc_pool_block_len = MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB;
129  MP_STATE_MEM(gc_pool_start) = (byte*)end - gc_pool_block_len * BYTES_PER_BLOCK;
130  MP_STATE_MEM(gc_pool_end) = end;
131 
132 #if MICROPY_ENABLE_FINALISER
133  assert(MP_STATE_MEM(gc_pool_start) >= MP_STATE_MEM(gc_finaliser_table_start) + gc_finaliser_table_byte_len);
134 #endif
135 
136  // clear ATBs
137  memset(MP_STATE_MEM(gc_alloc_table_start), 0, MP_STATE_MEM(gc_alloc_table_byte_len));
138 
139 #if MICROPY_ENABLE_FINALISER
140  // clear FTBs
141  memset(MP_STATE_MEM(gc_finaliser_table_start), 0, gc_finaliser_table_byte_len);
142 #endif
143 
144  // set last free ATB index to start of heap
145  MP_STATE_MEM(gc_last_free_atb_index) = 0;
146 
147  // unlock the GC
148  MP_STATE_MEM(gc_lock_depth) = 0;
149 
150  // allow auto collection
151  MP_STATE_MEM(gc_auto_collect_enabled) = 1;
152 
153  #if MICROPY_GC_ALLOC_THRESHOLD
154  // by default, maxuint for gc threshold, effectively turning gc-by-threshold off
155  MP_STATE_MEM(gc_alloc_threshold) = (size_t)-1;
156  MP_STATE_MEM(gc_alloc_amount) = 0;
157  #endif
158 
159  #if MICROPY_PY_THREAD
160  mp_thread_mutex_init(&MP_STATE_MEM(gc_mutex));
161  #endif
162 
163  DEBUG_printf("GC layout:\n");
164  DEBUG_printf(" alloc table at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", MP_STATE_MEM(gc_alloc_table_start), MP_STATE_MEM(gc_alloc_table_byte_len), MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB);
165 #if MICROPY_ENABLE_FINALISER
166  DEBUG_printf(" finaliser table at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", MP_STATE_MEM(gc_finaliser_table_start), gc_finaliser_table_byte_len, gc_finaliser_table_byte_len * BLOCKS_PER_FTB);
167 #endif
168  DEBUG_printf(" pool at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", MP_STATE_MEM(gc_pool_start), gc_pool_block_len * BYTES_PER_BLOCK, gc_pool_block_len);
169 }
170 
171 void gc_lock(void) {
172  GC_ENTER();
173  MP_STATE_MEM(gc_lock_depth)++;
174  GC_EXIT();
175 }
176 
177 void gc_unlock(void) {
178  GC_ENTER();
179  MP_STATE_MEM(gc_lock_depth)--;
180  GC_EXIT();
181 }
182 
183 bool gc_is_locked(void) {
184  return MP_STATE_MEM(gc_lock_depth) != 0;
185 }
186 
187 // ptr should be of type void*
188 #define VERIFY_PTR(ptr) ( \
189  ((uintptr_t)(ptr) & (BYTES_PER_BLOCK - 1)) == 0 /* must be aligned on a block */ \
190  && ptr >= (void*)MP_STATE_MEM(gc_pool_start) /* must be above start of pool */ \
191  && ptr < (void*)MP_STATE_MEM(gc_pool_end) /* must be below end of pool */ \
192  )
193 
194 // ptr should be of type void*
195 #define VERIFY_MARK_AND_PUSH(ptr) \
196  do { \
197  if (VERIFY_PTR(ptr)) { \
198  size_t _block = BLOCK_FROM_PTR(ptr); \
199  if (ATB_GET_KIND(_block) == AT_HEAD) { \
200  /* an unmarked head, mark it, and push it on gc stack */ \
201  DEBUG_printf("gc_mark(%p)\n", ptr); \
202  ATB_HEAD_TO_MARK(_block); \
203  if (MP_STATE_MEM(gc_sp) < &MP_STATE_MEM(gc_stack)[MICROPY_ALLOC_GC_STACK_SIZE]) { \
204  *MP_STATE_MEM(gc_sp)++ = _block; \
205  } else { \
206  MP_STATE_MEM(gc_stack_overflow) = 1; \
207  } \
208  } \
209  } \
210  } while (0)
211 
212 STATIC void gc_drain_stack(void) {
213  while (MP_STATE_MEM(gc_sp) > MP_STATE_MEM(gc_stack)) {
214  // pop the next block off the stack
215  size_t block = *--MP_STATE_MEM(gc_sp);
216 
217  // work out number of consecutive blocks in the chain starting with this one
218  size_t n_blocks = 0;
219  do {
220  n_blocks += 1;
221  } while (ATB_GET_KIND(block + n_blocks) == AT_TAIL);
222 
223  // check this block's children
224  void **ptrs = (void**)PTR_FROM_BLOCK(block);
225  for (size_t i = n_blocks * BYTES_PER_BLOCK / sizeof(void*); i > 0; i--, ptrs++) {
226  void *ptr = *ptrs;
227  VERIFY_MARK_AND_PUSH(ptr);
228  }
229  }
230 }
231 
232 STATIC void gc_deal_with_stack_overflow(void) {
233  while (MP_STATE_MEM(gc_stack_overflow)) {
234  MP_STATE_MEM(gc_stack_overflow) = 0;
235  MP_STATE_MEM(gc_sp) = MP_STATE_MEM(gc_stack);
236 
237  // scan entire memory looking for blocks which have been marked but not their children
238  for (size_t block = 0; block < MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; block++) {
239  // trace (again) if mark bit set
240  if (ATB_GET_KIND(block) == AT_MARK) {
241  *MP_STATE_MEM(gc_sp)++ = block;
242  gc_drain_stack();
243  }
244  }
245  }
246 }
247 
248 STATIC void gc_sweep(void) {
249  #if MICROPY_PY_GC_COLLECT_RETVAL
250  MP_STATE_MEM(gc_collected) = 0;
251  #endif
252  // free unmarked heads and their tails
253  int free_tail = 0;
254  for (size_t block = 0; block < MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; block++) {
255  switch (ATB_GET_KIND(block)) {
256  case AT_HEAD:
257 #if MICROPY_ENABLE_FINALISER
258  if (FTB_GET(block)) {
259  mp_obj_base_t *obj = (mp_obj_base_t*)PTR_FROM_BLOCK(block);
260  if (obj->type != NULL) {
261  // if the object has a type then see if it has a __del__ method
262  mp_obj_t dest[2];
263  mp_load_method_maybe(MP_OBJ_FROM_PTR(obj), MP_QSTR___del__, dest);
264  if (dest[0] != MP_OBJ_NULL) {
265  // load_method returned a method, execute it in a protected environment
266  #if MICROPY_ENABLE_SCHEDULER
267  mp_sched_lock();
268  #endif
269  mp_call_function_1_protected(dest[0], dest[1]);
270  #if MICROPY_ENABLE_SCHEDULER
271  mp_sched_unlock();
272  #endif
273  }
274  }
275  // clear finaliser flag
276  FTB_CLEAR(block);
277  }
278 #endif
279  free_tail = 1;
280  DEBUG_printf("gc_sweep(%x)\n", PTR_FROM_BLOCK(block));
281  #if MICROPY_PY_GC_COLLECT_RETVAL
282  MP_STATE_MEM(gc_collected)++;
283  #endif
284  // fall through to free the head
285 
286  case AT_TAIL:
287  if (free_tail) {
288  ATB_ANY_TO_FREE(block);
289  }
290  break;
291 
292  case AT_MARK:
293  ATB_MARK_TO_HEAD(block);
294  free_tail = 0;
295  break;
296  }
297  }
298 }
299 
300 void gc_collect_start(void) {
301  GC_ENTER();
302  MP_STATE_MEM(gc_lock_depth)++;
303  #if MICROPY_GC_ALLOC_THRESHOLD
304  MP_STATE_MEM(gc_alloc_amount) = 0;
305  #endif
306  MP_STATE_MEM(gc_stack_overflow) = 0;
307  MP_STATE_MEM(gc_sp) = MP_STATE_MEM(gc_stack);
308  // Trace root pointers. This relies on the root pointers being organised
309  // correctly in the mp_state_ctx structure. We scan nlr_top, dict_locals,
310  // dict_globals, then the root pointer section of mp_state_vm.
311  void **ptrs = (void**)(void*)&mp_state_ctx;
312  gc_collect_root(ptrs, offsetof(mp_state_ctx_t, vm.qstr_last_chunk) / sizeof(void*));
313 }
314 
315 void gc_collect_root(void **ptrs, size_t len) {
316  for (size_t i = 0; i < len; i++) {
317  void *ptr = ptrs[i];
318  VERIFY_MARK_AND_PUSH(ptr);
319  gc_drain_stack();
320  }
321 }
322 
323 void gc_collect_end(void) {
324  gc_deal_with_stack_overflow();
325  gc_sweep();
326  MP_STATE_MEM(gc_last_free_atb_index) = 0;
327  MP_STATE_MEM(gc_lock_depth)--;
328  GC_EXIT();
329 }
330 
331 void gc_info(gc_info_t *info) {
332  GC_ENTER();
333  info->total = MP_STATE_MEM(gc_pool_end) - MP_STATE_MEM(gc_pool_start);
334  info->used = 0;
335  info->free = 0;
336  info->max_free = 0;
337  info->num_1block = 0;
338  info->num_2block = 0;
339  info->max_block = 0;
340  bool finish = false;
341  for (size_t block = 0, len = 0, len_free = 0; !finish;) {
342  size_t kind = ATB_GET_KIND(block);
343  switch (kind) {
344  case AT_FREE:
345  info->free += 1;
346  len_free += 1;
347  len = 0;
348  break;
349 
350  case AT_HEAD:
351  info->used += 1;
352  len = 1;
353  break;
354 
355  case AT_TAIL:
356  info->used += 1;
357  len += 1;
358  break;
359 
360  case AT_MARK:
361  // shouldn't happen
362  break;
363  }
364 
365  block++;
366  finish = (block == MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB);
367  // Get next block type if possible
368  if (!finish) {
369  kind = ATB_GET_KIND(block);
370  }
371 
372  if (finish || kind == AT_FREE || kind == AT_HEAD) {
373  if (len == 1) {
374  info->num_1block += 1;
375  } else if (len == 2) {
376  info->num_2block += 1;
377  }
378  if (len > info->max_block) {
379  info->max_block = len;
380  }
381  if (finish || kind == AT_HEAD) {
382  if (len_free > info->max_free) {
383  info->max_free = len_free;
384  }
385  len_free = 0;
386  }
387  }
388  }
389 
390  info->used *= BYTES_PER_BLOCK;
391  info->free *= BYTES_PER_BLOCK;
392  GC_EXIT();
393 }
394 
395 void *gc_alloc(size_t n_bytes, bool has_finaliser) {
396  size_t n_blocks = ((n_bytes + BYTES_PER_BLOCK - 1) & (~(BYTES_PER_BLOCK - 1))) / BYTES_PER_BLOCK;
397  DEBUG_printf("gc_alloc(" UINT_FMT " bytes -> " UINT_FMT " blocks)\n", n_bytes, n_blocks);
398 
399  // check for 0 allocation
400  if (n_blocks == 0) {
401  return NULL;
402  }
403 
404  GC_ENTER();
405 
406  // check if GC is locked
407  if (MP_STATE_MEM(gc_lock_depth) > 0) {
408  GC_EXIT();
409  return NULL;
410  }
411 
412  size_t i;
413  size_t end_block;
414  size_t start_block;
415  size_t n_free = 0;
416  int collected = !MP_STATE_MEM(gc_auto_collect_enabled);
417 
418  #if MICROPY_GC_ALLOC_THRESHOLD
419  if (!collected && MP_STATE_MEM(gc_alloc_amount) >= MP_STATE_MEM(gc_alloc_threshold)) {
420  GC_EXIT();
421  gc_collect();
422  GC_ENTER();
423  }
424  #endif
425 
426  for (;;) {
427 
428  // look for a run of n_blocks available blocks
429  for (i = MP_STATE_MEM(gc_last_free_atb_index); i < MP_STATE_MEM(gc_alloc_table_byte_len); i++) {
430  byte a = MP_STATE_MEM(gc_alloc_table_start)[i];
431  if (ATB_0_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 0; goto found; } } else { n_free = 0; }
432  if (ATB_1_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 1; goto found; } } else { n_free = 0; }
433  if (ATB_2_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 2; goto found; } } else { n_free = 0; }
434  if (ATB_3_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 3; goto found; } } else { n_free = 0; }
435  }
436 
437  GC_EXIT();
438  // nothing found!
439  if (collected) {
440  return NULL;
441  }
442  DEBUG_printf("gc_alloc(" UINT_FMT "): no free mem, triggering GC\n", n_bytes);
443  gc_collect();
444  collected = 1;
445  GC_ENTER();
446  }
447 
448  // found, ending at block i inclusive
449 found:
450  // get starting and end blocks, both inclusive
451  end_block = i;
452  start_block = i - n_free + 1;
453 
454  // Set last free ATB index to block after last block we found, for start of
455  // next scan. To reduce fragmentation, we only do this if we were looking
456  // for a single free block, which guarantees that there are no free blocks
457  // before this one. Also, whenever we free or shink a block we must check
458  // if this index needs adjusting (see gc_realloc and gc_free).
459  if (n_free == 1) {
460  MP_STATE_MEM(gc_last_free_atb_index) = (i + 1) / BLOCKS_PER_ATB;
461  }
462 
463  // mark first block as used head
464  ATB_FREE_TO_HEAD(start_block);
465 
466  // mark rest of blocks as used tail
467  // TODO for a run of many blocks can make this more efficient
468  for (size_t bl = start_block + 1; bl <= end_block; bl++) {
469  ATB_FREE_TO_TAIL(bl);
470  }
471 
472  // get pointer to first block
473  // we must create this pointer before unlocking the GC so a collection can find it
474  void *ret_ptr = (void*)(MP_STATE_MEM(gc_pool_start) + start_block * BYTES_PER_BLOCK);
475  DEBUG_printf("gc_alloc(%p)\n", ret_ptr);
476 
477  #if MICROPY_GC_ALLOC_THRESHOLD
478  MP_STATE_MEM(gc_alloc_amount) += n_blocks;
479  #endif
480 
481  GC_EXIT();
482 
483  #if MICROPY_GC_CONSERVATIVE_CLEAR
484  // be conservative and zero out all the newly allocated blocks
485  memset((byte*)ret_ptr, 0, (end_block - start_block + 1) * BYTES_PER_BLOCK);
486  #else
487  // zero out the additional bytes of the newly allocated blocks
488  // This is needed because the blocks may have previously held pointers
489  // to the heap and will not be set to something else if the caller
490  // doesn't actually use the entire block. As such they will continue
491  // to point to the heap and may prevent other blocks from being reclaimed.
492  memset((byte*)ret_ptr + n_bytes, 0, (end_block - start_block + 1) * BYTES_PER_BLOCK - n_bytes);
493  #endif
494 
495  #if MICROPY_ENABLE_FINALISER
496  if (has_finaliser) {
497  // clear type pointer in case it is never set
498  ((mp_obj_base_t*)ret_ptr)->type = NULL;
499  // set mp_obj flag only if it has a finaliser
500  GC_ENTER();
501  FTB_SET(start_block);
502  GC_EXIT();
503  }
504  #else
505  (void)has_finaliser;
506  #endif
507 
508  #if EXTENSIVE_HEAP_PROFILING
510  #endif
511 
512  return ret_ptr;
513 }
514 
515 /*
516 void *gc_alloc(mp_uint_t n_bytes) {
517  return _gc_alloc(n_bytes, false);
518 }
519 
520 void *gc_alloc_with_finaliser(mp_uint_t n_bytes) {
521  return _gc_alloc(n_bytes, true);
522 }
523 */
524 
525 // force the freeing of a piece of memory
526 // TODO: freeing here does not call finaliser
527 void gc_free(void *ptr) {
528  GC_ENTER();
529  if (MP_STATE_MEM(gc_lock_depth) > 0) {
530  // TODO how to deal with this error?
531  GC_EXIT();
532  return;
533  }
534 
535  DEBUG_printf("gc_free(%p)\n", ptr);
536 
537  if (ptr == NULL) {
538  GC_EXIT();
539  } else {
540  // get the GC block number corresponding to this pointer
541  assert(VERIFY_PTR(ptr));
542  size_t block = BLOCK_FROM_PTR(ptr);
543  assert(ATB_GET_KIND(block) == AT_HEAD);
544 
545  #if MICROPY_ENABLE_FINALISER
546  FTB_CLEAR(block);
547  #endif
548 
549  // set the last_free pointer to this block if it's earlier in the heap
550  if (block / BLOCKS_PER_ATB < MP_STATE_MEM(gc_last_free_atb_index)) {
551  MP_STATE_MEM(gc_last_free_atb_index) = block / BLOCKS_PER_ATB;
552  }
553 
554  // free head and all of its tail blocks
555  do {
556  ATB_ANY_TO_FREE(block);
557  block += 1;
558  } while (ATB_GET_KIND(block) == AT_TAIL);
559 
560  GC_EXIT();
561 
562  #if EXTENSIVE_HEAP_PROFILING
564  #endif
565  }
566 }
567 
568 size_t gc_nbytes(const void *ptr) {
569  GC_ENTER();
570  if (VERIFY_PTR(ptr)) {
571  size_t block = BLOCK_FROM_PTR(ptr);
572  if (ATB_GET_KIND(block) == AT_HEAD) {
573  // work out number of consecutive blocks in the chain starting with this on
574  size_t n_blocks = 0;
575  do {
576  n_blocks += 1;
577  } while (ATB_GET_KIND(block + n_blocks) == AT_TAIL);
578  GC_EXIT();
579  return n_blocks * BYTES_PER_BLOCK;
580  }
581  }
582 
583  // invalid pointer
584  GC_EXIT();
585  return 0;
586 }
587 
588 #if 0
589 // old, simple realloc that didn't expand memory in place
590 void *gc_realloc(void *ptr, mp_uint_t n_bytes) {
591  mp_uint_t n_existing = gc_nbytes(ptr);
592  if (n_bytes <= n_existing) {
593  return ptr;
594  } else {
595  bool has_finaliser;
596  if (ptr == NULL) {
597  has_finaliser = false;
598  } else {
599 #if MICROPY_ENABLE_FINALISER
600  has_finaliser = FTB_GET(BLOCK_FROM_PTR((mp_uint_t)ptr));
601 #else
602  has_finaliser = false;
603 #endif
604  }
605  void *ptr2 = gc_alloc(n_bytes, has_finaliser);
606  if (ptr2 == NULL) {
607  return ptr2;
608  }
609  memcpy(ptr2, ptr, n_existing);
610  gc_free(ptr);
611  return ptr2;
612  }
613 }
614 
615 #else // Alternative gc_realloc impl
616 
617 void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) {
618  // check for pure allocation
619  if (ptr_in == NULL) {
620  return gc_alloc(n_bytes, false);
621  }
622 
623  // check for pure free
624  if (n_bytes == 0) {
625  gc_free(ptr_in);
626  return NULL;
627  }
628 
629  void *ptr = ptr_in;
630 
631  // sanity check the ptr
632  if (!VERIFY_PTR(ptr)) {
633  return NULL;
634  }
635 
636  // get first block
637  size_t block = BLOCK_FROM_PTR(ptr);
638 
639  GC_ENTER();
640 
641  // sanity check the ptr is pointing to the head of a block
642  if (ATB_GET_KIND(block) != AT_HEAD) {
643  GC_EXIT();
644  return NULL;
645  }
646 
647  if (MP_STATE_MEM(gc_lock_depth) > 0) {
648  GC_EXIT();
649  return NULL;
650  }
651 
652  // compute number of new blocks that are requested
653  size_t new_blocks = (n_bytes + BYTES_PER_BLOCK - 1) / BYTES_PER_BLOCK;
654 
655  // Get the total number of consecutive blocks that are already allocated to
656  // this chunk of memory, and then count the number of free blocks following
657  // it. Stop if we reach the end of the heap, or if we find enough extra
658  // free blocks to satisfy the realloc. Note that we need to compute the
659  // total size of the existing memory chunk so we can correctly and
660  // efficiently shrink it (see below for shrinking code).
661  size_t n_free = 0;
662  size_t n_blocks = 1; // counting HEAD block
663  size_t max_block = MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB;
664  for (size_t bl = block + n_blocks; bl < max_block; bl++) {
665  byte block_type = ATB_GET_KIND(bl);
666  if (block_type == AT_TAIL) {
667  n_blocks++;
668  continue;
669  }
670  if (block_type == AT_FREE) {
671  n_free++;
672  if (n_blocks + n_free >= new_blocks) {
673  // stop as soon as we find enough blocks for n_bytes
674  break;
675  }
676  continue;
677  }
678  break;
679  }
680 
681  // return original ptr if it already has the requested number of blocks
682  if (new_blocks == n_blocks) {
683  GC_EXIT();
684  return ptr_in;
685  }
686 
687  // check if we can shrink the allocated area
688  if (new_blocks < n_blocks) {
689  // free unneeded tail blocks
690  for (size_t bl = block + new_blocks, count = n_blocks - new_blocks; count > 0; bl++, count--) {
691  ATB_ANY_TO_FREE(bl);
692  }
693 
694  // set the last_free pointer to end of this block if it's earlier in the heap
695  if ((block + new_blocks) / BLOCKS_PER_ATB < MP_STATE_MEM(gc_last_free_atb_index)) {
696  MP_STATE_MEM(gc_last_free_atb_index) = (block + new_blocks) / BLOCKS_PER_ATB;
697  }
698 
699  GC_EXIT();
700 
701  #if EXTENSIVE_HEAP_PROFILING
703  #endif
704 
705  return ptr_in;
706  }
707 
708  // check if we can expand in place
709  if (new_blocks <= n_blocks + n_free) {
710  // mark few more blocks as used tail
711  for (size_t bl = block + n_blocks; bl < block + new_blocks; bl++) {
712  assert(ATB_GET_KIND(bl) == AT_FREE);
713  ATB_FREE_TO_TAIL(bl);
714  }
715 
716  GC_EXIT();
717 
718  #if MICROPY_GC_CONSERVATIVE_CLEAR
719  // be conservative and zero out all the newly allocated blocks
720  memset((byte*)ptr_in + n_blocks * BYTES_PER_BLOCK, 0, (new_blocks - n_blocks) * BYTES_PER_BLOCK);
721  #else
722  // zero out the additional bytes of the newly allocated blocks (see comment above in gc_alloc)
723  memset((byte*)ptr_in + n_bytes, 0, new_blocks * BYTES_PER_BLOCK - n_bytes);
724  #endif
725 
726  #if EXTENSIVE_HEAP_PROFILING
728  #endif
729 
730  return ptr_in;
731  }
732 
733  #if MICROPY_ENABLE_FINALISER
734  bool ftb_state = FTB_GET(block);
735  #else
736  bool ftb_state = false;
737  #endif
738 
739  GC_EXIT();
740 
741  if (!allow_move) {
742  // not allowed to move memory block so return failure
743  return NULL;
744  }
745 
746  // can't resize inplace; try to find a new contiguous chain
747  void *ptr_out = gc_alloc(n_bytes, ftb_state);
748 
749  // check that the alloc succeeded
750  if (ptr_out == NULL) {
751  return NULL;
752  }
753 
754  DEBUG_printf("gc_realloc(%p -> %p)\n", ptr_in, ptr_out);
755  memcpy(ptr_out, ptr_in, n_blocks * BYTES_PER_BLOCK);
756  gc_free(ptr_in);
757  return ptr_out;
758 }
759 #endif // Alternative gc_realloc impl
760 
761 void gc_dump_info(void) {
762  gc_info_t info;
763  gc_info(&info);
764  mp_printf(&mp_plat_print, "GC: total: %u, used: %u, free: %u\n",
765  (uint)info.total, (uint)info.used, (uint)info.free);
766  mp_printf(&mp_plat_print, " No. of 1-blocks: %u, 2-blocks: %u, max blk sz: %u, max free sz: %u\n",
767  (uint)info.num_1block, (uint)info.num_2block, (uint)info.max_block, (uint)info.max_free);
768 }
769 
770 void gc_dump_alloc_table(void) {
771  GC_ENTER();
772  static const size_t DUMP_BYTES_PER_LINE = 64;
773  #if !EXTENSIVE_HEAP_PROFILING
774  // When comparing heap output we don't want to print the starting
775  // pointer of the heap because it changes from run to run.
776  mp_printf(&mp_plat_print, "GC memory layout; from %p:", MP_STATE_MEM(gc_pool_start));
777  #endif
778  for (size_t bl = 0; bl < MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; bl++) {
779  if (bl % DUMP_BYTES_PER_LINE == 0) {
780  // a new line of blocks
781  {
782  // check if this line contains only free blocks
783  size_t bl2 = bl;
784  while (bl2 < MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB && ATB_GET_KIND(bl2) == AT_FREE) {
785  bl2++;
786  }
787  if (bl2 - bl >= 2 * DUMP_BYTES_PER_LINE) {
788  // there are at least 2 lines containing only free blocks, so abbreviate their printing
789  mp_printf(&mp_plat_print, "\n (%u lines all free)", (uint)(bl2 - bl) / DUMP_BYTES_PER_LINE);
790  bl = bl2 & (~(DUMP_BYTES_PER_LINE - 1));
791  if (bl >= MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB) {
792  // got to end of heap
793  break;
794  }
795  }
796  }
797  // print header for new line of blocks
798  // (the cast to uint32_t is for 16-bit ports)
799  //mp_printf(&mp_plat_print, "\n%05x: ", (uint)(PTR_FROM_BLOCK(bl) & (uint32_t)0xfffff));
800  mp_printf(&mp_plat_print, "\n%05x: ", (uint)((bl * BYTES_PER_BLOCK) & (uint32_t)0xfffff));
801  }
802  int c = ' ';
803  switch (ATB_GET_KIND(bl)) {
804  case AT_FREE: c = '.'; break;
805  /* this prints out if the object is reachable from BSS or STACK (for unix only)
806  case AT_HEAD: {
807  c = 'h';
808  void **ptrs = (void**)(void*)&mp_state_ctx;
809  mp_uint_t len = offsetof(mp_state_ctx_t, vm.stack_top) / sizeof(mp_uint_t);
810  for (mp_uint_t i = 0; i < len; i++) {
811  mp_uint_t ptr = (mp_uint_t)ptrs[i];
812  if (VERIFY_PTR(ptr) && BLOCK_FROM_PTR(ptr) == bl) {
813  c = 'B';
814  break;
815  }
816  }
817  if (c == 'h') {
818  ptrs = (void**)&c;
819  len = ((mp_uint_t)MP_STATE_THREAD(stack_top) - (mp_uint_t)&c) / sizeof(mp_uint_t);
820  for (mp_uint_t i = 0; i < len; i++) {
821  mp_uint_t ptr = (mp_uint_t)ptrs[i];
822  if (VERIFY_PTR(ptr) && BLOCK_FROM_PTR(ptr) == bl) {
823  c = 'S';
824  break;
825  }
826  }
827  }
828  break;
829  }
830  */
831  /* this prints the uPy object type of the head block */
832  case AT_HEAD: {
833  void **ptr = (void**)(MP_STATE_MEM(gc_pool_start) + bl * BYTES_PER_BLOCK);
834  if (*ptr == &mp_type_tuple) { c = 'T'; }
835  else if (*ptr == &mp_type_list) { c = 'L'; }
836  else if (*ptr == &mp_type_dict) { c = 'D'; }
837  else if (*ptr == &mp_type_str || *ptr == &mp_type_bytes) { c = 'S'; }
838  #if MICROPY_PY_BUILTINS_BYTEARRAY
839  else if (*ptr == &mp_type_bytearray) { c = 'A'; }
840  #endif
841  #if MICROPY_PY_ARRAY
842  else if (*ptr == &mp_type_array) { c = 'A'; }
843  #endif
844  #if MICROPY_PY_BUILTINS_FLOAT
845  else if (*ptr == &mp_type_float) { c = 'F'; }
846  #endif
847  else if (*ptr == &mp_type_fun_bc) { c = 'B'; }
848  else if (*ptr == &mp_type_module) { c = 'M'; }
849  else {
850  c = 'h';
851  #if 0
852  // This code prints "Q" for qstr-pool data, and "q" for qstr-str
853  // data. It can be useful to see how qstrs are being allocated,
854  // but is disabled by default because it is very slow.
855  for (qstr_pool_t *pool = MP_STATE_VM(last_pool); c == 'h' && pool != NULL; pool = pool->prev) {
856  if ((qstr_pool_t*)ptr == pool) {
857  c = 'Q';
858  break;
859  }
860  for (const byte **q = pool->qstrs, **q_top = pool->qstrs + pool->len; q < q_top; q++) {
861  if ((const byte*)ptr == *q) {
862  c = 'q';
863  break;
864  }
865  }
866  }
867  #endif
868  }
869  break;
870  }
871  case AT_TAIL: c = '='; break;
872  case AT_MARK: c = 'm'; break;
873  }
874  mp_printf(&mp_plat_print, "%c", c);
875  }
876  mp_print_str(&mp_plat_print, "\n");
877  GC_EXIT();
878 }
879 
880 #if DEBUG_PRINT
881 void gc_test(void) {
882  mp_uint_t len = 500;
883  mp_uint_t *heap = malloc(len);
884  gc_init(heap, heap + len / sizeof(mp_uint_t));
885  void *ptrs[100];
886  {
887  mp_uint_t **p = gc_alloc(16, false);
888  p[0] = gc_alloc(64, false);
889  p[1] = gc_alloc(1, false);
890  p[2] = gc_alloc(1, false);
891  p[3] = gc_alloc(1, false);
892  mp_uint_t ***p2 = gc_alloc(16, false);
893  p2[0] = p;
894  p2[1] = p;
895  ptrs[0] = p2;
896  }
897  for (int i = 0; i < 25; i+=2) {
898  mp_uint_t *p = gc_alloc(i, false);
899  printf("p=%p\n", p);
900  if (i & 3) {
901  //ptrs[i] = p;
902  }
903  }
904 
905  printf("Before GC:\n");
907  printf("Starting GC...\n");
909  gc_collect_root(ptrs, sizeof(ptrs) / sizeof(void*));
910  gc_collect_end();
911  printf("After GC:\n");
913 }
914 #endif
915 
916 #endif // MICROPY_ENABLE_GC
const mp_print_t mp_plat_print
Definition: mpprint.c:51
void gc_dump_alloc_table(void)
const mp_obj_type_t mp_type_fun_bc
Definition: objfun.c:331
void gc_collect(void)
Definition: port.cpp:113
void gc_dump_info(void)
uintptr_t mp_uint_t
Definition: mpconfigport.h:74
void * memset(void *b, int c, size_t len)
Definition: memset.c:3
void mp_load_method_maybe(mp_obj_t obj, qstr attr, mp_obj_t *dest)
Definition: runtime.c:1040
const mp_obj_type_t mp_type_float
#define assert(e)
Definition: assert.h:9
int DEBUG_printf(const char *fmt,...)
const mp_obj_type_t mp_type_bytearray
void gc_init(void *start, void *end)
void gc_free(void *ptr)
size_t free
Definition: gc.h:56
unsigned int size_t
Definition: stddef.h:7
int mp_print_str(const mp_print_t *print, const char *str)
Definition: mpprint.c:53
unsigned int uintptr_t
Definition: stdint.h:14
#define MP_OBJ_FROM_PTR(p)
Definition: obj.h:233
#define offsetof(type, field)
Definition: stddef.h:9
const mp_obj_type_t mp_type_bytes
Definition: objstr.c:1964
void gc_collect_start(void)
mp_state_ctx_t mp_state_ctx
Definition: mpstate.c:33
#define MP_STATE_VM(x)
Definition: mpstate.h:241
#define STATIC
Definition: mpconfig.h:1178
const mp_obj_type_t mp_type_array
size_t max_free
Definition: gc.h:57
size_t used
Definition: gc.h:55
void gc_lock(void)
#define MP_STATE_MEM(x)
Definition: mpstate.h:242
c(generic_all_nodes)
void gc_collect_root(void **ptrs, size_t len)
unsigned int uint32_t
Definition: stdint.h:6
#define NULL
Definition: stddef.h:4
#define MP_OBJ_NULL
Definition: obj.h:73
void gc_info(gc_info_t *info)
#define UINT_FMT
Definition: mpconfigport.h:71
const mp_obj_type_t mp_type_str
Definition: objstr.c:1950
void gc_collect_end(void)
void * gc_realloc(void *ptr, size_t n_bytes, bool allow_move)
unsigned char byte
Definition: misc.h:37
Definition: gc.h:53
void start()
Definition: rt0.cpp:31
void * malloc(size_t size)
Definition: malloc.c:44
const mp_obj_type_t mp_type_tuple
Definition: objtuple.c:220
const mp_obj_type_t mp_type_dict
Definition: objdict.c:552
const mp_obj_type_t mp_type_module
Definition: objmodule.c:94
size_t total
Definition: gc.h:54
#define BITS_PER_BYTE
Definition: mpconfig.h:1186
size_t num_2block
Definition: gc.h:59
uint64_t mp_obj_t
Definition: obj.h:39
int mp_printf(const mp_print_t *print, const char *fmt,...)
Definition: mpprint.c:380
void gc_unlock(void)
void * gc_alloc(size_t n_bytes, bool has_finaliser)
bool gc_is_locked(void)
char heap[kHeapSize]
Definition: boot.cpp:8
size_t max_block
Definition: gc.h:60
void mp_call_function_1_protected(mp_obj_t fun, mp_obj_t arg)
Definition: runtime_utils.c:30
const mp_obj_type_t mp_type_list
Definition: objlist.c:444
size_t gc_nbytes(const void *ptr)
size_t num_1block
Definition: gc.h:58
void * memcpy(void *dst, const void *src, size_t n)
Definition: memcpy.c:3
unsigned int uint
Definition: misc.h:38