altos/lisp: Change lisp objects to use ao_poly everywhere. Add const
[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 uint8_t ao_lisp_pool[AO_LISP_POOL] __attribute__((aligned(4)));
21
22 #ifdef AO_LISP_MAKE_CONST
23 #include <stdlib.h>
24 uint8_t ao_lisp_const[AO_LISP_POOL_CONST] __attribute__((aligned(4)));
25 #endif
26
27 uint8_t ao_lisp_exception;
28
29 struct ao_lisp_root {
30         void                            **addr;
31         const struct ao_lisp_type       *type;
32 };
33
34 #define AO_LISP_ROOT    16
35
36 static struct ao_lisp_root      ao_lisp_root[AO_LISP_ROOT];
37
38 static uint8_t  ao_lisp_busy[AO_LISP_POOL / 32];
39
40 static uint8_t  ao_lisp_moving[AO_LISP_POOL / 32];
41
42 uint16_t        ao_lisp_top;
43
44 static inline void mark(uint8_t *tag, int offset) {
45         int     byte = offset >> 5;
46         int     bit = (offset >> 2) & 7;
47         tag[byte] |= (1 << bit);
48 }
49
50 static inline void clear(uint8_t *tag, int offset) {
51         int     byte = offset >> 5;
52         int     bit = (offset >> 2) & 7;
53         tag[byte] &= ~(1 << bit);
54 }
55
56 static inline int busy(uint8_t *tag, int offset) {
57         int     byte = offset >> 5;
58         int     bit = (offset >> 2) & 7;
59         return (tag[byte] >> bit) & 1;
60 }
61
62 static inline int min(int a, int b) { return a < b ? a : b; }
63 static inline int max(int a, int b) { return a > b ? a : b; }
64
65 static inline int limit(int offset) {
66         return min(AO_LISP_POOL, max(offset, 0));
67 }
68
69 static int
70 mark_object(uint8_t *tag, void *addr, int size) {
71         int     base;
72         int     bound;
73
74         if (!addr)
75                 return 1;
76
77         if ((uint8_t *) addr < ao_lisp_pool || ao_lisp_pool + AO_LISP_POOL <= (uint8_t*) addr)
78                 return 1;
79
80         base = (uint8_t *) addr - ao_lisp_pool;
81         bound = base + size;
82
83         base = limit(base);
84         bound = limit(bound);
85         if (busy(tag, base))
86                 return 1;
87         while (base < bound) {
88                 mark(tag, base);
89                 base += 4;
90         }
91         return 0;
92 }
93
94 static int
95 clear_object(uint8_t *tag, void *addr, int size) {
96         int     base;
97         int     bound;
98         if (!addr)
99                 return 1;
100
101         base = (uint8_t *) addr - ao_lisp_pool;
102         bound = base + size;
103
104         base = limit(base);
105         bound = limit(bound);
106         if (!busy(tag, base))
107                 return 1;
108         while (base < bound) {
109                 clear(tag, base);
110                 base += 4;
111         }
112         return 0;
113 }
114
115 static void     *move_old, *move_new;
116 static int      move_size;
117
118 static void
119 move_object(void)
120 {
121         int     i;
122
123         memset(ao_lisp_moving, '\0', sizeof (ao_lisp_moving));
124         for (i = 0; i < AO_LISP_ROOT; i++)
125                 if (ao_lisp_root[i].addr) {
126                         void *new;
127                         new = ao_lisp_move(ao_lisp_root[i].type, *ao_lisp_root[i].addr);
128                         if (new)
129                                 *ao_lisp_root[i].addr = new;
130                 }
131 }
132
133 static void
134 collect(void)
135 {
136         int     i;
137
138         /* Mark */
139         memset(ao_lisp_busy, '\0', sizeof (ao_lisp_busy));
140         for (i = 0; i < AO_LISP_ROOT; i++)
141                 if (ao_lisp_root[i].addr)
142                         ao_lisp_mark(ao_lisp_root[i].type, *ao_lisp_root[i].addr);
143
144         /* Compact */
145         ao_lisp_top = 0;
146         for (i = 0; i < AO_LISP_POOL; i += 4) {
147                 if (!busy(ao_lisp_busy, i))
148                         break;
149         }
150         ao_lisp_top = i;
151         while(i < AO_LISP_POOL) {
152                 if (busy(ao_lisp_busy, i)) {
153                         move_old = &ao_lisp_pool[i];
154                         move_new = &ao_lisp_pool[ao_lisp_top];
155                         move_size = 0;
156                         move_object();
157                         clear_object(ao_lisp_busy, move_old, move_size);
158                         i += move_size;
159                         ao_lisp_top += move_size;
160                 } else {
161                         i += 4;
162                 }
163         }
164 }
165
166
167 void
168 ao_lisp_mark(const struct ao_lisp_type *type, void *addr)
169 {
170         if (mark_object(ao_lisp_busy, addr, type->size(addr)))
171                 return;
172         type->mark(addr);
173 }
174
175 int
176 ao_lisp_mark_memory(void *addr, int size)
177 {
178         return mark_object(ao_lisp_busy, addr, size);
179 }
180
181 static void *
182 check_move(void *addr, int size)
183 {
184         if (addr == move_old) {
185                 memmove(move_new, move_old, size);
186                 move_size = (size + 3) & ~3;
187                 addr = move_new;
188         }
189         return addr;
190 }
191
192 void *
193 ao_lisp_move(const struct ao_lisp_type *type, void *addr)
194 {
195         int     size = type->size(addr);
196
197         if (!addr)
198                 return NULL;
199
200         addr = check_move(addr, size);
201         if (mark_object(ao_lisp_moving, addr, size))
202                 return addr;
203         type->move(addr);
204         return addr;
205 }
206
207 void *
208 ao_lisp_move_memory(void *addr, int size)
209 {
210         if (!addr)
211                 return NULL;
212
213         addr = check_move(addr, size);
214         if (mark_object(ao_lisp_moving, addr, size))
215                 return NULL;
216         return addr;
217 }
218
219 void *
220 ao_lisp_alloc(int size)
221 {
222         void    *addr;
223
224         size = ao_lisp_mem_round(size);
225 #ifdef AO_LISP_MAKE_CONST
226         if (ao_lisp_top + size > AO_LISP_POOL_CONST) {
227                 fprintf(stderr, "Too much constant data, increase AO_LISP_POOL_CONST\n");
228                 exit(1);
229         }
230         addr = ao_lisp_const + ao_lisp_top;
231 #else
232         if (ao_lisp_top + size > AO_LISP_POOL) {
233                 collect();
234                 if (ao_lisp_top + size > AO_LISP_POOL) {
235                         ao_lisp_exception |= AO_LISP_OOM;
236                         return NULL;
237                 }
238         }
239         addr = ao_lisp_pool + ao_lisp_top;
240 #endif
241         ao_lisp_top += size;
242         return addr;
243 }
244
245 int
246 ao_lisp_root_add(const struct ao_lisp_type *type, void *addr)
247 {
248         int     i;
249         for (i = 0; i < AO_LISP_ROOT; i++) {
250                 if (!ao_lisp_root[i].addr) {
251                         ao_lisp_root[i].addr = addr;
252                         ao_lisp_root[i].type = type;
253                         return 1;
254                 }
255         }
256         return 0;
257 }
258
259 void
260 ao_lisp_root_clear(void *addr)
261 {
262         int     i;
263         for (i = 0; i < AO_LISP_ROOT; i++) {
264                 if (ao_lisp_root[i].addr == addr) {
265                         ao_lisp_root[i].addr = 0;
266                         ao_lisp_root[i].type = 0;
267                         break;
268                 }
269         }
270 }