python-on-a-chip online compiler

Dependencies:   mbed TSI

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers heap.c Source File

heap.c

Go to the documentation of this file.
00001 /*
00002 # This file is Copyright 2002 Dean Hall.
00003 # This file is part of the PyMite VM.
00004 # This file is licensed under the MIT License.
00005 # See the LICENSE file for details.
00006 */
00007 
00008 
00009 #undef __FILE_ID__
00010 #define __FILE_ID__ 0x06
00011 
00012 
00013 /**
00014  * \file
00015  * \brief VM Heap
00016  *
00017  * VM heap operations.
00018  * All of PyMite's dynamic memory is obtained from this heap.
00019  * The heap provides dynamic memory on demand.
00020  */
00021 
00022 
00023 #include "pm.h"
00024 
00025 
00026 /** The size of the temporary roots stack */
00027 #define HEAP_NUM_TEMP_ROOTS 24
00028 
00029 /**
00030  * The maximum size a live chunk can be (a live chunk is one that is in use).
00031  * The live chunk size is determined by the size field in the *object*
00032  * descriptor.  That field is nine bits with two assumed lsbs (zeros):
00033  * (0x1FF << 2) == 2044
00034  */
00035 #ifdef PM_PLAT_POINTER_SIZE
00036 #if PM_PLAT_POINTER_SIZE == 8
00037 #define HEAP_MAX_LIVE_CHUNK_SIZE 2040
00038 #else
00039 #define HEAP_MAX_LIVE_CHUNK_SIZE 2044
00040 #endif
00041 #endif
00042 
00043 /**
00044  * The maximum size a free chunk can be (a free chunk is one that is not in use).
00045  * The free chunk size is limited by the size field in the *heap* descriptor.
00046  * That field is fourteen bits with two assumed least significant bits (zeros):
00047  * (0x3FFF << 2) == 65532
00048  * For 64-bit platforms, the value is 4 bytes less so that a max-sized chunk is
00049  * a multiple of 8, so that max-sized chunks created during heap_init() have a
00050  * good boundary value.
00051  */
00052 #ifdef PM_PLAT_POINTER_SIZE
00053 #if PM_PLAT_POINTER_SIZE == 8
00054 #define HEAP_MAX_FREE_CHUNK_SIZE 65528
00055 #else
00056 #define HEAP_MAX_FREE_CHUNK_SIZE 65532
00057 #endif
00058 #endif
00059 
00060 /** The minimum size a chunk can be
00061  * (rounded up to a multiple of platform-pointer-size) */
00062 #ifdef PM_PLAT_POINTER_SIZE
00063 #if PM_PLAT_POINTER_SIZE == 8
00064 #define HEAP_MIN_CHUNK_SIZE ((sizeof(PmHeapDesc_t) + 7) & ~7)
00065 #else
00066 #define HEAP_MIN_CHUNK_SIZE ((sizeof(PmHeapDesc_t) + 3) & ~3)
00067 #endif
00068 #endif
00069 
00070 
00071 
00072 /**
00073  * Gets the GC's mark bit for the object.
00074  * This MUST NOT be called on objects that are free.
00075  */
00076 #define OBJ_GET_GCVAL(pobj) (((pPmObj_t)pobj)->od & OD_MARK_MASK)
00077 
00078 /**
00079  * Sets the GC's mark bit for the object
00080  * This MUST NOT be called on objects that are free.
00081  */
00082 #ifdef HAVE_GC
00083 #define OBJ_SET_GCVAL(pobj, gcval) \
00084     do \
00085     { \
00086         ((pPmObj_t)pobj)->od = (gcval) ? ((pPmObj_t)pobj)->od | OD_MARK_MASK \
00087                                        : ((pPmObj_t)pobj)->od & ~OD_MARK_MASK;\
00088     } \
00089     while (0)
00090 #else
00091 #define OBJ_SET_GCVAL(pobj, gcval)
00092 #endif /* HAVE_GC */
00093 
00094 #define CHUNK_GET_SIZE(pchunk) (((pPmHeapDesc_t)pchunk)->hd & HD_SIZE_MASK)
00095 
00096 /** Sets the size of the chunk in bytes. */
00097 #define CHUNK_SET_SIZE(pchunk, size) \
00098     do \
00099     { \
00100         ((pPmHeapDesc_t)(pchunk))->hd &= ~HD_SIZE_MASK; \
00101         ((pPmHeapDesc_t)(pchunk))->hd |= ((size) & HD_SIZE_MASK); \
00102     } \
00103     while (0)
00104 
00105 #define OBJ_SET_SIZE(pobj, size) \
00106     do \
00107     { \
00108         ((pPmObj_t)pobj)->od &= ~OD_SIZE_MASK; \
00109         ((pPmObj_t)pobj)->od |= ((size) & OD_SIZE_MASK); \
00110     } \
00111     while (0)
00112 
00113 
00114 /**
00115  * The following is a diagram of the heap descriptor at the head of the chunk:
00116  * @verbatim
00117  *                MSb          LSb
00118  *                7 6 5 4 3 2 1 0
00119  *      pchunk-> +-+-+-+-+-+-+-+-+     S := Size of the chunk (2 LSbs dropped)
00120  *               |     S     |F|R|     F := Chunk free bit (not in use)
00121  *               +-----------+-+-+     R := Bit reserved for future use
00122  *               |     S         |
00123  *               +---------------+
00124  *               |     P(L)      |     P := hd_prev: Pointer to previous node
00125  *               |     P(H)      |     N := hd_next: Pointer to next node
00126  *               |     N(L)      |
00127  *               |     N(H)      |
00128  *               +---------------+
00129  *               | unused space  |
00130  *               ...           ...
00131  *               | end chunk     |
00132  *               +---------------+
00133  * @endverbatim
00134  *
00135  * On an 8-bit MCU with 16-bit addresses, the theoretical minimum size of the
00136  * heap descriptor is 6 bytes.  The effective size (due to pointer alignment)
00137  * is usually 8 bytes.  On an MCU with 32-bit addresses, the heap descriptor's
00138  * size is 12 bytes.
00139  */
00140 typedef struct PmHeapDesc_s
00141 {
00142     /** Heap descriptor */
00143     uint16_t hd;
00144 
00145     /** Ptr to prev heap chunk */
00146     struct PmHeapDesc_s *prev;
00147 
00148     /** Ptr to next heap chunk */
00149     struct PmHeapDesc_s *next;
00150 } PmHeapDesc_t,
00151  *pPmHeapDesc_t;
00152 
00153 typedef struct PmHeap_s
00154 {
00155     /** Pointer to base of heap.  Set at initialization of VM */
00156     uint8_t *base;
00157 
00158     /** Size of the heap.  Set at initialization of VM */
00159     uint32_t size;
00160 
00161     /** Ptr to list of free chunks; sorted smallest to largest. */
00162     pPmHeapDesc_t pfreelist;
00163 
00164     /** The amount of heap space available in free list */
00165     uint32_t avail;
00166 
00167 #ifdef HAVE_GC
00168     /** Garbage collection mark value */
00169     uint8_t gcval;
00170 
00171     /** Boolean to indicate if GC should run automatically */
00172     uint8_t auto_gc;
00173 
00174     /* #239: Fix GC when 2+ unlinked allocs occur */
00175     /** Stack of objects to be held as temporary roots */
00176     pPmObj_t temp_roots[HEAP_NUM_TEMP_ROOTS];
00177 
00178     uint8_t temp_root_index;
00179 #endif                          /* HAVE_GC */
00180 
00181 } PmHeap_t,
00182  *pPmHeap_t;
00183 
00184 
00185 /** The PyMite heap */
00186 static PmHeap_t pmHeap PM_PLAT_HEAP_ATTR;
00187 
00188 
00189 #if 0
00190 static void
00191 heap_gcPrintFreelist(void)
00192 {
00193     pPmHeapDesc_t pchunk = pmHeap.pfreelist;
00194 
00195     printf("DEBUG: pmHeap.avail = %d\n", pmHeap.avail);
00196     printf("DEBUG: freelist:\n");
00197     while (pchunk != C_NULL)
00198     {
00199         printf("DEBUG:     free chunk (%d bytes) @ 0x%0x\n",
00200                CHUNK_GET_SIZE(pchunk), (int)pchunk);
00201         pchunk = pchunk->next;
00202     }
00203 }
00204 #endif
00205 
00206 
00207 #if 0
00208 /** DEBUG: dumps the heap and roots list to a file */
00209 static void
00210 heap_dump(void)
00211 {
00212     static int n = 0;
00213     uint16_t s;
00214     uint32_t i;
00215     char filename[17] = "pmheapdump0N.bin\0";
00216     FILE *fp;
00217 
00218     filename[11] = '0' + n++;
00219     fp = fopen(filename, "wb");
00220 
00221     /* magic : PMDUMP for little endian or PMUDMP for big endian */
00222     fwrite(&"PM", 1, 2, fp);
00223     s = 0x5544;
00224     fwrite(&s, sizeof(uint16_t), 1, fp);
00225     fwrite(&"MP", 1, 2, fp);
00226 
00227     /* pointer size */
00228     s = sizeof(intptr_t);
00229     fwrite(&s, sizeof(uint16_t), 1, fp);
00230 
00231     /* dump version */
00232     s = 1;
00233     fwrite(&s, sizeof(uint16_t), 1, fp);
00234 
00235     /* pmfeatures */
00236     s = 0;
00237 #ifdef USE_STRING_CACHE
00238     s |= 1<<0;
00239 #endif
00240 #ifdef HAVE_DEFAULTARGS
00241     s |= 1<<1;
00242 #endif
00243 #ifdef HAVE_CLOSURES
00244     s |= 1<<2;
00245 #endif
00246 #ifdef HAVE_CLASSES
00247     s |= 1<<3;
00248 #endif
00249     fwrite(&s, sizeof(uint16_t), 1, fp);
00250 
00251     /* Size of heap */
00252     fwrite(&pmHeap.size, sizeof(uint32_t), 1, fp);
00253 
00254     /* Write base address of heap */
00255     fwrite((void*)&pmHeap.base, sizeof(intptr_t), 1, fp);
00256 
00257     /* Write contents of heap */
00258     fwrite(pmHeap.base, 1, pmHeap.size, fp);
00259 
00260     /* Write num roots*/
00261     i = 10;
00262     fwrite(&i, sizeof(uint32_t), 1, fp);
00263 
00264     /* Write heap root ptrs */
00265     fwrite((void *)&gVmGlobal.pnone, sizeof(intptr_t), 1, fp);
00266     fwrite((void *)&gVmGlobal.pfalse, sizeof(intptr_t), 1, fp);
00267     fwrite((void *)&gVmGlobal.ptrue, sizeof(intptr_t), 1, fp);
00268     fwrite((void *)&gVmGlobal.pzero, sizeof(intptr_t), 1, fp);
00269     fwrite((void *)&gVmGlobal.pone, sizeof(intptr_t), 1, fp);
00270     fwrite((void *)&gVmGlobal.pnegone, sizeof(intptr_t), 1, fp);
00271     fwrite((void *)&gVmGlobal.pcodeStr, sizeof(intptr_t), 1, fp);
00272     fwrite((void *)&gVmGlobal.builtins, sizeof(intptr_t), 1, fp);
00273     fwrite((void *)&gVmGlobal.nativeframe, sizeof(intptr_t), 1, fp);
00274     fwrite((void *)&gVmGlobal.threadList, sizeof(intptr_t), 1, fp);
00275     fclose(fp);
00276 }
00277 #endif
00278 
00279 
00280 /* Removes the given chunk from the free list; leaves list in sorted order */
00281 static PmReturn_t
00282 heap_unlinkFromFreelist(pPmHeapDesc_t pchunk)
00283 {
00284     C_ASSERT(pchunk != C_NULL);
00285 
00286     pmHeap.avail -= CHUNK_GET_SIZE(pchunk);
00287 
00288     if (pchunk->next != C_NULL)
00289     {
00290         pchunk->next->prev = pchunk->prev;
00291     }
00292 
00293     /* If pchunk was the first chunk in the free list, update the heap ptr */
00294     if (pchunk->prev == C_NULL)
00295     {
00296         pmHeap.pfreelist = pchunk->next;
00297     }
00298     else
00299     {
00300         pchunk->prev->next = pchunk->next;
00301     }
00302 
00303     return PM_RET_OK;
00304 }
00305 
00306 
00307 /* Inserts in order a chunk into the free list.  Caller adjusts heap state */
00308 static PmReturn_t
00309 heap_linkToFreelist(pPmHeapDesc_t pchunk)
00310 {
00311     uint16_t size;
00312     pPmHeapDesc_t pscan;
00313 
00314     /* Ensure the object is already free */
00315     C_ASSERT(OBJ_GET_FREE(pchunk) != 0);
00316 
00317     pmHeap.avail += CHUNK_GET_SIZE(pchunk);
00318 
00319     /* If free list is empty, add to head of list */
00320     if (pmHeap.pfreelist == C_NULL)
00321     {
00322         pmHeap.pfreelist = pchunk;
00323         pchunk->next = C_NULL;
00324         pchunk->prev = C_NULL;
00325 
00326         return PM_RET_OK;
00327     }
00328 
00329     /* Scan free list for insertion point */
00330     pscan = pmHeap.pfreelist;
00331     size = CHUNK_GET_SIZE(pchunk);
00332     while ((CHUNK_GET_SIZE(pscan) < size) && (pscan->next != C_NULL))
00333     {
00334         pscan = pscan->next;
00335     }
00336 
00337     /*
00338      * Insert chunk after the scan chunk (next is NULL).
00339      * This is a slightly rare case where the last chunk in the free list
00340      * is smaller than the chunk being freed.
00341      */
00342     if (size > CHUNK_GET_SIZE(pscan))
00343     {
00344         pchunk->next = pscan->next;
00345         pscan->next = pchunk;
00346         pchunk->prev = pscan;
00347     }
00348 
00349     /* Insert chunk before the scan chunk */
00350     else
00351     {
00352         pchunk->next = pscan;
00353         pchunk->prev = pscan->prev;
00354 
00355         /* If chunk will be first item in free list */
00356         if (pscan->prev == C_NULL)
00357         {
00358             pmHeap.pfreelist = pchunk;
00359         }
00360         else
00361         {
00362             pscan->prev->next = pchunk;
00363         }
00364         pscan->prev = pchunk;
00365     }
00366 
00367     return PM_RET_OK;
00368 }
00369 
00370 
00371 PmReturn_t
00372 heap_init(uint8_t *base, uint32_t size)
00373 {
00374     pPmHeapDesc_t pchunk;
00375     uint32_t hs;
00376     uint8_t *adjbase;
00377 
00378     /* Round-up Heap base by the size of the platform pointer */
00379     adjbase = base + ((sizeof(intptr_t) - 1) & ~(sizeof(intptr_t) - 1));
00380     pmHeap.base = adjbase;
00381     pmHeap.size = size - (adjbase - base);
00382 
00383 #if __DEBUG__
00384     /* Fill the heap with a non-NULL value to bring out any heap bugs. */
00385     sli_memset(pmHeap.base, 0xAA, pmHeap.size);
00386 #endif
00387 
00388     /* Init heap globals */
00389     pmHeap.pfreelist = C_NULL;
00390     pmHeap.avail = 0;
00391 #ifdef HAVE_GC
00392     pmHeap.gcval = (uint8_t)0;
00393     pmHeap.temp_root_index = (uint8_t)0;
00394     heap_gcSetAuto(C_TRUE);
00395 #endif /* HAVE_GC */
00396 
00397     pchunk = (pPmHeapDesc_t)pmHeap.base;
00398     hs = pmHeap.size;
00399 
00400     /* #180 Proactively link memory previously lost/neglected at tail of heap */
00401     if ((hs % HEAP_MAX_FREE_CHUNK_SIZE) < HEAP_MIN_CHUNK_SIZE)
00402     {
00403         OBJ_SET_FREE(pchunk, 1);
00404         CHUNK_SET_SIZE(pchunk, HEAP_MIN_CHUNK_SIZE);
00405         heap_linkToFreelist(pchunk);
00406         hs -= HEAP_MIN_CHUNK_SIZE;
00407         pchunk = (pPmHeapDesc_t)((uint8_t *)pchunk + HEAP_MIN_CHUNK_SIZE);
00408     }
00409 
00410     /* Create as many max-sized chunks as possible in the freelist */
00411     for (;
00412          hs >= HEAP_MAX_FREE_CHUNK_SIZE; hs -= HEAP_MAX_FREE_CHUNK_SIZE)
00413     {
00414         OBJ_SET_FREE(pchunk, 1);
00415         CHUNK_SET_SIZE(pchunk, HEAP_MAX_FREE_CHUNK_SIZE);
00416         heap_linkToFreelist(pchunk);
00417         pchunk = (pPmHeapDesc_t)((uint8_t *)pchunk + HEAP_MAX_FREE_CHUNK_SIZE);
00418     }
00419 
00420     /* Add any leftover memory to the freelist */
00421     if (hs >= HEAP_MIN_CHUNK_SIZE)
00422     {
00423         /* Round down to a multiple of four */
00424         hs = hs & ~3;
00425         OBJ_SET_FREE(pchunk, 1);
00426         CHUNK_SET_SIZE(pchunk, hs);
00427         heap_linkToFreelist(pchunk);
00428     }
00429 
00430     C_DEBUG_PRINT(VERBOSITY_LOW, "heap_init(), id=%p, s=%u\n",
00431                   pmHeap.base, (unsigned int)pmHeap.avail);
00432 
00433 #if USE_STRING_CACHE
00434     string_cacheInit();
00435 #endif
00436 
00437     return PM_RET_OK;
00438 }
00439 
00440 
00441 /**
00442  * Obtains a chunk of memory from the free list
00443  *
00444  * Performs the Best Fit algorithm.
00445  * Iterates through the freelist to see if a chunk of suitable size exists.
00446  * Shaves a chunk to perfect size iff the remainder is greater than
00447  * the minimum chunk size.
00448  *
00449  * @param size Requested chunk size
00450  * @param r_pchunk Return ptr to chunk
00451  * @return Return status
00452  */
00453 static PmReturn_t
00454 heap_getChunkImpl(uint16_t size, uint8_t **r_pchunk)
00455 {
00456     PmReturn_t retval;
00457     pPmHeapDesc_t pchunk;
00458     pPmHeapDesc_t premainderChunk;
00459 
00460     C_ASSERT(r_pchunk != C_NULL);
00461 
00462     /* Skip to the first chunk that can hold the requested size */
00463     pchunk = pmHeap.pfreelist;
00464     while ((pchunk != C_NULL) && (CHUNK_GET_SIZE(pchunk) < size))
00465     {
00466         pchunk = pchunk->next;
00467     }
00468 
00469     /* No chunk of appropriate size was found, raise OutOfMemory exception */
00470     if (pchunk == C_NULL)
00471     {
00472         *r_pchunk = C_NULL;
00473         PM_RAISE(retval, PM_RET_EX_MEM);
00474         return retval;
00475     }
00476 
00477     /* Remove the chunk from the free list */
00478     retval = heap_unlinkFromFreelist(pchunk);
00479     PM_RETURN_IF_ERROR(retval);
00480 
00481     /* Check if a chunk should be carved from what is available */
00482     if (CHUNK_GET_SIZE(pchunk) - size >= HEAP_MIN_CHUNK_SIZE)
00483     {
00484         /* Create the heap descriptor for the remainder chunk */
00485         premainderChunk = (pPmHeapDesc_t)((uint8_t *)pchunk + size);
00486         OBJ_SET_FREE(premainderChunk, 1);
00487         CHUNK_SET_SIZE(premainderChunk, CHUNK_GET_SIZE(pchunk) - size);
00488 
00489         /* Put the remainder chunk back in the free list */
00490         retval = heap_linkToFreelist(premainderChunk);
00491         PM_RETURN_IF_ERROR(retval);
00492 
00493         /* Convert the chunk from a heap descriptor to an object descriptor */
00494         OBJ_SET_SIZE(pchunk, 0);
00495         OBJ_SET_FREE(pchunk, 0);
00496         OBJ_SET_SIZE(pchunk, size);
00497 
00498         C_DEBUG_PRINT(VERBOSITY_HIGH,
00499                       "heap_getChunkImpl()carved, id=%p, s=%d\n", pchunk,
00500                       size);
00501     }
00502     else
00503     {
00504         /* Set chunk's type to none (overwrites size field's high byte) */
00505         OBJ_SET_TYPE((pPmObj_t)pchunk, OBJ_TYPE_NON);
00506         OBJ_SET_FREE(pchunk, 0);
00507 
00508         C_DEBUG_PRINT(VERBOSITY_HIGH,
00509                       "heap_getChunkImpl()exact, id=%p, s=%d\n", pchunk,
00510                       PM_OBJ_GET_SIZE(pchunk));
00511     }
00512 
00513     /*
00514      * Set the chunk's GC mark so it will be collected during the next GC cycle
00515      * if it is not reachable
00516      */
00517     OBJ_SET_GCVAL(pchunk, pmHeap.gcval);
00518 
00519     /* Return the chunk */
00520     *r_pchunk = (uint8_t *)pchunk;
00521 
00522     return retval;
00523 }
00524 
00525 
00526 /*
00527  * Allocates chunk of memory.
00528  * Filters out invalid sizes.
00529  * Rounds the size up to the next multiple of the platform pointer size.
00530  * Obtains a chunk of at least the desired size.
00531  */
00532 PmReturn_t
00533 heap_getChunk(uint16_t requestedsize, uint8_t **r_pchunk)
00534 {
00535     PmReturn_t retval;
00536     uint16_t adjustedsize;
00537 
00538     /* Ensure size request is valid */
00539     if (requestedsize > HEAP_MAX_LIVE_CHUNK_SIZE)
00540     {
00541         PM_RAISE(retval, PM_RET_EX_MEM);
00542         return retval;
00543     }
00544 
00545     else if (requestedsize < HEAP_MIN_CHUNK_SIZE)
00546     {
00547         requestedsize = HEAP_MIN_CHUNK_SIZE;
00548     }
00549 
00550     /*
00551      * Round up the size to a multiple of N bytes,
00552      * where N is 8 for 64-bit platforms and 4 for all else.
00553      * This maintains pointer alignment in the heap (required).
00554      */
00555 #ifdef PM_PLAT_POINTER_SIZE
00556 #if PM_PLAT_POINTER_SIZE == 8
00557     adjustedsize = ((requestedsize + 7) & ~7);
00558 #else
00559     adjustedsize = ((requestedsize + 3) & ~3);
00560 #endif /* PM_PLAT_POINTER_SIZE */
00561 #else
00562     adjustedsize = ((requestedsize + 3) & ~3);
00563 #endif /* PM_PLAT_POINTER_SIZE */
00564 
00565     /* Attempt to get a chunk */
00566     retval = heap_getChunkImpl(adjustedsize, r_pchunk);
00567 
00568 #ifdef HAVE_GC
00569     /* Perform GC if out of memory, gc is enabled and not in native session */
00570     if ((retval == PM_RET_EX_MEM) && (pmHeap.auto_gc == C_TRUE)
00571         && (gVmGlobal.nativeframe.nf_active == C_FALSE))
00572     {
00573         retval = heap_gcRun();
00574         PM_RETURN_IF_ERROR(retval);
00575 
00576         /* Attempt to get a chunk */
00577         retval = heap_getChunkImpl(adjustedsize, r_pchunk);
00578     }
00579 #endif /* HAVE_GC */
00580 
00581     /* Ensure that the pointer is N-byte aligned */
00582     if (retval == PM_RET_OK)
00583     {
00584 #ifdef PM_PLAT_POINTER_SIZE
00585 #if PM_PLAT_POINTER_SIZE == 8
00586         C_ASSERT(((intptr_t)*r_pchunk & 7) == 0);
00587 #else
00588         C_ASSERT(((intptr_t)*r_pchunk & 3) == 0);
00589 #endif /* PM_PLAT_POINTER_SIZE */
00590 #else
00591         C_ASSERT(((intptr_t)*r_pchunk & 3) == 0);
00592 #endif /* PM_PLAT_POINTER_SIZE */
00593     }
00594 
00595     return retval;
00596 }
00597 
00598 
00599 /* Releases chunk to the free list */
00600 PmReturn_t
00601 heap_freeChunk(pPmObj_t ptr)
00602 {
00603     PmReturn_t retval;
00604 
00605     C_DEBUG_PRINT(VERBOSITY_HIGH, "heap_freeChunk(), id=%p, s=%d\n",
00606                   ptr, PM_OBJ_GET_SIZE(ptr));
00607 
00608     /* Ensure the chunk falls within the heap */
00609     C_ASSERT(((uint8_t *)ptr >= &pmHeap.base[0])
00610               && ((uint8_t *)ptr <= &pmHeap.base[pmHeap.size]));
00611 
00612     /* Insert the chunk into the freelist */
00613     OBJ_SET_FREE(ptr, 1);
00614 
00615     /* Clear type so that heap descriptor's size's upper byte is zero */
00616     OBJ_SET_TYPE(ptr, 0);
00617     retval = heap_linkToFreelist((pPmHeapDesc_t)ptr);
00618     PM_RETURN_IF_ERROR(retval);
00619 
00620     return retval;
00621 }
00622 
00623 
00624 uint32_t
00625 heap_getAvail (void)
00626 {
00627     return pmHeap.avail;
00628 }
00629 
00630 
00631 uint32_t
00632 heap_getSize (void)
00633 {
00634     return pmHeap.size;
00635 }
00636 
00637 
00638 #ifdef HAVE_GC
00639 /*
00640  * Marks the given object and the objects it references.
00641  *
00642  * @param   pobj Any non-free heap object
00643  * @return  Return code
00644  */
00645 static PmReturn_t
00646 heap_gcMarkObj(pPmObj_t pobj)
00647 {
00648     PmReturn_t retval = PM_RET_OK;
00649     int16_t i = 0;
00650     int16_t n;
00651     PmType_t type;
00652 
00653     /* Return if ptr is null or object is already marked */
00654     if (pobj == C_NULL)
00655     {
00656         return retval;
00657     }
00658     if (OBJ_GET_GCVAL(pobj) == pmHeap.gcval)
00659     {
00660         return retval;
00661     }
00662 
00663     /* The pointer must be within the heap (native frame is special case) */
00664     C_ASSERT((((uint8_t *)pobj >= &pmHeap.base[0])
00665               && ((uint8_t *)pobj <= &pmHeap.base[pmHeap.size]))
00666              || ((uint8_t *)pobj == (uint8_t *)&gVmGlobal.nativeframe));
00667 
00668     /* The object must not already be free */
00669     C_ASSERT(OBJ_GET_FREE(pobj) == 0);
00670 
00671     type = (PmType_t)OBJ_GET_TYPE(pobj);
00672     switch (type)
00673     {
00674         /* Objects with no references to other objects */
00675         case OBJ_TYPE_NON:
00676         case OBJ_TYPE_INT:
00677         case OBJ_TYPE_FLT:
00678         case OBJ_TYPE_STR:
00679         case OBJ_TYPE_NOB:
00680         case OBJ_TYPE_BOOL:
00681         case OBJ_TYPE_CIO:
00682             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00683             break;
00684 
00685         case OBJ_TYPE_TUP:
00686             i = ((pPmTuple_t)pobj)->length;
00687 
00688             /* Mark tuple head */
00689             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00690 
00691             /* Mark each obj in tuple */
00692             while (--i >= 0)
00693             {
00694                 retval = heap_gcMarkObj(((pPmTuple_t)pobj)->val[i]);
00695                 PM_RETURN_IF_ERROR(retval);
00696             }
00697             break;
00698 
00699         case OBJ_TYPE_LST:
00700 
00701             /* Mark the list */
00702             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00703 
00704             /* Mark the seglist */
00705             retval = heap_gcMarkObj((pPmObj_t)((pPmList_t)pobj)->val);
00706             break;
00707 
00708         case OBJ_TYPE_DIC:
00709             /* Mark the dict head */
00710             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00711 
00712             /* Mark the keys seglist */
00713             retval = heap_gcMarkObj((pPmObj_t)((pPmDict_t)pobj)->d_keys);
00714             PM_RETURN_IF_ERROR(retval);
00715 
00716             /* Mark the vals seglist */
00717             retval = heap_gcMarkObj((pPmObj_t)((pPmDict_t)pobj)->d_vals);
00718             break;
00719 
00720         case OBJ_TYPE_COB:
00721             /* Mark the code obj head */
00722             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00723 
00724             /* Mark the names tuple */
00725             retval = heap_gcMarkObj((pPmObj_t)((pPmCo_t)pobj)->co_names);
00726             PM_RETURN_IF_ERROR(retval);
00727 
00728             /* Mark the consts tuple */
00729             retval = heap_gcMarkObj((pPmObj_t)((pPmCo_t)pobj)->co_consts);
00730             PM_RETURN_IF_ERROR(retval);
00731 
00732             /* #122: Mark the code image if it is in RAM */
00733             if (((pPmCo_t)pobj)->co_memspace == MEMSPACE_RAM)
00734             {
00735                 retval = heap_gcMarkObj((pPmObj_t)
00736                                         (((pPmCo_t)pobj)->co_codeimgaddr));
00737                 PM_RETURN_IF_ERROR(retval);
00738             }
00739 
00740 #ifdef HAVE_CLOSURES
00741             /* #256: Add support for closures */
00742             /* Mark the cellvars tuple */
00743             retval = heap_gcMarkObj((pPmObj_t)((pPmCo_t)pobj)->co_cellvars);
00744 #endif /* HAVE_CLOSURES */
00745             break;
00746 
00747         case OBJ_TYPE_MOD:
00748         case OBJ_TYPE_FXN:
00749             /* Module and Func objs are implemented via the PmFunc_t */
00750             /* Mark the func obj head */
00751             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00752 
00753             /* Mark the code obj */
00754             retval = heap_gcMarkObj((pPmObj_t)((pPmFunc_t)pobj)->f_co);
00755             PM_RETURN_IF_ERROR(retval);
00756 
00757             /* Mark the attr dict */
00758             retval = heap_gcMarkObj((pPmObj_t)((pPmFunc_t)pobj)->f_attrs);
00759             PM_RETURN_IF_ERROR(retval);
00760 
00761             /* Mark the globals dict */
00762             retval = heap_gcMarkObj((pPmObj_t)((pPmFunc_t)pobj)->f_globals);
00763             PM_RETURN_IF_ERROR(retval);
00764 
00765 #ifdef HAVE_DEFAULTARGS
00766             /* Mark the default args tuple */
00767             retval = heap_gcMarkObj((pPmObj_t)((pPmFunc_t)pobj)->f_defaultargs);
00768             PM_RETURN_IF_ERROR(retval);
00769 #endif /* HAVE_DEFAULTARGS */
00770 
00771 #ifdef HAVE_CLOSURES
00772             /* #256: Mark the closure tuple */
00773             retval = heap_gcMarkObj((pPmObj_t)((pPmFunc_t)pobj)->f_closure);
00774 #endif /* HAVE_CLOSURES */
00775             break;
00776 
00777 #ifdef HAVE_CLASSES
00778         case OBJ_TYPE_CLI:
00779             /* Mark the obj head */
00780             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00781 
00782             /* Mark the class */
00783             retval = heap_gcMarkObj((pPmObj_t)((pPmInstance_t)pobj)->cli_class);
00784             PM_RETURN_IF_ERROR(retval);
00785 
00786             /* Mark the attrs dict */
00787             retval = heap_gcMarkObj((pPmObj_t)((pPmInstance_t)pobj)->cli_attrs);
00788             break;
00789 
00790         case OBJ_TYPE_MTH:
00791             /* Mark the obj head */
00792             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00793 
00794             /* Mark the instance */
00795             retval = heap_gcMarkObj((pPmObj_t)((pPmMethod_t)pobj)->m_instance);
00796             PM_RETURN_IF_ERROR(retval);
00797 
00798             /* Mark the func */
00799             retval = heap_gcMarkObj((pPmObj_t)((pPmMethod_t)pobj)->m_func);
00800             PM_RETURN_IF_ERROR(retval);
00801 
00802             /* Mark the attrs dict */
00803             retval = heap_gcMarkObj((pPmObj_t)((pPmMethod_t)pobj)->m_attrs);
00804             break;
00805 
00806         case OBJ_TYPE_CLO:
00807             /* Mark the obj head */
00808             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00809 
00810             /* Mark the attrs dict */
00811             retval = heap_gcMarkObj((pPmObj_t)((pPmClass_t)pobj)->cl_attrs);
00812             PM_RETURN_IF_ERROR(retval);
00813 
00814             /* Mark the base tuple */
00815             retval = heap_gcMarkObj((pPmObj_t)((pPmClass_t)pobj)->cl_bases);
00816             break;
00817 #endif /* HAVE_CLASSES */
00818 
00819         /*
00820          * An obj in ram should not be of these types.
00821          * Images arrive in RAM as string objects (image is array of bytes)
00822          */
00823         case OBJ_TYPE_CIM:
00824         case OBJ_TYPE_NIM:
00825             PM_RAISE(retval, PM_RET_EX_SYS);
00826             return retval;
00827 
00828         case OBJ_TYPE_FRM:
00829         {
00830             pPmObj_t *ppobj2 = C_NULL;
00831 
00832             /* Mark the frame obj head */
00833             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00834 
00835             /* Mark the previous frame, if this isn't a generator's frame */
00836             /* Issue #129: Fix iterator losing its object */
00837             if ((((pPmFrame_t)pobj)->fo_func->f_co->co_flags & CO_GENERATOR) == 0)
00838             {
00839                 retval = heap_gcMarkObj((pPmObj_t)((pPmFrame_t)pobj)->fo_back);
00840                 PM_RETURN_IF_ERROR(retval);
00841             }
00842 
00843             /* Mark the fxn obj */
00844             retval = heap_gcMarkObj((pPmObj_t)((pPmFrame_t)pobj)->fo_func);
00845             PM_RETURN_IF_ERROR(retval);
00846 
00847             /* Mark the blockstack */
00848             retval = heap_gcMarkObj((pPmObj_t)
00849                                     ((pPmFrame_t)pobj)->fo_blockstack);
00850             PM_RETURN_IF_ERROR(retval);
00851 
00852             /* Mark the attrs dict */
00853             retval = heap_gcMarkObj((pPmObj_t)((pPmFrame_t)pobj)->fo_attrs);
00854             PM_RETURN_IF_ERROR(retval);
00855 
00856             /* Mark the globals dict */
00857             retval = heap_gcMarkObj((pPmObj_t)((pPmFrame_t)pobj)->fo_globals);
00858             PM_RETURN_IF_ERROR(retval);
00859 
00860             /* Mark each obj in the locals list and the stack */
00861             ppobj2 = ((pPmFrame_t)pobj)->fo_locals;
00862             while (ppobj2 < ((pPmFrame_t)pobj)->fo_sp)
00863             {
00864                 retval = heap_gcMarkObj(*ppobj2);
00865                 PM_RETURN_IF_ERROR(retval);
00866                 ppobj2++;
00867             }
00868             break;
00869         }
00870 
00871         case OBJ_TYPE_BLK:
00872             /* Mark the block obj head */
00873             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00874 
00875             /* Mark the next block in the stack */
00876             retval = heap_gcMarkObj((pPmObj_t)((pPmBlock_t)pobj)->next);
00877             break;
00878 
00879         case OBJ_TYPE_SGL:
00880             /* Mark the seglist obj head */
00881             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00882 
00883             /* Mark the seglist's segments */
00884             n = ((pSeglist_t)pobj)->sl_length;
00885             pobj = (pPmObj_t)((pSeglist_t)pobj)->sl_rootseg;
00886             for (i = 0; i < n; i++)
00887             {
00888                 /* Mark the segment item */
00889                 retval = heap_gcMarkObj(((pSegment_t)pobj)->s_val[i % SEGLIST_OBJS_PER_SEG]);
00890                 PM_RETURN_IF_ERROR(retval);
00891 
00892                 /* Mark the segment obj head */
00893                 if ((i % SEGLIST_OBJS_PER_SEG) == 0)
00894                 {
00895                     OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00896                 }
00897 
00898                 /* Point to the next segment */
00899                 else
00900                 if ((i % SEGLIST_OBJS_PER_SEG) == (SEGLIST_OBJS_PER_SEG - 1))
00901                 {
00902                     pobj = (pPmObj_t)((pSegment_t)pobj)->next;
00903                     if (pobj == C_NULL)
00904                     {
00905                         break;
00906                     }
00907                 }
00908             }
00909             break;
00910 
00911         case OBJ_TYPE_SQI:
00912             /* Mark the sequence iterator obj head */
00913             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00914 
00915             /* Mark the sequence */
00916             retval = heap_gcMarkObj(((pPmSeqIter_t)pobj)->si_sequence);
00917             break;
00918 
00919         case OBJ_TYPE_THR:
00920             /* Mark the thread obj head */
00921             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00922 
00923             /* Mark the current frame */
00924             retval = heap_gcMarkObj((pPmObj_t)((pPmThread_t)pobj)->pframe);
00925             break;
00926 
00927         case OBJ_TYPE_NFM:
00928             /*
00929              * Mark the obj desc.  This doesn't really do much since the
00930              * native frame is declared static (not from the heap), but this
00931              * is here in case that ever changes
00932              */
00933             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00934 
00935             /* Mark the native frame's remaining fields if active */
00936             if (gVmGlobal.nativeframe.nf_active)
00937             {
00938                 /* Mark the frame stack */
00939                 retval = heap_gcMarkObj((pPmObj_t)
00940                                         gVmGlobal.nativeframe.nf_back);
00941                 PM_RETURN_IF_ERROR(retval);
00942 
00943                 /* Mark the function object */
00944                 retval = heap_gcMarkObj((pPmObj_t)
00945                                         gVmGlobal.nativeframe.nf_func);
00946                 PM_RETURN_IF_ERROR(retval);
00947 
00948                 /* Mark the stack object */
00949                 retval = heap_gcMarkObj(gVmGlobal.nativeframe.nf_stack);
00950                 PM_RETURN_IF_ERROR(retval);
00951 
00952                 /* Mark the args to the native func */
00953                 for (i = 0; i < NATIVE_GET_NUM_ARGS(); i++)
00954                 {
00955                     retval =
00956                         heap_gcMarkObj(gVmGlobal.nativeframe.nf_locals[i]);
00957                     PM_RETURN_IF_ERROR(retval);
00958                 }
00959             }
00960             break;
00961 
00962 #ifdef HAVE_BYTEARRAY
00963         case OBJ_TYPE_BYA:
00964             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00965 
00966             retval = heap_gcMarkObj((pPmObj_t)((pPmBytearray_t)pobj)->val);
00967             break;
00968 
00969         case OBJ_TYPE_BYS:
00970             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00971             break;
00972 #endif /* HAVE_BYTEARRAY */
00973 
00974         default:
00975             /* There should be no invalid types */
00976             PM_RAISE(retval, PM_RET_EX_SYS);
00977             break;
00978     }
00979     return retval;
00980 }
00981 
00982 
00983 /*
00984  * Marks the root objects so they won't be collected during the sweep phase.
00985  * Recursively marks all objects reachable from the roots.
00986  */
00987 static PmReturn_t
00988 heap_gcMarkRoots(void)
00989 {
00990     PmReturn_t retval;
00991     uint8_t i;
00992 
00993     /* Toggle the GC marking value so it differs from the last run */
00994     pmHeap.gcval ^= 1;
00995 
00996     /* Mark the constant objects */
00997     retval = heap_gcMarkObj(PM_NONE);
00998     PM_RETURN_IF_ERROR(retval);
00999     retval = heap_gcMarkObj(PM_FALSE);
01000     PM_RETURN_IF_ERROR(retval);
01001     retval = heap_gcMarkObj(PM_TRUE);
01002     PM_RETURN_IF_ERROR(retval);
01003     retval = heap_gcMarkObj(PM_ZERO);
01004     PM_RETURN_IF_ERROR(retval);
01005     retval = heap_gcMarkObj(PM_ONE);
01006     PM_RETURN_IF_ERROR(retval);
01007     retval = heap_gcMarkObj(PM_NEGONE);
01008     PM_RETURN_IF_ERROR(retval);
01009     retval = heap_gcMarkObj(PM_CODE_STR);
01010     PM_RETURN_IF_ERROR(retval);
01011 
01012     /* Mark the builtins dict */
01013     retval = heap_gcMarkObj(PM_PBUILTINS);
01014     PM_RETURN_IF_ERROR(retval);
01015 
01016     /* Mark the native frame if it is active */
01017     retval = heap_gcMarkObj((pPmObj_t)&gVmGlobal.nativeframe);
01018     PM_RETURN_IF_ERROR(retval);
01019 
01020     /* Mark the thread list */
01021     retval = heap_gcMarkObj((pPmObj_t)gVmGlobal.threadList);
01022     PM_RETURN_IF_ERROR(retval);
01023 
01024     /* Mark the temporary roots */
01025     for (i = 0; i < pmHeap.temp_root_index; i++)
01026     {
01027         retval = heap_gcMarkObj(pmHeap.temp_roots[i]);
01028         PM_RETURN_IF_ERROR(retval);
01029     }
01030 
01031     return retval;
01032 }
01033 
01034 
01035 #if USE_STRING_CACHE
01036 /**
01037  * Unlinks free objects from the string cache.
01038  * This function must only be called by the GC after the heap has been marked
01039  * and before the heap has been swept.
01040  *
01041  * This solves the problem where a string object would be collected
01042  * but its chunk was still linked into the free list
01043  *
01044  * @param gcval The current value for chunks marked by the GC
01045  */
01046 static PmReturn_t
01047 heap_purgeStringCache(uint8_t gcval)
01048 {
01049     PmReturn_t retval;
01050     pPmString_t *ppstrcache;
01051     pPmString_t pstr;
01052 
01053     /* Update string cache pointer if the first string objs are not marked */
01054     retval = string_getCache(&ppstrcache);
01055     if (ppstrcache == C_NULL)
01056     {
01057         return retval;
01058     }
01059     while ((*ppstrcache != C_NULL) && (OBJ_GET_GCVAL(*ppstrcache) != gcval))
01060     {
01061         *ppstrcache = (*ppstrcache)->next;
01062     }
01063     if (*ppstrcache == C_NULL)
01064     {
01065         return retval;
01066     }
01067 
01068     /* Unlink remaining strings that are not marked */
01069     for (pstr = *ppstrcache; pstr->next != C_NULL;)
01070     {
01071         /* Unlink consecutive non-marked strings */
01072         while ((pstr->next != C_NULL) && (OBJ_GET_GCVAL(pstr->next) != gcval))
01073         {
01074             pstr->next = pstr->next->next;
01075         }
01076 
01077         /* If not at end of cache, string must be marked, skip it */
01078         if (pstr->next != C_NULL)
01079         {
01080             pstr = pstr->next;
01081         }
01082     }
01083 
01084     return retval;
01085 }
01086 #endif
01087 
01088 
01089 /*
01090  * Reclaims any object that does not have a current mark.
01091  * Puts it in the free list.  Coalesces all contiguous free chunks.
01092  */
01093 static PmReturn_t
01094 heap_gcSweep(void)
01095 {
01096     PmReturn_t retval;
01097     pPmObj_t pobj;
01098     pPmHeapDesc_t pchunk;
01099     uint16_t totalchunksize;
01100 
01101 #if USE_STRING_CACHE
01102     retval = heap_purgeStringCache(pmHeap.gcval);
01103 #endif
01104 
01105     /* Start at the base of the heap */
01106     pobj = (pPmObj_t)pmHeap.base;
01107     while ((uint8_t *)pobj < &pmHeap.base[pmHeap.size])
01108     {
01109         /* Skip to the next unmarked or free chunk within the heap */
01110         while (!OBJ_GET_FREE(pobj)
01111                && (OBJ_GET_GCVAL(pobj) == pmHeap.gcval)
01112                && ((uint8_t *)pobj < &pmHeap.base[pmHeap.size]))
01113         {
01114             pobj = (pPmObj_t)((uint8_t *)pobj + PM_OBJ_GET_SIZE(pobj));
01115         }
01116 
01117         /* Stop if reached the end of the heap */
01118         if ((uint8_t *)pobj >= &pmHeap.base[pmHeap.size])
01119         {
01120             break;
01121         }
01122 
01123         /* Accumulate the sizes of all consecutive unmarked or free chunks */
01124         totalchunksize = 0;
01125 
01126         /* Coalesce all contiguous free chunks */
01127         pchunk = (pPmHeapDesc_t)pobj;
01128         while (OBJ_GET_FREE(pchunk)
01129                || (!OBJ_GET_FREE(pchunk)
01130                    && (OBJ_GET_GCVAL(pchunk) != pmHeap.gcval)))
01131         {
01132             /*
01133              * If the chunk is already free, unlink it because its size
01134              * is about to change
01135              */
01136             if (OBJ_GET_FREE(pchunk))
01137             {
01138                 if ((totalchunksize + CHUNK_GET_SIZE(pchunk))
01139                     > HEAP_MAX_FREE_CHUNK_SIZE)
01140                 {
01141                     break;
01142                 }
01143                 retval = heap_unlinkFromFreelist(pchunk);
01144                 PM_RETURN_IF_ERROR(retval);
01145             }
01146 
01147             /* Otherwise free and reclaim the unmarked chunk */
01148             else
01149             {
01150                 if ((totalchunksize + PM_OBJ_GET_SIZE(pchunk))
01151                     > HEAP_MAX_FREE_CHUNK_SIZE)
01152                 {
01153                     break;
01154                 }
01155                 OBJ_SET_TYPE(pchunk, 0);
01156                 OBJ_SET_FREE(pchunk, 1);
01157             }
01158             totalchunksize = totalchunksize + CHUNK_GET_SIZE(pchunk);
01159 
01160             C_DEBUG_PRINT(VERBOSITY_HIGH, "heap_gcSweep(), id=%p, s=%d\n",
01161                           pchunk, CHUNK_GET_SIZE(pchunk));
01162 
01163             /* Proceed to the next chunk */
01164             pchunk = (pPmHeapDesc_t)
01165                 ((uint8_t *)pchunk + CHUNK_GET_SIZE(pchunk));
01166 
01167             /* Stop if it's past the end of the heap */
01168             if ((uint8_t *)pchunk >= &pmHeap.base[pmHeap.size])
01169             {
01170                 break;
01171             }
01172         }
01173 
01174         /* Set the heap descriptor data */
01175         OBJ_SET_FREE(pobj, 1);
01176         CHUNK_SET_SIZE(pobj, totalchunksize);
01177 
01178         /* Insert chunk into free list */
01179         retval = heap_linkToFreelist((pPmHeapDesc_t)pobj);
01180         PM_RETURN_IF_ERROR(retval);
01181 
01182         /* Continue to the next chunk */
01183         pobj = (pPmObj_t)pchunk;
01184     }
01185 
01186     return PM_RET_OK;
01187 }
01188 
01189 
01190 /* Runs the mark-sweep garbage collector */
01191 PmReturn_t
01192 heap_gcRun(void)
01193 {
01194     PmReturn_t retval;
01195 
01196     /* #239: Fix GC when 2+ unlinked allocs occur */
01197     /* This assertion fails when there are too many objects on the temporary
01198      * root stack and a GC occurs; consider increasing PM_HEAP_NUM_TEMP_ROOTS
01199      */
01200     C_ASSERT(pmHeap.temp_root_index < HEAP_NUM_TEMP_ROOTS);
01201 
01202     C_DEBUG_PRINT(VERBOSITY_LOW, "heap_gcRun()\n");
01203 
01204     retval = heap_gcMarkRoots();
01205     PM_RETURN_IF_ERROR(retval);
01206 
01207     /*heap_dump();*/
01208     retval = heap_gcSweep();
01209     /*heap_dump();*/
01210     return retval;
01211 }
01212 
01213 
01214 /* Enables or disables automatic garbage collection */
01215 PmReturn_t
01216 heap_gcSetAuto(uint8_t auto_gc)
01217 {
01218     pmHeap.auto_gc = auto_gc;
01219     return PM_RET_OK;
01220 }
01221 
01222 void heap_gcPushTempRoot(pPmObj_t pobj, uint8_t *r_objid)
01223 {
01224     if (pmHeap.temp_root_index < HEAP_NUM_TEMP_ROOTS)
01225     {
01226         *r_objid = pmHeap.temp_root_index;
01227         pmHeap.temp_roots[pmHeap.temp_root_index] = pobj;
01228         pmHeap.temp_root_index++;
01229     }
01230     return;
01231 }
01232 
01233 
01234 void heap_gcPopTempRoot(uint8_t objid)
01235 {
01236     pmHeap.temp_root_index = objid;
01237 }
01238 
01239 #else
01240 
01241 void heap_gcPushTempRoot(pPmObj_t pobj, uint8_t *r_objid) {}
01242 void heap_gcPopTempRoot(uint8_t objid) {}
01243 
01244 #endif /* HAVE_GC */