-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Fix incorrect devirtualization in CG2 #37370
Conversation
When making a constrained virtual method call on a boxed value type, CG2 devirtualizes the call incorrectly resulting in a direct call. This breaks code patterns like: ``` foreach (TestEnum val in Enum.GetValues(typeof(TestEnum))) { buffer += val.ToString(); } ``` In the call to ToString(), `getCallInfo` is passed a boxed `TestEnum` and is calling `object.ToString()`. The constrained type is `TestEnum` so we end up making a direct call to `object.ToString()`. Fixed by removing the constrained type from devirtualization consideration which is how Crossgen treats this call.
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, thanks for fixing!
@@ -1301,7 +1301,7 @@ private void getFieldInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_MET | |||
// | |||
// Note that it is safe to devirtualize in the following cases, since a servicing event cannot later modify it | |||
// 1) Callvirt on a virtual final method of a value type - since value types are sealed types as per ECMA spec | |||
devirt = (constrainedType ?? targetMethod.OwningType).IsValueType || targetMethod.IsInternalCall; | |||
devirt = targetMethod.OwningType.IsValueType || targetMethod.IsInternalCall; |
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.
Btw, this code is still different from what crossgen does - the comment above ends at 1)
, but the VM side special cases 3 things - this seems to be missing an optimization around Delegate.Invoke
- we probably generate less efficient code for that as a result.
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 added the two other cases (delegates and intrinsics) so we line with up with CG1
Ensure our devirtualization matches crossgen1's behavior. We were not devirtualizing delegate invoke calls, and we were devirtualizing all internal calls (not just JIT intrinsics) which is not necessarily version resilient.
When making a constrained virtual method call on a boxed value type, CG2 devirtualizes the call incorrectly resulting in a direct call. This breaks code patterns like:
In the call to ToString(),
getCallInfo
is passed a boxedTestEnum
and is callingobject.ToString()
. The constrained type isTestEnum
so we end up making a direct call toobject.ToString()
. Fixed by removing the constrained type from devirtualization consideration which is how Crossgen treats this call.cc @dotnet/crossgen-contrib