2 # This file is Copyright 2003, 2006, 2007, 2009, 2010 Dean Hall.
4 # This file is part of the PyMite VM.
5 # The PyMite VM is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU GENERAL PUBLIC LICENSE Version 2.
8 # The PyMite VM is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 # A copy of the GNU GENERAL PUBLIC LICENSE Version 2
12 # is seen in the file COPYING in this directory.
17 #define __FILE_ID__ 0x06
25 * All of PyMite's dynamic memory is obtained from this heap.
26 * The heap provides dynamic memory on demand.
33 /** Checks for heap size definition. */
35 #warning PM_HEAP_SIZE not defined in src/platform/<yourplatform>/pmfeatures.h
36 #elif PM_HEAP_SIZE & 3
37 #error PM_HEAP_SIZE is not a multiple of four
41 /** The size of the temporary roots stack */
42 #define HEAP_NUM_TEMP_ROOTS 24
45 * The maximum size a live chunk can be (a live chunk is one that is in use).
46 * The live chunk size is limited by the size field in the *object* descriptor.
47 * That field is nine bits with two assumed least significant bits (zeros):
48 * (0x1FF << 2) == 2044
50 #define HEAP_MAX_LIVE_CHUNK_SIZE 2044
53 * The maximum size a free chunk can be (a free chunk is one that is not in use).
54 * The free chunk size is limited by the size field in the *heap* descriptor.
55 * That field is fourteen bits with two assumed least significant bits (zeros):
56 * (0x3FFF << 2) == 65532
58 #define HEAP_MAX_FREE_CHUNK_SIZE 65532
60 /** The minimum size a chunk can be (rounded up to a multiple of 4) */
61 #define HEAP_MIN_CHUNK_SIZE ((sizeof(PmHeapDesc_t) + 3) & ~3)
65 * Gets the GC's mark bit for the object.
66 * This MUST NOT be called on objects that are free.
68 #define OBJ_GET_GCVAL(pobj) ((((pPmObj_t)pobj)->od >> OD_MARK_SHIFT) & 1)
71 * Sets the GC's mark bit for the object
72 * This MUST NOT be called on objects that are free.
75 #define OBJ_SET_GCVAL(pobj, gcval) \
78 ((pPmObj_t)pobj)->od = (gcval) ? ((pPmObj_t)pobj)->od | OD_MARK_BIT \
79 : ((pPmObj_t)pobj)->od & ~OD_MARK_BIT;\
83 #define OBJ_SET_GCVAL(pobj, gcval)
88 * The following is a diagram of the heap descriptor at the head of the chunk:
92 * pchunk-> +-+-+-+-+-+-+-+-+
93 * | S[9:2] | S := Size of the chunk (2 LSbs dropped)
94 * +-+-+-----------+ F := Chunk free bit (not in use)
95 * |F|R| S[15:10] | R := Bit reserved for future use
97 * | P(L) | P := hd_prev: Pointer to previous node
98 * | P(H) | N := hd_next: Pointer to next node
100 * | N(H) | Theoretical min size == 6
101 * +---------------+ Effective min size == 8
102 * | unused space | (12 on 32-bit MCUs)
108 typedef struct PmHeapDesc_s
110 /** Heap descriptor */
113 /** Ptr to prev heap chunk */
114 struct PmHeapDesc_s
*prev
;
116 /** Ptr to next heap chunk */
117 struct PmHeapDesc_s
*next
;
121 typedef struct PmHeap_s
124 * WARNING: Leave 'base' field at the top of struct to increase chance
125 * of alignment when compiler doesn't recognize the aligned attribute
126 * which is specific to GCC
128 /** Global declaration of heap. */
129 uint8_t base
[PM_HEAP_SIZE
];
131 /** Ptr to list of free chunks; sorted smallest to largest. */
132 pPmHeapDesc_t pfreelist
;
134 /** The amount of heap space available in free list */
135 #if PM_HEAP_SIZE > 65535
142 /** Garbage collection mark value */
145 /** Boolean to indicate if GC should run automatically */
148 /* #239: Fix GC when 2+ unlinked allocs occur */
149 /** Stack of objects to be held as temporary roots */
150 pPmObj_t temp_roots
[HEAP_NUM_TEMP_ROOTS
];
152 uint8_t temp_root_index
;
159 /** The PyMite heap */
160 static PmHeap_t pmHeap PM_PLAT_HEAP_ATTR
;
165 heap_gcPrintFreelist(void)
167 pPmHeapDesc_t pchunk
= pmHeap
.pfreelist
;
169 printf("DEBUG: pmHeap.avail = %d\n", pmHeap
.avail
);
170 printf("DEBUG: freelist:\n");
171 while (pchunk
!= C_NULL
)
173 printf("DEBUG: free chunk (%d bytes) @ 0x%0x\n",
174 OBJ_GET_SIZE(pchunk
), (int)pchunk
);
175 pchunk
= pchunk
->next
;
182 /** DEBUG: dumps the heap and roots list to a file */
193 snprintf(filename
, 32, "pmheapdump%02d.bin", n
++);
194 fp
= fopen(filename
, "wb");
196 /* magic : PMDUMP for little endian or PMUDMP for big endian */
197 fwrite(&"PM", 1, 2, fp
);
199 fwrite(&s
, sizeof(uint16_t), 1, fp
);
200 fwrite(&"MP", 1, 2, fp
);
203 s
= sizeof(intptr_t);
204 fwrite(&s
, sizeof(uint16_t), 1, fp
);
208 fwrite(&s
, sizeof(uint16_t), 1, fp
);
212 #ifdef USE_STRING_CACHE
215 #ifdef HAVE_DEFAULTARGS
224 fwrite(&s
, sizeof(uint16_t), 1, fp
);
228 fwrite(&i
, sizeof(uint32_t), 1, fp
);
230 /* Write base address of heap */
232 fwrite((void*)(&b
), sizeof(intptr_t), 1, fp
);
234 /* Write contents of heap */
235 fwrite(&pmHeap
.base
, 1, PM_HEAP_SIZE
, fp
);
239 fwrite(&i
, sizeof(uint32_t), 1, fp
);
241 /* Write heap root ptrs */
242 fwrite((void *)&gVmGlobal
.pnone
, sizeof(intptr_t), 1, fp
);
243 fwrite((void *)&gVmGlobal
.pfalse
, sizeof(intptr_t), 1, fp
);
244 fwrite((void *)&gVmGlobal
.ptrue
, sizeof(intptr_t), 1, fp
);
245 fwrite((void *)&gVmGlobal
.pzero
, sizeof(intptr_t), 1, fp
);
246 fwrite((void *)&gVmGlobal
.pone
, sizeof(intptr_t), 1, fp
);
247 fwrite((void *)&gVmGlobal
.pnegone
, sizeof(intptr_t), 1, fp
);
248 fwrite((void *)&gVmGlobal
.pcodeStr
, sizeof(intptr_t), 1, fp
);
249 fwrite((void *)&gVmGlobal
.builtins
, sizeof(intptr_t), 1, fp
);
250 fwrite((void *)&gVmGlobal
.nativeframe
, sizeof(intptr_t), 1, fp
);
251 fwrite((void *)&gVmGlobal
.threadList
, sizeof(intptr_t), 1, fp
);
257 /* Removes the given chunk from the free list; leaves list in sorted order */
259 heap_unlinkFromFreelist(pPmHeapDesc_t pchunk
)
261 C_ASSERT(pchunk
!= C_NULL
);
263 pmHeap
.avail
-= OBJ_GET_SIZE(pchunk
);
265 if (pchunk
->next
!= C_NULL
)
267 pchunk
->next
->prev
= pchunk
->prev
;
270 /* If pchunk was the first chunk in the free list, update the heap ptr */
271 if (pchunk
->prev
== C_NULL
)
273 pmHeap
.pfreelist
= pchunk
->next
;
277 pchunk
->prev
->next
= pchunk
->next
;
284 /* Inserts in order a chunk into the free list. Caller adjusts heap state */
286 heap_linkToFreelist(pPmHeapDesc_t pchunk
)
291 /* Ensure the object is already free */
292 C_ASSERT(OBJ_GET_FREE(pchunk
) != 0);
294 pmHeap
.avail
+= OBJ_GET_SIZE(pchunk
);
296 /* If free list is empty, add to head of list */
297 if (pmHeap
.pfreelist
== C_NULL
)
299 pmHeap
.pfreelist
= pchunk
;
300 pchunk
->next
= C_NULL
;
301 pchunk
->prev
= C_NULL
;
306 /* Scan free list for insertion point */
307 pscan
= pmHeap
.pfreelist
;
308 size
= OBJ_GET_SIZE(pchunk
);
309 while ((OBJ_GET_SIZE(pscan
) < size
) && (pscan
->next
!= C_NULL
))
315 * Insert chunk after the scan chunk (next is NULL).
316 * This is a slightly rare case where the last chunk in the free list
317 * is smaller than the chunk being freed.
319 if (size
> OBJ_GET_SIZE(pscan
))
321 pchunk
->next
= pscan
->next
;
322 pscan
->next
= pchunk
;
323 pchunk
->prev
= pscan
;
326 /* Insert chunk before the scan chunk */
329 pchunk
->next
= pscan
;
330 pchunk
->prev
= pscan
->prev
;
332 /* If chunk will be first item in free list */
333 if (pscan
->prev
== C_NULL
)
335 pmHeap
.pfreelist
= pchunk
;
339 pscan
->prev
->next
= pchunk
;
341 pscan
->prev
= pchunk
;
349 * Initializes the heap state variables
354 pPmHeapDesc_t pchunk
;
356 #if PM_HEAP_SIZE > 65535
363 /* Fill the heap with a non-NULL value to bring out any heap bugs. */
364 sli_memset(pmHeap
.base
, 0xAA, sizeof(pmHeap
.base
));
367 /* Init heap globals */
368 pmHeap
.pfreelist
= C_NULL
;
371 pmHeap
.gcval
= (uint8_t)0;
372 pmHeap
.temp_root_index
= (uint8_t)0;
373 heap_gcSetAuto(C_TRUE
);
376 /* Create as many max-sized chunks as possible in the freelist */
377 for (pchunk
= (pPmHeapDesc_t
)pmHeap
.base
, hs
= PM_HEAP_SIZE
;
378 hs
>= HEAP_MAX_FREE_CHUNK_SIZE
; hs
-= HEAP_MAX_FREE_CHUNK_SIZE
)
380 OBJ_SET_FREE(pchunk
, 1);
381 OBJ_SET_SIZE(pchunk
, HEAP_MAX_FREE_CHUNK_SIZE
);
382 heap_linkToFreelist(pchunk
);
384 (pPmHeapDesc_t
)((uint8_t *)pchunk
+ HEAP_MAX_FREE_CHUNK_SIZE
);
387 /* Add any leftover memory to the freelist */
388 if (hs
>= HEAP_MIN_CHUNK_SIZE
)
390 /* Round down to a multiple of four */
392 OBJ_SET_FREE(pchunk
, 1);
393 OBJ_SET_SIZE(pchunk
, hs
);
394 heap_linkToFreelist(pchunk
);
397 C_DEBUG_PRINT(VERBOSITY_LOW
, "heap_init(), id=%p, s=%d\n",
398 pmHeap
.base
, pmHeap
.avail
);
407 * Obtains a chunk of memory from the free list
409 * Performs the Best Fit algorithm.
410 * Iterates through the freelist to see if a chunk of suitable size exists.
411 * Shaves a chunk to perfect size iff the remainder is greater than
412 * the minimum chunk size.
414 * @param size Requested chunk size
415 * @param r_pchunk Return ptr to chunk
416 * @return Return status
419 heap_getChunkImpl(uint16_t size
, uint8_t **r_pchunk
)
422 pPmHeapDesc_t pchunk
;
423 pPmHeapDesc_t premainderChunk
;
425 C_ASSERT(r_pchunk
!= C_NULL
);
427 /* Skip to the first chunk that can hold the requested size */
428 pchunk
= pmHeap
.pfreelist
;
429 while ((pchunk
!= C_NULL
) && (OBJ_GET_SIZE(pchunk
) < size
))
431 pchunk
= pchunk
->next
;
434 /* No chunk of appropriate size was found, raise OutOfMemory exception */
435 if (pchunk
== C_NULL
)
438 PM_RAISE(retval
, PM_RET_EX_MEM
);
442 /* Remove the chunk from the free list */
443 retval
= heap_unlinkFromFreelist(pchunk
);
444 PM_RETURN_IF_ERROR(retval
);
446 /* Check if a chunk should be carved from what is available */
447 if (OBJ_GET_SIZE(pchunk
) - size
>= HEAP_MIN_CHUNK_SIZE
)
449 /* Create the heap descriptor for the remainder chunk */
450 premainderChunk
= (pPmHeapDesc_t
)((uint8_t *)pchunk
+ size
);
451 OBJ_SET_FREE(premainderChunk
, 1);
452 OBJ_SET_SIZE(premainderChunk
, OBJ_GET_SIZE(pchunk
) - size
);
454 /* Put the remainder chunk back in the free list */
455 retval
= heap_linkToFreelist(premainderChunk
);
456 PM_RETURN_IF_ERROR(retval
);
458 /* Convert the chunk from a heap descriptor to an object descriptor */
459 OBJ_SET_SIZE(pchunk
, 0);
460 OBJ_SET_FREE(pchunk
, 0);
461 OBJ_SET_SIZE(pchunk
, size
);
463 C_DEBUG_PRINT(VERBOSITY_HIGH
,
464 "heap_getChunkImpl()carved, id=%p, s=%d\n", pchunk
,
469 /* Set chunk's type to none (overwrites size field's high byte) */
470 OBJ_SET_TYPE((pPmObj_t
)pchunk
, OBJ_TYPE_NON
);
471 OBJ_SET_FREE(pchunk
, 0);
473 C_DEBUG_PRINT(VERBOSITY_HIGH
,
474 "heap_getChunkImpl()exact, id=%p, s=%d\n", pchunk
,
475 OBJ_GET_SIZE(pchunk
));
479 * Set the chunk's GC mark so it will be collected during the next GC cycle
480 * if it is not reachable
482 OBJ_SET_GCVAL(pchunk
, pmHeap
.gcval
);
484 /* Return the chunk */
485 *r_pchunk
= (uint8_t *)pchunk
;
492 * Allocates chunk of memory.
493 * Filters out invalid sizes.
494 * Rounds the size up to the next multiple of 4.
495 * Obtains a chunk of at least the desired size.
498 heap_getChunk(uint16_t requestedsize
, uint8_t **r_pchunk
)
501 uint16_t adjustedsize
;
503 /* Ensure size request is valid */
504 if (requestedsize
> HEAP_MAX_LIVE_CHUNK_SIZE
)
506 PM_RAISE(retval
, PM_RET_EX_MEM
);
510 else if (requestedsize
< HEAP_MIN_CHUNK_SIZE
)
512 requestedsize
= HEAP_MIN_CHUNK_SIZE
;
516 * Round up the size to a multiple of 4 bytes.
517 * This maintains alignment on 32-bit platforms (required).
519 adjustedsize
= ((requestedsize
+ 3) & ~3);
521 /* Attempt to get a chunk */
522 retval
= heap_getChunkImpl(adjustedsize
, r_pchunk
);
525 /* Perform GC if out of memory, gc is enabled and not in native session */
526 if ((retval
== PM_RET_EX_MEM
) && (pmHeap
.auto_gc
== C_TRUE
)
527 && (gVmGlobal
.nativeframe
.nf_active
== C_FALSE
))
529 retval
= heap_gcRun();
530 PM_RETURN_IF_ERROR(retval
);
532 /* Attempt to get a chunk */
533 retval
= heap_getChunkImpl(adjustedsize
, r_pchunk
);
537 /* Ensure that the pointer is 4-byte aligned */
538 if (retval
== PM_RET_OK
)
540 C_ASSERT(((intptr_t)*r_pchunk
& 3) == 0);
547 /* Releases chunk to the free list */
549 heap_freeChunk(pPmObj_t ptr
)
553 C_DEBUG_PRINT(VERBOSITY_HIGH
, "heap_freeChunk(), id=%p, s=%d\n",
554 ptr
, OBJ_GET_SIZE(ptr
));
556 /* Ensure the chunk falls within the heap */
557 C_ASSERT(((uint8_t *)ptr
>= pmHeap
.base
)
558 && ((uint8_t *)ptr
< pmHeap
.base
+ PM_HEAP_SIZE
));
560 /* Insert the chunk into the freelist */
561 OBJ_SET_FREE(ptr
, 1);
563 /* Clear type so that heap descriptor's size's upper byte is zero */
564 OBJ_SET_TYPE(ptr
, 0);
565 retval
= heap_linkToFreelist((pPmHeapDesc_t
)ptr
);
566 PM_RETURN_IF_ERROR(retval
);
572 /* Returns, by reference, the number of bytes available in the heap */
573 #if PM_HEAP_SIZE > 65535
586 * Marks the given object and the objects it references.
588 * @param pobj Any non-free heap object
589 * @return Return code
592 heap_gcMarkObj(pPmObj_t pobj
)
594 PmReturn_t retval
= PM_RET_OK
;
599 /* Return if ptr is null or object is already marked */
604 if (OBJ_GET_GCVAL(pobj
) == pmHeap
.gcval
)
609 /* The pointer must be within the heap (native frame is special case) */
610 C_ASSERT((((uint8_t *)pobj
>= &pmHeap
.base
[0])
611 && ((uint8_t *)pobj
<= &pmHeap
.base
[PM_HEAP_SIZE
]))
612 || ((uint8_t *)pobj
== (uint8_t *)&gVmGlobal
.nativeframe
));
614 /* The object must not already be free */
615 C_ASSERT(OBJ_GET_FREE(pobj
) == 0);
617 type
= (PmType_t
)OBJ_GET_TYPE(pobj
);
620 /* Objects with no references to other objects */
628 OBJ_SET_GCVAL(pobj
, pmHeap
.gcval
);
632 i
= ((pPmTuple_t
)pobj
)->length
;
634 /* Mark tuple head */
635 OBJ_SET_GCVAL(pobj
, pmHeap
.gcval
);
637 /* Mark each obj in tuple */
640 retval
= heap_gcMarkObj(((pPmTuple_t
)pobj
)->val
[i
]);
641 PM_RETURN_IF_ERROR(retval
);
648 OBJ_SET_GCVAL(pobj
, pmHeap
.gcval
);
650 /* Mark the seglist */
651 retval
= heap_gcMarkObj((pPmObj_t
)((pPmList_t
)pobj
)->val
);
655 /* Mark the dict head */
656 OBJ_SET_GCVAL(pobj
, pmHeap
.gcval
);
658 /* Mark the keys seglist */
659 retval
= heap_gcMarkObj((pPmObj_t
)((pPmDict_t
)pobj
)->d_keys
);
660 PM_RETURN_IF_ERROR(retval
);
662 /* Mark the vals seglist */
663 retval
= heap_gcMarkObj((pPmObj_t
)((pPmDict_t
)pobj
)->d_vals
);
667 /* Mark the code obj head */
668 OBJ_SET_GCVAL(pobj
, pmHeap
.gcval
);
670 /* Mark the names tuple */
671 retval
= heap_gcMarkObj((pPmObj_t
)((pPmCo_t
)pobj
)->co_names
);
672 PM_RETURN_IF_ERROR(retval
);
674 /* Mark the consts tuple */
675 retval
= heap_gcMarkObj((pPmObj_t
)((pPmCo_t
)pobj
)->co_consts
);
676 PM_RETURN_IF_ERROR(retval
);
678 /* #122: Mark the code image if it is in RAM */
679 if (((pPmCo_t
)pobj
)->co_memspace
== MEMSPACE_RAM
)
681 retval
= heap_gcMarkObj((pPmObj_t
)
682 (((pPmCo_t
)pobj
)->co_codeimgaddr
));
683 PM_RETURN_IF_ERROR(retval
);
687 /* #256: Add support for closures */
688 /* Mark the cellvars tuple */
689 retval
= heap_gcMarkObj((pPmObj_t
)((pPmCo_t
)pobj
)->co_cellvars
);
690 #endif /* HAVE_CLOSURES */
695 /* Module and Func objs are implemented via the PmFunc_t */
696 /* Mark the func obj head */
697 OBJ_SET_GCVAL(pobj
, pmHeap
.gcval
);
699 /* Mark the code obj */
700 retval
= heap_gcMarkObj((pPmObj_t
)((pPmFunc_t
)pobj
)->f_co
);
701 PM_RETURN_IF_ERROR(retval
);
703 /* Mark the attr dict */
704 retval
= heap_gcMarkObj((pPmObj_t
)((pPmFunc_t
)pobj
)->f_attrs
);
705 PM_RETURN_IF_ERROR(retval
);
707 /* Mark the globals dict */
708 retval
= heap_gcMarkObj((pPmObj_t
)((pPmFunc_t
)pobj
)->f_globals
);
709 PM_RETURN_IF_ERROR(retval
);
711 #ifdef HAVE_DEFAULTARGS
712 /* Mark the default args tuple */
713 retval
= heap_gcMarkObj((pPmObj_t
)((pPmFunc_t
)pobj
)->f_defaultargs
);
714 PM_RETURN_IF_ERROR(retval
);
715 #endif /* HAVE_DEFAULTARGS */
718 /* #256: Mark the closure tuple */
719 retval
= heap_gcMarkObj((pPmObj_t
)((pPmFunc_t
)pobj
)->f_closure
);
720 #endif /* HAVE_CLOSURES */
725 /* Mark the obj head */
726 OBJ_SET_GCVAL(pobj
, pmHeap
.gcval
);
729 retval
= heap_gcMarkObj((pPmObj_t
)((pPmInstance_t
)pobj
)->cli_class
);
730 PM_RETURN_IF_ERROR(retval
);
732 /* Mark the attrs dict */
733 retval
= heap_gcMarkObj((pPmObj_t
)((pPmInstance_t
)pobj
)->cli_attrs
);
737 /* Mark the obj head */
738 OBJ_SET_GCVAL(pobj
, pmHeap
.gcval
);
740 /* Mark the instance */
741 retval
= heap_gcMarkObj((pPmObj_t
)((pPmMethod_t
)pobj
)->m_instance
);
742 PM_RETURN_IF_ERROR(retval
);
745 retval
= heap_gcMarkObj((pPmObj_t
)((pPmMethod_t
)pobj
)->m_func
);
746 PM_RETURN_IF_ERROR(retval
);
748 /* Mark the attrs dict */
749 retval
= heap_gcMarkObj((pPmObj_t
)((pPmMethod_t
)pobj
)->m_attrs
);
753 /* Mark the obj head */
754 OBJ_SET_GCVAL(pobj
, pmHeap
.gcval
);
756 /* Mark the attrs dict */
757 retval
= heap_gcMarkObj((pPmObj_t
)((pPmClass_t
)pobj
)->cl_attrs
);
758 PM_RETURN_IF_ERROR(retval
);
760 /* Mark the base tuple */
761 retval
= heap_gcMarkObj((pPmObj_t
)((pPmClass_t
)pobj
)->cl_bases
);
763 #endif /* HAVE_CLASSES */
766 * An obj in ram should not be of these types.
767 * Images arrive in RAM as string objects (image is array of bytes)
771 PM_RAISE(retval
, PM_RET_EX_SYS
);
776 pPmObj_t
*ppobj2
= C_NULL
;
778 /* Mark the frame obj head */
779 OBJ_SET_GCVAL(pobj
, pmHeap
.gcval
);
781 /* Mark the previous frame, if this isn't a generator's frame */
782 /* Issue #129: Fix iterator losing its object */
783 if ((((pPmFrame_t
)pobj
)->fo_func
->f_co
->co_flags
& CO_GENERATOR
) == 0)
785 retval
= heap_gcMarkObj((pPmObj_t
)((pPmFrame_t
)pobj
)->fo_back
);
786 PM_RETURN_IF_ERROR(retval
);
789 /* Mark the fxn obj */
790 retval
= heap_gcMarkObj((pPmObj_t
)((pPmFrame_t
)pobj
)->fo_func
);
791 PM_RETURN_IF_ERROR(retval
);
793 /* Mark the blockstack */
794 retval
= heap_gcMarkObj((pPmObj_t
)
795 ((pPmFrame_t
)pobj
)->fo_blockstack
);
796 PM_RETURN_IF_ERROR(retval
);
798 /* Mark the attrs dict */
799 retval
= heap_gcMarkObj((pPmObj_t
)((pPmFrame_t
)pobj
)->fo_attrs
);
800 PM_RETURN_IF_ERROR(retval
);
802 /* Mark the globals dict */
803 retval
= heap_gcMarkObj((pPmObj_t
)((pPmFrame_t
)pobj
)->fo_globals
);
804 PM_RETURN_IF_ERROR(retval
);
806 /* Mark each obj in the locals list and the stack */
807 ppobj2
= ((pPmFrame_t
)pobj
)->fo_locals
;
808 while (ppobj2
< ((pPmFrame_t
)pobj
)->fo_sp
)
810 retval
= heap_gcMarkObj(*ppobj2
);
811 PM_RETURN_IF_ERROR(retval
);
818 /* Mark the block obj head */
819 OBJ_SET_GCVAL(pobj
, pmHeap
.gcval
);
821 /* Mark the next block in the stack */
822 retval
= heap_gcMarkObj((pPmObj_t
)((pPmBlock_t
)pobj
)->next
);
826 /* Mark the seglist obj head */
827 OBJ_SET_GCVAL(pobj
, pmHeap
.gcval
);
829 /* Mark the seglist's segments */
830 n
= ((pSeglist_t
)pobj
)->sl_length
;
831 pobj
= (pPmObj_t
)((pSeglist_t
)pobj
)->sl_rootseg
;
832 for (i
= 0; i
< n
; i
++)
834 /* Mark the segment item */
835 retval
= heap_gcMarkObj(((pSegment_t
)pobj
)->s_val
[i
% SEGLIST_OBJS_PER_SEG
]);
836 PM_RETURN_IF_ERROR(retval
);
838 /* Mark the segment obj head */
839 if ((i
% SEGLIST_OBJS_PER_SEG
) == 0)
841 OBJ_SET_GCVAL(pobj
, pmHeap
.gcval
);
844 /* Point to the next segment */
846 if ((i
% SEGLIST_OBJS_PER_SEG
) == (SEGLIST_OBJS_PER_SEG
- 1))
848 pobj
= (pPmObj_t
)((pSegment_t
)pobj
)->next
;
858 /* Mark the sequence iterator obj head */
859 OBJ_SET_GCVAL(pobj
, pmHeap
.gcval
);
861 /* Mark the sequence */
862 retval
= heap_gcMarkObj(((pPmSeqIter_t
)pobj
)->si_sequence
);
866 /* Mark the thread obj head */
867 OBJ_SET_GCVAL(pobj
, pmHeap
.gcval
);
869 /* Mark the current frame */
870 retval
= heap_gcMarkObj((pPmObj_t
)((pPmThread_t
)pobj
)->pframe
);
875 * Mark the obj desc. This doesn't really do much since the
876 * native frame is declared static (not from the heap), but this
877 * is here in case that ever changes
879 OBJ_SET_GCVAL(pobj
, pmHeap
.gcval
);
881 /* Mark the native frame's remaining fields if active */
882 if (gVmGlobal
.nativeframe
.nf_active
)
884 /* Mark the frame stack */
885 retval
= heap_gcMarkObj((pPmObj_t
)
886 gVmGlobal
.nativeframe
.nf_back
);
887 PM_RETURN_IF_ERROR(retval
);
889 /* Mark the function object */
890 retval
= heap_gcMarkObj((pPmObj_t
)
891 gVmGlobal
.nativeframe
.nf_func
);
892 PM_RETURN_IF_ERROR(retval
);
894 /* Mark the stack object */
895 retval
= heap_gcMarkObj(gVmGlobal
.nativeframe
.nf_stack
);
896 PM_RETURN_IF_ERROR(retval
);
898 /* Mark the args to the native func */
899 for (i
= 0; i
< NATIVE_GET_NUM_ARGS(); i
++)
902 heap_gcMarkObj(gVmGlobal
.nativeframe
.nf_locals
[i
]);
903 PM_RETURN_IF_ERROR(retval
);
908 #ifdef HAVE_BYTEARRAY
910 OBJ_SET_GCVAL(pobj
, pmHeap
.gcval
);
912 retval
= heap_gcMarkObj((pPmObj_t
)((pPmBytearray_t
)pobj
)->val
);
916 OBJ_SET_GCVAL(pobj
, pmHeap
.gcval
);
918 #endif /* HAVE_BYTEARRAY */
921 /* There should be no invalid types */
922 PM_RAISE(retval
, PM_RET_EX_SYS
);
930 * Marks the root objects so they won't be collected during the sweep phase.
931 * Recursively marks all objects reachable from the roots.
934 heap_gcMarkRoots(void)
939 /* Toggle the GC marking value so it differs from the last run */
942 /* Mark the constant objects */
943 retval
= heap_gcMarkObj(PM_NONE
);
944 PM_RETURN_IF_ERROR(retval
);
945 retval
= heap_gcMarkObj(PM_FALSE
);
946 PM_RETURN_IF_ERROR(retval
);
947 retval
= heap_gcMarkObj(PM_TRUE
);
948 PM_RETURN_IF_ERROR(retval
);
949 retval
= heap_gcMarkObj(PM_ZERO
);
950 PM_RETURN_IF_ERROR(retval
);
951 retval
= heap_gcMarkObj(PM_ONE
);
952 PM_RETURN_IF_ERROR(retval
);
953 retval
= heap_gcMarkObj(PM_NEGONE
);
954 PM_RETURN_IF_ERROR(retval
);
955 retval
= heap_gcMarkObj(PM_CODE_STR
);
956 PM_RETURN_IF_ERROR(retval
);
958 /* Mark the builtins dict */
959 retval
= heap_gcMarkObj(PM_PBUILTINS
);
960 PM_RETURN_IF_ERROR(retval
);
962 /* Mark the native frame if it is active */
963 retval
= heap_gcMarkObj((pPmObj_t
)&gVmGlobal
.nativeframe
);
964 PM_RETURN_IF_ERROR(retval
);
966 /* Mark the thread list */
967 retval
= heap_gcMarkObj((pPmObj_t
)gVmGlobal
.threadList
);
968 PM_RETURN_IF_ERROR(retval
);
970 /* Mark the temporary roots */
971 for (i
= 0; i
< pmHeap
.temp_root_index
; i
++)
973 retval
= heap_gcMarkObj(pmHeap
.temp_roots
[i
]);
974 PM_RETURN_IF_ERROR(retval
);
983 * Unlinks free objects from the string cache.
984 * This function must only be called by the GC after the heap has been marked
985 * and before the heap has been swept.
987 * This solves the problem where a string object would be collected
988 * but its chunk was still linked into the free list
990 * @param gcval The current value for chunks marked by the GC
993 heap_purgeStringCache(uint8_t gcval
)
996 pPmString_t
*ppstrcache
;
999 /* Update string cache pointer if the first string objs are not marked */
1000 retval
= string_getCache(&ppstrcache
);
1001 if (ppstrcache
== C_NULL
)
1005 while ((*ppstrcache
!= C_NULL
) && (OBJ_GET_GCVAL(*ppstrcache
) != gcval
))
1007 *ppstrcache
= (*ppstrcache
)->next
;
1009 if (*ppstrcache
== C_NULL
)
1014 /* Unlink remaining strings that are not marked */
1015 for (pstr
= *ppstrcache
; pstr
->next
!= C_NULL
;)
1017 /* Unlink consecutive non-marked strings */
1018 while ((pstr
->next
!= C_NULL
) && (OBJ_GET_GCVAL(pstr
->next
) != gcval
))
1020 pstr
->next
= pstr
->next
->next
;
1023 /* If not at end of cache, string must be marked, skip it */
1024 if (pstr
->next
!= C_NULL
)
1036 * Reclaims any object that does not have a current mark.
1037 * Puts it in the free list. Coalesces all contiguous free chunks.
1044 pPmHeapDesc_t pchunk
;
1045 uint16_t totalchunksize
;
1047 #if USE_STRING_CACHE
1048 retval
= heap_purgeStringCache(pmHeap
.gcval
);
1051 /* Start at the base of the heap */
1052 pobj
= (pPmObj_t
)pmHeap
.base
;
1053 while ((uint8_t *)pobj
< &pmHeap
.base
[PM_HEAP_SIZE
])
1055 /* Skip to the next unmarked or free chunk within the heap */
1056 while (!OBJ_GET_FREE(pobj
)
1057 && (OBJ_GET_GCVAL(pobj
) == pmHeap
.gcval
)
1058 && ((uint8_t *)pobj
< &pmHeap
.base
[PM_HEAP_SIZE
]))
1060 pobj
= (pPmObj_t
)((uint8_t *)pobj
+ OBJ_GET_SIZE(pobj
));
1063 /* Stop if reached the end of the heap */
1064 if ((uint8_t *)pobj
>= &pmHeap
.base
[PM_HEAP_SIZE
])
1069 /* Accumulate the sizes of all consecutive unmarked or free chunks */
1072 /* Coalesce all contiguous free chunks */
1073 pchunk
= (pPmHeapDesc_t
)pobj
;
1074 while (OBJ_GET_FREE(pchunk
)
1075 || (!OBJ_GET_FREE(pchunk
)
1076 && (OBJ_GET_GCVAL(pchunk
) != pmHeap
.gcval
)))
1078 if ((totalchunksize
+ OBJ_GET_SIZE(pchunk
))
1079 > HEAP_MAX_FREE_CHUNK_SIZE
)
1083 totalchunksize
= totalchunksize
+ OBJ_GET_SIZE(pchunk
);
1086 * If the chunk is already free, unlink it because its size
1087 * is about to change
1089 if (OBJ_GET_FREE(pchunk
))
1091 retval
= heap_unlinkFromFreelist(pchunk
);
1092 PM_RETURN_IF_ERROR(retval
);
1095 /* Otherwise free and reclaim the unmarked chunk */
1098 OBJ_SET_TYPE(pchunk
, 0);
1099 OBJ_SET_FREE(pchunk
, 1);
1102 C_DEBUG_PRINT(VERBOSITY_HIGH
, "heap_gcSweep(), id=%p, s=%d\n",
1103 pchunk
, OBJ_GET_SIZE(pchunk
));
1105 /* Proceed to the next chunk */
1106 pchunk
= (pPmHeapDesc_t
)
1107 ((uint8_t *)pchunk
+ OBJ_GET_SIZE(pchunk
));
1109 /* Stop if it's past the end of the heap */
1110 if ((uint8_t *)pchunk
>= &pmHeap
.base
[PM_HEAP_SIZE
])
1116 /* Set the heap descriptor data */
1117 OBJ_SET_FREE(pobj
, 1);
1118 OBJ_SET_SIZE(pobj
, totalchunksize
);
1120 /* Insert chunk into free list */
1121 retval
= heap_linkToFreelist((pPmHeapDesc_t
)pobj
);
1122 PM_RETURN_IF_ERROR(retval
);
1124 /* Continue to the next chunk */
1125 pobj
= (pPmObj_t
)pchunk
;
1132 /* Runs the mark-sweep garbage collector */
1138 /* #239: Fix GC when 2+ unlinked allocs occur */
1139 /* This assertion fails when there are too many objects on the temporary
1140 * root stack and a GC occurs; consider increasing PM_HEAP_NUM_TEMP_ROOTS
1142 C_ASSERT(pmHeap
.temp_root_index
< HEAP_NUM_TEMP_ROOTS
);
1144 C_DEBUG_PRINT(VERBOSITY_LOW
, "heap_gcRun()\n");
1147 retval
= heap_gcMarkRoots();
1148 PM_RETURN_IF_ERROR(retval
);
1150 retval
= heap_gcSweep();
1156 /* Enables or disables automatic garbage collection */
1158 heap_gcSetAuto(uint8_t auto_gc
)
1160 pmHeap
.auto_gc
= auto_gc
;
1164 void heap_gcPushTempRoot(pPmObj_t pobj
, uint8_t *r_objid
)
1166 if (pmHeap
.temp_root_index
< HEAP_NUM_TEMP_ROOTS
)
1168 *r_objid
= pmHeap
.temp_root_index
;
1169 pmHeap
.temp_roots
[pmHeap
.temp_root_index
] = pobj
;
1170 pmHeap
.temp_root_index
++;
1176 void heap_gcPopTempRoot(uint8_t objid
)
1178 pmHeap
.temp_root_index
= objid
;
1183 void heap_gcPushTempRoot(pPmObj_t pobj
, uint8_t *r_objid
) {}
1184 void heap_gcPopTempRoot(uint8_t objid
) {}
1186 #endif /* HAVE_GC */