Skip to content

Commit

Permalink
Merge branch 'fp-struct-info' into jit-empty-struct-passing
Browse files Browse the repository at this point in the history
  • Loading branch information
tomeksowi committed Jul 15, 2024
2 parents 67bdf3e + 823df8b commit cebecf8
Show file tree
Hide file tree
Showing 14 changed files with 237 additions and 418 deletions.
96 changes: 0 additions & 96 deletions src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -317,100 +317,6 @@ struct SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR
}
};

// StructFloatFieldInfoFlags: used on LoongArch64 and RISC-V architecture as a legacy representation of
// FpStructInRegistersInfo, returned by FpStructInRegistersInfo::ToOldFlags()
//
// `STRUCT_NO_FLOAT_FIELD` means structs are not passed using the float register(s).
//
// Otherwise, and only for structs with no more than two fields and a total struct size no larger
// than two pointers:
//
// The lowest four bits denote the floating-point info:
// bit 0: `1` means there is only one float or double field within the struct.
// bit 1: `1` means only the first field is floating-point type.
// bit 2: `1` means only the second field is floating-point type.
// bit 3: `1` means the two fields are both floating-point type.
// The bits[5:4] denoting whether the field size is 8-bytes:
// bit 4: `1` means the first field's size is 8.
// bit 5: `1` means the second field's size is 8.
//
// Note that bit 0 and 3 cannot both be set.
enum StructFloatFieldInfoFlags
{
STRUCT_NO_FLOAT_FIELD = 0x0,
STRUCT_FLOAT_FIELD_ONLY_ONE = 0x1,
STRUCT_FLOAT_FIELD_ONLY_TWO = 0x8,
STRUCT_FLOAT_FIELD_FIRST = 0x2,
STRUCT_FLOAT_FIELD_SECOND = 0x4,
STRUCT_FIRST_FIELD_SIZE_IS8 = 0x10,
STRUCT_SECOND_FIELD_SIZE_IS8 = 0x20,
};

// Bitfields for FpStructInRegistersInfo::flags
namespace FpStruct
{
enum Flags
{
// Positions of flags and bitfields
PosOnlyOne = 0,
PosBothFloat = 1,
PosFloatInt = 2,
PosIntFloat = 3,
PosSizeShift1st = 4, // 2 bits
PosSizeShift2nd = 6, // 2 bits

UseIntCallConv = 0, // struct is passed according to integer calling convention

// The flags and bitfields
OnlyOne = 1 << PosOnlyOne, // has only one field, which is floating-point
BothFloat = 1 << PosBothFloat, // has two fields, both are floating-point
FloatInt = 1 << PosFloatInt, // has two fields, 1st is floating and 2nd is integer
IntFloat = 1 << PosIntFloat, // has two fields, 2nd is floating and 1st is integer
SizeShift1stMask = 0b11 << PosSizeShift1st, // log2(size) of 1st field
SizeShift2ndMask = 0b11 << PosSizeShift2nd, // log2(size) of 2nd field
// Note: flags OnlyOne, BothFloat, FloatInt, and IntFloat are mutually exclusive
};
}

