diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 5bc0d12cbd6508..d77fcfb5ef02b5 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -8989,6 +8989,7 @@ void Compiler::impImportBlockCode(BasicBlock* block) FALLTHROUGH; case CORINFO_FIELD_STATIC_SHARED_STATIC_HELPER: case CORINFO_FIELD_STATIC_ADDRESS: + case CORINFO_FIELD_STATIC_RELOCATABLE: // Replace static read-only fields with constant if possible if ((aflags & CORINFO_ACCESS_GET) && (fieldInfo.fieldFlags & CORINFO_FLG_FIELD_FINAL)) { @@ -9005,7 +9006,6 @@ void Compiler::impImportBlockCode(BasicBlock* block) case CORINFO_FIELD_STATIC_RVA_ADDRESS: case CORINFO_FIELD_STATIC_GENERICS_STATIC_HELPER: case CORINFO_FIELD_STATIC_READYTORUN_HELPER: - case CORINFO_FIELD_STATIC_RELOCATABLE: op1 = impImportStaticFieldAddress(&resolvedToken, (CORINFO_ACCESS_FLAGS)aflags, &fieldInfo, lclTyp, &indirFlags); break; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs index f2d0736abb2d00..6377ce28dd89d5 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -1237,6 +1237,13 @@ public SerializedFrozenObjectNode SerializedFrozenObject(MetadataType owningType return _frozenObjectNodes.GetOrAdd(new SerializedFrozenObjectKey(owningType, allocationSiteId, data)); } + public FrozenRuntimeTypeNode SerializedMaximallyConstructableRuntimeTypeObject(TypeDesc type) + { + if (ConstructedEETypeNode.CreationAllowed(type)) + return SerializedConstructedRuntimeTypeObject(type); + return SerializedNecessaryRuntimeTypeObject(type); + } + private NodeCache _frozenConstructedRuntimeTypeNodes; public FrozenRuntimeTypeNode SerializedConstructedRuntimeTypeObject(TypeDesc type) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypePreinit.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypePreinit.cs index fc7494e1c383b9..1b097fb92a681a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypePreinit.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypePreinit.cs @@ -375,7 +375,7 @@ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack this; - public override void WriteFieldData(ref ObjectDataBuilder builder, NodeFactory factory) => throw new NotImplementedException(); } private sealed class ReadOnlySpanValue : BaseValueTypeValue, IInternalModelingOnlyValue @@ -2641,7 +2651,7 @@ public override bool Equals(Value value) return this == value; } - public abstract ReferenceTypeValue ToForeignInstance(int baseInstructionCounter); + public abstract ReferenceTypeValue ToForeignInstance(int baseInstructionCounter, TypePreinit preinitContext); } private struct AllocationSite @@ -2669,7 +2679,7 @@ public AllocatedReferenceTypeValue(TypeDesc type, AllocationSite allocationSite) AllocationSite = allocationSite; } - public override ReferenceTypeValue ToForeignInstance(int baseInstructionCounter) => + public override ReferenceTypeValue ToForeignInstance(int baseInstructionCounter, TypePreinit preinitContext) => new ForeignTypeInstance( Type, new AllocationSite(AllocationSite.OwningType, AllocationSite.InstructionCounter - baseInstructionCounter), @@ -2889,7 +2899,7 @@ public override void WriteFieldData(ref ObjectDataBuilder builder, NodeFactory f } } - public override ReferenceTypeValue ToForeignInstance(int baseInstructionCounter) => this; + public override ReferenceTypeValue ToForeignInstance(int baseInstructionCounter, TypePreinit preinitContext) => this; } private sealed class StringInstance : ReferenceTypeValue, IHasInstanceFields @@ -2947,7 +2957,15 @@ public override bool GetRawData(NodeFactory factory, out object data) return true; } - public override ReferenceTypeValue ToForeignInstance(int baseInstructionCounter) => this; + public override ReferenceTypeValue ToForeignInstance(int baseInstructionCounter, TypePreinit preinitContext) + { + string value = ValueAsString; + if (!preinitContext._internedStrings.TryGetValue(value, out StringInstance result)) + { + preinitContext._internedStrings.Add(value, result = new StringInstance(Type, value)); + } + return result; + } Value IHasInstanceFields.GetField(FieldDesc field) => new FieldAccessor(_value).GetField(field); void IHasInstanceFields.SetField(FieldDesc field, Value value) => ThrowHelper.ThrowInvalidProgramException(); ByRefValue IHasInstanceFields.GetFieldAddress(FieldDesc field) => new FieldAccessor(_value).GetFieldAddress(field); diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs index 29e7bce7804672..885f2ce2026c50 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs @@ -83,8 +83,8 @@ public FrozenRuntimeTypeNode NecessaryRuntimeTypeIfPossible(TypeDesc type) { bool canPotentiallyConstruct = _devirtualizationManager == null ? true : _devirtualizationManager.CanConstructType(type); - if (canPotentiallyConstruct && ConstructedEETypeNode.CreationAllowed(type)) - return _nodeFactory.SerializedConstructedRuntimeTypeObject(type); + if (canPotentiallyConstruct) + return _nodeFactory.SerializedMaximallyConstructableRuntimeTypeObject(type); return _nodeFactory.SerializedNecessaryRuntimeTypeObject(type); } diff --git a/src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.cs b/src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.cs index f2c52e452e9e73..5252e1f2d58c33 100644 --- a/src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.cs +++ b/src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.cs @@ -657,6 +657,7 @@ class OtherClass static int s_intValue = OtherClass.IntValue; static string s_stringValue = OtherClass.StringValue; static object s_objectValue = OtherClass.ObjectValue; + static bool s_areStringsSame = Object.ReferenceEquals(OtherClass.StringValue, "Hello"); public static void Run() { @@ -664,6 +665,7 @@ public static void Run() Assert.AreEqual(OtherClass.IntValue, s_intValue); Assert.AreSame(OtherClass.StringValue, s_stringValue); Assert.AreSame(OtherClass.ObjectValue, s_objectValue); + Assert.True(s_areStringsSame); } } @@ -1282,8 +1284,8 @@ public static void Run() Assert.True(!Foo.IsChar); Assert.True(Foo.IsBool); - Assert.IsLazyInitialized(typeof(CharHolder)); - Assert.IsLazyInitialized(typeof(IsChar)); + Assert.IsPreinitialized(typeof(CharHolder)); + Assert.IsPreinitialized(typeof(IsChar)); Assert.True(IsChar.Is); } } diff --git a/src/tests/nativeaot/SmokeTests/UnitTests/Interfaces.cs b/src/tests/nativeaot/SmokeTests/UnitTests/Interfaces.cs index e4b7f437382360..6c70d13c31a5f4 100644 --- a/src/tests/nativeaot/SmokeTests/UnitTests/Interfaces.cs +++ b/src/tests/nativeaot/SmokeTests/UnitTests/Interfaces.cs @@ -689,6 +689,8 @@ class C3 : IInterface, IInterface static IInterface s_c3a = new C3(); static IInterface s_c3b = new C3(); + // Works around https://github.com/dotnet/runtime/issues/94399 + [MethodImpl(MethodImplOptions.NoOptimization)] public static void Run() { if (s_c1.Method(null) != "Method(object)")