|
// 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.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, 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 override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
{
MethodWithGCInfo otherNode = (MethodWithGCInfo)other;
return comparer.Compare(_method, otherNode._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();
}
}
|