Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
454596f
[RISC-V] Fix struct info value in crossgen2
clamp03 Mar 12, 2024
2d80de2
[RISC-V] Fix assertion in crossgen
clamp03 Mar 15, 2024
1462c93
Check size in GetRiscV64PassStructInRegisterFlags early, use named co…
tomeksowi Mar 20, 2024
7f1b761
Simplify managed branch of GetRiscV64PassStructInRegisterFlags
tomeksowi Mar 21, 2024
3117efe
Fix assert IsPrimitiveType for 2nd field
tomeksowi Mar 21, 2024
2a715bf
Handle empty structs
tomeksowi Mar 22, 2024
d009311
Apply FIELD_SIZE_IS8 flags only when there's at least one float
tomeksowi Mar 25, 2024
774f21a
Handle empty array struct elements
tomeksowi Mar 25, 2024
959f1f4
Enregister any field type <= 8 bytes, not just primitives; i.e. point…
tomeksowi Mar 25, 2024
5c53430
Simplify native layout branch of GetRiscV64PassStructInRegisterFlags
tomeksowi Mar 26, 2024
0ac27ce
Rewrite native branch to look at only at native layout info
tomeksowi Mar 26, 2024
98cd8c3
Calculate flags already in GetFlattenedFieldTypes to avoid returning …
tomeksowi Mar 27, 2024
b846eff
Ignore empty structs during field flattenting because RISC-V calling …
tomeksowi Mar 27, 2024
50515a4
Simplify crossgen2 GetRISCV64PassStructInRegisterFlags, make C++ and …
tomeksowi Mar 27, 2024
188e1f0
Remove early exit if nFields == 0 because it wasn't doing much, the l…
tomeksowi Mar 28, 2024
4507267
Return early from HasImpliedRepeatedFields. GetApproxFieldDescListRaw…
tomeksowi Apr 8, 2024
e6c5de6
Cleanup GetRiscV64PassStructInRegisterFlags call sites
tomeksowi Apr 9, 2024
ca936b4
Stackalloc field types to avoid GC allocations
tomeksowi Apr 18, 2024
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/tools/Common/JitInterface/CorInfoTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1235,6 +1235,7 @@ public struct SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR
// bit 5: `1` means the second field's size is 8.
//
// Note that bit 0 and 3 cannot both be set.
[Flags]
public enum StructFloatFieldInfoFlags
{
STRUCT_NO_FLOAT_FIELD = 0x0,
Expand Down
273 changes: 95 additions & 178 deletions src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs
Original file line number Diff line number Diff line change
@@ -1,210 +1,127 @@
// 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;
using System.Diagnostics;
using ILCompiler;
using Internal.TypeSystem;
using static Internal.JitInterface.StructFloatFieldInfoFlags;

namespace Internal.JitInterface
{

internal static class RISCV64PassStructInRegister
{
public static uint GetRISCV64PassStructInRegisterFlags(TypeDesc typeDesc)
private const int
ENREGISTERED_PARAMTYPE_MAXSIZE = 16,
TARGET_POINTER_SIZE = 8;

private static bool HandleInlineArray(int elementTypeIndex, int nElements, Span<StructFloatFieldInfoFlags> types, ref int typeIndex)
{
FieldDesc firstField = null;
uint floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
int numIntroducedFields = 0;
foreach (FieldDesc field in typeDesc.GetFields())
int nFlattenedFieldsPerElement = typeIndex - elementTypeIndex;
if (nFlattenedFieldsPerElement == 0)
return true;

Debug.Assert(nFlattenedFieldsPerElement == 1 || nFlattenedFieldsPerElement == 2);

if (nElements > 2)
return false;

if (nElements == 2)
{
if (!field.IsStatic)
{
firstField ??= field;
numIntroducedFields++;
}
if (typeIndex + nFlattenedFieldsPerElement > 2)
return false;

Debug.Assert(elementTypeIndex == 0);
Debug.Assert(typeIndex == 1);
types[typeIndex++] = types[elementTypeIndex]; // duplicate the array element type
}
return true;
}

if ((numIntroducedFields == 0) || (numIntroducedFields > 2) || (typeDesc.GetElementSize().AsInt > 16))
private static bool FlattenFieldTypes(TypeDesc td, Span<StructFloatFieldInfoFlags> types, ref int typeIndex)
{
IEnumerable<FieldDesc> fields = td.GetFields();
int nFields = 0;
int elementTypeIndex = typeIndex;
FieldDesc prevField = null;
foreach (FieldDesc field in fields)
{
return (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
}
if (field.IsStatic)
continue;
nFields++;

MetadataType mdType = typeDesc as MetadataType;
Debug.Assert(mdType != null);
if (prevField != null && prevField.Offset.AsInt + prevField.FieldType.GetElementSize().AsInt > field.Offset.AsInt)
return false; // overlapping fields

TypeDesc firstFieldElementType = firstField.FieldType;
int firstFieldSize = firstFieldElementType.GetElementSize().AsInt;
bool hasImpliedRepeatedFields = mdType.HasImpliedRepeatedFields();
prevField = field;

if (hasImpliedRepeatedFields)
{
numIntroducedFields = typeDesc.GetElementSize().AsInt / firstFieldSize;
if (numIntroducedFields > 2)
TypeFlags category = field.FieldType.Category;
if (category == TypeFlags.ValueType)
{
return (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
TypeDesc nested = field.FieldType;
if (!FlattenFieldTypes(nested, types, ref typeIndex))
return false;
}
}

int fieldIndex = 0;
foreach (FieldDesc field in typeDesc.GetFields())
{
if (fieldIndex > 1)
else if (field.FieldType.GetElementSize().AsInt <= TARGET_POINTER_SIZE)
{
return (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
if (typeIndex >= 2)
return false;

StructFloatFieldInfoFlags type =
(category is TypeFlags.Single or TypeFlags.Double ? STRUCT_FLOAT_FIELD_FIRST : (StructFloatFieldInfoFlags)0) |
(field.FieldType.GetElementSize().AsInt == TARGET_POINTER_SIZE ? STRUCT_FIRST_FIELD_SIZE_IS8 : (StructFloatFieldInfoFlags)0);
types[typeIndex++] = type;
}
else if (field.IsStatic)
else
{
continue;
return false;
}
}

Debug.Assert(fieldIndex < numIntroducedFields);
if ((td as MetadataType).HasImpliedRepeatedFields())
{
Debug.Assert(nFields == 1);
int nElements = td.GetElementSize().AsInt / prevField.FieldType.GetElementSize().AsInt;
if (!HandleInlineArray(elementTypeIndex, nElements, types, ref typeIndex))
return false;
}
return true;
}

switch (field.FieldType.Category)
{
case TypeFlags.Double:
{
if (numIntroducedFields == 1)
{
floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_ONE;
}
else if (fieldIndex == 0)
{
floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_DOUBLE;
}
else if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST) != 0)
{
floatFieldFlags ^= (uint)StructFloatFieldInfoFlags.STRUCT_MERGE_FIRST_SECOND_8;
}
else
{
floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_SECOND_FIELD_DOUBLE;
}

// Pass with two integer registers in `struct {int a, int b, float/double c}` cases
if (fieldIndex == 1 &&
(floatFieldFlags |
(uint)StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_SIZE_IS8 |
(uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND) ==
floatFieldFlags)
{
floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
}
}
break;

case TypeFlags.Single:
{
if (numIntroducedFields == 1)
{
floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_ONE;
}
else if (fieldIndex == 0)
{
floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST;
}
else if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST) != 0)
{
floatFieldFlags ^= (uint)StructFloatFieldInfoFlags.STRUCT_MERGE_FIRST_SECOND;
}
else
{
floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND;
}

// Pass with two integer registers in `struct {int a, int b, float/double c}` cases
if (fieldIndex == 1 &&
(floatFieldFlags |
(uint)StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_SIZE_IS8 |
(uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND) ==
floatFieldFlags)
{
floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
}
}
break;

case TypeFlags.ValueType:
//case TypeFlags.Class:
//case TypeFlags.Array:
//case TypeFlags.SzArray:
{
uint floatFieldFlags2 = GetRISCV64PassStructInRegisterFlags(field.FieldType);
if (numIntroducedFields == 1)
{
floatFieldFlags = floatFieldFlags2;
}
else if (field.FieldType.GetElementSize().AsInt > 8)
{
return (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
}
else if (fieldIndex == 0)
{
if ((floatFieldFlags2 & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_ONE) != 0)
{
floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST;
}
if (field.FieldType.GetElementSize().AsInt == 8)
{
floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_SIZE_IS8;
}
}
else
{
Debug.Assert(fieldIndex == 1);
if ((floatFieldFlags2 & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_ONE) != 0)
{
floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_MERGE_FIRST_SECOND;
}
if (field.FieldType.GetElementSize().AsInt == 8)
{
floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_SECOND_FIELD_SIZE_IS8;
}

floatFieldFlags2 = floatFieldFlags & ((uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST | (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND);
if (floatFieldFlags2 == 0)
{
floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
}
else if (floatFieldFlags2 == ((uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST | (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND))
{
floatFieldFlags ^= ((uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO | (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST | (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND);
}
}
}
break;

default:
{
if (field.FieldType.GetElementSize().AsInt == 8)
{
if (numIntroducedFields > 1)
{
if (fieldIndex == 0)
{
floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_SIZE_IS8;
}
else if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST) != 0)
{
floatFieldFlags |= (uint)StructFloatFieldInfoFlags.STRUCT_SECOND_FIELD_SIZE_IS8;
}
else
{
floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
}
}
}
else if (fieldIndex == 1)
{
floatFieldFlags = (floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST) > 0 ? floatFieldFlags : (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD;
}
break;
}
}
public static uint GetRISCV64PassStructInRegisterFlags(TypeDesc td)
{
if (td.GetElementSize().AsInt > ENREGISTERED_PARAMTYPE_MAXSIZE)
return (uint)STRUCT_NO_FLOAT_FIELD;

fieldIndex++;
}
Span<StructFloatFieldInfoFlags> types = stackalloc StructFloatFieldInfoFlags[] {
STRUCT_NO_FLOAT_FIELD, STRUCT_NO_FLOAT_FIELD
};
int nFields = 0;
if (!FlattenFieldTypes(td, types, ref nFields) || nFields == 0)
return (uint)STRUCT_NO_FLOAT_FIELD;

Debug.Assert(nFields == 1 || nFields == 2);

Debug.Assert((uint)(STRUCT_FLOAT_FIELD_SECOND | STRUCT_SECOND_FIELD_SIZE_IS8)
== (uint)(STRUCT_FLOAT_FIELD_FIRST | STRUCT_FIRST_FIELD_SIZE_IS8) << 1,
"SECOND flags need to be FIRST shifted by 1");
StructFloatFieldInfoFlags flags = types[0] | (StructFloatFieldInfoFlags)((uint)types[1] << 1);

const StructFloatFieldInfoFlags bothFloat = STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND;
if ((flags & bothFloat) == 0)
return (uint)STRUCT_NO_FLOAT_FIELD;

return floatFieldFlags;
if ((flags & bothFloat) == bothFloat)
{
Debug.Assert(nFields == 2);
flags ^= (bothFloat | STRUCT_FLOAT_FIELD_ONLY_TWO); // replace bothFloat with ONLY_TWO
}
else if (nFields == 1)
{
Debug.Assert((flags & STRUCT_FLOAT_FIELD_FIRST) != 0);
flags ^= (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_ONLY_ONE); // replace FIRST with ONLY_ONE
}
return (uint)flags;
}
}
}
16 changes: 2 additions & 14 deletions src/coreclr/vm/callingconvention.h
Original file line number Diff line number Diff line change
Expand Up @@ -1816,17 +1816,7 @@ int ArgIteratorTemplate<ARGITERATOR_BASE>::GetNextOffset()
}
else
{
MethodTable* pMethodTable = nullptr;

if (!thValueType.IsTypeDesc())
pMethodTable = thValueType.AsMethodTable();
else
{
_ASSERTE(thValueType.IsNativeValueType());
pMethodTable = thValueType.AsNativeValueType();
}
_ASSERTE(pMethodTable != nullptr);
flags = MethodTable::GetRiscV64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable);
flags = MethodTable::GetRiscV64PassStructInRegisterFlags(thValueType);
if (flags & STRUCT_HAS_FLOAT_FIELDS_MASK)
{
cFPRegs = (flags & STRUCT_FLOAT_FIELD_ONLY_TWO) ? 2 : 1;
Expand Down Expand Up @@ -2038,9 +2028,7 @@ void ArgIteratorTemplate<ARGITERATOR_BASE>::ComputeReturnFlags()
if (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE)
{
assert(!thValueType.IsTypeDesc());

MethodTable *pMethodTable = thValueType.AsMethodTable();
flags = (MethodTable::GetRiscV64PassStructInRegisterFlags((CORINFO_CLASS_HANDLE)pMethodTable) & 0xff) << RETURN_FP_SIZE_SHIFT;
flags = (MethodTable::GetRiscV64PassStructInRegisterFlags(thValueType) & 0xff) << RETURN_FP_SIZE_SHIFT;
break;
}
#else
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/vm/jitinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9641,7 +9641,7 @@ uint32_t CEEInfo::getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE cls)
uint32_t size = STRUCT_NO_FLOAT_FIELD;

#if defined(TARGET_RISCV64)
size = (uint32_t)MethodTable::GetRiscV64PassStructInRegisterFlags(cls);
size = (uint32_t)MethodTable::GetRiscV64PassStructInRegisterFlags(TypeHandle(cls));
#endif // TARGET_RISCV64

EE_TO_JIT_TRANSITION_LEAF();
Expand Down
Loading