diff --git a/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.Aot.cs b/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.Aot.cs
index 5ecca71fa86..bd3bb233b12 100644
--- a/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.Aot.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.Aot.cs
@@ -91,7 +91,7 @@ public CompilationBuilder UsePreinitializationManager(PreinitializationManager m
protected PreinitializationManager GetPreinitializationManager()
{
if (_preinitializationManager == null)
- return new PreinitializationManager(_context, _compilationGroup, GetILProvider());
+ return new PreinitializationManager(_context, _compilationGroup, GetILProvider(), enableInterpreter: false);
return _preinitializationManager;
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/CompilerMetadataFieldLayoutAlgorithm.cs b/src/ILCompiler.Compiler/src/Compiler/CompilerMetadataFieldLayoutAlgorithm.cs
index 48daeab7087..dd4a418314f 100644
--- a/src/ILCompiler.Compiler/src/Compiler/CompilerMetadataFieldLayoutAlgorithm.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/CompilerMetadataFieldLayoutAlgorithm.cs
@@ -13,22 +13,27 @@ namespace ILCompiler
{
internal class CompilerMetadataFieldLayoutAlgorithm : MetadataFieldLayoutAlgorithm
{
+ // GC statics start with a pointer to the "EEType" that signals the size and GCDesc to the GC
+ public static LayoutInt GetGCStaticFieldOffset(TypeSystemContext context) => context.Target.LayoutPointerSize;
+
protected override void PrepareRuntimeSpecificStaticFieldLayout(TypeSystemContext context, ref ComputedStaticFieldLayout layout)
{
- // GC statics start with a pointer to the "EEType" that signals the size and GCDesc to the GC
- layout.GcStatics.Size = context.Target.LayoutPointerSize;
- layout.ThreadGcStatics.Size = context.Target.LayoutPointerSize;
+ LayoutInt offset = GetGCStaticFieldOffset(context);
+ layout.GcStatics.Size = offset;
+ layout.ThreadGcStatics.Size = offset;
}
protected override void FinalizeRuntimeSpecificStaticFieldLayout(TypeSystemContext context, ref ComputedStaticFieldLayout layout)
{
+ LayoutInt offset = GetGCStaticFieldOffset(context);
+
// If the size of GCStatics is equal to the size set in PrepareRuntimeSpecificStaticFieldLayout, we
// don't have any GC statics
- if (layout.GcStatics.Size == context.Target.LayoutPointerSize)
+ if (layout.GcStatics.Size == offset)
{
layout.GcStatics.Size = LayoutInt.Zero;
}
- if (layout.ThreadGcStatics.Size == context.Target.LayoutPointerSize)
+ if (layout.ThreadGcStatics.Size == offset)
{
layout.ThreadGcStatics.Size = LayoutInt.Zero;
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FrozenArrayNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FrozenObjectNode.cs
similarity index 56%
rename from src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FrozenArrayNode.cs
rename to src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FrozenObjectNode.cs
index 8d210f5c19e..5b2fed54786 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FrozenArrayNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/FrozenObjectNode.cs
@@ -2,32 +2,32 @@
// 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
{
///
- /// Represents a frozen array
+ /// Represents a frozen object that is statically preallocated within the data section
+ /// of the executable instead of on the GC heap.
///
- public class FrozenArrayNode : EmbeddedObjectNode, ISymbolDefinitionNode
+ public class FrozenObjectNode : EmbeddedObjectNode, ISymbolDefinitionNode
{
- private PreInitFieldInfo _preInitFieldInfo;
+ private readonly FieldDesc _field;
+ private readonly TypePreinit.ISerializableReference _data;
- public FrozenArrayNode(PreInitFieldInfo preInitFieldInfo)
+ public FrozenObjectNode(FieldDesc field, TypePreinit.ISerializableReference data)
{
- _preInitFieldInfo = preInitFieldInfo;
+ _field = field;
+ _data = data;
}
public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
{
- sb.Append(nameMangler.CompilationUnitPrefix).Append("__FrozenArr_")
- .Append(nameMangler.GetMangledFieldName(_preInitFieldInfo.Field));
+ sb.Append(nameMangler.CompilationUnitPrefix).Append("__FrozenObj_")
+ .Append(nameMangler.GetMangledFieldName(_field));
}
public override bool StaticDependenciesAreComputed => true;
@@ -38,41 +38,18 @@ 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;
+ // The frozen object symbol points at the EEType portion of the object, skipping over the sync block
+ return OffsetFromBeginningOfArray + _field.Context.Target.PointerSize;
}
}
- private IEETypeNode GetEETypeNode(NodeFactory factory)
- {
- var fieldType = _preInitFieldInfo.Type;
- 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
- _preInitFieldInfo.WriteData(ref dataBuilder, factory, relocsOnly);
+ _data.WriteContent(ref dataBuilder, factory);
}
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
@@ -105,7 +82,7 @@ protected override void OnMarked(NodeFactory factory)
public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
{
- return _preInitFieldInfo.CompareTo(((FrozenArrayNode)other)._preInitFieldInfo, comparer);
+ return comparer.Compare(((FrozenObjectNode)other)._field, _field);
}
}
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsNode.cs
index e4b5d721492..db1023f1e74 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsNode.cs
@@ -14,14 +14,16 @@ namespace ILCompiler.DependencyAnalysis
{
public class GCStaticsNode : ObjectNode, IExportableSymbolNode, ISortableSymbolNode, ISymbolNodeWithDebugInfo
{
- private MetadataType _type;
- private List _preInitFieldInfos;
+ private readonly MetadataType _type;
+ private readonly TypePreinit.PreinitializationInfo _preinitializationInfo;
- public GCStaticsNode(MetadataType type)
+ public GCStaticsNode(MetadataType type, PreinitializationManager preinitManager)
{
Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Specific));
_type = type;
- _preInitFieldInfos = PreInitFieldInfo.GetPreInitFieldInfos(_type, hasGCStaticBase: true);
+
+ if (preinitManager.IsPreinitialized(type))
+ _preinitializationInfo = preinitManager.GetPreinitializationInfo(_type);
}
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
@@ -51,8 +53,8 @@ private ISymbolNode GetGCStaticEETypeNode(NodeFactory factory)
public GCStaticsPreInitDataNode NewPreInitDataNode()
{
- Debug.Assert(_preInitFieldInfos != null);
- return new GCStaticsPreInitDataNode(_type, _preInitFieldInfos);
+ Debug.Assert(_preinitializationInfo != null && _preinitializationInfo.IsPreinitialized);
+ return new GCStaticsPreInitDataNode(_preinitializationInfo);
}
protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory)
@@ -65,9 +67,6 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact
}
dependencyList.Add(factory.GCStaticsRegion, "GCStatics Region");
- dependencyList.Add(GetGCStaticEETypeNode(factory), "GCStatic EEType");
- if (_preInitFieldInfos != null)
- dependencyList.Add(factory.GCStaticsPreInitDataNode(_type), "PreInitData node");
dependencyList.Add(factory.GCStaticIndirection(_type), "GC statics indirection");
EETypeNode.AddDependenciesForStaticsNode(factory, _type, ref dependencyList);
@@ -82,47 +81,25 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
{
- if (factory.Target.Abi == TargetAbi.CoreRT || factory.Target.Abi == TargetAbi.CppCodegen)
- {
- ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly);
+ ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly);
- builder.RequireInitialPointerAlignment();
+ builder.RequireInitialPointerAlignment();
- int delta = GCStaticRegionConstants.Uninitialized;
+ int delta = GCStaticRegionConstants.Uninitialized;
- // Set the flag that indicates next pointer following EEType is the preinit data
- if (_preInitFieldInfos != null)
- delta |= GCStaticRegionConstants.HasPreInitializedData;
+ // Set the flag that indicates next pointer following EEType is the preinit data
+ bool isPreinitialized = _preinitializationInfo != null && _preinitializationInfo.IsPreinitialized;
+ if (isPreinitialized)
+ delta |= GCStaticRegionConstants.HasPreInitializedData;
- builder.EmitPointerReloc(GetGCStaticEETypeNode(factory), delta);
-
- if (_preInitFieldInfos != null)
- builder.EmitPointerReloc(factory.GCStaticsPreInitDataNode(_type));
+ builder.EmitPointerReloc(GetGCStaticEETypeNode(factory), delta);
- builder.AddSymbol(this);
+ if (isPreinitialized)
+ builder.EmitPointerReloc(factory.GCStaticsPreInitDataNode(_type));
- return builder.ToObjectData();
- }
- else
- {
- if (_preInitFieldInfos == null)
- {
- ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly);
+ builder.AddSymbol(this);
- builder.RequireInitialPointerAlignment();
-
- builder.EmitZeros(_type.GCStaticFieldSize.AsInt);
-
- builder.AddSymbol(this);
-
- return builder.ToObjectData();
- }
- else
- {
- _preInitFieldInfos.Sort(PreInitFieldInfo.FieldDescCompare);
- return GCStaticsPreInitDataNode.GetDataForPreInitDataField(this, _type, _preInitFieldInfos, 0, factory, relocsOnly);
- }
- }
+ return builder.ToObjectData();
}
public override int ClassCode => -522346696;
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsPreInitDataNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsPreInitDataNode.cs
index e78801952ee..2c82481af06 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsPreInitDataNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticsPreInitDataNode.cs
@@ -2,8 +2,6 @@
// 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;
@@ -19,28 +17,23 @@ namespace ILCompiler.DependencyAnalysis
///
public class GCStaticsPreInitDataNode : ObjectNode, ISymbolDefinitionNode
{
- private MetadataType _type;
- private List _sortedPreInitFields;
+ private TypePreinit.PreinitializationInfo _preinitializationInfo;
- public GCStaticsPreInitDataNode(MetadataType type, List preInitFields)
+ public GCStaticsPreInitDataNode(TypePreinit.PreinitializationInfo preinitializationInfo)
{
- Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Specific));
- _type = type;
-
- // sort the PreInitFieldInfo to appear in increasing offset order for easier emitting
- _sortedPreInitFields = new List(preInitFields);
- _sortedPreInitFields.Sort(PreInitFieldInfo.FieldDescCompare);
+ Debug.Assert(!preinitializationInfo.Type.IsCanonicalSubtype(CanonicalFormKind.Specific));
+ _preinitializationInfo = preinitializationInfo;
}
- protected override string GetName(NodeFactory factory) => GetMangledName(_type, factory.NameMangler);
+ protected override string GetName(NodeFactory factory) => GetMangledName(_preinitializationInfo.Type, factory.NameMangler);
public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
{
- sb.Append(GetMangledName(_type, nameMangler));
+ sb.Append(GetMangledName(_preinitializationInfo.Type, nameMangler));
}
public int Offset => 0;
- public MetadataType Type => _type;
+ public MetadataType Type => _preinitializationInfo.Type;
public static string GetMangledName(TypeDesc type, NameMangler nameMangler)
{
@@ -49,66 +42,52 @@ public static string GetMangledName(TypeDesc type, NameMangler nameMangler)
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)
+ public override ObjectNodeSection Section
{
- // 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);
+ get
+ {
+ if (Type.Context.Target.IsWindows)
+ return ObjectNodeSection.ReadOnlyDataSection;
+ else
+ return ObjectNodeSection.DataSection;
+ }
}
+ public override bool IsShareable => EETypeNode.IsTypeNodeShareable(_preinitializationInfo.Type);
- public static ObjectData GetDataForPreInitDataField(
- ISymbolDefinitionNode node,
- MetadataType _type, List sortedPreInitFields,
- int startOffset,
- NodeFactory factory, bool relocsOnly = false)
+ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
{
ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly);
- builder.RequireInitialAlignment(_type.GCStaticFieldAlignment.AsInt);
+ MetadataType type = _preinitializationInfo.Type;
- int staticOffset = startOffset;
- int staticOffsetEnd = _type.GCStaticFieldSize.AsInt;
- int idx = 0;
+ builder.RequireInitialAlignment(factory.Target.PointerSize);
- while (staticOffset < staticOffsetEnd)
+ // GC static fields don't begin at offset 0, need to subtract that.
+ int initialOffset = CompilerMetadataFieldLayoutAlgorithm.GetGCStaticFieldOffset(factory.TypeSystemContext).AsInt;
+
+ foreach (FieldDesc field in type.GetFields())
{
- PreInitFieldInfo fieldInfo = idx < sortedPreInitFields.Count ? sortedPreInitFields[idx] : null;
- int writeTo = staticOffsetEnd;
- if (fieldInfo != null)
- writeTo = fieldInfo.Field.Offset.AsInt;
-
- // Emit the zero before the next preinitField
- builder.EmitZeros(writeTo - staticOffset);
- staticOffset = writeTo;
-
- if (fieldInfo != null)
- {
- int count = builder.CountBytes;
-
- if (fieldInfo.Field.FieldType.IsValueType)
- {
- // Emit inlined data for value types
- fieldInfo.WriteData(ref builder, factory);
- }
- else
- {
- // Emit a pointer reloc to the frozen data for reference types
- builder.EmitPointerReloc(factory.SerializedFrozenArray(fieldInfo));
- }
-
- staticOffset += builder.CountBytes - count;
- idx++;
- }
+ if (!field.IsStatic || field.HasRva || field.IsLiteral || field.IsThreadStatic || !field.HasGCStaticBase)
+ continue;
+
+ int padding = field.Offset.AsInt - initialOffset - builder.CountBytes;
+ Debug.Assert(padding >= 0);
+ builder.EmitZeros(padding);
+
+ TypePreinit.ISerializableValue val = _preinitializationInfo.GetFieldValue(field);
+ int currentOffset = builder.CountBytes;
+ if (val != null)
+ val.WriteFieldData(ref builder, field, factory);
+ else
+ builder.EmitZeroPointer();
+ Debug.Assert(builder.CountBytes - currentOffset == field.FieldType.GetElementSize().AsInt);
}
- builder.AddSymbol(node);
+ int pad = _preinitializationInfo.Type.GCStaticFieldSize.AsInt - builder.CountBytes - initialOffset;
+ Debug.Assert(pad >= 0);
+ builder.EmitZeros(pad);
+
+ builder.AddSymbol(this);
return builder.ToObjectData();
}
@@ -117,7 +96,7 @@ public static ObjectData GetDataForPreInitDataField(
public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
{
- return comparer.Compare(_type, ((GCStaticsPreInitDataNode)other)._type);
+ return comparer.Compare(_preinitializationInfo.Type, ((GCStaticsPreInitDataNode)other)._preinitializationInfo.Type);
}
}
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs
index 28f0c005ea9..dbafc0502f4 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs
@@ -186,7 +186,7 @@ private void CreateNodeCaches()
{
if (_compilationModuleGroup.ContainsType(type) && !_compilationModuleGroup.ShouldReferenceThroughImportTable(type))
{
- return new NonGCStaticsNode(type, this);
+ return new NonGCStaticsNode(type, PreinitializationManager);
}
else
{
@@ -198,7 +198,7 @@ private void CreateNodeCaches()
{
if (_compilationModuleGroup.ContainsType(type) && !_compilationModuleGroup.ShouldReferenceThroughImportTable(type))
{
- return new GCStaticsNode(type);
+ return new GCStaticsNode(type, PreinitializationManager);
}
else
{
@@ -334,9 +334,9 @@ private void CreateNodeCaches()
return new FrozenStringNode(data, Target);
});
- _frozenArrayNodes = new NodeCache((PreInitFieldInfo fieldInfo) =>
+ _frozenObjectNodes = new NodeCache(key =>
{
- return new FrozenArrayNode(fieldInfo);
+ return new FrozenObjectNode(key.Owner, key.SerializableObject);
});
_interfaceDispatchCells = new NodeCache(callSiteCell =>
@@ -988,11 +988,11 @@ public FrozenStringNode SerializedStringObject(string data)
return _frozenStringNodes.GetOrAdd(data);
}
- private NodeCache _frozenArrayNodes;
+ private NodeCache _frozenObjectNodes;
- public FrozenArrayNode SerializedFrozenArray(PreInitFieldInfo preInitFieldInfo)
+ public FrozenObjectNode SerializedFrozenObject(FieldDesc owningField, TypePreinit.ISerializableReference data)
{
- return _frozenArrayNodes.GetOrAdd(preInitFieldInfo);
+ return _frozenObjectNodes.GetOrAdd(new SerializedFrozenObjectKey(owningField, data));
}
private NodeCache _eagerCctorIndirectionNodes;
@@ -1231,5 +1231,21 @@ public UninitializedWritableDataBlobKey(Utf8String name, int size, int alignment
public override bool Equals(object obj) => obj is UninitializedWritableDataBlobKey && Equals((UninitializedWritableDataBlobKey)obj);
public override int GetHashCode() => Name.GetHashCode();
}
+
+ protected struct SerializedFrozenObjectKey : IEquatable
+ {
+ public readonly FieldDesc Owner;
+ public readonly TypePreinit.ISerializableReference SerializableObject;
+
+ public SerializedFrozenObjectKey(FieldDesc owner, TypePreinit.ISerializableReference obj)
+ {
+ Owner = owner;
+ SerializableObject = obj;
+ }
+
+ public override bool Equals(object obj) => obj is SerializedFrozenObjectKey && Equals((SerializedFrozenObjectKey)obj);
+ public bool Equals(SerializedFrozenObjectKey other) => Owner == other.Owner;
+ public override int GetHashCode() => Owner.GetHashCode();
+ }
}
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NonGCStaticsNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NonGCStaticsNode.cs
index f29c5c74983..e237ff48d60 100644
--- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NonGCStaticsNode.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NonGCStaticsNode.cs
@@ -20,21 +20,14 @@ namespace ILCompiler.DependencyAnalysis
///
public class NonGCStaticsNode : ObjectNode, IExportableSymbolNode, ISortableSymbolNode, ISymbolNodeWithDebugInfo
{
- private MetadataType _type;
- private NodeFactory _factory;
- private List _sortedPreInitFields;
+ private readonly MetadataType _type;
+ private readonly PreinitializationManager _preinitializationManager;
- public NonGCStaticsNode(MetadataType type, NodeFactory factory)
+ public NonGCStaticsNode(MetadataType type, PreinitializationManager preinitializationManager)
{
Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Specific));
_type = type;
- _factory = factory;
- var preInitFieldInfos = PreInitFieldInfo.GetPreInitFieldInfos(_type, hasGCStaticBase: false);
- if (preInitFieldInfos != null)
- {
- _sortedPreInitFields = new List(preInitFieldInfos);
- _sortedPreInitFields.Sort(PreInitFieldInfo.FieldDescCompare);
- }
+ _preinitializationManager = preinitializationManager;
}
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
@@ -58,9 +51,9 @@ int ISymbolDefinitionNode.Offset
get
{
// Make sure the NonGCStatics symbol always points to the beginning of the data.
- if (_factory.PreinitializationManager.HasLazyStaticConstructor(_type))
+ if (_preinitializationManager.HasLazyStaticConstructor(_type))
{
- return GetClassConstructorContextStorageSize(_factory.Target, _type);
+ return GetClassConstructorContextStorageSize(_type.Context.Target, _type);
}
else
{
@@ -69,7 +62,7 @@ int ISymbolDefinitionNode.Offset
}
}
- public bool HasCCtorContext => _factory.PreinitializationManager.HasLazyStaticConstructor(_type);
+ public bool HasCCtorContext => _preinitializationManager.HasLazyStaticConstructor(_type);
public IDebugInfo DebugInfo => NullTypeIndexDebugInfo.Instance;
@@ -146,31 +139,28 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly)
builder.RequireInitialAlignment(_type.NonGCStaticFieldAlignment.AsInt);
}
- if (_sortedPreInitFields != null)
+ if (_preinitializationManager.IsPreinitialized(_type))
{
- int staticOffsetBegin = builder.CountBytes;
- int staticOffsetEnd = builder.CountBytes + _type.NonGCStaticFieldSize.AsInt;
- int staticOffset = staticOffsetBegin;
- int idx = 0;
-
- while (staticOffset < staticOffsetEnd)
+ TypePreinit.PreinitializationInfo preinitInfo = _preinitializationManager.GetPreinitializationInfo(_type);
+ int initialOffset = builder.CountBytes;
+ foreach (FieldDesc field in _type.GetFields())
{
- int writeTo = staticOffsetEnd;
- if (idx < _sortedPreInitFields.Count)
- writeTo = staticOffsetBegin + _sortedPreInitFields[idx].Field.Offset.AsInt;
-
- // Emit the zeros before the next preinitField
- builder.EmitZeros(writeTo - staticOffset);
- staticOffset = writeTo;
-
- // Emit the data
- if (idx < _sortedPreInitFields.Count)
- {
- _sortedPreInitFields[idx].WriteData(ref builder, factory);
- idx++;
- staticOffset = builder.CountBytes;
- }
+ if (!field.IsStatic || field.HasRva || field.IsLiteral || field.IsThreadStatic || field.HasGCStaticBase)
+ continue;
+
+ int padding = field.Offset.AsInt - builder.CountBytes + initialOffset;
+ Debug.Assert(padding >= 0);
+ builder.EmitZeros(padding);
+
+ TypePreinit.ISerializableValue val = preinitInfo.GetFieldValue(field);
+ int currentOffset = builder.CountBytes;
+ val.WriteFieldData(ref builder, field, factory);
+ Debug.Assert(builder.CountBytes - currentOffset == field.FieldType.GetElementSize().AsInt);
}
+
+ int pad = _type.NonGCStaticFieldSize.AsInt - builder.CountBytes + initialOffset;
+ Debug.Assert(pad >= 0);
+ builder.EmitZeros(pad);
}
else
{
diff --git a/src/ILCompiler.Compiler/src/Compiler/PreInitFieldInfo.cs b/src/ILCompiler.Compiler/src/Compiler/PreInitFieldInfo.cs
deleted file mode 100644
index d8c8fc57065..00000000000
--- a/src/ILCompiler.Compiler/src/Compiler/PreInitFieldInfo.cs
+++ /dev/null
@@ -1,392 +0,0 @@
-// 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;
-using ILCompiler.DependencyAnalysis;
-
-namespace ILCompiler
-{
- public abstract class PreInitFixupInfo : IComparable
- {
- ///
- /// Offset into the blob
- ///
- public int Offset { get; }
-
- public PreInitFixupInfo(int offset)
- {
- Offset = offset;
- }
-
- int IComparable.CompareTo(PreInitFixupInfo other)
- {
- return this.Offset - other.Offset;
- }
-
- ///
- /// Writes fixup data into current ObjectDataBuilder. Caller needs to make sure ObjectDataBuilder is
- /// at correct offset before writing.
- ///
- public abstract void WriteData(ref ObjectDataBuilder builder, NodeFactory factory);
- }
-
- public class PreInitTypeFixupInfo : PreInitFixupInfo
- {
- public TypeDesc TypeFixup { get; }
-
- public PreInitTypeFixupInfo(int offset, TypeDesc type)
- :base(offset)
- {
- TypeFixup = type;
- }
-
- public override void WriteData(ref ObjectDataBuilder builder, NodeFactory factory)
- {
- builder.EmitPointerRelocOrIndirectionReference(factory.MaximallyConstructableType(TypeFixup));
- }
- }
-
- public class PreInitMethodFixupInfo : PreInitFixupInfo
- {
- public MethodDesc MethodFixup { get; }
-
- public PreInitMethodFixupInfo(int offset, MethodDesc method)
- : base(offset)
- {
- MethodFixup = method;
- }
-
- public override void WriteData(ref ObjectDataBuilder builder, NodeFactory factory)
- {
- builder.EmitPointerReloc(factory.ExactCallableAddress(MethodFixup));
- }
- }
-
- public class PreInitFieldFixupInfo : PreInitFixupInfo
- {
- public FieldDesc FieldFixup { get; }
-
- public PreInitFieldFixupInfo(int offset, FieldDesc field)
- : base(offset)
- {
- FieldFixup = field;
- }
-
- public override void WriteData(ref ObjectDataBuilder builder, NodeFactory factory)
- {
- MetadataType type = (MetadataType)FieldFixup.OwningType;
-
- // Do not support fixing up fields from external modules
- if (!factory.CompilationModuleGroup.ContainsType(type))
- throw new BadImageFormatException();
-
- ISymbolNode staticBase = FieldFixup.HasGCStaticBase ? factory.TypeGCStaticsSymbol(type) : factory.TypeNonGCStaticsSymbol(type);
- builder.EmitPointerReloc(staticBase, FieldFixup.Offset.AsInt);
- }
- }
-
- public class PreInitFieldInfo
- {
- public FieldDesc Field { get; }
-
- ///
- /// The type of the real field data. This could be a subtype of the field type.
- ///
- public TypeDesc Type { get; }
-
- ///
- /// Points to the underlying contents of the data.
- ///
- public byte[] Data { get; }
-
- ///
- /// Start offset of the real contents in the data.
- ///
- public int Offset { get; }
-
- ///
- /// Number of elements, if this is a frozen array.
- ///
- public int Length { get; }
-
- ///
- /// List of fixup to be apply to the data blob
- /// This is needed for information that can't be encoded into blob ahead of time before codegen
- ///
- private List FixupInfos;
-
- public PreInitFieldInfo(FieldDesc field, TypeDesc type, byte[] data, int offset, int length, List fixups)
- {
- Field = field;
- Type = type;
- Data = data;
- Offset = offset;
- Length = length;
- FixupInfos = fixups;
-
- if (FixupInfos != null)
- FixupInfos.Sort();
- }
-
- public void WriteData(ref ObjectDataBuilder builder, NodeFactory factory, bool relocsOnly = false)
- {
- int offset = Offset;
-
- if (FixupInfos != null)
- {
- int startOffset = builder.CountBytes;
-
- for (int i = 0; i < FixupInfos.Count; ++i)
- {
- var fixupInfo = FixupInfos[i];
-
- // do we have overlapping fixups?
- if (fixupInfo.Offset < offset)
- throw new BadImageFormatException();
-
- if (!relocsOnly)
- {
- // emit bytes before fixup
- builder.EmitBytes(Data, offset, fixupInfo.Offset - offset);
- }
-
- // write the fixup
- FixupInfos[i].WriteData(ref builder, factory);
-
- // move pointer past the fixup
- offset = Offset + builder.CountBytes - startOffset;
- }
- }
-
- if (offset > Data.Length)
- throw new BadImageFormatException();
-
- if (!relocsOnly)
- {
- // Emit remaining bytes
- builder.EmitBytes(Data, offset, Data.Length - offset);
- }
- }
-
- public static List GetPreInitFieldInfos(TypeDesc type, bool hasGCStaticBase)
- {
- List list = null;
-
- foreach (var field in type.GetFields())
- {
- if (!field.IsStatic || field.IsThreadStatic)
- continue;
-
- if (field.HasGCStaticBase != hasGCStaticBase)
- continue;
-
- var dataField = GetPreInitDataField(field);
- if (dataField != null)
- {
- if (list == null)
- list = new List();
- list.Add(ConstructPreInitFieldInfo(field, dataField));
- }
- }
-
- return list;
- }
-
- ///
- /// Retrieves the corresponding static preinitialized data field by looking at various attributes
- ///
- private static FieldDesc GetPreInitDataField(FieldDesc thisField)
- {
- Debug.Assert(thisField.IsStatic);
-
- var field = thisField as EcmaField;
- if (field == null)
- return null;
-
- var decoded = field.GetDecodedCustomAttribute("System.Runtime.CompilerServices", "InitDataBlobAttribute");
- if (decoded == null)
- return null;
-
- var decodedValue = decoded.Value;
- if (decodedValue.FixedArguments.Length != 2)
- throw new BadImageFormatException();
-
- var typeDesc = decodedValue.FixedArguments[0].Value as TypeDesc;
- if (typeDesc == null)
- throw new BadImageFormatException();
-
- if (decodedValue.FixedArguments[1].Type != field.Context.GetWellKnownType(WellKnownType.String))
- throw new BadImageFormatException();
-
- var fieldName = (string)decodedValue.FixedArguments[1].Value;
- var dataField = typeDesc.GetField(fieldName);
- if (dataField== null)
- throw new BadImageFormatException();
-
- return dataField;
- }
-
- ///
- /// Extract preinitialize data as byte[] from a RVA field, and perform necessary validations.
- ///
- private static PreInitFieldInfo ConstructPreInitFieldInfo(FieldDesc field, FieldDesc dataField)
- {
- if (!dataField.HasRva)
- throw new BadImageFormatException();
-
- var ecmaDataField = dataField as EcmaField;
- if (ecmaDataField == null)
- throw new NotSupportedException();
-
- var rvaData = ecmaDataField.GetFieldRvaData();
- var fieldType = field.FieldType;
- int elementCount;
- int realDataOffset;
- TypeDesc realDataType = null;
-
- //
- // Construct fixups
- //
- List fixups = null;
-
- var typeFixupAttrs = ecmaDataField.GetDecodedCustomAttributes("System.Runtime.CompilerServices", "TypeHandleFixupAttribute");
- foreach (var typeFixupAttr in typeFixupAttrs)
- {
- if (typeFixupAttr.FixedArguments[0].Type != field.Context.GetWellKnownType(WellKnownType.Int32))
- throw new BadImageFormatException();
-
- int offset = (int)typeFixupAttr.FixedArguments[0].Value;
- var typeArg = typeFixupAttr.FixedArguments[1].Value;
- var fixupType = typeArg as TypeDesc;
- if (fixupType == null)
- {
- if (typeArg is string fixupTypeName)
- {
- fixupType = CustomAttributeTypeNameParser.GetTypeByCustomAttributeTypeName(ecmaDataField.Module, fixupTypeName, throwIfNotFound: true);
- }
- else
- {
- throw new BadImageFormatException();
- }
- }
-
- fixups = fixups ?? new List();
-
- if (offset == 0 && fieldType.IsSzArray)
- {
- // For array field, offset 0 is the element type handle followed by the element count
- realDataType = fixupType;
- }
- else
- {
- fixups.Add(new PreInitTypeFixupInfo(offset, fixupType));
- }
- }
-
- var methodFixupAttrs = ecmaDataField.GetDecodedCustomAttributes("System.Runtime.CompilerServices", "MethodAddrFixupAttribute");
- foreach (var methodFixupAttr in methodFixupAttrs)
- {
- if (methodFixupAttr.FixedArguments[0].Type != field.Context.GetWellKnownType(WellKnownType.Int32))
- throw new BadImageFormatException();
-
- int offset = (int)methodFixupAttr.FixedArguments[0].Value;
- TypeDesc fixupType = methodFixupAttr.FixedArguments[1].Value as TypeDesc;
- if (fixupType == null)
- throw new BadImageFormatException();
-
- string methodName = methodFixupAttr.FixedArguments[2].Value as string;
- if (methodName == null)
- throw new BadImageFormatException();
-
- var method = fixupType.GetMethod(methodName, signature : null);
- if (method == null)
- throw new BadImageFormatException();
-
- fixups = fixups ?? new List();
-
- fixups.Add(new PreInitMethodFixupInfo(offset, method));
- }
-
- var fieldFixupAttrs = ecmaDataField.GetDecodedCustomAttributes("System.Runtime.CompilerServices", "FieldAddrFixupAttribute");
- foreach (var fieldFixupAttr in fieldFixupAttrs)
- {
- if (fieldFixupAttr.FixedArguments[0].Type != field.Context.GetWellKnownType(WellKnownType.Int32))
- throw new BadImageFormatException();
-
- int offset = (int)fieldFixupAttr.FixedArguments[0].Value;
- TypeDesc fixupType = fieldFixupAttr.FixedArguments[1].Value as TypeDesc;
- if (fixupType == null)
- throw new BadImageFormatException();
-
- string fieldName = fieldFixupAttr.FixedArguments[2].Value as string;
- if (fieldName == null)
- throw new BadImageFormatException();
-
- var fixupField = fixupType.GetField(fieldName);
- if (fixupField == null)
- throw new BadImageFormatException();
-
- if (!fixupField.IsStatic)
- throw new BadImageFormatException();
-
- fixups = fixups ?? new List();
-
- fixups.Add(new PreInitFieldFixupInfo(offset, fixupField));
- }
-
- if (fieldType.IsValueType || fieldType.IsPointer)
- {
- elementCount = -1;
- realDataOffset = 0;
- realDataType = fieldType;
- }
- else if (fieldType.IsSzArray)
- {
- // Offset 0 is the element type handle fixup followed by the element count
- if (realDataType == null)
- throw new BadImageFormatException();
-
- int ptrSize = fieldType.Context.Target.PointerSize;
- elementCount = rvaData[ptrSize] | rvaData[ptrSize + 1] << 8 | rvaData[ptrSize + 2] << 16 | rvaData[ptrSize + 3] << 24;
- realDataOffset = ptrSize * 2;
- realDataType = realDataType.MakeArrayType();
- }
- else
- {
- throw new NotSupportedException();
- }
-
- return new PreInitFieldInfo(field, realDataType, rvaData, realDataOffset, elementCount, fixups);
- }
-
- public static int FieldDescCompare(PreInitFieldInfo fieldInfo1, PreInitFieldInfo fieldInfo2)
- {
- return fieldInfo1.Field.Offset.AsInt - fieldInfo2.Field.Offset.AsInt;
- }
-
- public int CompareTo(PreInitFieldInfo other, TypeSystemComparer comparer)
- {
- if (Length != other.Length)
- return Length - other.Length;
-
- var compare = comparer.Compare(Field, other.Field);
- if (compare != 0)
- return compare;
-
- Debug.Assert(Data.Length == other.Data.Length);
- for (int i = 0; i < Data.Length; i++)
- {
- if (Data[i] != other.Data[i])
- return Data[i] - other.Data[i];
- }
-
- return 0;
- }
- }
-}
diff --git a/src/ILCompiler.Compiler/src/Compiler/PreinitializationManager.cs b/src/ILCompiler.Compiler/src/Compiler/PreinitializationManager.cs
index b77b18f69a5..d3a03d8dff2 100644
--- a/src/ILCompiler.Compiler/src/Compiler/PreinitializationManager.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/PreinitializationManager.cs
@@ -2,6 +2,7 @@
// 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.Diagnostics;
using Internal.IL;
using Internal.TypeSystem;
@@ -13,14 +14,13 @@ namespace ILCompiler
public class PreinitializationManager
{
private readonly bool _supportsLazyCctors;
- private readonly CompilationModuleGroup _compilationModuleGroup;
- private readonly ILProvider _ilprovider;
+ private readonly bool _enableInterpreter;
- public PreinitializationManager(TypeSystemContext context, CompilationModuleGroup compilationGroup, ILProvider ilprovider)
+ public PreinitializationManager(TypeSystemContext context, CompilationModuleGroup compilationGroup, ILProvider ilprovider, bool enableInterpreter)
{
_supportsLazyCctors = context.SystemModule.GetType("System.Runtime.CompilerServices", "ClassConstructorRunner", false) != null;
- _compilationModuleGroup = compilationGroup;
- _ilprovider = ilprovider;
+ _preinitHashTable = new PreinitializationInfoHashtable(compilationGroup, ilprovider);
+ _enableInterpreter = enableInterpreter;
}
///
@@ -29,8 +29,28 @@ public PreinitializationManager(TypeSystemContext context, CompilationModuleGrou
///
public bool HasLazyStaticConstructor(TypeDesc type)
{
- return type.HasStaticConstructor && !HasEagerConstructorAttribute(type) && _supportsLazyCctors &&
- (!(type is MetadataType) || !((MetadataType)type).IsModuleType);
+ if (!type.HasStaticConstructor)
+ return false;
+
+ // If the cctor runs eagerly at startup, it's not lazy
+ if (HasEagerConstructorAttribute(type))
+ return false;
+
+ // If the class library doesn't support lazy cctors, everything is preinitialized before Main
+ // either by interpretting the cctor at compile time, or by running the cctor eagerly at startup.
+ if (!_supportsLazyCctors)
+ return false;
+
+ // Would be odd to see a type with a cctor that is not MetadataType
+ Debug.Assert(type is MetadataType);
+ var mdType = (MetadataType)type;
+
+ // The cctor on the Module type is the module constructor. That's special.
+ if (mdType.IsModuleType)
+ return false;
+
+ // If we can't interpret the cctor at compile time, the cctor is lazy.
+ return !IsPreinitialized(mdType);
}
///
@@ -39,7 +59,19 @@ public bool HasLazyStaticConstructor(TypeDesc type)
///
public bool HasEagerStaticConstructor(TypeDesc type)
{
- return type.HasStaticConstructor && (HasEagerConstructorAttribute(type) || !_supportsLazyCctors);
+ if (!type.HasStaticConstructor)
+ return false;
+
+ // Would be odd to see a type with a cctor that is not MetadataType
+ Debug.Assert(type is MetadataType);
+ var mdType = (MetadataType)type;
+
+ // If the type is preinitialized at compile time, that's not eager.
+ if (IsPreinitialized(mdType))
+ return false;
+
+ // If the type is marked as eager or classlib doesn't have a cctor runner, it's eager.
+ return HasEagerConstructorAttribute(type) || !_supportsLazyCctors;
}
private static bool HasEagerConstructorAttribute(TypeDesc type)
@@ -48,5 +80,92 @@ private static bool HasEagerConstructorAttribute(TypeDesc type)
return mdType != null &&
mdType.HasCustomAttribute("System.Runtime.CompilerServices", "EagerStaticClassConstructionAttribute");
}
+
+ public bool IsPreinitialized(MetadataType type)
+ {
+ // If the cctor interpreter is not enabled, no type is preinitialized.
+ if (!_enableInterpreter)
+ return false;
+
+ if (!type.HasStaticConstructor)
+ return false;
+
+ // The cctor on the Module type is the module constructor. That's special.
+ if (type.IsModuleType)
+ return false;
+
+ if (type.HasInstantiation)
+ {
+ // Generic definitions cannot be preinitialized
+ if (type.IsGenericDefinition)
+ return false;
+
+ // If the type has a canonical form the runtime type loader could create
+ // a new type sharing code with this one. They need to agree on how
+ // initialization happens. We can't preinitialize runtime-created
+ // generic types at compile time.
+ if (type.ConvertToCanonForm(CanonicalFormKind.Specific)
+ .IsCanonicalSubtype(CanonicalFormKind.Any))
+ return false;
+ }
+
+ return GetPreinitializationInfo(type).IsPreinitialized;
+ }
+
+ public void LogStatistics(Logger logger)
+ {
+ if (!_enableInterpreter)
+ return;
+
+ int totalEligibleTypes = 0;
+ int totalPreinitializedTypes = 0;
+
+ if (logger.IsVerbose)
+ {
+ foreach (var item in LockFreeReaderHashtable.Enumerator.Get(_preinitHashTable))
+ {
+ totalEligibleTypes++;
+ if (item.IsPreinitialized)
+ {
+ logger.Writer.WriteLine($"Preinitialized type '{item.Type}'");
+ totalPreinitializedTypes++;
+ }
+ else
+ {
+ logger.Writer.WriteLine($"Could not preinitialize '{item.Type}': {item.FailureReason}");
+ }
+ }
+
+ logger.Writer.WriteLine($"Preinitialized {totalPreinitializedTypes} types out of {totalEligibleTypes}.");
+ }
+ }
+
+ public TypePreinit.PreinitializationInfo GetPreinitializationInfo(MetadataType type)
+ {
+ return _preinitHashTable.GetOrCreateValue(type);
+ }
+
+ class PreinitializationInfoHashtable : LockFreeReaderHashtable
+ {
+ private readonly CompilationModuleGroup _compilationGroup;
+ private readonly ILProvider _ilProvider;
+
+ public PreinitializationInfoHashtable(CompilationModuleGroup compilationGroup, ILProvider ilProvider)
+ {
+ _compilationGroup = compilationGroup;
+ _ilProvider = ilProvider;
+ }
+
+ protected override bool CompareKeyToValue(MetadataType key, TypePreinit.PreinitializationInfo value) => key == value.Type;
+ protected override bool CompareValueToValue(TypePreinit.PreinitializationInfo value1, TypePreinit.PreinitializationInfo value2) => value1.Type == value2.Type;
+ protected override int GetKeyHashCode(MetadataType key) => key.GetHashCode();
+ protected override int GetValueHashCode(TypePreinit.PreinitializationInfo value) => value.Type.GetHashCode();
+
+ protected override TypePreinit.PreinitializationInfo CreateValueFromKey(MetadataType key)
+ {
+ return TypePreinit.ScanType(_compilationGroup, _ilProvider, key);
+ }
+ }
+ private PreinitializationInfoHashtable _preinitHashTable;
}
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/TypePreinit.cs b/src/ILCompiler.Compiler/src/Compiler/TypePreinit.cs
new file mode 100644
index 00000000000..b4e37c67453
--- /dev/null
+++ b/src/ILCompiler.Compiler/src/Compiler/TypePreinit.cs
@@ -0,0 +1,1987 @@
+// 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 ILCompiler.DependencyAnalysis;
+
+using Internal.IL;
+using Internal.TypeSystem;
+
+namespace ILCompiler
+{
+ // Class that computes the initial state of static fields on a type by interpreting the static constructor.
+ //
+ // Values are represented by instances of an abstract Value class. Several specialized descendants of
+ // the Value class exist, representing value types (including e.g. a specialized class representing
+ // RuntimeFieldHandle), or reference types (including e.g. specialized class representing an array).
+ //
+ // For simplicity, non-reference values are represented as byte arrays. This requires many short lived array
+ // allocations, but makes a lot of things simpler (e.g. byrefs to values are essentially free because they
+ // only carry a reference to the original array and an optional index).
+ //
+ // When dealing with non-reference types (valuetypes and unmanaged pointers) we need to be careful
+ // about assignment semantics. Some operations need to make a copy of the valuetype bytes while others
+ // are fine to reuse the original byte array. Whenever storing a value into a location, we need to assign
+ // a new value to the existing Value instance to keep byrefs working.
+ public class TypePreinit
+ {
+ private readonly MetadataType _type;
+ private readonly CompilationModuleGroup _compilationGroup;
+ private readonly ILProvider _ilProvider;
+ private readonly Dictionary _fieldValues = new Dictionary();
+ private readonly Dictionary _internedStrings = new Dictionary();
+
+ private TypePreinit(MetadataType owningType, CompilationModuleGroup compilationGroup, ILProvider ilProvider)
+ {
+ _type = owningType;
+ _compilationGroup = compilationGroup;
+ _ilProvider = ilProvider;
+
+ // Zero initialize all fields we model.
+ foreach (var field in owningType.GetFields())
+ {
+ if (!field.IsStatic || field.IsLiteral || field.IsThreadStatic || field.HasRva)
+ continue;
+
+ _fieldValues.Add(field, NewUninitializedLocationValue(field.FieldType));
+ }
+ }
+
+ public static PreinitializationInfo ScanType(CompilationModuleGroup compilationGroup, ILProvider ilProvider, MetadataType type)
+ {
+ Debug.Assert(type.HasStaticConstructor);
+ Debug.Assert(!type.IsGenericDefinition);
+ Debug.Assert(!type.IsCanonicalSubtype(CanonicalFormKind.Any));
+
+ var preinit = new TypePreinit(type, compilationGroup, ilProvider);
+
+ Status status;
+ try
+ {
+ status = preinit.TryScanMethod(type.GetStaticConstructor(), null, null, out _);
+ }
+ catch (TypeSystemException ex)
+ {
+ status = Status.Fail(type.GetStaticConstructor(), ex.Message);
+ }
+
+ if (status.IsSuccessful)
+ {
+ var values = new List>();
+ foreach (var kvp in preinit._fieldValues)
+ values.Add(new KeyValuePair(kvp.Key, kvp.Value));
+
+ return new PreinitializationInfo(type, values);
+ }
+
+ return new PreinitializationInfo(type, status.FailureReason);
+ }
+
+ private Status TryScanMethod(MethodDesc method, Value[] parameters, Stack recursionProtect, out Value returnValue)
+ {
+ MethodIL methodIL = _ilProvider.GetMethodIL(method);
+ if (methodIL == null)
+ {
+ returnValue = null;
+ return Status.Fail(method, "Extern method");
+ }
+
+ return TryScanMethod(methodIL, parameters, recursionProtect, out returnValue);
+ }
+
+ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack recursionProtect, out Value returnValue)
+ {
+ returnValue = default;
+
+ if (recursionProtect != null && recursionProtect.Contains(methodIL.OwningMethod))
+ return Status.Fail(methodIL.OwningMethod, "Recursion");
+
+ ILExceptionRegion[] ehRegions = methodIL.GetExceptionRegions();
+ if (ehRegions != null && ehRegions.Length > 0)
+ {
+ // We don't care about catch/filter/fault because those only run when an exception happens
+ // (exceptions will never happen here). But finally needs to run in non-exceptional paths
+ // and we don't model that yet.
+ foreach (ILExceptionRegion ehRegion in ehRegions)
+ {
+ if (ehRegion.Kind == ILExceptionRegionKind.Finally)
+ return Status.Fail(methodIL.OwningMethod, "Finally regions");
+ }
+ }
+
+ var reader = new ILReader(methodIL.GetILBytes());
+
+ TypeSystemContext context = methodIL.OwningMethod.Context;
+
+ var stack = new Stack(methodIL.MaxStack, context.Target);
+
+ LocalVariableDefinition[] localTypes = methodIL.GetLocals();
+ Value[] locals = new Value[localTypes.Length];
+ for (int i = 0; i < localTypes.Length; i++)
+ {
+ locals[i] = NewUninitializedLocationValue(localTypes[i].Type);
+ }
+
+ // Read IL opcodes and interpret their semantics.
+ //
+ // This is not a full interpreter and we're allowed to not interpret everything. If a semantic is
+ // not implemented by the interpreter, we simply fail.
+ //
+ // We also need to do basic sanity checking for invalid IL to protect us from crashing. These
+ // all throw the TypeSystem's InvalidProgramException. The exception doesn't need to exactly match
+ // the runtime exception. We just need something reasonably catchable to abort interpreting.
+ //
+ // We throw instead of returning false to aid debuggability of the interpreter (we shouldn't see
+ // exceptions in normal code so an exception is usually a bug).
+
+ while (reader.HasNext)
+ {
+ ILOpcode opcode = reader.ReadILOpcode();
+ switch (opcode)
+ {
+ case ILOpcode.ldc_i4_m1:
+ case ILOpcode.ldc_i4_s:
+ case ILOpcode.ldc_i4:
+ case ILOpcode.ldc_i4_0:
+ case ILOpcode.ldc_i4_1:
+ case ILOpcode.ldc_i4_2:
+ case ILOpcode.ldc_i4_3:
+ case ILOpcode.ldc_i4_4:
+ case ILOpcode.ldc_i4_5:
+ case ILOpcode.ldc_i4_6:
+ case ILOpcode.ldc_i4_7:
+ case ILOpcode.ldc_i4_8:
+ {
+ int value = opcode switch
+ {
+ ILOpcode.ldc_i4_m1 => -1,
+ ILOpcode.ldc_i4_s => (sbyte)reader.ReadILByte(),
+ ILOpcode.ldc_i4 => (int)reader.ReadILUInt32(),
+ _ => opcode - ILOpcode.ldc_i4_0,
+ };
+ stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32(value));
+ }
+ break;
+
+ case ILOpcode.ldc_i8:
+ stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64((long)reader.ReadILUInt64()));
+ break;
+
+ case ILOpcode.ldc_r4:
+ case ILOpcode.ldc_r8:
+ stack.Push(StackValueKind.Float, ValueTypeValue.FromDouble(
+ opcode == ILOpcode.ldc_r4 ? reader.ReadILFloat() : reader.ReadILDouble()));
+ break;
+
+ case ILOpcode.sizeof_:
+ {
+ TypeDesc type = (TypeDesc)methodIL.GetObject(reader.ReadILToken());
+ stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32(type.GetElementSize().AsInt));
+ }
+ break;
+
+ case ILOpcode.ldnull:
+ stack.Push((ReferenceTypeValue)null);
+ break;
+
+ case ILOpcode.newarr:
+ {
+ if (!stack.TryPopIntValue(out int elementCount))
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+
+ const int MaximumInterpretedArraySize = 8192;
+
+ TypeDesc elementType = (TypeDesc)methodIL.GetObject(reader.ReadILToken());
+ if (elementCount > 0
+ && (elementType.IsGCPointer
+ || (elementType.IsValueType && ((DefType)elementType).ContainsGCPointers)))
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode, "GC pointers");
+ }
+
+ if (elementCount < 0
+ || elementCount > MaximumInterpretedArraySize)
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode, "Array out of bounds");
+ }
+
+ stack.Push(new ArrayInstance(elementType.MakeArrayType(), elementCount));
+ }
+ break;
+
+ case ILOpcode.dup:
+ if (stack.Count == 0)
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+ stack.Push(stack.Peek());
+ break;
+
+ case ILOpcode.ldstr:
+ {
+ string s = (string)methodIL.GetObject(reader.ReadILToken());
+ if (!_internedStrings.TryGetValue(s, out StringInstance instance))
+ {
+ instance = new StringInstance(context.GetWellKnownType(WellKnownType.String), s);
+ _internedStrings.Add(s, instance);
+ }
+ stack.Push(instance);
+ }
+ break;
+
+ case ILOpcode.ret:
+ {
+ bool returnsVoid = methodIL.OwningMethod.Signature.ReturnType.IsVoid;
+ if ((returnsVoid && stack.Count > 0)
+ || (!returnsVoid && stack.Count != 1))
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+
+ if (!returnsVoid)
+ {
+ returnValue = stack.PopIntoLocation(methodIL.OwningMethod.Signature.ReturnType);
+ }
+ return Status.Success;
+ }
+
+ case ILOpcode.nop:
+ case ILOpcode.volatile_:
+ break;
+
+ case ILOpcode.stsfld:
+ {
+ FieldDesc field = (FieldDesc)methodIL.GetObject(reader.ReadILToken());
+ if (!field.IsStatic || field.IsLiteral)
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+
+ if (field.OwningType != _type)
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode, "Store into other static");
+ }
+
+ if (field.IsThreadStatic || field.HasRva)
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode, "Unsupported static");
+ }
+
+ if (_fieldValues[field] is IAssignableValue assignableField)
+ assignableField.Assign(stack.PopIntoLocation(field.FieldType));
+ else
+ _fieldValues[field] = stack.PopIntoLocation(field.FieldType);
+ }
+ break;
+
+ case ILOpcode.ldsfld:
+ {
+ FieldDesc field = (FieldDesc)methodIL.GetObject(reader.ReadILToken());
+ if (!field.IsStatic || field.IsLiteral)
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+
+ if (field.OwningType != _type)
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode, "Load from other static" + (field.IsInitOnly ? " initonly " : ""));
+ }
+
+ if (field.IsThreadStatic || field.HasRva)
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode, "Unsupported static");
+ }
+
+ stack.PushFromLocation(field.FieldType, _fieldValues[field]);
+ }
+ break;
+
+ case ILOpcode.ldsflda:
+ {
+ FieldDesc field = (FieldDesc)methodIL.GetObject(reader.ReadILToken());
+ if (!field.IsStatic || field.IsLiteral)
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+
+ if (field.OwningType != _type)
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode, "Address of other static");
+ }
+
+ if (field.IsThreadStatic || field.HasRva)
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode, "Unsupported static");
+ }
+
+ if (!(_fieldValues[field] is ValueTypeValue vtfield))
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode, "Unsupported byref");
+ }
+
+ stack.Push(vtfield.CreateByRef());
+ }
+ break;
+
+ case ILOpcode.call:
+ {
+ MethodDesc method = (MethodDesc)methodIL.GetObject(reader.ReadILToken());
+ MethodSignature methodSig = method.Signature;
+ int paramOffset = methodSig.IsStatic ? 0 : 1;
+ int numParams = methodSig.Length + paramOffset;
+
+ TypeDesc owningType = method.OwningType;
+ if (!_compilationGroup.CanInline(methodIL.OwningMethod, method))
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode, "Cannot inline");
+ }
+
+ if (owningType.HasStaticConstructor
+ && owningType != methodIL.OwningMethod.OwningType
+ && !((MetadataType)owningType).IsBeforeFieldInit)
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode, "Static constructor");
+ }
+
+ Value[] methodParams = new Value[numParams];
+ for (int i = numParams - 1; i >= 0; i--)
+ {
+ methodParams[i] = stack.PopIntoLocation(GetArgType(method, i));
+ }
+
+ Value retVal;
+ if (!method.IsIntrinsic || !TryHandleIntrinsicCall(method, methodParams, out retVal))
+ {
+ recursionProtect ??= new Stack();
+ recursionProtect.Push(methodIL.OwningMethod);
+ Status callResult = TryScanMethod(method, methodParams, recursionProtect, out retVal);
+ if (!callResult.IsSuccessful)
+ {
+ recursionProtect.Pop();
+ return callResult;
+ }
+ recursionProtect.Pop();
+ }
+
+ if (!methodSig.ReturnType.IsVoid)
+ stack.PushFromLocation(methodSig.ReturnType, retVal);
+ }
+ break;
+
+ case ILOpcode.newobj:
+ {
+ MethodDesc ctor = (MethodDesc)methodIL.GetObject(reader.ReadILToken());
+ MethodSignature ctorSig = ctor.Signature;
+
+ TypeDesc owningType = ctor.OwningType;
+ if (!_compilationGroup.CanInline(methodIL.OwningMethod, ctor)
+ || !_compilationGroup.ContainsType(owningType))
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode, "Cannot inline");
+ }
+
+ if (owningType.HasStaticConstructor
+ && owningType != methodIL.OwningMethod.OwningType
+ && !((MetadataType)owningType).IsBeforeFieldInit)
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode, "Static constructor");
+ }
+
+ if (!owningType.IsDefType)
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode, "Not a class or struct");
+ }
+
+ if (owningType.HasFinalizer)
+ {
+ // Finalizer might have observable side effects
+ return Status.Fail(methodIL.OwningMethod, opcode, "Finalizable class");
+ }
+
+ if (((DefType)owningType).ContainsGCPointers)
+ {
+ // We don't want to end up with GC pointers in the frozen region
+ // because write barriers can't handle that.
+ return Status.Fail(methodIL.OwningMethod, opcode, "GC pointers");
+ }
+
+ Value instance;
+ Value ctorArg0;
+ if (owningType.IsValueType)
+ {
+ instance = new ValueTypeValue(owningType);
+ ctorArg0 = ((ValueTypeValue)instance).CreateByRef();
+ }
+ else
+ {
+ instance = new ObjectInstance((DefType)owningType);
+ ctorArg0 = instance;
+ }
+
+ Value[] ctorParameters = new Value[ctorSig.Length + 1];
+ ctorParameters[0] = ctorArg0;
+ for (int i = ctorSig.Length - 1; i >= 0; i--)
+ {
+ ctorParameters[i + 1] = stack.PopIntoLocation(GetArgType(ctor, i + 1));
+ }
+ recursionProtect ??= new Stack();
+ recursionProtect.Push(methodIL.OwningMethod);
+ Status ctorCallResult = TryScanMethod(ctor, ctorParameters, recursionProtect, out _);
+ if (!ctorCallResult.IsSuccessful)
+ {
+ recursionProtect.Pop();
+ return ctorCallResult;
+ }
+
+ recursionProtect.Pop();
+
+ stack.PushFromLocation(owningType, instance);
+ }
+ break;
+
+ case ILOpcode.stfld:
+ {
+ FieldDesc field = (FieldDesc)methodIL.GetObject(reader.ReadILToken());
+
+ if (field.FieldType.IsGCPointer
+ || field.IsStatic)
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode, "Reference field");
+ }
+
+ Value value = stack.PopIntoLocation(field.FieldType);
+ StackEntry instance = stack.Pop();
+
+ var settableInstance = instance.Value as IHasInstanceFields;
+ if (settableInstance == null)
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode);
+ }
+
+ settableInstance.SetField(field, value);
+ }
+ break;
+
+ case ILOpcode.ldfld:
+ {
+ FieldDesc field = (FieldDesc)methodIL.GetObject(reader.ReadILToken());
+
+ if (field.FieldType.IsGCPointer
+ || field.IsStatic)
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode);
+ }
+
+ StackEntry instance = stack.Pop();
+
+ var loadableInstance = instance.Value as IHasInstanceFields;
+ if (loadableInstance == null)
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode);
+ }
+
+ Value fieldValue = loadableInstance.GetField(field);
+
+ stack.PushFromLocation(field.FieldType, fieldValue);
+ }
+ break;
+
+ case ILOpcode.ldflda:
+ {
+ FieldDesc field = (FieldDesc)methodIL.GetObject(reader.ReadILToken());
+ if (field.FieldType.IsGCPointer
+ || field.IsStatic)
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode);
+ }
+
+ StackEntry instance = stack.Pop();
+
+ var loadableInstance = instance.Value as IHasInstanceFields;
+ if (loadableInstance == null)
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode);
+ }
+
+ stack.Push(loadableInstance.GetFieldAddress(field));
+ }
+ break;
+
+ case ILOpcode.conv_i:
+ case ILOpcode.conv_u:
+ case ILOpcode.conv_i2:
+ case ILOpcode.conv_i4:
+ case ILOpcode.conv_i8:
+ case ILOpcode.conv_u2:
+ case ILOpcode.conv_u8:
+ {
+ StackEntry popped = stack.Pop();
+ if (popped.ValueKind == StackValueKind.Int32)
+ {
+ int val = popped.Value.AsInt32();
+ switch (opcode)
+ {
+ case ILOpcode.conv_i:
+ stack.Push(StackValueKind.NativeInt,
+ context.Target.PointerSize == 8 ? ValueTypeValue.FromInt64(val) : ValueTypeValue.FromInt32(val));
+ break;
+ case ILOpcode.conv_u:
+ stack.Push(StackValueKind.NativeInt,
+ context.Target.PointerSize == 8 ? ValueTypeValue.FromInt64((uint)val) : ValueTypeValue.FromInt32(val));
+ break;
+ case ILOpcode.conv_i2:
+ stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32((short)val));
+ break;
+ case ILOpcode.conv_i8:
+ stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64(val));
+ break;
+ case ILOpcode.conv_u2:
+ stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32((ushort)val));
+ break;
+ case ILOpcode.conv_u8:
+ stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64((uint)val));
+ break;
+ default:
+ return Status.Fail(methodIL.OwningMethod, opcode);
+ }
+ }
+ else if (popped.ValueKind == StackValueKind.NativeInt)
+ {
+ long val = context.Target.PointerSize == 8 ? popped.Value.AsInt64() : popped.Value.AsInt32();
+ switch (opcode)
+ {
+ case ILOpcode.conv_i4:
+ stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32((int)val));
+ break;
+ default:
+ return Status.Fail(methodIL.OwningMethod, opcode);
+ }
+ }
+ else if (popped.ValueKind == StackValueKind.Int64)
+ {
+ long val = popped.Value.AsInt64();
+ switch (opcode)
+ {
+ case ILOpcode.conv_u:
+ stack.Push(StackValueKind.NativeInt,
+ context.Target.PointerSize == 8 ? ValueTypeValue.FromInt64(val) : ValueTypeValue.FromInt32((int)val));
+ break;
+ }
+ }
+ else if (popped.ValueKind == StackValueKind.Float)
+ {
+ double val = popped.Value.AsDouble();
+ switch (opcode)
+ {
+ case ILOpcode.conv_i8:
+ stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64((long)val));
+ break;
+ default:
+ return Status.Fail(methodIL.OwningMethod, opcode);
+ }
+ }
+ else
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode);
+ }
+ }
+ break;
+
+ case ILOpcode.ldarg_0:
+ case ILOpcode.ldarg_1:
+ case ILOpcode.ldarg_2:
+ case ILOpcode.ldarg_3:
+ case ILOpcode.ldarg_s:
+ case ILOpcode.ldarg:
+ {
+ int index = opcode switch
+ {
+ ILOpcode.ldarg_s => reader.ReadILByte(),
+ ILOpcode.ldarg => reader.ReadILUInt16(),
+ _ => opcode - ILOpcode.ldarg_0,
+ };
+ stack.PushFromLocation(GetArgType(methodIL.OwningMethod, index), parameters[index]);
+ }
+ break;
+
+ case ILOpcode.starg_s:
+ case ILOpcode.starg:
+ {
+ int index = opcode == ILOpcode.starg ? reader.ReadILUInt16() : reader.ReadILByte();
+ TypeDesc argType = GetArgType(methodIL.OwningMethod, index);
+ if (parameters[index] is IAssignableValue assignableParam)
+ assignableParam.Assign(stack.PopIntoLocation(argType));
+ else
+ parameters[index] = stack.PopIntoLocation(argType);
+ }
+ break;
+
+ case ILOpcode.ldtoken:
+ {
+ var token = methodIL.GetObject(reader.ReadILToken());
+ if (!(token is FieldDesc field))
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode);
+ }
+
+ stack.Push(new StackEntry(StackValueKind.ValueType, new RuntimeFieldHandleValue(field)));
+ }
+ break;
+
+ case ILOpcode.ldloc_0:
+ case ILOpcode.ldloc_1:
+ case ILOpcode.ldloc_2:
+ case ILOpcode.ldloc_3:
+ case ILOpcode.ldloc_s:
+ case ILOpcode.ldloc:
+ {
+ int index = opcode switch
+ {
+ ILOpcode.ldloc_s => reader.ReadILByte(),
+ ILOpcode.ldloc => reader.ReadILUInt16(),
+ _ => opcode - ILOpcode.ldloc_0,
+ };
+
+ if (index >= locals.Length)
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+
+ stack.PushFromLocation(localTypes[index].Type, locals[index]);
+ }
+ break;
+
+ case ILOpcode.stloc_0:
+ case ILOpcode.stloc_1:
+ case ILOpcode.stloc_2:
+ case ILOpcode.stloc_3:
+ case ILOpcode.stloc_s:
+ case ILOpcode.stloc:
+ {
+ int index = opcode switch
+ {
+ ILOpcode.stloc_s => reader.ReadILByte(),
+ ILOpcode.stloc => reader.ReadILUInt16(),
+ _ => opcode - ILOpcode.stloc_0,
+ };
+
+ if (index >= locals.Length)
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+
+ TypeDesc localType = localTypes[index].Type;
+ if (locals[index] is IAssignableValue assignableLocal)
+ assignableLocal.Assign(stack.PopIntoLocation(localType));
+ else
+ locals[index] = stack.PopIntoLocation(localType);
+
+ }
+ break;
+
+ case ILOpcode.ldloca_s:
+ case ILOpcode.ldloca:
+ {
+ int index = opcode switch
+ {
+ ILOpcode.ldloca_s => reader.ReadILByte(),
+ ILOpcode.ldloca => reader.ReadILUInt16(),
+ _ => throw new NotImplementedException(), // Unreachable
+ };
+
+ if (index >= locals.Length)
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+
+ if (locals[index] is ValueTypeValue vtvalue)
+ {
+ stack.Push(vtvalue.CreateByRef());
+ }
+ else
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode);
+ }
+ }
+ break;
+
+ case ILOpcode.initobj:
+ {
+ StackEntry popped = stack.Pop();
+ if (popped.ValueKind != StackValueKind.ByRef)
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+
+ TypeDesc token = (TypeDesc)methodIL.GetObject(reader.ReadILToken());
+ if (token.IsGCPointer)
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode);
+ }
+ ((ByRefValue)popped.Value).Initialize(token.GetElementSize().AsInt);
+ }
+ break;
+
+ case ILOpcode.br:
+ case ILOpcode.brfalse:
+ case ILOpcode.brtrue:
+ case ILOpcode.blt:
+ case ILOpcode.blt_un:
+ case ILOpcode.bgt:
+ case ILOpcode.bgt_un:
+ case ILOpcode.beq:
+ case ILOpcode.bne_un:
+ case ILOpcode.bge:
+ case ILOpcode.bge_un:
+ case ILOpcode.ble:
+ case ILOpcode.ble_un:
+ case ILOpcode.br_s:
+ case ILOpcode.brfalse_s:
+ case ILOpcode.brtrue_s:
+ case ILOpcode.blt_s:
+ case ILOpcode.blt_un_s:
+ case ILOpcode.bgt_s:
+ case ILOpcode.bgt_un_s:
+ case ILOpcode.beq_s:
+ case ILOpcode.bne_un_s:
+ case ILOpcode.bge_s:
+ case ILOpcode.bge_un_s:
+ case ILOpcode.ble_s:
+ case ILOpcode.ble_un_s:
+ {
+ int delta = opcode >= ILOpcode.br ?
+ (int)reader.ReadILUInt32() :
+ (sbyte)reader.ReadILByte();
+ int target = reader.Offset + delta;
+ if (target < 0
+ || target > reader.Size)
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+
+ ILOpcode normalizedOpcode = opcode >= ILOpcode.br ?
+ opcode - ILOpcode.br + ILOpcode.br_s:
+ opcode;
+
+ bool branchTaken;
+ if (normalizedOpcode == ILOpcode.brtrue_s || normalizedOpcode == ILOpcode.brfalse_s)
+ {
+ StackEntry condition = stack.Pop();
+ if (condition.ValueKind == StackValueKind.Int32 || (condition.ValueKind == StackValueKind.NativeInt && context.Target.PointerSize == 4))
+ branchTaken = normalizedOpcode == ILOpcode.brfalse_s
+ ? condition.Value.AsInt32() == 0 : condition.Value.AsInt32() != 0;
+ else if (condition.ValueKind == StackValueKind.Int64 || (condition.ValueKind == StackValueKind.NativeInt && context.Target.PointerSize == 8))
+ branchTaken = normalizedOpcode == ILOpcode.brfalse_s
+ ? condition.Value.AsInt64() == 0 : condition.Value.AsInt64() != 0;
+ else if (condition.ValueKind == StackValueKind.ObjRef)
+ branchTaken = normalizedOpcode == ILOpcode.brfalse_s
+ ? condition.Value == null : condition.Value != null;
+ else
+ return Status.Fail(methodIL.OwningMethod, opcode);
+ }
+ else if (normalizedOpcode == ILOpcode.blt_s || normalizedOpcode == ILOpcode.bgt_s
+ || normalizedOpcode == ILOpcode.bge_s || normalizedOpcode == ILOpcode.beq_s
+ || normalizedOpcode == ILOpcode.ble_s || normalizedOpcode == ILOpcode.blt_un_s
+ || normalizedOpcode == ILOpcode.ble_un_s || normalizedOpcode == ILOpcode.bge_un_s
+ || normalizedOpcode == ILOpcode.bgt_un_s || normalizedOpcode == ILOpcode.bne_un_s)
+ {
+ StackEntry value2 = stack.Pop();
+ StackEntry value1 = stack.Pop();
+
+ if (value1.ValueKind == StackValueKind.Int32 && value2.ValueKind == StackValueKind.Int32)
+ {
+ branchTaken = normalizedOpcode switch
+ {
+ ILOpcode.blt_s => value1.Value.AsInt32() < value2.Value.AsInt32(),
+ ILOpcode.blt_un_s => (uint)value1.Value.AsInt32() < (uint)value2.Value.AsInt32(),
+ ILOpcode.bgt_s => value1.Value.AsInt32() > value2.Value.AsInt32(),
+ ILOpcode.bgt_un_s => (uint)value1.Value.AsInt32() > (uint)value2.Value.AsInt32(),
+ ILOpcode.bge_s => value1.Value.AsInt32() >= value2.Value.AsInt32(),
+ ILOpcode.bge_un_s => (uint)value1.Value.AsInt32() >= (uint)value2.Value.AsInt32(),
+ ILOpcode.beq_s => value1.Value.AsInt32() == value2.Value.AsInt32(),
+ ILOpcode.bne_un_s => value1.Value.AsInt32() != value2.Value.AsInt32(),
+ ILOpcode.ble_s => value1.Value.AsInt32() <= value2.Value.AsInt32(),
+ ILOpcode.ble_un_s => (uint)value1.Value.AsInt32() <= (uint)value2.Value.AsInt32(),
+ _ => throw new NotImplementedException() // unreachable
+ };
+ }
+ else if (value1.ValueKind == StackValueKind.Int64 && value2.ValueKind == StackValueKind.Int64)
+ {
+ branchTaken = normalizedOpcode switch
+ {
+ ILOpcode.blt_s => value1.Value.AsInt64() < value2.Value.AsInt64(),
+ ILOpcode.blt_un_s => (ulong)value1.Value.AsInt64() < (ulong)value2.Value.AsInt64(),
+ ILOpcode.bgt_s => value1.Value.AsInt64() > value2.Value.AsInt64(),
+ ILOpcode.bgt_un_s => (ulong)value1.Value.AsInt64() > (ulong)value2.Value.AsInt64(),
+ ILOpcode.bge_s => value1.Value.AsInt64() >= value2.Value.AsInt64(),
+ ILOpcode.bge_un_s => (ulong)value1.Value.AsInt64() >= (ulong)value2.Value.AsInt64(),
+ ILOpcode.beq_s => value1.Value.AsInt64() == value2.Value.AsInt64(),
+ ILOpcode.bne_un_s => value1.Value.AsInt64() != value2.Value.AsInt64(),
+ ILOpcode.ble_s => value1.Value.AsInt64() <= value2.Value.AsInt64(),
+ ILOpcode.ble_un_s => (ulong)value1.Value.AsInt64() <= (ulong)value2.Value.AsInt64(),
+ _ => throw new NotImplementedException() // unreachable
+ };
+ }
+ else if (value1.ValueKind == StackValueKind.Float && value2.ValueKind == StackValueKind.Float)
+ {
+ branchTaken = normalizedOpcode switch
+ {
+ ILOpcode.blt_s => value1.Value.AsDouble() < value2.Value.AsDouble(),
+ ILOpcode.blt_un_s => !(value1.Value.AsDouble() >= value2.Value.AsDouble()),
+ ILOpcode.bgt_s => value1.Value.AsDouble() > value2.Value.AsDouble(),
+ ILOpcode.bgt_un_s => !(value1.Value.AsDouble() <= value2.Value.AsDouble()),
+ ILOpcode.bge_s => value1.Value.AsDouble() >= value2.Value.AsDouble(),
+ ILOpcode.bge_un_s => !(value1.Value.AsDouble() < value2.Value.AsDouble()),
+ ILOpcode.beq_s => value1.Value.AsDouble() == value2.Value.AsDouble(),
+ ILOpcode.bne_un_s => value1.Value.AsDouble() != value2.Value.AsDouble(),
+ ILOpcode.ble_s => value1.Value.AsDouble() <= value2.Value.AsDouble(),
+ ILOpcode.ble_un_s => !(value1.Value.AsDouble() > value2.Value.AsDouble()),
+ _ => throw new NotImplementedException() // unreachable
+ };
+ }
+ else
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode);
+ }
+ }
+ else
+ {
+ Debug.Assert(normalizedOpcode == ILOpcode.br_s);
+ branchTaken = true;
+ }
+
+ if (branchTaken)
+ {
+ // Don't allow backwards branches so that we don't have to worry about infinite loops
+ if (target < reader.Offset)
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode, "Backwards branch");
+ }
+
+ reader.Seek(target);
+ }
+ }
+ break;
+
+ case ILOpcode.leave:
+ case ILOpcode.leave_s:
+ {
+ stack.Clear();
+
+ // We assume no finally regions (would have to run them here)
+ // This is validated before, but we're being paranoid.
+ foreach (ILExceptionRegion ehRegion in ehRegions)
+ {
+ Debug.Assert(ehRegion.Kind != ILExceptionRegionKind.Finally);
+ }
+
+ int delta = opcode == ILOpcode.leave ?
+ (int)reader.ReadILUInt32() :
+ (sbyte)reader.ReadILByte();
+ int target = reader.Offset + delta;
+ if (target < 0
+ || target > reader.Size)
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+
+ reader.Seek(target);
+ }
+ break;
+
+ case ILOpcode.clt:
+ case ILOpcode.clt_un:
+ case ILOpcode.cgt:
+ case ILOpcode.cgt_un:
+ {
+ StackEntry value1 = stack.Pop();
+ StackEntry value2 = stack.Pop();
+
+ bool condition;
+ if (value1.ValueKind == StackValueKind.Int32 && value2.ValueKind == StackValueKind.Int32)
+ {
+ if (opcode == ILOpcode.cgt)
+ condition = value1.Value.AsInt32() < value2.Value.AsInt32();
+ else if (opcode == ILOpcode.cgt_un)
+ condition = (uint)value1.Value.AsInt32() < (uint)value2.Value.AsInt32();
+ else if (opcode == ILOpcode.clt)
+ condition = value1.Value.AsInt32() > value2.Value.AsInt32();
+ else if (opcode == ILOpcode.clt_un)
+ condition = (uint)value1.Value.AsInt32() > (uint)value2.Value.AsInt32();
+ else
+ return Status.Fail(methodIL.OwningMethod, opcode);
+ }
+ else if (value1.ValueKind == StackValueKind.Int64 && value2.ValueKind == StackValueKind.Int64)
+ {
+ if (opcode == ILOpcode.cgt)
+ condition = value1.Value.AsInt64() < value2.Value.AsInt64();
+ else if (opcode == ILOpcode.cgt_un)
+ condition = (ulong)value1.Value.AsInt64() < (ulong)value2.Value.AsInt64();
+ else if (opcode == ILOpcode.clt)
+ condition = value1.Value.AsInt64() > value2.Value.AsInt64();
+ else if (opcode == ILOpcode.clt_un)
+ condition = (ulong)value1.Value.AsInt64() > (ulong)value2.Value.AsInt64();
+ else
+ return Status.Fail(methodIL.OwningMethod, opcode);
+ }
+ else if (value1.ValueKind == StackValueKind.Float && value2.ValueKind == StackValueKind.Float)
+ {
+ if (opcode == ILOpcode.cgt)
+ condition = value1.Value.AsDouble() < value2.Value.AsDouble();
+ else if (opcode == ILOpcode.cgt_un)
+ condition = !(value1.Value.AsDouble() >= value2.Value.AsDouble());
+ else if (opcode == ILOpcode.clt)
+ condition = value1.Value.AsDouble() > value2.Value.AsDouble();
+ else if (opcode == ILOpcode.clt_un)
+ condition = !(value1.Value.AsDouble() <= value2.Value.AsDouble());
+ else
+ return Status.Fail(methodIL.OwningMethod, opcode);
+ }
+ else if (value1.ValueKind == StackValueKind.ObjRef && value2.ValueKind == StackValueKind.ObjRef)
+ {
+ if (opcode == ILOpcode.cgt_un)
+ condition = value1.Value == null && value2.Value != null;
+ else
+ return Status.Fail(methodIL.OwningMethod, opcode);
+ }
+ else
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode);
+ }
+
+ stack.Push(StackValueKind.Int32, condition
+ ? ValueTypeValue.FromInt32(1)
+ : ValueTypeValue.FromInt32(0));
+ }
+ break;
+
+ case ILOpcode.ceq:
+ {
+ StackEntry value1 = stack.Pop();
+ StackEntry value2 = stack.Pop();
+
+ if (value1.ValueKind == value2.ValueKind)
+ {
+ stack.Push(StackValueKind.Int32,
+ Value.Equals(value1.Value, value2.Value)
+ ? ValueTypeValue.FromInt32(1)
+ : ValueTypeValue.FromInt32(0));
+ }
+ else
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode);
+ }
+ }
+ break;
+
+ case ILOpcode.neg:
+ {
+ StackEntry value = stack.Pop();
+ if (value.ValueKind == StackValueKind.Int32)
+ stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32(-value.Value.AsInt32()));
+ else
+ return Status.Fail(methodIL.OwningMethod, opcode);
+ }
+ break;
+
+ case ILOpcode.or:
+ case ILOpcode.shl:
+ case ILOpcode.add:
+ case ILOpcode.sub:
+ case ILOpcode.mul:
+ case ILOpcode.and:
+ case ILOpcode.div:
+ case ILOpcode.rem:
+ {
+ bool isDivRem = opcode == ILOpcode.div || opcode == ILOpcode.rem;
+
+ StackEntry value2 = stack.Pop();
+ StackEntry value1 = stack.Pop();
+ if (value1.ValueKind == StackValueKind.Int32 && value2.ValueKind == StackValueKind.Int32)
+ {
+ if (isDivRem && value2.Value.AsInt32() == 0)
+ return Status.Fail(methodIL.OwningMethod, opcode, "Division by zero");
+
+ int result = opcode switch
+ {
+ ILOpcode.or => value1.Value.AsInt32() | value2.Value.AsInt32(),
+ ILOpcode.shl => value1.Value.AsInt32() << value2.Value.AsInt32(),
+ ILOpcode.add => value1.Value.AsInt32() + value2.Value.AsInt32(),
+ ILOpcode.sub => value1.Value.AsInt32() - value2.Value.AsInt32(),
+ ILOpcode.and => value1.Value.AsInt32() & value2.Value.AsInt32(),
+ ILOpcode.mul => value1.Value.AsInt32() * value2.Value.AsInt32(),
+ ILOpcode.div => value1.Value.AsInt32() / value2.Value.AsInt32(),
+ ILOpcode.rem => value1.Value.AsInt32() % value2.Value.AsInt32(),
+ _ => throw new NotImplementedException(), // unreachable
+ };
+
+ stack.Push(StackValueKind.Int32, ValueTypeValue.FromInt32(result));
+ }
+ else if (value1.ValueKind == StackValueKind.Int64 && value2.ValueKind == StackValueKind.Int64)
+ {
+ if (isDivRem && value2.Value.AsInt64() == 0)
+ return Status.Fail(methodIL.OwningMethod, opcode, "Division by zero");
+
+ long result = opcode switch
+ {
+ ILOpcode.or => value1.Value.AsInt64() | value2.Value.AsInt64(),
+ ILOpcode.add => value1.Value.AsInt64() + value2.Value.AsInt64(),
+ ILOpcode.sub => value1.Value.AsInt64() - value2.Value.AsInt64(),
+ ILOpcode.and => value1.Value.AsInt64() & value2.Value.AsInt64(),
+ ILOpcode.mul => value1.Value.AsInt64() * value2.Value.AsInt64(),
+ ILOpcode.div => value1.Value.AsInt64() / value2.Value.AsInt64(),
+ ILOpcode.rem => value1.Value.AsInt64() % value2.Value.AsInt64(),
+ _ => throw new NotImplementedException(), // unreachable
+ };
+
+ stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64(result));
+ }
+ else if (value1.ValueKind == StackValueKind.Float && value2.ValueKind == StackValueKind.Float)
+ {
+ if (isDivRem && value2.Value.AsDouble() == 0)
+ return Status.Fail(methodIL.OwningMethod, opcode, "Division by zero");
+
+ if (opcode == ILOpcode.or || opcode == ILOpcode.shl || opcode == ILOpcode.and)
+ ThrowHelper.ThrowInvalidProgramException();
+
+ double result = opcode switch
+ {
+ ILOpcode.add => value1.Value.AsDouble() + value2.Value.AsDouble(),
+ ILOpcode.sub => value1.Value.AsDouble() - value2.Value.AsDouble(),
+ ILOpcode.mul => value1.Value.AsDouble() * value2.Value.AsDouble(),
+ ILOpcode.div => value1.Value.AsDouble() / value2.Value.AsDouble(),
+ ILOpcode.rem => value1.Value.AsDouble() % value2.Value.AsDouble(),
+ _ => throw new NotImplementedException(), // unreachable
+ };
+
+ stack.Push(StackValueKind.Float, ValueTypeValue.FromDouble(result));
+ }
+ else if (value1.ValueKind == StackValueKind.Int64 && value2.ValueKind == StackValueKind.Int32
+ && opcode == ILOpcode.shl)
+ {
+ long result = value1.Value.AsInt64() << value2.Value.AsInt32();
+ stack.Push(StackValueKind.Int64, ValueTypeValue.FromInt64(result));
+ }
+ else
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode);
+ }
+ }
+ break;
+
+ case ILOpcode.ldlen:
+ {
+ StackEntry popped = stack.Pop();
+ if (popped.Value is ArrayInstance arrayInstance)
+ {
+ stack.Push(StackValueKind.NativeInt, ValueTypeValue.FromInt64(arrayInstance.Length));
+ }
+ else if (popped.Value == null)
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode, "Null array");
+ }
+ else
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+ }
+ break;
+
+ case ILOpcode.stelem:
+ case ILOpcode.stelem_i:
+ case ILOpcode.stelem_i1:
+ case ILOpcode.stelem_i2:
+ case ILOpcode.stelem_i4:
+ case ILOpcode.stelem_i8:
+ case ILOpcode.stelem_r4:
+ case ILOpcode.stelem_r8:
+ {
+ TypeDesc elementType = opcode switch
+ {
+ ILOpcode.stelem_i => context.GetWellKnownType(WellKnownType.IntPtr),
+ ILOpcode.stelem_i1 => context.GetWellKnownType(WellKnownType.SByte),
+ ILOpcode.stelem_i2 => context.GetWellKnownType(WellKnownType.Int16),
+ ILOpcode.stelem_i4 => context.GetWellKnownType(WellKnownType.Int32),
+ ILOpcode.stelem_i8 => context.GetWellKnownType(WellKnownType.Int64),
+ ILOpcode.stelem_r4 => context.GetWellKnownType(WellKnownType.Single),
+ ILOpcode.stelem_r8 => context.GetWellKnownType(WellKnownType.Double),
+ _ => (TypeDesc)methodIL.GetObject(reader.ReadILToken()),
+ };
+
+ if (elementType.IsGCPointer)
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode);
+ }
+
+ Value value = stack.PopIntoLocation(elementType);
+ if (!stack.TryPopIntValue(out int index))
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+ StackEntry array = stack.Pop();
+ if (array.Value is ArrayInstance arrayInstance)
+ {
+ if (!arrayInstance.TryStoreElement(index, value))
+ return Status.Fail(methodIL.OwningMethod, opcode, "Out of range access");
+ }
+ else if (array.Value == null)
+ {
+ return Status.Fail(methodIL.OwningMethod, opcode, "Null array");
+ }
+ else
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+ }
+ break;
+
+ case ILOpcode.box:
+ {
+ TypeDesc type = (TypeDesc)methodIL.GetObject(reader.ReadILToken());
+ if (type.IsValueType)
+ {
+ if (type.IsNullable)
+ return Status.Fail(methodIL.OwningMethod, opcode);
+
+ Value value = stack.PopIntoLocation(type);
+ stack.Push(StackValueKind.ObjRef, ObjectInstance.Box((DefType)type, ((ValueTypeValue)value).InstanceBytes));
+ }
+ }
+ break;
+
+ case ILOpcode.unbox_any:
+ {
+ TypeDesc type = (TypeDesc)methodIL.GetObject(reader.ReadILToken());
+ StackEntry entry = stack.Pop();
+ if (entry.Value is ObjectInstance objInst
+ && objInst.TryUnboxAny(type, out Value unboxed))
+ {
+ stack.PushFromLocation(type, unboxed);
+ }
+ else
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+ }
+ break;
+
+ default:
+ return Status.Fail(methodIL.OwningMethod, opcode);
+ }
+
+ }
+
+ return Status.Fail(methodIL.OwningMethod, "Control fell through");
+ }
+
+ private static Value NewUninitializedLocationValue(TypeDesc locationType)
+ {
+ if (locationType.IsGCPointer || locationType.IsByRef)
+ {
+ return null;
+ }
+ else
+ {
+ Debug.Assert(locationType.IsValueType || locationType.IsPointer || locationType.IsFunctionPointer);
+ return new ValueTypeValue(locationType);
+ }
+ }
+
+ private bool TryHandleIntrinsicCall(MethodDesc method, Value[] parameters, out Value retVal)
+ {
+ retVal = default;
+
+ switch (method.Name)
+ {
+ case "InitializeArray":
+ if (method.OwningType is MetadataType mdType
+ && mdType.Name == "RuntimeHelpers" && mdType.Namespace == "System.Runtime.CompilerServices"
+ && mdType.Module == mdType.Context.SystemModule
+ && parameters[0] is ArrayInstance array
+ && parameters[1] is RuntimeFieldHandleValue fieldHandle
+ && fieldHandle.Field.IsStatic && fieldHandle.Field.HasRva
+ && fieldHandle.Field is Internal.TypeSystem.Ecma.EcmaField ecmaField)
+ {
+ byte[] rvaData = Internal.TypeSystem.Ecma.EcmaFieldExtensions.GetFieldRvaData(ecmaField);
+ return array.TryInitialize(rvaData);
+ }
+ return false;
+ }
+
+ return false;
+ }
+
+ private TypeDesc GetArgType(MethodDesc method, int index)
+ {
+ var sig = method.Signature;
+ int offset = 0;
+ if (!sig.IsStatic)
+ {
+ if (index == 0)
+ return method.OwningType.IsValueType ? method.OwningType.MakeByRefType() : method.OwningType;
+ offset = 1;
+ }
+
+ if ((uint)(index - offset) >= (uint)sig.Length)
+ ThrowHelper.ThrowInvalidProgramException();
+
+ return sig[index - offset];
+ }
+
+ class Stack : Stack
+ {
+ private readonly TargetDetails _target;
+
+ public Stack(int capacity, TargetDetails target) : base(capacity)
+ {
+ _target = target;
+ }
+
+ public new StackEntry Pop()
+ {
+ if (Count < 1)
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+
+ return base.Pop();
+ }
+
+ public bool TryPopIntValue(out int value)
+ {
+ if (Count == 0)
+ {
+ value = 0;
+ return false;
+ }
+
+ StackEntry entry = Pop();
+ if (entry.ValueKind == StackValueKind.Int32)
+ {
+ value = entry.Value.AsInt32();
+ return true;
+ }
+ else if (entry.ValueKind == StackValueKind.NativeInt)
+ {
+ if (_target.PointerSize == 8)
+ {
+ long longValue = entry.Value.AsInt64();
+ if (longValue < int.MinValue || longValue > int.MaxValue)
+ {
+ value = 0;
+ return false;
+ }
+ value = (int)longValue;
+ return true;
+ }
+
+ value = entry.Value.AsInt32();
+ return true;
+ }
+
+ value = 0;
+ return false;
+ }
+
+ public void Push(StackValueKind kind, Value val)
+ {
+ Push(new StackEntry(kind, val));
+ }
+
+ public void Push(ReferenceTypeValue value)
+ {
+ Push(StackValueKind.ObjRef, value);
+ }
+
+ public void Push(ByRefValue value)
+ {
+ Push(StackValueKind.ByRef, value);
+ }
+
+ public void PushFromLocation(TypeDesc locationType, Value value)
+ {
+ switch (locationType.UnderlyingType.Category)
+ {
+ case TypeFlags.Boolean:
+ case TypeFlags.Byte:
+ Push(StackValueKind.Int32, ValueTypeValue.FromInt32((byte)value.AsSByte())); break;
+ case TypeFlags.Char:
+ case TypeFlags.UInt16:
+ Push(StackValueKind.Int32, ValueTypeValue.FromInt32((ushort)value.AsInt16())); break;
+ case TypeFlags.SByte:
+ Push(StackValueKind.Int32, ValueTypeValue.FromInt32(value.AsSByte())); break;
+ case TypeFlags.Int16:
+ Push(StackValueKind.Int32, ValueTypeValue.FromInt32(value.AsInt16())); break;
+ case TypeFlags.Int32:
+ case TypeFlags.UInt32:
+ Push(StackValueKind.Int32, value); break;
+ case TypeFlags.Int64:
+ case TypeFlags.UInt64:
+ Push(StackValueKind.Int64, value); break;
+ case TypeFlags.IntPtr:
+ case TypeFlags.UIntPtr:
+ case TypeFlags.Pointer:
+ case TypeFlags.FunctionPointer:
+ Push(StackValueKind.NativeInt, value); break;
+ case TypeFlags.Single:
+ Push(StackValueKind.Float, ValueTypeValue.FromDouble(value.AsSingle())); break;
+ case TypeFlags.Double:
+ Push(StackValueKind.Float, value); break;
+ case TypeFlags.ValueType:
+ case TypeFlags.Nullable:
+ Push(StackValueKind.ValueType, value); break;
+ case TypeFlags.Class:
+ case TypeFlags.Interface:
+ case TypeFlags.Array:
+ case TypeFlags.SzArray:
+ Push(StackValueKind.ObjRef, value); break;
+ case TypeFlags.ByRef:
+ Push(StackValueKind.ByRef, value); break;
+ default:
+ throw new NotImplementedException();
+ }
+ }
+
+ public Value PopIntoLocation(TypeDesc locationType)
+ {
+ if (Count == 0)
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+
+ locationType = locationType.UnderlyingType;
+
+ StackEntry popped = Pop();
+
+ switch (popped.ValueKind)
+ {
+ case StackValueKind.Int64:
+ if (!locationType.IsWellKnownType(WellKnownType.Int64)
+ && !locationType.IsWellKnownType(WellKnownType.UInt64))
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+ return popped.Value;
+
+ case StackValueKind.Int32:
+ if (!locationType.IsWellKnownType(WellKnownType.Int32)
+ && !locationType.IsWellKnownType(WellKnownType.UInt32))
+ {
+ int value = popped.Value.AsInt32();
+ switch (locationType.Category)
+ {
+ case TypeFlags.SByte:
+ case TypeFlags.Byte:
+ case TypeFlags.Boolean:
+ return ValueTypeValue.FromSByte((sbyte)value);
+ case TypeFlags.Int16:
+ case TypeFlags.UInt16:
+ case TypeFlags.Char:
+ return ValueTypeValue.FromInt16((short)value);
+ // case TypeFlags.IntPtr: sign extend
+ // case TypeFlags.UIntPtr: zero extend
+ }
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+ return popped.Value;
+
+ case StackValueKind.NativeInt:
+ // If it's none of the natural pointer types, we might need to truncate.
+ if (!locationType.IsPointer
+ && !locationType.IsFunctionPointer
+ && !locationType.IsWellKnownType(WellKnownType.IntPtr)
+ && !locationType.IsWellKnownType(WellKnownType.UIntPtr))
+ {
+ long value = _target.PointerSize == 8 ? popped.Value.AsInt64() : popped.Value.AsInt32();
+ switch (locationType.Category)
+ {
+ case TypeFlags.SByte:
+ case TypeFlags.Byte:
+ case TypeFlags.Boolean:
+ return ValueTypeValue.FromSByte((sbyte)value);
+ case TypeFlags.Int16:
+ case TypeFlags.UInt16:
+ case TypeFlags.Char:
+ return ValueTypeValue.FromInt16((short)value);
+ case TypeFlags.Int32:
+ case TypeFlags.UInt32:
+ return ValueTypeValue.FromInt32((int)value);
+ // case TypeFlags.ByRef: start GC tracking
+ }
+
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+ return popped.Value;
+
+ case StackValueKind.Float:
+ if (locationType.IsWellKnownType(WellKnownType.Double))
+ {
+ return popped.Value;
+ }
+ else if (!locationType.IsWellKnownType(WellKnownType.Single))
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+ return ValueTypeValue.FromSingle((float)popped.Value.AsDouble());
+
+ case StackValueKind.ByRef:
+ if (!locationType.IsByRef)
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+ return popped.Value;
+
+ case StackValueKind.ObjRef:
+ if (!locationType.IsGCPointer)
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+ return popped.Value;
+
+ case StackValueKind.ValueType:
+ if (!locationType.IsValueType
+ || ((BaseValueTypeValue)popped.Value).Size != ((DefType)locationType).InstanceFieldSize.AsInt)
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+ return popped.Value;
+
+ default:
+ throw new NotImplementedException();
+ }
+ }
+ }
+
+ private enum StackValueKind
+ {
+ Unknown,
+ Int32,
+ Int64,
+ NativeInt,
+ Float,
+ ByRef,
+ ObjRef,
+ ValueType,
+ }
+
+ ///
+ /// Represents a field value that can be serialized into a preinitialized blob.
+ ///
+ public interface ISerializableValue
+ {
+ void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory);
+ }
+
+ ///
+ /// Represents a frozen object whose contents can be serialized into the executable.
+ ///
+ public interface ISerializableReference : ISerializableValue
+ {
+ void WriteContent(ref ObjectDataBuilder builder, NodeFactory factory);
+ }
+
+ ///
+ /// Represents a value with instance fields. This is either a reference type, or a byref to
+ /// a valuetype.
+ ///
+ private interface IHasInstanceFields
+ {
+ void SetField(FieldDesc field, Value value);
+ Value GetField(FieldDesc field);
+ ByRefValue GetFieldAddress(FieldDesc field);
+ }
+
+ ///
+ /// Represents a value that can be assigned into.
+ ///
+ private interface IAssignableValue
+ {
+ void Assign(Value value);
+ }
+
+ private abstract class Value : ISerializableValue
+ {
+ public abstract bool Equals(Value value);
+
+ public static bool Equals(Value value1, Value value2)
+ {
+ if (value1 == value2)
+ {
+ return true;
+ }
+ if (value1 == null || value2 == null)
+ {
+ return false;
+ }
+ return value1.Equals(value2);
+ }
+
+ public abstract void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory);
+
+ private T ThrowInvalidProgram()
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ return default;
+ }
+
+ public virtual sbyte AsSByte() => ThrowInvalidProgram();
+ public virtual short AsInt16() => ThrowInvalidProgram();
+ public virtual int AsInt32() => ThrowInvalidProgram();
+ public virtual long AsInt64() => ThrowInvalidProgram();
+ public virtual float AsSingle() => ThrowInvalidProgram();
+ public virtual double AsDouble() => ThrowInvalidProgram();
+ }
+
+ private abstract class BaseValueTypeValue : Value
+ {
+ public abstract int Size { get; }
+ }
+
+ // Also represents pointers and function pointer.
+ private class ValueTypeValue : BaseValueTypeValue, IAssignableValue
+ {
+ public readonly byte[] InstanceBytes;
+
+ public override int Size => InstanceBytes.Length;
+
+ public ValueTypeValue(TypeDesc type)
+ {
+ Debug.Assert(type.IsValueType || type.IsPointer || type.IsFunctionPointer);
+ InstanceBytes = new byte[type.GetElementSize().AsInt];
+ }
+
+ private ValueTypeValue(byte[] bytes)
+ {
+ InstanceBytes = bytes;
+ }
+
+ public ByRefValue CreateByRef()
+ {
+
+ return new ByRefValue(InstanceBytes, 0);
+ }
+
+ void IAssignableValue.Assign(Value value)
+ {
+ if (!(value is ValueTypeValue other)
+ || other.Size != Size)
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+
+ Array.Copy(((ValueTypeValue)value).InstanceBytes, InstanceBytes, InstanceBytes.Length);
+ }
+
+ public override bool Equals(Value value)
+ {
+ if (!(value is ValueTypeValue vtvalue)
+ || vtvalue.InstanceBytes.Length != InstanceBytes.Length)
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+
+ for (int i = 0; i < InstanceBytes.Length; i++)
+ {
+ if (InstanceBytes[i] != ((ValueTypeValue)value).InstanceBytes[i])
+ return false;
+ }
+
+ return true;
+ }
+
+ public override void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory)
+ {
+ Debug.Assert(field.FieldType.GetElementSize().AsInt == InstanceBytes.Length);
+ builder.EmitBytes(InstanceBytes);
+ }
+
+ private byte[] AsExactByteCount(int size)
+ {
+ if (InstanceBytes.Length != size)
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+ return InstanceBytes;
+ }
+
+ public override sbyte AsSByte() => (sbyte)AsExactByteCount(1)[0];
+ public override short AsInt16() => BitConverter.ToInt16(AsExactByteCount(2), 0);
+ public override int AsInt32() => BitConverter.ToInt32(AsExactByteCount(4), 0);
+ public override long AsInt64() => BitConverter.ToInt64(AsExactByteCount(8), 0);
+ public override float AsSingle() => BitConverter.ToSingle(AsExactByteCount(4), 0);
+ public override double AsDouble() => BitConverter.ToDouble(AsExactByteCount(8), 0);
+ public static ValueTypeValue FromSByte(sbyte value) => new ValueTypeValue(new byte[1] { (byte)value });
+ public static ValueTypeValue FromInt16(short value) => new ValueTypeValue(BitConverter.GetBytes(value));
+ public static ValueTypeValue FromInt32(int value) => new ValueTypeValue(BitConverter.GetBytes(value));
+ public static ValueTypeValue FromInt64(long value) => new ValueTypeValue(BitConverter.GetBytes(value));
+ public static ValueTypeValue FromSingle(float value) => new ValueTypeValue(BitConverter.GetBytes(value));
+ public static ValueTypeValue FromDouble(double value) => new ValueTypeValue(BitConverter.GetBytes(value));
+ }
+
+ private class RuntimeFieldHandleValue : BaseValueTypeValue
+ {
+ public FieldDesc Field { get; private set; }
+
+ public RuntimeFieldHandleValue(FieldDesc field)
+ {
+ Field = field;
+ }
+
+ public override int Size => Field.Context.Target.PointerSize;
+
+ public override bool Equals(Value value)
+ {
+ if (!(value is RuntimeFieldHandleValue))
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+
+ return Field == ((RuntimeFieldHandleValue)value).Field;
+ }
+
+ public override void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory)
+ {
+ Debug.Assert(field.FieldType.IsWellKnownType(WellKnownType.RuntimeFieldHandle));
+ builder.EmitPointerReloc(factory.RuntimeFieldHandle(Field));
+ }
+ }
+
+ private class ByRefValue : Value, IHasInstanceFields
+ {
+ public readonly byte[] PointedToBytes;
+ public readonly int PointedToOffset;
+
+ public ByRefValue(byte[] pointedToBytes, int pointedToOffset)
+ {
+ PointedToBytes = pointedToBytes;
+ PointedToOffset = pointedToOffset;
+ }
+
+ public override bool Equals(Value value)
+ {
+ if (!(value is ByRefValue))
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+
+ return PointedToBytes == ((ByRefValue)value).PointedToBytes
+ && PointedToOffset == ((ByRefValue)value).PointedToOffset;
+ }
+
+ Value IHasInstanceFields.GetField(FieldDesc field) => new FieldAccessor(PointedToBytes, PointedToOffset).GetField(field);
+ void IHasInstanceFields.SetField(FieldDesc field, Value value) => new FieldAccessor(PointedToBytes, PointedToOffset).SetField(field, value);
+ ByRefValue IHasInstanceFields.GetFieldAddress(FieldDesc field) => new FieldAccessor(PointedToBytes, PointedToOffset).GetFieldAddress(field);
+
+ public void Initialize(int size)
+ {
+ if ((uint)size > (uint)(PointedToBytes.Length - PointedToOffset))
+ {
+ ThrowHelper.ThrowInvalidProgramException();
+ }
+
+ for (int i = PointedToOffset; i < PointedToOffset + size; i++)
+ {
+ PointedToBytes[i] = 0;
+ }
+ }
+
+ public override void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory)
+ {
+ // This would imply we have a byref-typed static field. The layout algorithm should have blocked this.
+ throw new NotImplementedException();
+ }
+ }
+
+ private abstract class ReferenceTypeValue : Value, ISerializableReference
+ {
+ protected readonly TypeDesc _type;
+
+ protected ReferenceTypeValue(TypeDesc type) { _type = type; }
+
+ public override bool Equals(Value value)
+ {
+ return this == value;
+ }
+
+ public abstract void WriteContent(ref ObjectDataBuilder builder, NodeFactory factory);
+ }
+
+ private class ArrayInstance : ReferenceTypeValue
+ {
+ private readonly int _elementCount;
+ private readonly int _elementSize;
+ private readonly byte[] _data;
+
+ public ArrayInstance(ArrayType type, int elementCount)
+ : base(type)
+ {
+ _elementCount = elementCount;
+ _elementSize = type.ElementType.GetElementSize().AsInt;
+ _data = new byte[elementCount * _elementSize];
+ }
+
+ public bool TryInitialize(byte[] bytes)
+ {
+ if (bytes.Length != _data.Length)
+ return false;
+
+ Array.Copy(bytes, _data, bytes.Length);
+ return true;
+ }
+
+ public int Length
+ {
+ get
+ {
+ return _elementCount;
+ }
+ }
+
+ public bool TryStoreElement(int index, Value value)
+ {
+ Debug.Assert(value is ValueTypeValue);
+
+ if ((uint)index > (uint)Length)
+ return false;
+
+ var valueToStore = value as ValueTypeValue;
+ Debug.Assert(valueToStore.InstanceBytes.Length == _elementSize);
+ Array.Copy(valueToStore.InstanceBytes, 0, _data, index * _elementSize, valueToStore.InstanceBytes.Length);
+ return true;
+ }
+
+ public override void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory)
+ {
+ builder.EmitPointerReloc(factory.SerializedFrozenObject(field, this));
+ }
+
+ public override void WriteContent(ref ObjectDataBuilder builder, NodeFactory factory)
+ {
+ // EEType
+ var node = factory.ConstructedTypeSymbol(_type);
+ Debug.Assert(!node.RepresentsIndirectionCell); // Arrays are always local
+ builder.EmitPointerReloc(node);
+
+ // numComponents
+ builder.EmitInt(_elementCount);
+
+ int pointerSize = _type.Context.Target.PointerSize;
+ Debug.Assert(pointerSize == 8 || pointerSize == 4);
+
+ if (pointerSize == 8)
+ {
+ // padding numComponents in 64-bit
+ builder.EmitInt(0);
+ }
+
+ builder.EmitBytes(_data);
+ }
+ }
+
+ private class StringInstance : ReferenceTypeValue
+ {
+ private readonly string _value;
+
+ public StringInstance(TypeDesc stringType, string value)
+ : base(stringType)
+ {
+ _value = value;
+ }
+
+ public override void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory)
+ {
+ builder.EmitPointerReloc(factory.SerializedStringObject(_value));
+ }
+
+ public override void WriteContent(ref ObjectDataBuilder builder, NodeFactory factory)
+ {
+ // Not actually used by SerializedStringObject.
+ throw new NotImplementedException();
+ }
+ }
+
+ private class ObjectInstance : ReferenceTypeValue, IHasInstanceFields
+ {
+ private readonly byte[] _data;
+
+ public ObjectInstance(DefType type)
+ : base(type)
+ {
+ int size = type.InstanceByteCount.AsInt;
+ if (type.IsValueType)
+ size += type.Context.Target.PointerSize;
+ _data = new byte[size];
+ }
+
+ public static ObjectInstance Box(DefType type, byte[] data)
+ {
+ var inst = new ObjectInstance(type);
+ Array.Copy(data, 0, inst._data, type.Context.Target.PointerSize, data.Length);
+ return inst;
+ }
+
+ public bool TryUnboxAny(TypeDesc type, out Value value)
+ {
+ value = null;
+
+ if (!type.IsValueType || type.IsNullable)
+ return false;
+
+ if (_type.UnderlyingType != type.UnderlyingType)
+ return false;
+
+ var result = new ValueTypeValue(type);
+ Array.Copy(_data, type.Context.Target.PointerSize, result.InstanceBytes, 0, result.InstanceBytes.Length);
+ value = result;
+ return true;
+ }
+
+ Value IHasInstanceFields.GetField(FieldDesc field) => new FieldAccessor(_data).GetField(field);
+ void IHasInstanceFields.SetField(FieldDesc field, Value value) => new FieldAccessor(_data).SetField(field, value);
+ ByRefValue IHasInstanceFields.GetFieldAddress(FieldDesc field) => new FieldAccessor(_data).GetFieldAddress(field);
+
+ public override void WriteFieldData(ref ObjectDataBuilder builder, FieldDesc field, NodeFactory factory)
+ {
+ builder.EmitPointerReloc(factory.SerializedFrozenObject(field, this));
+ }
+
+ public override void WriteContent(ref ObjectDataBuilder builder, NodeFactory factory)
+ {
+ // EEType
+ var node = factory.ConstructedTypeSymbol(_type);
+ Debug.Assert(!node.RepresentsIndirectionCell); // Shouldn't have allowed preinitializing this
+ builder.EmitPointerReloc(node);
+
+ // We skip the first pointer because that's the EEType pointer
+ // we just initialized above.
+ int pointerSize = factory.Target.PointerSize;
+ builder.EmitBytes(_data, pointerSize, _data.Length - pointerSize);
+ }
+ }
+
+ private struct FieldAccessor
+ {
+ private readonly byte[] _instanceBytes;
+ private readonly int _offset;
+
+ public FieldAccessor(byte[] bytes, int offset = 0)
+ {
+ _instanceBytes = bytes;
+ _offset = 0;
+ }
+
+ public Value GetField(FieldDesc field)
+ {
+ Debug.Assert(!field.IsStatic);
+ Debug.Assert(!field.FieldType.IsGCPointer);
+ int fieldOffset = field.Offset.AsInt;
+ int fieldSize = field.FieldType.GetElementSize().AsInt;
+ if (fieldOffset + fieldSize > _instanceBytes.Length - _offset)
+ ThrowHelper.ThrowInvalidProgramException();
+
+ var result = new ValueTypeValue(field.FieldType);
+ Array.Copy(_instanceBytes, _offset + fieldOffset, result.InstanceBytes, 0, fieldSize);
+ return result;
+ }
+
+ public void SetField(FieldDesc field, Value value)
+ {
+ Debug.Assert(!field.IsStatic);
+ Debug.Assert(!field.FieldType.IsGCPointer);
+ int fieldOffset = field.Offset.AsInt;
+ int fieldSize = field.FieldType.GetElementSize().AsInt;
+ if (fieldOffset + fieldSize > _instanceBytes.Length - _offset)
+ ThrowHelper.ThrowInvalidProgramException();
+
+ Array.Copy(((ValueTypeValue)value).InstanceBytes, 0, _instanceBytes, _offset + fieldOffset, fieldSize);
+ }
+
+ public ByRefValue GetFieldAddress(FieldDesc field)
+ {
+ Debug.Assert(!field.IsStatic);
+ Debug.Assert(!field.FieldType.IsGCPointer);
+ int fieldOffset = field.Offset.AsInt;
+ int fieldSize = field.FieldType.GetElementSize().AsInt;
+ if (fieldOffset + fieldSize > _instanceBytes.Length - _offset)
+ ThrowHelper.ThrowInvalidProgramException();
+
+ return new ByRefValue(_instanceBytes, _offset + fieldOffset);
+ }
+ }
+
+ private struct StackEntry
+ {
+ public readonly StackValueKind ValueKind;
+ public readonly Value Value;
+
+ public StackEntry(StackValueKind valueKind, Value value)
+ {
+ ValueKind = valueKind;
+ Value = value;
+ }
+ }
+
+ private struct Status
+ {
+ public string FailureReason { get; }
+
+ public static Status Success => default;
+
+ public bool IsSuccessful => FailureReason == null;
+
+ private Status(string message)
+ {
+ FailureReason = message;
+ }
+
+ public static Status Fail(MethodDesc method, ILOpcode opcode, string detail = null)
+ {
+ return new Status($"Method '{method}', opcode '{opcode}' {detail ?? ""}");
+ }
+
+ public static Status Fail(MethodDesc method, string detail)
+ {
+ return new Status($"Method '{method}': {detail}");
+ }
+ }
+
+ public class PreinitializationInfo
+ {
+ private readonly Dictionary _fieldValues;
+
+ public MetadataType Type { get; }
+
+ public string FailureReason { get; }
+
+ public bool IsPreinitialized => _fieldValues != null;
+
+ public PreinitializationInfo(MetadataType type, IEnumerable> fieldValues)
+ {
+ Type = type;
+ _fieldValues = new Dictionary();
+ foreach (var field in fieldValues)
+ _fieldValues.Add(field.Key, field.Value);
+ }
+
+ public PreinitializationInfo(MetadataType type, string failureReason)
+ {
+ Type = type;
+ FailureReason = failureReason;
+ }
+
+ public ISerializableValue GetFieldValue(FieldDesc field)
+ {
+ Debug.Assert(IsPreinitialized);
+ Debug.Assert(field.OwningType == Type);
+ Debug.Assert(field.IsStatic && !field.HasRva && !field.IsThreadStatic && !field.IsLiteral);
+ return _fieldValues[field];
+ }
+ }
+ }
+}
diff --git a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj
index 7a753288572..b17fa10ee9f 100644
--- a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj
+++ b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj
@@ -168,8 +168,7 @@
-
-
+
@@ -357,6 +356,7 @@
+
diff --git a/src/ILCompiler/src/Program.cs b/src/ILCompiler/src/Program.cs
index d205c313eef..ec4659ce035 100644
--- a/src/ILCompiler/src/Program.cs
+++ b/src/ILCompiler/src/Program.cs
@@ -49,6 +49,8 @@ internal class Program
private string _exportsFile;
private bool _useScanner;
private bool _noScanner;
+ private bool _preinitStatics;
+ private bool _noPreinitStatics;
private bool _emitStackTraceData;
private string _mapFileName;
private string _metadataLogFileName;
@@ -190,6 +192,8 @@ private ArgumentSyntax ParseCommandLine(string[] args)
syntax.DefineOptionList("removefeature", ref _removedFeatures, "Framework features to remove");
syntax.DefineOption("singlethreaded", ref _singleThreaded, "Run compilation on a single thread");
syntax.DefineOption("instructionset", ref _instructionSet, "Instruction set to allow or disallow");
+ syntax.DefineOption("preinitstatics", ref _preinitStatics, "Interpret static constructors at compile time if possible (implied by -O)");
+ syntax.DefineOption("nopreinitstatics", ref _noPreinitStatics, "Do not interpret static constructors at compile time");
syntax.DefineOption("targetarch", ref _targetArchitectureStr, "Target architecture for cross compilation");
syntax.DefineOption("targetos", ref _targetOSStr, "Target OS for cross compilation");
@@ -648,7 +652,15 @@ private int Run(string[] args)
useScanner &= !_noScanner;
- builder.UseILProvider(ilProvider);
+ // Enable static data preinitialization in optimized builds.
+ bool preinitStatics = _preinitStatics ||
+ (_optimizationMode != OptimizationMode.None && !_isCppCodegen && !_multiFile);
+ preinitStatics &= !_noPreinitStatics;
+
+ var preinitManager = new PreinitializationManager(typeSystemContext, compilationGroup, ilProvider, preinitStatics);
+ builder
+ .UseILProvider(ilProvider)
+ .UsePreinitializationManager(preinitManager);
ILScanResults scanResults = null;
if (useScanner)
@@ -774,6 +786,8 @@ private int Run(string[] args)
if (debugInfoProvider is IDisposable)
((IDisposable)debugInfoProvider).Dispose();
+ preinitManager.LogStatistics(logger);
+
return 0;
}
diff --git a/tests/src/Simple/PreInitData/PreInitData.cs b/tests/src/Simple/PreInitData/PreInitData.cs
deleted file mode 100644
index e1585efe2bf..00000000000
--- a/tests/src/Simple/PreInitData/PreInitData.cs
+++ /dev/null
@@ -1,264 +0,0 @@
-// 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.Runtime.InteropServices;
-using System.Runtime.CompilerServices;
-using Internal.Runtime.CompilerServices;
-
-#region Place holder types for internal System.Private.CoreLib types
-
-namespace System.Runtime.CompilerServices
-{
- [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
- class PreInitializedAttribute: Attribute
- {
- }
-
- [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
- class InitDataBlobAttribute: Attribute
- {
- public InitDataBlobAttribute(Type type, string fieldName)
- {
-
- }
- }
-
- [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
- class TypeHandleFixupAttribute: Attribute
- {
- public TypeHandleFixupAttribute(int offset, Type fixupType)
- {
- }
- }
-
- [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
- class MethodAddrFixupAttribute: Attribute
- {
- public MethodAddrFixupAttribute(int offset, Type fixupType, string methodName)
- {
- }
- }
-}
-
-
-namespace System.Runtime.InteropServices
-{
- [AttributeUsage(AttributeTargets.Method)]
- public sealed class UnmanagedCallersOnlyAttribute : Attribute
- {
- public string EntryPoint;
-
- public CallingConvention CallingConvention;
-
- public UnmanagedCallersOnlyAttribute()
- {
- }
- }
-}
-
-#endregion
-
-namespace System.Runtime.InteropServices
-{
- [AttributeUsage((System.AttributeTargets.Method | System.AttributeTargets.Class))]
- internal class McgIntrinsicsAttribute : Attribute
- {
- }
-
- [McgIntrinsics]
- internal static class AddrofIntrinsics
- {
- // This method is implemented elsewhere in the toolchain
- internal static IntPtr AddrOf(T ftn) { throw new PlatformNotSupportedException(); }
- }
-}
-
-class Details
-{
- private static IntPtr PreInitializedInt32Field_DataBlob;
-
- [TypeHandleFixupAttribute(0, typeof(IntPtr))]
- private static IntPtr PreInitializedIntField_DataBlob;
-
-#if TARGET_64BIT
- [TypeHandleFixupAttribute(0, typeof(RuntimeTypeHandle))]
- [TypeHandleFixupAttribute(16, typeof(int))]
- [TypeHandleFixupAttribute(24, typeof(short))]
- [TypeHandleFixupAttribute(32, typeof(long))]
- [TypeHandleFixupAttribute(40, typeof(string))]
-#else
- [TypeHandleFixupAttribute(0, typeof(RuntimeTypeHandle))]
- [TypeHandleFixupAttribute(8, typeof(int))]
- [TypeHandleFixupAttribute(12, typeof(short))]
- [TypeHandleFixupAttribute(16, typeof(long))]
- [TypeHandleFixupAttribute(20, typeof(string))]
-#endif
- private static IntPtr PreInitializedTypeField_DataBlob;
-
-#if TARGET_64BIT
- [TypeHandleFixupAttribute(0, typeof(IntPtr))]
- [MethodAddrFixupAttribute(16, typeof(NativeMethods), "Func1")]
- [MethodAddrFixupAttribute(24, typeof(NativeMethods), "Func2")]
-#else
- [TypeHandleFixupAttribute(0, typeof(IntPtr))]
- [MethodAddrFixupAttribute(8, typeof(NativeMethods), "Func1")]
- [MethodAddrFixupAttribute(12, typeof(NativeMethods), "Func2")]
-#endif
- private static IntPtr PreInitializedMethodTypeField_DataBlob;
-}
-
-static class NativeMethods
-{
- [UnmanagedCallersOnly]
- internal static void Func1(int a)
- {
- }
-
- [UnmanagedCallersOnly]
- internal static void Func2(float b)
- {
- }
-}
-
-class PreInitData
-{
- internal static string StaticStringFieldBefore = "BEFORE";
-
- //
- // Reference type fields
- //
- [PreInitialized]
- [InitDataBlob(typeof(Details), "PreInitializedIntField_DataBlob")]
- internal static int[] PreInitializedIntField;
-
- [PreInitialized]
- [InitDataBlob(typeof(Details), "PreInitializedTypeField_DataBlob")]
- internal static RuntimeTypeHandle[] PreInitializedTypeField;
-
- [PreInitialized]
- [InitDataBlob(typeof(Details), "PreInitializedMethodField_DataBlob")]
- internal static IntPtr[] PreInitializedMethodField;
-
- //
- // Primitive type fields
- //
- [PreInitialized]
- [InitDataBlob(typeof(Details), "PreInitializedInt32Field_DataBlob")]
- internal static int PreInitializedInt32Field; // = 0x12345678
-
- internal static string StaticStringFieldAfter = "AFTER";
-}
-
-public class PreInitDataTest
-{
- const int Pass = 100;
- const int Fail = -1;
-
- public static int Main(string[] args)
- {
- int result = Pass;
-
- if (!TestPreInitPrimitiveData())
- {
- Console.WriteLine("Failed");
- result = Fail;
- }
-
- if (!TestPreInitIntData())
- {
- Console.WriteLine("Failed");
- result = Fail;
- }
-
- if (!TestPreInitTypeData())
- {
- Console.WriteLine("Failed");
- result = Fail;
- }
-
- if (!TestPreInitMethodData())
- {
- Console.WriteLine("Failed");
- result = Fail;
- }
-
- // Make sure PreInitializedField works with other statics
- if (!TestOtherStatics())
- {
- Console.WriteLine("Failed");
- result = Fail;
- }
-
- return result;
- }
-
- static bool TestPreInitPrimitiveData()
- {
- Console.WriteLine("Testing preinitialized primitive data...");
-
- if (PreInitData.PreInitializedInt32Field != 0x12345678)
- return false;
-
- return true;
- }
-
- static bool TestPreInitIntData()
- {
- Console.WriteLine("Testing preinitialized int array...");
-
- for (int i = 0; i < PreInitData.PreInitializedIntField.Length; ++i)
- {
- if (PreInitData.PreInitializedIntField[i] != i + 1)
- return false;
- }
-
- return true;
- }
-
- static bool TestPreInitTypeData()
- {
- Console.WriteLine("Testing preinitialized type array...");
-
- if (!PreInitData.PreInitializedTypeField[0].Equals(typeof(int).TypeHandle))
- return false;
- if (!PreInitData.PreInitializedTypeField[1].Equals(typeof(short).TypeHandle))
- return false;
- if (!PreInitData.PreInitializedTypeField[2].Equals(typeof(long).TypeHandle))
- return false;
- if (!PreInitData.PreInitializedTypeField[3].Equals(typeof(string).TypeHandle))
- return false;
-
- return true;
- }
-
- public delegate void Func1Proc(int a);
- public delegate void Func2Proc(float a);
-
- static bool TestPreInitMethodData()
- {
- Console.WriteLine("Testing preinitialized method array...");
-
- if (PreInitData.PreInitializedMethodField[0] != System.Runtime.InteropServices.AddrofIntrinsics.AddrOf(NativeMethods.Func1))
- return false;
-
- if (PreInitData.PreInitializedMethodField[1] != System.Runtime.InteropServices.AddrofIntrinsics.AddrOf(NativeMethods.Func2))
- return false;
-
- return true;
- }
-
- static bool TestOtherStatics()
- {
- Console.WriteLine("Testing other statics work well with preinitialized data in the same type...");
-
- if (PreInitData.StaticStringFieldBefore != "BEFORE")
- return false;
-
- if (PreInitData.StaticStringFieldAfter != "AFTER")
- return false;
-
- return true;
- }
-}
diff --git a/tests/src/Simple/PreInitData/PreInitData.ilproj b/tests/src/Simple/PreInitData/PreInitData.ilproj
deleted file mode 100644
index 6455ed8e37d..00000000000
--- a/tests/src/Simple/PreInitData/PreInitData.ilproj
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/tests/src/Simple/PreInitData/PreInitData32.il b/tests/src/Simple/PreInitData/PreInitData32.il
deleted file mode 100644
index 2885e60d32a..00000000000
--- a/tests/src/Simple/PreInitData/PreInitData32.il
+++ /dev/null
@@ -1,194 +0,0 @@
-// 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.
-
-// =============================================================
-// PreInitialized data
-// =============================================================
-
-.class private auto ansi beforefieldinit Details
- extends [mscorlib]System.Object
-{
- .field private static valuetype ''/'__StaticDataSize=4' PreInitializedInt32Field_DataBlob at Data_Int32
-
- .field private static valuetype ''/'__StaticArrayInitTypeSize=24' PreInitializedField_DataBlob at Data_IntArray
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32,
- class [mscorlib]System.Type) = ( 01 00 00 00 00 00 59 53 79 73 74 65 6D 2E 49 6E // ......YSystem.In
- 74 33 32 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 // t32, mscorlib, V
- 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 // ersion=4.0.0.0,
- 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C // Culture=neutral,
- 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D // PublicKeyToken=
- 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 // b77a5c561934e089
- 00 00 ) // 9..
-
- .field private static valuetype ''/'__StaticArrayInitTypeSize=24' PreInitializedTypeField_DataBlob at Data_TypeArray
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32,
- class [mscorlib]System.Type) = ( 01 00 14 00 00 00 5A 53 79 73 74 65 6D 2E 53 74 // ......ZSystem.St
- 72 69 6E 67 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 // ring, mscorlib,
- 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C // Version=4.0.0.0,
- 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C // Culture=neutral
- 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E // , PublicKeyToken
- 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 // =b77a5c561934e08
- 39 00 00 ) // 9..
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32,
- class [mscorlib]System.Type) = ( 01 00 10 00 00 00 59 53 79 73 74 65 6D 2E 49 6E // ......YSystem.In
- 74 36 34 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 // t64, mscorlib, V
- 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 // ersion=4.0.0.0,
- 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C // Culture=neutral,
- 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D // PublicKeyToken=
- 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 // b77a5c561934e089
- 00 00 )
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32,
- class [mscorlib]System.Type) = ( 01 00 0C 00 00 00 59 53 79 73 74 65 6D 2E 49 6E // ......YSystem.In
- 74 31 36 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 // t16, mscorlib, V
- 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 // ersion=4.0.0.0,
- 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C // Culture=neutral,
- 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D // PublicKeyToken=
- 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 // b77a5c561934e089
- 00 00 )
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32,
- class [mscorlib]System.Type) = ( 01 08 00 00 00 00 59 53 79 73 74 65 6D 2E 49 6E // ......YSystem.In
- 74 33 32 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 // t32, mscorlib, V
- 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 // ersion=4.0.0.0,
- 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C // Culture=neutral,
- 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D // PublicKeyToken=
- 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 // b77a5c561934e089
- 00 00 )
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32,
- class [mscorlib]System.Type) = ( 01 00 00 00 00 00 6B 53 79 73 74 65 6D 2E 52 75 // ......kSystem.Ru
- 6E 74 69 6D 65 54 79 70 65 48 61 6E 64 6C 65 2C // ntimeTypeHandle,
- 20 53 79 73 74 65 6D 2E 52 75 6E 74 69 6D 65 2C // System.Runtime,
- 20 56 65 72 73 69 6F 6E 3D 34 2E 32 2E 30 2E 30 // Version=4.2.0.0
- 2C 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 // , Culture=neutra
- 6C 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 // l, PublicKeyToke
- 6E 3D 62 30 33 66 35 66 37 66 31 31 64 35 30 61 // n=b03f5f7f11d50a
- 33 61 00 00 ) // 3a..
-
- .field private static valuetype ''/'__StaticArrayInitTypeSize=16' PreInitializedMethodField_DataBlob at Data_MethodArray
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32,
- class [mscorlib]System.Type) = ( 01 00 00 00 00 00 5A 53 79 73 74 65 6D 2E 49 6E // ......ZSystem.In
- 74 50 74 72 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 // tPtr, mscorlib,
- 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C // Version=4.0.0.0,
- 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C // Culture=neutral
- 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E // , PublicKeyToken
- 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 // =b77a5c561934e08
- 39 00 00 )
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.MethodAddrFixupAttribute::.ctor(int32,
- class [mscorlib]System.Type,
- string) = ( 01 00 08 00 00 00 0D 4E 61 74 69 76 65 4D 65 74 // .......NativeMet
- 68 6F 64 73 05 46 75 6E 63 31 00 00 ) // hods.Func1..
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.MethodAddrFixupAttribute::.ctor(int32,
- class [mscorlib]System.Type,
- string) = ( 01 00 0C 00 00 00 0D 4E 61 74 69 76 65 4D 65 74 // .......NativeMet
- 68 6F 64 73 05 46 75 6E 63 32 00 00 ) // hods.Func2..
-} // end of class Details
-
-.class public auto ansi beforefieldinit PreInitData
-{
- .field private static string StaticStringFieldBefore
-
- //===================================================================
- // preinitialized fields
- .field private static int32[] PreInitializedIntField
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.PreInitializedAttribute::.ctor() = ( 01 00 00 00 )
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.InitDataBlobAttribute::.ctor(class [mscorlib]System.Type,
- string) = ( 01 00 07 44 65 74 61 69 6C 73 1C 50 72 65 49 6E // ...Details.PreIn
- 69 74 69 61 6C 69 7A 65 64 46 69 65 6C 64 5F 44 // itializedField_D
- 61 74 61 42 6C 6F 62 00 00 ) // ataBlob..
-
- .field private static class [System.Private.CoreLib]Internal.Runtime.CompilerServices.FixupRuntimeTypeHandle[] PreInitializedTypeField
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.PreInitializedAttribute::.ctor() = ( 01 00 00 00 )
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.InitDataBlobAttribute::.ctor(class [mscorlib]System.Type,
- string) = ( 01 00 07 44 65 74 61 69 6C 73 20 50 72 65 49 6E // ...Details.PreIn
- 69 74 69 61 6C 69 7A 65 64 54 79 70 65 46 69 65 // itializedTypeFie
- 6C 64 5F 44 61 74 61 42 6C 6F 62 00 00 ) // ld_DataBlob..
-
- .field static assembly native int[] PreInitializedMethodField
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.PreInitializedAttribute::.ctor() = ( 01 00 00 00 )
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.InitDataBlobAttribute::.ctor(class [mscorlib]System.Type,
- string) = ( 01 00 07 44 65 74 61 69 6C 73 22 50 72 65 49 6E // ...Details.PreIn
- 69 74 69 61 6C 69 7A 65 64 4D 65 74 68 6F 64 46 // itializedMethodF
- 69 65 6C 64 5F 44 61 74 61 42 6C 6F 62 00 00 ) // ield_DataBlob..
-
- .field static assembly int32 PreInitializedInt32Field
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.PreInitializedAttribute::.ctor() = ( 01 00 00 00 )
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.InitDataBlobAttribute::.ctor(class [mscorlib]System.Type,
- string) = ( 01 00 07 44 65 74 61 69 6C 73 21 50 72 65 49 6E // ...Details!PreIn
- 69 74 69 61 6C 69 7A 65 64 49 6E 74 33 32 46 69 // itializedInt32Fi
- 65 6C 64 5F 44 61 74 61 42 6C 6F 62 00 00 ) // eld_DataBlob..
-
- //===================================================================
-
- .field private static string StaticStringFieldAfter
-
- .method private hidebysig specialname rtspecialname static
- void .cctor() cil managed
- {
- // Code size 21 (0x15)
- .maxstack 8
- IL_0000: ldstr "BEFORE"
- IL_0005: stsfld string PreInitData::StaticStringFieldBefore
- IL_000a: ldstr "AFTER"
- IL_000f: stsfld string PreInitData::StaticStringFieldAfter
- IL_0014: ret
- } // end of method PreInitData::.cctor
-} // end of class PreInitData
-
-.class private auto ansi ''
- extends [mscorlib]System.Object
-{
- .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
- .class explicit ansi sealed nested private '__StaticDataSize=4'
- extends [mscorlib]System.ValueType
- {
- .pack 1
- .size 4
- } // end of class '__StaticDataSize=4'
- .class explicit ansi sealed nested private '__StaticArrayInitTypeSize=24'
- extends [mscorlib]System.ValueType
- {
- .pack 1
- .size 24
- } // end of class '__StaticArrayInitTypeSize=24'
- .class explicit ansi sealed nested private '__StaticArrayInitTypeSize=16'
- extends [mscorlib]System.ValueType
- {
- .pack 1
- .size 16
- } // end of class '__StaticArrayInitTypeSize=16'
-} // end of class ''
-
-.class private abstract auto ansi sealed beforefieldinit NativeMethods
- extends [mscorlib]System.Object
-{
- .method assembly hidebysig static void
- Func1(int32 a) cil managed
- {
- .custom instance void [System.Private.CoreLib]System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = ( 01 00 00 00 )
- // Code size 2 (0x2)
- .maxstack 8
- IL_0000: nop
- IL_0001: ret
- } // end of method NativeMethods::Func1
-
- .method assembly hidebysig static void
- Func2(float32 b) cil managed
- {
- .custom instance void [System.Private.CoreLib]System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = ( 01 00 00 00 )
- // Code size 2 (0x2)
- .maxstack 8
- IL_0000: nop
- IL_0001: ret
- } // end of method NativeMethods::Func2
-
-} // end of class NativeMethods
-
-// =============================================================
-// Data blob
-// =============================================================
-
-.data cil Data_IntArray = bytearray ( 00 00 00 00 04 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00)
-.data cil Data_TypeArray = bytearray ( 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00)
-.data cil Data_MethodArray = bytearray ( 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 )
-.data cil Data_Int32 = bytearray ( 78 56 34 12 )
-
diff --git a/tests/src/Simple/PreInitData/PreInitData64.il b/tests/src/Simple/PreInitData/PreInitData64.il
deleted file mode 100644
index aabec11e749..00000000000
--- a/tests/src/Simple/PreInitData/PreInitData64.il
+++ /dev/null
@@ -1,192 +0,0 @@
-// 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.
-
-// =============================================================
-// PreInitialized data
-// =============================================================
-
-.class private auto ansi beforefieldinit Details
- extends [mscorlib]System.Object
-{
- .field private static valuetype ''/'__StaticDataSize=4' PreInitializedInt32Field_DataBlob at Data_Int32
- .field private static valuetype ''/'__StaticArrayInitTypeSize=32' PreInitializedField_DataBlob at Data_IntArray
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32,
- class [mscorlib]System.Type) = ( 01 00 00 00 00 00 59 53 79 73 74 65 6D 2E 49 6E // ......YSystem.In
- 74 33 32 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 // t32, mscorlib, V
- 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 // ersion=4.0.0.0,
- 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C // Culture=neutral,
- 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D // PublicKeyToken=
- 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 // b77a5c561934e089
- 00 00 ) // 9..
-
- .field private static valuetype ''/'__StaticArrayInitTypeSize=48' PreInitializedTypeField_DataBlob at Data_TypeArray
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32,
- class [mscorlib]System.Type) = ( 01 00 28 00 00 00 5A 53 79 73 74 65 6D 2E 53 74 // ......ZSystem.St
- 72 69 6E 67 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 // ring, mscorlib,
- 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C // Version=4.0.0.0,
- 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C // Culture=neutral
- 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E // , PublicKeyToken
- 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 // =b77a5c561934e08
- 39 00 00 ) // 9..
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32,
- class [mscorlib]System.Type) = ( 01 00 20 00 00 00 59 53 79 73 74 65 6D 2E 49 6E // ......YSystem.In
- 74 36 34 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 // t64, mscorlib, V
- 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 // ersion=4.0.0.0,
- 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C // Culture=neutral,
- 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D // PublicKeyToken=
- 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 // b77a5c561934e089
- 00 00 )
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32,
- class [mscorlib]System.Type) = ( 01 00 18 00 00 00 59 53 79 73 74 65 6D 2E 49 6E // ......YSystem.In
- 74 31 36 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 // t16, mscorlib, V
- 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 // ersion=4.0.0.0,
- 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C // Culture=neutral,
- 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D // PublicKeyToken=
- 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 // b77a5c561934e089
- 00 00 )
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32,
- class [mscorlib]System.Type) = ( 01 00 10 00 00 00 59 53 79 73 74 65 6D 2E 49 6E // ......YSystem.In
- 74 33 32 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 56 // t32, mscorlib, V
- 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C 20 // ersion=4.0.0.0,
- 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C 2C // Culture=neutral,
- 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E 3D // PublicKeyToken=
- 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 39 // b77a5c561934e089
- 00 00 )
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32,
- class [mscorlib]System.Type) = ( 01 00 00 00 00 00 6B 53 79 73 74 65 6D 2E 52 75 // ......kSystem.Ru
- 6E 74 69 6D 65 54 79 70 65 48 61 6E 64 6C 65 2C // ntimeTypeHandle,
- 20 53 79 73 74 65 6D 2E 52 75 6E 74 69 6D 65 2C // System.Runtime,
- 20 56 65 72 73 69 6F 6E 3D 34 2E 32 2E 30 2E 30 // Version=4.2.0.0
- 2C 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 // , Culture=neutra
- 6C 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 // l, PublicKeyToke
- 6E 3D 62 30 33 66 35 66 37 66 31 31 64 35 30 61 // n=b03f5f7f11d50a
- 33 61 00 00 ) // 3a..
-
- .field private static valuetype ''/'__StaticArrayInitTypeSize=32' PreInitializedMethodField_DataBlob at Data_MethodArray
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.TypeHandleFixupAttribute::.ctor(int32,
- class [mscorlib]System.Type) = ( 01 00 00 00 00 00 5A 53 79 73 74 65 6D 2E 49 6E // ......ZSystem.In
- 74 50 74 72 2C 20 6D 73 63 6F 72 6C 69 62 2C 20 // tPtr, mscorlib,
- 56 65 72 73 69 6F 6E 3D 34 2E 30 2E 30 2E 30 2C // Version=4.0.0.0,
- 20 43 75 6C 74 75 72 65 3D 6E 65 75 74 72 61 6C // Culture=neutral
- 2C 20 50 75 62 6C 69 63 4B 65 79 54 6F 6B 65 6E // , PublicKeyToken
- 3D 62 37 37 61 35 63 35 36 31 39 33 34 65 30 38 // =b77a5c561934e08
- 39 00 00 )
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.MethodAddrFixupAttribute::.ctor(int32,
- class [mscorlib]System.Type,
- string) = ( 01 00 10 00 00 00 0D 4E 61 74 69 76 65 4D 65 74 // .......NativeMet
- 68 6F 64 73 05 46 75 6E 63 31 00 00 ) // hods.Func1..
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.MethodAddrFixupAttribute::.ctor(int32,
- class [mscorlib]System.Type,
- string) = ( 01 00 18 00 00 00 0D 4E 61 74 69 76 65 4D 65 74 // .......NativeMet
- 68 6F 64 73 05 46 75 6E 63 32 00 00 ) // hods.Func2..
-} // end of class Details
-
-.class public auto ansi beforefieldinit PreInitData
-{
- .field private static string StaticStringFieldBefore
-
- //===================================================================
- // preinitialized fields
- .field private static int32[] PreInitializedIntField
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.PreInitializedAttribute::.ctor() = ( 01 00 00 00 )
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.InitDataBlobAttribute::.ctor(class [mscorlib]System.Type,
- string) = ( 01 00 07 44 65 74 61 69 6C 73 1C 50 72 65 49 6E // ...Details.PreIn
- 69 74 69 61 6C 69 7A 65 64 46 69 65 6C 64 5F 44 // itializedField_D
- 61 74 61 42 6C 6F 62 00 00 ) // ataBlob..
-
- .field private static class [System.Private.CoreLib]Internal.Runtime.CompilerServices.FixupRuntimeTypeHandle[] PreInitializedTypeField
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.PreInitializedAttribute::.ctor() = ( 01 00 00 00 )
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.InitDataBlobAttribute::.ctor(class [mscorlib]System.Type,
- string) = ( 01 00 07 44 65 74 61 69 6C 73 20 50 72 65 49 6E // ...Details.PreIn
- 69 74 69 61 6C 69 7A 65 64 54 79 70 65 46 69 65 // itializedTypeFie
- 6C 64 5F 44 61 74 61 42 6C 6F 62 00 00 ) // ld_DataBlob..
-
- .field static assembly native int[] PreInitializedMethodField
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.PreInitializedAttribute::.ctor() = ( 01 00 00 00 )
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.InitDataBlobAttribute::.ctor(class [mscorlib]System.Type,
- string) = ( 01 00 07 44 65 74 61 69 6C 73 22 50 72 65 49 6E // ...Details.PreIn
- 69 74 69 61 6C 69 7A 65 64 4D 65 74 68 6F 64 46 // itializedMethodF
- 69 65 6C 64 5F 44 61 74 61 42 6C 6F 62 00 00 ) // ield_DataBlob..
-
- .field static assembly int32 PreInitializedInt32Field
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.PreInitializedAttribute::.ctor() = ( 01 00 00 00 )
- .custom instance void [System.Private.CoreLib]System.Runtime.CompilerServices.InitDataBlobAttribute::.ctor(class [mscorlib]System.Type,
- string) = ( 01 00 07 44 65 74 61 69 6C 73 21 50 72 65 49 6E // ...Details!PreIn
- 69 74 69 61 6C 69 7A 65 64 49 6E 74 33 32 46 69 // itializedInt32Fi
- 65 6C 64 5F 44 61 74 61 42 6C 6F 62 00 00 ) // eld_DataBlob..
-
- //===================================================================
-
- .field private static string StaticStringFieldAfter
-
- .method private hidebysig specialname rtspecialname static
- void .cctor() cil managed
- {
- // Code size 21 (0x15)
- .maxstack 8
- IL_0000: ldstr "BEFORE"
- IL_0005: stsfld string PreInitData::StaticStringFieldBefore
- IL_000a: ldstr "AFTER"
- IL_000f: stsfld string PreInitData::StaticStringFieldAfter
- IL_0014: ret
- } // end of method PreInitData::.cctor
-} // end of class PreInitData
-
-.class private auto ansi ''
- extends [mscorlib]System.Object
-{
- .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
- .class explicit ansi sealed nested private '__StaticDataSize=4'
- extends [mscorlib]System.ValueType
- {
- .pack 1
- .size 4
- } // end of class '__StaticDataSize=4'
- .class explicit ansi sealed nested private '__StaticArrayInitTypeSize=32'
- extends [mscorlib]System.ValueType
- {
- .pack 1
- .size 32
- } // end of class '__StaticArrayInitTypeSize=32'
- .class explicit ansi sealed nested private '__StaticArrayInitTypeSize=48'
- extends [mscorlib]System.ValueType
- {
- .pack 1
- .size 48
- } // end of class '__StaticArrayInitTypeSize=16'
-} // end of class ''
-
-.class private abstract auto ansi sealed beforefieldinit NativeMethods
- extends [mscorlib]System.Object
-{
- .method assembly hidebysig static void
- Func1(int32 a) cil managed
- {
- .custom instance void [System.Private.CoreLib]System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = ( 01 00 00 00 )
- // Code size 2 (0x2)
- .maxstack 8
- IL_0000: nop
- IL_0001: ret
- } // end of method NativeMethods::Func1
-
- .method assembly hidebysig static void
- Func2(float32 b) cil managed
- {
- .custom instance void [System.Private.CoreLib]System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = ( 01 00 00 00 )
- // Code size 2 (0x2)
- .maxstack 8
- IL_0000: nop
- IL_0001: ret
- } // end of method NativeMethods::Func2
-
-} // end of class NativeMethods
-
-// =============================================================
-// Data blob
-// =============================================================
-
-.data cil Data_IntArray = bytearray ( 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00)
-.data cil Data_TypeArray = bytearray ( 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00)
-.data cil Data_MethodArray = bytearray ( 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00)
-.data cil Data_Int32 = bytearray ( 78 56 34 12 )
diff --git a/tests/src/Simple/PreInitData/PreInitDataTest.il b/tests/src/Simple/PreInitData/PreInitDataTest.il
deleted file mode 100644
index 1f0ad1ed979..00000000000
--- a/tests/src/Simple/PreInitData/PreInitDataTest.il
+++ /dev/null
@@ -1,508 +0,0 @@
-// 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.
-
-.assembly extern mscorlib
-{
- .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
- .ver 4:0:0:0
-}
-
-.assembly extern System.Private.CoreLib
-{
- .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....:
- .ver 4:0:0:0
-}
-
-.assembly PreInitData
-{
- .hash algorithm 0x00008004
- .ver 0:0:0:0
-}
-
-.module PreInitData.exe
-.imagebase 0x00400000
-.file alignment 0x00000200
-.stackreserve 0x00100000
-.subsystem 0x0003 // WINDOWS_CUI
-.corflags 0x00000001 // ILONLY
-// Image base: 0x024E0000
-
-// =============================================================
-// Intrinsics
-// =============================================================
-
-.class private auto ansi beforefieldinit System.Runtime.InteropServices.McgIntrinsicsAttribute
- extends [mscorlib]System.Attribute
-{
- .custom instance void [mscorlib]System.AttributeUsageAttribute::.ctor(valuetype [mscorlib]System.AttributeTargets) = ( 01 00 44 00 00 00 00 00 ) // ..D.....
- .method public hidebysig specialname rtspecialname
- instance void .ctor() cil managed
- {
- // Code size 8 (0x8)
- .maxstack 8
- IL_0000: ldarg.0
- IL_0001: call instance void [mscorlib]System.Attribute::.ctor()
- IL_0006: nop
- IL_0007: ret
- } // end of method McgIntrinsicsAttribute::.ctor
-
-} // end of class System.Runtime.InteropServices.McgIntrinsicsAttribute
-
-.class private abstract auto ansi sealed beforefieldinit System.Runtime.InteropServices.AddrofIntrinsics
- extends [mscorlib]System.Object
-{
- .custom instance void System.Runtime.InteropServices.McgIntrinsicsAttribute::.ctor() = ( 01 00 00 00 )
- .method assembly hidebysig static native int
- AddrOf(!!T ftn) cil managed
- {
- // Code size 7 (0x7)
- .maxstack 8
- IL_0000: nop
- IL_0001: newobj instance void [mscorlib]System.PlatformNotSupportedException::.ctor()
- IL_0006: throw
- } // end of method AddrofIntrinsics::AddrOf
-
-} // end of class System.Runtime.InteropServices.AddrofIntrinsics
-
-// =============================================================
-// Test code
-// =============================================================
-
-.class public auto ansi beforefieldinit PreInitDataTest
- extends [mscorlib]System.Object
-{
-.class auto ansi sealed nested public Func1Proc
- extends [mscorlib]System.MulticastDelegate
- {
- .method public hidebysig specialname rtspecialname
- instance void .ctor(object 'object',
- native int 'method') runtime managed
- {
- } // end of method Func1Proc::.ctor
-
- .method public hidebysig newslot virtual
- instance void Invoke(int32 a) runtime managed
- {
- } // end of method Func1Proc::Invoke
-
- .method public hidebysig newslot virtual
- instance class [mscorlib]System.IAsyncResult
- BeginInvoke(int32 a,
- class [mscorlib]System.AsyncCallback callback,
- object 'object') runtime managed
- {
- } // end of method Func1Proc::BeginInvoke
-
- .method public hidebysig newslot virtual
- instance void EndInvoke(class [mscorlib]System.IAsyncResult result) runtime managed
- {
- } // end of method Func1Proc::EndInvoke
-
- } // end of class Func1Proc
-
- .class auto ansi sealed nested public Func2Proc
- extends [mscorlib]System.MulticastDelegate
- {
- .method public hidebysig specialname rtspecialname
- instance void .ctor(object 'object',
- native int 'method') runtime managed
- {
- } // end of method Func2Proc::.ctor
-
- .method public hidebysig newslot virtual
- instance void Invoke(float32 a) runtime managed
- {
- } // end of method Func2Proc::Invoke
-
- .method public hidebysig newslot virtual
- instance class [mscorlib]System.IAsyncResult
- BeginInvoke(float32 a,
- class [mscorlib]System.AsyncCallback callback,
- object 'object') runtime managed
- {
- } // end of method Func2Proc::BeginInvoke
-
- .method public hidebysig newslot virtual
- instance void EndInvoke(class [mscorlib]System.IAsyncResult result) runtime managed
- {
- } // end of method Func2Proc::EndInvoke
-
- } // end of class Func2Proc
-
- .field private static literal int32 Pass = int32(0x00000064)
- .field private static literal int32 Fail = int32(0xFFFFFFFF)
- .method public hidebysig static int32 Main(string[] args) cil managed
- {
- .entrypoint
- // Code size 151 (0x97)
- .maxstack 2
- .locals init (int32 V_0,
- bool V_1,
- bool V_2,
- bool V_3,
- bool V_4,
- bool V_5,
- int32 V_6)
- IL_0000: nop
- IL_0001: ldc.i4.s 100
- IL_0003: stloc.0
- IL_0004: call bool PreInitDataTest::TestPreInitPrimitiveData()
- IL_0009: ldc.i4.0
- IL_000a: ceq
- IL_000c: stloc.1
- IL_000d: ldloc.1
- IL_000e: brfalse.s IL_001f
-
- IL_0010: nop
- IL_0011: ldstr "Failed"
- IL_0016: call void [mscorlib]System.Console::WriteLine(string)
- IL_001b: nop
- IL_001c: ldc.i4.m1
- IL_001d: stloc.0
- IL_001e: nop
- IL_001f: call bool PreInitDataTest::TestPreInitIntData()
- IL_0024: ldc.i4.0
- IL_0025: ceq
- IL_0027: stloc.2
- IL_0028: ldloc.2
- IL_0029: brfalse.s IL_003a
-
- IL_002b: nop
- IL_002c: ldstr "Failed"
- IL_0031: call void [mscorlib]System.Console::WriteLine(string)
- IL_0036: nop
- IL_0037: ldc.i4.m1
- IL_0038: stloc.0
- IL_0039: nop
- IL_003a: call bool PreInitDataTest::TestPreInitTypeData()
- IL_003f: ldc.i4.0
- IL_0040: ceq
- IL_0042: stloc.3
- IL_0043: ldloc.3
- IL_0044: brfalse.s IL_0055
-
- IL_0046: nop
- IL_0047: ldstr "Failed"
- IL_004c: call void [mscorlib]System.Console::WriteLine(string)
- IL_0051: nop
- IL_0052: ldc.i4.m1
- IL_0053: stloc.0
- IL_0054: nop
- IL_0055: call bool PreInitDataTest::TestPreInitMethodData()
- IL_005a: ldc.i4.0
- IL_005b: ceq
- IL_005d: stloc.s V_4
- IL_005f: ldloc.s V_4
- IL_0061: brfalse.s IL_0072
-
- IL_0063: nop
- IL_0064: ldstr "Failed"
- IL_0069: call void [mscorlib]System.Console::WriteLine(string)
- IL_006e: nop
- IL_006f: ldc.i4.m1
- IL_0070: stloc.0
- IL_0071: nop
- IL_0072: call bool PreInitDataTest::TestOtherStatics()
- IL_0077: ldc.i4.0
- IL_0078: ceq
- IL_007a: stloc.s V_5
- IL_007c: ldloc.s V_5
- IL_007e: brfalse.s IL_008f
-
- IL_0080: nop
- IL_0081: ldstr "Failed"
- IL_0086: call void [mscorlib]System.Console::WriteLine(string)
- IL_008b: nop
- IL_008c: ldc.i4.m1
- IL_008d: stloc.0
- IL_008e: nop
- IL_008f: ldloc.0
- IL_0090: stloc.s V_6
- IL_0092: br.s IL_0094
-
- IL_0094: ldloc.s V_6
- IL_0096: ret
- } // end of method PreInitDataTest::Main
-
- .method private hidebysig static bool TestPreInitPrimitiveData() cil managed
- {
- // Code size 41 (0x29)
- .maxstack 2
- .locals init (bool V_0,
- bool V_1)
- IL_0000: nop
- IL_0001: ldstr "Testing preinitialized primitive data..."
- IL_0006: call void [mscorlib]System.Console::WriteLine(string)
- IL_000b: nop
- IL_000c: ldsfld int32 PreInitData::PreInitializedInt32Field
- IL_0011: ldc.i4 0x12345678
- IL_0016: ceq
- IL_0018: ldc.i4.0
- IL_0019: ceq
- IL_001b: stloc.0
- IL_001c: ldloc.0
- IL_001d: brfalse.s IL_0023
-
- IL_001f: ldc.i4.0
- IL_0020: stloc.1
- IL_0021: br.s IL_0027
-
- IL_0023: ldc.i4.1
- IL_0024: stloc.1
- IL_0025: br.s IL_0027
-
- IL_0027: ldloc.1
- IL_0028: ret
- } // end of method PreInitDataTest::TestPreInitPrimitiveData
-
- .method private hidebysig static bool TestPreInitIntData() cil managed
- {
- // Code size 65 (0x41)
- .maxstack 3
- .locals init (int32 V_0,
- bool V_1,
- bool V_2,
- bool V_3)
- IL_0000: nop
- IL_0001: ldstr "Testing preinitialized int array..."
- IL_0006: call void [mscorlib]System.Console::WriteLine(string)
- IL_000b: nop
- IL_000c: ldc.i4.0
- IL_000d: stloc.0
- IL_000e: br.s IL_002d
-
- IL_0010: nop
- IL_0011: ldsfld int32[] PreInitData::PreInitializedIntField
- IL_0016: ldloc.0
- IL_0017: ldelem.i4
- IL_0018: ldloc.0
- IL_0019: ldc.i4.1
- IL_001a: add
- IL_001b: ceq
- IL_001d: ldc.i4.0
- IL_001e: ceq
- IL_0020: stloc.1
- IL_0021: ldloc.1
- IL_0022: brfalse.s IL_0028
-
- IL_0024: ldc.i4.0
- IL_0025: stloc.2
- IL_0026: br.s IL_003f
-
- IL_0028: nop
- IL_0029: ldloc.0
- IL_002a: ldc.i4.1
- IL_002b: add
- IL_002c: stloc.0
- IL_002d: ldloc.0
- IL_002e: ldsfld int32[] PreInitData::PreInitializedIntField
- IL_0033: ldlen
- IL_0034: conv.i4
- IL_0035: clt
- IL_0037: stloc.3
- IL_0038: ldloc.3
- IL_0039: brtrue.s IL_0010
-
- IL_003b: ldc.i4.1
- IL_003c: stloc.2
- IL_003d: br.s IL_003f
-
- IL_003f: ldloc.2
- IL_0040: ret
- } // end of method PreInitDataTest::TestPreInitIntData
-
-.method private hidebysig static bool TestPreInitTypeData() cil managed
-{
- // Code size 191 (0xbf)
- .maxstack 2
- .locals init (bool V_0,
- bool V_1,
- bool V_2,
- bool V_3,
- bool V_4)
- IL_0000: nop
- IL_0001: ldstr "Testing preinitialized type array..."
- IL_0006: call void [mscorlib]System.Console::WriteLine(string)
- IL_000b: nop
- IL_000c: ldsfld valuetype [mscorlib]System.RuntimeTypeHandle[] PreInitData::PreInitializedTypeField
- IL_0011: ldc.i4.0
- IL_0012: ldelema [mscorlib]System.RuntimeTypeHandle
- IL_0017: ldtoken [mscorlib]System.Int32
- IL_001c: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
- IL_0021: callvirt instance valuetype [mscorlib]System.RuntimeTypeHandle [mscorlib]System.Type::get_TypeHandle()
- IL_0026: call instance bool [mscorlib]System.RuntimeTypeHandle::Equals(valuetype [mscorlib]System.RuntimeTypeHandle)
- IL_002b: ldc.i4.0
- IL_002c: ceq
- IL_002e: stloc.0
- IL_002f: ldloc.0
- IL_0030: brfalse.s IL_0039
- IL_0032: ldc.i4.0
- IL_0033: stloc.1
- IL_0034: br IL_00bd
- IL_0039: ldsfld valuetype [mscorlib]System.RuntimeTypeHandle[] PreInitData::PreInitializedTypeField
- IL_003e: ldc.i4.1
- IL_003f: ldelema [mscorlib]System.RuntimeTypeHandle
- IL_0044: ldtoken [mscorlib]System.Int16
- IL_0049: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
- IL_004e: callvirt instance valuetype [mscorlib]System.RuntimeTypeHandle [mscorlib]System.Type::get_TypeHandle()
- IL_0053: call instance bool [mscorlib]System.RuntimeTypeHandle::Equals(valuetype [mscorlib]System.RuntimeTypeHandle)
- IL_0058: ldc.i4.0
- IL_0059: ceq
- IL_005b: stloc.2
- IL_005c: ldloc.2
- IL_005d: brfalse.s IL_0063
- IL_005f: ldc.i4.0
- IL_0060: stloc.1
- IL_0061: br.s IL_00bd
- IL_0063: ldsfld valuetype [mscorlib]System.RuntimeTypeHandle[] PreInitData::PreInitializedTypeField
- IL_0068: ldc.i4.2
- IL_0069: ldelema [mscorlib]System.RuntimeTypeHandle
- IL_006e: ldtoken [mscorlib]System.Int64
- IL_0073: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
- IL_0078: callvirt instance valuetype [mscorlib]System.RuntimeTypeHandle [mscorlib]System.Type::get_TypeHandle()
- IL_007d: call instance bool [mscorlib]System.RuntimeTypeHandle::Equals(valuetype [mscorlib]System.RuntimeTypeHandle)
- IL_0082: ldc.i4.0
- IL_0083: ceq
- IL_0085: stloc.3
- IL_0086: ldloc.3
- IL_0087: brfalse.s IL_008d
- IL_0089: ldc.i4.0
- IL_008a: stloc.1
- IL_008b: br.s IL_00bd
- IL_008d: ldsfld valuetype [mscorlib]System.RuntimeTypeHandle[] PreInitData::PreInitializedTypeField
- IL_0092: ldc.i4.3
- IL_0093: ldelema [mscorlib]System.RuntimeTypeHandle
- IL_0098: ldtoken [mscorlib]System.String
- IL_009d: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
- IL_00a2: callvirt instance valuetype [mscorlib]System.RuntimeTypeHandle [mscorlib]System.Type::get_TypeHandle()
- IL_00a7: call instance bool [mscorlib]System.RuntimeTypeHandle::Equals(valuetype [mscorlib]System.RuntimeTypeHandle)
- IL_00ac: ldc.i4.0
- IL_00ad: ceq
- IL_00af: stloc.s V_4
- IL_00b1: ldloc.s V_4
- IL_00b3: brfalse.s IL_00b9
- IL_00b5: ldc.i4.0
- IL_00b6: stloc.1
- IL_00b7: br.s IL_00bd
- IL_00b9: ldc.i4.1
- IL_00ba: stloc.1
- IL_00bb: br.s IL_00bd
- IL_00bd: ldloc.1
- IL_00be: ret
-} // end of method PreInitDataTest::TestPreInitTypeData
-
-
- .method private hidebysig static bool TestPreInitMethodData() cil managed
- {
- // Code size 92 (0x5c)
- .maxstack 3
- .locals init (bool V_0,
- bool V_1,
- bool V_2)
- IL_0000: nop
- IL_0001: ldstr "Testing preinitialized method array..."
- IL_0006: call void [mscorlib]System.Console::WriteLine(string)
- IL_000b: nop
- IL_000c: ldsfld native int[] PreInitData::PreInitializedMethodField
- IL_0011: ldc.i4.0
- IL_0012: ldelem.i
- IL_0013: ldnull
- IL_0014: ldftn void NativeMethods::Func1(int32)
- IL_001a: newobj instance void PreInitDataTest/Func1Proc::.ctor(object,
- native int)
- IL_001f: call native int System.Runtime.InteropServices.AddrofIntrinsics::AddrOf(!!0)
- IL_0024: call bool [mscorlib]System.IntPtr::op_Inequality(native int,
- native int)
- IL_0029: stloc.0
- IL_002a: ldloc.0
- IL_002b: brfalse.s IL_0031
-
- IL_002d: ldc.i4.0
- IL_002e: stloc.1
- IL_002f: br.s IL_005a
-
- IL_0031: ldsfld native int[] PreInitData::PreInitializedMethodField
- IL_0036: ldc.i4.1
- IL_0037: ldelem.i
- IL_0038: ldnull
- IL_0039: ldftn void NativeMethods::Func2(float32)
- IL_003f: newobj instance void PreInitDataTest/Func2Proc::.ctor(object,
- native int)
- IL_0044: call native int System.Runtime.InteropServices.AddrofIntrinsics::AddrOf(!!0)
- IL_0049: call bool [mscorlib]System.IntPtr::op_Inequality(native int,
- native int)
- IL_004e: stloc.2
- IL_004f: ldloc.2
- IL_0050: brfalse.s IL_0056
-
- IL_0052: ldc.i4.0
- IL_0053: stloc.1
- IL_0054: br.s IL_005a
-
- IL_0056: ldc.i4.1
- IL_0057: stloc.1
- IL_0058: br.s IL_005a
-
- IL_005a: ldloc.1
- IL_005b: ret
- } // end of method PreInitDataTest::TestPreInitMethodData
-
-
- .method private hidebysig static bool TestOtherStatics() cil managed
- {
- // Code size 64 (0x40)
- .maxstack 2
- .locals init (bool V_0,
- bool V_1,
- bool V_2)
- IL_0000: nop
- IL_0001: ldstr "Testing other statics work well with preinitialize"
- + "d data in the same type..."
- IL_0006: call void [mscorlib]System.Console::WriteLine(string)
- IL_000b: nop
- IL_000c: ldsfld string PreInitData::StaticStringFieldBefore
- IL_0011: ldstr "BEFORE"
- IL_0016: call bool [mscorlib]System.String::op_Inequality(string,
- string)
- IL_001b: stloc.0
- IL_001c: ldloc.0
- IL_001d: brfalse.s IL_0023
-
- IL_001f: ldc.i4.0
- IL_0020: stloc.1
- IL_0021: br.s IL_003e
-
- IL_0023: ldsfld string PreInitData::StaticStringFieldAfter
- IL_0028: ldstr "AFTER"
- IL_002d: call bool [mscorlib]System.String::op_Inequality(string,
- string)
- IL_0032: stloc.2
- IL_0033: ldloc.2
- IL_0034: brfalse.s IL_003a
-
- IL_0036: ldc.i4.0
- IL_0037: stloc.1
- IL_0038: br.s IL_003e
-
- IL_003a: ldc.i4.1
- IL_003b: stloc.1
- IL_003c: br.s IL_003e
-
- IL_003e: ldloc.1
- IL_003f: ret
- } // end of method PreInitDataTest::TestOtherStatics
-
- .method public hidebysig specialname rtspecialname
- instance void .ctor() cil managed
- {
- // Code size 8 (0x8)
- .maxstack 8
- IL_0000: ldarg.0
- IL_0001: call instance void [mscorlib]System.Object::.ctor()
- IL_0006: nop
- IL_0007: ret
- } // end of method PreInitDataTest::.ctor
-
-} // end of class PreInitDataTest
-
diff --git a/tests/src/Simple/PreInitData/PreInitData.cmd b/tests/src/Simple/Preinitialization/Preinitialization.cmd
similarity index 100%
rename from tests/src/Simple/PreInitData/PreInitData.cmd
rename to tests/src/Simple/Preinitialization/Preinitialization.cmd
diff --git a/tests/src/Simple/Preinitialization/Preinitialization.cs b/tests/src/Simple/Preinitialization/Preinitialization.cs
new file mode 100644
index 00000000000..55e0155828f
--- /dev/null
+++ b/tests/src/Simple/Preinitialization/Preinitialization.cs
@@ -0,0 +1,588 @@
+// 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.Runtime.InteropServices;
+
+using BindingFlags = System.Reflection.BindingFlags;
+
+internal class Program
+{
+ private static int Main()
+ {
+#if !MULTIMODULE_BUILD
+ TestLdstr.Run();
+ TestException.Run();
+ TestThreadStaticNotInitialized.Run();
+ TestUntouchedThreadStaticInitialized.Run();
+ TestPointers.Run();
+ TestConstants.Run();
+ TestArray.Run();
+ TestMdArray.Run();
+ TestSimpleObject.Run();
+ TestFinalizableObject.Run();
+ TestStoreIntoOtherStatic.Run();
+ TestCctorCycle.Run();
+ TestReferenceTypeAllocation.Run();
+ TestReferenceTypeWithGCPointerAllocation.Run();
+ TestRelationalOperators.Run();
+ TestTryFinally.Run();
+ TestTryCatch.Run();
+ TestBadClass.Run();
+ TestRefs.Run();
+#else
+ Console.WriteLine("Preinitialization is disabled in multimodule builds for now. Skipping test.");
+#endif
+
+ return 100;
+ }
+}
+
+class TestLdstr
+{
+ static string s_mine;
+ static bool s_literalsEqual;
+
+ static string GetOtherString() => "Hello";
+
+ static TestLdstr()
+ {
+ s_mine = nameof(TestLdstr);
+ s_literalsEqual = Object.ReferenceEquals("Hello", GetOtherString());
+ }
+
+ public static void Run()
+ {
+ Assert.IsPreinitialized(typeof(TestLdstr));
+ Assert.AreSame(nameof(TestLdstr), s_mine);
+ Assert.True(s_literalsEqual);
+ }
+}
+
+class TestException
+{
+ static bool s_wasThrown;
+
+ static TestException()
+ {
+ try
+ {
+ throw new Exception();
+ }
+ catch (Exception)
+ {
+ s_wasThrown = true;
+ }
+ }
+
+ public static void Run()
+ {
+ Assert.IsLazyInitialized(typeof(TestException));
+ Assert.True(s_wasThrown);
+ }
+}
+
+class TestThreadStaticNotInitialized
+{
+ [ThreadStatic]
+ static bool s_wasRun = true;
+
+ public static void Run()
+ {
+ Assert.IsLazyInitialized(typeof(TestThreadStaticNotInitialized));
+ Assert.True(s_wasRun);
+ }
+}
+
+class TestUntouchedThreadStaticInitialized
+{
+ [ThreadStatic]
+#pragma warning disable 169
+ static bool s_unused;
+#pragma warning restore 169
+ static bool s_wasRun = true;
+
+ public static void Run()
+ {
+ Assert.IsPreinitialized(typeof(TestUntouchedThreadStaticInitialized));
+ Assert.True(s_wasRun);
+ }
+}
+
+unsafe class TestPointers
+{
+ static byte* s_myByte = (byte*)123;
+ static void* s_myVoid = GimmeVoid(s_myByte);
+ static byte*[] s_byteStarArray = new byte*[] { (byte*)123, (byte*)456 };
+
+ static void* GimmeVoid(byte* template)
+ {
+ return template;
+ }
+
+ public static void Run()
+ {
+ Assert.IsPreinitialized(typeof(TestPointers));
+ Assert.AreEqual((void*)123, s_myByte);
+ Assert.AreEqual((void*)123, s_myVoid);
+
+ Assert.AreEqual(2, s_byteStarArray.Length);
+ Assert.AreEqual((byte*)123, s_byteStarArray[0]);
+ Assert.AreEqual((byte*)456, s_byteStarArray[1]);
+ }
+}
+
+class TestConstants
+{
+ static bool s_bool = true;
+ static int s_smallInt = 3;
+ static int s_mediumInd = 70;
+ static int s_bigInt = 2000000;
+ static long s_hugeInt = 20000000000;
+ static float s_float = 3.14f;
+ static double s_double = 3.14;
+
+ public static void Run()
+ {
+ Assert.IsPreinitialized(typeof(TestConstants));
+ Assert.AreEqual(true, s_bool);
+ Assert.AreEqual(3, s_smallInt);
+ Assert.AreEqual(70, s_mediumInd);
+ Assert.AreEqual(2000000, s_bigInt);
+ Assert.AreEqual(20000000000, s_hugeInt);
+ Assert.AreEqual(3.14f, s_float);
+ Assert.AreEqual(3.14, s_double);
+ }
+}
+
+class TestArray
+{
+ struct MyValueType
+ {
+ public bool B;
+ public int I;
+ }
+
+ enum MyEnum
+ {
+ One, Two
+ }
+
+ static byte[] s_byteArray;
+ static MyValueType[] s_valueTypeArray;
+ static int s_byteArrayCount;
+ static MyEnum[] s_enumArray;
+
+ static TestArray()
+ {
+ s_byteArray = new byte[]
+ {
+ 1, 2, 3, 9, 8, 7, 1, 2, 3, 9, 8, 7
+ };
+
+ s_byteArrayCount = s_byteArray.Length;
+
+ s_valueTypeArray = new MyValueType[2]
+ {
+ new MyValueType { B = false, I = 555 },
+ new MyValueType { B = true, I = 565 },
+ };
+
+ s_enumArray = new MyEnum[2] { MyEnum.One, MyEnum.Two };
+ }
+
+ public static void Run()
+ {
+ Assert.IsPreinitialized(typeof(TestArray));
+ Assert.AreEqual(s_byteArray.Length, 12);
+ Assert.AreEqual(s_byteArray[0], 1);
+ Assert.AreEqual(s_byteArray[1], 2);
+ Assert.AreEqual(s_byteArray[11], 7);
+ Assert.AreEqual(s_byteArrayCount, 12);
+
+ Assert.AreEqual(s_valueTypeArray.Length, 2);
+ Assert.AreEqual(s_valueTypeArray[0].B, false);
+ Assert.AreEqual(s_valueTypeArray[0].I, 555);
+ Assert.AreEqual(s_valueTypeArray[1].B, true);
+ Assert.AreEqual(s_valueTypeArray[1].I, 565);
+
+ Assert.AreEqual(s_enumArray.Length, 2);
+ Assert.AreEqual((int)s_enumArray[0], (int)MyEnum.One);
+ Assert.AreEqual((int)s_enumArray[1], (int)MyEnum.Two);
+ }
+}
+
+class TestMdArray
+{
+ static byte[,] s_myMdArray = new byte[10, 10];
+
+ public static void Run()
+ {
+ Assert.IsLazyInitialized(typeof(TestMdArray));
+ Assert.AreEqual(100, s_myMdArray.Length);
+ }
+}
+
+class TestSimpleObject
+{
+ static object s_object = new object();
+
+ public static void Run()
+ {
+ Assert.IsPreinitialized(typeof(TestSimpleObject));
+ Assert.AreSame(typeof(object), s_object.GetType());
+ }
+}
+
+class TestFinalizableObject
+{
+ class Finalizable
+ {
+ ~Finalizable()
+ {
+ Console.WriteLine("Finalized");
+ }
+ }
+
+ static object s_object = new Finalizable();
+
+ public static void Run()
+ {
+ Assert.IsLazyInitialized(typeof(TestFinalizableObject));
+ Assert.AreSame(typeof(Finalizable), s_object.GetType());
+ }
+}
+
+static class TestStoreIntoOtherStatic
+{
+ class Park
+ {
+ public static int s_parked;
+ }
+
+ static TestStoreIntoOtherStatic()
+ {
+ Park.s_parked = 123;
+ }
+
+ public static void Run()
+ {
+ Assert.IsLazyInitialized(typeof(TestStoreIntoOtherStatic));
+ }
+}
+
+static class TestCctorCycle
+{
+ static readonly int s_value = Cycler.s_theValue;
+
+ class Cycler
+ {
+ public static readonly int s_theValue = s_value;
+ }
+
+ public static void Run()
+ {
+ Assert.IsLazyInitialized(typeof(TestCctorCycle));
+ Assert.AreEqual(0, s_value);
+ }
+}
+
+class TestReferenceTypeAllocation
+{
+ class ReferenceType
+ {
+ public int IntValue;
+ public double DoubleValue;
+
+ public ReferenceType(int intValue, double doubleValue)
+ {
+ IntValue = intValue;
+ DoubleValue = doubleValue;
+ }
+ }
+
+ static ReferenceType s_referenceType = new ReferenceType(12345, 3.14159);
+
+ public static void Run()
+ {
+ Assert.IsPreinitialized(typeof(TestReferenceTypeAllocation));
+ Assert.AreEqual(12345, s_referenceType.IntValue);
+ Assert.AreEqual(3.14159, s_referenceType.DoubleValue);
+ }
+}
+
+class TestReferenceTypeWithGCPointerAllocation
+{
+ class ReferenceType
+ {
+ public string StringValue;
+
+ public ReferenceType(string stringvalue)
+ {
+ StringValue = stringvalue;
+ }
+ }
+
+ static ReferenceType s_referenceType = new ReferenceType("hi");
+
+ public static void Run()
+ {
+ Assert.IsLazyInitialized(typeof(TestReferenceTypeWithGCPointerAllocation));
+ Assert.AreSame("hi", s_referenceType.StringValue);
+ }
+}
+
+static class TestRelationalOperators
+{
+ static int s_zeroInt = 0;
+ static double s_zeroDouble = 0.0;
+ static long s_zeroLong = 0;
+ static int s_minusOneInt = -1;
+ static long s_minusOneLong = -1;
+
+ static bool s_finished;
+
+ static TestRelationalOperators()
+ {
+ if (s_zeroInt > 0)
+ throw new Exception();
+ if (s_zeroInt < 0)
+ throw new Exception();
+ if (s_zeroInt >= 0 && s_zeroInt <= 0)
+ {
+ if (s_zeroLong > 0)
+ throw new Exception();
+ if (s_zeroLong < 0)
+ throw new Exception();
+ if (s_zeroLong >= 0 && s_zeroLong <= 0)
+ {
+ if (s_zeroDouble > 0)
+ throw new Exception();
+ if (s_zeroDouble < 0)
+ throw new Exception();
+ if (s_zeroDouble >= 0 && s_zeroDouble <= 0)
+ {
+ if ((uint)s_minusOneInt < (uint)s_zeroInt)
+ throw new Exception();
+ if ((uint)s_zeroInt > (uint)s_minusOneInt)
+ throw new Exception();
+ if ((ulong)s_minusOneLong < (ulong)s_zeroLong)
+ throw new Exception();
+ if ((ulong)s_zeroLong > (ulong)s_minusOneLong)
+ throw new Exception();
+
+ if (s_zeroInt == 0 && s_zeroLong == 0)
+ s_finished = true;
+ }
+ }
+ }
+ }
+
+ public static void Run()
+ {
+ Assert.IsPreinitialized(typeof(TestRelationalOperators));
+ Assert.AreEqual(true, s_finished);
+ }
+}
+
+class TestTryFinally
+{
+ static int s_cookie;
+
+ static TestTryFinally()
+ {
+ try
+ {
+ if (new byte[0].Length > 0)
+ throw new Exception();
+ }
+ finally
+ {
+ s_cookie = 1985;
+ }
+ }
+
+ public static void Run()
+ {
+ Assert.IsLazyInitialized(typeof(TestTryFinally));
+ Assert.AreEqual(1985, s_cookie);
+ }
+}
+
+class TestTryCatch
+{
+ static int s_cookie;
+
+ static TestTryCatch()
+ {
+ try
+ {
+ if (s_cookie > 0)
+ throw null;
+ }
+ catch (Exception)
+ {
+ s_cookie = 100;
+ }
+ s_cookie = 2020;
+ }
+
+ public static void Run()
+ {
+ Assert.IsPreinitialized(typeof(TestTryCatch));
+ Assert.AreEqual(2020, s_cookie);
+ }
+}
+
+class TestBadClass
+{
+ [StructLayout(LayoutKind.Explicit)]
+ class BadLayoutClass
+ {
+ }
+
+ static int s_cookie;
+ static object s_badClass;
+
+ static object MakeBadLayoutClass() => new BadLayoutClass();
+
+ static TestBadClass()
+ {
+ try
+ {
+ s_badClass = MakeBadLayoutClass();
+ s_cookie = -1;
+ }
+ catch (Exception)
+ {
+ s_cookie = 1;
+ }
+ }
+
+ public static void Run()
+ {
+ Assert.IsLazyInitialized(typeof(TestBadClass));
+ Assert.AreEqual(1, s_cookie);
+ Assert.AreSame(null, s_badClass);
+ }
+}
+
+class TestRefs
+{
+ struct IntStruct { public int Value { get; set; } }
+ struct DoubleStruct { public double Value { get; set; } }
+
+ static IntStruct s_value1;
+ static IntStruct s_value2;
+ static DoubleStruct s_doubleValue;
+
+ static ref IntStruct PickOne(int which)
+ {
+ if (which == 1)
+ return ref s_value1;
+ return ref s_value2;
+ }
+
+ static void Set(ref IntStruct location, int value)
+ {
+ location.Value = value;
+ }
+
+ static TestRefs()
+ {
+ ref IntStruct loc1 = ref PickOne(1);
+ Set(ref loc1, 41);
+ s_value1.Value++;
+
+ s_value2.Value = 98;
+ ref IntStruct loc2 = ref PickOne(2);
+ if (loc2.Value == 98)
+ {
+ loc2.Value++;
+ }
+ if (s_value2.Value == 99)
+ {
+ s_value2.Value++;
+ }
+
+ ref DoubleStruct dblRef = ref s_doubleValue;
+ dblRef.Value = 3.14;
+ }
+
+ public static void Run()
+ {
+ Assert.IsPreinitialized(typeof(TestRefs));
+ Assert.AreEqual(42, s_value1.Value);
+ Assert.AreEqual(100, s_value2.Value);
+ Assert.AreEqual(3.14, s_doubleValue.Value);
+ }
+}
+
+static class Assert
+{
+ private static bool HasCctor(Type type)
+ {
+ return type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Static, null, Type.EmptyTypes, null) != null;
+ }
+
+ public static void IsPreinitialized(Type type)
+ {
+ if (HasCctor(type))
+ throw new Exception();
+ }
+
+ public static void IsLazyInitialized(Type type)
+ {
+ if (!HasCctor(type))
+ throw new Exception();
+ }
+
+ public static unsafe void AreEqual(void* v1, void* v2)
+ {
+ if (v1 != v2)
+ throw new Exception();
+ }
+
+ public static unsafe void AreEqual(bool v1, bool v2)
+ {
+ if (v1 != v2)
+ throw new Exception();
+ }
+
+ public static unsafe void AreEqual(int v1, int v2)
+ {
+ if (v1 != v2)
+ throw new Exception();
+ }
+
+ public static unsafe void AreEqual(long v1, long v2)
+ {
+ if (v1 != v2)
+ throw new Exception();
+ }
+
+ public static unsafe void AreEqual(float v1, float v2)
+ {
+ if (v1 != v2)
+ throw new Exception();
+ }
+
+ public static unsafe void AreEqual(double v1, double v2)
+ {
+ if (v1 != v2)
+ throw new Exception();
+ }
+
+ public static void True(bool v)
+ {
+ if (!v)
+ throw new Exception();
+ }
+
+ public static void AreSame(T v1, T v2) where T : class
+ {
+ if (v1 != v2)
+ throw new Exception();
+ }
+}
diff --git a/tests/src/Simple/Preinitialization/Preinitialization.csproj b/tests/src/Simple/Preinitialization/Preinitialization.csproj
new file mode 100644
index 00000000000..9a3883111e1
--- /dev/null
+++ b/tests/src/Simple/Preinitialization/Preinitialization.csproj
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/src/Simple/PreInitData/PreInitData.sh b/tests/src/Simple/Preinitialization/Preinitialization.sh
similarity index 100%
rename from tests/src/Simple/PreInitData/PreInitData.sh
rename to tests/src/Simple/Preinitialization/Preinitialization.sh
diff --git a/tests/src/Simple/PreInitData/no_cpp b/tests/src/Simple/Preinitialization/no_cpp
similarity index 100%
rename from tests/src/Simple/PreInitData/no_cpp
rename to tests/src/Simple/Preinitialization/no_cpp