PageRenderTime 200ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/vm/src/heap.c

https://github.com/wycc/NanoVMArduino
C | 518 lines | 317 code | 96 blank | 105 comment | 54 complexity | 5037925100ba0040b5d3f90a03e532ef MD5 | raw file
  1. //
  2. // NanoVM, a tiny java VM for the Atmel AVR family
  3. // Copyright (C) 2005 by Till Harbaum <Till@Harbaum.org>
  4. //
  5. // This program is free software; you can redistribute it and/or modify
  6. // it under the terms of the GNU General Public License as published by
  7. // the Free Software Foundation; either version 2 of the License, or
  8. // (at your option) any later version.
  9. //
  10. // This program is distributed in the hope that it will be useful,
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. // GNU General Public License for more details.
  14. //
  15. // You should have received a copy of the GNU General Public License
  16. // along with this program; if not, write to the Free Software
  17. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18. //
  19. //
  20. // This file contains the heap. It can be requested to
  21. // create/delete objects on the heap and does some
  22. // simple garbage collection.
  23. //
  24. // The heap is being used top-to-bottom allowing the
  25. // virtual machines stack to grow inside the heap from bottom to
  26. // top
  27. //
  28. #include <string.h>
  29. #include "types.h"
  30. #include "config.h"
  31. #include "debug.h"
  32. #include "error.h"
  33. #include "utils.h"
  34. #include "heap.h"
  35. #include "stack.h"
  36. #include "vm.h"
  37. u08_t heap[HEAPSIZE];
  38. u16_t heap_base = 0;
  39. typedef struct {
  40. heap_id_t id;
  41. u16_t len; // actually 15 bits for the len and 1 bit for the fieldref flag
  42. } PACKED heap_t;
  43. #define HEAP_ID_FREE 0
  44. #define HEAP_LEN_MASK 0x7FFF
  45. #define HEAP_FIELDREF_MASK 0x8000
  46. #ifdef NVM_USE_HEAP_IDMAP
  47. // A heap id map must not be larger than 256 elements meaning a limit of 2048
  48. // heap elements. Each heap element consists of the header (heap_t) and the
  49. // actual data which makes up at least 1 byte. Limiting the heap size to 10kB
  50. // makes sure that are always heap ids available.
  51. #if HEAPSIZE > 10240
  52. #error The maximum heap size is 10kB when using a heap id map.
  53. #endif
  54. u08_t heap_idmap[(HEAPSIZE/(sizeof(heap_t)+1))/8];
  55. const u08_t heap_idmap_mask[8] = {0x01, 0x02, 0x04, 0x08,
  56. 0x10, 0x20, 0x40, 0x80};
  57. #endif
  58. // return the real heap base (where memory can be "stolen"
  59. // from
  60. u08_t *heap_get_base(void) {
  61. return heap;
  62. }
  63. #ifdef NVM_USE_MEMCPY_UP
  64. // a version of memcpy that can only copy overlapping chunks
  65. // if the target address is higher
  66. void heap_memcpy_up(u08_t *dst, u08_t *src, u16_t len) {
  67. dst += len; src += len;
  68. while(len--) *--dst = *--src;
  69. }
  70. #endif
  71. #ifdef DEBUG_JVM
  72. // make some sanity checks on the heap in order to detect
  73. // heap corruption as early as possible
  74. void heap_check(void) {
  75. u16_t current = heap_base;
  76. heap_t *h = (heap_t*)&heap[current];
  77. u16_t len;
  78. if(h->id != HEAP_ID_FREE) {
  79. DEBUGF("heap_check(): start element not free element\n");
  80. error(ERROR_HEAP_CORRUPTED);
  81. }
  82. // (no HEAP_LEN_MASK required for free chunk)
  83. current += h->len + sizeof(heap_t);
  84. while(current < sizeof(heap)) {
  85. h = (heap_t*)&heap[current];
  86. len = h->len & HEAP_LEN_MASK;
  87. if(len > sizeof(heap)) {
  88. DEBUGF("heap_check(): single chunk too big\n");
  89. heap_show();
  90. error(ERROR_HEAP_ILLEGAL_CHUNK_SIZE);
  91. }
  92. if(len + sizeof(heap_t) > sizeof(heap) - current) {
  93. DEBUGF("heap_check(): total size error\n");
  94. heap_show();
  95. error(ERROR_HEAP_CORRUPTED);
  96. }
  97. current += len + sizeof(heap_t);
  98. }
  99. if(current != sizeof(heap)) {
  100. DEBUGF("heap_check(): heap sum mismatch\n");
  101. heap_show();
  102. error(ERROR_HEAP_CORRUPTED);
  103. }
  104. }
  105. #endif
  106. #ifdef UNIX
  107. void heap_show(void) {
  108. u16_t current = heap_base;
  109. DEBUGF("Heap:\n");
  110. while(current < sizeof(heap)) {
  111. heap_t *h = (heap_t*)&heap[current];
  112. u16_t len = h->len & HEAP_LEN_MASK;
  113. if(h->id == HEAP_ID_FREE) {
  114. DEBUGF("- %d free bytes\n", len);
  115. } else {
  116. DEBUGF("- chunk id %x with %d bytes:\n", h->id, len);
  117. if(len > sizeof(heap))
  118. error(ERROR_HEAP_ILLEGAL_CHUNK_SIZE);
  119. DEBUG_HEXDUMP(h+1, len);
  120. }
  121. if(len + sizeof(heap_t) > sizeof(heap) - current) {
  122. DEBUGF("heap_show(): total size error\n");
  123. error(ERROR_HEAP_CORRUPTED);
  124. }
  125. current += len + sizeof(heap_t);
  126. }
  127. DEBUGF("- %d bytes stolen\n", heap_base);
  128. }
  129. #endif
  130. // search for chunk with id in heap and return chunk header
  131. // address
  132. heap_t *heap_search(heap_id_t id) {
  133. u16_t current = heap_base;
  134. while(current < sizeof(heap)) {
  135. heap_t *h = (heap_t*)&heap[current];
  136. if(h->id == id) return h;
  137. current += (h->len & HEAP_LEN_MASK) + sizeof(heap_t);
  138. }
  139. return NULL;
  140. }
  141. #ifdef NVM_USE_HEAP_IDMAP
  142. void heap_init_ids(void) {
  143. memset(heap_idmap, 0, sizeof(heap_idmap));
  144. heap_idmap[0] = 0x01; // mark HEAP_ID_FREE
  145. }
  146. void heap_mark_id(heap_id_t id) {
  147. DEBUGF(" heap_mark_id(id=0x%04x)\n", id);
  148. heap_idmap[id/8] |= heap_idmap_mask[id%8];
  149. }
  150. u08_t heap_id_marked(heap_id_t id) {
  151. return heap_idmap[id/8] & heap_idmap_mask[id%8];
  152. }
  153. heap_id_t heap_new_id(void) {
  154. u08_t byte,bit;
  155. for(byte=0;;byte++) {
  156. if(heap_idmap[byte] != 0xFF)
  157. for(bit=0;;bit++)
  158. if(!(heap_idmap[byte] & heap_idmap_mask[bit])) {
  159. heap_idmap[byte] |= heap_idmap_mask[bit];
  160. return byte*8+bit;
  161. }
  162. // check failure here before incrementing
  163. // to allow for id maps with 256 elements
  164. if(byte == sizeof(heap_idmap)-1)
  165. return 0;
  166. }
  167. }
  168. // in some cases, references to heap objects may be inside
  169. // other heap objects. this currently happens only when
  170. // a class is instanciated and this class contains fields.
  171. // the heap element created by the constructor is marked with
  172. // the fieldref bit and it is searched for references during
  173. // garbage collections
  174. void heap_mark_child_ids(void) {
  175. bool_t again;
  176. do {
  177. u16_t current = heap_base;
  178. again = FALSE;
  179. DEBUGF("heap_mark_child_ids(): starting heap walk\n");
  180. while(current < sizeof(heap)) {
  181. heap_t *h = (heap_t*)&heap[current];
  182. // check for elements with the fieldref flag
  183. if(h->len & HEAP_FIELDREF_MASK && heap_id_marked(h->id)) {
  184. u08_t fields = (u08_t)((h->len & HEAP_LEN_MASK) / sizeof(nvm_ref_t));
  185. u08_t i;
  186. // check all fields in the heap element
  187. DEBUGF("- checking id 0x%04x\n", h->id);
  188. for(i=0;i<fields;i++) {
  189. nvm_ref_t ref = ((nvm_ref_t*)(h+1))[i];
  190. // mark heap element only if field actually references
  191. // a heap element and that wasn't already marked before
  192. if((ref & NVM_TYPE_MASK) == NVM_TYPE_HEAP &&
  193. !heap_id_marked(ref & ~NVM_TYPE_MASK)) {
  194. heap_mark_id(ref & ~NVM_TYPE_MASK);
  195. // we could check here if any of the newly marked heap elements
  196. // has the fieldref flag set but doing would require walking the
  197. // heap for every single one (!) so it's cheaper to just do the
  198. // walk here again as soon as we newly marked any heap elements
  199. again = TRUE;
  200. }
  201. }
  202. }
  203. current += (h->len & HEAP_LEN_MASK) + sizeof(heap_t);
  204. }
  205. } while(again);
  206. }
  207. #else // NVM_USE_HEAP_IDMAP
  208. heap_id_t heap_new_id(void) {
  209. heap_id_t id;
  210. for(id=1;id;id++)
  211. if(heap_search(id) == NULL)
  212. return id;
  213. return 0;
  214. }
  215. // in some cases, references to heap objects may be inside
  216. // other heap objects. this currently happens only when
  217. // a class is instanciated and this class contains fields.
  218. // the heap element created by the constructor is marked with
  219. // the fieldref bit and it is searched for references during
  220. // garbage collections
  221. bool_t heap_fieldref(heap_id_t id) {
  222. nvm_ref_t id16 = id | NVM_TYPE_HEAP;
  223. u16_t current = heap_base;
  224. // walk through the entire heap
  225. while(current < sizeof(heap)) {
  226. heap_t *h = (heap_t*)&heap[current];
  227. // check for entries with the fieldref flag
  228. if(h->len & HEAP_FIELDREF_MASK) {
  229. u08_t entries = (u08_t)((h->len & HEAP_LEN_MASK) / sizeof(nvm_ref_t));
  230. u08_t i;
  231. // check all entries in the heap element for
  232. // the reference we are searching for
  233. for(i=0;i<entries;i++) {
  234. if(((nvm_ref_t*)(h+1))[i] == id16)
  235. return TRUE;
  236. }
  237. }
  238. current += (h->len & HEAP_LEN_MASK) + sizeof(heap_t);
  239. }
  240. return FALSE;
  241. }
  242. #endif // NVM_USE_HEAP_IDMAP
  243. bool_t heap_alloc_internal(heap_id_t id, bool_t fieldref, u16_t size) {
  244. u16_t req = size + sizeof(heap_t); // total mem required
  245. // search for free block
  246. heap_t *h = (heap_t*)&heap[heap_base];
  247. // (no HEAP_LEN_MASK required for free chunk)
  248. if(h->len >= req) {
  249. // reduce the size of the free chunk
  250. // (no HEAP_LEN_MASK required for free chunk)
  251. h->len -= req;
  252. // and create the new chunk behind this one
  253. // (no HEAP_LEN_MASK required for free chunk)
  254. h = (heap_t*)&heap[heap_base + sizeof(heap_t) + h->len];
  255. h->id = id;
  256. h->len = fieldref ? size | HEAP_FIELDREF_MASK : size & HEAP_LEN_MASK;
  257. #ifdef NVM_INITIALIZE_ALLOCATED
  258. // fill memory with zero
  259. u08_t *ptr = (void*)(h+1);
  260. while(size--)
  261. *ptr++ = 0;
  262. #endif
  263. return TRUE;
  264. }
  265. DEBUGF("heap_alloc_internal(%d): out of memory\n", size);
  266. return FALSE;
  267. }
  268. heap_id_t heap_alloc(bool_t fieldref, u16_t size) {
  269. heap_id_t id = heap_new_id();
  270. DEBUGF("heap_alloc(size=%d) -> id=0x%04x\n", size, id);
  271. if(!id) error(ERROR_HEAP_OUT_OF_IDS);
  272. if(!heap_alloc_internal(id, fieldref, size)) {
  273. heap_garbage_collect();
  274. // we need to reallocate heap id, gc. threw away the old one...
  275. id = heap_new_id();
  276. if(!id) error(ERROR_HEAP_OUT_OF_IDS);
  277. if(!heap_alloc_internal(id, fieldref, size))
  278. error(ERROR_HEAP_OUT_OF_MEMORY);
  279. DEBUGF("heap_alloc(size=%d) -> id=0x%04x successfull after gc\n",
  280. size, id);
  281. }
  282. return id;
  283. }
  284. void heap_realloc(heap_id_t id, u16_t size) {
  285. heap_t *h, *h_new;
  286. DEBUGF("heap_realloc(id=0x%04x, size=%d)\n", id, size);
  287. // check free mem and call garbage collection if required
  288. h = (heap_t*)&heap[heap_base];
  289. // (no HEAP_LEN_MASK required for free chunk)
  290. if(h->len < size + sizeof(heap_t))
  291. heap_garbage_collect();
  292. // get info on old chunk
  293. h = heap_search(id);
  294. // allocate space for bigger one
  295. if(!heap_alloc_internal(id, h->len & HEAP_FIELDREF_MASK ? TRUE : FALSE, size))
  296. error(ERROR_HEAP_OUT_OF_MEMORY);
  297. h_new = heap_search(id);
  298. utils_memcpy(h_new+1, h+1, h->len & HEAP_LEN_MASK);
  299. // this chunk is not immediately available for new allocation
  300. // but it will be removed by the garbage collection next time
  301. h->id = HEAP_ID_FREE;
  302. }
  303. u16_t heap_get_len(heap_id_t id) {
  304. heap_t *h = heap_search(id);
  305. if(!h) error(ERROR_HEAP_CHUNK_DOES_NOT_EXIST);
  306. return h->len & HEAP_LEN_MASK;
  307. }
  308. void *heap_get_addr(heap_id_t id) {
  309. heap_t *h = heap_search(id);
  310. if(!h) error(ERROR_HEAP_CHUNK_DOES_NOT_EXIST);
  311. return h+1;
  312. }
  313. void heap_init(void) {
  314. heap_t *h;
  315. DEBUGF("heap_init()\n");
  316. // just one big free block
  317. h = (heap_t*)&heap[0];
  318. h->id = HEAP_ID_FREE;
  319. // (no HEAP_LEN_MASK required for free chunk)
  320. h->len = sizeof(heap) - sizeof(heap_t);
  321. #ifdef NVM_USE_HEAP_IDMAP
  322. heap_init_ids();
  323. #endif
  324. }
  325. // walk through the heap, check for every object
  326. // if it's still being used and remove it if not
  327. void heap_garbage_collect(void) {
  328. u16_t current = heap_base;
  329. heap_t *h;
  330. // (no HEAP_LEN_MASK required for free chunk)
  331. DEBUGF("heap_garbage_collect() free space before: %d\n", ((heap_t*)&heap[heap_base])->len);
  332. #ifdef NVM_USE_HEAP_IDMAP
  333. heap_init_ids();
  334. stack_mark_heap_root_ids();
  335. heap_mark_child_ids();
  336. #endif
  337. // set current to stack-top
  338. // walk through the entire heap
  339. while(current < sizeof(heap)) {
  340. u16_t len;
  341. h = (heap_t*)&heap[current];
  342. len = (h->len & HEAP_LEN_MASK) + sizeof(heap_t);
  343. // found an entry
  344. if(h->id != HEAP_ID_FREE) {
  345. // check if it's still used
  346. #ifdef NVM_USE_HEAP_IDMAP
  347. if(!heap_id_marked(h->id)) {
  348. #else
  349. if((!stack_heap_id_in_use(h->id))&&(!heap_fieldref(h->id))) {
  350. #endif
  351. // it is not used, remove it
  352. DEBUGF("HEAP: removing unused object with id 0x%04x (len %d)\n",
  353. h->id, len);
  354. // move everything before to the top
  355. #ifdef NVM_USE_MEMCPY_UP
  356. heap_memcpy_up(heap+heap_base+len, heap+heap_base, current-heap_base);
  357. #else
  358. memmove(heap+heap_base+len, heap+heap_base, current-heap_base);
  359. #endif
  360. // add freed mem to free chunk
  361. h = (heap_t*)&heap[heap_base];
  362. // (no HEAP_LEN_MASK required for free chunk)
  363. h->len += len;
  364. }
  365. }
  366. current += len;
  367. }
  368. if(current != sizeof(heap)) {
  369. DEBUGF("heap_garbage_collect(): total size error\n");
  370. error(ERROR_HEAP_CORRUPTED);
  371. }
  372. // (no HEAP_LEN_MASK required for free chunk)
  373. DEBUGF("heap_garbage_collect() free space after: %d\n", ((heap_t*)&heap[heap_base])->len);
  374. }
  375. // "steal" some bytes from the bottom of the heap (where
  376. // the free chunk is)
  377. void heap_steal(u16_t bytes) {
  378. heap_t *h = (heap_t*)&heap[heap_base];
  379. u16_t len;
  380. DEBUGF("HEAP: request to steal %d bytes\n", bytes);
  381. if(h->id != HEAP_ID_FREE) {
  382. DEBUGF("heap_steal(%d): start element not free element\n", bytes);
  383. error(ERROR_HEAP_CORRUPTED);
  384. }
  385. // try to make space if necessary
  386. // (no HEAP_LEN_MASK required for free chunk)
  387. len = h->len;
  388. if(len < bytes)
  389. heap_garbage_collect();
  390. // (no HEAP_LEN_MASK required for free chunk)
  391. len = h->len;
  392. if(len < bytes)
  393. error(ERROR_HEAP_OUT_OF_STACK_MEMORY);
  394. // finally steal ...
  395. heap_base += bytes;
  396. h = (heap_t*)&heap[heap_base];
  397. h->id = HEAP_ID_FREE;
  398. // (no HEAP_LEN_MASK required for free chunk)
  399. h->len = len - bytes;
  400. }
  401. // someone wants us to give some bytes back :-)
  402. void heap_unsteal(u16_t bytes) {
  403. heap_t *h = (heap_t*)&heap[heap_base];
  404. u16_t len;
  405. if(h->id != HEAP_ID_FREE) {
  406. DEBUGF("heap_unsteal(%d): start element not free element\n", bytes);
  407. error(ERROR_HEAP_CORRUPTED);
  408. }
  409. DEBUGF("HEAP: request to unsteal %d bytes\n", bytes);
  410. if(heap_base < bytes) {
  411. DEBUGF("stack underrun by %d bytes\n", bytes - heap_base);
  412. error(ERROR_HEAP_STACK_UNDERRUN);
  413. }
  414. // finally unsteal ...
  415. // (no HEAP_LEN_MASK required for free chunk)
  416. len = h->len;
  417. heap_base -= bytes;
  418. h = (heap_t*)&heap[heap_base];
  419. h->id = HEAP_ID_FREE;
  420. // (no HEAP_LEN_MASK required for free chunk)
  421. h->len = len + bytes;
  422. }