mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-24 14:30:43 -08:00
490 lines
10 KiB
C
490 lines
10 KiB
C
/*
|
|
TEST_HEADER
|
|
id = $HopeName$
|
|
summary = pointer to null addr to fix (function)
|
|
language = c
|
|
link = testlib.o newfmt.o
|
|
END_HEADER
|
|
*/
|
|
|
|
#include "testlib.h"
|
|
#include "mpscamc.h"
|
|
#include "arg.h"
|
|
|
|
/* what follows is hacked from newfmt.c
|
|
*/
|
|
|
|
#include "mps.h"
|
|
#include "newfmt.h"
|
|
|
|
/* tags */
|
|
|
|
enum {MCpadsingle, MCpadmany, MCheart, MCdata};
|
|
|
|
/* some options on the format are controlled by global
|
|
variables. Of course for efficiency we'd do it in the
|
|
pre-processor, but that would require recompilation...
|
|
|
|
variable default function
|
|
|
|
formatcomments 1 print comments on scanning, fixing, copying
|
|
checkcomments 1 print comments on checking
|
|
|
|
*/
|
|
|
|
int formatcomments=0;
|
|
int checkcomments=0;
|
|
int countcomments=1;
|
|
int alloccomments=0;
|
|
|
|
int nextid=1000000;
|
|
|
|
enum
|
|
{
|
|
SCANCALL_COUNT,
|
|
SCANOBJ_COUNT, /* = #objects scanned (real ones, that is) */
|
|
SCANPS_COUNT, /* = #pad singles scanned */
|
|
SCANPM_COUNT, /* etc ... */
|
|
SCANHEART_COUNT,
|
|
COPY_COUNT,
|
|
SKIP_COUNT,
|
|
FWD_COUNT,
|
|
ISFWD_COUNT,
|
|
RESERVE_COUNT,
|
|
ALLOC_COUNT,
|
|
PAD_SINGLE_COUNT,
|
|
PAD_MULTI_COUNT
|
|
};
|
|
|
|
int counters[PAD_MULTI_COUNT+1];
|
|
int prevcounters[PAD_MULTI_COUNT+1];
|
|
int maxcounters[PAD_MULTI_COUNT+1] = {0};
|
|
|
|
static long int maxcopy = 0;
|
|
static int freeze=0;
|
|
|
|
#define INCCOUNT(c) if(!freeze) counters[c]+=1
|
|
|
|
#define MAXSIZE 10000
|
|
|
|
/* a cell can be one of four things, depending on its type:
|
|
MCpadsingle - a single pad item, MPS_PF_ALIGN in size
|
|
MCpadmulti - a longer pad item, at least 2*MPS_PF_ALIGN in size
|
|
MCheart - a broken heart, aka forwarding object
|
|
MCdata - a real object
|
|
*/
|
|
|
|
typedef int tag;
|
|
|
|
/* typedef union mycell mycell;
|
|
(it's in the header)
|
|
*/
|
|
|
|
struct padsingle {tag tag;};
|
|
|
|
struct padmulti {tag tag; mps_addr_t next;};
|
|
|
|
struct heart {tag tag; size_t size; mps_addr_t obj;};
|
|
|
|
struct data
|
|
{
|
|
tag tag;
|
|
size_t size;
|
|
long int id;
|
|
long int copycount;
|
|
long int numrefs;
|
|
int checkedflag;
|
|
struct refitem {mycell *addr; long int id;} ref[MAXSIZE];
|
|
};
|
|
|
|
union mycell
|
|
{
|
|
tag tag;
|
|
struct padsingle padsingle;
|
|
struct padmulti padmulti;
|
|
struct heart heart;
|
|
struct data data;
|
|
};
|
|
|
|
/* the scanning function doesn't try to fix null refs
|
|
|
|
size is the total size of the object in bytes
|
|
every other word from ref onwards, including the word
|
|
at ref, is a reference; after each one comes the id
|
|
of the object referenced, so we can check the graph is
|
|
correct later!
|
|
|
|
nb: This way of doing things is not really guaranteed to work
|
|
on all systems. It would be more correct to use an array
|
|
of structures, or an array of unions.
|
|
*/
|
|
|
|
static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit);
|
|
static mps_addr_t myskip(mps_addr_t object);
|
|
static void myfwd(mps_addr_t object, mps_addr_t to);
|
|
static mps_addr_t myisfwd(mps_addr_t object);
|
|
static void mycopy(mps_addr_t object, mps_addr_t to);
|
|
static void mypad(mps_addr_t base, size_t size);
|
|
|
|
struct mps_fmt_A_s fmtA =
|
|
{
|
|
MPS_PF_ALIGN,
|
|
&myscan,
|
|
&myskip,
|
|
&mycopy,
|
|
&myfwd,
|
|
&myisfwd,
|
|
&mypad
|
|
};
|
|
|
|
/* in the following, size is the number of refs you want
|
|
the allocated object to have
|
|
*/
|
|
|
|
mycell *allocdumb(mps_ap_t ap, size_t size)
|
|
{
|
|
mps_addr_t p;
|
|
mycell *q;
|
|
size_t bytes;
|
|
size_t alignment;
|
|
|
|
bytes = offsetof(struct data, ref) + size;
|
|
|
|
alignment = MPS_PF_ALIGN; /* needed to make it as wide as size_t */
|
|
|
|
/* twiddle the value of size to make it aligned */
|
|
bytes = (bytes+alignment-1) & ~(alignment-1);
|
|
|
|
do
|
|
{
|
|
die(mps_reserve(&p, ap, bytes), "Reserve: ");
|
|
INCCOUNT(RESERVE_COUNT);
|
|
q=p;
|
|
q->data.tag = MCdata;
|
|
q->data.id = nextid;
|
|
q->data.copycount = 0;
|
|
q->data.numrefs = 0;
|
|
q->data.checkedflag = 0;
|
|
q->data.size = bytes;
|
|
}
|
|
while (!mps_commit(ap, p, bytes));
|
|
INCCOUNT(ALLOC_COUNT);
|
|
commentif(alloccomments, "allocated id %li at %p.", nextid, q);
|
|
nextid += 1;
|
|
|
|
return q;
|
|
}
|
|
|
|
mycell *allocone(mps_ap_t ap, int size)
|
|
{
|
|
mps_addr_t p;
|
|
mycell *q;
|
|
int i;
|
|
size_t bytes;
|
|
size_t alignment;
|
|
|
|
bytes = offsetof(struct data, ref) + size*sizeof(struct refitem);
|
|
|
|
alignment = MPS_PF_ALIGN; /* needed to make it as wide as size_t */
|
|
|
|
/* twiddle the value of size to make it aligned */
|
|
bytes = (bytes+alignment-1) & ~(alignment-1);
|
|
|
|
do
|
|
{
|
|
die(mps_reserve(&p, ap, bytes), "Reserve: ");
|
|
INCCOUNT(RESERVE_COUNT);
|
|
q=p;
|
|
q->data.tag = MCdata;
|
|
q->data.id = nextid;
|
|
q->data.copycount = 0;
|
|
q->data.numrefs = size;
|
|
q->data.checkedflag = 0;
|
|
q->data.size = bytes;
|
|
|
|
for(i=0; i<size; i+=1)
|
|
{
|
|
q->data.ref[i].addr = NULL;
|
|
q->data.ref[i].id = 0;
|
|
}
|
|
}
|
|
while (!mps_commit(ap, p, bytes));
|
|
INCCOUNT(ALLOC_COUNT);
|
|
commentif(alloccomments, "allocated id %li at %p.", nextid, q);
|
|
nextid += 1;
|
|
|
|
return q;
|
|
}
|
|
|
|
static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit)
|
|
{
|
|
int i;
|
|
|
|
INCCOUNT(SCANCALL_COUNT);
|
|
MPS_SCAN_BEGIN(ss)
|
|
{
|
|
while (base < limit)
|
|
{
|
|
mycell *obj = base;
|
|
mps_res_t res;
|
|
mps_addr_t p;
|
|
|
|
commentif(formatcomments, "scan %p.\n", base);
|
|
switch (obj->tag)
|
|
{
|
|
case MCpadsingle:
|
|
INCCOUNT(SCANPS_COUNT);
|
|
base = (mps_addr_t) ((char *) obj + sizeof(obj->padsingle));
|
|
break;
|
|
case MCpadmany:
|
|
INCCOUNT(SCANPM_COUNT);
|
|
base = (obj->padmulti.next);
|
|
break;
|
|
case MCdata:
|
|
INCCOUNT(SCANOBJ_COUNT);
|
|
/* actual scanning is done in here */
|
|
|
|
for (i=0; i<(obj->data.numrefs); i++)
|
|
{
|
|
p = obj->data.ref[i].addr;
|
|
if (p != NULL)
|
|
{
|
|
commentif(formatcomments, "fix: %d=%p.\n", i, p);
|
|
|
|
/* copy ref to p for fixing, to avoid a pun (although
|
|
the pun would probably work fine almost everywhere)
|
|
*/
|
|
comment("About to fix with null pointer...");
|
|
p = NULL;
|
|
res = mps_fix(ss, (mps_addr_t *) &p);
|
|
error("fix with null pointer");
|
|
if (res != MPS_RES_OK) return res;
|
|
obj->data.ref[i].addr = p;
|
|
}
|
|
}
|
|
base = (mps_addr_t) ((char *) obj + (obj->data.size));
|
|
break;
|
|
case MCheart:
|
|
INCCOUNT(SCANHEART_COUNT);
|
|
base = (mps_addr_t) ((char *) obj + (obj->heart.size));
|
|
break;
|
|
default:
|
|
asserts(0, "scan: bizarre obj tag at %p.", obj);
|
|
}
|
|
}
|
|
}
|
|
MPS_SCAN_END(ss);
|
|
return MPS_RES_OK;
|
|
}
|
|
|
|
static mps_addr_t myskip(mps_addr_t object)
|
|
{
|
|
mycell *obj = object;
|
|
|
|
INCCOUNT(SKIP_COUNT);
|
|
switch(obj->tag)
|
|
{
|
|
case MCpadsingle:
|
|
return (mps_addr_t) ((char *) obj + sizeof(obj->padsingle));
|
|
case MCpadmany:
|
|
return obj->padmulti.next;
|
|
case MCheart:
|
|
return (mps_addr_t) ((char *) obj + (obj->heart.size));
|
|
case MCdata:
|
|
return (mps_addr_t) ((char *) obj + (obj->data.size));
|
|
default:
|
|
asserts(0, "skip: bizarre obj tag at %p.", obj);
|
|
return 0; /* not reached */
|
|
}
|
|
}
|
|
|
|
static void mycopy(mps_addr_t object, mps_addr_t to)
|
|
{
|
|
mycell *boj = object;
|
|
mycell *toj = to;
|
|
|
|
asserts(boj->tag = MCdata, "copy: non-data object");
|
|
|
|
INCCOUNT(COPY_COUNT);
|
|
commentif(formatcomments, "copy: %p -> %p\n", object, to);
|
|
|
|
/* this line would be bad, because the objects might overlap,
|
|
and then C doesn't guarantee to do the right thing!
|
|
|
|
*toj = *boj;
|
|
*/
|
|
|
|
memmove(to, object, boj->data.size);
|
|
if (!freeze)
|
|
{
|
|
toj->data.copycount = (toj->data.copycount)+1;
|
|
if (toj->data.copycount > maxcopy) maxcopy = toj->data.copycount;
|
|
}
|
|
}
|
|
|
|
/* pad stores not its size but a pointer to the next object,
|
|
because we know we'll never be asked to copy it
|
|
*/
|
|
|
|
static void mypad(mps_addr_t base, size_t size)
|
|
{
|
|
mycell *obj = base;
|
|
|
|
asserts(size >= MPS_PF_ALIGN, "pad: size too small.");
|
|
|
|
if (size == MPS_PF_ALIGN)
|
|
{
|
|
INCCOUNT(PAD_SINGLE_COUNT);
|
|
obj->padsingle.tag = MCpadsingle;
|
|
}
|
|
else
|
|
{
|
|
asserts(size >= sizeof(struct padmulti), "pad: awkward size.");
|
|
INCCOUNT(PAD_MULTI_COUNT);
|
|
obj->padmulti.tag = MCpadmany;
|
|
obj->padmulti.next = (mps_addr_t) ((char *) base + size);
|
|
}
|
|
}
|
|
|
|
static mps_addr_t myisfwd(mps_addr_t object)
|
|
{
|
|
mycell *obj = object;
|
|
|
|
INCCOUNT(ISFWD_COUNT);
|
|
if (obj->tag != MCheart)
|
|
{
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
return obj->heart.obj;
|
|
}
|
|
}
|
|
|
|
static void myfwd(mps_addr_t object, mps_addr_t to)
|
|
{
|
|
mycell *obj = object;
|
|
size_t size;
|
|
|
|
asserts(obj->tag == MCdata || obj->tag == MCheart,
|
|
"fwd: unexpected object tag at %p.", obj);
|
|
INCCOUNT(FWD_COUNT);
|
|
|
|
if (obj->tag == MCdata)
|
|
{
|
|
size = obj->data.size;
|
|
}
|
|
else /* obj->tag == MCheart */
|
|
{
|
|
size = obj->heart.size;
|
|
}
|
|
obj->data.tag = MCheart;
|
|
obj->heart.obj = to;
|
|
obj->heart.size = size;
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------
|
|
Access methods for mycell objects
|
|
*/
|
|
|
|
/* set the nth reference of obj to to (n from 0 to size-1) */
|
|
|
|
void setref(mycell *obj, int n, mycell *to)
|
|
{
|
|
asserts(obj->tag = MCdata, "setref: from non-data object.");
|
|
asserts(to->tag = MCdata, "setref: to non-data object.");
|
|
asserts(obj->data.numrefs > n, "setref: access beyond object size.");
|
|
|
|
obj->data.ref[n].addr = to;
|
|
obj->data.ref[n].id = to->data.id;
|
|
}
|
|
|
|
mycell *getref(mycell *obj, int n)
|
|
{
|
|
asserts(obj->tag = MCdata, "getref: from non-data object.");
|
|
asserts(obj->data.numrefs > n, "getref: access beyond object size.");
|
|
return obj->data.ref[n].addr;
|
|
}
|
|
|
|
mps_addr_t getdata(mycell *obj)
|
|
{
|
|
return (mps_addr_t) &(obj->data.ref[0]);
|
|
}
|
|
|
|
long int getid(mycell *obj)
|
|
{
|
|
asserts(obj->tag = MCdata, "getid: non-data object.");
|
|
return obj->data.id;
|
|
}
|
|
|
|
long int getcopycount(mycell *obj)
|
|
{
|
|
asserts(obj->tag = MCdata, "getcopycount: non-data object.");
|
|
return obj->data.copycount;
|
|
}
|
|
|
|
long int getsize(mycell *obj)
|
|
{
|
|
asserts(obj->tag = MCdata, "getsize: non-data object.");
|
|
return obj->data.numrefs;
|
|
}
|
|
|
|
/* ---- Now the test itself! ---- */
|
|
|
|
void *stackpointer;
|
|
|
|
static void test(void)
|
|
{
|
|
mps_space_t space;
|
|
mps_pool_t pool;
|
|
mps_thr_t thread;
|
|
mps_root_t root;
|
|
|
|
mps_fmt_t format;
|
|
mps_ap_t ap;
|
|
|
|
int j;
|
|
mycell *a;
|
|
|
|
cdie(mps_space_create(&space), "create space");
|
|
|
|
cdie(mps_thread_reg(&thread, space), "register thread");
|
|
|
|
cdie(
|
|
mps_root_create_reg(&root, space, MPS_RANK_AMBIG, 0, thread,
|
|
mps_stack_scan_ambig, stackpointer, 0),
|
|
"create root");
|
|
|
|
cdie(
|
|
mps_fmt_create_A(&format, space, &fmtA),
|
|
"create format");
|
|
|
|
formatcomments = 0;
|
|
|
|
cdie(
|
|
mps_pool_create(&pool, space, mps_class_amc(), format),
|
|
"create pool");
|
|
|
|
cdie(
|
|
mps_ap_create(&ap, pool, MPS_RANK_EXACT),
|
|
"create ap");
|
|
|
|
a = allocone(ap, 100);
|
|
setref(a, 0, a);
|
|
|
|
for (j=1; j<1000; j++)
|
|
{
|
|
allocone(ap, 1000);
|
|
}
|
|
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
void *m;
|
|
stackpointer=&m; /* hack to get stack pointer */
|
|
|
|
easy_tramp(test);
|
|
return 0;
|
|
}
|