diff --git a/src/coreclr/jit/abi.cpp b/src/coreclr/jit/abi.cpp index e80533fc783a06..fd899b899546b1 100644 --- a/src/coreclr/jit/abi.cpp +++ b/src/coreclr/jit/abi.cpp @@ -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 diff --git a/src/coreclr/jit/abi.h b/src/coreclr/jit/abi.h index 1e51a14d9c09a5..97232bf911c4a3 100644 --- a/src/coreclr/jit/abi.h +++ b/src/coreclr/jit/abi.h @@ -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; @@ -77,7 +84,7 @@ class RegisterQueue { } - unsigned Count() + unsigned Count() const { return m_numRegs - m_index; } @@ -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) @@ -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 diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 774eee3fe3cb20..77ef1962ebf551 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -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); diff --git a/src/coreclr/jit/targetriscv64.cpp b/src/coreclr/jit/targetriscv64.cpp index 5c51f66f83c402..29f71dc76d8fa2 100644 --- a/src/coreclr/jit/targetriscv64.cpp +++ b/src/coreclr/jit/targetriscv64.cpp @@ -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