GCC Code Coverage Report


Directory: ../src/
File: /home/joels/Current/lispbm/src/lbm_defrag_mem.c
Date: 2025-08-08 18:10:24
Exec Total Coverage
Lines: 126 128 98.4%
Functions: 8 8 100.0%
Branches: 44 51 86.3%

Line Branch Exec Source
1 /*
2 Copyright 2024, 2025 Joel Svensson svenssonjoel@yahoo.se
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 3 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,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <lbm_defrag_mem.h>
19 #include <heap.h>
20 #include <lbm_memory.h>
21
22 678078 static inline lbm_uint bs2ws(lbm_uint bs) {
23
2/2
✓ Branch 0 taken 648145 times.
✓ Branch 1 taken 29933 times.
678078 return bs % sizeof(lbm_uint) == 0 ? bs / sizeof(lbm_uint) : (bs / sizeof(lbm_uint)) + 1;
24 }
25
26 #define DEFRAG_MEM_HEADER_SIZE 2
27 #define DEFRAG_MEM_HEADER_BYTES (DEFRAG_MEM_HEADER_SIZE*sizeof(lbm_uint))
28 #define DEFRAG_MEM_DATA(X) &(X)[DEFRAG_MEM_HEADER_SIZE];
29 // length and flags.
30 // Currently only one flag that tells if we should do a compaction before allocation.
31 #define DEFRAG_MEM_SIZE(X) X[0]
32 #define DEFRAG_MEM_FLAGS(X) X[1]
33
34 // TODO: We can move the GC index to the end (or elsewhere) of an array to save space in these
35 // headers in the case of ByteArrays.
36 #define DEFRAG_ALLOC_SIZE(X) X[0]
37 #define DEFRAG_ALLOC_DATA(X) X[1]
38 //#define DEFRAG_ALLOC_INDEX(X) X[2] // GC index for traversal of high-level arrays
39 #define DEFRAG_ALLOC_CELLPTR(X) X[2]
40 #define DEFRAG_ALLOC_ARRAY_HEADER_SIZE 3
41
42 337 lbm_value lbm_defrag_mem_create(lbm_uint nbytes) {
43 337 lbm_value res = ENC_SYM_TERROR;
44 337 lbm_uint nwords = bs2ws(nbytes); // multiple of 4.
45
1/2
✓ Branch 0 taken 337 times.
✗ Branch 1 not taken.
337 if (nwords > 0) {
46 337 res = ENC_SYM_MERROR;
47 337 lbm_uint *data = (lbm_uint*)lbm_malloc(DEFRAG_MEM_HEADER_BYTES + nwords * sizeof(lbm_uint));
48
1/2
✓ Branch 0 taken 337 times.
✗ Branch 1 not taken.
337 if (data) {
49 337 memset((uint8_t*)data , 0, DEFRAG_MEM_HEADER_BYTES + nwords*sizeof(lbm_uint));
50 337 data[0] = nwords;
51 337 data[1] = 0; //flags
52 337 lbm_value cell = lbm_heap_allocate_cell(LBM_TYPE_DEFRAG_MEM, (lbm_uint)data, ENC_SYM_DEFRAG_MEM_TYPE);
53
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 337 times.
337 if (cell == ENC_SYM_MERROR) {
54 lbm_free(data);
55 } else {
56 337 res = cell;
57 }
58 }
59 }
60 337 return res;
61 }
62
63 196 static void free_defrag_allocation(lbm_uint *allocation) {
64 196 lbm_uint size = DEFRAG_ALLOC_SIZE(allocation); // array allocation is size in bytes
65 // a defrag-mem allocation is always bigger than 0
66 196 lbm_uint nwords = bs2ws(size) + DEFRAG_ALLOC_ARRAY_HEADER_SIZE;
67 196 lbm_value cell_back_ptr = DEFRAG_ALLOC_CELLPTR(allocation);
68
69 // I have a feeling that it should be impossible for the
70 // cell to be recovered if we end up here.
71 // if the cell is recovered, then the data should also have been
72 // cleared in the defrag_mem.
73
74 196 cell_back_ptr = lbm_set_ptr_type(cell_back_ptr, LBM_TYPE_CONS);
75 196 bool marked = lbm_cdr(cell_back_ptr) & LBM_GC_MASK;
76
2/2
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 28 times.
196 lbm_value new_cdr = marked ? (ENC_SYM_NIL | LBM_GC_MARKED) : ENC_SYM_NIL;
77 196 lbm_set_car_and_cdr(cell_back_ptr, ENC_SYM_NIL, new_cdr);
78 // possible optimize, if not marked. dont bother setting anything.
79
80
2/2
✓ Branch 0 taken 1064 times.
✓ Branch 1 taken 196 times.
1260 for (lbm_uint i = 0; i < nwords; i ++) {
81 1064 allocation[i] = 0;
82 }
83 196 }
84
85 // Called by GC.
86 // As it is called by GC, gc bits may be set and needs to be
87 // honored!
88 56 void lbm_defrag_mem_destroy(lbm_uint *defrag_mem) {
89 56 lbm_uint nwords = DEFRAG_MEM_SIZE(defrag_mem);
90 56 lbm_uint *defrag_data = DEFRAG_MEM_DATA(defrag_mem);
91
2/2
✓ Branch 0 taken 532 times.
✓ Branch 1 taken 56 times.
588 for (lbm_uint i = 0; i < nwords;) {
92 532 lbm_uint a = defrag_data[i];
93
2/2
✓ Branch 0 taken 196 times.
✓ Branch 1 taken 336 times.
532 if (a != 0) {
94 196 lbm_uint *allocation = &defrag_data[i];
95 196 lbm_uint alloc_words =
96 DEFRAG_ALLOC_ARRAY_HEADER_SIZE +
97 196 bs2ws(DEFRAG_ALLOC_SIZE(allocation));
98 196 free_defrag_allocation(allocation);
99 196 i += alloc_words;
100 }
101 336 else i ++;
102 }
103 56 lbm_free(defrag_mem);
104 56 }
105
106
107 57036 static void lbm_defrag_mem_defrag(lbm_uint *defrag_mem, lbm_uint bytes) {
108 57036 lbm_uint mem_size = ((lbm_uint*)defrag_mem)[0]; // mem size words
109 57036 lbm_uint *mem_data = DEFRAG_MEM_DATA(defrag_mem);
110 57036 lbm_uint hole_start = 0;
111
112 57036 lbm_uint until_size = DEFRAG_ALLOC_ARRAY_HEADER_SIZE + bs2ws(bytes); // defrag until hole is this size or complete defrag.
113
114
2/2
✓ Branch 0 taken 292208 times.
✓ Branch 1 taken 56952 times.
349160 for (lbm_uint i = 0; i < mem_size; ) {
115 // check if there is an allocation here
116
2/2
✓ Branch 0 taken 117488 times.
✓ Branch 1 taken 174720 times.
292208 if (mem_data[i] != 0) {
117 117488 lbm_uint *source = &mem_data[i];
118 117488 lbm_uint *target = &mem_data[hole_start];
119 117488 lbm_uint alloc_bytes = DEFRAG_ALLOC_SIZE(source);
120 117488 lbm_uint alloc_words = bs2ws(alloc_bytes);
121 // move allocation into hole
122
2/2
✓ Branch 0 taken 116984 times.
✓ Branch 1 taken 504 times.
117488 if (hole_start == i) {
123 116984 i += alloc_words + DEFRAG_ALLOC_ARRAY_HEADER_SIZE;
124 116984 hole_start = i;
125 } else {
126 504 lbm_uint move_dist = i - hole_start;
127
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 420 times.
504 if (move_dist >= until_size) break;
128 420 lbm_uint clear_ix = (hole_start + alloc_words + DEFRAG_ALLOC_ARRAY_HEADER_SIZE);
129 420 memmove(target, source, (alloc_words + DEFRAG_ALLOC_ARRAY_HEADER_SIZE) * sizeof(lbm_uint));
130 420 memset(&mem_data[clear_ix],0, move_dist* sizeof(lbm_uint));
131 420 DEFRAG_ALLOC_DATA(target) = (lbm_uint)&target[DEFRAG_ALLOC_ARRAY_HEADER_SIZE];
132 420 lbm_value cell = DEFRAG_ALLOC_CELLPTR(target);
133
134 420 lbm_set_car(cell,(lbm_uint)target);
135 // move home and i forwards.
136 // i can move to the original end of allocation.
137 420 hole_start += alloc_words + DEFRAG_ALLOC_ARRAY_HEADER_SIZE;
138 420 i += alloc_words + DEFRAG_ALLOC_ARRAY_HEADER_SIZE;
139 }
140 } else {
141 // no allocation hole remains but i increments.
142 174720 i ++;
143 }
144 }
145 57036 }
146
147 // Allocate an array from the defragable pool
148 // these arrays must be recognizable by GC so that
149 // gc can free them by performing a call into the defrag_mem api.
150 // At the same time they need to be just bytearrays..
151 // Allocation must always scan from start.
152 //
153
154 #define INIT 0
155 #define FREE_LEN 1
156
157 // An array allocated in defragmem has the following layout inside of the defrag mem
158 //
159 // [header (size, data-ptr) cell_back_ptr | data | padding ]
160
161 173209 lbm_value lbm_defrag_mem_alloc_internal(lbm_uint *defrag_mem, lbm_uint bytes, lbm_uint type) {
162
163
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 173209 times.
173209 if (bytes == 0) return ENC_SYM_EERROR;
164 173209 lbm_value cell = lbm_heap_allocate_cell(LBM_TYPE_CONS, ENC_SYM_NIL, type);
165
166
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 173209 times.
173209 if (cell == ENC_SYM_MERROR) {
167 return cell;
168 }
169
170 173209 lbm_uint mem_size = DEFRAG_MEM_SIZE(defrag_mem);
171 173209 lbm_uint *mem_data = DEFRAG_MEM_DATA(defrag_mem);
172
173 173209 lbm_uint num_words = bs2ws(bytes);
174 173209 lbm_uint alloc_words = num_words + DEFRAG_ALLOC_ARRAY_HEADER_SIZE;
175
176 173209 uint8_t state = INIT;
177 173209 lbm_uint free_words = 0;
178 173209 lbm_uint free_start = 0;
179 173209 bool alloc_found = false;
180 173209 lbm_value res = ENC_SYM_MERROR;
181
182
2/2
✓ Branch 0 taken 1229479 times.
✓ Branch 1 taken 113904 times.
1343383 for (lbm_uint i = 0; i < mem_size;) {
183
2/3
✓ Branch 0 taken 388501 times.
✓ Branch 1 taken 840978 times.
✗ Branch 2 not taken.
1229479 switch(state) {
184 388501 case INIT:
185
2/2
✓ Branch 0 taken 116873 times.
✓ Branch 1 taken 271628 times.
388501 if (mem_data[i] == 0) {
186 116873 free_start = i;
187 116873 free_words = 1;
188 116873 state = FREE_LEN;
189 116873 i++;
190 } else {
191 // jump to next spot
192 271628 i += bs2ws(mem_data[i]) + DEFRAG_ALLOC_ARRAY_HEADER_SIZE;
193 }
194 388501 break;
195 840978 case FREE_LEN:
196
2/2
✓ Branch 0 taken 840754 times.
✓ Branch 1 taken 224 times.
840978 if (mem_data[i] == 0) {
197 840754 free_words ++;
198
2/2
✓ Branch 0 taken 59305 times.
✓ Branch 1 taken 781449 times.
840754 if (free_words >= alloc_words) {
199 59305 alloc_found = true;
200 } else {
201 781449 i ++;
202 }
203 } else {
204 224 state = INIT;
205 224 i++;
206 }
207 840978 break;
208 }
209
2/2
✓ Branch 0 taken 59305 times.
✓ Branch 1 taken 1170174 times.
1229479 if (alloc_found) break;
210 }
211
2/2
✓ Branch 0 taken 59305 times.
✓ Branch 1 taken 113904 times.
173209 if (alloc_found) {
212 59305 lbm_uint *allocation = (lbm_uint*)&mem_data[free_start];
213 59305 DEFRAG_ALLOC_SIZE(allocation) = bytes;
214 59305 DEFRAG_ALLOC_DATA(allocation) = (lbm_uint)&allocation[DEFRAG_ALLOC_ARRAY_HEADER_SIZE]; //data starts after back_ptr
215 59305 DEFRAG_ALLOC_CELLPTR(allocation) = cell;
216 59305 lbm_set_car(cell, (lbm_uint)allocation);
217 59305 res = cell;
218 } else {
219 113904 DEFRAG_MEM_FLAGS(defrag_mem) = 1;
220 113904 lbm_set_car_and_cdr(cell, ENC_SYM_NIL, ENC_SYM_NIL);
221 }
222 173209 return res;
223 }
224
225 116173 lbm_value lbm_defrag_mem_alloc(lbm_uint *defrag_mem, lbm_uint bytes) {
226
227 116173 lbm_value res = lbm_defrag_mem_alloc_internal(defrag_mem, bytes, ENC_SYM_DEFRAG_ARRAY_TYPE); // Try to allocate
228
2/2
✓ Branch 0 taken 57036 times.
✓ Branch 1 taken 59137 times.
116173 if (lbm_is_symbol_merror(res)) {
229
1/2
✓ Branch 0 taken 57036 times.
✗ Branch 1 not taken.
57036 if (DEFRAG_MEM_FLAGS(defrag_mem)) { //if we already performed GC, then also defrag
230 57036 lbm_defrag_mem_defrag(defrag_mem, bytes);
231 57036 res = lbm_defrag_mem_alloc_internal(defrag_mem, bytes, ENC_SYM_DEFRAG_ARRAY_TYPE); // then try again
232 57036 DEFRAG_MEM_FLAGS(defrag_mem) = 0;
233 }
234 }
235
2/2
✓ Branch 0 taken 59305 times.
✓ Branch 1 taken 56868 times.
116173 if (!lbm_is_symbol_merror(res)) {
236 59305 res = lbm_set_ptr_type(res, LBM_TYPE_ARRAY);
237 }
238 116173 return res;
239 }
240
241 // Allocate a high-level array from DM area
242 /* lbm_value lbm_defrag_mem_alloc_lisparray(lbm_uint *defrag_mem, lbm_uint elts) { */
243
244 /* size_t bytes = elts * sizeof(lbm_uint); */
245 /* lbm_value res = lbm_defrag_mem_alloc_internal(defrag_mem, bytes, ENC_SYM_DEFRAG_LISPARRAY_TYPE); // Try to allocate */
246 /* if (lbm_is_symbol_merror(res)) { */
247 /* if (DEFRAG_MEM_FLAGS(defrag_mem)) { //if we already performed GC, then also defrag */
248 /* lbm_defrag_mem_defrag(defrag_mem, bytes); */
249 /* res = lbm_defrag_mem_alloc_internal(defrag_mem, bytes, ENC_SYM_DEFRAG_LISPARRAY_TYPE); // then try again */
250 /* DEFRAG_MEM_FLAGS(defrag_mem) = 0; */
251 /* } */
252 /* } */
253 /* if (!lbm_is_symbol_merror(res)) { */
254 /* res = lbm_set_ptr_type(res, LBM_TYPE_LISPARRAY); */
255 /* } */
256 /* return res; */
257 /* } */
258
259
260
261 // IF GC frees a defrag allocation, the cell pointing to it will also be destroyed by GC.
262
263 // is it guaranteed there are no copies of a heap-cell representing a defrag allocation.
264 // Same guarantee must hold for arrays in general or it would not be possible to explicitly free
265 // any array.
266
267 // At the time of free from GC all we have is the pointer.
268 57988 void lbm_defrag_mem_free(lbm_uint* data) {
269 57988 lbm_uint nbytes = data[0];
270 57988 lbm_uint words_to_wipe = DEFRAG_ALLOC_ARRAY_HEADER_SIZE + bs2ws(nbytes);
271
2/2
✓ Branch 0 taken 598948 times.
✓ Branch 1 taken 57988 times.
656936 for (lbm_uint i = 0; i < words_to_wipe; i ++) {
272 598948 data[i] = 0;
273 }
274 57988 }
275
276