Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYi Zhang (CLR) <yizhang82@users.noreply.github.com>2017-05-21 07:02:40 +0300
committerGitHub <noreply@github.com>2017-05-21 07:02:40 +0300
commit8eb9ffe39300ca9647546580d1ee071a0c8de34d (patch)
tree319f1eb3e566808b85d3f5b9463b3d544681bfb8
parent422c171ff9a8e38c3e349d17c2e1344042258ff1 (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.
-rw-r--r--src/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs17
-rw-r--r--src/Common/src/Internal/Runtime/RuntimeConstants.cs18
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FrozenArrayNode.cs93
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticEETypeNode.cs5
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsNode.cs29
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsPreInitDataNode.cs109
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs28
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/PreInitFieldInfo.cs116
-rw-r--r--src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj3
-rw-r--r--src/Runtime.Base/src/System/Object.cs22
-rw-r--r--src/Runtime.Base/src/System/Runtime/EETypePtr.cs8
-rw-r--r--src/System.Private.CoreLib/src/System/Object.cs10
-rw-r--r--src/Test.CoreLib/src/System/Object.cs10
-rw-r--r--src/Test.CoreLib/src/System/Runtime/RuntimeImports.cs13
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);
}
}