From b745b69d785507c6f4cd26570c27716ccc4679fb Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Fri, 14 Apr 2023 09:43:23 +0100 Subject: [PATCH 01/23] Part of making transforms part of the public MPS. (cherry picked from commit 6bd64a230a510bc19323da127d622a19bfd43f63) --- code/comm.gmk | 11 + code/commpost.nmk | 3 - code/commpre.nmk | 8 + code/mps.c | 5 + code/mps.xcodeproj/project.pbxproj | 124 ++ code/mpsitr.c | 135 +++ code/mpstr.h | 66 ++ code/testlib.h | 2 + code/trans.c | 397 +++++++ code/trans.h | 75 ++ code/ztfm.c | 1465 ++++++++++++++++++++++++ design/transform.txt | 126 ++ manual/source/custom/cet/index.rst | 9 + manual/source/custom/cet/transform.rst | 132 +++ manual/source/design/index.rst | 1 + manual/source/index.rst | 1 + tool/testcases.txt | 1 + 17 files changed, 2558 insertions(+), 3 deletions(-) create mode 100644 code/mpsitr.c create mode 100644 code/mpstr.h create mode 100644 code/trans.c create mode 100644 code/trans.h create mode 100644 code/ztfm.c create mode 100644 design/transform.txt create mode 100644 manual/source/custom/cet/index.rst create mode 100644 manual/source/custom/cet/transform.rst diff --git a/code/comm.gmk b/code/comm.gmk index a2cd68baab..ec8f93bd22 100644 --- a/code/comm.gmk +++ b/code/comm.gmk @@ -221,6 +221,10 @@ MPMCOMMON = \ POOLS = $(AMC) $(AMS) $(AWL) $(LO) $(MV2) $(MVFF) $(SNC) MPM = $(MPMCOMMON) $(MPMPF) $(POOLS) $(PLINTH) +# Custom CET extensions go here: + +MPM += mpsitr.c trans.c + # These map the source file lists onto object files and dependency files # in the platform/variety directory. @@ -301,6 +305,10 @@ TEST_TARGETS=\ UNBUILDABLE_TARGETS=\ replay # depends on the EPVM pool +# Add tests for the custom CET build here: + +TEST_TARGETS += ztfm + ALL_TARGETS=$(LIB_TARGETS) $(TEST_TARGETS) $(EXTRA_TARGETS) @@ -568,6 +576,9 @@ $(PFM)/$(VARIETY)/zcoll: $(PFM)/$(VARIETY)/zcoll.o \ $(PFM)/$(VARIETY)/zmess: $(PFM)/$(VARIETY)/zmess.o \ $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a +$(PFM)/$(VARIETY)/ztfm: $(PFM)/$(VARIETY)/ztfm.o \ + $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a + $(PFM)/$(VARIETY)/mpseventcnv: $(PFM)/$(VARIETY)/eventcnv.o \ $(PFM)/$(VARIETY)/mps.a diff --git a/code/commpost.nmk b/code/commpost.nmk index 470db2519b..c669f48996 100644 --- a/code/commpost.nmk +++ b/code/commpost.nmk @@ -219,9 +219,6 @@ $(PFM)\$(VARIETY)\btcv.exe: $(PFM)\$(VARIETY)\btcv.obj \ $(PFM)\$(VARIETY)\bttest.exe: $(PFM)\$(VARIETY)\bttest.obj \ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) -$(PFM)\$(VARIETY)\cvmicv.exe: $(PFM)\$(VARIETY)\cvmicv.obj \ - $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) - $(PFM)\$(VARIETY)\djbench.exe: $(PFM)\$(VARIETY)\djbench.obj \ $(TESTLIBOBJ) $(TESTTHROBJ) diff --git a/code/commpre.nmk b/code/commpre.nmk index 236e08ad2e..3ff41b1766 100644 --- a/code/commpre.nmk +++ b/code/commpre.nmk @@ -113,6 +113,10 @@ OPTIONAL_TARGETS=mpseventsql.exe UNBUILDABLE_TARGETS=replay.exe +# Add tests for the custom CET build here: + +TEST_TARGETS=$(TEST_TARGETS) ztfm.exe + ALL_TARGETS=$(LIB_TARGETS) $(TEST_TARGETS) $(EXTRA_TARGETS) @@ -189,6 +193,10 @@ TESTTHR = [testthrw3] POOLS = $(AMC) $(AMS) $(AWL) $(LO) $(MV2) $(MVFF) $(SNC) MPM = $(MPMCOMMON) $(MPMPF) $(POOLS) $(PLINTH) +# Custom CET extensions go here: + +MPMCOMMON = $(MPMCOMMON) [mpsitr] [trans] + # CHECK PARAMETERS # diff --git a/code/mps.c b/code/mps.c index 41c8d076af..5a761c23a7 100644 --- a/code/mps.c +++ b/code/mps.c @@ -81,6 +81,11 @@ #include "vm.c" #include "policy.c" +/* Configura CET customisations */ + +#include "trans.c" +#include "mpsitr.c" + /* Additional pool classes */ #include "poolamc.c" diff --git a/code/mps.xcodeproj/project.pbxproj b/code/mps.xcodeproj/project.pbxproj index 42f60ccb58..be24cfea23 100644 --- a/code/mps.xcodeproj/project.pbxproj +++ b/code/mps.xcodeproj/project.pbxproj @@ -120,6 +120,7 @@ 3114A6B9156E9763001E0AA3 /* PBXTargetDependency */, 31D60063156D3F5C00337B26 /* PBXTargetDependency */, 31D60087156D3FE600337B26 /* PBXTargetDependency */, + 220FD3F419533E8F00967A35 /* PBXTargetDependency */, 3114A6D5156E9839001E0AA3 /* PBXTargetDependency */, 2265D72220E54020003019E8 /* PBXTargetDependency */, 2D07B9791636FCBD00DB751B /* PBXTargetDependency */, @@ -131,6 +132,13 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 220FD3DD195339C000967A35 /* fmtdy.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAC6156BE48D00753214 /* fmtdy.c */; }; + 220FD3DE195339C000967A35 /* fmtdytst.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAC7156BE48D00753214 /* fmtdytst.c */; }; + 220FD3DF195339C000967A35 /* fmthe.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAE4156BE6D500753214 /* fmthe.c */; }; + 220FD3E1195339C000967A35 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; }; + 220FD3E3195339C000967A35 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; + 220FD3F119533E7200967A35 /* fmtno.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CACC156BE4C200753214 /* fmtno.c */; }; + 220FD3F219533E7900967A35 /* ztfm.c in Sources */ = {isa = PBXBuildFile; fileRef = 220FD3F019533C3200967A35 /* ztfm.c */; }; 2215A9C9192A495F00E9E2CE /* pooln.c in Sources */ = {isa = PBXBuildFile; fileRef = 22FACEDE18880933000FDBC1 /* pooln.c */; }; 2231BB5118CA97D8002D6322 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; }; 2231BB5318CA97D8002D6322 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; @@ -349,6 +357,20 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 220FD3DA195339C000967A35 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 31EEABFA156AAF9D00714D05; + remoteInfo = mps; + }; + 220FD3F319533E8F00967A35 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 220FD3D8195339C000967A35; + remoteInfo = ztfm; + }; 2215A9AB192A47BB00E9E2CE /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; @@ -1038,6 +1060,15 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + 220FD3E4195339C000967A35 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; 2231BB5418CA97D8002D6322 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -1493,6 +1524,12 @@ /* Begin PBXFileReference section */ 2213454C1DB0386600E14202 /* prmc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = prmc.h; sourceTree = ""; }; 2213454D1DB038D400E14202 /* prmcxc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = prmcxc.c; sourceTree = ""; }; + 220FD3E9195339C000967A35 /* ztfm */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ztfm; sourceTree = BUILT_PRODUCTS_DIR; }; + 220FD3EA195339E500967A35 /* mpsitr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mpsitr.c; sourceTree = ""; }; + 220FD3EB195339F000967A35 /* trans.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = trans.c; sourceTree = ""; }; + 220FD3ED19533A8700967A35 /* mpscvm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mpscvm.h; sourceTree = ""; }; + 220FD3EE19533A8700967A35 /* trans.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = trans.h; sourceTree = ""; }; + 220FD3F019533C3200967A35 /* ztfm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ztfm.c; sourceTree = ""; }; 2231BB5918CA97D8002D6322 /* locbwcss */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = locbwcss; sourceTree = BUILT_PRODUCTS_DIR; }; 2231BB6718CA97DC002D6322 /* locusss */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = locusss; sourceTree = BUILT_PRODUCTS_DIR; }; 2231BB6818CA9834002D6322 /* locbwcss.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = locbwcss.c; sourceTree = ""; }; @@ -1808,6 +1845,14 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 220FD3E2195339C000967A35 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 220FD3E3195339C000967A35 /* libmps.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 2231BB5218CA97D8002D6322 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -2394,6 +2439,7 @@ 3114A6BA156E9768001E0AA3 /* walkt0.c */, 31D6005E156D3F4A00337B26 /* zcoll.c */, 31D6007B156D3FCC00337B26 /* zmess.c */, + 220FD3F019533C3200967A35 /* ztfm.c */, ); name = Tests; sourceTree = ""; @@ -2485,6 +2531,7 @@ 223E796519EAB00B00DC26A6 /* sncss */, 22EA3F4520D2B0D90065F5B6 /* forktest */, 2265D71D20E53F9C003019E8 /* mpseventpy */, + 220FD3E9195339C000967A35 /* ztfm */, ); name = Products; sourceTree = ""; @@ -2539,7 +2586,9 @@ 311F2F6517398B3B00C15B6A /* mpsacl.h */, 311F2F6617398B3B00C15B6A /* mpsavm.h */, 22FACEDB188808D5000FDBC1 /* mpscmfs.h */, + 220FD3ED19533A8700967A35 /* mpscvm.h */, 31EEABF5156AAF7C00714D05 /* mpsi.c */, + 220FD3EA195339E500967A35 /* mpsitr.c */, 311F2F6717398B3B00C15B6A /* mpsio.h */, 311F2F6817398B3B00C15B6A /* mpslib.h */, 311F2F6917398B3B00C15B6A /* mpstd.h */, @@ -2583,6 +2632,8 @@ 31EEAC1F156AB2B200714D05 /* traceanc.c */, 31EEAC0D156AB27B00714D05 /* tract.c */, 311F2F7A17398B8E00C15B6A /* tract.h */, + 220FD3EB195339F000967A35 /* trans.c */, + 220FD3EE19533A8700967A35 /* trans.h */, 310F5D7118B6675F007EFCBC /* tree.c */, 310F5D7218B6675F007EFCBC /* tree.h */, 31EEAC44156AB32500714D05 /* version.c */, @@ -2667,6 +2718,24 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + 220FD3D8195339C000967A35 /* ztfm */ = { + isa = PBXNativeTarget; + buildConfigurationList = 220FD3E5195339C000967A35 /* Build configuration list for PBXNativeTarget "ztfm" */; + buildPhases = ( + 220FD3DB195339C000967A35 /* Sources */, + 220FD3E2195339C000967A35 /* Frameworks */, + 220FD3E4195339C000967A35 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + 220FD3D9195339C000967A35 /* PBXTargetDependency */, + ); + name = ztfm; + productName = zmess; + productReference = 220FD3E9195339C000967A35 /* ztfm */; + productType = "com.apple.product-type.tool"; + }; 2231BB4C18CA97D8002D6322 /* locbwcss */ = { isa = PBXNativeTarget; buildConfigurationList = 2231BB5518CA97D8002D6322 /* Build configuration list for PBXNativeTarget "locbwcss" */; @@ -3610,6 +3679,7 @@ 3114A6AB156E9759001E0AA3 /* walkt0 */, 31D60053156D3F3500337B26 /* zcoll */, 31D60070156D3FBC00337B26 /* zmess */, + 220FD3D8195339C000967A35 /* ztfm */, 3114A6C5156E9815001E0AA3 /* mpseventcnv */, 2265D71120E53F9C003019E8 /* mpseventpy */, 2D07B9701636FC9900DB751B /* mpseventsql */, @@ -3695,6 +3765,19 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 220FD3DB195339C000967A35 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 220FD3F219533E7900967A35 /* ztfm.c in Sources */, + 220FD3DD195339C000967A35 /* fmtdy.c in Sources */, + 220FD3DE195339C000967A35 /* fmtdytst.c in Sources */, + 220FD3DF195339C000967A35 /* fmthe.c in Sources */, + 220FD3F119533E7200967A35 /* fmtno.c in Sources */, + 220FD3E1195339C000967A35 /* testlib.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 2231BB4F18CA97D8002D6322 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -4202,6 +4285,16 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 220FD3D9195339C000967A35 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 31EEABFA156AAF9D00714D05 /* mps */; + targetProxy = 220FD3DA195339C000967A35 /* PBXContainerItemProxy */; + }; + 220FD3F419533E8F00967A35 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 220FD3D8195339C000967A35 /* ztfm */; + targetProxy = 220FD3F319533E8F00967A35 /* PBXContainerItemProxy */; + }; 2215A9AA192A47BB00E9E2CE /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 3104AFF1156D37A0000A585A /* all */; @@ -4695,6 +4788,27 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + 220FD3E6195339C000967A35 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 220FD3E7195339C000967A35 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 220FD3E8195339C000967A35 /* RASH */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = RASH; + }; 2215A9AE192A47BB00E9E2CE /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -6103,6 +6217,16 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 220FD3E5195339C000967A35 /* Build configuration list for PBXNativeTarget "ztfm" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 220FD3E6195339C000967A35 /* Debug */, + 220FD3E7195339C000967A35 /* Release */, + 220FD3E8195339C000967A35 /* RASH */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 2215A9AD192A47BB00E9E2CE /* Build configuration list for PBXAggregateTarget "testci" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/code/mpsitr.c b/code/mpsitr.c new file mode 100644 index 0000000000..866731fd3d --- /dev/null +++ b/code/mpsitr.c @@ -0,0 +1,135 @@ +/* mpsitr.c: MEMORY POOL SYSTEM C INTERFACE LAYER TO TRANSFORMS + * + * $Id$ + * Copyright 2011 Ravenbrook Limited. See end of file for license. + * + * .purpose: This code bridges between the MPS interface to C to Transforms + * and the internal implementation of Transforms. It is + * analogous to the MPS C Interface Layer , but for the Transforms + * extension. + */ + +#include "mpm.h" +#include "mpstr.h" +#include "trans.h" +#include "ss.h" + + +SRCID(mpsitr, "$Id$"); + + +mps_res_t mps_transform_create(mps_transform_t *mps_transform_o, + mps_arena_t arena) +{ + Transform transform = NULL; + Res res; + + AVER(mps_transform_o != NULL); + + ArenaEnter(arena); + res = TransformCreate(&transform, arena); + ArenaLeave(arena); + if (res != ResOK) + return res; + + *mps_transform_o = (mps_transform_t)transform; + return MPS_RES_OK; +} + + +mps_res_t mps_transform_add_oldnew(mps_transform_t transform, + mps_addr_t *mps_old_list, + mps_addr_t *mps_new_list, + size_t mps_count) +{ + Ref *old_list = (Ref *)mps_old_list; + Ref *new_list = (Ref *)mps_new_list; + Count count = mps_count; + Arena arena; + Res res; + + AVER(mps_old_list != NULL); + AVER(mps_new_list != NULL); + /* count: cannot check */ + + arena = TransformArena(transform); + + ArenaEnter(arena); + res = TransformAddOldNew(transform, old_list, new_list, count); + ArenaLeave(arena); + + return res; +} + + +mps_res_t mps_transform_apply(mps_bool_t *applied_o, + mps_transform_t transform) +{ + Arena arena; + Res res; + + AVER(applied_o != NULL); + + arena = TransformArena(transform); + ArenaEnter(arena); + STACK_CONTEXT_BEGIN(arena) { + res = TransformApply(applied_o, transform); + } STACK_CONTEXT_END(arena); + ArenaLeave(arena); + + return res; +} + + +void mps_transform_destroy(mps_transform_t transform) +{ + Arena arena; + + arena = TransformArena(transform); + + ArenaEnter(arena); + TransformDestroy(transform); + ArenaLeave(arena); +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2011 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/code/mpstr.h b/code/mpstr.h new file mode 100644 index 0000000000..d718e4ed1e --- /dev/null +++ b/code/mpstr.h @@ -0,0 +1,66 @@ +/* mpstr.h: RAVENBROOK MEMORY POOL SYSTEM C INTERFACE TO TRANSFORMS + * + * $Id$ + * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * + * .readership: customers, MPS developers. + */ + +#ifndef mpstr_h +#define mpstr_h + +#include "mps.h" + +typedef struct mps_transform_s *mps_transform_t; + +extern mps_res_t mps_transform_create(mps_transform_t *, mps_arena_t); + +extern mps_res_t mps_transform_add_oldnew(mps_transform_t, mps_addr_t *, mps_addr_t *, size_t); + +extern mps_res_t mps_transform_apply(mps_bool_t *, mps_transform_t); + +extern void mps_transform_destroy(mps_transform_t); + +#endif /* mpstr_h */ + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2011 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/code/testlib.h b/code/testlib.h index f140b0a9e1..9a414f2b5d 100644 --- a/code/testlib.h +++ b/code/testlib.h @@ -114,6 +114,7 @@ #if defined(MPS_OS_W3) && defined(MPS_ARCH_I6) #define PRIuLONGEST "llu" +#define PRIdLONGEST "lld" #define SCNuLONGEST "llu" #define SCNXLONGEST "llX" #define PRIXLONGEST "llX" @@ -122,6 +123,7 @@ typedef long long longest_t; #define MPS_WORD_CONST(n) (n##ull) #else #define PRIuLONGEST "lu" +#define PRIdLONGEST "ld" #define SCNuLONGEST "lu" #define SCNXLONGEST "lX" #define PRIXLONGEST "lX" diff --git a/code/trans.c b/code/trans.c new file mode 100644 index 0000000000..565b15800d --- /dev/null +++ b/code/trans.c @@ -0,0 +1,397 @@ +/* trans.c: RAVENBROOK MEMORY POOL SYSTEM TRANSFORMS + * + * $Id$ + * Copyright 2011-2018 Ravenbrook Limited. See end of file for license. + * + * This code is specific to Configura. + * + * A transform is a special kind of garbage collection that replaces references + * to a set of objects. The transform is piggybacked onto a garbage + * collection by overriding the fix method for a trace. The mapping used to + * replace the references is built up in a hash table by the client. + * + * + * Rationale + * + * This design was arrived at after some pain. The MPS isn't really designed + * for this kind of thing, and the pools generally assume that they're doing + * a garbage collection when they're asked to condemn, scan, fix, and reclaim + * stuff. This makes it very hard to apply the transform without also doing + * a garbage collection. Changing this would require a significant reworking + * of the MPS to generalise its ideas, and would bloat the pool classes. + * + * + * Assumptions: + * + * - Single-threaded mutator. In fact this code might work if other mutator + * threads are running, since the shield ought to operate correctly. + * However, in this case there can only be one trace running so that the + * correct fix method is chosen by ScanStateInit. + */ + +#include "trans.h" +#include "table.h" + + +#define TransformSig ((Sig)0x51926A45) /* SIGnature TRANSform */ + +typedef struct mps_transform_s { + Sig sig; /* */ + Arena arena; /* owning arena */ + Table oldToNew; /* map to apply to refs */ + Epoch epoch; /* epoch in which transform was created */ + Bool aborted; /* no longer transforming, just GCing */ +} TransformStruct; + + +Bool TransformCheck(Transform transform) +{ + CHECKS(Transform, transform); + CHECKU(Arena, transform->arena); + /* .check.boot: avoid bootstrap problem in transformTableAlloc where + transformTableFree checks the transform while the table is being + destroyed */ + if (transform->oldToNew != NULL) + CHECKD(Table, transform->oldToNew); + CHECKL(BoolCheck(transform->aborted)); + CHECKL(transform->epoch <= ArenaEpoch(transform->arena)); + return TRUE; +} + + +/* Allocator functions for the Table oldToNew */ + +static void *transformTableAlloc(void *closure, size_t size) +{ + Transform transform = (Transform)closure; + Res res; + void *p; + + AVERT(Transform, transform); + + res = ControlAlloc(&p, transform->arena, size); + if (res != ResOK) + return NULL; + + return p; +} + +static void transformTableFree(void *closure, void *p, size_t size) +{ + Transform transform = (Transform)closure; + AVERT(Transform, transform); + ControlFree(transform->arena, p, size); +} + + +Res TransformCreate(Transform *transformReturn, Arena arena) +{ + Transform transform; + Res res; + void *p; + + AVER(transformReturn != NULL); + AVERT(Arena, arena); + + res = ControlAlloc(&p, arena, sizeof(TransformStruct)); + if (res != ResOK) + goto failAlloc; + transform = (Transform)p; + + transform->oldToNew = NULL; + transform->arena = arena; + transform->epoch = ArenaEpoch(arena); + transform->aborted = FALSE; + + transform->sig = TransformSig; + + AVERT(Transform, transform); + + res = TableCreate(&transform->oldToNew, + 0, /* don't grow table until TransformAddOldNew */ + transformTableAlloc, + transformTableFree, + transform, + 0, 1); /* these can't be old references */ + if (res != ResOK) + goto failTable; + + *transformReturn = transform; + return ResOK; + +failTable: + ControlFree(arena, transform, sizeof(TransformStruct)); +failAlloc: + return res; +} + + +void TransformDestroy(Transform transform) +{ + Arena arena; + Table oldToNew; + + AVERT(Transform, transform); + + /* TODO: Log some transform statistics. */ + + /* Workaround bootstrap problem, see .check.boot */ + oldToNew = transform->oldToNew; + transform->oldToNew = NULL; + TableDestroy(oldToNew); + + arena = TransformArena(transform); + transform->sig = SigInvalid; + ControlFree(arena, transform, sizeof(TransformStruct)); +} + + +/* TransformArena -- return transform's arena + * + * Must be thread-safe as it is called outside the arena lock. See + * + */ + +Arena TransformArena(Transform transform) +{ + Arena arena; + AVER(TESTT(Transform, transform)); + arena = transform->arena; + AVER(TESTT(Arena, arena)); + return arena; +} + + +Res TransformAddOldNew(Transform transform, + Ref old_list[], + Ref new_list[], + Count count) +{ + Res res; + Index i; + Count added = 0; + + AVERT(Transform, transform); + AVER(old_list != NULL); + AVER(new_list != NULL); + /* count: cannot check */ + + res = TableGrow(transform->oldToNew, count); + if (res != ResOK) + return res; + + for(i = 0; i < count; ++i) { + /* NOTE: If the mutator isn't adding references while the arena is parked, + we might need to access the client-provided lists, using ArenaRead. */ + if(old_list[i] == NULL) + continue; /* permitted, but no transform to do */ + if(old_list[i] == new_list[i]) + continue; /* ignore identity-transforms */ + + /* Old refs must be in managed memory. */ + { + Seg seg; + AVER(SegOfAddr(&seg, transform->arena, old_list[i])); + } + + res = TableDefine(transform->oldToNew, (Word)old_list[i], new_list[i]); + AVER(res != ResFAIL); /* It's a static error to add the same old twice. */ + if (res != ResOK) + return res; + + ++added; + } + + AVERT(Transform, transform); + + return ResOK; +} + + +/* TransformApply -- transform references on the heap */ + +static Res transformFix(Seg seg, ScanState ss, Ref *refIO) +{ + Ref ref; + Transform transform; + Res res; + + AVERT_CRITICAL(Seg, seg); + AVERT_CRITICAL(ScanState, ss); + AVER_CRITICAL(refIO != NULL); + + transform = ss->fixClosure; + AVERT_CRITICAL(Transform, transform); + + if (!transform->aborted) { + void *refNew; + + ref = *refIO; + + if (TableLookup(&refNew, transform->oldToNew, (Word)ref)) { + if (ss->rank == RankAMBIG) { + /* We rely on the fact that all ambiguous references are fixed before + any others, so no references will have transformed by the time we + abort. + NOTE: Configura CVM prints a message and exits if a transform + fails. See Configura's + //depot/project/cet/kernel/internal/version3.2/cvm/c2/updateHeap2.m#4 + line 133.*/ + transform->aborted = TRUE; + } else { + /* NOTE: We could fix refNew in the table before copying it, + since any summaries etc. collected in the scan state will still + apply when it's copied. That could save a few snap-outs. */ + *refIO = refNew; + } + } + } + + /* Now progress to a normal GC fix. */ + /* TODO: Make a clean interface to this kind of dynamic binding. */ + ss->fix = ss->arena->emergency ? SegFixEmergency : SegFix; + TRACE_SCAN_BEGIN(ss) { + res = TRACE_FIX12(ss, refIO); + } TRACE_SCAN_END(ss); + ss->fix = transformFix; + + return res; +} + + +static void transformCondemn(void *closure, Word old, void *value) +{ + Seg seg; + GenDesc gen; + Bool b; + Trace trace = closure; + + AVERT(Trace, trace); + UNUSED(value); + + /* Find segment containing old address. */ + b = SegOfAddr(&seg, trace->arena, (Ref)old); + AVER(b); /* old refs must be in managed memory, else client param error */ + + /* Condemn generation containing seg if not already condemned. */ + gen = PoolSegPoolGen(SegPool(seg), seg)->gen; + AVERT(GenDesc, gen); + if (RingIsSingle(&gen->trace[trace->ti].traceRing)) + GenDescStartTrace(gen, trace); +} + + +Res TransformApply(Bool *appliedReturn, Transform transform) +{ + Res res; + Arena arena; + Globals globals; + Trace trace; + double mortality; + + AVER(appliedReturn != NULL); + AVERT(Transform, transform); + + arena = TransformArena(transform); + + /* If there have been any flips since the transform was created, the old + and new pointers will be invalid, since they are not scanned as roots. + NOTE: Configura CVM parks the arena before adding references. See + //depot/project/cet/kernel/internal/version3.2/cvm/c2/updateHeap2.m#4 + line 114. */ + if (transform->epoch != ArenaEpoch(arena)) + return ResPARAM; + + globals = ArenaGlobals(arena); + AVERT(Globals, globals); + + ArenaPark(globals); + + res = TraceCreate(&trace, arena, TraceStartWhyEXTENSION); + AVER(res == ResOK); /* parking should make a trace available */ + if (res != ResOK) + return res; + + /* Condemn the generations containing the transform's old objects, + so that all references to them are scanned. */ + TraceCondemnStart(trace); + TableMap(transform->oldToNew, transformCondemn, trace); + res = TraceCondemnEnd(&mortality, trace); + if (res != ResOK) { + /* Nothing to transform. */ + TraceDestroyInit(trace); + goto done; + } + + trace->fix = transformFix; + trace->fixClosure = transform; + + res = TraceStart(trace, 1.0, 0.0); + AVER(res == ResOK); /* transformFix can't fail */ + + /* If transformFix during traceFlip found ambiguous references and + aborted the transform then the rest of the trace is just a normal GC. + Note that aborting a trace part-way through is pretty much impossible + without corrupting the mutator graph. We could safely + if (transform->aborted) { + trace->fix = PoolFix; + trace->fixClosure = NULL; + } + */ + + /* Force the trace to complete now. */ + ArenaPark(globals); + +done: + if (transform->aborted) { + *appliedReturn = FALSE; + } else { + *appliedReturn = TRUE; + /* I'm not sure why the interface is defined this way. RB 2012-08-03 */ + TransformDestroy(transform); + } + + return ResOK; +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2011-2018 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/code/trans.h b/code/trans.h new file mode 100644 index 0000000000..4a19f5fe92 --- /dev/null +++ b/code/trans.h @@ -0,0 +1,75 @@ +/* trans.h: RAVENBROOK MEMORY POOL SYSTEM TRANSFORMS + * + * $Id$ + * Copyright 2011 Ravenbrook Limited. See end of file for license. + */ + +#ifndef trans_h +#define trans_h + +#include "mpm.h" + + +typedef struct mps_transform_s *Transform; + +typedef struct OldNewStruct *OldNew; + +extern Res TransformCreate(Transform *transformReturn, Arena arena); + +extern Res TransformAddOldNew(Transform transform, + Ref old_list[], + Ref new_list[], + Count count); + +extern Res TransformApply(Bool *appliedReturn, Transform transform); + +extern void TransformDestroy(Transform transform); + +extern Bool TransformCheck(Transform transform); + +extern Arena TransformArena(Transform transform); + + +#endif /* trans_h */ + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2011 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/code/ztfm.c b/code/ztfm.c new file mode 100644 index 0000000000..8dc81a5be2 --- /dev/null +++ b/code/ztfm.c @@ -0,0 +1,1465 @@ +/* ztfm.c: Transform test + * + * $Id$ + * Copyright (c) 2010 Ravenbrook Limited. See end of file for license. + * Portions copyright (C) 2002 Global Graphics Software. + * + * OBJECTIVE + * + * + * DESIGN OVERVIEW + * + * CODE OVERVIEW + * + * DEPENDENCIES + * + * This test uses the dylan object format, but the reliance on this + * particular format is not great and could be removed. + * + * + * BUGS, FUTURE IMPROVEMENTS, ETC + * + * HISTORY + * + * This code was created by first copying . + */ + +#include "fmtdy.h" +#include "fmtdytst.h" +#include "mps.h" +#include "mpsavm.h" +#include "mpscamc.h" +#include "mpslib.h" +#include "mpstr.h" +#include "testlib.h" + +#include /* printf */ + + +#define progressf(args) \ + printf args; +#define Xprogressf(args) \ + do{if(0)printf args;}while(FALSE) + +/* testChain -- generation parameters for the test */ +#define genCOUNT 2 +static mps_gen_param_s testChain[genCOUNT] = { + { 100, 0.85 }, { 170, 0.45 } }; + + +/* myroot -- arrays of references that are the root */ +#define myrootAmbigCOUNT 30000 +static void *myrootAmbig[myrootAmbigCOUNT]; +#define myrootExactCOUNT 1000000 +static void *myrootExact[myrootExactCOUNT]; + +static mps_root_t root_stackreg; +static void *stack_start; +static mps_thr_t stack_thr; + + +/* =========== Transform ==============*/ +static void get(mps_arena_t arena); + +static ulongest_t serial = 0; + +/* Tree nodes + * + * To make a node: + * - use next unique serial; + * - choose an id (eg. next in sequence); + * - choose a version (eg. 0). + * + * To make a New node from (or in connection with) an Old: + * - use next unique serial; + * - copy id of Old; + * - ver = ver(Old) + 1. + * + * To invalidate an Old node, when adding an OldNew pair for it: + * - ver = -1 + */ + +struct node_t { + mps_word_t word0; + mps_word_t word1; + mps_word_t serial_dyi; /* unique across every node ever */ + mps_word_t id_dyi; /* .id: replacement nodes copy this */ + mps_word_t ver_dyi; /* .version: distinguish new from old */ + struct node_t *left; + struct node_t *right; + mps_word_t tour_dyi; /* latest tour that visited this node */ + mps_word_t tourIdHash_dyi; /* hash of node ids, computed last tour */ +}; + +/* Tour -- a particular journey to visit to every node in the world + * + * A tour starts with the node at world[0], tours the graph reachable + * from it, then any further bits of graph reachable from world[1], + * and so on. + * + * As it does so, the tour computes a tourReport, characterising the + * state of everything reachable (from world) in the form a few numbers. + * + * Each tour explores the subgraph rooted at each node exactly once. + * That is: if the tour re-encounters a node already visited on that + * tour, it uses the values already computed for that node. + * + * The tourIdHash deliberately depends on the order in which nodes are + * encountered. Therefore, the tourIdHash of a tour depends on the + * entirety of the state of the world and all the world-reachable nodes. + * + * The tourVerSum, being a simple sum, does not depend on the order of + * visiting. + */ + +enum { + cVer = 10 +}; +typedef struct tourReportStruct { + ulongest_t tour; /* tour serial */ + ulongest_t tourIdHash; /* hash of node ids, computed last tour */ + ulongest_t acNodesVer[cVer]; /* count of nodes of each Ver */ +} *tourReport; + +static void tour_subgraph(tourReport tr_o, struct node_t *node); + +static ulongest_t tourSerial = 0; + +static void tourWorld(tourReport tr_o, mps_addr_t *world, ulongest_t countWorld) +{ + ulongest_t i; + + tourSerial += 1; + tr_o->tour = tourSerial; + tr_o->tourIdHash = 0; + for(i = 0; i < cVer; i++) { + tr_o->acNodesVer[i] = 0; + } + Xprogressf(( "[tour %"SCNuLONGEST"] BEGIN, world: %p, countWorld: %"SCNuLONGEST"\n", + tourSerial, (void *)world, countWorld)); + + for(i = 0; i < countWorld; i++) { + struct node_t *node; + node = (struct node_t *)world[i]; + tour_subgraph(tr_o, node); + tr_o->tourIdHash += i * (node ? DYI_INT(node->tourIdHash_dyi) : 0); + } +} + +static void tour_subgraph(tourReport tr_o, struct node_t *node) +{ + ulongest_t tour; + ulongest_t ver; + ulongest_t id; + struct node_t *left; + struct node_t *right; + ulongest_t tourIdHashLeft; + ulongest_t tourIdHashRight; + ulongest_t tourIdHash; + + Insist(tr_o != NULL); + + /* node == NULL is permitted */ + if(node == NULL) + return; + + tour = tr_o->tour; + if(DYI_INT(node->tour_dyi) == tour) + return; /* already visited */ + + /* this is a newly discovered node */ + Insist(DYI_INT(node->tour_dyi) < tour); + + /* mark as visited */ + node->tour_dyi = INT_DYI(tour); + + /* 'local' idHash = id, used for any re-encounters while computing the computed idHash */ + node->tourIdHash_dyi = node->id_dyi; + + /* record this node in the array of ver counts */ + ver = DYI_INT(node->ver_dyi); + Insist(ver < cVer); + tr_o->acNodesVer[ver] += 1; + + /* tour the subgraphs (NULL is permitted) */ + left = node->left; + right = node->right; + tour_subgraph(tr_o, left); + tour_subgraph(tr_o, right); + + /* computed idHash of subgraph at this node */ + id = DYI_INT(node->id_dyi); + tourIdHashLeft = left ? DYI_INT(left->tourIdHash_dyi) : 0; + tourIdHashRight = right ? DYI_INT(right->tourIdHash_dyi) : 0; + tourIdHash = (13*id + 17*tourIdHashLeft + 19*tourIdHashRight) & DYLAN_UINT_MASK; + Insist(tourIdHash <= DYLAN_UINT_MAX); + node->tourIdHash_dyi = INT_DYI(tourIdHash); + + Insist(DYI_INT(node->tour_dyi) == tour); + Xprogressf(( "[tour %"SCNuLONGEST"] new completed node: %p, ver: %"SCNuLONGEST", tourIdHash: %"SCNuLONGEST"\n", + tour, (void*)node, ver, tourIdHash )); +} + +static struct tourReportStruct trBefore; +static struct tourReportStruct trAfter; + +static void before(mps_addr_t *world, ulongest_t countWorld) +{ + tourWorld(&trBefore, world, countWorld); +} + +static void after(mps_addr_t *world, ulongest_t countWorld, + ulongest_t verOld, + longest_t deltaCVerOld, + ulongest_t verNew, + longest_t deltaCVerNew) +{ + longest_t dCVerOld; + longest_t dCVerNew; + + tourWorld(&trAfter, world, countWorld); + + dCVerOld = ((long)trAfter.acNodesVer[verOld] - (long)trBefore.acNodesVer[verOld]); + dCVerNew = ((long)trAfter.acNodesVer[verNew] - (long)trBefore.acNodesVer[verNew]); + + progressf(("tourWorld: (%"PRIuLONGEST" %"PRIuLONGEST":%"PRIuLONGEST"/%"PRIuLONGEST":%"PRIuLONGEST") -> (%"PRIuLONGEST" %"PRIuLONGEST":%+"PRIdLONGEST"/%"PRIuLONGEST":%+"PRIdLONGEST"), %s\n", + trBefore.tourIdHash, + verOld, + trBefore.acNodesVer[verOld], + verNew, + trBefore.acNodesVer[verNew], + + trAfter.tourIdHash, + verOld, + dCVerOld, + verNew, + dCVerNew, + + trBefore.tourIdHash == trAfter.tourIdHash ? "same" : "XXXXX DIFFERENT XXXXX" + )); + Insist(trBefore.tourIdHash == trAfter.tourIdHash); + Insist(dCVerOld == deltaCVerOld); + Insist(dCVerNew == deltaCVerNew); +} + + +static mps_res_t mps_arena_transform_objects_list(mps_bool_t *transform_done_o, + mps_arena_t mps_arena, + mps_addr_t *old_list, + size_t old_list_count, + mps_addr_t *new_list, + size_t new_list_count) +{ + mps_res_t res; + mps_transform_t transform; + mps_bool_t applied = FALSE; + + Insist(old_list_count == new_list_count); + + res = mps_transform_create(&transform, mps_arena); + if(res == MPS_RES_OK) { + /* We have a transform */ + res = mps_transform_add_oldnew(transform, old_list, new_list, old_list_count); + if(res == MPS_RES_OK) { + res = mps_transform_apply(&applied, transform); + } + if(applied) { + /* Transform has been destroyed */ + Insist(res == MPS_RES_OK); + } else { + mps_transform_destroy(transform); + } + } + + /* Always set *transform_done_o (even if there is also a non-ResOK */ + /* return code): it is a status report, not a material return. */ + *transform_done_o = applied; + return res; +} + +static void Transform(mps_arena_t arena, mps_ap_t ap) +{ + ulongest_t i; + ulongest_t keepCount = 0; + mps_word_t v; + struct node_t *node; + mps_res_t res; + mps_bool_t transform_done; + ulongest_t old, new; + ulongest_t perset; + + mps_arena_park(arena); + + { + /* Test with sets of pre-built nodes, a known distance apart. + * + * This gives control over whether new nodes are on the same + * segment as the olds or not. + */ + + ulongest_t iPerset; + ulongest_t aPerset[] = {0, 1, 1, 10, 10, 1000, 1000}; + ulongest_t cPerset = NELEMS(aPerset); + ulongest_t stepPerset; + ulongest_t countWorld = 0; + + /* randomize the order of set sizes from aPerset */ + stepPerset = 1 + (rnd() % (cPerset - 1)); + + progressf(("INT_DYI(1): %"PRIuLONGEST"; DYI_INT(5): %"PRIuLONGEST"\n", + (ulongest_t)INT_DYI(1), (ulongest_t)DYI_INT(5) )); + progressf(("Will make and transform sets of old nodes into new nodes. Set sizes: ")); + for(iPerset = stepPerset; + aPerset[iPerset] != 0; + iPerset = (iPerset + stepPerset) % cPerset) { + countWorld += aPerset[iPerset] * 2; /* 2: old + new */ + progressf(("%"PRIuLONGEST", ", aPerset[iPerset])); + } + progressf(("total: %"PRIuLONGEST".\n", countWorld)); + Insist(countWorld <= myrootExactCOUNT); + + keepCount = 0; + + for(iPerset = stepPerset; + aPerset[iPerset] != 0; + iPerset = (iPerset + stepPerset) % cPerset) { + ulongest_t j; + ulongest_t first; + ulongest_t skip; + ulongest_t count; + + perset = aPerset[iPerset]; + first = keepCount; + skip = 0; + count = perset; + progressf(("perset: %"PRIuLONGEST", first: %"PRIuLONGEST"\n", perset, first)); + + /* Make a set of olds, and a set of news */ + for(j = 0; j < 2 * perset; j++) { + ulongest_t slots = (sizeof(struct node_t) / sizeof(mps_word_t)) - 2; + /* make_dylan_vector: fills slots with INT_DYI(0) */ + die(make_dylan_vector(&v, ap, slots), "make_dylan_vector"); + node = (struct node_t *)v; + node->serial_dyi = INT_DYI(serial++); + node->id_dyi = INT_DYI(j % perset); + node->ver_dyi = INT_DYI(1 + (j >= perset)); + node->left = NULL; + node->right = NULL; + node->tour_dyi = INT_DYI(tourSerial); + node->tourIdHash_dyi = INT_DYI(0); + myrootExact[keepCount++ % myrootExactCOUNT] = (void*)v; + get(arena); + /*printf("Object %"PRIuLONGEST" at %p.\n", keepCount, (void*)v);*/ + } + v = 0; + + /* >=10? pick subset */ + if(perset >= 10) { + /* subset of [first..first+perset) */ + + skip = (rnd() % (2 * perset)); + if(skip > (perset - 1)) + skip = 0; + + count = 1 + rnd() % (2 * (perset - skip)); + if(skip + count > perset) + count = perset - skip; + + Insist(skip < perset); + Insist(count >= 1); + Insist(skip + count <= perset); + } + + /* >=10? sometimes build tree */ + if(perset >= 10 && count >= 4 && rnd() % 2 == 0) { + struct node_t **oldNodes = (struct node_t **)&myrootExact[first + skip]; + struct node_t **newNodes = (struct node_t **)&myrootExact[first + skip + perset]; + progressf(("Building tree in %"PRIuLONGEST" nodes.\n", count)); + for(j = 1; (2 * j) + 1 < count; j++) { + oldNodes[j]->left = oldNodes[2 * j]; + oldNodes[j]->right = oldNodes[(2 * j) + 1]; + if(1){newNodes[j]->left = newNodes[2 * j]; + newNodes[j]->right = newNodes[(2 * j) + 1];} + } + } + + /* transform {count} olds into {count} news */ + before(myrootExact, countWorld); + /* after(myrootExact, countWorld, 1, 0, 2, 0); */ + progressf(("Transform [%"PRIuLONGEST"..%"PRIuLONGEST") to [%"PRIuLONGEST"..%"PRIuLONGEST").\n", + first + skip, first + skip + count, first + skip + perset, first + skip + count + perset)); + res = mps_arena_transform_objects_list(&transform_done, arena, + &myrootExact[first + skip], count, + &myrootExact[first + skip + perset], count); + Insist(res == MPS_RES_OK); + Insist(transform_done); + /* Olds decrease; news were in world already so don't increase. */ + after(myrootExact, countWorld, 1, -(longest_t)count, 2, 0); + } + } + + { + /* Transforming in various situations + * + * First, make two sets of 1024 nodes. + */ + + perset = 1024; + Insist(2*perset < myrootExactCOUNT); + for(keepCount = 0; keepCount < 2*perset; keepCount++) { + ulongest_t slots = (sizeof(struct node_t) / sizeof(mps_word_t)) - 2; + /* make_dylan_vector: fills slots with INT_DYI(0) */ + die(make_dylan_vector(&v, ap, slots), "make_dylan_vector"); + node = (struct node_t *)v; + node->serial_dyi = INT_DYI(serial++); + node->id_dyi = INT_DYI(keepCount % perset); + node->ver_dyi = INT_DYI(1 + (keepCount >= perset)); + node->left = NULL; + node->right = NULL; + myrootExact[keepCount % myrootExactCOUNT] = (void*)v; + get(arena); + /* printf("Object %u at %p.\n", keepCount, (void*)v); */ + } + v = 0; + + + /* Functions before() and after() checksum the world, and verify + * that the expected transform occurred. + */ + before(myrootExact, perset); + after(myrootExact, perset, 1, 0, 2, 0); + + /* Don't transform node 0: its ref coincides with a segbase, so + * there are probably ambiguous refs to it on the stack. + * Don't transform last node either: this test code may leave an + * ambiguous reference to it on the stack. + */ + + /* Refs in root */ + /* ============ */ + old = 1; + new = 1 + perset; + Insist(myrootExact[old] != myrootExact[new]); + before(myrootExact, perset); + res = mps_arena_transform_objects_list(&transform_done, arena, &myrootExact[old], 1, &myrootExact[new], 1); + Insist(res == MPS_RES_OK); + Insist(transform_done); + Insist(myrootExact[old] == myrootExact[new]); + after(myrootExact, perset, 1, -1, 2, +1); + + /* Refs in root: ambiguous ref causes failure */ + /* ========================================== */ + old = 2; + new = 2 + perset; + Insist(myrootExact[old] != myrootExact[new]); + /* Make an ambiguous reference. This must make the transform fail. */ + myrootAmbig[1] = myrootExact[old]; + before(myrootExact, perset); + res = mps_arena_transform_objects_list(&transform_done, arena, &myrootExact[old], 1, &myrootExact[new], 1); + Insist(res == MPS_RES_OK); + Insist(!transform_done); + Insist(myrootExact[old] != myrootExact[new]); + after(myrootExact, perset, 1, 0, 2, 0); + + /* Ref in an object */ + /* ================ */ + old = 3; + new = 3 + perset; + node = myrootExact[4]; + progressf(("node: %p\n", (void *)node)); + node->left = myrootExact[old]; + Insist(myrootExact[old] != myrootExact[new]); + before(myrootExact, perset); + res = mps_arena_transform_objects_list(&transform_done, arena, &myrootExact[old], 1, &myrootExact[new], 1); + Insist(res == MPS_RES_OK); + Insist(transform_done); + Insist(myrootExact[old] == myrootExact[new]); + after(myrootExact, perset, 1, -1, 2, +1); + } + + { + /* Tests with mps_transform_t + * + * **** USES OBJECTS CREATED IN PREVIOUS TEST GROUP **** + */ + + mps_transform_t t1; + mps_transform_t t2; + mps_bool_t applied = FALSE; + ulongest_t k, l; + mps_addr_t nullref1 = NULL; + mps_addr_t nullref2 = NULL; + + k = 9; /* start with this object (in set of 1024) */ + + /* Destroy */ + before(myrootExact, perset); + res = mps_transform_create(&t1, arena); + Insist(res == MPS_RES_OK); + mps_transform_destroy(t1); + t1 = NULL; + + /* Empty (no add) */ + before(myrootExact, perset); + res = mps_transform_create(&t1, arena); + Insist(res == MPS_RES_OK); + res = mps_transform_apply(&applied, t1); + Insist(res == MPS_RES_OK); + Insist(applied); + t1 = NULL; + after(myrootExact, perset, 1, 0, 2, 0); + + /* Identity-transform */ + before(myrootExact, perset); + res = mps_transform_create(&t1, arena); + Insist(res == MPS_RES_OK); + for(l = k + 4; k < l; k++) { + mps_transform_add_oldnew(t1, &myrootExact[k], &myrootExact[k], 1); + } + mps_transform_add_oldnew(t1, &myrootExact[k], &myrootExact[k], 10); + k += 10; + res = mps_transform_apply(&applied, t1); + Insist(res == MPS_RES_OK); + Insist(applied); + t1 = NULL; + after(myrootExact, perset, 1, 0, 2, 0); + + /* Mixed non-trivial, NULL- and identity-transforms */ + before(myrootExact, perset); + res = mps_transform_create(&t1, arena); + Insist(res == MPS_RES_OK); + { + mps_transform_add_oldnew(t1, &myrootExact[k], &myrootExact[k + perset], 1); + k += 1; + /* NULL */ + mps_transform_add_oldnew(t1, &nullref1, &myrootExact[k + perset], 1); + k += 1; + /* identity */ + mps_transform_add_oldnew(t1, &myrootExact[k], &myrootExact[k], 1); + k += 1; + /* NULL */ + mps_transform_add_oldnew(t1, &nullref2, &myrootExact[k + perset], 1); + k += 1; + } + mps_transform_add_oldnew(t1, &myrootExact[k], &myrootExact[k + perset], 10); + k += 10; + res = mps_transform_apply(&applied, t1); + Insist(res == MPS_RES_OK); + Insist(applied); + t1 = NULL; + after(myrootExact, perset, 1, -11, 2, +11); + + /* Non-trivial transform */ + before(myrootExact, perset); + res = mps_transform_create(&t1, arena); + Insist(res == MPS_RES_OK); + for(l = k + 4; k < l; k++) { + mps_transform_add_oldnew(t1, &myrootExact[k], &myrootExact[k + perset], 1); + } + mps_transform_add_oldnew(t1, &myrootExact[k], &myrootExact[k + perset], 10); + k += 10; + res = mps_transform_apply(&applied, t1); + Insist(res == MPS_RES_OK); + Insist(applied); + t1 = NULL; + after(myrootExact, perset, 1, -14, 2, +14); + + /* Two transforms, first destroyed unused */ + before(myrootExact, perset); + res = mps_transform_create(&t1, arena); + Insist(res == MPS_RES_OK); + mps_transform_add_oldnew(t1, &myrootExact[k], &myrootExact[k + perset], 10); + k += 10; + l = k; + res = mps_transform_create(&t2, arena); + Insist(res == MPS_RES_OK); + mps_transform_add_oldnew(t2, &myrootExact[l], &myrootExact[l + perset], 10); + l += 10; + res = mps_transform_apply(&applied, t2); + Insist(res == MPS_RES_OK); + Insist(applied); + t2 = NULL; + mps_transform_destroy(t1); + after(myrootExact, perset, 1, -10, 2, +10); + + /* Two transforms, both live [-- not supported yet. RHSK 2010-12-16] */ + before(myrootExact, perset); + res = mps_transform_create(&t1, arena); + Insist(res == MPS_RES_OK); + mps_transform_add_oldnew(t1, &myrootExact[k], &myrootExact[k + perset], 10); + k += 10; + l = k; + res = mps_transform_create(&t2, arena); + Insist(res == MPS_RES_OK); + mps_transform_add_oldnew(t2, &myrootExact[l], &myrootExact[l + perset], 10); + l += 10; + res = mps_transform_apply(&applied, t2); + Insist(res == MPS_RES_OK); + Insist(applied); + t2 = NULL; + k = l; + after(myrootExact, perset, 1, -10, 2, +10); + + /* Attempt to destroy after applied. */ + before(myrootExact, perset); + res = mps_transform_create(&t1, arena); + Insist(res == MPS_RES_OK); + res = mps_transform_apply(&applied, t1); + Insist(res == MPS_RES_OK); + Insist(applied); + after(myrootExact, perset, 1, 0, 2, 0); + } + + /* Large number of objects */ + { + ulongest_t count; + mps_transform_t t; + mps_bool_t applied; + + /* LARGE! */ + perset = myrootExactCOUNT / 2; + + Insist(2*perset <= myrootExactCOUNT); + for(keepCount = 0; keepCount < 2*perset; keepCount++) { + ulongest_t slots = (sizeof(struct node_t) / sizeof(mps_word_t)) - 2; + /* make_dylan_vector: fills slots with INT_DYI(0) */ + die(make_dylan_vector(&v, ap, slots), "make_dylan_vector"); + node = (struct node_t *)v; + node->serial_dyi = INT_DYI(serial++); + node->id_dyi = INT_DYI(keepCount % perset); + node->ver_dyi = INT_DYI(1 + (keepCount >= perset)); + node->left = NULL; + node->right = NULL; + myrootExact[keepCount % myrootExactCOUNT] = (void*)v; + get(arena); + /* printf("Object %u at %p.\n", keepCount, (void*)v); */ + } + v = 0; + + /* Refs in root */ + /* ============ */ + /* don't transform 0: its ref coincides with a segbase, so causes ambig refs on stack */ + /* don't transform last: its ambig ref may be left on the stack */ + old = 1; + new = 1 + perset; + count = perset - 2; + Insist(myrootExact[old] != myrootExact[new]); + before(myrootExact, perset); + res = mps_transform_create(&t, arena); + Insist(res == MPS_RES_OK); + for(i = 0; i < count; i++) { + res = mps_transform_add_oldnew(t, &myrootExact[old + i], &myrootExact[new + i], 1); + Insist(res == MPS_RES_OK); + } + res = mps_transform_apply(&applied, t); + Insist(applied); + Insist(myrootExact[old] == myrootExact[new]); + after(myrootExact, perset, 1, -(longest_t)count, 2, +(longest_t)count); + } + + printf(" ...made and kept: %"PRIuLONGEST" objects.\n", + keepCount); +} + + +static ulongest_t cols(size_t bytes) +{ + double M; /* Mebibytes */ + ulongest_t cM; /* hundredths of a Mebibyte */ + + M = (double)bytes / (1UL<<20); + cM = (ulongest_t)(M * 100 + 0.5); /* round to nearest */ + return cM; +} + +/* showStatsAscii -- present collection stats, 'graphically' + * + */ +static void showStatsAscii(size_t notcon, size_t con, size_t live, size_t alimit) +{ + ulongest_t n = cols(notcon); + ulongest_t c = cols(notcon + con); + ulongest_t l = cols(notcon + live); /* a fraction of con */ + ulongest_t a = cols(alimit); + ulongest_t count; + ulongest_t i; + + /* if we can show alimit within 200 cols, do so */ + count = (a < 200) ? a + 1 : c; + + for(i = 0; i < count; i++) { + printf( (i == a) ? "A" + : (i < n) ? "n" + : (i < l) ? "L" + : (i < c) ? "_" + : " " + ); + } + printf("\n"); +} + + +/* print_M -- print count of bytes as Mebibytes or Megabytes + * + * Print as a whole number, "m" for the decimal point, and + * then the decimal fraction. + * + * Input: 208896 + * Output: (Mebibytes) 0m199 + * Output: (Megabytes) 0m209 + */ +#if 0 +#define bPerM (1UL << 20) /* Mebibytes */ +#else +#define bPerM (1000000UL) /* Megabytes */ +#endif +static void print_M(size_t bytes) +{ + size_t M; /* M thingies */ + double Mfrac; /* fraction of an M thingy */ + + M = bytes / bPerM; + Mfrac = (double)(bytes % bPerM); + Mfrac = (Mfrac / bPerM); + + printf("%1"PRIuLONGEST"m%03.f", (ulongest_t)M, Mfrac * 1000); +} + + +/* showStatsText -- present collection stats + * + * prints: + * Coll End 0m137[->0m019 14%-live] (0m211-not ) + */ +static void showStatsText(size_t notcon, size_t con, size_t live) +{ + double liveFrac = (double)live / (double)con; + + print_M(con); + printf("[->"); + print_M(live); + printf("% 3.f%%-live]", liveFrac * 100); + printf(" ("); + print_M(notcon); + printf("-not "); + printf(")\n"); +} + +/* get -- get messages + * + */ +static void get(mps_arena_t arena) +{ + mps_message_type_t type; + + while (mps_message_queue_type(&type, arena)) { + mps_message_t message; + static mps_clock_t mclockBegin = 0; + static mps_clock_t mclockEnd = 0; + mps_word_t *obj; + mps_word_t objind; + mps_addr_t objaddr; + + cdie(mps_message_get(&message, arena, type), + "get"); + + switch(type) { + case mps_message_type_gc_start(): { + mclockBegin = mps_message_clock(arena, message); + printf(" %5"PRIuLONGEST": (%5"PRIuLONGEST")", + mclockBegin, mclockBegin - mclockEnd); + printf(" Coll Begin (%s)\n", + mps_message_gc_start_why(arena, message)); + break; + } + case mps_message_type_gc(): { + size_t con = mps_message_gc_condemned_size(arena, message); + size_t notcon = mps_message_gc_not_condemned_size(arena, message); + /* size_t other = 0; -- cannot determine; new method reqd */ + size_t live = mps_message_gc_live_size(arena, message); + size_t alimit = mps_arena_reserved(arena); + + mclockEnd = mps_message_clock(arena, message); + + printf(" %5"PRIuLONGEST": (%5"PRIuLONGEST")", + mclockEnd, mclockEnd - mclockBegin); + printf(" Coll End "); + showStatsText(notcon, con, live); + if(rnd()==0) showStatsAscii(notcon, con, live, alimit); + break; + } + case mps_message_type_finalization(): { + mps_message_finalization_ref(&objaddr, arena, message); + obj = objaddr; + objind = DYLAN_INT_INT(DYLAN_VECTOR_SLOT(obj, 0)); + printf(" Finalization for object %"PRIuLONGEST" at %p\n", (ulongest_t)objind, objaddr); + break; + } + default: { + cdie(0, "message type"); + break; + } + } + + mps_message_discard(arena, message); + } +} + + +/* .catalog: The Catalog client: + * + * This is an MPS client for testing the MPS. It simulates + * converting a multi-page "Catalog" document from a page-description + * into a bitmap. + * + * The intention is that this task will cause memory usage that is + * fairly realistic (much more so than randomly allocated objects + * with random interconnections. The patterns in common with real + * clients are: + * - the program input and its task are 'fractal', with a + * self-similar hierarchy; + * - object allocation is prompted by each successive element of + * the input/task; + * - objects are often used to store a transformed version of the + * program input; + * - there may be several stages of transformation; + * - at each stage, the old object (holding the untransformed data) + * may become dead; + * - sometimes a tree of objects becomes dead once an object at + * some level of the hierarchy has been fully processed; + * - there is more than one hierarchy, and objects in different + * hierarchies interact. + * + * The entity-relationship diagram is: + * Catalog -< Page -< Article -< Polygon + * v + * | + * Palette --------------------< Colour + * + * The first hierarchy is a Catalog, containing Pages, each + * containing Articles (bits of artwork etc), each composed of + * Polygons. Each polygon has a single colour. + * + * The second hierarchy is a top-level Palette, containing Colours. + * Colours (in this client) are expensive, large objects (perhaps + * because of complex colour modelling or colour blending). + * + * The things that matter for their effect on MPS behaviour are: + * - when objects are allocated, and how big they are; + * - how the reference graph mutates over time; + * - how the mutator accesses objects (barrier hits). + */ + +enum { + CatalogRootIndex = 0, + CatalogSig = 0x0000CA2A, /* CATAlog */ + CatalogFix = 1, + CatalogVar = 10, + PageSig = 0x0000BA9E, /* PAGE */ + PageFix = 1, + PageVar = 100, + ArtSig = 0x0000A621, /* ARTIcle */ + ArtFix = 1, + ArtVar = 100, + PolySig = 0x0000B071, /* POLYgon */ + PolyFix = 1, + PolyVar = 100 +}; + +static void CatalogCheck(void) +{ + mps_word_t w; + void *Catalog, *Page, *Art, *Poly; + ulongest_t Catalogs = 0, Pages = 0, Arts = 0, Polys = 0; + int i, j, k; + + /* retrieve Catalog from root */ + Catalog = myrootExact[CatalogRootIndex]; + if(!Catalog) + return; + Insist(DYLAN_VECTOR_SLOT(Catalog, 0) == DYLAN_INT(CatalogSig)); + Catalogs += 1; + + for(i = 0; i < CatalogVar; i += 1) { + /* retrieve Page from Catalog */ + w = DYLAN_VECTOR_SLOT(Catalog, CatalogFix + i); + /* printf("Page = 0x%8x\n", (unsigned int) w); */ + if(w == DYLAN_INT(0)) + break; + Page = (void *)w; + Insist(DYLAN_VECTOR_SLOT(Page, 0) == DYLAN_INT(PageSig)); + Pages += 1; + + for(j = 0; j < PageVar; j += 1) { + /* retrieve Art from Page */ + w = DYLAN_VECTOR_SLOT(Page, PageFix + j); + if(w == DYLAN_INT(0)) + break; + Art = (void *)w; + Insist(DYLAN_VECTOR_SLOT(Art, 0) == DYLAN_INT(ArtSig)); + Arts += 1; + + for(k = 0; k < ArtVar; k += 1) { + /* retrieve Poly from Art */ + w = DYLAN_VECTOR_SLOT(Art, ArtFix + k); + if(w == DYLAN_INT(0)) + break; + Poly = (void *)w; + Insist(DYLAN_VECTOR_SLOT(Poly, 0) == DYLAN_INT(PolySig)); + Polys += 1; + } + } + } + printf("Catalog ok with: Catalogs: %"PRIuLONGEST", Pages: %"PRIuLONGEST", Arts: %"PRIuLONGEST", Polys: %"PRIuLONGEST".\n", + Catalogs, Pages, Arts, Polys); +} + + +/* CatalogDo -- make a Catalog and its tree of objects + * + * .catalog.broken: this code, when compiled with + * moderate optimization, may have ambiguous interior pointers but + * lack corresponding ambiguous base pointers to MPS objects. This + * means the interior pointers are unmanaged references, and the + * code goes wrong. The hack in poolamc.c#4 cures this, but not very + * nicely. For further discussion, see: + * + */ +static void CatalogDo(mps_arena_t arena, mps_ap_t ap) +{ + mps_word_t v; + void *Catalog, *Page, *Art, *Poly; + int i, j, k; + + die(make_dylan_vector(&v, ap, CatalogFix + CatalogVar), "Catalog"); + DYLAN_VECTOR_SLOT(v, 0) = DYLAN_INT(CatalogSig); + Catalog = (void *)v; + + /* store Catalog in root */ + myrootExact[CatalogRootIndex] = Catalog; + get(arena); + + fflush(stdout); + CatalogCheck(); + + for(i = 0; i < CatalogVar; i += 1) { + die(make_dylan_vector(&v, ap, PageFix + PageVar), "Page"); + DYLAN_VECTOR_SLOT(v, 0) = DYLAN_INT(PageSig); + Page = (void *)v; + + /* store Page in Catalog */ + DYLAN_VECTOR_SLOT(Catalog, CatalogFix + i) = (mps_word_t)Page; + get(arena); + + printf("Page %d: make articles\n", i); + fflush(stdout); + + for(j = 0; j < PageVar; j += 1) { + die(make_dylan_vector(&v, ap, ArtFix + ArtVar), "Art"); + DYLAN_VECTOR_SLOT(v, 0) = DYLAN_INT(ArtSig); + Art = (void *)v; + + /* store Art in Page */ + DYLAN_VECTOR_SLOT(Page, PageFix + j) = (mps_word_t)Art; + get(arena); + + for(k = 0; k < ArtVar; k += 1) { + die(make_dylan_vector(&v, ap, PolyFix + PolyVar), "Poly"); + DYLAN_VECTOR_SLOT(v, 0) = DYLAN_INT(PolySig); + Poly = (void *)v; + + /* store Poly in Art */ + DYLAN_VECTOR_SLOT(Art, ArtFix + k) = (mps_word_t)Poly; + /* get(arena); */ + } + } + } + fflush(stdout); + CatalogCheck(); +} + + +/* MakeThing -- make an object of the size requested (in bytes) + * + * Any size is accepted. MakeThing may round it up (MakeThing always + * makes a dylan vector, which has a minimum size of 8 bytes). Vector + * slots, if any, are initialized to DYLAN_INT(0). + * + * After making the object, calls get(), to retrieve MPS messages. + * + * make_dylan_vector [fmtdytst.c] says: + * size = (slots + 2) * sizeof(mps_word_t); + * That is: a dylan vector has two header words before the first slot. + */ +static void* MakeThing(mps_arena_t arena, mps_ap_t ap, size_t size) +{ + mps_word_t v; + ulongest_t words; + ulongest_t slots; + + words = (size + (sizeof(mps_word_t) - 1) ) / sizeof(mps_word_t); + if(words < 2) + words = 2; + + slots = words - 2; + die(make_dylan_vector(&v, ap, slots), "make_dylan_vector"); + get(arena); + + return (void *)v; +} + +static void BigdropSmall(mps_arena_t arena, mps_ap_t ap, size_t big, char small_ref) +{ + static ulongest_t keepCount = 0; + ulongest_t i; + + mps_arena_park(arena); + for(i = 0; i < 100; i++) { + (void) MakeThing(arena, ap, big); + if(small_ref == 'A') { + myrootAmbig[keepCount++ % myrootAmbigCOUNT] = MakeThing(arena, ap, 1); + } else if(small_ref == 'E') { + myrootExact[keepCount++ % myrootExactCOUNT] = MakeThing(arena, ap, 1); + } else { + cdie(0, "BigdropSmall: small must be 'A' or 'E'.\n"); + } + } +} + + +/* df -- diversity function + * + * Either deterministic based on "number", or 'random' (ie. call rnd). + */ + +static ulongest_t df(unsigned randm, ulongest_t number) +{ + if(randm == 0) { + return number; + } else { + return rnd(); + } +} + +static void Make(mps_arena_t arena, mps_ap_t ap, unsigned randm, unsigned keep1in, unsigned keepTotal, unsigned keepRootspace, unsigned sizemethod) +{ + unsigned keepCount = 0; + ulongest_t objCount = 0; + + Insist(keepRootspace <= myrootExactCOUNT); + + objCount = 0; + while(keepCount < keepTotal) { + mps_word_t v; + unsigned slots = 2; /* minimum */ + switch(sizemethod) { + case 0: { + /* minimum */ + slots = 2; + break; + } + case 1: { + slots = 2; + if(df(randm, objCount) % 10000 == 0) { + printf("*"); + slots = 300000; + } + break; + } + case 2: { + slots = 2; + if(df(randm, objCount) % 6661 == 0) { /* prime */ + printf("*"); + slots = 300000; + } + break; + } + default: { + printf("bad script command: sizemethod %u unknown.\n", sizemethod); + cdie(FALSE, "bad script command!"); + break; + } + } + die(make_dylan_vector(&v, ap, slots), "make_dylan_vector"); + DYLAN_VECTOR_SLOT(v, 0) = DYLAN_INT(objCount); + DYLAN_VECTOR_SLOT(v, 1) = (mps_word_t)NULL; + objCount++; + if(df(randm, objCount) % keep1in == 0) { + /* keep this one */ + myrootExact[df(randm, keepCount) % keepRootspace] = (void*)v; + keepCount++; + } + get(arena); + } + printf(" ...made and kept: %u objects, storing cyclically in " + "first %u roots " + "(actually created %"PRIuLONGEST" objects, in accord with " + "keep-1-in %u).\n", + keepCount, keepRootspace, objCount, keep1in); +} + + +static void Rootdrop(char rank_char) +{ + ulongest_t i; + + if(rank_char == 'A') { + for(i = 0; i < myrootAmbigCOUNT; ++i) { + myrootAmbig[i] = NULL; + } + } else if(rank_char == 'E') { + for(i = 0; i < myrootExactCOUNT; ++i) { + myrootExact[i] = NULL; + } + } else { + cdie(0, "Rootdrop: rank must be 'A' or 'E'.\n"); + } +} + +#if 0 +#define stackwipedepth 50000 +static void stackwipe(void) +{ + unsigned iw; + ulongest_t aw[stackwipedepth]; + + /* http://xkcd.com/710/ */ + /* I don't want my friends to stop calling; I just want the */ + /* compiler to stop optimising away my code. */ + + /* Do you ever get two even numbers next to each other? Hmmmm :-) */ + for(iw = 0; iw < stackwipedepth; iw++) { + if((iw & 1) == 0) { + aw[iw] = 1; + } else { + aw[iw] = 0; + } + } + for(iw = 1; iw < stackwipedepth; iw++) { + if(aw[iw - 1] + aw[iw] != 1) { + printf("Errrr....\n"); + break; + } + } +} +#endif + +static void StackScan(mps_arena_t arena, int on) +{ + if(on) { + Insist(root_stackreg == NULL); + die(mps_root_create_reg(&root_stackreg, arena, + mps_rank_ambig(), (mps_rm_t)0, stack_thr, + mps_stack_scan_ambig, stack_start, 0), + "root_stackreg"); + Insist(root_stackreg != NULL); + } else { + Insist(root_stackreg != NULL); + mps_root_destroy(root_stackreg); + root_stackreg = NULL; + Insist(root_stackreg == NULL); + } +} + + +/* checksi -- check count of sscanf items is correct + */ + +static void checksi(int si, int si_shouldBe, const char *script, const char *scriptAll) +{ + if(si != si_shouldBe) { + printf("bad script command (sscanf found wrong number of params) %s (full script %s).\n", script, scriptAll); + cdie(FALSE, "bad script command!"); + } +} + +/* testscriptC -- actually runs a test script + * + */ +static void testscriptC(mps_arena_t arena, mps_ap_t ap, const char *script) +{ + const char *scriptAll = script; + int si, sb; /* sscanf items, sscanf bytes */ + + while(*script != '\0') { + switch(*script) { + case 'C': { + si = sscanf(script, "Collect%n", + &sb); + checksi(si, 0, script, scriptAll); + script += sb; + printf(" Collect\n"); + /* stackwipe(); */ + mps_arena_collect(arena); + mps_arena_release(arena); + break; + } + case 'T': { + si = sscanf(script, "Transform%n", + &sb); + checksi(si, 0, script, scriptAll); + script += sb; + printf(" Transform\n"); + Transform(arena, ap); + break; + } + case 'K': { + si = sscanf(script, "Katalog()%n", + &sb); + checksi(si, 0, script, scriptAll); + script += sb; + printf(" Katalog()\n"); + CatalogDo(arena, ap); + break; + } + case 'B': { + ulongest_t big = 0; + char small_ref = ' '; + si = sscanf(script, "BigdropSmall(big %"SCNuLONGEST", small %c)%n", + &big, &small_ref, &sb); + checksi(si, 2, script, scriptAll); + script += sb; + printf(" BigdropSmall(big %"PRIuLONGEST", small %c)\n", big, small_ref); + BigdropSmall(arena, ap, big, small_ref); + break; + } + case 'M': { + unsigned randm = 0; + unsigned keep1in = 0; + unsigned keepTotal = 0; + unsigned keepRootspace = 0; + unsigned sizemethod = 0; + si = sscanf(script, "Make(random %u, keep-1-in %u, keep %u, rootspace %u, sizemethod %u)%n", + &randm, &keep1in, &keepTotal, &keepRootspace, &sizemethod, &sb); + checksi(si, 5, script, scriptAll); + script += sb; + printf(" Make(random %u, keep-1-in %u, keep %u, rootspace %u, sizemethod %u).\n", + randm, keep1in, keepTotal, keepRootspace, sizemethod); + Make(arena, ap, randm, keep1in, keepTotal, keepRootspace, sizemethod); + break; + } + case 'R': { + char drop_ref = ' '; + si = sscanf(script, "Rootdrop(rank %c)%n", + &drop_ref, &sb); + checksi(si, 1, script, scriptAll); + script += sb; + printf(" Rootdrop(rank %c)\n", drop_ref); + Rootdrop(drop_ref); + break; + } + case 'S': { + unsigned on = 0; + si = sscanf(script, "StackScan(%u)%n", + &on, &sb); + checksi(si, 1, script, scriptAll); + script += sb; + printf(" StackScan(%u)\n", on); + StackScan(arena, on != 0); + break; + } + case 'Z': { + ulongest_t s0; + si = sscanf(script, "ZRndStateSet(%"SCNuLONGEST")%n", + &s0, &sb); + checksi(si, 1, script, scriptAll); + script += sb; + printf(" ZRndStateSet(%"PRIuLONGEST")\n", s0); + rnd_state_set((unsigned long)s0); + break; + } + case ' ': + case ',': + case '.': { + script++; + break; + } + default: { + printf("unknown script command '%c' (script %s).\n", + *script, scriptAll); + cdie(FALSE, "unknown script command!"); + return; + } + } + get(arena); + } + +} + + +/* testscriptB -- create pools and objects; call testscriptC + * + * Is called via mps_tramp, so matches mps_tramp_t function prototype, + * and use trampDataStruct to pass parameters. + */ + +typedef struct trampDataStruct { + mps_arena_t arena; + mps_thr_t thr; + const char *script; +} trampDataStruct; + +static void *testscriptB(void *arg, size_t s) +{ + trampDataStruct trampData; + mps_arena_t arena; + mps_thr_t thr; + const char *script; + mps_fmt_t fmt; + mps_chain_t chain; + mps_pool_t amc; + int i; + mps_root_t root_table_Ambig; + mps_root_t root_table_Exact; + mps_ap_t ap; + void *stack_starts_here; /* stack scanning starts here */ + + Insist(s == sizeof(trampDataStruct)); + trampData = *(trampDataStruct*)arg; + arena = trampData.arena; + thr = trampData.thr; + script = trampData.script; + + die(mps_fmt_create_A(&fmt, arena, dylan_fmt_A()), "fmt_create"); + die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); + die(mps_pool_create(&amc, arena, mps_class_amc(), fmt, chain), + "pool_create amc"); + + for(i = 0; i < myrootAmbigCOUNT; ++i) { + myrootAmbig[i] = NULL; + } + die(mps_root_create_table(&root_table_Ambig, arena, mps_rank_ambig(), (mps_rm_t)0, + myrootAmbig, (size_t)myrootAmbigCOUNT), + "root_create - ambig"); + + for(i = 0; i < myrootExactCOUNT; ++i) { + myrootExact[i] = NULL; + } + die(mps_root_create_table(&root_table_Exact, arena, mps_rank_exact(), (mps_rm_t)0, + myrootExact, (size_t)myrootExactCOUNT), + "root_create - exact"); + + die(mps_ap_create(&ap, amc, mps_rank_exact()), "ap_create"); + + /* root_stackreg: stack & registers are ambiguous roots = mutator's workspace */ + stack_start = &stack_starts_here; + stack_thr = thr; + die(mps_root_create_reg(&root_stackreg, arena, + mps_rank_ambig(), (mps_rm_t)0, stack_thr, + mps_stack_scan_ambig, stack_start, 0), + "root_stackreg"); + + + mps_message_type_enable(arena, mps_message_type_gc_start()); + mps_message_type_enable(arena, mps_message_type_gc()); + mps_message_type_enable(arena, mps_message_type_finalization()); + + testscriptC(arena, ap, script); + + printf(" Destroy roots, pools, arena etc.\n\n"); + mps_root_destroy(root_stackreg); + mps_ap_destroy(ap); + mps_root_destroy(root_table_Exact); + mps_root_destroy(root_table_Ambig); + mps_pool_destroy(amc); + mps_chain_destroy(chain); + mps_fmt_destroy(fmt); + + return NULL; +} + + +/* testscriptA -- create arena, thr; call testscriptB + */ +static void testscriptA(const char *script) +{ + mps_arena_t arena; + int si, sb; /* sscanf items, sscanf bytes */ + ulongest_t arenasize = 0; + mps_thr_t thr; + trampDataStruct trampData; + + si = sscanf(script, "Arena(size %"SCNuLONGEST")%n", &arenasize, &sb); + cdie(si == 1, "bad script command: Arena(size %%"PRIuLONGEST")"); + script += sb; + printf(" Create arena, size = %"PRIuLONGEST".\n", arenasize); + + /* arena */ + die(mps_arena_create(&arena, mps_arena_class_vm(), arenasize), + "arena_create"); + + /* thr: used to stop/restart multiple threads */ + die(mps_thread_reg(&thr, arena), "thread"); + + /* call testscriptB! */ + trampData.arena = arena; + trampData.thr = thr; + trampData.script = script; + testscriptB(&trampData, sizeof trampData); + + mps_thread_dereg(thr); + mps_arena_destroy(arena); +} + + +/* main -- runs various test scripts + * + */ +int main(int argc, char *argv[]) +{ + randomize(argc, argv); + mps_lib_assert_fail_install(assert_die); + + /* 1<<19 == 524288 == 1/2 Mebibyte */ + /* 16<<20 == 16777216 == 16 Mebibyte */ + + testscriptA("Arena(size 500000000), " + "Transform" + /*", Collect, Rootdrop(rank E), Collect, Collect"*/ + "."); + + printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); + return 0; +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (c) 2001-2013 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/design/transform.txt b/design/transform.txt new file mode 100644 index 0000000000..68122ca759 --- /dev/null +++ b/design/transform.txt @@ -0,0 +1,126 @@ +MPS TRANSFORM DESIGN +==================== +Richard Brooksby, Ravenbrook Limited, 2012-09-04 + + +1. Introduction +--------------- + +This document describes the Transform mechanism of the Memory Pool +System. Transforms allow the client code to replace a set of object +references on the heap. Transforms exist to satisfy a requirement from +Configura, and are not intended to become part of the open source +MPS. + +The readership of this document is any developer intending to modify the +Transform implementation. + +This document is Configura client confidential. + + +2. Background +------------- + +Göran Rydqvist of Configura originally expressed the requirement for the +MPS to support the change of layout of objects in CET [[Crab +2010-02-25][]]. Ravenbrook proposed several methods [[RHSK +2010-09-21][]] but Configura selected idea 1: + +> Make all objects "direct", without IOHeaders. If you need to add fields, +> then use a special new MPS function (that doesn't exist yet): +> +> mps_arena_transform_objects(&my_transform_function); +> +> This traverses the object graph, lets your transform_function basically +> "realloc()" the field-block, and MPS fixes up all references from other +> objects to point to the new field-block. +> +> Unfortunately, this idea is probably killed off by ambiguous references +> :-(. You could only run the patch if you could *guarantee* there are no +> ambiguous refs you want. In other words, any object refs on the stack +> would become instant death (or worse: subtle slow death :-). Therefore we +> don't really like this idea (unfortunately). There are safer and simpler +> ways to do it, we think... + +An initial implementation was made by RHSK and released to Configura as +"experimental", however Configura put it into production. + +During work on adapting the MPS to 64-bit Windows, RB reformed and +reimplemented transforms based on RHSK's original work. + + +3. Overview +----------- + +The client program builds a table mapping "old" references to "new" ones +in a `Transform` object. This is then "applied", causing a garbage +collection trace in which the fix function is substituted by +`transformFix`, which spots "old" references and replaces them with +"new" ones, in addition to applying the usual garbage collection fix +function. + + +4. NOT YET WRITTEN +------------------ + +Points to cover: + +* Ambiguous references and aborting the transform + + * How ambiguous references are avoided using stackAtEnter + +* How the implementation is an add-on to the MPS + +* How the code is kept separate from the mainstream MPS + +* Why it has its own hash table implementation (no good reason) + +* Why it does a garbage collection and not just a transforming scan + + * Nice side-effect is that "old" objects are killed + +* Why the arena must be parked + + + +A. References +------------- + +[Crab 2010-02-25]: http://info.ravenbrook.com/mail/2010/02/25/16-35-45/0/ +[Crab 2010-02-25] "Incremental object" (email); Göran Rydqvist; +Configura; 2010-02-25; +. + +[RHSK 2010-09-21]: http://info.ravenbrook.com/mail/2010/09/21/16-54-59/0/ +[RHSK 2010-09-21] "Incremental object ideas" (email); Richard Kistruck; +Ravenbrook Limited; 2010-09-21; +. + +[Crab 2010-09-22]: http://info.ravenbrook.com/mail/2010/09/22/09-27-53/0/ +[Crab 2010-09-22] "Incremental object ideas" (email); Göran Rydqvist; +Configura; 2010-09-22; +. + + +B. Document History +------------------- + +* 2012-09-04 [RB](mailto:rb@ravenbrook.com) First draft. + + +C. Copyright and Licence +------------------------ + +This document is copyright © 2012 [Ravenbrook Limited](http://www.ravenbrook.com). 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 the 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/manual/source/custom/cet/index.rst b/manual/source/custom/cet/index.rst new file mode 100644 index 0000000000..33fa940fa9 --- /dev/null +++ b/manual/source/custom/cet/index.rst @@ -0,0 +1,9 @@ +.. _custom_cet: + +Custom features for CET +*********************** + +.. toctree:: + :numbered: + + transform diff --git a/manual/source/custom/cet/transform.rst b/manual/source/custom/cet/transform.rst new file mode 100644 index 0000000000..aa9c27a6d8 --- /dev/null +++ b/manual/source/custom/cet/transform.rst @@ -0,0 +1,132 @@ +.. Sources: + + ``_ + +.. index:: + single: transform; introduction + +Transforms +========== + +In a long-running interactive system, it may be desirable to change the format of live objects. In some programming languages (notably Smalltalk), when the programmer edits a class definition, objects belonging to the class must be updated so that they are valid instances of the redefined class. This may involve adding or removing fields from each instance and so changing the size of the allocated objects. + +If the object has grown as a result of the redefinition, this redefinition can't be done in-place, so what actually happens is that for each instance of the old version of the class, a corresponding instance of the new version of the class is created, and all references to the old instance are rewritten to refer to the new instance. And discovering "all references" to an object is a task that falls to the garbage collector. + +*Transforms* are a general mechanism by which the client program requests the MPS to replace references to one set of objects (the *old* objects) with references to another (the *new* objects). The MPS performs this task by carrying out a complete garbage collection, in the course of which all references to old objects are discovered and substituted with references to the corresponding new object. + + +Transform cautions +------------------ + +1. The arena must be :term:`parked ` (for example, by + calling :c:func:`mps_arena_park`) before creating the transform and + not :term:`unclamped ` before applying the + transform. + +2. A transform cannot be applied if there is an :term:`ambiguous + reference` to any of the old objects. (Because the MPS cannot know + whether or not the reference should be updated to point to the new + object.) + + +.. index:: + single: transform; interface + +Transform interface +------------------- + +:: + + #include "mpstr.h" + + +.. c:type:: mps_transform_t + + The type of transforms. A transform represents a mapping from + *old* objects to *new* objects. + + +.. c:function:: mps_res_t mps_transform_create(mps_transform_t *transform_o, mps_arena_t arena) + + Create an empty transform. + + ``transform_o`` points to a location that will hold the address of + the new transform. + + ``arena`` is the arena in which to create the transform. + + :c:func:`mps_transform_create` returns :c:macro:`MPS_RES_OK` if + successful. The MPS may exhaust some resource in the course of + :c:func:`mps_transform_create` and will return an appropriate + :term:`result code` if so. + + .. note:: + + The arena must be :term:`parked ` (for example, + by calling :c:func:`mps_arena_park`) before creating a + transform, and if :c:func:`mps_transform_apply` is called on + a transform, it must be called before the arena is + :term:`unclamped `. + + +.. c:function:: mps_res_t mps_transform_add_oldnew(mps_transform_t transform, mps_addr_t *old_array, mps_addr_t *new_array, size_t count) + + Add mappings from old to new objects to a transform. + + ``transform`` is the transform to which the mappings will be added. + + ``old_array`` points to an array of references to old objects. + + ``new_array`` points to an array of references to new objects. + + ``count`` is the number of references in each array. + + :c:func:`mps_transform_add_oldnew` returns :c:macro:`MPS_RES_OK` + if successful. The MPS may exhaust some resource in the course of + :c:func:`mps_transform_add_oldnew` and will return an appropriate + :term:`result code` if so. + + .. note:: + + An old object must be added at most once to a transform. + + +.. c:function:: mps_res_t mps_transform_apply(mps_bool_t *applied_o, mps_transform_t transform) + + Attempt to apply a transform. + + ``applied_o`` points to a location that will hold a Boolean + indicating whether or not the transform was applied. + + ``transform`` is the transform to apply. + + If the arena is currently incapable of applying the transform, + then an appropriate :term:`result code` is returned, and the + location pointed to by ``applied_o`` is not updated. Possible + causes include (but are not limited to) the arena not being in the + :term:`parked state` (in which case the result code is + :c:macro:`MPS_RES_LIMIT`), or a collection having taken place + since ``transform`` was created (in which case the result code is + :c:macro:`MPS_RES_PARAM`). + + If the arena is *capable* of applying the transform, then the MPS + carries out a garbage collection, the arena is left in the + :term:`parked state`, :c:func:`mps_transform_apply` returns + :c:macro:`MPS_RES_OK`, and the location pointed to by + ``applied_o`` is updated. + + If in the course of the ambiguous reference was discovered, then + the transform is aborted and ``*applied_o`` is set to false. In + this case, *no* references to the old objects are updated. (That + is, either *all* of the transform is applied, or *none* of it.) + + If the transform was successfully applied, it is destroyed (as if + :c:func:`mps_transform_destroy` had been called). + + +.. c:function:: void mps_transform_destroy(mps_transform_t transform) + + Destroy a transform. + + ``transform`` is the transform to destroy. + diff --git a/manual/source/design/index.rst b/manual/source/design/index.rst index 8b074f61e9..8a7a95a65d 100644 --- a/manual/source/design/index.rst +++ b/manual/source/design/index.rst @@ -62,6 +62,7 @@ Design testthr thread-manager thread-safety + transform type version-library vm diff --git a/manual/source/index.rst b/manual/source/index.rst index 5d61d583c2..b70fe2627f 100644 --- a/manual/source/index.rst +++ b/manual/source/index.rst @@ -7,6 +7,7 @@ Memory Pool System guide/index topic/index pool/index + custom/cet/index design/index design/old diff --git a/tool/testcases.txt b/tool/testcases.txt index b19de92d12..787ba139d0 100644 --- a/tool/testcases.txt +++ b/tool/testcases.txt @@ -43,6 +43,7 @@ teletest =N interactive walkt0 zcoll =L zmess +ztfm =L ============= ================ ========================================== Key to flags From 72f0d63b2606c673605039fac4dd1e0e656fcd85 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Sun, 23 Jan 2022 21:08:47 +0000 Subject: [PATCH 02/23] Make transforms part of the public MPS. * Move transforms sources to the core sections of the makefiles. * Move function declarations to the public header mps.h. * Update copyright notices for transforms code. * Remove references to Configura from the comments. * Remove trailing whitespace. * Translate design to reStructuredText. * Move documentation to reference section of manual. * Add warning about unsuitability when ambiguous references may exist. (cherry picked from commit b1c30975d91a8ab871ca4d94de1754d5ee44ef03) --- code/comm.gmk | 13 +- code/commpre.nmk | 13 +- code/mps.c | 3 - code/mps.h | 9 + code/mpsitr.c | 58 ++--- code/mpstr.h | 66 ----- code/trans.c | 68 ++--- code/trans.h | 52 ++-- code/ztfm.c | 235 ++++++++---------- design/transform.txt | 173 +++++++------ manual/source/glossary/t.rst | 9 + manual/source/release.rst | 5 + manual/source/topic/index.rst | 1 + .../{custom/cet => topic}/transform.rst | 91 ++++--- 14 files changed, 352 insertions(+), 444 deletions(-) delete mode 100644 code/mpstr.h rename manual/source/{custom/cet => topic}/transform.rst (56%) diff --git a/code/comm.gmk b/code/comm.gmk index ec8f93bd22..daaf0e6cfb 100644 --- a/code/comm.gmk +++ b/code/comm.gmk @@ -191,6 +191,7 @@ MPMCOMMON = \ meter.c \ mpm.c \ mpsi.c \ + mpsitr.c \ nailboard.c \ policy.c \ pool.c \ @@ -214,6 +215,7 @@ MPMCOMMON = \ trace.c \ traceanc.c \ tract.c \ + trans.c \ tree.c \ version.c \ vm.c \ @@ -221,10 +223,6 @@ MPMCOMMON = \ POOLS = $(AMC) $(AMS) $(AWL) $(LO) $(MV2) $(MVFF) $(SNC) MPM = $(MPMCOMMON) $(MPMPF) $(POOLS) $(PLINTH) -# Custom CET extensions go here: - -MPM += mpsitr.c trans.c - # These map the source file lists onto object files and dependency files # in the platform/variety directory. @@ -297,7 +295,8 @@ TEST_TARGETS=\ teletest \ walkt0 \ zcoll \ - zmess + zmess \ + ztfm # This target records programs that we were once able to build but # can't at the moment: @@ -305,10 +304,6 @@ TEST_TARGETS=\ UNBUILDABLE_TARGETS=\ replay # depends on the EPVM pool -# Add tests for the custom CET build here: - -TEST_TARGETS += ztfm - ALL_TARGETS=$(LIB_TARGETS) $(TEST_TARGETS) $(EXTRA_TARGETS) diff --git a/code/commpre.nmk b/code/commpre.nmk index 3ff41b1766..61e076b37c 100644 --- a/code/commpre.nmk +++ b/code/commpre.nmk @@ -98,7 +98,8 @@ TEST_TARGETS=\ teletest.exe \ walkt0.exe \ zcoll.exe \ - zmess.exe + zmess.exe \ + ztfm.exe # Stand-alone programs go in EXTRA_TARGETS if they should always be # built, or in OPTIONAL_TARGETS if they should only be built if @@ -113,10 +114,6 @@ OPTIONAL_TARGETS=mpseventsql.exe UNBUILDABLE_TARGETS=replay.exe -# Add tests for the custom CET build here: - -TEST_TARGETS=$(TEST_TARGETS) ztfm.exe - ALL_TARGETS=$(LIB_TARGETS) $(TEST_TARGETS) $(EXTRA_TARGETS) @@ -149,6 +146,7 @@ MPMCOMMON=\ [meter] \ [mpm] \ [mpsi] \ + [mpsitr] \ [nailboard] \ [policy] \ [pool] \ @@ -173,6 +171,7 @@ MPMCOMMON=\ [trace] \ [traceanc] \ [tract] \ + [trans] \ [tree] \ [version] \ [vm] \ @@ -193,10 +192,6 @@ TESTTHR = [testthrw3] POOLS = $(AMC) $(AMS) $(AWL) $(LO) $(MV2) $(MVFF) $(SNC) MPM = $(MPMCOMMON) $(MPMPF) $(POOLS) $(PLINTH) -# Custom CET extensions go here: - -MPMCOMMON = $(MPMCOMMON) [mpsitr] [trans] - # CHECK PARAMETERS # diff --git a/code/mps.c b/code/mps.c index 5a761c23a7..378965605a 100644 --- a/code/mps.c +++ b/code/mps.c @@ -80,9 +80,6 @@ #include "failover.c" #include "vm.c" #include "policy.c" - -/* Configura CET customisations */ - #include "trans.c" #include "mpsitr.c" diff --git a/code/mps.h b/code/mps.h index 4c56265c2e..2514236351 100644 --- a/code/mps.h +++ b/code/mps.h @@ -833,6 +833,15 @@ extern mps_res_t _mps_fix2(mps_ss_t, mps_addr_t *); MPS_END +/* Transforms interface. */ + +typedef struct mps_transform_s *mps_transform_t; +extern mps_res_t mps_transform_create(mps_transform_t *, mps_arena_t); +extern mps_res_t mps_transform_add_oldnew(mps_transform_t, mps_addr_t *, mps_addr_t *, size_t); +extern mps_res_t mps_transform_apply(mps_bool_t *, mps_transform_t); +extern void mps_transform_destroy(mps_transform_t); + + #endif /* mps_h */ diff --git a/code/mpsitr.c b/code/mpsitr.c index 866731fd3d..522d809969 100644 --- a/code/mpsitr.c +++ b/code/mpsitr.c @@ -1,16 +1,14 @@ /* mpsitr.c: MEMORY POOL SYSTEM C INTERFACE LAYER TO TRANSFORMS * * $Id$ - * Copyright 2011 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2011-2022 Ravenbrook Limited. See end of file for license. * - * .purpose: This code bridges between the MPS interface to C to Transforms - * and the internal implementation of Transforms. It is - * analogous to the MPS C Interface Layer , but for the Transforms - * extension. + * .purpose: This code bridges between the MPS interface to transforms in + * and the internal implementation of Transforms in . */ #include "mpm.h" -#include "mpstr.h" +#include "mps.h" #include "trans.h" #include "ss.h" @@ -95,41 +93,29 @@ void mps_transform_destroy(mps_transform_t transform) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2011 Ravenbrook Limited . - * All rights reserved. This is an open source license. Contact - * Ravenbrook for commercial licensing options. - * + * Copyright (C) 2011-2022 Ravenbrook Limited . + * * 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. - * + * 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. - * + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * * 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. + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR 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/code/mpstr.h b/code/mpstr.h deleted file mode 100644 index d718e4ed1e..0000000000 --- a/code/mpstr.h +++ /dev/null @@ -1,66 +0,0 @@ -/* mpstr.h: RAVENBROOK MEMORY POOL SYSTEM C INTERFACE TO TRANSFORMS - * - * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. - * - * .readership: customers, MPS developers. - */ - -#ifndef mpstr_h -#define mpstr_h - -#include "mps.h" - -typedef struct mps_transform_s *mps_transform_t; - -extern mps_res_t mps_transform_create(mps_transform_t *, mps_arena_t); - -extern mps_res_t mps_transform_add_oldnew(mps_transform_t, mps_addr_t *, mps_addr_t *, size_t); - -extern mps_res_t mps_transform_apply(mps_bool_t *, mps_transform_t); - -extern void mps_transform_destroy(mps_transform_t); - -#endif /* mpstr_h */ - - -/* C. COPYRIGHT AND LICENSE - * - * Copyright (C) 2011 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/code/trans.c b/code/trans.c index 565b15800d..ef006a4f02 100644 --- a/code/trans.c +++ b/code/trans.c @@ -1,9 +1,7 @@ -/* trans.c: RAVENBROOK MEMORY POOL SYSTEM TRANSFORMS +/* trans.c: TRANSFORMS IMPLEMENTATION * * $Id$ - * Copyright 2011-2018 Ravenbrook Limited. See end of file for license. - * - * This code is specific to Configura. + * Copyright 2011-2022 Ravenbrook Limited. See end of file for license. * * A transform is a special kind of garbage collection that replaces references * to a set of objects. The transform is piggybacked onto a garbage @@ -230,13 +228,9 @@ static Res transformFix(Seg seg, ScanState ss, Ref *refIO) if (TableLookup(&refNew, transform->oldToNew, (Word)ref)) { if (ss->rank == RankAMBIG) { - /* We rely on the fact that all ambiguous references are fixed before - any others, so no references will have transformed by the time we - abort. - NOTE: Configura CVM prints a message and exits if a transform - fails. See Configura's - //depot/project/cet/kernel/internal/version3.2/cvm/c2/updateHeap2.m#4 - line 133.*/ + /* We rely on the fact that ambiguous references are fixed + first, so that no exact references have been transformed + yet. */ transform->aborted = TRUE; } else { /* NOTE: We could fix refNew in the table before copying it, @@ -296,9 +290,7 @@ Res TransformApply(Bool *appliedReturn, Transform transform) /* If there have been any flips since the transform was created, the old and new pointers will be invalid, since they are not scanned as roots. - NOTE: Configura CVM parks the arena before adding references. See - //depot/project/cet/kernel/internal/version3.2/cvm/c2/updateHeap2.m#4 - line 114. */ + The client program must park the arena before applying the transform. */ if (transform->epoch != ArenaEpoch(arena)) return ResPARAM; @@ -357,41 +349,29 @@ Res TransformApply(Bool *appliedReturn, Transform transform) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2011-2018 Ravenbrook Limited . - * All rights reserved. This is an open source license. Contact - * Ravenbrook for commercial licensing options. - * + * Copyright (C) 2011-2022 Ravenbrook Limited . + * * 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. - * + * 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. - * + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * * 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. + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR 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/code/trans.h b/code/trans.h index 4a19f5fe92..a572138601 100644 --- a/code/trans.h +++ b/code/trans.h @@ -1,7 +1,7 @@ -/* trans.h: RAVENBROOK MEMORY POOL SYSTEM TRANSFORMS +/* trans.h: TRANSFORMS INTERFACE * * $Id$ - * Copyright 2011 Ravenbrook Limited. See end of file for license. + * Copyright 2011-2022 Ravenbrook Limited. See end of file for license. */ #ifndef trans_h @@ -35,41 +35,29 @@ extern Arena TransformArena(Transform transform); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2011 Ravenbrook Limited . - * All rights reserved. This is an open source license. Contact - * Ravenbrook for commercial licensing options. - * + * Copyright (C) 2011-2022 Ravenbrook Limited . + * * 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. - * + * 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. - * + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * * 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. + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR 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/code/ztfm.c b/code/ztfm.c index 8dc81a5be2..2f2c237692 100644 --- a/code/ztfm.c +++ b/code/ztfm.c @@ -1,27 +1,7 @@ -/* ztfm.c: Transform test +/* ztfm.c: Transforms test * * $Id$ - * Copyright (c) 2010 Ravenbrook Limited. See end of file for license. - * Portions copyright (C) 2002 Global Graphics Software. - * - * OBJECTIVE - * - * - * DESIGN OVERVIEW - * - * CODE OVERVIEW - * - * DEPENDENCIES - * - * This test uses the dylan object format, but the reliance on this - * particular format is not great and could be removed. - * - * - * BUGS, FUTURE IMPROVEMENTS, ETC - * - * HISTORY - * - * This code was created by first copying . + * Copyright (c) 2011-2022 Ravenbrook Limited. See end of file for license. */ #include "fmtdy.h" @@ -30,7 +10,6 @@ #include "mpsavm.h" #include "mpscamc.h" #include "mpslib.h" -#include "mpstr.h" #include "testlib.h" #include /* printf */ @@ -93,28 +72,28 @@ struct node_t { /* Tour -- a particular journey to visit to every node in the world * - * A tour starts with the node at world[0], tours the graph reachable - * from it, then any further bits of graph reachable from world[1], + * A tour starts with the node at world[0], tours the graph reachable + * from it, then any further bits of graph reachable from world[1], * and so on. * - * As it does so, the tour computes a tourReport, characterising the + * As it does so, the tour computes a tourReport, characterising the * state of everything reachable (from world) in the form a few numbers. * - * Each tour explores the subgraph rooted at each node exactly once. - * That is: if the tour re-encounters a node already visited on that + * Each tour explores the subgraph rooted at each node exactly once. + * That is: if the tour re-encounters a node already visited on that * tour, it uses the values already computed for that node. * - * The tourIdHash deliberately depends on the order in which nodes are - * encountered. Therefore, the tourIdHash of a tour depends on the - * entirety of the state of the world and all the world-reachable nodes. + * The tourIdHash deliberately depends on the order in which nodes are + * encountered. Therefore, the tourIdHash of a tour depends on the + * entirety of the state of the world and all the world-reachable nodes. * - * The tourVerSum, being a simple sum, does not depend on the order of + * The tourVerSum, being a simple sum, does not depend on the order of * visiting. */ enum { cVer = 10 -}; +}; typedef struct tourReportStruct { ulongest_t tour; /* tour serial */ ulongest_t tourIdHash; /* hash of node ids, computed last tour */ @@ -128,7 +107,7 @@ static ulongest_t tourSerial = 0; static void tourWorld(tourReport tr_o, mps_addr_t *world, ulongest_t countWorld) { ulongest_t i; - + tourSerial += 1; tr_o->tour = tourSerial; tr_o->tourIdHash = 0; @@ -158,15 +137,15 @@ static void tour_subgraph(tourReport tr_o, struct node_t *node) ulongest_t tourIdHash; Insist(tr_o != NULL); - + /* node == NULL is permitted */ if(node == NULL) return; - + tour = tr_o->tour; if(DYI_INT(node->tour_dyi) == tour) return; /* already visited */ - + /* this is a newly discovered node */ Insist(DYI_INT(node->tour_dyi) < tour); @@ -180,13 +159,13 @@ static void tour_subgraph(tourReport tr_o, struct node_t *node) ver = DYI_INT(node->ver_dyi); Insist(ver < cVer); tr_o->acNodesVer[ver] += 1; - + /* tour the subgraphs (NULL is permitted) */ left = node->left; right = node->right; tour_subgraph(tr_o, left); tour_subgraph(tr_o, right); - + /* computed idHash of subgraph at this node */ id = DYI_INT(node->id_dyi); tourIdHashLeft = left ? DYI_INT(left->tourIdHash_dyi) : 0; @@ -216,12 +195,12 @@ static void after(mps_addr_t *world, ulongest_t countWorld, { longest_t dCVerOld; longest_t dCVerNew; - + tourWorld(&trAfter, world, countWorld); dCVerOld = ((long)trAfter.acNodesVer[verOld] - (long)trBefore.acNodesVer[verOld]); dCVerNew = ((long)trAfter.acNodesVer[verNew] - (long)trBefore.acNodesVer[verNew]); - + progressf(("tourWorld: (%"PRIuLONGEST" %"PRIuLONGEST":%"PRIuLONGEST"/%"PRIuLONGEST":%"PRIuLONGEST") -> (%"PRIuLONGEST" %"PRIuLONGEST":%+"PRIdLONGEST"/%"PRIuLONGEST":%+"PRIdLONGEST"), %s\n", trBefore.tourIdHash, verOld, @@ -253,9 +232,9 @@ static mps_res_t mps_arena_transform_objects_list(mps_bool_t *transform_done_o, mps_res_t res; mps_transform_t transform; mps_bool_t applied = FALSE; - + Insist(old_list_count == new_list_count); - + res = mps_transform_create(&transform, mps_arena); if(res == MPS_RES_OK) { /* We have a transform */ @@ -270,7 +249,7 @@ static mps_res_t mps_arena_transform_objects_list(mps_bool_t *transform_done_o, mps_transform_destroy(transform); } } - + /* Always set *transform_done_o (even if there is also a non-ResOK */ /* return code): it is a status report, not a material return. */ *transform_done_o = applied; @@ -293,10 +272,10 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) { /* Test with sets of pre-built nodes, a known distance apart. * - * This gives control over whether new nodes are on the same + * This gives control over whether new nodes are on the same * segment as the olds or not. */ - + ulongest_t iPerset; ulongest_t aPerset[] = {0, 1, 1, 10, 10, 1000, 1000}; ulongest_t cPerset = NELEMS(aPerset); @@ -317,9 +296,9 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) } progressf(("total: %"PRIuLONGEST".\n", countWorld)); Insist(countWorld <= myrootExactCOUNT); - + keepCount = 0; - + for(iPerset = stepPerset; aPerset[iPerset] != 0; iPerset = (iPerset + stepPerset) % cPerset) { @@ -327,7 +306,7 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) ulongest_t first; ulongest_t skip; ulongest_t count; - + perset = aPerset[iPerset]; first = keepCount; skip = 0; @@ -352,11 +331,11 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) /*printf("Object %"PRIuLONGEST" at %p.\n", keepCount, (void*)v);*/ } v = 0; - + /* >=10? pick subset */ if(perset >= 10) { /* subset of [first..first+perset) */ - + skip = (rnd() % (2 * perset)); if(skip > (perset - 1)) skip = 0; @@ -364,12 +343,12 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) count = 1 + rnd() % (2 * (perset - skip)); if(skip + count > perset) count = perset - skip; - + Insist(skip < perset); Insist(count >= 1); Insist(skip + count <= perset); } - + /* >=10? sometimes build tree */ if(perset >= 10 && count >= 4 && rnd() % 2 == 0) { struct node_t **oldNodes = (struct node_t **)&myrootExact[first + skip]; @@ -382,13 +361,13 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) newNodes[j]->right = newNodes[(2 * j) + 1];} } } - + /* transform {count} olds into {count} news */ before(myrootExact, countWorld); /* after(myrootExact, countWorld, 1, 0, 2, 0); */ progressf(("Transform [%"PRIuLONGEST"..%"PRIuLONGEST") to [%"PRIuLONGEST"..%"PRIuLONGEST").\n", first + skip, first + skip + count, first + skip + perset, first + skip + count + perset)); - res = mps_arena_transform_objects_list(&transform_done, arena, + res = mps_arena_transform_objects_list(&transform_done, arena, &myrootExact[first + skip], count, &myrootExact[first + skip + perset], count); Insist(res == MPS_RES_OK); @@ -397,13 +376,13 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) after(myrootExact, countWorld, 1, -(longest_t)count, 2, 0); } } - + { /* Transforming in various situations * * First, make two sets of 1024 nodes. */ - + perset = 1024; Insist(2*perset < myrootExactCOUNT); for(keepCount = 0; keepCount < 2*perset; keepCount++) { @@ -422,16 +401,16 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) } v = 0; - - /* Functions before() and after() checksum the world, and verify + + /* Functions before() and after() checksum the world, and verify * that the expected transform occurred. */ before(myrootExact, perset); after(myrootExact, perset, 1, 0, 2, 0); - /* Don't transform node 0: its ref coincides with a segbase, so + /* Don't transform node 0: its ref coincides with a segbase, so * there are probably ambiguous refs to it on the stack. - * Don't transform last node either: this test code may leave an + * Don't transform last node either: this test code may leave an * ambiguous reference to it on the stack. */ @@ -482,7 +461,7 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) * * **** USES OBJECTS CREATED IN PREVIOUS TEST GROUP **** */ - + mps_transform_t t1; mps_transform_t t2; mps_bool_t applied = FALSE; @@ -683,10 +662,10 @@ static void showStatsAscii(size_t notcon, size_t con, size_t live, size_t alimit ulongest_t a = cols(alimit); ulongest_t count; ulongest_t i; - + /* if we can show alimit within 200 cols, do so */ count = (a < 200) ? a + 1 : c; - + for(i = 0; i < count; i++) { printf( (i == a) ? "A" : (i < n) ? "n" @@ -701,7 +680,7 @@ static void showStatsAscii(size_t notcon, size_t con, size_t live, size_t alimit /* print_M -- print count of bytes as Mebibytes or Megabytes * - * Print as a whole number, "m" for the decimal point, and + * Print as a whole number, "m" for the decimal point, and * then the decimal fraction. * * Input: 208896 @@ -762,7 +741,7 @@ static void get(mps_arena_t arena) cdie(mps_message_get(&message, arena, type), "get"); - + switch(type) { case mps_message_type_gc_start(): { mclockBegin = mps_message_clock(arena, message); @@ -780,7 +759,7 @@ static void get(mps_arena_t arena) size_t alimit = mps_arena_reserved(arena); mclockEnd = mps_message_clock(arena, message); - + printf(" %5"PRIuLONGEST": (%5"PRIuLONGEST")", mclockEnd, mclockEnd - mclockBegin); printf(" Coll End "); @@ -800,34 +779,34 @@ static void get(mps_arena_t arena) break; } } - + mps_message_discard(arena, message); } } /* .catalog: The Catalog client: - * - * This is an MPS client for testing the MPS. It simulates - * converting a multi-page "Catalog" document from a page-description + * + * This is an MPS client for testing the MPS. It simulates + * converting a multi-page "Catalog" document from a page-description * into a bitmap. * - * The intention is that this task will cause memory usage that is - * fairly realistic (much more so than randomly allocated objects - * with random interconnections. The patterns in common with real + * The intention is that this task will cause memory usage that is + * fairly realistic (much more so than randomly allocated objects + * with random interconnections. The patterns in common with real * clients are: - * - the program input and its task are 'fractal', with a + * - the program input and its task are 'fractal', with a * self-similar hierarchy; - * - object allocation is prompted by each successive element of + * - object allocation is prompted by each successive element of * the input/task; - * - objects are often used to store a transformed version of the + * - objects are often used to store a transformed version of the * program input; * - there may be several stages of transformation; - * - at each stage, the old object (holding the untransformed data) + * - at each stage, the old object (holding the untransformed data) * may become dead; - * - sometimes a tree of objects becomes dead once an object at + * - sometimes a tree of objects becomes dead once an object at * some level of the hierarchy has been fully processed; - * - there is more than one hierarchy, and objects in different + * - there is more than one hierarchy, and objects in different * hierarchies interact. * * The entity-relationship diagram is: @@ -836,12 +815,12 @@ static void get(mps_arena_t arena) * | * Palette --------------------< Colour * - * The first hierarchy is a Catalog, containing Pages, each - * containing Articles (bits of artwork etc), each composed of - * Polygons. Each polygon has a single colour. + * The first hierarchy is a Catalog, containing Pages, each + * containing Articles (bits of artwork etc), each composed of + * Polygons. Each polygon has a single colour. * - * The second hierarchy is a top-level Palette, containing Colours. - * Colours (in this client) are expensive, large objects (perhaps + * The second hierarchy is a top-level Palette, containing Colours. + * Colours (in this client) are expensive, large objects (perhaps * because of complex colour modelling or colour blending). * * The things that matter for their effect on MPS behaviour are: @@ -889,7 +868,7 @@ static void CatalogCheck(void) Page = (void *)w; Insist(DYLAN_VECTOR_SLOT(Page, 0) == DYLAN_INT(PageSig)); Pages += 1; - + for(j = 0; j < PageVar; j += 1) { /* retrieve Art from Page */ w = DYLAN_VECTOR_SLOT(Page, PageFix + j); @@ -917,11 +896,11 @@ static void CatalogCheck(void) /* CatalogDo -- make a Catalog and its tree of objects * - * .catalog.broken: this code, when compiled with - * moderate optimization, may have ambiguous interior pointers but - * lack corresponding ambiguous base pointers to MPS objects. This - * means the interior pointers are unmanaged references, and the - * code goes wrong. The hack in poolamc.c#4 cures this, but not very + * .catalog.broken: this code, when compiled with + * moderate optimization, may have ambiguous interior pointers but + * lack corresponding ambiguous base pointers to MPS objects. This + * means the interior pointers are unmanaged references, and the + * code goes wrong. The hack in poolamc.c#4 cures this, but not very * nicely. For further discussion, see: * */ @@ -934,7 +913,7 @@ static void CatalogDo(mps_arena_t arena, mps_ap_t ap) die(make_dylan_vector(&v, ap, CatalogFix + CatalogVar), "Catalog"); DYLAN_VECTOR_SLOT(v, 0) = DYLAN_INT(CatalogSig); Catalog = (void *)v; - + /* store Catalog in root */ myrootExact[CatalogRootIndex] = Catalog; get(arena); @@ -950,10 +929,10 @@ static void CatalogDo(mps_arena_t arena, mps_ap_t ap) /* store Page in Catalog */ DYLAN_VECTOR_SLOT(Catalog, CatalogFix + i) = (mps_word_t)Page; get(arena); - + printf("Page %d: make articles\n", i); fflush(stdout); - + for(j = 0; j < PageVar; j += 1) { die(make_dylan_vector(&v, ap, ArtFix + ArtVar), "Art"); DYLAN_VECTOR_SLOT(v, 0) = DYLAN_INT(ArtSig); @@ -981,8 +960,8 @@ static void CatalogDo(mps_arena_t arena, mps_ap_t ap) /* MakeThing -- make an object of the size requested (in bytes) * - * Any size is accepted. MakeThing may round it up (MakeThing always - * makes a dylan vector, which has a minimum size of 8 bytes). Vector + * Any size is accepted. MakeThing may round it up (MakeThing always + * makes a dylan vector, which has a minimum size of 8 bytes). Vector * slots, if any, are initialized to DYLAN_INT(0). * * After making the object, calls get(), to retrieve MPS messages. @@ -1004,7 +983,7 @@ static void* MakeThing(mps_arena_t arena, mps_ap_t ap, size_t size) slots = words - 2; die(make_dylan_vector(&v, ap, slots), "make_dylan_vector"); get(arena); - + return (void *)v; } @@ -1012,7 +991,7 @@ static void BigdropSmall(mps_arena_t arena, mps_ap_t ap, size_t big, char small_ { static ulongest_t keepCount = 0; ulongest_t i; - + mps_arena_park(arena); for(i = 0; i < 100; i++) { (void) MakeThing(arena, ap, big); @@ -1045,7 +1024,7 @@ static void Make(mps_arena_t arena, mps_ap_t ap, unsigned randm, unsigned keep1i { unsigned keepCount = 0; ulongest_t objCount = 0; - + Insist(keepRootspace <= myrootExactCOUNT); objCount = 0; @@ -1102,7 +1081,7 @@ static void Make(mps_arena_t arena, mps_ap_t ap, unsigned randm, unsigned keep1i static void Rootdrop(char rank_char) { ulongest_t i; - + if(rank_char == 'A') { for(i = 0; i < myrootAmbigCOUNT; ++i) { myrootAmbig[i] = NULL; @@ -1122,11 +1101,11 @@ static void stackwipe(void) { unsigned iw; ulongest_t aw[stackwipedepth]; - + /* http://xkcd.com/710/ */ /* I don't want my friends to stop calling; I just want the */ /* compiler to stop optimising away my code. */ - + /* Do you ever get two even numbers next to each other? Hmmmm :-) */ for(iw = 0; iw < stackwipedepth; iw++) { if((iw & 1) == 0) { @@ -1340,7 +1319,7 @@ static void *testscriptB(void *arg, size_t s) "root_create - exact"); die(mps_ap_create(&ap, amc, mps_rank_exact()), "ap_create"); - + /* root_stackreg: stack & registers are ambiguous roots = mutator's workspace */ stack_start = &stack_starts_here; stack_thr = thr; @@ -1409,7 +1388,7 @@ int main(int argc, char *argv[]) { randomize(argc, argv); mps_lib_assert_fail_install(assert_die); - + /* 1<<19 == 524288 == 1/2 Mebibyte */ /* 16<<20 == 16777216 == 16 Mebibyte */ @@ -1425,41 +1404,29 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2013 Ravenbrook Limited . - * All rights reserved. This is an open source license. Contact - * Ravenbrook for commercial licensing options. - * + * Copyright (C) 2011-2022 Ravenbrook Limited . + * * 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. - * + * 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. - * + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * * 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. + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR 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/design/transform.txt b/design/transform.txt index 68122ca759..503e8b5cd0 100644 --- a/design/transform.txt +++ b/design/transform.txt @@ -1,46 +1,55 @@ -MPS TRANSFORM DESIGN -==================== -Richard Brooksby, Ravenbrook Limited, 2012-09-04 +.. mode: -*- rst -*- -1. Introduction ---------------- +Transforms +========== + +:Tag: design.mps.transform +:Author: Richard Brooksby +:Date: 2012-09-04 +:Status: complete +:Revision: $Id$ +:Copyright: See `Copyright and License`_. +:Index terms: + pair: transforms; design + -This document describes the Transform mechanism of the Memory Pool -System. Transforms allow the client code to replace a set of object -references on the heap. Transforms exist to satisfy a requirement from -Configura, and are not intended to become part of the open source -MPS. +Introduction +------------ + +This document describes the Transform mechanism of the Memory Pool System. +Transforms allow the client code to replace a set of object references on the +heap. The readership of this document is any developer intending to modify the Transform implementation. -This document is Configura client confidential. - -2. Background -------------- +Background +---------- Göran Rydqvist of Configura originally expressed the requirement for the -MPS to support the change of layout of objects in CET [[Crab -2010-02-25][]]. Ravenbrook proposed several methods [[RHSK -2010-09-21][]] but Configura selected idea 1: - -> Make all objects "direct", without IOHeaders. If you need to add fields, -> then use a special new MPS function (that doesn't exist yet): -> -> mps_arena_transform_objects(&my_transform_function); -> -> This traverses the object graph, lets your transform_function basically -> "realloc()" the field-block, and MPS fixes up all references from other -> objects to point to the new field-block. -> -> Unfortunately, this idea is probably killed off by ambiguous references -> :-(. You could only run the patch if you could *guarantee* there are no -> ambiguous refs you want. In other words, any object refs on the stack -> would become instant death (or worse: subtle slow death :-). Therefore we -> don't really like this idea (unfortunately). There are safer and simpler -> ways to do it, we think... +MPS to support the change of layout of objects in CET [GR_2010-02-25]_. +Ravenbrook proposed several methods [RHSK_2010-09-21]_ including: + + If you need to add fields, then use a special new MPS function (that + doesn't exist yet):: + + mps_arena_transform_objects(&my_transform_function); + + This traverses the object graph, lets your transform_function + basically ``realloc()`` the field-block, and MPS fixes up all + references from other objects to point to the new field-block. + + Unfortunately, this idea is probably killed off by ambiguous + references :-(. You could only run the patch if you could + *guarantee* there are no ambiguous refs you want. In other words, + any object refs on the stack would become instant death (or worse: + subtle slow death :-). Therefore we don't really like this idea + (unfortunately). There are safer and simpler ways to do it, we + think... + +which Configura selected [GR_2010-09-22]_. An initial implementation was made by RHSK and released to Configura as "experimental", however Configura put it into production. @@ -49,78 +58,92 @@ During work on adapting the MPS to 64-bit Windows, RB reformed and reimplemented transforms based on RHSK's original work. -3. Overview ------------ +Overview +-------- The client program builds a table mapping "old" references to "new" ones -in a `Transform` object. This is then "applied", causing a garbage +in a ``Transform`` object. This is then "applied", causing a garbage collection trace in which the fix function is substituted by -`transformFix`, which spots "old" references and replaces them with +``transformFix()``, which spots "old" references and replaces them with "new" ones, in addition to applying the usual garbage collection fix function. -4. NOT YET WRITTEN ------------------- +Not yet written +--------------- -Points to cover: +* Ambiguous references and aborting the transform. -* Ambiguous references and aborting the transform +* How ambiguous references are avoided using ``arena->stackWarm``. - * How ambiguous references are avoided using stackAtEnter +* How the implementation is an add-on to the MPS. -* How the implementation is an add-on to the MPS +* How the code is kept separate from the mainstream MPS. -* How the code is kept separate from the mainstream MPS +* Why it has its own hash table implementation (no good reason). -* Why it has its own hash table implementation (no good reason) +* Why it does a garbage collection and not just a transforming scan. -* Why it does a garbage collection and not just a transforming scan +* Nice side-effect is that "old" objects are killed. - * Nice side-effect is that "old" objects are killed +* Why the arena must be parked. -* Why the arena must be parked +References +---------- -A. References -------------- +.. [GR_2010-02-25] + "Incremental object" (e-mail); + Göran Rydqvist; Configura; 2010-02-25; + . -[Crab 2010-02-25]: http://info.ravenbrook.com/mail/2010/02/25/16-35-45/0/ -[Crab 2010-02-25] "Incremental object" (email); Göran Rydqvist; -Configura; 2010-02-25; -. +.. [RHSK_2010-09-21] + "Incremental object ideas" (e-mail); + Richard Kistruck; Ravenbrook Limited; 2010-09-21; + . -[RHSK 2010-09-21]: http://info.ravenbrook.com/mail/2010/09/21/16-54-59/0/ -[RHSK 2010-09-21] "Incremental object ideas" (email); Richard Kistruck; -Ravenbrook Limited; 2010-09-21; -. +.. [GR_2010-09-22] + "Incremental object ideas" (e-mail); + Göran Rydqvist; Configura; 2010-09-22; + . -[Crab 2010-09-22]: http://info.ravenbrook.com/mail/2010/09/22/09-27-53/0/ -[Crab 2010-09-22] "Incremental object ideas" (email); Göran Rydqvist; -Configura; 2010-09-22; -. +Document History +---------------- -B. Document History -------------------- +- 2012-09-04 RB_ First draft. -* 2012-09-04 [RB](mailto:rb@ravenbrook.com) First draft. +- 2022-01-23 GDR_ Converted to reStructuredText. +.. _RB: https://www.ravenbrook.com/consultants/rb/ +.. _GDR: https://www.ravenbrook.com/consultants/gdr/ -C. Copyright and Licence ------------------------- -This document is copyright © 2012 [Ravenbrook Limited](http://www.ravenbrook.com). All rights reserved. This is an -open source license. Contact Ravenbrook for commercial licensing -options. +Copyright and License +--------------------- -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +Copyright © 2012–2020 `Ravenbrook Limited `_. -1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: -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. +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. -3. Redistributions in any form must be accompanied by information on how to obtain complete source code for the 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. +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. -**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.** +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 AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR 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/manual/source/glossary/t.rst b/manual/source/glossary/t.rst index 4d094735d7..879f7a8a54 100644 --- a/manual/source/glossary/t.rst +++ b/manual/source/glossary/t.rst @@ -233,6 +233,15 @@ Memory Management Glossary: T objects are reachable. Those that were not reachable may be :term:`reclaimed`. + transform + + .. mps:specifc:: + + A mapping from old :term:`references` to new references, + represented by :c:type:`mps_transform_t`, that can be applied + to all references managed by the MPS. See + :ref:`topic-transform`. + translation buffer translation lookaside buffer diff --git a/manual/source/release.rst b/manual/source/release.rst index cba65bcc3a..ee9e4475bc 100644 --- a/manual/source/release.rst +++ b/manual/source/release.rst @@ -47,6 +47,11 @@ New features :ref:`topic-scanning-protocol`. This allows the client program to safely update references in the visited objects. +#. The new **transforms** feature updates references throughout the + automatically managed portion of the heap. For some use cases this + may be more convenient than :c:func:`mps_pool_walk`. See + :ref:`topic-transform`. + Interface changes ................. diff --git a/manual/source/topic/index.rst b/manual/source/topic/index.rst index b3139c16fb..c592c98d21 100644 --- a/manual/source/topic/index.rst +++ b/manual/source/topic/index.rst @@ -26,6 +26,7 @@ Reference debugging telemetry weak + transform plinth platform porting diff --git a/manual/source/custom/cet/transform.rst b/manual/source/topic/transform.rst similarity index 56% rename from manual/source/custom/cet/transform.rst rename to manual/source/topic/transform.rst index aa9c27a6d8..75ea1a441d 100644 --- a/manual/source/custom/cet/transform.rst +++ b/manual/source/topic/transform.rst @@ -1,22 +1,26 @@ -.. Sources: - - ``_ - .. index:: single: transform; introduction +.. _topic-transform: + Transforms ========== In a long-running interactive system, it may be desirable to change the format of live objects. In some programming languages (notably Smalltalk), when the programmer edits a class definition, objects belonging to the class must be updated so that they are valid instances of the redefined class. This may involve adding or removing fields from each instance and so changing the size of the allocated objects. -If the object has grown as a result of the redefinition, this redefinition can't be done in-place, so what actually happens is that for each instance of the old version of the class, a corresponding instance of the new version of the class is created, and all references to the old instance are rewritten to refer to the new instance. And discovering "all references" to an object is a task that falls to the garbage collector. +If the object has grown as a result of the redefinition, this +redefinition can't be done in-place, so what actually happens is that +for each instance of the old version of the class, a corresponding +instance of the new version of the class is created, and all +:term:`references` to the old instance are rewritten to refer to the new +instance. Discovering "all references" to an object is a task that falls +to the garbage collector. *Transforms* are a general mechanism by which the client program requests the MPS to replace references to one set of objects (the *old* objects) with references to another (the *new* objects). The MPS performs this task by carrying out a complete garbage collection, in the course of which all references to old objects are discovered and substituted with references to the corresponding new object. -Transform cautions ------------------- +Cautions +-------- 1. The arena must be :term:`parked ` (for example, by calling :c:func:`mps_arena_park`) before creating the transform and @@ -28,32 +32,45 @@ Transform cautions whether or not the reference should be updated to point to the new object.) +.. warning:: + + The second caution means that transforms may be unsuitable for + client programs that treat the :term:`registers` and :term:`control + stack` as a :term:`root`, by using :c:func:`mps_root_create_thread` + and similar functions, unless the program can guarantee that none of + the old references will be referenced by this root. + + An alternative and more robust approach is to segregate the + :term:`formatted objects` that need to be updated into a suitable + :term:`pool`, and iterate over them using the function + :c:func:`mps_pool_walk`. + .. index:: single: transform; interface -Transform interface -------------------- +Interface +--------- :: - #include "mpstr.h" + #include "mps.h" .. c:type:: mps_transform_t - The type of transforms. A transform represents a mapping from - *old* objects to *new* objects. + The type of :term:`transforms`. A transform represents a mapping from *old* + :term:`references` to *new* references. .. c:function:: mps_res_t mps_transform_create(mps_transform_t *transform_o, mps_arena_t arena) - Create an empty transform. + Create an empty :term:`transform`. ``transform_o`` points to a location that will hold the address of the new transform. - ``arena`` is the arena in which to create the transform. + ``arena`` is the :term:`arena` in which to create the transform. :c:func:`mps_transform_create` returns :c:macro:`MPS_RES_OK` if successful. The MPS may exhaust some resource in the course of @@ -71,15 +88,16 @@ Transform interface .. c:function:: mps_res_t mps_transform_add_oldnew(mps_transform_t transform, mps_addr_t *old_array, mps_addr_t *new_array, size_t count) - Add mappings from old to new objects to a transform. + Add mappings from an old :term:`reference` to a new reference to a + :term:`transform`. ``transform`` is the transform to which the mappings will be added. - ``old_array`` points to an array of references to old objects. + ``old_array`` points to an array of old references. - ``new_array`` points to an array of references to new objects. + ``new_array`` points to an array of corresponding new references. - ``count`` is the number of references in each array. + ``count`` is the number of references in both arrays. :c:func:`mps_transform_add_oldnew` returns :c:macro:`MPS_RES_OK` if successful. The MPS may exhaust some resource in the course of @@ -88,45 +106,46 @@ Transform interface .. note:: - An old object must be added at most once to a transform. + Each old reference must be added at most once to a given + transform. .. c:function:: mps_res_t mps_transform_apply(mps_bool_t *applied_o, mps_transform_t transform) - Attempt to apply a transform. + Attempt to apply a :term:`transform`. ``applied_o`` points to a location that will hold a Boolean indicating whether or not the transform was applied. ``transform`` is the transform to apply. - If the arena is currently incapable of applying the transform, - then an appropriate :term:`result code` is returned, and the - location pointed to by ``applied_o`` is not updated. Possible + If the :term:`arena` is currently incapable of applying the + transform, then an appropriate :term:`result code` is returned, and + the location pointed to by ``applied_o`` is not updated. Possible causes include (but are not limited to) the arena not being in the :term:`parked state` (in which case the result code is - :c:macro:`MPS_RES_LIMIT`), or a collection having taken place - since ``transform`` was created (in which case the result code is + :c:macro:`MPS_RES_LIMIT`), or a collection having taken place since + ``transform`` was created (in which case the result code is :c:macro:`MPS_RES_PARAM`). If the arena is *capable* of applying the transform, then the MPS - carries out a garbage collection, the arena is left in the + carries out a :term:`garbage collection`, the arena is left in the :term:`parked state`, :c:func:`mps_transform_apply` returns - :c:macro:`MPS_RES_OK`, and the location pointed to by - ``applied_o`` is updated. + :c:macro:`MPS_RES_OK`, and the location pointed to by ``applied_o`` + is updated. - If in the course of the ambiguous reference was discovered, then - the transform is aborted and ``*applied_o`` is set to false. In - this case, *no* references to the old objects are updated. (That - is, either *all* of the transform is applied, or *none* of it.) + If in the course of the application, an :term:`ambiguous reference` + was discovered, then the transform is aborted and ``*applied_o`` is + set to false. In this case, *no* references to the old objects are + updated. (That is, either *all* of the transform is applied, or + *none* of it.) - If the transform was successfully applied, it is destroyed (as if - :c:func:`mps_transform_destroy` had been called). + If the transform was successfully applied, it is destroyed, as if + :c:func:`mps_transform_destroy` had been called. .. c:function:: void mps_transform_destroy(mps_transform_t transform) - Destroy a transform. + Destroy a :term:`transform`. ``transform`` is the transform to destroy. - From 3ad9c5ff7d55d476f477c48f2cc7d3a3ff9184ca Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Mon, 12 Jun 2023 14:35:11 +0100 Subject: [PATCH 03/23] Fixing type puns found by GCC 11.3.0. --- code/ztfm.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/code/ztfm.c b/code/ztfm.c index 2f2c237692..40074c6a2c 100644 --- a/code/ztfm.c +++ b/code/ztfm.c @@ -351,14 +351,16 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) /* >=10? sometimes build tree */ if(perset >= 10 && count >= 4 && rnd() % 2 == 0) { - struct node_t **oldNodes = (struct node_t **)&myrootExact[first + skip]; - struct node_t **newNodes = (struct node_t **)&myrootExact[first + skip + perset]; + void **oldNodes = &myrootExact[first + skip]; + void **newNodes = &myrootExact[first + skip + perset]; progressf(("Building tree in %"PRIuLONGEST" nodes.\n", count)); for(j = 1; (2 * j) + 1 < count; j++) { - oldNodes[j]->left = oldNodes[2 * j]; - oldNodes[j]->right = oldNodes[(2 * j) + 1]; - if(1){newNodes[j]->left = newNodes[2 * j]; - newNodes[j]->right = newNodes[(2 * j) + 1];} + struct node_t *oldNode = oldNodes[j]; + struct node_t *newNode = newNodes[j]; + oldNode->left = oldNodes[2 * j]; + oldNode->right = oldNodes[(2 * j) + 1]; + newNode->left = newNodes[2 * j]; + newNode->right = newNodes[(2 * j) + 1]; } } From 5b9f759795cc997f7c54b5ba224a33eeaf2a4c49 Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Mon, 12 Jun 2023 14:44:21 +0100 Subject: [PATCH 04/23] Removing custom CET section (containing only transforms) from the manual now that they are part of the main MPS. --- manual/source/custom/cet/index.rst | 9 --------- manual/source/index.rst | 1 - 2 files changed, 10 deletions(-) delete mode 100644 manual/source/custom/cet/index.rst diff --git a/manual/source/custom/cet/index.rst b/manual/source/custom/cet/index.rst deleted file mode 100644 index 33fa940fa9..0000000000 --- a/manual/source/custom/cet/index.rst +++ /dev/null @@ -1,9 +0,0 @@ -.. _custom_cet: - -Custom features for CET -*********************** - -.. toctree:: - :numbered: - - transform diff --git a/manual/source/index.rst b/manual/source/index.rst index b70fe2627f..5d61d583c2 100644 --- a/manual/source/index.rst +++ b/manual/source/index.rst @@ -7,7 +7,6 @@ Memory Pool System guide/index topic/index pool/index - custom/cet/index design/index design/old From 08aaf467cbf8b09c3688e9b63910f5da46f8cbf9 Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Mon, 12 Jun 2023 14:44:35 +0100 Subject: [PATCH 05/23] Fixing typo in Sphinx directive. --- manual/source/glossary/t.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manual/source/glossary/t.rst b/manual/source/glossary/t.rst index 879f7a8a54..b1ea2e1c33 100644 --- a/manual/source/glossary/t.rst +++ b/manual/source/glossary/t.rst @@ -235,7 +235,7 @@ Memory Management Glossary: T transform - .. mps:specifc:: + .. mps:specific:: A mapping from old :term:`references` to new references, represented by :c:type:`mps_transform_t`, that can be applied From 380fbf5e873a4588031036be33b0417727171eee Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Mon, 12 Jun 2023 15:05:58 +0100 Subject: [PATCH 06/23] Avoid creating ambiguous references in stack slots that prevent the transform completing. --- code/ztfm.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/code/ztfm.c b/code/ztfm.c index 40074c6a2c..c8eea6d589 100644 --- a/code/ztfm.c +++ b/code/ztfm.c @@ -355,12 +355,15 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) void **newNodes = &myrootExact[first + skip + perset]; progressf(("Building tree in %"PRIuLONGEST" nodes.\n", count)); for(j = 1; (2 * j) + 1 < count; j++) { - struct node_t *oldNode = oldNodes[j]; - struct node_t *newNode = newNodes[j]; - oldNode->left = oldNodes[2 * j]; - oldNode->right = oldNodes[(2 * j) + 1]; - newNode->left = newNodes[2 * j]; - newNode->right = newNodes[(2 * j) + 1]; + /* You might be tempted to lift some of these gnarly casts + into local variables, but if you do you will probably + create ambiguous references in stack slots and prevent + the transform from succeeding, as observed in Git commit + a7ebcbdf0. */ + ((struct node_t *)oldNodes[j])->left = oldNodes[2 * j]; + ((struct node_t *)oldNodes[j])->right = oldNodes[(2 * j) + 1]; + ((struct node_t *)newNodes[j])->left = newNodes[2 * j]; + ((struct node_t *)newNodes[j])->right = newNodes[(2 * j) + 1]; } } From 9f71e3165d1cd49f4813a2cbe59ae8210aa4b856 Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Wed, 14 Jun 2023 08:47:49 +0100 Subject: [PATCH 07/23] Temporarily disabling ztfm test except on Windows because GCC (in particular) spills ambiguous references to stack and prevents it working properly. --- tool/testcases.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tool/testcases.txt b/tool/testcases.txt index f1022ebc99..721ed9a3f4 100644 --- a/tool/testcases.txt +++ b/tool/testcases.txt @@ -44,7 +44,7 @@ teletest =N interactive walkt0 zcoll =L zmess -ztfm =L +ztfm =L =W ============= ================ ========================================== Key to flags From cec1509244dcd12732c3e610a0ac2210fecf0d76 Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Wed, 14 Jun 2023 09:13:09 +0100 Subject: [PATCH 08/23] Suppressing "may be used uninitialized" warning in rash build with GCC 11.3.0. --- code/trans.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/trans.c b/code/trans.c index ef006a4f02..a19d877c12 100644 --- a/code/trans.c +++ b/code/trans.c @@ -255,7 +255,7 @@ static Res transformFix(Seg seg, ScanState ss, Ref *refIO) static void transformCondemn(void *closure, Word old, void *value) { - Seg seg; + Seg seg = NULL; /* suppress "may be used uninitialized" from GCC 11.3.0 */ GenDesc gen; Bool b; Trace trace = closure; From b0cca65526966c0a733d665b1bdd9f32024877c9 Mon Sep 17 00:00:00 2001 From: Jonathan Holburn Date: Thu, 15 Jun 2023 11:14:22 +0100 Subject: [PATCH 09/23] add FIXMEs to ztfm.c testbench after walkthrough with RB --- code/ztfm.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/code/ztfm.c b/code/ztfm.c index c8eea6d589..56d98013d6 100644 --- a/code/ztfm.c +++ b/code/ztfm.c @@ -1,5 +1,6 @@ /* ztfm.c: Transforms test * + * FIXME: Write stuff * $Id$ * Copyright (c) 2011-2022 Ravenbrook Limited. See end of file for license. */ @@ -14,7 +15,7 @@ #include /* printf */ - +/* FIXME: What's this? */ #define progressf(args) \ printf args; #define Xprogressf(args) \ @@ -59,7 +60,7 @@ static ulongest_t serial = 0; */ struct node_t { - mps_word_t word0; + mps_word_t word0; /* FIXME: what are these fields for? */ mps_word_t word1; mps_word_t serial_dyi; /* unique across every node ever */ mps_word_t id_dyi; /* .id: replacement nodes copy this */ @@ -89,8 +90,11 @@ struct node_t { * * The tourVerSum, being a simple sum, does not depend on the order of * visiting. + * + * FIXME: Why? What role does this have in the test as a whole? */ +/* FIXME: What's this? */ enum { cVer = 10 }; From 521e664e4481134f5d193bc62877226d3e7376e5 Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Fri, 16 Jun 2023 07:43:57 +0100 Subject: [PATCH 10/23] Converting FIXMEs to comments containing what we have deduced about the test, and converting some to TODOs. Linking issues to be resolved. --- code/ztfm.c | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/code/ztfm.c b/code/ztfm.c index 56d98013d6..2a0629cc76 100644 --- a/code/ztfm.c +++ b/code/ztfm.c @@ -1,8 +1,21 @@ /* ztfm.c: Transforms test * - * FIXME: Write stuff * $Id$ * Copyright (c) 2011-2022 Ravenbrook Limited. See end of file for license. + * + * .overview: This test creates data structures and then applies MPS + * transforms (see design.mps.transform) to them and verifies the + * result. It uses various checksums to verify that the structures + * are equivalent before and after the transform. + * + * .issues: TODO: This test has issues and needs refactoring: + * + * - GitHub issue #242 + * "Testing of transforms is unclear and overengineered" + * + * - GitHub issue #243 + * "ztfm.c fails to meet its requirement to test transforms on all + * platforms" */ #include "fmtdy.h" @@ -15,7 +28,8 @@ #include /* printf */ -/* FIXME: What's this? */ +/* Hacky progress output with switches. */ +/* TODO: Tidy this up by extending testlib if necessary. */ #define progressf(args) \ printf args; #define Xprogressf(args) \ @@ -43,7 +57,15 @@ static void get(mps_arena_t arena); static ulongest_t serial = 0; + /* Tree nodes + * + * The node structure is punned with a Dylan vector. The first two + * fields map to the Dylan wrapper and vector length. The fields with + * suffix "dyi" will be tagged as Dylan integers. TODO: Fix this + * somewhat unsafe punning. + * + * TODO: Is this actually a tree or a graph? * * To make a node: * - use next unique serial; @@ -60,8 +82,8 @@ static ulongest_t serial = 0; */ struct node_t { - mps_word_t word0; /* FIXME: what are these fields for? */ - mps_word_t word1; + mps_word_t word0; /* Dylan wrapper pointer */ + mps_word_t word1; /* Dylan vector length */ mps_word_t serial_dyi; /* unique across every node ever */ mps_word_t id_dyi; /* .id: replacement nodes copy this */ mps_word_t ver_dyi; /* .version: distinguish new from old */ @@ -71,7 +93,8 @@ struct node_t { mps_word_t tourIdHash_dyi; /* hash of node ids, computed last tour */ }; -/* Tour -- a particular journey to visit to every node in the world + +/* Tour -- a every node in the world calculating report with hash * * A tour starts with the node at world[0], tours the graph reachable * from it, then any further bits of graph reachable from world[1], @@ -90,14 +113,13 @@ struct node_t { * * The tourVerSum, being a simple sum, does not depend on the order of * visiting. - * - * FIXME: Why? What role does this have in the test as a whole? */ -/* FIXME: What's this? */ +/* Maximum count of node versions. TODO: Should not be an enum. */ enum { cVer = 10 }; + typedef struct tourReportStruct { ulongest_t tour; /* tour serial */ ulongest_t tourIdHash; /* hash of node ids, computed last tour */ From ebe0af12c69d4766e6074bc1ae15b7dff82b3408 Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Fri, 16 Jun 2023 09:14:14 +0100 Subject: [PATCH 11/23] Moving transforms design statements out of leader comment of trans.c, updating, clarifying, and cross-referencing, in response to review . --- code/trace.c | 15 ++++++++------- code/trans.c | 36 ++++++++++++------------------------ design/transform.txt | 16 +++++++++++++--- 3 files changed, 33 insertions(+), 34 deletions(-) diff --git a/code/trace.c b/code/trace.c index 9507eb8f72..0a3d95f451 100644 --- a/code/trace.c +++ b/code/trace.c @@ -1,11 +1,12 @@ /* trace.c: GENERIC TRACER IMPLEMENTATION * * $Id$ - * Copyright (c) 2001-2020 Ravenbrook Limited. + * Copyright (c) 2001-2023 Ravenbrook Limited. * See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * - * .design: . */ + * .design: design.mps.trace. + */ #include "locus.h" #include "mpm.h" @@ -81,10 +82,10 @@ void ScanStateInit(ScanState ss, TraceSet ts, Arena arena, AVERT(Rank, rank); /* white is arbitrary and can't be checked */ - /* NOTE: We can only currently support scanning for a set of traces - with the same fix method. To remove this restriction, it would be - necessary to dispatch to the fix methods of sets of traces in - TraceFix. */ + /* .fix.single: NOTE: We can only currently support scanning for a + set of traces with the same fix method. To remove this + restriction, it would be necessary to dispatch to the fix methods + of sets of traces in TraceFix. See also impl.c.trans.park. */ ss->fix = NULL; ss->fixClosure = NULL; TRACE_SET_ITER(ti, trace, ts, arena) { @@ -1920,7 +1921,7 @@ Res TraceDescribe(Trace trace, mps_lib_FILE *stream, Count depth) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2020 Ravenbrook Limited . + * Copyright (C) 2001-2023 Ravenbrook Limited . * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/code/trans.c b/code/trans.c index a19d877c12..62846ff9f6 100644 --- a/code/trans.c +++ b/code/trans.c @@ -1,30 +1,14 @@ /* trans.c: TRANSFORMS IMPLEMENTATION * * $Id$ - * Copyright 2011-2022 Ravenbrook Limited. See end of file for license. + * Copyright 2011-2023 Ravenbrook Limited. See end of file for license. * - * A transform is a special kind of garbage collection that replaces references - * to a set of objects. The transform is piggybacked onto a garbage - * collection by overriding the fix method for a trace. The mapping used to - * replace the references is built up in a hash table by the client. - * - * - * Rationale - * - * This design was arrived at after some pain. The MPS isn't really designed - * for this kind of thing, and the pools generally assume that they're doing - * a garbage collection when they're asked to condemn, scan, fix, and reclaim - * stuff. This makes it very hard to apply the transform without also doing - * a garbage collection. Changing this would require a significant reworking - * of the MPS to generalise its ideas, and would bloat the pool classes. - * - * - * Assumptions: - * - * - Single-threaded mutator. In fact this code might work if other mutator - * threads are running, since the shield ought to operate correctly. - * However, in this case there can only be one trace running so that the - * correct fix method is chosen by ScanStateInit. + * A transform is a special kind of garbage collection that replaces + * references to a set of objects. The transform is piggybacked onto + * a garbage collection by overriding the fix method for a trace + * (design.mps.trace.fix). The mapping used to replace the references + * is built up in a hash table by the client. See + * design.mps.transform. */ #include "trans.h" @@ -297,6 +281,10 @@ Res TransformApply(Bool *appliedReturn, Transform transform) globals = ArenaGlobals(arena); AVERT(Globals, globals); + /* .park: Parking the arena ensures that there is a trace available + and that no other traces are running, so that the tracer will + dispatch to transformFix correctly. See + impl.c.trace.fix.single. */ ArenaPark(globals); res = TraceCreate(&trace, arena, TraceStartWhyEXTENSION); @@ -349,7 +337,7 @@ Res TransformApply(Bool *appliedReturn, Transform transform) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2011-2022 Ravenbrook Limited . + * Copyright (C) 2011-2023 Ravenbrook Limited . * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/design/transform.txt b/design/transform.txt index 503e8b5cd0..d060152f17 100644 --- a/design/transform.txt +++ b/design/transform.txt @@ -68,6 +68,14 @@ collection trace in which the fix function is substituted by "new" ones, in addition to applying the usual garbage collection fix function. +This design was arrived at after some pain. The MPS isn't really +designed for generalized transformation of the object graph, and the +pools generally assume that they're doing a garbage collection when +they're asked to condemn, scan, fix, and reclaim stuff. This makes it +very hard to apply the transform without also doing a garbage +collection. Changing this would require a significant reworking of +the MPS to generalise its ideas, and would bloat the pool classes. + Not yet written --------------- @@ -86,8 +94,7 @@ Not yet written * Nice side-effect is that "old" objects are killed. -* Why the arena must be parked. - +* Why the arena must be parked (see impl.c.trans.park). References @@ -116,6 +123,9 @@ Document History - 2022-01-23 GDR_ Converted to reStructuredText. +- 2023-06-16 RB_ Updated and improved in order to make Transforms part + of the public MPS. + .. _RB: https://www.ravenbrook.com/consultants/rb/ .. _GDR: https://www.ravenbrook.com/consultants/gdr/ @@ -123,7 +133,7 @@ Document History Copyright and License --------------------- -Copyright © 2012–2020 `Ravenbrook Limited `_. +Copyright © 2012–2023 `Ravenbrook Limited `_. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are From 3b97736d83d9b6167dc9144437884d66a0205cc1 Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Fri, 16 Jun 2023 09:19:52 +0100 Subject: [PATCH 12/23] Clarifying comments on TableCreate in response to review --- code/trans.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/trans.c b/code/trans.c index 62846ff9f6..754d33b6cd 100644 --- a/code/trans.c +++ b/code/trans.c @@ -90,11 +90,11 @@ Res TransformCreate(Transform *transformReturn, Arena arena) AVERT(Transform, transform); res = TableCreate(&transform->oldToNew, - 0, /* don't grow table until TransformAddOldNew */ + 0, /* no point guessing size before TransformAddOldNew */ transformTableAlloc, transformTableFree, transform, - 0, 1); /* these can't be old references */ + 0, 1); /* use invalid refs as special keys */ if (res != ResOK) goto failTable; From 0c3b0affe411f9923b36eb75f297aa517f10be2b Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Fri, 16 Jun 2023 09:26:49 +0100 Subject: [PATCH 13/23] Removing statements that are no longer true or are rendered untrue by publishing transforms, and adding some hints for later writers resolving GitHub issue #245. Response to review . --- design/transform.txt | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/design/transform.txt b/design/transform.txt index d060152f17..96403ce257 100644 --- a/design/transform.txt +++ b/design/transform.txt @@ -84,17 +84,13 @@ Not yet written * How ambiguous references are avoided using ``arena->stackWarm``. -* How the implementation is an add-on to the MPS. - -* How the code is kept separate from the mainstream MPS. - -* Why it has its own hash table implementation (no good reason). - * Why it does a garbage collection and not just a transforming scan. + [This is partly explained in Overview_ above. RB 2023-06-16] * Nice side-effect is that "old" objects are killed. -* Why the arena must be parked (see impl.c.trans.park). +* Why the arena must be parked [When writing this up see + impl.c.trans.park. RB 2023-06-16]. References From d74a0df82bdbb40c3221388bf077167ad998ec66 Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Fri, 16 Jun 2023 09:33:58 +0100 Subject: [PATCH 14/23] Removing mpsitr.c, a relic of custom/cet, and merging its contents into mpsi.c. Reponse to review . --- code/comm.gmk | 1 - code/commpre.nmk | 1 - code/mps.c | 1 - code/mpsi.c | 83 +++++++++++++++++++++++++++++++- code/mpsitr.c | 121 ----------------------------------------------- 5 files changed, 81 insertions(+), 126 deletions(-) delete mode 100644 code/mpsitr.c diff --git a/code/comm.gmk b/code/comm.gmk index 6224ca9806..390ec78550 100644 --- a/code/comm.gmk +++ b/code/comm.gmk @@ -191,7 +191,6 @@ MPMCOMMON = \ meter.c \ mpm.c \ mpsi.c \ - mpsitr.c \ nailboard.c \ policy.c \ pool.c \ diff --git a/code/commpre.nmk b/code/commpre.nmk index 318f705c12..082ea5cc06 100644 --- a/code/commpre.nmk +++ b/code/commpre.nmk @@ -147,7 +147,6 @@ MPMCOMMON=\ [meter] \ [mpm] \ [mpsi] \ - [mpsitr] \ [nailboard] \ [policy] \ [pool] \ diff --git a/code/mps.c b/code/mps.c index 378965605a..e7872b3c14 100644 --- a/code/mps.c +++ b/code/mps.c @@ -81,7 +81,6 @@ #include "vm.c" #include "policy.c" #include "trans.c" -#include "mpsitr.c" /* Additional pool classes */ diff --git a/code/mpsi.c b/code/mpsi.c index 5f19333639..10e26ab07a 100644 --- a/code/mpsi.c +++ b/code/mpsi.c @@ -1,7 +1,7 @@ /* mpsi.c: MEMORY POOL SYSTEM C INTERFACE LAYER * * $Id$ - * Copyright (c) 2001-2020 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2023 Ravenbrook Limited. See end of file for license. * Portions copyright (c) 2002 Global Graphics Software. * * .purpose: This code bridges between the MPS interface to C, @@ -47,6 +47,7 @@ #include "mpm.h" #include "mps.h" #include "sac.h" +#include "trans.h" #include @@ -2081,9 +2082,87 @@ void _mps_args_set_key(mps_arg_s args[MPS_ARGS_MAX], unsigned i, } +/* Transforms */ + + +mps_res_t mps_transform_create(mps_transform_t *mps_transform_o, + mps_arena_t arena) +{ + Transform transform = NULL; + Res res; + + AVER(mps_transform_o != NULL); + + ArenaEnter(arena); + res = TransformCreate(&transform, arena); + ArenaLeave(arena); + if (res != ResOK) + return res; + + *mps_transform_o = (mps_transform_t)transform; + return MPS_RES_OK; +} + + +mps_res_t mps_transform_add_oldnew(mps_transform_t transform, + mps_addr_t *mps_old_list, + mps_addr_t *mps_new_list, + size_t mps_count) +{ + Ref *old_list = (Ref *)mps_old_list; + Ref *new_list = (Ref *)mps_new_list; + Count count = mps_count; + Arena arena; + Res res; + + AVER(mps_old_list != NULL); + AVER(mps_new_list != NULL); + /* count: cannot check */ + + arena = TransformArena(transform); + + ArenaEnter(arena); + res = TransformAddOldNew(transform, old_list, new_list, count); + ArenaLeave(arena); + + return res; +} + + +mps_res_t mps_transform_apply(mps_bool_t *applied_o, + mps_transform_t transform) +{ + Arena arena; + Res res; + + AVER(applied_o != NULL); + + arena = TransformArena(transform); + ArenaEnter(arena); + STACK_CONTEXT_BEGIN(arena) { + res = TransformApply(applied_o, transform); + } STACK_CONTEXT_END(arena); + ArenaLeave(arena); + + return res; +} + + +void mps_transform_destroy(mps_transform_t transform) +{ + Arena arena; + + arena = TransformArena(transform); + + ArenaEnter(arena); + TransformDestroy(transform); + ArenaLeave(arena); +} + + /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2020 Ravenbrook Limited . + * Copyright (C) 2001-2023 Ravenbrook Limited . * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/code/mpsitr.c b/code/mpsitr.c deleted file mode 100644 index 522d809969..0000000000 --- a/code/mpsitr.c +++ /dev/null @@ -1,121 +0,0 @@ -/* mpsitr.c: MEMORY POOL SYSTEM C INTERFACE LAYER TO TRANSFORMS - * - * $Id$ - * Copyright (c) 2011-2022 Ravenbrook Limited. See end of file for license. - * - * .purpose: This code bridges between the MPS interface to transforms in - * and the internal implementation of Transforms in . - */ - -#include "mpm.h" -#include "mps.h" -#include "trans.h" -#include "ss.h" - - -SRCID(mpsitr, "$Id$"); - - -mps_res_t mps_transform_create(mps_transform_t *mps_transform_o, - mps_arena_t arena) -{ - Transform transform = NULL; - Res res; - - AVER(mps_transform_o != NULL); - - ArenaEnter(arena); - res = TransformCreate(&transform, arena); - ArenaLeave(arena); - if (res != ResOK) - return res; - - *mps_transform_o = (mps_transform_t)transform; - return MPS_RES_OK; -} - - -mps_res_t mps_transform_add_oldnew(mps_transform_t transform, - mps_addr_t *mps_old_list, - mps_addr_t *mps_new_list, - size_t mps_count) -{ - Ref *old_list = (Ref *)mps_old_list; - Ref *new_list = (Ref *)mps_new_list; - Count count = mps_count; - Arena arena; - Res res; - - AVER(mps_old_list != NULL); - AVER(mps_new_list != NULL); - /* count: cannot check */ - - arena = TransformArena(transform); - - ArenaEnter(arena); - res = TransformAddOldNew(transform, old_list, new_list, count); - ArenaLeave(arena); - - return res; -} - - -mps_res_t mps_transform_apply(mps_bool_t *applied_o, - mps_transform_t transform) -{ - Arena arena; - Res res; - - AVER(applied_o != NULL); - - arena = TransformArena(transform); - ArenaEnter(arena); - STACK_CONTEXT_BEGIN(arena) { - res = TransformApply(applied_o, transform); - } STACK_CONTEXT_END(arena); - ArenaLeave(arena); - - return res; -} - - -void mps_transform_destroy(mps_transform_t transform) -{ - Arena arena; - - arena = TransformArena(transform); - - ArenaEnter(arena); - TransformDestroy(transform); - ArenaLeave(arena); -} - - -/* C. COPYRIGHT AND LICENSE - * - * Copyright (C) 2011-2022 Ravenbrook Limited . - * - * 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. - * - * 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 AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR 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. - */ From dc6feb3fb954f1e2325710caae980d4e354cd58d Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Fri, 16 Jun 2023 09:43:52 +0100 Subject: [PATCH 15/23] Adding spaces to conform to rule.code.style.control. --- code/trans.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/trans.c b/code/trans.c index 754d33b6cd..bce12e50d4 100644 --- a/code/trans.c +++ b/code/trans.c @@ -162,12 +162,12 @@ Res TransformAddOldNew(Transform transform, if (res != ResOK) return res; - for(i = 0; i < count; ++i) { + for (i = 0; i < count; ++i) { /* NOTE: If the mutator isn't adding references while the arena is parked, we might need to access the client-provided lists, using ArenaRead. */ - if(old_list[i] == NULL) + if (old_list[i] == NULL) continue; /* permitted, but no transform to do */ - if(old_list[i] == new_list[i]) + if (old_list[i] == new_list[i]) continue; /* ignore identity-transforms */ /* Old refs must be in managed memory. */ From 54e34edc8e2c9adac8a724ae4f4910d989b39eb8 Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Fri, 16 Jun 2023 09:58:57 +0100 Subject: [PATCH 16/23] Marking up list of causes as a list, for clarity, in response to review --- manual/source/topic/transform.rst | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/manual/source/topic/transform.rst b/manual/source/topic/transform.rst index 75ea1a441d..b2a5d64981 100644 --- a/manual/source/topic/transform.rst +++ b/manual/source/topic/transform.rst @@ -6,7 +6,13 @@ Transforms ========== -In a long-running interactive system, it may be desirable to change the format of live objects. In some programming languages (notably Smalltalk), when the programmer edits a class definition, objects belonging to the class must be updated so that they are valid instances of the redefined class. This may involve adding or removing fields from each instance and so changing the size of the allocated objects. +In a long-running interactive system, it may be desirable to change +the format of live objects. In some programming languages (notably +Smalltalk), when the programmer edits a class definition, objects +belonging to the class must be updated so that they are valid +instances of the redefined class. This may involve adding or removing +fields from each instance and so changing the size of the allocated +objects. If the object has grown as a result of the redefinition, this redefinition can't be done in-place, so what actually happens is that @@ -16,7 +22,12 @@ instance of the new version of the class is created, and all instance. Discovering "all references" to an object is a task that falls to the garbage collector. -*Transforms* are a general mechanism by which the client program requests the MPS to replace references to one set of objects (the *old* objects) with references to another (the *new* objects). The MPS performs this task by carrying out a complete garbage collection, in the course of which all references to old objects are discovered and substituted with references to the corresponding new object. +*Transforms* are a general mechanism by which the client program + requests the MPS to replace references to one set of objects (the + *old* objects) with references to another (the *new* objects). The + MPS performs this task by carrying out a complete garbage collection, + in the course of which all references to old objects are discovered + and substituted with references to the corresponding new object. Cautions @@ -122,11 +133,13 @@ Interface If the :term:`arena` is currently incapable of applying the transform, then an appropriate :term:`result code` is returned, and the location pointed to by ``applied_o`` is not updated. Possible - causes include (but are not limited to) the arena not being in the - :term:`parked state` (in which case the result code is - :c:macro:`MPS_RES_LIMIT`), or a collection having taken place since - ``transform`` was created (in which case the result code is - :c:macro:`MPS_RES_PARAM`). + causes include (but are not limited to): + + - the arena not being in the :term:`parked state` (in which case + the result code is :c:macro:`MPS_RES_LIMIT`) + + - a collection having taken place since ``transform`` was created + (in which case the result code is :c:macro:`MPS_RES_PARAM`). If the arena is *capable* of applying the transform, then the MPS carries out a :term:`garbage collection`, the arena is left in the From 0eeefadc3bbd2c00a39d57b48baeb05a99b73412 Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Fri, 16 Jun 2023 11:24:41 +0100 Subject: [PATCH 17/23] Clarifying relationship between mps_pool_walk and transforms with brief description of purpose and links, in response to review . --- manual/source/release.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/manual/source/release.rst b/manual/source/release.rst index 12080fbc35..cabf9158f2 100644 --- a/manual/source/release.rst +++ b/manual/source/release.rst @@ -42,15 +42,15 @@ New features experimental: the implementation is likely to change in future versions of the MPS. See :ref:`design-monitor`. +#. The newly-public **transforms** feature updates references to a set + of objects throughout the automatically managed portion of the + heap, allowing them to be replaced by new versions. See + :ref:`topic-transform`. + #. The new function :c:func:`mps_pool_walk` visits all areas of :term:`formatted objects` in a pool using the - :ref:`topic-scanning-protocol`. This allows the client program to - safely update references in the visited objects. - -#. The new **transforms** feature updates references throughout the - automatically managed portion of the heap. For some use cases this - may be more convenient than :c:func:`mps_pool_walk`. See - :ref:`topic-transform`. + :ref:`topic-scanning-protocol`, support hot reloading and + serialization. See :ref:`design-walk`. #. A :term:`virtual memory arena` can now be configured to call functions when it acquires a new chunk of :term:`address space`, From d769826ccb1b1ce11177848de3e03e187394af9d Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Fri, 16 Jun 2023 11:58:34 +0100 Subject: [PATCH 18/23] Transforms are no longer implicitly destroyed by applying them, making the interface more consistent with the rest of the MPS and simplifying the documentation. In response to review . --- code/trans.c | 8 +------ code/ztfm.c | 39 +++++++++++++++++++------------ manual/source/topic/transform.rst | 6 ++--- 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/code/trans.c b/code/trans.c index bce12e50d4..a8821d48e0 100644 --- a/code/trans.c +++ b/code/trans.c @@ -323,13 +323,7 @@ Res TransformApply(Bool *appliedReturn, Transform transform) ArenaPark(globals); done: - if (transform->aborted) { - *appliedReturn = FALSE; - } else { - *appliedReturn = TRUE; - /* I'm not sure why the interface is defined this way. RB 2012-08-03 */ - TransformDestroy(transform); - } + *appliedReturn = !transform->aborted; return ResOK; } diff --git a/code/ztfm.c b/code/ztfm.c index 2a0629cc76..f2f00d0cab 100644 --- a/code/ztfm.c +++ b/code/ztfm.c @@ -268,12 +268,8 @@ static mps_res_t mps_arena_transform_objects_list(mps_bool_t *transform_done_o, if(res == MPS_RES_OK) { res = mps_transform_apply(&applied, transform); } - if(applied) { - /* Transform has been destroyed */ - Insist(res == MPS_RES_OK); - } else { - mps_transform_destroy(transform); - } + Insist(!applied || res == MPS_RES_OK); + mps_transform_destroy(transform); } /* Always set *transform_done_o (even if there is also a non-ResOK */ @@ -516,6 +512,20 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) res = mps_transform_apply(&applied, t1); Insist(res == MPS_RES_OK); Insist(applied); + mps_transform_destroy(t1); + t1 = NULL; + after(myrootExact, perset, 1, 0, 2, 0); + + /* Apply twice */ + before(myrootExact, perset); + res = mps_transform_create(&t1, arena); + Insist(res == MPS_RES_OK); + res = mps_transform_apply(&applied, t1); + Insist(res == MPS_RES_OK); + Insist(applied); + res = mps_transform_apply(&applied, t1); + Insist(res == MPS_RES_PARAM); + mps_transform_destroy(t1); t1 = NULL; after(myrootExact, perset, 1, 0, 2, 0); @@ -531,6 +541,7 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) res = mps_transform_apply(&applied, t1); Insist(res == MPS_RES_OK); Insist(applied); + mps_transform_destroy(t1); t1 = NULL; after(myrootExact, perset, 1, 0, 2, 0); @@ -556,6 +567,7 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) res = mps_transform_apply(&applied, t1); Insist(res == MPS_RES_OK); Insist(applied); + mps_transform_destroy(t1); t1 = NULL; after(myrootExact, perset, 1, -11, 2, +11); @@ -571,6 +583,7 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) res = mps_transform_apply(&applied, t1); Insist(res == MPS_RES_OK); Insist(applied); + mps_transform_destroy(t1); t1 = NULL; after(myrootExact, perset, 1, -14, 2, +14); @@ -588,8 +601,10 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) res = mps_transform_apply(&applied, t2); Insist(res == MPS_RES_OK); Insist(applied); + mps_transform_destroy(t2); t2 = NULL; mps_transform_destroy(t1); + t1 = NULL; after(myrootExact, perset, 1, -10, 2, +10); /* Two transforms, both live [-- not supported yet. RHSK 2010-12-16] */ @@ -606,18 +621,11 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) res = mps_transform_apply(&applied, t2); Insist(res == MPS_RES_OK); Insist(applied); + mps_transform_destroy(t2); t2 = NULL; + /* TODO: This test block does not destroy t1. Why is that? RB 2023-06-16 */ k = l; after(myrootExact, perset, 1, -10, 2, +10); - - /* Attempt to destroy after applied. */ - before(myrootExact, perset); - res = mps_transform_create(&t1, arena); - Insist(res == MPS_RES_OK); - res = mps_transform_apply(&applied, t1); - Insist(res == MPS_RES_OK); - Insist(applied); - after(myrootExact, perset, 1, 0, 2, 0); } /* Large number of objects */ @@ -663,6 +671,7 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) } res = mps_transform_apply(&applied, t); Insist(applied); + mps_transform_destroy(t); Insist(myrootExact[old] == myrootExact[new]); after(myrootExact, perset, 1, -(longest_t)count, 2, +(longest_t)count); } diff --git a/manual/source/topic/transform.rst b/manual/source/topic/transform.rst index b2a5d64981..1d19809aec 100644 --- a/manual/source/topic/transform.rst +++ b/manual/source/topic/transform.rst @@ -153,12 +153,12 @@ Interface updated. (That is, either *all* of the transform is applied, or *none* of it.) - If the transform was successfully applied, it is destroyed, as if - :c:func:`mps_transform_destroy` had been called. + The transform can only be applied once, and should be destroyed + after use, using :c:func:`mps_transform_destroy`. .. c:function:: void mps_transform_destroy(mps_transform_t transform) - Destroy a :term:`transform`. + Destroy a :term:`transform`, allowing its resources to be recycled. ``transform`` is the transform to destroy. From 38b78128f36b4593a8659da3ffdea08436e51cbb Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Fri, 16 Jun 2023 12:29:06 +0100 Subject: [PATCH 19/23] Clarifying why and documenting that transformed references must be in automatic pools, in response to review . --- code/trans.c | 5 ++++- design/transform.txt | 3 +++ manual/source/topic/transform.rst | 4 +++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/code/trans.c b/code/trans.c index a8821d48e0..3fb0d2c304 100644 --- a/code/trans.c +++ b/code/trans.c @@ -170,7 +170,10 @@ Res TransformAddOldNew(Transform transform, if (old_list[i] == new_list[i]) continue; /* ignore identity-transforms */ - /* Old refs must be in managed memory. */ + /* .old-white: Old refs must be in managed memory, because + transformFix is only reached when a reference is to something + in the condemned set. Other referenes are eliminated by + TraceFix, and we can't (currently) transformation of them. */ { Seg seg; AVER(SegOfAddr(&seg, transform->arena, old_list[i])); diff --git a/design/transform.txt b/design/transform.txt index 96403ce257..5767283e88 100644 --- a/design/transform.txt +++ b/design/transform.txt @@ -92,6 +92,9 @@ Not yet written * Why the arena must be parked [When writing this up see impl.c.trans.park. RB 2023-06-16]. +* Why we can't transform arbitrary references (see + impl.c.trans.old-white). + References ---------- diff --git a/manual/source/topic/transform.rst b/manual/source/topic/transform.rst index 1d19809aec..9381603436 100644 --- a/manual/source/topic/transform.rst +++ b/manual/source/topic/transform.rst @@ -104,7 +104,9 @@ Interface ``transform`` is the transform to which the mappings will be added. - ``old_array`` points to an array of old references. + ``old_array`` points to an array of old references, all of which + must be to objects in pools whose blocks are automatically managed + (see :ref:`pool-properties`). ``new_array`` points to an array of corresponding new references. From 66634595514aa7101dc444b094b1d0548a992e4a Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Fri, 16 Jun 2023 12:48:42 +0100 Subject: [PATCH 20/23] Enforcing parking restrictions when adding references to transforms in response to review . --- code/trans.c | 13 ++++++++++--- design/transform.txt | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/code/trans.c b/code/trans.c index 3fb0d2c304..2fa6bac059 100644 --- a/code/trans.c +++ b/code/trans.c @@ -152,19 +152,26 @@ Res TransformAddOldNew(Transform transform, Res res; Index i; Count added = 0; + Arena arena; AVERT(Transform, transform); AVER(old_list != NULL); AVER(new_list != NULL); /* count: cannot check */ - + + /* .assume.parked: If the mutator isn't adding references while the + arena is parked, we might need to access the client-provided + lists (old_list, new_list), using ArenaRead. Insisting on + parking keeps things simple. */ + arena = transform->arena; + AVER(ArenaGlobals(arena)->clamped); + AVER(arena->busyTraces == TraceSetEMPTY); + res = TableGrow(transform->oldToNew, count); if (res != ResOK) return res; for (i = 0; i < count; ++i) { - /* NOTE: If the mutator isn't adding references while the arena is parked, - we might need to access the client-provided lists, using ArenaRead. */ if (old_list[i] == NULL) continue; /* permitted, but no transform to do */ if (old_list[i] == new_list[i]) diff --git a/design/transform.txt b/design/transform.txt index 5767283e88..0975215a01 100644 --- a/design/transform.txt +++ b/design/transform.txt @@ -90,7 +90,7 @@ Not yet written * Nice side-effect is that "old" objects are killed. * Why the arena must be parked [When writing this up see - impl.c.trans.park. RB 2023-06-16]. + impl.c.trans.park and impl.c.trans.assume.parked. RB 2023-06-16]. * Why we can't transform arbitrary references (see impl.c.trans.old-white). From 0b27a6c59b40fa541dddf06297170f60ea40a745 Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Fri, 16 Jun 2023 13:02:43 +0100 Subject: [PATCH 21/23] Removing bogus test block introduced in d769826cc that does not work when transforms are empty or trivial. Raising this for later in GitHub issue #242. --- code/ztfm.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/code/ztfm.c b/code/ztfm.c index f2f00d0cab..f5a497130c 100644 --- a/code/ztfm.c +++ b/code/ztfm.c @@ -516,19 +516,6 @@ static void Transform(mps_arena_t arena, mps_ap_t ap) t1 = NULL; after(myrootExact, perset, 1, 0, 2, 0); - /* Apply twice */ - before(myrootExact, perset); - res = mps_transform_create(&t1, arena); - Insist(res == MPS_RES_OK); - res = mps_transform_apply(&applied, t1); - Insist(res == MPS_RES_OK); - Insist(applied); - res = mps_transform_apply(&applied, t1); - Insist(res == MPS_RES_PARAM); - mps_transform_destroy(t1); - t1 = NULL; - after(myrootExact, perset, 1, 0, 2, 0); - /* Identity-transform */ before(myrootExact, perset); res = mps_transform_create(&t1, arena); From 24417c20f608558099a94837c1a4e1de3ecb3f8d Mon Sep 17 00:00:00 2001 From: Richard Brooksby Date: Fri, 16 Jun 2023 13:39:42 +0100 Subject: [PATCH 22/23] Adding design.mps.transform to design index. --- design/index.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/design/index.txt b/design/index.txt index ab28baaa74..fcd59cf587 100644 --- a/design/index.txt +++ b/design/index.txt @@ -108,6 +108,7 @@ testthr_ Multi-threaded testing thread-manager_ Thread manager thread-safety_ Thread safety in the MPS trace_ Tracer +transform_ Transforms type_ General MPS types version-library_ Library version mechanism vm_ Virtual mapping @@ -185,6 +186,7 @@ writef_ The WriteF function .. _thread-manager: thread-manager .. _thread-safety: thread-safety .. _trace: trace +.. _transform: transform .. _type: type .. _version-library: version-library .. _vm: vm @@ -226,6 +228,7 @@ Document History - 2016-03-22 RB_ Add write-barier. - 2016-03-27 RB_ Goodbye pool MV *sniff*. - 2020-08-31 GDR_ Add walk. +- 2023-06-16 RB_ Add transform. .. _RB: https://www.ravenbrook.com/consultants/rb .. _NB: https://www.ravenbrook.com/consultants/nb @@ -236,7 +239,7 @@ Document History Copyright and License --------------------- -Copyright © 2002–2020 `Ravenbrook Limited `_. +Copyright © 2002–2023 `Ravenbrook Limited `_. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are From 8b169cfe9eea6122fa556ed6e5d89749924c806d Mon Sep 17 00:00:00 2001 From: Jonathan Holburn Date: Sat, 17 Jun 2023 01:47:47 +0100 Subject: [PATCH 23/23] remove ambiguity and add clarity about the set of reasons that a transform may fail and the possible set of return codes --- manual/source/topic/transform.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manual/source/topic/transform.rst b/manual/source/topic/transform.rst index 9381603436..22b82fe2a2 100644 --- a/manual/source/topic/transform.rst +++ b/manual/source/topic/transform.rst @@ -135,7 +135,7 @@ Interface If the :term:`arena` is currently incapable of applying the transform, then an appropriate :term:`result code` is returned, and the location pointed to by ``applied_o`` is not updated. Possible - causes include (but are not limited to): + causes are: - the arena not being in the :term:`parked state` (in which case the result code is :c:macro:`MPS_RES_LIMIT`)