Loose port of CoreCLR changes #7895 and #9138 to CPAOT (#6631)

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:
Tomáš Rylek 2018-12-04 01:04:53 +01:00 коммит произвёл GitHub
Родитель cf5dc501e8
Коммит 45c45fd04f
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 74 добавлений и 6 удалений

Просмотреть файл

@ -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)