/* nailboard.c: NAILBOARD IMPLEMENTATION * * $Id$ * Copyright (c) 2014 Ravenbrook Limited. See end of file for license. * * .sources: . */ #include "bt.h" #include "check.h" #include "mpm.h" #include "nailboard.h" SRCID(nailboard, "$Id$"); /* Log2 of scale factor between levels. See . */ #define LEVEL_SHIFT MPS_WORD_SHIFT /* nailboardLevels -- return the number of levels in a nailboard with * the given number of nails. * * See */ static Count nailboardLevels(Count nails) { return (SizeFloorLog2((Size)nails) + LEVEL_SHIFT) / LEVEL_SHIFT; } /* nailboardNails -- return the total number of nails in the board */ static Count nailboardNails(Nailboard board) { return RangeSize(&board->range) >> board->alignShift; } /* nailboardLevelBits -- return the number of bits in the bit table * for the given level. */ static Count nailboardLevelBits(Count nails, Index level) { Shift shift = (Shift)(level * LEVEL_SHIFT); return (nails + ((Count)1 << shift) - 1) >> shift; } Bool NailboardCheck(Nailboard board) { Index i; Count nails; CHECKS(Nailboard, board); CHECKL(RangeCheck(&board->range)); CHECKL(0 < board->levels); nails = nailboardNails(board); CHECKL(board->levels == nailboardLevels(nails)); CHECKL(nails == nailboardLevelBits(nails, 0)); CHECKL(nailboardLevelBits(nails, board->levels - 1) != 0); CHECKL(nailboardLevelBits(nails, board->levels) == 1); CHECKL(BoolCheck(board->newNails)); for (i = 0; i < board->levels; ++i) { CHECKL(board->level[i] != NULL); } return TRUE; } /* nailboardStructSize -- return the size of the nailboard structure, * plus the array of pointers to levels. */ static Size nailboardStructSize(Count levels) { return offsetof(NailboardStruct, level) + sizeof(BT *) * levels; } /* nailboardSize -- return the total size of the nailboard * * This is the size of the nailboard structure plus the combined sizes * of the bit tables. */ static Size nailboardSize(Count nails, Count levels) { Index i; Size size; size = nailboardStructSize(levels); for (i = 0; i < levels; ++i) { size += BTSize(nailboardLevelBits(nails, i)); } return size; } /* NailboardCreate -- allocate a nailboard * * Allocate a nailboard in the control pool for arena, to cover the * range of addresses from base to limit (which must be non-empty). If * successful, set *boardReturn to point to the nailboard and return * ResOK. Otherwise, return a result code to indicate failure. * * alignment specifies the granularity of the nails: that is, the * number of bytes covered by each nail. */ Res NailboardCreate(Nailboard *boardReturn, Arena arena, Align alignment, Addr base, Addr limit) { void *p; Nailboard board; Shift alignShift; Count nails, levels; Index i; Res res; AVER(boardReturn != NULL); AVERT(Arena, arena); AVERT(Align, alignment); AVER(base < limit); AVER(AddrIsAligned(base, alignment)); AVER(AddrIsAligned(limit, alignment)); alignShift = SizeLog2((Size)alignment); nails = AddrOffset(base, limit) >> alignShift; levels = nailboardLevels(nails); res = ControlAlloc(&p, arena, nailboardSize(nails, levels)); if (res != ResOK) return res; board = p; RangeInit(&board->range, base, limit); board->levels = levels; board->alignShift = alignShift; board->newNails = FALSE; p = PointerAdd(p, nailboardStructSize(levels)); for (i = 0; i < levels; ++i) { Count levelBits = nailboardLevelBits(nails, i); AVER(levelBits > 0); board->level[i] = p; BTResRange(board->level[i], 0, levelBits); p = PointerAdd(p, BTSize(levelBits)); } board->sig = NailboardSig; AVERT(Nailboard, board); *boardReturn = board; return ResOK; } /* NailboardDestroy -- destroy a nailboard */ void NailboardDestroy(Nailboard board, Arena arena) { Count nails; Size size; AVERT(Nailboard, board); AVERT(Arena, arena); nails = nailboardNails(board); size = nailboardSize(nails, board->levels); board->sig = SigInvalid; ControlFree(arena, board, size); } /* NailboardClearNewNails -- clear the "new nails" flag */ void (NailboardClearNewNails)(Nailboard board) { AVERT(Nailboard, board); NailboardClearNewNails(board); } /* NailboardNewNails -- return the "new nails" flag * * Return TRUE if any new nails have been set in the nailboard since * the last call to NailboardClearNewNails (or since the nailboard was * created, if there have never been any such calls), FALSE otherwise. */ Bool (NailboardNewNails)(Nailboard board) { AVERT(Nailboard, board); return NailboardNewNails(board); } /* nailboardIndex -- return the index of the nail corresponding to * addr in the given level. */ static Index nailboardIndex(Nailboard board, Index level, Addr addr) { Index i = AddrOffset(RangeBase(&board->range), addr) >> (board->alignShift + level * LEVEL_SHIFT); AVER_CRITICAL(i < nailboardLevelBits(nailboardNails(board), level)); return i; } /* nailboardAddr -- return the address corresponding to the index in * the given level. */ static Addr nailboardAddr(Nailboard board, Index level, Index index) { return AddrAdd(RangeBase(&board->range), index << (board->alignShift + level * LEVEL_SHIFT)); } /* nailboardIndexRange -- update *ibaseReturn and *ilimitReturn to be * the interval of indexes of nails in the given level, corresponding * to the interval of addresses base and limit. See * . */ static void nailboardIndexRange(Index *ibaseReturn, Index *ilimitReturn, Nailboard board, Index level, Addr base, Addr limit) { *ibaseReturn = nailboardIndex(board, level, base); *ilimitReturn = nailboardIndex(board, level, AddrSub(limit, 1)) + 1; } /* NailboardGet -- return nail corresponding to address * * Return the nail in the nailboard corresponding to the address addr. * It is an error if addr does not lie in the range of addresses * covered by the nailboard. */ Bool NailboardGet(Nailboard board, Addr addr) { AVERT(Nailboard, board); AVER(RangeContains(&board->range, addr)); return BTGet(board->level[0], nailboardIndex(board, 0, addr)); } /* NailboardSet -- set nail corresponding to address * * Set the nail in the nailboard corresponding to the address addr. * Return the old nail at that position. It is an error if addr does * not lie in the range of addresses covered by the nailboard. * * This function is on the critical path because it is called for * every fix of an ambiguous reference to an address in an AMC pool. */ Bool NailboardSet(Nailboard board, Addr addr) { Index i, j; AVERT_CRITICAL(Nailboard, board); AVER_CRITICAL(RangeContains(&board->range, addr)); j = nailboardIndex(board, 0, addr); if (BTGet(board->level[0], j)) return TRUE; board->newNails = TRUE; BTSet(board->level[0], j); for (i = 1; i < board->levels; ++i) { j = nailboardIndex(board, i, addr); if (BTGet(board->level[i], j)) break; BTSet(board->level[i], j); } return FALSE; } /* NailboardSetRange -- set all nails in range * * Set all nails in the nailboard corresponding to the range between * base and limit. It is an error if any part of the range is not * covered by the nailboard, or if any nail in the range is set. */ void NailboardSetRange(Nailboard board, Addr base, Addr limit) { Index i, ibase, ilimit; nailboardIndexRange(&ibase, &ilimit, board, 0, base, limit); AVER(BTIsResRange(board->level[0], ibase, ilimit)); BTSetRange(board->level[0], ibase, ilimit); for (i = 1; i < board->levels; ++i) { nailboardIndexRange(&ibase, &ilimit, board, i, base, limit); BTSetRange(board->level[i], ibase, ilimit); } } /* NailboardIsSetRange -- test if all nails are set in a range * * Return TRUE if all nails are set in the range between base and * limit, or FALSE if any nail is unset. It is an error if any part of * the range is not covered by the nailboard. * * This function is not expected to be efficient because it is only * used in an AVER in AMCWhiten to check that the unused part of the * buffer for a nailboarded segment has in fact been nailed. */ Bool NailboardIsSetRange(Nailboard board, Addr base, Addr limit) { Index ibase, ilimit; AVERT(Nailboard, board); nailboardIndexRange(&ibase, &ilimit, board, 0, base, limit); return BTIsSetRange(board->level[0], ibase, ilimit); } /* NailboardIsResRange -- test if all nails are reset in a range * * Return TRUE if no nails are set in the range between base and * limit, or FALSE if any nail is set. It is an error if any part of * the range is not covered by the nailboard. * * This function is on the critical path as it is called for every * object in every nailed segment. It must take time that is no more * than logarithmic in the size of the range. * * See . */ Bool NailboardIsResRange(Nailboard board, Addr base, Addr limit) { Index i, ibase, ilimit; Index j, jbase, jlimit; Addr leftLimit, rightBase; AVERT_CRITICAL(Nailboard, board); /* Descend levels until ibase and ilimit are two or more bits apart: * that is, until there is an "inner" part to the range. */ i = board->levels; do { -- i; nailboardIndexRange(&ibase, &ilimit, board, i, base, limit); if (BTIsResRange(board->level[i], ibase, ilimit)) /* The entire range was clear. This is expected to be the common * case. */ return TRUE; if (i == 0) /* At level 0 there is only one nail per bit so the set bit is known * to be within the range. */ return FALSE; } while (ibase + 1 >= ilimit - 1); /* At this point we know there is an "inner" part. Are there any * bits set in it? */ if (!BTIsResRange(board->level[i], ibase + 1, ilimit - 1)) return FALSE; /* At this point we know that in level i, there is is a bit set at * ibase or at ilimit - 1 (or both), and everything between them is * reset. */ AVER_CRITICAL(BTGet(board->level[i], ibase) || BTGet(board->level[i], ilimit - 1)); /* Left splinter */ for (j = i, jbase = ibase;;) { leftLimit = nailboardAddr(board, j, jbase + 1); AVER_CRITICAL(base < leftLimit); AVER_CRITICAL(leftLimit < limit); -- j; nailboardIndexRange(&jbase, &jlimit, board, j, base, leftLimit); if (jbase + 1 < jlimit && !BTIsResRange(board->level[j], jbase + 1, jlimit)) return FALSE; /* */ if (!BTGet(board->level[j], jbase)) break; if (j == 0) return FALSE; } /* Right splinter */ for (j = i, jlimit = ilimit;;) { rightBase = nailboardAddr(board, j, jlimit - 1); AVER_CRITICAL(base < rightBase); AVER_CRITICAL(rightBase < limit); -- j; nailboardIndexRange(&jbase, &jlimit, board, j, rightBase, limit); if (jbase < jlimit - 1 && !BTIsResRange(board->level[j], jbase, jlimit - 1)) return FALSE; /* */ if (!BTGet(board->level[j], jlimit - 1)) break; if (j == 0) return FALSE; } return TRUE; } Res NailboardDescribe(Nailboard board, mps_lib_FILE *stream, Count depth) { Index i, j; Res res; if (!TESTT(Nailboard, board)) return ResFAIL; if (stream == NULL) return ResFAIL; res = WriteF(stream, depth, "Nailboard $P {\n", (WriteFP)board, " levels: $U\n", (WriteFU)board->levels, " newNails: $S\n", WriteFYesNo(board->newNails), " alignShift: $U\n", (WriteFU)board->alignShift, NULL); if (res != ResOK) return res; res = RangeDescribe(&board->range, stream, depth + 2); if (res != ResOK) return res; for(i = 0; i < board->levels; ++i) { Count levelNails = nailboardLevelBits(nailboardNails(board), i); Count resetNails = BTCountResRange(board->level[i], 0, levelNails); res = WriteF(stream, depth + 2, "Level $U ($U bits, $U set): ", (WriteFU)i, (WriteFU)levelNails, (WriteFU)(levelNails - resetNails), NULL); if (res != ResOK) return res; for (j = 0; j < levelNails; ++j) { char c = BTGet(board->level[i], j) ? '*' : '.'; res = WriteF(stream, 0, "$C", (WriteFC)c, NULL); if (res != ResOK) return res; } res = WriteF(stream, 0, "\n", NULL); if (res != ResOK) return res; } res = WriteF(stream, depth, "} Nailboard $P\n", (WriteFP)board, NULL); if (res != ResOK) return res; return ResOK; } /* C. COPYRIGHT AND LICENSE * * Copyright (C) 2014 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. */