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

294 lines
13 KiB
C#
Raw Permalink Normal View History

2008-11-17 18:29:00 -05:00
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
2008-03-05 09:52:00 -05:00
/*******************************************************************/
/* 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. */
/*******************************************************************/
namespace System.GCs {
using Microsoft.Bartok.Runtime;
using System.Runtime.CompilerServices;
internal class InteriorPtrTable {
// WARNING: don't initialize any static fields in this class
// without manually running the class constructor at startup!
2008-11-17 18:29:00 -05:00
internal const uint OFFSET_NO_DATA = 0;
internal const uint OFFSET_SKEW = 1;
2008-03-05 09:52:00 -05:00
/*
* Returns a pointer to the beginning of an object such that the
* pointer is less than or equal to addr. N.B. Before may return a
* pointer to an alignment or an unused space token.
*/
private static UIntPtr Before(UIntPtr addr) {
UIntPtr page = PageTable.Page(addr);
2008-11-17 18:29:00 -05:00
uint offset = PageTable.Extra(page);
2008-03-05 09:52:00 -05:00
// OFFSET_NO_DATA and negative offsets should always fail this
// test.
if (PageTable.PageAddr(page) + (offset-OFFSET_SKEW) > addr) {
// If the addr is an interior pointer of an object on a
// previous page, go back one entry.
--page;
offset = PageTable.Extra(page);
}
if (offset == OFFSET_NO_DATA) {
// Scroll back until we find a page entry with real data in
// it. This handles the case of a large object allocated
// across pages.
do {
--page;
offset = PageTable.Extra(page);
}
while (offset == OFFSET_NO_DATA);
}
VTable.Assert(offset > OFFSET_NO_DATA, "No offset data");
// Unused: since we currently do not use negative offsets in the
// page table. This would be more efficient for really big
// objects, but the OFFSET_NO_DATA value works fine too.
/*
// Scroll backwards using big steps. Offset will never be
// OFFSET_NO_DATA in this loop.
while (offset < OFFSET_NO_DATA) {
entry += (offset - OFFSET_SKEW);
offset = *entry;
}
*/
return PageTable.PageAddr(page) + (offset - OFFSET_SKEW);
}
/*
* Returns a pointer to the first object on the given page.
* N.B. If called on a page with no ~allocated~ first object it may
* return a pointer to the unused space token.
*/
internal static UIntPtr First(UIntPtr page)
{
2008-11-17 18:29:00 -05:00
uint offset = PageTable.Extra(page);
2008-03-05 09:52:00 -05:00
UIntPtr pageAddr = PageTable.PageAddr(page);
UIntPtr currAddr;
if (offset != OFFSET_NO_DATA) {
currAddr = pageAddr + (offset - OFFSET_SKEW);
} else {
currAddr = Before(pageAddr);
VTable.Assert(currAddr <= pageAddr);
UIntPtr nextPageStart = PageTable.PagePad(currAddr+1);
while (currAddr < pageAddr) {
if (Allocator.IsAlignment(currAddr)) {
currAddr += UIntPtr.Size;
} else if (BumpAllocator.IsUnusedSpace(currAddr)) {
currAddr = PageTable.PagePad(currAddr) + PreHeader.Size;
} else {
if (currAddr >= nextPageStart) {
InteriorPtrTable.SetFirst(currAddr);
nextPageStart = PageTable.PagePad(currAddr+1);
}
currAddr += ObjectSize(currAddr);
}
}
}
currAddr = Allocator.SkipAlignment(currAddr);
return currAddr;
}
/*
* Returns a pointer past the last object _that_fits_completely_ on
* the given page. Note that the "last" object on a page may
* actually start on a previous page.
*/
internal static UIntPtr Last(UIntPtr page)
{
UIntPtr currAddr = InteriorPtrTable.First(page);
UIntPtr endAddr = PageTable.PageAddr(page + 1);
// Look out for the unused space token: this page may not
// have been completely allocated: its "first" object might not
// be valid.
if (BumpAllocator.IsUnusedSpace(currAddr) || currAddr >= endAddr) {
// Back up to the previous object. Should be fast
// since First updated the InteriorPtrTable entries.
currAddr = Before(PageTable.PageAddr(page));
}
// REVIEW this is very similar to Find(addr) below.
VTable.Assert(currAddr <= endAddr);
while (true) {
// Watch out for alignment padding; advance the pointer if
// it points to a syncblock index rather than a vtable
// pointer. Note that we must do this before scrolling,
// since the page table value was set before we knew the
// required alignment.
if (Allocator.IsAlignment(currAddr)) {
currAddr += UIntPtr.Size;
} else if (BumpAllocator.IsUnusedSpace(currAddr)) {
UIntPtr nextAddr =
PageTable.PagePad(currAddr) + PreHeader.Size;
if (nextAddr >= endAddr) {
return currAddr;
} else {
currAddr = nextAddr;
}
} else {
VTable.Assert(currAddr <= endAddr);
UIntPtr size = ObjectSize(currAddr);
UIntPtr postAddr = currAddr + size;
if (postAddr > endAddr) {
if (postAddr - PreHeader.Size > endAddr) {
// The object spills over onto the next page
return currAddr;
} else {
// The object ended at or before the page boundary
return postAddr;
}
} else {
currAddr = postAddr;
}
}
}
}
2008-11-17 18:29:00 -05:00
// Finds the object base for an interior pointer. In the case of a
// pointer to the tail of an object and the head of another, it will
// return the former object (the one whose tail we point at). To
// get the base pointer for a pointer into the pre-header, you should
// add PreHeader.Size before calling this.
2008-03-05 09:52:00 -05:00
internal static UIntPtr Find(UIntPtr addr)
{
UIntPtr page = PageTable.Page(addr);
UIntPtr currAddr = InteriorPtrTable.First(page);
// Look out for the unused space token: this page may not
// have been completely allocated: its "first" object might not
// be valid.
if (BumpAllocator.IsUnusedSpace(currAddr) || currAddr > addr) {
// Back up to the previous object. Should be fast
// since First updated the InteriorPtrTable entries.
currAddr = Before(PageTable.PageAddr(page));
}
VTable.Assert(!BumpAllocator.IsUnusedSpace(currAddr),
"InteriorPtrTable.Find 0");
VTable.Assert(currAddr <= addr, "InteriorPtrTable.Find 1");
while (true) {
// Watch out for alignment padding; advance the pointer if
// it points to a syncblock index rather than a vtable
// pointer. Note that we must do this before scrolling,
// since the page table value was set before we knew the
// required alignment.
if (Allocator.IsAlignment(currAddr)) {
currAddr += UIntPtr.Size;
} else if (BumpAllocator.IsUnusedSpace(currAddr)) {
UIntPtr postAddr =
PageTable.PagePad(currAddr) + PreHeader.Size;
VTable.Assert(postAddr <= addr, "InteriorPtrTable.Find 2");
currAddr = postAddr;
} else {
VTable.Assert(currAddr <= addr, "InteriorPtrTable.Find 3");
UIntPtr size = ObjectSize(currAddr);
VTable.Assert(size >= UIntPtr.Zero,
"InteriorPtrTable.Find 4");
UIntPtr postAddr = currAddr + size;
if (postAddr > addr) {
return currAddr;
} else {
currAddr = postAddr;
}
}
}
}
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);
}
[Inline]
internal static unsafe void SetFirst(UIntPtr newAddr) {
2008-11-17 18:29:00 -05:00
VTable.Assert(PageTable.IsGcPage(PageTable.Page(newAddr)),
"SetFirst on a non-GC page");
2008-03-05 09:52:00 -05:00
UIntPtr page = PageTable.Page(newAddr);
UIntPtr offset = newAddr - PageTable.PageAddr(page);
2008-11-17 18:29:00 -05:00
PageTable.SetExtra(page, unchecked((uint)(offset+OFFSET_SKEW)));
2008-03-05 09:52:00 -05:00
}
[Inline]
internal static unsafe void ClearFirst(UIntPtr page) {
PageTable.SetExtra(page, OFFSET_NO_DATA);
}
[Inline]
internal static unsafe void ClearFirst(UIntPtr startPage,
UIntPtr endPage)
{
PageTable.SetExtra(startPage, endPage - startPage, OFFSET_NO_DATA);
}
[System.Diagnostics.Conditional("DEBUG")]
internal static unsafe void VerifyFirst(UIntPtr previousObjectAddr,
UIntPtr objectAddr)
{
UIntPtr page = PageTable.Page(objectAddr);
if (previousObjectAddr != UIntPtr.Zero) {
UIntPtr previousPage = PageTable.Page(previousObjectAddr);
UIntPtr pageCursor = previousPage + 1;
while (pageCursor < page) {
2008-11-17 18:29:00 -05:00
uint cursorOffset = PageTable.Extra(pageCursor);
2008-03-05 09:52:00 -05:00
UIntPtr objAddr = (PageTable.PageAddr(pageCursor) +
cursorOffset - OFFSET_SKEW);
if(!(cursorOffset <= OFFSET_NO_DATA ||
BumpAllocator.IsUnusedSpace(objAddr) ||
Allocator.IsAlignment(objAddr) ||
BumpAllocator.IsRestOfPageZero(objAddr))) {
VTable.DebugPrint
("cursorOffset={0:x} OFFSET_NO_DATA={1:x} objAddr={2:x} unused={3} isalign={4} iszero={5}\n",
__arglist((cursorOffset),
(OFFSET_NO_DATA),
((long)objAddr),
(BumpAllocator.IsUnusedSpace(objAddr)),
(Allocator.IsAlignment(objAddr)),
(BumpAllocator.IsRestOfPageZero(objAddr))));
}
VTable.Assert(cursorOffset <= OFFSET_NO_DATA ||
BumpAllocator.IsUnusedSpace(objAddr) ||
Allocator.IsAlignment(objAddr) ||
BumpAllocator.IsRestOfPageZero(objAddr),
"VerifyFirst 1");
pageCursor++;
}
}
2008-11-17 18:29:00 -05:00
uint offset = PageTable.Extra(page);
2008-03-05 09:52:00 -05:00
if (offset > OFFSET_NO_DATA) {
UIntPtr firstAddr =
PageTable.PageAddr(page) + offset - OFFSET_SKEW;
if(!(firstAddr == objectAddr ||
(firstAddr + UIntPtr.Size == objectAddr &&
Allocator.IsAlignment(firstAddr)))) {
VTable.DebugPrint
("firstAddr={0:x} objectAddr={1:x} isalign={2}\n",
__arglist(((long)firstAddr),
((long)objectAddr),
(Allocator.IsAlignment(firstAddr))));
}
VTable.Assert(firstAddr == objectAddr ||
(firstAddr + 4 == objectAddr &&
Allocator.IsAlignment(firstAddr)),
"VerifyFirst 2");
}
}
}
}