GCC Code Coverage Report
Directory: ../src/ Exec Total Coverage
File: /home/joels/Current/lispbm/src/heap.c Lines: 693 848 81.7 %
Date: 2025-01-19 11:10:47 Branches: 250 365 68.5 %

Line Branch Exec Source
1
/*
2
    Copyright 2018, 2020, 2022 - 2024 Joel Svensson  svenssonjoel@yahoo.se
3
                          2022        Benjamin Vedder
4
5
    This program is free software: you can redistribute it and/or modify
6
    it under the terms of the GNU General Public License as published by
7
    the Free Software Foundation, either version 3 of the License, or
8
    (at your option) any later version.
9
10
    This program is distributed in the hope that it will be useful,
11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
    GNU General Public License for more details.
14
15
    You should have received a copy of the GNU General Public License
16
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
*/
18
19
#include <stdio.h>
20
#include <stdlib.h>
21
#include <stdint.h>
22
#include <stdarg.h>
23
#include <inttypes.h>
24
#include <lbm_memory.h>
25
#include <lbm_custom_type.h>
26
#include <lbm_defrag_mem.h>
27
28
#include "heap.h"
29
#include "symrepr.h"
30
#include "stack.h"
31
#include "lbm_channel.h"
32
#include "platform_mutex.h"
33
#include "eval_cps.h"
34
#ifdef VISUALIZE_HEAP
35
#include "heap_vis.h"
36
#endif
37
38
39
33797927
static inline lbm_value lbm_set_gc_mark(lbm_value x) {
40
33797927
  return x | LBM_GC_MARKED;
41
}
42
43
33631697
static inline lbm_value lbm_clr_gc_mark(lbm_value x) {
44
33631697
  return x & ~LBM_GC_MASK;
45
}
46
47
804518484
static inline bool lbm_get_gc_mark(lbm_value x) {
48
804518484
  return x & LBM_GC_MASK;
49
}
50
51
// flag is the same bit as mark, but in car
52
static inline bool lbm_get_gc_flag(lbm_value x) {
53
  return x & LBM_GC_MARKED;
54
}
55
56
static inline lbm_value lbm_set_gc_flag(lbm_value x) {
57
  return x | LBM_GC_MARKED;
58
}
59
60
static inline lbm_value lbm_clr_gc_flag(lbm_value x) {
61
  return x & ~LBM_GC_MASK;
62
}
63
64
65
lbm_heap_state_t lbm_heap_state;
66
67
lbm_const_heap_t *lbm_const_heap_state;
68
69
lbm_cons_t *lbm_heaps[2] = {NULL, NULL};
70
71
static mutex_t lbm_const_heap_mutex;
72
static bool    lbm_const_heap_mutex_initialized = false;
73
74
static mutex_t lbm_mark_mutex;
75
static bool    lbm_mark_mutex_initialized = false;
76
77
#ifdef USE_GC_PTR_REV
78
void lbm_gc_lock(void) {
79
  mutex_lock(&lbm_mark_mutex);
80
}
81
void lbm_gc_unlock(void) {
82
  mutex_unlock(&lbm_mark_mutex);
83
}
84
#else
85
void lbm_gc_lock(void) {
86
}
87
void lbm_gc_unlock(void) {
88
}
89
#endif
90
91
/****************************************************/
92
/* ENCODERS DECODERS                                */
93
94
2849162
lbm_value lbm_enc_i32(int32_t x) {
95
#ifndef LBM64
96
2849162
  lbm_value i = lbm_cons((lbm_uint)x, ENC_SYM_RAW_I_TYPE);
97
2849162
  if (lbm_type_of(i) == LBM_TYPE_SYMBOL) return i;
98
2839488
  return lbm_set_ptr_type(i, LBM_TYPE_I32);
99
#else
100
  return (((lbm_uint)x) << LBM_VAL_SHIFT) | LBM_TYPE_I32;
101
#endif
102
}
103
104
3682448
lbm_value lbm_enc_u32(uint32_t x) {
105
#ifndef LBM64
106
3682448
  lbm_value u = lbm_cons(x, ENC_SYM_RAW_U_TYPE);
107
3682448
  if (lbm_type_of(u) == LBM_TYPE_SYMBOL) return u;
108
3679620
  return lbm_set_ptr_type(u, LBM_TYPE_U32);
109
#else
110
  return (((lbm_uint)x) << LBM_VAL_SHIFT) | LBM_TYPE_U32;
111
#endif
112
}
113
114
19877
lbm_value lbm_enc_float(float x) {
115
#ifndef LBM64
116
  lbm_uint t;
117
19877
  memcpy(&t, &x, sizeof(lbm_float));
118
19877
  lbm_value f = lbm_cons(t, ENC_SYM_RAW_F_TYPE);
119
19877
  if (lbm_type_of(f) == LBM_TYPE_SYMBOL) return f;
120
19877
  return lbm_set_ptr_type(f, LBM_TYPE_FLOAT);
121
#else
122
  lbm_uint t = 0;
123
  memcpy(&t, &x, sizeof(float));
124
  return (((lbm_uint)t) << LBM_VAL_SHIFT) | LBM_TYPE_FLOAT;
125
#endif
126
}
127
128
#ifndef LBM64
129
8430394
static lbm_value enc_64_on_32(uint8_t *source, lbm_uint type_qual, lbm_uint type) {
130
8430394
  lbm_value res = lbm_cons(ENC_SYM_NIL,ENC_SYM_NIL);
131
8430394
  if (lbm_type_of(res) != LBM_TYPE_SYMBOL) {
132
8428072
    uint8_t* storage = lbm_malloc(sizeof(uint64_t));
133
8428072
    if (storage) {
134
8425364
      memcpy(storage,source, sizeof(uint64_t));
135
8425364
      lbm_set_car_and_cdr(res, (lbm_uint)storage,  type_qual);
136
8425364
      res = lbm_set_ptr_type(res, type);
137
    } else {
138
2708
      res = ENC_SYM_MERROR;
139
    }
140
  }
141
8430394
  return res;
142
}
143
#endif
144
145
4493656
lbm_value lbm_enc_i64(int64_t x) {
146
#ifndef LBM64
147
4493656
  return enc_64_on_32((uint8_t *)&x, ENC_SYM_IND_I_TYPE, LBM_TYPE_I64);
148
#else
149
  lbm_value u = lbm_cons((uint64_t)x, ENC_SYM_RAW_I_TYPE);
150
  if (lbm_type_of(u) == LBM_TYPE_SYMBOL) return u;
151
  return lbm_set_ptr_type(u, LBM_TYPE_I64);
152
#endif
153
}
154
155
3371030
lbm_value lbm_enc_u64(uint64_t x) {
156
#ifndef LBM64
157
3371030
  return enc_64_on_32((uint8_t *)&x, ENC_SYM_IND_U_TYPE, LBM_TYPE_U64);
158
#else
159
  lbm_value u = lbm_cons(x, ENC_SYM_RAW_U_TYPE);
160
  if (lbm_type_of(u) == LBM_TYPE_SYMBOL) return u;
161
  return lbm_set_ptr_type(u, LBM_TYPE_U64);
162
#endif
163
}
164
165
565708
lbm_value lbm_enc_double(double x) {
166
#ifndef LBM64
167
565708
  return enc_64_on_32((uint8_t *)&x, ENC_SYM_IND_F_TYPE, LBM_TYPE_DOUBLE);
168
#else
169
  lbm_uint t;
170
  memcpy(&t, &x, sizeof(double));
171
  lbm_value f = lbm_cons(t, ENC_SYM_RAW_F_TYPE);
172
  if (lbm_type_of(f) == LBM_TYPE_SYMBOL) return f;
173
  return lbm_set_ptr_type(f, LBM_TYPE_DOUBLE);
174
#endif
175
}
176
177
// Type specific (as opposed to the dec_as_X) functions
178
// should only be run on values KNOWN to represent a value of the type
179
// that the decoder decodes.
180
181
30521
float lbm_dec_float(lbm_value x) {
182
#ifndef LBM64
183
  float f_tmp;
184
30521
  lbm_uint tmp = lbm_car(x);
185
30521
  memcpy(&f_tmp, &tmp, sizeof(float));
186
30521
  return f_tmp;
187
#else
188
  uint32_t tmp = (uint32_t)(x >> LBM_VAL_SHIFT);
189
  float f_tmp;
190
  memcpy(&f_tmp, &tmp, sizeof(float));
191
  return f_tmp;
192
#endif
193
}
194
195
564784
double lbm_dec_double(lbm_value x) {
196
#ifndef LBM64
197
564784
  double d = 0.0;
198
564784
  if (lbm_is_ptr(x)) {
199
564784
    uint32_t *data = (uint32_t*)lbm_ref_cell(x)->car;
200
564784
    memcpy(&d, data, sizeof(double));
201
  }
202
564784
  return d;
203
#else
204
  double f_tmp;
205
  lbm_uint tmp = lbm_car(x);
206
  memcpy(&f_tmp, &tmp, sizeof(double));
207
  return f_tmp;
208
#endif
209
}
210
211
7013714
uint64_t lbm_dec_u64(lbm_value x) {
212
#ifndef LBM64
213
7013714
  uint64_t u = 0;
214
7013714
  if (lbm_is_ptr(x)) {
215
7013714
    uint32_t *data = (uint32_t*)lbm_ref_cell(x)->car;
216
7013714
    memcpy(&u, data, 8);
217
  }
218
7013714
  return u;
219
#else
220
  return (uint64_t)lbm_car(x);
221
#endif
222
}
223
224
9255920
int64_t lbm_dec_i64(lbm_value x) {
225
#ifndef LBM64
226
9255920
  int64_t i = 0;
227
9255920
  if (lbm_is_ptr(x)) {
228
9255920
    uint32_t *data = (uint32_t*)lbm_ref_cell(x)->car;
229
9255920
    memcpy(&i, data, 8);
230
  }
231
9255920
  return i;
232
#else
233
  return (int64_t)lbm_car(x);
234
#endif
235
}
236
237
790872
char *lbm_dec_str(lbm_value val) {
238
790872
  char *res = 0;
239
790872
  if (lbm_is_array_r(val)) {
240
790676
    lbm_array_header_t *array = (lbm_array_header_t *)lbm_car(val);
241
790676
    if (array) res = (char *)array->data;
242
  }
243
790872
  return res;
244
}
245
246
599126
lbm_array_header_t *lbm_dec_array_r(lbm_value val) {
247
599126
  lbm_array_header_t *array = NULL;
248
599126
  if (lbm_is_array_r(val)) {
249
597894
    array = (lbm_array_header_t *)lbm_car(val);
250
  }
251
599126
  return array;
252
}
253
254
3528
lbm_array_header_t *lbm_dec_array_rw(lbm_value val) {
255
3528
  lbm_array_header_t *array = NULL;
256
3528
  if (lbm_is_array_rw(val)) {
257
3444
    array = (lbm_array_header_t *)lbm_car(val);
258
  }
259
3528
  return array;
260
}
261
262
28
lbm_array_header_t *lbm_dec_lisp_array_r(lbm_value val) {
263
28
  lbm_array_header_t *array = NULL;
264
28
  if (lbm_is_lisp_array_r(val)) {
265
28
    array = (lbm_array_header_t *)lbm_car(val);
266
  }
267
28
  return array;
268
}
269
270
lbm_array_header_t *lbm_dec_lisp_array_rw(lbm_value val) {
271
  lbm_array_header_t *array = NULL;
272
  if (lbm_is_lisp_array_rw(val)) {
273
    array = (lbm_array_header_t *)lbm_car(val);
274
  }
275
  return array;
276
}
277
278
11020237
lbm_char_channel_t *lbm_dec_channel(lbm_value val) {
279
11020237
  lbm_char_channel_t *res = NULL;
280
281
11020237
  if (lbm_type_of(val) == LBM_TYPE_CHANNEL) {
282
11020237
    res = (lbm_char_channel_t *)lbm_car(val);
283
  }
284
11020237
  return res;
285
}
286
287
lbm_uint lbm_dec_custom(lbm_value val) {
288
  lbm_uint res = 0;
289
  if (lbm_type_of(val) == LBM_TYPE_CUSTOM) {
290
    res = (lbm_uint)lbm_car(val);
291
  }
292
  return res;
293
}
294
295
60872
uint8_t lbm_dec_as_char(lbm_value a) {
296
60872
  uint8_t r = 0;
297


60872
  switch (lbm_type_of_functional(a)) {
298
60648
  case LBM_TYPE_CHAR:
299
60648
    r = (uint8_t)lbm_dec_char(a); break;
300
28
  case LBM_TYPE_I:
301
28
    r = (uint8_t)lbm_dec_i(a); break;
302
28
  case LBM_TYPE_U:
303
28
    r = (uint8_t)lbm_dec_u(a); break;
304
28
  case LBM_TYPE_I32:
305
28
    r = (uint8_t)lbm_dec_i32(a); break;
306
28
  case LBM_TYPE_U32:
307
28
    r = (uint8_t)lbm_dec_u32(a); break;
308
28
  case LBM_TYPE_FLOAT:
309
28
    r = (uint8_t)lbm_dec_float(a); break;
310
28
  case LBM_TYPE_I64:
311
28
    r = (uint8_t)lbm_dec_i64(a); break;
312
28
  case LBM_TYPE_U64:
313
28
    r = (uint8_t)lbm_dec_u64(a); break;
314
28
  case LBM_TYPE_DOUBLE:
315
28
    r = (uint8_t) lbm_dec_double(a); break;
316
  }
317
60872
  return r;
318
}
319
320
8421342
uint32_t lbm_dec_as_u32(lbm_value a) {
321
8421342
  uint32_t r = 0;
322


8421342
  switch (lbm_type_of_functional(a)) {
323
561938
  case LBM_TYPE_CHAR:
324
561938
    r = (uint32_t)lbm_dec_char(a); break;
325
1275571
  case LBM_TYPE_I:
326
1275571
    r = (uint32_t)lbm_dec_i(a); break;
327
1786599
  case LBM_TYPE_U:
328
1786599
    r = (uint32_t)lbm_dec_u(a); break;
329
4795104
  case LBM_TYPE_I32: /* fall through */
330
  case LBM_TYPE_U32:
331
4795104
    r = (uint32_t)lbm_dec_u32(a); break;
332
28
  case LBM_TYPE_FLOAT:
333
28
    r = (uint32_t)lbm_dec_float(a); break;
334
28
  case LBM_TYPE_I64:
335
28
    r = (uint32_t)lbm_dec_i64(a); break;
336
84
  case LBM_TYPE_U64:
337
84
    r = (uint32_t)lbm_dec_u64(a); break;
338
28
  case LBM_TYPE_DOUBLE:
339
28
    r = (uint32_t)lbm_dec_double(a); break;
340
  }
341
8421342
  return r;
342
}
343
344
206218238
int32_t lbm_dec_as_i32(lbm_value a) {
345
206218238
  int32_t r = 0;
346


206218238
  switch (lbm_type_of_functional(a)) {
347
5809904
  case LBM_TYPE_CHAR:
348
5809904
    r = (int32_t)lbm_dec_char(a); break;
349
196725550
  case LBM_TYPE_I:
350
196725550
    r = (int32_t)lbm_dec_i(a); break;
351
196
  case LBM_TYPE_U:
352
196
    r = (int32_t)lbm_dec_u(a); break;
353
3674140
  case LBM_TYPE_I32:
354
3674140
    r = (int32_t)lbm_dec_i32(a); break;
355
28
  case LBM_TYPE_U32:
356
28
    r = (int32_t)lbm_dec_u32(a); break;
357
28
  case LBM_TYPE_FLOAT:
358
28
    r = (int32_t)lbm_dec_float(a); break;
359
56
  case LBM_TYPE_I64:
360
56
    r = (int32_t)lbm_dec_i64(a); break;
361
56
  case LBM_TYPE_U64:
362
56
    r = (int32_t)lbm_dec_u64(a); break;
363
28
  case LBM_TYPE_DOUBLE:
364
28
    r = (int32_t) lbm_dec_double(a); break;
365
  }
366
206218238
  return r;
367
}
368
369
6730604
int64_t lbm_dec_as_i64(lbm_value a) {
370
6730604
  int64_t r = 0;
371


6730604
  switch (lbm_type_of_functional(a)) {
372
562266
  case LBM_TYPE_CHAR:
373
562266
    r = (int64_t)lbm_dec_char(a); break;
374
1402938
  case LBM_TYPE_I:
375
1402938
    r = (int64_t)lbm_dec_i(a); break;
376
168
  case LBM_TYPE_U:
377
168
    r = (int64_t)lbm_dec_u(a); break;
378
168
  case LBM_TYPE_I32:
379
168
    r = (int64_t)lbm_dec_i32(a); break;
380
168
  case LBM_TYPE_U32:
381
168
    r = (int64_t)lbm_dec_u32(a); break;
382
56
  case LBM_TYPE_FLOAT:
383
56
    r = (int64_t)lbm_dec_float(a); break;
384
4764672
  case LBM_TYPE_I64:
385
4764672
    r = (int64_t)lbm_dec_i64(a); break;
386
112
  case LBM_TYPE_U64:
387
112
    r = (int64_t)lbm_dec_u64(a); break;
388
56
  case LBM_TYPE_DOUBLE:
389
56
    r = (int64_t) lbm_dec_double(a); break;
390
  }
391
6730604
  return r;
392
}
393
394
4488314
uint64_t lbm_dec_as_u64(lbm_value a) {
395
4488314
  uint64_t r = 0;
396


4488314
  switch (lbm_type_of_functional(a)) {
397
562238
  case LBM_TYPE_CHAR:
398
562238
    r = (uint64_t)lbm_dec_char(a); break;
399
280592
  case LBM_TYPE_I:
400
280592
    r = (uint64_t)lbm_dec_i(a); break;
401
168
  case LBM_TYPE_U:
402
168
    r = (uint64_t)lbm_dec_u(a); break;
403
168
  case LBM_TYPE_I32:
404
168
    r = (uint64_t)lbm_dec_i32(a); break;
405
168
  case LBM_TYPE_U32:
406
168
    r = (uint64_t)lbm_dec_u32(a); break;
407
56
  case LBM_TYPE_FLOAT:
408
56
    r = (uint64_t)lbm_dec_float(a); break;
409
168
  case LBM_TYPE_I64:
410
168
    r = (uint64_t)lbm_dec_i64(a); break;
411
3644700
  case LBM_TYPE_U64:
412
3644700
    r = (uint64_t)lbm_dec_u64(a); break;
413
56
  case LBM_TYPE_DOUBLE:
414
56
    r = (uint64_t)lbm_dec_double(a); break;
415
  }
416
4488314
  return r;
417
}
418
419
2324
lbm_uint lbm_dec_as_uint(lbm_value a) {
420
2324
  lbm_uint r = 0;
421


2324
  switch (lbm_type_of_functional(a)) {
422
  case LBM_TYPE_CHAR:
423
    r = (lbm_uint)lbm_dec_char(a); break;
424
2324
  case LBM_TYPE_I:
425
2324
    r = (lbm_uint)lbm_dec_i(a); break;
426
  case LBM_TYPE_U:
427
    r = (lbm_uint)lbm_dec_u(a); break;
428
  case LBM_TYPE_I32:
429
    r = (lbm_uint)lbm_dec_i32(a); break;
430
  case LBM_TYPE_U32:
431
    r = (lbm_uint)lbm_dec_u32(a); break;
432
  case LBM_TYPE_FLOAT:
433
    r = (lbm_uint)lbm_dec_float(a); break;
434
  case LBM_TYPE_I64:
435
    r = (lbm_uint)lbm_dec_i64(a); break;
436
  case LBM_TYPE_U64:
437
    r = (lbm_uint) lbm_dec_u64(a); break;
438
  case LBM_TYPE_DOUBLE:
439
    r = (lbm_uint)lbm_dec_double(a); break;
440
  }
441
2324
  return r;
442
}
443
444
644
lbm_int lbm_dec_as_int(lbm_value a) {
445
644
  lbm_int r = 0;
446


644
  switch (lbm_type_of_functional(a)) {
447
  case LBM_TYPE_CHAR:
448
    r = (lbm_int)lbm_dec_char(a); break;
449
644
  case LBM_TYPE_I:
450
644
    r = (lbm_int)lbm_dec_i(a); break;
451
  case LBM_TYPE_U:
452
    r = (lbm_int)lbm_dec_u(a); break;
453
  case LBM_TYPE_I32:
454
    r = (lbm_int)lbm_dec_i32(a); break;
455
  case LBM_TYPE_U32:
456
    r = (lbm_int)lbm_dec_u32(a); break;
457
  case LBM_TYPE_FLOAT:
458
    r = (lbm_int)lbm_dec_float(a); break;
459
  case LBM_TYPE_I64:
460
    r = (lbm_int)lbm_dec_i64(a); break;
461
  case LBM_TYPE_U64:
462
    r = (lbm_int)lbm_dec_u64(a); break;
463
  case LBM_TYPE_DOUBLE:
464
    r = (lbm_int)lbm_dec_double(a); break;
465
  }
466
644
  return r;
467
}
468
469
17053
float lbm_dec_as_float(lbm_value a) {
470
17053
  float r = 0;
471


17053
  switch (lbm_type_of_functional(a)) {
472
1092
  case LBM_TYPE_CHAR:
473
1092
    r = (float)lbm_dec_char(a); break;
474
1624
  case LBM_TYPE_I:
475
1624
    r = (float)lbm_dec_i(a); break;
476
140
  case LBM_TYPE_U:
477
140
    r = (float)lbm_dec_u(a); break;
478
140
  case LBM_TYPE_I32:
479
140
    r = (float)lbm_dec_i32(a); break;
480
196
  case LBM_TYPE_U32:
481
196
    r = (float)lbm_dec_u32(a); break;
482
13553
  case LBM_TYPE_FLOAT:
483
13553
    r = (float)lbm_dec_float(a); break;
484
140
  case LBM_TYPE_I64:
485
140
    r = (float)lbm_dec_i64(a); break;
486
140
  case LBM_TYPE_U64:
487
140
    r = (float)lbm_dec_u64(a); break;
488
28
  case LBM_TYPE_DOUBLE:
489
28
    r = (float)lbm_dec_double(a); break;
490
  }
491
17053
  return r;
492
}
493
494
563944
double lbm_dec_as_double(lbm_value a) {
495
563944
  double r = 0;
496


563944
  switch (lbm_type_of_functional(a)) {
497
281168
  case LBM_TYPE_CHAR:
498
281168
    r = (double)lbm_dec_char(a); break;
499
280620
  case LBM_TYPE_I:
500
280620
    r = (double)lbm_dec_i(a); break;
501
140
  case LBM_TYPE_U:
502
140
    r = (double)lbm_dec_u(a); break;
503
140
  case LBM_TYPE_I32:
504
140
    r = (double)lbm_dec_i32(a); break;
505
140
  case LBM_TYPE_U32:
506
140
    r = (double)lbm_dec_u32(a); break;
507
364
  case LBM_TYPE_FLOAT:
508
364
    r = (double)lbm_dec_float(a); break;
509
140
  case LBM_TYPE_I64:
510
140
    r = (double)lbm_dec_i64(a); break;
511
140
  case LBM_TYPE_U64:
512
140
    r = (double)lbm_dec_u64(a); break;
513
1092
  case LBM_TYPE_DOUBLE:
514
1092
    r = (double)lbm_dec_double(a); break;
515
  }
516
563944
  return r;
517
}
518
519
/****************************************************/
520
/* HEAP MANAGEMENT                                  */
521
522
21588
static int generate_freelist(size_t num_cells) {
523
21588
  size_t i = 0;
524
525
21588
  if (!lbm_heap_state.heap) return 0;
526
527
21588
  lbm_heap_state.freelist = lbm_enc_cons_ptr(0);
528
529
  lbm_cons_t *t;
530
531
  // Add all cells to free list
532
200534016
  for (i = 1; i < num_cells; i ++) {
533
200512428
    t = lbm_ref_cell(lbm_enc_cons_ptr(i-1));
534
200512428
    t->car = ENC_SYM_RECOVERED;    // all cars in free list are "RECOVERED"
535
200512428
    t->cdr = lbm_enc_cons_ptr(i);
536
  }
537
538
  // Replace the incorrect pointer at the last cell.
539
21588
  t = lbm_ref_cell(lbm_enc_cons_ptr(num_cells-1));
540
21588
  t->cdr = ENC_SYM_NIL;
541
542
21588
  return 1;
543
}
544
545
347733
void lbm_nil_freelist(void) {
546
347733
  lbm_heap_state.freelist = ENC_SYM_NIL;
547
347733
  lbm_heap_state.num_alloc = lbm_heap_state.heap_size;
548
347733
}
549
550
21588
static void heap_init_state(lbm_cons_t *addr, lbm_uint num_cells,
551
                            lbm_uint* gc_stack_storage, lbm_uint gc_stack_size) {
552
21588
  lbm_heap_state.heap         = addr;
553
21588
  lbm_heap_state.heap_bytes   = (unsigned int)(num_cells * sizeof(lbm_cons_t));
554
21588
  lbm_heap_state.heap_size    = num_cells;
555
556
21588
  lbm_stack_create(&lbm_heap_state.gc_stack, gc_stack_storage, gc_stack_size);
557
558
21588
  lbm_heap_state.num_alloc           = 0;
559
21588
  lbm_heap_state.num_alloc_arrays    = 0;
560
21588
  lbm_heap_state.gc_num              = 0;
561
21588
  lbm_heap_state.gc_marked           = 0;
562
21588
  lbm_heap_state.gc_recovered        = 0;
563
21588
  lbm_heap_state.gc_recovered_arrays = 0;
564
21588
  lbm_heap_state.gc_least_free       = num_cells;
565
21588
  lbm_heap_state.gc_last_free        = num_cells;
566
21588
}
567
568
347733
void lbm_heap_new_freelist_length(void) {
569
347733
  lbm_uint l = lbm_heap_state.heap_size - lbm_heap_state.num_alloc;
570
347733
  lbm_heap_state.gc_last_free = l;
571
347733
  if (l < lbm_heap_state.gc_least_free)
572
3834
    lbm_heap_state.gc_least_free = l;
573
347733
}
574
575
21588
int lbm_heap_init(lbm_cons_t *addr, lbm_uint num_cells,
576
                  lbm_uint gc_stack_size) {
577
578
21588
  if (((uintptr_t)addr % 8) != 0) return 0;
579
580
21588
  memset(addr,0, sizeof(lbm_cons_t) * num_cells);
581
582
21588
  lbm_uint *gc_stack_storage = (lbm_uint*)lbm_malloc(gc_stack_size * sizeof(lbm_uint));
583
21588
  if (gc_stack_storage == NULL) return 0;
584
585
21588
  heap_init_state(addr, num_cells,
586
                  gc_stack_storage, gc_stack_size);
587
588
21588
  lbm_heaps[0] = addr;
589
590
21588
  return generate_freelist(num_cells);
591
}
592
593
594
365203540
lbm_value lbm_heap_allocate_cell(lbm_type ptr_type, lbm_value car, lbm_value cdr) {
595
  lbm_value r;
596
365203540
  lbm_value cell = lbm_heap_state.freelist;
597
365203540
  if (cell) {
598
365153048
    lbm_uint heap_ix = lbm_dec_ptr(cell);
599
365153048
    lbm_heap_state.freelist = lbm_heap_state.heap[heap_ix].cdr;
600
365153048
    lbm_heap_state.num_alloc++;
601
365153048
    lbm_heap_state.heap[heap_ix].car = car;
602
365153048
    lbm_heap_state.heap[heap_ix].cdr = cdr;
603
365153048
    r = lbm_set_ptr_type(cell, ptr_type);
604
  } else {
605
50492
    r = ENC_SYM_MERROR;
606
  }
607
365203540
  return r;
608
}
609
610
1255054
lbm_value lbm_heap_allocate_list(lbm_uint n) {
611
1255054
  if (n == 0) return ENC_SYM_NIL;
612
1251750
  if (lbm_heap_num_free() < n) return ENC_SYM_MERROR;
613
614
1250486
  lbm_value curr = lbm_heap_state.freelist;
615
1250486
  lbm_value res  = curr;
616
1250486
  if (lbm_type_of(curr) == LBM_TYPE_CONS) {
617
618
1250486
    lbm_cons_t *c_cell = NULL;
619
1250486
    lbm_uint count = 0;
620
    do {
621
6465422
      c_cell = lbm_ref_cell(curr);
622
6465422
      c_cell->car = ENC_SYM_NIL;
623
6465422
      curr = c_cell->cdr;
624
6465422
      count ++;
625
6465422
    } while (count < n);
626
1250486
    lbm_heap_state.freelist = curr;
627
1250486
    c_cell->cdr = ENC_SYM_NIL;
628
1250486
    lbm_heap_state.num_alloc+=count;
629
1250486
    return res;
630
  }
631
  return ENC_SYM_FATAL_ERROR;
632
}
633
634
622254
lbm_value lbm_heap_allocate_list_init_va(unsigned int n, va_list valist) {
635
622254
  if (n == 0) return ENC_SYM_NIL;
636
622254
  if (lbm_heap_num_free() < n) return ENC_SYM_MERROR;
637
638
618044
  lbm_value curr = lbm_heap_state.freelist;
639
618044
  lbm_value res  = curr;
640
618044
  if (lbm_type_of(curr) == LBM_TYPE_CONS) {
641
642
618044
    lbm_cons_t *c_cell = NULL;
643
618044
    unsigned int count = 0;
644
    do {
645
1519224
      c_cell = lbm_ref_cell(curr);
646
1519224
      c_cell->car = va_arg(valist, lbm_value);
647
1519224
      curr = c_cell->cdr;
648
1519224
      count ++;
649
1519224
    } while (count < n);
650
618044
    lbm_heap_state.freelist = curr;
651
618044
    c_cell->cdr = ENC_SYM_NIL;
652
618044
    lbm_heap_state.num_alloc+=count;
653
618044
    return res;
654
  }
655
  return ENC_SYM_FATAL_ERROR;
656
}
657
658
622254
lbm_value lbm_heap_allocate_list_init(unsigned int n, ...) {
659
    va_list valist;
660
622254
    va_start(valist, n);
661
622254
    lbm_value r = lbm_heap_allocate_list_init_va(n, valist);
662
622254
    va_end(valist);
663
622254
    return r;
664
}
665
666
lbm_uint lbm_heap_num_allocated(void) {
667
  return lbm_heap_state.num_alloc;
668
}
669
lbm_uint lbm_heap_size(void) {
670
  return lbm_heap_state.heap_size;
671
}
672
673
lbm_uint lbm_heap_size_bytes(void) {
674
  return lbm_heap_state.heap_bytes;
675
}
676
677
252
void lbm_get_heap_state(lbm_heap_state_t *res) {
678
252
  *res = lbm_heap_state;
679
252
}
680
681
lbm_uint lbm_get_gc_stack_max(void) {
682
  return lbm_get_max_stack(&lbm_heap_state.gc_stack);
683
}
684
685
lbm_uint lbm_get_gc_stack_size(void) {
686
  return lbm_heap_state.gc_stack.size;
687
}
688
689
#ifdef USE_GC_PTR_REV
690
/* ************************************************************
691
   Deutch-Schorr-Waite (DSW) pointer reversal GC for 2-ptr cells
692
   with a hack-solution for the lisp-array case (n-ptr cells).
693
694
   DSW visits each branch node 3 times compared to 2 times for
695
   the stack based recursive mark.
696
   Where the stack based recursive mark performs a stack push/pop,
697
   DSW rearranges the, current, prev, next and a ptr field on
698
   the heap.
699
700
   DSW changes the structure of the heap and it introduces an
701
   invalid pointer (LBM_PTR_NULL) temporarily during marking.
702
   Since the heap will be "messed up" while marking, a mutex
703
   is introuded to keep other processes out of the heap while
704
   marking.
705
706
   TODO: See if the extra index field in arrays can be used
707
   to mark arrays without resorting to recursive mark calls.
708
*/
709
710
static inline void value_assign(lbm_value *a, lbm_value b) {
711
  lbm_value a_old = *a & LBM_GC_MASK;
712
  *a = a_old | (b & ~LBM_GC_MASK);
713
}
714
715
void lbm_gc_mark_phase_nm(lbm_value root) {
716
  bool work_to_do = true;
717
  if (!lbm_is_ptr(root)) return;
718
719
  lbm_value curr = root;
720
  lbm_value prev = lbm_enc_cons_ptr(LBM_PTR_NULL);
721
722
  while (work_to_do) {
723
    // follow leftwards pointers
724
    while (lbm_is_ptr(curr) &&
725
           (lbm_dec_ptr(curr) != LBM_PTR_NULL) &&
726
           ((curr & LBM_PTR_TO_CONSTANT_BIT) == 0) &&
727
           !lbm_get_gc_mark(lbm_cdr(curr))) {
728
      // Mark the cell if not a constant cell
729
      lbm_cons_t *cell = lbm_ref_cell(curr);
730
      cell->cdr = lbm_set_gc_mark(cell->cdr);
731
      if (lbm_is_cons_rw(curr)) {
732
        lbm_value next = 0;
733
        value_assign(&next, cell->car);
734
        value_assign(&cell->car, prev);
735
        value_assign(&prev,curr);
736
        value_assign(&curr, next);
737
      } else if (lbm_type_of(curr) == LBM_TYPE_LISPARRAY) {
738
        lbm_array_header_extended_t *arr = (lbm_array_header_extended_t*)cell->car;
739
        lbm_value *arr_data = (lbm_value *)arr->data;
740
        size_t  arr_size = (size_t)arr->size / sizeof(lbm_value);
741
        // C stack recursion as deep as there are nested arrays.
742
        // TODO: Try to do this without recursion on the C side.
743
        for (size_t i = 0; i < arr_size; i ++) {
744
          lbm_gc_mark_phase_nm(arr_data[i]);
745
        }
746
      }
747
      // Will jump out next iteration as gc mark is set in curr.
748
    }
749
    while (lbm_is_ptr(prev) &&
750
           (lbm_dec_ptr(prev) != LBM_PTR_NULL) &&
751
           lbm_get_gc_flag(lbm_car(prev)) ) {
752
      // clear the flag
753
      lbm_cons_t *cell = lbm_ref_cell(prev);
754
      cell->car = lbm_clr_gc_flag(cell->car);
755
      lbm_value next = 0;
756
      value_assign(&next, cell->cdr);
757
      value_assign(&cell->cdr, curr);
758
      value_assign(&curr, prev);
759
      value_assign(&prev, next);
760
    }
761
    if (lbm_is_ptr(prev) &&
762
        lbm_dec_ptr(prev) == LBM_PTR_NULL) {
763
      work_to_do = false;
764
    } else if (lbm_is_ptr(prev)) {
765
      // set the flag
766
      lbm_cons_t *cell = lbm_ref_cell(prev);
767
      cell->car = lbm_set_gc_flag(cell->car);
768
      lbm_value next = 0;
769
      value_assign(&next, cell->car);
770
      value_assign(&cell->car, curr);
771
      value_assign(&curr, cell->cdr);
772
      value_assign(&cell->cdr, next);
773
    }
774
  }
775
}
776
777
void lbm_gc_mark_phase(lbm_value root) {
778
    mutex_lock(&lbm_const_heap_mutex);
779
    lbm_gc_mark_phase_nm(root);
780
    mutex_unlock(&lbm_const_heap_mutex);
781
}
782
783
#else
784
/* ************************************************************
785
   Explicit stack "recursive" mark phase
786
787
   Trees are marked in a left subtree before rigth subtree, car first then cdr,
788
   way to favor lisp lists. This means that stack will grow slowly when
789
   marking right-leaning (cdr-recursive) data-structures while left-leaning
790
   (car-recursive) structures uses a lot of stack.
791
792
   Lisp arrays contain an extra book-keeping field to keep track
793
   of how far into the array the marking process has gone.
794
795
   TODO: DSW should be used as a last-resort if the GC stack is exhausted.
796
         If we use DSW as last-resort can we get away with a way smaller
797
         GC stack and unchanged performance (on sensible programs)?
798
*/
799
800
extern eval_context_t *ctx_running;
801
4810937
void lbm_gc_mark_phase(lbm_value root) {
802
  lbm_value t_ptr;
803
4810937
  lbm_stack_t *s = &lbm_heap_state.gc_stack;
804
4810937
  s->data[s->sp++] = root;
805
806
28305716
  while (!lbm_stack_is_empty(s)) {
807
    lbm_value curr;
808
23494779
    lbm_pop(s, &curr);
809
810
53585134
  mark_shortcut:
811
812
53585134
    if (!lbm_is_ptr(curr) ||
813
32490084
        (curr & LBM_PTR_TO_CONSTANT_BIT)) {
814
23035209
      continue;
815
    }
816
817
32490084
    lbm_cons_t *cell = &lbm_heap_state.heap[lbm_dec_ptr(curr)];
818
819
32490084
    if (lbm_get_gc_mark(cell->cdr)) {
820
1939277
      continue;
821
    }
822
823
30550807
    t_ptr = lbm_type_of(curr);
824
825
    // An array is marked in O(N) time using an additional 32bit
826
    // value per array that keeps track of how far into the array GC
827
    // has progressed.
828
30550807
    if (t_ptr == LBM_TYPE_LISPARRAY) {
829
1372
      lbm_push(s, curr); // put array back as bookkeeping.
830
1372
      lbm_array_header_extended_t *arr = (lbm_array_header_extended_t*)cell->car;
831
1372
      lbm_value *arrdata = (lbm_value *)arr->data;
832
1372
      uint32_t index = arr->index;
833
834
      // Potential optimization.
835
      // 1. CONS pointers are set to curr and recurse.
836
      // 2. Any other ptr is marked immediately and index is increased.
837

1372
      if (lbm_is_ptr(arrdata[index]) && ((arrdata[index] & LBM_PTR_TO_CONSTANT_BIT) == 0) &&
838
1162
          !((arrdata[index] & LBM_CONTINUATION_INTERNAL) == LBM_CONTINUATION_INTERNAL)) {
839
1008
        lbm_cons_t *elt = &lbm_heap_state.heap[lbm_dec_ptr(arrdata[index])];
840
1008
        if (!lbm_get_gc_mark(elt->cdr)) {
841
490
          curr = arrdata[index];
842
490
          goto mark_shortcut;
843
        }
844
      }
845
882
      if (index < ((arr->size/(sizeof(lbm_value))) - 1)) {
846
714
        arr->index++;
847
714
        continue;
848
      }
849
850
168
      arr->index = 0;
851
168
      cell->cdr = lbm_set_gc_mark(cell->cdr);
852
168
      lbm_heap_state.gc_marked ++;
853
168
      lbm_pop(s, &curr); // Remove array from GC stack as we are done marking it.
854
168
      continue;
855
30549435
    } else if (t_ptr == LBM_TYPE_CHANNEL) {
856
175040
      cell->cdr = lbm_set_gc_mark(cell->cdr);
857
175040
      lbm_heap_state.gc_marked ++;
858
      // TODO: Can channels be explicitly freed ?
859
175040
      if (cell->car != ENC_SYM_NIL) {
860
175040
        lbm_char_channel_t *chan = (lbm_char_channel_t *)cell->car;
861
175040
        curr = chan->dependency;
862
175040
        goto mark_shortcut;
863
      }
864
      continue;
865
    }
866
867
30374395
    cell->cdr = lbm_set_gc_mark(cell->cdr);
868
30374395
    lbm_heap_state.gc_marked ++;
869
870
30374395
    if (t_ptr == LBM_TYPE_CONS) {
871
29914825
      if (lbm_is_ptr(cell->cdr)) {
872
18682638
        if (!lbm_push(s, cell->cdr)) {
873
          lbm_critical_error();
874
          break;
875
        }
876
      }
877
29914825
      curr = cell->car;
878
29914825
      goto mark_shortcut; // Skip a push/pop
879
    }
880
  }
881
4810937
}
882
#endif
883
884
//Environments are proper lists with a 2 element list stored in each car.
885
11488347
void lbm_gc_mark_env(lbm_value env) {
886
11488347
  lbm_value curr = env;
887
  lbm_cons_t *c;
888
889
13112509
  while (lbm_is_ptr(curr)) {
890
1624162
    c = lbm_ref_cell(curr);
891
1624162
    c->cdr = lbm_set_gc_mark(c->cdr); // mark the environent list structure.
892
1624162
    lbm_cons_t *b = lbm_ref_cell(c->car);
893
1624162
    b->cdr = lbm_set_gc_mark(b->cdr); // mark the binding list head cell.
894
1624162
    lbm_gc_mark_phase(b->cdr);        // mark the bound object.
895
1624162
    lbm_heap_state.gc_marked +=2;
896
1624162
    curr = c->cdr;
897
  }
898
11488347
}
899
900
901
360891
void lbm_gc_mark_aux(lbm_uint *aux_data, lbm_uint aux_size) {
902
6482097
  for (lbm_uint i = 0; i < aux_size; i ++) {
903
6121206
    if (lbm_is_ptr(aux_data[i])) {
904
3701706
      lbm_type pt_t = lbm_type_of(aux_data[i]);
905
3701706
      lbm_uint pt_v = lbm_dec_ptr(aux_data[i]);
906

3701706
      if( pt_t >= LBM_POINTER_TYPE_FIRST &&
907
1823338
          pt_t <= LBM_POINTER_TYPE_LAST &&
908
1823338
          pt_v < lbm_heap_state.heap_size) {
909
1823338
        lbm_gc_mark_phase(aux_data[i]);
910
      }
911
    }
912
  }
913
360891
}
914
915
722946
void lbm_gc_mark_roots(lbm_uint *roots, lbm_uint num_roots) {
916
1810583
  for (lbm_uint i = 0; i < num_roots; i ++) {
917
1087637
    lbm_gc_mark_phase(roots[i]);
918
  }
919
722946
}
920
921
// Sweep moves non-marked heap objects to the free list.
922
347733
int lbm_gc_sweep_phase(void) {
923
347733
  unsigned int i = 0;
924
347733
  lbm_cons_t *heap = (lbm_cons_t *)lbm_heap_state.heap;
925
926
772375125
  for (i = 0; i < lbm_heap_state.heap_size; i ++) {
927
772027392
    if ( lbm_get_gc_mark(heap[i].cdr)) {
928
33631697
      heap[i].cdr = lbm_clr_gc_mark(heap[i].cdr);
929
    } else {
930
      // Check if this cell is a pointer to an array
931
      // and free it.
932
738395695
      if (lbm_type_of(heap[i].cdr) == LBM_TYPE_SYMBOL) {
933

52176013
        switch(heap[i].cdr) {
934
935
8364740
        case ENC_SYM_IND_I_TYPE: /* fall through */
936
        case ENC_SYM_IND_U_TYPE:
937
        case ENC_SYM_IND_F_TYPE:
938
8364740
          lbm_memory_free((lbm_uint*)heap[i].car);
939
8364740
          break;
940
1036
        case ENC_SYM_DEFRAG_ARRAY_TYPE:
941
1036
          lbm_defrag_mem_free((lbm_uint*)heap[i].car);
942
1036
          break;
943
294576
        case ENC_SYM_LISPARRAY_TYPE: /* fall through */
944
        case ENC_SYM_ARRAY_TYPE:{
945
294576
          lbm_array_header_t *arr = (lbm_array_header_t*)heap[i].car;
946
294576
          lbm_memory_free((lbm_uint *)arr->data);
947
294576
          lbm_heap_state.gc_recovered_arrays++;
948
294576
          lbm_memory_free((lbm_uint *)arr);
949
294576
        } break;
950
303744
        case ENC_SYM_CHANNEL_TYPE:{
951
303744
          lbm_char_channel_t *chan = (lbm_char_channel_t*)heap[i].car;
952
303744
          lbm_memory_free((lbm_uint*)chan->state);
953
303744
          lbm_memory_free((lbm_uint*)chan);
954
303744
        } break;
955
        case ENC_SYM_CUSTOM_TYPE: {
956
          lbm_uint *t = (lbm_uint*)heap[i].car;
957
          lbm_custom_type_destroy(t);
958
          lbm_memory_free(t);
959
          } break;
960
28
        case ENC_SYM_DEFRAG_MEM_TYPE: {
961
28
          lbm_uint *ptr = (lbm_uint *)heap[i].car;
962
28
          lbm_defrag_mem_destroy(ptr);
963
28
          } break;
964
43211889
        default:
965
43211889
          break;
966
        }
967
686219682
      }
968
      // create pointer to use as new freelist
969
738395695
      lbm_uint addr = lbm_enc_cons_ptr(i);
970
971
      // Clear the "freed" cell.
972
738395695
      heap[i].car = ENC_SYM_RECOVERED;
973
738395695
      heap[i].cdr = lbm_heap_state.freelist;
974
738395695
      lbm_heap_state.freelist = addr;
975
738395695
      lbm_heap_state.num_alloc --;
976
738395695
      lbm_heap_state.gc_recovered ++;
977
    }
978
  }
979
347733
  return 1;
980
}
981
982
347733
void lbm_gc_state_inc(void) {
983
347733
  lbm_heap_state.gc_num ++;
984
347733
  lbm_heap_state.gc_recovered = 0;
985
347733
  lbm_heap_state.gc_marked = 0;
986
347733
}
987
988
// construct, alter and break apart
989
364545798
lbm_value lbm_cons(lbm_value car, lbm_value cdr) {
990
364545798
  return lbm_heap_allocate_cell(LBM_TYPE_CONS, car, cdr);
991
}
992
993
228980392
lbm_value lbm_car(lbm_value c){
994
995
228980392
  if (lbm_is_ptr(c) ){
996
228980224
    lbm_cons_t *cell = lbm_ref_cell(c);
997
228980224
    return cell->car;
998
  }
999
1000
168
  if (lbm_is_symbol_nil(c)) {
1001
168
    return c; // if nil, return nil.
1002
  }
1003
1004
  return ENC_SYM_TERROR;
1005
}
1006
1007
// TODO: Many comparisons "is this the nil symbol" can be
1008
// streamlined a bit. NIL is 0 and cannot be confused with any other
1009
// lbm_value.
1010
1011
45988
lbm_value lbm_caar(lbm_value c) {
1012
45988
  if (lbm_is_ptr(c)) {
1013
45988
    lbm_value tmp = lbm_ref_cell(c)->car;
1014
1015
45988
    if (lbm_is_ptr(tmp)) {
1016
45988
      return lbm_ref_cell(tmp)->car;
1017
    } else if (lbm_is_symbol_nil(tmp)) {
1018
      return tmp;
1019
    }
1020
  } else if (lbm_is_symbol_nil(c)){
1021
    return c;
1022
  }
1023
  return ENC_SYM_TERROR;
1024
}
1025
1026
1027
11620
lbm_value lbm_cadr(lbm_value c) {
1028
11620
  if (lbm_is_ptr(c)) {
1029
11620
    lbm_value tmp = lbm_ref_cell(c)->cdr;
1030
1031
11620
    if (lbm_is_ptr(tmp)) {
1032
11620
      return lbm_ref_cell(tmp)->car;
1033
    } else if (lbm_is_symbol_nil(tmp)) {
1034
      return tmp;
1035
    }
1036
  } else if (lbm_is_symbol_nil(c)) {
1037
    return c;
1038
  }
1039
  return ENC_SYM_TERROR;
1040
}
1041
1042
112504202
lbm_value lbm_cdr(lbm_value c){
1043
112504202
  if (lbm_is_ptr(c)) {
1044
111937594
    lbm_cons_t *cell = lbm_ref_cell(c);
1045
111937594
    return cell->cdr;
1046
  }
1047
566608
  if (lbm_is_symbol_nil(c)) {
1048
566608
    return ENC_SYM_NIL; // if nil, return nil.
1049
  }
1050
  return ENC_SYM_TERROR;
1051
}
1052
1053
lbm_value lbm_cddr(lbm_value c) {
1054
  if (lbm_is_ptr(c)) {
1055
    lbm_value tmp = lbm_ref_cell(c)->cdr;
1056
    if (lbm_is_ptr(tmp)) {
1057
      return lbm_ref_cell(tmp)->cdr;
1058
    }
1059
  }
1060
  if (lbm_is_symbol_nil(c)) {
1061
    return ENC_SYM_NIL;
1062
  }
1063
  return ENC_SYM_TERROR;
1064
}
1065
1066
6512756
int lbm_set_car(lbm_value c, lbm_value v) {
1067
6512756
  int r = 0;
1068
1069
6512756
  if (lbm_type_of(c) == LBM_TYPE_CONS) {
1070
6512728
    lbm_cons_t *cell = lbm_ref_cell(c);
1071
6512728
    cell->car = v;
1072
6512728
    r = 1;
1073
  }
1074
6512756
  return r;
1075
}
1076
1077
99149302
int lbm_set_cdr(lbm_value c, lbm_value v) {
1078
99149302
  int r = 0;
1079
99149302
  if (lbm_is_cons_rw(c)){
1080
98582778
    lbm_cons_t *cell = lbm_ref_cell(c);
1081
98582778
    cell->cdr = v;
1082
98582778
    r = 1;
1083
  }
1084
99149302
  return r;
1085
}
1086
1087
8428220
int lbm_set_car_and_cdr(lbm_value c, lbm_value car_val, lbm_value cdr_val) {
1088
8428220
  int r = 0;
1089
8428220
  if (lbm_is_cons_rw(c)) {
1090
8428220
    lbm_cons_t *cell = lbm_ref_cell(c);
1091
8428220
    cell->car = car_val;
1092
8428220
    cell->cdr = cdr_val;
1093
8428220
    r = 1;
1094
  }
1095
8428220
  return r;
1096
}
1097
1098
/* calculate length of a proper list */
1099
1248594
lbm_uint lbm_list_length(lbm_value c) {
1100
1248594
  lbm_uint len = 0;
1101
1102
7210750
  while (lbm_is_cons(c)){
1103
5962156
    len ++;
1104
5962156
    c = lbm_cdr(c);
1105
  }
1106
1248594
  return len;
1107
}
1108
1109
/* calculate the length of a list and check that each element
1110
   fullfills the predicate pred */
