| File: Compiler\DependencyAnalysis\ReadyToRun\InliningInfoNode.cs | Web Access |
| Project: src\runtime\src\coreclr\tools\aot\ILCompiler.ReadyToRun\ILCompiler.ReadyToRun.csproj (ILCompiler.ReadyToRun) |
// 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.Reflection.Metadata.Ecma335; using ILCompiler.ReadyToRun.TypeSystem; using Internal; using Internal.NativeFormat; using Internal.ReadyToRunConstants; using Internal.Text; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; namespace ILCompiler.DependencyAnalysis.ReadyToRun { /// <summary> /// Stores information about what methods got inlined into other methods. /// </summary> public class InliningInfoNode : ModuleSpecificHeaderTableNode { public enum InfoType { InliningInfo2, CrossModuleInliningForCrossModuleDataOnly, CrossModuleAllMethods } private readonly InfoType _inlineInfoType; private ReadyToRunSymbolNodeFactory _symbolNodeFactory; public InliningInfoNode(EcmaModule module, InfoType inlineInfoType) : base(module) { _inlineInfoType = inlineInfoType; if (AllowCrossModuleInlines) { Debug.Assert(module == null); // Cross module inlining always covers all modules } else { Debug.Assert(module != null); // InliningInfo2 is restricted to a single module at a time } } public void Initialize(ReadyToRunSymbolNodeFactory symbolNodeFactory) { _symbolNodeFactory = symbolNodeFactory; } protected override string ModuleSpecificName => "__ReadyToRunInliningInfoTable__"; private bool AllowCrossModuleInlines => _inlineInfoType == InfoType.CrossModuleAllMethods || _inlineInfoType == InfoType.CrossModuleInliningForCrossModuleDataOnly; private bool ReportAllInlinesInSearch => _inlineInfoType == InfoType.CrossModuleAllMethods; public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { // This node does not trigger generation of other nodes. if (relocsOnly) return new ObjectData(Array.Empty<byte>(), Array.Empty<Relocation>(), 1, new ISymbolDefinitionNode[] { this }); Dictionary<MethodDesc, HashSet<MethodDesc>> inlineeToInliners = new Dictionary<MethodDesc, HashSet<MethodDesc>>(); // Build a map from inlinee to the list of inliners // We are only interested in the generic definitions of these. foreach (MethodWithGCInfo methodNode in factory.EnumerateCompiledMethods(_module, CompiledMethodCategory.All)) { MethodDesc[] inlinees = methodNode.InlinedMethods; if (inlinees.Length == 0) { continue; } MethodDesc inliner = methodNode.Method; EcmaMethod inlinerDefinition = (EcmaMethod)inliner.GetPrimaryMethodDesc().GetTypicalMethodDefinition(); if (inlinerDefinition.IsNonVersionable()) { // Non-versionable methods don't need to be reported continue; } // Only encode inlining info for inliners within the active module, or if cross module inline format is in use Debug.Assert(AllowCrossModuleInlines || (inlinerDefinition.Module == _module)); bool inlinerReportAllVersionsWithInlinee = !AllowCrossModuleInlines || factory.CompilationModuleGroup.CrossModuleCompileable(inlinerDefinition); foreach (MethodDesc inlinee in inlinees) { MethodDesc inlineeDefinition = inlinee.GetTypicalMethodDefinition(); if (!(inlineeDefinition is EcmaMethod or AsyncMethodVariant)) { // We don't record non-ECMA methods because they don't have tokens that // diagnostic tools could reason about anyway. continue; } if (inlinee.IsNonVersionable()) { // Non-versionable methods don't need to be reported continue; } if (ReportAllInlinesInSearch) { // We'll definitely track this inline } else if (factory.CompilationModuleGroup.VersionsWithMethodBody(inlineeDefinition)) { if (!inlinerReportAllVersionsWithInlinee) { // We won't report this method continue; } } else { Debug.Assert(factory.CompilationModuleGroup.CrossModuleInlineable(inlineeDefinition)); if (_inlineInfoType != InfoType.CrossModuleInliningForCrossModuleDataOnly) { // We won't report this method continue; } } if (!inlineeToInliners.TryGetValue(inlineeDefinition, out HashSet<MethodDesc> inliners)) { inliners = new HashSet<MethodDesc>(); inlineeToInliners.Add(inlineeDefinition, inliners); } inliners.Add(inlinerDefinition); } } // Serialize the map as a hash table NativeWriter writer = new NativeWriter(); Section section = writer.NewSection(); VertexHashtable hashtable = new VertexHashtable(); section.Place(hashtable); foreach (var inlineeWithInliners in inlineeToInliners) { MethodDesc inlinee = inlineeWithInliners.Key; EcmaMethod ecmaInlinee = (EcmaMethod)inlinee.GetPrimaryMethodDesc(); int inlineeRid = MetadataTokens.GetRowNumber(ecmaInlinee.Handle); int hashCode; if (AllowCrossModuleInlines) { // CrossModuleInlineInfo format hashCode = inlinee.GetHashCode(); } else { // InliningInfo2 format hashCode = VersionResilientHashCode.ModuleNameHashCode(ecmaInlinee.Module); hashCode ^= inlineeRid; } var sig = new VertexSequence(); if (!AllowCrossModuleInlines) { // Format of the sequence: // FOR InliningInfo2 table format // Inlinee RID with flag in the lowest bit // - if flag is set, followed by module ID // Followed by inliner RIDs deltas with flag in the lowest bit // - if flag is set, followed by module ID Debug.Assert(_module != null); bool isForeignInlinee = ecmaInlinee.Module != _module; sig.Append(new UnsignedConstant((uint)(inlineeRid << 1 | (isForeignInlinee ? 1 : 0)))); if (isForeignInlinee) { sig.Append(new UnsignedConstant((uint)factory.ManifestMetadataTable.ModuleToIndex(ecmaInlinee.Module))); } // We're only concerned with metadata here, so we can convert all to EcmaMethod and lose info about AsyncVariant vs Task-Returning List<EcmaMethod> sortedInliners = new List<EcmaMethod>(inlineeWithInliners.Value.Count); foreach (var inliner in inlineeWithInliners.Value) { sortedInliners.Add((EcmaMethod)inliner.GetPrimaryMethodDesc()); } sortedInliners.MergeSort((a, b) => { if (a == b) return 0; int aRid = MetadataTokens.GetRowNumber(a.Handle); int bRid = MetadataTokens.GetRowNumber(b.Handle); if (aRid < bRid) return -1; else if (aRid > bRid) return 1; int result = a.Module.CompareTo(b.Module); Debug.Assert(result != 0); return result; }); int baseRid = 0; foreach (EcmaMethod inliner in sortedInliners) { int inlinerRid = MetadataTokens.GetRowNumber(inliner.Handle); int ridDelta = inlinerRid - baseRid; baseRid = inlinerRid; Debug.Assert(ridDelta >= 0); bool isForeignInliner = inliner.Module != _module; sig.Append(new UnsignedConstant((uint)(ridDelta << 1 | (isForeignInliner ? 1 : 0)))); if (isForeignInliner) { sig.Append(new UnsignedConstant((uint)factory.ManifestMetadataTable.ModuleToIndex(inliner.Module))); } } } else { // Format of the sequence: // FOR CrossModuleInlineInfo format // Index with 2 flags field in lowest 2 bits to define the inlinee // - If flags & 1 == 0 then index is a MethodDef RID, and if the module is a composite image, a module index of the method follows // - If flags & 1 == 1, then index is an index into the ILBody import section // - If flags & 2 == 0 then what follows is: // - Inliner RID deltas - See definition below // - if flags & 2 == 2 then what follows is: // - count of delta encoded indices into the ILBody import section // - the sequence of delta encoded indices into the ILBody import section // - Inliner RID deltas - See definition below // // Inliner RID deltas (for multi-module version bubble images (specified by the module having the READYTORUN_FLAG_MULTIMODULE_VERSION_BUBBLE flag set) // - a sequence of inliner RID deltas with flag in the lowest bit // - if flag is set, the inliner RID is followed by a module ID // - otherwise the module is the same as the module of the inlinee method // // Inliner RID deltas (for single module version bubble images) // - a sequence of inliner RID deltas bool crossModuleMultiModuleFormat = (factory.CompilationModuleGroup.GetReadyToRunFlags() & ReadyToRunFlags.READYTORUN_FLAG_MultiModuleVersionBubble) != 0; Debug.Assert(_module == null); bool isCrossModuleInlinee = !factory.CompilationModuleGroup.VersionsWithMethodBody(inlinee); Debug.Assert(!isCrossModuleInlinee || factory.CompilationModuleGroup.CrossModuleInlineable(inlinee)); EcmaMethod[] sortedInliners = new EcmaMethod[inlineeWithInliners.Value.Count]; inlineeWithInliners.Value.CopyTo(sortedInliners); sortedInliners.MergeSort((a, b) => { if (a == b) return 0; bool isCrossModuleInlinerA = !factory.CompilationModuleGroup.VersionsWithMethodBody(a); bool isCrossModuleInlinerB = !factory.CompilationModuleGroup.VersionsWithMethodBody(b); if (isCrossModuleInlinerA != isCrossModuleInlinerB) { if (isCrossModuleInlinerA) return -1; else return 1; } int result; if (isCrossModuleInlinerA) { int indexA = _symbolNodeFactory.CheckILBodyFixupSignature(a).IndexFromBeginningOfArray; int indexB = _symbolNodeFactory.CheckILBodyFixupSignature(b).IndexFromBeginningOfArray; Debug.Assert(indexA != indexB); result = indexA.CompareTo(indexB); } else { int aRid = MetadataTokens.GetRowNumber(a.Handle); int bRid = MetadataTokens.GetRowNumber(b.Handle); if (aRid < bRid) return -1; else if (aRid > bRid) return 1; result = a.Module.CompareTo(b.Module); } Debug.Assert(result != 0); return result; }); uint crossModuleInlinerCount = 0; foreach (var method in sortedInliners) { if (factory.CompilationModuleGroup.VersionsWithMethodBody(method)) break; Debug.Assert(factory.CompilationModuleGroup.CrossModuleInlineable(method)); crossModuleInlinerCount++; } uint encodedInlinee; checked { uint indexOfInlinee; if (isCrossModuleInlinee) { indexOfInlinee = (uint)_symbolNodeFactory.CheckILBodyFixupSignature(inlinee).IndexFromBeginningOfArray; } else { indexOfInlinee = (uint)MetadataTokens.GetRowNumber(ecmaInlinee.Handle); } encodedInlinee = indexOfInlinee << (int)ReadyToRunCrossModuleInlineFlags.CrossModuleInlinerIndexShift; if (isCrossModuleInlinee) encodedInlinee |= (uint)ReadyToRunCrossModuleInlineFlags.CrossModuleInlinee; if (crossModuleInlinerCount > 0) encodedInlinee |= (uint)ReadyToRunCrossModuleInlineFlags.HasCrossModuleInliners; sig.Append(new UnsignedConstant(encodedInlinee)); if (crossModuleMultiModuleFormat && !isCrossModuleInlinee) sig.Append(new UnsignedConstant((uint)factory.ManifestMetadataTable.ModuleToIndex(ecmaInlinee.Module))); int inlinerIndex = 0; if (crossModuleInlinerCount > 0) { sig.Append(new UnsignedConstant(crossModuleInlinerCount)); uint baseIndex = 0; for (; inlinerIndex < crossModuleInlinerCount; inlinerIndex++) { var inliner = sortedInliners[inlinerIndex]; uint ilBodyIndex = (uint)_symbolNodeFactory.CheckILBodyFixupSignature(inliner).IndexFromBeginningOfArray; uint ridDelta = ilBodyIndex - baseIndex; sig.Append(new UnsignedConstant(ridDelta)); } } uint baseRid = 0; for (; inlinerIndex < sortedInliners.Length; inlinerIndex++) { var inliner = sortedInliners[inlinerIndex]; uint inlinerRid = (uint)MetadataTokens.GetRowNumber(inliner.Handle); uint ridDelta = inlinerRid - baseRid; baseRid = inlinerRid; bool isForeignInliner = inliner.Module != ecmaInlinee.Module; Debug.Assert(!isForeignInliner || crossModuleMultiModuleFormat); if (crossModuleMultiModuleFormat) { uint encodedRid = ridDelta << (int)ReadyToRunCrossModuleInlineFlags.InlinerRidShift; if (isForeignInliner) encodedRid |= (uint)ReadyToRunCrossModuleInlineFlags.InlinerRidHasModule; sig.Append(new UnsignedConstant(encodedRid)); if (isForeignInliner) { sig.Append(new UnsignedConstant((uint)factory.ManifestMetadataTable.ModuleToIndex(inliner.Module))); } } else { sig.Append(new UnsignedConstant(ridDelta)); } } } } hashtable.Append((uint)hashCode, section.Place(sig)); } MemoryStream writerContent = new MemoryStream(); writer.Save(writerContent); return new ObjectData( data: writerContent.ToArray(), relocs: null, alignment: 8, definedSymbols: new ISymbolDefinitionNode[] { this }); } public override int ClassCode => -87382891; } }