// On RISC-V and LoongArch a struct with up to two non-empty fields, at least one of them floating-point,
// can be passed in registers according to hardware FP calling convention. FpStructInRegistersInfo represents
// passing information for such parameters.
struct FpStructInRegistersInfo
{
FpStruct::Flags flags;
uint32_t offset1st;
uint32_t offset2nd;

unsigned SizeShift1st() const { return (flags >> FpStruct::PosSizeShift1st) & 0b11; }
unsigned SizeShift2nd() const { return (flags >> FpStruct::PosSizeShift2nd) & 0b11; }

unsigned Size1st() const { return 1u << SizeShift1st(); }
unsigned Size2nd() const { return 1u << SizeShift2nd(); }

const char* FlagName() const
{
switch (flags & (FpStruct::OnlyOne | FpStruct::BothFloat | FpStruct::FloatInt | FpStruct::IntFloat))
{
case FpStruct::OnlyOne: return "OnlyOne";
case FpStruct::BothFloat: return "BothFloat";
case FpStruct::FloatInt: return "FloatInt";
case FpStruct::IntFloat: return "IntFloat";
default: return "?";
}
}

StructFloatFieldInfoFlags ToOldFlags() const
{
return StructFloatFieldInfoFlags(
((flags & FpStruct::OnlyOne) ? STRUCT_FLOAT_FIELD_ONLY_ONE : 0) |
((flags & FpStruct::BothFloat) ? STRUCT_FLOAT_FIELD_ONLY_TWO : 0) |
((flags & FpStruct::FloatInt) ? STRUCT_FLOAT_FIELD_FIRST : 0) |
((flags & FpStruct::IntFloat) ? STRUCT_FLOAT_FIELD_SECOND : 0) |
((SizeShift1st() == 3) ? STRUCT_FIRST_FIELD_SIZE_IS8 : 0) |
((SizeShift2nd() == 3) ? STRUCT_SECOND_FIELD_SIZE_IS8 : 0));
}
};

#include "corinfoinstructionset.h"

// CorInfoHelpFunc defines the set of helpers (accessed via the ICorDynamicInfo::getHelperFtn())
Expand Down Expand Up @@ -2028,11 +1934,9 @@ struct CORINFO_SWIFT_LOWERING
#define MAX_FPSTRUCT_LOWERED_ELEMENTS 2

