diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs index 9a8b6c203479d1..45093206bcf66a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs @@ -378,6 +378,15 @@ private void ImportCall(ILOpcode opcode, int token) } return; } + + if (IsExpandedUnsafeIntrinsic(method)) + { + // Size optimization. If we're in shared code, call to an Unsafe intrinsic would bring generic + // lookup for the generic dictionary. We would not be able to get rid of it later. + // To add insult to injury, the dictionary would be empty most of the time. + // RyuJIT recognizes these intrinsically and it's not going to ask for the generic dictionary. + return; + } } TypeDesc exactType = method.OwningType; @@ -1399,6 +1408,36 @@ private static bool IsEETypePtrOf(MethodDesc method) return false; } + private static bool IsExpandedUnsafeIntrinsic(MethodDesc method) + { + if (method.IsIntrinsic && method.OwningType is MetadataType mdType + && mdType.Name == "Unsafe" && mdType.Namespace == "System.Runtime.CompilerServices") + { + // Unsafe intrinsics RyuJIT expands intrinsically + switch (method.Name) + { + case "Add": + case "AddByteOffset": + case "AreSame": + case "As": + case "AsPointer": + case "AsRef": + case "ByteOffset": + case "IsAddressGreaterThan": + case "IsAddressLessThan": + case "IsNullRef": + case "NullRef": + case "SizeOf": + case "SkipInit": + case "Subtract": + case "SubtractByteOffset": + return true; + } + } + + return false; + } + private DefType GetWellKnownType(WellKnownType wellKnownType) { return _compilation.TypeSystemContext.GetWellKnownType(wellKnownType);