Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
heap.c
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 */
Generated on Tue Jul 12 2022 23:13:47 by
1.7.2