From fefc1f80dff6779c0863401d1e50229d8f12cdf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 24 Nov 2022 12:04:17 +0900 Subject: [PATCH] Remove pointer relocs from GC statics --- .../CompilerHelpers/StartupCodeHelpers.cs | 11 ++++-- .../DependencyAnalysis/GCStaticsNode.cs | 37 +++++++++++++------ .../DependencyAnalysis/NodeFactory.cs | 14 ------- 3 files changed, 33 insertions(+), 29 deletions(-) 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 341e6baf0d5b5..b906a335fe16d 100644 --- a/src/coreclr/nativeaot/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs +++ b/src/coreclr/nativeaot/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs @@ -188,14 +188,14 @@ private static unsafe object[] InitializeStatics(IntPtr gcStaticRegionStart, int ref object rawSpineData = ref Unsafe.As(ref Unsafe.As(spine).Data); int currentBase = 0; - for (IntPtr* block = (IntPtr*)gcStaticRegionStart; block < (IntPtr*)gcStaticRegionEnd; block++) + for (IntPtr** block = (IntPtr**)gcStaticRegionStart; block < (IntPtr**)gcStaticRegionEnd; block++) { // Gc Static regions can be shared by modules linked together during compilation. To ensure each // is initialized once, the static region pointer is stored with lowest bit set in the image. // The first time we initialize the static region its pointer is replaced with an object reference // whose lowest bit is no longer set. - IntPtr* pBlock = (IntPtr*)*block; - nint blockAddr = *pBlock; + IntPtr* pBlock = *block; + nint blockAddr = MethodTable.SupportsRelativePointers ? (nint)ReadRelPtr32(pBlock) : *pBlock; if ((blockAddr & GCStaticRegionConstants.Uninitialized) == GCStaticRegionConstants.Uninitialized) { object? obj = null; @@ -215,7 +215,7 @@ private static unsafe object[] InitializeStatics(IntPtr gcStaticRegionStart, int // which are pointer relocs to GC objects in frozen segment. // It actually has all GC fields including non-preinitialized fields and we simply copy over the // entire blob to this object, overwriting everything. - IntPtr pPreInitDataAddr = *(pBlock + 1); + void* pPreInitDataAddr = MethodTable.SupportsRelativePointers ? ReadRelPtr32((int*)pBlock + 1) : (void*)*(pBlock + 1); RuntimeImports.RhBulkMoveWithWriteBarrier(ref obj.GetRawData(), ref *(byte *)pPreInitDataAddr, obj.GetRawObjectDataSize()); } @@ -231,6 +231,9 @@ private static unsafe object[] InitializeStatics(IntPtr gcStaticRegionStart, int } return spine; + + static void* ReadRelPtr32(void* address) + => (byte*)address + *(int*)address; } private static unsafe void RehydrateData(IntPtr dehydratedData, int length) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GCStaticsNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GCStaticsNode.cs index 15bb92230cfad..4bceb9b1de282 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GCStaticsNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GCStaticsNode.cs @@ -12,7 +12,7 @@ namespace ILCompiler.DependencyAnalysis public class GCStaticsNode : ObjectNode, ISymbolDefinitionNode, ISortableSymbolNode { private readonly MetadataType _type; - private readonly TypePreinit.PreinitializationInfo _preinitializationInfo; + private readonly GCStaticsPreInitDataNode _preinitializationInfo; public GCStaticsNode(MetadataType type, PreinitializationManager preinitManager) { @@ -21,7 +21,10 @@ public GCStaticsNode(MetadataType type, PreinitializationManager preinitManager) _type = type; if (preinitManager.IsPreinitialized(type)) - _preinitializationInfo = preinitManager.GetPreinitializationInfo(_type); + { + var info = preinitManager.GetPreinitializationInfo(_type); + _preinitializationInfo = new GCStaticsPreInitDataNode(info); + } } protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); @@ -45,12 +48,6 @@ private ISymbolNode GetGCStaticEETypeNode(NodeFactory factory) return factory.GCStaticEEType(map); } - public GCStaticsPreInitDataNode NewPreInitDataNode() - { - Debug.Assert(_preinitializationInfo != null && _preinitializationInfo.IsPreinitialized); - return new GCStaticsPreInitDataNode(_preinitializationInfo); - } - protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) { DependencyList dependencyList = new DependencyList(); @@ -79,19 +76,37 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); + // Even though we're only generating 32-bit relocs here (if SupportsRelativePointers), + // align the blob at pointer boundary since at runtime we're going to write a pointer in here. builder.RequireInitialPointerAlignment(); int delta = GCStaticRegionConstants.Uninitialized; // Set the flag that indicates next pointer following MethodTable is the preinit data - bool isPreinitialized = _preinitializationInfo != null && _preinitializationInfo.IsPreinitialized; + bool isPreinitialized = _preinitializationInfo != null; if (isPreinitialized) delta |= GCStaticRegionConstants.HasPreInitializedData; - builder.EmitPointerReloc(GetGCStaticEETypeNode(factory), delta); + if (factory.Target.SupportsRelativePointers) + builder.EmitReloc(GetGCStaticEETypeNode(factory), RelocType.IMAGE_REL_BASED_RELPTR32, delta); + else + builder.EmitPointerReloc(GetGCStaticEETypeNode(factory), delta); if (isPreinitialized) - builder.EmitPointerReloc(factory.GCStaticsPreInitDataNode(_type)); + { + if (factory.Target.SupportsRelativePointers) + builder.EmitReloc(_preinitializationInfo, RelocType.IMAGE_REL_BASED_RELPTR32); + else + builder.EmitPointerReloc(_preinitializationInfo); + } + else if (factory.Target.SupportsRelativePointers && factory.Target.PointerSize == 8) + { + // At runtime, we replace the EEType pointer with a full pointer to the data on the GC + // heap. If the EEType pointer was 32-bit relative, and we don't have a 32-bit relative + // pointer to the preinit data following it, and the pointer size on the target + // machine is 8, we need to emit additional 4 bytes to make room for the full pointer. + builder.EmitZeros(4); + } builder.AddSymbol(this); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs index efd7f86102b1f..a8a0d2a522e2a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -206,13 +206,6 @@ private void CreateNodeCaches() } }); - _GCStaticsPreInitDataNodes = new NodeCache((MetadataType type) => - { - ISymbolNode gcStaticsNode = TypeGCStaticsSymbol(type); - Debug.Assert(gcStaticsNode is GCStaticsNode); - return ((GCStaticsNode)gcStaticsNode).NewPreInitDataNode(); - }); - _GCStaticIndirectionNodes = new NodeCache((MetadataType type) => { ISymbolNode gcStaticsNode = TypeGCStaticsSymbol(type); @@ -627,13 +620,6 @@ public ISortableSymbolNode TypeGCStaticsSymbol(MetadataType type) return _GCStatics.GetOrAdd(type); } - private NodeCache _GCStaticsPreInitDataNodes; - - public GCStaticsPreInitDataNode GCStaticsPreInitDataNode(MetadataType type) - { - return _GCStaticsPreInitDataNodes.GetOrAdd(type); - } - private NodeCache _GCStaticIndirectionNodes; public EmbeddedObjectNode GCStaticIndirection(MetadataType type)