Skip to content

Commit 4823617

Browse files
committed
Catch-up merge from master into branch/2023-05-31/hash-arrays
2 parents 7a72635 + 8de6f8a commit 4823617

File tree

23 files changed

+1200
-127
lines changed

23 files changed

+1200
-127
lines changed

mps/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+
*/

mps/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
*

mps/code/arenavm.c

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,10 @@ static Bool vmChunkDestroy(Tree tree, void *closure)
428428
{
429429
Chunk chunk;
430430
VMChunk vmChunk;
431+
Arena arena;
432+
Addr base;
433+
Size size;
434+
VMArena vmArena;
431435

432436
AVERT(Tree, tree);
433437
AVER(closure == UNUSED_POINTER);
@@ -437,8 +441,14 @@ static Bool vmChunkDestroy(Tree tree, void *closure)
437441
AVERT(Chunk, chunk);
438442
vmChunk = Chunk2VMChunk(chunk);
439443
AVERT(VMChunk, vmChunk);
444+
arena = ChunkArena(chunk);
445+
vmArena = MustBeA(VMArena, arena);
446+
base = chunk->base;
447+
size = ChunkSize(chunk);
440448

441-
(void)vmArenaUnmapSpare(ChunkArena(chunk), ChunkSize(chunk), chunk);
449+
(*vmArena->contracted)(arena, base, size);
450+
451+
(void)vmArenaUnmapSpare(arena, size, chunk);
442452

443453
SparseArrayFinish(&vmChunk->pages);
444454

@@ -778,6 +788,7 @@ static void VMArenaDestroy(Arena arena)
778788
* <design/arena#.chunk.delete> */
779789
arena->primary = NULL;
780790
TreeTraverseAndDelete(&arena->chunkTree, vmChunkDestroy, UNUSED_POINTER);
791+
AVER(arena->chunkTree == TreeEMPTY);
781792

782793
/* Must wait until the chunks are destroyed, since vmChunkDestroy
783794
calls vmArenaUnmapSpare which uses the spare land. */
@@ -1223,7 +1234,6 @@ static Bool vmChunkCompact(Tree tree, void *closure)
12231234
{
12241235
Chunk chunk;
12251236
Arena arena = closure;
1226-
VMArena vmArena = MustBeA(VMArena, arena);
12271237

12281238
AVERT(Tree, tree);
12291239

@@ -1232,11 +1242,6 @@ static Bool vmChunkCompact(Tree tree, void *closure)
12321242
if(chunk != arena->primary
12331243
&& BTIsResRange(chunk->allocTable, 0, chunk->pages))
12341244
{
1235-
Addr base = chunk->base;
1236-
Size size = ChunkSize(chunk);
1237-
/* Callback before destroying the chunk, as the arena is (briefly)
1238-
invalid afterwards. See job003893. */
1239-
(*vmArena->contracted)(arena, base, size);
12401245
vmChunkDestroy(tree, UNUSED_POINTER);
12411246
return TRUE;
12421247
} else {

mps/code/comm.gmk

Lines changed: 8 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 \
@@ -267,6 +268,7 @@ TEST_TARGETS=\
267268
btcv \
268269
bttest \
269270
djbench \
271+
extcon \
270272
finalcv \
271273
finaltest \
272274
forktest \
@@ -445,6 +447,9 @@ ifdef VARIETY
445447
$(PFM)/$(VARIETY)/abqtest: $(PFM)/$(VARIETY)/abqtest.o \
446448
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
447449

450+
$(PFM)/$(VARIETY)/addrobj: $(PFM)/$(VARIETY)/addrobj.o \
451+
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
452+
448453
$(PFM)/$(VARIETY)/airtest: $(PFM)/$(VARIETY)/airtest.o \
449454
$(FMTSCMOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
450455

@@ -487,6 +492,9 @@ $(PFM)/$(VARIETY)/bttest: $(PFM)/$(VARIETY)/bttest.o \
487492
$(PFM)/$(VARIETY)/djbench: $(PFM)/$(VARIETY)/djbench.o \
488493
$(TESTLIBOBJ) $(TESTTHROBJ)
489494

495+
$(PFM)/$(VARIETY)/extcon: $(PFM)/$(VARIETY)/extcon.o \
496+
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
497+
490498
$(PFM)/$(VARIETY)/finalcv: $(PFM)/$(VARIETY)/finalcv.o \
491499
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
492500

mps/code/commpost.nmk

Lines changed: 6 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

@@ -225,6 +228,9 @@ $(PFM)\$(VARIETY)\cvmicv.exe: $(PFM)\$(VARIETY)\cvmicv.obj \
225228
$(PFM)\$(VARIETY)\djbench.exe: $(PFM)\$(VARIETY)\djbench.obj \
226229
$(TESTLIBOBJ) $(TESTTHROBJ)
227230

231+
$(PFM)\$(VARIETY)\extcon.exe: $(PFM)\$(VARIETY)\extcon.obj \
232+
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
233+
228234
$(PFM)\$(VARIETY)\finalcv.exe: $(PFM)\$(VARIETY)\finalcv.obj \
229235
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
230236

mps/code/commpre.nmk

Lines changed: 2 additions & 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 \
@@ -73,6 +74,7 @@ TEST_TARGETS=\
7374
btcv.exe \
7475
bttest.exe \
7576
djbench.exe \
77+
extcon.exe \
7678
finalcv.exe \
7779
finaltest.exe \
7880
fotest.exe \

0 commit comments

Comments
 (0)