Skip to content

Commit 7721525

Browse files
committed
Merging branch/2023-05-16/interior-pointers for GitHub pull request #223 <#223>.
2 parents edcc10d + 4956861 commit 7721525

File tree

19 files changed

+738
-112
lines changed

19 files changed

+738
-112
lines changed

code/addrobj.c

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
/* addrobj.c: BASE ADDRESS FROM INTERIOR POINTER TEST
2+
*
3+
* Copyright (c) 2023 Ravenbrook Limited. See end of file for license.
4+
*
5+
* .overview This test is for mps_addr_object(). Its intention is to
6+
* verify that the function returns the appropriate base pointer to an
7+
* object when provided with an interior pointer. It also tests that the
8+
* function fails appropriately when the provided with a pointer to
9+
* unmanaged memory, or to an object in a pool that doesn't support this
10+
* feature.
11+
*
12+
* .limitations Objects that have been moved should cause the function to
13+
* fail with MPS_RES_FAIL, however this is not tested. It could be tested if
14+
* a testbench deliberately created a forwarding object, however this might
15+
* confuse a pool that does automatic garbage collection such as AMC or AMCZ,
16+
* so any such test would need to be designed to handle that.
17+
* This test only examines behaviour in AMCZ and MVFF pools, i.e. A pool (AMCZ)
18+
* which currently implements mps_addr_object() and one (MVFF) that doesn't.
19+
*/
20+
21+
#include "mps.h"
22+
#include "testlib.h"
23+
#include "fmtdy.h"
24+
#include "fmtdytst.h"
25+
#include "mpsavm.h"
26+
#include "mpscamc.h"
27+
#include "mpscmvff.h"
28+
#include "stdio.h"
29+
#include <stdlib.h>
30+
31+
/* Define an object size to allocate. The size chosen doesn't matter much, except that this testbench assumes
32+
that the object is large enough that a pointer could point to the interior of the object, without also
33+
pointing to the base pointer of the object at the same time. For char pointers, this is probably 2 bytes.
34+
Since we are using the Dylan library, we define the size of the object in terms of Dylan slots. See
35+
fmtdytst.c for details of the Dylan object structure.*/
36+
#define N_SLOT_TESTOBJ 100
37+
38+
static void test_main(void)
39+
{
40+
mps_arena_t arena;
41+
mps_pool_t amcz_pool, mvff_pool;
42+
mps_ap_t obj_ap;
43+
mps_fmt_t obj_fmt;
44+
mps_root_t testobj_root;
45+
mps_res_t res;
46+
/* In another testbench (extcon.c) we observed unreliable failures to do with registering the cold end
47+
of the stack. See GitHub issue #210
48+
<https://github.com/Ravenbrook/mps/issues/210>. For now, we
49+
declare this as a separate root. */
50+
static mps_addr_t testobj;
51+
mps_addr_t out, in;
52+
53+
/* Create arena */
54+
die(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), "mps_arena_create_k");
55+
56+
57+
/* INTRO TO TESTS: There are several tests. They test the expected "normal" operation of the
58+
function, using an interior pointer, also corner cases where the interior pointer equals the
59+
base pointer, where it equals the limit pointer. We also test asking about an address in unmanaged
60+
memory, and about an address in a pool which currently does not support mps_addr_object. If you write
61+
more tests, describe them here.*/
62+
63+
64+
/* TEST 1: Test using an interior pointer in an object in an AMCZ pool.
65+
At the time of writing this test, the AMCZ pool is the only pool where
66+
there exists a requirement to provide base addresses from interior pointers.
67+
Currently, the AMCZ pool (and by extension, the AMC pool which shares the same
68+
module as AMCZ) is the only pool for which mps_addr_object is implemented */
69+
70+
/* Use the dylan format for convenience */
71+
die(dylan_fmt(&obj_fmt, arena), "dylan_fmt");
72+
73+
/* Create the pool */
74+
MPS_ARGS_BEGIN(args) {
75+
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, obj_fmt);
76+
die(mps_pool_create_k(&amcz_pool, arena, mps_class_amcz(), args), "mps_pool_create_k amcz");
77+
} MPS_ARGS_END(args);
78+
79+
/* Create an area of ambiguous pointers to keep the object alive and in place, in this case
80+
the area only contains room for a single reference since we are only using one object to test */
81+
die(mps_root_create_area(&testobj_root, arena,
82+
mps_rank_ambig(), (mps_rm_t)0,
83+
&testobj, &testobj+1,
84+
mps_scan_area, NULL),
85+
"mps_root_create_area");
86+
87+
/* Create the allocation point */
88+
die(mps_ap_create_k(&obj_ap, amcz_pool, mps_args_none), "mps_ap_create_k");
89+
90+
/* Make a Dylan object, size = (N_SLOT_TESTOBJ+2) * sizeof(mps_word_t).
91+
(See fmtdytst.c for size calculation) */
92+
{
93+
/* Because make_dylan_vector returns its pointer-to-object as an mps_word_t rather than an
94+
mps_addr_t, and commits the object, we need to somehow safely allocate our object without
95+
type punning and without risking that our object be destroyed.
96+
Rather than redefine our reference table with type mps_word_t, which hides the intention of the table,
97+
park the arena to disable garbage collection. Allocate our dylan object on the (unregistered) stack
98+
storing its address in an mps_word_t. Then store this mps_word_t as an mps_addr_t in our reference
99+
table, and release the arena since our object is now safely pinned.
100+
Another approach would be to create another static registered root for ambiguous references of type
101+
mps_word_t and then copy to the mps_addr_t root, which would avoid needing to park the arena.
102+
*/
103+
mps_word_t p_word;
104+
mps_arena_park(arena);
105+
die(make_dylan_vector(&p_word, obj_ap, N_SLOT_TESTOBJ), "make_dylan_vector");
106+
/* If we hadn't parked the arena, our vector might have been GC'd here */
107+
testobj = (mps_addr_t)p_word;
108+
mps_arena_release(arena);
109+
}
110+
111+
/* Construct a pointer to roughly halfway inside the object */
112+
in = (mps_addr_t)((char *)testobj + (N_SLOT_TESTOBJ/2) * sizeof(mps_word_t));
113+
114+
/* Ensure that this is an interior pointer, and not the base pointer,
115+
since we want to make sure we are testing with a true interior pointer and not
116+
one that also happens to be the base pointer. This Insist is intended to protect
117+
against the testbench losing its ability to test "true" interior pointers (i.e. ones
118+
which don't match the base pointer) if the test object sizes were changed to be very
119+
small. Note that we don't currently consider the "limit" of the object as a corner case
120+
(so we don't Insist(in != limit) ) but we do consider limit+1, i.e. the pointer to the
121+
next object to be a corner case. This test could be updated to consider in == limit as a
122+
corner case. */
123+
Insist(in > testobj);
124+
125+
/* Do Test */
126+
res = mps_addr_object(&out, arena, in);
127+
Insist(out == testobj);
128+
Insist(res == MPS_RES_OK);
129+
printf("Interior pointer input: passed\n");
130+
131+
132+
/* TEST 2: Test using the base pointer itself as an input*/
133+
134+
in = testobj;
135+
136+
/* Do Test */
137+
res = mps_addr_object(&out, arena, in);
138+
Insist(out == testobj);
139+
Insist(res == MPS_RES_OK);
140+
printf("Base pointer input: passed\n");
141+
142+
143+
144+
/* TEST 3: Test using a pointer one-off-the-end of the object*/
145+
146+
in = (mps_addr_t)((char *)testobj + (N_SLOT_TESTOBJ + 2) * sizeof(mps_word_t));
147+
148+
/* Do Test */
149+
res = mps_addr_object(&out, arena, in);
150+
Insist(res == MPS_RES_FAIL);
151+
printf("Pointer to next object input: passed\n");
152+
153+
154+
/* Clean up from above tests */
155+
mps_root_destroy(testobj_root);
156+
mps_ap_destroy(obj_ap);
157+
mps_pool_destroy(amcz_pool);
158+
mps_fmt_destroy(obj_fmt);
159+
160+
161+
/* TEST 4: Test using a pointer in unmanaged memory */
162+
163+
/* Use malloc to allocate non-mps-managed memory on the heap */
164+
in = malloc(sizeof(mps_word_t));
165+
Insist(NULL != in);
166+
167+
/* Do the test */
168+
res = mps_addr_object(&out, arena, in);
169+
170+
/* Expect MPS to fail to find a base pointer for addresses not in managed memory */
171+
Insist(res == MPS_RES_FAIL);
172+
printf("Pointer to unmanaged memory input: passed\n");
173+
174+
/* clean up from this test */
175+
if (NULL != in)
176+
free(in);
177+
178+
179+
/* TEST 5: Test using a pointer in a pool which currently doesn't implement mps_addr_object */
180+
181+
/* Create mvff pool for which mps_addr_object is not implemented */
182+
die(mps_pool_create_k(&mvff_pool, arena, mps_class_mvff(), mps_args_none), "mps_pool_create_k mvff");
183+
184+
/* allocate an object (just some memory) in this pool */
185+
die(mps_alloc(&in, mvff_pool, sizeof(mps_word_t)), "mps_alloc");
186+
187+
/* Do the test */
188+
res = mps_addr_object(&out, arena, in);
189+
190+
Insist(res == MPS_RES_UNIMPL);
191+
printf("Pointer to object in pool where mps_addr_object not implemented: passed\n");
192+
193+
194+
/* If more tests are added here, briefly describe them above under "INTRO TO TESTS" comment */
195+
196+
/* Final clean up */
197+
mps_free(mvff_pool, in, sizeof(mps_word_t));
198+
mps_pool_destroy(mvff_pool);
199+
mps_arena_destroy(arena);
200+
}
201+
202+
int main(int argc, char *argv[])
203+
{
204+
testlib_init(argc, argv);
205+
206+
test_main();
207+
208+
printf("%s: Conculsion, failed to find any defects.\n", argv[0]);
209+
210+
return 0;
211+
}
212+
213+
/* C. COPYRIGHT AND LICENSE
214+
*
215+
* Copyright (C) 2022-2023 Ravenbrook Limited <https://www.ravenbrook.com/>.
216+
*
217+
* Redistribution and use in source and binary forms, with or without
218+
* modification, are permitted provided that the following conditions are
219+
* met:
220+
*
221+
* 1. Redistributions of source code must retain the above copyright
222+
* notice, this list of conditions and the following disclaimer.
223+
*
224+
* 2. Redistributions in binary form must reproduce the above copyright
225+
* notice, this list of conditions and the following disclaimer in the
226+
* documentation and/or other materials provided with the
227+
* distribution.
228+
*
229+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
230+
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
231+
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
232+
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
233+
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
234+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
235+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
236+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
237+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
238+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
239+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
240+
*/