// Lowering information on fields of a struct passed by hardware floating-point calling convention on RISC-V and LoongArch
// NOTE: all array elements past numLoweredElements are zero-initialized
struct CORINFO_FPSTRUCT_LOWERING
{
// Whether the struct should be passed by integer calling convention (cannot be passed by FP calling convention).
// If true, all other fields of CORINFO_FPSTRUCT_LOWERING are zeroed.
bool byIntegerCallConv;
// Types of lowered struct fields.
CorInfoType loweredElements[MAX_FPSTRUCT_LOWERED_ELEMENTS];
Expand Down
23 changes: 13 additions & 10 deletions src/coreclr/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -931,17 +931,20 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd,
if (structSize <= (TARGET_POINTER_SIZE * 2))
{
const CORINFO_FPSTRUCT_LOWERING* lowering = GetFpStructLowering(clsHnd);
if (lowering->numLoweredElements == 1)
if (!lowering->byIntegerCallConv)
{
useType = JITtype2varType(lowering->loweredElements[0]);
assert(varTypeIsFloating(useType));
howToReturnStruct = SPK_PrimitiveType;
}
else if (!lowering->byIntegerCallConv)
{
assert(lowering->numLoweredElements == 2);
howToReturnStruct = SPK_ByValue;
useType = TYP_STRUCT;
if (lowering->numLoweredElements == 1)
{
useType = JITtype2varType(lowering->loweredElements[0]);
assert(varTypeIsFloating(useType));
howToReturnStruct = SPK_PrimitiveType;
}
else
{
assert(lowering->numLoweredElements == 2);
howToReturnStruct = SPK_ByValue;
useType = TYP_STRUCT;
}
}
}

Expand Down
20 changes: 11 additions & 9 deletions src/coreclr/jit/targetriscv64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,17 +73,19 @@ ABIPassingInformation RiscV64Classifier::Classify(Compiler* comp,
else if (!structLayout->IsBlockLayout())
{
lowering = comp->GetFpStructLowering(structLayout->GetClassHandle());
assert((lowering->numLoweredElements > 0) == !lowering->byIntegerCallConv);
assert(lowering->numLoweredElements <= 2);
INDEBUG(unsigned debugIntFields = 0;)
for (size_t i = 0; i < lowering->numLoweredElements; ++i)
if (!lowering->byIntegerCallConv)
{
var_types type = JITtype2varType(lowering->loweredElements[i]);
floatFields += (unsigned)varTypeIsFloating(type);
INDEBUG(debugIntFields += (unsigned)varTypeIsIntegralOrI(type);)
assert((lowering->numLoweredElements == 1) || (lowering->numLoweredElements == 2));
INDEBUG(unsigned debugIntFields = 0;)
for (size_t i = 0; i < lowering->numLoweredElements; ++i)
{
var_types type = JITtype2varType(lowering->loweredElements[i]);
floatFields += (unsigned)varTypeIsFloating(type);
INDEBUG(debugIntFields += (unsigned)varTypeIsIntegralOrI(type);)
}
intFields = lowering->numLoweredElements - floatFields;
assert(debugIntFields == intFields);
}
intFields = lowering->numLoweredElements - floatFields;
assert(debugIntFields == intFields);
}
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,90 @@

namespace Internal.JitInterface
{
internal static class LoongArch64PassStructInRegister
// StructFloatFieldInfoFlags: used on LoongArch64 and RISC-V architecture as a legacy representation of
// FpStructInRegistersInfo, returned by FpStructInRegistersInfo.ToOldFlags()
//
// `STRUCT_NO_FLOAT_FIELD` means structs are not passed using the float register(s).
//
// Otherwise, and only for structs with no more than two fields and a total struct size no larger
// than two pointers:
//
// The lowest four bits denote the floating-point info:
// bit 0: `1` means there is only one float or double field within the struct.
// bit 1: `1` means only the first field is floating-point type.
// bit 2: `1` means only the second field is floating-point type.
// bit 3: `1` means the two fields are both floating-point type.
// The bits[5:4] denoting whether the field size is 8-bytes:
// bit 4: `1` means the first field's size is 8.
// 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,
STRUCT_FLOAT_FIELD_ONLY_ONE = 0x1,
STRUCT_FLOAT_FIELD_ONLY_TWO = 0x8,
STRUCT_FLOAT_FIELD_FIRST = 0x2,
STRUCT_FLOAT_FIELD_SECOND = 0x4,
STRUCT_FIRST_FIELD_SIZE_IS8 = 0x10,
STRUCT_SECOND_FIELD_SIZE_IS8 = 0x20,
};


// Bitfields for FpStructInRegistersInfo.flags
[Flags]
public enum FpStruct
{
// Positions of flags and bitfields
PosOnlyOne = 0,
PosBothFloat = 1,
PosFloatInt = 2,
PosIntFloat = 3,
PosSizeShift1st = 4, // 2 bits
PosSizeShift2nd = 6, // 2 bits

UseIntCallConv = 0, // struct is passed according to integer calling convention

// The flags and bitfields
OnlyOne = 1 << PosOnlyOne, // has only one field, which is floating-point
BothFloat = 1 << PosBothFloat, // has two fields, both are floating-point
FloatInt = 1 << PosFloatInt, // has two fields, 1st is floating and 2nd is integer
IntFloat = 1 << PosIntFloat, // has two fields, 2nd is floating and 1st is integer
SizeShift1stMask = 0b11 << PosSizeShift1st, // log2(size) of 1st field
SizeShift2ndMask = 0b11 << PosSizeShift2nd, // log2(size) of 2nd field
// Note: flags OnlyOne, BothFloat, FloatInt, and IntFloat are mutually exclusive
}

// On RISC-V and LoongArch a struct with up to two non-empty fields, at least one of them floating-point,
// can be passed in registers according to hardware FP calling convention. FpStructInRegistersInfo represents
// passing information for such parameters.
public struct FpStructInRegistersInfo
{
public FpStruct flags;
public uint offset1st;
public uint offset2nd;

public uint SizeShift1st() { return (uint)((int)flags >> (int)FpStruct.PosSizeShift1st) & 0b11; }

public uint SizeShift2nd() { return (uint)((int)flags >> (int)FpStruct.PosSizeShift2nd) & 0b11; }

public uint Size1st() { return 1u << (int)SizeShift1st(); }
public uint Size2nd() { return 1u << (int)SizeShift2nd(); }

public StructFloatFieldInfoFlags ToOldFlags()
{
return
((flags & FpStruct.OnlyOne) != 0 ? StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_ONE : 0) |
((flags & FpStruct.BothFloat) != 0 ? StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO : 0) |
((flags & FpStruct.FloatInt) != 0 ? StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST : 0) |
((flags & FpStruct.IntFloat) != 0 ? StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND : 0) |
((SizeShift1st() == 3) ? StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_SIZE_IS8 : 0) |
((SizeShift2nd() == 3) ? StructFloatFieldInfoFlags.STRUCT_SECOND_FIELD_SIZE_IS8 : 0);
}
}

internal static class RiscVLoongArch64FpStruct
{
private const int
ENREGISTERED_PARAMTYPE_MAXSIZE = 16,
Expand Down Expand Up @@ -125,8 +208,10 @@ private static bool FlattenFields(TypeDesc td, uint offset, ref FpStructInRegist

private static bool IsAligned(uint val, uint alignment) => 0 == (val & (alignment - 1));

public static FpStructInRegistersInfo GetLoongArch64PassFpStructInRegistersInfo(TypeDesc td)
public static FpStructInRegistersInfo GetFpStructInRegistersInfo(TypeDesc td, TargetArchitecture arch)
{
Debug.Assert(arch is TargetArchitecture.RiscV64 or TargetArchitecture.LoongArch64);

if (td.GetElementSize().AsInt > ENREGISTERED_PARAMTYPE_MAXSIZE)
return new FpStructInRegistersInfo{};

Expand Down
27 changes: 5 additions & 22 deletions src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3456,31 +3456,14 @@ private void getSwiftLowering(CORINFO_CLASS_STRUCT_* structHnd, ref CORINFO_SWIF

private void getFpStructLowering(CORINFO_CLASS_STRUCT_* structHnd, ref CORINFO_FPSTRUCT_LOWERING lowering)
{
TypeDesc typeDesc = HandleToObject(structHnd);
var target = _compilation.TypeSystemContext.Target;
FpStructInRegistersInfo info;
if (target.Architecture is TargetArchitecture.RiscV64)
{
info = RiscV64PassFpStructInRegisters.GetRiscV64PassFpStructInRegistersInfo(typeDesc);
}
else if (target.Architecture is TargetArchitecture.LoongArch64)
{
info = LoongArch64PassStructInRegister.GetLoongArch64PassFpStructInRegistersInfo(typeDesc);
}
else
{
Debug.Assert(false, "Unsupported architecture for getFpStructInRegistersInfo");
return;
}

FpStructInRegistersInfo info = RiscVLoongArch64FpStruct.GetFpStructInRegistersInfo(
HandleToObject(structHnd), _compilation.TypeSystemContext.Target.Architecture);
if (info.flags != FpStruct.UseIntCallConv)
{
lowering = new CORINFO_FPSTRUCT_LOWERING {
byIntegerCallConv = false,
numLoweredElements = ((info.flags & FpStruct.OnlyOne) != 0) ? 1 : 2,
};
lowering.byIntegerCallConv = false;
lowering.Offsets[0] = info.offset1st;
lowering.Offsets[1] = info.offset2nd;
lowering.numLoweredElements = ((info.flags & FpStruct.OnlyOne) != 0) ? 1 : 2;

if ((info.flags & (FpStruct.BothFloat | FpStruct.FloatInt | FpStruct.OnlyOne)) != 0)
lowering.LoweredElements[0] = (info.SizeShift1st() == 3) ? CorInfoType.CORINFO_TYPE_DOUBLE : CorInfoType.CORINFO_TYPE_FLOAT;
Expand All @@ -3503,7 +3486,7 @@ private void getFpStructLowering(CORINFO_CLASS_STRUCT_* structHnd, ref CORINFO_F
}
else
{
lowering = new CORINFO_FPSTRUCT_LOWERING{ byIntegerCallConv = true };
lowering.byIntegerCallConv = true;
}
}

Expand Down
Loading

0 comments on commit cebecf8

Please sign in to comment.