Skip to content
Open
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
6 changes: 6 additions & 0 deletions src/coreclr/inc/crosscomp.h
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,9 @@ typedef struct _T_DISPATCHER_CONTEXT {
#define ARM64_MAX_BREAKPOINTS 8
#define ARM64_MAX_WATCHPOINTS 2

#ifndef CONTEXT_UNWOUND_TO_CALL
Comment thread
AaronRobinsonMSFT marked this conversation as resolved.
#define CONTEXT_UNWOUND_TO_CALL 0x20000000
#endif

typedef union _NEON128 {
struct {
Expand Down Expand Up @@ -413,7 +415,9 @@ enum
#define LOONGARCH64_MAX_BREAKPOINTS 8
#define LOONGARCH64_MAX_WATCHPOINTS 2

#ifndef CONTEXT_UNWOUND_TO_CALL
#define CONTEXT_UNWOUND_TO_CALL 0x20000000
#endif

typedef struct DECLSPEC_ALIGN(16) _T_CONTEXT {

Expand Down Expand Up @@ -546,7 +550,9 @@ typedef struct _T_KNONVOLATILE_CONTEXT_POINTERS {
#define RISCV64_MAX_BREAKPOINTS 8
#define RISCV64_MAX_WATCHPOINTS 2

#ifndef CONTEXT_UNWOUND_TO_CALL
#define CONTEXT_UNWOUND_TO_CALL 0x20000000
#endif

typedef struct DECLSPEC_ALIGN(16) _T_CONTEXT {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ public void InitManifestMutableModule(MutableModule mutableModule)

public ModuleToken GetModuleTokenForType(EcmaType type, bool allowDynamicallyCreatedReference, bool throwIfNotFound = true)
{
if (_compilationModuleGroup.VersionsWithType(type))
// 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(type) || type.IsModuleType)
{
return new ModuleToken(type.EcmaModule, (mdToken)MetadataTokens.GetToken(type.Handle));
}
Expand Down Expand Up @@ -438,4 +440,3 @@ public DummyTypeInfo GetTypeFromSpecification(MetadataReader reader, ModuleToken
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,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 @@ -602,7 +602,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 @@ -45,7 +45,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);
dataBuilder.EmitTypeSignature(_typeDesc, innerContext);

if ((fixupKind == ReadyToRunFixupKind.Check_TypeLayout) ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3182,6 +3182,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 @@ -13848,6 +13848,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