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/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 64926e9eecae..b1e8c4cee8e4 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..010732668932 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
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..8124fd8e1cd4
--- /dev/null
+++ b/src/coreclr/tools/Common/Internal/Runtime/DehydratedData.cs
@@ -0,0 +1,118 @@
+// 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
+{
+ ///
+ /// 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. 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
+ {
+ 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;
+ if (numExtraBytes > MaxExtraPayloadBytes)
+ throw new InvalidOperationException(); // decoder can only decode this many extra bytes
+
+ 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.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);
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/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..a40eb4b40738
--- /dev/null
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DehydratedDataNode.cs
@@ -0,0 +1,243 @@
+// 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 => 0xbd80526;
+
+ 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);
+ 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();
+ 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[4];
+ int dehydratedSegmentPosition = 0;
+ foreach (ObjectData o in factory.MetadataManager.GetDehydratableData())
+ {
+ 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 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;
+ 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);
+
+ // 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);
+
+ 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/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/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/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/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/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 b63026dae37a..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;
@@ -60,15 +62,45 @@ 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,
- ManifestResourceBlockingPolicy resourceBlockingPolicy, DynamicInvokeThunkGenerationPolicy dynamicInvokeThunkGenerationPolicy)
+ ManifestResourceBlockingPolicy resourceBlockingPolicy, DynamicInvokeThunkGenerationPolicy dynamicInvokeThunkGenerationPolicy,
+ MetadataManagerOptions options)
{
_typeSystemContext = typeSystemContext;
_blockingPolicy = blockingPolicy;
_resourceBlockingPolicy = resourceBlockingPolicy;
_dynamicInvokeThunkGenerationPolicy = dynamicInvokeThunkGenerationPolicy;
+ _options = options;
+ }
+
+ public bool IsDataDehydrated => (_options & MetadataManagerOptions.DehydrateData) != 0;
+
+ 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)
@@ -155,6 +187,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)
@@ -838,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 f407ec5fed91..19b9c9c5594c 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;
@@ -965,7 +966,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.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj
index 66fd2256b6d6..5887e5d4ff2a 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
@@ -332,6 +335,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);
diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs
index fa843edaf500..15a18a20eee6 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),
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 (),