Skip to content

Conversation

@EgorBo
Copy link
Member

@EgorBo EgorBo commented Jul 23, 2021

Example (@stephentoub's repro)

using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;

public class Program
{
    public static void Main()
    {
        for (int i = 0; i < 100; i++)
        {
            Test();
            Thread.Sleep(16); // promote Test to tier1 (and collect pgo)
        }
    }


    [MethodImpl(MethodImplOptions.NoInlining)]
    static int Test() => GetIList().Count;


    [MethodImpl(MethodImplOptions.NoInlining)]
    // returns IList interface
    static IList<Program> GetIList() => new List<Program>();
}

Currently we give up on devirtualizing GetIList() call in Test method despite the fact we have everything we need to do so:

impDevirtualizeCall: Trying to devirtualize interface call:
    class for 'this' is System.Collections.Generic.IList`1[[Program, ConsoleApp1]] (attrib 00200400)
    base method is System.Collections.Generic.ICollection`1[__Canon]::get_Count
Considering guarded devirtualization at IL offset 5 (0x5)
Likely class for 00007FFB2F079158 (System.Collections.Generic.IList`1[[Program, ConsoleApp1]]) is 00007FFB2F079E00
 (System.Collections.Generic.List`1[[Program, ConsoleApp1]]) [likelihood:100 classes seen:1]
Can't figure out which method would be invoked, sorry

namely:

Likely class for IList`1[Program] is List`1[Program]

The problem is in VM's resolveVirtualMethodHelper that checks that List<Program>.CanCastToInterface(IList<__Canon>) is false and gives up - doesn't try to use the provided generic context.

Codegen for Test Before and After: https://www.diffchecker.com/GUi21Vt0

/cc @AndyAyersMS

@ghost
Copy link

ghost commented Jul 23, 2021

I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label.

@EgorBo EgorBo marked this pull request as draft July 23, 2021 22:12
@EgorBo EgorBo marked this pull request as ready for review July 24, 2021 08:25
@EgorBo
Copy link
Member Author

EgorBo commented Jul 24, 2021

jit-diff (pmi, separate runs + jit-analyze):

Total bytes of base: 61253869
Total bytes of diff: 61255413
Total bytes of delta: 1544 (0.00% of base)
Total relative delta: 14.71
    diff is a regression.
    relative diff is a regression.


Top file regressions (bytes):
        2107 : Newtonsoft.Json.dasm (0.22% of base)
         631 : System.Speech.dasm (0.14% of base)
         301 : System.Linq.Parallel.dasm (0.01% of base)
         272 : Microsoft.CodeAnalysis.dasm (0.01% of base)
         199 : Microsoft.CodeAnalysis.CSharp.dasm (0.00% of base)
          47 : System.Text.Json.dasm (0.00% of base)
          37 : System.ComponentModel.Composition.dasm (0.01% of base)
          18 : xunit.assert.dasm (0.01% of base)
          18 : xunit.core.dasm (0.02% of base)
          18 : xunit.execution.dotnet.dasm (0.01% of base)

Top file improvements (bytes):
       -1704 : Microsoft.CodeAnalysis.VisualBasic.dasm (-0.03% of base)
        -212 : Microsoft.Diagnostics.Tracing.TraceEvent.dasm (-0.01% of base)
        -116 : System.Private.CoreLib.dasm (-0.00% of base)
         -28 : System.Security.Cryptography.Pkcs.dasm (-0.01% of base)
         -23 : System.ComponentModel.Annotations.dasm (-0.05% of base)
         -10 : xunit.runner.reporters.netcoreapp10.dasm (-0.02% of base)
          -8 : System.Diagnostics.StackTrace.dasm (-0.18% of base)
          -3 : System.Linq.Expressions.dasm (-0.00% of base)

18 total files with Code Size differences (8 improved, 10 regressed), 255 unchanged.

Top method regressions (bytes):
        1298 (18.81% of base) : Newtonsoft.Json.dasm - DynamicProxyMetaObject`1:BuildCallMethodWithResult(String,DynamicMetaObjectBinder,IEnumerable`1,DynamicMetaObject,Fallback):DynamicMetaObject:this (8 methods)
         359 (10.06% of base) : Newtonsoft.Json.dasm - DynamicProxyMetaObject`1:CallMethodNoResult(String,DynamicMetaObjectBinder,ref,Fallback):DynamicMetaObject:this (8 methods)
         272 (47.39% of base) : Microsoft.CodeAnalysis.dasm - SuppressMessageAttributeState:DecodeLocalSuppressMessageAttributes(ISymbol,IEnumerable`1):ImmutableDictionary`2
         260 ( 4.73% of base) : Newtonsoft.Json.dasm - DynamicProxyMetaObject`1:CallMethodReturnLast(String,DynamicMetaObjectBinder,IEnumerable`1,Fallback):DynamicMetaObject:this (8 methods)
         252 (144.83% of base) : System.Linq.Parallel.dasm - Lookup`2:System.Collections.IEnumerable.GetEnumerator():IEnumerator:this (8 methods)
         237 ( 6.47% of base) : System.Speech.dasm - Backend:InitFromBinaryGrammar(StreamMarshaler):this
         136 (14.91% of base) : System.Speech.dasm - RecognizedPhrase:AppendPropertiesSML(XmlDocument,XmlElement,SemanticValue,NumberFormatInfo):this
         125 ( 4.36% of base) : Microsoft.CodeAnalysis.CSharp.dasm - SyntaxAndDeclarationManager:ReplaceSyntaxTree(SyntaxTree,SyntaxTree):SyntaxAndDeclarationManager:this
         121 ( 8.83% of base) : Microsoft.CodeAnalysis.CSharp.dasm - SyntaxAndDeclarationManager:AddSyntaxTrees(IEnumerable`1):SyntaxAndDeclarationManager:this
         110 (550.00% of base) : System.Speech.dasm - SemanticValue:System.Collections.IEnumerable.GetEnumerator():IEnumerator:this
         110 ( 6.91% of base) : Newtonsoft.Json.dasm - JsonValidatingReader:get_CurrentMemberSchemas():IList`1:this
         108 ( 7.81% of base) : Microsoft.CodeAnalysis.CSharp.dasm - SyntaxAndDeclarationManager:RemoveSyntaxTrees(HashSet`1):SyntaxAndDeclarationManager:this
          88 (31.10% of base) : System.Speech.dasm - RecognizedPhrase:AppendAttributes(XmlElement,SemanticValue):this
          70 ( 4.52% of base) : Newtonsoft.Json.dasm - ExpressionReflectionDelegateFactory:BuildMethodCall(MethodBase,Type,ParameterExpression,ParameterExpression):Expression:this
          66 (19.64% of base) : System.ComponentModel.Composition.dasm - PartManager:GetImportedContractNames():IEnumerable`1:this
          63 (15.11% of base) : System.ComponentModel.Composition.dasm - ExportsChangeEventArgs:get_ChangedContractNames():IEnumerable`1:this
          61 (11.69% of base) : System.ComponentModel.Composition.dasm - ReflectionPartCreationInfo:GetConstructor():ConstructorInfo:this
          60 (10.54% of base) : System.Speech.dasm - ObjectTokenCategory:FindMatchingTokens(String,String):IList`1:this
          49 (245.00% of base) : System.Linq.Parallel.dasm - EnumerableWrapperWeakToStrong:System.Collections.IEnumerable.GetEnumerator():IEnumerator:this
          40 ( 7.78% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - Binder:BindCaseBlocks(SyntaxList`1,BoundExpression,bool,byref,DiagnosticBag):ImmutableArray`1:this
          36 (180.00% of base) : System.Private.CoreLib.dasm - EventPayload:System.Collections.IEnumerable.GetEnumerator():IEnumerator:this
          35 ( 0.38% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - SourceMemberFieldSymbol:Create(SourceMemberContainerTypeSymbol,FieldDeclarationSyntax,Binder,MembersAndInitializersBuilder,byref,byref,DiagnosticBag)
          35 ( 6.64% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - SyntaxNodeExtensions:CreateSkippedTrivia(VisualBasicSyntaxNode,bool,bool,DiagnosticInfo):SyntaxList`1
          31 ( 1.52% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - Binder:BindFieldInitializer(ImmutableArray`1,VisualBasicSyntaxNode,ArrayBuilder`1,DiagnosticBag,bool):this
          30 (150.00% of base) : System.Text.Json.dasm - JsonObject:TryGetPropertyValue(String,byref):bool:this
          23 ( 5.82% of base) : Newtonsoft.Json.dasm - JsonSchemaBuilder:ProcessExtends(JToken):this
          22 (10.14% of base) : Newtonsoft.Json.dasm - ExpandoObjectConverter:ReadList(JsonReader):Object:this
          20 (37.74% of base) : System.Text.Json.dasm - JsonObject:GetItem(String):JsonNode:this
          18 ( 1.44% of base) : xunit.assert.dasm - ArgumentFormatter:FormatComplexValue(Object,int,Type):String
          18 ( 1.44% of base) : xunit.core.dasm - ArgumentFormatter:FormatComplexValue(Object,int,Type):String
          18 ( 1.44% of base) : xunit.execution.dotnet.dasm - ArgumentFormatter:FormatComplexValue(Object,int,Type):String
          18 ( 4.34% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - <Microsoft-Diagnostics-Tracing-AutomatedAnalysis-IAutomatedAnalysisTrace-get_Processes>d__8:MoveNext():bool:this
           5 ( 7.35% of base) : System.Text.Json.dasm - JsonNode:get_Item(String):JsonNode:this

Top method improvements (bytes):
       -1782 (-5.08% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - VisualBasicCommandLineParser:Parse(IEnumerable`1,String,String,String):VisualBasicCommandLineArguments:this
        -144 (-1.68% of base) : System.Private.CoreLib.dasm - TlsOverPerCoreLockedStacksArrayPool`1:Trim():bool:this (10 methods)
        -124 (-0.55% of base) : Microsoft.CodeAnalysis.CSharp.dasm - CSharpCommandLineParser:Parse(IEnumerable`1,String,String,String):CSharpCommandLineArguments:this
         -59 (-5.92% of base) : System.ComponentModel.Composition.dasm - CompositionServices:GetPartMetadataForType(Type,int):IDictionary`2
         -54 (-5.54% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - TraceProcesses:ToString():String:this (2 methods)
         -51 (-1.15% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - TraceLog:CopyRawEvents(TraceEventDispatcher,IStreamWriter):this
         -27 (-5.56% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - TraceThreads:ToString():String:this
         -27 (-5.56% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - TraceModuleFiles:ToString():String:this
         -23 (-3.06% of base) : System.ComponentModel.Annotations.dasm - UIHintImplementation:BuildControlParametersDictionary():IDictionary`2:this
         -20 (-7.25% of base) : System.Security.Cryptography.Pkcs.dasm - DecryptorPalWindows:CreateRecipientInfos(SafeCryptMsgHandle):RecipientInfoCollection
         -20 (-1.36% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - Binder:BindCollectionInitializerElement(ExpressionSyntax,BoundWithLValueExpressionPlaceholder,LookupResult,DiagnosticBag):BoundExpression:this
         -20 (-1.80% of base) : System.ComponentModel.Composition.dasm - ComposablePartExportProvider:Recompose(CompositionBatch,AtomicComposition):this
         -20 (-1.02% of base) : System.ComponentModel.Composition.dasm - DirectoryCatalog:Refresh():this
         -19 (-1.13% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - ActivityComputer:ResolveWellKnownSymbols():this
         -16 (-4.83% of base) : Microsoft.CodeAnalysis.CSharp.dasm - CSharpAttributeData:GetSecurityAttributeActionSyntaxLocation(AttributeSyntax,TypedConstant,byref):Location
         -16 (-4.91% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - <>c__DisplayClass32_0:<SetupCallbacks>b__1(TraceProcess):this
         -16 (-2.02% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - Binder:BindForLoopBodyAndNextControlVariables(ForOrForEachBlockSyntax,byref,byref,DiagnosticBag):this
         -15 (-3.39% of base) : Microsoft.CodeAnalysis.CSharp.dasm - DeclarationTable:GetNames(Declaration,Predicate`1):ISet`1
         -15 (-2.49% of base) : Newtonsoft.Json.dasm - JsonSchemaBuilder:ProcessProperties(JToken):IDictionary`2:this
         -14 (-1.40% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - Binder:ShouldBindWithoutArguments(byref,byref):bool:this
         -14 (-5.65% of base) : System.ComponentModel.Composition.dasm - <>c__DisplayClass15_0:<Clear>b__0():IEnumerable`1:this
         -13 (-2.81% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - TypeSymbolExtensions:IsCompatibleWithGenericIEnumerableOfType(TypeSymbol,TypeSymbol,byref):bool
         -10 (-3.77% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - TraceLog:GetEvent(int):TraceEvent:this
         -10 (-2.04% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - TraceEventStats:ToString():String:this
         -10 (-1.23% of base) : Newtonsoft.Json.dasm - DefaultContractResolver:CreateProperties(Type,int):IList`1:this
         -10 (-3.70% of base) : Newtonsoft.Json.dasm - ExpandoObjectConverter:ReadObject(JsonReader):Object:this
          -8 (-3.11% of base) : System.Text.Json.dasm - JsonSerializerOptionsUpdateHandler:ClearCache(ref)
          -8 (-0.39% of base) : System.Security.Cryptography.Pkcs.dasm - ManagedPkcsPal:Decode(ReadOnlySpan`1,byref,byref,byref,byref,byref):DecryptorPal:this
          -8 (-0.95% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - TraceLogEventSource:Process():bool:this
          -8 (-1.77% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - TraceEventStackSource:ForEach(Action`1):this
          -8 (-3.40% of base) : System.Diagnostics.StackTrace.dasm - StackTraceSymbols:System.IDisposable.Dispose():this
          -8 (-1.40% of base) : System.Private.CoreLib.dasm - TaskScheduler:GetTaskSchedulersForDebugger():ref
          -5 (-1.09% of base) : xunit.runner.reporters.netcoreapp10.dasm - VstsReporterMessageHandler:VstsAddTest(String,String,String,ITest):this
          -5 (-1.11% of base) : xunit.runner.reporters.netcoreapp10.dasm - VstsReporterMessageHandler:VstsUpdateTest(ITest,String,Nullable`1,String,String,String):this
          -5 (-1.12% of base) : System.ComponentModel.Composition.dasm - AttributedModelServices:AddExportedValue(CompositionBatch,String,__Canon):ComposablePart
          -5 (-1.21% of base) : System.ComponentModel.Composition.dasm - AttributedModelServices:AddExportedValue(CompositionBatch,String,ubyte):ComposablePart
          -5 (-1.21% of base) : System.ComponentModel.Composition.dasm - AttributedModelServices:AddExportedValue(CompositionBatch,String,short):ComposablePart
          -5 (-1.21% of base) : System.ComponentModel.Composition.dasm - AttributedModelServices:AddExportedValue(CompositionBatch,String,int):ComposablePart
          -5 (-1.17% of base) : System.ComponentModel.Composition.dasm - AttributedModelServices:AddExportedValue(CompositionBatch,String,double):ComposablePart
          -5 (-1.18% of base) : System.ComponentModel.Composition.dasm - AttributedModelServices:AddExportedValue(CompositionBatch,String,Vector`1):ComposablePart
          -5 (-1.21% of base) : System.ComponentModel.Composition.dasm - AttributedModelServices:AddExportedValue(CompositionBatch,String,long):ComposablePart
          -5 (-1.21% of base) : System.ComponentModel.Composition.dasm - AttributedModelServices:AddExportedValue(CompositionBatch,String,Nullable`1):ComposablePart
          -3 (-0.51% of base) : System.Linq.Expressions.dasm - ExpandoObject:System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<System.String,System.Object>>.CopyTo(ref,int):this

Top method regressions (percentages):
         110 (550.00% of base) : System.Speech.dasm - SemanticValue:System.Collections.IEnumerable.GetEnumerator():IEnumerator:this
          49 (245.00% of base) : System.Linq.Parallel.dasm - EnumerableWrapperWeakToStrong:System.Collections.IEnumerable.GetEnumerator():IEnumerator:this
          36 (180.00% of base) : System.Private.CoreLib.dasm - EventPayload:System.Collections.IEnumerable.GetEnumerator():IEnumerator:this
          30 (150.00% of base) : System.Text.Json.dasm - JsonObject:TryGetPropertyValue(String,byref):bool:this
         252 (144.83% of base) : System.Linq.Parallel.dasm - Lookup`2:System.Collections.IEnumerable.GetEnumerator():IEnumerator:this (8 methods)
         272 (47.39% of base) : Microsoft.CodeAnalysis.dasm - SuppressMessageAttributeState:DecodeLocalSuppressMessageAttributes(ISymbol,IEnumerable`1):ImmutableDictionary`2
          20 (37.74% of base) : System.Text.Json.dasm - JsonObject:GetItem(String):JsonNode:this
          88 (31.10% of base) : System.Speech.dasm - RecognizedPhrase:AppendAttributes(XmlElement,SemanticValue):this
          66 (19.64% of base) : System.ComponentModel.Composition.dasm - PartManager:GetImportedContractNames():IEnumerable`1:this
        1298 (18.81% of base) : Newtonsoft.Json.dasm - DynamicProxyMetaObject`1:BuildCallMethodWithResult(String,DynamicMetaObjectBinder,IEnumerable`1,DynamicMetaObject,Fallback):DynamicMetaObject:this (8 methods)
          63 (15.11% of base) : System.ComponentModel.Composition.dasm - ExportsChangeEventArgs:get_ChangedContractNames():IEnumerable`1:this
         136 (14.91% of base) : System.Speech.dasm - RecognizedPhrase:AppendPropertiesSML(XmlDocument,XmlElement,SemanticValue,NumberFormatInfo):this
          61 (11.69% of base) : System.ComponentModel.Composition.dasm - ReflectionPartCreationInfo:GetConstructor():ConstructorInfo:this
          60 (10.54% of base) : System.Speech.dasm - ObjectTokenCategory:FindMatchingTokens(String,String):IList`1:this
          22 (10.14% of base) : Newtonsoft.Json.dasm - ExpandoObjectConverter:ReadList(JsonReader):Object:this
         359 (10.06% of base) : Newtonsoft.Json.dasm - DynamicProxyMetaObject`1:CallMethodNoResult(String,DynamicMetaObjectBinder,ref,Fallback):DynamicMetaObject:this (8 methods)
         121 ( 8.83% of base) : Microsoft.CodeAnalysis.CSharp.dasm - SyntaxAndDeclarationManager:AddSyntaxTrees(IEnumerable`1):SyntaxAndDeclarationManager:this
         108 ( 7.81% of base) : Microsoft.CodeAnalysis.CSharp.dasm - SyntaxAndDeclarationManager:RemoveSyntaxTrees(HashSet`1):SyntaxAndDeclarationManager:this
          40 ( 7.78% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - Binder:BindCaseBlocks(SyntaxList`1,BoundExpression,bool,byref,DiagnosticBag):ImmutableArray`1:this
           5 ( 7.35% of base) : System.Text.Json.dasm - JsonNode:get_Item(String):JsonNode:this
         110 ( 6.91% of base) : Newtonsoft.Json.dasm - JsonValidatingReader:get_CurrentMemberSchemas():IList`1:this
          35 ( 6.64% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - SyntaxNodeExtensions:CreateSkippedTrivia(VisualBasicSyntaxNode,bool,bool,DiagnosticInfo):SyntaxList`1
         237 ( 6.47% of base) : System.Speech.dasm - Backend:InitFromBinaryGrammar(StreamMarshaler):this
          23 ( 5.82% of base) : Newtonsoft.Json.dasm - JsonSchemaBuilder:ProcessExtends(JToken):this
         260 ( 4.73% of base) : Newtonsoft.Json.dasm - DynamicProxyMetaObject`1:CallMethodReturnLast(String,DynamicMetaObjectBinder,IEnumerable`1,Fallback):DynamicMetaObject:this (8 methods)
          70 ( 4.52% of base) : Newtonsoft.Json.dasm - ExpressionReflectionDelegateFactory:BuildMethodCall(MethodBase,Type,ParameterExpression,ParameterExpression):Expression:this
         125 ( 4.36% of base) : Microsoft.CodeAnalysis.CSharp.dasm - SyntaxAndDeclarationManager:ReplaceSyntaxTree(SyntaxTree,SyntaxTree):SyntaxAndDeclarationManager:this
          18 ( 4.34% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - <Microsoft-Diagnostics-Tracing-AutomatedAnalysis-IAutomatedAnalysisTrace-get_Processes>d__8:MoveNext():bool:this
          31 ( 1.52% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - Binder:BindFieldInitializer(ImmutableArray`1,VisualBasicSyntaxNode,ArrayBuilder`1,DiagnosticBag,bool):this
          18 ( 1.44% of base) : xunit.assert.dasm - ArgumentFormatter:FormatComplexValue(Object,int,Type):String
          18 ( 1.44% of base) : xunit.core.dasm - ArgumentFormatter:FormatComplexValue(Object,int,Type):String
          18 ( 1.44% of base) : xunit.execution.dotnet.dasm - ArgumentFormatter:FormatComplexValue(Object,int,Type):String
          35 ( 0.38% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - SourceMemberFieldSymbol:Create(SourceMemberContainerTypeSymbol,FieldDeclarationSyntax,Binder,MembersAndInitializersBuilder,byref,byref,DiagnosticBag)

Top method improvements (percentages):
         -20 (-7.25% of base) : System.Security.Cryptography.Pkcs.dasm - DecryptorPalWindows:CreateRecipientInfos(SafeCryptMsgHandle):RecipientInfoCollection
         -59 (-5.92% of base) : System.ComponentModel.Composition.dasm - CompositionServices:GetPartMetadataForType(Type,int):IDictionary`2
         -14 (-5.65% of base) : System.ComponentModel.Composition.dasm - <>c__DisplayClass15_0:<Clear>b__0():IEnumerable`1:this
         -27 (-5.56% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - TraceThreads:ToString():String:this
         -27 (-5.56% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - TraceModuleFiles:ToString():String:this
         -54 (-5.54% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - TraceProcesses:ToString():String:this (2 methods)
       -1782 (-5.08% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - VisualBasicCommandLineParser:Parse(IEnumerable`1,String,String,String):VisualBasicCommandLineArguments:this
         -16 (-4.91% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - <>c__DisplayClass32_0:<SetupCallbacks>b__1(TraceProcess):this
         -16 (-4.83% of base) : Microsoft.CodeAnalysis.CSharp.dasm - CSharpAttributeData:GetSecurityAttributeActionSyntaxLocation(AttributeSyntax,TypedConstant,byref):Location
         -10 (-3.77% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - TraceLog:GetEvent(int):TraceEvent:this
         -10 (-3.70% of base) : Newtonsoft.Json.dasm - ExpandoObjectConverter:ReadObject(JsonReader):Object:this
          -8 (-3.40% of base) : System.Diagnostics.StackTrace.dasm - StackTraceSymbols:System.IDisposable.Dispose():this
         -15 (-3.39% of base) : Microsoft.CodeAnalysis.CSharp.dasm - DeclarationTable:GetNames(Declaration,Predicate`1):ISet`1
          -8 (-3.11% of base) : System.Text.Json.dasm - JsonSerializerOptionsUpdateHandler:ClearCache(ref)
         -23 (-3.06% of base) : System.ComponentModel.Annotations.dasm - UIHintImplementation:BuildControlParametersDictionary():IDictionary`2:this
         -13 (-2.81% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - TypeSymbolExtensions:IsCompatibleWithGenericIEnumerableOfType(TypeSymbol,TypeSymbol,byref):bool
         -15 (-2.49% of base) : Newtonsoft.Json.dasm - JsonSchemaBuilder:ProcessProperties(JToken):IDictionary`2:this
         -10 (-2.04% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - TraceEventStats:ToString():String:this
         -16 (-2.02% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - Binder:BindForLoopBodyAndNextControlVariables(ForOrForEachBlockSyntax,byref,byref,DiagnosticBag):this
         -20 (-1.80% of base) : System.ComponentModel.Composition.dasm - ComposablePartExportProvider:Recompose(CompositionBatch,AtomicComposition):this
          -8 (-1.77% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - TraceEventStackSource:ForEach(Action`1):this
        -144 (-1.68% of base) : System.Private.CoreLib.dasm - TlsOverPerCoreLockedStacksArrayPool`1:Trim():bool:this (10 methods)
          -8 (-1.40% of base) : System.Private.CoreLib.dasm - TaskScheduler:GetTaskSchedulersForDebugger():ref
         -14 (-1.40% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - Binder:ShouldBindWithoutArguments(byref,byref):bool:this
         -20 (-1.36% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - Binder:BindCollectionInitializerElement(ExpressionSyntax,BoundWithLValueExpressionPlaceholder,LookupResult,DiagnosticBag):BoundExpression:this
         -10 (-1.23% of base) : Newtonsoft.Json.dasm - DefaultContractResolver:CreateProperties(Type,int):IList`1:this
          -5 (-1.21% of base) : System.ComponentModel.Composition.dasm - AttributedModelServices:AddExportedValue(CompositionBatch,String,ubyte):ComposablePart
          -5 (-1.21% of base) : System.ComponentModel.Composition.dasm - AttributedModelServices:AddExportedValue(CompositionBatch,String,int):ComposablePart
          -5 (-1.21% of base) : System.ComponentModel.Composition.dasm - AttributedModelServices:AddExportedValue(CompositionBatch,String,short):ComposablePart
          -5 (-1.21% of base) : System.ComponentModel.Composition.dasm - AttributedModelServices:AddExportedValue(CompositionBatch,String,long):ComposablePart
          -5 (-1.21% of base) : System.ComponentModel.Composition.dasm - AttributedModelServices:AddExportedValue(CompositionBatch,String,Nullable`1):ComposablePart
          -5 (-1.18% of base) : System.ComponentModel.Composition.dasm - AttributedModelServices:AddExportedValue(CompositionBatch,String,Vector`1):ComposablePart
          -5 (-1.17% of base) : System.ComponentModel.Composition.dasm - AttributedModelServices:AddExportedValue(CompositionBatch,String,double):ComposablePart
         -51 (-1.15% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - TraceLog:CopyRawEvents(TraceEventDispatcher,IStreamWriter):this
         -19 (-1.13% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - ActivityComputer:ResolveWellKnownSymbols():this
          -5 (-1.12% of base) : System.ComponentModel.Composition.dasm - AttributedModelServices:AddExportedValue(CompositionBatch,String,__Canon):ComposablePart
          -5 (-1.11% of base) : xunit.runner.reporters.netcoreapp10.dasm - VstsReporterMessageHandler:VstsUpdateTest(ITest,String,Nullable`1,String,String,String):this
          -5 (-1.09% of base) : xunit.runner.reporters.netcoreapp10.dasm - VstsReporterMessageHandler:VstsAddTest(String,String,String,ITest):this
         -20 (-1.02% of base) : System.ComponentModel.Composition.dasm - DirectoryCatalog:Refresh():this
          -8 (-0.95% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - TraceLogEventSource:Process():bool:this
        -124 (-0.55% of base) : Microsoft.CodeAnalysis.CSharp.dasm - CSharpCommandLineParser:Parse(IEnumerable`1,String,String,String):CSharpCommandLineArguments:this
          -3 (-0.51% of base) : System.Linq.Expressions.dasm - ExpandoObject:System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<System.String,System.Object>>.CopyTo(ref,int):this
          -8 (-0.39% of base) : System.Security.Cryptography.Pkcs.dasm - ManagedPkcsPal:Decode(ReadOnlySpan`1,byref,byref,byref,byref,byref):DecryptorPal:this

76 total methods with Code Size differences (43 improved, 33 regressed), 275855 unchanged.

Diff examples:
https://www.diffchecker.com/ZkePKvPt
https://www.diffchecker.com/OGeSOsMd

But the impact should be way bigger in FullPGO mode

@EgorBo
Copy link
Member Author

EgorBo commented Jul 24, 2021

Another example:

using System;
using System.Collections.Generic;
using System.Threading;

public class Program
{
    public static void Main()
    {
        for (int i = 0; i < 100; i++)
        {
            Test(new List<string> {"1", "2"});
            Thread.Sleep(16);
        }
    }

    static void Test(IEnumerable<object> enumerable)
    {
        foreach (object item in enumerable)
            Console.WriteLine(item);
    }
}

Codegen diff for Test: https://www.diffchecker.com/wI0KD9qg

(GetEnumerator, get_Current are devirtualized, but not inlined - it looks like we don't update generic context for guarded call-sites, minimal repro https://gist.github.com/EgorBo/03fbcc79a6dc5fac8534016803676011 - GetMyStruct is devirtualized but not inlined)

@jkotas
Copy link
Member

jkotas commented Jul 25, 2021

Does crossgen2 need the same fix, or does it work fine already?

@EgorBo
Copy link
Member Author

EgorBo commented Jul 25, 2021

Does crossgen2 need the same fix, or does it work fine already?

@jkotas it seems it does already, here is the codegen for Test (r2rdump):

int Program.Test()


1e70c0: 48 83 ec 28             sub     rsp, 40
                                UWOP_ALLOC_SMALL 40                                0

1e70c4: ff 15 0e 0c 0e 00       call    qword ptr [0x2c7cd8]  // System.Collections.Generic.IList`1<Program> Program.GetIList() (METHOD_ENTRY_DEF_TOKEN)
1e70ca: 48 8b c8                mov     rcx, rax
1e70cd: 48 8b 05 dc 53 0e 00    mov     rax, qword ptr [0x2cc4b0] // System.Collections.Generic.List`1<Program> (TYPE_HANDLE)
1e70d4: 48 3b 01                cmp     rax, qword ptr [rcx]
1e70d7: 75 08                   jne     0x1e70e1
1e70d9: 8b 41 10                mov     eax, dword ptr [rcx + 16]
1e70dc: 48 83 c4 28             add     rsp, 40
1e70e0: c3                      ret
1e70e1: 4c 8d 1d c8 53 0d 00    lea     r11, [0x2bc4b0]       // int System.Collections.Generic.ICollection`1<Program>.get_Count() (VIRTUAL_ENTRY)
1e70e8: ff 15 c2 53 0d 00       call    qword ptr [0x2bc4b0]  // int System.Collections.Generic.ICollection`1<Program>.get_Count() (VIRTUAL_ENTRY)
1e70ee: eb ec                   jmp     0x1e70dc

^ it contains a GDV fast path for List<Program> while jit originally didn't: https://www.diffchecker.com/GUi21Vt0 (on the left)

@EgorBo
Copy link
Member Author

EgorBo commented Jul 25, 2021

From my understanding, this change (similar to mine) in crossgen2 is responsible for that: https://github.com/dotnet/runtime/blob/main/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs#L1190-L1198

@EgorBo
Copy link
Member Author

EgorBo commented Jul 25, 2021

Changed the code to align with crossgen2, jit-diff is the same.

@AndyAyersMS
Copy link
Member

I worry that this check is too simplistic ... if the base class for the method being invoke is a shared interface class, it seems like we need to use the owning context to determine the exact base class. And I don't know if we can make that determination properly with just class handles; it might require looking at signatures.

What this change does is use the owning context as the exact base class. Seems like that could be inaccurate if the type dependence of the interface class is more complex than just a single directly used type parameter.

cc @davidwrighton

@EgorBo
Copy link
Member Author

EgorBo commented Jul 27, 2021

Initially the change was like this c50eb8d so it first tried the original base class, then it tried the generic context (also, it ignored cases where current object is shared instantiation).
The change relies on CanCastToInterface to filter out cases we can't resolve.

Copy link
Member

@davidwrighton davidwrighton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The analysis that @EgorBo did around the behavior of CanCastToInterface looks correct to me. That should weed out the cases this can't reliably handle at this time. Well, the CanCastToInterface is needed as is the weeding out with canonically equivalent interfaces, but since both of those checks are in place, this should work. With the signature checks that @AndyAyersMS suggests, there are some cases which could be permitted even if the CanCastToInterface check will fail, but I really don't think we should invest in trying to make that work at this time.

@AndyAyersMS
Copy link
Member

David, thanks for double-checking.

@EgorBo
Copy link
Member Author

EgorBo commented Aug 19, 2021

Merging to 7.0

@EgorBo EgorBo merged commit 59b6c36 into dotnet:main Aug 19, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Sep 18, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants