Pgo phase3 (#47558)
authorDavid Wrighton <davidwr@microsoft.com>
Tue, 2 Feb 2021 00:21:48 +0000 (16:21 -0800)
committerGitHub <noreply@github.com>
Tue, 2 Feb 2021 00:21:48 +0000 (16:21 -0800)
- Fix class type probes (the increment of the count wasn't correct)
- Use the 64bit integer encoders for all the pgo data
- New section for R2R PE Files containing instrumentation data
- R2RDump functionality to display all the data embedded in the file
- Enable BBOPT for optimized builds in a more unconditional fashion

Future PGO work will include
- Move Pgo type handle histogram processing into JIT (which will make type guessing work in crossgen2 as well as in the runtime)
- Triggers for controlling Pgo data extraction
- Size control for pgo instrumentation data

With this checkin, the feature is functional from a crossgen2.exe point of view, but its not polished, and it cannot be used from the Crossgen2 sdk integration yet (as the sdk does not have the ability to pass an extra pair of arguments to the compiler.

32 files changed:
src/coreclr/inc/corjit.h
src/coreclr/inc/pgo_formatprocessing.h
src/coreclr/inc/readytorun.h
src/coreclr/tools/Common/Internal/NativeFormat/NativeFormatWriter.cs
src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs
src/coreclr/tools/Common/Pgo/TypeSystemEntityOrUnknown.cs
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstrumentationDataTableNode.cs [new file with mode: 0644]
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ProfileData.cs
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs
src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj
src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj
src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PgoInfo.cs [new file with mode: 0644]
src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PgoInfoKey.cs [new file with mode: 0644]
src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunMethod.cs
src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs
src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs
src/coreclr/tools/aot/crossgen2/Program.cs
src/coreclr/tools/aot/crossgen2/Properties/Resources.resx
src/coreclr/tools/dotnet-pgo/dotnet-pgo.csproj
src/coreclr/tools/r2rdump/CommandLineOptions.cs
src/coreclr/tools/r2rdump/R2RDump.cs
src/coreclr/tools/r2rdump/TextDumper.cs
src/coreclr/vm/jithelpers.cpp
src/coreclr/vm/jitinterface.cpp
src/coreclr/vm/jitinterface.h
src/coreclr/vm/nativeimage.cpp
src/coreclr/vm/pgo.cpp
src/coreclr/vm/pgo.h
src/coreclr/vm/readytoruninfo.cpp
src/coreclr/vm/readytoruninfo.h

index 4516181..72df3af 100644 (file)
@@ -272,7 +272,6 @@ public:
             OFFSET_MASK    = 0x3FFFFFFF
         };
 
-        UINT32 ILOffset;
         UINT32 Count;
         CORINFO_CLASS_HANDLE ClassTable[SIZE];
     };
index 621bd90..9ef06e5 100644 (file)
@@ -381,40 +381,6 @@ inline bool ReadInstrumentationSchemaWithLayoutIntoSArray(const uint8_t *pByte,
     return ReadInstrumentationSchemaWithLayout(pByte, cbDataMax, initialOffset, lambda);
 }
 
-
-template<class ByteWriter>
-bool WriteCompressedIntToBytes(int32_t value, ByteWriter& byteWriter)
-{
-    uint8_t isSigned = 0;
-
-    // This function is modeled on CorSigCompressSignedInt, but differs in that
-    // it handles arbitrary int32 values, not just a subset
-    if (value < 0)
-        isSigned = 1;
-
-    if ((value & SIGN_MASK_ONEBYTE) == 0 || (value & SIGN_MASK_ONEBYTE) == SIGN_MASK_ONEBYTE)
-    {
-        return byteWriter((uint8_t)((value & ~SIGN_MASK_ONEBYTE) << 1 | isSigned));
-    }
-    else if ((value & SIGN_MASK_TWOBYTE) == 0 || (value & SIGN_MASK_TWOBYTE) == SIGN_MASK_TWOBYTE)
-    {
-        int32_t iData = (int32_t)((value & ~SIGN_MASK_TWOBYTE) << 1 | isSigned);
-        _ASSERTE(iData <= 0x3fff);
-        byteWriter(uint8_t((iData >> 8) | 0x80));
-        return byteWriter(uint8_t(iData & 0xff));
-    }
-    else
-    {
-        // Unlike CorSigCompressSignedInt, this just writes a header byte
-        // then a full 4 bytes, ignoring the whole signed bit detail
-        byteWriter(0xC0);
-        byteWriter(uint8_t((value >> 24) & 0xff));
-        byteWriter(uint8_t((value >> 16) & 0xff));
-        byteWriter(uint8_t((value >> 8) & 0xff));
-        return byteWriter(uint8_t((value >> 0) & 0xff));
-    }
-}
-
 #define SIGN_MASK_ONEBYTE_64BIT  0xffffffffffffffc0LL
 #define SIGN_MASK_TWOBYTE_64BIT  0xffffffffffffe000LL
 #define SIGN_MASK_FOURBYTE_64BIT 0xffffffff80000000LL
@@ -469,10 +435,10 @@ bool WriteCompressedIntToBytes(int64_t value, ByteWriter& byteWriter)
 template<class ByteWriter>
 bool WriteIndividualSchemaToBytes(ICorJitInfo::PgoInstrumentationSchema prevSchema, ICorJitInfo::PgoInstrumentationSchema curSchema, ByteWriter& byteWriter)
 {
-    int32_t ilOffsetDiff = curSchema.ILOffset - prevSchema.ILOffset;
-    int32_t OtherDiff = curSchema.Other - prevSchema.Other;
-    int32_t CountDiff = curSchema.Count - prevSchema.Count;
-    int32_t TypeDiff = (int32_t)curSchema.InstrumentationKind - (int32_t)prevSchema.InstrumentationKind;
+    int64_t ilOffsetDiff = (int64_t)curSchema.ILOffset - (int64_t)prevSchema.ILOffset;
+    int64_t OtherDiff = (int64_t)curSchema.Other - (int64_t)prevSchema.Other;
+    int64_t CountDiff = (int64_t)curSchema.Count - (int64_t)prevSchema.Count;
+    int64_t TypeDiff = (int64_t)curSchema.InstrumentationKind - (int64_t)prevSchema.InstrumentationKind;
 
     InstrumentationDataProcessingState modifyMask = (InstrumentationDataProcessingState)0;
 
index e2746b9..fb2b641 100644 (file)
@@ -16,7 +16,7 @@
 
 // Keep these in sync with src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs
 #define READYTORUN_MAJOR_VERSION 0x0005
-#define READYTORUN_MINOR_VERSION 0x0001
+#define READYTORUN_MINOR_VERSION 0x0002
 
 #define MINIMUM_READYTORUN_MAJOR_VERSION 0x003
 
@@ -79,6 +79,7 @@ enum class ReadyToRunSectionType : uint32_t
     InliningInfo2               = 114, // Added in V4.1
     ComponentAssemblies         = 115, // Added in V4.1
     OwnerCompositeExecutable    = 116, // Added in V4.1
+    PgoInstrumentationData      = 117, // Added in 5.2
 
     // If you add a new section consider whether it is a breaking or non-breaking change.
     // Usually it is non-breaking, but if it is preferable to have older runtimes fail
index ca83b03..8abb41a 100644 (file)
@@ -1640,6 +1640,61 @@ namespace Internal.NativeFormat
 #else
     internal
 #endif
+    class PgoInstrumentedDataVertex : Vertex
+    {
+        private uint _version;
+
+        private BlobVertex _instrumentationData;
+
+        public PgoInstrumentedDataVertex(uint version, BlobVertex instrumentationData)
+        {
+            _version = version;
+            _instrumentationData = instrumentationData;
+        }
+
+        internal override void Save(NativeWriter writer)
+        {
+            int existingOffset = _instrumentationData._offset;
+            if (existingOffset != -1)
+            {
+                writer.WriteUnsigned((_version << 2) | 3);
+                writer.WriteUnsigned((uint)(writer.GetCurrentOffset() - existingOffset));
+            }
+            else
+            {
+                writer.WriteUnsigned((_version << 2) | 1);
+                _instrumentationData.Save(writer);
+            }
+        }
+    }
+
+#if NATIVEFORMAT_PUBLICWRITER
+    public
+#else
+    internal
+#endif
+    class PgoInstrumentedDataWithSignatureBlobVertex : PgoInstrumentedDataVertex
+    {
+        private BlobVertex _signatureBlob;
+
+        public PgoInstrumentedDataWithSignatureBlobVertex(BlobVertex signaureBlob, uint version, BlobVertex instrumentationData)
+            : base(version, instrumentationData)
+        {
+            _signatureBlob = signaureBlob;
+        }
+
+        internal override void Save(NativeWriter writer)
+        {
+            _signatureBlob.Save(writer);
+            base.Save(writer);
+        }
+    }
+
+#if NATIVEFORMAT_PUBLICWRITER
+    public
+#else
+    internal
+#endif
     class DebugInfoVertex : Vertex
     {
         private BlobVertex _debugInfo;
index cdd5bac..6f30285 100644 (file)
@@ -15,7 +15,7 @@ namespace Internal.Runtime
         public const uint Signature = 0x00525452; // 'RTR'
 
         public const ushort CurrentMajorVersion = 5;
-        public const ushort CurrentMinorVersion = 1;
+        public const ushort CurrentMinorVersion = 2;
     }
 
 #pragma warning disable 0169
@@ -65,6 +65,7 @@ namespace Internal.Runtime
         InliningInfo2 = 114, // Added in 4.1
         ComponentAssemblies = 115, // Added in 4.1
         OwnerCompositeExecutable = 116, // Added in 4.1
+        PgoInstrumentationData = 117, // Added in 5.2
 
         //
         // CoreRT ReadyToRun sections
index c839d4a..dc5bd8c 100644 (file)
@@ -1,11 +1,12 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+using System;
 using Internal.TypeSystem;
 
 namespace Internal.Pgo
 {
-    public struct TypeSystemEntityOrUnknown
+    public struct TypeSystemEntityOrUnknown : IEquatable<TypeSystemEntityOrUnknown>
     {
         public TypeSystemEntityOrUnknown(int unknownIndex)
         {
@@ -23,5 +24,21 @@ namespace Internal.Pgo
         public FieldDesc AsField => _data as FieldDesc;
         public int AsUnknown => (!(_data is int)) || _data == null ? 0 : (int)_data;
         public bool IsNull => _data == null;
+
+        public bool Equals(TypeSystemEntityOrUnknown other)
+        {
+            if ((_data is int) && (other._data is int))
+            {
+                return other.AsUnknown == AsUnknown;
+            }
+            else
+            {
+                return object.ReferenceEquals(_data, other._data);
+            }
+        }
+
+        public override int GetHashCode() => _data.GetHashCode();
+
+        public override bool Equals(object obj) => obj is TypeSystemEntityOrUnknown other && other.Equals(this);
     }
 }
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstrumentationDataTableNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstrumentationDataTableNode.cs
new file mode 100644 (file)
index 0000000..78d1cf3
--- /dev/null
@@ -0,0 +1,211 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Reflection.Metadata.Ecma335;
+
+using Internal.JitInterface;
+using Internal.NativeFormat;
+using Internal.Pgo;
+using Internal.Runtime;
+using Internal.Text;
+using Internal.TypeSystem;
+using Internal.TypeSystem.Ecma;
+
+namespace ILCompiler.DependencyAnalysis.ReadyToRun
+{
+    public class InstrumentationDataTableNode : HeaderTableNode
+    {
+        private readonly NodeFactory _factory;
+        private ReadyToRunSymbolNodeFactory _symbolNodeFactory;
+        private readonly MethodDesc[] _instrumentationDataMethods;
+        private readonly ProfileDataManager _profileDataManager;
+
+        public InstrumentationDataTableNode(NodeFactory factory, MethodDesc[] instrumentationDataMethods, ProfileDataManager profileDataManager)
+            : base(factory.Target)
+        {
+            _factory = factory;
+            _instrumentationDataMethods = instrumentationDataMethods;
+            _profileDataManager = profileDataManager;
+        }
+
+        public void Initialize(ReadyToRunSymbolNodeFactory symbolNodeFactory)
+        {
+            _symbolNodeFactory = symbolNodeFactory;
+        }
+
+        class PgoValueEmitter : IPgoEncodedValueEmitter<TypeSystemEntityOrUnknown>
+        {
+            public PgoValueEmitter(CompilationModuleGroup compilationGroup, ReadyToRunSymbolNodeFactory factory, bool actuallyCaptureOutput)
+            {
+                _compilationGroup = compilationGroup;
+                _symbolFactory = factory;
+                _actuallyCaptureOutput = actuallyCaptureOutput;
+            }
+
+            public void Clear()
+            {
+                _longs.Clear();
+                _imports.Clear();
+                _typeConversions.Clear();
+                _unknownTypesFound = 0;
+            }
+
+            public IReadOnlyList<Import> ReferencedImports => _imports;
+            List<long> _longs = new List<long>();
+            List<Import> _imports = new List<Import>();
+            Dictionary<TypeSystemEntityOrUnknown, int> _typeConversions = new Dictionary<TypeSystemEntityOrUnknown, int>();
+            int _unknownTypesFound = 0;
+            CompilationModuleGroup _compilationGroup;
+            ReadyToRunSymbolNodeFactory _symbolFactory;
+            bool _actuallyCaptureOutput;
+
+            public byte[] ToByteArray()
+            {
+                return PgoProcessor.PgoEncodedCompressedLongGenerator(_longs).ToArray();
+            }
+
+            public bool EmitDone()
+            {
+                // This writer wants the done emitted as a schema terminator
+                return false;
+            }
+            public void EmitLong(long value, long previousValue)
+            {
+                if (_actuallyCaptureOutput)
+                    _longs.Add(value - previousValue);
+            }
+            public void EmitType(TypeSystemEntityOrUnknown type, TypeSystemEntityOrUnknown previousValue)
+            {
+                EmitLong(TypeToInt(type), TypeToInt(previousValue));
+            }
+
+            private int TypeToInt(TypeSystemEntityOrUnknown type)
+            {
+                if (type.IsNull || (type.AsType == null && type.AsUnknown == 0))
+                    return 0;
+
+                if (_typeConversions.TryGetValue(type, out int computedInt))
+                {
+                    return computedInt;
+                }
+                if (type.AsType != null && _compilationGroup.VersionsWithType(type.AsType))
+                {
+                    Import typeHandleImport = (Import)_symbolFactory.CreateReadyToRunHelper(ReadyToRunHelperId.TypeHandle, type.AsType);
+                    _imports.Add(typeHandleImport);
+
+                    if (_actuallyCaptureOutput)
+                    {
+                        if (typeHandleImport.Table.IndexFromBeginningOfArray >= 0xF)
+                        {
+                            // The current implementation of this table only allows for 15 different
+                            // import tables to be used. This is probably enough for long term
+                            // but this code will throw if we use more import tables and attempt
+                            // to encode pgo data
+                            throw new Exception("Unexpected high index for table import");
+                        }
+
+                        computedInt = (typeHandleImport.IndexFromBeginningOfArray << 4) | typeHandleImport.Table.IndexFromBeginningOfArray;
+                    }
+                    else
+                    {
+                        computedInt = _imports.Count << 1;
+                    }
+                }
+                else
+                {
+                    computedInt = ((++_unknownTypesFound) << 4) | 0xF;
+                }
+                _typeConversions.Add(type, computedInt);
+                return computedInt;
+            }
+        }
+
+        public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
+        {
+            sb.Append(nameMangler.CompilationUnitPrefix);
+            sb.Append("__ReadyToRunInstrumentationDataTable");
+        }
+
+        protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory)
+        {
+            PgoValueEmitter pgoEmitter = new PgoValueEmitter(_factory.CompilationModuleGroup, _symbolNodeFactory, false);
+            foreach (MethodDesc method in _instrumentationDataMethods)
+            {
+                PgoProcessor.EncodePgoData(_profileDataManager[method].SchemaData, pgoEmitter, false);
+            }
+            DependencyListEntry[] symbols = new DependencyListEntry[pgoEmitter.ReferencedImports.Count];
+            for (int i = 0; i < symbols.Length; i++)
+            {
+                symbols[i] = new DependencyListEntry(pgoEmitter.ReferencedImports[i], "Pgo Instrumentation Data");
+            }
+
+            return new DependencyList(symbols);
+        }
+
+
+        public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
+        {
+            if (relocsOnly)
+            {
+                return new ObjectData(Array.Empty<byte>(), Array.Empty<Relocation>(), 1, Array.Empty<ISymbolDefinitionNode>());
+            }
+
+            PgoValueEmitter pgoEmitter = new PgoValueEmitter(_factory.CompilationModuleGroup, _symbolNodeFactory, true);
+            NativeWriter hashtableWriter = new NativeWriter();
+
+            Section hashtableSection = hashtableWriter.NewSection();
+            VertexHashtable vertexHashtable = new VertexHashtable();
+            hashtableSection.Place(vertexHashtable);
+
+            Dictionary<byte[], BlobVertex> uniqueInstrumentationData = new Dictionary<byte[], BlobVertex>(ByteArrayComparer.Instance);
+
+            foreach (MethodDesc method in _instrumentationDataMethods)
+            {
+                pgoEmitter.Clear();
+                PgoProcessor.EncodePgoData(_profileDataManager[method].SchemaData, pgoEmitter, false);
+
+                // In composite R2R format, always enforce owning type to let us share generic instantiations among modules
+                EcmaMethod typicalMethod = (EcmaMethod)method.GetTypicalMethodDefinition();
+                ModuleToken moduleToken = new ModuleToken(typicalMethod.Module, typicalMethod.Handle);
+
+                ArraySignatureBuilder signatureBuilder = new ArraySignatureBuilder();
+                signatureBuilder.EmitMethodSignature(
+                    new MethodWithToken(method, moduleToken, constrainedType: null, unboxing: false, context: null),
+                    enforceDefEncoding: true,
+                    enforceOwningType: _factory.CompilationModuleGroup.EnforceOwningType(moduleToken.Module),
+                    factory.SignatureContext,
+                    isInstantiatingStub: false);
+                byte[] signature = signatureBuilder.ToArray();
+                BlobVertex signatureBlob = new BlobVertex(signature);
+
+                byte[] encodedInstrumentationData = pgoEmitter.ToByteArray();
+                BlobVertex instrumentationDataBlob = null;
+                if (!uniqueInstrumentationData.TryGetValue(encodedInstrumentationData, out instrumentationDataBlob))
+                {
+                    instrumentationDataBlob = new BlobVertex(encodedInstrumentationData);
+                    hashtableSection.Place(instrumentationDataBlob);
+                    uniqueInstrumentationData.Add(encodedInstrumentationData, instrumentationDataBlob);
+                }
+
+                PgoInstrumentedDataWithSignatureBlobVertex pgoDataVertex = new PgoInstrumentedDataWithSignatureBlobVertex(signatureBlob, 0, instrumentationDataBlob);
+                hashtableSection.Place(pgoDataVertex);
+                vertexHashtable.Append(unchecked((uint)ReadyToRunHashCode.MethodHashCode(method)), pgoDataVertex);
+            }
+
+            MemoryStream hashtableContent = new MemoryStream();
+            hashtableWriter.Save(hashtableContent);
+            return new ObjectData(
+                data: hashtableContent.ToArray(),
+                relocs: null,
+                alignment: 8,
+                definedSymbols: new ISymbolDefinitionNode[] { this });
+        }
+
+        public override int ClassCode => 1887299452;
+    }
+}
index 48a9834..209a5a1 100644 (file)
@@ -55,6 +55,8 @@ namespace ILCompiler.DependencyAnalysis
 
         public CompilationModuleGroup CompilationModuleGroup { get; }
 
+        public ProfileDataManager ProfileDataManager { get; }
+
         public NameMangler NameMangler { get; }
 
         public MetadataManager MetadataManager { get; }
@@ -146,6 +148,7 @@ namespace ILCompiler.DependencyAnalysis
         public NodeFactory(
             CompilerTypeSystemContext context,
             CompilationModuleGroup compilationModuleGroup,
+            ProfileDataManager profileDataManager,
             NameMangler nameMangler,
             CopiedCorHeaderNode corHeaderNode,
             DebugDirectoryNode debugDirectoryNode,
@@ -154,6 +157,7 @@ namespace ILCompiler.DependencyAnalysis
         {
             TypeSystemContext = context;
             CompilationModuleGroup = compilationModuleGroup;
+            ProfileDataManager = profileDataManager;
             Target = context.Target;
             NameMangler = nameMangler;
             MetadataManager = new ReadyToRunTableManager(context);
@@ -318,6 +322,8 @@ namespace ILCompiler.DependencyAnalysis
 
         public ImportSectionsTableNode ImportSectionsTable;
 
+        public InstrumentationDataTableNode InstrumentationDataTable;
+
         public Import ModuleImport;
 
         public ISymbolNode PersonalityRoutine;
@@ -639,6 +645,29 @@ namespace ILCompiler.DependencyAnalysis
                 graph.AddRoot(FilterFuncletPersonalityRoutine, "Filter funclet personality routine is faster to root early rather than referencing it from each unwind info");
             }
 
+            if ((ProfileDataManager != null) && (ProfileDataManager.EmbedPgoDataInR2RImage))
+            {
+                // Profile instrumentation data attaches here
+                HashSet<MethodDesc> methodsToInsertInstrumentationDataFor = new HashSet<MethodDesc>();
+                foreach (EcmaModule inputModule in CompilationModuleGroup.CompilationModuleSet)
+                {
+                    foreach (MethodDesc method in ProfileDataManager.GetMethodsForModuleDesc(inputModule))
+                    {
+                        if (ProfileDataManager[method].SchemaData != null)
+                        {
+                            methodsToInsertInstrumentationDataFor.Add(method);
+                        }
+                    }
+                }
+                if (methodsToInsertInstrumentationDataFor.Count != 0)
+                {
+                    MethodDesc[] methodsToInsert = methodsToInsertInstrumentationDataFor.ToArray();
+                    methodsToInsert.MergeSort(new TypeSystemComparer().Compare);
+                    InstrumentationDataTable = new InstrumentationDataTableNode(this, methodsToInsert, ProfileDataManager);
+                    Header.Add(Internal.Runtime.ReadyToRunSectionType.PgoInstrumentationData, InstrumentationDataTable, InstrumentationDataTable);
+                }
+            }
+
             MethodImports = new ImportSectionNode(
                 "MethodImports",
                 CorCompileImportType.CORCOMPILE_IMPORT_TYPE_STUB_DISPATCH,
index 77cf879..406f86b 100644 (file)
@@ -110,8 +110,10 @@ namespace ILCompiler
                                   IReadOnlyList<string> mibcFiles,
                                   CallChainProfile callChainProfile,
                                   CompilerTypeSystemContext context,
-                                  ReadyToRunCompilationModuleGroupBase compilationGroup)
+                                  ReadyToRunCompilationModuleGroupBase compilationGroup,
+                                  bool embedPgoDataInR2RImage)
         {
+            EmbedPgoDataInR2RImage = embedPgoDataInR2RImage;
             _ibcParser = new IBCProfileParser(logger, possibleReferenceModules);
             _compilationGroup = compilationGroup;
             _callChainProfile = callChainProfile;
@@ -269,6 +271,7 @@ namespace ILCompiler
             }
         }
 
+        public bool EmbedPgoDataInR2RImage { get; }
         public CallChainProfile CallChainProfile => _callChainProfile;
     }
 }
index 7f42ede..266e4cc 100644 (file)
@@ -298,6 +298,8 @@ namespace ILCompiler
             _generateProfileFile = generateProfileFile;
             _customPESectionAlignment = customPESectionAlignment;
             SymbolNodeFactory = new ReadyToRunSymbolNodeFactory(nodeFactory, verifyTypeAndFieldLayout);
+            if (nodeFactory.InstrumentationDataTable != null)
+                nodeFactory.InstrumentationDataTable.Initialize(SymbolNodeFactory);
             _corInfoImpls = new ConditionalWeakTable<Thread, CorInfoImpl>();
             _inputFiles = inputFiles;
             _compositeRootPath = compositeRootPath;
@@ -385,6 +387,7 @@ namespace ILCompiler
             NodeFactory componentFactory = new NodeFactory(
                 _nodeFactory.TypeSystemContext,
                 _nodeFactory.CompilationModuleGroup,
+                null,
                 _nodeFactory.NameMangler,
                 copiedCorHeader,
                 debugDirectory,
index c01ef6e..a6f6194 100644 (file)
@@ -233,6 +233,7 @@ namespace ILCompiler
             NodeFactory factory = new NodeFactory(
                 _context,
                 _compilationGroup,
+                _profileData,
                 _nameMangler,
                 corHeaderNode,
                 debugDirectoryNode,
index 2cbb680..8afb7c4 100644 (file)
     <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\ImportSectionsTableNode.cs" />
     <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\InliningInfoNode.cs" />
     <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\InstanceEntryPointTableNode.cs" />
+    <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\InstrumentationDataTableNode.cs" />
     <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\OwnerCompositeExecutableNode.cs" />
     <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\PrecodeMethodImport.cs" />
     <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\DelayLoadMethodImport.cs" />
index 94d1104..6259c06 100644 (file)
@@ -27,6 +27,7 @@
     <Compile Include="..\..\Common\Internal\Runtime\ModuleHeaders.cs" Link="Common\ModuleHeaders.cs" />
     <Compile Include="..\..\Common\Internal\Runtime\ReadyToRunConstants.cs" Link="Common\ReadyToRunConstants.cs" />
     <Compile Include="..\..\Common\Internal\Runtime\ReadyToRunInstructionSet.cs" Link="Common\ReadyToRunInstructionSet.cs" />
+    <Compile Include="..\..\Common\Pgo\PgoFormat.cs" Link="Common\PgoFormat.cs" />
     <Compile Include="..\..\Common\TypeSystem\Common\TargetArchitecture.cs" Link="Common\TargetArchitecture.cs" />
   </ItemGroup>
 </Project>
diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PgoInfo.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PgoInfo.cs
new file mode 100644 (file)
index 0000000..0b132c3
--- /dev/null
@@ -0,0 +1,113 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Linq;
+using System.Text;
+using Internal.Pgo;
+
+namespace ILCompiler.Reflection.ReadyToRun
+{
+    public class PgoInfo
+    {
+        public PgoInfoKey Key { get; }
+        public int Offset { get; }
+        public int PgoFormatVersion { get; }
+        public byte[] Image { get; }
+        private ReadyToRunReader _r2rReader;
+
+        public PgoInfo(PgoInfoKey key, ReadyToRunReader r2rReader, int pgoFormatVersion, byte[] image, int offset)
+        {
+            PgoFormatVersion = pgoFormatVersion;
+            Key = key;
+            Offset = offset;
+            Image = image;
+            _r2rReader = r2rReader;
+        }
+
+        // The empty singleton cannot be used for anything except reference equality comparison
+        public static PgoInfo EmptySingleton => new PgoInfo(null, null, 0, null, 0);
+
+        PgoSchemaElem[] _pgoData;
+        int _size;
+
+        class PgoDataLoader : IPgoSchemaDataLoader<string>
+        {
+            ReadyToRunReader _r2rReader;
+            SignatureFormattingOptions _formatOptions;
+
+            public PgoDataLoader(ReadyToRunReader r2rReader, SignatureFormattingOptions formatOptions)
+            {
+                _formatOptions = formatOptions;
+                _r2rReader = r2rReader;
+            }
+
+            string IPgoSchemaDataLoader<string>.TypeFromLong(long input)
+            {
+                int tableIndex = checked((int)(input & 0xF));
+                int fixupIndex = checked((int)(input >> 4));
+                if (tableIndex == 0xF)
+                {
+                    return $"Unknown type {fixupIndex}";
+                }
+                else
+                {
+                    return _r2rReader.ImportSections[tableIndex].Entries[fixupIndex].Signature.ToString(_formatOptions);
+                }
+            }
+        }
+
+        void EnsurePgoData()
+        {
+            if (_pgoData == null)
+            {
+                var compressedIntParser = new PgoProcessor.PgoEncodedCompressedIntParser(Image, Offset);
+
+                SignatureFormattingOptions formattingOptions = new SignatureFormattingOptions();
+
+                _pgoData = PgoProcessor.ParsePgoData<string>(new PgoDataLoader(_r2rReader, formattingOptions), compressedIntParser, true).ToArray();
+                _size = compressedIntParser.Offset - Offset;
+            }
+        }
+
+        public PgoSchemaElem[] PgoData
+        {
+            get
+            {
+                EnsurePgoData();
+                return _pgoData;
+            }
+        }
+
+        public int Size
+        {
+            get
+            {
+                EnsurePgoData();
+                return _size;
+            }
+        }
+
+        public override string ToString()
+        {
+            StringBuilder sb = new StringBuilder();
+            foreach (PgoSchemaElem elem in PgoData)
+            {
+                sb.AppendLine($"ILOffset: {elem.ILOffset} InstrumentationKind: {elem.InstrumentationKind} Other: {elem.Other} Count: {elem.Count}");
+                if (elem.DataHeldInDataLong)
+                {
+                    sb.AppendLine(elem.DataLong.ToString());
+                }
+                else
+                {
+                    foreach (object o in elem.DataObject)
+                    {
+                        sb.AppendLine(o.ToString());
+                    }
+                }
+            }
+
+            return sb.ToString();
+        }
+    }
+}
diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PgoInfoKey.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/PgoInfoKey.cs
new file mode 100644 (file)
index 0000000..0140521
--- /dev/null
@@ -0,0 +1,138 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Reflection.Metadata;
+using System.Text;
+using Internal.Pgo;
+
+namespace ILCompiler.Reflection.ReadyToRun
+{
+    public class PgoInfoKey : IEquatable<PgoInfoKey>
+    {
+        /// <summary>
+        /// The type that the method belongs to
+        /// </summary>
+        public string DeclaringType { get; }
+
+        public string Name { get; }
+
+        /// <summary>
+        /// The method metadata handle
+        /// </summary>
+        public EntityHandle MethodHandle { get; }
+
+        public IAssemblyMetadata ComponentReader { get; }
+
+        public MethodSignature<string> Signature { get; }
+
+        /// <summary>
+        /// The signature with format: namespace.class.methodName<S, T, ...>(S, T, ...)
+        /// </summary>
+        public string SignatureString { get; }
+
+        public PgoInfoKey(IAssemblyMetadata componentReader, string owningType, EntityHandle methodHandle, string[] instanceArgs)
+        {
+            ComponentReader = componentReader;
+            EntityHandle owningTypeHandle;
+            DisassemblingGenericContext genericContext = new DisassemblingGenericContext(typeParameters: Array.Empty<string>(), methodParameters: instanceArgs);
+            DisassemblingTypeProvider typeProvider = new DisassemblingTypeProvider();
+            MethodHandle = methodHandle;
+
+            // get the method signature from the method handle
+            switch (MethodHandle.Kind)
+            {
+                case HandleKind.MethodDefinition:
+                    {
+                        MethodDefinition methodDef = componentReader.MetadataReader.GetMethodDefinition((MethodDefinitionHandle)MethodHandle);
+                        Name = componentReader.MetadataReader.GetString(methodDef.Name);
+                        Signature = methodDef.DecodeSignature<string, DisassemblingGenericContext>(typeProvider, genericContext);
+                        owningTypeHandle = methodDef.GetDeclaringType();
+                    }
+                    break;
+
+                case HandleKind.MemberReference:
+                    {
+                        MemberReference memberRef = componentReader.MetadataReader.GetMemberReference((MemberReferenceHandle)MethodHandle);
+                        Name = componentReader.MetadataReader.GetString(memberRef.Name);
+                        Signature = memberRef.DecodeMethodSignature<string, DisassemblingGenericContext>(typeProvider, genericContext);
+                        owningTypeHandle = memberRef.Parent;
+                    }
+                    break;
+
+                default:
+                    throw new NotImplementedException();
+            }
+
+            if (owningType != null)
+            {
+                DeclaringType = owningType;
+            }
+            else
+            {
+                DeclaringType = MetadataNameFormatter.FormatHandle(componentReader.MetadataReader, owningTypeHandle);
+            }
+
+            StringBuilder sb = new StringBuilder();
+            sb.Append(Signature.ReturnType);
+            sb.Append(" ");
+            sb.Append(DeclaringType);
+            sb.Append(".");
+            sb.Append(Name);
+
+            if (Signature.GenericParameterCount != 0)
+            {
+                sb.Append("<");
+                for (int i = 0; i < Signature.GenericParameterCount; i++)
+                {
+                    if (i > 0)
+                    {
+                        sb.Append(", ");
+                    }
+                    if (instanceArgs != null && instanceArgs.Length > i)
+                    {
+                        sb.Append(instanceArgs[i]);
+                    }
+                    else
+                    {
+                        sb.Append("!");
+                        sb.Append(i);
+                    }
+                }
+                sb.Append(">");
+            }
+
+            sb.Append("(");
+            for (int i = 0; i < Signature.ParameterTypes.Length; i++)
+            {
+                if (i > 0)
+                {
+                    sb.Append(", ");
+                }
+                sb.AppendFormat($"{Signature.ParameterTypes[i]}");
+            }
+            sb.Append(")");
+
+            SignatureString = sb.ToString();
+        }
+
+        public override string ToString() => SignatureString;
+        public override int GetHashCode() => SignatureString.GetHashCode();
+        public override bool Equals(object obj) => obj is PgoInfoKey other && other.Equals(this);
+
+        // Equality check. This isn't precisely accurate, but it should be good enough
+        public bool Equals(PgoInfoKey other)
+        {
+            return SignatureString.Equals(other.SignatureString);
+        }
+
+        public static PgoInfoKey FromReadyToRunMethod(ReadyToRunMethod method)
+        {
+            var key = new PgoInfoKey(method.ComponentReader, method.DeclaringType, method.MethodHandle, method.InstanceArgs);
+            Debug.Assert(key.SignatureString == method.SignatureString);
+            return key;
+        }
+    }
+}
\ No newline at end of file
index 59c455a..57c71db 100644 (file)
@@ -327,6 +327,18 @@ namespace ILCompiler.Reflection.ReadyToRun
 
         private BaseGcInfo _gcInfo;
 
+        public PgoInfo PgoInfo
+        {
+            get
+            {
+                EnsureInitialized();
+                if (_pgoInfo == PgoInfo.EmptySingleton)
+                    return null;
+                return _pgoInfo;
+            }
+        }
+
+        private PgoInfo _pgoInfo;
 
         private ReadyToRunReader _readyToRunReader;
         private List<FixupCell> _fixupCells;
@@ -342,6 +354,8 @@ namespace ILCompiler.Reflection.ReadyToRun
             }
         }
 