code/arena.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1364,6 +1364,22 @@ Bool ArenaHasAddr(Arena arena, Addr addr)
13641364
return TractOfAddr(&tract, arena, addr);
13651365
}
13661366

1367+
/* ArenaAddrObject -- return base pointer of managed object */
1368+
Res ArenaAddrObject(Addr *pReturn, Arena arena, Addr addr)
1369+
{
1370+
Tract tract;
1371+
1372+
AVER(pReturn != NULL);
1373+
AVERT(Arena, arena);
1374+
1375+
if (!TractOfAddr(&tract, arena, addr)) {
1376+
/* address does not belong to the arena */
1377+
return ResFAIL;
1378+
}
1379+
1380+
return PoolAddrObject(pReturn, TractPool(tract), addr);
1381+
}
1382+
13671383

13681384
/* C. COPYRIGHT AND LICENSE
13691385
*

code/comm.gmk

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ LIB_TARGETS=mps.a mpsplan.a
253253

254254
TEST_TARGETS=\
255255
abqtest \
256+
addrobj \
256257
airtest \
257258
amcss \
258259
amcsshe \
@@ -446,6 +447,9 @@ ifdef VARIETY
446447
$(PFM)/$(VARIETY)/abqtest: $(PFM)/$(VARIETY)/abqtest.o \
447448
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
448449

450+
$(PFM)/$(VARIETY)/addrobj: $(PFM)/$(VARIETY)/addrobj.o \
451+
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
452+
449453
$(PFM)/$(VARIETY)/airtest: $(PFM)/$(VARIETY)/airtest.o \
450454
$(FMTSCMOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
451455

code/commpost.nmk

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ $(PFM)\cool\mps.lib: $(MPMOBJ)
177177
$(PFM)\$(VARIETY)\abqtest.exe: $(PFM)\$(VARIETY)\abqtest.obj \
178178
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
179179

180+
$(PFM)\$(VARIETY)\addrobj.exe: $(PFM)\$(VARIETY)\addrobj.obj \
181+
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
182+
180183
$(PFM)\$(VARIETY)\airtest.exe: $(PFM)\$(VARIETY)\airtest.obj \
181184
$(PFM)\$(VARIETY)\mps.lib $(FMTSCHEMEOBJ) $(TESTLIBOBJ)
182185

code/commpre.nmk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ LIB_TARGETS=mps.lib
5959

6060
TEST_TARGETS=\
6161
abqtest.exe \
62+
addrobj.exe \
6263
airtest.exe \
6364
amcss.exe \
6465
amcsshe.exe \

code/mpm.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ extern Res PoolTraceBegin(Pool pool, Trace trace);
236236
extern void PoolFreeWalk(Pool pool, FreeBlockVisitor f, void *p);
237237
extern Size PoolTotalSize(Pool pool);
238238
extern Size PoolFreeSize(Pool pool);
239+
extern Res PoolAddrObject(Addr *pReturn, Pool pool, Addr addr);
239240

240241
extern Res PoolAbsInit(Pool pool, Arena arena, PoolClass klass, ArgList arg);
241242
extern void PoolAbsFinish(Inst inst);
@@ -267,6 +268,7 @@ extern void PoolTrivFreeWalk(Pool pool, FreeBlockVisitor f, void *p);
267268
extern PoolDebugMixin PoolNoDebugMixin(Pool pool);
268269
extern BufferClass PoolNoBufferClass(void);
269270
extern Size PoolNoSize(Pool pool);
271+
extern Res PoolTrivAddrObject(Addr *pReturn, Pool pool, Addr addr);
270272

271273
/* See .critical.macros. */
272274
#define PoolFreeMacro(pool, old, size) Method(Pool, pool, free)(pool, old, size)
@@ -536,6 +538,7 @@ extern Res ArenaStartCollect(Globals globals, TraceStartWhy why);
536538
extern Res ArenaCollect(Globals globals, TraceStartWhy why);
537539
extern Bool ArenaBusy(Arena arena);
538540
extern Bool ArenaHasAddr(Arena arena, Addr addr);
541+
extern Res ArenaAddrObject(Addr *pReturn, Arena arena, Addr addr);
539542
extern void ArenaChunkInsert(Arena arena, Chunk chunk);
540543
extern void ArenaChunkRemoved(Arena arena, Chunk chunk);
541544
extern void ArenaAccumulateTime(Arena arena, Clock start, Clock now);

