Skip to content

Conversation

@AaronRobinsonMSFT
Copy link
Member

@AaronRobinsonMSFT AaronRobinsonMSFT commented Jul 21, 2025

This removes usage of the secret argument in P/Invokes. It removes sharing for all P/Invoke IL stubs except CALLI.

Depends on #118462

@AaronRobinsonMSFT AaronRobinsonMSFT added this to the 11.0.0 milestone Jul 21, 2025
@AaronRobinsonMSFT AaronRobinsonMSFT added NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons) area-Interop-coreclr labels Jul 21, 2025
@AaronRobinsonMSFT
Copy link
Member Author

@kg @janvorli This is a simple change that removes the shared IL stubs and use of the secret argument for P/Invokes. This isn't something that would go in until .NET 11, but might be sufficient to prototype P/Invokes via the interpreter. A follow-up approach would be to remove the use of IL Stubs entirely and rely on the transient code pattern. Using the transient code pattern proved to be a bit more work thant I'd anticipated so I reduced it to this approach.

/cc @jkotas

@jkotas
Copy link
Member

jkotas commented Jul 21, 2025

There is more cleanup that should be done as part of this:

  • MethodDesc::RequiresMethodDescCallingConvention should drop IsNDirect condition
  • CORJIT_FLAG_PUBLISH_SECRET_PARAM should not be set for PInvoke stubs
  • HasMDContextArg() should drop IsNDirect condition
  • There are probably some changes needed around GetActualInteropMethodDesc

@AaronRobinsonMSFT
Copy link
Member Author

There is more cleanup that should be done as part of this:

  • MethodDesc::RequiresMethodDescCallingConvention should drop IsNDirect condition
  • CORJIT_FLAG_PUBLISH_SECRET_PARAM should not be set for PInvoke stubs
  • HasMDContextArg() should drop IsNDirect condition
  • There are probably some changes needed around GetActualInteropMethodDesc

Yep. This was a quick and dirty solution I could pass off to the interpreter team.

@AaronRobinsonMSFT
Copy link
Member Author

  • MethodDesc::RequiresMethodDescCallingConvention should drop IsNDirect condition

Done.

  • CORJIT_FLAG_PUBLISH_SECRET_PARAM should not be set for PInvoke stubs

Inserted a SF_IsForwardStub(m_dwStubFlags) check before the flag is set in stubs.

  • HasMDContextArg() should drop IsNDirect condition

Removed the PInvoke check.

  • There are probably some changes needed around GetActualInteropMethodDesc

Usage is blocked by HasMDContextArg() so updates flow to the single usage. Suggestions on what I could assert?

@jkotas
Copy link
Member

jkotas commented Jul 29, 2025

Usage is blocked by HasMDContextArg() so updates flow to the single usage.

If I am reading the code correctly,

m_crawl.pFunc = m_crawl.pFrame->GetFunction();
will cause stackwalker to report the IL stub method desc instead of the actual method desc. It may have undesirable fallout - I am thinking about problems like:

  • Are IL stubs going to show up in stacktraces, e.g. when P/Invoke throws an exception that gets unhandled?
  • Can it confuse debuggers?

This removes usage of the secret argument in P/Invokes.
It also removes all usage of shared IL stubs.
Clean-up dead code in RequiresMethodDescCallingConvention and RequiresStableEntryPoint.
@AaronRobinsonMSFT
Copy link
Member Author

will cause stackwalker to report the IL stub method desc instead of the actual method desc. It may have undesirable fallout - I am thinking about problems like:

You're right. I don't know the best way to do this. I suppose I could get the target from the DynamicMethodDesc and pass that back?

@jkotas
Copy link
Member

jkotas commented Jul 29, 2025

I think it would best to minimize stackwalker dependencies if possible.

Can we get InlinedCallFrame::m_Datum initialized to point to the actual target method desc? It is done here:

