From a52bd66588b3386d8e983d34efe68f556c421b59 Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Mon, 3 Feb 2020 10:05:51 -0800 Subject: [PATCH] Make fixup decoding lazy (#31646) --- .../ReadyToRunMethod.cs | 118 +++++++++- .../ReadyToRunReader.cs | 214 ++++++------------ src/coreclr/src/tools/r2rdump/Extensions.cs | 6 - src/coreclr/src/tools/r2rdump/TextDumper.cs | 2 +- 4 files changed, 181 insertions(+), 159 deletions(-) diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.Reflection.ReadyToRun/ReadyToRunMethod.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.Reflection.ReadyToRun/ReadyToRunMethod.cs index 933186588c3..ef72d9b1732 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.Reflection.ReadyToRun/ReadyToRunMethod.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.Reflection.ReadyToRun/ReadyToRunMethod.cs @@ -14,6 +14,41 @@ using System.Text; namespace ILCompiler.Reflection.ReadyToRun { + /// + /// This structure represents a single precode fixup cell decoded from the + /// nibble-oriented per-method fixup blob. Each method entrypoint fixup + /// represents an array of cells that must be fixed up before the method + /// can start executing. + /// + public struct FixupCell + { + public int Index { get; set; } + + /// + /// Zero-based index of the import table within the import tables section. + /// + public uint TableIndex; + + /// + /// Zero-based offset of the entry in the import table; it must be a multiple + /// of the target architecture pointer size. + /// + public uint CellOffset; + + /// + /// Fixup cell signature (textual representation of the typesystem object). + /// + public string Signature; + + public FixupCell(int index, uint tableIndex, uint cellOffset, string signature) + { + Index = index; + TableIndex = tableIndex; + CellOffset = cellOffset; + Signature = signature; + } + } + public abstract class BaseUnwindInfo { public int Size { get; set; } @@ -51,6 +86,7 @@ namespace ILCompiler.Reflection.ReadyToRun public class RuntimeFunction { private ReadyToRunReader _readyToRunReader; + private EHInfo _ehInfo; private DebugInfo _debugInfo; /// @@ -89,7 +125,17 @@ namespace ILCompiler.Reflection.ReadyToRun public BaseUnwindInfo UnwindInfo { get; } - public EHInfo EHInfo { get; } + public EHInfo EHInfo + { + get + { + if (_ehInfo == null) + { + _readyToRunReader.RuntimeFunctionToEHInfo.TryGetValue(StartAddress, out _ehInfo); + } + return _ehInfo; + } + } public DebugInfo DebugInfo { @@ -112,8 +158,7 @@ namespace ILCompiler.Reflection.ReadyToRun int codeOffset, ReadyToRunMethod method, BaseUnwindInfo unwindInfo, - BaseGcInfo gcInfo, - EHInfo ehInfo) + BaseGcInfo gcInfo) { _readyToRunReader = readyToRunReader; Id = id; @@ -148,7 +193,6 @@ namespace ILCompiler.Reflection.ReadyToRun } CodeOffset = codeOffset; method.GcInfo = gcInfo; - EHInfo = ehInfo; } } @@ -205,12 +249,24 @@ namespace ILCompiler.Reflection.ReadyToRun public BaseGcInfo GcInfo { get; set; } - public FixupCell[] Fixups { get; set; } + private ReadyToRunReader _readyToRunReader; + private List _fixupCells; + private int? _fixupOffset; + + public IReadOnlyList Fixups + { + get + { + EnsureFixupCells(); + return _fixupCells; + } + } /// /// Extracts the method signature from the metadata by rid /// public ReadyToRunMethod( + ReadyToRunReader readyToRunReader, int index, MetadataReader metadataReader, EntityHandle methodHandle, @@ -218,8 +274,10 @@ namespace ILCompiler.Reflection.ReadyToRun string owningType, string constrainedType, string[] instanceArgs, - FixupCell[] fixups) + int? fixupOffset) { + _readyToRunReader = readyToRunReader; + _fixupOffset = fixupOffset; Index = index; MethodHandle = methodHandle; EntryPointRuntimeFunctionId = entryPointId; @@ -268,8 +326,6 @@ namespace ILCompiler.Reflection.ReadyToRun DeclaringType = MetadataNameFormatter.FormatHandle(MetadataReader, owningTypeHandle); } - Fixups = fixups; - StringBuilder sb = new StringBuilder(); sb.Append(Signature.ReturnType); sb.Append(" "); @@ -312,5 +368,51 @@ namespace ILCompiler.Reflection.ReadyToRun SignatureString = sb.ToString(); } + + private void EnsureFixupCells() + { + if (_fixupCells != null) + { + return; + } + if (!_fixupOffset.HasValue) + { + return; + } + _fixupCells = new List(); + NibbleReader reader = new NibbleReader(_readyToRunReader.Image, _fixupOffset.Value); + + // The following algorithm has been loosely ported from CoreCLR, + // src\vm\ceeload.inl, BOOL Module::FixupDelayListAux + uint curTableIndex = reader.ReadUInt(); + + while (true) + { + uint fixupIndex = reader.ReadUInt(); // Accumulate the real rva from the delta encoded rva + + while (true) + { + ReadyToRunImportSection importSection = _readyToRunReader.ImportSections[(int)curTableIndex]; + ReadyToRunImportSection.ImportSectionEntry entry = importSection.Entries[(int)fixupIndex]; + _fixupCells.Add(new FixupCell(_fixupCells.Count, curTableIndex, fixupIndex, entry.Signature)); + + uint delta = reader.ReadUInt(); + + // Delta of 0 means end of entries in this table + if (delta == 0) + break; + + fixupIndex += delta; + } + + uint tableIndex = reader.ReadUInt(); + + if (tableIndex == 0) + break; + + curTableIndex = curTableIndex + tableIndex; + + } // Done with all entries in this table + } } } diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs index f6485e8322f..c02fe2986d2 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs @@ -20,41 +20,6 @@ using Debug = System.Diagnostics.Debug; namespace ILCompiler.Reflection.ReadyToRun { - /// - /// This structure represents a single precode fixup cell decoded from the - /// nibble-oriented per-method fixup blob. Each method entrypoint fixup - /// represents an array of cells that must be fixed up before the method - /// can start executing. - /// - public struct FixupCell - { - public int Index { get; set; } - - /// - /// Zero-based index of the import table within the import tables section. - /// - public uint TableIndex; - - /// - /// Zero-based offset of the entry in the import table; it must be a multiple - /// of the target architecture pointer size. - /// - public uint CellOffset; - - /// - /// Fixup cell signature (textual representation of the typesystem object). - /// - public string Signature; - - public FixupCell(int index, uint tableIndex, uint cellOffset, string signature) - { - Index = index; - TableIndex = tableIndex; - CellOffset = cellOffset; - Signature = signature; - } - } - /// /// based on src/inc/pedecoder.h IMAGE_FILE_MACHINE_NATIVE_OS_OVERRIDE /// @@ -350,7 +315,7 @@ namespace ILCompiler.Reflection.ReadyToRun // initialize R2RMethods ParseMethodDefEntrypoints(isEntryPoint); ParseInstanceMethodEntrypoints(isEntryPoint); - ParseRuntimeFunctions(isEntryPoint, runtimeFunctionOffset, runtimeFunctionSize); + ParseRuntimeFunctions(isEntryPoint, runtimeFunctionOffset); } AvailableTypes = new List(); @@ -543,9 +508,9 @@ namespace ILCompiler.Reflection.ReadyToRun { EntityHandle methodHandle = MetadataTokens.MethodDefinitionHandle((int)rid); int runtimeFunctionId; - FixupCell[] fixups; - GetRuntimeFunctionIndexFromOffset(offset, out runtimeFunctionId, out fixups); - ReadyToRunMethod method = new ReadyToRunMethod(Methods.Count, this.MetadataReader, methodHandle, runtimeFunctionId, owningType: null, constrainedType: null, instanceArgs: null, fixups: fixups); + int? fixupOffset; + GetRuntimeFunctionIndexFromOffset(offset, out runtimeFunctionId, out fixupOffset); + ReadyToRunMethod method = new ReadyToRunMethod(this, Methods.Count, this.MetadataReader, methodHandle, runtimeFunctionId, owningType: null, constrainedType: null, instanceArgs: null, fixupOffset: fixupOffset); if (method.EntryPointRuntimeFunctionId < 0 || method.EntryPointRuntimeFunctionId >= isEntryPoint.Length) { @@ -617,9 +582,10 @@ namespace ILCompiler.Reflection.ReadyToRun } int runtimeFunctionId; - FixupCell[] fixups; - GetRuntimeFunctionIndexFromOffset((int)decoder.Offset, out runtimeFunctionId, out fixups); + int? fixupOffset; + GetRuntimeFunctionIndexFromOffset((int)decoder.Offset, out runtimeFunctionId, out fixupOffset); ReadyToRunMethod method = new ReadyToRunMethod( + this, Methods.Count, mdReader == null ? MetadataReader : mdReader, methodHandle, @@ -627,7 +593,7 @@ namespace ILCompiler.Reflection.ReadyToRun owningType, constrainedType, methodTypeArgs, - fixups); + fixupOffset); if (method.EntryPointRuntimeFunctionId >= 0 && method.EntryPointRuntimeFunctionId < isEntryPoint.Length) { isEntryPoint[method.EntryPointRuntimeFunctionId] = true; @@ -642,83 +608,83 @@ namespace ILCompiler.Reflection.ReadyToRun /// Get the RVAs of the runtime functions for each method /// based on ZapUnwindInfo::Save /// - private void ParseRuntimeFunctions(bool[] isEntryPoint, int runtimeFunctionOffset, int runtimeFunctionSize) + private void ParseRuntimeFunctions(bool[] isEntryPoint, int runtimeFunctionOffset) { - int curOffset = 0; foreach (ReadyToRunMethod method in Methods) { int runtimeFunctionId = method.EntryPointRuntimeFunctionId; if (runtimeFunctionId == -1) continue; - curOffset = runtimeFunctionOffset + runtimeFunctionId * runtimeFunctionSize; - BaseGcInfo gcInfo = null; - int codeOffset = 0; - do + int runtimeFunctionSize = CalculateRuntimeFunctionSize(); + ParseRuntimeFunctionsForMethod(isEntryPoint, runtimeFunctionOffset + runtimeFunctionId * runtimeFunctionSize, method, runtimeFunctionId); + } + } + + private void ParseRuntimeFunctionsForMethod(bool[] isEntryPoint, int curOffset, ReadyToRunMethod method, int runtimeFunctionId) + { + BaseGcInfo gcInfo = null; + int codeOffset = 0; + do + { + int startRva = NativeReader.ReadInt32(Image, ref curOffset); + int endRva = -1; + if (Machine == Machine.Amd64) { - int startRva = NativeReader.ReadInt32(Image, ref curOffset); - int endRva = -1; - if (Machine == Machine.Amd64) - { - endRva = NativeReader.ReadInt32(Image, ref curOffset); - } - int unwindRva = NativeReader.ReadInt32(Image, ref curOffset); - int unwindOffset = GetOffset(unwindRva); + endRva = NativeReader.ReadInt32(Image, ref curOffset); + } + int unwindRva = NativeReader.ReadInt32(Image, ref curOffset); + int unwindOffset = GetOffset(unwindRva); - BaseUnwindInfo unwindInfo = null; - if (Machine == Machine.Amd64) + BaseUnwindInfo unwindInfo = null; + if (Machine == Machine.Amd64) + { + unwindInfo = new Amd64.UnwindInfo(Image, unwindOffset); + if (isEntryPoint[runtimeFunctionId]) { - unwindInfo = new Amd64.UnwindInfo(Image, unwindOffset); - if (isEntryPoint[runtimeFunctionId]) - { - gcInfo = new Amd64.GcInfo(Image, unwindOffset + unwindInfo.Size, Machine, ReadyToRunHeader.MajorVersion); - } + gcInfo = new Amd64.GcInfo(Image, unwindOffset + unwindInfo.Size, Machine, ReadyToRunHeader.MajorVersion); } - else if (Machine == Machine.I386) + } + else if (Machine == Machine.I386) + { + unwindInfo = new x86.UnwindInfo(Image, unwindOffset); + if (isEntryPoint[runtimeFunctionId]) { - unwindInfo = new x86.UnwindInfo(Image, unwindOffset); - if (isEntryPoint[runtimeFunctionId]) - { - gcInfo = new x86.GcInfo(Image, unwindOffset, Machine, ReadyToRunHeader.MajorVersion); - } + gcInfo = new x86.GcInfo(Image, unwindOffset, Machine, ReadyToRunHeader.MajorVersion); } - else if (Machine == Machine.ArmThumb2) + } + else if (Machine == Machine.ArmThumb2) + { + unwindInfo = new Arm.UnwindInfo(Image, unwindOffset); + if (isEntryPoint[runtimeFunctionId]) { - unwindInfo = new Arm.UnwindInfo(Image, unwindOffset); - if (isEntryPoint[runtimeFunctionId]) - { - gcInfo = new Amd64.GcInfo(Image, unwindOffset + unwindInfo.Size, Machine, ReadyToRunHeader.MajorVersion); // Arm and Arm64 use the same GcInfo format as x64 - } + gcInfo = new Amd64.GcInfo(Image, unwindOffset + unwindInfo.Size, Machine, ReadyToRunHeader.MajorVersion); // Arm and Arm64 use the same GcInfo format as x64 } - else if (Machine == Machine.Arm64) + } + else if (Machine == Machine.Arm64) + { + unwindInfo = new Arm64.UnwindInfo(Image, unwindOffset); + if (isEntryPoint[runtimeFunctionId]) { - unwindInfo = new Arm64.UnwindInfo(Image, unwindOffset); - if (isEntryPoint[runtimeFunctionId]) - { - gcInfo = new Amd64.GcInfo(Image, unwindOffset + unwindInfo.Size, Machine, ReadyToRunHeader.MajorVersion); - } + gcInfo = new Amd64.GcInfo(Image, unwindOffset + unwindInfo.Size, Machine, ReadyToRunHeader.MajorVersion); } - - EHInfo ehInfo = null; - RuntimeFunctionToEHInfo.TryGetValue(startRva, out ehInfo); - - RuntimeFunction rtf = new RuntimeFunction( - this, - runtimeFunctionId, - startRva, - endRva, - unwindRva, - codeOffset, - method, - unwindInfo, - gcInfo, - ehInfo); - - method.RuntimeFunctions.Add(rtf); - runtimeFunctionId++; - codeOffset += rtf.Size; } - while (runtimeFunctionId < isEntryPoint.Length && !isEntryPoint[runtimeFunctionId]); + + RuntimeFunction rtf = new RuntimeFunction( + this, + runtimeFunctionId, + startRva, + endRva, + unwindRva, + codeOffset, + method, + unwindInfo, + gcInfo); + + method.RuntimeFunctions.Add(rtf); + runtimeFunctionId++; + codeOffset += rtf.Size; } + while (runtimeFunctionId < isEntryPoint.Length && !isEntryPoint[runtimeFunctionId]); } /// @@ -889,9 +855,9 @@ namespace ILCompiler.Reflection.ReadyToRun /// Reads the method entrypoint from the offset. Used for non-generic methods /// based on NativeImageDumper::DumpReadyToRunMethods /// - private void GetRuntimeFunctionIndexFromOffset(int offset, out int runtimeFunctionIndex, out FixupCell[] fixupCells) + private void GetRuntimeFunctionIndexFromOffset(int offset, out int runtimeFunctionIndex, out int? fixupOffset) { - fixupCells = null; + fixupOffset = null; // get the id of the entry point runtime function from the MethodEntryPoints NativeArray uint id = 0; // the RUNTIME_FUNCTIONS index @@ -905,7 +871,7 @@ namespace ILCompiler.Reflection.ReadyToRun offset -= (int)val; } - fixupCells = DecodeFixupCells(offset); + fixupOffset = offset; id >>= 2; } @@ -917,46 +883,6 @@ namespace ILCompiler.Reflection.ReadyToRun runtimeFunctionIndex = (int)id; } - private FixupCell[] DecodeFixupCells(int offset) - { - List cells = new List(); - NibbleReader reader = new NibbleReader(Image, offset); - - // The following algorithm has been loosely ported from CoreCLR, - // src\vm\ceeload.inl, BOOL Module::FixupDelayListAux - uint curTableIndex = reader.ReadUInt(); - - while (true) - { - uint fixupIndex = reader.ReadUInt(); // Accumulate the real rva from the delta encoded rva - - while (true) - { - ReadyToRunImportSection importSection = ImportSections[(int)curTableIndex]; - ReadyToRunImportSection.ImportSectionEntry entry = importSection.Entries[(int)fixupIndex]; - cells.Add(new FixupCell(cells.Count, curTableIndex, fixupIndex, entry.Signature)); - - uint delta = reader.ReadUInt(); - - // Delta of 0 means end of entries in this table - if (delta == 0) - break; - - fixupIndex += delta; - } - - uint tableIndex = reader.ReadUInt(); - - if (tableIndex == 0) - break; - - curTableIndex = curTableIndex + tableIndex; - - } // Done with all entries in this table - - return cells.ToArray(); - } - private AssemblyReferenceHandle GetAssemblyAtIndex(int refAsmIndex, out MetadataReader metadataReader) { Debug.Assert(refAsmIndex != 0); diff --git a/src/coreclr/src/tools/r2rdump/Extensions.cs b/src/coreclr/src/tools/r2rdump/Extensions.cs index ed8ea875555..a4c65a57873 100644 --- a/src/coreclr/src/tools/r2rdump/Extensions.cs +++ b/src/coreclr/src/tools/r2rdump/Extensions.cs @@ -214,12 +214,6 @@ namespace R2RDump } writer.WriteLine(); - if (theThis.Method.GcInfo is ILCompiler.Reflection.ReadyToRun.Amd64.GcInfo gcInfo) - { - writer.WriteLine("GC info:"); - writer.WriteLine(gcInfo.ToString()); - } - if (theThis.EHInfo != null) { writer.WriteLine($@"EH info @ {theThis.EHInfo.RelativeVirtualAddress:X4}, #clauses = {theThis.EHInfo.EHClauses.Count}"); diff --git a/src/coreclr/src/tools/r2rdump/TextDumper.cs b/src/coreclr/src/tools/r2rdump/TextDumper.cs index c4646e1f7c4..d9cde7c4183 100644 --- a/src/coreclr/src/tools/r2rdump/TextDumper.cs +++ b/src/coreclr/src/tools/r2rdump/TextDumper.cs @@ -138,7 +138,7 @@ namespace R2RDump if (_options.GC && method.GcInfo != null) { - _writer.WriteLine("GcInfo:"); + _writer.WriteLine("GC info:"); _writer.Write(method.GcInfo); if (_options.Raw) -- 2.34.1