diff --git a/mps/manual/source/guide/index.rst b/mps/manual/source/guide/index.rst index a7218dd2ba5..fd98e0481e5 100644 --- a/mps/manual/source/guide/index.rst +++ b/mps/manual/source/guide/index.rst @@ -13,4 +13,4 @@ Guide debug perf advanced - + malloc diff --git a/mps/manual/source/guide/lang.rst b/mps/manual/source/guide/lang.rst index 68473613ba2..88c7ec93eb5 100644 --- a/mps/manual/source/guide/lang.rst +++ b/mps/manual/source/guide/lang.rst @@ -266,6 +266,25 @@ code for creating the object format for the toy Scheme interpreter:: } MPS_ARGS_END(args); if (res != MPS_RES_OK) error("Couldn't create obj format"); +The keyword arguments specify the :term:`alignment` and the +:term:`format methods` required by the AMC pool class. These are +described in the following sections. + +.. topics:: + + :ref:`topic-format`. + + +.. index:: + single: alignment + single: alignment; object + single: Scheme; object alignment + +.. _guide-lang-alignment: + +Alignment +^^^^^^^^^ + The argument for the keyword :c:macro:`MPS_KEY_FMT_ALIGN` is the :term:`alignment` of objects belonging to this format. Determining the alignment is hard to do portably, because it depends on the target @@ -294,13 +313,19 @@ memory. Here are some things you might try: #define ALIGNMENT sizeof(mps_word_t) -The other keyword arguments specify the :term:`format methods` -required by the AMC pool class, which are described in the following -sections. +#. The MPS interface provides the type :c:type:`MPS_PF_ALIGN`, which + is the :term:`natural alignment` of the platform: the largest + alignment that might be required. So as a last resort, you can + use:: -.. topics:: + #define ALIGNMENT MPS_PF_ALIGN - :ref:`topic-format`. + But this may be larger than necessary and so waste space. For + example, on Windows on x86-64, :c:type:`MPS_PF_ALIGN` is 16 bytes, + but this is only necessary for SSE_ types; ordinary types on this + platform require no more than 8-byte alignment. + + .. _SSE: http://msdn.microsoft.com/en-us/library/t467de55.aspx .. index:: diff --git a/mps/manual/source/guide/malloc.rst b/mps/manual/source/guide/malloc.rst new file mode 100644 index 00000000000..6718ce8b19c --- /dev/null +++ b/mps/manual/source/guide/malloc.rst @@ -0,0 +1,68 @@ +.. index:: + single: malloc; implementing + single: free; implementing + +.. _guide-malloc: + +Implementing malloc and free +============================ + +The MPS function :c:func:`mps_free` is unlike the Standard C Library +function :c:func:`free` in that it takes a ``size`` argument. That's +because it's nearly always the case that either the size of an object +is known statically based on its type (for example, a structure), or +else the size of the object is easily computed from information that +needs to be stored anyway (for example, a vector), and so memory can +be saved by not storing the size separately. + +But sometimes you need to interact with :term:`foreign code` which +requires :c:func:`malloc` and :c:func:`free` (or a pair of functions +with the same interface). How can you use the MPS to implement these +functions? By storing the size in a header adjacent to the object, +like this:: + + #include "mps.h" + + static mps_pool_t malloc_pool; + + typedef union { + size_t size; + char alignment[MPS_PF_ALIGN]; /* see note below */ + } header_u; + + void *malloc(size_t size) { + mps_res_t res; + mps_addr_t p; + header_u *header; + size += sizeof *header; + res = mps_alloc(&p, malloc_pool, size); + if (res != MPS_RES_OK) + return NULL; + header = p; + header->size = size; + return header + 1; + } + + void free(void *p) { + if (p) { + header_u *header = ((header_u *)p) - 1; + mps_free(malloc_pool, header, header->size); + } + } + +The ``alignment`` member of ``union header_u`` ensures that +allocations are aligned to the platform's :term:`natural alignment` +(see :ref:`guide-lang-alignment`). + +The pool needs to belong to a :term:`manually managed ` pool class, for example :ref:`pool-mvff` (or its +:ref:`debugging counterpart `):: + + #include "mpscmvff.h" + + void malloc_pool_init(mps_arena_t arena) { + mps_res_t res; + res = mps_pool_create_k(&malloc_pool, arena, mps_class_mvff(), mps_args_none); + if (res != RES_OK) + abort(); + } diff --git a/mps/test/function/229.c b/mps/test/function/229.c new file mode 100644 index 00000000000..27d1f087a93 --- /dev/null +++ b/mps/test/function/229.c @@ -0,0 +1,74 @@ +/* +TEST_HEADER + id = $Id: //info.ravenbrook.com/project/mps/master/test/function/1.c#2 $ + summary = test malloc and free (for the manual) + language = c + link = testlib.o + parameters = POINTERS=1000 ITERATIONS=10000 +END_HEADER +*/ + +#include "mpscmvff.h" +#include "testlib.h" + +static mps_pool_t malloc_pool; + +typedef union { + size_t size; + char alignment[MPS_PF_ALIGN]; +} header_u; + +static void *xmalloc(size_t size) +{ + mps_res_t res; + mps_addr_t p; + header_u *header; + size += sizeof *header; + res = mps_alloc(&p, malloc_pool, size); + if (res != MPS_RES_OK) + return NULL; + header = p; + header->size = size; + return header + 1; +} + +static void xfree(void *p) +{ + if (p) { + header_u *header = ((header_u *)p) - 1; + mps_free(malloc_pool, header, header->size); + } +} + +static void test(void) +{ + mps_arena_t arena; + size_t i, j; + void *p[POINTERS] = {0}; + + cdie(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), + "create arena"); + cdie(mps_pool_create_k(&malloc_pool, arena, mps_class_mvff(), mps_args_none), + "create pool"); + + for (i = 0; i < ITERATIONS; ++i) { + j = ranint(POINTERS); + xfree(p[j]); + p[j] = xmalloc(ranint(POINTERS)); + } + for (j = 0; j < POINTERS; ++j) { + xfree(p[j]); + } + asserts(mps_pool_free_size(malloc_pool) == mps_pool_total_size(malloc_pool), + "free size != total_size"); + + mps_pool_destroy(malloc_pool); + mps_arena_destroy(arena); +} + +int main(void) +{ + easy_tramp(test); + pass(); + return 0; +} diff --git a/mps/test/testsets/passing b/mps/test/testsets/passing index 4c0a615cd88..fde2ca5de8d 100644 --- a/mps/test/testsets/passing +++ b/mps/test/testsets/passing @@ -168,3 +168,4 @@ function/224.c % 225 -- no such test function/226.c function/227.c +function/229.c \ No newline at end of file