+        public string[] InstanceArgs { get; set; }
+
         public int RuntimeFunctionCount { get; set; }
 
         /// <summary>
@@ -357,6 +371,7 @@ namespace ILCompiler.Reflection.ReadyToRun
             string[] instanceArgs,
             int? fixupOffset)
         {
+            InstanceArgs = (string[])instanceArgs?.Clone();
             _readyToRunReader = readyToRunReader;
             _fixupOffset = fixupOffset;
             MethodHandle = methodHandle;
@@ -472,6 +487,14 @@ namespace ILCompiler.Reflection.ReadyToRun
                     _gcInfo = new Amd64.GcInfo(_readyToRunReader.Image, gcInfoOffset, _readyToRunReader.Machine, _readyToRunReader.ReadyToRunHeader.MajorVersion);
                 }
             }
+            if (_pgoInfo == null)
+            {
+                _pgoInfo = _readyToRunReader.GetPgoInfoByKey(PgoInfoKey.FromReadyToRunMethod(this));
+                if (_pgoInfo == null)
+                {
+                    _pgoInfo = PgoInfo.EmptySingleton;
+                }
+            }
         }
 
         private void EnsureFixupCells()
index 8b4c35b..12bbd06 100644 (file)
@@ -119,6 +119,9 @@ namespace ILCompiler.Reflection.ReadyToRun
         // Methods
         private List<InstanceMethod> _instanceMethods;
 
