GCC Code Coverage Report


Directory: ../src/
File: /home/joels/Current/lispbm/src/lbm_image.c
Date: 2025-08-08 18:10:24
Exec Total Coverage
Lines: 527 593 88.9%
Functions: 47 48 97.9%
Branches: 258 466 55.4%

Line Branch Exec Source
1 /*
2 Copyright 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 <extensions.h>
19 #include <lbm_image.h>
20 #include <heap.h>
21 #include <env.h>
22 #include <lbm_flat_value.h>
23 #include <eval_cps.h>
24 #include <extensions.h>
25
26 // Assumptions about the image memory:
27 // * It is part of the address space.
28 // * Image is always available at the same address (across reboots)
29 // * It is a write-once memory.
30 // * Can be cleared in its entirety.
31 // * Can check on a byte-level is "is-writable" (has a known initial state when cleared)
32
33 // Details
34 // * @const_start @const_end is tricky.
35 // * Constant heap is needed because of small amount of RAM.
36 // * Arbitrary pointers will be tricky.
37
38 // Want to be able to build an image incrementally.
39 // Can @const_start @const_end be used in conjuction with a
40 // more exlicit image manipulation subsystem.
41
42 //TBD:
43 // * does an image contain running threads ? (I would prefer if no)
44 // instead there is a startup-entry that represents some code that will
45 // be executed after setting up an image.
46 // This startup-entry can spawn threads and initialize resources.
47 // * Will we have a heap-image or will all bindings move into the const heap.
48
49 // FEB 26:
50 // -- There will be no "heap-image" or "memory-image"
51 // Just flattened and stored bindings from the environment (which is as good but likely smaller).
52 // -- There will be an image of the const-heap. in fact the const heap lives in the image always.
53 // A bit problematic with building on image incrementally as it is in flash and the contents cannot be changed.
54 // Contents can be added though! (keep track of const-heap write-ptr is an issue)
55 // -- Maybe we should implement image for a read-write memory and then make the flash situation a special case?
56 // -- Maybe size fields should always be in bytes.
57 // -- TODO: a flatten function that flattens a value directly to flash and also does not flatten things that are
58 // already in flash, but rather then just refers to them.
59
60 // FEB 27:
61 // -- Symbol numbering will be an issue.
62 // * Store the symboltable in the image and restore it on boot.
63 // * Names already in flash can be refered to.
64 // * Names in ram can be copied.
65 // * Entire subtable may already be in flash - leave in place and refer to it.
66 // -- loading an image and then adding to it can be tricky to make possible.
67 // * const-heap write pointer needs to be stored. (but if it is stored then it cannot be changed)
68 // Could allow multiple "const-heap-write-pointer" fields in the image and use the one that appears last...
69 // * Symboltable could be created incrementally in a similar way. Append later symbol_table data fields
70 // to the previously loaded.
71
72 // FEB 28:
73 // -- Symbol numbering problem. The structure of the symboltable may
74 // need to change. It is currently impossible to append a symbol list stored
75 // in flash to the global symbol table. A richer structure is needed.
76 // -- The symbols in the SYMTAB can all be in flash (all in the image) and
77 // all name strings can be written to flash as well.
78 // * May be easiest if names go to the flash heap (as it is now)
79 // and table entries are a tagged field in the image (1 extra byte per symbol...)
80 // * Means the image must be initialized (to a degree) before symbols are created.
81
82 // MARCH 1:
83 // -- Symbols are added to and restored from the image.
84 // -- lbm_add_symbol_const, still creates a lbm_memory list structure.
85 // Const symbols should also be stored into the image and add_symbol_const
86 // should check and reuse stored symbol id.
87 // Check order of initialization to see how easy this is to fix.
88
89 // Offline image tools
90 // - Image compaction: remove overwrite fields and compact the image.
91 // - Change of base address: relabel all memory accesses.
92 // - ...
93
94
95 // MARCH 5
96 // -- Can constants (anything on const heap) contain references into non-constant heap?
97 // - I think not, but should verify this.
98 // - eval_cps move_to_flash performs a deep copy into flash.
99
100 // Endianess woes...
101 // - little endian least-significant byte at least address
102 // - big endian most-significant byte at least address
103 // - all platforms we target currently are little-endian
104 //
105 // 0x11223344
106 // | | | '--- [44] addr
107 // | | '----- [33] addr + 1
108 // | '------- [22] addr + 2
109 // '--------- [11] addr + 3
110 //
111 // Images are going to be mainly little endian. (what endianess does flatvalues use? I think BE)
112
113 // constant heap should be 4byte aligned so that there are 2 unused low end bits
114 // in all cell-pointers into constant heap.
115
116 // March 8
117 // -- flattening lead to duplication of shared nodes.
118 // if a = '(1 2 3) and b = (cons 4 a) and c = (cons 5 a)
119 // then the result of flattening a b c each contains a full copy of a.
120 // -- flattening a value that in turn points to a constant value, duplicates
121 // the constant value.
122
123 // TODO: Put more info into the IMAGE_INITIALIZED FIELD
124 // - 32/64 bit etc
125
126 // Sharing recovery:
127 //
128 // Construct a mapping of cons-cell address to key and flat value offset
129 // | Address | KEY | Offset |
130 // | addr0 | k0 | offs0 |
131 //
132 // Flat value needs a new reference values. Potentially one new for each kind of cons-cell (cons, byte-array etc)
133 // so that correct pointer can be created.
134 // [ REF_X | addr0 ]
135 //
136
137 // unflatten will create the column "new address"
138 // When unflattening k0 at offset offs0, fill in the new address field with the cons-cell address created.
139 // | Address | KEY | Offset | new address |
140 // | addr0 | k1 | offs0 | newaddr0 |
141
142 // Unflattening needs to check:
143 // - for each cell type thing, if it is at an offset that is shared.
144 // If it is, we need to fill in the "new address" field.
145 // - A shared node will only be created once! but there may be many REF_X pointing to it.
146 // - When unflattening a REF_X field, search through the mapping for a match on the "address" field.
147 // - Ordering is needed so that unflattening of a ref to X happens only after X has been assigned a
148 // a new address.
149 // Cost: search through the mapping for each unflattened cons-cell (including array etc).
150 // - Not all keys will exist in the mapping. Can check once and then not perform per lookup check.
151
152 // Flattening:
153 // - phase 0: find sharing and create collect the set of shared addresses into a table:
154 // | addr | k | empty |
155 // - phase 1: flatten, while flattening a cell see if it is at an address that exists in the table.
156 // if the address is in the table,
157 // see if offset = empty => set offset to current pos in flat value and flatten value as usual.
158 // see if offset = offs => create a REFX: to addr
159
160 // The key field is needed so that one knows when unflattening that a node is shared and the
161 // new address should be added to the table at key , offset.
162 // Possibly flattening a shared node could be a special field in the flat value.
163 // the flat value could hold [ Shared node | orig_address | flat_val ].
164 // Then no key field is needed in the sharing mapping table.
165
166 #ifdef LBM64
167 #define IMAGE_INITIALIZED (uint32_t)0xBEEF4001 // [ 0xBEEF4001 ]
168 #else
169 #define IMAGE_INITIALIZED (uint32_t)0xBEEF2001 // [ 0xBEEF2001 ]
170 #endif
171 // Address downwards ->
172 #define CONSTANT_HEAP_IX (uint32_t)0x02 // [ 0x02 | uint32]
173 #define BINDING_CONST (uint32_t)0x03 // [ 0x03 | key | lbm_uint ]
174 #define BINDING_FLAT (uint32_t)0x04 // [ 0x04 | size | key | flatval ]
175 #define SYMBOL_ENTRY (uint32_t)0x06 // [ 0x06 | NEXT_PTR | ID | NAME PTR ] // symbol_entry with highest address is root.
176 #define SYMBOL_LINK_ENTRY (uint32_t)0x07 // [ 0x07 | C_LINK_PTR | NEXT_PTR | ID | NAME PTR ]
177 #define EXTENSION_TABLE (uint32_t)0x08 // [ 0x08 | NUM | EXT ...]
178 #define VERSION_ENTRY (uint32_t)0x09 // [ 0x09 | size | string ]
179 #define SHARING_TABLE (uint32_t)0x10 // [ 0x10 | n | n-entries}
180 // Size is in number of 32bit words, even on 64 bit images.
181
182 // To be able to work on an image incrementally (even though it is not recommended)
183 // many fields are allowed to be duplicated and the later ones have priority
184 // over earlier ones.
185
186
187 #define DOWNWARDS true
188 #define UPWARDS false
189
190 static lbm_image_write_fun image_write = NULL;
191
192 static uint32_t *image_address = NULL;
193 static int32_t write_index = 0;
194 static uint32_t image_size = 0;
195 static bool image_has_extensions = false;
196 static char* image_version = NULL;
197
198 35 uint32_t *lbm_image_get_image(void) {
199 35 return image_address;
200 }
201
202 35 uint32_t lbm_image_get_size(void) {
203 35 return image_size;
204 }
205
206 346782 int32_t lbm_image_get_write_index(void) {
207 346782 return write_index;
208 }
209
210 223 bool lbm_image_has_extensions(void) {
211 223 return image_has_extensions;
212 }
213
214 213299 uint32_t read_u32(int32_t index) {
215 213299 return *((uint32_t*)(image_address + index));
216 }
217
218 uint64_t read_u64(int32_t index) {
219 // image_addres is an u32 ptr. so addr + i is a step of i * 4 bytes
220 return *((uint64_t*)(image_address + index));
221 }
222
223 8373724 bool write_u32(uint32_t w, int32_t *i, bool direction) {
224 8373724 bool r = image_write(w, *i, false);
225
2/2
✓ Branch 0 taken 8019416 times.
✓ Branch 1 taken 354308 times.
8373724 (*i) += direction ? -1 : 1;
226 8373724 return r;
227 }
228
229 2209562 bool write_u64(uint64_t dw, int32_t *i, bool direction) {
230 2209562 uint32_t *words = (uint32_t*)&dw;
231
232 // downwards ... hw lw
233 // ix ix-1
234 // upwards hw lw ...
235 // ix+1 ix
236
237 // true = downwards
238
239 2209562 bool r = true;
240
1/2
✓ Branch 0 taken 2209562 times.
✗ Branch 1 not taken.
2209562 if (direction) {
241
2/4
✓ Branch 0 taken 2209562 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2209562 times.
✗ Branch 3 not taken.
2209562 r = r && write_u32(words[1], i, direction);
242
2/4
✓ Branch 0 taken 2209562 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2209562 times.
✗ Branch 3 not taken.
2209562 r = r && write_u32(words[0], i, direction);
243 } else {
244 r = r && write_u32(words[0], i, direction);
245 r = r && write_u32(words[1], i, direction);
246 }
247 2209562 return r;
248 }
249
250 // fv_write function write values as big endian.
251
252 uint32_t fv_buf_ix = 0;
253 uint8_t fv_buf[4] = {0};
254 24658 bool fv_write_u8(uint8_t b) {
255 24658 bool r = true;
256
2/2
✓ Branch 0 taken 6090 times.
✓ Branch 1 taken 18568 times.
24658 if (fv_buf_ix >= 4) {
257 6090 r = write_u32(((uint32_t*)fv_buf)[0], &write_index, UPWARDS);
258 6090 memset(fv_buf,0,4);
259 6090 fv_buf_ix = 0;
260 }
261 24658 fv_buf[fv_buf_ix] = b;
262 24658 fv_buf_ix++;
263 24658 return r;
264 }
265
266 152 bool fv_write_flush(void) {
267
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 152 times.
152 if (fv_buf_ix == 0) return true;
268 else {
269 152 bool r = write_u32(((uint32_t*)fv_buf)[0], &write_index, UPWARDS);;
270 152 fv_buf_ix = 0;
271 152 memset(fv_buf,0,4);
272 152 return r;
273 }
274 }
275
276 4010 bool fv_write_u32(uint32_t w) {
277 4010 uint8_t * bytes = (uint8_t*)&w;
278 return
279
1/2
✓ Branch 0 taken 4010 times.
✗ Branch 1 not taken.
8020 fv_write_u8(bytes[3]) &&
280
1/2
✓ Branch 0 taken 4010 times.
✗ Branch 1 not taken.
8020 fv_write_u8(bytes[2]) &&
281
2/4
✓ Branch 0 taken 4010 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4010 times.
✗ Branch 3 not taken.
12030 fv_write_u8(bytes[1]) &&
282 4010 fv_write_u8(bytes[0]);
283 }
284
285 6 bool fv_write_u64(uint64_t dw) {
286 6 uint8_t * bytes = (uint8_t*)&dw;
287 return
288
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
12 fv_write_u8(bytes[7]) &&
289
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
12 fv_write_u8(bytes[6]) &&
290
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
12 fv_write_u8(bytes[5]) &&
291
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
12 fv_write_u8(bytes[4]) &&
292
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
12 fv_write_u8(bytes[3]) &&
293
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
12 fv_write_u8(bytes[2]) &&
294
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
18 fv_write_u8(bytes[1]) &&
295 6 fv_write_u8(bytes[0]);
296 }
297
298
299 4471636 bool write_lbm_uint(lbm_uint ptr_val, int32_t *i, bool direction) {
300 #ifdef LBM64
301 2209562 return write_u64(ptr_val, i, direction);
302 #else
303 2262074 return write_u32(ptr_val, i, direction);
304 #endif
305 }
306
307 170 bool write_lbm_value(lbm_value v, int32_t *i, bool direction) {
308 #ifdef LBM64
309 return write_u64(v, i, direction);
310 #else
311 170 return write_u32(v, i, direction);
312 #endif
313 }
314
315 // ////////////////////////////////////////////////////////////
316 // Flatten a value into image
317
318 // TODO: Consants things that are stored in the image
319 // does not need to be flattened. Could refer to these by
320 // reference. Some new kinds of flat values needs to be added
321 // for this referencing to work.
322
323 // TODO: Symbols in a flat_value in an image can be stored as
324 // its numerical representation rather than its string rep.
325
326 3736 static bool i_f_cons(void ) {
327 3736 return fv_write_u8(S_CONS);
328 }
329
330 13 static bool i_f_lisp_array(uint32_t size) {
331 // arrays are smaller than 2^32 elements long
332 13 bool r = fv_write_u8(S_LBM_LISP_ARRAY);
333
2/4
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
✗ Branch 3 not taken.
13 r = r && fv_write_u32(size);
334 13 return r;
335 }
336
337 2201 static bool i_f_sym(lbm_value sym) {
338 2201 lbm_uint sym_id = lbm_dec_sym(sym);
339 2201 bool r = fv_write_u8(S_SYM_VALUE);
340 #ifndef LBM64
341
2/4
✓ Branch 0 taken 2201 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2201 times.
✗ Branch 3 not taken.
2201 r = r && fv_write_u32(sym_id);
342 #else
343 r = r && fv_write_u64(sym_id);
344 #endif
345 2201 return r;
346 }
347
348 1663 static bool i_f_i(lbm_int i) {
349 1663 bool res = true;
350 #ifndef LBM64
351
2/4
✓ Branch 0 taken 1663 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1663 times.
✗ Branch 3 not taken.
1663 res = res && fv_write_u8(S_I28_VALUE);
352
2/4
✓ Branch 0 taken 1663 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1663 times.
✗ Branch 3 not taken.
1663 res = res && fv_write_u32((uint32_t)i);
353 #else
354 res = res && fv_write_u8(S_I56_VALUE);
355 res = res && fv_write_u64((uint64_t)i);
356 #endif
357 1663 return res;
358 }
359
360 4 static bool i_f_u(lbm_uint u) {
361 4 bool res = true;
362 #ifndef LBM64
363
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 res = res && fv_write_u8(S_U28_VALUE);
364
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 res = res && fv_write_u32((uint32_t)u);
365 #else
366 res = res && fv_write_u8(S_U56_VALUE);
367 res = res && fv_write_u64((uint64_t)u);
368 #endif
369 4 return res;
370 }
371
372 22 static bool i_f_b(uint8_t b) {
373 22 bool res = true;
374
2/4
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
✗ Branch 3 not taken.
22 res = res && fv_write_u8(S_BYTE_VALUE);
375
2/4
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
✗ Branch 3 not taken.
22 res = res && fv_write_u8(b);
376 22 return res;
377 }
378
379 2 static bool i_f_i32(int32_t w) {
380 2 bool res = true;
381
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 res = res && fv_write_u8(S_I32_VALUE);
382
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 res = res && fv_write_u32((uint32_t)w);
383 2 return res;
384 }
385
386 2 static bool i_f_u32(uint32_t w) {
387 2 bool res = true;
388
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 res = res && fv_write_u8(S_U32_VALUE);
389
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 res = res && fv_write_u32(w);
390 2 return res;
391 }
392
393 10 static bool i_f_float(float f) {
394 10 bool res = true;
395
2/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 res = res && fv_write_u8(S_FLOAT_VALUE);
396 uint32_t u;
397 10 memcpy(&u, &f, sizeof(uint32_t));
398
2/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 res = res && fv_write_u32((uint32_t)u);
399 10 return res;
400 }
401
402 2 static bool i_f_double(double d) {
403 2 bool res = true;
404
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 res = res && fv_write_u8(S_DOUBLE_VALUE);
405 uint64_t u;
406 2 memcpy(&u, &d, sizeof(uint64_t));
407
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 res = res && fv_write_u64(u);
408 2 return res;
409 }
410
411 2 static bool i_f_i64(int64_t w) {
412 2 bool res = true;
413
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 res = res && fv_write_u8(S_I64_VALUE);
414
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 res = res && fv_write_u64((uint64_t)w);
415 2 return res;
416 }
417
418 2 static bool i_f_u64(uint64_t w) {
419 2 bool res = true;
420
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 res = res && fv_write_u8(S_U64_VALUE);
421
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 res = res && fv_write_u64(w);
422 2 return res;
423 }
424
425 // num_bytes is specifically an uint32_t
426 89 static bool i_f_lbm_array(uint32_t num_bytes, uint8_t *data) {
427 89 bool res = fv_write_u8(S_LBM_ARRAY);
428
2/4
✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 89 times.
✗ Branch 3 not taken.
89 res = res && fv_write_u32(num_bytes);
429
1/2
✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
89 if (res) {
430
2/2
✓ Branch 0 taken 774 times.
✓ Branch 1 taken 89 times.
863 for (uint32_t i = 0; i < num_bytes; i ++ ) {
431
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 774 times.
774 if (!fv_write_u8(data[i])) return false;
432 }
433 }
434 89 return res;
435 }
436
437
438
439 // ////////////////////////////////////////////////////////////
440 //
441
442 376 char *lbm_image_get_version(void) {
443
2/2
✓ Branch 0 taken 153 times.
✓ Branch 1 taken 223 times.
376 if (image_version) {
444 153 return image_version;
445 } else {
446 223 int32_t pos = (int32_t)image_size-2; // fixed position version string.
447 223 uint32_t val = read_u32(pos); pos --;
448
1/2
✓ Branch 0 taken 223 times.
✗ Branch 1 not taken.
223 if (val == VERSION_ENTRY) {
449 223 int32_t size = (int32_t)read_u32(pos);
450 223 image_version = (char*)(image_address + (pos - size));
451 223 return image_version;
452 }
453 }
454 return NULL;
455 }
456
457 // ////////////////////////////////////////////////////////////
458 // Constant heaps as part of an image.
459
460 lbm_const_heap_t image_const_heap;
461 lbm_uint image_const_heap_start_ix = 0;
462
463 373066 bool image_const_heap_write(lbm_uint w, lbm_uint ix) {
464 #ifdef LBM64
465 157742 int32_t i = (int32_t)(image_const_heap_start_ix + (ix * 2));
466 157742 uint32_t *words = (uint32_t*)&w;
467 157742 bool r = image_write(words[0], i, false);
468
2/4
✓ Branch 0 taken 157742 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 157742 times.
✗ Branch 3 not taken.
157742 r = r && image_write(words[1], i + 1, false);
469 157742 return r;
470 #else
471 215324 int32_t i = (int32_t)(image_const_heap_start_ix + ix);
472 215324 return write_u32(w, &i, false);
473 #endif
474 }
475
476 // ////////////////////////////////////////////////////////////
477 // Image manipulation
478
479 302656 lbm_uint *lbm_image_add_symbol(char *name, lbm_uint id, lbm_uint symlist) {
480 // 64 bit | 32 bit
481 // image[i] = SYMBOL_ENTRY | image[i] = SYMBOL_ENTRY
482 // image[i-1] = symlist_ptr_high_word | image[i-1] = symlist_ptr
483 // image[i-2] = symlist_ptr_low_word | image[i-2] = id
484 // image[i-3] = id_high_word | image[i-3] = name_ptr
485 // image[i-4] = id_low_word
486 // image[i-5] = name_ptr_high_word
487 // image[i-6] = name_ptr_low_word
488 302656 bool r = write_u32(SYMBOL_ENTRY, &write_index,DOWNWARDS);
489
2/4
✓ Branch 0 taken 302656 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 302656 times.
✗ Branch 3 not taken.
302656 r = r && write_lbm_uint(symlist, &write_index, DOWNWARDS);
490
2/4
✓ Branch 0 taken 302656 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 302656 times.
✗ Branch 3 not taken.
302656 r = r && write_lbm_uint(id, &write_index, DOWNWARDS);
491
2/4
✓ Branch 0 taken 302656 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 302656 times.
✗ Branch 3 not taken.
302656 r = r && write_lbm_uint((lbm_uint)name, &write_index, DOWNWARDS);
492 302656 lbm_uint entry_ptr = (lbm_uint)(image_address + write_index + 1);
493
1/2
✓ Branch 0 taken 302656 times.
✗ Branch 1 not taken.
302656 if (r)
494 302656 return (lbm_uint*)entry_ptr;
495 return NULL;
496 }
497
498 // The symbol id is written to the link address upon image-boot
499 890917 lbm_uint *lbm_image_add_and_link_symbol(char *name, lbm_uint id, lbm_uint symlist, lbm_uint *link) {
500 // 64 bit | 32 bit
501 // image[i] = SYMBOL_ENTRY | image[i] = SYMBOL_ENTRY
502 // image[i-1] = link_ptr_high | image[i-1] link_ptr
503 // image[i-2] = link_ptr_low | image[i-2] = symlist_ptr
504 // image[i-3] = symlist_ptr_high_word | image[i-3] = id
505 // image[i-4] = symlist_ptr_low_word | image[i-4] = name_ptr
506 // image[i-5] = id_high_word
507 // image[i-6] = id_low_word
508 // image[i-7] = name_ptr_high_word
509 // image[i-8] = name_ptr_low_word
510 890917 bool r = write_u32(SYMBOL_LINK_ENTRY, &write_index,DOWNWARDS);
511
2/4
✓ Branch 0 taken 890917 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 890917 times.
✗ Branch 3 not taken.
890917 r = r && write_lbm_uint((lbm_uint)link, &write_index, DOWNWARDS);
512
2/4
✓ Branch 0 taken 890917 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 890917 times.
✗ Branch 3 not taken.
890917 r = r && write_lbm_uint(symlist, &write_index, DOWNWARDS);
513
2/4
✓ Branch 0 taken 890917 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 890917 times.
✗ Branch 3 not taken.
890917 r = r && write_lbm_uint(id, &write_index, DOWNWARDS);
514
2/4
✓ Branch 0 taken 890917 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 890917 times.
✗ Branch 3 not taken.
890917 r = r && write_lbm_uint((lbm_uint)name, &write_index, DOWNWARDS);
515 890917 lbm_uint entry_ptr = (lbm_uint)(image_address + write_index + 1);
516
1/2
✓ Branch 0 taken 890917 times.
✗ Branch 1 not taken.
890917 if (r)
517 890917 return (lbm_uint*)entry_ptr;
518 return NULL;
519 }
520
521 // ////////////////////////////////////////////////////////////
522 // Construction site
523
524 // Sharing is detected and annotated by:
525 // 1. Generate an array of addresses of shared structures. (sharing table)
526 // Sharing table will contain a boolean field where "having been flattened"
527 // status is tracked.
528 // 2. Flatten values and for each ptr-cell, check if it's address is in the
529 // sharing table. If the cell is in the sharing table and the boolean
530 // flag is not set: Set the flag and flatten the value with a shared tag
531 // is set: Do not flatten value, create a REF tag.
532
533 // Note: the boolean field may not be needed, it may be possible to recreate that
534 // information on the fly during flattening using the GC bit.
535 // But since all complete traversals require a GC this change just moves the
536 // bookkeeping cost forward (first to the size phase).
537
538 // The sharing table could be a temporary list on the LBM heap
539 // if only it wasn't for GC. GC cannot be run while doing pointer
540 // reversal traversals.
541 // Another option would be to allocate an area in LBM mem and fill that
542 // that with temporary sharing data. Only problem is that we do not know how much
543 // temporary data is needed until after doing at least one traversal where we
544 // also need to accumulate shared addresses in order to not count them doubly.
545 // ** allocating lbm_memory_longest_free amount of temp data at flattening
546 // could be a good solution. But even then the final number of shared
547 // nodes could be written to the image so that the unflattener does not need
548 // guess and allocate in chunks.
549 // - Very little lbm mem could be available here.
550
551 // Sharing is restored by:
552 // 1. Allocate a new column for the sharing table in LBM mem, the size is now known.
553 // 2. Unflatten flat values:
554 // if a value has a shared tag, fill in the address it is unflattened to into the
555 // new sharing table column.
556 // if a value has the ref tag, look it up in sharing table and read out the new address.
557 //
558 // The process is order dependent and shared tag for address 'a' must the unflattened
559 // before a ref tag for the same address 'a'.
560
561 // Tricky: The traversals for size computation and flattening has to be
562 // made aware of sharing in some way. The traversal always must fully traverse
563 // a value in order to not just partially mark it (it will then be destroyed by GC).
564 // Options:
565 // 1: run GC after a complete traversal of all values in env for size and flattening.
566 // This means that all the sizes must be stored temporarily...
567 // 2: Give a sharing table to the traversal and have the traversal switch to gc_mark_phace
568 // at each shared node to bookkeep it and keep it safe from upcomming gc.
569
570 //
571 // NOTE about sharing detection: It should be possible to traverse the
572 // heap structures using the same explicit stack algorithm as
573 // lbm_gc_mark_phase uses. Because if a structure is too large
574 // to be GC'd it cannot be used anyway, so no point in
575 // serialising/deserialising it.
576 //
577 // There are probably pros/cons to both approaches (ptr-rev vs
578 // explicit stack).
579 // ptr-rev -> correct
580 // explicit stack -> fast, simple but sensitive to programming style and limits
581 // programmer.
582 //
583 // For full benefit of pointer reversal the -DLBM_USE_GC_PTR_REV
584 // build flag is needed.
585
586
587 #ifdef LBM64
588 #define SHARING_TABLE_ENTRY_SIZE (2 + 1 + 1)
589 #else
590 #define SHARING_TABLE_ENTRY_SIZE (1 + 1 + 1)
591 #endif
592
593 #define SHARING_TABLE_TRUE 0xDEADBEEFu
594 #define SHARING_TABLE_FALSE 0xDEADBEEFu
595
596 2432 int32_t index_sharing_table(sharing_table *st, int32_t i) {
597
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2432 times.
2432 if (i < 0) return i; // maybe check if more than num?
598 2432 return st->start - 2 - (i * SHARING_TABLE_ENTRY_SIZE);
599 }
600
601 // Search sharing table, O(N) where N shared nodes
602 15557 int32_t sharing_table_contains(sharing_table *st, lbm_uint addr) {
603 15557 int32_t num = st->num;
604 15557 uint32_t st_tag = read_u32(st->start);
605
1/2
✓ Branch 0 taken 15557 times.
✗ Branch 1 not taken.
15557 if (st_tag == SHARING_TABLE) {
606 // sharing table tag exists but not the num field.
607
2/2
✓ Branch 0 taken 2364 times.
✓ Branch 1 taken 15489 times.
17853 for (int32_t i = 0; i < num; i ++ ) {
608 lbm_uint a;
609 2364 int32_t ix = index_sharing_table(st, i);
610 #ifdef LBM64
611 a = read_u64(ix);
612 #else
613 2364 a = read_u32(ix);
614 #endif
615
2/2
✓ Branch 0 taken 68 times.
✓ Branch 1 taken 2296 times.
2364 if (addr == a) {
616 68 return i;
617 }
618 }
619 }
620 15489 return -1;
621 }
622
623 #define SHARING_TABLE_SIZED_FIELD 0
624 #define SHARING_TABLE_FLATTENED_FIELD 1
625
626 18 bool sharing_table_set_field(sharing_table *st, int32_t ix, int32_t field, uint32_t value) {
627 int32_t wix;
628 #ifdef LBM64
629 wix = index_sharing_table(st, ix) - 2 - field;
630 #else
631 18 wix = index_sharing_table(st, ix) - 1 - field;
632 #endif
633 18 return write_u32(value, &wix, DOWNWARDS); // Dir irrelevant
634 }
635
636 50 uint32_t sharing_table_get_field(sharing_table *st, int32_t ix, int32_t field) {
637 int32_t wix;
638 #ifdef LBM64
639 wix = index_sharing_table(st, ix) - 2 - field;
640 #else
641 50 wix = index_sharing_table(st, ix) - 1 - field;
642 #endif
643 50 return read_u32(wix);
644 }
645
646 7758 static int detect_shared(lbm_value v, bool shared, void *acc) {
647 7758 sharing_table *st = (sharing_table*)acc;
648
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 7749 times.
7758 if (shared) {
649
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 if (lbm_is_ptr(v)) {
650 9 lbm_uint addr = v;
651 9 int32_t ix = sharing_table_contains(st,addr);
652
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 if (ix < 0) {
653 // Create place in table for a shared address and skip
654 // enough words for boolean fields.
655 #ifdef LBM64
656 write_u64(addr, &write_index, DOWNWARDS);
657 #else
658 9 write_u32(addr, &write_index, DOWNWARDS);
659 #endif
660 9 write_index -= 2; // skip 2 words for "sized" and "flattened" booleans.
661 9 st->num++;
662 }
663 }
664 }
665 7758 return TRAV_FUN_SUBTREE_PROCEED;
666 }
667
668 35 sharing_table lbm_image_sharing(void) {
669 35 lbm_value *env = lbm_get_global_env();
670
671 sharing_table st;
672 35 st.start = write_index;
673 35 st.num = 0;
674
675 35 write_u32(SHARING_TABLE, &write_index, DOWNWARDS);
676 35 write_index -= 1; // skip a word where size is to be written out of order.
677 // index is now correct for starting to write sharing table rows.
678
679
1/2
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
35 if (env) {
680
2/2
✓ Branch 0 taken 1120 times.
✓ Branch 1 taken 35 times.
1155 for (int i = 0; i < GLOBAL_ENV_ROOTS; i ++) {
681 1120 lbm_value curr = env[i];
682
2/2
✓ Branch 0 taken 161 times.
✓ Branch 1 taken 1120 times.
1281 while(lbm_is_cons(curr)) {
683 // lbm_value name_field = lbm_caar(curr);
684 161 lbm_value val_field = lbm_cdr(lbm_car(curr));
685
2/2
✓ Branch 0 taken 152 times.
✓ Branch 1 taken 9 times.
161 if (!lbm_is_constant(val_field)) {
686 152 lbm_ptr_rev_trav(detect_shared, val_field, &st);
687 }
688 161 curr = lbm_cdr(curr);
689 }
690 }
691 // clean out all mark-bits
692 35 lbm_perform_gc();
693 }
694 // Write the number of shared nodes, 0 or more, to table entry.
695 35 int32_t wix = st.start - 1;
696 35 write_u32((uint32_t)st.num,&wix, DOWNWARDS);
697
698 35 return st;
699 }
700
701 // ////////////////////////////////////////////////////////////
702 //
703
704 typedef struct {
705 int32_t s;
706 sharing_table *st;
707 } size_accumulator;
708
709 7765 static int size_acc(lbm_value v, bool shared, void *acc) {
710 (void) shared;
711 7765 size_accumulator *sa = (size_accumulator*)acc;
712
713 7765 int32_t ix = sharing_table_contains(sa->st, v);
714
715
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 7740 times.
7765 if (ix >= 0) {
716
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 9 times.
25 if (SHARING_TABLE_TRUE == sharing_table_get_field(sa->st, ix, SHARING_TABLE_SIZED_FIELD)) {
717 // shared node has been sized already and should return size of a ref node
718 // sizeof S_REF and addr
719 #ifdef LBM64
720 sa->s += 9;
721 #else
722 16 sa->s += 5;
723 #endif
724 16 return TRAV_FUN_SUBTREE_DONE;
725 } else {
726 // setting the sized field to not include the size in future occurrances.
727 9 sharing_table_set_field(sa->st, ix, SHARING_TABLE_SIZED_FIELD, SHARING_TABLE_TRUE);
728 #ifdef LBM64
729 sa->s += 9;
730 #else
731 9 sa->s += 5;
732 #endif
733 }
734 }
735
736 7749 lbm_uint t = lbm_type_of(v);
737
738
3/4
✓ Branch 0 taken 3859 times.
✓ Branch 1 taken 3890 times.
✓ Branch 2 taken 3859 times.
✗ Branch 3 not taken.
7749 if (t >= LBM_POINTER_TYPE_FIRST && t < LBM_POINTER_TYPE_LAST) {
739 3859 t = t & ~(LBM_PTR_TO_CONSTANT_BIT);
740 }
741
742
4/4
✓ Branch 0 taken 3859 times.
✓ Branch 1 taken 3890 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 3858 times.
7749 if (lbm_is_ptr(v) && (v & LBM_PTR_TO_CONSTANT_BIT)) {
743 1 sa->s += (int32_t)sizeof(lbm_uint) + 1;
744 1 return TRAV_FUN_SUBTREE_DONE;
745 }
746
747
13/14
✓ Branch 0 taken 3736 times.
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 1663 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 2 times.
✓ Branch 8 taken 2 times.
✓ Branch 9 taken 10 times.
✓ Branch 10 taken 2 times.
✓ Branch 11 taken 2201 times.
✓ Branch 12 taken 89 times.
✗ Branch 13 not taken.
7748 switch (t) {
748 3736 case LBM_TYPE_CONS:
749 3736 sa->s += 1;
750 3736 break;
751 13 case LBM_TYPE_LISPARRAY:
752 13 sa->s += 4 + 1;
753 13 break;
754 22 case LBM_TYPE_BYTE:
755 22 sa->s += 2;
756 22 break;
757 4 case LBM_TYPE_U:
758 4 sa->s += (int32_t)sizeof(lbm_uint) + 1;
759 4 break;
760 1663 case LBM_TYPE_I:
761 1663 sa->s += (int32_t)sizeof(lbm_uint) + 1;
762 1663 break;
763 2 case LBM_TYPE_U32:
764 2 sa->s += 4 + 1;
765 2 break;
766 2 case LBM_TYPE_I32:
767 2 sa->s += 4 + 1;
768 2 break;
769 2 case LBM_TYPE_U64:
770 2 sa->s += 8 + 1;
771 2 break;
772 2 case LBM_TYPE_I64:
773 2 sa->s += 8 + 1;
774 2 break;
775 10 case LBM_TYPE_FLOAT:
776 10 sa->s += 4 + 1;
777 10 break;
778 2 case LBM_TYPE_DOUBLE:
779 2 sa->s += 8 + 1;
780 2 break;
781 2201 case LBM_TYPE_SYMBOL:
782 2201 sa->s += (int32_t)sizeof(lbm_uint) + 1;
783 2201 break;
784 89 case LBM_TYPE_ARRAY: {
785 89 lbm_int arr_size = lbm_heap_array_get_size(v);
786 89 const uint8_t *d = lbm_heap_array_get_data_ro(v);
787
2/4
✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 89 times.
✗ Branch 3 not taken.
89 if (arr_size > 0 && d != NULL) {
788 89 sa->s += 1 + 4 + arr_size;
789 }
790 89 }break;
791 }
792 7748 return TRAV_FUN_SUBTREE_CONTINUE;
793 }
794
795 typedef struct {
796 bool res;
797 sharing_table *st;
798 int arg;
799 } flatten_node_meta_data;
800
801 7765 static int flatten_node(lbm_value v, bool shared, void *arg) {
802 (void)shared;
803 7765 flatten_node_meta_data *md = (flatten_node_meta_data*)arg;
804 7765 bool *acc = &md->res;
805
806 7765 int32_t ix = sharing_table_contains(md->st, v);
807
808
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 7740 times.
7765 if (ix >= 0) {
809
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 9 times.
25 if (SHARING_TABLE_TRUE == sharing_table_get_field(md->st, ix, SHARING_TABLE_FLATTENED_FIELD)) {
810 // Shared node already flattened.
811 16 fv_write_u8(S_REF);
812 #ifdef LBM64
813 fv_write_u64((lbm_uint)v);
814 #else
815 16 fv_write_u32((lbm_uint)v);
816 #endif
817 16 return TRAV_FUN_SUBTREE_DONE;
818 } else {
819 // Shared node not yet flattened.
820 9 sharing_table_set_field(md->st, ix, SHARING_TABLE_FLATTENED_FIELD, SHARING_TABLE_TRUE);
821 9 fv_write_u8(S_SHARED);
822 #ifdef LBM64
823 fv_write_u64((lbm_uint)v);
824 #else
825 9 fv_write_u32((lbm_uint)v);
826 #endif
827 // Continue flattening along this subtree.
828 }
829 }
830
831 7749 lbm_uint t = lbm_type_of(v);
832
833
3/4
✓ Branch 0 taken 3859 times.
✓ Branch 1 taken 3890 times.
✓ Branch 2 taken 3859 times.
✗ Branch 3 not taken.
7749 if (t >= LBM_POINTER_TYPE_FIRST && t < LBM_POINTER_TYPE_LAST) {
834 3859 t = t & ~(LBM_PTR_TO_CONSTANT_BIT);
835 }
836
837
4/4
✓ Branch 0 taken 3859 times.
✓ Branch 1 taken 3890 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 3858 times.
7749 if (lbm_is_ptr(v) && (v & LBM_PTR_TO_CONSTANT_BIT)) {
838
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 *acc = *acc && fv_write_u8(S_CONSTANT_REF);
839 #ifdef LBM64
840 *acc = *acc && fv_write_u64((lbm_uint)v);
841 #else
842
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 *acc = *acc && fv_write_u32((lbm_uint)v);
843 #endif
844 1 return TRAV_FUN_SUBTREE_DONE;
845 }
846
847
13/14
✓ Branch 0 taken 3736 times.
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 1663 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 2 times.
✓ Branch 8 taken 2 times.
✓ Branch 9 taken 10 times.
✓ Branch 10 taken 2 times.
✓ Branch 11 taken 2201 times.
✓ Branch 12 taken 89 times.
✗ Branch 13 not taken.
7748 switch (t) {
848 3736 case LBM_TYPE_CONS:
849
2/4
✓ Branch 0 taken 3736 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3736 times.
✗ Branch 3 not taken.
3736 *acc = *acc && i_f_cons();
850 3736 break;
851 13 case LBM_TYPE_LISPARRAY: {
852 13 lbm_array_header_t *header = (lbm_array_header_t*)lbm_car(v);
853
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 if (header) {
854 13 uint32_t size = (uint32_t)(header->size / sizeof(lbm_value));
855
2/4
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
✗ Branch 3 not taken.
13 *acc = *acc && i_f_lisp_array(size);
856 } else {
857 // hmm
858 }
859 13 } break;
860 22 case LBM_TYPE_BYTE:
861
2/4
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
✗ Branch 3 not taken.
22 *acc = *acc && i_f_b((uint8_t)lbm_dec_as_char(v));
862 22 break;
863 4 case LBM_TYPE_U:
864
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 *acc = *acc && i_f_u(lbm_dec_u(v));
865 4 break;
866 1663 case LBM_TYPE_I:
867
2/4
✓ Branch 0 taken 1663 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1663 times.
✗ Branch 3 not taken.
1663 *acc = *acc && i_f_i(lbm_dec_i(v));
868 1663 break;
869 2 case LBM_TYPE_U32:
870
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 *acc = *acc && i_f_u32(lbm_dec_as_u32(v));
871 2 break;
872 2 case LBM_TYPE_I32:
873
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 *acc = *acc && i_f_i32(lbm_dec_as_i32(v));
874 2 break;
875 2 case LBM_TYPE_U64:
876
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 *acc = *acc && i_f_u64(lbm_dec_as_u64(v));
877 2 break;
878 2 case LBM_TYPE_I64:
879
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 *acc = *acc && i_f_i64(lbm_dec_as_i64(v));
880 2 break;
881 10 case LBM_TYPE_FLOAT:
882
2/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 *acc = *acc && i_f_float(lbm_dec_as_float(v));
883 10 break;
884 2 case LBM_TYPE_DOUBLE:
885
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 *acc = *acc && i_f_double(lbm_dec_as_double(v));
886 2 break;
887 2201 case LBM_TYPE_SYMBOL:
888
2/4
✓ Branch 0 taken 2201 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2201 times.
✗ Branch 3 not taken.
2201 *acc = *acc && i_f_sym(v);
889 2201 break;
890 89 case LBM_TYPE_ARRAY: {
891 89 lbm_int s = lbm_heap_array_get_size(v);
892 89 const uint8_t *d = lbm_heap_array_get_data_ro(v);
893
2/4
✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 89 times.
✗ Branch 3 not taken.
89 if (s > 0 && d != NULL) {
894
2/4
✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 89 times.
✗ Branch 3 not taken.
89 *acc = *acc && i_f_lbm_array((uint32_t)s, (uint8_t*)d);
895 }
896 89 }break;
897 default:
898 break;
899 }
900 7748 return TRAV_FUN_SUBTREE_CONTINUE;
901 }
902
903 // Performing GC after using the ptr_rev_trav to restore the
904 // GC-bit in the value traversed.
905 152 static int32_t image_flatten_size(sharing_table *st, lbm_value v) {
906 size_accumulator sa;
907 152 sa.s = 0;
908 152 sa.st = st;
909 152 lbm_ptr_rev_trav(size_acc, v, &sa);
910 152 lbm_perform_gc();
911 152 return sa.s; // Should always be "ok" now.
912 }
913
914 152 static bool image_flatten_value(sharing_table *st, lbm_value v) {
915 flatten_node_meta_data md;
916 152 md.res = true;
917 152 md.st = st;
918 152 md.arg = 0;
919 152 lbm_ptr_rev_trav(flatten_node, v, &md);
920 152 lbm_perform_gc();
921 152 return md.res; // ok = enough space in image for flat val.
922 }
923
924 // ////////////////////////////////////////////////////////////
925 //
926 35 bool lbm_image_save_global_env(void) {
927
928 35 sharing_table st = lbm_image_sharing();
929 35 lbm_value *env = lbm_get_global_env();
930
1/2
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
35 if (env) {
931
2/2
✓ Branch 0 taken 1120 times.
✓ Branch 1 taken 35 times.
1155 for (int i = 0; i < GLOBAL_ENV_ROOTS; i ++) {
932 1120 lbm_value curr = env[i];
933
2/2
✓ Branch 0 taken 161 times.
✓ Branch 1 taken 1120 times.
1281 while(lbm_is_cons(curr)) {
934 161 lbm_value name_field = lbm_caar(curr);
935 161 lbm_value val_field = lbm_cdr(lbm_car(curr));
936
937
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 152 times.
161 if (lbm_is_constant(val_field)) {
938 9 write_u32(BINDING_CONST, &write_index, DOWNWARDS);
939 9 write_lbm_value(name_field, &write_index, DOWNWARDS);
940 9 write_lbm_value(val_field, &write_index, DOWNWARDS);
941 } else {
942 152 int fv_size = image_flatten_size(&st, val_field);
943
1/2
✓ Branch 0 taken 152 times.
✗ Branch 1 not taken.
152 if (fv_size > 0) {
944
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 135 times.
152 fv_size = (fv_size % 4 == 0) ? (fv_size / 4) : (fv_size / 4) + 1; // num 32bit words
945
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 152 times.
152 if ((write_index - fv_size) <= (int32_t)image_const_heap.next) {
946 return false;
947 }
948 152 write_u32(BINDING_FLAT, &write_index, DOWNWARDS);
949 152 write_u32((uint32_t)fv_size , &write_index, DOWNWARDS);
950 152 write_lbm_value(name_field, &write_index, DOWNWARDS);
951 152 write_index = write_index - fv_size; // subtract fv_size
952
1/2
✓ Branch 0 taken 152 times.
✗ Branch 1 not taken.
152 if (image_flatten_value(&st, val_field)) { // adds fv_size back
953 // TODO: What error handling makes sense?
954 152 fv_write_flush();
955 }
956 152 write_index = write_index - fv_size - 1; // subtract fv_size
957 } else {
958 return false;
959 }
960 }
961 161 curr = lbm_cdr(curr);
962 }
963 }
964 35 return true;
965 }
966 return false;
967 }
968
969 // The extension table is created at system startup.
970 // Extensions can also be added dynamically.
971 // Dynamically added extensions have names starting with "ext-"
972 // and their names are placed in RAM by the reader.
973 //
974 // Symbol_id -> index in extension table mapping
975 // is created as extensions are added.
976 // dynamic extensions are added after "built-in" extensions
977 // and have higher indices.
978
979 35 bool lbm_image_save_extensions(void) {
980 35 bool r = true;
981 35 lbm_uint num = lbm_get_num_extensions();
982
1/2
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
35 if (num > 0) {
983
2/4
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 35 times.
✗ Branch 3 not taken.
35 r = r && write_u32(EXTENSION_TABLE, &write_index, DOWNWARDS);
984
2/4
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 35 times.
✗ Branch 3 not taken.
35 r = r && write_u32((uint32_t)num , &write_index, DOWNWARDS);
985
2/2
✓ Branch 0 taken 5460 times.
✓ Branch 1 taken 35 times.
5495 for (lbm_uint i = 0; i < num; i ++) {
986
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5460 times.
5460 if (!r) return r;
987
988 5460 char *name_ptr = extension_table[i].name;
989 lbm_uint addr;
990 // when PIC, name pointers may move around
991 // between restarts. It is also the case that
992 // the FPTRs will move around as well.
993 // This makes dynamic extensions useless on Linux.
994 // Static extensions are fine as they will be re-added after image-boot
995 // and faulty FPTRs will be replaced.
996 //#ifdef __PIC__
997 //r = store_symbol_name_flash(name_ptr, &addr);
998 //if (!r) return r;
999 //name_ptr = (char *)addr;
1000 //#else
1001
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5460 times.
5460 if (lbm_memory_ptr_inside((lbm_uint *)name_ptr)) {
1002 r = store_symbol_name_flash(name_ptr, &addr);
1003 if (!r) return r;
1004 name_ptr = (char *)addr;
1005 }
1006 //#endif
1007 #ifdef LBM64
1008 r = r && write_u64((uint64_t)name_ptr, &write_index, DOWNWARDS);
1009 r = r && write_u64((uint64_t)extension_table[i].fptr, &write_index, DOWNWARDS);
1010 #else
1011
2/4
✓ Branch 0 taken 5460 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5460 times.
✗ Branch 3 not taken.
5460 r = r && write_u32((uint32_t)name_ptr, &write_index, DOWNWARDS);
1012
2/4
✓ Branch 0 taken 5460 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5460 times.
✗ Branch 3 not taken.
5460 r = r && write_u32((uint32_t)extension_table[i].fptr, &write_index, DOWNWARDS);
1013 #endif
1014 } }
1015 35 return true;
1016 }
1017
1018 static uint32_t last_const_heap_ix = 0;
1019
1020 35 bool lbm_image_save_constant_heap_ix(void) {
1021 35 bool r = true; // saved or no need to save it.
1022
1/2
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
35 if (image_const_heap.next != last_const_heap_ix) {
1023 35 last_const_heap_ix = image_const_heap.next;
1024 35 r = write_u32(CONSTANT_HEAP_IX, &write_index, DOWNWARDS);
1025
2/4
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 35 times.
✗ Branch 3 not taken.
35 r = r && write_u32((uint32_t)image_const_heap.next, &write_index, DOWNWARDS);
1026 }
1027 35 return r;
1028 }
1029
1030 223 bool lbm_image_exists(void) {
1031 223 uint32_t val = read_u32((int32_t)image_size - 1);
1032 223 return val == IMAGE_INITIALIZED;
1033 }
1034
1035 44370 void lbm_image_init(uint32_t* image_mem_address,
1036 uint32_t image_size_words,
1037 lbm_image_write_fun image_write_fun) {
1038 44370 image_write = image_write_fun;
1039 44370 image_address = image_mem_address;
1040 44370 image_size = image_size_words;
1041 44370 write_index = (int32_t)image_size_words -1;
1042 44370 image_has_extensions = false;
1043 44370 image_version = NULL;
1044 44370 last_const_heap_ix = 0;
1045 44370 }
1046
1047 44335 void lbm_image_create(char *version_str) {
1048 44335 write_u32(IMAGE_INITIALIZED, &write_index, DOWNWARDS);
1049
1/2
✓ Branch 0 taken 44335 times.
✗ Branch 1 not taken.
44335 if (version_str) {
1050 44335 uint32_t bytes = strlen(version_str) + 1;
1051
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44335 times.
44335 uint32_t words = (bytes % 4 == 0) ? bytes / 4 : (bytes / 4) + 1;
1052 44335 write_u32(VERSION_ENTRY, &write_index, DOWNWARDS);
1053 44335 write_u32(words, &write_index, DOWNWARDS);
1054 44335 uint32_t w = 0;
1055 44335 char *buf = (char*)&w;
1056 44335 uint32_t i = 0;
1057 44335 int32_t ix = write_index - (int32_t)(words -1);
1058 44335 int wi = 0;
1059
2/2
✓ Branch 0 taken 486633 times.
✓ Branch 1 taken 44335 times.
530968 while (i < bytes) {
1060
2/2
✓ Branch 0 taken 44335 times.
✓ Branch 1 taken 442298 times.
486633 if (wi == 0 ) {
1061 44335 w = 0;
1062 }
1063
2/2
✓ Branch 0 taken 88407 times.
✓ Branch 1 taken 398226 times.
486633 if (wi == 4) wi = 0;
1064 486633 buf[wi] = version_str[i];
1065
2/2
✓ Branch 0 taken 88407 times.
✓ Branch 1 taken 398226 times.
486633 if (wi == 3) {
1066 88407 write_u32(w, &ix, UPWARDS);
1067 }
1068 486633 i ++;
1069 486633 wi ++;
1070 }
1071
1/2
✓ Branch 0 taken 44335 times.
✗ Branch 1 not taken.
44335 if (wi != 0) {
1072 44335 write_u32(w, &ix, UPWARDS);
1073 }
1074 44335 write_index -= (int32_t)words;
1075 }
1076 44335 }
1077
1078
1079 44370 bool lbm_image_boot(void) {
1080 //process image
1081 44370 int32_t pos = (int32_t)image_size-1;
1082 44370 last_const_heap_ix = 0;
1083
1084 sharing_table st;
1085 44370 lbm_uint *target_map = NULL; // Target addresses for shared/refs from the flat values.
1086
1087
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 135582 times.
✓ Branch 2 taken 135582 times.
✗ Branch 3 not taken.
135582 while (pos >= 0 && pos > (int32_t)last_const_heap_ix) {
1088 135582 uint32_t val = read_u32(pos);
1089 135582 pos --;
1090
10/10
✓ Branch 0 taken 44370 times.
✓ Branch 1 taken 44370 times.
✓ Branch 2 taken 35 times.
✓ Branch 3 taken 9 times.
✓ Branch 4 taken 152 times.
✓ Branch 5 taken 526 times.
✓ Branch 6 taken 1680 times.
✓ Branch 7 taken 35 times.
✓ Branch 8 taken 35 times.
✓ Branch 9 taken 44370 times.
135582 switch(val) {
1091 44370 case IMAGE_INITIALIZED: {
1092 44370 image_const_heap_start_ix = 0; // const heap starts at 0
1093 44370 lbm_const_heap_init(image_const_heap_write,
1094 &image_const_heap,
1095 (lbm_uint*)(image_address));
1096 // initialized is a one word field
1097 44370 } break;
1098 44370 case VERSION_ENTRY: {
1099 44370 uint32_t size = read_u32(pos); pos --;
1100 44370 image_version = (char*)(image_address + (pos - (int32_t)size + 1));
1101 44370 pos -= (int32_t)size;
1102 44370 } break;
1103 35 case CONSTANT_HEAP_IX: {
1104 35 uint32_t next = read_u32(pos);
1105 35 pos --;
1106 35 last_const_heap_ix = next;
1107 35 image_const_heap.next = next;
1108 35 } break;
1109 9 case BINDING_CONST: {
1110 // on 64 bit | on 32 bit
1111 // pos -> key_high | pos -> key
1112 // pos - 1 -> key_low | pos - 1 -> val
1113 // pos - 2 -> val_high
1114 // pos - 3 -> val_low
1115 #ifdef LBM64
1116 lbm_uint bind_key = read_u64(pos-1);
1117 lbm_uint bind_val = read_u64(pos-3);
1118 pos -= 4;
1119 #else
1120 9 lbm_uint bind_key = read_u32(pos);
1121 9 lbm_uint bind_val = read_u32(pos-1);
1122 9 pos -= 2;
1123 #endif
1124 9 lbm_uint ix_key = lbm_dec_sym(bind_key) & GLOBAL_ENV_MASK;
1125 9 lbm_value *global_env = lbm_get_global_env();
1126 9 lbm_uint orig_env = global_env[ix_key];
1127 9 lbm_value new_env = lbm_env_set(orig_env,bind_key,bind_val);
1128
1129
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (lbm_is_symbol(new_env)) {
1130 return false;
1131 }
1132 9 global_env[ix_key] = new_env;
1133 9 } break;
1134 152 case BINDING_FLAT: {
1135 // on 64 bit | on 32 bit
1136 // pos -> size | pos -> size
1137 // pos - 1 -> key_high | pos - 1 -> key
1138 // pos - 2 -> key_low
1139 //
1140 152 int32_t s = (int32_t)read_u32(pos);
1141 // size in 32 or 64 bit words.
1142 #ifdef LBM64
1143 lbm_uint bind_key = read_u64(pos-2);
1144 pos -= 3;
1145 #else
1146 152 lbm_uint bind_key = read_u32(pos-1);
1147 152 pos -= 2;
1148 #endif
1149
1150 152 pos -= s;
1151 lbm_flat_value_t fv;
1152 152 fv.buf = (uint8_t*)(image_address + pos);
1153 152 fv.buf_size = (uint32_t)s * sizeof(lbm_uint); // GEQ to actual buf
1154 152 fv.buf_pos = 0;
1155 lbm_value unflattened;
1156
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 126 times.
152 if (target_map) {
1157
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
26 if (!lbm_unflatten_value_sharing(&st, target_map, &fv, &unflattened)) {
1158 return false;
1159 }
1160 // When a value is unflattened it may contain shared subvalues
1161 // and references to shared values. A reference may point to either
1162 // values that are shared within the value that is currently unflattened
1163 // or to a value that has previously been unflattened.
1164 //
1165 // There is an ordering property that must be maintained that the node
1166 // with the S_SHARED tag is always processed before any corresponding S_REF tags.
1167 // This means that if a ref node is unflattened the target to point it to will
1168 // already exist.
1169 //
1170 // If GC needs to happen while unflattening a value, there is no danger of messing
1171 // up the addresses to point references to because:
1172 // 1. The S_SHARED node is local to the same value and will be recreated after GC
1173 // and the ref value in target map will be overwritten. Any local refs will be also
1174 // be recreated. Any refs to the S_Shared outside of this value, will be in values
1175 // processed in the future.
1176 // 2. S_SHARED nodes that have been created as part of prvious value are untouched
1177 // by running GC as they have already been unflattened and should be reachable
1178 // on the environment. Their mapping in the target map is still valid.
1179
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
26 if (lbm_is_symbol_merror(unflattened)) {
1180 //memset(target_map, 0, st.num * sizeof(lbm_uint));
1181 lbm_perform_gc();
1182 lbm_unflatten_value_sharing(&st, target_map, &fv, &unflattened);
1183 }
1184 } else {
1185 126 lbm_unflatten_value(&fv, &unflattened);
1186
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 126 times.
126 if (lbm_is_symbol_merror(unflattened)) {
1187 lbm_perform_gc();
1188 lbm_unflatten_value(&fv, &unflattened);
1189 }
1190 }
1191 152 lbm_uint ix_key = lbm_dec_sym(bind_key) & GLOBAL_ENV_MASK;
1192 152 lbm_value *global_env = lbm_get_global_env();
1193 152 lbm_uint orig_env = global_env[ix_key];
1194 152 lbm_value new_env = lbm_env_set(orig_env,bind_key,unflattened);
1195
1196
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 152 times.
152 if (lbm_is_symbol(new_env)) {
1197 return false;
1198 }
1199 152 global_env[ix_key] = new_env;
1200 152 pos --;
1201 152 } break;
1202 526 case SYMBOL_ENTRY: {
1203 // on 64 bit | on 32 bit
1204 // pos -> symlist_addr_high_word | pos -> symlist_ptr
1205 // pos - 1 -> symlist_addr_low_word | pos - 1 -> id
1206 // pos - 2 -> id_high_word | pos - 2 -> name_ptr
1207 // pos - 3 -> id_low_word |
1208 // pos - 4 -> name_ptr_high_word |
1209 // pos - 5 -> name_ptr_low_word |
1210 #ifdef LBM64
1211 int32_t entry_pos = pos - 5;
1212 lbm_uint *p = (lbm_uint*)(image_address + entry_pos);
1213 uint32_t sym_id = (uint32_t)(p[1]);
1214 lbm_uint next_id = lbm_symrepr_get_next_id();
1215 if (sym_id >= RUNTIME_SYMBOLS_START && sym_id >= next_id ) {
1216 lbm_symrepr_set_next_id(next_id + 1);
1217 }
1218 lbm_symrepr_set_symlist((lbm_uint*)(image_address + entry_pos));
1219 pos -= 6;
1220 #else
1221 526 int32_t entry_pos = pos - 2;
1222 526 lbm_uint *p = (lbm_uint*)(image_address + entry_pos);
1223 526 uint32_t sym_id = (uint32_t)(p[1]);
1224 526 lbm_uint next_id = lbm_symrepr_get_next_id();
1225
2/4
✓ Branch 0 taken 526 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 526 times.
✗ Branch 3 not taken.
526 if (sym_id >= RUNTIME_SYMBOLS_START && sym_id >= next_id ) {
1226 526 lbm_symrepr_set_next_id(next_id + 1);
1227 }
1228 526 lbm_symrepr_set_symlist((lbm_uint*)(image_address + entry_pos));
1229 526 pos -= 3;
1230 #endif
1231 526 } break;
1232 1680 case SYMBOL_LINK_ENTRY: {
1233 // on 64 bits | on 32 bit
1234 // pos -> link_ptr_high | pos -> link_ptr
1235 // pos - 1 -> link_ptr_low | pos - 1 -> symlist_ptr
1236 // pos - 2 -> symlist_addr_high_word | pos - 2 -> id
1237 // pos - 3 -> symlist_addr_low_word | pos - 3 -> name_ptr;
1238 // pos - 4 -> id_high_word
1239 // pos - 5 -> id_low_word
1240 // pos - 6 -> name_ptr_high_word
1241 // pos - 7 -> name_ptr_low_word
1242 //int32_t entry_pos = pos - (int32_t)(3 * (sizeof(lbm_uint) / 4));
1243 lbm_uint link_ptr;
1244 lbm_uint sym_id;
1245 #ifdef LBM64
1246 link_ptr = read_u64(pos-1);
1247 sym_id = read_u64(pos-5);
1248 *((lbm_uint*)link_ptr) = sym_id;
1249 lbm_uint next_id = lbm_symrepr_get_next_id();
1250 if (sym_id >= RUNTIME_SYMBOLS_START && sym_id >= next_id ) {
1251 lbm_symrepr_set_next_id(next_id + 1);
1252 }
1253 lbm_symrepr_set_symlist((lbm_uint*)(image_address + (pos - 7)));
1254 pos -= 8;
1255 #else
1256 1680 link_ptr = read_u32(pos);
1257 1680 sym_id = read_u32(pos-2);
1258 1680 *((lbm_uint*)link_ptr) = sym_id;
1259 1680 lbm_uint next_id = lbm_symrepr_get_next_id();
1260
2/4
✓ Branch 0 taken 1680 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1680 times.
✗ Branch 3 not taken.
1680 if (sym_id >= RUNTIME_SYMBOLS_START && sym_id >= next_id ) {
1261 1680 lbm_symrepr_set_next_id(next_id + 1);
1262 }
1263 1680 lbm_symrepr_set_symlist((lbm_uint*)(image_address + (pos - 3)));
1264 1680 pos -= 4;
1265 #endif
1266 1680 } break;
1267 35 case EXTENSION_TABLE: {
1268 // on 64 bit | on 32 bit
1269 // pos -> name_ptr_high | pos -> name_ptr
1270 // pos - 1 -> name_ptr_low | pos - 1 -> fptr
1271 // pos - 2 -> fptr_high
1272 // pos - 3 -> fptr_low
1273 35 int32_t num = (int32_t)read_u32(pos); pos --;
1274
1275 35 int32_t i = 0;
1276
2/2
✓ Branch 0 taken 5460 times.
✓ Branch 1 taken 35 times.
5495 for (i = 0; i < num; i ++) {
1277 lbm_uint name;
1278 lbm_uint fptr;
1279 #ifdef LBM64
1280 name = read_u64(pos-1);
1281 fptr = read_u64(pos-3);
1282 pos -= 4;
1283 #else
1284 5460 name = read_u32(pos);
1285 5460 fptr = read_u32(pos-1);
1286 5460 pos -= 2;
1287 #endif
1288 5460 extension_table[i].name = (char*)name;
1289 5460 extension_table[i].fptr = (extension_fptr)fptr;
1290 }
1291 35 lbm_extensions_set_next((lbm_uint)i);
1292 35 image_has_extensions = true;
1293 35 } break;
1294 35 case SHARING_TABLE: {
1295 35 st.start = pos +1;
1296 35 uint32_t num = read_u32(pos); pos --;
1297 35 st.num = (int32_t)num;
1298
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 29 times.
35 if (num > 0) {
1299 6 target_map = lbm_malloc(num * sizeof(lbm_uint));
1300
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (!target_map ) {
1301 return false;
1302 }
1303 6 memset(target_map, 0, num * sizeof(lbm_uint));
1304 }
1305 #ifdef LBM64
1306 pos -= (int32_t)(num + (num * 3));
1307 #else
1308 35 pos -= (int32_t)num * 3;
1309 #endif
1310 35 } break;
1311 44370 default:
1312 44370 write_index = pos+1;
1313 44370 goto done_loading_image;
1314 break;
1315 }
1316 }
1317 done_loading_image:
1318
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 44364 times.
44370 if (target_map) lbm_free(target_map);
1319 44370 return true;
1320 }
1321