assert(callType == CT_USER_FUNC);
void* pEmbedMethodHandle = nullptr;
CORINFO_METHOD_HANDLE embedMethodHandle =
comp->info.compCompHnd->embedMethodHandle(call->gtCallMethHnd, &pEmbedMethodHandle);
noway_assert((!embedMethodHandle) != (!pEmbedMethodHandle));
if (embedMethodHandle != nullptr)
{
// InlinedCallFrame.callSiteTarget = methodHandle
src = AddrGen(embedMethodHandle);
}
else
{
// InlinedCallFrame.callSiteTarget = *pEmbedMethodHandle
src = Ind(AddrGen(pEmbedMethodHandle));
}
.

It is also going to fix a type mismatch: InlinedCallFrame::m_Datum is expected to be NDirectMethodDesc (modulo some bit twiddling and special cases), but it is DynamicMethodDesc with this change.

@AaronRobinsonMSFT
Copy link
Member Author

It is also going to fix a type mismatch: InlinedCallFrame::m_Datum is expected to be NDirectMethodDesc (modulo some bit twiddling and special cases), but it is DynamicMethodDesc with this change.

Sure, I can give that a shot. This doesn't ever appear to be set for coreclr JIT, the embedMethodHandle is a nop.

CORINFO_METHOD_HANDLE CEEInfo::embedMethodHandle(CORINFO_METHOD_HANDLE handle,
void **ppIndirection)
{
CONTRACTL {
NOTHROW;
GC_NOTRIGGER;
MODE_PREEMPTIVE;
}
CONTRACTL_END;
if (ppIndirection != NULL)
*ppIndirection = NULL;
JIT_TO_EE_TRANSITION_LEAF();
EE_TO_JIT_TRANSITION_LEAF();
return handle;
}

Side note, I've really got no idea what is expected here. All of this is a huge confusion about various states of fields and very loosely typed in practice. I'm really just randomly guessing what things are expected at this point, sorry.

@AaronRobinsonMSFT
Copy link
Member Author

AaronRobinsonMSFT commented Jul 29, 2025

My confusion here is about m_Datum and the various other terms for things. It seems the m_Datum is also called callSiteTarget based on JIT comments, yay. I get that now. The CT_USER_FUNC seems to represent the actual MethodDesc, where as the CT_INDIRECT represents a calli, I think. I also believe I understand why setting m_Datum will make this work, but that is still a bit of a mystery to me in some cases about when some of these code paths are taken.

@AaronRobinsonMSFT AaronRobinsonMSFT marked this pull request as ready for review August 21, 2025 16:09
Copilot AI review requested due to automatic review settings August 21, 2025 16:09
@AaronRobinsonMSFT AaronRobinsonMSFT removed the NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons) label Aug 21, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR removes shared IL stubs for P/Invokes except for CALLI instructions. The primary objective is to eliminate the use of secret arguments in P/Invoke stubs while maintaining sharing only for CALLI scenarios.

Key changes:

  • Removes secret argument handling from P/Invoke IL stubs
  • Updates VarargPInvokeStubWorker to take fewer parameters
  • Modifies stub sharing logic to exclude forward P/Invokes (except CALLI)

