1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-01-08 12:40:49 -08:00

Add description of the object format to the language guide.

Copied from Perforce
 Change: 179906
 ServerID: perforce.ravenbrook.com
This commit is contained in:
Gareth Rees 2012-10-16 23:28:24 +01:00
parent 15505d7649
commit e07d0badc8
9 changed files with 659 additions and 142 deletions

View file

@ -204,8 +204,8 @@ Memory Management Glossary: C
A :term:`formatted object` that contains data from the
:term:`client program`. One of three types of formatted
objects, the other two being :term:`forwarded objects
<forwarded object>` and :term:`padding objects <padding
objects, the other two being :term:`forwarding objects
<forwarding object>` and :term:`padding objects <padding
object>`.
client program

View file

@ -272,16 +272,8 @@ Memory Management Glossary: F
object with a :term:`forwarding marker` that points to the
new location of the object. See :c:type:`mps_fmt_fwd_t`.
forwarded object
.. mps:specific::
A :term:`formatted object` that has been replaced by a
:term:`forwarding marker`. One of three types of formatted
objects, the other two being :term:`client objects <client
object>` and :term:`padding objects <padding object>`.
forwarding marker
forwarding object
forwarding pointer
Some :term:`garbage collectors <garbage collector>`
@ -295,7 +287,13 @@ Memory Management Glossary: F
.. seealso:: :term:`copying garbage collection`, :term:`two-space collector`.
.. mps:specific:: The term *forwarding marker* is used.
.. mps:specific::
The term *forwarding object* is used. This is a
:term:`formatted object` that has been replaced by a
:term:`forwarding marker`. One of three types of formatted
objects, the other two being :term:`client objects <client
object>` and :term:`padding objects <padding object>`.
fragmentation

View file

@ -345,6 +345,6 @@ Memory Management Glossary: I
A :term:`format method` that is called by a :term:`moving
<moving garbage collector>` :term:`pool <pool>` to
determine if a :term:`formatted object` is a
:term:`forwarded object`, and if so, to return the address
:term:`forwarding object`, and if so, to return the address
where the object was moved to. See
:c:type:`mps_fmt_isfwd_t`.

View file

@ -32,7 +32,7 @@ Memory Management Glossary: P
A :term:`formatted object` that consists of
:term:`padding`. One of three types of formatted objects,
the other two being :term:`client objects <client object>`
and :term:`forwarded objects <forwarded object>`.
and :term:`forwarding objects <forwarding object>`.
page

View file

@ -16,7 +16,7 @@ garbage collection to the runtime system for a programming language.
I'm assuming that you've downloaded and compiled the MPS (see
:ref:`guide-install`), and that you are familiar with the overall
architecture of the MPS (:ref:`guide-overview`).
architecture of the MPS (see :ref:`guide-overview`).
------------------
@ -94,12 +94,23 @@ operate on objects generically, testing ``TYPE(obj)`` as necessary. For example,
}
}
Each constructor (for example ``make_pair`` is the constructor for
pairs) allocates memory for the new object by calling ``malloc``,
but objects do not get freed, because they have :term:`indefinite
extent`, so it is necessary to prove that they are :term:`dead` before
their memory can be :term:`reclaimed <reclaim>`. And that task falls
to the :term:`garbage collector`.
Each constructor allocates memory for the new object by calling
``malloc``. For example ``make_pair`` is the constructor for pairs::
static obj_t make_pair(obj_t car, obj_t cdr)
{
obj_t obj = (obj_t)malloc(sizeof(pair_s));
if (obj == NULL) error("out of memory");
total += sizeof(pair_s);
obj->pair.type = TYPE_PAIR;
CAR(obj) = car;
CDR(obj) = cdr;
return obj;
}
Objects do not get freed, because it is necessary to prove that they
are :term:`dead` before their memory can be :term:`reclaimed
<reclaim>`. And that task falls to the :term:`garbage collector`.
-----------------------
@ -191,21 +202,20 @@ these features of the MPS.
classes.
-----------------
The object format
-----------------
-----------------------
Describing your objects
-----------------------
In order for the MPS to be able to collect your objects, you need to
In order for the MPS to be able to manage your objects, you need to
tell it how to perform various operations on those objects, which you
do by creating a set of :term:`format method <format method>`, and
using them to specify an :term:`object format`. Here's the code for
creating the object format for the Scheme interpreter::
do by creating an :term:`object format`. Here's the code for creating
the object format for the Scheme interpreter::
struct mps_fmt_A_s obj_fmt_s = {
sizeof(mps_word_t),
obj_scan,
obj_skip,
obj_copy,
NULL,
obj_fwd,
obj_isfwd,
obj_pad,
@ -223,7 +233,8 @@ belonging to this format. The Scheme interpreter needs its objects to
be allocated at addresses which are multiples of the machine's word
size.
The other elements of the structure are the format methods, which are described in the following sections.
The other elements of the structure are the :term:`format methods
<format method>`, which are described in the following sections.
.. topics::
@ -234,11 +245,424 @@ The other elements of the structure are the format methods, which are described
The scan method
---------------
The :term:`scan method` is a function of type
:c:type:`mps_fmt_scan_t`. It is called by the MPS to :term:`scan` a
block of memory. Its task is to identify all references within the
objects in the block of memory, and "fix" them, by calling the macros
:c:func:`MPS_FIX1` and :c:func:`MPS_FIX2` on each reference (usually
via the convenience macro :c:func:`MPS_FIX12`).
"Fixing" is a generic operation whose effect depends on the context in
which the scan method was called. The scan method is called to
discover references and so determine which objects are :term:`alive`
and which are :term:`dead`, and also to update references after
objects have been moved.
Here's the scan method for the Scheme example::
static mps_res_t obj_scan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit)
{
MPS_SCAN_BEGIN(ss) {
while (base < limit) {
obj_t obj = base;
switch (obj->type.type) {
case TYPE_PAIR:
FIX(obj->pair.car);
FIX(obj->pair.cdr);
base = (char *)base + ALIGN(sizeof(pair_s));
break;
case TYPE_INTEGER:
base = (char *)base + ALIGN(sizeof(integer_s));
break;
/* ... and so on for the other types ... */
default:
fprintf(stderr, "Unexpected object on the heap\n");
abort();
}
}
} MPS_SCAN_END(ss);
return MPS_RES_OK;
}
The scan method receives a :term:`scan state` (``ss``) argument, and
the block of memory to scan, from ``base`` (inclusive) to ``limit``
(exclusive). This block of memory is known to be packed with objects
belonging to the object format, and so the scan method loops over the
objects in the block, dispatching on the type of each object, and then
updating ``base`` to point to the next object in the block.
For each reference in an object the scan method fixes it by calling
:c:func:`MPS_FIX12` via the macro ``FIX``, which is defined as
follows::
#define FIX(ref) \
do { \
mps_addr_t _addr = (ref); /* copy to local to avoid type pun */ \
mps_res_t res = MPS_FIX12(ss, &_addr); \
if (res != MPS_RES_OK) return res; \
(ref) = _addr; \
} while(0)
Each call to :c:func:`MPS_FIX12` must appear between calls to the
macros :c:func:`MPS_SCAN_BEGIN` and :c:func:`MPS_SCAN_END`. It's
usually most convenient to call :c:func:`MPS_SCAN_BEGIN` at the start
of the function and :c:func:`MPS_SCAN_END` at the end.
There are a few things to watch out for:
1. When the MPS calls your scan method, it may be part-way through
moving your objects. It is therefore essential that the scan method
only examine objects in the range of addresses it is given. Objects
in other ranges of addresses are not guaranteed to be in a
consistent state.
2. Scanning is an operation on the :term:`critical path` of the MPS,
which means that it is important that it runs as quickly as
possible.
3. The "fix" operation may update the reference. So if your language
has :term:`tagged references <tagged reference>`, you must make
sure that the tag is restored after the reference is updated.
4. The "fix" operation may fail by returning a :term:`result code`
other than :c:macro:`MPS_RES_OK`. A scan function must propagate
such a result code to the caller, and should do so as soon as
practicable.
.. topics::
:ref:`topic-format`, :ref:`topic-scanning`.
---------------
The skip method
---------------
The :term:`skip method` is a function of type
:c:type:`mps_fmt_skip_t`. It is called by the MPS to skip over an
object belonging to the format.
Here's the skip method for the Scheme example::
static mps_addr_t obj_skip(mps_addr_t base)
{
obj_t obj = base;
switch (obj->type.type) {
case TYPE_PAIR:
base = (char *)base + ALIGN(sizeof(pair_s));
break;
case TYPE_INTEGER:
base = (char *)base + ALIGN(sizeof(integer_s));
break;
/* ... and so on for the other types ... */
default:
fprintf(stderr, "Unexpected object on the heap\n");
abort();
}
return base;
}
The argument ``base`` is the address to the base of the object. The
skip method must return the address of the base of the "next object":
in formats of variant A like this one, this is the address of the word
just past the end of the object.
You'll see that the code in the skip method that computes the "next
object" is the same as the corresponding code in the :term:`scan
method`, so that it's tempting for the latter to delegate this part of
its functionality to the former.
.. topics::
:ref:`topic-format`, :ref:`topic-scanning`.
------------------
The forward method
------------------
The :term:`forward method` is a function of type
:c:type:`mps_fmt_fwd_t`. It is called by the MPS after it has moved an
object, and its task is to replace the old object with a
:term:`forwarding object` pointing to the new location of the object.
The forwarding object must satisfy these properties:
1. It must be scannable and skippable, and so it will need to have a
type field to distinguish it from other Scheme objects.
2. It must contain a pointer to the new location of the object (a
:term:`forwarding pointer`).
3. The scan method and the skip method will both need to know the
length of the forwarding object. This can be arbitarily long (in
the case of string objects, for example) so it must contain a
length field.
This poses a problem, because the above analysis suggests that
forwarding objects need to contain at least three words, but Scheme
objects might be as small as two words (for example, integers).
This conundrum can be solved by having two types of forwarding object.
The first type is suitable for forwarding objects of three words or
longer::
typedef struct fwd_s {
type_t type; /* TYPE_FWD */
obj_t fwd; /* forwarded object */
size_t size; /* total size of this object */
} fwd_s;
while the second type is suitable for forwarding objects of two words::
typedef struct fwd2_s {
type_t type; /* TYPE_FWD2 */
obj_t fwd; /* forwarded object */
} fwd2_s;
Here's the forward method for the Scheme example::
static void obj_fwd(mps_addr_t old, mps_addr_t new)
{
obj_t obj = old;
mps_addr_t limit = obj_skip(old);
size_t size = (char *)limit - (char *)old;
assert(size >= ALIGN(sizeof(fwd2_s)));
if (size == ALIGN(sizeof(fwd2_s))) {
obj->type.type = TYPE_FWD2;
obj->fwd2.fwd = new;
} else {
obj->type.type = TYPE_FWD;
obj->fwd.fwd = new;
obj->fwd.size = size;
}
}
The argument ``old`` is the old address of the object, and ``new`` is
the location to which it has been moved.
The fowarding objects must be scannable and skippable, so the
following code must be added to ``obj_scan`` and ``obj_skip``::
case TYPE_FWD:
base = (char *)base + ALIGN(obj->fwd.size);
break;
case TYPE_FWD2:
base = (char *)base + ALIGN(sizeof(fwd2_s));
break;
.. note::
The Scheme interpreter has no objects consisting of a single word.
If it did, the type and the forwarding pointer would have to be
combined into a forwarding object consisting of a single word, for
example by relying on the fact that pointers are never equal to
the small integers that are used to represent types.
.. topics::
:ref:`topic-format`.
-----------------------
The is-forwarded method
-----------------------
The :term:`is-forwarded method` is a function of type
:c:type:`mps_fmt_isfwd_t`. It is called by the MPS to determine if an
object is a :term:`forwarding object`, and if it is, to determine the
location where that object was moved.
Here's the is-forwarded method for the Scheme example::
static mps_addr_t obj_isfwd(mps_addr_t addr)
{
obj_t obj = addr;
switch (obj->type.type) {
case TYPE_FWD2:
return obj->fwd2.fwd;
case TYPE_FWD:
return obj->fwd.fwd;
}
return NULL;
}
It receives the address of an object, and returns the address to which
that object was moved, or ``NULL`` if the object was not moved.
.. topics::
:ref:`topic-format`.
------------------
The padding method
------------------
The :term:`padding method` is a function of type
:c:type:`mps_fmt_pad_t`. It is called by the MPS to fill a block of
memory with a :term:`padding object`: this is an object that fills
gaps in a block of :term:`formatted objects <formatted object>`, for
example to enable the MPS to pack objects into fixed-size units (such
as operating system :term:`pages <page>`).
A padding object must be scannable and skippable, and not confusable
with a :term:`forwarding object`. This means they need a type and a
size. However, padding objects might need to be as small as the
alignment of the object format, which was specified to be a single
word. As with forwarding objects, this can be solved by having two
types of padding object. The first type is suitable for paddding
objects of two words or longer::
typedef struct pad_s {
type_t type; /* TYPE_PAD */
size_t size; /* total size of this object */
} pad_s;
while the second type is suitable for padding objects consisting of a
single word::
typedef struct pad1_s {
type_t type; /* TYPE_PAD1 */
} pad1_s;
Here's the padding method::
static void obj_pad(mps_addr_t addr, size_t size)
{
obj_t obj = addr;
assert(size >= ALIGN(sizeof(pad1_s)));
if (size == ALIGN(sizeof(pad1_s))) {
obj->type.type = TYPE_PAD1;
} else {
obj->type.type = TYPE_PAD;
obj->pad.size = size;
}
}
The argument ``addr`` is the address at which the padding object must be created, and ``size`` is its size in bytes: this will always be a multiple of the alignment of the object format.
The padding objects must be scannable and skippable, so the following
code must be added to ``obj_scan`` and ``obj_skip``::
case TYPE_PAD:
base = (char *)base + ALIGN(obj->pad.size);
break;
case TYPE_PAD1:
base = (char *)base + ALIGN(sizeof(pad1_s));
break;
.. topics::
:ref:`topic-format`.
-----------------
Generation chains
-----------------
The AMC pool requires not only an object format but a
:term:`generation chain`. This specifies the generation structure of
the :term:`generational garbage collection`.
The client program creates a generation chain by constructing an array
of structures of type :c:type:`mps_gen_param_s` and passing them to
:c:func:`mps_chain_create`. Here's the code for creating
the generation chain for the Scheme interpreter::
mps_gen_param_s obj_gen_params[] = {
{ 150, 0.85 },
{ 170, 0.45 },
};
res = mps_chain_create(&obj_chain,
arena,
LENGTH(obj_gen_params),
obj_gen_params);
if (res != MPS_RES_OK) error("Couldn't create obj chain");
-----------------
Creating the pool
-----------------
Now you know enough to create an AMC pool! Let's review the pool
creation code. First the object format::
struct mps_fmt_A_s obj_fmt_s = {
sizeof(mps_word_t),
obj_scan,
obj_skip,
NULL,
obj_fwd,
obj_isfwd,
obj_pad,
};
mps_fmt_t obj_fmt;
res = mps_fmt_create_A(&obj_fmt, arena, &obj_fmt_s);
if (res != MPS_RES_OK) error("Couldn't create obj format");
then the generation chain::
mps_gen_param_s obj_gen_params[] = {
{ 150, 0.85 },
{ 170, 0.45 },
};
mps_chain_t obj_chain;
res = mps_chain_create(&obj_chain,
arena,
LENGTH(obj_gen_params),
obj_gen_params);
if (res != MPS_RES_OK) error("Couldn't create obj chain");
and finally the pool::
mps_pool_t obj_pool;
res = mps_pool_create(&obj_pool,
arena,
mps_class_amc(),
obj_fmt,
obj_chain);
if (res != MPS_RES_OK) error("Couldn't create obj pool");
-----
Roots
-----
--------------
The trampoline
--------------
.. topics::
:ref:`topic-root`.
-------
Threads
-------
----------
Allocation
----------
.. topics::
:ref:`topic-allocation`.
--------
Messages
--------
.. topics::
:ref:`topic-message`.

View file

@ -16,7 +16,7 @@
* (define (church n f a) (if (eqv? n 0) a (church (- n 1) f (f a))))
* (church 1000 triangle 0)
*
* This won't produce interesting results but it will cause a garbage
* This won't produce interesting results but it will cause garbage
* collection cycles. Note that there's never any waiting for the MPS.
* THAT'S THE POINT.
*
@ -109,9 +109,10 @@ enum {
TYPE_PROMISE,
TYPE_CHARACTER,
TYPE_VECTOR,
TYPE_FWD2, /* two-word broken heart */
TYPE_FWD, /* three-words and up broken heart */
TYPE_PAD1 /* one-word padding object */
TYPE_FWD, /* three words and up forwarding object */
TYPE_FWD2, /* two-word forwarding object */
TYPE_PAD, /* two words and up padding object */
TYPE_PAD1, /* one-word padding object */
};
typedef struct type_s {
@ -193,17 +194,22 @@ typedef struct vector_s {
* See obj_pad, obj_fwd etc. to see how these are used.
*/
typedef struct fwd2_s {
type_t type; /* TYPE_FWD2 */
obj_t fwd; /* forwarded object */
} fwd2_s;
typedef struct fwd_s {
type_t type; /* TYPE_FWD */
obj_t fwd; /* forwarded object */
size_t size; /* total size of this object */
} fwd_s;
typedef struct fwd2_s {
type_t type; /* TYPE_FWD2 */
obj_t fwd; /* forwarded object */
} fwd2_s;
typedef struct pad_s {
type_t type; /* TYPE_PAD */
size_t size; /* total size of this object */
} pad_s;
typedef struct pad1_s {
type_t type; /* TYPE_PAD1 */
} pad1_s;
@ -222,6 +228,7 @@ typedef union obj_u {
vector_s vector;
fwd2_s fwd2;
fwd_s fwd;
pad_s pad;
} obj_s;
@ -317,7 +324,7 @@ static obj_t obj_unquote_splic; /* "unquote-splicing" symbol */
* be decoded by enclosing code.]
*/
static jmp_buf *error_handler;
static jmp_buf *error_handler = NULL;
static char error_message[MSGMAX+1];
@ -363,13 +370,17 @@ static void error(char *format, ...)
{
va_list args;
assert(error_handler != NULL);
va_start(args, format);
vsprintf(error_message, format, args);
vsnprintf(error_message, sizeof error_message, format, args);
va_end(args);
longjmp(*error_handler, 1);
if (error_handler) {
longjmp(*error_handler, 1);
} else {
fprintf(stderr, "Fatal error during initialization: %s\n",
error_message);
abort();
}
}
@ -826,8 +837,8 @@ static void print(obj_t obj, unsigned depth, FILE *stream)
} break;
default:
assert(0);
abort();
assert(0);
abort();
}
}
@ -2351,33 +2362,42 @@ static obj_t entry_gc(obj_t env, obj_t op_env, obj_t operator, obj_t operands)
/* special table */
static struct {char *name; obj_t *varp;} sptab[] = {
static struct {
char *name;
obj_t *varp;
} sptab[] = {
{"()", &obj_empty},
{"#[eof]", &obj_eof},
{"#[error]", &obj_error},
{"#t", &obj_true},
{"#f", &obj_false},
{"#[undefined]", &obj_undefined},
{"#[tail]", &obj_tail}
{"#[tail]", &obj_tail},
};
/* initial symbol table */
static struct {char *name; obj_t *varp;} isymtab[] = {
static struct {
char *name;
obj_t *varp;
} isymtab[] = {
{"quote", &obj_quote},
{"lambda", &obj_lambda},
{"begin", &obj_begin},
{"else", &obj_else},
{"quasiquote", &obj_quasiquote},
{"unquote", &obj_unquote},
{"unquote-splicing", &obj_unquote_splic}
{"unquote-splicing", &obj_unquote_splic},
};
/* operator table */
static struct {char *name; entry_t entry;} optab[] = {
static struct {
char *name;
entry_t entry;
} optab[] = {
{"quote", entry_quote},
{"define", entry_define},
{"set!", entry_set},
@ -2392,13 +2412,16 @@ static struct {char *name; entry_t entry;} optab[] = {
{"letrec", entry_letrec},
{"do", entry_do},
{"delay", entry_delay},
{"quasiquote", entry_quasiquote}
{"quasiquote", entry_quasiquote},
};
/* function table */
static struct {char *name; entry_t entry;} funtab[] = {
static struct {
char *name;
entry_t entry;
} funtab[] = {
{"not", entry_not},
{"boolean?", entry_booleanp},
{"eqv?", entry_eqvp},
@ -2442,7 +2465,7 @@ static struct {char *name; entry_t entry;} funtab[] = {
{"eval", entry_eval},
{"symbol->string", entry_symbol_to_string},
{"string->symbol", entry_string_to_symbol},
{"gc", entry_gc}
{"gc", entry_gc},
};
@ -2475,12 +2498,12 @@ static struct {char *name; entry_t entry;} funtab[] = {
static mps_res_t obj_scan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit)
{
#define FIX(ref) \
do { \
#define FIX(ref) \
do { \
mps_addr_t _addr = (ref); /* copy to local to avoid type pun */ \
mps_res_t res = MPS_FIX12(ss, &_addr); \
if (res != MPS_RES_OK) return res; \
(ref) = _addr; \
mps_res_t res = MPS_FIX12(ss, &_addr); \
if (res != MPS_RES_OK) return res; \
(ref) = _addr; \
} while(0)
MPS_SCAN_BEGIN(ss) {
@ -2530,20 +2553,21 @@ static mps_res_t obj_scan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit)
ALIGN(offsetof(vector_s, vector) +
obj->vector.length * sizeof(obj->vector.vector[0]));
break;
case TYPE_FWD:
base = (char *)base + ALIGN(obj->fwd.size);
break;
case TYPE_FWD2:
base = (char *)base + ALIGN(sizeof(fwd2_s));
break;
case TYPE_FWD:
base = (char *)base + ALIGN(obj->fwd.size);
case TYPE_PAD:
base = (char *)base + ALIGN(obj->pad.size);
break;
case TYPE_PAD1:
base = (char *)base + ALIGN(sizeof(pad1_s));
break;
default:
assert(0);
fprintf(stderr, "Unexpected object on the heap\n");
abort();
return MPS_RES_FAIL;
}
}
} MPS_SCAN_END(ss);
@ -2594,20 +2618,21 @@ static mps_addr_t obj_skip(mps_addr_t base)
ALIGN(offsetof(vector_s, vector) +
obj->vector.length * sizeof(obj->vector.vector[0]));
break;
case TYPE_FWD:
base = (char *)base + ALIGN(obj->fwd.size);
break;
case TYPE_FWD2:
base = (char *)base + ALIGN(sizeof(fwd2_s));
break;
case TYPE_FWD:
base = (char *)base + ALIGN(obj->fwd.size);
case TYPE_PAD:
base = (char *)base + ALIGN(obj->pad.size);
break;
case TYPE_PAD1:
base = (char *)base + ALIGN(sizeof(pad1_s));
break;
default:
assert(0);
fprintf(stderr, "Unexpected object on the heap\n");
abort();
return NULL;
}
return base;
}
@ -2625,10 +2650,10 @@ static mps_addr_t obj_isfwd(mps_addr_t addr)
{
obj_t obj = addr;
switch (obj->type.type) {
case TYPE_FWD2:
return obj->fwd2.fwd;
case TYPE_FWD:
return obj->fwd.fwd;
case TYPE_FWD2:
return obj->fwd2.fwd;
}
return NULL;
}
@ -2663,10 +2688,10 @@ static void obj_fwd(mps_addr_t old, mps_addr_t new)
*
* The job of `obj_pad` is to fill in a block of memory with a padding
* object that will be skipped by `obj_scan` or `obj_skip` but does
* nothing else. Because we've chosen to align to single words, we may
* nothing else. Because we've chosen to align to single words, we may
* have to pad a single word, so we have a special single-word padding
* object, `PAD1` for that purpose. Otherwise we can use forwarding
* objects with their `fwd` fields set to `NULL`.
* object, `PAD1` for that purpose. Otherwise we can use multi-word
* padding objects, `PAD`.
*/
static void obj_pad(mps_addr_t addr, size_t size)
@ -2675,31 +2700,13 @@ static void obj_pad(mps_addr_t addr, size_t size)
assert(size >= ALIGN(sizeof(pad1_s)));
if (size == ALIGN(sizeof(pad1_s))) {
obj->type.type = TYPE_PAD1;
} else if (size == ALIGN(sizeof(fwd2_s))) {
obj->type.type = TYPE_FWD2;
obj->fwd2.fwd = NULL;
} else {
obj->type.type = TYPE_FWD;
obj->fwd.fwd = NULL;
obj->fwd.size = size;
obj->type.type = TYPE_PAD;
obj->pad.size = size;
}
}
/* obj_copy -- object format copy method %%MPS
*
* The job of `obj_copy` is to make a copy of an object.
* TODO: Explain why this exists.
*/
static void obj_copy(mps_addr_t old, mps_addr_t new)
{
mps_addr_t limit = obj_skip(old);
size_t size = (char *)limit - (char *)old;
(void)memcpy(new, old, size);
}
/* obj_fmt_s -- object format parameter structure %%MPS
*
* This is simply a gathering of the object format methods and the chosen
@ -2710,10 +2717,10 @@ struct mps_fmt_A_s obj_fmt_s = {
sizeof(mps_word_t),
obj_scan,
obj_skip,
obj_copy,
NULL,
obj_fwd,
obj_isfwd,
obj_pad
obj_pad,
};
@ -2908,7 +2915,7 @@ static void *start(void *p, size_t s)
static mps_gen_param_s obj_gen_params[] = {
{ 150, 0.85 },
{ 170, 0.45 }
{ 170, 0.45 },
};

View file

@ -227,7 +227,7 @@ static obj_t obj_unquote_splic; /* "unquote-splicing" symbol */
* be decoded by enclosing code.]
*/
static jmp_buf *error_handler;
static jmp_buf *error_handler = NULL;
static char error_message[MSGMAX+1];
@ -247,13 +247,17 @@ static void error(char *format, ...)
{
va_list args;
assert(error_handler != NULL);
va_start(args, format);
vsprintf(error_message, format, args);
vsnprintf(error_message, sizeof error_message, format, args);
va_end(args);
longjmp(*error_handler, 1);
if (error_handler) {
longjmp(*error_handler, 1);
} else {
fprintf(stderr, "Fatal error during initialization: %s\n",
error_message);
abort();
}
}
@ -625,8 +629,8 @@ static void print(obj_t obj, unsigned depth, FILE *stream)
} break;
default:
assert(0);
abort();
assert(0);
abort();
}
}
@ -2059,32 +2063,41 @@ static obj_t entry_string_to_symbol(obj_t env, obj_t op_env, obj_t operator, obj
/* special table */
static struct {char *name; obj_t *varp;} sptab[] = {
static struct {
char *name;
obj_t *varp;
} sptab[] = {
{"()", &obj_empty},
{"#[eof]", &obj_eof},
{"#[error]", &obj_error},
{"#t", &obj_true},
{"#f", &obj_false},
{"#[undefined]", &obj_undefined}
{"#[undefined]", &obj_undefined},
};
/* initial symbol table */
static struct {char *name; obj_t *varp;} isymtab[] = {
static struct {
char *name;
obj_t *varp;
} isymtab[] = {
{"quote", &obj_quote},
{"lambda", &obj_lambda},
{"begin", &obj_begin},
{"else", &obj_else},
{"quasiquote", &obj_quasiquote},
{"unquote", &obj_unquote},
{"unquote-splicing", &obj_unquote_splic}
{"unquote-splicing", &obj_unquote_splic},
};
/* operator table */
static struct {char *name; entry_t entry;} optab[] = {
static struct {
char *name;
entry_t entry;
} optab[] = {
{"quote", entry_quote},
{"define", entry_define},
{"set!", entry_set},
@ -2099,13 +2112,16 @@ static struct {char *name; entry_t entry;} optab[] = {
{"letrec", entry_letrec},
{"do", entry_do},
{"delay", entry_delay},
{"quasiquote", entry_quasiquote}
{"quasiquote", entry_quasiquote},
};
/* function table */
static struct {char *name; entry_t entry;} funtab[] = {
static struct {
char *name;
entry_t entry;
} funtab[] = {
{"not", entry_not},
{"boolean?", entry_booleanp},
{"eqv?", entry_eqvp},
@ -2146,7 +2162,7 @@ static struct {char *name; entry_t entry;} funtab[] = {
{"vector-fill!", entry_vector_fill},
{"eval", entry_eval},
{"symbol->string", entry_symbol_to_string},
{"string->symbol", entry_string_to_symbol}
{"string->symbol", entry_string_to_symbol},
};

View file

@ -1,19 +1,20 @@
TODO
2. Create new types of objects for pools and topics.
3. Glossary entries need permalinks. See
<https://bitbucket.org/birkenfeld/sphinx/issue/996/expose-glossary-entry-link-on-hover>
5. Fix the general index so that (1) aren't interpreted as subentries.
7. Re-do the diagrams in vector form and using the colour palette.
8. Use a better bibliography extension. See for example
<http://wnielson.bitbucket.org/projects/sphinx-natbib/>
9. Hyphenate long function names across line endings (but what if you
copy them?)
2. Create new types of objects for pools and topics.
3. Glossary entries need permalinks. See
<https://bitbucket.org/birkenfeld/sphinx/issue/996/expose-glossary-entry-link-on-hover>
5. Fix the general index so that (1) aren't interpreted as
subentries.
7. Re-do the diagrams in vector form and using the colour palette.
8. Use a better bibliography extension. See for example
<http://wnielson.bitbucket.org/projects/sphinx-natbib/>
9. Hyphenate long function names across line endings (but what if you
copy them?)
11. Support MMREF-style anchors to the glossary (#garbage.collection
instead of #garbage-collection).
@ -21,17 +22,88 @@ TODO
QUERIES
1. Does the object format description (struct mps_fmt_A_s) have to be
static?
1. Does the object format description (struct mps_fmt_A_s etc) have
to be static? Is it OK to throw it away after calling
mps_fmt_create?
2. What is the difference, if any, between mps_word_t and MPS_T_WORD?
Same question for the generation structure (mps_gen_param_s)?
2. What is the difference, if any, between mps_word_t and MPS_T_WORD?
3. How can I explain why the Scheme example uses sizeof(mps_word_t)
as its alignment? Why not MPS_PF_ALIGN (or are client programs not
supposed to look at mpstd.h)? Why not something of its own
manufacture, like sizeof(union {long, size_t, void*})?
4. Why does the Scheme example have a copy method in its object
format when the reference manual says it's obsolete?
3. How can I explain why the Scheme example uses sizeof(mps_word_t) as
its alignment? Why not MPS_PF_ALIGN? Why not something of its own
manufacture, like sizeof(union {long, size_t, void*})?
I removed it.
5. What is the difference between the "event stream" and the
"telemetry stream"? Are these names for the same thing? Or is
there a distinction (for example, "event stream" refers to the
internal, unfiltered, stream of events and "telemetry stream"
refers to the filtered stream)?
6. The location dependency functions all take an arena as an
argument. What is the role of this argument?
7. What is the role of the third (addr) argument to mps_ld_isstale?
LDIsStale says "UNUSED(addr);" so maybe it is unused.
8. Is the material in the pool class comparison table at all accurate?
9. This code seems a bit confused about what to do:
4. Why does the Scheme example have a copy method in its object format
when the reference manual says it's obsolete? Can I remove it?
assert(0);
fprintf(stderr, "Unexpected object on the heap\n");
abort();
return MPS_RES_FAIL;
I took out the assertion and the return.
10. How does fixing interact with tagged references? Do I need to
remove the tag before fixing a reference? Do I need to restore the
tag afterwards? I thought that both would be necessary but the
critical path documentation has an example from OpenDylan with
tagged references that does neither:
<//info.ravenbrook.com/project/mps/master/design/critical-path.txt>
11. This code from mps_chat in the Scheme example is wrong:
if (type == mps_message_type_gc_start()) {
printf("Collection %lu started.\n", (unsigned long)mps_collections(arena));
mps_collections returns the total number of collections to date,
not the number of the collection that posted the message. This
means that if there have been multiple collections since the last
time the message queue was emptied, the output will look like
Collection 47 started.
...
Collection 47 started.
...
Collection 47 started.
...
Collection 47 started.
12. It seems "tricky" to re-use fowarding objects as padding objects
by setting their forwarding pointer to NULL. It would be simpler
to explain if we had TYPE_PAD for multiple-word padding objects.
There's enough to explain as it is!
I made this change.
13. The Scheme example says, "Adapting it to use the MPS took
approximately two hours". I doubt this would be the common case,
and it would be better to under-promise and over-deliver.
14. I need to document the values in mps_gen_param_s. I believe they
are the capacity (size of the generation in kilobytes) and the
mortality (the proportion of objects in this generation that are
expected to die in a collection). But what do they mean to the
MPS? And how should one go about choosing values?
DONE

View file

@ -1173,8 +1173,8 @@ Declared in ``mps.h``
browsers), or a null pointer if this is not possible.
It is recommended that a null pointer be returned for
:term:`padding objects <padding object>` and :term:`forwarded
objects <forwarded object>`.
:term:`padding objects <padding object>` and :term:`forwarding
objects <forwarding object>`.
The exact meaning of the return value is up to the :term:`client
program`, but it would typically bear some relation to a class or
@ -1295,7 +1295,7 @@ Declared in ``mps.h``
*addr* is the address of a candidate object.
If the *addr* is the address of a :term:`forwarded object`, return
If the *addr* is the address of a :term:`forwarding object`, return
the address where the object was moved to. This must be the value
of the *new* argument supplied to the :term:`forward method` when
the object was moved. If not, return a null pointer.
@ -1362,10 +1362,10 @@ Declared in ``mps.h``
:c:macro:`MPS_RES_OK`.
The scan method for an object format is called when the MPS needs
to scan objects in a block area of memory containing objects
belonging to that format. The scan method is called with a scan
state and the base and limit of the block of objects to scan. It
must then indicate references within the objects by calling
to scan objects in a block of memory containing objects belonging
to that format. The scan method is called with a scan state and
the base and limit of the block of objects to scan. It must then
indicate references within the objects by calling
:c:func:`MPS_FIX1` and :c:func:`MPS_FIX2`.
.. topics::