From 204d46a155406543922ebe82906f5caf65996466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 10 Nov 2022 20:10:55 +0900 Subject: [PATCH 1/5] wip --- .../CompilerHelpers/StartupCodeHelpers.cs | 64 ++++- .../src/System.Private.CoreLib.csproj | 3 + .../Test.CoreLib/src/Test.CoreLib.csproj | 3 + .../DependencyAnalysis/ObjectNodeSection.cs | 4 +- .../Compiler/DependencyAnalysis/Relocation.cs | 11 + .../SortableDependencyNode.cs | 4 +- .../Common/Internal/Runtime/DehydratedData.cs | 116 +++++++++ .../Common/Internal/Runtime/ModuleHeaders.cs | 2 +- .../Compiler/CompilationBuilder.Aot.cs | 7 + .../DehydratableObjectNode.cs | 29 +++ .../DependencyAnalysis/DehydratedDataNode.cs | 242 ++++++++++++++++++ .../Compiler/DependencyAnalysis/EETypeNode.cs | 6 +- .../GenericDefinitionEETypeNode.cs | 2 +- .../DependencyAnalysis/ObjectWriter.cs | 4 + .../Compiler/MetadataManager.cs | 34 +++ .../ILCompiler.Compiler.csproj | 5 + .../aot/ILCompiler/ILCompilerRootCommand.cs | 3 + 17 files changed, 529 insertions(+), 10 deletions(-) create mode 100644 src/coreclr/tools/Common/Internal/Runtime/DehydratedData.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DehydratableObjectNode.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DehydratedDataNode.cs diff --git a/src/coreclr/nativeaot/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs b/src/coreclr/nativeaot/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs index afd0348c047b..eab095b2a119 100644 --- a/src/coreclr/nativeaot/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs +++ b/src/coreclr/nativeaot/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs @@ -85,17 +85,32 @@ private static unsafe TypeManagerHandle[] CreateTypeManagers(IntPtr osModule, In moduleCount++; } - TypeManagerHandle[] modules = new TypeManagerHandle[moduleCount]; + // We cannot use the new keyword just yet, so stackalloc the array first + TypeManagerHandle* pHandles = stackalloc TypeManagerHandle[moduleCount]; int moduleIndex = 0; for (int i = 0; i < count; i++) { if (pModuleHeaders[i] != IntPtr.Zero) { - modules[moduleIndex] = RuntimeImports.RhpCreateTypeManager(osModule, pModuleHeaders[i], pClasslibFunctions, nClasslibFunctions); - moduleIndex++; + TypeManagerHandle handle = RuntimeImports.RhpCreateTypeManager(osModule, pModuleHeaders[i], pClasslibFunctions, nClasslibFunctions); + + // Rehydrate any dehydrated data structures + IntPtr dehydratedDataSection = RuntimeImports.RhGetModuleSection( + handle, ReadyToRunSectionType.DehydratedData, out int dehydratedDataLength); + if (dehydratedDataSection != IntPtr.Zero) + { + RehydrateData(dehydratedDataSection, dehydratedDataLength); + } + + pHandles[moduleIndex++] = handle; } } + // Any potentially dehydrated MethodTables got rehydrated, we can safely use `new` now. + TypeManagerHandle[] modules = new TypeManagerHandle[moduleCount]; + for (int i = 0; i < moduleCount; i++) + modules[i] = pHandles[i]; + return modules; } @@ -217,6 +232,49 @@ private static unsafe object[] InitializeStatics(IntPtr gcStaticRegionStart, int return spine; } + + private static unsafe void RehydrateData(IntPtr dehydratedData, int length) + { + // Destination for the hydrated data is in the first 32-bit relative pointer + byte* pDest = (byte*)ReadRelPtr32((void*)dehydratedData); + + // The dehydrated data follows + byte* pCurrent = (byte*)dehydratedData + sizeof(int); + byte* pEnd = (byte*)dehydratedData + length; + + // Fixup table immediately follows the command stream + int* pFixups = (int*)pEnd; + + while (pCurrent < pEnd) + { + pCurrent = DehydratedDataCommand.Decode(pCurrent, out int command, out int payload); + switch (command) + { + case DehydratedDataCommand.Copy: + // TODO: can we do any kind of memcpy here? + for (; payload > 0; payload--) + *pDest++ = *pCurrent++; + break; + case DehydratedDataCommand.ZeroFill: + pDest += payload; + break; + case DehydratedDataCommand.PtrReloc: + *(void**)pDest = ReadRelPtr32(pFixups + payload); + pDest += sizeof(void*); + break; + case DehydratedDataCommand.RelPtr32Reloc: + WriteRelPtr32(pDest, ReadRelPtr32(pFixups + payload)); + pDest += sizeof(int); + break; + } + } + + static void* ReadRelPtr32(void* address) + => (byte*)address + *(int*)address; + + static void WriteRelPtr32(void* dest, void* value) + => *(int*)dest = (int)((byte*)value - (byte*)dest); + } } [StructLayout(LayoutKind.Sequential)] diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 0c0e84b3b7d7..271949b251b1 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -40,6 +40,9 @@ + + Internal\Runtime\DehydratedData.cs + Internal\Runtime\RuntimeConstants.cs diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj b/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj index 9daa8e0ee004..da62a5daa830 100644 --- a/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj +++ b/src/coreclr/nativeaot/Test.CoreLib/src/Test.CoreLib.csproj @@ -103,6 +103,9 @@ Internal\Runtime\ModuleHeaders.cs + + Internal\Runtime\DehydratedData.cs + Internal\Runtime\RuntimeConstants.cs diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNodeSection.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNodeSection.cs index 89c4b895584a..de6ec995220e 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNodeSection.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNodeSection.cs @@ -7,7 +7,8 @@ public enum SectionType { ReadOnly, Writeable, - Executable + Executable, + Uninitialized, } /// @@ -47,6 +48,7 @@ public bool IsStandardSection public static readonly ObjectNodeSection TextSection = new ObjectNodeSection("text", SectionType.Executable); public static readonly ObjectNodeSection TLSSection = new ObjectNodeSection("TLS", SectionType.Writeable); public static readonly ObjectNodeSection BssSection = new ObjectNodeSection("bss", SectionType.Writeable); + public static readonly ObjectNodeSection HydrationTargetSection = new ObjectNodeSection("hydrated", SectionType.Uninitialized); public static readonly ObjectNodeSection ManagedCodeWindowsContentSection = new ObjectNodeSection(".managedcode$I", SectionType.Executable); public static readonly ObjectNodeSection FoldableManagedCodeWindowsContentSection = new ObjectNodeSection(".managedcode$I", SectionType.Executable); public static readonly ObjectNodeSection ManagedCodeUnixContentSection = new ObjectNodeSection("__managedcode", SectionType.Executable); diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs index 29186fd18ad4..25b94a376542 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs @@ -1,6 +1,7 @@ // 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.Diagnostics; namespace ILCompiler.DependencyAnalysis @@ -438,6 +439,16 @@ public static unsafe void WriteValue(RelocType relocType, void* location, long v } } + public static int GetSize(RelocType relocType) + { + return relocType switch + { + RelocType.IMAGE_REL_BASED_DIR64 => 8, + RelocType.IMAGE_REL_BASED_RELPTR32 => 4, + _ => throw new NotSupportedException(), + }; + } + public static unsafe long ReadValue(RelocType relocType, void* location) { switch (relocType) diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs index a29c732e0814..c6059b5cd1f0 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs @@ -42,7 +42,8 @@ protected enum ObjectNodePhase /// affect compiler correctness. Today that includes native layout tables. /// Ordered, - Unordered + Unordered, + Late, } protected enum ObjectNodeOrder @@ -94,6 +95,7 @@ protected enum ObjectNodeOrder StackTraceEmbeddedMetadataNode, StackTraceMethodMappingNode, ArrayOfEmbeddedDataNode, + DehydratedDataNode, } public class EmbeddedObjectNodeComparer : IComparer diff --git a/src/coreclr/tools/Common/Internal/Runtime/DehydratedData.cs b/src/coreclr/tools/Common/Internal/Runtime/DehydratedData.cs new file mode 100644 index 000000000000..ca632e0c25c7 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Runtime/DehydratedData.cs @@ -0,0 +1,116 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Debug = System.Diagnostics.Debug; + +namespace Internal.Runtime +{ + /// + /// Provides functionality to encode/decode dehydrated data instruction stream. + /// + /// + /// The instructions use a variable length encoding and are split in two parts: + /// the instruction command kind and command data (payload). + /// The payload is an integer. Instruction kind and payload can fit into a single + /// byte, the encoding is one byte. Bigger payloads produce bigger instructions. + /// + internal static class DehydratedDataCommand + { + public const byte Copy = 0x00; + public const byte ZeroFill = 0x01; + public const byte RelPtr32Reloc = 0x02; + public const byte PtrReloc = 0x03; + + private const byte DehydratedDataCommandMask = 0x03; + private const int DehydratedDataCommandPayloadShift = 2; + + private const int MaxRawShortPayload = (1 << (8 - DehydratedDataCommandPayloadShift)) - 1; + private const int MaxExtraPayloadBytes = 3; + private const int MaxShortPayload = MaxRawShortPayload - MaxExtraPayloadBytes; + + public static byte EncodeShort(int command, int commandData) + { + Debug.Assert((command & DehydratedDataCommandMask) == command); + Debug.Assert(commandData <= MaxShortPayload); + return (byte)(command | (commandData << DehydratedDataCommandPayloadShift)); + } + + public static int Encode(int command, int commandData, byte[] buffer) + { + Debug.Assert((command & DehydratedDataCommandMask) == command); + int remainingData = commandData - MaxShortPayload; + if (remainingData <= 0) + { + buffer[0] = EncodeShort(command, commandData); + return 1; + } + + int numExtraBytes = 0; + for (; remainingData != 0; remainingData >>= 8) + buffer[++numExtraBytes] = (byte)remainingData; + Debug.Assert(numExtraBytes <= MaxExtraPayloadBytes, "Decoder assumes this"); + + buffer[0] = (byte)(command | ((MaxShortPayload + numExtraBytes) << DehydratedDataCommandPayloadShift)); + return 1 + numExtraBytes; + } + + public static unsafe byte* Decode(byte* pB, out int command, out int payload) + { + byte b = *pB; + command = b & DehydratedDataCommandMask; + payload = b >> DehydratedDataCommandPayloadShift; + int extraBytes = payload - MaxShortPayload; + if (extraBytes > 0) + { + payload = *++pB; + if (extraBytes > 1) + { + payload += *++pB << 8; + if (extraBytes > 2) + payload += *++pB << 16; + } + + payload += MaxShortPayload; + } + + return pB + 1; + } + +#if false + static void Main() + { + int command, payload; + + byte[] buf = new byte[5]; + Debug.Assert(Encode(1, 0, buf) == 1); + Debug.Assert(buf[0] == 1); + Debug.Assert(D(buf, out command, out payload) == 1 && command == 1 && payload == 0); + Debug.Assert(Encode(1, 1, buf) == 1); + Debug.Assert(buf[0] == (1 | (1 << DehydratedDataCommandPayloadShift))); + Debug.Assert(D(buf, out command, out payload) == 1 && command == 1 && payload == 1); + Debug.Assert(Encode(1, 60, buf) == 1); + Debug.Assert(buf[0] == (1 | (60 << DehydratedDataCommandPayloadShift))); + Debug.Assert(D(buf, out command, out payload) == 1 && command == 1 && payload == 60); + Debug.Assert(Encode(1, 61, buf) == 2); + Debug.Assert(buf[0] == (1 | ((MaxShortPayload + 1) << DehydratedDataCommandPayloadShift))); + Debug.Assert(buf[1] == 1); + Debug.Assert(D(buf, out command, out payload) == 2 && command == 1 && payload == 61); + + Debug.Assert(Encode(3, 256, buf) == 2); + Debug.Assert(D(buf, out command, out payload) == 2 && command == 3 && payload == 256); + Debug.Assert(Encode(3, 6500, buf) == 3); + Debug.Assert(D(buf, out command, out payload) == 3 && command == 3 && payload == 6500); + Debug.Assert(Encode(3, 65000, buf) == 3); + Debug.Assert(D(buf, out command, out payload) == 3 && command == 3 && payload == 65000); + Debug.Assert(Encode(3, 100000, buf) == 4); + Debug.Assert(D(buf, out command, out payload) == 4 && command == 3 && payload == 100000); + + static unsafe int D(byte[] bytes, out int command, out int payload) + { + fixed (byte* pBytes = bytes) + return (int)(Decode(pBytes, out command, out payload) - pBytes); + } + } +#endif + } +} diff --git a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs index 9d081f719c97..144bd50f949d 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs @@ -81,7 +81,7 @@ public enum ReadyToRunSectionType TypeManagerIndirection = 204, EagerCctor = 205, FrozenObjectRegion = 206, - // 207 is unused - it was used by GCStaticDesc + DehydratedData = 207, ThreadStaticOffsetRegion = 208, // 209 is unused - it was used by ThreadStaticGCDescRegion // 210 is unused - it was used by ThreadStaticIndex diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs index bf1a053c0c61..1bd82830585d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilationBuilder.Aot.cs @@ -22,6 +22,7 @@ public partial class CompilationBuilder protected bool _methodBodyFolding; protected InstructionSetSupport _instructionSetSupport; protected SecurityMitigationOptions _mitigationOptions; + protected bool _dehydrate; protected bool _useDwarf5; partial void InitializePartial() @@ -84,6 +85,12 @@ public CompilationBuilder UseSecurityMitigationOptions(SecurityMitigationOptions return this; } + public CompilationBuilder UseDehydration(bool dehydrate) + { + _dehydrate = dehydrate; + return this; + } + public CompilationBuilder UseMethodBodyFolding(bool enable) { _methodBodyFolding = enable; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DehydratableObjectNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DehydratableObjectNode.cs new file mode 100644 index 000000000000..daf034850e44 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DehydratableObjectNode.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace ILCompiler.DependencyAnalysis +{ + public abstract class DehydratableObjectNode : ObjectNode + { + public sealed override ObjectNodeSection GetSection(NodeFactory factory) + { + return factory.MetadataManager.IsDataDehydrated ? ObjectNodeSection.HydrationTargetSection : GetDehydratedSection(factory); + } + + public sealed override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectData result = GetDehydratableData(factory, relocsOnly); + + // If we're not generating full data yet, or dehydration is not active, + // return the ObjectData as is. + if (relocsOnly || !factory.MetadataManager.IsDataDehydrated) + return result; + + // Otherwise return the dehydrated data + return factory.MetadataManager.PrepareForDehydration(this, result); + } + + protected abstract ObjectNodeSection GetDehydratedSection(NodeFactory factory); + protected abstract ObjectData GetDehydratableData(NodeFactory factory, bool relocsOnly = false); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DehydratedDataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DehydratedDataNode.cs new file mode 100644 index 000000000000..1e412e2056e8 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DehydratedDataNode.cs @@ -0,0 +1,242 @@ +// 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 Internal.Runtime; +using Internal.Text; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Responsible for generating dehydrated stream of data structures that need to be rehydrated + /// at runtime before use. + /// + /// + /// The things we're dehydrating: + /// * Pointers. On 64bit targets, absolute pointers take up 8 bytes + reloc in the image. The + /// size of the reloc can be up to 24 bytes (ELF/Linux). We replace relocs with a more efficient + /// index into a lookup table. + /// * Runs of zeros. These can be result of alignment or simply a result of how things ended up naturally. + /// + /// The dehydrated stream can be though of as an instruction stream with 3 kinds of instructions: + /// * Copy N bytes of literal data following the instruction. + /// * Generate N bytes of zeros. + /// * Generate a relocation to Nth entry in the lookup table that supplements the dehydrated stream. + /// + internal sealed class DehydratedDataNode : ObjectNode, ISymbolDefinitionNode + { + private ObjectAndOffsetSymbolNode _endSymbol; + + public DehydratedDataNode() + { + _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__dehydrated_data_End", true); + } + + public override bool IsShareable => false; + + protected internal override int Phase => (int)ObjectNodePhase.Late; + + public override int ClassCode => (int)ObjectNodeOrder.DehydratedDataNode; + + public override bool StaticDependenciesAreComputed => true; + + public int Offset => 0; + + public ISymbolNode EndSymbol => _endSymbol; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__dehydrated_data"); + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // This is a summary node that doesn't contribute to the dependency graph. + if (relocsOnly) + return new ObjectData(Array.Empty(), Array.Empty(), 1, Array.Empty()); + + ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); + builder.AddSymbol(this); + + // Count the number of occurences of reloc targets. We'll sort the reloc targets by the number of + // references to them so that we can assign the most efficient instruction encoding (1 byte) for + // the most popular targets. + ISymbolDefinitionNode firstSymbol = null; + var relocOccurences = new Dictionary(); + foreach (ObjectNode.ObjectData o in factory.MetadataManager.GetDehydratableData()) + { + firstSymbol ??= o.DefinedSymbols[0]; + + foreach (Relocation reloc in o.Relocs) + { + ISymbolNode target = reloc.Target; + if (target is ISymbolNodeWithLinkage withLinkage) + target = withLinkage.NodeForLinkage(factory); + relocOccurences.TryGetValue(target, out int num); + relocOccurences[target] = ++num; + } + } + + if (firstSymbol != null) + builder.EmitReloc(firstSymbol, RelocType.IMAGE_REL_BASED_RELPTR32, -firstSymbol.Offset); + + // Sort the reloc targets and create reloc lookup table. + KeyValuePair[] relocSort = new List>(relocOccurences).ToArray(); + Array.Sort(relocSort, (x, y) => y.Value.CompareTo(x.Value)); + for (int i = 0; i < relocSort.Length; i++) + relocSort[i] = new KeyValuePair(relocSort[i].Key, i); + var relocs = new Dictionary(relocSort); + + // Walk all the ObjectDatas and generate the dehydrated instruction stream. + byte[] buff = new byte[5]; + int dehydratedSegmentPosition = 0; + foreach (ObjectData o in factory.MetadataManager.GetDehydratableData()) + { + Debug.Assert(o.Data.Length != 0); + + if (o.Alignment > 1) + { + // Might need to emit a ZeroFill to align the data. + int oldPosition = dehydratedSegmentPosition; + dehydratedSegmentPosition = dehydratedSegmentPosition.AlignUp(o.Alignment); + if (dehydratedSegmentPosition > oldPosition) + builder.EmitByte(DehydratedDataCommand.EncodeShort(DehydratedDataCommand.ZeroFill, dehydratedSegmentPosition - oldPosition)); + } + + int currentReloc = 0; + int sourcePosition = 0; + + while (sourcePosition < o.Data.Length) + { + // The ObjectData can be though of as a run of bytes interrupted by relocations. + // We identify the next chunk of data by looking at the next relocation and + // emit the next chunk of data (if any) followed by a relocation (if any). + int bytesToCopy; + Relocation reloc; + if (currentReloc < o.Relocs.Length) + { + reloc = o.Relocs[currentReloc++]; + + // We assume relocations are sorted + Debug.Assert(reloc.Offset >= sourcePosition); + bytesToCopy = reloc.Offset - sourcePosition; + } + else + { + reloc = default; + bytesToCopy = o.Data.Length - sourcePosition; + } + + // Emit the run of bytes before the next relocation (or end of ObjectData, whichever + // comes first). + while (bytesToCopy > 0) + { + // Try to identify a continuos run of zeros. If we find one, split this run of + // bytes into chunks of data interleaved with zero fill instructions. + int chunkLength = 0; + int numZeros = 0; + const int MinProfitableZerofill = 4; + for (int i = sourcePosition; i < sourcePosition + bytesToCopy; i++) + { + if (o.Data[i] == 0) + { + numZeros++; + } + else if (numZeros >= MinProfitableZerofill) + { + break; + } + else + { + chunkLength += numZeros; + numZeros = 0; + chunkLength++; + } + } + + // If it wouldn't be profiable to emit zero fill, emit zeros at literal data. + if (numZeros < MinProfitableZerofill) + { + chunkLength += numZeros; + numZeros = 0; + } + + // Emit literal data if there's any. + if (chunkLength > 0) + { + int written = DehydratedDataCommand.Encode(DehydratedDataCommand.Copy, chunkLength, buff); + builder.EmitBytes(buff, 0, written); + builder.EmitBytes(o.Data, sourcePosition, chunkLength); + sourcePosition += chunkLength; + bytesToCopy -= chunkLength; + } + + // Emit a ZeroFill instruction if there's any zeros. + if (numZeros > 0) + { + int written = DehydratedDataCommand.Encode(DehydratedDataCommand.ZeroFill, numZeros, buff); + builder.EmitBytes(buff, 0, written); + sourcePosition += numZeros; + bytesToCopy -= numZeros; + } + } + + // Generate the next relocation if there's any. + if (reloc.Target != null) + { +#if DEBUG + unsafe + { + fixed (byte* pData = &o.Data[reloc.Offset]) + { + long delta = Relocation.ReadValue(reloc.RelocType, pData); + // Extra work needed to be able to encode/decode relocs with deltas + Debug.Assert(delta == 0); + } + } +#endif + + // The size of the relocation is included in the ObjectData bytes. Skip the literal bytes. + sourcePosition += Relocation.GetSize(reloc.RelocType); + + ISymbolNode target = reloc.Target; + if (target is ISymbolNodeWithLinkage withLinkage) + target = withLinkage.NodeForLinkage(factory); + + int targetIndex = relocs[target]; + + int relocCommand = reloc.RelocType switch + { + RelocType.IMAGE_REL_BASED_DIR64 => DehydratedDataCommand.PtrReloc, + RelocType.IMAGE_REL_BASED_RELPTR32 => DehydratedDataCommand.RelPtr32Reloc, + _ => throw new NotSupportedException(), + }; + + int written = DehydratedDataCommand.Encode(relocCommand, targetIndex, buff); + builder.EmitBytes(buff, 0, written); + } + } + + Debug.Assert(sourcePosition == o.Data.Length); + dehydratedSegmentPosition += o.Data.Length; + } + + _endSymbol.SetSymbolOffset(builder.CountBytes); + builder.AddSymbol(_endSymbol); + + for (int i = 0; i < relocSort.Length; i++) + builder.EmitReloc(relocSort[i].Key, RelocType.IMAGE_REL_BASED_RELPTR32); + + return builder.ToObjectData(); + } + + public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.ReadOnlyDataSection; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs index 91eec5ffef39..04a5b32c9acb 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs @@ -60,7 +60,7 @@ namespace ILCompiler.DependencyAnalysis /// | /// [Relative ptr] | Pointer to the generic argument and variance info (optional) /// - public partial class EETypeNode : ObjectNode, IEETypeNode, ISymbolDefinitionNode, ISymbolNodeWithLinkage + public partial class EETypeNode : DehydratableObjectNode, IEETypeNode, ISymbolDefinitionNode, ISymbolNodeWithLinkage { protected readonly TypeDesc _type; internal readonly EETypeOptionalFieldsBuilder _optionalFieldsBuilder = new EETypeOptionalFieldsBuilder(); @@ -117,7 +117,7 @@ public virtual ISymbolNode NodeForLinkage(NodeFactory factory) public TypeDesc Type => _type; - public override ObjectNodeSection GetSection(NodeFactory factory) + protected override ObjectNodeSection GetDehydratedSection(NodeFactory factory) { if (factory.Target.IsWindows) return ObjectNodeSection.ReadOnlyDataSection; @@ -561,7 +561,7 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact return dependencies; } - public override ObjectData GetData(NodeFactory factory, bool relocsOnly) + protected override ObjectData GetDehydratableData(NodeFactory factory, bool relocsOnly) { ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); objData.RequireInitialPointerAlignment(); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs index 36fa4b640ab2..d69d87db2754 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs @@ -34,7 +34,7 @@ protected internal override void ComputeOptionalEETypeFields(NodeFactory factory { } - public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + protected override ObjectData GetDehydratableData(NodeFactory factory, bool relocsOnly = false) { ObjectDataBuilder dataBuilder = new ObjectDataBuilder(factory, relocsOnly); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectWriter.cs index b984cda14384..d6eb8ee23148 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectWriter.cs @@ -131,6 +131,7 @@ public enum CustomSectionAttributes ReadOnly = 0x0000, Writeable = 0x0001, Executable = 0x0002, + Uninitialized = 0x0004, }; /// @@ -151,6 +152,9 @@ private static CustomSectionAttributes GetCustomSectionAttributes(ObjectNodeSect case SectionType.Writeable: attributes |= CustomSectionAttributes.Writeable; break; + case SectionType.Uninitialized: + attributes |= CustomSectionAttributes.Uninitialized | CustomSectionAttributes.Writeable; + break; } return attributes; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs index b63026dae37a..9e35b08545a2 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs @@ -60,6 +60,8 @@ public abstract class MetadataManager : ICompilationRootProvider private readonly SortedSet _typesWithStructMarshalling = new SortedSet(TypeSystemComparer.Instance); private HashSet _templateMethodEntries = new HashSet(); + private List<(DehydratableObjectNode Node, ObjectNode.ObjectData Data)> _dehydratableData = new List<(DehydratableObjectNode Node, ObjectNode.ObjectData data)>(); + internal NativeLayoutInfoNode NativeLayoutInfo { get; private set; } public MetadataManager(CompilerTypeSystemContext typeSystemContext, MetadataBlockingPolicy blockingPolicy, @@ -71,6 +73,32 @@ public abstract class MetadataManager : ICompilationRootProvider _dynamicInvokeThunkGenerationPolicy = dynamicInvokeThunkGenerationPolicy; } + public bool IsDataDehydrated => true; + + internal ObjectNode.ObjectData PrepareForDehydration(DehydratableObjectNode node, ObjectNode.ObjectData hydratedData) + { + _dehydratableData.Add((node, hydratedData)); + + return new ObjectNode.ObjectData(new byte[hydratedData.Data.Length], + Array.Empty(), + hydratedData.Alignment, + hydratedData.DefinedSymbols); + } + + public IEnumerable GetDehydratableData() + { +#if DEBUG + // We're making an assumption that PrepareForDehydration was called in the emission order. + // Double check that here. + var comparer = new CompilerComparer(); + for (int i = 1; i < _dehydratableData.Count; i++) + Debug.Assert(comparer.Compare(_dehydratableData[i - 1].Node, _dehydratableData[i].Node) < 0); +#endif + + foreach (var entry in _dehydratableData) + yield return entry.Data; + } + public void AttachToDependencyGraph(DependencyAnalyzerBase graph) { graph.NewMarkedNode += Graph_NewMarkedNode; @@ -155,6 +183,12 @@ public virtual void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFacto // The external references tables should go last header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.NativeReferences), nativeReferencesTableNode, nativeReferencesTableNode, nativeReferencesTableNode.EndSymbol); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.NativeStatics), nativeStaticsTableNode, nativeStaticsTableNode, nativeStaticsTableNode.EndSymbol); + + if (IsDataDehydrated) + { + var dehydratedDataNode = new DehydratedDataNode(); + header.Add(ReadyToRunSectionType.DehydratedData, dehydratedDataNode, dehydratedDataNode, dehydratedDataNode.EndSymbol); + } } protected virtual void Graph_NewMarkedNode(DependencyNodeCore obj) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 214570667ca9..fdee2473d392 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -180,6 +180,9 @@ TypeSystem\MetadataEmitter\TypeSystemMetadataEmitter.cs + + Common\DehydratedData.cs + Common\GCDescEncoder.cs @@ -319,6 +322,8 @@ + + diff --git a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs index b7b11d8bcb1d..17b496f08482 100644 --- a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs +++ b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs @@ -113,6 +113,8 @@ internal sealed class ILCompilerRootCommand : RootCommand new(new[] { "--instruction-set" }, "Instruction set to allow or disallow"); public Option Guard { get; } = new(new[] { "--guard" }, "Enable mitigations. Options: 'cf': CFG (Control Flow Guard, Windows only)"); + public Option Dehydrate { get; } = + new(new[] { "--dehydrate" }, "Dehydrate runtime data structures"); public Option PreinitStatics { get; } = new(new[] { "--preinitstatics" }, "Interpret static constructors at compile time if possible (implied by -O)"); public Option NoPreinitStatics { get; } = @@ -205,6 +207,7 @@ public ILCompilerRootCommand(string[] args) : base(".NET Native IL Compiler") AddOption(Parallelism); AddOption(InstructionSet); AddOption(Guard); + AddOption(Dehydrate); AddOption(PreinitStatics); AddOption(NoPreinitStatics); AddOption(SuppressedWarnings); From 3d9c1fd1a30d9a5f087f97795506a1bdc6431a29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 11 Nov 2022 15:34:09 +0900 Subject: [PATCH 2/5] wip --- .../Microsoft.NETCore.Native.targets | 1 + .../DependencyAnalysis/SortableDependencyNode.cs | 1 - .../Compiler/AnalysisBasedMetadataManager.cs | 7 ++++--- .../DependencyAnalysis/DehydratedDataNode.cs | 7 ++++--- .../DependencyAnalysis/FatFunctionPointerNode.cs | 6 +++--- .../DependencyAnalysis/GCStaticEETypeNode.cs | 6 +++--- .../DependencyAnalysis/GenericCompositionNode.cs | 6 +++--- .../DependencyAnalysis/GenericDictionaryNode.cs | 7 ++++--- .../NativeLayoutSignatureNode.cs | 6 +++--- .../DependencyAnalysis/RuntimeFieldHandleNode.cs | 6 +++--- .../DependencyAnalysis/RuntimeMethodHandleNode.cs | 6 +++--- .../TypeThreadStaticIndexNode.cs | 6 +++--- .../Compiler/GeneratingMetadataManager.cs | 4 ++-- .../Compiler/MetadataManager.cs | 14 ++++++++++++-- .../Compiler/UsageBasedMetadataManager.cs | 5 +++-- src/coreclr/tools/aot/ILCompiler/Program.cs | 5 +++++ 16 files changed, 56 insertions(+), 37 deletions(-) diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets index 3b29974d5af1..f81642bb72cc 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets @@ -214,6 +214,7 @@ The .NET Foundation licenses this file to you under the MIT license. + diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs index c6059b5cd1f0..010732668932 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs @@ -95,7 +95,6 @@ protected enum ObjectNodeOrder StackTraceEmbeddedMetadataNode, StackTraceMethodMappingNode, ArrayOfEmbeddedDataNode, - DehydratedDataNode, } public class EmbeddedObjectNodeComparer : IComparer diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs index f287624d266b..92458d4d7abf 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/AnalysisBasedMetadataManager.cs @@ -35,7 +35,7 @@ public AnalysisBasedMetadataManager(CompilerTypeSystemContext typeSystemContext) new NoDynamicInvokeThunkGenerationPolicy(), Array.Empty(), Array.Empty>(), Array.Empty>(), Array.Empty>(), Array.Empty(), - Array.Empty()) + Array.Empty(), default) { } @@ -51,8 +51,9 @@ public AnalysisBasedMetadataManager(CompilerTypeSystemContext typeSystemContext) IEnumerable> reflectableMethods, IEnumerable> reflectableFields, IEnumerable reflectableAttributes, - IEnumerable rootedCctorContexts) - : base(typeSystemContext, blockingPolicy, resourceBlockingPolicy, logFile, stackTracePolicy, invokeThunkGenerationPolicy) + IEnumerable rootedCctorContexts, + MetadataManagerOptions options) + : base(typeSystemContext, blockingPolicy, resourceBlockingPolicy, logFile, stackTracePolicy, invokeThunkGenerationPolicy, options) { _modulesWithMetadata = new List(modulesWithMetadata); _typesWithRootedCctorContext = new List(rootedCctorContexts); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DehydratedDataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DehydratedDataNode.cs index 1e412e2056e8..e6a144e1dd5c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DehydratedDataNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DehydratedDataNode.cs @@ -41,7 +41,7 @@ public DehydratedDataNode() protected internal override int Phase => (int)ObjectNodePhase.Late; - public override int ClassCode => (int)ObjectNodeOrder.DehydratedDataNode; + public override int ClassCode => 0xbd80526; public override bool StaticDependenciesAreComputed => true; @@ -86,6 +86,8 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) if (firstSymbol != null) builder.EmitReloc(firstSymbol, RelocType.IMAGE_REL_BASED_RELPTR32, -firstSymbol.Offset); + else + return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this, _endSymbol }); // Sort the reloc targets and create reloc lookup table. KeyValuePair[] relocSort = new List>(relocOccurences).ToArray(); @@ -99,8 +101,6 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) int dehydratedSegmentPosition = 0; foreach (ObjectData o in factory.MetadataManager.GetDehydratableData()) { - Debug.Assert(o.Data.Length != 0); - if (o.Alignment > 1) { // Might need to emit a ZeroFill to align the data. @@ -231,6 +231,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) _endSymbol.SetSymbolOffset(builder.CountBytes); builder.AddSymbol(_endSymbol); + // Dehydrated data is followed by the reloc lookup table. for (int i = 0; i < relocSort.Length; i++) builder.EmitReloc(relocSort[i].Key, RelocType.IMAGE_REL_BASED_RELPTR32); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FatFunctionPointerNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FatFunctionPointerNode.cs index 3833faacc6d3..8fa6109155c6 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FatFunctionPointerNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FatFunctionPointerNode.cs @@ -13,7 +13,7 @@ namespace ILCompiler.DependencyAnalysis /// method body along with the instantiation context the canonical body requires. /// Pointers to these structures can be created by e.g. ldftn/ldvirtftn of a method with a canonical body. /// - public class FatFunctionPointerNode : ObjectNode, IMethodNode, ISymbolDefinitionNode + public class FatFunctionPointerNode : DehydratableObjectNode, IMethodNode, ISymbolDefinitionNode { private bool _isUnboxingStub; @@ -41,7 +41,7 @@ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) public MethodDesc Method { get; } - public override ObjectNodeSection GetSection(NodeFactory factory) + protected override ObjectNodeSection GetDehydratedSection(NodeFactory factory) { if (factory.Target.IsWindows) return ObjectNodeSection.ReadOnlyDataSection; @@ -53,7 +53,7 @@ public override ObjectNodeSection GetSection(NodeFactory factory) protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); - public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + protected override ObjectData GetDehydratableData(NodeFactory factory, bool relocsOnly = false) { var builder = new ObjectDataBuilder(factory, relocsOnly); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GCStaticEETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GCStaticEETypeNode.cs index b96839ea5f7a..addb715ec677 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GCStaticEETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GCStaticEETypeNode.cs @@ -16,7 +16,7 @@ namespace ILCompiler.DependencyAnalysis /// types. It only fills out enough pieces of the MethodTable structure so that the GC can operate on it. Runtime should /// never see these. /// - public class GCStaticEETypeNode : ObjectNode, ISymbolDefinitionNode + public class GCStaticEETypeNode : DehydratableObjectNode, ISymbolDefinitionNode { private GCPointerMap _gcMap; private TargetDetails _target; @@ -29,7 +29,7 @@ public GCStaticEETypeNode(TargetDetails target, GCPointerMap gcMap) protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); - public override ObjectNodeSection GetSection(NodeFactory factory) + protected override ObjectNodeSection GetDehydratedSection(NodeFactory factory) { if (_target.IsWindows) return ObjectNodeSection.ReadOnlyDataSection; @@ -57,7 +57,7 @@ int ISymbolDefinitionNode.Offset public override bool IsShareable => true; - public override ObjectData GetData(NodeFactory factory, bool relocsOnly) + protected override ObjectData GetDehydratableData(NodeFactory factory, bool relocsOnly = false) { ObjectDataBuilder dataBuilder = new ObjectDataBuilder(factory, relocsOnly); dataBuilder.RequireInitialPointerAlignment(); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericCompositionNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericCompositionNode.cs index 43c2eb784892..eccd640049a6 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericCompositionNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericCompositionNode.cs @@ -15,7 +15,7 @@ namespace ILCompiler.DependencyAnalysis /// Describes how a generic type instance is composed - the number of generic arguments, their types, /// and variance information. /// - public class GenericCompositionNode : ObjectNode, ISymbolDefinitionNode + public class GenericCompositionNode : DehydratableObjectNode, ISymbolDefinitionNode { private GenericCompositionDetails _details; @@ -53,7 +53,7 @@ public int Offset } } - public override ObjectNodeSection GetSection(NodeFactory factory) + protected override ObjectNodeSection GetDehydratedSection(NodeFactory factory) { if (factory.Target.IsWindows) return ObjectNodeSection.FoldableReadOnlyDataSection; @@ -65,7 +65,7 @@ public override ObjectNodeSection GetSection(NodeFactory factory) public override bool StaticDependenciesAreComputed => true; - public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + protected override ObjectData GetDehydratableData(NodeFactory factory, bool relocsOnly = false) { bool hasVariance = _details.Variance != null; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDictionaryNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDictionaryNode.cs index 659b7f4b2766..c6c63fa64985 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDictionaryNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDictionaryNode.cs @@ -17,7 +17,7 @@ namespace ILCompiler.DependencyAnalysis /// at runtime to look up runtime artifacts that depend on the concrete /// context the generic type or method was instantiated with. /// - public abstract class GenericDictionaryNode : ObjectNode, ISymbolDefinitionNode, ISortableSymbolNode + public abstract class GenericDictionaryNode : DehydratableObjectNode, ISymbolDefinitionNode, ISortableSymbolNode { private readonly NodeFactory _factory; @@ -43,14 +43,15 @@ public abstract class GenericDictionaryNode : ObjectNode, ISymbolDefinitionNode, int ISymbolDefinitionNode.Offset => HeaderSize; - public override ObjectNodeSection GetSection(NodeFactory factory) => GetDictionaryLayout(_factory).DictionarySection(_factory); + protected override ObjectNodeSection GetDehydratedSection(NodeFactory factory) + => GetDictionaryLayout(_factory).DictionarySection(_factory); public GenericDictionaryNode(NodeFactory factory) { _factory = factory; } - public sealed override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + protected override ObjectData GetDehydratableData(NodeFactory factory, bool relocsOnly = false) { ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); builder.AddSymbol(this); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutSignatureNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutSignatureNode.cs index 322b84207d4f..fe00421b74e6 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutSignatureNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NativeLayoutSignatureNode.cs @@ -14,7 +14,7 @@ namespace ILCompiler.DependencyAnalysis /// to the TypeManager that contains the native layout info blob of interest, and the second item /// is an offset into that native layout info blob /// - public class NativeLayoutSignatureNode : ObjectNode, ISymbolDefinitionNode + public class NativeLayoutSignatureNode : DehydratableObjectNode, ISymbolDefinitionNode { private TypeSystemEntity _identity; private Utf8String _identityPrefix; @@ -55,7 +55,7 @@ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) public int Offset => 0; protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); - public override ObjectNodeSection GetSection(NodeFactory factory) + protected override ObjectNodeSection GetDehydratedSection(NodeFactory factory) { if (factory.Target.IsWindows) return ObjectNodeSection.ReadOnlyDataSection; @@ -72,7 +72,7 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact return dependencies; } - public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + protected override ObjectData GetDehydratableData(NodeFactory factory, bool relocsOnly = false) { // This node does not trigger generation of other nodes. if (relocsOnly) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/RuntimeFieldHandleNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/RuntimeFieldHandleNode.cs index c7823bae87fa..644bc136b590 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/RuntimeFieldHandleNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/RuntimeFieldHandleNode.cs @@ -8,7 +8,7 @@ namespace ILCompiler.DependencyAnalysis { - public class RuntimeFieldHandleNode : ObjectNode, ISymbolDefinitionNode + public class RuntimeFieldHandleNode : DehydratableObjectNode, ISymbolDefinitionNode { private FieldDesc _targetField; @@ -30,7 +30,7 @@ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) public override bool IsShareable => false; public override bool StaticDependenciesAreComputed => true; - public override ObjectNodeSection GetSection(NodeFactory factory) + protected override ObjectNodeSection GetDehydratedSection(NodeFactory factory) { if (factory.Target.IsWindows) return ObjectNodeSection.ReadOnlyDataSection; @@ -47,7 +47,7 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact return result; } - public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + protected override ObjectData GetDehydratableData(NodeFactory factory, bool relocsOnly = false) { ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/RuntimeMethodHandleNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/RuntimeMethodHandleNode.cs index 4cbd7b21996e..bda295744e7d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/RuntimeMethodHandleNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/RuntimeMethodHandleNode.cs @@ -8,7 +8,7 @@ namespace ILCompiler.DependencyAnalysis { - public class RuntimeMethodHandleNode : ObjectNode, ISymbolDefinitionNode + public class RuntimeMethodHandleNode : DehydratableObjectNode, ISymbolDefinitionNode { private MethodDesc _targetMethod; @@ -36,7 +36,7 @@ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) public override bool IsShareable => false; public override bool StaticDependenciesAreComputed => true; - public override ObjectNodeSection GetSection(NodeFactory factory) + protected override ObjectNodeSection GetDehydratedSection(NodeFactory factory) { if (factory.Target.IsWindows) return ObjectNodeSection.ReadOnlyDataSection; @@ -62,7 +62,7 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact private static Utf8String s_NativeLayoutSignaturePrefix = new Utf8String("__RMHSignature_"); - public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + protected override ObjectData GetDehydratableData(NodeFactory factory, bool relocsOnly = false) { ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeThreadStaticIndexNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeThreadStaticIndexNode.cs index 799a10a07864..f9200bef525b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeThreadStaticIndexNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/TypeThreadStaticIndexNode.cs @@ -9,7 +9,7 @@ namespace ILCompiler.DependencyAnalysis /// /// Represents a node containing information necessary at runtime to locate type's thread static base. /// - public class TypeThreadStaticIndexNode : ObjectNode, ISymbolDefinitionNode, ISortableSymbolNode + public class TypeThreadStaticIndexNode : DehydratableObjectNode, ISymbolDefinitionNode, ISortableSymbolNode { private MetadataType _type; @@ -24,7 +24,7 @@ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) } public int Offset => 0; protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); - public override ObjectNodeSection GetSection(NodeFactory factory) + protected override ObjectNodeSection GetDehydratedSection(NodeFactory factory) { if (factory.Target.IsWindows) return ObjectNodeSection.ReadOnlyDataSection; @@ -42,7 +42,7 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact }; } - public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + protected override ObjectData GetDehydratableData(NodeFactory factory, bool relocsOnly = false) { ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/GeneratingMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/GeneratingMetadataManager.cs index 3a462b4c9ee8..381a81ba82e0 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/GeneratingMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/GeneratingMetadataManager.cs @@ -24,8 +24,8 @@ public abstract class GeneratingMetadataManager : MetadataManager public GeneratingMetadataManager(CompilerTypeSystemContext typeSystemContext, MetadataBlockingPolicy blockingPolicy, ManifestResourceBlockingPolicy resourceBlockingPolicy, string logFile, StackTraceEmissionPolicy stackTracePolicy, - DynamicInvokeThunkGenerationPolicy invokeThunkGenerationPolicy) - : base(typeSystemContext, blockingPolicy, resourceBlockingPolicy, invokeThunkGenerationPolicy) + DynamicInvokeThunkGenerationPolicy invokeThunkGenerationPolicy, MetadataManagerOptions options) + : base(typeSystemContext, blockingPolicy, resourceBlockingPolicy, invokeThunkGenerationPolicy, options) { _metadataLogFile = logFile; _stackTraceEmissionPolicy = stackTracePolicy; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs index 9e35b08545a2..87d51cd0eeca 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs @@ -36,6 +36,8 @@ public abstract class MetadataManager : ICompilationRootProvider { internal const int MetadataOffsetMask = 0xFFFFFF; + protected readonly MetadataManagerOptions _options; + private byte[] _metadataBlob; private List> _typeMappings; private List> _fieldMappings; @@ -65,15 +67,17 @@ public abstract class MetadataManager : ICompilationRootProvider internal NativeLayoutInfoNode NativeLayoutInfo { get; private set; } public MetadataManager(CompilerTypeSystemContext typeSystemContext, MetadataBlockingPolicy blockingPolicy, - ManifestResourceBlockingPolicy resourceBlockingPolicy, DynamicInvokeThunkGenerationPolicy dynamicInvokeThunkGenerationPolicy) + ManifestResourceBlockingPolicy resourceBlockingPolicy, DynamicInvokeThunkGenerationPolicy dynamicInvokeThunkGenerationPolicy, + MetadataManagerOptions options) { _typeSystemContext = typeSystemContext; _blockingPolicy = blockingPolicy; _resourceBlockingPolicy = resourceBlockingPolicy; _dynamicInvokeThunkGenerationPolicy = dynamicInvokeThunkGenerationPolicy; + _options = options; } - public bool IsDataDehydrated => true; + public bool IsDataDehydrated => (_options & MetadataManagerOptions.DehydrateData) != 0; internal ObjectNode.ObjectData PrepareForDehydration(DehydratableObjectNode node, ObjectNode.ObjectData hydratedData) { @@ -872,4 +876,10 @@ public enum MetadataCategory Description = 0x01, RuntimeMapping = 0x02, } + + [Flags] + public enum MetadataManagerOptions + { + DehydrateData = 0x01, + } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index f5e9126850d8..6ecd1041fe26 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -76,12 +76,13 @@ public sealed class UsageBasedMetadataManager : GeneratingMetadataManager DynamicInvokeThunkGenerationPolicy invokeThunkGenerationPolicy, FlowAnnotations flowAnnotations, UsageBasedMetadataGenerationOptions generationOptions, + MetadataManagerOptions options, Logger logger, IEnumerable> featureSwitchValues, IEnumerable rootEntireAssembliesModules, IEnumerable additionalRootedAssemblies, IEnumerable trimmedAssemblies) - : base(typeSystemContext, blockingPolicy, resourceBlockingPolicy, logFile, stackTracePolicy, invokeThunkGenerationPolicy) + : base(typeSystemContext, blockingPolicy, resourceBlockingPolicy, logFile, stackTracePolicy, invokeThunkGenerationPolicy, options) { _compilationModuleGroup = group; _generationOptions = generationOptions; @@ -962,7 +963,7 @@ public MetadataManager ToAnalysisBasedMetadataManager() return new AnalysisBasedMetadataManager( _typeSystemContext, _blockingPolicy, _resourceBlockingPolicy, _metadataLogFile, _stackTraceEmissionPolicy, _dynamicInvokeThunkGenerationPolicy, _modulesWithMetadata, reflectableTypes.ToEnumerable(), reflectableMethods.ToEnumerable(), - reflectableFields.ToEnumerable(), _customAttributesWithMetadata, rootedCctorContexts); + reflectableFields.ToEnumerable(), _customAttributesWithMetadata, rootedCctorContexts, _options); } private void AddDataflowDependency(ref DependencyList dependencies, NodeFactory factory, MethodIL methodIL, string reason) diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index 91c8a13369f7..edea8de21d16 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -334,6 +334,10 @@ public int Run() var flowAnnotations = new ILLink.Shared.TrimAnalysis.FlowAnnotations(logger, ilProvider, compilerGeneratedState); + MetadataManagerOptions metadataOptions = default; + if (Get(_command.Dehydrate)) + metadataOptions |= MetadataManagerOptions.DehydrateData; + MetadataManager metadataManager = new UsageBasedMetadataManager( compilationGroup, typeSystemContext, @@ -344,6 +348,7 @@ public int Run() invokeThunkGenerationPolicy, flowAnnotations, metadataGenerationOptions, + metadataOptions, logger, featureSwitches, Get(_command.ConditionallyRootedAssemblies), From f1e2b3d367cbc4f7b0a3aba40c2e2655b0b4495c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Mon, 14 Nov 2022 17:09:37 +0900 Subject: [PATCH 3/5] Fix tests --- .../aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs index b86d53165e51..e70aca84829c 100644 --- a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs +++ b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs @@ -70,6 +70,7 @@ public void Trim (ILCompilerOptions options, ILogWriter logWriter) new NoDynamicInvokeThunkGenerationPolicy (), new FlowAnnotations (logger, ilProvider, compilerGeneratedState), UsageBasedMetadataGenerationOptions.ReflectionILScanning, + options: default, logger, Array.Empty> (), Array.Empty (), From 2a8111a242ab0c0a4797ba543ed83063e611f23a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Wed, 16 Nov 2022 16:30:08 +0900 Subject: [PATCH 4/5] Apply suggestions from code review Co-authored-by: Sven Boemer --- src/coreclr/tools/Common/Internal/Runtime/DehydratedData.cs | 2 +- .../Compiler/DependencyAnalysis/DehydratedDataNode.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/tools/Common/Internal/Runtime/DehydratedData.cs b/src/coreclr/tools/Common/Internal/Runtime/DehydratedData.cs index ca632e0c25c7..b291df6da95a 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/DehydratedData.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/DehydratedData.cs @@ -11,7 +11,7 @@ namespace Internal.Runtime /// /// The instructions use a variable length encoding and are split in two parts: /// the instruction command kind and command data (payload). - /// The payload is an integer. Instruction kind and payload can fit into a single + /// The payload is an integer. If the instruction kind and payload can fit into a single /// byte, the encoding is one byte. Bigger payloads produce bigger instructions. /// internal static class DehydratedDataCommand diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DehydratedDataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DehydratedDataNode.cs index e6a144e1dd5c..a40eb4b40738 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DehydratedDataNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DehydratedDataNode.cs @@ -97,7 +97,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) var relocs = new Dictionary(relocSort); // Walk all the ObjectDatas and generate the dehydrated instruction stream. - byte[] buff = new byte[5]; + byte[] buff = new byte[4]; int dehydratedSegmentPosition = 0; foreach (ObjectData o in factory.MetadataManager.GetDehydratableData()) { @@ -138,7 +138,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) // comes first). while (bytesToCopy > 0) { - // Try to identify a continuos run of zeros. If we find one, split this run of + // Try to identify a continuous run of zeros. If we find one, split this run of // bytes into chunks of data interleaved with zero fill instructions. int chunkLength = 0; int numZeros = 0; From 24eb4a5c46329175d8ba7f9dd0d47826295f27ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 18 Nov 2022 14:49:27 +0900 Subject: [PATCH 5/5] FB --- src/coreclr/tools/Common/Internal/Runtime/DehydratedData.cs | 4 +++- .../aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/Common/Internal/Runtime/DehydratedData.cs b/src/coreclr/tools/Common/Internal/Runtime/DehydratedData.cs index b291df6da95a..8124fd8e1cd4 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/DehydratedData.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/DehydratedData.cs @@ -1,6 +1,7 @@ // 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 Debug = System.Diagnostics.Debug; namespace Internal.Runtime @@ -48,7 +49,8 @@ public static int Encode(int command, int commandData, byte[] buffer) int numExtraBytes = 0; for (; remainingData != 0; remainingData >>= 8) buffer[++numExtraBytes] = (byte)remainingData; - Debug.Assert(numExtraBytes <= MaxExtraPayloadBytes, "Decoder assumes this"); + if (numExtraBytes > MaxExtraPayloadBytes) + throw new InvalidOperationException(); // decoder can only decode this many extra bytes buffer[0] = (byte)(command | ((MaxShortPayload + numExtraBytes) << DehydratedDataCommandPayloadShift)); return 1 + numExtraBytes; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs index b382e3babb8d..b05ed634686e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs @@ -73,7 +73,7 @@ public void TestDependencyGraphInvariants(EcmaMethod method) new FullyBlockedMetadataBlockingPolicy(), new FullyBlockedManifestResourceBlockingPolicy(), null, new NoStackTraceEmissionPolicy(), new NoDynamicInvokeThunkGenerationPolicy(), new ILLink.Shared.TrimAnalysis.FlowAnnotations(Logger.Null, ilProvider, compilerGeneratedState), UsageBasedMetadataGenerationOptions.None, - Logger.Null, Array.Empty>(), Array.Empty(), Array.Empty(), Array.Empty()); + default, Logger.Null, Array.Empty>(), Array.Empty(), Array.Empty(), Array.Empty()); CompilationBuilder builder = new RyuJitCompilationBuilder(context, compilationGroup) .UseILProvider(ilProvider);