Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
21 changes: 21 additions & 0 deletions src/coreclr/jit/abi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,27 @@ ABIPassingInformation ABIPassingInformation::FromSegment(Compiler* comp, const A
return info;
}

//-----------------------------------------------------------------------------
// FromSegments:
// Create ABIPassingInformation from multiple segments.
//
// Parameters:
// comp - Compiler instance
// segments - Segments that represents the passing information
//
// Return Value:
// An instance of ABIPassingInformation.
//
ABIPassingInformation ABIPassingInformation::FromSegments(Compiler* comp,
std::initializer_list<ABIPassingSegment> segments)
{
ABIPassingInformation info;
info.NumSegments = segments.size();
info.Segments = new (comp, CMK_ABI) ABIPassingSegment[info.NumSegments];
std::copy(segments.begin(), segments.end(), info.Segments);
return info;
}

#ifdef DEBUG
//-----------------------------------------------------------------------------
// Dump:
Expand Down
22 changes: 21 additions & 1 deletion src/coreclr/jit/abi.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ 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;

Expand All @@ -58,6 +59,7 @@ struct ABIPassingInformation
bool IsSplitAcrossRegistersAndStack() const;

static ABIPassingInformation FromSegment(Compiler* comp, const ABIPassingSegment& segment);
static ABIPassingInformation FromSegments(Compiler* comp, std::initializer_list<ABIPassingSegment> segments);

#ifdef DEBUG
void Dump() const;
Expand All @@ -77,7 +79,7 @@ class RegisterQueue
{
}

unsigned Count()
unsigned Count() const
{
return m_numRegs - m_index;
}
Expand Down Expand Up @@ -179,6 +181,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 +207,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 @@ -1805,7 +1805,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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Most places within the JIT that use lambdas use camelCase naming convention.

No need to address this here.

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 ABIPassingInformation::FromSegments(comp, {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 ABIPassingInformation::FromSegments(comp, {PassSlot(false, 0, TARGET_POINTER_SIZE),
PassSlot(false, TARGET_POINTER_SIZE,
passedSize - TARGET_POINTER_SIZE)});
}
}

unreached();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this do anything? Won't we have a compiler error if this becomes accidentally reachable?

Feel free to remove this separately.

}

#endif // TARGET_RISCV64