GCC Code Coverage Report


Directory: ../src/
File: /home/joels/Current/lispbm/src/lbm_image.c
Date: 2025-10-28 15:15:18
Exec Total Coverage
Lines: 527 595 88.6%
Functions: 47 49 95.9%
Branches: 259 466 55.6%

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