Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ public void Run(R2RTestCase testCase)
// Step 2: Run each crossgen2 compilation and validate
var driver = new R2RDriver(_output);
List<string> refPaths = BuildReferencePaths();
refPaths.AddRange(assemblyPaths.Values);

foreach(var compilation in testCase.Compilations)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ public ModuleToken GetModuleTokenForType(TypeDesc type, bool allowDynamicallyCre
ModuleToken token;
if (type is EcmaType ecmaType)
{
if (_compilationModuleGroup.VersionsWithType(ecmaType))
// The global module type always has TypeDef token 0x02000001 (ECMA-335 II.22.37),
// so its token remains valid even outside the version bubble.
if (_compilationModuleGroup.VersionsWithType(ecmaType) || ecmaType.IsModuleType)
{
return new ModuleToken(ecmaType.Module, (mdToken)MetadataTokens.GetToken(ecmaType.Handle));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,7 @@ public ObjectNode.ObjectData ToObjectData()
return _builder.ToObjectData();
}

public SignatureContext EmitFixup(NodeFactory factory, ReadyToRunFixupKind fixupKind, IEcmaModule targetModule, SignatureContext outerContext)
public SignatureContext EmitFixup(NodeFactory factory, ReadyToRunFixupKind fixupKind, IEcmaModule targetModule, SignatureContext outerContext, bool hasStableToken = false)
{
if (targetModule == outerContext.LocalContext)
{
Expand All @@ -611,7 +611,7 @@ public SignatureContext EmitFixup(NodeFactory factory, ReadyToRunFixupKind fixup
else
{
EmitByte((byte)(fixupKind | ReadyToRunFixupKind.ModuleOverride));
if (!(targetModule is Internal.TypeSystem.Ecma.MutableModule) && !factory.CompilationModuleGroup.VersionsWithModule((ModuleDesc)targetModule))
if (!hasStableToken && !(targetModule is Internal.TypeSystem.Ecma.MutableModule) && !factory.CompilationModuleGroup.VersionsWithModule((ModuleDesc)targetModule))
{
throw new InternalCompilerErrorException("Attempt to use token from a module not within the version bubble");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
}

IEcmaModule targetModule = factory.SignatureContext.GetTargetModule(_typeDesc);
SignatureContext innerContext = dataBuilder.EmitFixup(factory, fixupKind, targetModule, factory.SignatureContext);
// The global module type has a fixed token even outside the version bubble.
bool hasStableToken = fixupKind == ReadyToRunFixupKind.TypeHandle && _typeDesc is EcmaType { IsModuleType: true };
SignatureContext innerContext = dataBuilder.EmitFixup(factory, fixupKind, targetModule, factory.SignatureContext, hasStableToken);
if ((fixupKind == ReadyToRunFixupKind.Check_TypeLayout) ||
(fixupKind == ReadyToRunFixupKind.Verify_TypeLayout))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3761,6 +3761,20 @@ private void reportInliningDecision(CORINFO_METHOD_STRUCT_* inlinerHnd, CORINFO_
}
_precodeFixups = previouslyStashedFixups;

// Static methods, constructors, and instance methods on default-initialized value types
// can be the first use of a module.
// Preserve activation if the module has, or outside the version bubble can gain, an initializer.
EcmaModule inlineeModule = (inlinee.OwningType as MetadataType)?.Module as EcmaModule;
if ((inlinee.Signature.IsStatic || inlinee.IsConstructor || inlinee.OwningType.IsValueType) &&
inlineeModule is not null &&
inlineeModule != _compilation.TypeSystemContext.SystemModule &&
inlineeModule != (MethodBeingCompiled.OwningType as MetadataType)?.Module &&
(!_compilation.CompilationModuleGroup.VersionsWithModule(inlineeModule) ||
inlineeModule.GetGlobalModuleType().HasStaticConstructor))
{
classMustBeLoadedBeforeCodeIsRun(inlineeModule.GetGlobalModuleType());
}

// If during inlining we found new inlinees, then if the inline was successful, add them to the set of fixups
// for the entire method.
HashSet<MethodDesc> previouslyStashedInlinees = _stashedInlinedMethods.Pop();
Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/vm/jitinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14664,6 +14664,11 @@ BOOL LoadDynamicInfoEntry(Module *currentModule,
// We can take address of RVA field only since ngened code is domain neutral
_ASSERTE(pField->IsRVA());

if (g_fEEStarted)
{
pField->GetModule()->EnsureActive();
}

result = (size_t)pField->GetStaticAddressHandle(NULL);
}
break;
Expand Down
11 changes: 11 additions & 0 deletions src/tests/readytorun/ModuleInitializer/FieldLibrary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace ModuleInitializerTest;

public sealed class FieldHolder
{
private static bool s_isSet;

public static bool IsSet => s_isSet;
}
8 changes: 8 additions & 0 deletions src/tests/readytorun/ModuleInitializer/FieldLibrary.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<Compile Include="FieldLibrary.cs" />
</ItemGroup>
</Project>
24 changes: 24 additions & 0 deletions src/tests/readytorun/ModuleInitializer/InitializerLibrary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.CompilerServices;

namespace ModuleInitializerTest;

public static class InitializerLibrary
{
[ModuleInitializer]
public static void Initialize()
{
GetIsSet(null) = true;
}

[UnsafeAccessor(UnsafeAccessorKind.StaticField, Name = "s_isSet")]
private static extern ref bool GetIsSet(FieldHolder holder);

// Inlining this empty call must not remove the module's initialization dependency.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Touch()
{
}
}
11 changes: 11 additions & 0 deletions src/tests/readytorun/ModuleInitializer/InitializerLibrary.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<Compile Include="InitializerLibrary.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="FieldLibrary.csproj" />
</ItemGroup>
</Project>
22 changes: 22 additions & 0 deletions src/tests/readytorun/ModuleInitializer/RvaFieldLibrary.il
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

.assembly extern mscorlib {}
.assembly RvaFieldLibrary {}
.module RvaFieldLibrary.dll

.method private specialname rtspecialname static void .cctor() cil managed
{
.maxstack 2
ldstr "RvaFieldLibrary.Initialized"
ldc.i4.1
call void [mscorlib]System.AppContext::SetSwitch(string, bool)
ret
}

.class public auto ansi beforefieldinit RvaFieldHolder extends [mscorlib]System.Object
{
.field public static int32 Value at ValueData
}

.data ValueData = int32(42)
8 changes: 8 additions & 0 deletions src/tests/readytorun/ModuleInitializer/RvaFieldLibrary.ilproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk.IL">
<PropertyGroup>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<Compile Include="RvaFieldLibrary.il" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.CompilerServices;
using Xunit;

namespace ModuleInitializerTest;

public static class UnsafeAccessorModuleInitializer
{
[Fact]
public static void TestEntryPoint()
{
InitializerLibrary.Touch();
Assert.True(FieldHolder.IsSet, "The module initializer did not update the private static field.");
}

[Fact]
[MethodImpl(MethodImplOptions.NoInlining)]
public static void RvaFieldAccess()
{
Assert.Equal(42, RvaFieldHolder.Value);
Assert.True(AppContext.TryGetSwitch("RvaFieldLibrary.Initialized", out bool initialized) && initialized,
"Accessing the RVA field did not run its module initializer.");
}

[Fact]
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ValueTypeInstanceMethodAccess()
{
Assert.Equal(42, default(ValueTypeLibrary).GetValue());
Assert.True(AppContext.TryGetSwitch("ValueTypeLibrary.Initialized", out bool initialized) && initialized,
"Calling the value-type instance method did not run its module initializer.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Preserve separate modules and run the test through composite ReadyToRun. -->
<RequiresProcessIsolation>true</RequiresProcessIsolation>
<AlwaysUseCrossGen2>true</AlwaysUseCrossGen2>
<CLRTestPriority>1</CLRTestPriority>
</PropertyGroup>
<ItemGroup>
<Compile Include="UnsafeAccessorModuleInitializer.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="FieldLibrary.csproj" />
<ProjectReference Include="InitializerLibrary.csproj" />
<ProjectReference Include="RvaFieldLibrary.ilproj" />
<ProjectReference Include="ValueTypeLibrary.csproj" />
</ItemGroup>
</Project>
19 changes: 19 additions & 0 deletions src/tests/readytorun/ModuleInitializer/ValueTypeLibrary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.CompilerServices;

namespace ModuleInitializerTest;

public struct ValueTypeLibrary
{
[ModuleInitializer]
public static void Initialize()
{
AppContext.SetSwitch("ValueTypeLibrary.Initialized", true);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetValue() => 42;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<Compile Include="ValueTypeLibrary.cs" />
</ItemGroup>
</Project>
Loading