d008519b2ccf56c0c30074fb030f4e6d30c5eb76
[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         /* Mark */
124         memset(ao_lisp_busy, '\0', sizeof (ao_lisp_busy));
125         for (i = 0; i < AO_LISP_ROOT; i++)
126                 if (ao_lisp_root[i].addr)
127                         ao_lisp_mark(ao_lisp_root[i].type, *ao_lisp_root[i].addr);
128
129         /* Compact */
130         ao_lisp_top = 0;
131         for (i = 0; i < AO_LISP_POOL; i += 4) {
132                 if (!busy(ao_lisp_busy, i))
133                         break;
134         }
135         ao_lisp_top = i;
136         while(i < AO_LISP_POOL) {
137                 if (busy(ao_lisp_busy, i)) {
138                         move_old = &ao_lisp_pool[i];
139                         move_new = &ao_lisp_pool[ao_lisp_top];
140                         move_size = 0;
141                         move_object();
142                         clear_object(ao_lisp_busy, move_old, move_size);
143                         i += move_size;
144                         ao_lisp_top += move_size;
145                 } else {
146                         i += 4;
147                 }
148         }
149 }
150
151
152 void
153 ao_lisp_mark(const struct ao_lisp_mem_type *type, void *addr)
154 {
155         if (mark_object(ao_lisp_busy, addr, type->size(addr)))
156                 return;
157         type->mark(addr);
158 }
159
160 int
161 ao_lisp_mark_memory(void *addr, int size)
162 {
163         return mark_object(ao_lisp_busy, addr, size);
164 }
165
166 static void *
167 check_move(void *addr, int size)
168 {
169         if (addr == move_old) {
170                 memmove(move_new, move_old, size);
171                 move_size = (size + 3) & ~3;
172                 addr = move_new;
173         }
174         return addr;
175 }
176
177 void *
178 ao_lisp_move(const struct ao_lisp_mem_type *type, void *addr)
179 {
180         int     size = type->size(addr);
181
182         if (!addr)
183                 return NULL;
184
185         addr = check_move(addr, size);
186         if (mark_object(ao_lisp_moving, addr, size))
187                 return addr;
188         type->move(addr);
189         return addr;
190 }
191
192 void *
193 ao_lisp_move_memory(void *addr, int size)
194 {
195         if (!addr)
196                 return NULL;
197
198         addr = check_move(addr, size);
199         if (mark_object(ao_lisp_moving, addr, size))
200                 return NULL;
201         return addr;
202 }
203
204 void *
205 ao_lisp_alloc(int size)
206 {
207         void    *addr;
208
209         size = (size + 3) & ~3;
210         if (ao_lisp_top + size > AO_LISP_POOL) {
211                 collect();
212                 if (ao_lisp_top + size > AO_LISP_POOL)
213                         return NULL;
214         }
215         addr = ao_lisp_pool + ao_lisp_top;
216         ao_lisp_top += size;
217         return addr;
218 }
219
220 int
221 ao_lisp_root_add(const struct ao_lisp_mem_type *type, void *addr)
222 {
223         int     i;
224         for (i = 0; i < AO_LISP_ROOT; i++) {
225                 if (!ao_lisp_root[i].addr) {
226                         ao_lisp_root[i].addr = addr;
227                         ao_lisp_root[i].type = type;
228                         return 1;
229                 }
230         }
231         return 0;
232 }
233
234 void
235 ao_lisp_root_clear(void *addr)
236 {
237         int     i;
238         for (i = 0; i < AO_LISP_ROOT; i++) {
239                 if (ao_lisp_root[i].addr == addr) {
240                         ao_lisp_root[i].addr = 0;
241                         ao_lisp_root[i].type = 0;
242                         break;
243                 }
244         }
245 }