453 lines
19 KiB
C#
453 lines
19 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 abstract class ReferenceVisitor {
|
|
|
|
internal struct ObjectDescriptor {
|
|
[ManualRefCounts]
|
|
[Inline]
|
|
internal ObjectDescriptor(VTable vtable, UIntPtr objectBase) :
|
|
this(vtable, objectBase, UIntPtr.Zero, UIntPtr.Zero,
|
|
null, null)
|
|
{
|
|
}
|
|
|
|
[ManualRefCounts]
|
|
[Inline]
|
|
internal ObjectDescriptor(VTable vtable, UIntPtr objectBase,
|
|
UIntPtr secondBase) :
|
|
this(vtable, objectBase, secondBase, UIntPtr.Zero, null, null)
|
|
{
|
|
}
|
|
|
|
[ManualRefCounts]
|
|
[Inline]
|
|
internal ObjectDescriptor(VTable vtable,
|
|
UIntPtr objectBase,
|
|
UIntPtr secondBase,
|
|
UIntPtr extra) :
|
|
this(vtable, objectBase, secondBase, extra, null, null)
|
|
{}
|
|
|
|
[ManualRefCounts]
|
|
[Inline]
|
|
internal ObjectDescriptor(VTable vtable,
|
|
UIntPtr objectBase,
|
|
UIntPtr secondBase,
|
|
UIntPtr extra,
|
|
Object realObjectBase,
|
|
Object realSecondBase)
|
|
{
|
|
this.vtable = vtable;
|
|
this.objectBase = objectBase;
|
|
this.secondBase = secondBase;
|
|
this.extra = extra;
|
|
this.realObjectBase = realObjectBase;
|
|
this.realSecondBase = realSecondBase;
|
|
}
|
|
|
|
// this struct is never in the heap, so the fields should
|
|
// be accessed the quick way. (and no, this struct isn't
|
|
// always accessed through a local var - it might be accessed
|
|
// via a managed pointer in the NoInline reference fields
|
|
// visitor methods.)
|
|
|
|
[NoBarriers]
|
|
internal new VTable vtable;
|
|
[NoBarriers]
|
|
internal UIntPtr objectBase;
|
|
[NoBarriers]
|
|
internal UIntPtr secondBase;
|
|
[NoBarriers]
|
|
internal UIntPtr extra;
|
|
[NoBarriers]
|
|
internal Object realObjectBase;
|
|
[NoBarriers]
|
|
internal Object realSecondBase;
|
|
}
|
|
|
|
// Rationale for cutting the fact that no stack probes are permitted in this
|
|
// tree of calls:
|
|
//
|
|
// There is no rationale. It is a bug which is tracked with all the cases that
|
|
// require fixing.
|
|
//
|
|
// Bug 436
|
|
[NoStackLinkCheckTransCut]
|
|
[ManualRefCounts]
|
|
[Inline]
|
|
internal virtual UIntPtr VisitReferenceFields(Object obj)
|
|
{
|
|
return this.VisitReferenceFields(Magic.addressOf(obj),
|
|
obj.vtable);
|
|
}
|
|
|
|
[Inline]
|
|
internal abstract UIntPtr VisitReferenceFields(UIntPtr objectBase,
|
|
VTable vtable);
|
|
|
|
[Inline]
|
|
protected abstract unsafe
|
|
void Filter(UIntPtr *location, ref ObjectDescriptor objDesc);
|
|
|
|
// Rationale for cutting the fact that no stack probes are permitted in this
|
|
// tree of calls:
|
|
//
|
|
// There is no rationale. This is a bug which has been filed. It is a temporary
|
|
// hack to break the recursion in this method. Recursion needs to open up an
|
|
// opportunity for stack probing. Eventually, perhaps this method can be
|
|
// rewritten to avoid the recursion.
|
|
//
|
|
// Bug 436
|
|
[NoStackLinkCheckTransCut]
|
|
[ManualRefCounts]
|
|
[NoInline]
|
|
protected unsafe
|
|
UIntPtr VisitReferenceFieldsTemplateNoInline(ref ObjectDescriptor objDesc)
|
|
{
|
|
return VisitReferenceFieldsTemplate(ref objDesc);
|
|
}
|
|
|
|
[ManualRefCounts]
|
|
[Inline]
|
|
protected unsafe
|
|
UIntPtr VisitReferenceFieldsTemplate(ref ObjectDescriptor objDesc)
|
|
{
|
|
UIntPtr pointerTracking = objDesc.vtable.pointerTrackingMask;
|
|
uint objectTag = (pointerTracking & 0xf);
|
|
UIntPtr size;
|
|
switch (objectTag) {
|
|
case ObjectLayout.SPARSE_TAG: {
|
|
UIntPtr *sparseObject = (UIntPtr *) objDesc.objectBase;
|
|
size = ObjectLayout.ObjectSize(objDesc.vtable);
|
|
pointerTracking >>= 4;
|
|
while (pointerTracking != 0) {
|
|
uint index = pointerTracking & 0xf;
|
|
pointerTracking >>= 4;
|
|
// The cast to int prevents C# from taking the
|
|
// index * sizeof(UIntPtr) to long:
|
|
UIntPtr *loc = sparseObject + (int) index;
|
|
this.Filter(loc, ref objDesc);
|
|
}
|
|
break;
|
|
}
|
|
case ObjectLayout.DENSE_TAG: {
|
|
// skip vtable
|
|
int postHeaderSize = PostHeader.Size;
|
|
UIntPtr *denseObject = (UIntPtr *)
|
|
(objDesc.objectBase + postHeaderSize);
|
|
size = ObjectLayout.ObjectSize(objDesc.vtable);
|
|
pointerTracking >>= 4;
|
|
while (pointerTracking != 0) {
|
|
if ((pointerTracking & ((UIntPtr)0x1)) != 0) {
|
|
this.Filter(denseObject, ref objDesc);
|
|
}
|
|
pointerTracking >>= 1;
|
|
denseObject++;
|
|
}
|
|
break;
|
|
}
|
|
case ObjectLayout.PTR_VECTOR_TAG: {
|
|
int postHeaderSize = PostHeader.Size;
|
|
uint length = *(uint*)(objDesc.objectBase + postHeaderSize);
|
|
size = ObjectLayout.ArraySize(objDesc.vtable, length);
|
|
int preHeaderSize = PreHeader.Size;
|
|
UIntPtr *elementAddress = (UIntPtr *)
|
|
(objDesc.objectBase + objDesc.vtable.baseLength -
|
|
preHeaderSize);
|
|
for (uint i = 0; i < length; i++, elementAddress++) {
|
|
this.Filter(elementAddress, ref objDesc);
|
|
}
|
|
break;
|
|
}
|
|
case ObjectLayout.OTHER_VECTOR_TAG: {
|
|
int postHeaderSize = PostHeader.Size;
|
|
uint length = *(uint*)(objDesc.objectBase + postHeaderSize);
|
|
size = ObjectLayout.ArraySize(objDesc.vtable, length);
|
|
if (objDesc.vtable.arrayOf == StructuralType.Struct) {
|
|
// pretend the struct is boxed and account for the
|
|
// presence of the vtable field
|
|
VTable elementVTable = objDesc.vtable.arrayElementClass;
|
|
UIntPtr elementMask = elementVTable.pointerTrackingMask;
|
|
// A structure with no references will have a SPARSE
|
|
// descriptor with no offset values.
|
|
if (elementMask != (UIntPtr) ObjectLayout.SPARSE_TAG) {
|
|
int preHeaderSize = PreHeader.Size;
|
|
UIntPtr elementAddress = (objDesc.objectBase +
|
|
objDesc.vtable.baseLength -
|
|
preHeaderSize -
|
|
postHeaderSize);
|
|
int elementSize = objDesc.vtable.arrayElementSize;
|
|
objDesc.vtable = elementVTable;
|
|
for (uint i = 0; i < length; i++) {
|
|
objDesc.objectBase = elementAddress;
|
|
this.VisitReferenceFieldsTemplateNoInline(ref objDesc);
|
|
elementAddress += elementSize;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ObjectLayout.PTR_ARRAY_TAG: {
|
|
int postHeaderSize = PostHeader.Size;
|
|
uint length = *(uint*)(objDesc.objectBase + postHeaderSize +
|
|
sizeof(uint));
|
|
size = ObjectLayout.ArraySize(objDesc.vtable, length);
|
|
int preHeaderSize = PreHeader.Size;
|
|
UIntPtr *elementAddress = (UIntPtr *)
|
|
(objDesc.objectBase + objDesc.vtable.baseLength -
|
|
preHeaderSize);
|
|
for (uint i = 0; i < length; i++, elementAddress++) {
|
|
this.Filter(elementAddress, ref objDesc);
|
|
}
|
|
break;
|
|
}
|
|
case ObjectLayout.OTHER_ARRAY_TAG: {
|
|
int postHeaderSize = PostHeader.Size;
|
|
uint length = *(uint*)(objDesc.objectBase + postHeaderSize +
|
|
sizeof(uint));
|
|
size = ObjectLayout.ArraySize(objDesc.vtable, length);
|
|
if (objDesc.vtable.arrayOf == StructuralType.Struct) {
|
|
// pretend the struct is boxed and account for the
|
|
// presence of the PostHeader
|
|
VTable elementVTable = objDesc.vtable.arrayElementClass;
|
|
UIntPtr elementMask = elementVTable.pointerTrackingMask;
|
|
// A structure with no references will have a SPARSE
|
|
// descriptor with no offset values.
|
|
if (elementMask != (UIntPtr) ObjectLayout.SPARSE_TAG) {
|
|
int preHeaderSize = PreHeader.Size;
|
|
int elementSize = objDesc.vtable.arrayElementSize;
|
|
UIntPtr elementAddress =
|
|
objDesc.objectBase + objDesc.vtable.baseLength -
|
|
preHeaderSize - postHeaderSize;
|
|
objDesc.vtable = elementVTable;
|
|
for (uint i = 0; i < length; i++) {
|
|
objDesc.objectBase = elementAddress;
|
|
this.VisitReferenceFieldsTemplateNoInline(ref objDesc);
|
|
elementAddress += elementSize;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ObjectLayout.STRING_TAG: {
|
|
int postHeaderSize = PostHeader.Size;
|
|
uint arrayLength =
|
|
*(uint*)(objDesc.objectBase + postHeaderSize);
|
|
size = ObjectLayout.StringSize(objDesc.vtable, arrayLength);
|
|
break;
|
|
}
|
|
default: {
|
|
// escape case
|
|
VTable.Assert((objectTag & 0x1) == 0,
|
|
"ReferenceVisitor: (objectTag & 0x1) == 0");
|
|
UIntPtr *largeObject = (UIntPtr *) objDesc.objectBase;
|
|
size = ObjectLayout.ObjectSize(objDesc.vtable);
|
|
int *pointerDescription = (int *) pointerTracking;
|
|
int count = *pointerDescription;
|
|
for (int i = 1; i <= count; i++) {
|
|
UIntPtr *loc = largeObject + *(pointerDescription+i);
|
|
this.Filter(loc, ref objDesc);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return size;
|
|
}
|
|
|
|
// Rationale for cutting the fact that no stack probes are permitted in this
|
|
// tree of calls:
|
|
//
|
|
// There is no rationale. This is a bug which has been filed. It is a temporary
|
|
// hack to break the recursion in this method. Recursion needs to open up an
|
|
// opportunity for stack probing. Eventually, perhaps this method can be
|
|
// rewritten to avoid the recursion.
|
|
//
|
|
// Bug 436
|
|
[NoStackLinkCheckTransCut]
|
|
[ManualRefCounts]
|
|
protected unsafe
|
|
void VisitReferenceFieldsTemplate(ref ObjectDescriptor objDesc,
|
|
int count)
|
|
{
|
|
UIntPtr pointerTracking = objDesc.vtable.pointerTrackingMask;
|
|
uint objectTag = (pointerTracking & 0xf);
|
|
switch (objectTag) {
|
|
case ObjectLayout.PTR_VECTOR_TAG:
|
|
case ObjectLayout.PTR_ARRAY_TAG: {
|
|
UIntPtr *elementAddress = (UIntPtr *) objDesc.objectBase;
|
|
for (int i = 0; i < count; i++, elementAddress++) {
|
|
this.Filter(elementAddress, ref objDesc);
|
|
}
|
|
break;
|
|
}
|
|
case ObjectLayout.OTHER_VECTOR_TAG:
|
|
case ObjectLayout.OTHER_ARRAY_TAG: {
|
|
if (objDesc.vtable.arrayOf == StructuralType.Struct) {
|
|
// pretend the struct is boxed and account for the
|
|
// presence of the vtable field
|
|
VTable elementVTable = objDesc.vtable.arrayElementClass;
|
|
UIntPtr elementMask = elementVTable.pointerTrackingMask;
|
|
// A structure with no references will have a SPARSE
|
|
// descriptor with no offset values.
|
|
if (elementMask != (UIntPtr) ObjectLayout.SPARSE_TAG) {
|
|
int postHeaderSize = PostHeader.Size;
|
|
objDesc.objectBase -= postHeaderSize;
|
|
objDesc.secondBase -= postHeaderSize;
|
|
objDesc.extra += postHeaderSize;
|
|
int elementSize = objDesc.vtable.arrayElementSize;
|
|
objDesc.vtable = elementVTable;
|
|
for (int i = 0; i < count; i++) {
|
|
this.VisitReferenceFieldsTemplateNoInline(ref objDesc);
|
|
objDesc.objectBase += elementSize;
|
|
objDesc.secondBase += elementSize;
|
|
objDesc.extra -= elementSize;
|
|
}
|
|
objDesc.objectBase += postHeaderSize;
|
|
objDesc.secondBase += postHeaderSize;
|
|
objDesc.extra -= postHeaderSize;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
throw new Exception("Indexing non-indexed type");
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
internal abstract class DirectReferenceVisitor : ReferenceVisitor
|
|
{
|
|
|
|
[Inline]
|
|
internal abstract unsafe void Visit(UIntPtr *location);
|
|
|
|
}
|
|
|
|
// This visitor should be used in concurrent settings.
|
|
internal abstract class MutableReferenceVisitor : DirectReferenceVisitor
|
|
{
|
|
|
|
#region HELP_DEVIRT
|
|
// TODO: Evaluate the performance impact of these [Inline] annotations,
|
|
// which appeared as part of the CoCo code dump.
|
|
|
|
// This method simply forces the compiler to generate a copy
|
|
// of VisitReferenceFieldsTemplate in this class.
|
|
[Inline]
|
|
[ManualRefCounts]
|
|
internal override UIntPtr VisitReferenceFields(Object obj)
|
|
{
|
|
return this.VisitReferenceFields(Magic.addressOf(obj),
|
|
obj.vtable);
|
|
}
|
|
|
|
// This method simply forces the compiler to generate a copy
|
|
// of VisitReferenceFieldsTemplate in this class.
|
|
[Inline]
|
|
[ManualRefCounts]
|
|
internal override UIntPtr VisitReferenceFields(UIntPtr objectBase,
|
|
VTable vtable)
|
|
{
|
|
ObjectDescriptor objDesc =
|
|
new ObjectDescriptor(vtable, objectBase);
|
|
return VisitReferenceFieldsTemplate(ref objDesc);
|
|
}
|
|
#endregion
|
|
|
|
[Inline]
|
|
protected override unsafe
|
|
void Filter(UIntPtr *location, ref ObjectDescriptor objDesc)
|
|
{
|
|
this.Visit(location);
|
|
}
|
|
|
|
}
|
|
|
|
// This visitor should only be used in situations where the
|
|
// reference fields are known to not be modified by other threads
|
|
// while the visitor is traversing the object.
|
|
internal abstract class NonNullReferenceVisitor : DirectReferenceVisitor
|
|
{
|
|
|
|
#region HELP_DEVIRT
|
|
// This method simply forces the compiler to generate a copy
|
|
// of VisitReferenceFieldsTemplate in this class.
|
|
[ManualRefCounts]
|
|
[Inline]
|
|
internal override UIntPtr VisitReferenceFields(Object obj)
|
|
{
|
|
return this.VisitReferenceFields(Magic.addressOf(obj),
|
|
obj.vtable);
|
|
}
|
|
|
|
// This method simply forces the compiler to generate a copy
|
|
// of VisitReferenceFieldsTemplate in this class.
|
|
[ManualRefCounts]
|
|
[Inline]
|
|
internal override UIntPtr VisitReferenceFields(UIntPtr objectBase,
|
|
VTable vtable)
|
|
{
|
|
ObjectDescriptor objDesc =
|
|
new ObjectDescriptor(vtable, objectBase);
|
|
return VisitReferenceFieldsTemplate(ref objDesc);
|
|
}
|
|
#endregion
|
|
|
|
[Inline]
|
|
protected override unsafe
|
|
void Filter(UIntPtr *location, ref ObjectDescriptor objDesc)
|
|
{
|
|
if (*location != UIntPtr.Zero) {
|
|
this.Visit(location);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
internal abstract class OffsetReferenceVisitor : ReferenceVisitor
|
|
{
|
|
|
|
internal abstract void FieldOffset(UIntPtr offset,
|
|
ref ObjectDescriptor objDesc);
|
|
|
|
#region HELP_DEVIRT
|
|
// This method simply forces the compiler to generate a copy
|
|
// of VisitReferenceFieldsTemplate in this class.
|
|
[ManualRefCounts]
|
|
internal override sealed
|
|
UIntPtr VisitReferenceFields(UIntPtr objectBase, VTable vtable)
|
|
{
|
|
ObjectDescriptor objDesc =
|
|
new ObjectDescriptor(vtable, objectBase);
|
|
return VisitReferenceFieldsTemplate(ref objDesc);
|
|
}
|
|
#endregion
|
|
|
|
[Inline]
|
|
protected override unsafe sealed
|
|
void Filter(UIntPtr *location, ref ObjectDescriptor objDesc)
|
|
{
|
|
UIntPtr offset = ((UIntPtr) location) - objDesc.objectBase;
|
|
this.FieldOffset(offset, ref objDesc);
|
|
}
|
|
|
|
}
|
|
|
|
}
|