GCC Code Coverage Report


Directory: ../src/
File: /home/joels/Current/lispbm/src/lbm_defrag_mem.c
Date: 2025-10-28 15:15:18
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 1017522 static inline lbm_uint bs2ws(lbm_uint bs) {
23
2/2
✓ Branch 0 taken 974261 times.
✓ Branch 1 taken 43261 times.
1017522 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 505 lbm_value lbm_defrag_mem_create(lbm_uint nbytes) {
43 505 lbm_value res = ENC_SYM_TERROR;
44 505 lbm_uint nwords = bs2ws(nbytes); // multiple of 4.
45
1/2
✓ Branch 0 taken 505 times.
✗ Branch 1 not taken.
505 if (nwords > 0) {
46 505 res = ENC_SYM_MERROR;
47 505 lbm_uint *data = (lbm_uint*)lbm_malloc(DEFRAG_MEM_HEADER_BYTES + nwords * sizeof(lbm_uint));
48
1/2
✓ Branch 0 taken 505 times.
✗ Branch 1 not taken.
505 if (data) {
49 505 memset((uint8_t*)data , 0, DEFRAG_MEM_HEADER_BYTES + nwords*sizeof(lbm_uint));
50 505 data[0] = nwords;
51 505 data[1] = 0; //flags
52 505 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 505 times.
505 if (cell == ENC_SYM_MERROR) {
54 lbm_free(data);
55 } else {
56 505 res = cell;
57 }
58 }
59 }
60 505 return res;
61 }
62
63 280 static void free_defrag_allocation(lbm_uint *allocation) {
64 280 lbm_uint size = DEFRAG_ALLOC_SIZE(allocation); // array allocation is size in bytes
65 // a defrag-mem allocation is always bigger than 0
66 280 lbm_uint nwords = bs2ws(size) + DEFRAG_ALLOC_ARRAY_HEADER_SIZE;
67 280 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 280 cell_back_ptr = lbm_set_ptr_type(cell_back_ptr, LBM_TYPE_CONS);
75 280 bool marked = lbm_cdr(cell_back_ptr) & LBM_GC_MASK;
76
2/2
✓ Branch 0 taken 252 times.
✓ Branch 1 taken 28 times.
280 lbm_value new_cdr = marked ? (ENC_SYM_NIL | LBM_GC_MARKED) : ENC_SYM_NIL;
77 280 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 1568 times.
✓ Branch 1 taken 280 times.
1848 for (lbm_uint i = 0; i < nwords; i ++) {
81 1568 allocation[i] = 0;
82 }
83 280 }
84
85 // Called by GC.
86 // As it is called by GC, gc bits may be set and needs to be
87 // honored!
88 84 void lbm_defrag_mem_destroy(lbm_uint *defrag_mem) {
89 84 lbm_uint nwords = DEFRAG_MEM_SIZE(defrag_mem);
90 84 lbm_uint *defrag_data = DEFRAG_MEM_DATA(defrag_mem);
91
2/2
✓ Branch 0 taken 812 times.
✓ Branch 1 taken 84 times.
896 for (lbm_uint i = 0; i < nwords;) {
92 812 lbm_uint a = defrag_data[i];
93
2/2
✓ Branch 0 taken 280 times.
✓ Branch 1 taken 532 times.
812 if (a != 0) {
94 280 lbm_uint *allocation = &defrag_data[i];
95 280 lbm_uint alloc_words =
96 DEFRAG_ALLOC_ARRAY_HEADER_SIZE +
97 280 bs2ws(DEFRAG_ALLOC_SIZE(allocation));
98 280 free_defrag_allocation(allocation);
99 280 i += alloc_words;
100 }
101 532 else i ++;
102 }
103 84 lbm_free(defrag_mem);
104 84 }
105
106
107 85624 static void lbm_defrag_mem_defrag(lbm_uint *defrag_mem, lbm_uint bytes) {
108 85624 lbm_uint mem_size = ((lbm_uint*)defrag_mem)[0]; // mem size words
109 85624 lbm_uint *mem_data = DEFRAG_MEM_DATA(defrag_mem);
110 85624 lbm_uint hole_start = 0;
111
112 85624 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 522900 times.
✓ Branch 1 taken 85512 times.
608412 for (lbm_uint i = 0; i < mem_size; ) {
115 // check if there is an allocation here
116
2/2
✓ Branch 0 taken 176260 times.
✓ Branch 1 taken 346640 times.
522900 if (mem_data[i] != 0) {
117 176260 lbm_uint *source = &mem_data[i];
118 176260 lbm_uint *target = &mem_data[hole_start];
119 176260 lbm_uint alloc_bytes = DEFRAG_ALLOC_SIZE(source);
120 176260 lbm_uint alloc_words = bs2ws(alloc_bytes);
121 // move allocation into hole
122
2/2
✓ Branch 0 taken 175532 times.
✓ Branch 1 taken 728 times.
176260 if (hole_start == i) {
123 175532 i += alloc_words + DEFRAG_ALLOC_ARRAY_HEADER_SIZE;
124 175532 hole_start = i;
125 } else {
126 728 lbm_uint move_dist = i - hole_start;
127
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 616 times.
728 if (move_dist >= until_size) break;
128 616 lbm_uint clear_ix = (hole_start + alloc_words + DEFRAG_ALLOC_ARRAY_HEADER_SIZE);
129 616 memmove(target, source, (alloc_words + DEFRAG_ALLOC_ARRAY_HEADER_SIZE) * sizeof(lbm_uint));
130 616 memset(&mem_data[clear_ix],0, move_dist* sizeof(lbm_uint));
131 616 DEFRAG_ALLOC_DATA(target) = (lbm_uint)&target[DEFRAG_ALLOC_ARRAY_HEADER_SIZE];
132 616 lbm_value cell = DEFRAG_ALLOC_CELLPTR(target);
133
134 616 lbm_set_car(cell,(lbm_uint)target);
135 // move home and i forwards.
136 // i can move to the original end of allocation.
137 616 hole_start += alloc_words + DEFRAG_ALLOC_ARRAY_HEADER_SIZE;
138 616 i += alloc_words + DEFRAG_ALLOC_ARRAY_HEADER_SIZE;
139 }
140 } else {
141 // no allocation hole remains but i increments.
142 346640 i ++;
143 }
144 }
145 85624 }
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 259981 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 259981 times.
259981 if (bytes == 0) return ENC_SYM_EERROR;
164 259981 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 259981 times.
259981 if (cell == ENC_SYM_MERROR) {
167 return cell;
168 }
169
170 259981 lbm_uint mem_size = DEFRAG_MEM_SIZE(defrag_mem);
171 259981 lbm_uint *mem_data = DEFRAG_MEM_DATA(defrag_mem);
172
173 259981 lbm_uint num_words = bs2ws(bytes);
174 259981 lbm_uint alloc_words = num_words + DEFRAG_ALLOC_ARRAY_HEADER_SIZE;
175
176 259981 uint8_t state = INIT;
177 259981 lbm_uint free_words = 0;
178 259981 lbm_uint free_start = 0;
179 259981 bool alloc_found = false;
180 259981 lbm_value res = ENC_SYM_MERROR;
181
182
2/2
✓ Branch 0 taken 2085887 times.
✓ Branch 1 taken 171024 times.
2256911 for (lbm_uint i = 0; i < mem_size;) {
183
2/3
✓ Branch 0 taken 611241 times.
✓ Branch 1 taken 1474646 times.
✗ Branch 2 not taken.
2085887 switch(state) {
184 611241 case INIT:
185
2/2
✓ Branch 0 taken 203645 times.
✓ Branch 1 taken 407596 times.
611241 if (mem_data[i] == 0) {
186 203645 free_start = i;
187 203645 free_words = 1;
188 203645 state = FREE_LEN;
189 203645 i++;
190 } else {
191 // jump to next spot
192 407596 i += bs2ws(mem_data[i]) + DEFRAG_ALLOC_ARRAY_HEADER_SIZE;
193 }
194 611241 break;
195 1474646 case FREE_LEN:
196
2/2
✓ Branch 0 taken 1474338 times.
✓ Branch 1 taken 308 times.
1474646 if (mem_data[i] == 0) {
197 1474338 free_words ++;
198
2/2
✓ Branch 0 taken 88957 times.
✓ Branch 1 taken 1385381 times.
1474338 if (free_words >= alloc_words) {
199 88957 alloc_found = true;
200 } else {
201 1385381 i ++;
202 }
203 } else {
204 308 state = INIT;
205 308 i++;
206 }
207 1474646 break;
208 }
209
2/2
✓ Branch 0 taken 88957 times.
✓ Branch 1 taken 1996930 times.
2085887 if (alloc_found) break;
210 }
211
2/2
✓ Branch 0 taken 88957 times.
✓ Branch 1 taken 171024 times.
259981 if (alloc_found) {
212 88957 lbm_uint *allocation = (lbm_uint*)&mem_data[free_start];
213 88957 DEFRAG_ALLOC_SIZE(allocation) = bytes;
214 88957 DEFRAG_ALLOC_DATA(allocation) = (lbm_uint)&allocation[DEFRAG_ALLOC_ARRAY_HEADER_SIZE]; //data starts after back_ptr
215 88957 DEFRAG_ALLOC_CELLPTR(allocation) = cell;
216 88957 lbm_set_car(cell, (lbm_uint)allocation);
217 88957 res = cell;
218 } else {
219 171024 DEFRAG_MEM_FLAGS(defrag_mem) = 1;
220 171024 lbm_set_car_and_cdr(cell, ENC_SYM_NIL, ENC_SYM_NIL);
221 }
222 259981 return res;
223 }
224
225 174357 lbm_value lbm_defrag_mem_alloc(lbm_uint *defrag_mem, lbm_uint bytes) {
226
227 174357 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 85624 times.
✓ Branch 1 taken 88733 times.
174357 if (lbm_is_symbol_merror(res)) {
229
1/2
✓ Branch 0 taken 85624 times.
✗ Branch 1 not taken.
85624 if (DEFRAG_MEM_FLAGS(defrag_mem)) { //if we already performed GC, then also defrag
230 85624 lbm_defrag_mem_defrag(defrag_mem, bytes);
231 85624 res = lbm_defrag_mem_alloc_internal(defrag_mem, bytes, ENC_SYM_DEFRAG_ARRAY_TYPE); // then try again
232 85624 DEFRAG_MEM_FLAGS(defrag_mem) = 0;
233 }
234 }
235
2/2
✓ Branch 0 taken 88957 times.
✓ Branch 1 taken 85400 times.
174357 if (!lbm_is_symbol_merror(res)) {
236 88957 res = lbm_set_ptr_type(res, LBM_TYPE_ARRAY);
237 }
238 174357 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 86996 void lbm_defrag_mem_free(lbm_uint* data) {
269 86996 lbm_uint nbytes = data[0];
270 86996 lbm_uint words_to_wipe = DEFRAG_ALLOC_ARRAY_HEADER_SIZE + bs2ws(nbytes);
271
2/2
✓ Branch 0 taken 969024 times.
✓ Branch 1 taken 86996 times.
1056020 for (lbm_uint i = 0; i < words_to_wipe; i ++) {
272 969024 data[i] = 0;
273 }
274 86996 }
275
276