Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JIT: Update class for Unsafe.As<>() #85954

Merged
merged 12 commits into from May 9, 2023
Merged

Conversation

EgorBo
Copy link
Member

@EgorBo EgorBo commented May 8, 2023

We used to import T Unsafe.As<T>(object o) where T : class as a no-op and didn't benefit from a more exact T hint.

Closes #84377 (cc @stephentoub)
Closes #85906 (cc @MichalStrehovsky) - checked that it also devirtualizes the interface call

public class ClassA
{
    public virtual int GetVal() => 1;
}

public sealed class ClassB : ClassA
{
    public override int GetVal() => 42;
}


void GetValUnsafe(ClassA tc) => Unsafe.As<ClassB>(tc).GetVal();

Old codegen:

; Method GetValUnsafe(ClassA):this
       sub      rsp, 40
       mov      rcx, rdx
       mov      rax, qword ptr [rdx]
       mov      rax, qword ptr [rax+48H]
       call     [rax+20H]ClassA:GetVal():int:this
       nop      
       add      rsp, 40
       ret      
; Total bytes of code: 23

New codegen:

; Method GetValUnsafe(ClassB):this
       cmp      byte  ptr [rdx], dl
       ret      
; Total bytes of code: 3

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label May 8, 2023
@ghost ghost assigned EgorBo May 8, 2023
@ghost
Copy link

ghost commented May 8, 2023

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

Issue Details

We used to import T Unsafe.As<T>(object o) where T : class as no-op basically and ignored T.

Closes #84377 (cc @stephentoub)
Closes #85906 (cc @MichalStrehovsky)

public class ClassA
{
    public virtual int GetVal() => 1;
}

public sealed class ClassB : ClassA
{
    public override int GetVal() => 42;
}


void GetValUnsafe(ClassA tc) => Unsafe.As<ClassB>(tc).GetVal();

Old codegen:

; Method GetValUnsafe(ClassA):this
       sub      rsp, 40
       mov      rcx, rdx
       mov      rax, qword ptr [rdx]
       mov      rax, qword ptr [rax+48H]
       call     [rax+20H]ClassA:GetVal():int:this
       nop      
       add      rsp, 40
       ret      
; Total bytes of code: 23

New codegen:

; Method GetValUnsafe(ClassB):this
       cmp      byte  ptr [rdx], dl
       ret      
; Total bytes of code: 3
Author: EgorBo
Assignees: -
Labels:

area-CodeGen-coreclr

Milestone: -

{
CORINFO_GENERICHANDLE_RESULT embedInfo;
info.compCompHnd->embedGenericHandle(pResolvedToken, false, &embedInfo);
if (!embedInfo.lookup.lookupKind.needsRuntimeLookup)
Copy link
Member

Choose a reason for hiding this comment

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

I think you want to check for CORINFO_FLG_SHAREDINST flag here.

I do not think you need to re-resolve the signature when it is not shared. sig->sigInst.methInst[0] should have the exact type for the non-shared case.

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 do not think you need to re-resolve the signature when it is not shared. sig->sigInst.methInst[0] should have the exact type for the non-shared case.

Thanks, I was wondering about that too. I though that since I only need it here I can request that on demand, will change the original sig.

Copy link
Member Author

Choose a reason for hiding this comment

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

@jkotas it seems that we're losing the exact sig due to this line of getCallInfo:

pTargetMD = pTargetMD->GetWrappedMethodDesc();

this wrapped pMD is then used for getMethodSigInternal does it look correct to you?

Copy link
Member Author

@EgorBo EgorBo May 9, 2023

Choose a reason for hiding this comment

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

Myabe it should be here if (pTargetMD->IsWrapperStub()) ?

Copy link
Member

Choose a reason for hiding this comment

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

Does it work with the last update, or do you still need help with this?

Copy link
Member Author

Choose a reason for hiding this comment

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

Does it work with the last update, or do you still need help with this?

Still need help, the current impl doesn't work since we always have System._Canon as methodInst[0], I just prepared the code to pick up a fix in the getCallInfo

Copy link
Member

Choose a reason for hiding this comment

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

getCallInfo implementation is correct. getCallInfo returns information about how the method should be called. Unsafe.As<object> should be called as Unsafe.As<Canon> with hidden argument, so it is that's what getCallInfo returns, including signature for Unsafe.As<Canon>. "Fixing" getCallInfo to return some kind of different signature would require reviewing all places that use the signature returned by getCallInfo and making sure that they behave correctly.

I think you want to take the method handle from CORINFO_RESOLVED_TOKEN and call getMethodSig on that. It should return what you are looking for.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah I see, thanks! I assume we only need to do that for Unsafe.As?

Copy link
Member

Choose a reason for hiding this comment

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

Ah I see, thanks! I assume we only need to do that for Unsafe.As?

Yes - to fix the issues that you have linked.

A similar approach can be probably used to improve the type precision in other places, but that can be left for future.

const CORINFO_CLASS_HANDLE inst = sig->sigInst.methInst[0];
assert(inst != nullptr);

if ((info.compCompHnd->getClassAttribs(inst) & CORINFO_FLG_SHAREDINST) == 0)
Copy link
Member

Choose a reason for hiding this comment

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

This is going to make us skip e.g. Unsafe.As<List<Canon>>. We do not want that.

Delete this early out and just depend on isMoreSpecificType to see whether the type is more exact.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks, addressed

@EgorBo EgorBo marked this pull request as ready for review May 9, 2023 14:48
@EgorBo
Copy link
Member Author

EgorBo commented May 9, 2023

Failure is #35066

@EgorBo EgorBo merged commit 7f35658 into dotnet:main May 9, 2023
126 of 129 checks passed
@EgorBo EgorBo deleted the unsafe-update-type branch May 9, 2023 21:42
@dotnet dotnet locked as resolved and limited conversation to collaborators Jun 9, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
Projects
None yet
3 participants