Reviewed Changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/coreclr/vm/stubmgr.h Adds new GetFirstArg method and refactors GetThisPtr to use it
src/coreclr/vm/stubmgr.cpp Updates logging format specifiers and removes P/Invoke handling from IL stub tracing
src/coreclr/vm/stackwalk.cpp Moves variable declaration closer to usage
src/coreclr/vm/prestub.cpp Removes precompiled IL stub sharing for P/Invokes and updates assertion logic
src/coreclr/vm/method.hpp Renames RequiresMethodDescCallingConvention to RequiresMDContextArg and updates related methods
src/coreclr/vm/method.cpp Implements renamed methods and removes P/Invoke context argument requirements
src/coreclr/vm/jitinterface.cpp Adds P/Invoke method descriptor to VASigCookie creation
src/coreclr/vm/i386/asmhelpers.asm Reduces parameter count for VarargPInvokeStubWorker call
src/coreclr/vm/i386/asmhelpers.S Removes unused parameter from assembly stub
src/coreclr/vm/frames.h Updates comments to clarify PInvokeMethodDesc usage
src/coreclr/vm/dynamicmethod.cpp Updates method name calls to use new convention
src/coreclr/vm/dllimport.h Adds shared stub flag and reorganizes stub checking logic
src/coreclr/vm/dllimport.cpp Major refactoring of P/Invoke stub generation to remove secret argument usage
src/coreclr/vm/comdelegate.cpp Removes redundant MethodDesc qualification
src/coreclr/vm/codeman.h Converts CodeHeapRequestInfo to proper class with encapsulated members
src/coreclr/vm/codeman.cpp Updates to use new CodeHeapRequestInfo interface and improves method descriptor handling
src/coreclr/vm/cgensys.h Updates VarargPInvokeStubWorker signature
src/coreclr/vm/ceeload.h Adds MethodDesc field to VASigCookie and improves encapsulation
src/coreclr/vm/ceeload.cpp Refactors VASigCookie creation to embed MethodDesc and use loader allocator
src/coreclr/vm/amd64/pinvokestubs.S Removes unused parameter from stub worker call
src/coreclr/vm/amd64/PInvokeStubs.asm Removes unused parameter from stub worker call
Comments suppressed due to low confidence (1)

src/coreclr/vm/method.cpp:1

  • The removal of the fEstimateForChunk parameter and related logic eliminates the interface check optimization that was previously guarded by the parameter. This may impact performance for interface method processing during chunk estimation.
// Licensed to the .NET Foundation under one or more agreements.

@AaronRobinsonMSFT AaronRobinsonMSFT merged commit eb247f3 into dotnet:main Aug 21, 2025
98 of 99 checks passed
@AaronRobinsonMSFT AaronRobinsonMSFT deleted the remove_shared_ilstubs branch August 21, 2025 16:12
jkotas added a commit to jkotas/runtime that referenced this pull request Aug 23, 2025
GCStress is not able to find the original version of the code for PInvoke stubs after dotnet#117901. We need to compensate
for the MethodDesc adjustment done for PInvoke stubs when storing the original version of the code during GC stress.
jkotas added a commit to jkotas/runtime that referenced this pull request Aug 23, 2025
Interop marshalling of varargs needs MethodDesc calling convention to support ldftn <PInvoke method with varargs>. It is not possible to smugle the target MethodDesc* via vararg cookie in this case.
jkotas added a commit to jkotas/runtime that referenced this pull request Aug 23, 2025
GCStress is not able to find the original version of the code for PInvoke stubs after dotnet#117901. We need to compensate
for the MethodDesc adjustment done for PInvoke stubs when storing the original version of the code during GC stress.
jkotas added a commit to jkotas/runtime that referenced this pull request Aug 23, 2025
Interop marshalling of varargs needs MethodDesc calling convention to support ldftn <PInvoke method with varargs>. It is not possible to smugle the target MethodDesc* via vararg cookie in this case.
VSadov pushed a commit to VSadov/runtime that referenced this pull request Aug 23, 2025
GCStress is not able to find the original version of the code for PInvoke stubs after dotnet#117901. We need to compensate
for the MethodDesc adjustment done for PInvoke stubs when storing the original version of the code during GC stress.
VSadov pushed a commit to VSadov/runtime that referenced this pull request Aug 23, 2025
Interop marshalling of varargs needs MethodDesc calling convention to support ldftn <PInvoke method with varargs>. It is not possible to smugle the target MethodDesc* via vararg cookie in this case.
jkotas added a commit that referenced this pull request Aug 24, 2025
* Fix GCStress regression

GCStress is not able to find the original version of the code for PInvoke stubs after #117901. We need to compensate
for the MethodDesc adjustment done for PInvoke stubs when storing the original version of the code during GC stress.

* Partial revert of #117901 for vararg stubs

Interop marshalling of varargs needs MethodDesc calling convention to support ldftn <PInvoke method with varargs>. It is not possible to smuggle the target MethodDesc* via vararg cookie in this case.

Co-authored-by: Aaron Robinson <arobins@microsoft.com>
@github-actions github-actions bot locked and limited conversation to collaborators Sep 21, 2025
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.

2 participants