1111
unsigned int lbm_list_length_pred(lbm_value c, bool *pres, bool (*pred)(lbm_value)) {
1112
  bool res = true;
1113
  unsigned int len = 0;
1114
1115
  while (lbm_is_cons(c)){
1116
    len ++;
1117
    res = res && pred(lbm_car(c));
1118
    c = lbm_cdr(c);
1119
  }
1120
  *pres = res;
1121
  return len;
1122
}
1123
1124
/* reverse a proper list */
1125
lbm_value lbm_list_reverse(lbm_value list) {
1126
  if (lbm_type_of(list) == LBM_TYPE_SYMBOL) {
1127
    return list;
1128
  }
1129
1130
  lbm_value curr = list;
1131
1132
  lbm_value new_list = ENC_SYM_NIL;
1133
  while (lbm_is_cons(curr)) {
1134
1135
    new_list = lbm_cons(lbm_car(curr), new_list);
1136
    if (lbm_type_of(new_list) == LBM_TYPE_SYMBOL) {
1137
      return ENC_SYM_MERROR;
1138
    }
1139
    curr = lbm_cdr(curr);
1140
  }
1141
  return new_list;
1142
}
1143
1144
1960
lbm_value lbm_list_destructive_reverse(lbm_value list) {
1145
1960
  if (lbm_type_of(list) == LBM_TYPE_SYMBOL) {
1146
    return list;
1147
  }
1148
1960
  lbm_value curr = list;
1149
1960
  lbm_value last_cell = ENC_SYM_NIL;
1150
1151
7168
  while (lbm_is_cons_rw(curr)) {
1152
5208
    lbm_value next = lbm_cdr(curr);
1153
5208
    lbm_set_cdr(curr, last_cell);
1154
5208
    last_cell = curr;
1155
5208
    curr = next;
1156
  }
1157
1960
  return last_cell;
1158
}
1159
1160
1161
330056
lbm_value lbm_list_copy(int *m, lbm_value list) {
1162
330056
  lbm_value curr = list;
1163
330056
  lbm_uint n = lbm_list_length(list);
1164
330056
  lbm_uint copy_n = n;
1165

330056
  if (*m >= 0 && (lbm_uint)*m < n) {
1166
5414
    copy_n = (lbm_uint)*m;
1167
324642
  } else if (*m == -1) {
1168
295498
    *m = (int)n; // TODO: smaller range in target variable.
1169
  }
1170
330056
  if (copy_n == 0) return ENC_SYM_NIL;
1171
329832
  lbm_uint new_list = lbm_heap_allocate_list(copy_n);
1172
329832
  if (lbm_is_symbol(new_list)) return new_list;
1173
329244
  lbm_value curr_targ = new_list;
1174
1175

4089516
  while (lbm_is_cons(curr) && copy_n > 0) {
1176
3760272
    lbm_value v = lbm_car(curr);
1177
3760272
    lbm_set_car(curr_targ, v);
1178
3760272
    curr_targ = lbm_cdr(curr_targ);
1179
3760272
    curr = lbm_cdr(curr);
1180
3760272
    copy_n --;
1181
  }
1182
1183
329244
  return new_list;
1184
}
1185
1186
// Append for proper lists only
1187
// Destructive update of list1.
1188
23660
lbm_value lbm_list_append(lbm_value list1, lbm_value list2) {
1189
1190

47320
  if(lbm_is_list_rw(list1) &&
1191
23660
     lbm_is_list(list2)) {
1192
1193
23660
    lbm_value curr = list1;
1194
54782
    while(lbm_type_of(lbm_cdr(curr)) == LBM_TYPE_CONS) {
1195
31122
      curr = lbm_cdr(curr);
1196
    }
1197
23660
    if (lbm_is_symbol_nil(curr)) return list2;
1198
23632
    lbm_set_cdr(curr, list2);
1199
23632
    return list1;
1200
  }
1201
  return ENC_SYM_EERROR;
1202
}
1203
1204
84
lbm_value lbm_list_drop(unsigned int n, lbm_value ls) {
1205
84
  lbm_value curr = ls;
1206

784
  while (lbm_type_of_functional(curr) == LBM_TYPE_CONS &&
1207
         n > 0) {
1208
700
    curr = lbm_cdr(curr);
1209
700
    n --;
1210
  }
1211
84
  return curr;
1212
}
1213
1214
151040
lbm_value lbm_index_list(lbm_value l, int32_t n) {
1215
151040
  lbm_value curr = l;
1216
1217
151040
  if (n < 0) {
1218
112
    int32_t len = (int32_t)lbm_list_length(l);
1219
112
    n = len + n;
1220
112
    if (n < 0) return ENC_SYM_NIL;
1221
  }
1222
1223

227470
  while (lbm_is_cons(curr) &&
1224
          n > 0) {
1225
76430
    curr = lbm_cdr(curr);
1226
76430
    n --;
1227
  }
1228
151040
  if (lbm_is_cons(curr)) {
1229
151012
    return lbm_car(curr);
1230
  } else {
1231
28
    return ENC_SYM_NIL;
1232
  }
1233
}
1234
1235
// High-level arrays are just bytearrays but with a different tag and pointer type.
1236
// These arrays will be inspected by GC and the elements of the array will be marked.
1237
1238
// Arrays are part of the heap module because their lifespan is managed
1239
// by the garbage collector. The data in the array is not stored
1240
// in the "heap of cons cells".
1241
295956
int lbm_heap_allocate_array_base(lbm_value *res, bool byte_array, lbm_uint size){
1242
1243
295956
  lbm_uint tag = ENC_SYM_ARRAY_TYPE;
1244
295956
  lbm_uint type = LBM_TYPE_ARRAY;
1245
295956
  if (!byte_array) {
1246
812
      tag = ENC_SYM_LISPARRAY_TYPE;
1247
812
      type = LBM_TYPE_LISPARRAY;
1248
812
      size = sizeof(lbm_value) * size;
1249
  }
1250
295956
  lbm_array_header_t *array = NULL;
1251
295956
  if (byte_array) {
1252
295144
    array = (lbm_array_header_t*)lbm_malloc(sizeof(lbm_array_header_t));
1253
  } else {
1254
812
    array = (lbm_array_header_t*)lbm_malloc(sizeof(lbm_array_header_extended_t));
1255
  }
1256
1257
295956
  if (array == NULL) {
1258
58
    *res = ENC_SYM_MERROR;
1259
58
    return 0;
1260
  }
1261
295898
  array->data = NULL;
1262
295898
  if ( size > 0) {
1263
295814
    if (!byte_array) {
1264
812
      lbm_array_header_extended_t *ext_array = (lbm_array_header_extended_t*)array;
1265
812
      ext_array->index = 0;
1266
    }
1267
1268
295814
    array->data = (lbm_uint*)lbm_malloc(size);
1269
1270
295814
    if (array->data == NULL) {
1271
5952
      lbm_memory_free((lbm_uint*)array);
1272
5952
      *res = ENC_SYM_MERROR;
1273
5952
      return 0;
1274
    }
1275
    // It is more important to zero out high-level arrays.
1276
    // 0 is symbol NIL which is perfectly safe for the GC to inspect.
1277
289862
    memset(array->data, 0, size);
1278
  }
1279
289946
  array->size = size;
1280
1281
  // allocating a cell for array's heap-presence
1282
289946
  lbm_value cell = lbm_heap_allocate_cell(type, (lbm_uint) array, tag);
1283
289946
  if (cell == ENC_SYM_MERROR) {
1284
88
    lbm_memory_free((lbm_uint*)array->data);
1285
88
    lbm_memory_free((lbm_uint*)array);
1286
88
    *res = ENC_SYM_MERROR;
1287
88
    return 0;
1288
  }
1289
289858
  *res = cell;
1290
1291
289858
  lbm_heap_state.num_alloc_arrays ++;
1292
1293
289858
  return 1;
1294
}
1295
1296
295144
int lbm_heap_allocate_array(lbm_value *res, lbm_uint size){
1297
295144
  return lbm_heap_allocate_array_base(res, true, size);
1298
}
1299
1300
812
int lbm_heap_allocate_lisp_array(lbm_value *res, lbm_uint size) {
1301
812
  return lbm_heap_allocate_array_base(res, false, size);
1302
}
1303
1304
// Convert a C array into an lbm_array.
1305
// if the array is in LBM_MEMORY, the lifetime will be managed by the GC after lifting.
1306
int lbm_lift_array(lbm_value *value, char *data, lbm_uint num_elt) {
1307
1308
  lbm_array_header_t *array = NULL;
1309
  lbm_value cell = lbm_heap_allocate_cell(LBM_TYPE_CONS, ENC_SYM_NIL, ENC_SYM_ARRAY_TYPE);
1310
1311
  if (cell == ENC_SYM_MERROR) {
1312
    *value = cell;
1313
    return 0;
1314
  }
1315
1316
  array = (lbm_array_header_t*)lbm_malloc(sizeof(lbm_array_header_t));
1317
1318
  if (array == NULL) {
1319
    lbm_set_car_and_cdr(cell, ENC_SYM_NIL, ENC_SYM_NIL);
1320
    *value = ENC_SYM_MERROR;
1321
    return 0;
1322
  }
1323
1324
  array->data = (lbm_uint*)data;
1325
  array->size = num_elt;
1326
1327
  lbm_set_car(cell, (lbm_uint)array);
1328
1329
  cell = lbm_set_ptr_type(cell, LBM_TYPE_ARRAY);
1330
  *value = cell;
1331
  return 1;
1332
}
1333
1334
237384
lbm_int lbm_heap_array_get_size(lbm_value arr) {
1335
1336
237384
  lbm_int r = -1;
1337
237384
  lbm_array_header_t *header = lbm_dec_array_r(arr);
1338
237384
  if (header) {
1339
237384
    r = (lbm_int)header->size;
1340
  }
1341
237384
  return r;
1342
}
1343
1344
118692
const uint8_t *lbm_heap_array_get_data_ro(lbm_value arr) {
1345
118692
  uint8_t *r = NULL;
1346
118692
  lbm_array_header_t *header = lbm_dec_array_r(arr);
1347
118692
  if (header) {
1348
118692
    r = (uint8_t*)header->data;
1349
  }
1350
118692
  return r;
1351
}
1352
1353
uint8_t *lbm_heap_array_get_data_rw(lbm_value arr) {
1354
  uint8_t *r = NULL;
1355
  lbm_array_header_t *header = lbm_dec_array_rw(arr);
1356
  if (header) {
1357
    r = (uint8_t*)header->data;
1358
  }
1359
  return r;
1360
}
1361
1362
1363
/* Explicitly freeing an array.
1364
1365
   This is a highly unsafe operation and can only be safely
1366
   used if the heap cell that points to the array has not been made
1367
   accessible to the program.
1368
1369
   So This function can be used to free an array in case an array
1370
   is being constructed and some error case appears while doing so
1371
   If the array still have not become available it can safely be
1372
   "explicitly" freed.
1373
1374
   The problem is that if the "array" heap-cell is made available to
1375
   the program, this cell can easily be duplicated and we would have
1376
   to search the entire heap to find all cells pointing to the array
1377
   memory in question and "null"-them out before freeing the memory
1378
*/
1379
1380
112
int lbm_heap_explicit_free_array(lbm_value arr) {
1381
1382
112
  int r = 0;
1383

112
  if (lbm_is_array_rw(arr) && lbm_cdr(arr) == ENC_SYM_ARRAY_TYPE) {
1384
112
    lbm_array_header_t *header = (lbm_array_header_t*)lbm_car(arr);
1385
112
    if (header == NULL) {
1386
      return 0;
1387
    }
1388
112
    lbm_memory_free((lbm_uint*)header->data);
1389
112
    lbm_memory_free((lbm_uint*)header);
1390
1391
112
    arr = lbm_set_ptr_type(arr, LBM_TYPE_CONS);
1392
112
    lbm_set_car(arr, ENC_SYM_NIL);
1393
112
    lbm_set_cdr(arr, ENC_SYM_NIL);
1394
112
    r = 1;
1395
  }
1396
1397
112
  return r;
1398
}
1399
1400
lbm_uint lbm_size_of(lbm_type t) {
1401
  lbm_uint s = 0;
1402
  switch(t) {
1403
  case LBM_TYPE_BYTE:
1404
    s = 1;
1405
    break;
1406
  case LBM_TYPE_I: /* fall through */
1407
  case LBM_TYPE_U:
1408
  case LBM_TYPE_SYMBOL:
1409
    s = sizeof(lbm_uint);
1410
    break;
1411
  case LBM_TYPE_I32: /* fall through */
1412
  case LBM_TYPE_U32:
1413
  case LBM_TYPE_FLOAT:
1414
    s = 4;
1415
    break;
1416
  case LBM_TYPE_I64: /* fall through */
1417
  case LBM_TYPE_U64:
1418
  case LBM_TYPE_DOUBLE:
1419
    s = 8;
1420
    break;
1421
  }
1422
  return s;
1423
}
1424
1425
static bool dummy_flash_write(lbm_uint ix, lbm_uint val) {
1426
  (void)ix;
1427
  (void)val;
1428
  return false;
1429
}
1430
1431
static const_heap_write_fun const_heap_write = dummy_flash_write;
1432
1433
21588
int lbm_const_heap_init(const_heap_write_fun w_fun,
1434
                        lbm_const_heap_t *heap,
1435
                        lbm_uint *addr,
1436
                        lbm_uint  num_words) {
1437
21588
  if (((uintptr_t)addr % 4) != 0) return 0;
1438
21588
  if ((num_words % 2) != 0) return 0;
1439
1440
21588
  if (!lbm_const_heap_mutex_initialized) {
1441
21588
    mutex_init(&lbm_const_heap_mutex);
1442
21588
    lbm_const_heap_mutex_initialized = true;
1443
  }
1444
1445
21588
  if (!lbm_mark_mutex_initialized) {
1446
21588
    mutex_init(&lbm_mark_mutex);
1447
21588
    lbm_mark_mutex_initialized = true;
1448
  }
1449
1450
21588
  const_heap_write = w_fun;
1451
1452
21588
  heap->heap = addr;
1453
21588
  heap->size = num_words;
1454
21588
  heap->next = 0;
1455
1456
21588
  lbm_const_heap_state = heap;
1457
  // ref_cell views the lbm_uint array as an lbm_cons_t array
1458
21588
  lbm_heaps[1] = (lbm_cons_t*)addr;
1459
21588
  return 1;
1460
}
1461
1462
2408
lbm_flash_status lbm_allocate_const_cell(lbm_value *res) {
1463
2408
  lbm_flash_status r = LBM_FLASH_FULL;
1464
1465
2408
  mutex_lock(&lbm_const_heap_mutex);
1466
  // waste a cell if we have ended up unaligned after writing an array to flash.
1467
2408
  if (lbm_const_heap_state->next % 2 == 1) {
1468
28
    lbm_const_heap_state->next++;
1469
  }
1470
1471
2408
  if (lbm_const_heap_state &&
1472
2408
      (lbm_const_heap_state->next+1) < lbm_const_heap_state->size) {
1473
    // A cons cell uses two words.
1474
2408
    lbm_value cell = lbm_const_heap_state->next;
1475
2408
    lbm_const_heap_state->next += 2;
1476
2408
    *res = (cell << LBM_ADDRESS_SHIFT) | LBM_PTR_BIT | LBM_TYPE_CONS | LBM_PTR_TO_CONSTANT_BIT;
1477
2408
    r = LBM_FLASH_WRITE_OK;
1478
  }
1479
2408
  mutex_unlock(&lbm_const_heap_mutex);
1480
2408
  return r;
1481
}
1482
1483
28
lbm_flash_status lbm_allocate_const_raw(lbm_uint nwords, lbm_uint *res) {
1484
28
  lbm_flash_status r = LBM_FLASH_FULL;
1485
1486
28
  if (lbm_const_heap_state &&
1487
28
      (lbm_const_heap_state->next + nwords) < lbm_const_heap_state->size) {
1488
28
    lbm_uint ix = lbm_const_heap_state->next;
1489
28
    *res = (lbm_uint)&lbm_const_heap_state->heap[ix];
1490
28
    lbm_const_heap_state->next += nwords;
1491
28
    r = LBM_FLASH_WRITE_OK;
1492
  }
1493
28
  return r;
1494
}
1495
1496
462
lbm_flash_status lbm_write_const_raw(lbm_uint *data, lbm_uint n, lbm_uint *res) {
1497
1498
462
  lbm_flash_status r = LBM_FLASH_FULL;
1499
1500
462
  if (lbm_const_heap_state &&
1501
462
      (lbm_const_heap_state->next + n) < lbm_const_heap_state->size) {
1502
462
    lbm_uint ix = lbm_const_heap_state->next;
1503
1504
1442
    for (unsigned int i = 0; i < n; i ++) {
1505
980
      if (!const_heap_write(ix + i, ((lbm_uint*)data)[i]))
1506
        return LBM_FLASH_WRITE_ERROR;
1507
    }
1508
462
    lbm_const_heap_state->next += n;
1509
462
    *res = (lbm_uint)&lbm_const_heap_state->heap[ix];
1510
462
    r = LBM_FLASH_WRITE_OK;
1511
  }
1512
462
  return r;
1513
}
1514
1515
84
lbm_flash_status lbm_const_write(lbm_uint *tgt, lbm_uint val) {
1516
1517
84
  if (lbm_const_heap_state) {
1518
84
    lbm_uint flash = (lbm_uint)lbm_const_heap_state->heap;
1519
84
    lbm_uint ix = (((lbm_uint)tgt - flash) / sizeof(lbm_uint)); // byte address to ix
1520
84
    if (const_heap_write(ix, val)) {
1521
84
      return LBM_FLASH_WRITE_OK;
1522
    }
1523
    return LBM_FLASH_WRITE_ERROR;
1524
  }
1525
  return LBM_FLASH_FULL;
1526
}
1527
1528
2408
lbm_flash_status write_const_cdr(lbm_value cell, lbm_value val) {
1529
2408
  lbm_uint addr = lbm_dec_ptr(cell);
1530
2408
  if (const_heap_write(addr+1, val))
1531
2408
    return LBM_FLASH_WRITE_OK;
1532
  return LBM_FLASH_WRITE_ERROR;
1533
}
1534
1535
2408
lbm_flash_status write_const_car(lbm_value cell, lbm_value val) {
1536
2408
  lbm_uint addr = lbm_dec_ptr(cell);
1537
2408
  if (const_heap_write(addr, val))
1538
2408
    return LBM_FLASH_WRITE_OK;
1539
  return LBM_FLASH_WRITE_ERROR;
1540
}
1541
1542
lbm_uint lbm_flash_memory_usage(void) {
1543
  return lbm_const_heap_state->next;
1544
}