295 lines
10 KiB
C#
295 lines
10 KiB
C#
|
/*******************************************************************/
|
||
|
/* 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. */
|
||
|
/*******************************************************************/
|
||
|
|
||
|
//
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//
|
||
|
|
||
|
//#define DEBUG_CARDS
|
||
|
|
||
|
namespace System.GCs {
|
||
|
|
||
|
using Microsoft.Bartok.Runtime;
|
||
|
using System.Threading;
|
||
|
using System.Runtime.CompilerServices;
|
||
|
|
||
|
internal unsafe class CardTable : RememberedSet {
|
||
|
|
||
|
internal static CardTable instance;
|
||
|
|
||
|
private static UIntPtr firstCardNo;
|
||
|
private static UIntPtr totalCards;
|
||
|
|
||
|
// To save time in computing the correct card to be marked,
|
||
|
// set the value of cardTableBase such that given a card number c,
|
||
|
// the corresponding card entry is simply cardTableBase+c.
|
||
|
// Note: the card number c of address p is p >>CardBits. Since our
|
||
|
// heap does not necessarily start from address 0, the card numbers
|
||
|
// may NOT start from 0, either.
|
||
|
|
||
|
private static byte *cardTablePtr; // real start of the card table
|
||
|
private static byte *cardTableBase;
|
||
|
|
||
|
// A card is clean before any pointer update in it. It is dirty after an
|
||
|
// update. During a collection, if the card has inter-generational
|
||
|
// pointers, the pointers are traced.
|
||
|
|
||
|
internal enum CardType: byte{
|
||
|
Clean = 0,
|
||
|
Dirty = 1
|
||
|
}
|
||
|
|
||
|
// CardBits, CardSize, etc. are defined as properties instead of
|
||
|
// const int to avoid calling static constructor of CardTye
|
||
|
// during GC.cctor.
|
||
|
|
||
|
// TODO: fine tune the CardBits for performance
|
||
|
|
||
|
internal static byte CardBits {
|
||
|
get {
|
||
|
if (UIntPtr.Size == 4) {
|
||
|
return 9;
|
||
|
} else if (UIntPtr.Size == 8) {
|
||
|
return 10;
|
||
|
} else {
|
||
|
VTable.Assert(false, "Not supported pointer type");
|
||
|
return 0xff;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal static UIntPtr CardSize {
|
||
|
get { return (UIntPtr)1 << CardBits;} // size of a card in bytes
|
||
|
}
|
||
|
|
||
|
internal static UIntPtr FirstCardNo {
|
||
|
get {
|
||
|
return firstCardNo;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal static void Initialize() {
|
||
|
// The card table covers all the heap. Below is not accurate heap
|
||
|
// size. It includes all the memory space, starting from address 0.
|
||
|
// TODO: find more accurate heap size and start
|
||
|
|
||
|
UIntPtr heapSize = PageTable.pageTableCount * PageTable.PageSize;
|
||
|
VTable.Assert((heapSize >> CardBits) << CardBits == heapSize,
|
||
|
"Assumption: pageSize is expected to be multiple of CardSize");
|
||
|
UIntPtr heapStart = (UIntPtr)0;
|
||
|
|
||
|
totalCards = heapSize >> CardBits;
|
||
|
firstCardNo = heapStart >> CardBits;
|
||
|
|
||
|
UIntPtr tablePtr =PageManager.AllocateNonheapMemory(null, totalCards);
|
||
|
VTable.Assert(tablePtr != UIntPtr.Zero);
|
||
|
cardTablePtr = (byte*) tablePtr;
|
||
|
cardTableBase = (byte*)(tablePtr - firstCardNo);
|
||
|
|
||
|
CardTable.instance = (CardTable)
|
||
|
BootstrapMemory.Allocate(typeof(CardTable));
|
||
|
|
||
|
OffsetTable.Initialize(totalCards);
|
||
|
|
||
|
for (UIntPtr c = firstCardNo; c < firstCardNo + totalCards; c++) {
|
||
|
VTable.Assert(!CardIsDirty(c), "Card table initialization error");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Inline]
|
||
|
internal static bool IsValidCardNo(UIntPtr c)
|
||
|
{
|
||
|
return (c >= firstCardNo && c < firstCardNo + totalCards);
|
||
|
}
|
||
|
|
||
|
[Inline]
|
||
|
internal static UIntPtr CardNo(UIntPtr addr)
|
||
|
{
|
||
|
UIntPtr c = addr >> CardBits;
|
||
|
VTable.Assert(IsValidCardNo(c), "CardNo invalid");
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
[Inline]
|
||
|
internal static UIntPtr CardAddr(UIntPtr c)
|
||
|
{
|
||
|
VTable.Assert(IsValidCardNo(c), "CardAddr invalid");
|
||
|
return c << CardBits;
|
||
|
}
|
||
|
|
||
|
[Inline]
|
||
|
internal static UIntPtr NextCardAddr(UIntPtr c)
|
||
|
{
|
||
|
VTable.Assert(IsValidCardNo(c), "NextCardAddr invalid");
|
||
|
return CardAddr(c) + CardSize;
|
||
|
}
|
||
|
|
||
|
// The following two Clean functions are supposed to reset unused
|
||
|
// entries in the remembered set. They are not meaningful for cardtable.
|
||
|
|
||
|
[Inline]
|
||
|
internal override void Clean() {
|
||
|
// Nothing to do
|
||
|
}
|
||
|
|
||
|
[Inline]
|
||
|
internal override void Clean(Thread thread) {
|
||
|
// Nothing to do
|
||
|
}
|
||
|
|
||
|
// There is no duplicate entries in cardtable.
|
||
|
|
||
|
internal override void Uniquify() {
|
||
|
// Nothing to do
|
||
|
}
|
||
|
|
||
|
internal override void Reset() {
|
||
|
Util.MemClear((UIntPtr)cardTablePtr, totalCards);
|
||
|
}
|
||
|
|
||
|
[Inline]
|
||
|
private static bool CardIsDirty(UIntPtr c)
|
||
|
{
|
||
|
VTable.Assert(IsValidCardNo(c), "CardIsDirty invalid");
|
||
|
return (*(cardTableBase+c) == (byte)CardType.Dirty);
|
||
|
}
|
||
|
|
||
|
[Inline]
|
||
|
internal static PageType CardGeneration(UIntPtr c)
|
||
|
{
|
||
|
VTable.Assert(IsValidCardNo(c), "CardGeneration invalid");
|
||
|
return PageTable.Type(PageTable.Page(CardAddr(c)));
|
||
|
}
|
||
|
|
||
|
[Inline]
|
||
|
internal static bool IsMyLiveGcCard(UIntPtr c)
|
||
|
{
|
||
|
VTable.Assert(IsValidCardNo(c), "IsMyLiveGcCard invalid");
|
||
|
UIntPtr page = PageTable.Page(CardAddr(c));
|
||
|
return (PageTable.IsMyPage(page) &&
|
||
|
PageTable.IsLiveGcPage(PageTable.Type(page)));
|
||
|
}
|
||
|
|
||
|
[Inline]
|
||
|
internal static bool IsMyGcCard(UIntPtr c)
|
||
|
{
|
||
|
VTable.Assert(IsValidCardNo(c), "IsMyGcCard invalid");
|
||
|
UIntPtr page = PageTable.Page(CardAddr(c));
|
||
|
return (PageTable.IsMyGcPage(page));
|
||
|
}
|
||
|
|
||
|
[Inline]
|
||
|
internal static UIntPtr PageAddrOfCard(UIntPtr c)
|
||
|
{
|
||
|
return PageTable.PageAlign(CardAddr(c));
|
||
|
}
|
||
|
|
||
|
[Inline]
|
||
|
internal override void RecordReference(ref Object reference,
|
||
|
Object toObj)
|
||
|
{
|
||
|
Record((UIntPtr) Magic.toPointer(ref reference));
|
||
|
}
|
||
|
|
||
|
[Inline]
|
||
|
internal override void RecordReference(UIntPtr* fromAddr,
|
||
|
Object toObj)
|
||
|
{
|
||
|
Record((UIntPtr) fromAddr);
|
||
|
}
|
||
|
|
||
|
// So far, we scan the whole object that has any dirty field. So we just
|
||
|
// make the first card of this cloned object dirty.
|
||
|
// TODO: make all the cards of the object dirty if we scan fields
|
||
|
// in the dirty card only.
|
||
|
|
||
|
[Inline]
|
||
|
internal override void RecordClonedObject(Object clonedObject)
|
||
|
{
|
||
|
Record(Magic.addressOf(clonedObject));
|
||
|
}
|
||
|
|
||
|
[RequiredByBartok]
|
||
|
[Inline]
|
||
|
public static void Record(UIntPtr fromAddr)
|
||
|
{
|
||
|
*(cardTableBase + CardNo(fromAddr)) = (byte)CardType.Dirty;
|
||
|
}
|
||
|
|
||
|
internal override void Scan(NonNullReferenceVisitor ptrVisitor,
|
||
|
PageType genToCollect)
|
||
|
{
|
||
|
#if DEBUG_CARDS
|
||
|
VTable.DebugPrint("************ Scan to collect generation {0:x8} ******\n",
|
||
|
__arglist(genToCollect));
|
||
|
for (UIntPtr i = firstCardNo; i < firstCardNo + totalCards; i++) {
|
||
|
if (CardIsDirty(i)) {
|
||
|
VTable.DebugPrint("dirty card {0:x8} gen {1:x8}\n", __arglist(i, CardGeneration(i)));
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
for (UIntPtr c = firstCardNo; c < firstCardNo + totalCards; ) {
|
||
|
if (CardIsDirty(c) && IsMyLiveGcCard(c) &&
|
||
|
CardGeneration(c) > genToCollect) {
|
||
|
UIntPtr last = c + 1;
|
||
|
while (last < firstCardNo + totalCards && CardIsDirty(last) &&
|
||
|
IsMyLiveGcCard(last) &&
|
||
|
CardGeneration(last) > genToCollect) {
|
||
|
last++;
|
||
|
}
|
||
|
#if DEBUG_CARDS
|
||
|
VTable.DebugPrint("Scan from {0:x8} to {1:x8} to collect gen {2:x8}\n",
|
||
|
__arglist(c, last-1, genToCollect));
|
||
|
#endif
|
||
|
VisitObjectsInCards(ptrVisitor, c, last - 1);
|
||
|
c = last;
|
||
|
} else {
|
||
|
c++;
|
||
|
}
|
||
|
}
|
||
|
#if DEBUG_CARDS
|
||
|
VTable.DebugPrint("************ End Scan ******\n");
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
private static void VisitObjectsInCards(NonNullReferenceVisitor ptrVisitor,
|
||
|
UIntPtr first, UIntPtr last)
|
||
|
{
|
||
|
UIntPtr objectPtr = OffsetTable.FirstObjPtrWithFieldInCard(first);
|
||
|
VTable.Assert(objectPtr != UIntPtr.Zero, "No first obj ptr");
|
||
|
|
||
|
// TODO: in semispace.Visit, update cards in visiting promoted objects.
|
||
|
// So far, since we have only two generations, and after scanning, all nursery
|
||
|
// objects are promoted to mature generation, thus there is no inter-generational
|
||
|
// pointers anymore, and we do not need to update cards during visit. But we
|
||
|
// need to do that once we have more than two generations.
|
||
|
|
||
|
UIntPtr limit = NextCardAddr(last);
|
||
|
|
||
|
while (objectPtr != UIntPtr.Zero && objectPtr < limit) {
|
||
|
#if DEBUG_CARDS
|
||
|
VTable.DebugPrint(" visiting obj ptr {0:x8}\n", __arglist(objectPtr));
|
||
|
#endif
|
||
|
Object obj = Magic.fromAddress(objectPtr);
|
||
|
UIntPtr objSize = ptrVisitor.VisitReferenceFields(obj);
|
||
|
UIntPtr nextObjectPtr = OffsetTable.PtrToNextObject(objectPtr, objSize, limit);
|
||
|
VTable.Assert(CardNo(objectPtr) <= CardNo(nextObjectPtr),
|
||
|
"Next object should be below the current one");
|
||
|
if (CardNo(objectPtr) < CardNo(nextObjectPtr)) {
|
||
|
UIntPtr c = CardNo(objectPtr);
|
||
|
UIntPtr offset = objectPtr - CardAddr(c);
|
||
|
if ( offset > OffsetTable.GetOffset(c)) {
|
||
|
OffsetTable.SetOffset(c, offset);
|
||
|
}
|
||
|
}
|
||
|
objectPtr = nextObjectPtr;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|