diff --git a/mps/.p4ignore b/mps/.p4ignore index fb2e5c024e5..25cd419ae6d 100644 --- a/mps/.p4ignore +++ b/mps/.p4ignore @@ -16,5 +16,8 @@ TAGS *.dSYM code/*/*/*.d *.pyc +test/obj test/test/log test/test/obj +....gcda +....gcno \ No newline at end of file diff --git a/mps/code/abq.c b/mps/code/abq.c index b915bb42c9b..22286354c77 100644 --- a/mps/code/abq.c +++ b/mps/code/abq.c @@ -1,7 +1,7 @@ /* abq.c: QUEUE IMPLEMENTATION * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .purpose: A fixed-length FIFO queue. * @@ -185,18 +185,10 @@ Res ABQDescribe(ABQ abq, ABQDescribeElement describeElement, mps_lib_FILE *strea if(res != ResOK) return res; - res = METER_WRITE(abq->push, stream); - if(res != ResOK) - return res; - res = METER_WRITE(abq->pop, stream); - if(res != ResOK) - return res; - res = METER_WRITE(abq->peek, stream); - if(res != ResOK) - return res; - res = METER_WRITE(abq->delete, stream); - if(res != ResOK) - return res; + METER_WRITE(abq->push, stream); + METER_WRITE(abq->pop, stream); + METER_WRITE(abq->peek, stream); + METER_WRITE(abq->delete, stream); res = WriteF(stream, "}\n", NULL); if(res != ResOK) @@ -311,7 +303,7 @@ static void *ABQElement(ABQ abq, Index index) { /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/abqtest.c b/mps/code/abqtest.c index 49d285c761f..367bafe730b 100644 --- a/mps/code/abqtest.c +++ b/mps/code/abqtest.c @@ -7,16 +7,16 @@ #include "abq.h" #include "mps.h" #include "mpsavm.h" +#include "mpscmfs.h" #include "mpstd.h" #include "testlib.h" -#include /* free, malloc */ #include /* printf */ SRCID(abqtest, "$Id$"); - +static mps_pool_t pool; static ABQStruct abq; /* the ABQ which we will use */ static Size abqSize; /* the size of the current ABQ */ @@ -51,9 +51,12 @@ static TestBlock testBlocks = NULL; static TestBlock CreateTestBlock(unsigned no) { - TestBlock b = malloc(sizeof(TestBlockStruct)); - cdie(b != NULL, "malloc"); + TestBlock b; + mps_addr_t p; + die(mps_alloc(&p, pool, sizeof(TestBlockStruct)), "alloc"); + + b = p; b->next = testBlocks; b->id = no; b->base = 0; @@ -79,7 +82,7 @@ static void DestroyTestBlock(TestBlock b) } } - free(b); + mps_free(pool, b, sizeof(TestBlockStruct)); } typedef struct TestClosureStruct *TestClosure; @@ -147,9 +150,6 @@ static void step(void) } } - -#define testArenaSIZE (((size_t)4)<<20) - extern int main(int argc, char *argv[]) { mps_arena_t arena; @@ -159,9 +159,14 @@ extern int main(int argc, char *argv[]) abqSize = 0; - die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE), + die(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), "mps_arena_create"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_MFS_UNIT_SIZE, sizeof(TestBlockStruct)); + die(mps_pool_create_k(&pool, arena, mps_class_mfs(), args), "pool_create"); + } MPS_ARGS_END(args); + die(ABQInit((Arena)arena, &abq, NULL, ABQ_SIZE, sizeof(TestBlock)), "ABQInit"); diff --git a/mps/code/airtest.c b/mps/code/airtest.c index 31932bb4286..56e1bd8c129 100644 --- a/mps/code/airtest.c +++ b/mps/code/airtest.c @@ -35,7 +35,7 @@ #include "fmtscheme.h" #define OBJ_LEN (1u << 4) -#define OBJ_COUNT 1 +#define OBJ_COUNT 10 static void test_air(int interior, int stack) { @@ -90,20 +90,15 @@ static mps_gen_param_s obj_gen_params[] = { { 170, 0.45 } }; -static void test_main(int interior, int stack) +static void test_main(void *marker, int interior, int stack) { mps_res_t res; mps_chain_t obj_chain; mps_fmt_t obj_fmt; mps_thr_t thread; mps_root_t reg_root = NULL; - void *marker = ▮ - MPS_ARGS_BEGIN(args) { - MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, 1 << 20); - MPS_ARGS_DONE(args); - res = mps_arena_create_k(&scheme_arena, mps_arena_class_vm(), args); - } MPS_ARGS_END(args); + res = mps_arena_create_k(&scheme_arena, mps_arena_class_vm(), mps_args_none); if (res != MPS_RES_OK) error("Couldn't create arena"); res = mps_chain_create(&obj_chain, scheme_arena, @@ -149,12 +144,14 @@ static void test_main(int interior, int stack) int main(int argc, char *argv[]) { + void *marker = ▮ + testlib_init(argc, argv); - test_main(TRUE, TRUE); - test_main(TRUE, FALSE); - /* not test_main(FALSE, TRUE) -- see .fail.lii6ll. */ - test_main(FALSE, FALSE); + test_main(marker, TRUE, TRUE); + test_main(marker, TRUE, FALSE); + /* not test_main(marker, FALSE, TRUE) -- see .fail.lii6ll. */ + test_main(marker, FALSE, FALSE); printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); return 0; diff --git a/mps/code/amcss.c b/mps/code/amcss.c index 20f4dae3740..48892a45830 100644 --- a/mps/code/amcss.c +++ b/mps/code/amcss.c @@ -78,19 +78,6 @@ static void report(mps_arena_t arena) printf(" not_condemned %"PRIuLONGEST"\n", (ulongest_t)not_condemned); printf(" clock: %"PRIuLONGEST"\n", (ulongest_t)mps_message_clock(arena, message)); printf("}\n"); - - if(condemned > (gen1SIZE + gen2SIZE + (size_t)128) * 1024) { - /* When condemned size is larger than could happen in a gen 2 - * collection (discounting ramps, natch), guess that was a dynamic - * collection, and reset the commit limit, so it doesn't run out. - * - * GDR 2013-03-12: Fiddling with the commit limit was causing - * the test to fail sometimes (see job003440), so I've commented - * out this feature. - */ - /* die(mps_arena_commit_limit_set(arena, 2 * testArenaSIZE), "set limit"); */ - } - } else { cdie(0, "unknown message type"); break; @@ -98,8 +85,6 @@ static void report(mps_arena_t arena) mps_message_discard(arena, message); } - - return; } @@ -310,11 +295,7 @@ int main(int argc, char *argv[]) "arena_create"); mps_message_type_enable(arena, mps_message_type_gc()); mps_message_type_enable(arena, mps_message_type_gc_start()); - /* GDR 2013-03-12: Fiddling with the commit limit was causing - * the test to fail sometimes (see job003440), so I've commented - * out this feature. - */ - /*die(mps_arena_commit_limit_set(arena, testArenaSIZE), "set limit");*/ + die(mps_arena_commit_limit_set(arena, 2*testArenaSIZE), "set limit"); die(mps_thread_reg(&thread, arena), "thread_reg"); test(arena, mps_class_amc(), exactRootsCOUNT); test(arena, mps_class_amcz(), 0); diff --git a/mps/code/amcsshe.c b/mps/code/amcsshe.c index 11a5950f497..8b3a0c65e36 100644 --- a/mps/code/amcsshe.c +++ b/mps/code/amcsshe.c @@ -91,18 +91,6 @@ static void report(mps_arena_t arena) printf("not_condemned %"PRIuLONGEST"\n", (ulongest_t)not_condemned); mps_message_discard(arena, message); - - if (condemned > (gen1SIZE + gen2SIZE + (size_t)128) * 1024) { - /* When condemned size is larger than could happen in a gen 2 - * collection (discounting ramps, natch), guess that was a dynamic - * collection, and reset the commit limit, so it doesn't run out. - * - * GDR 2013-03-07: Fiddling with the commit limit was causing - * the test to fail sometimes (see job003432), so I've commented - * out this feature. - */ - /*die(mps_arena_commit_limit_set(arena, 2 * testArenaSIZE), "set limit");*/ - } } } @@ -257,14 +245,10 @@ int main(int argc, char *argv[]) testlib_init(argc, argv); - die(mps_arena_create(&arena, mps_arena_class_vm(), 3*testArenaSIZE), + die(mps_arena_create(&arena, mps_arena_class_vm(), 2*testArenaSIZE), "arena_create\n"); mps_message_type_enable(arena, mps_message_type_gc()); - /* GDR 2013-03-07: Fiddling with the commit limit was causing - * the test to fail sometimes (see job003432), so I've commented - * out this feature. - */ - /*die(mps_arena_commit_limit_set(arena, testArenaSIZE), "set limit");*/ + die(mps_arena_commit_limit_set(arena, 2*testArenaSIZE), "set limit"); die(mps_thread_reg(&thread, arena), "thread_reg"); test(arena, mps_class_amc(), exactRootsCOUNT); test(arena, mps_class_amcz(), 0); diff --git a/mps/code/amcssth.c b/mps/code/amcssth.c index 79eff570b0b..2f91edee93e 100644 --- a/mps/code/amcssth.c +++ b/mps/code/amcssth.c @@ -4,11 +4,20 @@ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (c) 2002 Global Graphics Software. * - * .posix: This is Posix only. + * .mode: This test case has two modes: + * + * .mode.walk: In this mode, the main thread parks the arena half way + * through the test case and runs mps_arena_formatted_objects_walk(). + * This checks that walking works while the other threads continue to + * allocate in the background. + * + * .mode.commit: In this mode, the arena's commit limit is set. This + * checks that the MPS can make progress inside a tight limit in the + * presence of allocation on multiple threads. But this is + * incompatible with .mode.walk: if the arena is parked, then the + * arena has no chance to make progress. */ -#define _POSIX_C_SOURCE 199309L - #include "fmtdy.h" #include "fmtdytst.h" #include "testlib.h" @@ -19,6 +28,11 @@ #include /* fflush, printf, putchar */ +enum { + ModeWALK = 0, /* .mode.walk */ + ModeCOMMIT = 1 /* .mode.commit */ +}; + /* These values have been tuned in the hope of getting one dynamic collection. */ #define testArenaSIZE ((size_t)1000*1024) @@ -65,21 +79,15 @@ static void report(mps_arena_t arena) printf("not_condemned %"PRIuLONGEST"\n", (ulongest_t)not_condemned); mps_message_discard(arena, message); - - if (condemned > (gen1SIZE + gen2SIZE + (size_t)128) * 1024) - /* When condemned size is larger than could happen in a gen 2 - * collection (discounting ramps, natch), guess that was a dynamic - * collection, and reset the commit limit, so it doesn't run out. */ - die(mps_arena_commit_limit_set(arena, 2 * testArenaSIZE), "set limit"); } } -mps_arena_t arena; -mps_fmt_t format; -mps_chain_t chain; -mps_root_t exactRoot, ambigRoot; -unsigned long objs = 0; +static mps_arena_t arena; +static mps_fmt_t format; +static mps_chain_t chain; +static mps_root_t exactRoot, ambigRoot; +static unsigned long objs = 0; /* make -- create one new object */ @@ -210,7 +218,7 @@ static void *kid_thread(void *arg) /* test -- the body of the test */ -static void *test(mps_class_t pool_class, size_t roots_count) +static void *test_pool(mps_class_t pool_class, size_t roots_count, int mode) { size_t i; mps_word_t collections, rampSwitch; @@ -221,6 +229,7 @@ static void *test(mps_class_t pool_class, size_t roots_count) mps_pool_t pool; testthr_t kids[10]; closure_s cl; + int walked = FALSE, ramped = FALSE; die(mps_pool_create(&pool, arena, pool_class, format, chain), "pool_create(amc)"); @@ -250,21 +259,23 @@ static void *test(mps_class_t pool_class, size_t roots_count) if (collections != c) { collections = c; - printf("\nCollection %lu started, %lu objects.\n", c, objs); + printf("\nCollection %lu started, %lu objects, committed=%lu.\n", + c, objs, (unsigned long)mps_arena_committed(arena)); report(arena); for (i = 0; i < exactRootsCOUNT; ++i) cdie(exactRoots[i] == objNULL || dylan_check(exactRoots[i]), "all roots check"); - if (collections == collectionsCOUNT / 2) { + if (mode == ModeWALK && collections >= collectionsCOUNT / 2 && !walked) { unsigned long object_count = 0; mps_arena_park(arena); mps_arena_formatted_objects_walk(arena, test_stepper, &object_count, 0); mps_arena_release(arena); printf("stepped on %lu objects.\n", object_count); + walked = TRUE; } - if (collections == rampSwitch) { + if (collections >= rampSwitch && !ramped) { int begin_ramp = !ramping || /* Every other time, switch back immediately. */ (collections & 1); @@ -289,6 +300,7 @@ static void *test(mps_class_t pool_class, size_t roots_count) ramping = 1; } } + ramped = TRUE; } churn(ap, roots_count); @@ -317,31 +329,37 @@ static void *test(mps_class_t pool_class, size_t roots_count) return NULL; } -int main(int argc, char *argv[]) +static void test_arena(int mode) { mps_thr_t thread; mps_root_t reg_root; void *marker = ▮ - testlib_init(argc, argv); - die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE), "arena_create"); + if (mode == ModeCOMMIT) + die(mps_arena_commit_limit_set(arena, 2 * testArenaSIZE), "set limit"); mps_message_type_enable(arena, mps_message_type_gc()); init(); die(mps_thread_reg(&thread, arena), "thread_reg"); die(mps_root_create_reg(®_root, arena, mps_rank_ambig(), 0, thread, - mps_stack_scan_ambig, marker, 0), "root_create"); + mps_stack_scan_ambig, marker, 0), "root_create"); - test(mps_class_amc(), exactRootsCOUNT); - test(mps_class_amcz(), 0); + test_pool(mps_class_amc(), exactRootsCOUNT, mode); + test_pool(mps_class_amcz(), 0, mode); mps_root_destroy(reg_root); mps_thread_dereg(thread); - finish(); report(arena); mps_arena_destroy(arena); +} + +int main(int argc, char *argv[]) +{ + testlib_init(argc, argv); + test_arena(ModeWALK); + test_arena(ModeCOMMIT); printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); return 0; diff --git a/mps/code/amsss.c b/mps/code/amsss.c index 1d04f199349..9128561ecf2 100644 --- a/mps/code/amsss.c +++ b/mps/code/amsss.c @@ -27,7 +27,7 @@ #define totalSizeSTEP 200 * (size_t)1024 /* objNULL needs to be odd so that it's ignored in exactRoots. */ #define objNULL ((mps_addr_t)MPS_WORD_CONST(0xDECEA5ED)) -#define testArenaSIZE ((size_t)16<<20) +#define testArenaSIZE ((size_t)1<<20) #define initTestFREQ 3000 #define splatTestFREQ 6000 static mps_gen_param_s testChain[1] = { { 160, 0.90 } }; @@ -107,7 +107,8 @@ static mps_addr_t make(void) static mps_pool_debug_option_s freecheckOptions = { NULL, 0, (const void *)"Dead", 4 }; -static void *test(void *arg, size_t haveAmbigous) +static void test_pool(mps_class_t pool_class, mps_arg_s args[], + mps_bool_t haveAmbiguous) { mps_pool_t pool; mps_root_t exactRoot, ambigRoot = NULL; @@ -116,14 +117,13 @@ static void *test(void *arg, size_t haveAmbigous) mps_ap_t busy_ap; mps_addr_t busy_init; - pool = (mps_pool_t)arg; - + die(mps_pool_create_k(&pool, arena, pool_class, args), "pool_create"); die(mps_ap_create(&ap, pool, mps_rank_exact()), "BufferCreate"); die(mps_ap_create(&busy_ap, pool, mps_rank_exact()), "BufferCreate 2"); for(i = 0; i < exactRootsCOUNT; ++i) exactRoots[i] = objNULL; - if (haveAmbigous) + if (haveAmbiguous) for(i = 0; i < ambigRootsCOUNT; ++i) ambigRoots[i] = rnd_addr(); @@ -132,7 +132,7 @@ static void *test(void *arg, size_t haveAmbigous) &exactRoots[0], exactRootsCOUNT, (mps_word_t)1), "root_create_table(exact)"); - if (haveAmbigous) + if (haveAmbiguous) die(mps_root_create_table(&ambigRoot, arena, mps_rank_ambig(), (mps_rm_t)0, &ambigRoots[0], ambigRootsCOUNT), @@ -154,7 +154,7 @@ static void *test(void *arg, size_t haveAmbigous) } r = (size_t)rnd(); - if (!haveAmbigous || (r & 1)) { + if (!haveAmbiguous || (r & 1)) { i = (r >> 1) % exactRootsCOUNT; if (exactRoots[i] != objNULL) cdie(dylan_check(exactRoots[i]), "dying root check"); @@ -187,80 +187,49 @@ static void *test(void *arg, size_t haveAmbigous) mps_ap_destroy(busy_ap); mps_ap_destroy(ap); mps_root_destroy(exactRoot); - if (haveAmbigous) + if (haveAmbiguous) mps_root_destroy(ambigRoot); - return NULL; + mps_pool_destroy(pool); } int main(int argc, char *argv[]) { + int i; mps_thr_t thread; mps_fmt_t format; mps_chain_t chain; - mps_pool_t pool; - void *r; testlib_init(argc, argv); die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE), "arena_create"); + die(mps_arena_commit_limit_set(arena, 2 * testArenaSIZE), "commit_limit_set"); + mps_message_type_enable(arena, mps_message_type_gc_start()); mps_message_type_enable(arena, mps_message_type_gc()); die(mps_thread_reg(&thread, arena), "thread_reg"); die(mps_fmt_create_A(&format, arena, dylan_fmt_A()), "fmt_create"); die(mps_chain_create(&chain, arena, 1, testChain), "chain_create"); - /* TODO: Add tests using the arena default chain. */ - - printf("\n\n****************************** Testing AMS Debug\n"); - MPS_ARGS_BEGIN(args) { - MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain); - MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format); - MPS_ARGS_ADD(args, MPS_KEY_AMS_SUPPORT_AMBIGUOUS, FALSE); - MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, &freecheckOptions); - die(mps_pool_create_k(&pool, arena, mps_class_ams_debug(), args), - "pool_create(ams_debug,share)"); - } MPS_ARGS_END(args); - mps_tramp(&r, test, pool, 0); - mps_pool_destroy(pool); - - printf("\n\n****************************** Testing AMS Debug\n"); - MPS_ARGS_BEGIN(args) { - MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain); - MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format); - MPS_ARGS_ADD(args, MPS_KEY_AMS_SUPPORT_AMBIGUOUS, TRUE); - MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, &freecheckOptions); - die(mps_pool_create_k(&pool, arena, mps_class_ams_debug(), args), - "pool_create(ams_debug,ambig)"); - } MPS_ARGS_END(args); - mps_tramp(&r, test, pool, 1); - mps_pool_destroy(pool); - - printf("\n\n****************************** Testing AMS\n"); - MPS_ARGS_BEGIN(args) { - MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain); - MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format); - MPS_ARGS_ADD(args, MPS_KEY_AMS_SUPPORT_AMBIGUOUS, TRUE); - MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, &freecheckOptions); - die(mps_pool_create_k(&pool, arena, mps_class_ams(), args), - "pool_create(ams,ambig)"); - } MPS_ARGS_END(args); - mps_tramp(&r, test, pool, 1); - mps_pool_destroy(pool); - - printf("\n\n****************************** Testing AMS\n"); - MPS_ARGS_BEGIN(args) { - MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain); - MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format); - MPS_ARGS_ADD(args, MPS_KEY_AMS_SUPPORT_AMBIGUOUS, FALSE); - MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, &freecheckOptions); - die(mps_pool_create_k(&pool, arena, mps_class_ams(), args), - "pool_create(ams,share)"); - } MPS_ARGS_END(args); - mps_tramp(&r, test, pool, 0); - mps_pool_destroy(pool); + for (i = 0; i < 8; i++) { + int debug = i % 2; + int ownChain = (i / 2) % 2; + int ambig = (i / 4) % 2; + printf("\n\n*** AMS%s with %sCHAIN and %sSUPPORT_AMBIGUOUS\n", + debug ? " Debug" : "", + ownChain ? "" : "!", + ambig ? "" : "!"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format); + if (ownChain) + MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain); + MPS_ARGS_ADD(args, MPS_KEY_AMS_SUPPORT_AMBIGUOUS, ambig); + MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, &freecheckOptions); + test_pool(debug ? mps_class_ams_debug() : mps_class_ams(), args, ambig); + } MPS_ARGS_END(args); + } mps_chain_destroy(chain); mps_fmt_destroy(format); diff --git a/mps/code/arenavm.c b/mps/code/arenavm.c index 1bcc6272f35..81e0684c436 100644 --- a/mps/code/arenavm.c +++ b/mps/code/arenavm.c @@ -480,7 +480,7 @@ ARG_DEFINE_KEY(arena_contracted, Fun); static Res VMArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args) { - Size userSize; /* size requested by user */ + Size userSize = VM_ARENA_SIZE_DEFAULT; /* size requested by user */ Size chunkSize; /* size actually created */ Size vmArenaSize; /* aligned size of VMArenaStruct */ Res res; @@ -495,8 +495,8 @@ static Res VMArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args) AVER(class == VMArenaClassGet()); AVERT(ArgList, args); - ArgRequire(&arg, args, MPS_KEY_ARENA_SIZE); - userSize = arg.val.size; + if (ArgPick(&arg, args, MPS_KEY_ARENA_SIZE)) + userSize = arg.val.size; AVER(userSize > 0); @@ -593,6 +593,8 @@ static void VMArenaFinish(Arena arena) AVERT(VMArena, vmArena); arenaVM = vmArena->vm; + EVENT1(ArenaDestroy, vmArena); + /* destroy all chunks, including the primary */ arena->primary = NULL; RING_FOR(node, &arena->chunkRing, next) { @@ -612,7 +614,6 @@ static void VMArenaFinish(Arena arena) VMUnmap(arenaVM, VMBase(arenaVM), VMLimit(arenaVM)); VMDestroy(arenaVM); - EVENT1(ArenaDestroy, vmArena); } diff --git a/mps/code/arg.h b/mps/code/arg.h index dd458efe7b7..bb184dddad9 100644 --- a/mps/code/arg.h +++ b/mps/code/arg.h @@ -1,7 +1,7 @@ /* arg.h: Keyword argument lists * * $Id$ - * Copyright (c) 2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2013-2014 Ravenbrook Limited. See end of file for license. * * .source: See . */ @@ -28,6 +28,7 @@ typedef struct mps_key_s { } KeyStruct; #define ARG_DEFINE_KEY(id, type) \ + extern const KeyStruct _mps_key_##id; \ const KeyStruct _mps_key_##id = {KeySig, #id, ArgCheck##type} #define argsNone mps_args_none @@ -62,7 +63,7 @@ extern Bool ArgCheckPool(Arg arg); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/awlut.c b/mps/code/awlut.c index c62153ed44c..464a2dba91f 100644 --- a/mps/code/awlut.c +++ b/mps/code/awlut.c @@ -275,9 +275,6 @@ static void *setup(void *v, size_t s) die(mps_fmt_create_A(&dylanweakfmt, arena, dylan_fmt_A_weak()), "Format Create (weak)\n"); MPS_ARGS_BEGIN(args) { - /* Ask the leafpool to allocate in the nursery, as we're using it to test - weaknesss and want things to die in it promptly. */ - MPS_ARGS_ADD(args, MPS_KEY_GEN, 0); MPS_ARGS_ADD(args, MPS_KEY_FORMAT, dylanfmt); die(mps_pool_create_k(&leafpool, arena, mps_class_lo(), args), "Leaf Pool Create\n"); diff --git a/mps/code/awluthe.c b/mps/code/awluthe.c index d3b2d572319..6ea468977f1 100644 --- a/mps/code/awluthe.c +++ b/mps/code/awluthe.c @@ -277,9 +277,6 @@ static void *setup(void *v, size_t s) die(EnsureHeaderFormat(&dylanfmt, arena), "EnsureHeaderFormat"); die(EnsureHeaderWeakFormat(&dylanweakfmt, arena), "EnsureHeaderWeakFormat"); MPS_ARGS_BEGIN(args) { - /* Ask the leafpool to allocate in the nursery, as we're using it to test - weaknesss and want things to die in it promptly. */ - MPS_ARGS_ADD(args, MPS_KEY_GEN, 0); MPS_ARGS_ADD(args, MPS_KEY_FORMAT, dylanfmt); die(mps_pool_create_k(&leafpool, arena, mps_class_lo(), args), "Leaf Pool Create\n"); diff --git a/mps/code/buffer.c b/mps/code/buffer.c index baa553a19a2..bbae1f3eacd 100644 --- a/mps/code/buffer.c +++ b/mps/code/buffer.c @@ -980,14 +980,14 @@ Bool BufferIsTrappedByMutator(Buffer buffer) * * Just represent the two patterns by two different pointers to dummies. */ -AllocPatternStruct AllocPatternRampStruct = {'\0'}; +static AllocPatternStruct AllocPatternRampStruct = {'\0'}; AllocPattern AllocPatternRamp(void) { return &AllocPatternRampStruct; } -AllocPatternStruct AllocPatternRampCollectAllStruct = {'\0'}; +static AllocPatternStruct AllocPatternRampCollectAllStruct = {'\0'}; AllocPattern AllocPatternRampCollectAll(void) { @@ -1075,7 +1075,7 @@ static Res bufferTrivInit(Buffer buffer, Pool pool, ArgList args) AVERT(Buffer, buffer); AVERT(Pool, pool); UNUSED(args); - EVENT3(BufferInit, buffer, pool, buffer->isMutator); + EVENT3(BufferInit, buffer, pool, BOOLOF(buffer->isMutator)); return ResOK; } @@ -1288,7 +1288,7 @@ static Res segBufInit(Buffer buffer, Pool pool, ArgList args) segbuf->rankSet = RankSetEMPTY; AVERT(SegBuf, segbuf); - EVENT3(BufferInitSeg, buffer, pool, buffer->isMutator); + EVENT3(BufferInitSeg, buffer, pool, BOOLOF(buffer->isMutator)); return ResOK; } @@ -1515,7 +1515,7 @@ static Res rankBufInit(Buffer buffer, Pool pool, ArgList args) BufferSetRankSet(buffer, RankSetSingle(rank)); /* There's nothing to check that the superclass doesn't, so no AVERT. */ - EVENT4(BufferInitRank, buffer, pool, buffer->isMutator, rank); + EVENT4(BufferInitRank, buffer, pool, BOOLOF(buffer->isMutator), rank); return ResOK; } diff --git a/mps/code/cbs.c b/mps/code/cbs.c index 176a15fef16..118d226a603 100644 --- a/mps/code/cbs.c +++ b/mps/code/cbs.c @@ -1077,8 +1077,7 @@ Res CBSDescribe(CBS cbs, mps_lib_FILE *stream) res = SplayTreeDescribe(cbsSplay(cbs), stream, &cbsSplayNodeDescribe); if (res != ResOK) return res; - res = METER_WRITE(cbs->treeSearch, stream); - if (res != ResOK) return res; + METER_WRITE(cbs->treeSearch, stream); res = WriteF(stream, "}\n", NULL); return res; diff --git a/mps/code/chain.h b/mps/code/chain.h index e47f8000c0b..ec5bc228ebf 100644 --- a/mps/code/chain.h +++ b/mps/code/chain.h @@ -81,7 +81,6 @@ extern Bool ChainCheck(Chain chain); extern double ChainDeferral(Chain chain); extern Res ChainCondemnAuto(double *mortalityReturn, Chain chain, Trace trace); -extern Res ChainCondemnAll(Chain chain, Trace trace); extern void ChainStartGC(Chain chain, Trace trace); extern void ChainEndGC(Chain chain, Trace trace); extern size_t ChainGens(Chain chain); diff --git a/mps/code/config.h b/mps/code/config.h index d304d57a87a..2bfb3ebca44 100644 --- a/mps/code/config.h +++ b/mps/code/config.h @@ -280,6 +280,16 @@ #define ATTRIBUTE_NO_SANITIZE_ADDRESS #endif +/* Attribute for functions that do not return. + * GCC: + * Clang: + */ +#if defined(MPS_BUILD_GC) || defined(MPS_BUILD_LL) +#define ATTRIBUTE_NORETURN __attribute__((__noreturn__)) +#else +#define ATTRIBUTE_NORETURN +#endif + /* EPVMDefaultSubsequentSegSIZE is a default for the alignment of * subsequent segments (non-initial at each save level) in EPVM. See @@ -405,6 +415,8 @@ pool to be very heavily used. */ #define CONTROL_EXTEND_BY 4096 +#define VM_ARENA_SIZE_DEFAULT ((Size)1 << 20) + /* Stack configuration */ diff --git a/mps/code/djbench.c b/mps/code/djbench.c index 59f05130cf2..c1f37f412b2 100644 --- a/mps/code/djbench.c +++ b/mps/code/djbench.c @@ -56,6 +56,7 @@ static mps_bool_t zoned = TRUE; /* arena allocates using zones */ \ for (k = 0; k < nblocks; ++k) { \ blocks[k].p = NULL; \ + blocks[k].s = 0; \ } \ \ for (j = 0; j < npass; ++j) { \ diff --git a/mps/code/event.c b/mps/code/event.c index 2e7468c3752..475fa4f875c 100644 --- a/mps/code/event.c +++ b/mps/code/event.c @@ -44,13 +44,13 @@ char EventBuffer[EventKindLIMIT][EventBufferSIZE]; char *EventLast[EventKindLIMIT]; /* Pointers to the last even written out of each buffer. */ -char *EventWritten[EventKindLIMIT]; +static char *EventWritten[EventKindLIMIT]; EventControlSet EventKindControl; /* Bit set used to control output. */ /* A single event structure output once per buffer flush. */ -EventEventClockSyncStruct eventClockSyncStruct; +static EventEventClockSyncStruct eventClockSyncStruct; /* eventClockSync -- Populate and write the clock sync event. */ @@ -422,7 +422,7 @@ void EventDump(mps_lib_FILE *stream) for (kind = 0; kind < EventKindLIMIT; ++kind) { for (event = (Event)EventLast[kind]; - event < (Event)(EventBuffer[kind] + EventBufferSIZE); + (char *)event < EventBuffer[kind] + EventBufferSIZE; event = (Event)((char *)event + event->any.size)) { /* Try to keep going even if there's an error, because this is used as a backtrace and we'll take what we can get. */ diff --git a/mps/code/eventcnv.c b/mps/code/eventcnv.c index 355b6efa2a2..7b7e060470f 100644 --- a/mps/code/eventcnv.c +++ b/mps/code/eventcnv.c @@ -197,6 +197,12 @@ static Res eventRead(Bool *eofOut, EventUnion *event, FILE *stream) return ResIO; } + if (event->any.size < sizeof(event->any)) + return ResFAIL; /* invalid size: too small */ + + if (event->any.size > sizeof(*event)) + return ResFAIL; /* invalid size: too large */ + /* Read the rest of the event. */ rest = event->any.size - sizeof(event->any); if (rest > 0) { diff --git a/mps/code/eventcom.h b/mps/code/eventcom.h index 6114b045d48..931c975596d 100644 --- a/mps/code/eventcom.h +++ b/mps/code/eventcom.h @@ -1,6 +1,6 @@ /* -- Event Logging Common Definitions * - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * $Id$ * * .sources: mps.design.telemetry @@ -56,7 +56,8 @@ ENUM_DECLARE(EventKind) enum EventDefinitionsEnum { EVENT_LIST(EVENT_ENUM, X) - EventEnumWarningSuppressor /* suppress comma-at-end-of-enum warning */ + /* suppress comma-at-end-of-enum warning */ + EventEnumWarningSuppressor = USHRT_MAX }; @@ -89,7 +90,11 @@ typedef Word EventFW; /* word */ typedef unsigned EventFU; /* unsigned integer */ typedef char EventFS[EventStringLengthMAX + sizeof('\0')]; /* string */ typedef double EventFD; /* double */ -typedef int EventFB; /* boolean */ +/* EventFB must be unsigned (even though Bool is a typedef for int) + * because it used as the type of a bitfield with width 1, and we need + * the legals values of the field to be 0 and 1 (not 0 and -1 which + * would be the case for int : 1). */ +typedef unsigned EventFB; /* Boolean */ /* Event packing bitfield specifiers */ #define EventFP_BITFIELD @@ -133,7 +138,7 @@ typedef union EventUnion { /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/eventsql.c b/mps/code/eventsql.c index 46bfa688a9b..e1c9ea22381 100644 --- a/mps/code/eventsql.c +++ b/mps/code/eventsql.c @@ -102,7 +102,7 @@ typedef sqlite3_int64 int64; * and for reporting errors. */ -unsigned int verbosity = 0; +static unsigned int verbosity = 0; #define LOG_ALWAYS 0 #define LOG_OFTEN 1 @@ -533,7 +533,7 @@ static void logFileCompleted(sqlite3 *db, /* An array of table-creation statement strings. */ -const char *createStatements[] = { +static const char *createStatements[] = { "CREATE TABLE IF NOT EXISTS event_kind (name TEXT," " description TEXT," " enum INTEGER PRIMARY KEY)", @@ -571,7 +571,7 @@ static void makeTables(sqlite3 *db) } } -const char *glueTables[] = { +static const char *glueTables[] = { "event_kind", "event_type", "event_param", diff --git a/mps/code/eventtxt.c b/mps/code/eventtxt.c index 5b7f7ab1312..4c50ac994f9 100644 --- a/mps/code/eventtxt.c +++ b/mps/code/eventtxt.c @@ -29,15 +29,20 @@ * $Id$ */ +#include "mps.h" +#include "mpsavm.h" +#include "mpscmvff.h" +#include "check.h" #include "config.h" #include "eventdef.h" #include "eventcom.h" #include "table.h" #include "testlib.h" /* for ulongest_t and associated print formats */ +#include #include -#include -#include +#include /* exit, EXIT_FAILURE, EXIT_SUCCESS */ +#include /* strcpy, strlen */ static const char *prog; /* program name */ static const char *logFileName = NULL; @@ -106,15 +111,19 @@ static void parseArgs(int argc, char *argv[]) static void *tableAlloc(void *closure, size_t size) { - UNUSED(closure); - return malloc(size); + mps_pool_t pool = closure; + mps_addr_t p; + mps_res_t res; + res = mps_alloc(&p, pool, size); + if (res != MPS_RES_OK) + everror("allocation failed: %d", res); + return p; } static void tableFree(void *closure, void *p, size_t size) { - UNUSED(closure); - UNUSED(size); - free(p); + mps_pool_t pool = closure; + mps_free(pool, p, size); } /* Printing routines */ @@ -171,7 +180,7 @@ static double parseDouble(char **pInOut) #define MAX_STRING_LENGTH 1024 -char strBuf[MAX_STRING_LENGTH]; +static char strBuf[MAX_STRING_LENGTH]; static char *parseString(char **pInOut) { @@ -215,21 +224,21 @@ static Table internTable; /* dictionary of intern ids to strings */ static Table labelTable; /* dictionary of addrs to intern ids */ -static void createTables(void) +static void createTables(mps_pool_t pool) { Res res; /* MPS intern IDs are serials from zero up, so we can use -1 * and -2 as specials. */ res = TableCreate(&internTable, (size_t)1<<4, - tableAlloc, tableFree, NULL, + tableAlloc, tableFree, pool, (Word)-1, (Word)-2); if (res != ResOK) everror("Couldn't make intern table."); /* We assume that 0 and 1 are invalid as Addrs. */ res = TableCreate(&labelTable, (size_t)1<<7, - tableAlloc, tableFree, NULL, + tableAlloc, tableFree, pool, 0, 1); if (res != ResOK) everror("Couldn't make label table."); @@ -238,19 +247,19 @@ static void createTables(void) /* recordIntern -- record an interned string in the table. a copy of * the string from the parsed buffer into a newly-allocated block. */ -static void recordIntern(char *p) +static void recordIntern(mps_pool_t pool, char *p) { ulongest_t stringId; char *string; - char *copy; + mps_addr_t copy; size_t len; Res res; stringId = parseHex(&p); string = parseString(&p); len = strlen(string); - copy = malloc(len+1); - if (copy == NULL) + res = mps_alloc(©, pool, len + 1); + if (res != MPS_RES_OK) everror("Couldn't allocate space for a string."); (void)strcpy(copy, string); res = TableDefine(internTable, (Word)stringId, (void *)copy); @@ -258,12 +267,55 @@ static void recordIntern(char *p) everror("Couldn't create an intern mapping."); } -/* recordLabel records a label (an association between an address and - * a string ID). Note that the event log may have been generated on a - * platform with addresses larger than Word on the current platform. - * If that happens then we are scuppered because our Table code uses - * Word as the key type: there's nothing we can do except detect this - * bad case (see also the EventInit handling and warning code). +/* Over time there may be multiple labels associated with an address, + * so we keep a list, recording for each label the clock when the + * association was made. This means that printAddr can select the + * label that was in force at the time of the event. + */ + +typedef struct LabelStruct *Label; +typedef struct LabelStruct { + ulongest_t clock; /* clock of this label */ + ulongest_t id; /* string id of this label */ +} LabelStruct; + +typedef struct LabelListStruct *LabelList; +typedef struct LabelListStruct { + size_t n; /* number of labels in array */ + Label labels; /* labels, sorted in order by clock */ +} LabelListStruct; + +/* labelFind returns the index of the first entry in list with a clock + * value that's greater than 'clock', or list->n if there is no such + * label. The list is assumed to be sorted. + */ + +static size_t labelFind(LabelList list, ulongest_t clock) +{ + size_t low = 0, high = list->n; + while (low < high) { + size_t mid = (low + high) / 2; + assert(NONNEGATIVE(mid) && mid < list->n); + if (list->labels[mid].clock > clock) { + high = mid; + } else { + low = mid + 1; + } + } + assert(NONNEGATIVE(low) && low <= list->n); + assert(low == list->n || list->labels[low].clock > clock); + return low; +} + +/* recordLabel records a label: an association (made at the time given + * by 'clock') between an address and a string ID. These are encoded + * as two hexadecimal numbers in the string pointed to by 'p'. + * + * Note that the event log may have been generated on a platform with + * addresses larger than Word on the current platform. If that happens + * then we are scuppered because our Table code uses Word as the key + * type: there's nothing we can do except detect this bad case (see + * also the EventInit handling and warning code). * * We can and do handle the case where string IDs (which are Words on * the MPS platform) are larger than void* on the current platform. @@ -274,25 +326,50 @@ static void recordIntern(char *p) * probably a bad idea and maybe doomed to failure. */ -static void recordLabel(char *p) +static void recordLabel(mps_pool_t pool, ulongest_t clock, char *p) { ulongest_t address; - ulongest_t *stringIdP; + LabelList list; + Label newlabels; + mps_addr_t tmp; + size_t pos; Res res; - + address = parseHex(&p); if (address > (Word)-1) { (void)printf("label address too large!"); return; } - - stringIdP = malloc(sizeof(ulongest_t)); - if (stringIdP == NULL) - everror("Can't allocate space for a string's ID"); - *stringIdP = parseHex(&p); - res = TableDefine(labelTable, (Word)address, (void *)stringIdP); + + if (TableLookup(&tmp, labelTable, address)) { + list = tmp; + } else { + /* First label for this address */ + res = mps_alloc(&tmp, pool, sizeof(LabelListStruct)); + if (res != MPS_RES_OK) + everror("Can't allocate space for a label list"); + list = tmp; + list->n = 0; + res = TableDefine(labelTable, (Word)address, list); + if (res != ResOK) + everror("Couldn't create a label mapping."); + } + + res = mps_alloc(&tmp, pool, sizeof(LabelStruct) * (list->n + 1)); if (res != ResOK) - everror("Couldn't create an intern mapping."); + everror("Couldn't allocate space for list of labels."); + newlabels = tmp; + + pos = labelFind(list, clock); + memcpy(newlabels, list->labels, sizeof(LabelStruct) * pos); + newlabels[pos].clock = clock; + newlabels[pos].id = parseHex(&p); + memcpy(newlabels + pos + 1, list->labels + pos, + sizeof(LabelStruct) * (list->n - pos)); + if (list->n > 0) + mps_free(pool, list->labels, sizeof(LabelStruct) * list->n); + list->labels = newlabels; + ++ list->n; } /* output code */ @@ -308,20 +385,23 @@ static int hexWordWidth = (MPS_WORD_WIDTH+3)/4; /* printAddr -- output a ulongest_t in hex, with the interned string * if the value is in the label table */ -static void printAddr(ulongest_t addr, const char *ident) +static void printAddr(ulongest_t clock, ulongest_t addr, const char *ident) { - ulongest_t label; - void *alias; + void *tmp; printf("%s:%0*" PRIXLONGEST, ident, hexWordWidth, addr); - if (TableLookup(&alias, labelTable, addr)) { - label = *(ulongest_t*)alias; - putchar('['); - if (TableLookup(&alias, internTable, label)) - printStr((char *)alias); - else - printf("unknown label %" PRIuLONGEST, label); - putchar(']'); + if (TableLookup(&tmp, labelTable, addr)) { + LabelList list = tmp; + size_t pos = labelFind(list, clock); + if (pos > 0) { + ulongest_t id = list->labels[pos - 1].id; + putchar('['); + if (TableLookup(&tmp, internTable, id)) + printStr((char *)tmp); + else + printf("unknown label %" PRIXLONGEST, id); + putchar(']'); + } } putchar(' '); } @@ -332,7 +412,7 @@ static void printAddr(ulongest_t addr, const char *ident) #define processParamA(ident) \ val_hex = parseHex(&p); \ - printAddr(val_hex, #ident); + printAddr(clock, val_hex, #ident); #define processParamP processParamA #define processParamW processParamA @@ -375,7 +455,7 @@ static const char *eventName[EventCodeMAX+EventCodeMAX]; /* readLog -- read and parse log. Returns the number of events written. */ -static void readLog(FILE *input) +static void readLog(mps_pool_t pool, FILE *input) { int i; @@ -415,9 +495,9 @@ static void readLog(FILE *input) /* for a few particular codes, we do local processing. */ if (code == EventInternCode) { - recordIntern(q); + recordIntern(pool, q); } else if (code == EventLabelCode) { - recordLabel(q); + recordLabel(pool, clock, q); } else if (code == EventEventInitCode) { ulongest_t major, median, minor, maxCode, maxNameLen, wordWidth, clocksPerSec; major = parseHex(&q); /* EVENT_VERSION_MAJOR */ @@ -476,6 +556,9 @@ static void readLog(FILE *input) int main(int argc, char *argv[]) { + mps_arena_t arena; + mps_pool_t pool; + mps_res_t res; FILE *input; parseArgs(argc, argv); @@ -488,8 +571,20 @@ int main(int argc, char *argv[]) everror("unable to open %s", logFileName); } - createTables(); - readLog(input); + res = mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none); + if (res != MPS_RES_OK) + everror("failed to create arena: %d", res); + + res = mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none); + if (res != MPS_RES_OK) + everror("failed to create pool: %d", res); + + createTables(pool); + readLog(pool, input); + + mps_pool_destroy(pool); + mps_arena_destroy(arena); + (void)fclose(input); return 0; } diff --git a/mps/code/exposet0.c b/mps/code/exposet0.c index f471dd2a3bc..21f2567cbaa 100644 --- a/mps/code/exposet0.c +++ b/mps/code/exposet0.c @@ -72,12 +72,6 @@ static void report(mps_arena_t arena) printf("not_condemned %"PRIuLONGEST"\n", (ulongest_t)not_condemned); mps_message_discard(arena, message); - - if (condemned > (gen1SIZE + gen2SIZE + (size_t)128) * 1024) - /* When condemned size is larger than could happen in a gen 2 - * collection (discounting ramps, natch), guess that was a dynamic - * collection, and reset the commit limit, so it doesn't run out. */ - die(mps_arena_commit_limit_set(arena, 2 * testArenaSIZE), "set limit"); } } diff --git a/mps/code/fbmtest.c b/mps/code/fbmtest.c index 9f3754b3496..231d02482a7 100644 --- a/mps/code/fbmtest.c +++ b/mps/code/fbmtest.c @@ -560,7 +560,7 @@ extern int main(int argc, char *argv[]) Align align; testlib_init(argc, argv); - align = (1 << rnd() % 4) * MPS_PF_ALIGN; + align = sizeof(void *) << (rnd() % 4); NAllocateTried = NAllocateSucceeded = NDeallocateTried = NDeallocateSucceeded = 0; diff --git a/mps/code/finaltest.c b/mps/code/finaltest.c index 02c1448d137..f449d7e1acf 100644 --- a/mps/code/finaltest.c +++ b/mps/code/finaltest.c @@ -6,6 +6,20 @@ * * DESIGN * + * .mode: This test has two modes. + * + * .mode.park: In this mode, we use the arena's default generation + * chain, leave the arena parked and call mps_arena_collect. This + * tests that the default generation chain works and that all segments + * get condemned via TraceStartCollectAll. (See job003771 item 4.) + * + * .mode.poll: In this mode, we use our own generation chain (with + * small generations), allocate into generation 1, unclamp the arena, + * and provoke collection by allocating. This tests that custom + * generation chains work, and that segments get condemned via + * TracePoll even if there is no allocation into generation 0 of the + * chain. (See job003771 item 5.) + * * DEPENDENCIES * * This test uses the dylan object format, but the reliance on this @@ -16,6 +30,7 @@ * This code was created by first copying */ +#include "mpm.h" #include "testlib.h" #include "mpslib.h" #include "mps.h" @@ -30,10 +45,15 @@ #include /* fflush, printf, stdout */ +enum { + ModePARK, /* .mode.park */ + ModePOLL /* .mode.poll */ +}; + #define testArenaSIZE ((size_t)16<<20) #define rootCOUNT 20 -#define maxtreeDEPTH 10 +#define maxtreeDEPTH 9 #define collectionCOUNT 10 @@ -126,17 +146,21 @@ static mps_addr_t test_awl_find_dependent(mps_addr_t addr) static void *root[rootCOUNT]; -static void test_trees(const char *name, mps_arena_t arena, mps_ap_t ap, +static void test_trees(int mode, const char *name, mps_arena_t arena, + mps_ap_t ap, mps_word_t (*make)(mps_word_t, mps_ap_t), void (*reg)(mps_word_t, mps_arena_t)) { size_t collections = 0; size_t finals = 0; size_t i; + int object_alloc; object_count = 0; printf("Making some %s finalized trees of objects.\n", name); + mps_arena_park(arena); + /* make some trees */ for(i = 0; i < rootCOUNT; ++i) { root[i] = (void *)(*make)(maxtreeDEPTH, ap); @@ -151,10 +175,23 @@ static void test_trees(const char *name, mps_arena_t arena, mps_ap_t ap, while (finals < object_count && collections < collectionCOUNT) { mps_word_t final_this_time = 0; - printf("Collecting..."); - (void)fflush(stdout); - die(mps_arena_collect(arena), "collect"); - printf(" Done.\n"); + switch (mode) { + default: + case ModePARK: + printf("Collecting..."); + (void)fflush(stdout); + die(mps_arena_collect(arena), "collect"); + printf(" Done.\n"); + break; + case ModePOLL: + mps_arena_release(arena); + printf("Allocating..."); + (void)fflush(stdout); + object_alloc = 0; + while (object_alloc < 1000 && !mps_message_poll(arena)) + (void)DYLAN_INT(object_alloc++); + break; + } ++ collections; while (mps_message_poll(arena)) { mps_message_t message; @@ -167,12 +204,17 @@ static void test_trees(const char *name, mps_arena_t arena, mps_ap_t ap, } finals += final_this_time; printf("%"PRIuLONGEST" objects finalized: total %"PRIuLONGEST - " of %"PRIuLONGEST"\n", final_this_time, finals, object_count); + " of %"PRIuLONGEST"\n", (ulongest_t)final_this_time, + (ulongest_t)finals, (ulongest_t)object_count); } - cdie(finals == object_count, "Not all objects were finalized."); + if (finals != object_count) + error("Not all objects were finalized for %s in mode %s.", + BufferOfAP(ap)->pool->class->name, + mode == ModePOLL ? "POLL" : "PARK"); } -static void *test(mps_arena_t arena, mps_class_t pool_class) +static void test_pool(int mode, mps_arena_t arena, mps_chain_t chain, + mps_class_t pool_class) { mps_ap_t ap; mps_fmt_t fmt; @@ -181,10 +223,13 @@ static void *test(mps_arena_t arena, mps_class_t pool_class) die(mps_fmt_create_A(&fmt, arena, dylan_fmt_A()), "fmt_create\n"); MPS_ARGS_BEGIN(args) { - /* Allocate into generation 0 so that they get finalized quickly. */ - MPS_ARGS_ADD(args, MPS_KEY_GEN, 0); MPS_ARGS_ADD(args, MPS_KEY_FORMAT, fmt); - MPS_ARGS_ADD(args, MPS_KEY_AWL_FIND_DEPENDENT, test_awl_find_dependent); + if (mode == ModePOLL) { + MPS_ARGS_ADD(args, MPS_KEY_CHAIN, chain); + MPS_ARGS_ADD(args, MPS_KEY_GEN, 1); + } + if (pool_class == mps_class_awl()) + MPS_ARGS_ADD(args, MPS_KEY_AWL_FIND_DEPENDENT, test_awl_find_dependent); die(mps_pool_create_k(&pool, arena, pool_class, args), "pool_create\n"); } MPS_ARGS_END(args); @@ -193,19 +238,25 @@ static void *test(mps_arena_t arena, mps_class_t pool_class) "root_create\n"); die(mps_ap_create(&ap, pool, mps_rank_exact()), "ap_create\n"); - mps_message_type_enable(arena, mps_message_type_finalization()); - - mps_arena_park(arena); - - test_trees("numbered", arena, ap, make_numbered_tree, register_numbered_tree); - test_trees("indirect", arena, ap, make_indirect_tree, register_indirect_tree); + test_trees(mode, "numbered", arena, ap, make_numbered_tree, + register_numbered_tree); + test_trees(mode, "indirect", arena, ap, make_indirect_tree, + register_indirect_tree); mps_ap_destroy(ap); mps_root_destroy(mps_root); mps_pool_destroy(pool); mps_fmt_destroy(fmt); +} - return NULL; + +static void test_mode(int mode, mps_arena_t arena, mps_chain_t chain) +{ + test_pool(mode, arena, chain, mps_class_amc()); + test_pool(mode, arena, chain, mps_class_amcz()); + test_pool(mode, arena, chain, mps_class_ams()); + /* test_pool(mode, arena, chain, mps_class_lo()); TODO: job003773 */ + /* test_pool(mode, arena, chain, mps_class_awl()); TODO: job003772 */ } @@ -213,19 +264,27 @@ int main(int argc, char *argv[]) { mps_arena_t arena; mps_thr_t thread; + mps_chain_t chain; + mps_gen_param_s params[2]; + size_t gens = 2; + size_t i; testlib_init(argc, argv); die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE), "arena_create\n"); + mps_message_type_enable(arena, mps_message_type_finalization()); die(mps_thread_reg(&thread, arena), "thread_reg\n"); + for (i = 0; i < gens; ++i) { + params[i].mps_capacity = 1; + params[i].mps_mortality = 0.5; + } + die(mps_chain_create(&chain, arena, gens, params), "chain_create\n"); - test(arena, mps_class_amc()); - test(arena, mps_class_amcz()); - test(arena, mps_class_ams()); - test(arena, mps_class_awl()); - /* TODO: test(arena, mps_class_lo()); */ + test_mode(ModePOLL, arena, chain); + test_mode(ModePARK, arena, NULL); + mps_chain_destroy(chain); mps_thread_dereg(thread); mps_arena_destroy(arena); diff --git a/mps/code/fmtscheme.c b/mps/code/fmtscheme.c index 5299c8f3525..11900130a71 100644 --- a/mps/code/fmtscheme.c +++ b/mps/code/fmtscheme.c @@ -12,14 +12,8 @@ /* special objects */ -obj_t obj_empty; /* (), the empty list */ -obj_t obj_eof; /* end of file */ -obj_t obj_error; /* error indicator */ -obj_t obj_true; /* #t, boolean true */ -obj_t obj_false; /* #f, boolean false */ -obj_t obj_undefined; /* undefined result indicator */ -obj_t obj_tail; /* tail recursion indicator */ -obj_t obj_deleted; /* deleted key in hashtable */ +static obj_t obj_true; /* #t, boolean true */ +static obj_t obj_false; /* #f, boolean false */ /* MPS globals */ diff --git a/mps/code/fotest.c b/mps/code/fotest.c index 9dab6650ede..2e63d4e121b 100644 --- a/mps/code/fotest.c +++ b/mps/code/fotest.c @@ -171,7 +171,7 @@ int main(int argc, char *argv[]) die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE), "mps_arena_create"); - alignment = (1 << (rnd() % 4)) * MPS_PF_ALIGN; + alignment = sizeof(void *) << (rnd() % 4); MPS_ARGS_BEGIN(args) { MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, (64 + rnd() % 64) * 1024); MPS_ARGS_ADD(args, MPS_KEY_MEAN_SIZE, (1 + rnd() % 8) * 8); @@ -190,7 +190,7 @@ int main(int argc, char *argv[]) die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE), "mps_arena_create"); - alignment = (1 << (rnd() % 4)) * MPS_PF_ALIGN; + alignment = sizeof(void *) << (rnd() % 4); MPS_ARGS_BEGIN(args) { MPS_ARGS_ADD(args, MPS_KEY_ALIGN, alignment); MPS_ARGS_ADD(args, MPS_KEY_MIN_SIZE, (1 + rnd() % 4) * 4); diff --git a/mps/code/gcbench.c b/mps/code/gcbench.c index 80c68d7f711..2a18d1a7d10 100644 --- a/mps/code/gcbench.c +++ b/mps/code/gcbench.c @@ -318,8 +318,8 @@ int main(int argc, char *argv[]) { double mort = 0.0; cap = (size_t)strtoul(optarg, &p, 10); switch(toupper(*p)) { - case 'G': cap *= 1024; /* fall through */ - case 'M': cap *= 1024; /* fall through */ + case 'G': cap <<= 20; p++; break; + case 'M': cap <<= 10; p++; break; case 'K': p++; break; default: cap = 0; break; } @@ -340,9 +340,9 @@ int main(int argc, char *argv[]) { char *p; arenasize = (unsigned)strtoul(optarg, &p, 10); switch(toupper(*p)) { - case 'G': arenasize *= 1024; - case 'M': arenasize *= 1024; - case 'K': arenasize *= 1024; break; + case 'G': arenasize <<= 30; break; + case 'M': arenasize <<= 20; break; + case 'K': arenasize <<= 10; break; case '\0': break; default: fprintf(stderr, "Bad arena size %s\n", optarg); diff --git a/mps/code/global.c b/mps/code/global.c index 24cd0a81013..812f90dad3d 100644 --- a/mps/code/global.c +++ b/mps/code/global.c @@ -393,25 +393,7 @@ void GlobalsFinish(Globals arenaGlobals) Arena arena; Rank rank; - /* Check that the tear-down is complete: that the client has - * destroyed all data structures associated with the arena. We do - * this *before* calling AVERT(Globals, arenaGlobals) because the - * AVERT will crash if there are any remaining data structures, and - * it is politer to assert than to crash. (The crash would happen - * because by this point in the code the control pool has been - * destroyed and so the address space containing all these rings has - * potentially been unmapped, and so RingCheck dereferences a - * pointer into that unmapped memory.) See job000652. */ arena = GlobalsArena(arenaGlobals); - AVER(RingIsSingle(&arena->formatRing)); - AVER(RingIsSingle(&arena->chainRing)); - AVER(RingIsSingle(&arena->messageRing)); - AVER(RingIsSingle(&arena->threadRing)); - for(rank = 0; rank < RankLIMIT; ++rank) - AVER(RingIsSingle(&arena->greyRing[rank])); - AVER(RingIsSingle(&arenaGlobals->poolRing)); - AVER(RingIsSingle(&arenaGlobals->rootRing)); - AVERT(Globals, arenaGlobals); STATISTIC_STAT(EVENT2(ArenaWriteFaults, arena, @@ -441,6 +423,7 @@ void GlobalsPrepareToDestroy(Globals arenaGlobals) TraceId ti; Trace trace; Chain defaultChain; + Rank rank; AVERT(Globals, arenaGlobals); @@ -495,6 +478,31 @@ void GlobalsPrepareToDestroy(Globals arenaGlobals) arena->finalPool = NULL; PoolDestroy(pool); } + + /* Check that the tear-down is complete: that the client has + * destroyed all data structures associated with the arena. We do + * this here rather than in GlobalsFinish because by the time that + * is called, the control pool has been destroyed and so the address + * space containing all these rings has potentially been unmapped, + * and so RingCheck dereferences a pointer into that unmapped memory + * and we get a crash instead of an assertion. See job000652. + */ + AVER(RingIsSingle(&arena->formatRing)); + AVER(RingIsSingle(&arena->chainRing)); + AVER(RingIsSingle(&arena->messageRing)); + AVER(RingIsSingle(&arena->threadRing)); + AVER(RingIsSingle(&arenaGlobals->rootRing)); + for(rank = 0; rank < RankLIMIT; ++rank) + AVER(RingIsSingle(&arena->greyRing[rank])); + + /* At this point the following pools still exist: + * 0. arena->freeCBSBlockPoolStruct + * 1. arena->reservoirStruct + * 2. arena->controlPoolStruct + * 3. arena->controlPoolStruct.blockPoolStruct + * 4. arena->controlPoolStruct.spanPoolStruct + */ + AVER(RingLength(&arenaGlobals->poolRing) == 5); } @@ -595,6 +603,7 @@ void ArenaLeaveRecursive(Arena arena) * version. The format is platform-specific. We won't necessarily * publish this. */ +extern MutatorFaultContext mps_exception_info; MutatorFaultContext mps_exception_info = NULL; @@ -1101,7 +1110,7 @@ void ArenaSetEmergency(Arena arena, Bool emergency) AVERT(Arena, arena); AVERT(Bool, emergency); - EVENT2(ArenaSetEmergency, arena, emergency); + EVENT2(ArenaSetEmergency, arena, BOOLOF(emergency)); arena->emergency = emergency; } diff --git a/mps/code/ll.gmk b/mps/code/ll.gmk index dc2595c511f..55e4b6cff5d 100644 --- a/mps/code/ll.gmk +++ b/mps/code/ll.gmk @@ -10,17 +10,20 @@ # common makefile fragment () requires. CC = clang -CFLAGSDEBUG = -O -g3 +CFLAGSDEBUG = -O0 -g3 CFLAGSOPT = -O2 -g3 CFLAGSCOMPILER := \ -pedantic \ -Waggregate-return \ -Wall \ -Wcast-qual \ + -Wconversion \ + -Wduplicate-enum \ -Werror \ -Wextra \ -Winline \ -Wmissing-prototypes \ + -Wmissing-variable-declarations \ -Wnested-externs \ -Wno-extended-offsetof \ -Wpointer-arith \ diff --git a/mps/code/lockcov.c b/mps/code/lockcov.c index a7289dca9ce..75ded6f202a 100644 --- a/mps/code/lockcov.c +++ b/mps/code/lockcov.c @@ -4,21 +4,37 @@ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ +#include "mps.h" +#include "mpsavm.h" +#include "mpscmfs.h" #include "mpm.h" #include "testlib.h" #include "mpslib.h" #include /* printf */ -#include /* free, malloc */ int main(int argc, char *argv[]) { - Lock a = malloc(LockSize()); - Lock b = malloc(LockSize()); + mps_arena_t arena; + mps_pool_t pool; + mps_addr_t p; + Lock a, b; testlib_init(argc, argv); + die(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), + "arena_create"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_MFS_UNIT_SIZE, LockSize()); + die(mps_pool_create_k(&pool, arena, mps_class_mfs(), args), "pool_create"); + } MPS_ARGS_END(args); + + die(mps_alloc(&p, pool, LockSize()), "alloc a"); + a = p; + die(mps_alloc(&p, pool, LockSize()), "alloc b"); + b = p; + Insist(a != NULL); Insist(b != NULL); @@ -46,8 +62,11 @@ int main(int argc, char *argv[]) LockReleaseMPM(a); LockFinish(a); LockReleaseGlobalRecursive(); - free(a); - free(b); + + mps_free(pool, a, LockSize()); + mps_free(pool, b, LockSize()); + mps_pool_destroy(pool); + mps_arena_destroy(arena); printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); return 0; diff --git a/mps/code/lockut.c b/mps/code/lockut.c index ec22369bc13..e93bdea6815 100644 --- a/mps/code/lockut.c +++ b/mps/code/lockut.c @@ -4,18 +4,20 @@ * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ +#include "mps.h" +#include "mpsavm.h" +#include "mpscmfs.h" #include "mpm.h" #include "testlib.h" #include "testthr.h" #include /* printf */ -#include /* malloc */ #define nTHREADS 4 static Lock lock; -unsigned long shared, tmp; +static unsigned long shared, tmp; static void incR(unsigned long i) @@ -63,12 +65,23 @@ static void *thread0(void *p) int main(int argc, char *argv[]) { + mps_arena_t arena; + mps_pool_t pool; + mps_addr_t p; testthr_t t[10]; unsigned i; testlib_init(argc, argv); - lock = malloc(LockSize()); + die(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), + "arena_create"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_MFS_UNIT_SIZE, LockSize()); + die(mps_pool_create_k(&pool, arena, mps_class_mfs(), args), "pool_create"); + } MPS_ARGS_END(args); + + die(mps_alloc(&p, pool, LockSize()), "alloc"); + lock = p; Insist(lock != NULL); LockInit(lock); @@ -86,6 +99,10 @@ int main(int argc, char *argv[]) LockFinish(lock); + mps_free(pool, lock, LockSize()); + mps_pool_destroy(pool); + mps_arena_destroy(arena); + printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); return 0; } diff --git a/mps/code/locus.c b/mps/code/locus.c index a704c8b820b..6e6e22128b7 100644 --- a/mps/code/locus.c +++ b/mps/code/locus.c @@ -287,13 +287,21 @@ Res ChainAlloc(Seg *segReturn, Chain chain, Serial genNr, SegClass class, double ChainDeferral(Chain chain) { + double time = DBL_MAX; + size_t i; + AVERT(Chain, chain); - if (chain->activeTraces != TraceSetEMPTY) - return DBL_MAX; - else - return chain->gens[0].capacity * 1024.0 - - (double)GenDescNewSize(&chain->gens[0]); + if (chain->activeTraces == TraceSetEMPTY) { + for (i = 0; i < chain->genCount; ++i) { + double genTime = chain->gens[i].capacity * 1024.0 + - (double)GenDescNewSize(&chain->gens[i]); + if (genTime < time) + time = genTime; + } + } + + return time; } @@ -306,7 +314,7 @@ double ChainDeferral(Chain chain) Res ChainCondemnAuto(double *mortalityReturn, Chain chain, Trace trace) { Res res; - Serial topCondemnedGenSerial, currGenSerial; + size_t topCondemnedGen, i; GenDesc gen; ZoneSet condemnedSet = ZoneSetEMPTY; Size condemnedSize = 0, survivorSize = 0, genNewSize, genTotalSize; @@ -314,33 +322,39 @@ Res ChainCondemnAuto(double *mortalityReturn, Chain chain, Trace trace) AVERT(Chain, chain); AVERT(Trace, trace); - /* Find lowest gen within its capacity, set topCondemnedGenSerial to the */ - /* preceeding one. */ - currGenSerial = 0; - gen = &chain->gens[0]; - AVERT(GenDesc, gen); - genNewSize = GenDescNewSize(gen); - do { /* At this point, we've decided to collect currGenSerial. */ - topCondemnedGenSerial = currGenSerial; + /* Find the highest generation that's over capacity. We will condemn + * this and all lower generations in the chain. */ + topCondemnedGen = chain->genCount; + for (;;) { + /* It's an error to call this function unless some generation is + * over capacity as reported by ChainDeferral. */ + AVER(topCondemnedGen > 0); + if (topCondemnedGen == 0) + return ResFAIL; + -- topCondemnedGen; + gen = &chain->gens[topCondemnedGen]; + AVERT(GenDesc, gen); + genNewSize = GenDescNewSize(gen); + if (genNewSize >= gen->capacity * (Size)1024) + break; + } + + /* At this point, we've decided to condemn topCondemnedGen and all + * lower generations. */ + for (i = 0; i <= topCondemnedGen; ++i) { + gen = &chain->gens[i]; + AVERT(GenDesc, gen); condemnedSet = ZoneSetUnion(condemnedSet, gen->zones); genTotalSize = GenDescTotalSize(gen); + genNewSize = GenDescNewSize(gen); condemnedSize += genTotalSize; survivorSize += (Size)(genNewSize * (1.0 - gen->mortality)) /* predict survivors will survive again */ + (genTotalSize - genNewSize); - - /* is there another one to consider? */ - currGenSerial += 1; - if (currGenSerial >= chain->genCount) - break; /* reached the top */ - gen = &chain->gens[currGenSerial]; - AVERT(GenDesc, gen); - genNewSize = GenDescNewSize(gen); - } while (genNewSize >= gen->capacity * (Size)1024); + } AVER(condemnedSet != ZoneSetEMPTY || condemnedSize == 0); - EVENT3(ChainCondemnAuto, chain, topCondemnedGenSerial, chain->genCount); - UNUSED(topCondemnedGenSerial); /* only used for EVENT */ + EVENT3(ChainCondemnAuto, chain, topCondemnedGen, chain->genCount); /* Condemn everything in these zones. */ if (condemnedSet != ZoneSetEMPTY) { @@ -354,41 +368,6 @@ Res ChainCondemnAuto(double *mortalityReturn, Chain chain, Trace trace) } -/* ChainCondemnAll -- condemn everything in the chain */ - -Res ChainCondemnAll(Chain chain, Trace trace) -{ - Ring node, nextNode; - Bool haveWhiteSegs = FALSE; - Res res; - - /* Condemn every segment in every pool using this chain. */ - /* Finds the pools by iterating over the PoolGens in gen 0. */ - RING_FOR(node, &chain->gens[0].locusRing, nextNode) { - PoolGen nursery = RING_ELT(PoolGen, genRing, node); - Pool pool = nursery->pool; - Ring segNode, nextSegNode; - - AVERT(Pool, pool); - AVER(PoolHasAttr(pool, AttrGC)); - RING_FOR(segNode, PoolSegRing(pool), nextSegNode) { - Seg seg = SegOfPoolRing(segNode); - - res = TraceAddWhite(trace, seg); - if (res != ResOK) - goto failBegin; - haveWhiteSegs = TRUE; - } - } - - return ResOK; - -failBegin: - AVER(!haveWhiteSegs); /* Would leave white sets inconsistent. */ - return res; -} - - /* ChainStartGC -- called to notify start of GC for this chain */ void ChainStartGC(Chain chain, Trace trace) @@ -416,9 +395,11 @@ void ChainEndGC(Chain chain, Trace trace) Res PoolGenInit(PoolGen gen, Chain chain, Serial nr, Pool pool) { /* Can't check gen, because it's not been initialized. */ + AVER(gen != NULL); AVERT(Chain, chain); AVER(nr <= chain->genCount); AVERT(Pool, pool); + AVER(PoolHasAttr(pool, AttrGC)); gen->nr = nr; gen->pool = pool; diff --git a/mps/code/meter.h b/mps/code/meter.h index 7a7f8266e87..f1731400e42 100644 --- a/mps/code/meter.h +++ b/mps/code/meter.h @@ -1,7 +1,7 @@ /* meter.h: METER INTERFACE * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .sources: mps.design.metrics. * @@ -45,9 +45,12 @@ extern void MeterEmit(Meter meter); #define METER_ACC(meter, delta) \ STATISTIC(MeterAccumulate(&(meter), delta)) #if defined(STATISTICS) -#define METER_WRITE(meter, stream) MeterWrite(&(meter), stream) +#define METER_WRITE(meter, stream) BEGIN \ + Res _res = MeterWrite(&(meter), (stream)); \ + if (_res != ResOK) return _res; \ + END #elif defined(STATISTICS_NONE) -#define METER_WRITE(meter, stream) (ResOK) +#define METER_WRITE(meter, stream) NOOP #else #error "No statistics configured." #endif @@ -59,7 +62,7 @@ extern void MeterEmit(Meter meter); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/misc.h b/mps/code/misc.h index 6ba4be5f49d..fed416157dd 100644 --- a/mps/code/misc.h +++ b/mps/code/misc.h @@ -1,7 +1,7 @@ /* misc.h: MISCELLANEOUS DEFINITIONS * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2001 Global Graphics Software. * * Small general things which are useful for C but aren't part of the @@ -50,6 +50,7 @@ typedef const struct SrcIdStruct { #define SRCID(id, scmid) \ static SrcIdStruct id ## FileSrcIdStruct = \ {__FILE__, (scmid), __DATE__, __TIME__}; \ + extern SrcId id ## SrcId; \ SrcId id ## SrcId = &id ## FileSrcIdStruct @@ -170,6 +171,16 @@ typedef const struct SrcIdStruct { ((type *)(void *)((char *)(p) - offsetof(type, field))) +/* BITFIELD -- coerce a value into a bitfield + * + * This coerces value to the given width and type in a way that avoids + * warnings from gcc -Wconversion about possible loss of data. + */ + +#define BITFIELD(type, value, width) ((type)value & (((type)1 << (width)) - 1)) +#define BOOLOF(v) BITFIELD(unsigned, (v), 1) + + /* Bit Sets -- sets of integers in [0,N-1]. * * Can be used on any unsigned integral type, ty. These definitions @@ -191,6 +202,7 @@ typedef const struct SrcIdStruct { #define BS_SUB(s1, s2) BS_SUPER((s2), (s1)) #define BS_IS_SINGLE(s) ( ((s) != 0) && (((s) & ((s)-1)) == 0) ) #define BS_SYM_DIFF(s1, s2) ((s1) ^ (s2)) +#define BS_BITFIELD(ty, s) BITFIELD(ty ## Set, (s), ty ## LIMIT) #endif /* misc_h */ @@ -198,7 +210,7 @@ typedef const struct SrcIdStruct { /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/mpm.c b/mps/code/mpm.c index f544acca401..a46fdf5fb10 100644 --- a/mps/code/mpm.c +++ b/mps/code/mpm.c @@ -174,16 +174,16 @@ Word (WordAlignDown)(Word word, Align alignment) /* SizeIsP2 -- test whether a size is a power of two */ -Bool SizeIsP2(Size size) +Bool (SizeIsP2)(Size size) { - return WordIsP2((Word)size); + return SizeIsP2(size); } /* WordIsP2 -- tests whether a word is a power of two */ -Bool WordIsP2(Word word) +Bool (WordIsP2)(Word word) { - return word > 0 && (word & (word - 1)) == 0; + return WordIsP2(word); } diff --git a/mps/code/mpm.h b/mps/code/mpm.h index 8ed0d6aa608..e6a0da4e834 100644 --- a/mps/code/mpm.h +++ b/mps/code/mpm.h @@ -143,11 +143,13 @@ extern Bool ResIsAllocFailure(Res res); * SizeFloorLog2 returns the floor of the logarithm in base 2 of size. * size can be any positive non-zero value. */ -extern Bool SizeIsP2(Size size); +extern Bool (SizeIsP2)(Size size); +#define SizeIsP2(size) WordIsP2((Word)size) extern Shift SizeLog2(Size size); extern Shift SizeFloorLog2(Size size); -extern Bool WordIsP2(Word word); +extern Bool (WordIsP2)(Word word); +#define WordIsP2(word) ((word) > 0 && ((word) & ((word) - 1)) == 0) /* Formatted Output -- see , */ @@ -712,10 +714,10 @@ extern Addr (SegLimit)(Seg seg); #define SegSummary(seg) (((GCSeg)(seg))->summary) -#define SegSetPM(seg, mode) ((void)((seg)->pm = (mode))) -#define SegSetSM(seg, mode) ((void)((seg)->sm = (mode))) -#define SegSetDepth(seg, d) ((void)((seg)->depth = (d))) -#define SegSetNailed(seg, ts) ((void)((seg)->nailed = (ts))) +#define SegSetPM(seg, mode) ((void)((seg)->pm = BS_BITFIELD(Access, (mode)))) +#define SegSetSM(seg, mode) ((void)((seg)->sm = BS_BITFIELD(Access, (mode)))) +#define SegSetDepth(seg, d) ((void)((seg)->depth = BITFIELD(unsigned, (d), ShieldDepthWIDTH))) +#define SegSetNailed(seg, ts) ((void)((seg)->nailed = BS_BITFIELD(Trace, (ts)))) /* Buffer Interface -- see */ @@ -927,8 +929,6 @@ extern void (ShieldFlush)(Arena arena); extern void ProtSetup(void); extern void ProtSet(Addr base, Addr limit, AccessSet mode); -extern void ProtTramp(void **resultReturn, void *(*f)(void *, size_t), - void *p, size_t s); extern void ProtSync(Arena arena); extern Bool ProtCanStepInstruction(MutatorFaultContext context); extern Res ProtStepInstruction(MutatorFaultContext context); diff --git a/mps/code/mpmst.h b/mps/code/mpmst.h index 85e95a78ec1..5d38f3a3319 100644 --- a/mps/code/mpmst.h +++ b/mps/code/mpmst.h @@ -1,7 +1,7 @@ /* mpmst.h: MEMORY POOL MANAGER DATA STRUCTURES * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2001 Global Graphics Software. * * .design: This header file crosses module boundaries. The relevant @@ -275,8 +275,8 @@ typedef struct SegStruct { /* segment structure */ RingStruct poolRing; /* link in list of segs in pool */ Addr limit; /* limit of segment */ unsigned depth : ShieldDepthWIDTH; /* see */ - AccessSet pm : AccessSetWIDTH; /* protection mode, */ - AccessSet sm : AccessSetWIDTH; /* shield mode, */ + AccessSet pm : AccessLIMIT; /* protection mode, */ + AccessSet sm : AccessLIMIT; /* shield mode, */ TraceSet grey : TraceLIMIT; /* traces for which seg is grey */ TraceSet white : TraceLIMIT; /* traces for which seg is white */ TraceSet nailed : TraceLIMIT; /* traces for which seg has nailed objects */ @@ -738,7 +738,7 @@ typedef struct AllocPatternStruct { /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/mpmtypes.h b/mps/code/mpmtypes.h index 693a2262247..e22e77d8f69 100644 --- a/mps/code/mpmtypes.h +++ b/mps/code/mpmtypes.h @@ -271,7 +271,7 @@ typedef struct TraceMessageStruct *TraceMessage; /* trace end */ #define AccessSetEMPTY ((AccessSet)0) /* */ #define AccessREAD ((AccessSet)(1<<0)) #define AccessWRITE ((AccessSet)(1<<1)) -#define AccessSetWIDTH (2) +#define AccessLIMIT (2) #define RefSetEMPTY BS_EMPTY(RefSet) #define RefSetUNIV BS_UNIV(RefSet) #define ZoneSetEMPTY BS_EMPTY(ZoneSet) diff --git a/mps/code/mpsi.c b/mps/code/mpsi.c index 3df0913df83..45ffff50166 100644 --- a/mps/code/mpsi.c +++ b/mps/code/mpsi.c @@ -1380,7 +1380,7 @@ void (mps_tramp)(void **r_o, AVER(FUNCHECK(f)); /* Can't check p and s as they are interpreted by the client */ - ProtTramp(r_o, f, p, s); + *r_o = (*f)(p, s); } diff --git a/mps/code/mpsw3.h b/mps/code/mpsw3.h deleted file mode 100644 index f32b7a09831..00000000000 --- a/mps/code/mpsw3.h +++ /dev/null @@ -1,79 +0,0 @@ -/* mpsw3.h: RAVENBROOK MEMORY POOL SYSTEM C INTERFACE, WINDOWS PART - * - * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. - * - * .readership: customers, MPS developers. - * .sources: . - */ - -#ifndef mpsw3_h -#define mpsw3_h - -#include "mps.h" /* needed for mps_tramp_t */ -#include "mpswin.h" /* needed for SEH filter */ - - -extern LONG mps_SEH_filter(LPEXCEPTION_POINTERS, void **, size_t *); -extern void mps_SEH_handler(void *, size_t); - - -#define mps_tramp(r_o, f, p, s) \ - MPS_BEGIN \ - void **_r_o = (r_o); \ - mps_tramp_t _f = (f); \ - void *_p = (p); \ - size_t _s = (s); \ - void *_hp = NULL; size_t _hs = 0; \ - __try { \ - *_r_o = (*_f)(_p, _s); \ - } __except(mps_SEH_filter(GetExceptionInformation(), \ - &_hp, &_hs)) { \ - mps_SEH_handler(_hp, _hs); \ - } \ - MPS_END - - -#endif /* mpsw3_h */ - - -/* C. COPYRIGHT AND LICENSE - * - * Copyright (C) 2001-2002 Ravenbrook Limited . - * All rights reserved. This is an open source license. Contact - * Ravenbrook for commercial licensing options. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. Redistributions in any form must be accompanied by information on how - * to obtain complete source code for this software and any accompanying - * software that uses this software. The source code must either be - * included in the distribution or be available for no more than the cost - * of distribution plus a nominal fee, and must be freely redistributable - * under reasonable conditions. For an executable file, complete source - * code means the source code for all modules it contains. It does not - * include source code for modules or files that typically accompany the - * major components of the operating system on which the executable file - * runs. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS - * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR - * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ diff --git a/mps/code/nailboard.c b/mps/code/nailboard.c index 8744514662b..852c98949e7 100644 --- a/mps/code/nailboard.c +++ b/mps/code/nailboard.c @@ -30,26 +30,36 @@ static Count nailboardLevels(Count nails) } +/* nailboardNails -- return the total number of nails in the board */ + +static Count nailboardNails(Nailboard board) +{ + return RangeSize(&board->range) >> board->alignShift; +} + + /* nailboardLevelBits -- return the number of bits in the bit table * for the given level. */ -static Count nailboardLevelBits(Nailboard board, Index level) +static Count nailboardLevelBits(Count nails, Index level) { - /* Use <= rather than < because of .check.levels. */ - AVER(level <= board->levels); - return RangeSize(&board->range) >> (board->alignShift + level * LEVEL_SHIFT); + Shift shift = (Shift)(level * LEVEL_SHIFT); + return (nails + ((Count)1 << shift) - 1) >> shift; } Bool NailboardCheck(Nailboard board) { Index i; + Count nails; CHECKS(Nailboard, board); CHECKL(RangeCheck(&board->range)); CHECKL(0 < board->levels); - CHECKL(board->levels == nailboardLevels(nailboardLevelBits(board, 0))); - CHECKL(nailboardLevelBits(board, board->levels - 1) != 0); - CHECKL(nailboardLevelBits(board, board->levels) == 0); /* .check.levels */ + nails = nailboardNails(board); + CHECKL(board->levels == nailboardLevels(nails)); + CHECKL(nails == nailboardLevelBits(nails, 0)); + CHECKL(nailboardLevelBits(nails, board->levels - 1) != 0); + CHECKL(nailboardLevelBits(nails, board->levels) == 1); CHECKL(BoolCheck(board->newNails)); for (i = 0; i < board->levels; ++i) { CHECKL(board->level[i] != NULL); @@ -80,8 +90,7 @@ static Size nailboardSize(Count nails, Count levels) Size size; size = nailboardStructSize(levels); for (i = 0; i < levels; ++i) { - size += BTSize(nails); - nails >>= LEVEL_SHIFT; + size += BTSize(nailboardLevelBits(nails, i)); } return size; } @@ -130,11 +139,11 @@ Res NailboardCreate(Nailboard *boardReturn, Arena arena, Align alignment, p = PointerAdd(p, nailboardStructSize(levels)); for (i = 0; i < levels; ++i) { - AVER(nails > 0); + Count levelBits = nailboardLevelBits(nails, i); + AVER(levelBits > 0); board->level[i] = p; - BTResRange(board->level[i], 0, nails); - p = PointerAdd(p, BTSize(nails)); - nails >>= LEVEL_SHIFT; + BTResRange(board->level[i], 0, levelBits); + p = PointerAdd(p, BTSize(levelBits)); } board->sig = NailboardSig; @@ -154,7 +163,7 @@ void NailboardDestroy(Nailboard board, Arena arena) AVERT(Nailboard, board); AVERT(Arena, arena); - nails = nailboardLevelBits(board, 0); + nails = nailboardNails(board); size = nailboardSize(nails, board->levels); board->sig = SigInvalid; @@ -191,8 +200,10 @@ Bool (NailboardNewNails)(Nailboard board) static Index nailboardIndex(Nailboard board, Index level, Addr addr) { - return AddrOffset(RangeBase(&board->range), addr) + Index i = AddrOffset(RangeBase(&board->range), addr) >> (board->alignShift + level * LEVEL_SHIFT); + AVER_CRITICAL(i < nailboardLevelBits(nailboardNails(board), level)); + return i; } @@ -414,7 +425,7 @@ Res NailboardDescribe(Nailboard board, mps_lib_FILE *stream) return res; for(i = 0; i < board->levels; ++i) { - Count levelNails = nailboardLevelBits(board, i); + Count levelNails = nailboardLevelBits(nailboardNails(board), i); Count resetNails = BTCountResRange(board->level[i], 0, levelNails); res = WriteF(stream, " Level $U ($U bits, $U set): ", i, levelNails, levelNails - resetNails, NULL); diff --git a/mps/code/poolams.c b/mps/code/poolams.c index c67a2efd684..65b2dcf754f 100644 --- a/mps/code/poolams.c +++ b/mps/code/poolams.c @@ -74,6 +74,11 @@ Bool AMSSegCheck(AMSSeg amsseg) CHECKD_NOSIG(BT, amsseg->nongreyTable); CHECKD_NOSIG(BT, amsseg->nonwhiteTable); + /* If tables are shared, they mustn't both be in use. */ + CHECKL(!(amsseg->ams->shareAllocTable + && amsseg->allocTableInUse + && amsseg->colourTablesInUse)); + return TRUE; } @@ -167,6 +172,15 @@ static Res amsCreateTables(AMS ams, BT *allocReturn, goto failWhite; } +#if defined(AVER_AND_CHECK) + /* Invalidate the colour tables in checking varieties. The algorithm + * is designed not to depend on the initial values of these tables, + * so by invalidating them we get some checking of this. + */ + BTResRange(nongreyTable, 0, length); + BTSetRange(nonwhiteTable, 0, length); +#endif + *allocReturn = allocTable; *nongreyReturn = nongreyTable; *nonwhiteReturn = nonwhiteTable; @@ -1023,7 +1037,20 @@ static void AMSBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit) AVER(limitIndex <= amsseg->firstFree); if (limitIndex == amsseg->firstFree) /* is it at the end? */ { amsseg->firstFree = initIndex; - } else { /* start using allocTable */ + } else if (ams->shareAllocTable && amsseg->colourTablesInUse) { + /* The nonwhiteTable is shared with allocTable and in use, so we + * mustn't start using allocTable. In this case we know: 1. the + * segment has been condemned (because colour tables are turned + * on in AMSCondemn); 2. the segment has not yet been reclaimed + * (because colour tables are turned off in AMSReclaim); 3. the + * unused portion of the buffer is black (see AMSCondemn). So we + * need to whiten the unused portion of the buffer. The + * allocTable will be turned back on (if necessary) in + * AMSReclaim, when we know that the nonwhite grains are exactly + * the allocated grains. + */ + } else { + /* start using allocTable */ amsseg->allocTableInUse = TRUE; BTSetRange(amsseg->allocTable, 0, amsseg->firstFree); if (amsseg->firstFree < amsseg->grains) @@ -1415,16 +1442,20 @@ static Res AMSFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) /* doing that here (this can be called from RootScan, during flip). */ clientRef = *refIO; + AVER_CRITICAL(SegBase(seg) <= clientRef); + AVER_CRITICAL(clientRef < SegLimit(seg)); /* see .ref-limit */ base = AddrSub((Addr)clientRef, format->headerSize); /* can get an ambiguous reference too close to the base of the * segment, so when we subtract the header we are not in the * segment any longer. This isn't a real reference, * so we can just skip it. */ if (base < SegBase(seg)) { - return ResOK; + AVER_CRITICAL(ss->rank == RankAMBIG); + return ResOK; } i = AMS_ADDR_INDEX(seg, base); + AVER_CRITICAL(i < amsseg->grains); AVER_CRITICAL(!AMS_IS_INVALID_COLOUR(seg, i)); ss->wasMarked = TRUE; @@ -1584,12 +1615,13 @@ static void AMSReclaim(Pool pool, Trace trace, Seg seg) /* preservedInPlaceCount is updated on fix */ trace->preservedInPlaceSize += (grains - amsseg->free) << ams->grainShift; + /* Ensure consistency of segment even if are just about to free it */ + amsseg->colourTablesInUse = FALSE; + SegSetWhite(seg, TraceSetDel(SegWhite(seg), trace)); + if (amsseg->free == grains && SegBuffer(seg) == NULL) { /* No survivors */ SegFree(seg); - } else { - amsseg->colourTablesInUse = FALSE; - SegSetWhite(seg, TraceSetDel(SegWhite(seg), trace)); } } diff --git a/mps/code/poolawl.c b/mps/code/poolawl.c index 6332a91b7cd..cbece0b41fe 100644 --- a/mps/code/poolawl.c +++ b/mps/code/poolawl.c @@ -305,10 +305,14 @@ DEFINE_SEG_CLASS(AWLSegClass, class) * it's possible to tweak them in a debugger. */ +extern Count AWLSegSALimit; Count AWLSegSALimit = AWL_SEG_SA_LIMIT; +extern Bool AWLHaveSegSALimit; Bool AWLHaveSegSALimit = AWL_HAVE_SEG_SA_LIMIT; +extern Count AWLTotalSALimit; Count AWLTotalSALimit = AWL_TOTAL_SA_LIMIT; +extern Bool AWLHaveTotalSALimit; Bool AWLHaveTotalSALimit = AWL_HAVE_TOTAL_SA_LIMIT; @@ -558,6 +562,7 @@ static Res AWLInit(Pool pool, ArgList args) AVERT(Format, format); pool->format = format; + pool->alignment = format->alignment; AVER(FUNCHECK(findDependent)); awl->findDependent = findDependent; @@ -570,7 +575,7 @@ static Res AWLInit(Pool pool, ArgList args) if (res != ResOK) goto failGenInit; - awl->alignShift = SizeLog2(pool->alignment); + awl->alignShift = SizeLog2(PoolAlignment(pool)); awl->size = (Size)0; awl->succAccesses = 0; @@ -937,7 +942,7 @@ static Res awlScanSinglePass(Bool *anyScannedReturn, i = awlIndexOfAddr(base, awl, p); if (!BTGet(awlseg->alloc, i)) { - p = AddrAdd(p, pool->alignment); + p = AddrAdd(p, PoolAlignment(pool)); continue; } hp = AddrAdd(p, format->headerSize); @@ -954,7 +959,8 @@ static Res awlScanSinglePass(Bool *anyScannedReturn, } objectLimit = AddrSub(objectLimit, format->headerSize); AVER(p < objectLimit); - p = AddrAlignUp(objectLimit, pool->alignment); + AVER(AddrIsAligned(objectLimit, PoolAlignment(pool))); + p = objectLimit; } AVER(p == limit); @@ -1051,7 +1057,7 @@ static Res AWLFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) switch(ss->rank) { case RankAMBIG: /* not a real pointer if not aligned or not allocated */ - if (!AddrIsAligned(base, pool->alignment) || !BTGet(awlseg->alloc, i)) + if (!AddrIsAligned(base, sizeof(void *)) || !BTGet(awlseg->alloc, i)) return ResOK; /* falls through */ case RankEXACT: @@ -1125,7 +1131,7 @@ static void AWLReclaim(Pool pool, Trace trace, Seg seg) } q = format->skip(AddrAdd(p, format->headerSize)); q = AddrSub(q, format->headerSize); - q = AddrAlignUp(q, pool->alignment); + AVER(AddrIsAligned(q, PoolAlignment(pool))); j = awlIndexOfAddr(base, awl, q); AVER(j <= awlseg->grains); if(BTGet(awlseg->mark, i)) { @@ -1243,13 +1249,13 @@ static void AWLWalk(Pool pool, Seg seg, FormattedObjectsStepMethod f, i = awlIndexOfAddr(base, awl, object); if (!BTGet(awlseg->alloc, i)) { /* This grain is free */ - object = AddrAdd(object, pool->alignment); + object = AddrAdd(object, PoolAlignment(pool)); continue; } object = AddrAdd(object, format->headerSize); next = format->skip(object); next = AddrSub(next, format->headerSize); - next = AddrAlignUp(next, pool->alignment); + AVER(AddrIsAligned(next, PoolAlignment(pool))); if (BTGet(awlseg->mark, i) && BTGet(awlseg->scanned, i)) (*f)(object, pool->format, pool, p, s); object = next; diff --git a/mps/code/poolmfs.c b/mps/code/poolmfs.c index fbb125a71f2..139b3f5c872 100644 --- a/mps/code/poolmfs.c +++ b/mps/code/poolmfs.c @@ -136,7 +136,7 @@ static Res MFSInit(Pool pool, ArgList args) mfs->sig = MFSSig; AVERT(MFS, mfs); - EVENT5(PoolInitMFS, pool, arena, extendBy, extendSelf, unitSize); + EVENT5(PoolInitMFS, pool, arena, extendBy, BOOLOF(extendSelf), unitSize); return ResOK; } diff --git a/mps/code/poolmv.h b/mps/code/poolmv.h index 098cd3eaa2e..8e6885254bc 100644 --- a/mps/code/poolmv.h +++ b/mps/code/poolmv.h @@ -1,41 +1,16 @@ /* poolmv.h: MANUAL VARIABLE POOL * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * * .purpose: This is the interface to the manual-variable pool class. * - * .mv: Manual-variable pools manage variably-sized blocks of memory in a - * flexible manner. They have higher overheads than a fixed-size pool. + * .mv: Manual-variable pools manage variably-sized blocks of memory + * in a flexible manner. They have higher overheads than a fixed-size + * pool. * - * .init: This class adds the following arguments to PoolCreate: - * - * Size extendBy - * - * extendBy is the default number of bytes reserved by the pool at a time. - * A large size will make allocation cheaper but have a higher resource - * overhead. A typical value might be 65536. See note 2. - * - * Size avgSize - * - * avgSize is an estimate of the average size of an allocation, and is used - * to choose the size of internal tables. An accurate estimate will - * improve the efficiency of the pool. A low estimate will make the pool - * less space efficient. A high estimate will make the pool less time - * efficient. A typical value might be 32. avgSize must not be less than - * extendBy. - * - * Size maxSize - * - * maxSize is an estimate of the maximum total size that the pool will - * reach. Setting this parameter does not actually contrain the pool, but - * an accurate estimate will improve the efficiency of the pool. maxSize - * must not be less than extendBy. - * - * Notes - * 2. The documentation could suggest a segment size according to the - * distribution of allocation size requests. richard 1994-11-08 + * .design: See */ #ifndef poolmv_h @@ -59,7 +34,7 @@ extern Bool MVCheck(MV mv); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/poolmv2.c b/mps/code/poolmv2.c index b2903481b45..f6d85b1b134 100644 --- a/mps/code/poolmv2.c +++ b/mps/code/poolmv2.c @@ -78,7 +78,6 @@ typedef struct MVTStruct Bool abqOverflow; /* ABQ dropped some candidates */ /* .* */ Bool splinter; /* Saved splinter */ - Seg splinterSeg; /* Saved splinter seg */ Addr splinterBase; /* Saved splinter base */ Addr splinterLimit; /* Saved splinter size */ @@ -133,7 +132,7 @@ typedef struct MVTStruct DEFINE_POOL_CLASS(MVTPoolClass, this) { - INHERIT_CLASS(this, AbstractSegBufPoolClass); + INHERIT_CLASS(this, AbstractBufferPoolClass); this->name = "MVT"; this->size = sizeof(MVTStruct); this->offset = offsetof(MVTStruct, poolStruct); @@ -292,7 +291,6 @@ static Res MVTInit(Pool pool, ArgList args) mvt->maxSize = maxSize; mvt->fragLimit = fragLimit; mvt->splinter = FALSE; - mvt->splinterSeg = NULL; mvt->splinterBase = (Addr)0; mvt->splinterLimit = (Addr)0; @@ -378,9 +376,7 @@ static Bool MVTCheck(MVT mvt) if (mvt->splinter) { CHECKL(AddrOffset(mvt->splinterBase, mvt->splinterLimit) >= mvt->minSize); - CHECKD(Seg, mvt->splinterSeg); - CHECKL(mvt->splinterBase >= SegBase(mvt->splinterSeg)); - CHECKL(mvt->splinterLimit <= SegLimit(mvt->splinterSeg)); + CHECKL(mvt->splinterBase < mvt->splinterLimit); } CHECKL(mvt->size == mvt->allocated + mvt->available + mvt->unavailable); @@ -951,7 +947,6 @@ static void MVTBufferEmpty(Pool pool, Buffer buffer, } mvt->splinter = TRUE; - mvt->splinterSeg = BufferSeg(buffer); mvt->splinterBase = base; mvt->splinterLimit = limit; } @@ -998,8 +993,6 @@ static void MVTFree(Pool pool, Addr base, Size size) AVER(mvt->size == mvt->allocated + mvt->available + mvt->unavailable); METER_ACC(mvt->exceptionReturns, SegSize(seg)); - if (SegBuffer(seg) != NULL) - BufferDetach(SegBuffer(seg), MVT2Pool(mvt)); MVTSegFree(mvt, seg); return; } @@ -1031,7 +1024,6 @@ static Res MVTDescribe(Pool pool, mps_lib_FILE *stream) " availLimit: $U \n", (WriteFU)mvt->availLimit, " abqOverflow: $S \n", mvt->abqOverflow?"TRUE":"FALSE", " splinter: $S \n", mvt->splinter?"TRUE":"FALSE", - " splinterSeg: $P \n", (WriteFP)mvt->splinterSeg, " splinterBase: $A \n", (WriteFA)mvt->splinterBase, " splinterLimit: $A \n", (WriteFU)mvt->splinterLimit, " size: $U \n", (WriteFU)mvt->size, @@ -1050,68 +1042,37 @@ static Res MVTDescribe(Pool pool, mps_lib_FILE *stream) res = FreelistDescribe(MVTFreelist(mvt), stream); if(res != ResOK) return res; - res = METER_WRITE(mvt->segAllocs, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->segFrees, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->bufferFills, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->bufferEmpties, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->poolFrees, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->poolSize, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->poolAllocated, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->poolAvailable, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->poolUnavailable, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->poolUtilization, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->finds, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->overflows, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->underflows, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->refills, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->refillPushes, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->returns, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->perfectFits, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->firstFits, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->secondFits, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->failures, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->emergencyContingencies, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->fragLimitContingencies, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->contingencySearches, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->contingencyHardSearches, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->splinters, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->splintersUsed, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->splintersDropped, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->sawdust, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->exceptions, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->exceptionSplinters, stream); - if (res != ResOK) return res; - res = METER_WRITE(mvt->exceptionReturns, stream); - if (res != ResOK) return res; + METER_WRITE(mvt->segAllocs, stream); + METER_WRITE(mvt->segFrees, stream); + METER_WRITE(mvt->bufferFills, stream); + METER_WRITE(mvt->bufferEmpties, stream); + METER_WRITE(mvt->poolFrees, stream); + METER_WRITE(mvt->poolSize, stream); + METER_WRITE(mvt->poolAllocated, stream); + METER_WRITE(mvt->poolAvailable, stream); + METER_WRITE(mvt->poolUnavailable, stream); + METER_WRITE(mvt->poolUtilization, stream); + METER_WRITE(mvt->finds, stream); + METER_WRITE(mvt->overflows, stream); + METER_WRITE(mvt->underflows, stream); + METER_WRITE(mvt->refills, stream); + METER_WRITE(mvt->refillPushes, stream); + METER_WRITE(mvt->returns, stream); + METER_WRITE(mvt->perfectFits, stream); + METER_WRITE(mvt->firstFits, stream); + METER_WRITE(mvt->secondFits, stream); + METER_WRITE(mvt->failures, stream); + METER_WRITE(mvt->emergencyContingencies, stream); + METER_WRITE(mvt->fragLimitContingencies, stream); + METER_WRITE(mvt->contingencySearches, stream); + METER_WRITE(mvt->contingencyHardSearches, stream); + METER_WRITE(mvt->splinters, stream); + METER_WRITE(mvt->splintersUsed, stream); + METER_WRITE(mvt->splintersDropped, stream); + METER_WRITE(mvt->sawdust, stream); + METER_WRITE(mvt->exceptions, stream); + METER_WRITE(mvt->exceptionSplinters, stream); + METER_WRITE(mvt->exceptionReturns, stream); res = WriteF(stream, "}\n", NULL); return res; @@ -1189,7 +1150,7 @@ static Res MVTSegAlloc(Seg *segReturn, MVT mvt, Size size, { /* Can't use plain old SegClass here because we need to call * SegBuffer() in MVTFree(). */ - Res res = SegAlloc(segReturn, GCSegClassGet(), + Res res = SegAlloc(segReturn, SegClassGet(), SegPrefDefault(), size, MVT2Pool(mvt), withReservoirPermit, argsNone); @@ -1213,7 +1174,6 @@ static Res MVTSegAlloc(Seg *segReturn, MVT mvt, Size size, */ static void MVTSegFree(MVT mvt, Seg seg) { - Buffer buffer; Size size; size = SegSize(seg); @@ -1223,16 +1183,6 @@ static void MVTSegFree(MVT mvt, Seg seg) mvt->size -= size; mvt->availLimit = mvt->size * mvt->fragLimit / 100; AVER(mvt->size == mvt->allocated + mvt->available + mvt->unavailable); - - /* If the client program allocates the exactly the entire buffer then - frees the allocated memory then we'll try to free the segment with - the buffer still attached. It's safe, but we must detach the buffer - first. See job003520 and job003672. */ - buffer = SegBuffer(seg); - if (buffer != NULL) { - AVER(BufferAP(buffer)->init == SegLimit(seg)); - BufferDetach(buffer, MVT2Pool(mvt)); - } SegFree(seg); METER_ACC(mvt->segFrees, size); diff --git a/mps/code/poolmvff.c b/mps/code/poolmvff.c index 201d2047104..4971550c76a 100644 --- a/mps/code/poolmvff.c +++ b/mps/code/poolmvff.c @@ -610,7 +610,7 @@ static Res MVFFInit(Pool pool, ArgList args) mvff->sig = MVFFSig; AVERT(MVFF, mvff); EVENT8(PoolInitMVFF, pool, arena, extendBy, avgSize, align, - slotHigh, arenaHigh, firstFit); + BOOLOF(slotHigh), BOOLOF(arenaHigh), BOOLOF(firstFit)); return ResOK; failInit: diff --git a/mps/code/prmci3li.c b/mps/code/prmci3li.c index 6a36ae7db2b..5b5d150b8cc 100644 --- a/mps/code/prmci3li.c +++ b/mps/code/prmci3li.c @@ -36,27 +36,34 @@ SRCID(prmci3li, "$Id$"); MRef Prmci3AddressHoldingReg(MutatorFaultContext mfc, unsigned int regnum) { + MRef gregs; + + AVER(mfc != NULL); AVER(NONNEGATIVE(regnum)); AVER(regnum <= 7); + AVER(mfc->ucontext != NULL); + + /* TODO: The current arrangement of the fix operation (taking a Ref *) + forces us to pun these registers (actually `int` on LII3GC). We can + suppress the warning by casting through `void *` and this might make + it safe, but does it really? RB 2012-09-10 */ + AVER(sizeof(void *) == sizeof(*mfc->ucontext->uc_mcontext.gregs)); + gregs = (void *)mfc->ucontext->uc_mcontext.gregs; /* .source.i486 */ /* .assume.regref */ /* The register numbers (REG_EAX etc.) are defined in but only if _GNU_SOURCE is defined: see .feature.li in config.h. */ - /* TODO: The current arrangement of the fix operation (taking a Ref *) - forces us to pun these registers (actually `int` on LII3GC). We can - suppress the warning my casting through `char *` and this might make - it safe, but does it really? RB 2012-09-10 */ switch (regnum) { - case 0: return (MRef)((char *)&mfc->ucontext->uc_mcontext.gregs[REG_EAX]); - case 1: return (MRef)((char *)&mfc->ucontext->uc_mcontext.gregs[REG_ECX]); - case 2: return (MRef)((char *)&mfc->ucontext->uc_mcontext.gregs[REG_EDX]); - case 3: return (MRef)((char *)&mfc->ucontext->uc_mcontext.gregs[REG_EBX]); - case 4: return (MRef)((char *)&mfc->ucontext->uc_mcontext.gregs[REG_ESP]); - case 5: return (MRef)((char *)&mfc->ucontext->uc_mcontext.gregs[REG_EBP]); - case 6: return (MRef)((char *)&mfc->ucontext->uc_mcontext.gregs[REG_ESI]); - case 7: return (MRef)((char *)&mfc->ucontext->uc_mcontext.gregs[REG_EDI]); + case 0: return &gregs[REG_EAX]; + case 1: return &gregs[REG_ECX]; + case 2: return &gregs[REG_EDX]; + case 3: return &gregs[REG_EBX]; + case 4: return &gregs[REG_ESP]; + case 5: return &gregs[REG_EBP]; + case 6: return &gregs[REG_ESI]; + case 7: return &gregs[REG_EDI]; default: NOTREACHED; return NULL; /* Avoids compiler warning. */ diff --git a/mps/code/prmci3xc.c b/mps/code/prmci3xc.c index 786145fc084..eafeff61540 100644 --- a/mps/code/prmci3xc.c +++ b/mps/code/prmci3xc.c @@ -34,8 +34,13 @@ SRCID(prmci3li, "$Id$"); MRef Prmci3AddressHoldingReg(MutatorFaultContext mfc, unsigned int regnum) { + THREAD_STATE_S *threadState; + + AVER(mfc != NULL); AVER(NONNEGATIVE(regnum)); AVER(regnum <= 7); + AVER(mfc->threadState != NULL); + threadState = mfc->threadState; /* .source.i486 */ /* .assume.regref */ @@ -44,17 +49,17 @@ MRef Prmci3AddressHoldingReg(MutatorFaultContext mfc, unsigned int regnum) config.h. */ /* TODO: The current arrangement of the fix operation (taking a Ref *) forces us to pun these registers (actually `int` on LII3GC). We can - suppress the warning my casting through `char *` and this might make + suppress the warning by casting through `void *` and this might make it safe, but does it really? RB 2012-09-10 */ switch (regnum) { - case 0: return (MRef)((char *)&mfc->threadState->__eax); - case 1: return (MRef)((char *)&mfc->threadState->__ecx); - case 2: return (MRef)((char *)&mfc->threadState->__edx); - case 3: return (MRef)((char *)&mfc->threadState->__ebx); - case 4: return (MRef)((char *)&mfc->threadState->__esp); - case 5: return (MRef)((char *)&mfc->threadState->__ebp); - case 6: return (MRef)((char *)&mfc->threadState->__esi); - case 7: return (MRef)((char *)&mfc->threadState->__edi); + case 0: return (void *)&threadState->__eax; + case 1: return (void *)&threadState->__ecx; + case 2: return (void *)&threadState->__edx; + case 3: return (void *)&threadState->__ebx; + case 4: return (void *)&threadState->__esp; + case 5: return (void *)&threadState->__ebp; + case 6: return (void *)&threadState->__esi; + case 7: return (void *)&threadState->__edi; default: NOTREACHED; return NULL; /* Avoids compiler warning. */ diff --git a/mps/code/prmci6li.c b/mps/code/prmci6li.c index 2f8bf9afc62..c00c1359014 100644 --- a/mps/code/prmci6li.c +++ b/mps/code/prmci6li.c @@ -33,12 +33,19 @@ SRCID(prmci6li, "$Id$"); MRef Prmci6AddressHoldingReg(MutatorFaultContext mfc, unsigned int regnum) { - Word *gregs; + MRef gregs; + AVER(mfc != NULL); AVER(NONNEGATIVE(regnum)); AVER(regnum <= 15); + AVER(mfc->ucontext != NULL); - gregs = (Word *)&mfc->ucontext->uc_mcontext.gregs; + /* TODO: The current arrangement of the fix operation (taking a Ref *) + forces us to pun these registers (actually `int` on LII6GC). We can + suppress the warning by casting through `void *` and this might make + it safe, but does it really? RB 2012-09-10 */ + AVER(sizeof(void *) == sizeof(*mfc->ucontext->uc_mcontext.gregs)); + gregs = (void *)mfc->ucontext->uc_mcontext.gregs; /* .assume.regref */ /* The register numbers (REG_RAX etc.) are defined in diff --git a/mps/code/prmci6xc.c b/mps/code/prmci6xc.c index 02ccb840b6c..131447cd0cc 100644 --- a/mps/code/prmci6xc.c +++ b/mps/code/prmci6xc.c @@ -31,33 +31,38 @@ SRCID(prmci6li, "$Id$"); MRef Prmci6AddressHoldingReg(MutatorFaultContext mfc, unsigned int regnum) { + THREAD_STATE_S *threadState; + + AVER(mfc != NULL); AVER(NONNEGATIVE(regnum)); AVER(regnum <= 15); + AVER(mfc->threadState != NULL); + threadState = mfc->threadState; /* .assume.regref */ /* The register numbers (REG_RAX etc.) are defined in but only if _XOPEN_SOURCE is defined: see .feature.xc in config.h. */ /* MRef (a Word *) is not compatible with pointers to the register - types (actually a __uint64_t). To avoid aliasing optimization - problems, The registers are cast through (char *) */ + types (actually a __uint64_t). To avoid aliasing optimization + problems, the registers are cast through (void *). */ switch (regnum) { - case 0: return (MRef)((char *)&mfc->threadState->__rax); - case 1: return (MRef)((char *)&mfc->threadState->__rcx); - case 2: return (MRef)((char *)&mfc->threadState->__rdx); - case 3: return (MRef)((char *)&mfc->threadState->__rbx); - case 4: return (MRef)((char *)&mfc->threadState->__rsp); - case 5: return (MRef)((char *)&mfc->threadState->__rbp); - case 6: return (MRef)((char *)&mfc->threadState->__rsi); - case 7: return (MRef)((char *)&mfc->threadState->__rdi); - case 8: return (MRef)((char *)&mfc->threadState->__r8); - case 9: return (MRef)((char *)&mfc->threadState->__r9); - case 10: return (MRef)((char *)&mfc->threadState->__r10); - case 11: return (MRef)((char *)&mfc->threadState->__r11); - case 12: return (MRef)((char *)&mfc->threadState->__r12); - case 13: return (MRef)((char *)&mfc->threadState->__r13); - case 14: return (MRef)((char *)&mfc->threadState->__r14); - case 15: return (MRef)((char *)&mfc->threadState->__r15); + case 0: return (void *)&threadState->__rax; + case 1: return (void *)&threadState->__rcx; + case 2: return (void *)&threadState->__rdx; + case 3: return (void *)&threadState->__rbx; + case 4: return (void *)&threadState->__rsp; + case 5: return (void *)&threadState->__rbp; + case 6: return (void *)&threadState->__rsi; + case 7: return (void *)&threadState->__rdi; + case 8: return (void *)&threadState->__r8; + case 9: return (void *)&threadState->__r9; + case 10: return (void *)&threadState->__r10; + case 11: return (void *)&threadState->__r11; + case 12: return (void *)&threadState->__r12; + case 13: return (void *)&threadState->__r13; + case 14: return (void *)&threadState->__r14; + case 15: return (void *)&threadState->__r15; default: NOTREACHED; return NULL; /* Avoids compiler warning. */ diff --git a/mps/code/protan.c b/mps/code/protan.c index 86afa645e63..0e8abf8ac7e 100644 --- a/mps/code/protan.c +++ b/mps/code/protan.c @@ -1,7 +1,7 @@ /* protan.c: ANSI MEMORY PROTECTION * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * * DESIGN @@ -63,22 +63,9 @@ void ProtSync(Arena arena) } -/* ProtTramp -- protection trampoline */ - -void ProtTramp(void **rReturn, void *(*f)(void *, size_t), - void *p, size_t s) -{ - AVER(rReturn != NULL); - AVER(FUNCHECK(f)); - /* Can't check p and s as they are interpreted by the client */ - - *(rReturn) = (*(f))(p, s); -} - - /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/protix.c b/mps/code/protix.c index e79972af9e6..5b310333e84 100644 --- a/mps/code/protix.c +++ b/mps/code/protix.c @@ -1,7 +1,7 @@ /* protix.c: PROTECTION FOR UNIX * * $Id$ - * Copyright (c) 2001,2007 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * Somewhat generic across different Unix systems. Shared between * Darwin (OS X), FreeBSD, and Linux. @@ -111,28 +111,9 @@ void ProtSync(Arena arena) } -/* ProtTramp -- protection trampoline - * - * The protection trampoline is trivial under Unix, as there is - * nothing that needs to be done in the dynamic context of the mutator in - * order to catch faults. (Contrast this with Win32 Structured Exception - * Handling.) - */ - -void ProtTramp(void **resultReturn, void *(*f)(void *, size_t), - void *p, size_t s) -{ - AVER(resultReturn != NULL); - AVER(FUNCHECK(f)); - /* Can't check p and s as they are interpreted by the client */ - - *resultReturn = (*f)(p, s); -} - - /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2007 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/protw3.c b/mps/code/protw3.c index 1f76f0a4e80..4ff3e85da42 100644 --- a/mps/code/protw3.c +++ b/mps/code/protw3.c @@ -1,7 +1,7 @@ /* protw3.c: PROTECTION FOR WIN32 * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ #include "mpm.h" @@ -128,27 +128,9 @@ void ProtSync(Arena arena) } -/* ProtTramp -- wrap a mutator thread in a Structured Exception Handler filter - * - * This was the method by which we installed an exception handler on Windows - * prior to MPS 1.111. Now we are using Vectored Exception Handlers, so this - * is deprecated and just calls through to the mutator function. - */ - -void ProtTramp(void **resultReturn, void *(*f)(void *, size_t), - void *p, size_t s) -{ - AVER(resultReturn != NULL); - AVER(FUNCHECK(f)); - /* Can't check p and s as they are interpreted by the client */ - - *resultReturn = f(p, s); -} - - /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/protxc.c b/mps/code/protxc.c index 62ade6ed81b..7e8f230d061 100644 --- a/mps/code/protxc.c +++ b/mps/code/protxc.c @@ -240,7 +240,7 @@ static void protCatchOne(void) different size" warnings in GCC, for the XCI3GC build. */ mfcStruct.address = (Addr)(Word)request.code[1]; AVER(sizeof(*mfcStruct.threadState) == sizeof(THREAD_STATE_S)); - mfcStruct.threadState = (THREAD_STATE_S *)request.old_state; + mfcStruct.threadState = (void *)request.old_state; if (ArenaAccess(mfcStruct.address, AccessREAD | AccessWRITE, @@ -279,6 +279,7 @@ static void protCatchOne(void) * handler won't cause a deadlock. */ +ATTRIBUTE_NORETURN static void *protCatchThread(void *p) { UNUSED(p); for (;;) diff --git a/mps/code/qs.c b/mps/code/qs.c index 8fb31ee0aab..0a90f14f069 100644 --- a/mps/code/qs.c +++ b/mps/code/qs.c @@ -50,7 +50,7 @@ static mps_addr_t isMoved(mps_addr_t object); static void copy(mps_addr_t object, mps_addr_t to); static void pad(mps_addr_t base, size_t size); -struct mps_fmt_A_s fmt_A_s = +static struct mps_fmt_A_s fmt_A_s = { (mps_align_t)4, scan, skip, copy, diff --git a/mps/code/ring.c b/mps/code/ring.c index ff60149ce40..54902ec3818 100644 --- a/mps/code/ring.c +++ b/mps/code/ring.c @@ -1,7 +1,7 @@ /* ring.c: RING IMPLEMENTATION * * $Id$ - * Copyright (c) 2001,2003 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .intro: This is a portable implementation of Rings. * @@ -52,6 +52,16 @@ Bool RingIsSingle(Ring ring) return (ring->next == ring); } +Size RingLength(Ring ring) +{ + Size size = 0; + Ring node, next; + AVERT(Ring, ring); + RING_FOR(node, ring, next) + ++ size; + return size; +} + /* RingInit -- initialize a ring node */ @@ -131,7 +141,7 @@ Ring (RingPrev)(Ring ring) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2003 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/ring.h b/mps/code/ring.h index d5b64076f6c..cbde6afe814 100644 --- a/mps/code/ring.h +++ b/mps/code/ring.h @@ -1,7 +1,7 @@ /* ring.h: RING INTERFACE * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2001 Global Graphics Software. */ @@ -30,6 +30,7 @@ typedef struct RingStruct { /* double-ended queue structure */ extern Bool RingCheck(Ring ring); extern Bool RingCheckSingle(Ring ring); extern Bool RingIsSingle(Ring ring); +extern Size RingLength(Ring ring); /* .ring.init: */ extern void (RingInit)(Ring ring); @@ -115,7 +116,7 @@ extern Ring (RingPrev)(Ring ring); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/sac.c b/mps/code/sac.c index 435988cf867..3caf9ab893f 100644 --- a/mps/code/sac.c +++ b/mps/code/sac.c @@ -48,18 +48,20 @@ static Bool SACCheck(SAC sac) CHECKL(esac->_middle > 0); /* check classes above middle */ prevSize = esac->_middle; - for (j = sac->middleIndex + 1, i = 0; - j <= sac->classesCount; ++j, i += 2) { + for (j = sac->middleIndex + 1, i = 0; j < sac->classesCount; ++j, i += 2) { CHECKL(prevSize < esac->_freelists[i]._size); b = sacFreeListBlockCheck(&(esac->_freelists[i])); if (!b) return b; prevSize = esac->_freelists[i]._size; } /* check overlarge class */ - CHECKL(esac->_freelists[i-2]._size == SizeMAX); - CHECKL(esac->_freelists[i-2]._count == 0); - CHECKL(esac->_freelists[i-2]._count_max == 0); - CHECKL(esac->_freelists[i-2]._blocks == NULL); + CHECKL(prevSize < esac->_freelists[i]._size); + b = sacFreeListBlockCheck(&(esac->_freelists[i])); + if (!b) return b; + CHECKL(esac->_freelists[i]._size == SizeMAX); + CHECKL(esac->_freelists[i]._count == 0); + CHECKL(esac->_freelists[i]._count_max == 0); + CHECKL(esac->_freelists[i]._blocks == NULL); /* check classes below middle */ prevSize = esac->_middle; for (j = sac->middleIndex, i = 1; j > 0; --j, i += 2) { @@ -69,6 +71,7 @@ static Bool SACCheck(SAC sac) prevSize = esac->_freelists[i]._size; } /* check smallest class */ + CHECKL(prevSize > esac->_freelists[i]._size); CHECKL(esac->_freelists[i]._size == 0); b = sacFreeListBlockCheck(&(esac->_freelists[i])); return b; diff --git a/mps/code/seg.c b/mps/code/seg.c index 2fb680c7094..cfa224a8368 100644 --- a/mps/code/seg.c +++ b/mps/code/seg.c @@ -605,7 +605,7 @@ Res SegMerge(Seg *mergedSegReturn, Seg segLo, Seg segHi, if (ResOK != res) goto failMerge; - EVENT3(SegMerge, segLo, segHi, withReservoirPermit); + EVENT3(SegMerge, segLo, segHi, BOOLOF(withReservoirPermit)); /* Deallocate segHi object */ ControlFree(arena, segHi, class->size); AVERT(Seg, segLo); @@ -1212,7 +1212,7 @@ static void gcSegSetGreyInternal(Seg seg, TraceSet oldGrey, TraceSet grey) /* Internal method. Parameters are checked by caller */ gcseg = SegGCSeg(seg); arena = PoolArena(SegPool(seg)); - seg->grey = grey; + seg->grey = BS_BITFIELD(Trace, grey); /* If the segment is now grey and wasn't before, add it to the */ /* appropriate grey list so that TraceFindGrey can locate it */ @@ -1325,11 +1325,11 @@ static void gcSegSetWhite(Seg seg, TraceSet white) AVERT_CRITICAL(Tract, tract); AVER_CRITICAL(TRACT_SEG(&trseg, tract) && (trseg == seg)); - TractSetWhite(tract, white); + TractSetWhite(tract, BS_BITFIELD(Trace, white)); } AVER(addr == limit); - seg->white = white; + seg->white = BS_BITFIELD(Trace, white); } @@ -1362,7 +1362,7 @@ static void gcSegSetRankSet(Seg seg, RankSet rankSet) arena = PoolArena(SegPool(seg)); oldRankSet = seg->rankSet; - seg->rankSet = rankSet; + seg->rankSet = BS_BITFIELD(Rank, rankSet); if (oldRankSet == RankSetEMPTY) { if (rankSet != RankSetEMPTY) { @@ -1439,7 +1439,7 @@ static void gcSegSetRankSummary(Seg seg, RankSet rankSet, RefSet summary) wasShielded = (seg->rankSet != RankSetEMPTY && gcseg->summary != RefSetUNIV); willbeShielded = (rankSet != RankSetEMPTY && summary != RefSetUNIV); - seg->rankSet = rankSet; + seg->rankSet = BS_BITFIELD(Rank, rankSet); gcseg->summary = summary; if (willbeShielded && !wasShielded) { diff --git a/mps/code/steptest.c b/mps/code/steptest.c index c1af5b01089..deee85fef2e 100644 --- a/mps/code/steptest.c +++ b/mps/code/steptest.c @@ -71,19 +71,19 @@ static mps_addr_t ambigRoots[ambigRootsCOUNT]; /* Things we want to measure. Times are all in microseconds. */ -double alloc_time; /* Time spent allocating */ -double max_alloc_time; /* Max time taken to allocate one object */ -double step_time; /* Time spent in mps_arena_step returning 1 */ -double max_step_time; /* Max time of mps_arena_step returning 1 */ -double no_step_time; /* Time spent in mps_arena_step returning 0 */ -double max_no_step_time; /* Max time of mps_arena_step returning 0 */ +static double alloc_time; /* Time spent allocating */ +static double max_alloc_time; /* Max time taken to allocate one object */ +static double step_time; /* Time spent in mps_arena_step returning 1 */ +static double max_step_time; /* Max time of mps_arena_step returning 1 */ +static double no_step_time; /* Time spent in mps_arena_step returning 0 */ +static double max_no_step_time; /* Max time of mps_arena_step returning 0 */ -double total_clock_time; /* Time spent reading the clock */ -long clock_reads; /* Number of times clock is read */ -long steps; /* # of mps_arena_step calls returning 1 */ -long no_steps; /* # of mps_arena_step calls returning 0 */ -size_t alloc_bytes; /* # of bytes allocated */ -long commit_failures; /* # of times mps_commit fails */ +static double total_clock_time; /* Time spent reading the clock */ +static long clock_reads; /* Number of times clock is read */ +static long steps; /* # of mps_arena_step calls returning 1 */ +static long no_steps; /* # of mps_arena_step calls returning 0 */ +static size_t alloc_bytes; /* # of bytes allocated */ +static long commit_failures; /* # of times mps_commit fails */ /* Operating-system dependent timing. Defines two functions, void @@ -151,7 +151,7 @@ static double my_clock(void) * on thrush.ravenbrook.com on 2002-06-28, clock_time goes from 5.43 * us near process start to 7.45 us later). */ -double clock_time; /* current estimate of time to read the clock */ +static double clock_time; /* current estimate of time to read the clock */ /* take at least this many microseconds to set the clock */ #define CLOCK_TIME_SET 10000 diff --git a/mps/code/testlib.h b/mps/code/testlib.h index 0912373031d..9c197cae839 100644 --- a/mps/code/testlib.h +++ b/mps/code/testlib.h @@ -28,13 +28,10 @@ /* Suppress Pelles C warnings at warning level 2 */ -/* Some of these are also done in config.h. */ +/* This is also done in config.h. */ #ifdef MPS_BUILD_PC -/* "Structured Exception Handling is not portable." (mps_tramp). */ -#pragma warn(disable: 2008) - /* "Unreachable code" (AVER, if condition is constantly true). */ #pragma warn(disable: 2154) diff --git a/mps/code/trace.c b/mps/code/trace.c index 924badcd541..bd2b871fb11 100644 --- a/mps/code/trace.c +++ b/mps/code/trace.c @@ -390,6 +390,7 @@ Res TraceCondemnZones(Trace trace, ZoneSet condemnedSet) Seg seg; Arena arena; Res res; + Bool haveWhiteSegs = FALSE; AVERT(Trace, trace); AVER(condemnedSet != ZoneSetEMPTY); @@ -415,7 +416,8 @@ Res TraceCondemnZones(Trace trace, ZoneSet condemnedSet) { res = TraceAddWhite(trace, seg); if(res != ResOK) - return res; + goto failBegin; + haveWhiteSegs = TRUE; } } while (SegNext(&seg, arena, seg)); } @@ -426,6 +428,10 @@ Res TraceCondemnZones(Trace trace, ZoneSet condemnedSet) AVER(ZoneSetSuper(condemnedSet, trace->white)); return ResOK; + +failBegin: + AVER(!haveWhiteSegs); /* See .whiten.fail. */ + return res; } @@ -797,10 +803,11 @@ void TraceDestroy(Trace trace) (TraceStatReclaim, trace, trace->reclaimCount, trace->reclaimSize)); + EVENT1(TraceDestroy, trace); + trace->sig = SigInvalid; trace->arena->busyTraces = TraceSetDel(trace->arena->busyTraces, trace); trace->arena->flippedTraces = TraceSetDel(trace->arena->flippedTraces, trace); - EVENT1(TraceDestroy, trace); } @@ -1502,21 +1509,31 @@ static Res traceCondemnAll(Trace trace) { Res res; Arena arena; - Ring chainNode, nextChainNode; + Ring poolNode, nextPoolNode, chainNode, nextChainNode; Bool haveWhiteSegs = FALSE; arena = trace->arena; AVERT(Arena, arena); - /* Condemn all the chains. */ - RING_FOR(chainNode, &arena->chainRing, nextChainNode) { - Chain chain = RING_ELT(Chain, chainRing, chainNode); - AVERT(Chain, chain); - res = ChainCondemnAll(chain, trace); - if(res != ResOK) - goto failBegin; - haveWhiteSegs = TRUE; + /* Condemn all segments in pools with the GC attribute. */ + RING_FOR(poolNode, &ArenaGlobals(arena)->poolRing, nextPoolNode) { + Pool pool = RING_ELT(Pool, arenaRing, poolNode); + AVERT(Pool, pool); + + if (PoolHasAttr(pool, AttrGC)) { + Ring segNode, nextSegNode; + RING_FOR(segNode, PoolSegRing(pool), nextSegNode) { + Seg seg = SegOfPoolRing(segNode); + AVERT(Seg, seg); + + res = TraceAddWhite(trace, seg); + if (res != ResOK) + goto failBegin; + haveWhiteSegs = TRUE; + } + } } + /* Notify all the chains. */ RING_FOR(chainNode, &arena->chainRing, nextChainNode) { Chain chain = RING_ELT(Chain, chainRing, chainNode); @@ -1526,7 +1543,14 @@ static Res traceCondemnAll(Trace trace) return ResOK; failBegin: - AVER(!haveWhiteSegs); /* Would leave white sets inconsistent. */ + /* .whiten.fail: If we successfully whitened one or more segments, + * but failed to whiten them all, then the white sets would now be + * inconsistent. This can't happen in practice (at time of writing) + * because all PoolWhiten methods always succeed. If we ever have a + * pool class that fails to whiten a segment, then this assertion + * will be triggered. In that case, we'll have to recover here by + * blackening the segments again. */ + AVER(!haveWhiteSegs); return res; } @@ -1569,7 +1593,7 @@ static void TraceStartPoolGen(Chain chain, GenDesc desc, Bool top, Index i) Ring n, nn; RING_FOR(n, &desc->locusRing, nn) { PoolGen gen = RING_ELT(PoolGen, genRing, n); - EVENT11(TraceStartPoolGen, chain, top, i, desc, + EVENT11(TraceStartPoolGen, chain, BOOLOF(top), i, desc, desc->capacity, desc->mortality, desc->zones, gen->pool, gen->nr, gen->totalSize, gen->newSizeAtCreate); diff --git a/mps/code/version.c b/mps/code/version.c index 9f8d644e47a..4771c8a7c69 100644 --- a/mps/code/version.c +++ b/mps/code/version.c @@ -47,6 +47,7 @@ SRCID(version, "$Id$"); * (assuming we've made any substantial changes to the library this year). */ +extern char MPSCopyrightNotice[]; char MPSCopyrightNotice[] = "Portions copyright (c) 2010-2014 Ravenbrook Limited and Global Graphics Software."; @@ -59,6 +60,7 @@ char MPSCopyrightNotice[] = * see also guide.mps.version. */ +extern char MPSVersionString[]; char MPSVersionString[] = "@(#)Ravenbrook MPS, " "product." MPS_PROD_STRING ", " MPS_RELEASE ", platform." MPS_PF_STRING diff --git a/mps/code/vman.c b/mps/code/vman.c index 2b4f0c3ecb2..2e68118e27d 100644 --- a/mps/code/vman.c +++ b/mps/code/vman.c @@ -1,7 +1,7 @@ /* vman.c: ANSI VM: MALLOC-BASED PSEUDO MEMORY MAPPING * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. */ #include "mpm.h" @@ -118,13 +118,13 @@ void VMDestroy(VM vm) AVER(vm->mapped == (Size)0); AVER(vm->reserved == AddrOffset(vm->base, vm->limit)); + EVENT1(VMDestroy, vm); + memset((void *)vm->base, VMJunkBYTE, AddrOffset(vm->base, vm->limit)); free(vm->block); vm->sig = SigInvalid; - free(vm); - - EVENT1(VMDestroy, vm); + free(vm); } @@ -216,7 +216,7 @@ void VMUnmap(VM vm, Addr base, Addr limit) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/vmix.c b/mps/code/vmix.c index 01a0919b820..0903a031a2c 100644 --- a/mps/code/vmix.c +++ b/mps/code/vmix.c @@ -1,7 +1,7 @@ /* vmix.c: VIRTUAL MEMORY MAPPING FOR UNIX (ISH) * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .purpose: This is the implementation of the virtual memory mapping * interface (vm.h) for Unix-like operating systems. It was created @@ -186,6 +186,8 @@ void VMDestroy(VM vm) AVERT(VM, vm); AVER(vm->mapped == (Size)0); + EVENT1(VMDestroy, vm); + /* This appears to be pretty pointless, since the descriptor */ /* page is about to vanish completely. However, munmap might fail */ /* for some reason, and this would ensure that it was still */ @@ -197,8 +199,6 @@ void VMDestroy(VM vm) r = munmap((void *)vm, (size_t)SizeAlignUp(sizeof(VMStruct), vm->align)); AVER(r == 0); - - EVENT1(VMDestroy, vm); } @@ -304,7 +304,7 @@ void VMUnmap(VM vm, Addr base, Addr limit) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/vmw3.c b/mps/code/vmw3.c index e82f14ccd51..5be8153c73c 100644 --- a/mps/code/vmw3.c +++ b/mps/code/vmw3.c @@ -1,7 +1,7 @@ /* vmw3.c: VIRTUAL MEMORY MAPPING FOR WIN32 * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. * * .design: See . * @@ -191,6 +191,8 @@ void VMDestroy(VM vm) AVERT(VM, vm); AVER(vm->mapped == 0); + EVENT1(VMDestroy, vm); + /* This appears to be pretty pointless, since the vm descriptor page * is about to vanish completely. However, the VirtualFree might * fail and it would be nice to have a dead sig there. */ @@ -201,7 +203,6 @@ void VMDestroy(VM vm) b = VirtualFree((LPVOID)vm, (SIZE_T)0, MEM_RELEASE); AVER(b != 0); - EVENT1(VMDestroy, vm); } @@ -303,7 +304,7 @@ void VMUnmap(VM vm, Addr base, Addr limit) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/walk.c b/mps/code/walk.c index 3f7f67f99e1..4dd0d6b9e98 100644 --- a/mps/code/walk.c +++ b/mps/code/walk.c @@ -273,6 +273,20 @@ static Res rootWalk(Root root, void *p) } +/* rootWalkGrey -- make the root grey for the trace passed as p */ + +static Res rootWalkGrey(Root root, void *p) +{ + Trace trace = p; + + AVERT(Root, root); + AVERT(Trace, trace); + + RootGrey(root, trace); + return ResOK; +} + + /* ArenaRootsWalk -- walks all the root in the arena */ static Res ArenaRootsWalk(Globals arenaGlobals, mps_roots_stepper_t f, @@ -315,7 +329,7 @@ static Res ArenaRootsWalk(Globals arenaGlobals, mps_roots_stepper_t f, } /* Make the roots grey so that they are scanned */ - res = RootsIterate(arenaGlobals, (RootIterateFn)RootGrey, (void *)trace); + res = RootsIterate(arenaGlobals, rootWalkGrey, trace); /* Make this trace look like any other trace. */ arena->flippedTraces = TraceSetAdd(arena->flippedTraces, trace); @@ -330,6 +344,16 @@ static Res ArenaRootsWalk(Globals arenaGlobals, mps_roots_stepper_t f, break; } + /* Turn segments black again. */ + if (SegFirst(&seg, arena)) { + do { + if (PoolHasAttr(SegPool(seg), AttrGC)) { + SegSetGrey(seg, TraceSetDel(SegGrey(seg), trace)); + SegSetWhite(seg, TraceSetDel(SegWhite(seg), trace)); + } + } while (SegNext(&seg, arena, seg)); + } + rootsStepClosureFinish(rsc); /* Make this trace look like any other finished trace. */ trace->state = TraceFINISHED; diff --git a/mps/design/prot.txt b/mps/design/prot.txt index a56e53db096..0d30017fede 100644 --- a/mps/design/prot.txt +++ b/mps/design/prot.txt @@ -47,10 +47,6 @@ forbidden. A request to forbid read accesses (that is, ``AccessREAD`` is set) may also forbid write accesses, but read accesses will not be forbidden unless ``AccessREAD`` is set. -``void ProtTramp(void **resultReturn, void *(*f)(void *, size_t), void *p, size_t s)`` - -_`.if.tramp`: [undocumented] - ``void ProtSync(Space space)`` _`.if.sync`: ``ProtSync()`` is called to ensure that the actual diff --git a/mps/design/protli.txt b/mps/design/protli.txt index a5cc1d57a15..9b0958ac191 100644 --- a/mps/design/protli.txt +++ b/mps/design/protli.txt @@ -88,11 +88,6 @@ underlying object). _`.fun.sync`: ``ProtSync()`` does nothing in this implementation as ``ProtSet()`` sets the protection without any delay. -_`.fun.tramp`: The protection trampoline, ``ProtTramp()``, is trivial -under Linux, as there is nothing that needs to be done in the dynamic -context of the mutator in order to catch faults. (Contrast this with -Win32 Structured Exception Handling.) - Threads ------- diff --git a/mps/design/protsu.txt b/mps/design/protsu.txt index 616d9134c88..1bd57e1004a 100644 --- a/mps/design/protsu.txt +++ b/mps/design/protsu.txt @@ -110,11 +110,6 @@ access that is compatible with the access of the underlying object). _`.fun.sync`: ``ProtSync()``. This does nothing in this implementation as ProtSet sets the protection without any delay. -_`.fun.tramp`: ``ProtTramp()``. The protection trampoline is trivial -under SunOS, as there is nothing that needs to be done in the dynamic -context of the mutator in order to catch faults. (Contrast this with -Win32 Structured Exception Handling.) - Document History ---------------- diff --git a/mps/design/range.txt b/mps/design/range.txt index bf0f924430e..1b42d69ac22 100644 --- a/mps/design/range.txt +++ b/mps/design/range.txt @@ -51,7 +51,7 @@ between ``base`` (inclusive) and ``limit`` (exclusive). It must be the case that ``base <= limit``. If ``base == limit`` then the range is empty. -``void RangeInitCopy(Range dest, Range src)`` +``void RangeCopy(Range dest, Range src)`` Initialize ``dest`` to be a copy of ``src``. diff --git a/mps/design/type.txt b/mps/design/type.txt index 6ce0da3c035..baee04ef3d2 100644 --- a/mps/design/type.txt +++ b/mps/design/type.txt @@ -151,6 +151,14 @@ compared ``w3i3mv\hi\amcss.exe`` running with and without the macro for ``BoolCheck`` on the PC Aaron. "With" ran in 97.7% of the time (averaged over 3 runs). +_`.bool.bitfield`: When a Boolean needs to be stored in a bitfield, +the type of the bitfield must be ``unsigned:1``, not ``Bool:1``. +(That's because the two values of the type ``Bool:1`` are ``0`` and +``-1``, which means that assigning ``TRUE`` would require a sign +conversion.) To avoid warnings about loss of data from GCC with the +``-Wconversion`` option, ``misc.h`` provides the ``BOOLOF`` macro for +coercing a value to an unsigned single-bit field. + ``typedef unsigned BufferMode`` @@ -235,7 +243,8 @@ objects. ``typedef Size Epoch`` _`.epoch`: An ``Epoch`` is a count of the number of flips that have -occurred. It is used in the implementation of location dependencies. +occurred, in which objects may have moved. It is used in the +implementation of location dependencies. ``Epoch`` is converted to ``mps_word_t`` in the MPS C Interface, as a field of ``mps_ld_s``. @@ -351,7 +360,7 @@ references must be scanned in order to respect the properties of references of the ranks. Therefore they are declared explicitly with their integer values. -.. note:: Could ``Rank`` be a ``short``? +.. note:: Could ``Rank`` be an ``unsigned short`` or ``unsigned char``? .. note:: @@ -459,30 +468,35 @@ create a valid registered constant root that contains any references. become invalid; but you can't add them afterwards because the root is supposed to be constant.) -_`.rootmode.conv.c`: ``RootMode`` is converted to ``mps_rm_t`` in the MPS C -Interface. +_`.rootmode.conv.c`: ``RootMode`` is converted to ``mps_rm_t`` in the +MPS C Interface. ``typedef int RootVar`` -_`.rootvar`: The type ``RootVar`` is the type of the -discriminator for the union within ``RootStruct``. +_`.rootvar`: The type ``RootVar`` is the type of the discriminator for +the union within ``RootStruct``. ``typedef int SegPrefKind`` -_`.segprefkind`: The type ``SegPrefKind`` expresses a preference about -where the arena should place a segment. It takes one of the following +_`.segprefkind`: The type ``SegPrefKind`` expresses a preference for +addresses within an address space. It takes one of the following values: ================== ==================================================== Kind Description ================== ==================================================== -``SegPrefHigh`` Place the segment high in the address space. -``SegPrefLow`` Place the segment low in the address space. -``SegPrefZoneSet`` Place the segment in specified zones. +``SegPrefHigh`` Prefer high addresses. +``SegPrefLow`` Prefer low addresses. +``SegPrefZoneSet`` Prefer addresses in specified zones. ================== ==================================================== +.. note:: + + The name is misleading as this is used to refer to address + preferences in general, not just addresses of segments. + ``typedef unsigned Serial`` @@ -490,9 +504,8 @@ _`.serial`: A ``Serial`` is a number which is assigned to a structure when it is initialized. The serial number is taken from a field in the parent structure, which is incremented. Thus, every instance of a structure has a unique "name" which is a path of structures from the -global root. For example:: - - space[3].pool[5].buffer[2] +global root. For example, "the third arena's fifth pool's second +buffer". Why? Consistency checking, debugging, and logging. Not well thought out. @@ -509,6 +522,8 @@ right-hand operand of the ``<<`` or ``>>`` operators) is intended, to make the code clear. It should also be used for structure fields which have this use. +.. note:: Could ``Shift`` be an ``unsigned short`` or ``unsigned char``? + ``typedef unsigned long Sig`` @@ -640,7 +655,7 @@ _`.word.ops`: ``WordIsAligned()``, ``WordAlignUp()``, ``typedef Word ZoneSet`` _`.zoneset`: ``ZoneSet`` is a conservative approximation to a set of -zone. See design.mps.refset_. +zones. See design.mps.refset_. Abstract types diff --git a/mps/manual/source/design/.p4ignore b/mps/manual/source/design/.p4ignore index 0e9be69cee0..2457ad19192 100644 --- a/mps/manual/source/design/.p4ignore +++ b/mps/manual/source/design/.p4ignore @@ -1,5 +1,6 @@ # The files in this directory are generated by the "mps" extension to Sphinx, # except the index and the "old designs" index. *.rst +*.svg !index.rst !old.rst diff --git a/mps/manual/source/extensions/mps/designs.py b/mps/manual/source/extensions/mps/designs.py index 57959f1ea0c..230594e9392 100644 --- a/mps/manual/source/extensions/mps/designs.py +++ b/mps/manual/source/extensions/mps/designs.py @@ -22,10 +22,11 @@ TYPES = ''' Arena Attr Bool BootBlock BT Buffer BufferMode Byte Chain Chunk Clock Compare Count Epoch FindDelete Format FrameState Fun Globals Index Land LD Lock Message MessageType MutatorFaultContext Page - Pointer Pool PThreadext Range Rank RankSet Ref Res Reservoir Ring - Root RootMode RootVar ScanState Seg SegBuf SegPref SegPrefKind - Serial Shift Sig Size Space SplayNode SplayTree StackContext - Thread Trace TraceId TraceSet ULongest VM Word ZoneSet + Pointer Pool PThreadext Range Rank RankSet Ref RefSet Res + Reservoir Ring Root RootMode RootVar ScanState Seg SegBuf SegPref + SegPrefKind Serial Shift Sig Size Space SplayNode SplayTree + StackContext Thread Trace TraceId TraceSet TraceStartWhy + TraceState ULongest VM Word ZoneSet ''' diff --git a/mps/manual/source/pool/awl.rst b/mps/manual/source/pool/awl.rst index 4128339227a..094335ba3b8 100644 --- a/mps/manual/source/pool/awl.rst +++ b/mps/manual/source/pool/awl.rst @@ -282,11 +282,10 @@ the format of objects allocated in it: that it does not look like an aligned pointer. "Aligned pointer" means a word whose numeric value (that is, its - value when treated as an unsigned integer) is a multiple of the - architecture's :term:`natural alignment` (see - :c:macro:`MPS_PF_ALIGN`). If you're using a 32-bit architecture, - that means that an aligned pointer is a multiple of 4 and its bottom - two bits are both zero. + value when treated as an unsigned integer) is a multiple of the size + of a pointer. If you're using a 64-bit architecture, that means that + an aligned pointer is a multiple of 8 and its bottom three bits are + zero. The bottom line is that references from an object in an AWL pool must be untagged and aligned, and integers must be tagged with a diff --git a/mps/manual/source/pool/mv.rst b/mps/manual/source/pool/mv.rst index 433525f7362..361cd41c0b5 100644 --- a/mps/manual/source/pool/mv.rst +++ b/mps/manual/source/pool/mv.rst @@ -75,17 +75,19 @@ MV interface When creating an MV pool, :c:func:`mps_pool_create_k` may take three :term:`keyword arguments`: - * :c:macro:`MPS_KEY_EXTEND_BY` (type :c:type:`size_t`, default 65536) is the - :term:`size` of segment that the pool will request from the - :term:`arena`. + * :c:macro:`MPS_KEY_EXTEND_BY` (type :c:type:`size_t`, + default 65536) is the :term:`size` of segment that the pool will + request from the :term:`arena`. - * :c:macro:`MPS_KEY_MEAN_SIZE` (type :c:type:`size_t`, default 32) is the - predicted mean size of blocks that will be allocated from the - pool. + * :c:macro:`MPS_KEY_MEAN_SIZE` (type :c:type:`size_t`, default 32) + is the predicted mean size of blocks that will be allocated from + the pool. This value must be smaller than, or equal to, the + value for :c:macro:`MPS_KEY_EXTEND_BY`. - * :c:macro:`MPS_KEY_MAX_SIZE` (type :c:type:`size_t`, default 65536) is the - predicted maximum size of blocks that will be allocated from the - pool. + * :c:macro:`MPS_KEY_MAX_SIZE` (type :c:type:`size_t`, + default 65536) is the predicted maximum size of blocks that will + be allocated from the pool. This value must be larger than, or + equal to, the value for :c:macro:`MPS_KEY_EXTEND_BY`. The mean and maximum sizes are *hints* to the MPS: the pool will be less efficient if these are wrong, but nothing will break. diff --git a/mps/manual/source/release.rst b/mps/manual/source/release.rst index 294b6545b91..18cc8ed72e9 100644 --- a/mps/manual/source/release.rst +++ b/mps/manual/source/release.rst @@ -27,6 +27,45 @@ New features calling :c:func:`mps_pool_create_k`. +Interface changes +................. + +#. There is now a default value (currently 1 \ :term:`megabyte`) for + the :c:macro:`MPS_KEY_ARENA_SIZE` keyword argument to + :c:func:`mps_arena_create_k` when creating a virtual memory arena. + See :c:func:`mps_arena_class_vm`. + + +Other changes +............. + +#. The :ref:`pool-ams` pool class no longer triggers the assertion + ``!AMS_IS_INVALID_COLOUR(seg, i)`` under rare circumstances + (namely, detaching an :term:`allocation point` from a :term:`grey` + segment when :c:macro:`MPS_KEY_AMS_SUPPORT_AMBIGUOUS` is + ``FALSE``). See job001549_. + + .. _job001549: https://www.ravenbrook.com/project/mps/issue/job001549/ + +#. :c:func:`mps_arena_roots_walk` no longer triggers an assertion + failure when run twice in succession. See job003496_. + + .. _job003496: https://www.ravenbrook.com/project/mps/issue/job003496/ + +#. The alignment of :ref:`pool-awl` pools is now configurable via the + object format, as documented, and is no longer always + :c:macro:`MPS_PF_ALIGN`. See job003745_. + + .. _job003745: https://www.ravenbrook.com/project/mps/issue/job003745/ + +#. :program:`mpseventtxt` now successfully processes a telemetry log + containing multiple labels associated with the same address. See + job003756_. + + .. _job003756: https://www.ravenbrook.com/project/mps/issue/job003756/ + + + .. _release-notes-1.113: Release 1.113.0 diff --git a/mps/manual/source/topic/arena.rst b/mps/manual/source/topic/arena.rst index 5220ece4007..1925117cc84 100644 --- a/mps/manual/source/topic/arena.rst +++ b/mps/manual/source/topic/arena.rst @@ -233,18 +233,18 @@ Virtual memory arenas more efficient. When creating a virtual memory arena, :c:func:`mps_arena_create_k` - requires one :term:`keyword argument`: + accepts one :term:`keyword argument` on all platforms: - * :c:macro:`MPS_KEY_ARENA_SIZE` (type :c:type:`size_t`). is the - initial amount of virtual address space, in :term:`bytes (1)`, - that the arena will reserve (this space is initially reserved so - that the arena can subsequently use it without interference from - other parts of the program, but most of it is not committed, so - it doesn't require any RAM or backing store). The arena may - allocate more virtual address space beyond this initial - reservation as and when it deems it necessary. The MPS is most - efficient if you reserve an address space that is several times - larger than your peak memory usage. + * :c:macro:`MPS_KEY_ARENA_SIZE` (type :c:type:`size_t`, default + 2\ :superscript:`20`) is the initial amount of virtual address + space, in :term:`bytes (1)`, that the arena will reserve (this + space is initially reserved so that the arena can subsequently + use it without interference from other parts of the program, but + most of it is not committed, so it doesn't require any RAM or + backing store). The arena may allocate more virtual address + space beyond this initial reservation as and when it deems it + necessary. The MPS is most efficient if you reserve an address + space that is several times larger than your peak memory usage. .. note:: @@ -252,8 +252,8 @@ Virtual memory arenas more times it has to extend its address space, the less efficient garbage collection will become. - An optional :term:`keyword argument` may be passed, but is - only used on the Windows operating system: + A second optional :term:`keyword argument` may be passed, but it + only has any effect on the Windows operating system: * :c:macro:`MPS_KEY_VMW3_TOP_DOWN` (type :c:type:`mps_bool_t`). If true, the arena will allocate address space starting at the @@ -273,8 +273,9 @@ Virtual memory arenas If the MPS fails to allocate memory for the internal arena structures, :c:func:`mps_arena_create_k` returns - :c:macro:`MPS_RES_MEMORY`. Either ``size`` was far too small or - the operating system refused to provide enough memory. + :c:macro:`MPS_RES_MEMORY`. Either :c:macro:`MPS_KEY_ARENA_SIZE` + was far too small or the operating system refused to provide + enough memory. For example:: diff --git a/mps/manual/source/topic/error.rst b/mps/manual/source/topic/error.rst index 97ec2f73216..6b2e9452fc2 100644 --- a/mps/manual/source/topic/error.rst +++ b/mps/manual/source/topic/error.rst @@ -232,7 +232,7 @@ assertion that is listed here but for which you discovered a different cause), please :ref:`let us know ` so that we can improve this documentation. -``arenavm.c: BTIsResRange(vmChunk->pageTableMapped, 0, chunk->pageTablePages)`` +``sa.c: BTIsResRange(sa->mapped, 0, sa->length)`` The client program called :c:func:`mps_arena_destroy` without having destroyed all pools in that arena first. (The assertion is @@ -283,14 +283,6 @@ this documentation. point` instead. -``poolams.c: !AMS_IS_INVALID_COLOUR(seg, i)`` - - The client program failed to :term:`fix` a reference to an object - in an :ref:`pool-ams` pool, violating the :term:`tri-colour - invariant` that the MPS depends on for the correctness of its - :term:`incremental garbage collection`. - - ``poolams.c: AMS_ALLOCED(seg, i)`` The client program tried to :term:`fix` a :term:`reference` to a diff --git a/mps/manual/source/topic/format.rst b/mps/manual/source/topic/format.rst index 91ac7ae6462..b9bbdae61bb 100644 --- a/mps/manual/source/topic/format.rst +++ b/mps/manual/source/topic/format.rst @@ -468,22 +468,27 @@ Object format introspection Each :term:`pool class` determines for which objects the stepper function is called. Typically, all validly formatted objects are - visited. During a :term:`trace` this will in general be only the - :term:`black` objects, though the :ref:`pool-lo` pool, for - example, will walk all objects since they are validly formatted - whether they are black or :term:`white`. :term:`Padding objects` - may be visited at the pool class's discretion: the :term:`client - program` should handle this case. - - .. seealso:: - - :ref:`topic-arena`. + visited. :term:`Padding objects` may be visited at the pool + class's discretion: the stepper function must handle this + case. .. note:: This function is intended for heap analysis, tuning, and debugging, not for frequent use in production. + .. warning:: + + If a garbage collection is currently in progress (that is, if + the arena is in the :term:`clamped ` or + :term:`unclamped state`), then only objects that are known to + be currently valid are visited. + + For the most reliable results, ensure the arena is in the + :term:`parked state` by calling :c:func:`mps_arena_park` + before calling this function (and release it by calling + :c:func:`mps_arena_release` afterwards, if desired). + .. c:type:: void (*mps_formatted_objects_stepper_t)(mps_addr_t addr, mps_fmt_t fmt, mps_pool_t pool, void *p, size_t s) @@ -515,10 +520,6 @@ Object format introspection It must not access other memory managed by the MPS. - .. seealso:: - - :ref:`topic-arena`. - Obsolete interface ------------------ diff --git a/mps/manual/source/topic/telemetry.rst b/mps/manual/source/topic/telemetry.rst index f33de3516ce..a665aaf4b07 100644 --- a/mps/manual/source/topic/telemetry.rst +++ b/mps/manual/source/topic/telemetry.rst @@ -110,7 +110,7 @@ The MPS writes the telemetry to the log in an encoded form for speed. It can be decoded using the :ref:`mpseventcnv ` and :ref:`mpseventtxt ` programs:: - (gdb) shell mpseventcnv | mpseventtxt | sort > mpsio.txt + (gdb) shell mpseventcnv | sort | mpseventtxt > mpsio.txt The ``sort`` is useful because the events are not necessarily written to the telemetry file in time order, but each event starts with a diff --git a/mps/procedure/release-build.rst b/mps/procedure/release-build.rst index bdce60df4ca..ee65b5f645d 100644 --- a/mps/procedure/release-build.rst +++ b/mps/procedure/release-build.rst @@ -47,7 +47,7 @@ All relative paths are relative to 1.111.0), where *VERSION* is the number of the version you’re releasing, and *N* is the first unused release number (starting at zero). Look in the index of releases (``release/index.html``) for - existing release numbers for your version:: + existing release numbers for your version. :: VERSION=A.BBB RELEASE=$VERSION.N @@ -111,7 +111,7 @@ Run the script ``tool/release``, passing the options: * ``-P mps`` — project name * ``-b BRANCH`` — branch to make the release from: for example ``version/1.113`` * ``-C CHANGELEVEL`` — changelevel at which to make the release -* ``-d "DESCRIPTION"`` — changelevel at which to make the release +* ``-d "DESCRIPTION"`` — description of the release * ``-y`` — yes, really make the release If omitted, the project and branch are deduced from the current @@ -231,8 +231,6 @@ A. References Ravenbrook Limited; 2008-10-16; http://info.ravenbrook.com/mail/2008/10/16/13-08-20/0.txt -.. [Sphinx] "Sphinx: Python document generator"; http://sphinx-doc.org/ - B. Document History ------------------- diff --git a/mps/test/function/122.c b/mps/test/function/122.c index 6f1465289cb..4ba0c0c6090 100644 --- a/mps/test/function/122.c +++ b/mps/test/function/122.c @@ -133,7 +133,7 @@ static void test(void) die(allocrdumb(&a[0], aplo, 64, mps_rank_exact()), "alloc"); die(allocrdumb(&a[1], apamc, 64, mps_rank_exact()), "alloc"); die(allocrdumb(&a[3], apawl, 64, mps_rank_exact()), "alloc"); - a[2] = (mycell *)((int)a[3] | 4); + a[2] = (mycell *)((mps_word_t)a[3] | 4); die(allocrdumb(&b[0], aplo, 64, mps_rank_exact()), "alloc"); die(allocrdumb(&b[1], apamc, 64, mps_rank_exact()), "alloc"); diff --git a/mps/test/function/40.c b/mps/test/function/40.c index c57f8c62ba5..5257e1fcfc1 100644 --- a/mps/test/function/40.c +++ b/mps/test/function/40.c @@ -66,7 +66,7 @@ static void test(void) comment("%i of 10.", i); UC; z[i] = allocone(ap, 1, 1); - if (i % 8 == 0) { z[i] = (mycell *) ((int)z[i] + 4); } /* error to scan this! */ + if (i % 8 == 0) { z[i] = (mycell *) ((mps_word_t)z[i] + 4); } /* error to scan this! */ } for (i=0; i<1000; i++) { diff --git a/mps/test/testsets/passing b/mps/test/testsets/passing index 64c72fe2982..fc00f37feb0 100644 --- a/mps/test/testsets/passing +++ b/mps/test/testsets/passing @@ -110,7 +110,7 @@ function/118.c function/119.c function/120.c % function/121.c -- job003495 -% function/122.c -- job003496 +function/122.c function/123.c function/124.c function/125.c diff --git a/mps/tool/branch b/mps/tool/branch index f60eccae235..5a3b4d8652d 100755 --- a/mps/tool/branch +++ b/mps/tool/branch @@ -46,10 +46,10 @@ CHILD_RE = r'(?:{}|{})$'.format(TASK_BRANCH_RE, VERSION_BRANCH_RE) TASK_BRANCH_ENTRY = ''' - {child} + {date}/{task} Changes - {description} - In development (diffs). + {desc_html} + Active (diffs). ''' @@ -60,7 +60,7 @@ VERSION_BRANCH_ENTRY = ''' None. {parent}/...@{changelevel} - {description} + {desc_html} base
@@ -154,13 +154,16 @@ def main(argv): m = re.match(CHILD_RE, args.child) if not m: raise Error(fmt("Invalid child: {child}")) - if args.customer != m.group(3): + if not args.task and args.customer != m.group(3): raise Error(fmt("Customer mismatch between {parent} and {child}.")) args.date, args.task, _, args.version = m.groups() if not args.description: args.description = fmt("Branching {parent} to {child}.") print(fmt("description={description}")) + args.desc_html = re.sub(r'\b(job\d{6})\b', + fmt(r'\1'), + args.description) # Create the branch specification args.branch = fmt('{project}/{child}') diff --git a/mps/tool/p4-bisect b/mps/tool/p4-bisect new file mode 100755 index 00000000000..2886d823c59 --- /dev/null +++ b/mps/tool/p4-bisect @@ -0,0 +1,176 @@ +#!/usr/bin/env python +# +# +# Ravenbrook +# +# +# P4-BISECT -- FIND CHANGE THAT INTRODUCED A BUG +# +# Gareth Rees, Ravenbrook Limited, 2014-04-14 +# +# +# 1. INTRODUCTION +# +# This script automates (or partly automates) the process of finding, +# by binary search, the change that introduced a bug. +# +# The interface is modelled closely on git-bisect(1). + +import argparse +from functools import partial +import json +from os import unlink +import p4 +import subprocess +import sys + +BISECT_FILE = '.p4-bisect' + +def error(msg): + sys.stderr.write(msg) + sys.stderr.write('\n') + exit(1) + +def sync(*filespecs): + try: + p4.do('sync', *filespecs) + except p4.Error as e: + if 'file(s) up-to-date' not in e.args[0]: + raise + +class State(object): + def __init__(self, **d): + self.filespec = d['filespec'] + self.changes = d['changes'] + if 'current' in d: + self.current = d['current'] + + @classmethod + def load(cls): + try: + with open(BISECT_FILE, 'r') as f: + return cls(**json.load(f)) + except FileNotFoundError: + error("p4-bisect not in progress here.") + + def save(self): + with open(BISECT_FILE, 'w') as f: + json.dump(vars(self), f) + + def update(self): + n = len(self.changes) + if n == 0: + print("no changes remaining.".format(**vars(self))) + elif n == 1: + print("{} change remaining: {}.".format(n, self.changes[0])) + elif n == 2: + print("{} changes remaining: [{}, {}]." + .format(n, self.changes[0], self.changes[-1])) + else: + print("{} changes remaining: [{}, ..., {}]." + .format(n, self.changes[0], self.changes[-1])) + if n > 0: + self.current = self.changes[n // 2] + print("Syncing to changelevel {current}.".format(**vars(self))) + sync(*['{}@{}'.format(f, self.current) for f in self.filespec]) + self.save() + +def help(parser, args): + parser.print_help() + +def start(args): + args.filespec = args.filespec or ['...'] + changes = sorted(int(c['change']) for c in p4.run('changes', *args.filespec)) + if not changes: + error("No changes for {}".format(' '.join(args.filespec))) + if args.good is None: + args.good = changes[0] + if args.bad is None: + args.bad = changes[-1] + state = State(filespec=args.filespec, + changes=[c for c in changes if args.good <= c <= args.bad]) + state.update() + +def good(args): + state = State.load() + print("Change {current} good.".format(**vars(state))) + state.changes = [c for c in state.changes if c > state.current] + state.update() + +def bad(args): + state = State.load() + print("Change {current} bad.".format(**vars(state))) + state.changes = [c for c in state.changes if c < state.current] + state.update() + +def skip(args): + state = State.load() + print("Skipping change {current}.".format(**vars(state))) + state.changes.remove(state.current) + state.update() + +def reset(args): + state = State.load() + sync(*state.filespec) + unlink(BISECT_FILE) + +def run(args): + while True: + state = State.load() + if not state.changes: + break + result = subprocess.call([args.cmd] + args.args) + if result == 0: + good(None) + elif result == 125: + skip(None) + elif 0 < result < 128: + bad(None) + else: + exit(result) + +def main(argv): + parser = argparse.ArgumentParser( + prog='p4-bisect', epilog='For help on CMD, use p4-bisect CMD -h') + parser.set_defaults(func=partial(help, parser)) + subparsers = parser.add_subparsers() + a = subparsers.add_parser + + help_parser = a('help', help='show this help message') + help_parser.set_defaults(func=partial(help, parser)) + + start_parser = a('start', help='start a p4-bisect session') + aa = start_parser.add_argument + start_parser.add_argument('-f', '--filespec', action='append', + help='filespec(s) to search') + start_parser.add_argument('good', nargs='?', type=int, + help='known good changelevel') + start_parser.add_argument('bad', nargs='?', type=int, + help='known bad changelevel') + start_parser.set_defaults(func=start) + + good_parser = a('good', help='declare current revision good') + good_parser.set_defaults(func=good) + + bad_parser = a('bad', help='declare current revision bad') + bad_parser.set_defaults(func=bad) + + skip_parser = a('skip', help='skip current revision') + skip_parser.set_defaults(func=skip) + + reset_parser = a('reset', help='finish p4-bisect session') + reset_parser.set_defaults(func=reset) + + run_parser = a('run', help='run p4-bisect session automatically') + run_parser.add_argument('cmd', + help='command that determines if current ' + 'changelevel is good or bad') + run_parser.add_argument('args', nargs=argparse.REMAINDER, + help='arguments to pass to cmd') + run_parser.set_defaults(func=run) + + args = parser.parse_args(argv[1:]) + args.func(args) + +if __name__ == '__main__': + main(sys.argv) diff --git a/mps/tool/testcases.txt b/mps/tool/testcases.txt index ba49ae6f717..0c536f0a336 100644 --- a/mps/tool/testcases.txt +++ b/mps/tool/testcases.txt @@ -5,9 +5,9 @@ abqtest airtest amcss =P amcsshe =P -amcssth =B =P =T job003561, job003703 -amsss =B job001549 -amssshe =B job001549 +amcssth =P =T +amsss =P +amssshe =P apss arenacv awlut @@ -20,7 +20,7 @@ exposet0 =P expt825 fbmtest finalcv =P -finaltest +finaltest =P fotest gcbench =N benchmark locbwcss diff --git a/mps/tool/testopendylan b/mps/tool/testopendylan index 9c18938ef1e..0c185ff2fd0 100755 --- a/mps/tool/testopendylan +++ b/mps/tool/testopendylan @@ -20,8 +20,21 @@ # 2. CONFIGURATION -# MPS sources we are testing against -MPS=$(cd -- "$(dirname "$0")/.." && pwd) +# Check command-line argument +GC=$1 +case "$GC" in + mps) + # MPS sources we are testing against + CONFIGURE=--with-gc-path=$(cd -- "$(dirname "$0")/.." && pwd) + ;; + boehm) + CONFIGURE= + ;; + *) + echo "Backend '$GC' not supported: choose mps or boehm." + exit 1 +esac + # OpenDylan version for bootstrapping VERSION=2013.2 @@ -30,7 +43,7 @@ VERSION=2013.2 REMOTE=https://github.com/dylan-lang/opendylan.git # Directory to put everything in -TESTDIR="$PWD/.test" +TESTDIR="$PWD/.test/$GC" mkdir -p -- "$TESTDIR" cd -- "$TESTDIR" @@ -104,7 +117,7 @@ if [ -f "$REPO/Makefile" ]; then else ( cd -- "$REPO" && ./autogen.sh && - ./configure --with-mps="$MPS" --prefix="$PREFIX" + ./configure --with-gc="$GC" --prefix="$PREFIX" $CONFIGURE ) fi ( cd -- "$REPO" && @@ -128,6 +141,9 @@ else ( # # 2014-03-20 GDR Created based on [WELCOME]. # +# 2014-04-14 GDR Updated configure args based on revised build +# instructions [WELCOME]. +# # # C. COPYRIGHT AND LICENCE #