+        // PgoData
+        private Dictionary<PgoInfoKey, PgoInfo> _pgoInfos;
+
         // ImportSections
         private List<ReadyToRunImportSection> _importSections;
         private Dictionary<int, ReadyToRunSignature> _importSignatures;
@@ -447,6 +450,11 @@ namespace ILCompiler.Reflection.ReadyToRun
                 return;
             }
 
+            if (ReadyToRunHeader.Sections.TryGetValue(ReadyToRunSectionType.PgoInstrumentationData, out _))
+            {
+                ParsePgoMethods();
+            }
+
             _instanceMethods = new List<InstanceMethod>();
             foreach (ReadyToRunAssembly assembly in _readyToRunAssemblies)
             {
@@ -912,6 +920,129 @@ namespace ILCompiler.Reflection.ReadyToRun
             }
         }
 
+        public IEnumerable<PgoInfo> AllPgoInfos
+        {
+            get
+            {
+                EnsureMethods();
+                return _pgoInfos.Values;
+            }
+        }
+
+        public PgoInfo GetPgoInfoByKey(PgoInfoKey key)
+        {
+            EnsureMethods();
+            if (_pgoInfos != null)
+            {
+                _pgoInfos.TryGetValue(key, out var returnValue);
+                return returnValue;
+            }
+
+            return null;
+        }
+
+        /// <summary>
+        /// Initialize generic method instances with argument types and runtime function indices from InstanceMethodEntrypoints
+        /// </summary>
+        private void ParsePgoMethods()
+        {
+            _pgoInfos = new Dictionary<PgoInfoKey, PgoInfo>();
+            if (!ReadyToRunHeader.Sections.TryGetValue(ReadyToRunSectionType.PgoInstrumentationData, out ReadyToRunSection pgoInstrumentationDataSection))
+            {
+                return;
+            }
+            int pgoInstrumentationDataOffset = GetOffset(pgoInstrumentationDataSection.RelativeVirtualAddress);
+            NativeParser parser = new NativeParser(Image, (uint)pgoInstrumentationDataOffset);
+            NativeHashtable pgoInstrumentationData = new NativeHashtable(Image, parser, (uint)(pgoInstrumentationDataOffset + pgoInstrumentationDataSection.Size));
+            NativeHashtable.AllEntriesEnumerator allEntriesEnum = pgoInstrumentationData.EnumerateAllEntries();
+            NativeParser curParser = allEntriesEnum.GetNext();
+            while (!curParser.IsNull())
+            {
+                IAssemblyMetadata mdReader = GetGlobalMetadata();
+                SignatureFormattingOptions dummyOptions = new SignatureFormattingOptions();
+                SignatureDecoder decoder = new SignatureDecoder(_assemblyResolver, dummyOptions, mdReader?.MetadataReader, this, (int)curParser.Offset);
+
+                string owningType = null;
+
+                uint methodFlags = decoder.ReadUInt();
+                if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_OwnerType) != 0)
+                {
+                    mdReader = decoder.GetMetadataReaderFromModuleOverride() ?? mdReader;
+                    if ((_composite) && mdReader == null)
+                    {
+                        // The only types that don't have module overrides on them in composite images are primitive types within the system module
+                        mdReader = GetSystemModuleMetadataReader();
+                    }
+                    owningType = decoder.ReadTypeSignatureNoEmit();
+                }
+                if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_SlotInsteadOfToken) != 0)
+                {
+                    throw new NotImplementedException();
+                }
+                EntityHandle methodHandle;
+                int rid = (int)decoder.ReadUInt();
+                if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_MemberRefToken) != 0)
+                {
+                    methodHandle = MetadataTokens.MemberReferenceHandle(rid);
+                }
+                else
+                {
+                    methodHandle = MetadataTokens.MethodDefinitionHandle(rid);
+                }
+                string[] methodTypeArgs = null;
+                if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_MethodInstantiation) != 0)
+                {
+                    uint typeArgCount = decoder.ReadUInt();
+                    methodTypeArgs = new string[typeArgCount];
+                    for (int typeArgIndex = 0; typeArgIndex < typeArgCount; typeArgIndex++)
+                    {
+                        methodTypeArgs[typeArgIndex] = decoder.ReadTypeSignatureNoEmit();
+                    }
+                }
+
+                string constrainedType = null;
+                if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_Constrained) != 0)
+                {
+                    constrainedType = decoder.ReadTypeSignatureNoEmit();
+                }
+
+                GetPgoOffsetAndVersion(decoder.Offset, out int pgoFormatVersion, out int pgoOffset);
+
+                PgoInfoKey key = new PgoInfoKey(GetGlobalMetadata(), owningType, methodHandle, methodTypeArgs);
+                PgoInfo info = new PgoInfo(key, this, pgoFormatVersion, Image, pgoOffset);
+                _pgoInfos.Add(key, info);
+                curParser = allEntriesEnum.GetNext();
+            }
+
+            return;
+
+            // Broken out into helper function in case we ever implement pgo data that isn't tied to a signature
+            void GetPgoOffsetAndVersion(int offset, out int version, out int pgoDataOffset)
+            {
+                version = 0;
+                // get the id of the entry point runtime function from the MethodEntryPoints NativeArray
+                uint versionAndFlags = 0; // the RUNTIME_FUNCTIONS index
+                offset = (int)NativeReader.DecodeUnsigned(Image, (uint)offset, ref versionAndFlags);
+
+                switch (versionAndFlags & 3)
+                {
+                    case 3:
+                        uint val = 0;
+                        NativeReader.DecodeUnsigned(Image, (uint)offset, ref val);
+                        offset -= (int)val;
+                        break;
+                    case 1:
+                        // Offset already correct
+                        break;
+                    default:
+                        throw new Exception("Invalid Pgo format");
+                }
+
+                version = (int)(versionAndFlags >> 2);
+                pgoDataOffset = offset;
+            }
+        }
+
         private void CountRuntimeFunctions(bool[] isEntryPoint)
         {
             foreach (ReadyToRunMethod method in Methods)
index 04b4024..9097636 100644 (file)
@@ -32,6 +32,7 @@ namespace ILCompiler
         public bool Verbose;
         public bool Composite;
         public bool CompileNoMethods;
+        public bool EmbedPgoData;
 
         public string DgmlLogFileName;
         public bool GenerateFullDgmlLog;
@@ -102,6 +103,7 @@ namespace ILCompiler
                 syntax.DefineOption("tuning", ref Tuning, SR.TuningImageOption);
                 syntax.DefineOption("partial", ref Partial, SR.PartialImageOption);
                 syntax.DefineOption("compilebubblegenerics", ref CompileBubbleGenerics, SR.BubbleGenericsOption);
+                syntax.DefineOption("embed-pgo-data", ref EmbedPgoData, SR.EmbedPgoDataOption);
                 syntax.DefineOption("dgmllog|dgml-log-file-name", ref DgmlLogFileName, SR.SaveDependencyLogOption);
                 syntax.DefineOption("fulllog|generate-full-dmgl-log", ref GenerateFullDgmlLog, SR.SaveDetailedLogOption);
                 syntax.DefineOption("verbose", ref Verbose, SR.VerboseLoggingOption);
index 3b51b9b..260053c 100644 (file)
@@ -542,7 +542,8 @@ namespace ILCompiler
                         mibcFiles,
                         jsonProfile,
                         _typeSystemContext,
-                        compilationGroup);
+                        compilationGroup,
+                        _commandLineOptions.EmbedPgoData);
 
                     if (_commandLineOptions.Partial)
                         compilationGroup.ApplyProfilerGuidedCompilationRestriction(profileDataManager);
