GCC Code Coverage Report
Directory: ../src/ Exec Total Coverage
File: /home/joels/Current/lispbm/src/lbm_memory.c Lines: 228 254 89.8 %
Date: 2024-12-26 17:59:19 Branches: 95 133 71.4 %

Line Branch Exec Source
1
/*
2
    Copyright 2020 - 2024 Joel Svensson  svenssonjoel@yahoo.se
3
                     2024 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 <stdint.h>
20
#include <stdlib.h>
21
#include <stdio.h>
22
23
#include "lbm_memory.h"
24
#include "platform_mutex.h"
25
26
// pull in from eval_cps
27
void lbm_request_gc(void);
28
29
/* Status bit patterns */
30
#define FREE_OR_USED  0  //00b
31
#define END           1  //01b
32
#define START         2  //10b
33
#define START_END     3  //11b
34
35
/* States in memory_allocate state-machine*/
36
#define INIT                 0
37
#define FREE_LENGTH_CHECK    1
38
#define SKIP                 2
39
#define ALLOC_DONE           0xF00DF00D
40
#define ALLOC_FAILED         0xDEADBEAF
41
42
static lbm_uint *bitmap = NULL;
43
static lbm_uint *memory = NULL;
44
static lbm_uint memory_size;  // in 4 or 8 byte words depending on 32 or 64 bit platform
45
static lbm_uint bitmap_size;  // in 4 or 8 byte words
46
static lbm_uint memory_base_address = 0;
47
static lbm_uint memory_num_free = 0;
48
static lbm_uint memory_min_free = 0;
49
static volatile lbm_uint memory_reserve_level = 0;
50
static mutex_t lbm_mem_mutex;
51
static bool    lbm_mem_mutex_initialized;
52
static lbm_uint alloc_offset = 0;
53
54
43512
int lbm_memory_init(lbm_uint *data, lbm_uint data_size,
55
                    lbm_uint *bits, lbm_uint bits_size) {
56
57
43512
  if (!lbm_mem_mutex_initialized) {
58
21756
    mutex_init(&lbm_mem_mutex);
59
21756
    lbm_mem_mutex_initialized = true;
60
  }
61
62
43512
  alloc_offset = 0;
63
64
43512
  mutex_lock(&lbm_mem_mutex);
65
43512
  int res = 0;
66

43512
  if (data == NULL || bits == NULL) return 0;
67
68
43512
  if (((lbm_uint)data % sizeof(lbm_uint) != 0) ||
69
43512
      (data_size * 2) != (bits_size * sizeof(lbm_uint) * 8) ||
70
43512
      data_size % 4 != 0 ||
71

43512
      ((lbm_uint)bits % sizeof(lbm_uint) != 0) ||
72
43512
      bits_size < 1 ||
73
43512
      bits_size % 4 != 0) {
74
    // data is not aligned to sizeof lbm_uint
75
    // size is too small
76
    // or size is not a multiple of 4
77
  } else {
78
79
43512
    bitmap = bits;
80
43512
    bitmap_size = bits_size;
81
82
11182584
    for (lbm_uint i = 0; i < bitmap_size; i ++) {
83
11139072
      bitmap[i] = 0;
84
    }
85
86
43512
    memory = data;
87
43512
    memory_base_address = (lbm_uint)data;
88
43512
    memory_size = data_size;
89
43512
    memory_min_free = data_size;
90
43512
    memory_num_free = data_size;
91
43512
    memory_reserve_level = (lbm_uint)(0.1 * (lbm_float)data_size);
92
43512
    res = 1;
93
  }
94
43512
  mutex_unlock(&lbm_mem_mutex);
95
43512
  return res;
96
}
97
98
void lbm_memory_set_reserve(lbm_uint num_words) {
99
  memory_reserve_level = num_words;
100
}
101
102
lbm_uint lbm_memory_get_reserve(void) {
103
  return memory_reserve_level;
104
}
105
106
9686724
static inline lbm_uint address_to_bitmap_ix(lbm_uint *ptr) {
107
  #ifndef LBM64
108
9686724
  return ((lbm_uint)ptr - memory_base_address) >> 2;
109
  #else
110
  return ((lbm_uint)ptr - memory_base_address) >> 3;
111
  #endif
112
}
113
114
22792
lbm_int lbm_memory_address_to_ix(lbm_uint *ptr) {
115
  /* TODO: assuming that index
116
           will have more then enough room in the
117
           positive half of a 28bit integer */
118
22792
  return (lbm_int)address_to_bitmap_ix(ptr);
119
}
120
121
122
10410280
static inline lbm_uint *bitmap_ix_to_address(lbm_uint ix) {
123
10410280
  return &((lbm_uint*)(memory_base_address))[ix];// + (ix << 2));
124
}
125
126
#ifndef LBM64
127
#define WORD_IX_SHIFT 5
128
#define WORD_MOD_MASK 0x1F
129
#define BITMAP_SIZE_SHIFT 4  // 16 statuses per bitmap word
130
#else
131
#define WORD_IX_SHIFT 6      // divide by 64
132
#define WORD_MOD_MASK 0x3F   // mod 64
133
#define BITMAP_SIZE_SHIFT 5  // times 32, 32 statuses per bitmap word
134
#endif
135
136
280241894
static inline lbm_uint status(lbm_uint i) {
137
138
280241894
  lbm_uint ix = i << 1;                      // * 2
139
280241894
  lbm_uint word_ix = ix >> WORD_IX_SHIFT;    // / 32
140
280241894
  lbm_uint bit_ix  = ix & WORD_MOD_MASK;              // % 32
141
142
280241894
  lbm_uint mask = ((lbm_uint)3) << bit_ix;       // 000110..0
143
280241894
  if (word_ix > bitmap_size) {
144
    return (lbm_uint)NULL;
145
  }
146
280241894
  return (bitmap[word_ix] & mask) >> bit_ix;
147
}
148
149
40007218
static inline void set_status(lbm_uint i, lbm_uint status) {
150
40007218
  lbm_uint ix = i << 1;          // * 2
151
40007218
  lbm_uint word_ix = ix >> WORD_IX_SHIFT;    // / 32
152
40007218
  lbm_uint bit_ix  = ix & WORD_MOD_MASK;  // % 32
153
154
40007218
  lbm_uint clr_mask = ~(((lbm_uint)3) << bit_ix);
155
40007218
  lbm_uint mask = status << bit_ix;
156
157
40007218
  bitmap[word_ix] &= clr_mask;
158
40007218
  bitmap[word_ix] |= mask;
159
40007218
}
160
161
28
lbm_uint lbm_memory_num_words(void) {
162
28
  return memory_size;
163
}
164
165
44576
lbm_uint lbm_memory_num_free(void) {
166
44576
  return memory_num_free;
167
}
168
169
lbm_uint lbm_memory_maximum_used(void) {
170
  return (memory_size - memory_min_free);
171
}
172
173
347874
void lbm_memory_update_min_free(void) {
174
347874
  if (memory_num_free < memory_min_free)
175
3343
    memory_min_free = memory_num_free;
176
347874
}
177
178
3360
lbm_uint lbm_memory_longest_free(void) {
179

3360
  if (memory == NULL || bitmap == NULL) {
180
    return 0;
181
  }
182
3360
  mutex_lock(&lbm_mem_mutex);
183
3360
  unsigned int state = INIT;
184
3360
  lbm_uint max_length = 0;
185
186
3360
  lbm_uint curr_length = 0;
187
13765920
  for (unsigned int i = 0; i < (bitmap_size << BITMAP_SIZE_SHIFT); i ++) {
188
189

13762560
    switch(status(i)) {
190

13477520
    case FREE_OR_USED:
191
      switch (state) {
192
3808
      case INIT:
193
3808
        curr_length = 1;
194
3808
        if (curr_length > max_length) max_length = curr_length;
195
3808
        state = FREE_LENGTH_CHECK;
196
3808
        break;
197
10138828
      case FREE_LENGTH_CHECK:
198
10138828
        curr_length ++;
199
10138828
        if (curr_length > max_length) max_length = curr_length;
200
10138828
        state = FREE_LENGTH_CHECK;
201
10138828
        break;
202
3334884
      case SKIP:
203
3334884
        break;
204
      }
205
13477520
      break;
206
140504
    case END:
207
140504
      state = INIT;
208
140504
      break;
209
140504
    case START:
210
140504
      state = SKIP;
211
140504
      break;
212
4032
    case START_END:
213
4032
      state = INIT;
214
4032
      break;
215
    default:
216
      mutex_unlock(&lbm_mem_mutex);
217
      return 0;
218
      break;
219
    }
220
  }
221
3360
  mutex_unlock(&lbm_mem_mutex);
222
3360
  if (memory_num_free - max_length < memory_reserve_level) {
223
3360
    lbm_uint n = memory_reserve_level - (memory_num_free - max_length);
224
3360
    max_length -= n;
225
  }
226
3360
  return max_length;
227
}
228
229
10416126
static lbm_uint *lbm_memory_allocate_internal(lbm_uint num_words) {
230
231

10416126
  if (memory == NULL || bitmap == NULL) {
232
    return NULL;
233
  }
234
235
10416126
  mutex_lock(&lbm_mem_mutex);
236
237
10416126
  lbm_uint start_ix = 0;
238
10416126
  lbm_uint end_ix = 0;
239
10416126
  lbm_uint free_length = 0;
240
10416126
  unsigned int state = INIT;
241
10416126
  lbm_uint loop_max = (bitmap_size << BITMAP_SIZE_SHIFT);
242
243
101480546
  for (lbm_uint i = 0; i < loop_max; i ++) {
244

101474700
    switch(status(alloc_offset)) {
245

90219136
    case FREE_OR_USED:
246
      switch (state) {
247
10487048
      case INIT:
248
10487048
        start_ix = alloc_offset;
249
10487048
        if (num_words == 1) {
250
73352
          end_ix = alloc_offset;
251
73352
          state = ALLOC_DONE;
252
        } else {
253
10413696
          state = FREE_LENGTH_CHECK;
254
10413696
          free_length = 1;
255
        }
256
10487048
        break;
257
60077452
      case FREE_LENGTH_CHECK:
258
60077452
        free_length ++;
259
60077452
        if (free_length == num_words) {
260
10336928
          end_ix = alloc_offset;
261
10336928
          state = ALLOC_DONE;
262
        } else {
263
49740524
          state = FREE_LENGTH_CHECK;
264
        }
265
60077452
        break;
266
19654636
      case SKIP:
267
19654636
        break;
268
      }
269
90219136
      break;
270
10716780
    case END:
271
10716780
      state = INIT;
272
10716780
      break;
273
458774
    case START:
274
458774
      state = SKIP;
275
458774
      break;
276
80010
    case START_END:
277
80010
      state = INIT;
278
80010
      break;
279
    default: // error case
280
      mutex_unlock(&lbm_mem_mutex);
281
      return NULL;
282
    }
283
284
101474700
    if (state == ALLOC_DONE) break;
285
286
91064420
    alloc_offset++;
287
91064420
    if (alloc_offset == loop_max ) {
288
6182
      free_length = 0;
289
6182
      alloc_offset = 0;
290
6182
      state = INIT;
291
    }
292
  }
293
294
10416126
  if (state == ALLOC_DONE) {
295
10410280
    if (start_ix == end_ix) {
296
73352
      set_status(start_ix, START_END);
297
    } else {
298
10336928
      set_status(start_ix, START);
299
10336928
      set_status(end_ix, END);
300
    }
301
10410280
    memory_num_free -= num_words;
302
10410280
    mutex_unlock(&lbm_mem_mutex);
303
10410280
    return bitmap_ix_to_address(start_ix);
304
  }
305
5846
  mutex_unlock(&lbm_mem_mutex);
306
5846
  return NULL;
307
}
308
309
459620
lbm_uint *lbm_memory_allocate(lbm_uint num_words) {
310
459620
  if (memory_num_free - num_words < memory_reserve_level) {
311
    lbm_request_gc();
312
    return NULL;
313
  }
314
459620
  return lbm_memory_allocate_internal(num_words);
315
}
316
317
9701624
int lbm_memory_free(lbm_uint *ptr) {
318
9701624
  int r = 0;
319
9701624
  if (lbm_memory_ptr_inside(ptr)) {
320
9653942
    mutex_lock(&lbm_mem_mutex);
321
9653942
    lbm_uint ix = address_to_bitmap_ix(ptr);
322
9653942
    lbm_uint count_freed = 0;
323
9653942
    alloc_offset = ix;
324
9653942
    switch(status(ix)) {
325
9586088
    case START:
326
9586088
      set_status(ix, FREE_OR_USED);
327
46363876
      for (lbm_uint i = ix; i < (bitmap_size << BITMAP_SIZE_SHIFT); i ++) {
328
46363876
        count_freed ++;
329
46363876
        if (status(i) == END) {
330
9586088
          set_status(i, FREE_OR_USED);
331
9586088
          r = 1;
332
9586088
          break;
333
        }
334
      }
335
9586088
      break;
336
67854
    case START_END:
337
67854
      set_status(ix, FREE_OR_USED);
338
67854
      count_freed = 1;
339
67854
      r = 1;
340
67854
      break;
341
    default:
342
      break;
343
    }
344
9653942
    if (r) {
345

101289654
      while (alloc_offset > 0 && status(alloc_offset - 1) == FREE_OR_USED) {
346
91635712
        alloc_offset--;
347
      }
348
    }
349
9653942
    memory_num_free += count_freed;
350
9653942
    mutex_unlock(&lbm_mem_mutex);
351
  }
352
9701624
  return r;
353
}
354
//Malloc/free like interface
355
9911154
void* lbm_malloc(size_t size) {
356
9911154
  if (size == 0) return NULL;
357
  lbm_uint alloc_size;
358
359
9911154
  alloc_size = size / sizeof(lbm_uint);
360
9911154
  if (size % sizeof(lbm_uint)) alloc_size += 1;
361
362
9911154
  if (memory_num_free - alloc_size < memory_reserve_level) {
363
4280
    lbm_request_gc();
364
4280
    return NULL;
365
  }
366
9906874
  return lbm_memory_allocate_internal(alloc_size);
367
}
368
369
49632
void* lbm_malloc_reserve(size_t size) {
370
49632
  if (size == 0) return NULL;
371
  lbm_uint alloc_size;
372
373
49632
  alloc_size = size / sizeof(lbm_uint);
374
49632
  if (size % sizeof(lbm_uint)) alloc_size += 1;
375
376
49632
  if (memory_num_free - alloc_size < memory_reserve_level) {
377
88
    lbm_request_gc();
378
  }
379
49632
  return lbm_memory_allocate_internal(alloc_size);
380
}
381
382
39124
void lbm_free(void *ptr) {
383
39124
  lbm_memory_free(ptr);
384
39124
}
385
386
9990
int lbm_memory_shrink(lbm_uint *ptr, lbm_uint n) {
387

9990
  if (!lbm_memory_ptr_inside(ptr) || n == 0) return 0;
388
389
9990
  lbm_uint ix = address_to_bitmap_ix(ptr);
390
391
9990
  mutex_lock(&lbm_mem_mutex);
392
9990
  if (status(ix) == START_END) {
393
    mutex_unlock(&lbm_mem_mutex);
394
    return 1; // A one word arrays always succeeds at remaining at 1 word
395
  }
396
9990
  if (status(ix) != START) {
397
    mutex_unlock(&lbm_mem_mutex);
398
    return 0; // ptr does not point to the start of an allocated range.
399
  }
400
401
9990
  bool done = false;
402
9990
  unsigned int i = 0;
403
404
114620
  for (i = 0; i < ((bitmap_size << BITMAP_SIZE_SHIFT) - ix); i ++) {
405

114620
    if (status(ix+i) == END && i < n) {
406
      mutex_unlock(&lbm_mem_mutex);
407
      return 0; // cannot shrink allocation to a larger size
408
    }
409
410
114620
    if (i == (n-1)) {
411

19980
      if (status(ix+i) == END ||
412
9990
          status(ix+i) == START_END) {
413
        done = true;
414
      }
415
9990
      if (i == 0) {
416
1176
        set_status(ix+i, START_END);
417
      }
418
      else {
419
8814
        set_status(ix+i, END);
420
      }
421
9990
      break;
422
    }
423
  }
424
9990
  alloc_offset = ix+i;
425
426
9990
  lbm_uint count = 0;
427
9990
  if (!done) {
428
9990
    i++; // move to next position, prev position should be END or START_END
429
7542582
    for (;i < ((bitmap_size << BITMAP_SIZE_SHIFT) - ix); i ++) {
430
7542582
      count ++;
431
7542582
      if (status(ix+i) == END) {
432
9990
        set_status(ix+i, FREE_OR_USED);
433
9990
        break;
434
      }
435
    }
436
  }
437
438
9990
  memory_num_free += count;
439
9990
  mutex_unlock(&lbm_mem_mutex);
440
9990
  return 1;
441
}
442
443
9711614
int lbm_memory_ptr_inside(lbm_uint *ptr) {
444
19375546
  return ((lbm_uint)ptr >= (lbm_uint)memory &&
445
9663932
          (lbm_uint)ptr < (lbm_uint)memory + (memory_size * sizeof(lbm_uint)));
446
}