-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Ldvirtftn implementation for interpreter #116661
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
Conversation
- ldvirtftn based (generic virtual calls) - code pointer based (certain static virtual calls) - ldvirtftn path to delegate creation
…put the interpreter code bytes into the vtable (with this change, the vtable, etc, always have the InterpreterPrecode in it.
There was a problem hiding this 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 implements improvements for handling generic virtual calls in the interpreter by introducing an ldvirtftn-based pathway, supporting code pointer based static virtual calls, and updating delegate creation support.
- Adds tests in the interpreter for GenericVirtualMethod and new delegate methods.
- Updates JIT and interpreter helper functions to propagate interpreter code flags and allocate InterpreterPrecode correctly.
- Modifies the interpreter compiler to support a new ldvirtftn path and a corresponding calli opcode.
Reviewed Changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.
Show a summary per file
File | Description |
---|---|
src/tests/JIT/interpreter/Interpreter.cs | Adds tests for generic virtual methods and new call paths. |
src/coreclr/vm/prestub.cpp | Removes inline InterpreterPrecode allocation and migrates it to JitCompileCodeLocked. |
src/coreclr/vm/precode.h | Introduces FromEntryPoint helper in InterpreterPrecode. |
src/coreclr/vm/jitinterface.h & jitinterface.cpp | Updates UnsafeJitFunction signature and handling for interpreter flags. |
src/coreclr/interpreter/intops.def | Fixes the opcode name for calli. |
src/coreclr/interpreter/compiler.{h,cpp} | Adds EmitPushLdvirtftn and updates ldvirtftn handling in call emission. |
Comments suppressed due to low confidence (1)
src/coreclr/interpreter/compiler.cpp:2664
- It would help to document or clarify the expected initialization and invariant of extraParamArgLocation to ensure its value is correctly maintained across different code paths.
assert(extraParamArgLocation == INT_MAX);
src/coreclr/interpreter/compiler.cpp
Outdated
void DECLSPEC_NORETURN Interp_CompilationFail(const char *msg) | ||
{ | ||
// This is a fatal error, so we don't return. | ||
throw std::exception(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider throwing a more descriptive exception (or a custom exception type) in Interp_CompilationFail instead of using std::exception, to improve clarity when a fatal compilation error occurs.
void DECLSPEC_NORETURN Interp_CompilationFail(const char *msg) | |
{ | |
// This is a fatal error, so we don't return. | |
throw std::exception(); | |
// Custom exception for fatal compilation errors | |
class InterpCompilationException : public std::exception | |
{ | |
private: | |
const char* m_msg; | |
public: | |
explicit InterpCompilationException(const char* msg) : m_msg(msg) {} | |
virtual const char* what() const noexcept override | |
{ | |
return m_msg; | |
} | |
}; | |
void DECLSPEC_NORETURN Interp_CompilationFail(const char *msg) | |
{ | |
// This is a fatal error, so we don't return. | |
throw InterpCompilationException(msg); |
Copilot uses AI. Check for mistakes.
CORINFO_GENERICHANDLE_RESULT embedInfo; | ||
m_compHnd->embedGenericHandle(pResolvedToken, true, m_methodInfo->ftn, &embedInfo); | ||
assert(embedInfo.compileTimeHandle != NULL); | ||
int typeVar = EmitGenericHandleAsVar(embedInfo); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just for my clarity, GenericHandle
has nothing to do with generics ? The generic part just means that it can be any handle, for example a non-generic method handle ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes.
src/coreclr/interpreter/compiler.cpp
Outdated
// This check really only applies to intrinsic Array.Address methods | ||
if (callInfo.sig.callConv & CORINFO_CALLCONV_PARAMTYPE) | ||
{ | ||
Interp_CompilationFail("Currently do not support LDFTN of Parameterized functions"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The preexisting way of handling errors like this is too set m_hasInvalidCode
to true and go to exit_bad_code
. It would be nice to keep that concept or change to the new one everywhere.
Btw, I've found on Friday that we are not handling the CORJIT_BADCODE
that we return at exit_bad_code
at the caller, so it would need to be fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm now replacing all the m_hasInvalidCode
stuff with a new exception based flow. It's simple/straightforward.
@@ -429,17 +429,6 @@ PCODE MethodDesc::PrepareILBasedCode(PrepareCodeConfig* pConfig) | |||
LOG((LF_CLASSLOADER, LL_INFO1000000, | |||
" In PrepareILBasedCode, calling JitCompileCode\n")); | |||
pCode = JitCompileCode(pConfig); | |||
#ifdef FEATURE_INTERPRETER | |||
if (pConfig->IsInterpreterCode()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With your change, the IsInterpreterCode
method and the underlying field is no longer needed. Can you please remove it?
src/coreclr/interpreter/compiler.cpp
Outdated
callArgs[0] = thisVar; | ||
callArgs[1] = typeVar; | ||
callArgs[2] = methodVar; | ||
callArgs[3] = -1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we have a named constant for this -1? I think it's the call args end of list sentinel?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already have one: CALL_ARGS_TERMINATOR, it used just at few places.
{ | ||
// Resolve a virtual call using the helper function to a function pointer, and then call through that | ||
assert(!"Need to support ldvirtftn path"); | ||
assert(extraParamArgLocation == INT_MAX); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we have a comment explaining what it means if this assert fails? I assume it's for methods that have a hidden extra parameter as part of their calling convention, and we don't support those yet
LGTM aside from existing comments |
…inor code feedback/build errors.
assert(!"Unsupported element type for STELEM"); | ||
m_hasInvalidCode = true; | ||
goto exit_bad_code; | ||
BADCODE("Unsupported element type for STELEM"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this unsupported in a sense that the other element types than the one we currently handle are invalid or in the sense that we don't support it yet?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know. I'm just replacing the m_hasInvalidCode
logic, not changing behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was asking because the new mechanism would now let the interpreter compilation return error code and we would fall back to JIT. So during the bringup, there may be cases when we think things are working interpreted, but they'd be running jitted instead. When we generate the error as a result of bad IL, it is ok, because the JIT would fail too.
So it seems that for cases when we don't support something yet, we should assert.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thank you!
src/coreclr/interpreter/eeinterp.cpp
Outdated
// FIXME this shouldn't be here | ||
compHnd->setMethodAttribs(methodInfo->ftn, CORINFO_FLG_INTERPRETER); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// FIXME this shouldn't be here | |
compHnd->setMethodAttribs(methodInfo->ftn, CORINFO_FLG_INTERPRETER); |
Can this and CORINFO_FLG_INTERPRETER
be deleted too?
src/coreclr/interpreter/compiler.cpp
Outdated
@@ -47,9 +47,10 @@ thread_local ICorJitInfo* t_InterpJitInfoTls = nullptr; | |||
static const char *g_stackTypeString[] = { "I4", "I8", "R4", "R8", "O ", "VT", "MP", "F " }; | |||
|
|||
/*****************************************************************************/ | |||
void DECLSPEC_NORETURN Interp_NOMEM() | |||
void DECLSPEC_NORETURN Interp_CompilationFail(const char *msg) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
void DECLSPEC_NORETURN Interp_CompilationFail(const char *msg) |
This look unused. Delete it?
@davidwrighton I have tried to run this PR with coreclr tests and it crashes because of the runtime/src/coreclr/vm/prestub.cpp Lines 837 to 845 in 504fb7d
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit
…n the correct values in thisTransform
Tagging subscribers to this area: @BrzVlad, @janvorli, @kg |
1 similar comment
Tagging subscribers to this area: @BrzVlad, @janvorli, @kg |
@davidwrighton the interpreter test is failing on arm64 Windows in bc.GenericVirtualMethod with error code C0000602 |
@janvorli Yeah, I've spent my spare time today to setup an Windows Arm64 test machine for that. I suspect its another subtle bug in the calling convention work. |
Implement more complex call scenarios
And as a bonus, to keep the tests working...
Rework where we attach intepreted code to a method, so that we never put the interpreter code bytes into the vtable (with this change, the vtable, etc, always have the InterpreterPrecode in it.
Add scheme for enabling logging of the CallStubGenerator.
Fix calling convention handling for jit calling convention to interpreter calling convention for Unix X64 and Windows Arm64.