code/mpmst.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ typedef struct mps_pool_class_s {
6060
PoolRampEndMethod rampEnd; /* end a ramp pattern */
6161
PoolFramePushMethod framePush; /* push an allocation frame */
6262
PoolFramePopMethod framePop; /* pop an allocation frame */
63+
PoolAddrObjectMethod addrObject; /* return object's base pointer */
6364
PoolFreeWalkMethod freewalk; /* walk over free blocks */
6465
PoolBufferClassMethod bufferClass; /* default BufferClass of pool */
6566
PoolDebugMixinMethod debugMixin; /* find the debug mixin, if any */

code/mpmtypes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ typedef Res (*PoolFramePushMethod)(AllocFrame *frameReturn,
214214
Pool pool, Buffer buf);
215215
typedef Res (*PoolFramePopMethod)(Pool pool, Buffer buf,
216216
AllocFrame frame);
217+
typedef Res (*PoolAddrObjectMethod)(Addr *pReturn, Pool pool, Addr addr);
217218
typedef void (*PoolFreeWalkMethod)(Pool pool, FreeBlockVisitor f, void *p);
218219
typedef BufferClass (*PoolBufferClassMethod)(void);
219220
typedef PoolDebugMixin (*PoolDebugMixinMethod)(Pool pool);

code/mps.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,8 @@ extern mps_res_t _mps_fix2(mps_ss_t, mps_addr_t *);
845845
(ss)->_ufs = _mps_ufs; \
846846
MPS_END
847847

848+
/* Misc interface */
849+
extern mps_res_t mps_addr_object(mps_addr_t *p_o, mps_arena_t arena, mps_addr_t addr);
848850

849851
#endif /* mps_h */
850852

0 commit comments

Comments
 (0)