index b5abfd7..d7c4ba1 100644 (file)
   <data name="BubbleGenericsOption" xml:space="preserve">
     <value>Compile instantiations from reference modules used in the current module</value>
   </data>
+  <data name="EmbedPgoDataOption" xml:space="preserve">
+    <value>Embed instrumentation data in generated file</value>
+  </data>
   <data name="CodeGenOptions" xml:space="preserve">
     <value>Define a codegen option</value>
   </data>
index 550326c..dbe495a 100644 (file)
@@ -13,7 +13,6 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <Compile Include="..\Common\Pgo\PgoFormat.cs" Link="PgoFormat.cs" />
     <Compile Include="..\Common\Pgo\TypeSystemEntityOrUnknown.cs" Link="TypeSystemEntityOrUnknown.cs" />
   </ItemGroup>
 
index 7ada4e9..8857cdb 100644 (file)
@@ -24,6 +24,7 @@ namespace R2RDump
             command.AddOption(new Option<string[]>(new[] { "--section", "-s" }, "Get section by keyword"));
             command.AddOption(new Option<bool>(new[] { "--unwind" }, "Dump unwindInfo"));
             command.AddOption(new Option<bool>(new[] { "--gc" }, "Dump gcInfo and slot table"));
+            command.AddOption(new Option<bool>(new[] { "--pgo" }, "Dump embedded pgo instrumentation data"));
             command.AddOption(new Option<bool>(new[] { "--sectionContents", "--sc" }, "Dump section contents"));
             command.AddOption(new Option<bool>(new[] { "--entrypoints", "-e" }, "Dump list of method / instance entrypoints in the R2R file"));
             command.AddOption(new Option<bool>(new[] { "--normalize", "-n" }, "Normalize dump by sorting the various tables and methods (default = unsorted i.e. file order)"));
index 805e479..8a24273 100644 (file)
@@ -40,6 +40,7 @@ namespace R2RDump
 
         public bool Unwind { get; set; }
         public bool GC { get; set; }
+        public bool Pgo { get; set; }
         public bool SectionContents { get; set; }
         public bool EntryPoints { get; set; }
         public bool Normalize { get; set; }
@@ -240,6 +241,7 @@ namespace R2RDump
                 _options.Disasm = true;
                 _options.Unwind = true;
                 _options.GC = true;
+                _options.Pgo = true;
                 _options.SectionContents = true;
             }
 
index 5778752..f2fec94 100644 (file)
@@ -136,14 +136,46 @@ namespace R2RDump
             WriteDivider("R2R Methods");
             _writer.WriteLine($"{_r2r.Methods.Count()} methods");
             SkipLine();
+
+            HashSet<PgoInfoKey> pgoEntriesNotDumped = new HashSet<PgoInfoKey>();
+
+            if (_options.Pgo)
+            {
+                foreach (PgoInfo info in _r2r.AllPgoInfos)
+                {
+                    pgoEntriesNotDumped.Add(info.Key);
+                }
+            }
             foreach (ReadyToRunMethod method in NormalizedMethods())
             {
                 TextWriter temp = _writer;
                 _writer = new StringWriter();
                 DumpMethod(method);
+                if (_options.Pgo && method.PgoInfo != null)
+                {
+                    pgoEntriesNotDumped.Remove(method.PgoInfo.Key);
+                }
                 temp.Write(_writer.ToString());
                 _writer = temp;
             }
+
+            if (pgoEntriesNotDumped.Count > 0)
+            {
+                WriteDivider("Pgo entries without code");
+                _writer.WriteLine($"{pgoEntriesNotDumped.Count()} Pgo blobs");
+                IEnumerable<PgoInfoKey> pgoEntriesToDump = pgoEntriesNotDumped;
+                if (_options.Normalize)
+                {
+                    pgoEntriesToDump = pgoEntriesToDump.OrderBy((p) => p.SignatureString);
+                }
+                foreach (var entry in pgoEntriesToDump)
+                {
+                    WriteSubDivider();
+                    _writer.WriteLine(entry.SignatureString);
+                    _writer.WriteLine($"Handle: 0x{MetadataTokens.GetToken(entry.ComponentReader.MetadataReader, entry.MethodHandle):X8}");
+                    _writer.WriteLine(_r2r.GetPgoInfoByKey(entry));
+                }
+            }
         }
 
         /// <summary>
@@ -167,6 +199,18 @@ namespace R2RDump
             }
             SkipLine();
 
