Skip to content

Commit

Permalink
[RISC-V] New ABI classifier (dotnet#101114)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomeksowi authored and matouskozak committed Apr 30, 2024
1 parent 16fce51 commit e223770
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 6 deletions.
5 changes: 1 addition & 4 deletions src/coreclr/jit/abi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,10 +281,7 @@ bool ABIPassingInformation::IsSplitAcrossRegistersAndStack() const
//
ABIPassingInformation ABIPassingInformation::FromSegment(Compiler* comp, const ABIPassingSegment& segment)
{
ABIPassingInformation info;
info.NumSegments = 1;
info.Segments = new (comp, CMK_ABI) ABIPassingSegment(segment);
return info;
return {1, new (comp, CMK_ABI) ABIPassingSegment(segment)};
}

#ifdef DEBUG
Expand Down
27 changes: 26 additions & 1 deletion src/coreclr/jit/abi.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,16 @@ struct ABIPassingInformation
// multiple register segments and a struct segment.
// - On Windows x64, all parameters always fit into one stack slot or
// register, and thus always have NumSegments == 1
// - On RISC-V, structs can be split out over 2 segments, each can be an integer/float register or a stack slot
unsigned NumSegments = 0;
ABIPassingSegment* Segments = nullptr;

ABIPassingInformation(unsigned numSegments = 0, ABIPassingSegment* segments = nullptr)
: NumSegments(numSegments)
, Segments(segments)
{
}

bool HasAnyRegisterSegment() const;
bool HasAnyStackSegment() const;
bool HasExactlyOneRegisterSegment() const;
Expand All @@ -77,7 +84,7 @@ class RegisterQueue
{
}

unsigned Count()
unsigned Count() const
{
return m_numRegs - m_index;
}
Expand Down Expand Up @@ -179,6 +186,22 @@ class Arm32Classifier
WellKnownArg wellKnownParam);
};

class RiscV64Classifier
{
const ClassifierInfo& m_info;
RegisterQueue m_intRegs;
RegisterQueue m_floatRegs;
unsigned m_stackArgSize = 0;

public:
RiscV64Classifier(const ClassifierInfo& info);

ABIPassingInformation Classify(Compiler* comp,
var_types type,
ClassLayout* structLayout,
WellKnownArg wellKnownParam);
};

#if defined(TARGET_X86)
typedef X86Classifier PlatformClassifier;
#elif defined(WINDOWS_AMD64_ABI)
Expand All @@ -189,6 +212,8 @@ typedef SysVX64Classifier PlatformClassifier;
typedef Arm64Classifier PlatformClassifier;
#elif defined(TARGET_ARM)
typedef Arm32Classifier PlatformClassifier;
#elif defined(TARGET_RISCV64)
typedef RiscV64Classifier PlatformClassifier;
#endif

