| File: Compiler\DependencyAnalysis\ReadyToRun\MethodWithGCInfo.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.Buffers.Binary; using System.Linq; using Internal.JitInterface; using Internal.Pgo; using Internal.Text; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; namespace ILCompiler.DependencyAnalysis.ReadyToRun { public class MethodWithGCInfo : ObjectNode, IMethodBodyNode, INodeWithFunclets, IMethodCodeNodeWithTypeSignature { public readonly MethodGCInfoNode GCInfoNode; private readonly MethodDesc _method; private ObjectData _methodCode; private FrameInfo[] _frameInfos; private FrameInfo[] _coldFrameInfos; private byte[] _gcInfo; private ObjectData _ehInfo; private byte[] _debugLocInfos; private byte[] _debugVarInfos; private DebugEHClauseInfo[] _debugEHClauseInfos; private List<ISymbolNode> _fixups; private MethodDesc[] _inlinedMethods; private bool _lateTriggeredCompilation; private DependencyList _nonRelocationDependencies; public MethodWithGCInfo(MethodDesc methodDesc) { Debug.Assert(!methodDesc.IsUnboxingThunk()); GCInfoNode = new MethodGCInfoNode(this); _fixups = new List<ISymbolNode>(); _method = methodDesc; } protected override void OnMarked(NodeFactory context) { // Once past phase 1, no new methods which are interesting for compilation may be marked except for methods // specially enabled for higher phases if (context.CompilationCurrentPhase > 1) { SetCode(new ObjectNode.ObjectData(Array.Empty<byte>(), null, 1, Array.Empty<ISymbolDefinitionNode>())); InitializeFrameInfos(Array.Empty<FrameInfo>()); InitializeColdFrameInfos(Array.Empty<FrameInfo>()); } _lateTriggeredCompilation = context.CompilationCurrentPhase != 0; RegisterInlineeModuleIndices(context); } private void RegisterInlineeModuleIndices(NodeFactory factory) { if (_inlinedMethods != null) { foreach (var inlinee in _inlinedMethods) { MethodDesc inlineeDefinition = inlinee.GetTypicalMethodDefinition(); if (!(inlineeDefinition is EcmaMethod ecmaInlineeDefinition)) { // We don't record non-ECMA methods because they don't have tokens that // diagnostic tools could reason about anyway. continue; } if (!factory.CompilationModuleGroup.VersionsWithMethodBody(inlinee) && !factory.CompilationModuleGroup.CrossModuleInlineable(inlinee)) { // We cannot record inlining info across version bubble as cross-bubble assemblies // are not guaranteed to preserve token values unless CrossModule inlining is in place // Otherwise non-versionable methods may be inlined across the version bubble. Debug.Assert(inlinee.IsNonVersionable()); continue; } factory.ManifestMetadataTable.EnsureModuleIndexable(ecmaInlineeDefinition.Module); } } } public override int DependencyPhaseForDeferredStaticComputation => _lateTriggeredCompilation ? 2 : 0; public void SetCode(ObjectData data) { Debug.Assert(_methodCode == null); _methodCode = data; } public MethodDesc Method => _method; public List<ISymbolNode> Fixups => _fixups; public int Size => _methodCode.Data.Length; public bool IsEmpty => _methodCode.Data.Length == 0; public override ObjectData GetData(NodeFactory factory, bool relocsOnly) { return _methodCode; } /// <summary> /// This helper structure represents the "coordinates" of a single /// indirection cell in the import tables (index of the import /// section table and offset within the table). /// </summary> private struct FixupCell { public static readonly IComparer<FixupCell> Comparer = new CellComparer(); public int TableIndex; public int ImportOffset; public FixupCell(int tableIndex, int importOffset) { TableIndex = tableIndex; ImportOffset = importOffset; } private class CellComparer : IComparer<FixupCell> { public int Compare(FixupCell a, FixupCell b) { int result = a.TableIndex.CompareTo(b.TableIndex); if (result == 0) { result = a.ImportOffset.CompareTo(b.ImportOffset); } return result; } } } public MethodColdCodeNode ColdCodeNode { get; set; } public byte[] GetFixupBlob(NodeFactory factory) { Relocation[] relocations = GetData(factory, relocsOnly: true).Relocs; if (ColdCodeNode != null) { Relocation[] coldRelocations = ColdCodeNode.GetData(factory, relocsOnly: true).Relocs; if (relocations == null) { relocations = coldRelocations; } else if (coldRelocations != null) { relocations = Enumerable.Concat(relocations, coldRelocations).ToArray(); } } if (relocations == null) { return null; } List<FixupCell> fixupCells = null; foreach (Relocation reloc in relocations) { if (reloc.Target is Import fixupCell && fixupCell.EmitPrecode) { if (fixupCells == null) { fixupCells = new List<FixupCell>(); } fixupCells.Add(new FixupCell(fixupCell.Table.IndexFromBeginningOfArray, fixupCell.OffsetFromBeginningOfArray)); } } foreach (ISymbolNode node in _fixups) { if (fixupCells == null) { fixupCells = new List<FixupCell>(); } Import fixupCell = (Import)node; fixupCells.Add(new FixupCell(fixupCell.Table.IndexFromBeginningOfArray, fixupCell.OffsetFromBeginningOfArray)); } if (fixupCells == null) { return null; } fixupCells.MergeSortAllowDuplicates(FixupCell.Comparer); // Deduplicate fixupCells int j = 0; for (int i = 1; i < fixupCells.Count; i++) { if (FixupCell.Comparer.Compare(fixupCells[j], fixupCells[i]) != 0) { j++; if (i != j) { fixupCells[j] = fixupCells[i]; } } } // Move j to point after the last valid fixupCell in the array j++; if (j < fixupCells.Count) { fixupCells.RemoveRange(j, fixupCells.Count - j); } NibbleWriter writer = new NibbleWriter(); int curTableIndex = -1; int curOffset = 0; foreach (FixupCell cell in fixupCells) { Debug.Assert(cell.ImportOffset % factory.Target.PointerSize == 0); int offset = cell.ImportOffset / factory.Target.PointerSize; if (cell.TableIndex != curTableIndex) { // Write delta relative to the previous table index Debug.Assert(cell.TableIndex > curTableIndex); if (curTableIndex != -1) { writer.WriteUInt(0); // table separator, so add except for the first entry writer.WriteUInt((uint)(cell.TableIndex - curTableIndex)); // add table index delta } else { writer.WriteUInt((uint)cell.TableIndex); } curTableIndex = cell.TableIndex; // This is the first fixup in the current table. // We will write it out completely (without delta-encoding) writer.WriteUInt((uint)offset); } else if (offset != curOffset) // ignore duplicate fixup cells { // This is not the first entry in the current table. // We will write out the delta relative to the previous fixup value int delta = offset - curOffset; Debug.Assert(delta > 0); writer.WriteUInt((uint)delta); } // future entries for this table would be relative to this rva curOffset = offset; } writer.WriteUInt(0); // table separator writer.WriteUInt(0); // fixup list ends return writer.ToArray(); } protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) { DependencyList dependencyList = new DependencyList(new DependencyListEntry[] { new DependencyListEntry(GCInfoNode, "Unwind & GC info") }); if (this.ColdCodeNode != null) { dependencyList.Add(this.ColdCodeNode, "cold"); } foreach (ISymbolNode node in _fixups) { dependencyList.Add(node, "classMustBeLoadedBeforeCodeIsRun"); } if (_nonRelocationDependencies != null) { dependencyList.AddRange(_nonRelocationDependencies); } return dependencyList; } public override bool StaticDependenciesAreComputed => _methodCode != null; public virtual void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append(nameMangler.GetMangledMethodName(_method)); } protected override string GetName(NodeFactory factory) { Utf8StringBuilder sb = new Utf8StringBuilder(); sb.Append("MethodWithGCInfo("u8); AppendMangledName(factory.NameMangler, sb); sb.Append(")"u8); return sb.ToString(); } public override int ClassCode => 315213488; public override ObjectNodeSection GetSection(NodeFactory factory) { return factory.Format switch { ReadyToRunContainerFormat.PE => ObjectNodeSection.ManagedCodeWindowsContentSection, ReadyToRunContainerFormat.Wasm => ObjectNodeSection.WasmCodeSection, _ => ObjectNodeSection.ManagedCodeUnixContentSection }; } public FrameInfo[] FrameInfos => _frameInfos; public FrameInfo[] ColdFrameInfos => _coldFrameInfos; public byte[] GCInfo => _gcInfo; public ObjectData EHInfo => _ehInfo; public MethodDesc[] InlinedMethods => _inlinedMethods; public void InitializeFrameInfos(FrameInfo[] frameInfos) { Debug.Assert(_frameInfos == null); if (frameInfos != null) { _frameInfos = frameInfos; } else { // On x86, fake a single frame info representing the entire method _frameInfos = new FrameInfo[] { new FrameInfo((FrameInfoFlags)0, startOffset: 0, endOffset: 0, blobData: Array.Empty<byte>()) }; } } public void InitializeColdFrameInfos(FrameInfo[] coldFrameInfos) { Debug.Assert(_coldFrameInfos == null); _coldFrameInfos = coldFrameInfos; // TODO: x86 (see InitializeFrameInfos()) } public void InitializeGCInfo(byte[] gcInfo) { Debug.Assert(_gcInfo == null); _gcInfo = gcInfo; } public void InitializeEHInfo(ObjectData ehInfo) { Debug.Assert(_ehInfo == null); _ehInfo = ehInfo; } public byte[] DebugLocInfos => _debugLocInfos; public byte[] DebugVarInfos => _debugVarInfos; public DebugEHClauseInfo[] DebugEHClauseInfos => _debugEHClauseInfos; public void InitializeDebugLocInfos(OffsetMapping[] debugLocInfos) { Debug.Assert(_debugLocInfos == null); // Process the debug info from JIT format to R2R format immediately as it is large // and not used in the rest of the process except to emit. _debugLocInfos = DebugInfoTableNode.CreateBoundsBlobForMethod(debugLocInfos); } public void InitializeDebugVarInfos(NativeVarInfo[] debugVarInfos) { Debug.Assert(_debugVarInfos == null); // Process the debug info from JIT format to R2R format immediately as it is large // and not used in the rest of the process except to emit. _debugVarInfos = DebugInfoTableNode.CreateVarBlobForMethod(debugVarInfos, _method.Context.Target); } public void InitializeDebugEHClauseInfos(DebugEHClauseInfo[] debugEHClauseInfos) { Debug.Assert(_debugEHClauseInfos == null); _debugEHClauseInfos = debugEHClauseInfos; } public FuncletKind[] GetFuncletKinds() { // EH Clause structure contains 6 uint sized fields const int ClauseSize = 6 * sizeof(uint); const int FlagsFieldOffset = 0 * sizeof(uint); if (_ehInfo?.Data is not { Length: > 0 } data) return Array.Empty<FuncletKind>(); int clauseCount = data.Length / ClauseSize; ArrayBuilder<FuncletKind> funcletKinds = new ArrayBuilder<FuncletKind>(clauseCount); for (int i = 0; i < clauseCount; i++) { int baseOffset = i * ClauseSize; CORINFO_EH_CLAUSE_FLAGS flags = (CORINFO_EH_CLAUSE_FLAGS)BinaryPrimitives.ReadUInt32LittleEndian( data.AsSpan(baseOffset + FlagsFieldOffset)); if (flags.HasFlag(CORINFO_EH_CLAUSE_FLAGS.CORINFO_EH_CLAUSE_FINALLY)) { funcletKinds.Add(FuncletKind.Finally); } else if (flags.HasFlag(CORINFO_EH_CLAUSE_FLAGS.CORINFO_EH_CLAUSE_FAULT)) { funcletKinds.Add(FuncletKind.Fault); } else if (flags.HasFlag(CORINFO_EH_CLAUSE_FLAGS.CORINFO_EH_CLAUSE_FILTER)) { // Filters inspire two funclets: a filter funclet and a catch-like handler funclet funcletKinds.Add(FuncletKind.Filter); funcletKinds.Add(FuncletKind.CatchOrFilterHandler); } else { funcletKinds.Add(FuncletKind.CatchOrFilterHandler); } } return funcletKinds.ToArray(); } public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) { MethodWithGCInfo otherNode = (MethodWithGCInfo)other; MethodDesc methodLayoutAnchor = GetMethodLayoutAnchor(_method, out int methodLayoutRank); MethodDesc otherLayoutAnchor = GetMethodLayoutAnchor(otherNode._method, out int otherLayoutRank); int result = comparer.Compare(methodLayoutAnchor, otherLayoutAnchor); if (result != 0) { return result; } result = methodLayoutRank.CompareTo(otherLayoutRank); if (result != 0) { return result; } return comparer.Compare(_method, otherNode._method); static MethodDesc GetMethodLayoutAnchor(MethodDesc method, out int layoutRank) { if (method is AsyncResumptionStub resumptionStub) { Debug.Assert(resumptionStub.TargetMethod is not AsyncResumptionStub); layoutRank = 1; return resumptionStub.TargetMethod; } layoutRank = 0; return method; } } public void InitializeInliningInfo(MethodDesc[] inlinedMethods, NodeFactory factory) { Debug.Assert(_inlinedMethods == null); _inlinedMethods = inlinedMethods; if (this.Marked) RegisterInlineeModuleIndices(factory); } public void InitializeNonRelocationDependencies(DependencyList dependencies) { _nonRelocationDependencies = dependencies; } public int Offset => 0; public override bool IsShareable => throw new NotImplementedException(); public bool AsyncVariant => _method.IsAsyncVariant(); public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => IsEmpty; public override string ToString() => _method.ToString(); } }