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
18 changes: 18 additions & 0 deletions docs/design/datacontracts/ReJIT.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ bool IsEnabled();

RejitState GetRejitState(ILCodeVersionHandle codeVersionHandle);

bool IsDeoptimized(ILCodeVersionHandle codeVersionHandle);

TargetNUInt GetRejitId(ILCodeVersionHandle codeVersionHandle);

IEnumerable<TargetNUInt> GetRejitIds(TargetPointer methodDesc)
Expand All @@ -33,6 +35,7 @@ Data descriptors used:
| ProfControlBlock | NotificationProfilerCount | number of notification-only profilers currently attached |
| ILCodeVersionNode | VersionId | `ILCodeVersion` ReJIT ID
| ILCodeVersionNode | RejitState | a `RejitFlags` value |
| ILCodeVersionNode | Deoptimized | whether this IL code version has been deoptimized |

Global variables used:
| Global Name | Type | Purpose |
Expand Down Expand Up @@ -93,6 +96,21 @@ RejitState GetRejitState(ILCodeVersionHandle codeVersion)
}
}

bool IsDeoptimized(ILCodeVersionHandle codeVersion)
{
// ILCodeVersion::IsDeoptimized
if (codeVersion is not explicit)
{
return false;
}
else
{
// ILCodeVersionNode::IsDeoptimized
ILCodeVersionNode codeVersionNode = AsNode(codeVersion);
return codeVersionNode.Deoptimized;
}
}

TargetNUInt GetRejitId(ILCodeVersionHandle codeVersion)
{
// ILCodeVersion::GetVersionId
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/codeversion.h
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ struct cdac_data<ILCodeVersionNode>
static constexpr size_t Next = offsetof(ILCodeVersionNode, m_pNextILVersionNode);
static constexpr size_t RejitState = offsetof(ILCodeVersionNode, m_rejitState);
static constexpr size_t ILAddress = offsetof(ILCodeVersionNode, m_pIL);
static constexpr size_t Deoptimized = offsetof(ILCodeVersionNode, m_deoptimized);
};

class ILCodeVersionCollection
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/datadescriptor/datadescriptor.inc
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,7 @@ CDAC_TYPE_FIELD(ILCodeVersionNode, T_NUINT, VersionId, cdac_data<ILCodeVersionNo
CDAC_TYPE_FIELD(ILCodeVersionNode, T_POINTER, Next, cdac_data<ILCodeVersionNode>::Next)
CDAC_TYPE_FIELD(ILCodeVersionNode, T_UINT32, RejitState, cdac_data<ILCodeVersionNode>::RejitState)
CDAC_TYPE_FIELD(ILCodeVersionNode, T_POINTER, ILAddress, cdac_data<ILCodeVersionNode>::ILAddress)
CDAC_TYPE_FIELD(ILCodeVersionNode, T_UINT32, Deoptimized, cdac_data<ILCodeVersionNode>::Deoptimized)
CDAC_TYPE_END(ILCodeVersionNode)
#endif // FEATURE_CODE_VERSIONING

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ public interface IReJIT : IContract

RejitState GetRejitState(ILCodeVersionHandle codeVersionHandle) => throw new NotImplementedException();

bool IsDeoptimized(ILCodeVersionHandle codeVersionHandle) => throw new NotImplementedException();

TargetNUInt GetRejitId(ILCodeVersionHandle codeVersionHandle) => throw new NotImplementedException();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ RejitState IReJIT.GetRejitState(ILCodeVersionHandle ilCodeVersionHandle)
};
}

bool IReJIT.IsDeoptimized(ILCodeVersionHandle ilCodeVersionHandle)
{
if (!ilCodeVersionHandle.IsExplicit)
{
return false;
}
ILCodeVersionNode ilCodeVersionNode = AsNode(ilCodeVersionHandle);
return ilCodeVersionNode.Deoptimized != 0;
}

