Merged in f5soh/librepilot/update_credits (pull request #529)
[librepilot.git] / flight / libraries / PyMite / vm / heap.c
blob96b1e0941c2ce9f00a75a354b74518913617e07b
1 /*
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.
16 #undef __FILE_ID__
17 #define __FILE_ID__ 0x06
20 /**
21 * \file
22 * \brief VM Heap
24 * VM heap operations.
25 * All of PyMite's dynamic memory is obtained from this heap.
26 * The heap provides dynamic memory on demand.
30 #include "pm.h"
33 /** Checks for heap size definition. */
34 #ifndef PM_HEAP_SIZE
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
38 #endif
41 /** The size of the temporary roots stack */
42 #define HEAP_NUM_TEMP_ROOTS 24
44 /**
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
52 /**
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)
64 /**
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)
70 /**
71 * Sets the GC's mark bit for the object
72 * This MUST NOT be called on objects that are free.
74 #ifdef HAVE_GC
75 #define OBJ_SET_GCVAL(pobj, gcval) \
76 do \
77 { \
78 ((pPmObj_t)pobj)->od = (gcval) ? ((pPmObj_t)pobj)->od | OD_MARK_BIT \
79 : ((pPmObj_t)pobj)->od & ~OD_MARK_BIT;\
80 } \
81 while (0)
82 #else
83 #define OBJ_SET_GCVAL(pobj, gcval)
84 #endif /* HAVE_GC */
87 /**
88 * The following is a diagram of the heap descriptor at the head of the chunk:
89 * @verbatim
90 * MSb LSb
91 * 7 6 5 4 3 2 1 0
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
96 * +-+-+-----------+
97 * | P(L) | P := hd_prev: Pointer to previous node
98 * | P(H) | N := hd_next: Pointer to next node
99 * | N(L) |
100 * | N(H) | Theoretical min size == 6
101 * +---------------+ Effective min size == 8
102 * | unused space | (12 on 32-bit MCUs)
103 * ... ...
104 * | end chunk |
105 * +---------------+
106 * @endverbatim
108 typedef struct PmHeapDesc_s
110 /** Heap descriptor */
111 uint16_t hd;
113 /** Ptr to prev heap chunk */
114 struct PmHeapDesc_s *prev;
116 /** Ptr to next heap chunk */
117 struct PmHeapDesc_s *next;
118 } PmHeapDesc_t,
119 *pPmHeapDesc_t;
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
136 uint32_t avail;
137 #else
138 uint16_t avail;
139 #endif
141 #ifdef HAVE_GC
142 /** Garbage collection mark value */
143 uint8_t gcval;
145 /** Boolean to indicate if GC should run automatically */
146 uint8_t auto_gc;
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;
153 #endif /* HAVE_GC */
155 } PmHeap_t,
156 *pPmHeap_t;
159 /** The PyMite heap */
160 static PmHeap_t pmHeap PM_PLAT_HEAP_ATTR;
163 #if 0
164 static void
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;
178 #endif
181 #if 0
182 /** DEBUG: dumps the heap and roots list to a file */
183 static void
184 heap_dump(void)
186 static int n = 0;
187 uint16_t s;
188 uint32_t i;
189 void *b;
190 char filename[32];
191 FILE *fp;
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);
198 s = 0x5544;
199 fwrite(&s, sizeof(uint16_t), 1, fp);
200 fwrite(&"MP", 1, 2, fp);
202 /* pointer size */
203 s = sizeof(intptr_t);
204 fwrite(&s, sizeof(uint16_t), 1, fp);
206 /* dump version */
207 s = 1;
208 fwrite(&s, sizeof(uint16_t), 1, fp);
210 /* pmfeatures */
211 s = 0;
212 #ifdef USE_STRING_CACHE
213 s |= 1<<0;
214 #endif
215 #ifdef HAVE_DEFAULTARGS
216 s |= 1<<1;
217 #endif
218 #ifdef HAVE_CLOSURES
219 s |= 1<<2;
220 #endif
221 #ifdef HAVE_CLASSES
222 s |= 1<<3;
223 #endif
224 fwrite(&s, sizeof(uint16_t), 1, fp);
226 /* size of heap */
227 i = PM_HEAP_SIZE;
228 fwrite(&i, sizeof(uint32_t), 1, fp);
230 /* Write base address of heap */
231 b=&pmHeap.base;
232 fwrite((void*)(&b), sizeof(intptr_t), 1, fp);
234 /* Write contents of heap */
235 fwrite(&pmHeap.base, 1, PM_HEAP_SIZE, fp);
237 /* Write num roots*/
238 i = 10;
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);
252 fclose(fp);
254 #endif
257 /* Removes the given chunk from the free list; leaves list in sorted order */
258 static PmReturn_t
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;
275 else
277 pchunk->prev->next = pchunk->next;
280 return PM_RET_OK;
284 /* Inserts in order a chunk into the free list. Caller adjusts heap state */
285 static PmReturn_t
286 heap_linkToFreelist(pPmHeapDesc_t pchunk)
288 uint16_t size;
289 pPmHeapDesc_t pscan;
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;
303 return PM_RET_OK;
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))
311 pscan = pscan->next;
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 */
327 else
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;
337 else
339 pscan->prev->next = pchunk;
341 pscan->prev = pchunk;
344 return PM_RET_OK;
349 * Initializes the heap state variables
351 PmReturn_t
352 heap_init(void)
354 pPmHeapDesc_t pchunk;
356 #if PM_HEAP_SIZE > 65535
357 uint32_t hs;
358 #else
359 uint16_t hs;
360 #endif
362 #if __DEBUG__
363 /* Fill the heap with a non-NULL value to bring out any heap bugs. */
364 sli_memset(pmHeap.base, 0xAA, sizeof(pmHeap.base));
365 #endif
367 /* Init heap globals */
368 pmHeap.pfreelist = C_NULL;
369 pmHeap.avail = 0;
370 #ifdef HAVE_GC
371 pmHeap.gcval = (uint8_t)0;
372 pmHeap.temp_root_index = (uint8_t)0;
373 heap_gcSetAuto(C_TRUE);
374 #endif /* HAVE_GC */
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);
383 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 */
391 hs = hs & ~3;
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);
400 string_cacheInit();
402 return PM_RET_OK;
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
418 static PmReturn_t
419 heap_getChunkImpl(uint16_t size, uint8_t **r_pchunk)
421 PmReturn_t retval;
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)
437 *r_pchunk = C_NULL;
438 PM_RAISE(retval, PM_RET_EX_MEM);
439 return retval;
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,
465 size);
467 else
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;
487 return retval;
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.
497 PmReturn_t
498 heap_getChunk(uint16_t requestedsize, uint8_t **r_pchunk)
500 PmReturn_t retval;
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);
507 return retval;
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);
524 #ifdef HAVE_GC
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);
535 #endif /* HAVE_GC */
537 /* Ensure that the pointer is 4-byte aligned */
538 if (retval == PM_RET_OK)
540 C_ASSERT(((intptr_t)*r_pchunk & 3) == 0);
543 return retval;
547 /* Releases chunk to the free list */
548 PmReturn_t
549 heap_freeChunk(pPmObj_t ptr)
551 PmReturn_t retval;
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);
568 return retval;
572 /* Returns, by reference, the number of bytes available in the heap */
573 #if PM_HEAP_SIZE > 65535
574 uint32_t
575 #else
576 uint16_t
577 #endif
578 heap_getAvail(void)
580 return pmHeap.avail;
584 #ifdef HAVE_GC
586 * Marks the given object and the objects it references.
588 * @param pobj Any non-free heap object
589 * @return Return code
591 static PmReturn_t
592 heap_gcMarkObj(pPmObj_t pobj)
594 PmReturn_t retval = PM_RET_OK;
595 int16_t i = 0;
596 int16_t n;
597 PmType_t type;
599 /* Return if ptr is null or object is already marked */
600 if (pobj == C_NULL)
602 return retval;
604 if (OBJ_GET_GCVAL(pobj) == pmHeap.gcval)
606 return retval;
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);
618 switch (type)
620 /* Objects with no references to other objects */
621 case OBJ_TYPE_NON:
622 case OBJ_TYPE_INT:
623 case OBJ_TYPE_FLT:
624 case OBJ_TYPE_STR:
625 case OBJ_TYPE_NOB:
626 case OBJ_TYPE_BOOL:
627 case OBJ_TYPE_CIO:
628 OBJ_SET_GCVAL(pobj, pmHeap.gcval);
629 break;
631 case OBJ_TYPE_TUP:
632 i = ((pPmTuple_t)pobj)->length;
634 /* Mark tuple head */
635 OBJ_SET_GCVAL(pobj, pmHeap.gcval);
637 /* Mark each obj in tuple */
638 while (--i >= 0)
640 retval = heap_gcMarkObj(((pPmTuple_t)pobj)->val[i]);
641 PM_RETURN_IF_ERROR(retval);
643 break;
645 case OBJ_TYPE_LST:
647 /* Mark the list */
648 OBJ_SET_GCVAL(pobj, pmHeap.gcval);
650 /* Mark the seglist */
651 retval = heap_gcMarkObj((pPmObj_t)((pPmList_t)pobj)->val);
652 break;
654 case OBJ_TYPE_DIC:
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);
664 break;
666 case OBJ_TYPE_COB:
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);
686 #ifdef HAVE_CLOSURES
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 */
691 break;
693 case OBJ_TYPE_MOD:
694 case OBJ_TYPE_FXN:
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 */
717 #ifdef HAVE_CLOSURES
718 /* #256: Mark the closure tuple */
719 retval = heap_gcMarkObj((pPmObj_t)((pPmFunc_t)pobj)->f_closure);
720 #endif /* HAVE_CLOSURES */
721 break;
723 #ifdef HAVE_CLASSES
724 case OBJ_TYPE_CLI:
725 /* Mark the obj head */
726 OBJ_SET_GCVAL(pobj, pmHeap.gcval);
728 /* Mark the class */
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);
734 break;
736 case OBJ_TYPE_MTH:
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);
744 /* Mark the func */
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);
750 break;
752 case OBJ_TYPE_CLO:
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);
762 break;
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)
769 case OBJ_TYPE_CIM:
770 case OBJ_TYPE_NIM:
771 PM_RAISE(retval, PM_RET_EX_SYS);
772 return retval;
774 case OBJ_TYPE_FRM:
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);
812 ppobj2++;
814 break;
817 case OBJ_TYPE_BLK:
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);
823 break;
825 case OBJ_TYPE_SGL:
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 */
845 else
846 if ((i % SEGLIST_OBJS_PER_SEG) == (SEGLIST_OBJS_PER_SEG - 1))
848 pobj = (pPmObj_t)((pSegment_t)pobj)->next;
849 if (pobj == C_NULL)
851 break;
855 break;
857 case OBJ_TYPE_SQI:
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);
863 break;
865 case OBJ_TYPE_THR:
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);
871 break;
873 case OBJ_TYPE_NFM:
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++)
901 retval =
902 heap_gcMarkObj(gVmGlobal.nativeframe.nf_locals[i]);
903 PM_RETURN_IF_ERROR(retval);
906 break;
908 #ifdef HAVE_BYTEARRAY
909 case OBJ_TYPE_BYA:
910 OBJ_SET_GCVAL(pobj, pmHeap.gcval);
912 retval = heap_gcMarkObj((pPmObj_t)((pPmBytearray_t)pobj)->val);
913 break;
915 case OBJ_TYPE_BYS:
916 OBJ_SET_GCVAL(pobj, pmHeap.gcval);
917 break;
918 #endif /* HAVE_BYTEARRAY */
920 default:
921 /* There should be no invalid types */
922 PM_RAISE(retval, PM_RET_EX_SYS);
923 break;
925 return retval;
930 * Marks the root objects so they won't be collected during the sweep phase.
931 * Recursively marks all objects reachable from the roots.
933 static PmReturn_t
934 heap_gcMarkRoots(void)
936 PmReturn_t retval;
937 uint8_t i;
939 /* Toggle the GC marking value so it differs from the last run */
940 pmHeap.gcval ^= 1;
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);
977 return retval;
981 #if USE_STRING_CACHE
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
992 static PmReturn_t
993 heap_purgeStringCache(uint8_t gcval)
995 PmReturn_t retval;
996 pPmString_t *ppstrcache;
997 pPmString_t pstr;
999 /* Update string cache pointer if the first string objs are not marked */
1000 retval = string_getCache(&ppstrcache);
1001 if (ppstrcache == C_NULL)
1003 return retval;
1005 while ((*ppstrcache != C_NULL) && (OBJ_GET_GCVAL(*ppstrcache) != gcval))
1007 *ppstrcache = (*ppstrcache)->next;
1009 if (*ppstrcache == C_NULL)
1011 return retval;
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)
1026 pstr = pstr->next;
1030 return retval;
1032 #endif
1036 * Reclaims any object that does not have a current mark.
1037 * Puts it in the free list. Coalesces all contiguous free chunks.
1039 static PmReturn_t
1040 heap_gcSweep(void)
1042 PmReturn_t retval;
1043 pPmObj_t pobj;
1044 pPmHeapDesc_t pchunk;
1045 uint16_t totalchunksize;
1047 #if USE_STRING_CACHE
1048 retval = heap_purgeStringCache(pmHeap.gcval);
1049 #endif
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])
1066 break;
1069 /* Accumulate the sizes of all consecutive unmarked or free chunks */
1070 totalchunksize = 0;
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)
1081 break;
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 */
1096 else
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])
1112 break;
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;
1128 return PM_RET_OK;
1132 /* Runs the mark-sweep garbage collector */
1133 PmReturn_t
1134 heap_gcRun(void)
1136 PmReturn_t retval;
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");
1145 /*heap_dump();*/
1147 retval = heap_gcMarkRoots();
1148 PM_RETURN_IF_ERROR(retval);
1150 retval = heap_gcSweep();
1151 /*heap_dump();*/
1152 return retval;
1156 /* Enables or disables automatic garbage collection */
1157 PmReturn_t
1158 heap_gcSetAuto(uint8_t auto_gc)
1160 pmHeap.auto_gc = auto_gc;
1161 return PM_RET_OK;
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++;
1172 return;
1176 void heap_gcPopTempRoot(uint8_t objid)
1178 pmHeap.temp_root_index = objid;
1181 #else
1183 void heap_gcPushTempRoot(pPmObj_t pobj, uint8_t *r_objid) {}
1184 void heap_gcPopTempRoot(uint8_t objid) {}
1186 #endif /* HAVE_GC */