-
Notifications
You must be signed in to change notification settings - Fork 5.3k
JIT: Always use CallInfo method handle for devirtualization #123434
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
|
Tagging subscribers to this area: @JulieLeeMSFT, @dotnet/jit-contrib |
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 fixes a devirtualization regression introduced in #122023 by changing how method handles are obtained during late devirtualization. Instead of extracting the method handle from the GenTree (which could fail for certain indirect virtual calls), the PR stores the method handle in LateDevirtualizationInfo during import and retrieves it from there during late devirtualization.
Changes:
- Added
methodHndfield toLateDevirtualizationInfostructure to store the method handle - Simplified
IsDevirtualizationCandidateto just check if a call is virtual or generic virtual, removing the method handle extraction logic - Updated late devirtualization to use the stored method handle instead of attempting to extract it from the tree
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/jit/inline.h | Added methodHnd field to LateDevirtualizationInfo struct to store the method handle for late devirtualization |
| src/coreclr/jit/importercalls.cpp | Populated the new methodHnd field from callInfo->hMethod when creating late devirtualization info |
| src/coreclr/jit/gentree.h | Removed pMethHandle out parameter from IsDevirtualizationCandidate method signature |
| src/coreclr/jit/gentree.cpp | Simplified IsDevirtualizationCandidate to just return `IsVirtual() |
| src/coreclr/jit/fginline.cpp | Changed to retrieve method handle from gtLateDevirtualizationInfo->methodHnd instead of from out parameter, and removed now-unnecessary assertion |
This comment was marked as outdated.
This comment was marked as outdated.
@hez2010 this is not a correct way, either remove it, or replace with |
|
@EgorBot -amd -intel using System.Runtime.CompilerServices;
using System.Text;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
BenchmarkSwitcher.FromAssembly(typeof(ContainsTrue).Assembly).Run(args);
public class ContainsTrue
{
private string[] _found;
private string[] _array;
private List<string> _list;
[Params(512)]
public int Size;
[GlobalSetup(Targets = new[] { nameof(Array) })]
public void SetupArray()
{
_found = ArrayOfUniqueValues(Size);
_array = _found.ToArray();
}
[Benchmark]
public bool Array()
{
bool result = default;
string[] collection = _array;
string[] found = _found;
for (int i = 0; i < found.Length; i++)
result ^= collection.Contains(found[i]);
return result;
}
[GlobalSetup(Targets = new[] { nameof(ICollection) })]
public void SetupList()
{
_found = ArrayOfUniqueValues(Size);
_list = new List<string>(_found);
}
[Benchmark]
public bool ICollection() => Contains(_list);
[MethodImpl(MethodImplOptions.NoInlining)]
private bool Contains(ICollection<string> collection)
{
bool result = default;
string[] found = _found;
for (int i = 0; i < found.Length; i++)
result ^= collection.Contains(found[i]);
return result;
}
private static string[] ArrayOfUniqueValues(int count)
{
string[] result = new string[count];
var random = new Random(42);
var uniqueValues = new HashSet<string>();
while (uniqueValues.Count != count)
{
string value = GenerateRandomString(random, 1, 50);
if (!uniqueValues.Contains(value))
uniqueValues.Add(value);
}
uniqueValues.CopyTo(result);
return result;
}
private static string GenerateRandomString(Random random, int minLength, int maxLength)
{
var length = random.Next(minLength, maxLength);
var builder = new StringBuilder(length);
for (int i = 0; i < length; i++)
{
var rangeSelector = random.Next(0, 3);
if (rangeSelector == 0)
builder.Append((char)random.Next('a', 'z'));
else if (rangeSelector == 1)
builder.Append((char)random.Next('A', 'Z'));
else
builder.Append((char)random.Next('0', '9'));
}
return builder.ToString();
}
} |
|
Can confirm this fixes the regression seen in #123391
|
|
Test failures seem unrelated. |
We have some virtual calls that are implemented as calli in the JIT despite they actually have a method handle. When importing those methods, we didn't actually use the This PR unifies what we use for devirtualization, now we always have the method handle as long as the method is a virtual method. |
|
Hmm, ok. |
jakobbotsch
left a comment
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.
Thanks.
We already have the method handle in CallInfo, so we don't need to rely on the GenTree to extract the method handle for devirtualization.
Previously in #122023 we changed the JIT to only devirtualize a call when we can get the method handle from the GenTree, which was unnecessarily conservative especially when we already have the method handle in CallInfo.
Fixes #123391