294 lines
13 KiB
C#
294 lines
13 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. */
|
|
/*******************************************************************/
|
|
|
|
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!
|
|
|
|
internal const uint OFFSET_NO_DATA = 0;
|
|
internal const uint OFFSET_SKEW = 1;
|
|
|
|
/*
|
|
* 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);
|
|
uint offset = PageTable.Extra(page);
|
|
// 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)
|
|
{
|
|
uint offset = PageTable.Extra(page);
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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.
|
|
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) {
|
|
VTable.Assert(PageTable.IsGcPage(PageTable.Page(newAddr)),
|
|
"SetFirst on a non-GC page");
|
|
UIntPtr page = PageTable.Page(newAddr);
|
|
UIntPtr offset = newAddr - PageTable.PageAddr(page);
|
|
PageTable.SetExtra(page, unchecked((uint)(offset+OFFSET_SKEW)));
|
|
}
|
|
|
|
[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) {
|
|
uint cursorOffset = PageTable.Extra(pageCursor);
|
|
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++;
|
|
}
|
|
}
|
|
uint offset = PageTable.Extra(page);
|
|
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");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|