Make fixup decoding lazy (#31646)
authorAndrew Au <andrewau@microsoft.com>
Mon, 3 Feb 2020 18:05:51 +0000 (10:05 -0800)
committerGitHub <noreply@github.com>
Mon, 3 Feb 2020 18:05:51 +0000 (10:05 -0800)
src/coreclr/src/tools/crossgen2/ILCompiler.Reflection.ReadyToRun/ReadyToRunMethod.cs
src/coreclr/src/tools/crossgen2/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs
src/coreclr/src/tools/r2rdump/Extensions.cs
src/coreclr/src/tools/r2rdump/TextDumper.cs

index 933186588c3f40c31896764c41af62ef65ece799..ef72d9b17328ec51b90b5ae832336f6b9c2a1ade 100644 (file)
@@ -14,6 +14,41 @@ using System.Text;
 
 namespace ILCompiler.Reflection.ReadyToRun
 {
+    /// <summary>
+    /// 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.
+    /// </summary>
+    public struct FixupCell
+    {
+        public int Index { get; set; }
+
+        /// <summary>
+        /// Zero-based index of the import table within the import tables section.
+        /// </summary>
+        public uint TableIndex;
+
+        /// <summary>
+        /// Zero-based offset of the entry in the import table; it must be a multiple
+        /// of the target architecture pointer size.
+        /// </summary>
+        public uint CellOffset;
+
+        /// <summary>
+        /// Fixup cell signature (textual representation of the typesystem object).
+        /// </summary>
+        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;
 
         /// <summary>
@@ -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<FixupCell> _fixupCells;
+        private int? _fixupOffset;
+
+        public IReadOnlyList<FixupCell> Fixups
+        {
+            get
+            {
+                EnsureFixupCells();
+                return _fixupCells;
+            }
+        }
 
         /// <summary>
         /// Extracts the method signature from the metadata by rid
         /// </summary>
         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<FixupCell>();
+            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
+        }
     }
 }
index f6485e8322fdbd6844f5f958d8dd8f1496d91bda..c02fe2986d26edc7cea26b8845fbf99d2cb6e379 100644 (file)
@@ -20,41 +20,6 @@ using Debug = System.Diagnostics.Debug;
 
 namespace ILCompiler.Reflection.ReadyToRun
 {
-    /// <summary>
-    /// 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.
-    /// </summary>
-    public struct FixupCell
-    {
-        public int Index { get; set; }
-
-        /// <summary>
-        /// Zero-based index of the import table within the import tables section.
-        /// </summary>
-        public uint TableIndex;
-
-        /// <summary>
-        /// Zero-based offset of the entry in the import table; it must be a multiple
-        /// of the target architecture pointer size.
-        /// </summary>
-        public uint CellOffset;
-
-        /// <summary>
-        /// Fixup cell signature (textual representation of the typesystem object).
-        /// </summary>
-        public string Signature;
-
-        public FixupCell(int index, uint tableIndex, uint cellOffset, string signature)
-        {
-            Index = index;
-            TableIndex = tableIndex;
-            CellOffset = cellOffset;
-            Signature = signature;
-        }
-    }
-
     /// <summary>
     /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/inc/pedecoder.h">src/inc/pedecoder.h</a> IMAGE_FILE_MACHINE_NATIVE_OS_OVERRIDE
     /// </summary>
@@ -350,7 +315,7 @@ namespace ILCompiler.Reflection.ReadyToRun
                 // initialize R2RMethods
                 ParseMethodDefEntrypoints(isEntryPoint);
                 ParseInstanceMethodEntrypoints(isEntryPoint);
-                ParseRuntimeFunctions(isEntryPoint, runtimeFunctionOffset, runtimeFunctionSize);
+                ParseRuntimeFunctions(isEntryPoint, runtimeFunctionOffset);
             }
 
             AvailableTypes = new List<string>();
@@ -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 <a href="https://github.com/dotnet/coreclr/blob/master/src/zap/zapcode.cpp">ZapUnwindInfo::Save</a>
         /// </summary>
-        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]);
         }
 
         /// <summary>
@@ -889,9 +855,9 @@ namespace ILCompiler.Reflection.ReadyToRun
         /// Reads the method entrypoint from the offset. Used for non-generic methods
         /// based on <a href="https://github.com/dotnet/coreclr/blob/master/src/debug/daccess/nidump.cpp">NativeImageDumper::DumpReadyToRunMethods</a>
         /// </summary>
-        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<FixupCell> cells = new List<FixupCell>();
-            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);
index ed8ea87555551126e0cb544c92075d9c1bf592e0..a4c65a578731ffadfc8baf184f01aa85c7d01a6f 100644 (file)
@@ -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}");
index c4646e1f7c4e894b25676a186d0e130d19d73388..d9cde7c4183cde72abb9bc6d39bcb412b5c9a732 100644 (file)
@@ -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)