singrdk/base/Imported/Bartok/runtime/shared/GCs/OffsetTable.cs

377 lines
16 KiB
C#

//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
/*******************************************************************/
/* WARNING */
/* This file should be identical in the Bartok and Singularity */
/* depots. Master copy resides in Bartok Depot. Changes should be */
/* made to Bartok Depot and propagated to Singularity Depot. */
/*******************************************************************/
//#define DEBUG_OFFSETTABLE
namespace System.GCs {
using Microsoft.Bartok.Runtime;
using System.Threading;
using System.Runtime.CompilerServices;
internal unsafe class OffsetTable {
// OffsetTable is to identify objects in cards. Each card has one
// entry in the offsetTable.
// An entry in the offsetTable for a card has a special value
// (NoObjPtrToCard) if no object pointer points to the card.
// Otherwise, it is an offset of the last object in the card
// relative to the beginning of the card.
// Note: an object pointer points to the byte below its header,
// not the header itself. That is vtable for non reference counting
// collector, and refCount for reference counting.
private static byte *offsetTableBase;
// Special offset indicating that in a card, there is no object
// pointer to it. We make it as -1, and the effective
// offset of an pointer is within 0~127 when an offsetTable entry
// is 1 byte, or 0~2**15-1 if it is 2 bytes. However, to avoid time
// to initialize the offsetTable with -1, we simply add an bias 1
// to all the them, such that the special offset is 0, and the effective
// ones are within 1~128 (or 1~2**15). Correspondingly, in fetching
// the offset from the able, we should deduct the bias to get the real
// offset.
private static UIntPtr NoObjPtrToCard {
get { return (UIntPtr)0x0;}
}
// The bias.
private static byte OffsetBias {
get { return 0x1;}
}
// Assume pointers are naturally aligned within a card. Then
// its least 2 or 3 significant bits are 0 and need not be recorded.
private static byte OffsetIgnorableBits {
get {
VTable.Assert(UIntPtr.Size == 4 || UIntPtr.Size == 8,
"Not supported pointer type");
if (UIntPtr.Size == 4) {
return 2;
} else {
return 3;
}
}
}
// An entry in the offsetTable needs to be either 1 byte or 2 bytes,
// depending on the size of a card.
// Given one byte for an entry, we can represent 128 effective offsets,
// and one special offset NoObjPtrToCard. For x64,
// UIntPtr.Size*128 = 2**10. For x86, UIntPtr.Size * 128 = 2**9.
// That is, the size of a card is no bigger than 2**10 or 2**9.
// Given two bytes for an entry, we can represent 2**15 effective
// offsets, and one special offset. The size of card is no bigger than
// 2**18 or 2**17.
private static byte EntryBytes {
get {
VTable.Assert(UIntPtr.Size == 4 || UIntPtr.Size == 8,
"Not supported pointer type");
if ((UIntPtr.Size == 4 && CardTable.CardBits <= 9) ||
(UIntPtr.Size == 8 && CardTable.CardBits <= 10)) {
return 1;
} else {
VTable.Assert((UIntPtr.Size == 4 && CardTable.CardBits <= 17) ||
(UIntPtr.Size == 8 && CardTable.CardBits <= 18),
"Card size is unreasonable large");
return 2;
}
}
}
internal static void Initialize(UIntPtr totalCards) {
UIntPtr offsetTablePtr = PageManager.AllocateNonheapMemory(null,
((UIntPtr)EntryBytes)*totalCards);
VTable.Assert(offsetTablePtr != UIntPtr.Zero, "OffsetTable.Initialize: no memory");
offsetTableBase = (byte*)(offsetTablePtr - CardTable.FirstCardNo);
// The memory of offset table should have already been cleaned
for (UIntPtr c=CardTable.FirstCardNo; c<CardTable.FirstCardNo+totalCards;c++) {
VTable.Assert(NoObjectPtrToTheCard(c), "offset table initialization error");
}
}
[Inline]
private static void SetNoObjPtrToCard(UIntPtr c)
{
VTable.Assert(CardTable.IsValidCardNo(c), "SetNoObjPtrToCard: invalid card no");
SetCompressedBiasedOffset(c, NoObjPtrToCard);
}
[Inline]
internal static bool NoObjectPtrToTheCard(UIntPtr c)
{
VTable.Assert(CardTable.IsValidCardNo(c), "NoObjectPtrToTheCard: invalid card no");
return (GetCompressedBiasedOffset(c) == NoObjPtrToCard);
}
[Inline]
internal static void SetOffset(UIntPtr c, UIntPtr offset)
{
VTable.Assert((offset >> OffsetIgnorableBits) << OffsetIgnorableBits
== offset, "offset should be multiple of pointer size");
VTable.Assert(NoObjectPtrToTheCard(c) || GetOffset(c) < offset,
"A smaller offset offset is set to card ");
SetCompressedBiasedOffset(c, (offset >> OffsetIgnorableBits) + (UIntPtr)OffsetBias);
}
[Inline]
internal static UIntPtr GetOffset(UIntPtr c)
{
UIntPtr offset = GetCompressedBiasedOffset(c) - (UIntPtr)OffsetBias;
return offset << OffsetIgnorableBits;
}
[Inline]
private static bool IsValidCompressedBiasedOffset(UIntPtr value)
{
return value <= ((UIntPtr)2<<(EntryBytes * 8 -1));
}
[Inline]
private static void SetCompressedBiasedOffset(UIntPtr c, UIntPtr value)
{
VTable.Assert(IsValidCompressedBiasedOffset(value), "Invalid compressed biased value");
if (EntryBytes == 1) {
*(offsetTableBase + c) = (byte)value;
} else {
*((short*)offsetTableBase + c) = (short)value;
}
}
[Inline]
private static UIntPtr GetCompressedBiasedOffset(UIntPtr c)
{
if (EntryBytes == 1) {
return (UIntPtr) (*(offsetTableBase + c));
} else {
return (UIntPtr)(*((short*)offsetTableBase + c));
}
}
// OffsetTable records all objects promoted or directly allocated in mature
// generation.
internal static void SetLast(UIntPtr objPtr)
{
VTable.Assert(PageTable.IsGcPage(PageTable.Page(objPtr)), "Not GC page");
UIntPtr card = CardTable.CardNo(objPtr);
UIntPtr mask = (UIntPtr)(CardTable.CardSize -1);
UIntPtr offset = objPtr & mask;
VTable.Assert(offset < CardTable.CardSize, "Overflow");
SetOffset(card, offset);
#if DEBUG_OFFSETTABLE
VTable.DebugPrint("SetLast objPtr {0:x8}, card {1:x8}, offset {2:x8}\n",
__arglist(objPtr, card, offset));
#endif
}
internal static UIntPtr FirstObjPtrWithFieldInCard(UIntPtr c)
{
VTable.Assert(CardTable.IsValidCardNo(c), "Not valid card no");
VTable.Assert(CardTable.IsMyLiveGcCard(c), "Not my live GC card");
UIntPtr cardAddr = CardTable.CardAddr(c);
UIntPtr nextCardAddr = CardTable.NextCardAddr(c);
// Scan backward. May go to zombie areas
UIntPtr cardBefore = c -1;
while (cardBefore >= CardTable.FirstCardNo &&
OffsetTable.NoObjectPtrToTheCard(cardBefore) &&
CardTable.IsMyGcCard(cardBefore)){
cardBefore --;
}
UIntPtr ptr;
if (cardBefore < CardTable.FirstCardNo ||
!CardTable.IsMyGcCard(cardBefore)) {
// this case happens when c is the first live card that has an object.
VTable.Assert(CardTable.IsMyGcCard(cardBefore+1),
"The next card must be a GC card");
VTable.Assert(CardTable.CardAddr(cardBefore+1) ==
CardTable.PageAddrOfCard(cardBefore+1),
"The next card must be at the boundary of a page");
UIntPtr pageAddr = CardTable.PageAddrOfCard(cardBefore+1);
ptr =PtrToNextObject(pageAddr, (UIntPtr)PreHeader.Size, nextCardAddr);
} else {
VTable.Assert(!OffsetTable.NoObjectPtrToTheCard(cardBefore),
"A card with an object must have been found");
ptr = CardTable.CardAddr(cardBefore) +
OffsetTable.GetOffset(cardBefore);
}
VTable.Assert(ptr < nextCardAddr, "No objptr in this card");
// Scan forward. Update offset table in the course.
UIntPtr currentPtr = ptr;
while (currentPtr < cardAddr) {
UIntPtr size = OffsetTable.ObjectSize(currentPtr);
if (currentPtr + size - PreHeader.Size > cardAddr) {
VTable.Assert(CardTable.CardGeneration(CardTable.CardNo(currentPtr)) ==
CardTable.CardGeneration(c),
"The whole object must be in the same generation");
break;
} else {
ptr = currentPtr;
currentPtr = PtrToNextObject(currentPtr, size, nextCardAddr);
VTable.Assert(CardTable.CardNo(ptr) <= CardTable.CardNo(currentPtr),
"Previous ptr should be before current ptr");
if (CardTable.CardNo(ptr) < CardTable.CardNo(currentPtr)) {
UIntPtr ptrC = CardTable.CardNo(ptr);
UIntPtr offsetC = ptr - CardTable.CardAddr(ptrC);
if (offsetC > GetOffset(ptrC)) {
SetOffset(ptrC, offsetC);
}
}
}
}
VTable.Assert(currentPtr != UIntPtr.Zero, "current ptr is zero");
VTable.Assert(currentPtr < nextCardAddr, "No objptr found in this card");
#if DEBUG_OFFSETTABLE
UIntPtr temp = OffsetTable.FirstPtrFromInteriorTable(c);
if (temp != currentPtr) {
VTable.DebugPrint("Found ptr ({0:x8}) inconsistent with first ptr from interior table({1:x8})\n",
__arglist(currentPtr, temp));
VTable.Assert(false);
}
#endif
return currentPtr;
}
// The next object after the current object and below the limit
[Inline]
internal static UIntPtr PtrToNextObject(UIntPtr objPtr, UIntPtr size, UIntPtr limit)
{
return BumpAllocator.SkipNonObjectData(objPtr + size, limit);
}
[Inline]
internal static void ClearCards(UIntPtr c1, UIntPtr c2)
{
for (UIntPtr i = c1; i <= c2; i++) {
SetNoObjPtrToCard(i);
}
#if DEBUG_OFFSETTABLE
VTable.DebugPrint("ClearLast from {0:x8} to {1:x8}\n", __arglist(c1, c2));
#endif
}
internal static void ClearLast(UIntPtr from, UIntPtr to)
{
UIntPtr first = CardTable.CardNo(from);
UIntPtr last = CardTable.CardNo(to);
ClearCards(first, last);
}
// Copied from InteriorPtrTable.cs
internal static unsafe UIntPtr ObjectSize(UIntPtr addr) {
UIntPtr vtableAddr = Allocator.GetObjectVTable(addr);
UIntPtr vtablePage = PageTable.Page(vtableAddr);
if (PageTable.IsGcPage(vtablePage)) {
// The vtable field is really a forwarding pointer
vtableAddr = Allocator.GetObjectVTable(vtableAddr);
} else {
// Clear the lowest bits, if set
vtableAddr &= ~((UIntPtr)3);
}
VTable vtable =
Magic.toVTable(Magic.fromAddress(vtableAddr));
return ObjectLayout.ObjectSize(addr, vtable);
}
#if DEBUG_OFFSETTABLE
private static UIntPtr FirstPtrFromInteriorTable(UIntPtr c)
{
UIntPtr cardAddr = CardTable.CardAddr(c);
UIntPtr nextCardAddr = CardTable.NextCardAddr(c);
UIntPtr page = PageTable.Page(cardAddr);
UIntPtr pageAddr = PageTable.PageAddr(page);
UIntPtr currAddr;
if (page == 0) {
currAddr =PtrToNextObject(pageAddr,
(UIntPtr)PreHeader.Size, nextCardAddr);
} else {
short offset = PageTable.Extra(page);
currAddr = UIntPtr.Zero;
if (offset != InteriorPtrTable.OFFSET_NO_DATA) {
currAddr = pageAddr + (offset - InteriorPtrTable.OFFSET_SKEW);
}
// In general, we expect currAddr <= cardAddr. Or in the extreme
// case, when the object starts from the page boundary,
// currAddr - Object.HEADER_BYTES <= cardAddr. The contrary
// cases has to be handled by searching previous pages.
if (currAddr == UIntPtr.Zero ||
(currAddr > cardAddr &&
currAddr - PreHeader.Size > cardAddr)) {
// look from previous pages, in case that an object on
// them spans to the current page. In that case, we should
// should use that object's ptr.
currAddr = InteriorPtrTable.Last(page -1);
// Usually, Last() returns a pointer before or at the page
// boundary. However, there is one exception: when an object
// exactly fits to the last byte of the previous page, and the next
// object starts right from the page boundary (the first byte of
// the next page), then the pointer to this next object is returned.
// Example found: objPtr =3d09fa8, size=60, pageboundary=
// 3d0a000, next objPtr=3d0a008. Then returned pointer is
// 3d0a008, which is beyond the page boundary.
VTable.Assert(currAddr <= pageAddr ||
currAddr - PreHeader.Size <= pageAddr,
"object is expected before page or right at the beginning of it");
}
}
VTable.Assert(currAddr < nextCardAddr, "object is expected before next card");
while (currAddr < nextCardAddr) {
if (Allocator.IsAlignment(currAddr)) {
currAddr += UIntPtr.Size;
} else if (BumpAllocator.IsUnusedSpace(currAddr)) {
currAddr = PageTable.PagePad(currAddr) + PreHeader.Size;
} else {
UIntPtr size = InteriorPtrTable.ObjectSize(currAddr);
if (currAddr + size - PreHeader.Size > cardAddr) {
return currAddr;
}
currAddr += size;
}
}
VTable.Assert(false, "No obj ptr found by looking at interior table");
return UIntPtr.Zero;
}
#endif
}
}