476843d89ee1a4b04d9aa541f1cdabf451fae17a
[fw/altos] / src / lisp / ao_lisp_mem.c
1 /*
2  * Copyright © 2016 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  */
14
15 #define AO_LISP_CONST_BITS
16
17 #include "ao_lisp.h"
18 #include <stdio.h>
19
20 #ifdef AO_LISP_MAKE_CONST
21 #include <stdlib.h>
22 uint8_t ao_lisp_const[AO_LISP_POOL_CONST] __attribute__((aligned(4)));
23 #define ao_lisp_pool ao_lisp_const
24 #undef AO_LISP_POOL
25 #define AO_LISP_POOL AO_LISP_POOL_CONST
26 #else
27 uint8_t ao_lisp_pool[AO_LISP_POOL] __attribute__((aligned(4)));
28 #endif
29
30 #if 0
31 #define DBG_COLLECT_ALWAYS
32 #endif
33
34 #if 0
35 #define DBG_POOL
36 #endif
37
38 #if 0
39 #define DBG_INCLUDE
40 #define DBG_DUMP        0
41 #define DBG_OFFSET(a)   ((int) ((uint8_t *) (a) - ao_lisp_pool))
42 #define DBG(...) printf(__VA_ARGS__)
43 #define DBG_DO(a)       a
44 static int move_dump = 1;
45 static int move_depth;
46 #define DBG_RESET() (move_depth = 0)
47 #define DBG_MOVE(...) do { if(move_dump) { int d; for (d = 0; d < move_depth; d++) printf ("  "); printf(__VA_ARGS__); } } while (0)
48 #define DBG_MOVE_IN()   (move_depth++)
49 #define DBG_MOVE_OUT()  (move_depth--)
50 #else
51 #define DBG(...)
52 #define DBG_DO(a)
53 #define DBG_RESET()
54 #define DBG_MOVE(...)
55 #define DBG_MOVE_IN()
56 #define DBG_MOVE_OUT()
57 #endif
58
59 uint8_t ao_lisp_exception;
60
61 struct ao_lisp_root {
62         void                            **addr;
63         const struct ao_lisp_type       *type;
64 };
65
66 #define AO_LISP_ROOT    16
67
68 static struct ao_lisp_root      ao_lisp_root[AO_LISP_ROOT];
69
70 static uint8_t  ao_lisp_busy[AO_LISP_POOL / 32];
71 static uint8_t  ao_lisp_moving[AO_LISP_POOL / 32];
72 static uint8_t  ao_lisp_cons[AO_LISP_POOL / 32];
73 static uint8_t  ao_lisp_cons_last[AO_LISP_POOL / 32];
74 static uint8_t  ao_lisp_cons_noted;
75
76 uint16_t        ao_lisp_top;
77
78 static inline void mark(uint8_t *tag, int offset) {
79         int     byte = offset >> 5;
80         int     bit = (offset >> 2) & 7;
81         tag[byte] |= (1 << bit);
82 }
83
84 static inline void clear(uint8_t *tag, int offset) {
85         int     byte = offset >> 5;
86         int     bit = (offset >> 2) & 7;
87         tag[byte] &= ~(1 << bit);
88 }
89
90 static inline int busy(uint8_t *tag, int offset) {
91         int     byte = offset >> 5;
92         int     bit = (offset >> 2) & 7;
93         return (tag[byte] >> bit) & 1;
94 }
95
96 static inline int min(int a, int b) { return a < b ? a : b; }
97 static inline int max(int a, int b) { return a > b ? a : b; }
98
99 static inline int limit(int offset) {
100         return min(AO_LISP_POOL, max(offset, 0));
101 }
102
103 static int
104 mark_object(uint8_t *tag, void *addr, int size) {
105         int     base;
106         int     bound;
107
108         if (!addr)
109                 return 1;
110
111         if ((uint8_t *) addr < ao_lisp_pool || ao_lisp_pool + AO_LISP_POOL <= (uint8_t*) addr)
112                 return 1;
113
114         base = (uint8_t *) addr - ao_lisp_pool;
115         bound = base + size;
116
117         base = limit(base);
118         bound = limit(bound);
119         if (busy(tag, base))
120                 return 1;
121         while (base < bound) {
122                 mark(tag, base);
123                 base += 4;
124         }
125         return 0;
126 }
127
128 static int
129 clear_object(uint8_t *tag, void *addr, int size) {
130         int     base;
131         int     bound;
132         if (!addr)
133                 return 1;
134
135         base = (uint8_t *) addr - ao_lisp_pool;
136         bound = base + size;
137
138         base = limit(base);
139         bound = limit(bound);
140         if (!busy(tag, base))
141                 return 1;
142         while (base < bound) {
143                 clear(tag, base);
144                 base += 4;
145         }
146         return 0;
147 }
148
149 static int
150 busy_object(uint8_t *tag, void *addr) {
151         int     base;
152
153         if (!addr)
154                 return 1;
155
156         if ((uint8_t *) addr < ao_lisp_pool || ao_lisp_pool + AO_LISP_POOL <= (uint8_t*) addr)
157                 return 1;
158
159         base = (uint8_t *) addr - ao_lisp_pool;
160         base = limit(base);
161         if (busy(tag, base))
162                 return 1;
163         return 0;
164 }
165
166 static void
167 note_cons(void *addr)
168 {
169         DBG_MOVE("note cons %d\n", DBG_OFFSET(addr));
170         if (AO_LISP_IS_POOL(addr)) {
171                 ao_lisp_cons_noted = 1;
172                 mark(ao_lisp_cons, (uint8_t *) addr - ao_lisp_pool);
173         }
174 }
175
176
177 static void     *move_old, *move_new;
178 static int      move_size;
179
180 static void
181 move_object(void)
182 {
183         int     i;
184
185         DBG_RESET();
186         DBG_MOVE("move %d -> %d\n", DBG_OFFSET(move_old), DBG_OFFSET(move_new));
187         DBG_MOVE_IN();
188         memset(ao_lisp_moving, '\0', sizeof (ao_lisp_moving));
189         memset(ao_lisp_cons, '\0', sizeof (ao_lisp_cons));
190         ao_lisp_cons_noted = 0;
191         for (i = 0; i < AO_LISP_ROOT; i++) {
192                 if (!ao_lisp_root[i].addr)
193                         continue;
194                 if (ao_lisp_root[i].type) {
195                         void *addr = *ao_lisp_root[i].addr;
196                         if (!addr)
197                                 continue;
198                         DBG_MOVE("root %d\n", DBG_OFFSET(addr));
199                         if (!ao_lisp_move(ao_lisp_root[i].type,
200                                           ao_lisp_root[i].addr)) {
201                                 DBG_MOVE("root moves from %p to %p\n",
202                                          addr,
203                                          *ao_lisp_root[i].addr);
204                         }
205                 } else {
206                         ao_poly p = *(ao_poly *) ao_lisp_root[i].addr;
207                         if (!p)
208                                 continue;
209                         if (!ao_lisp_poly_move((ao_poly *) ao_lisp_root[i].addr, 0)) {
210                                 DBG_MOVE("root poly move from %04x to %04x\n",
211                                          p, *(ao_poly *) ao_lisp_root[i].addr);
212                         }
213                 }
214         }
215         while (ao_lisp_cons_noted) {
216                 memcpy(ao_lisp_cons_last, ao_lisp_cons, sizeof (ao_lisp_cons));
217                 memset(ao_lisp_cons, '\0', sizeof (ao_lisp_cons));
218                 ao_lisp_cons_noted = 0;
219                 for (i = 0; i < AO_LISP_POOL; i += 4) {
220                         if (busy(ao_lisp_cons_last, i)) {
221                                 void *addr = ao_lisp_pool + i;
222                                 DBG_MOVE("cons %d\n", DBG_OFFSET(addr));
223                                 if (!ao_lisp_move(&ao_lisp_cons_type, &addr)) {
224                                         DBG_MOVE("cons moves from %p to %p\n",
225                                                  ao_lisp_pool + i, addr);
226                                 }
227                         }
228                 }
229         }
230         DBG_MOVE_OUT();
231         DBG_MOVE("move done\n");
232 }
233
234 #if DBG_DUMP
235 static void
236 dump_busy(void)
237 {
238         int     i;
239         printf("busy:");
240         for (i = 0; i < ao_lisp_top; i += 4) {
241                 if ((i & 0xff) == 0)
242                         printf("\n");
243                 else if ((i & 0x1f) == 0)
244                         printf(" ");
245                 if (busy(ao_lisp_busy, i))
246                         putchar('*');
247                 else
248                         putchar('-');
249         }
250         printf ("\n");
251 }
252 #define DUMP_BUSY()     dump_busy()
253 #else
254 #define DUMP_BUSY()
255 #endif
256
257 static const struct ao_lisp_type const *ao_lisp_types[AO_LISP_NUM_TYPE] = {
258         [AO_LISP_CONS] = &ao_lisp_cons_type,
259         [AO_LISP_INT] = NULL,
260         [AO_LISP_STRING] = &ao_lisp_string_type,
261         [AO_LISP_OTHER] = (void *) 0x1,
262         [AO_LISP_ATOM] = &ao_lisp_atom_type,
263         [AO_LISP_BUILTIN] = &ao_lisp_builtin_type,
264         [AO_LISP_FRAME] = &ao_lisp_frame_type,
265         [AO_LISP_LAMBDA] = &ao_lisp_lambda_type,
266 };
267
268
269 static void
270 ao_lisp_mark_busy(void)
271 {
272         int i;
273
274         memset(ao_lisp_busy, '\0', sizeof (ao_lisp_busy));
275         memset(ao_lisp_cons, '\0', sizeof (ao_lisp_cons));
276         ao_lisp_cons_noted = 0;
277         DBG("mark\n");
278         for (i = 0; i < AO_LISP_ROOT; i++) {
279                 if (ao_lisp_root[i].type) {
280                         void **a = ao_lisp_root[i].addr, *v;
281                         if (a && (v = *a)) {
282                                 DBG("root %d\n", DBG_OFFSET(v));
283                                 ao_lisp_mark(ao_lisp_root[i].type, v);
284                         }
285                 } else {
286                         ao_poly *a = (ao_poly *) ao_lisp_root[i].addr, p;
287                         if (a && (p = *a)) {
288                                 DBG("root 0x%04x\n", p);
289                                 ao_lisp_poly_mark(p, 0);
290                         }
291                 }
292         }
293         while (ao_lisp_cons_noted) {
294                 memcpy(ao_lisp_cons_last, ao_lisp_cons, sizeof (ao_lisp_cons));
295                 memset(ao_lisp_cons, '\0', sizeof (ao_lisp_cons));
296                 ao_lisp_cons_noted = 0;
297                 for (i = 0; i < AO_LISP_POOL; i += 4) {
298                         if (busy(ao_lisp_cons_last, i)) {
299                                 void *v = ao_lisp_pool + i;
300                                 DBG("cons %d\n", DBG_OFFSET(v));
301                                 ao_lisp_mark(&ao_lisp_cons_type, v);
302                         }
303                 }
304         }
305 }
306
307 void
308 ao_lisp_collect(void)
309 {
310         int     i;
311         int     top;
312
313         DBG("collect\n");
314         /* Mark */
315         ao_lisp_mark_busy();
316
317         DUMP_BUSY();
318         /* Compact */
319         DBG("find first busy\n");
320         for (i = 0; i < ao_lisp_top; i += 4) {
321                 if (!busy(ao_lisp_busy, i))
322                         break;
323         }
324         top = i;
325         while(i < ao_lisp_top) {
326                 if (busy(ao_lisp_busy, i)) {
327                         DBG("busy %d -> %d\n", i, top);
328                         move_old = &ao_lisp_pool[i];
329                         move_new = &ao_lisp_pool[top];
330                         move_size = 0;
331                         move_object();
332                         DBG("\tbusy size %d\n", move_size);
333                         if (move_size == 0)
334                                 abort();
335                         clear_object(ao_lisp_busy, move_old, move_size);
336                         mark_object(ao_lisp_busy, move_new, move_size);
337                         if (busy_object(ao_lisp_cons, move_old)) {
338                                 clear_object(ao_lisp_cons, move_old, move_size);
339                                 mark_object(ao_lisp_cons, move_new, move_size);
340                         }
341                         i += move_size;
342                         top += move_size;
343                         DUMP_BUSY();
344                 } else {
345                         i += 4;
346                 }
347         }
348         ao_lisp_top = top;
349 }
350
351
352 int
353 ao_lisp_mark(const struct ao_lisp_type *type, void *addr)
354 {
355         if (!addr)
356                 return 1;
357         if (mark_object(ao_lisp_busy, addr, type->size(addr)))
358                 return 1;
359         type->mark(addr);
360         return 0;
361 }
362
363 int
364 ao_lisp_poly_mark(ao_poly p, uint8_t do_note_cons)
365 {
366         uint8_t type = ao_lisp_poly_type(p);
367
368         if (!p)
369                 return 1;
370         if (type == AO_LISP_CONS && do_note_cons) {
371                 note_cons(ao_lisp_ref(p));
372                 return 0;
373         } else {
374                 const struct ao_lisp_type *lisp_type = ao_lisp_types[ao_lisp_poly_type(p)];
375                 if (lisp_type)
376                         return ao_lisp_mark(lisp_type, ao_lisp_ref(p));
377                 return 1;
378         }
379 }
380
381 int
382 ao_lisp_mark_memory(void *addr, int size)
383 {
384         return mark_object(ao_lisp_busy, addr, size);
385 }
386
387 /*
388  * After the object has been moved, we have to reference it
389  * in the new location. This is only relevant for ao_lisp_poly_move
390  * as it needs to fetch the type byte from the object, which
391  * may have been overwritten by the copy
392  */
393 void *
394 ao_lisp_move_map(void *addr)
395 {
396         if (addr == move_old) {
397                 if (busy_object(ao_lisp_moving, addr))
398                         return move_new;
399         }
400         return addr;
401 }
402
403 static void *
404 check_move(void *addr, int size)
405 {
406         if (addr == move_old) {
407                 DBG_MOVE("mapping %d -> %d\n", DBG_OFFSET(addr), DBG_OFFSET(move_new));
408                 if (!busy_object(ao_lisp_moving, addr)) {
409                         DBG_MOVE("  copy %d\n", size);
410                         memmove(move_new, move_old, size);
411                         move_size = (size + 3) & ~3;
412                 }
413                 addr = move_new;
414         }
415         return addr;
416 }
417
418 int
419 ao_lisp_move(const struct ao_lisp_type *type, void **ref)
420 {
421         void            *addr = *ref;
422         uint8_t         *a = addr;
423         int             size = type->size(addr);
424
425         if (!addr)
426                 return 1;
427
428 #ifndef AO_LISP_MAKE_CONST
429         if (AO_LISP_IS_CONST(addr))
430                 return 1;
431 #endif
432         DBG_MOVE("object %d\n", DBG_OFFSET(addr));
433         if (!AO_LISP_IS_POOL(a))
434                 abort();
435         DBG_MOVE_IN();
436         addr = check_move(addr, size);
437         if (addr != *ref)
438                 *ref = addr;
439         if (mark_object(ao_lisp_moving, addr, size)) {
440                 DBG_MOVE("already moved\n");
441                 DBG_MOVE_OUT();
442                 return 1;
443         }
444         DBG_MOVE_OUT();
445         DBG_MOVE("recursing...\n");
446         DBG_MOVE_IN();
447         type->move(addr);
448         DBG_MOVE_OUT();
449         DBG_MOVE("done %d\n", DBG_OFFSET(addr));
450         return 0;
451 }
452
453 int
454 ao_lisp_move_memory(void **ref, int size)
455 {
456         void *addr = *ref;
457         if (!addr)
458                 return 1;
459
460         DBG_MOVE("memory %d\n", DBG_OFFSET(addr));
461         DBG_MOVE_IN();
462         addr = check_move(addr, size);
463         if (addr != *ref)
464                 *ref = addr;
465         if (mark_object(ao_lisp_moving, addr, size)) {
466                 DBG_MOVE("already moved\n");
467                 DBG_MOVE_OUT();
468                 return 1;
469         }
470         DBG_MOVE_OUT();
471         return 0;
472 }
473
474 int
475 ao_lisp_poly_move(ao_poly *ref, uint8_t do_note_cons)
476 {
477         uint8_t                         type;
478         ao_poly                         p = *ref;
479         const struct ao_lisp_type       *lisp_type;
480         int                             ret;
481         void                            *addr;
482
483         if (!p)
484                 return 1;
485
486         type = ao_lisp_poly_base_type(p);
487         addr = ao_lisp_ref(p);
488         if (type == AO_LISP_CONS && do_note_cons) {
489                 note_cons(addr);
490                 addr = check_move(addr, sizeof (struct ao_lisp_cons));
491                 ret = 1;
492         } else {
493
494                 if (type == AO_LISP_OTHER)
495                         type = ao_lisp_other_type(ao_lisp_move_map(ao_lisp_poly_other(p)));
496
497                 if (type >= AO_LISP_NUM_TYPE)
498                         abort();
499
500                 lisp_type = ao_lisp_types[type];
501                 if (!lisp_type)
502                         return 1;
503                 ret = ao_lisp_move(lisp_type, &addr);
504         }
505
506         if (addr != ao_lisp_ref(p)) {
507                 ao_poly np = ao_lisp_poly(addr, p & AO_LISP_TYPE_MASK);
508                 DBG("poly %d moved %04x -> %04x\n",
509                     type, p, np);
510                 *ref = np;
511         }
512         return ret;
513 }
514
515 #ifdef DBG_POOL
516 static int AO_LISP_POOL_CUR = AO_LISP_POOL / 8;
517
518 static void
519 ao_lisp_poison(void)
520 {
521         int     i;
522
523         printf("poison\n");
524         ao_lisp_mark_busy();
525         for (i = 0; i < AO_LISP_POOL_CUR; i += 4) {
526                 uint32_t        *a = (uint32_t *) &ao_lisp_pool[i];
527                 if (!busy_object(ao_lisp_busy, a))
528                         *a = 0xBEEFBEEF;
529         }
530         for (i = 0; i < AO_LISP_POOL_CUR; i += 2) {
531                 ao_poly         *a = (uint16_t *) &ao_lisp_pool[i];
532                 ao_poly         p = *a;
533
534                 if (!ao_lisp_is_const(p)) {
535                         void    *r = ao_lisp_ref(p);
536
537                         if (ao_lisp_pool <= (uint8_t *) r &&
538                             (uint8_t *) r <= ao_lisp_pool + AO_LISP_POOL_CUR)
539                         {
540                                 if (!busy_object(ao_lisp_busy, r)) {
541                                         printf("missing reference from %d to %d\n",
542                                                (int) ((uint8_t *) a - ao_lisp_pool),
543                                                (int) ((uint8_t *) r - ao_lisp_pool));
544                                 }
545                         }
546                 }
547         }
548 }
549
550 #else
551 #define AO_LISP_POOL_CUR AO_LISP_POOL
552 #endif
553
554 void *
555 ao_lisp_alloc(int size)
556 {
557         void    *addr;
558
559         size = ao_lisp_mem_round(size);
560 #ifdef DBG_COLLECT_ALWAYS
561         ao_lisp_collect();
562 #endif
563         if (ao_lisp_top + size > AO_LISP_POOL_CUR) {
564 #ifdef DBG_POOL
565                 if (AO_LISP_POOL_CUR < AO_LISP_POOL) {
566                         AO_LISP_POOL_CUR += AO_LISP_POOL / 8;
567                         ao_lisp_poison();
568                 } else
569 #endif
570                 ao_lisp_collect();
571 #ifdef DBG_POOL
572                 {
573                         int     i;
574
575                         for (i = ao_lisp_top; i < AO_LISP_POOL; i += 4) {
576                                 uint32_t        *p = (uint32_t *) &ao_lisp_pool[i];
577                                 *p = 0xbeefbeef;
578                         }
579                 }
580 #endif
581
582                 if (ao_lisp_top + size > AO_LISP_POOL) {
583                         ao_lisp_exception |= AO_LISP_OOM;
584                         return NULL;
585                 }
586         }
587         addr = ao_lisp_pool + ao_lisp_top;
588         ao_lisp_top += size;
589         return addr;
590 }
591
592 int
593 ao_lisp_root_add(const struct ao_lisp_type *type, void *addr)
594 {
595         int     i;
596         DBG("add root type %p addr %p\n", type, addr);
597         for (i = 0; i < AO_LISP_ROOT; i++) {
598                 if (!ao_lisp_root[i].addr) {
599                         ao_lisp_root[i].addr = addr;
600                         ao_lisp_root[i].type = type;
601                         return 1;
602                 }
603         }
604         abort();
605         return 0;
606 }
607
608 int
609 ao_lisp_root_poly_add(ao_poly *p)
610 {
611         return ao_lisp_root_add(NULL, p);
612 }
613
614 void
615 ao_lisp_root_clear(void *addr)
616 {
617         int     i;
618         for (i = 0; i < AO_LISP_ROOT; i++) {
619                 if (ao_lisp_root[i].addr == addr) {
620                         ao_lisp_root[i].addr = 0;
621                         ao_lisp_root[i].type = 0;
622                         break;
623                 }
624         }
625 }