208 lines
5.2 KiB
C
208 lines
5.2 KiB
C
/* Copyright (C) 2024 Peter McGoron
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this program. If not, see
|
|
* <https://www.gnu.org/licenses/>.
|
|
*/
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <limits.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include "uns.h"
|
|
#include "internal/c89_relo.h"
|
|
#include "cheney_c89.h"
|
|
|
|
struct ctx {
|
|
unsigned char *tospace;
|
|
unsigned char *tospace_end;
|
|
unsigned char *tospace_alloc;
|
|
};
|
|
|
|
const size_t uns_cheney_c89_ctx_size = sizeof(struct ctx);
|
|
|
|
static void zero_ctx(struct ctx *ctx)
|
|
{
|
|
ctx->tospace = 0;
|
|
ctx->tospace_end = 0;
|
|
ctx->tospace_alloc = 0;
|
|
}
|
|
|
|
void uns_cheney_c89_deinit(struct uns_gc *gc)
|
|
{
|
|
struct ctx *ctx = gc->ctx;
|
|
|
|
free(ctx->tospace);
|
|
}
|
|
|
|
/* Allocate without doing bounds checking. This is common code for
|
|
* collecting (when all values will fit into the tospace) and
|
|
* allocation (where bounds checking is done beforehand).
|
|
*/
|
|
static void *raw_alloc(struct ctx *ctx, size_t len, int is_record)
|
|
{
|
|
unsigned char *p;
|
|
|
|
/*
|
|
printf("%lu (%ld)\n", (unsigned long)(len + sizeof(struct uns_c89_relo_hdr)),
|
|
(long)(ctx->tospace_end - ctx->tospace_alloc));
|
|
*/
|
|
assert(ctx->tospace_alloc + sizeof(struct uns_c89_relo_hdr) + len <= ctx->tospace_end);
|
|
p = uns_c89_relo_init(ctx->tospace_alloc, len, is_record);
|
|
ctx->tospace_alloc = p + len;
|
|
|
|
return p;
|
|
}
|
|
|
|
/* Move an entire object pointed to by p from fromspace to tospace. */
|
|
static unsigned char *relocate(struct uns_gc *gc, unsigned char *p)
|
|
{
|
|
size_t l;
|
|
unsigned char *res;
|
|
struct ctx *ctx = gc->ctx;
|
|
|
|
if (!p)
|
|
return NULL;
|
|
res = uns_c89_relo_get_relo(p);
|
|
if (res)
|
|
return res;
|
|
|
|
l = uns_c89_relo_get_len(p);
|
|
res = raw_alloc(ctx, l, uns_c89_relo_is_record(p));
|
|
memcpy(res, p, l);
|
|
gc->after_collection += l + sizeof(struct uns_c89_relo_hdr);
|
|
|
|
uns_c89_relo_set_relo(p, res);
|
|
return res;
|
|
}
|
|
|
|
/* Scan each part of the record pointed to by "p" and relocate it. */
|
|
static void scan_record(struct uns_gc *gc, unsigned char *p)
|
|
{
|
|
size_t l = uns_c89_relo_get_record_len(p);
|
|
size_t i;
|
|
void *newp;
|
|
|
|
for (i = 0; i < l; i++) {
|
|
newp = relocate(gc, uns_c89_relo_record_get((void *)p, i));
|
|
uns_c89_relo_record_set((void *)p, i, newp);
|
|
}
|
|
}
|
|
|
|
int uns_cheney_c89_collect(struct uns_gc *gc)
|
|
{
|
|
/* Save fromspace */
|
|
struct ctx *ctx = gc->ctx;
|
|
unsigned char *fromspace = ctx->tospace;
|
|
unsigned char *fromspace_lim = ctx->tospace_alloc;
|
|
unsigned char *scanptr;
|
|
|
|
size_t newlen = gc->next_alloc;
|
|
struct uns_ctr *root;
|
|
|
|
assert(gc->next_alloc >= fromspace_lim - fromspace);
|
|
|
|
/* Bail out immediately if allocation fails. This preserves
|
|
* the objects as they were.
|
|
*/
|
|
ctx->tospace = malloc(newlen);
|
|
if (!ctx->tospace) {
|
|
ctx->tospace = fromspace;
|
|
return 1;
|
|
}
|
|
|
|
/* Setup statistics */
|
|
gc->before_collection = fromspace_lim - fromspace;
|
|
/*
|
|
printf("before: %ld\n", gc->before_collection);
|
|
*/
|
|
gc->after_collection = 0;
|
|
gc->collection_number += 1;
|
|
|
|
ctx->tospace_end = ctx->tospace + newlen;
|
|
ctx->tospace_alloc = ctx->tospace;
|
|
|
|
/* Relocate roots */
|
|
for (root = gc->roots; root; root = root->next)
|
|
root->p = relocate(gc, root->p);
|
|
|
|
scanptr = ctx->tospace;
|
|
while (scanptr != ctx->tospace_alloc) {
|
|
/* scanptr currently points to the header data that the mutator
|
|
* does not usually see.
|
|
*/
|
|
scanptr += sizeof(struct uns_c89_relo_hdr);
|
|
if (uns_c89_relo_is_record(scanptr))
|
|
scan_record(gc, scanptr);
|
|
scanptr += uns_c89_relo_get_len(scanptr);
|
|
}
|
|
|
|
free(fromspace);
|
|
if (gc->after_gc)
|
|
gc->after_gc(gc);
|
|
return 1;
|
|
}
|
|
|
|
static void *alloc(struct uns_gc *gc, size_t bytes, int is_record)
|
|
{
|
|
struct ctx *ctx = gc->ctx;
|
|
size_t total_bytes = bytes + sizeof(struct uns_c89_relo_hdr);
|
|
|
|
/* Make sure to check for header space when allocating */
|
|
if (ctx->tospace_end - ctx->tospace_alloc < total_bytes)
|
|
uns_cheney_c89_collect(gc);
|
|
if (ctx->tospace_end - ctx->tospace_alloc < total_bytes)
|
|
gc->oom(gc);
|
|
|
|
return raw_alloc(ctx, bytes, is_record);
|
|
}
|
|
|
|
void *uns_cheney_c89_alloc(struct uns_gc *gc, size_t bytes)
|
|
{
|
|
return alloc(gc, bytes, 0);
|
|
}
|
|
|
|
void *uns_cheney_c89_alloc_record(struct uns_gc *gc, size_t records)
|
|
{
|
|
size_t i;
|
|
unsigned char *res = alloc(gc, records*sizeof(void *), 1);
|
|
|
|
for (i = 0; i < records; i++)
|
|
uns_c89_relo_record_set((void *)res, i, NULL);
|
|
|
|
return res;
|
|
}
|
|
|
|
int uns_cheney_c89_init(struct uns_gc *gc)
|
|
{
|
|
struct ctx *ctx = gc->ctx;
|
|
|
|
gc->deinit = uns_cheney_c89_deinit;
|
|
gc->collect = uns_cheney_c89_collect;
|
|
gc->alloc = uns_cheney_c89_alloc;
|
|
gc->alloc_record = uns_cheney_c89_alloc_record;
|
|
gc->len = uns_c89_relo_get_len;
|
|
gc->record_set_ptr = uns_c89_relo_record_set;
|
|
gc->record_get_ptr = uns_c89_relo_record_get;
|
|
|
|
zero_ctx(ctx);
|
|
ctx->tospace = malloc(gc->next_alloc);
|
|
if (!ctx->tospace)
|
|
return 0;
|
|
|
|
ctx->tospace_alloc = ctx->tospace;
|
|
ctx->tospace_end = ctx->tospace + gc->next_alloc;
|
|
return 1;
|
|
}
|