#ifdef SWIFT_SUPPORT
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/jit/lclvars.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1804,7 +1804,8 @@ void Compiler::lvaClassifyParameterABI()
}
else
#endif
#if defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_ARM)
#if defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_ARM) || \
defined(TARGET_RISCV64)
{
PlatformClassifier classifier(cInfo);
lvaClassifyParameterABI(classifier);
Expand Down
150 changes: 150 additions & 0 deletions src/coreclr/jit/targetriscv64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,154 @@ const regNumber fltArgRegs [] = {REG_FLTARG_0, REG_FLTARG_1, REG_FLTARG_2, REG_F
const regMaskTP fltArgMasks[] = {RBM_FLTARG_0, RBM_FLTARG_1, RBM_FLTARG_2, RBM_FLTARG_3, RBM_FLTARG_4, RBM_FLTARG_5, RBM_FLTARG_6, RBM_FLTARG_7 };
// clang-format on

//-----------------------------------------------------------------------------
// RiscV64Classifier:
// Construct a new instance of the RISC-V 64 ABI classifier.
//
// Parameters:
// info - Info about the method being classified.
//
RiscV64Classifier::RiscV64Classifier(const ClassifierInfo& info)
: m_info(info)
, m_intRegs(intArgRegs, ArrLen(intArgRegs))
, m_floatRegs(fltArgRegs, ArrLen(fltArgRegs))
{
}

//-----------------------------------------------------------------------------
// Classify:
// Classify a parameter for the RISC-V 64 ABI.
//
// Parameters:
// comp - Compiler instance
// type - The type of the parameter
// structLayout - The layout of the struct. Expected to be non-null if
// varTypeIsStruct(type) is true.
// wellKnownParam - Well known type of the parameter (if it may affect its ABI classification)
//
// Returns:
// Classification information for the parameter.
//
ABIPassingInformation RiscV64Classifier::Classify(Compiler* comp,
var_types type,
ClassLayout* structLayout,
WellKnownArg /*wellKnownParam*/)
{
assert(!m_info.IsVarArgs); // TODO: varargs currently not supported on RISC-V

StructFloatFieldInfoFlags flags = STRUCT_NO_FLOAT_FIELD;
unsigned intFields = 0, floatFields = 0;
unsigned passedSize;

if (varTypeIsStruct(type))
{
passedSize = structLayout->GetSize();
if (passedSize > MAX_PASS_MULTIREG_BYTES)
{
passedSize = TARGET_POINTER_SIZE; // pass by reference
}
else if (!structLayout->IsBlockLayout())
{
flags = (StructFloatFieldInfoFlags)comp->info.compCompHnd->getRISCV64PassStructInRegisterFlags(
structLayout->GetClassHandle());

if ((flags & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0)
{
floatFields = 1;
}
else if ((flags & STRUCT_FLOAT_FIELD_ONLY_TWO) != 0)
{
floatFields = 2;
}
else if (flags != STRUCT_NO_FLOAT_FIELD)
{
assert((flags & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND)) != 0);
floatFields = 1;
intFields = 1;
}
}
}
else
{
assert(genTypeSize(type) <= TARGET_POINTER_SIZE);

if (varTypeIsFloating(type))
floatFields = 1;

passedSize = genTypeSize(type);
}

assert((floatFields > 0) || (intFields == 0));

auto PassSlot = [this](bool inFloatReg, unsigned offset, unsigned size) -> ABIPassingSegment {
assert(size > 0);
assert(size <= TARGET_POINTER_SIZE);
if (inFloatReg)
{
return ABIPassingSegment::InRegister(m_floatRegs.Dequeue(), offset, size);
}
else if (m_intRegs.Count() > 0)
{
return ABIPassingSegment::InRegister(m_intRegs.Dequeue(), offset, size);
}
else
{
assert((m_stackArgSize % TARGET_POINTER_SIZE) == 0);
ABIPassingSegment seg = ABIPassingSegment::OnStack(m_stackArgSize, offset, size);
m_stackArgSize += TARGET_POINTER_SIZE;
return seg;
}
};

if ((floatFields > 0) && (m_floatRegs.Count() >= floatFields) && (m_intRegs.Count() >= intFields))
{
// Hardware floating-point calling convention
if ((floatFields == 1) && (intFields == 0))
{
if (flags == STRUCT_NO_FLOAT_FIELD)
assert(varTypeIsFloating(type)); // standalone floating-point real
else
assert((flags & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0); // struct containing just one FP real

return ABIPassingInformation::FromSegment(comp, ABIPassingSegment::InRegister(m_floatRegs.Dequeue(), 0,
passedSize));
}
else
{
assert(varTypeIsStruct(type));
assert((floatFields + intFields) == 2);
assert(flags != STRUCT_NO_FLOAT_FIELD);
assert((flags & STRUCT_FLOAT_FIELD_ONLY_ONE) == 0);

unsigned firstSize = ((flags & STRUCT_FIRST_FIELD_SIZE_IS8) != 0) ? 8 : 4;
unsigned secondSize = ((flags & STRUCT_SECOND_FIELD_SIZE_IS8) != 0) ? 8 : 4;
unsigned offset = max(firstSize, secondSize); // TODO: cover empty fields and custom offsets / alignments

bool isFirstFloat = (flags & (STRUCT_FLOAT_FIELD_ONLY_TWO | STRUCT_FLOAT_FIELD_FIRST)) != 0;
bool isSecondFloat = (flags & (STRUCT_FLOAT_FIELD_ONLY_TWO | STRUCT_FLOAT_FIELD_SECOND)) != 0;
assert(isFirstFloat || isSecondFloat);

return {2, new (comp, CMK_ABI) ABIPassingSegment[]{PassSlot(isFirstFloat, 0, firstSize),
PassSlot(isSecondFloat, offset, secondSize)}};
}
}
else
{
// Integer calling convention
if (passedSize <= TARGET_POINTER_SIZE)
{
return ABIPassingInformation::FromSegment(comp, PassSlot(false, 0, passedSize));
}
else
{
assert(varTypeIsStruct(type));
return {2, new (comp, CMK_ABI)
ABIPassingSegment[]{PassSlot(false, 0, TARGET_POINTER_SIZE),
PassSlot(false, TARGET_POINTER_SIZE, passedSize - TARGET_POINTER_SIZE)}};
}
}

unreached();
}

#endif // TARGET_RISCV64

0 comments on commit e223770

Please sign in to comment.