+            if (_options.Pgo && method.PgoInfo != null)
+            {
+                _writer.WriteLine("PGO info:");
+                _writer.Write(method.PgoInfo);
+
+                if (_options.Raw)
+                {
+                    DumpBytes(method.PgoInfo.Offset, (uint)method.PgoInfo.Size, convertToOffset: false);
+                }
+                SkipLine();
+            }
+
             foreach (RuntimeFunction runtimeFunction in method.RuntimeFunctions)
             {
                 DumpRuntimeFunction(runtimeFunction);
index 317d9dc..5fe2fd3 100644 (file)
@@ -5248,7 +5248,7 @@ HCIMPL2(void, JIT_ClassProfile, Object *obj, void* tableAddress)
 
     ICorJitInfo::ClassProfile* const classProfile = (ICorJitInfo::ClassProfile*) tableAddress;
     volatile unsigned* pCount = (volatile unsigned*) &classProfile->Count;
-    const unsigned count = *pCount++;
+    const unsigned count = (*pCount)++;
     const unsigned S = ICorJitInfo::ClassProfile::SIZE;
     const unsigned N = ICorJitInfo::ClassProfile::SAMPLE_INTERVAL;
     _ASSERTE(N >= S);
index 6126a3b..7f4fee1 100644 (file)
@@ -11982,10 +11982,6 @@ HRESULT CEEJitInfo::getPgoInstrumentationResults(
     {
         if (pDataCur->m_pMD == pMD)
         {
-            *pSchema = pDataCur->m_schema.GetElements();
-            *pCountSchemaItems = pDataCur->m_schema.GetCount();
-            *pInstrumentationData = pDataCur->m_pInstrumentationData;
-            hr = pDataCur->m_hr;
             break;
         }
     }
@@ -11998,12 +11994,12 @@ HRESULT CEEJitInfo::getPgoInstrumentationResults(
         m_foundPgoData = newPgoData;
         newPgoData.SuppressRelease();
         
-        newPgoData->m_hr = PgoManager::getPgoInstrumentationResults(pMD, &newPgoData->m_schema, &newPgoData->m_pInstrumentationData);
+        newPgoData->m_hr = PgoManager::getPgoInstrumentationResults(pMD, &newPgoData->m_allocatedData, &newPgoData->m_schema, &newPgoData->m_cSchemaElems, &newPgoData->m_pInstrumentationData);
         pDataCur = m_foundPgoData;
     }
 
-    *pSchema = pDataCur->m_schema.GetElements();
-    *pCountSchemaItems = pDataCur->m_schema.GetCount();
+    *pSchema = pDataCur->m_schema;
+    *pCountSchemaItems = pDataCur->m_cSchemaElems;
     *pInstrumentationData = pDataCur->m_pInstrumentationData;
     hr = pDataCur->m_hr;
 #else
@@ -12781,7 +12777,14 @@ CORJIT_FLAGS GetCompileFlags(MethodDesc * ftn, CORJIT_FLAGS flags, CORINFO_METHO
             optType = methodInfo->ILCodeSize % OPT_RANDOM;
 
         if (g_pConfig->JitMinOpts())
+        {
             flags.Set(CORJIT_FLAGS::CORJIT_FLAG_MIN_OPT);
+        }
+        else
+        {
+            if (!flags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_TIER0))
+                flags.Set(CORJIT_FLAGS::CORJIT_FLAG_BBOPT);
+        }
 
         if (optType == OPT_SIZE)
         {
index 07a035a..c19e893 100644 (file)
@@ -945,7 +945,9 @@ protected :
 
         ComputedPgoData* m_next = nullptr;
         MethodDesc *m_pMD;
-        SArray<PgoInstrumentationSchema> m_schema;
+        NewArrayHolder<BYTE> m_allocatedData;
+        PgoInstrumentationSchema* m_schema;
+        UINT32 m_cSchemaElems;
         BYTE *m_pInstrumentationData = nullptr;
         HRESULT m_hr = E_NOTIMPL;
     };
index a4450d1..f09517b 100644 (file)
@@ -65,7 +65,7 @@ void NativeImage::Initialize(READYTORUN_HEADER *pHeader, LoaderAllocator *pLoade
 {
     LoaderHeap *pHeap = pLoaderAllocator->GetHighFrequencyHeap();
 
-    m_pReadyToRunInfo = new ReadyToRunInfo(/*pModule*/ NULL, m_pImageLayout, pHeader, /*compositeImage*/ NULL, pamTracker);
+    m_pReadyToRunInfo = new ReadyToRunInfo(/*pModule*/ NULL, pLoaderAllocator, m_pImageLayout, pHeader, /*compositeImage*/ NULL, pamTracker);
     m_pComponentAssemblies = m_pReadyToRunInfo->FindSection(ReadyToRunSectionType::ComponentAssemblies);
     m_componentAssemblyCount = m_pComponentAssemblies->Size / sizeof(READYTORUN_COMPONENT_ASSEMBLIES_ENTRY);
     
index 5e6be29..deef0ac 100644 (file)
@@ -55,7 +55,7 @@ const char* const         PgoManager::s_TypeHandle = "TypeHandle: %s\n";
 struct HistogramEntry
 {
     // Class that was observed at runtime
-    CORINFO_CLASS_HANDLE m_mt;
+    INT_PTR m_mt; // This may be an "unknown type handle"
     // Number of observations in the table
     unsigned             m_count;
 };
@@ -83,7 +83,6 @@ Histogram::Histogram(uint32_t histogramCount, INT_PTR* histogramEntries, unsigne
 
     for (unsigned k = 0; k < entryCount; k++)
     {
-
         if (histogramEntries[k] == 0)
         {
             continue;
@@ -91,17 +90,7 @@ Histogram::Histogram(uint32_t histogramCount, INT_PTR* histogramEntries, unsigne
         
         m_totalCount++;
 
-        if (IsUnknownTypeHandle(histogramEntries[k]))
-        {
-            if (AddTypeHandleToUnknownTypeHandleMask(histogramEntries[k], &unknownTypeHandleMask))
-            {
-                m_unknownTypes++;
-            }
-            // An unknown type handle will adjust total count but not set of entries
-            continue;
-        }
-
-        CORINFO_CLASS_HANDLE currentEntry = (CORINFO_CLASS_HANDLE)histogramEntries[k];
+        INT_PTR currentEntry = histogramEntries[k];
         
         bool found = false;
         unsigned h = 0;
@@ -141,7 +130,13 @@ void PgoManager::Initialize()
 
 void PgoManager::Shutdown()
 {
-    WritePgoData();
+    static bool written = false;
+
+    if (!written)
+    {
+        written = true;
+        WritePgoData();
+    }
 }
 
 void PgoManager::VerifyAddress(void* address)
@@ -678,6 +673,7 @@ HRESULT PgoManager::allocPgoInstrumentationBySchemaInstance(MethodDesc* pMD,
     *pInstrumentationData = NULL;
     int codehash;
     unsigned ilSize;
+
     if (!GetVersionResilientILCodeHashCode(pMD, &codehash, &ilSize))
     {
         return E_NOTIMPL;
@@ -701,13 +697,24 @@ HRESULT PgoManager::allocPgoInstrumentationBySchemaInstance(MethodDesc* pMD,
         prevSchema = pSchema[iSchema];
     }
 
-    S_SIZE_T allocationSize = S_SIZE_T(sizeof(HeaderList)) + S_SIZE_T(pSchema[countSchemaItems - 1].Offset + InstrumentationKindToSize(pSchema[countSchemaItems - 1].InstrumentationKind));
+    S_SIZE_T allocationSize = S_SIZE_T(sizeof(HeaderList)) + S_SIZE_T(pSchema[countSchemaItems - 1].Offset) + S_SIZE_T(pSchema[countSchemaItems - 1].Count) * S_SIZE_T(InstrumentationKindToSize(pSchema[countSchemaItems - 1].InstrumentationKind));
     
     if (allocationSize.IsOverflow())
     {
         return E_NOTIMPL;
     }
     size_t unsafeAllocationSize = allocationSize.Value();
+    if (unsafeAllocationSize % sizeof(size_t) == 4)
+    {
+        allocationSize = allocationSize + S_SIZE_T(4);
+
+        if (allocationSize.IsOverflow())
+        {
+            return E_NOTIMPL;
+        }
+        unsafeAllocationSize = allocationSize.Value();
+    }
+
     HeaderList* pHeaderList = NULL;
 
     if (pMD->IsDynamicMethod())
@@ -772,11 +779,12 @@ HRESULT PgoManager::allocPgoInstrumentationBySchemaInstance(MethodDesc* pMD,
 }
 
 #ifndef DACCESS_COMPILE
-HRESULT PgoManager::getPgoInstrumentationResults(MethodDesc* pMD, SArray<ICorJitInfo::PgoInstrumentationSchema>* pSchema, BYTE**pInstrumentationData)
+HRESULT PgoManager::getPgoInstrumentationResults(MethodDesc* pMD, BYTE** pAllocatedData, ICorJitInfo::PgoInstrumentationSchema** ppSchema, UINT32 *pCountSchemaItems, BYTE**pInstrumentationData)
 {
     // Initialize our out params
-    pSchema->Clear();
+    *pAllocatedData = NULL;
     *pInstrumentationData = NULL;
+    *pCountSchemaItems = 0;
 
     PgoManager *mgr;
     if (!pMD->IsDynamicMethod())
@@ -791,7 +799,7 @@ HRESULT PgoManager::getPgoInstrumentationResults(MethodDesc* pMD, SArray<ICorJit
     HRESULT hr = E_NOTIMPL;
     if (mgr != NULL)
     {
-        hr = mgr->getPgoInstrumentationResultsInstance(pMD, pSchema, pInstrumentationData);
+        hr = mgr->getPgoInstrumentationResultsInstance(pMD, pAllocatedData, ppSchema, pCountSchemaItems, pInstrumentationData);
     }
 
     // If not found in the data from the current run, look in the data from the text file
@@ -805,8 +813,11 @@ HRESULT PgoManager::getPgoInstrumentationResults(MethodDesc* pMD, SArray<ICorJit
             Header *found = s_textFormatPgoData.Lookup(CodeAndMethodHash(codehash, methodhash));
             if (found != NULL)
             {
-                if (ReadInstrumentationSchemaWithLayoutIntoSArray(found->GetData(), found->countsOffset, found->countsOffset, pSchema))
+                StackSArray<ICorJitInfo::PgoInstrumentationSchema> schemaArray;
+
+                if (ReadInstrumentationSchemaWithLayoutIntoSArray(found->GetData(), found->countsOffset, found->countsOffset, &schemaArray))
                 {
+                    
                     EX_TRY
                     {
                         // TypeHandles can't reliably be loaded at ReadPGO time
@@ -815,9 +826,9 @@ HRESULT PgoManager::getPgoInstrumentationResults(MethodDesc* pMD, SArray<ICorJit
                         // terminated strings in the TypeHandle slots, and this will
                         // translate any of those into loaded TypeHandles as appropriate
 
-                        for (unsigned iSchema = 0; iSchema < pSchema->GetCount(); iSchema++)
+                        for (unsigned iSchema = 0; iSchema < schemaArray.GetCount(); iSchema++)
                         {
-                            ICorJitInfo::PgoInstrumentationSchema *schema = &(*pSchema)[iSchema];
+                            ICorJitInfo::PgoInstrumentationSchema *schema = &(schemaArray)[iSchema];
                             if ((schema->InstrumentationKind & ICorJitInfo::PgoInstrumentationKind::MarshalMask) == ICorJitInfo::PgoInstrumentationKind::TypeHandle)
                             {
                                 for (int iEntry = 0; iEntry < schema->Count; iEntry++)
@@ -851,6 +862,12 @@ HRESULT PgoManager::getPgoInstrumentationResults(MethodDesc* pMD, SArray<ICorJit
                             }
                         }
 
+                        *pAllocatedData = new BYTE[schemaArray.GetCount() * sizeof(ICorJitInfo::PgoInstrumentationSchema)];
+                        memcpy(*pAllocatedData, schemaArray.OpenRawBuffer(), schemaArray.GetCount() * sizeof(ICorJitInfo::PgoInstrumentationSchema));
+                        schemaArray.CloseRawBuffer();
+                        *ppSchema = (ICorJitInfo::PgoInstrumentationSchema*)pAllocatedData;
+
+                        *pCountSchemaItems = schemaArray.GetCount();
                         *pInstrumentationData = found->GetData();
                         hr = S_OK;
                     }
@@ -859,7 +876,6 @@ HRESULT PgoManager::getPgoInstrumentationResults(MethodDesc* pMD, SArray<ICorJit
                         hr = E_FAIL;
                     }
                     EX_END_CATCH(RethrowTerminalExceptions)
-
                 }
                 else
                 {
@@ -872,13 +888,145 @@ HRESULT PgoManager::getPgoInstrumentationResults(MethodDesc* pMD, SArray<ICorJit
 
     return hr;
 }
+
+class R2RInstrumentationDataReader
+{
+    ReadyToRunInfo *m_pReadyToRunInfo;
+    Module* m_pModule;
+    PEDecoder* m_pNativeImage;
+
+public:
+    StackSArray<ICorJitInfo::PgoInstrumentationSchema> schemaArray;
+    StackSArray<BYTE> instrumentationData;
+
+    R2RInstrumentationDataReader(ReadyToRunInfo *pReadyToRunInfo, Module* pModule, PEDecoder* pNativeImage) :
+        m_pReadyToRunInfo(pReadyToRunInfo),
+        m_pModule(pModule),
+        m_pNativeImage(pNativeImage)
+    {}
+
+    bool operator()(const ICorJitInfo::PgoInstrumentationSchema &schema, int64_t dataItem, int32_t iDataItem)
+    {
+        if (iDataItem == 0)
+        {
+            schemaArray.Append(schema);
+            schemaArray[schemaArray.GetCount() - 1].Offset = instrumentationData.GetCount();
+        }
+
+        if ((schema.InstrumentationKind & ICorJitInfo::PgoInstrumentationKind::MarshalMask) == ICorJitInfo::PgoInstrumentationKind::TypeHandle)
+        {
+            intptr_t typeHandleData = 0;
+            if (dataItem != 0)
+            {
+                uint32_t importSection = dataItem & 0xF;
+                int64_t typeIndex = dataItem >> 4;
+                if (importSection != 0xF)
+                {
+                    COUNT_T countImportSections;
+                    PTR_CORCOMPILE_IMPORT_SECTION pImportSections = m_pReadyToRunInfo->GetImportSections(&countImportSections);
+
+                    if (importSection >= countImportSections)
+                    {
+                        _ASSERTE(!"Malformed pgo type handle data");
+                        return false;
+                    }
+
+                    PTR_CORCOMPILE_IMPORT_SECTION pImportSection = &pImportSections[importSection];
+                    COUNT_T cbData;
+                    TADDR pData = m_pNativeImage->GetDirectoryData(&pImportSection->Section, &cbData);
+                    uint32_t fixupIndex = (uint32_t)typeIndex;
+                    PTR_SIZE_T fixupAddress = dac_cast<PTR_SIZE_T>(pData + fixupIndex * sizeof(TADDR));
+                    if (!m_pModule->FixupNativeEntry(pImportSections + importSection, fixupIndex, fixupAddress))
+                    {
+                        return false;
+                    }
+
+                    typeHandleData = *(intptr_t*)fixupAddress;
+                }
+                else
+                {
+                    typeHandleData = HashToPgoUnknownTypeHandle((uint32_t)typeIndex);
+                }
+            }
+
+            BYTE* pTypeHandleData = (BYTE*)&typeHandleData;
+            for (size_t i = 0; i < sizeof(intptr_t); i++)
+            {
+                instrumentationData.Append(pTypeHandleData[i]);
+            }
+        }
+        else if ((schema.InstrumentationKind & ICorJitInfo::PgoInstrumentationKind::MarshalMask) == ICorJitInfo::PgoInstrumentationKind::FourByte)
+        {
+            BYTE* pFourByteData = (BYTE*)&dataItem;
+            for (int i = 0; i < 4; i++)
+            {
+                instrumentationData.Append(pFourByteData[i]);
+            }
+        }
+        else if ((schema.InstrumentationKind & ICorJitInfo::PgoInstrumentationKind::MarshalMask) == ICorJitInfo::PgoInstrumentationKind::EightByte)
+        {
+            BYTE* pEightByteData = (BYTE*)&dataItem;
+            for (int i = 0; i < 8; i++)
+            {
+                instrumentationData.Append(pEightByteData[i]);
+            }
+        }
+
+        return true;
+    }
+};
+
+HRESULT PgoManager::getPgoInstrumentationResultsFromR2RFormat(ReadyToRunInfo *pReadyToRunInfo,
+                                                              Module* pModule,
+                                                              PEDecoder* pNativeImage,
+                                                              BYTE* pR2RFormatData,
+                                                              size_t pR2RFormatDataMaxSize,
+                                                              BYTE** pAllocatedData,
+                                                              ICorJitInfo::PgoInstrumentationSchema** ppSchema,
+                                                              UINT32 *pCountSchemaItems,
+                                                              BYTE**pInstrumentationData)
+{
+    *pAllocatedData = NULL;
+    *ppSchema = NULL;
+    *pInstrumentationData = NULL;
+    *pCountSchemaItems = 0;
+
+    R2RInstrumentationDataReader r2rReader(pReadyToRunInfo, pModule, pNativeImage);
+
+    if (!ReadInstrumentationData(pR2RFormatData, pR2RFormatDataMaxSize, r2rReader))
+    {
+        return E_NOTIMPL;
+    }
+    else
+    {
+        while (r2rReader.instrumentationData.GetCount() & (sizeof(int64_t) - 1))
+        {
+            r2rReader.instrumentationData.Append(0);
+        }
+        size_t schemaDataSize = r2rReader.schemaArray.GetCount() * sizeof(ICorJitInfo::PgoInstrumentationSchema);
+        NewArrayHolder<BYTE> allocatedData = new BYTE[r2rReader.instrumentationData.GetCount() + schemaDataSize];
+
+        *pInstrumentationData = (BYTE*)allocatedData;
+        *pCountSchemaItems = r2rReader.schemaArray.GetCount();
+        memcpy(allocatedData, r2rReader.instrumentationData.OpenRawBuffer(), r2rReader.instrumentationData.GetCount());
+        r2rReader.instrumentationData.CloseRawBuffer();
+        *ppSchema = (ICorJitInfo::PgoInstrumentationSchema*)(((BYTE*)allocatedData) + r2rReader.instrumentationData.GetCount());
+        memcpy(*ppSchema, r2rReader.schemaArray.OpenRawBuffer(), schemaDataSize);
+        r2rReader.schemaArray.CloseRawBuffer();
+
+        allocatedData.SuppressRelease();
+        *pAllocatedData = allocatedData;
+        return S_OK;
+    }
+}
 #endif // DACCESS_COMPILE
 
-HRESULT PgoManager::getPgoInstrumentationResultsInstance(MethodDesc* pMD, SArray<ICorJitInfo::PgoInstrumentationSchema>* pSchema, BYTE**pInstrumentationData)
+HRESULT PgoManager::getPgoInstrumentationResultsInstance(MethodDesc* pMD, BYTE** pAllocatedData, ICorJitInfo::PgoInstrumentationSchema** ppSchema, UINT32 *pCountSchemaItems, BYTE**pInstrumentationData)
 {
     // Initialize our out params
-    pSchema->Clear();
+    *pAllocatedData = NULL;
     *pInstrumentationData = NULL;
+    *pCountSchemaItems = 0;
 
     HeaderList *found;
 
@@ -895,12 +1043,43 @@ HRESULT PgoManager::getPgoInstrumentationResultsInstance(MethodDesc* pMD, SArray
 
     if (found == NULL)
     {
+        // Prefer live collected data over data from pgo input, but if live data isn't present, use the data from the R2R file
+        // Consider merging this data with the live data instead in the future
+        if (pMD->GetModule()->IsReadyToRun() && pMD->GetModule()->GetReadyToRunInfo()->GetPgoInstrumentationData(pMD, pAllocatedData, ppSchema, pCountSchemaItems, pInstrumentationData))
+        {
+            return S_OK;
+        }
+
         return E_NOTIMPL;
     }
 
-    if (ReadInstrumentationSchemaWithLayoutIntoSArray(found->header.GetData(), found->header.countsOffset, found->header.countsOffset, pSchema))
+    StackSArray<ICorJitInfo::PgoInstrumentationSchema> schemaArray;
+    if (ReadInstrumentationSchemaWithLayoutIntoSArray(found->header.GetData(), found->header.countsOffset, found->header.countsOffset, &schemaArray))
     {
-        *pInstrumentationData = found->header.GetData();
+        size_t schemaDataSize = schemaArray.GetCount() * sizeof(ICorJitInfo::PgoInstrumentationSchema);
+        size_t instrumentationDataSize = 0;
+        if (schemaArray.GetCount() > 0)
+        {
+            auto lastSchema = schemaArray[schemaArray.GetCount() - 1];
+            instrumentationDataSize = lastSchema.Offset + lastSchema.Count * InstrumentationKindToSize(lastSchema.InstrumentationKind) - found->header.countsOffset;
+        }
+        *pAllocatedData = new BYTE[schemaDataSize + instrumentationDataSize];
+        *ppSchema = (ICorJitInfo::PgoInstrumentationSchema*)pAllocatedData;
+        *pCountSchemaItems = schemaArray.GetCount();
+        memcpy(*pAllocatedData, schemaArray.OpenRawBuffer(), schemaDataSize);
+        schemaArray.CloseRawBuffer();
+        
+        size_t* pInstrumentationDataDst = (size_t*)((*pAllocatedData) + schemaDataSize);
+        size_t* pInstrumentationDataDstEnd = (size_t*)((*pAllocatedData) + schemaDataSize + instrumentationDataSize);
+        *pInstrumentationData = (BYTE*)pInstrumentationDataDst;
+        volatile size_t*pSrc = (volatile size_t*)found->header.GetData();
+        // Use a volatile memcpy to copy the instrumentation data into a temporary buffer
+        // This allows the instrumentation data to be made stable for reading during the execution of the jit
+        // and since the copy moves through a volatile pointer, there will be no tearing of individual data elements
+        for (;pInstrumentationDataDst < pInstrumentationDataDstEnd; pInstrumentationDataDst++, pSrc++)
+        {
+            *pInstrumentationDataDst = *pSrc;
+        }
         return S_OK;
     }
     else
@@ -921,9 +1100,11 @@ CORINFO_CLASS_HANDLE PgoManager::getLikelyClass(MethodDesc* pMD, unsigned ilSize
     *pLikelihood = 0;
     *pNumberOfClasses = 0;
 
-    StackSArray<ICorJitInfo::PgoInstrumentationSchema> schema;
+    NewArrayHolder<BYTE> allocatedData;
+    ICorJitInfo::PgoInstrumentationSchema* schema;
     BYTE* pInstrumentationData;
-    HRESULT hr = getPgoInstrumentationResults(pMD, &schema, &pInstrumentationData);
+    UINT32 cSchema;
+    HRESULT hr = getPgoInstrumentationResults(pMD, &allocatedData, &schema, &cSchema, &pInstrumentationData);
 
     // Failed to find any sort of profile data for this method
     //
@@ -933,14 +1114,14 @@ CORINFO_CLASS_HANDLE PgoManager::getLikelyClass(MethodDesc* pMD, unsigned ilSize
     }
 
     // TODO This logic should be moved to the JIT
-    for (COUNT_T i = 0; i < schema.GetCount(); i++)
+    for (COUNT_T i = 0; i < cSchema; i++)
     {
         if (schema[i].ILOffset != (int32_t)ilOffset)
             continue;
 
         if ((schema[i].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::TypeHandleHistogramCount) &&
             (schema[i].Count == 1) &&
-            ((i + 1) < schema.GetCount()) &&
+            ((i + 1) < cSchema) &&
             (schema[i + 1].InstrumentationKind == ICorJitInfo::PgoInstrumentationKind::TypeHandleHistogramTypeHandle))
         {
             // Form a histogram
@@ -964,22 +1145,30 @@ CORINFO_CLASS_HANDLE PgoManager::getLikelyClass(MethodDesc* pMD, unsigned ilSize
 
                 case 1:
                 {
+                    if (IsUnknownTypeHandle(h.m_histogram[0].m_mt))
+                    {
+                        return NULL;
+                    }
                     *pLikelihood = 100;
-                    return h.m_histogram[0].m_mt;
+                    return (CORINFO_CLASS_HANDLE)h.m_histogram[0].m_mt;
                 }
                 break;
 
                 case 2:
                 {
-                    if (h.m_histogram[0].m_count >= h.m_histogram[1].m_count)
+                    if ((h.m_histogram[0].m_count >= h.m_histogram[1].m_count) && !IsUnknownTypeHandle(h.m_histogram[0].m_mt))
                     {
                         *pLikelihood = (100 * h.m_histogram[0].m_count) / h.m_totalCount;
-                        return h.m_histogram[0].m_mt;
+                        return (CORINFO_CLASS_HANDLE)h.m_histogram[0].m_mt;
                     }
-                    else
+                    else if (!IsUnknownTypeHandle(h.m_histogram[1].m_mt))
                     {
                         *pLikelihood = (100 * h.m_histogram[1].m_count) / h.m_totalCount;
-                        return h.m_histogram[1].m_mt;
+                        return (CORINFO_CLASS_HANDLE)h.m_histogram[1].m_mt;
+                    }
+                    else
+                    {
+                        return NULL;
                     }
                 }
                 break;
@@ -988,22 +1177,22 @@ CORINFO_CLASS_HANDLE PgoManager::getLikelyClass(MethodDesc* pMD, unsigned ilSize
                 {
                     // Find maximum entry and return it
                     //
-                    unsigned maxIndex = 0;
-                    unsigned maxCount = 0;
+                    unsigned maxKnownIndex = 0;
+                    unsigned maxKnownCount = 0;
 
                     for (unsigned m = 0; m < h.m_histogram.GetCount(); m++)
                     {
-                        if (h.m_histogram[m].m_count > maxCount)
+                        if ((h.m_histogram[m].m_count > maxKnownCount) && !IsUnknownTypeHandle(h.m_histogram[m].m_mt))
                         {
-                            maxIndex = m;
-                            maxCount = h.m_histogram[m].m_count;
+                            maxKnownIndex = m;
+                            maxKnownCount = h.m_histogram[m].m_count;
                         }
                     }
 
-                    if (maxCount > 0)
+                    if (maxKnownCount > 0)
                     {
-                        *pLikelihood = (100 * maxCount) / h.m_totalCount;
-                        return h.m_histogram[maxIndex].m_mt;
+                        *pLikelihood = (100 * maxKnownCount) / h.m_totalCount;;
+                        return (CORINFO_CLASS_HANDLE)h.m_histogram[maxKnownIndex].m_mt;
                     }
 
                     return NULL;
@@ -1031,8 +1220,10 @@ HRESULT PgoManager::allocPgoInstrumentationBySchema(MethodDesc* pMD, ICorJitInfo
 
 // Stub version for !FEATURE_PGO builds
 //
-HRESULT PgoManager::getPgoInstrumentationResults(MethodDesc* pMD, SArray<ICorJitInfo::PgoInstrumentationSchema>* pSchema, BYTE**pInstrumentationData)
+HRESULT PgoManager::getPgoInstrumentationResults(MethodDesc* pMD, NewArrayHolder<BYTE> *pAllocatedData, ICorJitInfo::PgoInstrumentationSchema** ppSchema, UINT32 *pCountSchemaItems, BYTE**pInstrumentationData)
 {
+    *pAllocatedData = NULL;
+    *pCountSchemaItems = 0;
     *pInstrumentationData = NULL;
     return E_NOTIMPL;
 }
@@ -1043,8 +1234,10 @@ void PgoManager::VerifyAddress(void* address)
 
 // Stub version for !FEATURE_PGO builds
 //
-CORINFO_CLASS_HANDLE PgoManager::getLikelyClass(MethodDesc* pMD, unsigned ilSize, unsigned ilOffset)
+CORINFO_CLASS_HANDLE PgoManager::getLikelyClass(MethodDesc* pMD, unsigned ilSize, unsigned ilOffset, UINT32* pLikelihood, UINT32* pNumberOfClasses)
 {
+    *pLikelihood = 0;
+    *pNumberOfClasses = 0;
     return NULL;
 }
 
index ffb43af..f50b966 100644 (file)
@@ -6,6 +6,7 @@
 #include "typehashingalgorithms.h"
 #include "shash.h"
 
+class ReadyToRunInfo;
 
 // PgoManager handles in-process and out of band profile data for jitted code.
 class PgoManager
@@ -21,8 +22,17 @@ public:
 
 public:
 
-    static HRESULT getPgoInstrumentationResults(MethodDesc* pMD, SArray<ICorJitInfo::PgoInstrumentationSchema>* pSchema, BYTE**pInstrumentationData);
+    static HRESULT getPgoInstrumentationResults(MethodDesc* pMD, BYTE **pAllocatedDatapAllocatedData, ICorJitInfo::PgoInstrumentationSchema** ppSchema, UINT32 *pCountSchemaItems, BYTE**pInstrumentationData);
     static HRESULT allocPgoInstrumentationBySchema(MethodDesc* pMD, ICorJitInfo::PgoInstrumentationSchema* pSchema, UINT32 countSchemaItems, BYTE** pInstrumentationData);
+    static HRESULT getPgoInstrumentationResultsFromR2RFormat(ReadyToRunInfo *pReadyToRunInfo,
+                                                             Module* pModule,
+                                                             PEDecoder* pNativeImage,
+                                                             BYTE* pR2RFormatData,
+                                                             size_t pR2RFormatDataMaxSize,
+                                                             BYTE** pAllocatedData,
+                                                             ICorJitInfo::PgoInstrumentationSchema** ppSchema,
+                                                             UINT32 *pCountSchemaItems,
+                                                             BYTE**pInstrumentationData);
 
     static void CreatePgoManager(PgoManager* volatile* ppPgoManager, bool loaderAllocator);
 
@@ -137,7 +147,9 @@ public:
 protected:
 
     HRESULT getPgoInstrumentationResultsInstance(MethodDesc* pMD,
-                                                 SArray<ICorJitInfo::PgoInstrumentationSchema>* pSchema,
+                                                 BYTE** pAllocatedData,
+                                                 ICorJitInfo::PgoInstrumentationSchema** ppSchema,
+                                                 UINT32 *pCountSchemaItems,
                                                  BYTE**pInstrumentationData);
 
     HRESULT allocPgoInstrumentationBySchemaInstance(MethodDesc* pMD,
index 8c8910a..3d2e25e 100644 (file)
@@ -612,7 +612,7 @@ PTR_ReadyToRunInfo ReadyToRunInfo::Initialize(Module * pModule, AllocMemTracker
 
     DoLog("Ready to Run initialized successfully");
 
-    return new (pMemory) ReadyToRunInfo(pModule, pLayout, pHeader, nativeImage, pamTracker);
+    return new (pMemory) ReadyToRunInfo(pModule, pModule->GetLoaderAllocator(), pLayout, pHeader, nativeImage, pamTracker);
 }
 
 bool ReadyToRunInfo::IsNativeImageSharedBy(PTR_Module pModule1, PTR_Module pModule2)
@@ -620,7 +620,7 @@ bool ReadyToRunInfo::IsNativeImageSharedBy(PTR_Module pModule1, PTR_Module pModu
     return pModule1->GetReadyToRunInfo()->m_pComposite == pModule2->GetReadyToRunInfo()->m_pComposite;
 }
 
-ReadyToRunInfo::ReadyToRunInfo(Module * pModule, PEImageLayout * pLayout, READYTORUN_HEADER * pHeader, NativeImage *pNativeImage, AllocMemTracker *pamTracker)
+ReadyToRunInfo::ReadyToRunInfo(Module * pModule, LoaderAllocator* pLoaderAllocator, PEImageLayout * pLayout, READYTORUN_HEADER * pHeader, NativeImage *pNativeImage, AllocMemTracker *pamTracker)
     : m_pModule(pModule),
     m_pHeader(pHeader),
     m_pNativeImage(pNativeImage),
@@ -701,6 +701,22 @@ ReadyToRunInfo::ReadyToRunInfo(Module * pModule, PEImageLayout * pLayout, READYT
         m_availableTypesHashtable = NativeHashtable(parser);
     }
 
+    // For format version 5.2 and later, there is an optional table of instrumentation data
+#ifdef FEATURE_PGO
+    if (IsImageVersionAtLeast(5, 2))
+    {
+        IMAGE_DATA_DIRECTORY * pPgoInstrumentationDataDir = m_pComposite->FindSection(ReadyToRunSectionType::PgoInstrumentationData);
+        if (pPgoInstrumentationDataDir)
+        {
+            NativeParser parser = NativeParser(&m_nativeReader, pPgoInstrumentationDataDir->VirtualAddress);
+            m_pgoInstrumentationDataHashtable = NativeHashtable(parser);
+        }
+
+        // Force the Pgo manager infrastructure to be initialized
+        pLoaderAllocator->GetOrCreatePgoManager();
+    }
+#endif
+
     if (!m_isComponentAssembly)
     {
         // For component assemblies we don't initialize the reverse lookup map mapping entry points to MethodDescs;
@@ -813,6 +829,77 @@ static bool SigMatchesMethodDesc(MethodDesc* pMD, SigPointer &sig, Module * pMod
     return true;
 }
 
+#ifndef CROSSGEN_COMPILE
+bool ReadyToRunInfo::GetPgoInstrumentationData(MethodDesc * pMD, BYTE** pAllocatedMemory, ICorJitInfo::PgoInstrumentationSchema**ppSchema, UINT *pcSchema, BYTE** pInstrumentationData)
+{
+    STANDARD_VM_CONTRACT;
+
+    PCODE pEntryPoint = NULL;
+#ifndef CROSSGEN_COMPILE
+#ifdef PROFILING_SUPPORTED
+    BOOL fShouldSearchCache = TRUE;
+#endif // PROFILING_SUPPORTED
+#endif // CROSSGEN_COMPILE
+    mdToken token = pMD->GetMemberDef();
+    int rid = RidFromToken(token);
+    if (rid == 0)
+        return false;
+
+    // If R2R code is disabled for this module, simply behave as if it is never found
+    if (m_readyToRunCodeDisabled)
+        return false;
+
+    if (m_pgoInstrumentationDataHashtable.IsNull())
+        return false;
+
+    NativeHashtable::Enumerator lookup = m_pgoInstrumentationDataHashtable.Lookup(GetVersionResilientMethodHashCode(pMD));
+    NativeParser entryParser;
+    while (lookup.GetNext(entryParser))
+    {
+        PCCOR_SIGNATURE pBlob = (PCCOR_SIGNATURE)entryParser.GetBlob();
+        SigPointer sig(pBlob);
+        if (SigMatchesMethodDesc(pMD, sig, m_pModule))
+        {
+            // Get the updated SigPointer location, so we can calculate the size of the blob,
+            // in order to skip the blob and find the entry point data.
+            entryParser = NativeParser(entryParser.GetNativeReader(), entryParser.GetOffset() + (uint)(sig.GetPtr() - pBlob));
+            uint32_t versionAndFlags = entryParser.GetUnsigned();
+            const uint32_t flagsMask = 0x3;
+            const uint32_t versionShift = 2;
+            uint32_t flags = versionAndFlags & flagsMask;
+            uint32_t version = versionAndFlags >> versionShift;
+
+            // Only version 0 is supported
+            if (version != 0)
+                return false;
+
+            uint offset = entryParser.GetOffset();
+
+            if (flags == 1)
+            {
+                // Offset is correct already
+            }
+            else if (flags == 3)
+            {
+                // Adjust offset as relative pointer
+                uint32_t val;
+                m_nativeReader.DecodeUnsigned(offset, &val);
+                offset -= val;
+            }
+
+            BYTE* instrumentationDataPtr = ((BYTE*)GetImage()->GetBase()) + offset;
+            IMAGE_DATA_DIRECTORY * pPgoInstrumentationDataDir = m_pComposite->FindSection(ReadyToRunSectionType::PgoInstrumentationData);
+            size_t maxSize = offset - pPgoInstrumentationDataDir->VirtualAddress + pPgoInstrumentationDataDir->Size;
+
+            return SUCCEEDED(PgoManager::getPgoInstrumentationResultsFromR2RFormat(this, m_pModule, m_pModule->GetNativeOrReadyToRunImage(), instrumentationDataPtr, maxSize, pAllocatedMemory, ppSchema, pcSchema, pInstrumentationData));
+        }
+    }
+
+    return false;
+}
+#endif // !CROSSGEN_COMPILE
+
+
 PCODE ReadyToRunInfo::GetEntryPoint(MethodDesc * pMD, PrepareCodeConfig* pConfig, BOOL fFixups)
 {
     STANDARD_VM_CONTRACT;
index 7a1919a..fcdfc76 100644 (file)
@@ -72,6 +72,7 @@ class ReadyToRunInfo
     NativeFormat::NativeArray       m_methodDefEntryPoints;
     NativeFormat::NativeHashtable   m_instMethodEntryPoints;
     NativeFormat::NativeHashtable   m_availableTypesHashtable;
+    NativeFormat::NativeHashtable   m_pgoInstrumentationDataHashtable;
 
     NativeFormat::NativeHashtable   m_pMetaDataHashtable;
     NativeFormat::NativeCuckooFilter m_attributesPresence;
@@ -82,7 +83,7 @@ class ReadyToRunInfo
     PTR_PersistentInlineTrackingMapR2R m_pPersistentInlineTrackingMap;
 
 public:
-    ReadyToRunInfo(Module * pModule, PEImageLayout * pLayout, READYTORUN_HEADER * pHeader, NativeImage * pNativeImage, AllocMemTracker *pamTracker);
+    ReadyToRunInfo(Module * pModule, LoaderAllocator* pLoaderAllocator, PEImageLayout * pLayout, READYTORUN_HEADER * pHeader, NativeImage * pNativeImage, AllocMemTracker *pamTracker);
 
     static BOOL IsReadyToRunEnabled();
 
@@ -104,6 +105,7 @@ public:
     PCODE GetEntryPoint(MethodDesc * pMD, PrepareCodeConfig* pConfig, BOOL fFixups);
 
     PTR_MethodDesc GetMethodDescForEntryPoint(PCODE entryPoint);
+    bool GetPgoInstrumentationData(MethodDesc * pMD, BYTE** pAllocatedMemory, ICorJitInfo::PgoInstrumentationSchema**ppSchema, UINT *pcSchema, BYTE** pInstrumentationData);
 
     BOOL HasHashtableOfTypes();
     BOOL TryLookupTypeTokenFromName(const NameHandle *pName, mdToken * pFoundTypeToken);