Skip to content
Draft
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
1 change: 1 addition & 0 deletions src/coreclr/vm/class.h
Original file line number Diff line number Diff line change
Expand Up @@ -1784,6 +1784,7 @@ template<> struct cdac_data<EEClass>
static constexpr size_t NumStaticFields = offsetof(EEClass, m_NumStaticFields);
static constexpr size_t NumThreadStaticFields = offsetof(EEClass, m_NumThreadStaticFields);
static constexpr size_t NumNonVirtualSlots = offsetof(EEClass, m_NumNonVirtualSlots);
static constexpr size_t BaseSizePadding = offsetof(EEClass, m_cbBaseSizePadding);
};

// --------------------------------------------------------------------------------------------
Expand Down
25 changes: 25 additions & 0 deletions src/coreclr/vm/datadescriptor/datadescriptor.inc
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,7 @@ CDAC_TYPE_FIELD(EEClass, T_UINT16, NumInstanceFields, cdac_data<EEClass>::NumIns
CDAC_TYPE_FIELD(EEClass, T_UINT16, NumStaticFields, cdac_data<EEClass>::NumStaticFields)
CDAC_TYPE_FIELD(EEClass, T_UINT16, NumThreadStaticFields, cdac_data<EEClass>::NumThreadStaticFields)
CDAC_TYPE_FIELD(EEClass, T_UINT16, NumNonVirtualSlots, cdac_data<EEClass>::NumNonVirtualSlots)
CDAC_TYPE_FIELD(EEClass, T_UINT8, BaseSizePadding, cdac_data<EEClass>::BaseSizePadding)
CDAC_TYPE_END(EEClass)

CDAC_TYPE_BEGIN(GenericsDictInfo)
Expand Down Expand Up @@ -1047,14 +1048,28 @@ CDAC_TYPE_FIELD(TransitionBlock, T_UINT32, OffsetOfArgs, sizeof(TransitionBlock)
// Offset to argument registers and first GCRefMap slot (platform-specific)
#if (defined(TARGET_AMD64) && !defined(UNIX_AMD64_ABI)) || defined(TARGET_WASM)
CDAC_TYPE_FIELD(TransitionBlock, T_UINT32, ArgumentRegisters, sizeof(TransitionBlock))
CDAC_TYPE_FIELD(TransitionBlock, T_UINT32, ArgumentRegistersOffset, sizeof(TransitionBlock))
CDAC_TYPE_FIELD(TransitionBlock, T_UINT32, FirstGCRefMapSlot, sizeof(TransitionBlock))
#elif defined(TARGET_ARM64)
CDAC_TYPE_FIELD(TransitionBlock, T_UINT32, ArgumentRegisters, offsetof(TransitionBlock, m_argumentRegisters))
CDAC_TYPE_FIELD(TransitionBlock, T_UINT32, ArgumentRegistersOffset, offsetof(TransitionBlock, m_argumentRegisters))
CDAC_TYPE_FIELD(TransitionBlock, T_UINT32, FirstGCRefMapSlot, offsetof(TransitionBlock, m_x8RetBuffReg))
#else
CDAC_TYPE_FIELD(TransitionBlock, T_UINT32, ArgumentRegisters, offsetof(TransitionBlock, m_argumentRegisters))
CDAC_TYPE_FIELD(TransitionBlock, T_UINT32, ArgumentRegistersOffset, offsetof(TransitionBlock, m_argumentRegisters))
CDAC_TYPE_FIELD(TransitionBlock, T_UINT32, FirstGCRefMapSlot, offsetof(TransitionBlock, m_argumentRegisters))
#endif

// Negative offset to where float argument registers are saved (relative to TransitionBlock pointer).
// This is -sizeof(FloatArgumentRegisters) (-padding) on platforms that have them, 0 otherwise.
#ifdef CALLDESCR_FPARGREGS
#ifdef TARGET_ARM
// ARM has 8-byte alignment padding after FloatArgumentRegisters
CDAC_TYPE_FIELD(TransitionBlock, T_INT32, OffsetOfFloatArgumentRegisters, -(int)(sizeof(FloatArgumentRegisters) + TARGET_POINTER_SIZE))
#else
CDAC_TYPE_FIELD(TransitionBlock, T_INT32, OffsetOfFloatArgumentRegisters, -(int)sizeof(FloatArgumentRegisters))
#endif
#endif // CALLDESCR_FPARGREGS
CDAC_TYPE_END(TransitionBlock)

#ifdef DEBUGGING_SUPPORTED
Expand Down Expand Up @@ -1505,6 +1520,14 @@ CDAC_GLOBAL(FeatureWebcil, T_UINT8, 1)
#else
CDAC_GLOBAL(FeatureWebcil, T_UINT8, 0)
#endif
#ifdef FEATURE_HFA
CDAC_GLOBAL(FeatureHFA, T_UINT8, 1)
#else
CDAC_GLOBAL(FeatureHFA, T_UINT8, 0)
#endif
#ifdef ARM_SOFTFP
CDAC_GLOBAL(FeatureArmSoftFP, T_UINT8, 1)
#endif
// See Object::GetGCSafeMethodTable
#ifdef TARGET_64BIT
CDAC_GLOBAL(ObjectToMethodTableUnmask, T_UINT8, 1 | 1 << 1 | 1 << 2)
Expand Down Expand Up @@ -1543,6 +1566,7 @@ CDAC_GLOBAL_POINTER(FreeObjectMethodTable, &::g_pFreeObjectMethodTable)
CDAC_GLOBAL_POINTER(ObjectMethodTable, &::g_pObjectClass)
CDAC_GLOBAL_POINTER(ObjectArrayMethodTable, &::g_pPredefinedArrayTypes[ELEMENT_TYPE_OBJECT])
CDAC_GLOBAL_POINTER(StringMethodTable, &::g_pStringClass)
CDAC_GLOBAL_POINTER(TypedReferenceMethodTable, &::g_TypedReferenceMT)
CDAC_GLOBAL_POINTER(SyncTableEntries, &::g_pSyncTable)
CDAC_GLOBAL_POINTER(MiniMetaDataBuffAddress, &::g_MiniMetaDataBuffAddress)
CDAC_GLOBAL_POINTER(MiniMetaDataBuffMaxSize, &::g_MiniMetaDataBuffMaxSize)
Expand Down Expand Up @@ -1612,6 +1636,7 @@ CDAC_GLOBAL_CONTRACT(AuxiliarySymbols, c1)
#if FEATURE_COMINTEROP
CDAC_GLOBAL_CONTRACT(BuiltInCOM, c1)
#endif // FEATURE_COMINTEROP
CDAC_GLOBAL_CONTRACT(CallingConvention, c1)
CDAC_GLOBAL_CONTRACT(CodeVersions, c1)
CDAC_GLOBAL_CONTRACT(CodeNotifications, c1)
#ifdef FEATURE_COMWRAPPERS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ public abstract class ContractRegistry
/// Gets an instance of the Debugger contract for the target.
/// </summary>
public virtual IDebugger Debugger => GetContract<IDebugger>();
/// <summary>
/// Gets an instance of the CallingConvention contract for the target.
/// </summary>
public virtual ICallingConvention CallingConvention => GetContract<ICallingConvention>();

/// <summary>
/// Attempts to get an instance of the requested contract for the target.
Expand Down
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.Collections.Generic;

namespace Microsoft.Diagnostics.DataContractReader.Contracts;

public readonly record struct ArgSlot(
int Offset,
CorElementType ElementType);

public readonly record struct ArgLayout(
bool IsPassedByRef,
IReadOnlyList<ArgSlot> Slots,
TypeHandle? ValueTypeHandle = null);

public readonly record struct CallSiteLayout(
int? ThisOffset,
bool IsValueTypeThis,
int? AsyncContinuationOffset,
int? VarArgCookieOffset,
IReadOnlyList<ArgLayout> Arguments);

public interface ICallingConvention : IContract
{
static string IContract.Name { get; } = nameof(CallingConvention);
Comment on lines +22 to +27

CallSiteLayout ComputeCallSiteLayout(MethodDescHandle method)
=> throw new NotImplementedException();
}

public readonly struct CallingConvention : ICallingConvention
{
// Everything throws NotImplementedException
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ public interface IRuntimeTypeSystem : IContract
// The component size is only available for strings and arrays. It is the size of the element type of the array, or the size of an ECMA 335 character (2 bytes)
uint GetComponentSize(TypeHandle typeHandle) => throw new NotImplementedException();

// Mirrors native MethodTable::GetNumInstanceFieldBytes: BaseSize - EEClass.BaseSizePadding.
int GetNumInstanceFieldBytes(TypeHandle typeHandle) => throw new NotImplementedException();

// True if the MethodTable is the sentinel value associated with unallocated space in the managed heap
bool IsFreeObjectMethodTable(TypeHandle typeHandle) => throw new NotImplementedException();
// True if the MethodTable is the System.Object MethodTable (g_pObjectClass)
Expand All @@ -129,6 +132,14 @@ public interface IRuntimeTypeSystem : IContract
bool ContainsGCPointers(TypeHandle typeHandle) => throw new NotImplementedException();
// True if the type requires 8-byte alignment on platforms that don't 8-byte align by default (FEATURE_64BIT_ALIGNMENT)
bool RequiresAlign8(TypeHandle typeHandle) => throw new NotImplementedException();
// True if the type is a Homogeneous Floating-point Aggregate (HFA), i.e. a value type whose
// fields are all of the same floating-point or short-vector type. Only meaningful on platforms with HFA (ARM/ARM64);
// returns false on other architectures.
bool IsHFA(TypeHandle typeHandle) => throw new NotImplementedException();
// Returns the size in bytes of this type if it is a hardware vector type (System.Numerics.Vector`1,
// System.Runtime.Intrinsics.Vector64`1, or System.Runtime.Intrinsics.Vector128`1) with a primitive
// element type, or 0 otherwise. Used by HFA element-size resolution on platforms with HFA.
int GetVectorSize(TypeHandle typeHandle) => throw new NotImplementedException();
// True if the MethodTable represents a continuation type used by the async continuation feature
bool IsContinuation(TypeHandle typeHandle) => throw new NotImplementedException();
/// <summary>
Expand Down Expand Up @@ -174,6 +185,12 @@ public interface IRuntimeTypeSystem : IContract
CorElementType GetSignatureCorElementType(TypeHandle typeHandle) => throw new NotImplementedException();
bool IsValueType(TypeHandle typeHandle) => throw new NotImplementedException();

// True if the type is a "ByRefLike" / ref struct (e.g. Span<T>, ReadOnlySpan<T>,
// TypedReference). ByRefLike types can have managed references *and* byref-typed
// fields embedded at arbitrary offsets, and require special handling in the GC
// scanner — the standard GCDesc walk only covers ordinary managed references.
bool IsByRefLike(TypeHandle typeHandle) => throw new NotImplementedException();

// Internal element type of the type. Unlike GetSignatureCorElementType, this returns the underlying primitive
// type for enums (e.g. I4 for an enum with int underlying type) and for PrimitiveValueType categories.
// For arrays, reference types, and TypeDescs, behaves identically to GetSignatureCorElementType.
Expand Down Expand Up @@ -270,6 +287,10 @@ public interface IRuntimeTypeSystem : IContract
TargetPointer GetFieldDescByName(TypeHandle typeHandle, string fieldName) => throw new NotImplementedException();
TargetPointer GetFieldDescStaticAddress(TargetPointer fieldDescPointer, bool unboxValueTypes = true) => throw new NotImplementedException();
TargetPointer GetFieldDescThreadStaticAddress(TargetPointer fieldDescPointer, TargetPointer thread, bool unboxValueTypes = true) => throw new NotImplementedException();

IEnumerable<TargetPointer> EnumerateInstanceFieldDescs(TypeHandle typeHandle) => throw new NotImplementedException();

TypeHandle LookupApproxFieldTypeHandle(TargetPointer fieldDescPointer) => throw new NotImplementedException();
#endregion FieldDesc inspection APIs
#region Other APIs
void GetCoreLibFieldDescAndDef(string typeNamespace, string typeName, string fieldName, out TargetPointer fieldDescAddr, out FieldDefinition fieldDef) => throw new NotImplementedException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public static class Globals
public const string FeatureOnStackReplacement = nameof(FeatureOnStackReplacement);
public const string FeaturePortableEntrypoints = nameof(FeaturePortableEntrypoints);
public const string FeatureWebcil = nameof(FeatureWebcil);
public const string FeatureHFA = nameof(FeatureHFA);
public const string FeatureArmSoftFP = nameof(FeatureArmSoftFP);

public const string ObjectToMethodTableUnmask = nameof(ObjectToMethodTableUnmask);
public const string SOSBreakingChangeVersion = nameof(SOSBreakingChangeVersion);
Expand All @@ -37,6 +39,7 @@ public static class Globals
public const string ObjectMethodTable = nameof(ObjectMethodTable);
public const string ObjectArrayMethodTable = nameof(ObjectArrayMethodTable);
public const string StringMethodTable = nameof(StringMethodTable);
public const string TypedReferenceMethodTable = nameof(TypedReferenceMethodTable);

public const string MiniMetaDataBuffAddress = nameof(MiniMetaDataBuffAddress);
public const string MiniMetaDataBuffMaxSize = nameof(MiniMetaDataBuffMaxSize);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using Microsoft.Diagnostics.DataContractReader.RuntimeTypeSystemHelpers;

namespace Microsoft.Diagnostics.DataContractReader.Contracts.CallingConventionHelpers;

internal sealed class AMD64UnixArgIterator : ArgIteratorBase
{
private readonly Target _target;

public override int NumArgumentRegisters => 6;
public override int NumFloatArgumentRegisters => 8;
public override int FloatRegisterSize => 16;
public override int EnregisteredParamTypeMaxSize => 16;
public override int EnregisteredReturnTypeIntegerMaxSize => 16;
public override int StackSlotSize => 8;
public override bool IsRetBuffPassedAsFirstArg => true;

public AMD64UnixArgIterator(
TransitionBlockLayout layout,
ArgIteratorData argData,
bool hasParamType,
bool hasAsyncContinuation)
: base(layout, argData, hasParamType, hasAsyncContinuation)
{
_target = layout.Target;
}

public override bool IsArgPassedByRefBySize(int size) => false;

private static bool CanClassifyStruct(ArgTypeInfo typeInfo)
=> typeInfo.RuntimeTypeHandle.IsMethodTable();

private SystemVStructDescriptor ClassifyStruct(ArgTypeInfo typeInfo, int structSize)
=> SystemVStructClassifier.Classify(_target, typeInfo.RuntimeTypeHandle, structSize);

protected override bool ValueTypeReturnNeedsRetBuf(ArgTypeInfo thRetType)
{
int size = thRetType.Size;
if (size > EnregisteredReturnTypeIntegerMaxSize)
return true;
if (!CanClassifyStruct(thRetType))
return true;
SystemVStructDescriptor descriptor = ClassifyStruct(thRetType, size);
return !descriptor.PassedInRegisters;
}

public override IEnumerable<ArgLocDesc> EnumerateArgs()
{
int idxGenReg = ComputeInitialNumRegistersUsed();
int idxStack = 0;
int idxFPReg = 0;

for (int argNum = 0; argNum < NumFixedArgs; argNum++)
{
CorElementType argType = GetArgumentType(argNum, out ArgTypeInfo argTypeInfo);
int argSize = GetElemSize(argType, argTypeInfo, _layout.PointerSize);

IReadOnlyList<ArgLocation> locations;

if (argType != CorElementType.ValueType && argType != CorElementType.TypedByRef)
{
locations = [PlaceScalar(argType, argSize, ref idxGenReg, ref idxFPReg, ref idxStack)];
}
else
{
SystemVStructDescriptor descriptor = default;
if (argSize <= SystemVStructDescriptor.MaxStructBytesToPassInRegisters
&& CanClassifyStruct(argTypeInfo))
{
descriptor = ClassifyStruct(argTypeInfo, argSize);
}

if (descriptor.PassedInRegisters
&& TryClassifySysVLocations(descriptor, idxGenReg, idxFPReg, out List<ArgLocation> sysvLocations))
{
locations = sysvLocations;
foreach (ArgLocation l in sysvLocations)
{
if (l.Kind == ArgLocationKind.GpRegister) idxGenReg++;
else if (l.Kind == ArgLocationKind.FpRegister) idxFPReg++;
}
}
else
{
locations = [PlaceStructOnStackLocal(argSize, argType, ref idxStack)];
}
}

bool isByRef = argType == CorElementType.Byref;

yield return new ArgLocDesc
{
ArgType = argType,
ArgSize = argSize,
ArgTypeInfo = argTypeInfo,
IsByRef = isByRef,
Locations = locations,
};
}
}

private ArgLocation PlaceScalar(
CorElementType argType, int argSize,
ref int idxGenReg, ref int idxFPReg, ref int idxStack)
{
int cbArg = StackElemSize(argSize);

if (argType is CorElementType.R4 or CorElementType.R8)
{
if (idxFPReg < NumFloatArgumentRegisters)
{
var loc = new ArgLocation
{
Kind = ArgLocationKind.FpRegister,
TransitionBlockOffset = _layout.OffsetOfFloatArgumentRegisters + idxFPReg * FloatRegisterSize,
Size = FloatRegisterSize,
ElementType = argType,
};
idxFPReg++;
return loc;
}
}
else
{
int cGenRegs = cbArg / 8;
if (cGenRegs == 0) cGenRegs = 1;
if (idxGenReg + cGenRegs <= NumArgumentRegisters)
{
var loc = new ArgLocation
{
Kind = ArgLocationKind.GpRegister,
TransitionBlockOffset = _layout.ArgumentRegistersOffset + idxGenReg * _layout.PointerSize,
Size = cGenRegs * _layout.PointerSize,
ElementType = argType,
};
idxGenReg += cGenRegs;
return loc;
}
}

return PlaceOnStackLocal(cbArg, argType, ref idxStack);
}

private ArgLocation PlaceStructOnStackLocal(int argSize, CorElementType argType, ref int idxStack)
=> PlaceOnStackLocal(StackElemSize(argSize), argType, ref idxStack);

private ArgLocation PlaceOnStackLocal(int cbArg, CorElementType argType, ref int idxStack)
{
int stackOfs = _layout.OffsetOfArgs + idxStack * _layout.PointerSize;
int slots = cbArg / _layout.PointerSize;
if (slots == 0) slots = 1;
idxStack += slots;
return new ArgLocation
{
Kind = ArgLocationKind.Stack,
TransitionBlockOffset = stackOfs,
Size = cbArg,
ElementType = argType,
};
}

private bool TryClassifySysVLocations(
SystemVStructDescriptor descriptor,
int startGenReg, int startFPReg,
out List<ArgLocation> locations)
{
int neededGen = 0, neededFP = 0;
for (int i = 0; i < descriptor.EightByteCount; i++)
{
SystemVClassification c = descriptor.Classification(i);
if (c is SystemVClassification.Integer or SystemVClassification.IntegerReference or SystemVClassification.IntegerByRef)
neededGen++;
else if (c == SystemVClassification.SSE)
neededFP++;
else { locations = null!; return false; }
}

if (startGenReg + neededGen > NumArgumentRegisters || startFPReg + neededFP > NumFloatArgumentRegisters)
{ locations = null!; return false; }

locations = new List<ArgLocation>(descriptor.EightByteCount);
int genIdx = startGenReg, fpIdx = startFPReg;
for (int i = 0; i < descriptor.EightByteCount; i++)
{
SystemVClassification c = descriptor.Classification(i);
switch (c)
{
case SystemVClassification.Integer:
locations.Add(new ArgLocation { Kind = ArgLocationKind.GpRegister, TransitionBlockOffset = _layout.ArgumentRegistersOffset + genIdx * _layout.PointerSize, Size = _layout.PointerSize, ElementType = CorElementType.I8 });
genIdx++; break;
case SystemVClassification.IntegerReference:
locations.Add(new ArgLocation { Kind = ArgLocationKind.GpRegister, TransitionBlockOffset = _layout.ArgumentRegistersOffset + genIdx * _layout.PointerSize, Size = _layout.PointerSize, ElementType = CorElementType.Class });
genIdx++; break;
case SystemVClassification.IntegerByRef:
locations.Add(new ArgLocation { Kind = ArgLocationKind.GpRegister, TransitionBlockOffset = _layout.ArgumentRegistersOffset + genIdx * _layout.PointerSize, Size = _layout.PointerSize, ElementType = CorElementType.Byref });
genIdx++; break;
case SystemVClassification.SSE:
locations.Add(new ArgLocation { Kind = ArgLocationKind.FpRegister, TransitionBlockOffset = _layout.OffsetOfFloatArgumentRegisters + fpIdx * FloatRegisterSize, Size = FloatRegisterSize, ElementType = CorElementType.R8 });
fpIdx++; break;
}
}
return true;
}
}
Loading
Loading