9e716da9b2ebfbf82b6ccce6c55b1afc53b3432a
[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_DUMP        0
40 #define DBG_OFFSET(a)   ((int) ((uint8_t *) (a) - ao_lisp_pool))
41 #define DBG(...) printf(__VA_ARGS__)
42 #define DBG_DO(a)       a
43 static int move_dump;
44 static int move_depth;
45 #define DBG_RESET() (move_depth = 0)
46 #define DBG_MOVE(...) do { if(move_dump) { int d; for (d = 0; d < move_depth; d++) printf ("  "); printf(__VA_ARGS__); } } while (0)
47 #define DBG_MOVE_IN()   (move_depth++)
48 #define DBG_MOVE_OUT()  (move_depth--)
49 #else
50 #define DBG(...)
51 #define DBG_DO(a)
52 #define DBG_RESET()
53 #define DBG_MOVE(...)
54 #define DBG_MOVE_IN()
55 #define DBG_MOVE_OUT()
56 #endif
57
58 uint8_t ao_lisp_exception;
59
60 struct ao_lisp_root {
61         void                            **addr;
62         const struct ao_lisp_type       *type;
63 };
64
65 #define AO_LISP_ROOT    16
66
67 static struct ao_lisp_root      ao_lisp_root[AO_LISP_ROOT];
68
69 static uint8_t  ao_lisp_busy[AO_LISP_POOL / 32];
70
71 static uint8_t  ao_lisp_moving[AO_LISP_POOL / 32];
72
73 uint16_t        ao_lisp_top;
74
75 static inline void mark(uint8_t *tag, int offset) {
76         int     byte = offset >> 5;
77         int     bit = (offset >> 2) & 7;
78         tag[byte] |= (1 << bit);
79 }
80
81 static inline void clear(uint8_t *tag, int offset) {
82         int     byte = offset >> 5;
83         int     bit = (offset >> 2) & 7;
84         tag[byte] &= ~(1 << bit);
85 }
86
87 static inline int busy(uint8_t *tag, int offset) {
88         int     byte = offset >> 5;
89         int     bit = (offset >> 2) & 7;
90         return (tag[byte] >> bit) & 1;
91 }
92
93 static inline int min(int a, int b) { return a < b ? a : b; }
94 static inline int max(int a, int b) { return a > b ? a : b; }
95
96 static inline int limit(int offset) {
97         return min(AO_LISP_POOL, max(offset, 0));
98 }
99
100 static int
101 mark_object(uint8_t *tag, void *addr, int size) {
102         int     base;
103         int     bound;
104
105         if (!addr)
106                 return 1;
107
108         if ((uint8_t *) addr < ao_lisp_pool || ao_lisp_pool + AO_LISP_POOL <= (uint8_t*) addr)
109                 return 1;
110
111         base = (uint8_t *) addr - ao_lisp_pool;
112         bound = base + size;
113
114         base = limit(base);
115         bound = limit(bound);
116         if (busy(tag, base))
117                 return 1;
118         while (base < bound) {
119                 mark(tag, base);
120                 base += 4;
121         }
122         return 0;
123 }
124
125 static int
126 clear_object(uint8_t *tag, void *addr, int size) {
127         int     base;
128         int     bound;
129         if (!addr)
130                 return 1;
131
132         base = (uint8_t *) addr - ao_lisp_pool;
133         bound = base + size;
134
135         base = limit(base);
136         bound = limit(bound);
137         if (!busy(tag, base))
138                 return 1;
139         while (base < bound) {
140                 clear(tag, base);
141                 base += 4;
142         }
143         return 0;
144 }
145
146 static int
147 busy_object(uint8_t *tag, void *addr) {
148         int     base;
149
150         if (!addr)
151                 return 1;
152
153         if ((uint8_t *) addr < ao_lisp_pool || ao_lisp_pool + AO_LISP_POOL <= (uint8_t*) addr)
154                 return 1;
155
156         base = (uint8_t *) addr - ao_lisp_pool;
157         base = limit(base);
158         if (busy(tag, base))
159                 return 1;
160         return 0;
161 }
162
163 static void     *move_old, *move_new;
164 static int      move_size;
165
166 static void
167 move_object(void)
168 {
169         int     i;
170
171         DBG_RESET();
172         DBG_MOVE("move %d -> %d\n", DBG_OFFSET(move_old), DBG_OFFSET(move_new));
173         DBG_MOVE_IN();
174         memset(ao_lisp_moving, '\0', sizeof (ao_lisp_moving));
175         for (i = 0; i < AO_LISP_ROOT; i++) {
176                 if (!ao_lisp_root[i].addr)
177                         continue;
178                 if (ao_lisp_root[i].type) {
179                         DBG_DO(void *addr = *ao_lisp_root[i].addr);
180                         DBG_MOVE("root %d\n", DBG_OFFSET(addr));
181                         if (!ao_lisp_move(ao_lisp_root[i].type,
182                                           ao_lisp_root[i].addr))
183                                 DBG_MOVE("root moves from %p to %p\n",
184                                          addr,
185                                          *ao_lisp_root[i].addr);
186                 } else {
187                         DBG_DO(ao_poly p = *(ao_poly *) ao_lisp_root[i].addr);
188                         if (!ao_lisp_poly_move((ao_poly *) ao_lisp_root[i].addr))
189                                 DBG_MOVE("root poly move from %04x to %04x\n",
190                                          p, *(ao_poly *) ao_lisp_root[i].addr);
191                 }
192         }
193         DBG_MOVE_OUT();
194         DBG_MOVE("move done\n");
195 }
196
197 #if DBG_DUMP
198 static void
199 dump_busy(void)
200 {
201         int     i;
202         printf("busy:");
203         for (i = 0; i < ao_lisp_top; i += 4) {
204                 if ((i & 0xff) == 0)
205                         printf("\n");
206                 else if ((i & 0x1f) == 0)
207                         printf(" ");
208                 if (busy(ao_lisp_busy, i))
209                         putchar('*');
210                 else
211                         putchar('-');
212         }
213         printf ("\n");
214 }
215 #define DUMP_BUSY()     dump_busy()
216 #else
217 #define DUMP_BUSY()
218 #endif
219
220 static void
221 ao_lisp_mark_busy(void)
222 {
223         int i;
224
225         memset(ao_lisp_busy, '\0', sizeof (ao_lisp_busy));
226         DBG("mark\n");
227         for (i = 0; i < AO_LISP_ROOT; i++) {
228                 if (ao_lisp_root[i].type) {
229                         void **a = ao_lisp_root[i].addr, *v;
230                         if (a && (v = *a)) {
231                                 DBG("root %p\n", v);
232                                 ao_lisp_mark(ao_lisp_root[i].type, v);
233                         }
234                 } else {
235                         ao_poly *a = (ao_poly *) ao_lisp_root[i].addr, p;
236                         if (a && (p = *a)) {
237                                 DBG("root %04x\n", p);
238                                 ao_lisp_poly_mark(p);
239                         }
240                 }
241         }
242 }
243
244 void
245 ao_lisp_collect(void)
246 {
247         int     i;
248         int     top;
249
250         DBG("collect\n");
251         /* Mark */
252         ao_lisp_mark_busy();
253
254         DUMP_BUSY();
255         /* Compact */
256         DBG("find first busy\n");
257         for (i = 0; i < ao_lisp_top; i += 4) {
258                 if (!busy(ao_lisp_busy, i))
259                         break;
260         }
261         top = i;
262         while(i < ao_lisp_top) {
263                 if (busy(ao_lisp_busy, i)) {
264                         DBG("busy %d -> %d\n", i, top);
265                         move_old = &ao_lisp_pool[i];
266                         move_new = &ao_lisp_pool[top];
267                         move_size = 0;
268                         move_object();
269                         DBG("\tbusy size %d\n", move_size);
270                         if (move_size == 0)
271                                 abort();
272                         clear_object(ao_lisp_busy, move_old, move_size);
273                         mark_object(ao_lisp_busy, move_new, move_size);
274                         i += move_size;
275                         top += move_size;
276                         DUMP_BUSY();
277                 } else {
278                         i += 4;
279                 }
280         }
281         ao_lisp_top = top;
282 }
283
284
285 int
286 ao_lisp_mark(const struct ao_lisp_type *type, void *addr)
287 {
288         if (!addr)
289                 return 1;
290         if (mark_object(ao_lisp_busy, addr, type->size(addr)))
291                 return 1;
292         type->mark(addr);
293         return 0;
294 }
295
296 int
297 ao_lisp_mark_memory(void *addr, int size)
298 {
299         return mark_object(ao_lisp_busy, addr, size);
300 }
301
302 /*
303  * After the object has been moved, we have to reference it
304  * in the new location. This is only relevant for ao_lisp_poly_move
305  * as it needs to fetch the type byte from the object, which
306  * may have been overwritten by the copy
307  */
308 void *
309 ao_lisp_move_map(void *addr)
310 {
311         if (addr == move_old) {
312                 if (busy_object(ao_lisp_moving, addr))
313                         return move_new;
314         }
315         return addr;
316 }
317
318 static void *
319 check_move(void *addr, int size)
320 {
321         if (addr == move_old) {
322                 DBG_MOVE("mapping %d -> %d\n", DBG_OFFSET(addr), DBG_OFFSET(move_new));
323                 if (!busy_object(ao_lisp_moving, addr)) {
324                         DBG_MOVE("  copy %d\n", size);
325                         memmove(move_new, move_old, size);
326                         move_size = (size + 3) & ~3;
327                 }
328                 addr = move_new;
329         }
330         return addr;
331 }
332
333 int
334 ao_lisp_move(const struct ao_lisp_type *type, void **ref)
335 {
336         void            *addr = *ref;
337         uint8_t         *a = addr;
338         int             size = type->size(addr);
339
340         if (!addr)
341                 return NULL;
342
343 #ifndef AO_LISP_MAKE_CONST
344         if (AO_LISP_IS_CONST(addr))
345                 return 1;
346 #endif
347         DBG_MOVE("object %d\n", DBG_OFFSET(addr));
348         if (a < ao_lisp_pool || ao_lisp_pool + AO_LISP_POOL <= a)
349                 abort();
350         DBG_MOVE_IN();
351         addr = check_move(addr, size);
352         if (addr != *ref)
353                 *ref = addr;
354         if (mark_object(ao_lisp_moving, addr, size)) {
355                 DBG_MOVE("already moved\n");
356                 DBG_MOVE_OUT();
357                 return 1;
358         }
359         DBG_MOVE_OUT();
360         DBG_MOVE("recursing...\n");
361         DBG_MOVE_IN();
362         type->move(addr);
363         DBG_MOVE_OUT();
364         DBG_MOVE("done %d\n", DBG_OFFSET(addr));
365         return 0;
366 }
367
368 int
369 ao_lisp_move_memory(void **ref, int size)
370 {
371         void *addr = *ref;
372         if (!addr)
373                 return NULL;
374
375         DBG_MOVE("memory %d\n", DBG_OFFSET(addr));
376         DBG_MOVE_IN();
377         addr = check_move(addr, size);
378         if (addr != *ref)
379                 *ref = addr;
380         if (mark_object(ao_lisp_moving, addr, size)) {
381                 DBG_MOVE("already moved\n");
382                 DBG_MOVE_OUT();
383                 return 1;
384         }
385         DBG_MOVE_OUT();
386         return 0;
387 }
388
389 #ifdef DBG_POOL
390 static int AO_LISP_POOL_CUR = AO_LISP_POOL / 8;
391
392 static void
393 ao_lisp_poison(void)
394 {
395         int     i;
396
397         printf("poison\n");
398         ao_lisp_mark_busy();
399         for (i = 0; i < AO_LISP_POOL_CUR; i += 4) {
400                 uint32_t        *a = (uint32_t *) &ao_lisp_pool[i];
401                 if (!busy_object(ao_lisp_busy, a))
402                         *a = 0xBEEFBEEF;
403         }
404         for (i = 0; i < AO_LISP_POOL_CUR; i += 2) {
405                 ao_poly         *a = (uint16_t *) &ao_lisp_pool[i];
406                 ao_poly         p = *a;
407
408                 if (!ao_lisp_is_const(p)) {
409                         void    *r = ao_lisp_ref(p);
410
411                         if (ao_lisp_pool <= (uint8_t *) r &&
412                             (uint8_t *) r <= ao_lisp_pool + AO_LISP_POOL_CUR)
413                         {
414                                 if (!busy_object(ao_lisp_busy, r)) {
415                                         printf("missing reference from %d to %d\n",
416                                                (int) ((uint8_t *) a - ao_lisp_pool),
417                                                (int) ((uint8_t *) r - ao_lisp_pool));
418                                 }
419                         }
420                 }
421         }
422 }
423
424 #else
425 #define AO_LISP_POOL_CUR AO_LISP_POOL
426 #endif
427
428 void *
429 ao_lisp_alloc(int size)
430 {
431         void    *addr;
432
433         size = ao_lisp_mem_round(size);
434 #ifdef DBG_COLLECT_ALWAYS
435         ao_lisp_collect();
436 #endif
437         if (ao_lisp_top + size > AO_LISP_POOL_CUR) {
438 #ifdef DBG_POOL
439                 if (AO_LISP_POOL_CUR < AO_LISP_POOL) {
440                         AO_LISP_POOL_CUR += AO_LISP_POOL / 8;
441                         ao_lisp_poison();
442                 } else
443 #endif
444                 ao_lisp_collect();
445 #ifdef DBG_POOL
446                 {
447                         int     i;
448
449                         for (i = ao_lisp_top; i < AO_LISP_POOL; i += 4) {
450                                 uint32_t        *p = (uint32_t *) &ao_lisp_pool[i];
451                                 *p = 0xbeefbeef;
452                         }
453                 }
454 #endif
455
456                 if (ao_lisp_top + size > AO_LISP_POOL) {
457                         ao_lisp_exception |= AO_LISP_OOM;
458                         return NULL;
459                 }
460         }
461         addr = ao_lisp_pool + ao_lisp_top;
462         ao_lisp_top += size;
463         return addr;
464 }
465
466 int
467 ao_lisp_root_add(const struct ao_lisp_type *type, void *addr)
468 {
469         int     i;
470         DBG("add root type %p addr %p\n", type, addr);
471         for (i = 0; i < AO_LISP_ROOT; i++) {
472                 if (!ao_lisp_root[i].addr) {
473                         ao_lisp_root[i].addr = addr;
474                         ao_lisp_root[i].type = type;
475                         return 1;
476                 }
477         }
478         abort();
479         return 0;
480 }
481
482 int
483 ao_lisp_root_poly_add(ao_poly *p)
484 {
485         return ao_lisp_root_add(NULL, p);
486 }
487
488 void
489 ao_lisp_root_clear(void *addr)
490 {
491         int     i;
492         for (i = 0; i < AO_LISP_ROOT; i++) {
493                 if (ao_lisp_root[i].addr == addr) {
494                         ao_lisp_root[i].addr = 0;
495                         ao_lisp_root[i].type = 0;
496                         break;
497                 }
498         }
499 }