diff options
author | Yi Zhang (CLR) <yizhang82@users.noreply.github.com> | 2017-05-21 07:02:40 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-05-21 07:02:40 +0300 |
commit | 8eb9ffe39300ca9647546580d1ee071a0c8de34d (patch) | |
tree | 319f1eb3e566808b85d3f5b9463b3d544681bfb8 /src | |
parent | 422c171ff9a8e38c3e349d17c2e1344042258ff1 (diff) |
Initial support for preinitialized array in CoreRT (#3558)
* Add PreInitFieldInfo that extracts preinitialized data information from fields that have [PreInitialized] and [InitDataBlob] pointing to RVA field
* Added FrozenArrayNode for frozen arrays with data extracted from preinit RVA data field
* Added GCStaticsPreInitDataNode (CoreRT only) to hold GC pointers for GC static fields. Pre-init data fields points to frozen data, while other data initialized to null.
* Changed GCStaticsNode to emit a pointer reloc to GCStaticspreInitDataNode and mask 0x2 for the GC static EEType.
* Changed InitializedStatics in StartupCodeHelper to check for 0x2 and memcpy the GC field data as needed
* Fixed a bug in bad GCStaticsEETypeNode size calculation - it's off-by-1 as static GC static field layout in CoreRT includes EEType already.
Diffstat (limited to 'src')
14 files changed, 474 insertions, 7 deletions
diff --git a/src/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs b/src/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs index e56a9d4dd..cf213e46a 100644 --- a/src/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs +++ b/src/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs @@ -144,6 +144,7 @@ namespace Internal.Runtime.CompilerHelpers private static unsafe void InitializeStatics(IntPtr gcStaticRegionStart, int length) { IntPtr gcStaticRegionEnd = (IntPtr)((byte*)gcStaticRegionStart + length); + for (IntPtr* block = (IntPtr*)gcStaticRegionStart; block < (IntPtr*)gcStaticRegionEnd; block++) { // Gc Static regions can be shared by modules linked together during compilation. To ensure each @@ -151,9 +152,21 @@ namespace Internal.Runtime.CompilerHelpers // 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; - if (((*pBlock).ToInt64() & 0x1L) == 1) + long blockAddr = (*pBlock).ToInt64(); + if ((blockAddr & GCStaticRegionConstants.Uninitialized) == GCStaticRegionConstants.Uninitialized) { - object obj = RuntimeImports.RhNewObject(new EETypePtr(new IntPtr((*pBlock).ToInt64() & ~0x1L))); + object obj = RuntimeImports.RhNewObject(new EETypePtr(new IntPtr(blockAddr & ~GCStaticRegionConstants.Mask))); + + if ((blockAddr & GCStaticRegionConstants.HasPreInitializedData) == GCStaticRegionConstants.HasPreInitializedData) + { + // The next pointer is preinitialized data blob that contains preinitialized static GC fields, + // 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); + RuntimeImports.RhBulkMoveWithWriteBarrier(ref obj.GetRawData(), ref *(byte *)pPreInitDataAddr, obj.GetRawDataSize()); + } + *pBlock = RuntimeImports.RhHandleAlloc(obj, GCHandleType.Normal); } } diff --git a/src/Common/src/Internal/Runtime/RuntimeConstants.cs b/src/Common/src/Internal/Runtime/RuntimeConstants.cs index d4ce52b4b..800f43883 100644 --- a/src/Common/src/Internal/Runtime/RuntimeConstants.cs +++ b/src/Common/src/Internal/Runtime/RuntimeConstants.cs @@ -27,4 +27,22 @@ namespace Internal.Runtime /// </summary> public const uint RVAPointsToIndirection = 0x80000000u; } + + internal static class GCStaticRegionConstants + { + /// <summary> + /// Flag set if the corresponding GCStatic entry has not yet been initialized and + /// the corresponding EEType pointer has been changed into a instance pointer of + /// that EEType. + /// </summary> + public const int Uninitialized = 0x1; + + /// <summary> + /// Flag set if the next pointer loc points to GCStaticsPreInitDataNode. + /// Otherise it is the next GCStatic entry. + /// </summary> + public const int HasPreInitializedData = 0x2; + + public const int Mask = Uninitialized | HasPreInitializedData; + } } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FrozenArrayNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FrozenArrayNode.cs new file mode 100644 index 000000000..d25273599 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FrozenArrayNode.cs @@ -0,0 +1,93 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler.DependencyAnalysis +{ + /// <summary> + /// Represents a frozen array + /// </summary> + public class FrozenArrayNode : EmbeddedObjectNode, ISymbolDefinitionNode + { + private PreInitFieldInfo _preInitFieldInfo; + + public FrozenArrayNode(PreInitFieldInfo preInitFieldInfo) + { + _preInitFieldInfo = preInitFieldInfo; + } + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix).Append("__FrozenArr_") + .Append(nameMangler.GetMangledFieldName(_preInitFieldInfo.Field)); + } + + public override bool StaticDependenciesAreComputed => true; + + int ISymbolNode.Offset => 0; + + int ISymbolDefinitionNode.Offset + { + get + { + // The frozen array symbol points at the EEType portion of the object, skipping over the sync block + return OffsetFromBeginningOfArray + _preInitFieldInfo.Field.Context.Target.PointerSize; + } + } + + private IEETypeNode GetEETypeNode(NodeFactory factory) + { + var fieldType = _preInitFieldInfo.Field.FieldType; + var node = factory.ConstructedTypeSymbol(fieldType); + Debug.Assert(!node.RepresentsIndirectionCell); // Array are always local + return node; + } + + public override void EncodeData(ref ObjectDataBuilder dataBuilder, NodeFactory factory, bool relocsOnly) + { + // Sync Block + dataBuilder.EmitZeroPointer(); + + // EEType + dataBuilder.EmitPointerReloc(GetEETypeNode(factory)); + + // numComponents + dataBuilder.EmitInt(_preInitFieldInfo.Length); + + int pointerSize = _preInitFieldInfo.Field.Context.Target.PointerSize; + Debug.Assert(pointerSize == 8 || pointerSize == 4); + + if (pointerSize == 8) + { + // padding numComponents in 64-bit + dataBuilder.EmitInt(0); + } + + // byte contents + dataBuilder.EmitBytes(_preInitFieldInfo.Data); + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory) + { + return new DependencyListEntry[] + { + new DependencyListEntry(GetEETypeNode(factory), "Frozen preinitialized array"), + }; + } + + protected override void OnMarked(NodeFactory factory) + { + factory.FrozenSegmentRegion.AddEmbeddedObject(this); + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticEETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticEETypeNode.cs index 804a863d4..7df1ca127 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticEETypeNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticEETypeNode.cs @@ -68,8 +68,9 @@ namespace ILCompiler.DependencyAnalysis dataBuilder.RequireInitialPointerAlignment(); dataBuilder.AddSymbol(this); - // +2 for SyncBlock and EETypePtr field - int totalSize = (_gcMap.Size + 2) * _target.PointerSize; + // +1 for SyncBlock (in CoreRT static size already includes EEType) + Debug.Assert(factory.Target.Abi == TargetAbi.CoreRT); + int totalSize = (_gcMap.Size + 1) * _target.PointerSize; // We only need to check for containsPointers because ThreadStatics are always allocated // on the GC heap (no matter what "HasGCStaticBase" says). diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsNode.cs index 2aec75c4c..62209b420 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsNode.cs @@ -2,21 +2,26 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; + using Internal.Text; using Internal.TypeSystem; using Debug = System.Diagnostics.Debug; +using GCStaticRegionConstants = Internal.Runtime.GCStaticRegionConstants; namespace ILCompiler.DependencyAnalysis { public class GCStaticsNode : ObjectNode, IExportableSymbolNode { private MetadataType _type; + private List<PreInitFieldInfo> _preInitFieldInfos; public GCStaticsNode(MetadataType type) { Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Specific)); _type = type; + _preInitFieldInfos = PreInitFieldInfo.GetPreInitFieldInfos(_type); } protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); @@ -42,10 +47,16 @@ namespace ILCompiler.DependencyAnalysis return factory.GCStaticEEType(map); } + public GCStaticsPreInitDataNode NewPreInitDataNode() + { + Debug.Assert(_preInitFieldInfos != null); + return new GCStaticsPreInitDataNode(_type, _preInitFieldInfos); + } + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) { DependencyList dependencyList = new DependencyList(); - + if (factory.TypeSystemContext.HasEagerStaticConstructor(_type)) { dependencyList.Add(factory.EagerCctorIndirection(_type.GetStaticConstructor()), "Eager .cctor"); @@ -55,7 +66,10 @@ namespace ILCompiler.DependencyAnalysis if (factory.Target.Abi == TargetAbi.CoreRT) { dependencyList.Add(GetGCStaticEETypeNode(factory), "GCStatic EEType"); + if (_preInitFieldInfos != null) + dependencyList.Add(factory.GCStaticsPreInitDataNode(_type), "PreInitData node"); } + dependencyList.Add(factory.GCStaticIndirection(_type), "GC statics indirection"); return dependencyList; } @@ -73,11 +87,22 @@ namespace ILCompiler.DependencyAnalysis if (factory.Target.Abi == TargetAbi.CoreRT) { - builder.EmitPointerReloc(GetGCStaticEETypeNode(factory), 1); + int delta = GCStaticRegionConstants.Uninitialized; + + // Set the flag that indicates next pointer following EEType is the preinit data + if (_preInitFieldInfos != null) + delta |= GCStaticRegionConstants.HasPreInitializedData; + + builder.EmitPointerReloc(GetGCStaticEETypeNode(factory), delta); + + if (_preInitFieldInfos != null) + builder.EmitPointerReloc(factory.GCStaticsPreInitDataNode(_type)); } else { builder.RequireInitialAlignment(_type.GCStaticFieldAlignment.AsInt); + + // @TODO - emit the frozen array node reloc builder.EmitZeros(_type.GCStaticFieldSize.AsInt); } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsPreInitDataNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsPreInitDataNode.cs new file mode 100644 index 000000000..b547bca6f --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsPreInitDataNode.cs @@ -0,0 +1,109 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; + +using Internal.Text; +using Internal.TypeSystem; + +using Debug = System.Diagnostics.Debug; + +namespace ILCompiler.DependencyAnalysis +{ + /// <summary> + /// Contains all GC static fields for a particular EEType. + /// Fields that have preinitialized data are pointer reloc pointing to frozen objects. + /// Other fields are initialized with 0. + /// We simply memcpy these over the GC static EEType object. + /// </summary> + public class GCStaticsPreInitDataNode : ObjectNode, ISymbolDefinitionNode + { + private MetadataType _type; + private List<PreInitFieldInfo> _sortedPreInitFields; + + public GCStaticsPreInitDataNode(MetadataType type, List<PreInitFieldInfo> preInitFields) + { + Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Specific)); + _type = type; + + // sort the PreInitFieldInfo to appear in increasing offset order for easier emitting + _sortedPreInitFields = new List<PreInitFieldInfo>(preInitFields); + _sortedPreInitFields.Sort(FieldDescCompare); + } + + static int FieldDescCompare(PreInitFieldInfo fieldInfo1, PreInitFieldInfo fieldInfo2) + { + return fieldInfo1.Field.Offset.AsInt - fieldInfo2.Field.Offset.AsInt; + } + + protected override string GetName(NodeFactory factory) => GetMangledName(_type, factory.NameMangler); + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(GetMangledName(_type, nameMangler)); + } + + public int Offset => 0; + public MetadataType Type => _type; + + public static string GetMangledName(TypeDesc type, NameMangler nameMangler) + { + return nameMangler.NodeMangler.GCStatics(type) + "__PreInitData"; + } + + public override bool StaticDependenciesAreComputed => true; + + public override ObjectNodeSection Section => ObjectNodeSection.ReadOnlyDataSection; + public override bool IsShareable => EETypeNode.IsTypeNodeShareable(_type); + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + // We only need this for CoreRT (at least for now) as we emit static field value directly in GCStaticsNode for N + Debug.Assert(factory.Target.Abi == TargetAbi.CoreRT); + + return GetDataForPreInitDataField( + this, _type, _sortedPreInitFields, + factory.Target.PointerSize, // CoreRT static size calculation includes EEType - skip it + factory, relocsOnly); + } + + public static ObjectData GetDataForPreInitDataField( + ISymbolDefinitionNode node, + MetadataType _type, List<PreInitFieldInfo> sortedPreInitFields, + int startOffset, + NodeFactory factory, bool relocsOnly = false) + { + ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); + + builder.RequireInitialAlignment(_type.GCStaticFieldAlignment.AsInt); + + int staticOffset = startOffset; + int staticOffsetEnd = _type.GCStaticFieldSize.AsInt; + int idx = 0; + + while (staticOffset < staticOffsetEnd) + { + int writeTo = staticOffsetEnd; + if (idx < sortedPreInitFields.Count) + writeTo = sortedPreInitFields[idx].Field.Offset.AsInt; + + // Emit the zero before the next preinitField + builder.EmitZeros(writeTo - staticOffset); + staticOffset = writeTo; + + // Emit a pointer reloc to the frozen data + if (idx < sortedPreInitFields.Count) + { + builder.EmitPointerReloc(factory.SerializedFrozenArray(sortedPreInitFields[idx])); + idx++; + staticOffset += factory.Target.PointerSize; + } + } + + builder.AddSymbol(node); + + return builder.ToObjectData(); + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs index 4e4b35357..139204519 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs @@ -235,6 +235,13 @@ namespace ILCompiler.DependencyAnalysis } }); + _GCStaticsPreInitDataNodes = new NodeCache<MetadataType, GCStaticsPreInitDataNode>((MetadataType type) => + { + ISymbolNode gcStaticsNode = TypeGCStaticsSymbol(type); + Debug.Assert(gcStaticsNode is GCStaticsNode); + return ((GCStaticsNode)gcStaticsNode).NewPreInitDataNode(); + }); + _GCStaticIndirectionNodes = new NodeCache<MetadataType, EmbeddedObjectNode>((MetadataType type) => { ISymbolNode gcStaticsNode = TypeGCStaticsSymbol(type); @@ -349,6 +356,11 @@ namespace ILCompiler.DependencyAnalysis return new FrozenStringNode(data, Target); }); + _frozenArrayNodes = new NodeCache<PreInitFieldInfo, FrozenArrayNode>((PreInitFieldInfo fieldInfo) => + { + return new FrozenArrayNode(fieldInfo); + }); + _interfaceDispatchCells = new NodeCache<DispatchCellKey, InterfaceDispatchCellNode>(callSiteCell => { return new InterfaceDispatchCellNode(callSiteCell.Target, callSiteCell.CallsiteId); @@ -512,6 +524,13 @@ namespace ILCompiler.DependencyAnalysis return _GCStatics.GetOrAdd(type); } + private NodeCache<MetadataType, GCStaticsPreInitDataNode> _GCStaticsPreInitDataNodes; + + public GCStaticsPreInitDataNode GCStaticsPreInitDataNode(MetadataType type) + { + return _GCStaticsPreInitDataNodes.GetOrAdd(type); + } + private NodeCache<MetadataType, EmbeddedObjectNode> _GCStaticIndirectionNodes; public EmbeddedObjectNode GCStaticIndirection(MetadataType type) @@ -842,6 +861,13 @@ namespace ILCompiler.DependencyAnalysis return _frozenStringNodes.GetOrAdd(data); } + private NodeCache<PreInitFieldInfo, FrozenArrayNode> _frozenArrayNodes; + + public FrozenArrayNode SerializedFrozenArray(PreInitFieldInfo preInitFieldInfo) + { + return _frozenArrayNodes.GetOrAdd(preInitFieldInfo); + } + private NodeCache<MethodDesc, EmbeddedObjectNode> _eagerCctorIndirectionNodes; public EmbeddedObjectNode EagerCctorIndirection(MethodDesc cctorMethod) @@ -892,7 +918,7 @@ namespace ILCompiler.DependencyAnalysis "__DispatchMapTableEnd", null); - public ArrayOfEmbeddedDataNode<FrozenStringNode> FrozenSegmentRegion = new ArrayOfFrozenObjectsNode<FrozenStringNode>( + public ArrayOfEmbeddedDataNode<EmbeddedObjectNode> FrozenSegmentRegion = new ArrayOfFrozenObjectsNode<EmbeddedObjectNode>( "__FrozenSegmentRegionStart", "__FrozenSegmentRegionEnd", null); diff --git a/src/ILCompiler.Compiler/src/Compiler/PreInitFieldInfo.cs b/src/ILCompiler.Compiler/src/Compiler/PreInitFieldInfo.cs new file mode 100644 index 000000000..db5f67dcc --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/PreInitFieldInfo.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. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using Debug = System.Diagnostics.Debug; + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler +{ + public class PreInitFieldInfo + { + public FieldDesc Field { get; } + + /// <summary> + /// Points to the underlying contents of the data. + /// </summary> + public byte[] Data { get; } + + /// <summary> + /// Number of elements, if this is a frozen array. + /// </summary> + public int Length { get; } + + public PreInitFieldInfo(FieldDesc field, byte[] data, int length) + { + Field = field; + Data = data; + Length = length; + } + + public static List<PreInitFieldInfo> GetPreInitFieldInfos(TypeDesc type) + { + List<PreInitFieldInfo> list = null; + + foreach (var field in type.GetFields()) + { + if (!field.IsStatic) + continue; + + var dataField = GetPreInitDataField(field); + if (dataField != null) + { + if (list == null) + list = new List<PreInitFieldInfo>(); + list.Add(ConstructPreInitFieldInfo(field, dataField)); + } + } + + return list; + } + + /// <summary> + /// Retrieves the corresponding static preinitialized data field by looking at various attributes + /// </summary> + private static FieldDesc GetPreInitDataField(FieldDesc thisField) + { + Debug.Assert(thisField.IsStatic); + + var field = thisField as EcmaField; + if (field == null) + return null; + + if (!field.HasCustomAttribute("System.Runtime.CompilerServices", "PreInitializedAttribute")) + return null; + + var decoded = field.GetDecodedCustomAttribute("System.Runtime.CompilerServices", "InitDataBlobAttribute"); + if (decoded == null) + return null; + + var decodedValue = decoded.Value; + if (decodedValue.FixedArguments.Length != 2) + return null; + + var typeDesc = decodedValue.FixedArguments[0].Value as TypeDesc; + if (typeDesc == null) + return null; + + if (decodedValue.FixedArguments[1].Type != field.Context.GetWellKnownType(WellKnownType.String)) + return null; + + var fieldName = (string)decodedValue.FixedArguments[1].Value; + return typeDesc.GetField(fieldName); + } + + /// <summary> + /// Extract preinitialize data as byte[] from a RVA field, and perform necessary validations. + /// </summary> + private static PreInitFieldInfo ConstructPreInitFieldInfo(FieldDesc field, FieldDesc dataField) + { + var arrType = field.FieldType as ArrayType; + if (arrType == null || !arrType.IsSzArray) + { + // We only support single dimensional arrays + throw new NotSupportedException(); + } + + if (!dataField.HasRva) + throw new BadImageFormatException(); + + var ecmaDataField = dataField as EcmaField; + if (ecmaDataField == null) + throw new NotSupportedException(); + + var rvaData = ecmaDataField.GetFieldRvaData(); + int elementSize = arrType.ElementType.GetElementSize().AsInt; + if (rvaData.Length % elementSize != 0) + throw new BadImageFormatException(); + + return new PreInitFieldInfo(field, rvaData, rvaData.Length / elementSize); + } + } +} diff --git a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj index 8c8ac269d..966f7e972 100644 --- a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj +++ b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj @@ -113,6 +113,9 @@ <Compile Include="Compiler\CompilerTypeSystemContext.Mangling.cs" /> <Compile Include="Compiler\CompilerTypeSystemContext.Sorting.cs" /> <Compile Include="Compiler\DependencyAnalysis\ILScanNodeFactory.cs" /> + <Compile Include="Compiler\PreInitFieldInfo.cs" /> + <Compile Include="Compiler\DependencyAnalysis\FrozenArrayNode.cs" /> + <Compile Include="Compiler\DependencyAnalysis\GCStaticsPreInitDataNode.cs" /> <Compile Include="Compiler\DependencyAnalysis\ImportedGenericDictionaryNode.cs" /> <Compile Include="Compiler\DependencyAnalysis\DynamicInvokeTemplateDataNode.cs" /> <Compile Include="Compiler\DependencyAnalysis\ReflectableMethodNode.cs" /> diff --git a/src/Runtime.Base/src/System/Object.cs b/src/Runtime.Base/src/System/Object.cs index 7d5475228..2e541f303 100644 --- a/src/Runtime.Base/src/System/Object.cs +++ b/src/Runtime.Base/src/System/Object.cs @@ -19,6 +19,7 @@ using System.Runtime.InteropServices; // TODO: remove when m_pEEType becomes EETypePtr using EEType = Internal.Runtime.EEType; +using ObjHeader = Internal.Runtime.ObjHeader; namespace System { @@ -69,15 +70,36 @@ namespace System } } + internal EETypePtr EETypePtr + { + get + { + return new EETypePtr(new IntPtr(m_pEEType)); + } + } + [StructLayout(LayoutKind.Sequential)] private class RawData { public byte Data; } + /// <summary> + /// Return beginning of all data (excluding ObjHeader and EEType*) within this object. + /// Note that for strings/arrays this would include the Length as well. + /// </summary> internal ref byte GetRawData() { return ref Unsafe.As<RawData>(this).Data; } + + /// <summary> + /// Return size of all data (excluding ObjHeader and EEType*). + /// Note that for strings/arrays this would include the Length as well. + /// </summary> + internal uint GetRawDataSize() + { + return EETypePtr.BaseSize - (uint)sizeof(ObjHeader) - (uint)sizeof(EEType*); + } } } diff --git a/src/Runtime.Base/src/System/Runtime/EETypePtr.cs b/src/Runtime.Base/src/System/Runtime/EETypePtr.cs index b8bc5e4cd..26355a84a 100644 --- a/src/Runtime.Base/src/System/Runtime/EETypePtr.cs +++ b/src/Runtime.Base/src/System/Runtime/EETypePtr.cs @@ -49,6 +49,14 @@ namespace System throw new NotImplementedException(); } #endif + + internal unsafe uint BaseSize + { + get + { + return ToPointer()->BaseSize; + } + } } } diff --git a/src/System.Private.CoreLib/src/System/Object.cs b/src/System.Private.CoreLib/src/System/Object.cs index 22dc37a1b..7207d24f6 100644 --- a/src/System.Private.CoreLib/src/System/Object.cs +++ b/src/System.Private.CoreLib/src/System/Object.cs @@ -22,6 +22,7 @@ using Internal.Reflection.Core.NonPortable; #if INPLACE_RUNTIME using EEType = Internal.Runtime.EEType; +using ObjHeader = Internal.Runtime.ObjHeader; #endif namespace System @@ -152,6 +153,15 @@ namespace System { return ref Unsafe.As<RawData>(this).Data; } + + /// <summary> + /// Return size of all data (excluding ObjHeader and EEType*). + /// Note that for strings/arrays this would include the Length as well. + /// </summary> + internal uint GetRawDataSize() + { + return EETypePtr.BaseSize - (uint)sizeof(ObjHeader) - (uint)sizeof(EEType*); + } } } diff --git a/src/Test.CoreLib/src/System/Object.cs b/src/Test.CoreLib/src/System/Object.cs index a6addf96b..60777622e 100644 --- a/src/Test.CoreLib/src/System/Object.cs +++ b/src/Test.CoreLib/src/System/Object.cs @@ -19,6 +19,7 @@ using System.Runtime.InteropServices; // TODO: remove when m_pEEType becomes EETypePtr using EEType = Internal.Runtime.EEType; +using ObjHeader = Internal.Runtime.ObjHeader; namespace System { @@ -89,5 +90,14 @@ namespace System { return ref Unsafe.As<RawData>(this).Data; } + + /// <summary> + /// Return size of all data (excluding ObjHeader and EEType*). + /// Note that for strings/arrays this would include the Length as well. + /// </summary> + internal unsafe uint GetRawDataSize() + { + return EEType->BaseSize - (uint)sizeof(ObjHeader) - (uint)sizeof(EEType*); + } } } diff --git a/src/Test.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/Test.CoreLib/src/System/Runtime/RuntimeImports.cs index ecdda3bdc..014fe31b5 100644 --- a/src/Test.CoreLib/src/System/Runtime/RuntimeImports.cs +++ b/src/Test.CoreLib/src/System/Runtime/RuntimeImports.cs @@ -8,6 +8,12 @@ using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using Internal.Runtime; +#if BIT64 +using nuint = System.UInt64; +#else +using nuint = System.UInt32; +#endif + namespace System.Runtime { // CONTRACT with Runtime @@ -96,5 +102,12 @@ namespace System.Runtime [MethodImplAttribute(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhpMemoryBarrier")] internal extern static void MemoryBarrier(); + + // Moves memory from smem to dmem. Size must be a positive value. + // This copy uses an intrinsic to be safe for copying arbitrary bits of + // heap memory + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhBulkMoveWithWriteBarrier")] + internal static extern unsafe void RhBulkMoveWithWriteBarrier(ref byte dmem, ref byte smem, nuint size); } } |