Skip to content
This repository was archived by the owner on Nov 1, 2020. It is now read-only.

Conversation

@MichalStrehovsky
Copy link
Member

@MichalStrehovsky MichalStrehovsky commented Jan 24, 2017

To support calling canonical interface methods on generic valuetypes,
the compiler needs to generate unboxing+instantiating thunks that bridge
the difference between two calling conventions.

As a refresher:

  • Instance methods on shared generic valuetypes expect two arguments
    (aside from the arguments declared in the signature): a ByRef to the
    first byte of the value of the valuetype (this), and a generic context
    argument (EEType)
  • Interface calls expect this to be a reference type (with the generic
    context to be inferred from this by the callee)

Instantiating and unboxing stubs bridge this by extracting a managed
pointer out of a boxed valuetype, along with the EEType of the boxed
valuetype (to provide the generic context) before dispatching to the
instance method.

We compile them by:

  • Pretending the unboxing stub is an instance method on a reference type
    with the same layout as a boxed valuetype
  • Having the unboxing stub load the m_pEEType field (to get generic
    context) and a byref to the actual value (to get a this expected by
    valuetype methods)
  • Generating a call to the instance method on the valuetype through a
    wrapper that has an explicit generic context parameter in it's
    signature.

Fixes #2520.

To support calling canonical interface methods on generic valuetypes,
the compiler needs to generate unboxing+instantiating thunks that bridge
the difference between two calling conventions.

As a refresher:
* Instance methods on shared generic valuetypes expect two arguments
(aside from the arguments declared in the signature): a ByRef to the
first byte of the value of the valuetype (`this`), and a generic context
argument (EEType)
* Interface calls expect `this` to be a reference type (with the generic
context to be inferred from `this` by the callee)

Instantiating and unboxing stubs bridge this by extracting a managed
pointer out of a boxed valuetype, along with the EEType of the boxed
valuetype (to provide the generic context) before dispatching to the
instance method.

We compile them by:
* Pretending the unboxing stub is an instance method on a reference type
with the same layout as a boxed valuetype
* Having the unboxing stub load the `m_pEEType` field (to get generic
context) and a byref to the actual value (to get a `this` expected by
valuetype methods)
* Generating a call to the instance method on the valuetype through a
wrapper that has an explicit generic context parameter in it's
signature.

parameters[0] = Context.GetWellKnownType(WellKnownType.Object).GetKnownField("m_pEEType").FieldType;
for (int i = 0; i < _methodRepresented.Signature.Length; i++)
parameters[i + 1] = _methodRepresented.Signature[0];
Copy link
Member Author

Choose a reason for hiding this comment

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

.Signature[i]...


// Does the method have a hidden parameter?
if (method.RequiresInstArg() && !isFatFunctionPointer)
if (method.RequiresInstArg() && !isFatFunctionPointer && !_compilation.TypeSystemContext.IsSpecialUnboxingThunkTargetMethod(method))
Copy link
Member Author

Choose a reason for hiding this comment

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

We could get rid of the special casing here by making RequiresInstArg (and likely it's friends too) a virtual method on MethodDesc. Might also help when we make MDArray.Address method shared generic friendly. I'm conflicted about that.

Copy link
Member

Choose a reason for hiding this comment

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

Probably not a bad idea.

{
get
{
StringBuilder sb = new StringBuilder();
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'm wondering if I should just move this all to NodeFactory so that I have access to NameMangler and can generate stable non-conflicting names. The only reason why this cares about names is to generate a unique symbol.

@MichalStrehovsky
Copy link
Member Author

@davidwrighton I think I made this work without special casing in RyuJIT's importer. PTAL

/// If the type is not generic, returns the <paramref name="type"/>.
/// </summary>
private static InstantiatedType InstantiateAsOpen(this MetadataType type)
public static TypeDesc InstantiateAsOpen(this TypeDesc type)
Copy link
Member

Choose a reason for hiding this comment

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

I believe this terminology for Open is quite confusing. It really means instantiate such that if the type parameters from an open instantiation were substituted in, the result would be the same as the type definition. Which is really bizarre. (The open instantiation is used to refer to the uninstantiated form in a number of documents, and is the same thing as the typical instantiation.)

/// Creates an open instantiation of a field. Given Foo&lt;T&gt;.Field, returns
/// Foo&lt;!0&gt;.Field. If the owning type is not generic, returns the <paramref name="field"/>.
/// </summary>
public static FieldDesc InstantiateAsOpen(this FieldDesc field)
Copy link
Member

Choose a reason for hiding this comment

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

Same naming issue as above. I don't have a good name, but I don't like Open.

// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
Copy link
Member

Choose a reason for hiding this comment

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

Somewhere in here we need a giant comment describing what's happening here. I understand what you're doing, but it wouldn't be obvious from the pile of code without knowing what the PR is supposed to do.

@davidwrighton
Copy link
Member

@MichalStrehovsky Yep, I think this is a correct approach. I couldn't do it this way in NUTC, as NUTC couldn't construct new types/methods, but this is viable.

@MichalStrehovsky MichalStrehovsky changed the title [WIP] Add instantiating unboxing stubs Add instantiating unboxing stubs Jan 27, 2017
@davidwrighton
Copy link
Member

lgtm

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants