Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Report by-ref-like types to the GC (SpanOfT) #9034

Merged
merged 3 commits into from
Jan 23, 2017

Conversation

kouvel
Copy link
Member

@kouvel kouvel commented Jan 21, 2017

Fixes #8517

@kouvel
Copy link
Member Author

kouvel commented Jan 21, 2017

@jkotas


_ASSERTE(pMT->IsByRefLike());

if (pMT == g_TypedReferenceMT)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should do the same thing as g_pByReferenceClass:

if (pMT->HasSameTypeDefAs(g_pByReferenceClass) || pMT == g_TypedReferenceMT)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand, could you please elaborate?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once the rules for byref-like type in C# compiler are relaxed, it should be possible to write:

struct MyStruct
{
    TypedReference field1;
    Span<byte> field2;
    TypedReference field3;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

field1 and field3 won't be reported correctly because of the early out.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CEEInfo::getClassGClayout has the same bug

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks to me like CEEInfo::getClassGClayout would only return early if the topmost object is a TypedReference, which seems acceptable. If a by-ref-like object contains a TypedReference as in your example, it would call ComputeGCLayout, which would iterate over all fields in the by-ref-like type. I'm not seeing an issue there either.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first field of TypedReference is actually byref, but it is not recorded as byref in metadata. It has been special cased for GC reporting in multiple places. This is yet another places that should be special cased for it.

Ideally, we would change the implementation of TypedReference to:

struct TypedReference
{
    ByReference<byte> _value;
    IntPtr _type;
...
}

and get rid of the special casing. But until/if that happens, we should continue special casing it.

Copy link
Member Author

@kouvel kouvel Jan 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If TypedReference is not recorded as byref, then it seems to me like it would have already had an issue before this change - it seems then that TypedRefernece would not have been reported properly to the GC. Are you suggesting that there is an issue with the reporting for TypedReference as well? If so, I'll need to test with TypedReference as well to see the problem before fixing it.

Copy link
Member

@jkotas jkotas Jan 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TypedReference has not been allowed as a field and so there is no problem today. They have been only allowed as regular arguments. We should not come for regular TypedReference arguments at all because of they are special cased above ReportPointersFromValueType.

Since you have added the special case for TypedReference here, I thought you want to prepare this code for case where TypedReference are allowed as fields of other byref-like types. There is a good chance that TypedReferences are going to be allowed as fields as natural fallout from the relaxations of rules for byref-like types in C#.

Either of these two options would sound reasonable to me:

  • Do not prepare this for TypedReference fields - do not handle TypedReference at all here
  • Prepare this for TypedReference fields - handle TypedReference same way as ByReference<T> here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok how does 797ec39 look? I kept the check considering TypedReference is currently not implemented the way in which you suggest, but the new code treats it similarly to ByReferece in the sense the first field of both is the pointer, which should conform to the way in which TypedReference is currently implemented.

if (!pMT->ContainsPointers())
return;

if(pMT->ContainsPointers())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't this be just

   if (!pMT->ContainsPointers() && !pMT->IsByRefLike())
       return;

   ... existing code...

?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I would be useful to add some comments if it needs to be what you have written.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I currently do not have a complete understanding of GC holes with respect to Unix and structs passed in registers, I'm conservatively preserving the previous behavior by calling ReportPointersFromStructInRegisters only under the previous conditions. #9033 is intended to track potential issues on Unix, a fix for which should hopefully resolve the question.


_ASSERTE(pMT->IsByRefLike());

// TODO: TypedReference should ideally be implemneted as a by-ref-like struct containing a ByReference<T> field,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: typo implemneted

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

@jkotas
Copy link
Member

jkotas commented Jan 21, 2017

It would be nice to make a matching change in CEEInfo::getClassGClayout in vm\jitinterface.cpp so that it is sync. Move the special case for g_TypedReferenceMT from CEEInfo::getClassGClayout to ComputeGCLayout. Something like:

static unsigned ComputeGCLayout(MethodTable * pMT, BYTE* gcPtrs)
{
    STANDARD_VM_CONTRACT;

    unsigned result = 0;

    _ASSERTE(pMT->IsValueType());

    // TODO: TypedReference should ideally be implemented as a by-ref-like struct containing a ByReference<T> field,  
    // in which case the check for g_TypedReferenceMT below would not be necessary
    if (pMT == g_TypedReferenceMT)
    {
        if (gcPtrs[0] == TYPE_GC_NONE)
        {
            gcPtrs[0] = TYPE_GC_BYREF;
            result++;
        }
        else if (gcPtrs[fieldStartIndex] != TYPE_GC_BYREF)
        {
            COMPlusThrowHR(COR_E_BADIMAGEFORMAT);
        }
        return result;
    }

    ... existing code ...
}

@jkotas
Copy link
Member

jkotas commented Jan 21, 2017

LGTM otherwise. Thanks!

@kouvel
Copy link
Member Author

kouvel commented Jan 21, 2017

if (gcPtrs[0] == TYPE_GC_NONE)

It looks like getClassGClayout memsets it to TYPE_GC_NONE but the recursive call from ComputeGCLayout does not, so I have omitted this part.

else if (gcPtrs[fieldStartIndex] != TYPE_GC_BYREF)

I don't see where this array is initialized to some default value, so I have omitted this part as well. Simplified to:

    if (pMT == g_TypedReferenceMT)
    {
        gcPtrs[0] = TYPE_GC_BYREF;
        gcPtrs[1] = TYPE_GC_NONE;
        return 1;
    }

And added the comment to getClassGClayout as well, which if/when changed, would remove the necessity for the addition to ComputeGCLayout.

@kouvel
Copy link
Member Author

kouvel commented Jan 21, 2017

@dotnet-bot test this please

1 similar comment
@kouvel
Copy link
Member Author

kouvel commented Jan 22, 2017

@dotnet-bot test this please

@kouvel kouvel merged commit 62ac5f0 into dotnet:master Jan 23, 2017
@kouvel kouvel deleted the SpanGcReporting branch January 23, 2017 07:10
@karelz karelz modified the milestone: 2.0.0 Aug 28, 2017
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
Report by-ref-like types to the GC (SpanOfT)

Fixes dotnet/coreclr#8517

Commit migrated from dotnet/coreclr@62ac5f0
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants