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