Based on Michal's suggestion I have implemented an approximate port of the two above CoreCLR changes. These changes improve performance of GetHashCode on Enum types by avoiding boxing thanks to transforming the calls to the equivalent method calls on their underlying types. My proposed compensation for #9138 is in the signature builder - we just check whether the MemberRef token we received has the proper owning type and, when it doesn't, we encode it explicitly in the signature. I have created a simple unit test that actually doesn't do much but I verified by submitting it to R2RDump that I indeed see direct calls to byte.GetHashCode and int.GetHashCode in the produced machine code. Thanks Tomas
This commit is contained in:
Родитель
cf5dc501e8
Коммит
45c45fd04f
|
@ -396,10 +396,24 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
break;
|
||||
|
||||
case CorTokenType.mdtMemberRef:
|
||||
// TODO: module override for methodrefs with external module context
|
||||
flags |= (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_MemberRefToken;
|
||||
EmitUInt(flags);
|
||||
EmitMethodRefToken(methodToken);
|
||||
{
|
||||
// TODO: module override for methodrefs with external module context
|
||||
flags |= (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_MemberRefToken;
|
||||
|
||||
MemberReference memberRef = methodToken.MetadataReader.GetMemberReference((MemberReferenceHandle)methodToken.Handle);
|
||||
if (methodToken.Module.GetObject(memberRef.Parent) != (object)method.OwningType)
|
||||
{
|
||||
// We have a memberref token for a different type - encode owning type explicitly in the signature
|
||||
flags |= (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_OwnerType;
|
||||
}
|
||||
|
||||
EmitUInt(flags);
|
||||
if ((flags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_OwnerType) != 0)
|
||||
{
|
||||
EmitTypeSignature(method.OwningType, context);
|
||||
}
|
||||
EmitMethodRefToken(methodToken);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -2627,14 +2627,23 @@ namespace Internal.JitInterface
|
|||
// to call.
|
||||
|
||||
MethodDesc directMethod = constrainedType.GetClosestDefType().TryResolveConstraintMethodApprox(exactType, method, out forceUseRuntimeLookup);
|
||||
#if !READYTORUN
|
||||
if (directMethod == null && constrainedType.IsEnum)
|
||||
{
|
||||
#if READYTORUN
|
||||
if (method.Name == "GetHashCode")
|
||||
{
|
||||
directMethod = constrainedType.UnderlyingType.FindVirtualFunctionTargetMethodOnObjectType(method);
|
||||
Debug.Assert(directMethod != null);
|
||||
|
||||
constrainedType = constrainedType.UnderlyingType;
|
||||
method = directMethod;
|
||||
}
|
||||
#else
|
||||
// Constrained calls to methods on enum methods resolve to System.Enum's methods. System.Enum is a reference
|
||||
// type though, so we would fail to resolve and box. We have a special path for those to avoid boxing.
|
||||
directMethod = _compilation.TypeSystemContext.TryResolveConstrainedEnumMethod(constrainedType, method);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (directMethod != null)
|
||||
{
|
||||
|
|
|
@ -649,6 +649,50 @@ internal class Program
|
|||
return success;
|
||||
}
|
||||
|
||||
private enum ByteEnum : byte
|
||||
{
|
||||
Value0,
|
||||
Value1,
|
||||
Value2,
|
||||
Value3,
|
||||
}
|
||||
|
||||
private enum IntEnum : int
|
||||
{
|
||||
Value0,
|
||||
Value1,
|
||||
Value2,
|
||||
Value3,
|
||||
}
|
||||
|
||||
private static bool EnumHashValueTest()
|
||||
{
|
||||
Console.WriteLine("ByteEnum.Value1.GetHashCode: ", ByteEnum.Value1.GetHashCode());
|
||||
Console.WriteLine("IntEnum.Value3.GetHashCode: ", IntEnum.Value3.GetHashCode());
|
||||
|
||||
ByteEnum[] byteEnumValues = { ByteEnum.Value3, ByteEnum.Value1, ByteEnum.Value0, ByteEnum.Value2, };
|
||||
foreach (ByteEnum enumValue in byteEnumValues)
|
||||
{
|
||||
Console.WriteLine("{0}.GetHashCode: {1}", enumValue, enumValue.GetHashCode());
|
||||
if (enumValue.GetHashCode() != (int)enumValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
IntEnum[] intEnumValues = { IntEnum.Value2, IntEnum.Value0, IntEnum.Value1, IntEnum.Value3, };
|
||||
foreach (IntEnum enumValue in intEnumValues)
|
||||
{
|
||||
Console.WriteLine("{0}.GetHashCode: {1}", enumValue, enumValue.GetHashCode());
|
||||
if (enumValue.GetHashCode() != (int)enumValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
if (args.Length > 0)
|
||||
|
@ -694,6 +738,7 @@ internal class Program
|
|||
RunTest("ClassParamGenericLookupTest", ClassParamGenericLookupTest());
|
||||
RunTest("MethodParamGenericLookupTest", MethodParamGenericLookupTest());
|
||||
RunTest("VectorTest", VectorTest());
|
||||
RunTest("EnumHashValueTest", EnumHashValueTest());
|
||||
|
||||
Console.WriteLine($@"{_passedTests.Count} tests pass:");
|
||||
foreach (string testName in _passedTests)
|
||||
|
|
Загрузка…
Ссылка в новой задаче