TargetNUInt IReJIT.GetRejitId(ILCodeVersionHandle ilCodeVersionHandle)
{
if (ilCodeVersionHandle.ILCodeVersionNode == TargetPointer.Null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ internal sealed partial class ILCodeVersionNode : IData<ILCodeVersionNode>
[Field] public TargetPointer Next { get; }
[Field] public uint RejitState { get; }
[Field] public TargetPointer ILAddress { get; }
[Field] public uint Deoptimized { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -2975,7 +2975,56 @@ public int GetPEFileMDInternalRW(ulong vmPEAssembly, ulong* pAddrMDInternalRW)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetPEFileMDInternalRW(vmPEAssembly, pAddrMDInternalRW) : HResults.E_NOTIMPL;

public int AreOptimizationsDisabled(ulong vmModule, uint methodTk, Interop.BOOL* pOptimizationsDisabled)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.AreOptimizationsDisabled(vmModule, methodTk, pOptimizationsDisabled) : HResults.E_NOTIMPL;
{
int hr = HResults.S_OK;
try
{
if (vmModule == 0)
throw new ArgumentException("Module pointer cannot be null.", nameof(vmModule));

if (pOptimizationsDisabled is null)
throw new ArgumentException("Output pointer cannot be null.", nameof(pOptimizationsDisabled));

Comment thread
barosiak marked this conversation as resolved.
if ((EcmaMetadataUtils.TokenType)(methodTk & EcmaMetadataUtils.TokenTypeMask) != EcmaMetadataUtils.TokenType.mdtMethodDef)
throw new ArgumentException("methodTk must be a MethodDef token.", nameof(methodTk));

*pOptimizationsDisabled = Interop.BOOL.FALSE;
if (_target.Contracts.TryGetContract<IReJIT>(out IReJIT rejit))
{
ILoader loader = _target.Contracts.Loader;
Contracts.ModuleHandle module = loader.GetModuleHandleFromModulePtr(new TargetPointer(vmModule));
ModuleLookupTables lookupTables = loader.GetLookupTables(module);
TargetPointer methodDesc = loader.GetModuleLookupMapElement(lookupTables.MethodDefToDesc, methodTk, out _);

if (methodDesc != TargetPointer.Null)
{
ICodeVersions codeVersions = _target.Contracts.CodeVersions;
ILCodeVersionHandle ilCodeVersion = codeVersions.GetActiveILCodeVersion(methodDesc);
if (rejit.IsDeoptimized(ilCodeVersion))
{
*pOptimizationsDisabled = Interop.BOOL.TRUE;
}
}
}
}
catch (System.Exception ex)
{
hr = ex.HResult;
}

#if DEBUG
if (_legacy is not null)
{
Interop.BOOL localPOptimizationsDisabled;
int hrLocal = _legacy.AreOptimizationsDisabled(vmModule, methodTk, &localPOptimizationsDisabled);
Debug.ValidateHResult(hr, hrLocal);
if (hr == HResults.S_OK)
Debug.Assert(*pOptimizationsDisabled == localPOptimizationsDisabled);
}
#endif

return hr;
}

public int GetDefinesBitField(uint* pDefines)
{
Expand Down
99 changes: 99 additions & 0 deletions src/native/managed/cdac/tests/DacDbiImplTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -635,4 +635,103 @@ private static (DacDbiImpl DacDbi, Target Target) CreateCheckContextDacDbi(MockT
}

private delegate void GetStackLimitDataCallback(TargetPointer threadPointer, out TargetPointer stackBase, out TargetPointer stackLimit, out TargetPointer frameAddress);

private const uint MdtMethodDef = 0x06000000;

private static DacDbiImpl CreateDacDbiWithMockContracts(
MockTarget.Architecture arch,
Mock<ILoader> mockLoader,
Mock<ICodeVersions> mockCodeVersions,
Mock<IReJIT> mockReJIT)
{
var target = new TestPlaceholderTarget.Builder(arch)
.UseReader((_, _) => -1)
.AddMockContract(mockLoader)
.AddMockContract(mockCodeVersions)
.AddMockContract(mockReJIT)
.Build();
return new DacDbiImpl(target, legacyObj: null);
}

private static Mock<ILoader> SetupMockLoader(TargetPointer modulePtr, uint methodTk, TargetPointer methodDesc)
{
var mockLoader = new Mock<ILoader>();
var moduleHandle = new Contracts.ModuleHandle(modulePtr);
var lookupTables = new ModuleLookupTables { MethodDefToDesc = new TargetPointer(0x4000) };
mockLoader.Setup(l => l.GetModuleHandleFromModulePtr(modulePtr)).Returns(moduleHandle);
mockLoader.Setup(l => l.GetLookupTables(moduleHandle)).Returns(lookupTables);
mockLoader.Setup(l => l.GetModuleLookupMapElement(lookupTables.MethodDefToDesc, methodTk, out It.Ref<TargetNUInt>.IsAny))
.Returns(methodDesc);
return mockLoader;
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void AreOptimizationsDisabled_NullOutput_ReturnsError(MockTarget.Architecture arch)
{
var dacDbi = CreateDacDbiWithMockContracts(
arch, new Mock<ILoader>(), new Mock<ICodeVersions>(), new Mock<IReJIT>());
int hr = dacDbi.AreOptimizationsDisabled(0x1000, MdtMethodDef | 1, null);
Assert.NotEqual(System.HResults.S_OK, hr);
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void AreOptimizationsDisabled_InvalidToken_ReturnsError(MockTarget.Architecture arch)
{
var dacDbi = CreateDacDbiWithMockContracts(
arch, new Mock<ILoader>(), new Mock<ICodeVersions>(), new Mock<IReJIT>());
Interop.BOOL result;
int hr = dacDbi.AreOptimizationsDisabled(0x1000, 0x01000001, &result);
Assert.NotEqual(System.HResults.S_OK, hr);
}

public static IEnumerable<object[]> ArchWithDeoptimized()
{
foreach (object[] stdArch in new MockTarget.StdArch())
{
yield return [stdArch[0], true];
yield return [stdArch[0], false];
}
}

[Theory]
[MemberData(nameof(ArchWithDeoptimized))]
public void AreOptimizationsDisabled_WithMethodDesc(MockTarget.Architecture arch, bool deoptimized)
{
TargetPointer modulePtr = new(0x1000);
uint methodTk = MdtMethodDef | 1;
TargetPointer methodDesc = new(0x2000);
var ilCodeVersion = ILCodeVersionHandle.CreateExplicit(new TargetPointer(0x3000));

Mock<ILoader> mockLoader = SetupMockLoader(modulePtr, methodTk, methodDesc);

var mockCodeVersions = new Mock<ICodeVersions>();
mockCodeVersions.Setup(cv => cv.GetActiveILCodeVersion(methodDesc)).Returns(ilCodeVersion);

var mockReJIT = new Mock<IReJIT>();
mockReJIT.Setup(r => r.IsDeoptimized(ilCodeVersion)).Returns(deoptimized);

var dacDbi = CreateDacDbiWithMockContracts(arch, mockLoader, mockCodeVersions, mockReJIT);
Interop.BOOL result;
int hr = dacDbi.AreOptimizationsDisabled(modulePtr.Value, methodTk, &result);
Assert.Equal(System.HResults.S_OK, hr);
Assert.Equal(deoptimized ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, result);
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void AreOptimizationsDisabled_NullMethodDesc_ReturnsFalse(MockTarget.Architecture arch)
{
TargetPointer modulePtr = new(0x1000);
uint methodTk = MdtMethodDef | 1;

Mock<ILoader> mockLoader = SetupMockLoader(modulePtr, methodTk, TargetPointer.Null);

var dacDbi = CreateDacDbiWithMockContracts(arch, mockLoader, new Mock<ICodeVersions>(), new Mock<IReJIT>());
Interop.BOOL result;
int hr = dacDbi.AreOptimizationsDisabled(modulePtr.Value, methodTk, &result);
Assert.Equal(System.HResults.S_OK, hr);
Assert.Equal(Interop.BOOL.FALSE, result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,15 @@ internal sealed class MockILCodeVersionNode : TypedView
private const string NextFieldName = "Next";
private const string RejitStateFieldName = "RejitState";
private const string ILAddressFieldName = "ILAddress";
private const string DeoptimizedFieldName = "Deoptimized";

public static Layout<MockILCodeVersionNode> CreateLayout(MockTarget.Architecture architecture)
=> new SequentialLayoutBuilder("ILCodeVersionNode", architecture)
.AddNUIntField(VersionIdFieldName)
.AddPointerField(NextFieldName)
.AddUInt32Field(RejitStateFieldName)
.AddPointerField(ILAddressFieldName)
.AddUInt32Field(DeoptimizedFieldName)
.Build<MockILCodeVersionNode>();

public ulong VersionId
Expand All @@ -177,6 +179,12 @@ public uint RejitState
get => ReadUInt32Field(RejitStateFieldName);
set => WriteUInt32Field(RejitStateFieldName, value);
}

public uint Deoptimized
{
get => ReadUInt32Field(DeoptimizedFieldName);
set => WriteUInt32Field(DeoptimizedFieldName, value);
}
}

internal sealed class MockGCCoverageInfo : TypedView
Expand Down Expand Up @@ -305,12 +313,13 @@ public MockILCodeVersioningState AddILCodeVersioningState()
=> ILCodeVersioningStateLayout.Create(
_codeVersionsAllocator.Allocate((ulong)ILCodeVersioningStateLayout.Size, "ILCodeVersioningState"));

public MockILCodeVersionNode AddILCodeVersionNode(ulong versionId, uint rejitFlags)
public MockILCodeVersionNode AddILCodeVersionNode(ulong versionId, uint rejitFlags, bool deoptimized = false)
{
MockILCodeVersionNode node = ILCodeVersionNodeLayout.Create(
_codeVersionsAllocator.Allocate((ulong)ILCodeVersionNodeLayout.Size, "ILCodeVersionNode"));
node.VersionId = versionId;
node.RejitState = rejitFlags;
node.Deoptimized = deoptimized ? 1u : 0u;
node.Next = 0;

return node;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ public MockReJITBuilder(MockMemorySpace.Builder builder, (ulong Start, ulong End
internal Layout<MockILCodeVersionNode> ILCodeVersionNodeLayout => _codeVersions.ILCodeVersionNodeLayout;
internal Layout<MockGCCoverageInfo> GCCoverageInfoLayout => _codeVersions.GCCoverageInfoLayout;

public MockILCodeVersionNode AddExplicitILCodeVersionNode(ulong rejitId, RejitFlags rejitFlags)
=> _codeVersions.AddILCodeVersionNode(rejitId, (uint)rejitFlags);
public MockILCodeVersionNode AddExplicitILCodeVersionNode(ulong rejitId, RejitFlags rejitFlags, bool deoptimized = false)
=> _codeVersions.AddILCodeVersionNode(rejitId, (uint)rejitFlags, deoptimized);

private ulong AddProfControlBlock(bool rejitOnAttachEnabled)
{
Expand Down
33 changes: 33 additions & 0 deletions src/native/managed/cdac/tests/ReJITTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,37 @@ public void GetRejitIds_SyntheticAndExplicit_Success(MockTarget.Architecture arc

Assert.Equal(expectedRejitIds, rejitIds);
}

[Theory]
[ClassData(typeof(MockTarget.StdArch))]
public void IsDeoptimized_Synthetic(MockTarget.Architecture arch)
{
ReJITContractContext context = CreateReJITContract(arch, _ => { });
ILCodeVersionHandle synthetic = ILCodeVersionHandle.CreateSynthetic(new TargetPointer(0x100), 100);
Assert.False(context.ReJIT.IsDeoptimized(synthetic));
}

public static IEnumerable<object[]> ArchWithDeoptimized()
{
foreach (object[] stdArch in new MockTarget.StdArch())
{
yield return [stdArch[0], true];
yield return [stdArch[0], false];
}
}

[Theory]
[MemberData(nameof(ArchWithDeoptimized))]
public void IsDeoptimized_Explicit(MockTarget.Architecture arch, bool deoptimized)
{
ILCodeVersionHandle explicitHandle = ILCodeVersionHandle.Invalid;
ReJITContractContext context = CreateReJITContract(
arch,
rejitBuilder =>
{
var node = rejitBuilder.AddExplicitILCodeVersionNode(1, MockReJITBuilder.RejitFlags.kStateActive, deoptimized: deoptimized);
explicitHandle = ILCodeVersionHandle.CreateExplicit(node.Address);
});
Assert.Equal(deoptimized, context.ReJIT.IsDeoptimized(explicitHandle));
}
}
Loading