diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index abc9eacef0b85..16a9fa1b3007c 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -307,45 +307,6 @@ struct SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR } }; -// StructFloadFieldInfoFlags: used on LoongArch64 architecture by `getLoongArch64PassStructInRegisterFlags` and -// `getRISCV64PassStructInRegisterFlags` API to convey struct argument passing information. -// -// `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, - - STRUCT_FIRST_FIELD_DOUBLE = (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FIRST_FIELD_SIZE_IS8), - STRUCT_SECOND_FIELD_DOUBLE = (STRUCT_FLOAT_FIELD_SECOND | STRUCT_SECOND_FIELD_SIZE_IS8), - STRUCT_FIELD_TWO_DOUBLES = (STRUCT_FIRST_FIELD_SIZE_IS8 | STRUCT_SECOND_FIELD_SIZE_IS8 | STRUCT_FLOAT_FIELD_ONLY_TWO), - - STRUCT_MERGE_FIRST_SECOND = (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_ONLY_TWO), - STRUCT_MERGE_FIRST_SECOND_8 = (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_ONLY_TWO | STRUCT_SECOND_FIELD_SIZE_IS8), - - STRUCT_HAS_FLOAT_FIELDS_MASK = (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND | STRUCT_FLOAT_FIELD_ONLY_TWO | STRUCT_FLOAT_FIELD_ONLY_ONE), - STRUCT_HAS_8BYTES_FIELDS_MASK = (STRUCT_FIRST_FIELD_SIZE_IS8 | STRUCT_SECOND_FIELD_SIZE_IS8), -}; - #include "corinfoinstructionset.h" // CorInfoHelpFunc defines the set of helpers (accessed via the ICorDynamicInfo::getHelperFtn()) @@ -1940,6 +1901,23 @@ struct CORINFO_SWIFT_LOWERING size_t numLoweredElements; }; +#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 +struct CORINFO_FPSTRUCT_LOWERING +{ + // Whether the struct should be passed by integer calling convention (cannot be passed by FP calling convention). + bool byIntegerCallConv; + // Types of lowered struct fields. + // Note: the integer field is denoted with a signed type reflecting size only so e.g. ushort is reported + // as CORINFO_TYPE_SHORT and object or string is reported as CORINFO_TYPE_LONG. + CorInfoType loweredElements[MAX_FPSTRUCT_LOWERED_ELEMENTS]; + // Offsets of lowered struct fields. + uint32_t offsets[MAX_FPSTRUCT_LOWERED_ELEMENTS]; + // Number of lowered struct fields. + size_t numLoweredElements; +}; + #define SIZEOF__CORINFO_Object TARGET_POINTER_SIZE /* methTable */ #define CORINFO_Array_MaxLength 0x7FFFFFC7 @@ -3065,8 +3043,9 @@ class ICorStaticInfo // Classifies a swift structure into primitives or an implicit byref for ABI purposes. virtual void getSwiftLowering(CORINFO_CLASS_HANDLE structHnd, CORINFO_SWIFT_LOWERING* pLowering) = 0; - virtual uint32_t getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE cls) = 0; - virtual uint32_t getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE cls) = 0; + // Returns lowering info for fields of a RISC-V/LoongArch struct passed in registers according to + // hardware floating-point calling convention. + virtual void getFpStructLowering(CORINFO_CLASS_HANDLE structHnd, CORINFO_FPSTRUCT_LOWERING* pLowering) = 0; }; /***************************************************************************** diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index fc415320cad83..a8d1923f1971d 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -520,11 +520,9 @@ void getSwiftLowering( CORINFO_CLASS_HANDLE structHnd, CORINFO_SWIFT_LOWERING* pLowering) override; -uint32_t getLoongArch64PassStructInRegisterFlags( - CORINFO_CLASS_HANDLE structHnd) override; - -uint32_t getRISCV64PassStructInRegisterFlags( - CORINFO_CLASS_HANDLE structHnd) override; +void getFpStructLowering( + CORINFO_CLASS_HANDLE structHnd, + CORINFO_FPSTRUCT_LOWERING* pLowering) override; uint32_t getThreadTLSIndex( void** ppIndirection) override; diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index a6ad06fade338..6ec44578ba2ae 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* 488a17ce-26c9-4ad0-a7b7-79bf320ea4d1 */ - 0x488a17ce, - 0x26c9, - 0x4ad0, - {0xa7, 0xb7, 0x79, 0xbf, 0x32, 0x0e, 0xa4, 0xd1} +constexpr GUID JITEEVersionIdentifier = { /* e770e8ad-50d5-4511-a435-a3ed3a847a47 */ + 0xe770e8ad, + 0x50d5, + 0x4511, + {0xa4, 0x35, 0xa3, 0xed, 0x3a, 0x84, 0x7a, 0x47} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/jit/ICorJitInfo_names_generated.h b/src/coreclr/jit/ICorJitInfo_names_generated.h index d6526b94ee9ee..4779f13a029b8 100644 --- a/src/coreclr/jit/ICorJitInfo_names_generated.h +++ b/src/coreclr/jit/ICorJitInfo_names_generated.h @@ -130,8 +130,7 @@ DEF_CLR_API(getMethodNameFromMetadata) DEF_CLR_API(getMethodHash) DEF_CLR_API(getSystemVAmd64PassStructInRegisterDescriptor) DEF_CLR_API(getSwiftLowering) -DEF_CLR_API(getLoongArch64PassStructInRegisterFlags) -DEF_CLR_API(getRISCV64PassStructInRegisterFlags) +DEF_CLR_API(getFpStructLowering) DEF_CLR_API(getThreadTLSIndex) DEF_CLR_API(getAddrOfCaptureThreadGlobal) DEF_CLR_API(getHelperFtn) diff --git a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp index 3410d97d1fc56..9c81be10f41f7 100644 --- a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp +++ b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp @@ -1238,22 +1238,13 @@ void WrapICorJitInfo::getSwiftLowering( API_LEAVE(getSwiftLowering); } -uint32_t WrapICorJitInfo::getLoongArch64PassStructInRegisterFlags( - CORINFO_CLASS_HANDLE structHnd) -{ - API_ENTER(getLoongArch64PassStructInRegisterFlags); - uint32_t temp = wrapHnd->getLoongArch64PassStructInRegisterFlags(structHnd); - API_LEAVE(getLoongArch64PassStructInRegisterFlags); - return temp; -} - -uint32_t WrapICorJitInfo::getRISCV64PassStructInRegisterFlags( - CORINFO_CLASS_HANDLE structHnd) +void WrapICorJitInfo::getFpStructLowering( + CORINFO_CLASS_HANDLE structHnd, + CORINFO_FPSTRUCT_LOWERING* pLowering) { - API_ENTER(getRISCV64PassStructInRegisterFlags); - uint32_t temp = wrapHnd->getRISCV64PassStructInRegisterFlags(structHnd); - API_LEAVE(getRISCV64PassStructInRegisterFlags); - return temp; + API_ENTER(getFpStructLowering); + wrapHnd->getFpStructLowering(structHnd, pLowering); + API_LEAVE(getFpStructLowering); } uint32_t WrapICorJitInfo::getThreadTLSIndex( diff --git a/src/coreclr/jit/buildstring.cpp b/src/coreclr/jit/buildstring.cpp index 3f0222ad2649a..9843c9fcf516f 100644 --- a/src/coreclr/jit/buildstring.cpp +++ b/src/coreclr/jit/buildstring.cpp @@ -1,17 +1,15 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#define STRINGIFY(L) #L -#define MAKESTRING(M, L) M(L) -#define STRINGIZE(X) MAKESTRING(STRINGIFY, X) +#include "utils.h" #if defined(__clang__) #define BUILD_COMPILER \ - "Clang " STRINGIZE(__clang_major__) "." STRINGIZE(__clang_minor__) "." STRINGIZE(__clang_patchlevel__) + "Clang " STRINGIFY(__clang_major__) "." STRINGIFY(__clang_minor__) "." STRINGIFY(__clang_patchlevel__) #elif defined(_MSC_VER) -#define BUILD_COMPILER "MSVC " STRINGIZE(_MSC_FULL_VER) +#define BUILD_COMPILER "MSVC " STRINGIFY(_MSC_FULL_VER) #elif defined(__GNUC__) -#define BUILD_COMPILER "GCC " STRINGIZE(__GNUC__) "." STRINGIZE(__GNUC_MINOR__) "." STRINGIZE(__GNUC_PATCHLEVEL__) +#define BUILD_COMPILER "GCC " STRINGIFY(__GNUC__) "." STRINGIFY(__GNUC_MINOR__) "." STRINGIFY(__GNUC_PATCHLEVEL__) #else #define BUILD_COMPILER "Unknown" #endif @@ -26,6 +24,8 @@ #define TARGET_ARCH_STRING "arm64" #elif defined(TARGET_LOONGARCH64) #define TARGET_ARCH_STRING "loongarch64" +#elif defined(TARGET_RISCV64) +#define TARGET_ARCH_STRING "riscv64" #else #define TARGET_ARCH_STRING "Unknown" #endif diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index b091a2efbbec5..a9b9b68517b7a 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -927,37 +927,24 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, howToReturnStruct = SPK_ByReference; useType = TYP_UNKNOWN; } -#elif defined(TARGET_LOONGARCH64) +#elif defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) if (structSize <= (TARGET_POINTER_SIZE * 2)) { - uint32_t floatFieldFlags = info.compCompHnd->getLoongArch64PassStructInRegisterFlags(clsHnd); - - if ((floatFieldFlags & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) - { - howToReturnStruct = SPK_PrimitiveType; - useType = (structSize > 4) ? TYP_DOUBLE : TYP_FLOAT; - } - else if (floatFieldFlags & (STRUCT_HAS_FLOAT_FIELDS_MASK ^ STRUCT_FLOAT_FIELD_ONLY_ONE)) - { - howToReturnStruct = SPK_ByValue; - useType = TYP_STRUCT; - } - } - -#elif defined(TARGET_RISCV64) - if (structSize <= (TARGET_POINTER_SIZE * 2)) - { - uint32_t floatFieldFlags = info.compCompHnd->getRISCV64PassStructInRegisterFlags(clsHnd); - - if ((floatFieldFlags & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) - { - howToReturnStruct = SPK_PrimitiveType; - useType = (structSize > 4) ? TYP_DOUBLE : TYP_FLOAT; - } - else if (floatFieldFlags & (STRUCT_HAS_FLOAT_FIELDS_MASK ^ STRUCT_FLOAT_FIELD_ONLY_ONE)) + const CORINFO_FPSTRUCT_LOWERING* lowering = GetFpStructLowering(clsHnd); + if (!lowering->byIntegerCallConv) { - 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; + } } } @@ -1998,6 +1985,9 @@ void Compiler::compInit(ArenaAllocator* pAlloc, #ifdef SWIFT_SUPPORT m_swiftLoweringCache = nullptr; #endif +#if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) + m_fpStructLoweringCache = nullptr; +#endif // check that HelperCallProperties are initialized @@ -8301,6 +8291,53 @@ void Compiler::GetStructTypeOffset( GetStructTypeOffset(structDesc, type0, type1, offset0, offset1); } +#elif defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) +//------------------------------------------------------------------------ +// GetFpStructLowering: Gets the information on passing of a struct according to hardware floating-point +// calling convention, i.e. the types and offsets of struct fields lowered for passing. +// +// Arguments: +// structHandle - type handle +// +// Return value: +// Lowering info for the struct fields +const CORINFO_FPSTRUCT_LOWERING* Compiler::GetFpStructLowering(CORINFO_CLASS_HANDLE structHandle) +{ + if (m_fpStructLoweringCache == nullptr) + m_fpStructLoweringCache = new (this, CMK_CallArgs) FpStructLoweringMap(getAllocator(CMK_CallArgs)); + + CORINFO_FPSTRUCT_LOWERING* lowering; + if (!m_fpStructLoweringCache->Lookup(structHandle, &lowering)) + { + lowering = new (this, CMK_CallArgs) CORINFO_FPSTRUCT_LOWERING; + info.compCompHnd->getFpStructLowering(structHandle, lowering); + m_fpStructLoweringCache->Set(structHandle, lowering); +#ifdef DEBUG + if (verbose) + { + printf("**** getFpStructInRegistersInfo(0x%x (%s, %u bytes)) =>\n", dspPtr(structHandle), + eeGetClassName(structHandle), info.compCompHnd->getClassSize(structHandle)); + + if (lowering->byIntegerCallConv) + { + printf(" pass by integer calling convention\n"); + } + else + { + printf(" may be passed by floating-point calling convention (%zu fields):\n", + lowering->numLoweredElements); + for (size_t i = 0; i < lowering->numLoweredElements; ++i) + { + const char* type = varTypeName(JITtype2varType(lowering->loweredElements[i])); + printf(" * field[%zu]: type %s at offset %u\n", i, type, lowering->offsets[i]); + } + } + } +#endif // DEBUG + } + return lowering; +} + #endif // defined(UNIX_AMD64_ABI) /*****************************************************************************/ diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 26d9f3b317899..24fd48b2ef3b7 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -561,17 +561,9 @@ class LclVarDsc unsigned char lvIsLastUseCopyOmissionCandidate : 1; #endif // FEATURE_IMPLICIT_BYREFS -#if defined(TARGET_LOONGARCH64) - unsigned char lvIs4Field1 : 1; // Set if the 1st field is int or float within struct for LA-ABI64. - unsigned char lvIs4Field2 : 1; // Set if the 2nd field is int or float within struct for LA-ABI64. - unsigned char lvIsSplit : 1; // Set if the argument is splited. -#endif // defined(TARGET_LOONGARCH64) - -#if defined(TARGET_RISCV64) - unsigned char lvIs4Field1 : 1; // Set if the 1st field is int or float within struct for RISCV64. - unsigned char lvIs4Field2 : 1; // Set if the 2nd field is int or float within struct for RISCV64. - unsigned char lvIsSplit : 1; // Set if the argument is splited. -#endif // defined(TARGET_RISCV64) +#if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) + unsigned char lvIsSplit : 1; // Set if the argument is split across last integer register and stack. +#endif // defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) unsigned char lvSingleDef : 1; // variable has a single def. Used to identify ref type locals that can get type // updates @@ -11496,6 +11488,11 @@ class Compiler void GetStructTypeOffset( CORINFO_CLASS_HANDLE typeHnd, var_types* type0, var_types* type1, uint8_t* offset0, uint8_t* offset1); +#elif defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) + typedef JitHashTable, CORINFO_FPSTRUCT_LOWERING*> + FpStructLoweringMap; + FpStructLoweringMap* m_fpStructLoweringCache; + const CORINFO_FPSTRUCT_LOWERING* GetFpStructLowering(CORINFO_CLASS_HANDLE structHandle); #endif // defined(UNIX_AMD64_ABI) void fgMorphMultiregStructArgs(GenTreeCall* call); diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 19811b685baee..a140ecb935cb4 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -29544,37 +29544,29 @@ void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp, #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) assert((structSize >= TARGET_POINTER_SIZE) && (structSize <= (2 * TARGET_POINTER_SIZE))); - -#ifdef TARGET_LOONGARCH64 - uint32_t floatFieldFlags = comp->info.compCompHnd->getLoongArch64PassStructInRegisterFlags(retClsHnd); -#else - uint32_t floatFieldFlags = comp->info.compCompHnd->getRISCV64PassStructInRegisterFlags(retClsHnd); -#endif BYTE gcPtrs[2] = {TYPE_GC_NONE, TYPE_GC_NONE}; comp->info.compCompHnd->getClassGClayout(retClsHnd, &gcPtrs[0]); - - if (floatFieldFlags & STRUCT_FLOAT_FIELD_ONLY_TWO) - { - comp->compFloatingPointUsed = true; - assert((structSize > 8) == ((floatFieldFlags & STRUCT_HAS_8BYTES_FIELDS_MASK) > 0)); - m_regType[0] = (floatFieldFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; - m_regType[1] = (floatFieldFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; - } - else if (floatFieldFlags & STRUCT_FLOAT_FIELD_FIRST) - { - comp->compFloatingPointUsed = true; - assert((structSize > 8) == ((floatFieldFlags & STRUCT_HAS_8BYTES_FIELDS_MASK) > 0)); - m_regType[0] = (floatFieldFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; - m_regType[1] = - (floatFieldFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? comp->getJitGCType(gcPtrs[1]) : TYP_INT; - } - else if (floatFieldFlags & STRUCT_FLOAT_FIELD_SECOND) + const CORINFO_FPSTRUCT_LOWERING* lowering = comp->GetFpStructLowering(retClsHnd); + if (!lowering->byIntegerCallConv) { comp->compFloatingPointUsed = true; - assert((structSize > 8) == ((floatFieldFlags & STRUCT_HAS_8BYTES_FIELDS_MASK) > 0)); - m_regType[0] = - (floatFieldFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? comp->getJitGCType(gcPtrs[0]) : TYP_INT; - m_regType[1] = (floatFieldFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; + assert(lowering->numLoweredElements == MAX_RET_REG_COUNT); + var_types types[MAX_RET_REG_COUNT] = {JITtype2varType(lowering->loweredElements[0]), + JITtype2varType(lowering->loweredElements[1])}; + assert(varTypeIsFloating(types[0]) || varTypeIsFloating(types[1])); + assert((structSize > 8) == ((genTypeSize(types[0]) == 8) || (genTypeSize(types[1]) == 8))); + for (unsigned i = 0; i < MAX_RET_REG_COUNT; ++i) + { + if (varTypeIsFloating(types[i])) + { + m_regType[i] = types[i]; + } + else + { + assert(varTypeIsIntegralOrI(types[i])); + m_regType[i] = (genTypeSize(types[i]) == 8) ? comp->getJitGCType(gcPtrs[i]) : TYP_INT; + } + } } else { diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index c7b5caa4b38ae..7b893e1c39dfb 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -4544,7 +4544,7 @@ struct CallArgABIInformation SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR StructDesc; #endif // UNIX_AMD64_ABI #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - // For LoongArch64's ABI, the struct which has float field(s) and no more than two fields + // For LoongArch64's and RISC-V 64's ABI, the struct which has float field(s) and no more than two fields // may be passed by float register(s). // e.g `struct {int a; float b;}` passed by an integer register and a float register. var_types StructFloatFieldType[2]; diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 4dc8fec909def..ad5926522e41d 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -901,24 +901,21 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un } else #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - uint32_t floatFlags = STRUCT_NO_FLOAT_FIELD; + const CORINFO_FPSTRUCT_LOWERING* lowering = nullptr; + var_types argRegTypeInStruct1 = TYP_UNKNOWN; var_types argRegTypeInStruct2 = TYP_UNKNOWN; if ((strip(corInfoType) == CORINFO_TYPE_VALUECLASS) && (argSize <= MAX_PASS_MULTIREG_BYTES)) { -#if defined(TARGET_LOONGARCH64) - floatFlags = info.compCompHnd->getLoongArch64PassStructInRegisterFlags(typeHnd); -#else - floatFlags = info.compCompHnd->getRISCV64PassStructInRegisterFlags(typeHnd); -#endif + lowering = GetFpStructLowering(typeHnd); } - if ((floatFlags & STRUCT_HAS_FLOAT_FIELDS_MASK) != 0) + if ((lowering != nullptr) && !lowering->byIntegerCallConv) { assert(varTypeIsStruct(argType)); int floatNum = 0; - if ((floatFlags & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) + if (lowering->numLoweredElements == 1) { assert(argSize <= 8); assert(varDsc->lvExactSize() <= argSize); @@ -926,41 +923,26 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un floatNum = 1; canPassArgInRegisters = varDscInfo->canEnreg(TYP_DOUBLE, 1); - argRegTypeInStruct1 = (varDsc->lvExactSize() == 8) ? TYP_DOUBLE : TYP_FLOAT; - } - else if ((floatFlags & STRUCT_FLOAT_FIELD_ONLY_TWO) != 0) - { - floatNum = 2; - canPassArgInRegisters = varDscInfo->canEnreg(TYP_DOUBLE, 2); - - argRegTypeInStruct1 = (floatFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; - argRegTypeInStruct2 = (floatFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; + argRegTypeInStruct1 = JITtype2varType(lowering->loweredElements[0]); + assert(varTypeIsFloating(argRegTypeInStruct1)); } - else if ((floatFlags & STRUCT_FLOAT_FIELD_FIRST) != 0) - { - floatNum = 1; - canPassArgInRegisters = varDscInfo->canEnreg(TYP_DOUBLE, 1); - canPassArgInRegisters = canPassArgInRegisters && varDscInfo->canEnreg(TYP_I_IMPL, 1); - - argRegTypeInStruct1 = (floatFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; - argRegTypeInStruct2 = (floatFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? TYP_LONG : TYP_INT; - } - else if ((floatFlags & STRUCT_FLOAT_FIELD_SECOND) != 0) + else { - floatNum = 1; - canPassArgInRegisters = varDscInfo->canEnreg(TYP_DOUBLE, 1); - canPassArgInRegisters = canPassArgInRegisters && varDscInfo->canEnreg(TYP_I_IMPL, 1); - - argRegTypeInStruct1 = (floatFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_LONG : TYP_INT; - argRegTypeInStruct2 = (floatFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; + assert(lowering->numLoweredElements == 2); + argRegTypeInStruct1 = genActualType(JITtype2varType(lowering->loweredElements[0])); + argRegTypeInStruct2 = genActualType(JITtype2varType(lowering->loweredElements[1])); + floatNum = (int)varTypeIsFloating(argRegTypeInStruct1) + (int)varTypeIsFloating(argRegTypeInStruct2); + canPassArgInRegisters = varDscInfo->canEnreg(TYP_DOUBLE, floatNum); + if (floatNum == 1) + canPassArgInRegisters = canPassArgInRegisters && varDscInfo->canEnreg(TYP_I_IMPL, 1); } assert((floatNum == 1) || (floatNum == 2)); if (!canPassArgInRegisters) { - // On LoongArch64, if there aren't any remaining floating-point registers to pass the argument, - // integer registers (if any) are used instead. + // On LoongArch64 and RISCV64, if there aren't any remaining floating-point registers to pass the + // argument, integer registers (if any) are used instead. canPassArgInRegisters = varDscInfo->canEnreg(argType, cSlotsToEnregister); argRegTypeInStruct1 = TYP_UNKNOWN; @@ -1094,13 +1076,11 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un { varDsc->SetArgReg( genMapRegArgNumToRegNum(firstAllocatedRegArgNum, argRegTypeInStruct1, info.compCallConv)); - varDsc->lvIs4Field1 = (genTypeSize(argRegTypeInStruct1) == 4) ? 1 : 0; if (argRegTypeInStruct2 != TYP_UNKNOWN) { secondAllocatedRegArgNum = varDscInfo->allocRegArg(argRegTypeInStruct2, 1); varDsc->SetOtherArgReg( genMapRegArgNumToRegNum(secondAllocatedRegArgNum, argRegTypeInStruct2, info.compCallConv)); - varDsc->lvIs4Field2 = (genTypeSize(argRegTypeInStruct2) == 4) ? 1 : 0; } else if (cSlots > 1) { @@ -1678,9 +1658,7 @@ void Compiler::lvaInitVarDsc(LclVarDsc* varDsc, varDsc->lvIsImplicitByRef = 0; #endif // FEATURE_IMPLICIT_BYREFS #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - varDsc->lvIs4Field1 = 0; - varDsc->lvIs4Field2 = 0; - varDsc->lvIsSplit = 0; + varDsc->lvIsSplit = 0; #endif // TARGET_LOONGARCH64 || TARGET_RISCV64 // Set the lvType (before this point it is TYP_UNDEF). diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 0f0753598f786..6d3db3e8fd5bb 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -2122,7 +2122,6 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call if (arg.NewAbiInfo.HasAnyFloatingRegisterSegment()) { // Struct passed according to hardware floating-point calling convention - assert(arg.NewAbiInfo.NumSegments <= 2); assert(!arg.NewAbiInfo.HasAnyStackSegment()); if (arg.NewAbiInfo.NumSegments == 2) { diff --git a/src/coreclr/jit/targetloongarch64.cpp b/src/coreclr/jit/targetloongarch64.cpp index 845a0e2991a40..e31e06d5d3399 100644 --- a/src/coreclr/jit/targetloongarch64.cpp +++ b/src/coreclr/jit/targetloongarch64.cpp @@ -78,45 +78,51 @@ ABIPassingInformation LoongArch64Classifier::Classify(Compiler* comp, { assert(!structLayout->IsBlockLayout()); - uint32_t floatFlags; - CORINFO_CLASS_HANDLE typeHnd = structLayout->GetClassHandle(); + CORINFO_CLASS_HANDLE typeHnd = structLayout->GetClassHandle(); + const CORINFO_FPSTRUCT_LOWERING* lowering = comp->GetFpStructLowering(typeHnd); - floatFlags = comp->info.compCompHnd->getLoongArch64PassStructInRegisterFlags(typeHnd); - - if ((floatFlags & STRUCT_HAS_FLOAT_FIELDS_MASK) != 0) + if (!lowering->byIntegerCallConv) { - if ((floatFlags & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) + slots = lowering->numLoweredElements; + if (lowering->numLoweredElements == 1) { assert(passedSize <= TARGET_POINTER_SIZE); + assert(varTypeIsFloating(JITtype2varType(lowering->loweredElements[0]))); - slots = 1; canPassArgInRegisters = m_floatRegs.Count() > 0; - - argRegTypeInStruct1 = (passedSize == 8) ? TYP_DOUBLE : TYP_FLOAT; - } - else if ((floatFlags & STRUCT_FLOAT_FIELD_ONLY_TWO) != 0) - { - slots = 2; - canPassArgInRegisters = m_floatRegs.Count() >= 2; - - argRegTypeInStruct1 = (floatFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; - argRegTypeInStruct2 = (floatFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; + argRegTypeInStruct1 = (passedSize == 8) ? TYP_DOUBLE : TYP_FLOAT; } - else if ((floatFlags & STRUCT_FLOAT_FIELD_FIRST) != 0) - { - slots = 2; - canPassArgInRegisters = (m_floatRegs.Count() > 0) && (m_intRegs.Count() > 0); - - argRegTypeInStruct1 = (floatFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; - argRegTypeInStruct2 = (floatFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? TYP_LONG : TYP_INT; - } - else if ((floatFlags & STRUCT_FLOAT_FIELD_SECOND) != 0) + else { - slots = 2; - canPassArgInRegisters = (m_floatRegs.Count() > 0) && (m_intRegs.Count() > 0); - - argRegTypeInStruct1 = (floatFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_LONG : TYP_INT; - argRegTypeInStruct2 = (floatFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; + assert(lowering->numLoweredElements == 2); + var_types types[] = { + JITtype2varType(lowering->loweredElements[0]), + JITtype2varType(lowering->loweredElements[1]), + }; + if (varTypeIsFloating(types[0]) && varTypeIsFloating(types[1])) + { + canPassArgInRegisters = m_floatRegs.Count() >= 2; + + argRegTypeInStruct1 = types[0]; + argRegTypeInStruct2 = types[1]; + } + else if (!varTypeIsFloating(types[1])) + { + assert(varTypeIsFloating(types[0])); + canPassArgInRegisters = (m_floatRegs.Count() > 0) && (m_intRegs.Count() > 0); + + argRegTypeInStruct1 = types[0]; + argRegTypeInStruct2 = (genTypeSize(types[1]) == 8) ? TYP_LONG : TYP_INT; + } + else + { + assert(!varTypeIsFloating(types[0])); + assert(varTypeIsFloating(types[1])); + canPassArgInRegisters = (m_floatRegs.Count() > 0) && (m_intRegs.Count() > 0); + + argRegTypeInStruct1 = (genTypeSize(types[0]) == 8) ? TYP_LONG : TYP_INT; + argRegTypeInStruct2 = types[1]; + } } assert((slots == 1) || (slots == 2)); diff --git a/src/coreclr/jit/targetriscv64.cpp b/src/coreclr/jit/targetriscv64.cpp index 6adb2b7b91ea1..308df8c980f8d 100644 --- a/src/coreclr/jit/targetriscv64.cpp +++ b/src/coreclr/jit/targetriscv64.cpp @@ -58,9 +58,10 @@ ABIPassingInformation RiscV64Classifier::Classify(Compiler* comp, ClassLayout* structLayout, WellKnownArg /*wellKnownParam*/) { - StructFloatFieldInfoFlags flags = STRUCT_NO_FLOAT_FIELD; - unsigned intFields = 0, floatFields = 0; - unsigned passedSize; + const CORINFO_FPSTRUCT_LOWERING* lowering = nullptr; + + unsigned intFields = 0, floatFields = 0; + unsigned passedSize; if (varTypeIsStruct(type)) { @@ -71,22 +72,19 @@ ABIPassingInformation RiscV64Classifier::Classify(Compiler* comp, } 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) + lowering = comp->GetFpStructLowering(structLayout->GetClassHandle()); + if (!lowering->byIntegerCallConv) { - assert((flags & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND)) != 0); - floatFields = 1; - intFields = 1; + 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); } } } @@ -104,11 +102,15 @@ ABIPassingInformation RiscV64Classifier::Classify(Compiler* comp, // Hardware floating-point calling convention if ((floatFields == 1) && (intFields == 0)) { - if (flags == STRUCT_NO_FLOAT_FIELD) + if (lowering == nullptr) + { assert(varTypeIsFloating(type)); // standalone floating-point real + } else - assert((flags & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0); // struct containing just one FP real - + { + assert(lowering->numLoweredElements == 1); // struct containing just one FP real + assert(varTypeIsFloating(JITtype2varType(lowering->loweredElements[0]))); + } return ABIPassingInformation::FromSegment(comp, ABIPassingSegment::InRegister(m_floatRegs.Dequeue(), 0, passedSize)); } @@ -116,15 +118,20 @@ ABIPassingInformation RiscV64Classifier::Classify(Compiler* comp, { 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; + assert(lowering != nullptr); + assert(!lowering->byIntegerCallConv); + assert(lowering->numLoweredElements == 2); + + var_types types[] = { + JITtype2varType(lowering->loweredElements[0]), + JITtype2varType(lowering->loweredElements[1]), + }; + unsigned firstSize = (genTypeSize(types[0]) == 8) ? 8 : 4; + unsigned secondSize = (genTypeSize(types[1]) == 8) ? 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; + bool isFirstFloat = varTypeIsFloating(types[0]); + bool isSecondFloat = varTypeIsFloating(types[1]); assert(isFirstFloat || isSecondFloat); regNumber firstReg = (isFirstFloat ? m_floatRegs : m_intRegs).Dequeue(); diff --git a/src/coreclr/jit/utils.h b/src/coreclr/jit/utils.h index b0b86736bc0b7..b6b81ddd02029 100644 --- a/src/coreclr/jit/utils.h +++ b/src/coreclr/jit/utils.h @@ -1190,4 +1190,7 @@ bool CastFromFloatOverflows(float fromValue, var_types toType); bool CastFromDoubleOverflows(double fromValue, var_types toType); } // namespace CheckedOps +#define STRINGIFY_(x) #x +#define STRINGIFY(x) STRINGIFY_(x) + #endif // _UTILS_H_ diff --git a/src/coreclr/tools/Common/Internal/Runtime/RiscVLoongArch64FpStruct.cs b/src/coreclr/tools/Common/Internal/Runtime/RiscVLoongArch64FpStruct.cs new file mode 100644 index 0000000000000..ba76758241bf5 --- /dev/null +++ b/src/coreclr/tools/Common/Internal/Runtime/RiscVLoongArch64FpStruct.cs @@ -0,0 +1,257 @@ +// 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.FpStruct; + +namespace Internal.JitInterface +{ + // 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, + TARGET_POINTER_SIZE = 8; + + private static void SetFpStructInRegistersInfoField(ref FpStructInRegistersInfo info, int index, + bool isFloating, uint size, uint offset) + { + Debug.Assert(index < 2); + if (isFloating) + Debug.Assert(size == sizeof(float) || size == sizeof(double)); + + Debug.Assert(size >= 1 && size <= 8); + Debug.Assert((size & (size - 1)) == 0, "size needs to be a power of 2"); + const int sizeShiftLUT = (0 << (1*2)) | (1 << (2*2)) | (2 << (4*2)) | (3 << (8*2)); + int sizeShift = (sizeShiftLUT >> ((int)size * 2)) & 0b11; + + // Use FloatInt and IntFloat as marker flags for 1st and 2nd field respectively being floating. + // Fix to real flags (with OnlyOne and BothFloat) after flattening is complete. + Debug.Assert((int)PosIntFloat == (int)PosFloatInt + 1, "FloatInt and IntFloat need to be adjacent"); + Debug.Assert((int)PosSizeShift2nd == (int)PosSizeShift1st + 2, "SizeShift1st and 2nd need to be adjacent"); + int floatFlag = Convert.ToInt32(isFloating) << ((int)PosFloatInt + index); + int sizeShiftMask = sizeShift << ((int)PosSizeShift1st + 2 * index); + + info.flags |= (FpStruct)(floatFlag | sizeShiftMask); + (index == 0 ? ref info.offset1st : ref info.offset2nd) = offset; + } + + private static bool HandleInlineArray(int elementTypeIndex, int nElements, ref FpStructInRegistersInfo info, ref int typeIndex) + { + int nFlattenedFieldsPerElement = typeIndex - elementTypeIndex; + if (nFlattenedFieldsPerElement == 0) + { + Debug.Assert(nElements == 1, "HasImpliedRepeatedFields must have returned a false positive"); + return true; // ignoring empty struct + } + + Debug.Assert(nFlattenedFieldsPerElement == 1 || nFlattenedFieldsPerElement == 2); + + if (nElements > 2) + return false; // array has too many elements + + if (nElements == 2) + { + if (typeIndex + nFlattenedFieldsPerElement > 2) + return false; // array has too many fields per element + + Debug.Assert(elementTypeIndex == 0); + Debug.Assert(typeIndex == 1); + + // Duplicate the array element info + Debug.Assert((int)FpStruct.IntFloat == ((int)FpStruct.FloatInt << 1), + "FloatInt and IntFloat need to be adjacent"); + Debug.Assert((int)FpStruct.SizeShift2ndMask == ((int)FpStruct.SizeShift1stMask << 2), + "SizeShift1st and 2nd need to be adjacent"); + // Take the 1st field info and shift up to the 2nd field's positions + int floatFlag = (int)(info.flags & FpStruct.FloatInt) << 1; + int sizeShiftMask = (int)(info.flags & FpStruct.SizeShift1stMask) << 2; + info.flags |= (FpStruct)(floatFlag | sizeShiftMask); // merge with 1st field + info.offset2nd = info.offset1st + info.Size1st(); // bump up the field offset + } + return true; + } + + private static bool FlattenFields(TypeDesc td, uint offset, ref FpStructInRegistersInfo info, ref int typeIndex) + { + IEnumerable fields = td.GetFields(); + int nFields = 0; + int elementTypeIndex = typeIndex; + FieldDesc prevField = null; + foreach (FieldDesc field in fields) + { + if (field.IsStatic) + continue; + nFields++; + + if (prevField != null && prevField.Offset.AsInt + prevField.FieldType.GetElementSize().AsInt > field.Offset.AsInt) + return false; // fields overlap, treat as union + + prevField = field; + + TypeFlags category = field.FieldType.Category; + if (category == TypeFlags.ValueType) + { + TypeDesc nested = field.FieldType; + if (!FlattenFields(nested, offset + (uint)field.Offset.AsInt, ref info, ref typeIndex)) + return false; + } + else if (field.FieldType.GetElementSize().AsInt <= TARGET_POINTER_SIZE) + { + if (typeIndex >= 2) + return false; // too many fields + + bool isFloating = category is TypeFlags.Single or TypeFlags.Double; + SetFpStructInRegistersInfoField(ref info, typeIndex++, + isFloating, (uint)field.FieldType.GetElementSize().AsInt, offset + (uint)field.Offset.AsInt); + } + else + { + return false; // field is too big + } + } + + if ((td as MetadataType).HasImpliedRepeatedFields()) + { + Debug.Assert(nFields == 1); + int nElements = td.GetElementSize().AsInt / prevField.FieldType.GetElementSize().AsInt; + if (!HandleInlineArray(elementTypeIndex, nElements, ref info, ref typeIndex)) + return false; + } + return true; + } + + private static bool IsAligned(uint val, uint alignment) => 0 == (val & (alignment - 1)); + + 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{}; + + FpStructInRegistersInfo info = new FpStructInRegistersInfo{}; + int nFields = 0; + if (!FlattenFields(td, 0, ref info, ref nFields)) + return new FpStructInRegistersInfo{}; + + if ((info.flags & (FloatInt | IntFloat)) == 0) + return new FpStructInRegistersInfo{}; // struct has no floating fields + + Debug.Assert(nFields == 1 || nFields == 2); + + if ((info.flags & (FloatInt | IntFloat)) == (FloatInt | IntFloat)) + { + Debug.Assert(nFields == 2); + info.flags ^= (FloatInt | IntFloat | BothFloat); // replace (FloatInt | IntFloat) with BothFloat + } + else if (nFields == 1) + { + Debug.Assert((info.flags & FloatInt) != 0); + Debug.Assert((info.flags & (IntFloat | SizeShift2ndMask)) == 0); + Debug.Assert(info.offset2nd == 0); + info.flags ^= (FloatInt | OnlyOne); // replace FloatInt with OnlyOne + } + Debug.Assert(nFields == ((info.flags & OnlyOne) != 0 ? 1 : 2)); + FpStruct floatFlags = info.flags & (OnlyOne | BothFloat | FloatInt | IntFloat); + Debug.Assert(floatFlags != 0); + Debug.Assert(((uint)floatFlags & ((uint)floatFlags - 1)) == 0, + "there can be only one of (OnlyOne | BothFloat | FloatInt | IntFloat)"); + if (nFields == 2) + { + uint end1st = info.offset1st + info.Size1st(); + uint end2nd = info.offset2nd + info.Size2nd(); + Debug.Assert(end1st <= info.offset2nd || end2nd <= info.offset1st, "fields must not overlap"); + } + Debug.Assert(info.offset1st + info.Size1st() <= td.GetElementSize().AsInt); + Debug.Assert(info.offset2nd + info.Size2nd() <= td.GetElementSize().AsInt); + + return info; + } + } +} diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 05a3389013be2..91146e192c102 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3465,16 +3465,40 @@ private void getSwiftLowering(CORINFO_CLASS_STRUCT_* structHnd, ref CORINFO_SWIF lowering = SwiftPhysicalLowering.LowerTypeForSwiftSignature(HandleToObject(structHnd)); } - private uint getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_STRUCT_* cls) + private void getFpStructLowering(CORINFO_CLASS_STRUCT_* structHnd, ref CORINFO_FPSTRUCT_LOWERING lowering) { - TypeDesc typeDesc = HandleToObject(cls); - return LoongArch64PassStructInRegister.GetLoongArch64PassStructInRegisterFlags(typeDesc); - } + FpStructInRegistersInfo info = RiscVLoongArch64FpStruct.GetFpStructInRegistersInfo( + HandleToObject(structHnd), _compilation.TypeSystemContext.Target.Architecture); + if (info.flags != FpStruct.UseIntCallConv) + { + lowering.byIntegerCallConv = false; + lowering.Offsets[0] = info.offset1st; + lowering.Offsets[1] = info.offset2nd; + lowering.numLoweredElements = ((info.flags & FpStruct.OnlyOne) != 0) ? 1 : 2; - private uint getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_STRUCT_* cls) - { - TypeDesc typeDesc = HandleToObject(cls); - return RISCV64PassStructInRegister.GetRISCV64PassStructInRegisterFlags(typeDesc); + if ((info.flags & (FpStruct.BothFloat | FpStruct.FloatInt | FpStruct.OnlyOne)) != 0) + lowering.LoweredElements[0] = (info.SizeShift1st() == 3) ? CorInfoType.CORINFO_TYPE_DOUBLE : CorInfoType.CORINFO_TYPE_FLOAT; + + if ((info.flags & (FpStruct.BothFloat | FpStruct.IntFloat)) != 0) + lowering.LoweredElements[1] = (info.SizeShift2nd() == 3) ? CorInfoType.CORINFO_TYPE_DOUBLE : CorInfoType.CORINFO_TYPE_FLOAT; + + if ((info.flags & (FpStruct.FloatInt | FpStruct.IntFloat)) != 0) + { + int index = ((info.flags & FpStruct.FloatInt) != 0) ? 1 : 0; + uint sizeShift = (index == 0) ? info.SizeShift1st() : info.SizeShift2nd(); + lowering.LoweredElements[index] = (CorInfoType)((int)CorInfoType.CORINFO_TYPE_BYTE + sizeShift * 2); + + // unittests + Debug.Assert((int)CorInfoType.CORINFO_TYPE_BYTE + 0 * 2 == (int)CorInfoType.CORINFO_TYPE_BYTE); + Debug.Assert((int)CorInfoType.CORINFO_TYPE_BYTE + 1 * 2 == (int)CorInfoType.CORINFO_TYPE_SHORT); + Debug.Assert((int)CorInfoType.CORINFO_TYPE_BYTE + 2 * 2 == (int)CorInfoType.CORINFO_TYPE_INT); + Debug.Assert((int)CorInfoType.CORINFO_TYPE_BYTE + 3 * 2 == (int)CorInfoType.CORINFO_TYPE_LONG); + } + } + else + { + lowering.byIntegerCallConv = true; + } } private uint getThreadTLSIndex(ref void* ppIndirection) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs index 62014eb27bfa1..f81cae4e251e4 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs @@ -1874,32 +1874,16 @@ private static void _getSwiftLowering(IntPtr thisHandle, IntPtr* ppException, CO } [UnmanagedCallersOnly] - private static uint _getLoongArch64PassStructInRegisterFlags(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* structHnd) + private static void _getFpStructLowering(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* structHnd, CORINFO_FPSTRUCT_LOWERING* pLowering) { var _this = GetThis(thisHandle); try { - return _this.getLoongArch64PassStructInRegisterFlags(structHnd); + _this.getFpStructLowering(structHnd, ref *pLowering); } catch (Exception ex) { *ppException = _this.AllocException(ex); - return default; - } - } - - [UnmanagedCallersOnly] - private static uint _getRISCV64PassStructInRegisterFlags(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* structHnd) - { - var _this = GetThis(thisHandle); - try - { - return _this.getRISCV64PassStructInRegisterFlags(structHnd); - } - catch (Exception ex) - { - *ppException = _this.AllocException(ex); - return default; } } @@ -2624,7 +2608,7 @@ private static uint _getJitFlags(IntPtr thisHandle, IntPtr* ppException, CORJIT_ private static IntPtr GetUnmanagedCallbacks() { - void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 177); + void** callbacks = (void**)Marshal.AllocCoTaskMem(sizeof(IntPtr) * 176); callbacks[0] = (delegate* unmanaged)&_isIntrinsic; callbacks[1] = (delegate* unmanaged)&_notifyMethodInfoUsage; @@ -2752,57 +2736,56 @@ private static IntPtr GetUnmanagedCallbacks() callbacks[123] = (delegate* unmanaged)&_getMethodHash; callbacks[124] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; callbacks[125] = (delegate* unmanaged)&_getSwiftLowering; - callbacks[126] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; - callbacks[127] = (delegate* unmanaged)&_getRISCV64PassStructInRegisterFlags; - callbacks[128] = (delegate* unmanaged)&_getThreadTLSIndex; - callbacks[129] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; - callbacks[130] = (delegate* unmanaged)&_getHelperFtn; - callbacks[131] = (delegate* unmanaged)&_getFunctionEntryPoint; - callbacks[132] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; - callbacks[133] = (delegate* unmanaged)&_getMethodSync; - callbacks[134] = (delegate* unmanaged)&_getLazyStringLiteralHelper; - callbacks[135] = (delegate* unmanaged)&_embedModuleHandle; - callbacks[136] = (delegate* unmanaged)&_embedClassHandle; - callbacks[137] = (delegate* unmanaged)&_embedMethodHandle; - callbacks[138] = (delegate* unmanaged)&_embedFieldHandle; - callbacks[139] = (delegate* unmanaged)&_embedGenericHandle; - callbacks[140] = (delegate* unmanaged)&_getLocationOfThisType; - callbacks[141] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; - callbacks[142] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; - callbacks[143] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; - callbacks[144] = (delegate* unmanaged)&_getJustMyCodeHandle; - callbacks[145] = (delegate* unmanaged)&_GetProfilingHandle; - callbacks[146] = (delegate* unmanaged)&_getCallInfo; - callbacks[147] = (delegate* unmanaged)&_getStaticFieldContent; - callbacks[148] = (delegate* unmanaged)&_getObjectContent; - callbacks[149] = (delegate* unmanaged)&_getStaticFieldCurrentClass; - callbacks[150] = (delegate* unmanaged)&_getVarArgsHandle; - callbacks[151] = (delegate* unmanaged)&_canGetVarArgsHandle; - callbacks[152] = (delegate* unmanaged)&_constructStringLiteral; - callbacks[153] = (delegate* unmanaged)&_emptyStringLiteral; - callbacks[154] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; - callbacks[155] = (delegate* unmanaged)&_GetDelegateCtor; - callbacks[156] = (delegate* unmanaged)&_MethodCompileComplete; - callbacks[157] = (delegate* unmanaged)&_getTailCallHelpers; - callbacks[158] = (delegate* unmanaged)&_convertPInvokeCalliToCall; - callbacks[159] = (delegate* unmanaged)&_notifyInstructionSetUsage; - callbacks[160] = (delegate* unmanaged)&_updateEntryPointForTailCall; - callbacks[161] = (delegate* unmanaged)&_allocMem; - callbacks[162] = (delegate* unmanaged)&_reserveUnwindInfo; - callbacks[163] = (delegate* unmanaged)&_allocUnwindInfo; - callbacks[164] = (delegate* unmanaged)&_allocGCInfo; - callbacks[165] = (delegate* unmanaged)&_setEHcount; - callbacks[166] = (delegate* unmanaged)&_setEHinfo; - callbacks[167] = (delegate* unmanaged)&_logMsg; - callbacks[168] = (delegate* unmanaged)&_doAssert; - callbacks[169] = (delegate* unmanaged)&_reportFatalError; - callbacks[170] = (delegate* unmanaged)&_getPgoInstrumentationResults; - callbacks[171] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; - callbacks[172] = (delegate* unmanaged)&_recordCallSite; - callbacks[173] = (delegate* unmanaged)&_recordRelocation; - callbacks[174] = (delegate* unmanaged)&_getRelocTypeHint; - callbacks[175] = (delegate* unmanaged)&_getExpectedTargetArchitecture; - callbacks[176] = (delegate* unmanaged)&_getJitFlags; + callbacks[126] = (delegate* unmanaged)&_getFpStructLowering; + callbacks[127] = (delegate* unmanaged)&_getThreadTLSIndex; + callbacks[128] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; + callbacks[129] = (delegate* unmanaged)&_getHelperFtn; + callbacks[130] = (delegate* unmanaged)&_getFunctionEntryPoint; + callbacks[131] = (delegate* unmanaged)&_getFunctionFixedEntryPoint; + callbacks[132] = (delegate* unmanaged)&_getMethodSync; + callbacks[133] = (delegate* unmanaged)&_getLazyStringLiteralHelper; + callbacks[134] = (delegate* unmanaged)&_embedModuleHandle; + callbacks[135] = (delegate* unmanaged)&_embedClassHandle; + callbacks[136] = (delegate* unmanaged)&_embedMethodHandle; + callbacks[137] = (delegate* unmanaged)&_embedFieldHandle; + callbacks[138] = (delegate* unmanaged)&_embedGenericHandle; + callbacks[139] = (delegate* unmanaged)&_getLocationOfThisType; + callbacks[140] = (delegate* unmanaged)&_getAddressOfPInvokeTarget; + callbacks[141] = (delegate* unmanaged)&_GetCookieForPInvokeCalliSig; + callbacks[142] = (delegate* unmanaged)&_canGetCookieForPInvokeCalliSig; + callbacks[143] = (delegate* unmanaged)&_getJustMyCodeHandle; + callbacks[144] = (delegate* unmanaged)&_GetProfilingHandle; + callbacks[145] = (delegate* unmanaged)&_getCallInfo; + callbacks[146] = (delegate* unmanaged)&_getStaticFieldContent; + callbacks[147] = (delegate* unmanaged)&_getObjectContent; + callbacks[148] = (delegate* unmanaged)&_getStaticFieldCurrentClass; + callbacks[149] = (delegate* unmanaged)&_getVarArgsHandle; + callbacks[150] = (delegate* unmanaged)&_canGetVarArgsHandle; + callbacks[151] = (delegate* unmanaged)&_constructStringLiteral; + callbacks[152] = (delegate* unmanaged)&_emptyStringLiteral; + callbacks[153] = (delegate* unmanaged)&_getFieldThreadLocalStoreID; + callbacks[154] = (delegate* unmanaged)&_GetDelegateCtor; + callbacks[155] = (delegate* unmanaged)&_MethodCompileComplete; + callbacks[156] = (delegate* unmanaged)&_getTailCallHelpers; + callbacks[157] = (delegate* unmanaged)&_convertPInvokeCalliToCall; + callbacks[158] = (delegate* unmanaged)&_notifyInstructionSetUsage; + callbacks[159] = (delegate* unmanaged)&_updateEntryPointForTailCall; + callbacks[160] = (delegate* unmanaged)&_allocMem; + callbacks[161] = (delegate* unmanaged)&_reserveUnwindInfo; + callbacks[162] = (delegate* unmanaged)&_allocUnwindInfo; + callbacks[163] = (delegate* unmanaged)&_allocGCInfo; + callbacks[164] = (delegate* unmanaged)&_setEHcount; + callbacks[165] = (delegate* unmanaged)&_setEHinfo; + callbacks[166] = (delegate* unmanaged)&_logMsg; + callbacks[167] = (delegate* unmanaged)&_doAssert; + callbacks[168] = (delegate* unmanaged)&_reportFatalError; + callbacks[169] = (delegate* unmanaged)&_getPgoInstrumentationResults; + callbacks[170] = (delegate* unmanaged)&_allocPgoInstrumentationBySchema; + callbacks[171] = (delegate* unmanaged)&_recordCallSite; + callbacks[172] = (delegate* unmanaged)&_recordRelocation; + callbacks[173] = (delegate* unmanaged)&_getRelocTypeHint; + callbacks[174] = (delegate* unmanaged)&_getExpectedTargetArchitecture; + callbacks[175] = (delegate* unmanaged)&_getJitFlags; return (IntPtr)callbacks; } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index a8d12e09723e5..902fa1159ed53 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1221,47 +1221,6 @@ public struct SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR public byte eightByteOffsets1; }; - // StructFloadFieldInfoFlags: used on LoongArch64 architecture by `getLoongArch64PassStructInRegisterFlags` and - // `getRISCV64PassStructInRegisterFlags` API to convey struct argument passing information. - // - // `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, - - STRUCT_FIRST_FIELD_DOUBLE = (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FIRST_FIELD_SIZE_IS8), - STRUCT_SECOND_FIELD_DOUBLE = (STRUCT_FLOAT_FIELD_SECOND | STRUCT_SECOND_FIELD_SIZE_IS8), - STRUCT_FIELD_TWO_DOUBLES = (STRUCT_FIRST_FIELD_SIZE_IS8 | STRUCT_SECOND_FIELD_SIZE_IS8 | STRUCT_FLOAT_FIELD_ONLY_TWO), - - STRUCT_MERGE_FIRST_SECOND = (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_ONLY_TWO), - STRUCT_MERGE_FIRST_SECOND_8 = (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_ONLY_TWO | STRUCT_SECOND_FIELD_SIZE_IS8), - - STRUCT_HAS_ONE_FLOAT_MASK = (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND), - STRUCT_HAS_FLOAT_FIELDS_MASK = (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND | STRUCT_FLOAT_FIELD_ONLY_TWO | STRUCT_FLOAT_FIELD_ONLY_ONE), - STRUCT_HAS_8BYTES_FIELDS_MASK = (STRUCT_FIRST_FIELD_SIZE_IS8 | STRUCT_SECOND_FIELD_SIZE_IS8), - }; - // DEBUGGER DATA public enum MappingTypes { @@ -1594,4 +1553,58 @@ public override string ToString() return stringBuilder.ToString(); } } + + public struct CORINFO_FPSTRUCT_LOWERING + { + private byte _byIntegerCallConv; + public bool byIntegerCallConv { get => _byIntegerCallConv != 0; set => _byIntegerCallConv = value ? (byte)1 : (byte)0; } + + [InlineArray(2)] + private struct FpStructLoweredTypes + { + public CorInfoType type; + } + + [InlineArray(2)] + private struct LoweredOffsets + { + public uint offset; + } + + private FpStructLoweredTypes _loweredElements; + + [UnscopedRef] + public Span LoweredElements => _loweredElements; + + private LoweredOffsets _offsets; + + [UnscopedRef] + public Span Offsets => _offsets; + + public nint numLoweredElements; + + // Override for a better unit test experience + public override string ToString() + { + if (byIntegerCallConv) + { + return "byIntegerCallConv"; + } + + var stringBuilder = new StringBuilder(); + stringBuilder.Append('{'); + for (int i = 0; i < numLoweredElements; i++) + { + if (i != 0) + { + stringBuilder.Append(", "); + } + stringBuilder.Append(LoweredElements[i]); + stringBuilder.Append(": "); + stringBuilder.Append(Offsets[i]); + } + stringBuilder.Append('}'); + return stringBuilder.ToString(); + } + } } diff --git a/src/coreclr/tools/Common/JitInterface/LoongArch64PassStructInRegister.cs b/src/coreclr/tools/Common/JitInterface/LoongArch64PassStructInRegister.cs deleted file mode 100644 index 250ea428c44d9..0000000000000 --- a/src/coreclr/tools/Common/JitInterface/LoongArch64PassStructInRegister.cs +++ /dev/null @@ -1,127 +0,0 @@ -// 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 LoongArch64PassStructInRegister - { - private const int - ENREGISTERED_PARAMTYPE_MAXSIZE = 16, - TARGET_POINTER_SIZE = 8; - - private static bool HandleInlineArray(int elementTypeIndex, int nElements, Span types, ref int typeIndex) - { - int nFlattenedFieldsPerElement = typeIndex - elementTypeIndex; - if (nFlattenedFieldsPerElement == 0) - return true; - - Debug.Assert(nFlattenedFieldsPerElement == 1 || nFlattenedFieldsPerElement == 2); - - if (nElements > 2) - return false; - - if (nElements == 2) - { - 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; - } - - private static bool FlattenFieldTypes(TypeDesc td, Span types, ref int typeIndex) - { - IEnumerable fields = td.GetFields(); - int nFields = 0; - int elementTypeIndex = typeIndex; - FieldDesc prevField = null; - foreach (FieldDesc field in fields) - { - if (field.IsStatic) - continue; - nFields++; - - if (prevField != null && prevField.Offset.AsInt + prevField.FieldType.GetElementSize().AsInt > field.Offset.AsInt) - return false; // overlapping fields - - prevField = field; - - TypeFlags category = field.FieldType.Category; - if (category == TypeFlags.ValueType) - { - TypeDesc nested = field.FieldType; - if (!FlattenFieldTypes(nested, types, ref typeIndex)) - return false; - } - else if (field.FieldType.GetElementSize().AsInt <= TARGET_POINTER_SIZE) - { - 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 - { - return false; - } - } - - 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; - } - - public static uint GetLoongArch64PassStructInRegisterFlags(TypeDesc td) - { - if (td.GetElementSize().AsInt > ENREGISTERED_PARAMTYPE_MAXSIZE) - return (uint)STRUCT_NO_FLOAT_FIELD; - - Span 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; - - 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; - } - } -} diff --git a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs deleted file mode 100644 index fae694f8768fc..0000000000000 --- a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs +++ /dev/null @@ -1,127 +0,0 @@ -// 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 - { - private const int - ENREGISTERED_PARAMTYPE_MAXSIZE = 16, - TARGET_POINTER_SIZE = 8; - - private static bool HandleInlineArray(int elementTypeIndex, int nElements, Span types, ref int typeIndex) - { - int nFlattenedFieldsPerElement = typeIndex - elementTypeIndex; - if (nFlattenedFieldsPerElement == 0) - return true; - - Debug.Assert(nFlattenedFieldsPerElement == 1 || nFlattenedFieldsPerElement == 2); - - if (nElements > 2) - return false; - - if (nElements == 2) - { - 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; - } - - private static bool FlattenFieldTypes(TypeDesc td, Span types, ref int typeIndex) - { - IEnumerable fields = td.GetFields(); - int nFields = 0; - int elementTypeIndex = typeIndex; - FieldDesc prevField = null; - foreach (FieldDesc field in fields) - { - if (field.IsStatic) - continue; - nFields++; - - if (prevField != null && prevField.Offset.AsInt + prevField.FieldType.GetElementSize().AsInt > field.Offset.AsInt) - return false; // overlapping fields - - prevField = field; - - TypeFlags category = field.FieldType.Category; - if (category == TypeFlags.ValueType) - { - TypeDesc nested = field.FieldType; - if (!FlattenFieldTypes(nested, types, ref typeIndex)) - return false; - } - else if (field.FieldType.GetElementSize().AsInt <= TARGET_POINTER_SIZE) - { - 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 - { - return false; - } - } - - 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; - } - - public static uint GetRISCV64PassStructInRegisterFlags(TypeDesc td) - { - if (td.GetElementSize().AsInt > ENREGISTERED_PARAMTYPE_MAXSIZE) - return (uint)STRUCT_NO_FLOAT_FIELD; - - Span 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; - - 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; - } - } -} diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index 1ddf962175b02..1b39a55d10d46 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -84,6 +84,7 @@ CORINFO_RESOLVED_TOKEN_PTR,CORINFO_RESOLVED_TOKEN*,CORINFO_RESOLVED_TOKEN*,CORIN CORINFO_EE_INFO*,ref CORINFO_EE_INFO CORINFO_TAILCALL_HELPERS*,ref CORINFO_TAILCALL_HELPERS CORINFO_SWIFT_LOWERING*,ref CORINFO_SWIFT_LOWERING +CORINFO_FPSTRUCT_LOWERING*,ref CORINFO_FPSTRUCT_LOWERING CORINFO_GENERICHANDLE_RESULT*,ref CORINFO_GENERICHANDLE_RESULT CORINFO_METHOD_INFO*,CORINFO_METHOD_INFO* CORINFO_FIELD_INFO*,CORINFO_FIELD_INFO* @@ -289,8 +290,7 @@ FUNCTIONS unsigned getMethodHash(CORINFO_METHOD_HANDLE ftn); bool getSystemVAmd64PassStructInRegisterDescriptor(CORINFO_CLASS_HANDLE structHnd, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr); void getSwiftLowering(CORINFO_CLASS_HANDLE structHnd, CORINFO_SWIFT_LOWERING* pLowering); - uint32_t getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd); - uint32_t getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd); + void getFpStructLowering(CORINFO_CLASS_HANDLE structHnd, CORINFO_FPSTRUCT_LOWERING* pLowering); uint32_t getThreadTLSIndex(void **ppIndirection); int32_t * getAddrOfCaptureThreadGlobal(void **ppIndirection); void* getHelperFtn (CorInfoHelpFunc ftnNum, void **ppIndirection); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs index 16736aef48853..cd945116e3e9c 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs @@ -234,6 +234,8 @@ internal struct ArgLocDesc public int m_byteStackSize; // Stack size in bytes public uint m_floatFlags; // struct with two-fields can be passed by registers. + public FpStructInRegistersInfo m_structFields; // RISC-V and LoongArch - Struct field info when using floating-point register(s) + // Initialize to represent a non-placed argument (no register or stack slots referenced). public void Init() { @@ -244,6 +246,7 @@ public void Init() m_byteStackIndex = -1; m_byteStackSize = 0; m_floatFlags = 0; + m_structFields = new FpStructInRegistersInfo(); m_fRequires64BitAlignment = false; } @@ -1328,18 +1331,14 @@ public int GetNextOffset() case TargetArchitecture.LoongArch64: { int cFPRegs = 0; - uint floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; + FpStructInRegistersInfo info = new FpStructInRegistersInfo{}; _hasArgLocDescForStructInRegs = false; switch (argType) { case CorElementType.ELEMENT_TYPE_R4: - // 32-bit floating point argument. - cFPRegs = 1; - break; - case CorElementType.ELEMENT_TYPE_R8: - // 64-bit floating point argument. + // Floating point argument cFPRegs = 1; break; @@ -1352,14 +1351,11 @@ public int GetNextOffset() } else { - floatFieldFlags = LoongArch64PassStructInRegister.GetLoongArch64PassStructInRegisterFlags(_argTypeHandle.GetRuntimeTypeHandle()); - if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO) != 0) + info = RiscVLoongArch64FpStruct.GetFpStructInRegistersInfo( + _argTypeHandle.GetRuntimeTypeHandle(), TargetArchitecture.LoongArch64); + if (info.flags != FpStruct.UseIntCallConv) { - cFPRegs = 2; - } - else if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_HAS_FLOAT_FIELDS_MASK) != 0) - { - cFPRegs = 1; + cFPRegs = ((info.flags & FpStruct.BothFloat) != 0) ? 2 : 1; } } @@ -1375,10 +1371,10 @@ public int GetNextOffset() if (cFPRegs > 0 && !IsVarArg) { - if (isValueType && ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_HAS_ONE_FLOAT_MASK) != 0)) + if ((info.flags & (FpStruct.FloatInt | FpStruct.IntFloat)) != 0) { Debug.Assert(cFPRegs == 1); - if ((_loongarch64IdxFPReg < 8) && (_loongarch64IdxGenReg < 8)) + if ((_loongarch64IdxFPReg < _transitionBlock.NumArgumentRegisters) && (_loongarch64IdxGenReg < _transitionBlock.NumArgumentRegisters)) { _argLocDescForStructInRegs = new ArgLocDesc(); _argLocDescForStructInRegs.m_idxFloatReg = _loongarch64IdxFPReg; @@ -1388,16 +1384,16 @@ public int GetNextOffset() _argLocDescForStructInRegs.m_cGenReg = 1; _hasArgLocDescForStructInRegs = true; - _argLocDescForStructInRegs.m_floatFlags = floatFieldFlags; + _argLocDescForStructInRegs.m_structFields = info; int argOfsInner = 0; - if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND) != 0) + if ((info.flags & FpStruct.IntFloat) != 0) { - argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _loongarch64IdxGenReg * 8; + argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _loongarch64IdxGenReg * _transitionBlock.PointerSize; } else { - argOfsInner = _transitionBlock.OffsetOfFloatArgumentRegisters + _loongarch64IdxFPReg * 8; + argOfsInner = _transitionBlock.OffsetOfFloatArgumentRegisters + _loongarch64IdxFPReg * _transitionBlock.FloatRegisterSize; } _loongarch64IdxFPReg++; @@ -1405,11 +1401,14 @@ public int GetNextOffset() return argOfsInner; } } - else if (cFPRegs + _loongarch64IdxFPReg <= 8) + else if (cFPRegs + _loongarch64IdxFPReg <= _transitionBlock.NumArgumentRegisters) { // Each floating point register in the argument area is 8 bytes. - int argOfsInner = _transitionBlock.OffsetOfFloatArgumentRegisters + _loongarch64IdxFPReg * 8; - if (floatFieldFlags == (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO) + int argOfsInner = _transitionBlock.OffsetOfFloatArgumentRegisters + _loongarch64IdxFPReg * _transitionBlock.FloatRegisterSize; + const FpStruct twoFloats = FpStruct.BothFloat + | (FpStruct)(2 << (int)FpStruct.PosSizeShift1st) + | (FpStruct)(2 << (int)FpStruct.PosSizeShift2nd); + if (info.flags == twoFloats) { // struct with two single-float fields. _argLocDescForStructInRegs = new ArgLocDesc(); @@ -1419,7 +1418,7 @@ public int GetNextOffset() Debug.Assert(argSize == 8); _hasArgLocDescForStructInRegs = true; - _argLocDescForStructInRegs.m_floatFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO; + _argLocDescForStructInRegs.m_structFields = info; } _loongarch64IdxFPReg += cFPRegs; return argOfsInner; @@ -1458,19 +1457,18 @@ public int GetNextOffset() case TargetArchitecture.RiscV64: { + if (IsVarArg) + throw new NotImplementedException("Varargs on RISC-V not supported yet"); + int cFPRegs = 0; - uint floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; + FpStructInRegistersInfo info = new FpStructInRegistersInfo{}; _hasArgLocDescForStructInRegs = false; switch (argType) { case CorElementType.ELEMENT_TYPE_R4: - // 32-bit floating point argument. - cFPRegs = 1; - break; - case CorElementType.ELEMENT_TYPE_R8: - // 64-bit floating point argument. + // Floating point argument cFPRegs = 1; break; @@ -1483,14 +1481,11 @@ public int GetNextOffset() } else { - floatFieldFlags = RISCV64PassStructInRegister.GetRISCV64PassStructInRegisterFlags(_argTypeHandle.GetRuntimeTypeHandle()); - if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO) != 0) - { - cFPRegs = 2; - } - else if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_HAS_FLOAT_FIELDS_MASK) != 0) + info = RiscVLoongArch64FpStruct.GetFpStructInRegistersInfo( + _argTypeHandle.GetRuntimeTypeHandle(), TargetArchitecture.RiscV64); + if (info.flags != FpStruct.UseIntCallConv) { - cFPRegs = 1; + cFPRegs = ((info.flags & FpStruct.BothFloat) != 0) ? 2 : 1; } } @@ -1506,36 +1501,40 @@ public int GetNextOffset() if (cFPRegs > 0 && !IsVarArg) { - if (isValueType && ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_HAS_ONE_FLOAT_MASK) != 0)) + if ((info.flags & (FpStruct.FloatInt | FpStruct.IntFloat)) != 0) { Debug.Assert(cFPRegs == 1); - if ((_riscv64IdxFPReg < 8) && (_riscv64IdxGenReg < 8)) + + if ((1 + _riscv64IdxFPReg <= _transitionBlock.NumArgumentRegisters) && (1 + _riscv64IdxGenReg <= _transitionBlock.NumArgumentRegisters)) { _argLocDescForStructInRegs = new ArgLocDesc(); _argLocDescForStructInRegs.m_idxFloatReg = _riscv64IdxFPReg; _argLocDescForStructInRegs.m_cFloatReg = 1; + _argLocDescForStructInRegs.m_structFields = info; + _argLocDescForStructInRegs.m_idxGenReg = _riscv64IdxGenReg; _argLocDescForStructInRegs.m_cGenReg = 1; _hasArgLocDescForStructInRegs = true; - _argLocDescForStructInRegs.m_floatFlags = floatFieldFlags; - int argOfsInner = - ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND) != 0) - ? _transitionBlock.OffsetOfArgumentRegisters + _riscv64IdxGenReg * 8 - : _transitionBlock.OffsetOfFloatArgumentRegisters + _riscv64IdxFPReg * 8; + int argOfsInner = ((info.flags & FpStruct.IntFloat) != 0) + ? _transitionBlock.OffsetOfArgumentRegisters + _riscv64IdxGenReg * _transitionBlock.PointerSize + : _transitionBlock.OffsetOfFloatArgumentRegisters + _riscv64IdxFPReg * _transitionBlock.FloatRegisterSize; _riscv64IdxFPReg++; _riscv64IdxGenReg++; return argOfsInner; } } - else if (cFPRegs + _riscv64IdxFPReg <= 8) + else if (cFPRegs + _riscv64IdxFPReg <= _transitionBlock.NumArgumentRegisters) { // Each floating point register in the argument area is 8 bytes. - int argOfsInner = _transitionBlock.OffsetOfFloatArgumentRegisters + _riscv64IdxFPReg * 8; - if (floatFieldFlags == (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO) + int argOfsInner = _transitionBlock.OffsetOfFloatArgumentRegisters + _riscv64IdxFPReg * _transitionBlock.FloatRegisterSize; + const FpStruct twoFloats = FpStruct.BothFloat + | (FpStruct)(2 << (int)FpStruct.PosSizeShift1st) + | (FpStruct)(2 << (int)FpStruct.PosSizeShift2nd); + if (info.flags == twoFloats) { // struct with two single-float fields. _argLocDescForStructInRegs = new ArgLocDesc(); @@ -1545,15 +1544,11 @@ public int GetNextOffset() Debug.Assert(argSize == 8); _hasArgLocDescForStructInRegs = true; - _argLocDescForStructInRegs.m_floatFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO; + _argLocDescForStructInRegs.m_structFields = info; } _riscv64IdxFPReg += cFPRegs; return argOfsInner; } - else - { - _riscv64IdxFPReg = 8; - } } { diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs index 7f2b1fd25ee4f..621168ecd66eb 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs @@ -314,14 +314,22 @@ public void ComputeReturnValueTreatment(CorElementType type, TypeHandle thRetTyp throw new NotSupportedException(); case CorElementType.ELEMENT_TYPE_R4: - if (!IsArmelABI) + if (IsRiscV64 || IsLoongArch64) + { + fpReturnSize = (uint)FpStruct.OnlyOne | (2 << (int)FpStruct.PosSizeShift1st); + } + else if (!IsArmelABI) { fpReturnSize = sizeof(float); } break; case CorElementType.ELEMENT_TYPE_R8: - if (!IsArmelABI) + if (IsRiscV64 || IsLoongArch64) + { + fpReturnSize = (uint)FpStruct.OnlyOne | (3 << (int)FpStruct.PosSizeShift1st); + } + else if (!IsArmelABI) { fpReturnSize = sizeof(double); } @@ -388,12 +396,10 @@ public void ComputeReturnValueTreatment(CorElementType type, TypeHandle thRetTyp if (size <= EnregisteredReturnTypeIntegerMaxSize) { - if (IsLoongArch64) - fpReturnSize = LoongArch64PassStructInRegister.GetLoongArch64PassStructInRegisterFlags(thRetType.GetRuntimeTypeHandle()) & 0xff; - else if (IsRiscV64) - fpReturnSize = RISCV64PassStructInRegister.GetRISCV64PassStructInRegisterFlags(thRetType.GetRuntimeTypeHandle()) & 0xff; + if (IsLoongArch64 || IsRiscV64) + fpReturnSize = (uint)RiscVLoongArch64FpStruct.GetFpStructInRegistersInfo( + thRetType.GetRuntimeTypeHandle(), Architecture).flags; break; - } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index 96e6df9805eb0..da8f861c4dba5 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -133,8 +133,7 @@ - - + diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj b/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj index 3ddf3e46a13cb..4ba7b607e0bde 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj @@ -82,11 +82,8 @@ JitInterface\SystemVStructClassificator.cs - - JitInterface\LoongArch64PassStructInRegister.cs - - - JitInterface\RISCV64PassStructInRegister.cs + + Common\RiscVLoongArch64FpStruct.cs JitInterface\SwiftPhysicalLowering.cs diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h index 74f884b3afa9e..a79c45df3588e 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h @@ -137,8 +137,7 @@ struct JitInterfaceCallbacks unsigned (* getMethodHash)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn); bool (* getSystemVAmd64PassStructInRegisterDescriptor)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE structHnd, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr); void (* getSwiftLowering)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE structHnd, CORINFO_SWIFT_LOWERING* pLowering); - uint32_t (* getLoongArch64PassStructInRegisterFlags)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE structHnd); - uint32_t (* getRISCV64PassStructInRegisterFlags)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE structHnd); + void (* getFpStructLowering)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE structHnd, CORINFO_FPSTRUCT_LOWERING* pLowering); uint32_t (* getThreadTLSIndex)(void * thisHandle, CorInfoExceptionClass** ppException, void** ppIndirection); int32_t* (* getAddrOfCaptureThreadGlobal)(void * thisHandle, CorInfoExceptionClass** ppException, void** ppIndirection); void* (* getHelperFtn)(void * thisHandle, CorInfoExceptionClass** ppException, CorInfoHelpFunc ftnNum, void** ppIndirection); @@ -1417,22 +1416,13 @@ class JitInterfaceWrapper : public ICorJitInfo if (pException != nullptr) throw pException; } - virtual uint32_t getLoongArch64PassStructInRegisterFlags( - CORINFO_CLASS_HANDLE structHnd) -{ - CorInfoExceptionClass* pException = nullptr; - uint32_t temp = _callbacks->getLoongArch64PassStructInRegisterFlags(_thisHandle, &pException, structHnd); - if (pException != nullptr) throw pException; - return temp; -} - - virtual uint32_t getRISCV64PassStructInRegisterFlags( - CORINFO_CLASS_HANDLE structHnd) + virtual void getFpStructLowering( + CORINFO_CLASS_HANDLE structHnd, + CORINFO_FPSTRUCT_LOWERING* pLowering) { CorInfoExceptionClass* pException = nullptr; - uint32_t temp = _callbacks->getRISCV64PassStructInRegisterFlags(_thisHandle, &pException, structHnd); + _callbacks->getFpStructLowering(_thisHandle, &pException, structHnd, pLowering); if (pException != nullptr) throw pException; - return temp; } virtual uint32_t getThreadTLSIndex( diff --git a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h index 8daff9f0882a5..419880ec2dbc4 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h @@ -633,6 +633,14 @@ struct Agnostic_GetSwiftLowering DWORD numLoweredElements; }; +struct Agnostic_GetFpStructLowering +{ + DWORD byIntegerCallConv; + DWORD loweredElements[MAX_FPSTRUCT_LOWERED_ELEMENTS]; + DWORD offsets[MAX_FPSTRUCT_LOWERED_ELEMENTS]; + DWORD numLoweredElements; +}; + struct Agnostic_ResolveVirtualMethodKey { DWORDLONG virtualMethod; diff --git a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h index 236bb4181ce5c..bb2c78da30854 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h @@ -128,8 +128,7 @@ LWM(GetSharedCCtorHelper, DWORDLONG, DWORD) LWM(GetStringConfigValue, DWORD, DWORD) LWM(GetSystemVAmd64PassStructInRegisterDescriptor, DWORDLONG, Agnostic_GetSystemVAmd64PassStructInRegisterDescriptor) LWM(GetSwiftLowering, DWORDLONG, Agnostic_GetSwiftLowering) -LWM(GetLoongArch64PassStructInRegisterFlags, DWORDLONG, DWORD) -LWM(GetRISCV64PassStructInRegisterFlags, DWORDLONG, DWORD) +LWM(GetFpStructLowering, DWORDLONG, Agnostic_GetFpStructLowering) LWM(GetTailCallHelpers, Agnostic_GetTailCallHelpers, Agnostic_CORINFO_TAILCALL_HELPERS) LWM(UpdateEntryPointForTailCall, Agnostic_CORINFO_CONST_LOOKUP, Agnostic_CORINFO_CONST_LOOKUP) LWM(GetThreadTLSIndex, DWORD, DLD) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index 066a6fac585bb..4901a4535de5f 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -6360,9 +6360,12 @@ void MethodContext::dmpGetSwiftLowering( { printf("GetSwiftLowering key structHnd-%016" PRIX64 ", value byReference-%u numLoweredElements-%u", key, value.byReference, value.numLoweredElements); - for (size_t i = 0; i < value.numLoweredElements; i++) + if (!value.byReference) { - printf(" [%zu] %u", i, value.loweredElements[i]); + for (size_t i = 0; i < value.numLoweredElements; i++) + { + printf(" [%zu] %u", i, value.loweredElements[i]); + } } } void MethodContext::repGetSwiftLowering(CORINFO_CLASS_HANDLE structHnd, CORINFO_SWIFT_LOWERING* pLowering) @@ -6382,54 +6385,57 @@ void MethodContext::repGetSwiftLowering(CORINFO_CLASS_HANDLE structHnd, CORINFO_ } } -void MethodContext::recGetLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd, DWORD value) +void MethodContext::recGetFpStructLowering(CORINFO_CLASS_HANDLE structHnd, CORINFO_FPSTRUCT_LOWERING* pLowering) { - if (GetLoongArch64PassStructInRegisterFlags == nullptr) - GetLoongArch64PassStructInRegisterFlags = new LightWeightMap(); + if (GetFpStructLowering == nullptr) + GetFpStructLowering = new LightWeightMap(); DWORDLONG key = CastHandle(structHnd); - GetLoongArch64PassStructInRegisterFlags->Add(key, value); - DEBUG_REC(dmpGetLoongArch64PassStructInRegisterFlags(key, value)); -} + Agnostic_GetFpStructLowering value; + ZeroMemory(&value, sizeof(value)); + value.byIntegerCallConv = pLowering->byIntegerCallConv ? 1 : 0; + if (!pLowering->byIntegerCallConv) + { + value.numLoweredElements = static_cast(pLowering->numLoweredElements); + for (size_t i = 0; i < pLowering->numLoweredElements; i++) + { + value.loweredElements[i] = static_cast(pLowering->loweredElements[i]); + value.offsets[i] = pLowering->offsets[i]; + } + } -void MethodContext::dmpGetLoongArch64PassStructInRegisterFlags(DWORDLONG key, DWORD value) -{ - printf("GetLoongArch64PassStructInRegisterFlags key %016" PRIX64 " value-%08X", key, value); + GetFpStructLowering->Add(key, value); + DEBUG_REC(dmpGetFpStructLowering(key, value)); } - -DWORD MethodContext::repGetLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd) +void MethodContext::dmpGetFpStructLowering( + DWORDLONG key, const Agnostic_GetFpStructLowering& value) { - DWORDLONG key = CastHandle(structHnd); - - DWORD value = LookupByKeyOrMissNoMessage(GetLoongArch64PassStructInRegisterFlags, key); - DEBUG_REP(dmpGetLoongArch64PassStructInRegisterFlags(key, value)); - return value; + printf("GetFpStructLowering key structHnd-%016" PRIX64 ", value byIntegerCallConv-%u numLoweredElements-%u", key, + value.byIntegerCallConv, value.numLoweredElements); + if (!value.byIntegerCallConv) + { + for (size_t i = 0; i < value.numLoweredElements; i++) + { + printf(" [%zu] %u", i, value.loweredElements[i]); + } + } } - -void MethodContext::recGetRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd, DWORD value) +void MethodContext::repGetFpStructLowering(CORINFO_CLASS_HANDLE structHnd, CORINFO_FPSTRUCT_LOWERING* pLowering) { - if (GetRISCV64PassStructInRegisterFlags == nullptr) - GetRISCV64PassStructInRegisterFlags = new LightWeightMap(); - DWORDLONG key = CastHandle(structHnd); + Agnostic_GetFpStructLowering value = LookupByKeyOrMiss(GetFpStructLowering, key, ": key %016" PRIX64 "", key); - GetRISCV64PassStructInRegisterFlags->Add(key, value); - DEBUG_REC(dmpGetRISCV64PassStructInRegisterFlags(key, value)); -} - -void MethodContext::dmpGetRISCV64PassStructInRegisterFlags(DWORDLONG key, DWORD value) -{ - printf("GetRISCV64PassStructInRegisterFlags key %016" PRIX64 " value-%08X", key, value); -} + DEBUG_REP(dmpGetFpStructLowering(key, value)); -DWORD MethodContext::repGetRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd) -{ - DWORDLONG key = CastHandle(structHnd); + pLowering->byIntegerCallConv = value.byIntegerCallConv != 0; + pLowering->numLoweredElements = value.numLoweredElements; - DWORD value = LookupByKeyOrMissNoMessage(GetRISCV64PassStructInRegisterFlags, key); - DEBUG_REP(dmpGetRISCV64PassStructInRegisterFlags(key, value)); - return value; + for (size_t i = 0; i < pLowering->numLoweredElements; i++) + { + pLowering->loweredElements[i] = static_cast(value.loweredElements[i]); + pLowering->offsets[i] = value.offsets[i]; + } } void MethodContext::recGetRelocTypeHint(void* target, WORD result) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index 408203e64c910..8088f80298618 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -787,13 +787,9 @@ class MethodContext void dmpGetSwiftLowering(DWORDLONG key, const Agnostic_GetSwiftLowering& value); void repGetSwiftLowering(CORINFO_CLASS_HANDLE structHnd, CORINFO_SWIFT_LOWERING* pLowering); - void recGetLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd, DWORD value); - void dmpGetLoongArch64PassStructInRegisterFlags(DWORDLONG key, DWORD value); - DWORD repGetLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd); - - void recGetRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd, DWORD value); - void dmpGetRISCV64PassStructInRegisterFlags(DWORDLONG key, DWORD value); - DWORD repGetRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd); + void recGetFpStructLowering(CORINFO_CLASS_HANDLE structHnd, CORINFO_FPSTRUCT_LOWERING* pLowering); + void dmpGetFpStructLowering(DWORDLONG key, const Agnostic_GetFpStructLowering& value); + void repGetFpStructLowering(CORINFO_CLASS_HANDLE structHnd, CORINFO_FPSTRUCT_LOWERING* pLowering); void recGetRelocTypeHint(void* target, WORD result); void dmpGetRelocTypeHint(DWORDLONG key, DWORD value); @@ -1161,7 +1157,7 @@ enum mcPackets Packet_GetAssemblyName = 191, Packet_IsIntrinsic = 192, Packet_UpdateEntryPointForTailCall = 193, - Packet_GetLoongArch64PassStructInRegisterFlags = 194, + //Packet_GetLoongArch64PassStructInRegisterFlags = 194, Packet_GetExactClasses = 195, Packet_GetRuntimeTypePointer = 196, Packet_PrintObjectDescription = 197, @@ -1177,7 +1173,7 @@ enum mcPackets Packet_GetThreadLocalFieldInfo = 207, Packet_GetThreadLocalStaticBlocksInfo = 208, Packet_GetThreadLocalStaticInfo_NativeAOT = 209, - Packet_GetRISCV64PassStructInRegisterFlags = 210, + //Packet_GetRISCV64PassStructInRegisterFlags = 210, Packet_GetObjectContent = 211, Packet_GetTypeLayout = 212, Packet_HaveSameMethodDefinition = 213, @@ -1190,6 +1186,7 @@ enum mcPackets Packet_IsGenericType = 220, Packet_GetTypeForBoxOnStack = 221, Packet_GetTypeDefinition = 222, + Packet_GetFpStructLowering = 223, }; void SetDebugDumpVariables(); diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp index 24495dbaa1734..257d35f3b3faa 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -1430,20 +1430,11 @@ void interceptor_ICJI::getSwiftLowering(CORINFO_CLASS_HANDLE structHnd, CORINFO_ mc->recGetSwiftLowering(structHnd, pLowering); } -uint32_t interceptor_ICJI::getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd) +void interceptor_ICJI::getFpStructLowering(CORINFO_CLASS_HANDLE structHnd, CORINFO_FPSTRUCT_LOWERING* pLowering) { - mc->cr->AddCall("getLoongArch64PassStructInRegisterFlags"); - uint32_t temp = original_ICorJitInfo->getLoongArch64PassStructInRegisterFlags(structHnd); - mc->recGetLoongArch64PassStructInRegisterFlags(structHnd, temp); - return temp; -} - -uint32_t interceptor_ICJI::getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd) -{ - mc->cr->AddCall("getRISCV64PassStructInRegisterFlags"); - uint32_t temp = original_ICorJitInfo->getRISCV64PassStructInRegisterFlags(structHnd); - mc->recGetRISCV64PassStructInRegisterFlags(structHnd, temp); - return temp; + mc->cr->AddCall("getFpStructLowering"); + original_ICorJitInfo->getFpStructLowering(structHnd, pLowering); + mc->recGetFpStructLowering(structHnd, pLowering); } // Stuff on ICorDynamicInfo diff --git a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp index 332c9d13d0a63..8ee5f6783f937 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp @@ -1015,18 +1015,12 @@ void interceptor_ICJI::getSwiftLowering( original_ICorJitInfo->getSwiftLowering(structHnd, pLowering); } -uint32_t interceptor_ICJI::getLoongArch64PassStructInRegisterFlags( - CORINFO_CLASS_HANDLE structHnd) -{ - mcs->AddCall("getLoongArch64PassStructInRegisterFlags"); - return original_ICorJitInfo->getLoongArch64PassStructInRegisterFlags(structHnd); -} - -uint32_t interceptor_ICJI::getRISCV64PassStructInRegisterFlags( - CORINFO_CLASS_HANDLE structHnd) +void interceptor_ICJI::getFpStructLowering( + CORINFO_CLASS_HANDLE structHnd, + CORINFO_FPSTRUCT_LOWERING* pLowering) { - mcs->AddCall("getRISCV64PassStructInRegisterFlags"); - return original_ICorJitInfo->getRISCV64PassStructInRegisterFlags(structHnd); + mcs->AddCall("getFpStructLowering"); + original_ICorJitInfo->getFpStructLowering(structHnd, pLowering); } uint32_t interceptor_ICJI::getThreadTLSIndex( diff --git a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp index 989c587d47d89..adb90f842194a 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp @@ -889,16 +889,11 @@ void interceptor_ICJI::getSwiftLowering( original_ICorJitInfo->getSwiftLowering(structHnd, pLowering); } -uint32_t interceptor_ICJI::getLoongArch64PassStructInRegisterFlags( - CORINFO_CLASS_HANDLE structHnd) -{ - return original_ICorJitInfo->getLoongArch64PassStructInRegisterFlags(structHnd); -} - -uint32_t interceptor_ICJI::getRISCV64PassStructInRegisterFlags( - CORINFO_CLASS_HANDLE structHnd) +void interceptor_ICJI::getFpStructLowering( + CORINFO_CLASS_HANDLE structHnd, + CORINFO_FPSTRUCT_LOWERING* pLowering) { - return original_ICorJitInfo->getRISCV64PassStructInRegisterFlags(structHnd); + original_ICorJitInfo->getFpStructLowering(structHnd, pLowering); } uint32_t interceptor_ICJI::getThreadTLSIndex( diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index 370d99ecad912..4180115db640e 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -1258,16 +1258,10 @@ void MyICJI::getSwiftLowering(CORINFO_CLASS_HANDLE structHnd, CORINFO_SWIFT_LOWE jitInstance->mc->repGetSwiftLowering(structHnd, pLowering); } -uint32_t MyICJI::getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd) +void MyICJI::getFpStructLowering(CORINFO_CLASS_HANDLE structHnd, CORINFO_FPSTRUCT_LOWERING* pLowering) { - jitInstance->mc->cr->AddCall("getLoongArch64PassStructInRegisterFlags"); - return jitInstance->mc->repGetLoongArch64PassStructInRegisterFlags(structHnd); -} - -uint32_t MyICJI::getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd) -{ - jitInstance->mc->cr->AddCall("getRISCV64PassStructInRegisterFlags"); - return jitInstance->mc->repGetRISCV64PassStructInRegisterFlags(structHnd); + jitInstance->mc->cr->AddCall("getFpStructLowering"); + jitInstance->mc->repGetFpStructLowering(structHnd, pLowering); } // Stuff on ICorDynamicInfo diff --git a/src/coreclr/vm/argdestination.h b/src/coreclr/vm/argdestination.h index 15f3e120ca1d3..eef0a531e0a8c 100644 --- a/src/coreclr/vm/argdestination.h +++ b/src/coreclr/vm/argdestination.h @@ -110,20 +110,23 @@ class ArgDestination int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_argLocDescForStructInRegs->m_idxFloatReg * 8; - if (m_argLocDescForStructInRegs->m_structFields == STRUCT_FLOAT_FIELD_ONLY_TWO) + static const FpStruct::Flags twoFloats = FpStruct::Flags(FpStruct::BothFloat + | (2 << FpStruct::PosSizeShift1st) + | (2 << FpStruct::PosSizeShift2nd)); + if (m_argLocDescForStructInRegs->m_structFields.flags == twoFloats) { // struct with two floats. _ASSERTE(m_argLocDescForStructInRegs->m_cFloatReg == 2); _ASSERTE(m_argLocDescForStructInRegs->m_cGenReg == 0); *(INT64*)((char*)m_base + argOfs) = NanBox | *(INT32*)src; *(INT64*)((char*)m_base + argOfs + 8) = NanBox | *((INT32*)src + 1); } - else if ((m_argLocDescForStructInRegs->m_structFields & STRUCT_FLOAT_FIELD_FIRST) != 0) + else if ((m_argLocDescForStructInRegs->m_structFields.flags & FpStruct::FloatInt) != 0) { // the first field is float or double. _ASSERTE(m_argLocDescForStructInRegs->m_cFloatReg == 1); _ASSERTE(m_argLocDescForStructInRegs->m_cGenReg == 1); - _ASSERTE((m_argLocDescForStructInRegs->m_structFields & STRUCT_FLOAT_FIELD_SECOND) == 0);//the second field is integer. + _ASSERTE((m_argLocDescForStructInRegs->m_structFields.flags & FpStruct::IntFloat) == 0);//the second field is integer. - if ((m_argLocDescForStructInRegs->m_structFields & STRUCT_FIRST_FIELD_SIZE_IS8) == 0) + if (m_argLocDescForStructInRegs->m_structFields.SizeShift1st() == 3) { *(INT64*)((char*)m_base + argOfs) = NanBox | *(INT32*)src; // the first field is float } @@ -133,7 +136,8 @@ class ArgDestination } argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_argLocDescForStructInRegs->m_idxGenReg * 8; - if ((m_argLocDescForStructInRegs->m_structFields & STRUCT_HAS_8BYTES_FIELDS_MASK) != 0) + if (m_argLocDescForStructInRegs->m_structFields.SizeShift1st() == 3 || + m_argLocDescForStructInRegs->m_structFields.SizeShift2nd() == 3) { *(UINT64*)((char*)m_base + argOfs) = *((UINT64*)src + 1); } @@ -142,15 +146,16 @@ class ArgDestination *(INT64*)((char*)m_base + argOfs) = *((INT32*)src + 1); // the second field is int32. } } - else if ((m_argLocDescForStructInRegs->m_structFields & STRUCT_FLOAT_FIELD_SECOND) != 0) + else if ((m_argLocDescForStructInRegs->m_structFields.flags & FpStruct::IntFloat) != 0) { // the second field is float or double. _ASSERTE(m_argLocDescForStructInRegs->m_cFloatReg == 1); _ASSERTE(m_argLocDescForStructInRegs->m_cGenReg == 1); - _ASSERTE((m_argLocDescForStructInRegs->m_structFields & STRUCT_FLOAT_FIELD_FIRST) == 0);//the first field is integer. + _ASSERTE((m_argLocDescForStructInRegs->m_structFields.flags & FpStruct::FloatInt) == 0);//the first field is integer. // destOffset - nonzero when copying values into Nullable, it is the offset of the T value inside of the Nullable. // here the first field maybe Nullable. - if ((m_argLocDescForStructInRegs->m_structFields & STRUCT_HAS_8BYTES_FIELDS_MASK) == 0) + if (m_argLocDescForStructInRegs->m_structFields.SizeShift1st() < 3 && + m_argLocDescForStructInRegs->m_structFields.SizeShift2nd() < 3) { // the second field is float. *(INT64*)((char*)m_base + argOfs) = NanBox | (destOffset == 0 ? *((INT32*)src + 1) : *(INT32*)src); diff --git a/src/coreclr/vm/callhelpers.cpp b/src/coreclr/vm/callhelpers.cpp index 25388ed164046..277073dae856c 100644 --- a/src/coreclr/vm/callhelpers.cpp +++ b/src/coreclr/vm/callhelpers.cpp @@ -539,7 +539,13 @@ void MethodDescCallSite::CallTargetWorker(const ARG_SLOT *pArguments, ARG_SLOT * #ifdef CALLDESCR_REGTYPEMAP callDescrData.dwRegTypeMap = dwRegTypeMap; #endif +#if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) + // Temporary conversion to old flags, CallDescrWorker needs to be overhauled anyway + // to work with arbitrary field offsets and sizes, and support struct size > 16 on RISC-V. + callDescrData.fpReturnSize = FpStructInRegistersInfo{FpStruct::Flags(fpReturnSize)}.ToOldFlags(); +#else callDescrData.fpReturnSize = fpReturnSize; +#endif callDescrData.pTarget = m_pCallTarget; #ifdef FEATURE_INTERPRETER diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h index 59a3e0eb6c869..e2bf88af122f8 100644 --- a/src/coreclr/vm/callingconvention.h +++ b/src/coreclr/vm/callingconvention.h @@ -42,7 +42,7 @@ struct ArgLocDesc int m_byteStackIndex; // Stack offset in bytes (or -1) int m_byteStackSize; // Stack size in bytes #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - int m_structFields; // Struct field info when using Float-register except two-doubles case. + FpStructInRegistersInfo m_structFields; // Struct field info when using floating-point register(s) #endif #if defined(UNIX_AMD64_ABI) @@ -97,7 +97,7 @@ struct ArgLocDesc m_hfaFieldSize = 0; #endif // defined(TARGET_ARM64) #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - m_structFields = STRUCT_NO_FLOAT_FIELD; + m_structFields = {}; #endif #if defined(UNIX_AMD64_ABI) m_eeClass = NULL; @@ -1670,18 +1670,14 @@ int ArgIteratorTemplate::GetNextOffset() #elif defined(TARGET_LOONGARCH64) int cFPRegs = 0; - int flags = 0; + FpStructInRegistersInfo info = {}; switch (argType) { case ELEMENT_TYPE_R4: - // 32-bit floating point argument. - cFPRegs = 1; - break; - case ELEMENT_TYPE_R8: - // 64-bit floating point argument. + // Floating point argument cFPRegs = 1; break; @@ -1697,10 +1693,10 @@ int ArgIteratorTemplate::GetNextOffset() } else { - flags = MethodTable::GetLoongArch64PassStructInRegisterFlags(thValueType); - if (flags & STRUCT_HAS_FLOAT_FIELDS_MASK) + info = MethodTable::GetFpStructInRegistersInfo(thValueType); + if (info.flags != FpStruct::UseIntCallConv) { - cFPRegs = (flags & STRUCT_FLOAT_FIELD_ONLY_TWO) ? 2 : 1; + cFPRegs = (info.flags & FpStruct::BothFloat) ? 2 : 1; } } @@ -1717,12 +1713,12 @@ int ArgIteratorTemplate::GetNextOffset() if (cFPRegs > 0 && !this->IsVarArg()) { - if (flags & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND)) + if (info.flags & (FpStruct::FloatInt | FpStruct::IntFloat)) { assert(cFPRegs == 1); - assert((STRUCT_FLOAT_FIELD_FIRST == (flags & STRUCT_HAS_FLOAT_FIELDS_MASK)) || (STRUCT_FLOAT_FIELD_SECOND == (flags & STRUCT_HAS_FLOAT_FIELDS_MASK))); + assert((info.flags & (FpStruct::OnlyOne | FpStruct::BothFloat)) == 0); - if ((1 + m_idxFPReg <= NUM_ARGUMENT_REGISTERS) && (m_idxGenReg + 1 <= NUM_ARGUMENT_REGISTERS)) + if ((1 + m_idxFPReg <= NUM_ARGUMENT_REGISTERS) && (1 + m_idxGenReg <= NUM_ARGUMENT_REGISTERS)) { int argOfs = 0; m_argLocDescForStructInRegs.Init(); @@ -1730,9 +1726,9 @@ int ArgIteratorTemplate::GetNextOffset() m_argLocDescForStructInRegs.m_cFloatReg = 1; m_argLocDescForStructInRegs.m_idxGenReg = m_idxGenReg; m_argLocDescForStructInRegs.m_cGenReg = 1; - m_argLocDescForStructInRegs.m_structFields = flags; + m_argLocDescForStructInRegs.m_structFields = info; - if (flags & STRUCT_FLOAT_FIELD_SECOND) + if (info.flags & FpStruct::IntFloat) { argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8; } @@ -1752,7 +1748,10 @@ int ArgIteratorTemplate::GetNextOffset() else if (cFPRegs + m_idxFPReg <= NUM_ARGUMENT_REGISTERS) { int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8; - if (flags == STRUCT_FLOAT_FIELD_ONLY_TWO) // struct with two float-fields. + static const FpStruct::Flags twoFloats = FpStruct::Flags(FpStruct::BothFloat + | (2 << FpStruct::PosSizeShift1st) + | (2 << FpStruct::PosSizeShift2nd)); + if (info.flags == twoFloats) // struct with two float-fields. { m_argLocDescForStructInRegs.Init(); m_hasArgLocDescForStructInRegs = true; @@ -1760,7 +1759,7 @@ int ArgIteratorTemplate::GetNextOffset() assert(cFPRegs == 2); m_argLocDescForStructInRegs.m_cFloatReg = 2; assert(argSize == 8); - m_argLocDescForStructInRegs.m_structFields = STRUCT_FLOAT_FIELD_ONLY_TWO; + m_argLocDescForStructInRegs.m_structFields = info; } m_idxFPReg += cFPRegs; return argOfs; @@ -1790,20 +1789,15 @@ int ArgIteratorTemplate::GetNextOffset() return argOfs; #elif defined(TARGET_RISCV64) - + assert(!this->IsVarArg()); // Varargs on RISC-V not supported yet int cFPRegs = 0; - int flags = 0; + FpStructInRegistersInfo info = {}; switch (argType) { - case ELEMENT_TYPE_R4: - // 32-bit floating point argument. - cFPRegs = 1; - break; - case ELEMENT_TYPE_R8: - // 64-bit floating point argument. + // Floating point argument cFPRegs = 1; break; @@ -1819,10 +1813,10 @@ int ArgIteratorTemplate::GetNextOffset() } else { - flags = MethodTable::GetRiscV64PassStructInRegisterFlags(thValueType); - if (flags & STRUCT_HAS_FLOAT_FIELDS_MASK) + info = MethodTable::GetFpStructInRegistersInfo(thValueType); + if (info.flags != FpStruct::UseIntCallConv) { - cFPRegs = (flags & STRUCT_FLOAT_FIELD_ONLY_TWO) ? 2 : 1; + cFPRegs = (info.flags & FpStruct::BothFloat) ? 2 : 1; } } @@ -1839,22 +1833,22 @@ int ArgIteratorTemplate::GetNextOffset() if (cFPRegs > 0 && !this->IsVarArg()) { - if (flags & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND)) + if (info.flags & (FpStruct::FloatInt | FpStruct::IntFloat)) { assert(cFPRegs == 1); - assert((STRUCT_FLOAT_FIELD_FIRST == (flags & STRUCT_HAS_FLOAT_FIELDS_MASK)) || (STRUCT_FLOAT_FIELD_SECOND == (flags & STRUCT_HAS_FLOAT_FIELDS_MASK))); + assert((info.flags & (FpStruct::OnlyOne | FpStruct::BothFloat)) == 0); - if ((1 + m_idxFPReg <= NUM_ARGUMENT_REGISTERS) && (m_idxGenReg + 1 <= NUM_ARGUMENT_REGISTERS)) + if ((1 + m_idxFPReg <= NUM_ARGUMENT_REGISTERS) && (1 + m_idxGenReg <= NUM_ARGUMENT_REGISTERS)) { m_argLocDescForStructInRegs.Init(); m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg; m_argLocDescForStructInRegs.m_cFloatReg = 1; - int argOfs = (flags & STRUCT_FLOAT_FIELD_SECOND) + int argOfs = (info.flags & FpStruct::IntFloat) ? TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8 : TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8; m_idxFPReg += 1; - m_argLocDescForStructInRegs.m_structFields = flags; + m_argLocDescForStructInRegs.m_structFields = info; m_argLocDescForStructInRegs.m_idxGenReg = m_idxGenReg; m_argLocDescForStructInRegs.m_cGenReg = 1; @@ -1868,7 +1862,10 @@ int ArgIteratorTemplate::GetNextOffset() else if (cFPRegs + m_idxFPReg <= NUM_ARGUMENT_REGISTERS) { int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8; - if (flags == STRUCT_FLOAT_FIELD_ONLY_TWO) // struct with two float-fields. + static const FpStruct::Flags twoFloats = FpStruct::Flags(FpStruct::BothFloat + | (2 << FpStruct::PosSizeShift1st) + | (2 << FpStruct::PosSizeShift2nd)); + if (info.flags == twoFloats) // struct with two float-fields. { m_argLocDescForStructInRegs.Init(); m_hasArgLocDescForStructInRegs = true; @@ -1876,7 +1873,7 @@ int ArgIteratorTemplate::GetNextOffset() assert(cFPRegs == 2); m_argLocDescForStructInRegs.m_cFloatReg = 2; assert(argSize == 8); - m_argLocDescForStructInRegs.m_structFields = STRUCT_FLOAT_FIELD_ONLY_TWO; + m_argLocDescForStructInRegs.m_structFields = info; } m_idxFPReg += cFPRegs; return argOfs; @@ -1941,7 +1938,7 @@ void ArgIteratorTemplate::ComputeReturnFlags() case ELEMENT_TYPE_R4: #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - flags |= STRUCT_FLOAT_FIELD_ONLY_ONE << RETURN_FP_SIZE_SHIFT; + flags |= (FpStruct::OnlyOne | (2 << FpStruct::PosSizeShift1st)) << RETURN_FP_SIZE_SHIFT; #else #ifndef ARM_SOFTFP flags |= sizeof(float) << RETURN_FP_SIZE_SHIFT; @@ -1951,7 +1948,7 @@ void ArgIteratorTemplate::ComputeReturnFlags() case ELEMENT_TYPE_R8: #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - flags |= (STRUCT_FLOAT_FIELD_ONLY_ONE | STRUCT_FIRST_FIELD_SIZE_IS8) << RETURN_FP_SIZE_SHIFT; + flags |= (FpStruct::OnlyOne | (3 << FpStruct::PosSizeShift1st)) << RETURN_FP_SIZE_SHIFT; #else #ifndef ARM_SOFTFP flags |= sizeof(double) << RETURN_FP_SIZE_SHIFT; @@ -2024,14 +2021,16 @@ void ArgIteratorTemplate::ComputeReturnFlags() if (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE) { assert(!thValueType.IsTypeDesc()); - flags = (MethodTable::GetLoongArch64PassStructInRegisterFlags(thValueType) & 0xff) << RETURN_FP_SIZE_SHIFT; + FpStructInRegistersInfo info = MethodTable::GetFpStructInRegistersInfo(thValueType); + flags |= info.flags << RETURN_FP_SIZE_SHIFT; break; } #elif defined(TARGET_RISCV64) if (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE) { assert(!thValueType.IsTypeDesc()); - flags = (MethodTable::GetRiscV64PassStructInRegisterFlags(thValueType) & 0xff) << RETURN_FP_SIZE_SHIFT; + FpStructInRegistersInfo info = MethodTable::GetFpStructInRegistersInfo(thValueType); + flags |= info.flags << RETURN_FP_SIZE_SHIFT; break; } #else diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index b9ff707e60947..86b5e68f06704 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -172,7 +172,7 @@ class ShuffleIterator if (m_currentFloatRegIndex < m_argLocDesc->m_cFloatReg) { #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - if ((m_argLocDesc->m_structFields & STRUCT_FLOAT_FIELD_SECOND) && (m_currentGenRegIndex < m_argLocDesc->m_cGenReg)) + if ((m_argLocDesc->m_structFields.flags & FpStruct::IntFloat) && (m_currentGenRegIndex < m_argLocDesc->m_cGenReg)) { // the first field is integer so just skip this. } diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index a1609b03b0e86..8436a63c706e4 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -9502,13 +9502,13 @@ CorInfoTypeWithMod CEEInfo::getArgType ( return result; } -// Now the implementation is only focused on the float fields info, +// Now the implementation is only focused on the float and int fields info, // while a struct-arg has no more than two fields and total size is no larger than two-pointer-size. // These depends on the platform's ABI rules. // -// The returned value's encoding details how a struct argument uses float registers: -// see the enum `StructFloatFieldInfoFlags`. -uint32_t CEEInfo::getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE cls) +// The returned value's encoding details how a struct argument uses float and int registers: +// see the struct `CORINFO_FPSTRUCT_LOWERING`. +void CEEInfo::getFpStructLowering(CORINFO_CLASS_HANDLE structHnd, CORINFO_FPSTRUCT_LOWERING* pLowering) { CONTRACTL { NOTHROW; @@ -9516,38 +9516,43 @@ uint32_t CEEInfo::getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE c MODE_PREEMPTIVE; } CONTRACTL_END; - JIT_TO_EE_TRANSITION_LEAF(); - - uint32_t size = STRUCT_NO_FLOAT_FIELD; - -#if defined(TARGET_LOONGARCH64) - size = (uint32_t)MethodTable::GetLoongArch64PassStructInRegisterFlags(TypeHandle(cls)); -#endif - - EE_TO_JIT_TRANSITION_LEAF(); - - return size; -} + JIT_TO_EE_TRANSITION(); -uint32_t CEEInfo::getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE cls) -{ - CONTRACTL { - NOTHROW; - GC_NOTRIGGER; - MODE_PREEMPTIVE; - } CONTRACTL_END; +#if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) + FpStructInRegistersInfo info = MethodTable::GetFpStructInRegistersInfo(TypeHandle(structHnd)); + if (info.flags != FpStruct::UseIntCallConv) + { + pLowering->byIntegerCallConv = false; + pLowering->offsets[0] = info.offset1st; + pLowering->offsets[1] = info.offset2nd; + pLowering->numLoweredElements = (info.flags & FpStruct::OnlyOne) ? 1ul : 2ul; - JIT_TO_EE_TRANSITION_LEAF(); + if (info.flags & (FpStruct::BothFloat | FpStruct::FloatInt | FpStruct::OnlyOne)) + pLowering->loweredElements[0] = (info.SizeShift1st() == 3) ? CORINFO_TYPE_DOUBLE : CORINFO_TYPE_FLOAT; - uint32_t size = STRUCT_NO_FLOAT_FIELD; + if (info.flags & (FpStruct::BothFloat | FpStruct::IntFloat)) + pLowering->loweredElements[1] = (info.SizeShift2nd() == 3) ? CORINFO_TYPE_DOUBLE : CORINFO_TYPE_FLOAT; -#if defined(TARGET_RISCV64) - size = (uint32_t)MethodTable::GetRiscV64PassStructInRegisterFlags(TypeHandle(cls)); -#endif // TARGET_RISCV64 + if (info.flags & (FpStruct::FloatInt | FpStruct::IntFloat)) + { + size_t index = ((info.flags & FpStruct::FloatInt) != 0) ? 1 : 0; + unsigned sizeShift = (index == 0) ? info.SizeShift1st() : info.SizeShift2nd(); + pLowering->loweredElements[index] = (CorInfoType)(CORINFO_TYPE_BYTE + sizeShift * 2); - EE_TO_JIT_TRANSITION_LEAF(); + // unittests + static_assert(CORINFO_TYPE_BYTE + 0 * 2 == CORINFO_TYPE_BYTE, ""); + static_assert(CORINFO_TYPE_BYTE + 1 * 2 == CORINFO_TYPE_SHORT, ""); + static_assert(CORINFO_TYPE_BYTE + 2 * 2 == CORINFO_TYPE_INT, ""); + static_assert(CORINFO_TYPE_BYTE + 3 * 2 == CORINFO_TYPE_LONG, ""); + } + } + else + { + pLowering->byIntegerCallConv = true; + } +#endif // TARGET_RISCV64 || TARGET_LOONGARCH64 - return size; + EE_TO_JIT_TRANSITION(); } /*********************************************************************/ diff --git a/src/coreclr/vm/loongarch64/profiler.cpp b/src/coreclr/vm/loongarch64/profiler.cpp index 0d056d5797c8d..8da4c3d5137c0 100644 --- a/src/coreclr/vm/loongarch64/profiler.cpp +++ b/src/coreclr/vm/loongarch64/profiler.cpp @@ -134,14 +134,14 @@ LPVOID ProfileArgIterator::GetNextArgAddr() UINT64* dst = (UINT64*)&pData->buffer[bufferPos]; m_bufferPos += 16; - if (pArgLocDesc->m_structFields & STRUCT_FLOAT_FIELD_FIRST) + if (pArgLocDesc->m_structFields.flags & FpStruct::FloatInt) { *dst++ = *(UINT64*)pArg; *dst = pData->argumentRegisters.a[pArgLocDesc->m_idxGenReg]; } else { - _ASSERTE(pArgLocDesc->m_structFields & STRUCT_FLOAT_FIELD_SECOND); + _ASSERTE(pArgLocDesc->m_structFields.flags & FpStruct::IntFloat); *dst++ = pData->argumentRegisters.a[pArgLocDesc->m_idxGenReg]; *dst = *(UINT64*)pArg; } @@ -163,8 +163,8 @@ LPVOID ProfileArgIterator::GetNextArgAddr() { if (pArgLocDesc->m_cFloatReg == 1) { - _ASSERTE(!(pArgLocDesc->m_structFields & STRUCT_FLOAT_FIELD_FIRST)); - _ASSERTE(pArgLocDesc->m_structFields & STRUCT_FLOAT_FIELD_SECOND); + _ASSERTE(!(pArgLocDesc->m_structFields.flags & FpStruct::FloatInt)); + _ASSERTE(pArgLocDesc->m_structFields.flags & FpStruct::IntFloat); UINT32 bufferPos = m_bufferPos; UINT64* dst = (UINT64*)&pData->buffer[bufferPos]; @@ -258,11 +258,11 @@ LPVOID ProfileArgIterator::GetReturnBufferAddr(void) return (LPVOID)pData->argumentRegisters.a[0]; } - UINT fpReturnSize = m_argIterator.GetFPReturnSize(); + FpStruct::Flags fpReturnSize = FpStruct::Flags(m_argIterator.GetFPReturnSize()); if (fpReturnSize != 0) { - if ((fpReturnSize & (UINT)STRUCT_FLOAT_FIELD_ONLY_ONE) || (fpReturnSize & (UINT)STRUCT_FLOAT_FIELD_ONLY_TWO)) + if (fpReturnSize & (FpStruct::OnlyOne | FpStruct::BothFloat)) { return &pData->floatArgumentRegisters.f[0]; } @@ -275,14 +275,14 @@ LPVOID ProfileArgIterator::GetReturnBufferAddr(void) // using the tail 16 bytes for return structure. UINT64* dst = (UINT64*)&pData->buffer[sizeof(pData->buffer) - 16]; - if (fpReturnSize & (UINT)STRUCT_FLOAT_FIELD_FIRST) + if (fpReturnSize & FpStruct::FloatInt) { *(double*)dst = pData->floatArgumentRegisters.f[0]; *(dst + 1) = pData->argumentRegisters.a[0]; } else { - _ASSERTE(fpReturnSize & (UINT)STRUCT_FLOAT_FIELD_SECOND); + _ASSERTE(fpReturnSize & FpStruct::IntFloat); *dst = pData->argumentRegisters.a[0]; *(double*)(dst + 1) = pData->floatArgumentRegisters.f[0]; } diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index a8f6bd04b8dbb..fba8c85980fc8 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -2734,35 +2734,89 @@ void MethodTable::AssignClassifiedEightByteTypes(SystemVStructRegisterPassingHe #endif // defined(UNIX_AMD64_ABI_ITF) #if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) -static bool HandleInlineArray(int elementTypeIndex, int nElements, StructFloatFieldInfoFlags types[2], int& typeIndex) +static void SetFpStructInRegistersInfoField(FpStructInRegistersInfo& info, int index, + bool isFloating, unsigned size, uint32_t offset) +{ + assert(index < 2); + if (isFloating) + assert(size == sizeof(float) || size == sizeof(double)); + + assert(size >= 1 && size <= 8); + assert((size & (size - 1)) == 0); // size needs to be a power of 2 + static const int sizeShiftLUT = (0 << (1*2)) | (1 << (2*2)) | (2 << (4*2)) | (3 << (8*2)); + int sizeShift = (sizeShiftLUT >> (size * 2)) & 0b11; + + using namespace FpStruct; + // Use FloatInt and IntFloat as marker flags for 1st and 2nd field respectively being floating. + // Fix to real flags (with OnlyOne and BothFloat) after flattening is complete. + static_assert(PosIntFloat == PosFloatInt + 1, "FloatInt and IntFloat need to be adjacent"); + static_assert(PosSizeShift2nd == PosSizeShift1st + 2, "SizeShift1st and 2nd need to be adjacent"); + int floatFlag = isFloating << (PosFloatInt + index); + int sizeShiftMask = sizeShift << (PosSizeShift1st + 2 * index); + + info.flags = FpStruct::Flags(info.flags | floatFlag | sizeShiftMask); + (index == 0 ? info.offset1st : info.offset2nd) = offset; +} + +static bool HandleInlineArray(int elementTypeIndex, int nElements, FpStructInRegistersInfo& info, int& typeIndex + DEBUG_ARG(int nestingLevel)) { int nFlattenedFieldsPerElement = typeIndex - elementTypeIndex; if (nFlattenedFieldsPerElement == 0) + { + assert(nElements == 1); // HasImpliedRepeatedFields must have returned a false positive + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s * ignoring empty struct\n", + nestingLevel * 4, "")); return true; + } assert(nFlattenedFieldsPerElement == 1 || nFlattenedFieldsPerElement == 2); if (nElements > 2) + { + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s * array has too many elements: %i\n", + nestingLevel * 4, "", nElements)); return false; + } if (nElements == 2) { if (typeIndex + nFlattenedFieldsPerElement > 2) + { + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s * array has too many fields per element: %i, fields already found: %i\n", + nestingLevel * 4, "", nFlattenedFieldsPerElement, typeIndex)); return false; + } assert(elementTypeIndex == 0); assert(typeIndex == 1); - types[typeIndex] = types[elementTypeIndex]; // duplicate the array element type + + // Duplicate the array element info + static_assert(FpStruct::IntFloat == (FpStruct::FloatInt << 1), + "FloatInt and IntFloat need to be adjacent"); + static_assert(FpStruct::SizeShift2ndMask == (FpStruct::SizeShift1stMask << 2), + "SizeShift1st and 2nd need to be adjacent"); + // Take the 1st field info and shift up to the 2nd field's positions + int floatFlag = (info.flags & FpStruct::FloatInt) << 1; + int sizeShiftMask = (info.flags & FpStruct::SizeShift1stMask) << 2; + info.flags = FpStruct::Flags(info.flags | floatFlag | sizeShiftMask); // merge with 1st field + info.offset2nd = info.offset1st + info.Size1st(); // bump up the field offset + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s * duplicated array element type\n", + nestingLevel * 4, "")); } return true; } -static bool FlattenFieldTypes(TypeHandle th, StructFloatFieldInfoFlags types[2], int& typeIndex) +static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInfo& info, int& typeIndex + DEBUG_ARG(int nestingLevel)) { bool isManaged = !th.IsTypeDesc(); MethodTable* pMT = isManaged ? th.AsMethodTable() : th.AsNativeValueType(); int nFields = isManaged ? pMT->GetNumIntroducedInstanceFields() : pMT->GetNativeLayoutInfo()->GetNumFields(); + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s flattening %s (%s, %i fields)\n", + nestingLevel * 4, "", pMT->GetDebugClassName(), (isManaged ? "managed" : "native"), nFields)); + // TODO: templatize isManaged and use if constexpr for differences when we migrate to C++17 // because the logic for both branches is nearly the same. if (isManaged) @@ -2772,27 +2826,45 @@ static bool FlattenFieldTypes(TypeHandle th, StructFloatFieldInfoFlags types[2], for (int i = 0; i < nFields; ++i) { if (i > 0 && fields[i-1].GetOffset() + fields[i-1].GetSize() > fields[i].GetOffset()) - return false; // overlapping fields + { + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s " + " * fields %s [%i..%i) and %s [%i..%i) overlap, treat as union\n", + nestingLevel * 4, "", + fields[i-1].GetDebugName(), fields[i-1].GetOffset(), fields[i-1].GetOffset() + fields[i-1].GetSize(), + fields[i].GetDebugName(), fields[i].GetOffset(), fields[i].GetOffset() + fields[i].GetSize())); + return false; + } CorElementType type = fields[i].GetFieldType(); if (type == ELEMENT_TYPE_VALUETYPE) { MethodTable* nested = fields[i].GetApproxFieldTypeHandleThrowing().GetMethodTable(); - if (!FlattenFieldTypes(TypeHandle(nested), types, typeIndex)) + if (!FlattenFields(TypeHandle(nested), offset + fields[i].GetOffset(), info, typeIndex DEBUG_ARG(nestingLevel + 1))) return false; } else if (fields[i].GetSize() <= TARGET_POINTER_SIZE) { if (typeIndex >= 2) + { + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s * too many fields\n", + nestingLevel * 4, "")); return false; + } + + bool isFloating = CorTypeInfo::IsFloat_NoThrow(type); + SetFpStructInRegistersInfoField(info, typeIndex++, + isFloating, CorTypeInfo::Size_NoThrow(type), offset + fields[i].GetOffset()); - StructFloatFieldInfoFlags retType = StructFloatFieldInfoFlags( - (CorTypeInfo::IsFloat_NoThrow(type) ? STRUCT_FLOAT_FIELD_FIRST : 0) | - (CorTypeInfo::Size_NoThrow(type) == TARGET_POINTER_SIZE ? STRUCT_FIRST_FIELD_SIZE_IS8 : 0)); - types[typeIndex++] = retType; + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s * found field %s [%i..%i), type: %s\n", + nestingLevel * 4, "", fields[i].GetDebugName(), + fields[i].GetOffset(), fields[i].GetOffset() + fields[i].GetSize(), CorTypeInfo::GetName(type))); } else { + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s " + " * field %s, type: %s, is too big (%i bytes)\n", + nestingLevel * 4, "", fields[i].GetDebugName(), + CorTypeInfo::GetName(type), fields[i].GetSize())); return false; } } @@ -2801,7 +2873,7 @@ static bool FlattenFieldTypes(TypeHandle th, StructFloatFieldInfoFlags types[2], { assert(nFields == 1); int nElements = pMT->GetNumInstanceFieldBytes() / fields[0].GetSize(); - if (!HandleInlineArray(elementTypeIndex, nElements, types, typeIndex)) + if (!HandleInlineArray(elementTypeIndex, nElements, info, typeIndex DEBUG_ARG(nestingLevel + 1))) return false; } } @@ -2811,115 +2883,119 @@ static bool FlattenFieldTypes(TypeHandle th, StructFloatFieldInfoFlags types[2], for (int i = 0; i < nFields; ++i) { if (i > 0 && fields[i-1].GetExternalOffset() + fields[i-1].NativeSize() > fields[i].GetExternalOffset()) - return false; // overlapping fields + { + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s " + " * fields %s [%i..%i) and %s [%i..%i) overlap, treat as union\n", + nestingLevel * 4, "", + fields[i-1].GetFieldDesc()->GetDebugName(), fields[i-1].GetExternalOffset(), fields[i-1].GetExternalOffset() + fields[i-1].NativeSize(), + fields[i].GetFieldDesc()->GetDebugName(), fields[i].GetExternalOffset(), fields[i].GetExternalOffset() + fields[i].NativeSize())); + return false; + } + static const char* categoryNames[] = {"FLOAT", "NESTED", "INTEGER", "ILLEGAL"}; NativeFieldCategory category = fields[i].GetCategory(); if (category == NativeFieldCategory::NESTED) { int elementTypeIndex = typeIndex; MethodTable* nested = fields[i].GetNestedNativeMethodTable(); - if (!FlattenFieldTypes(TypeHandle(nested), types, typeIndex)) + if (!FlattenFields(TypeHandle(nested), offset + fields[i].GetExternalOffset(), info, typeIndex DEBUG_ARG(nestingLevel + 1))) return false; // In native layout fixed arrays are marked as NESTED just like structs int nElements = fields[i].GetNumElements(); - if (!HandleInlineArray(elementTypeIndex, nElements, types, typeIndex)) + if (!HandleInlineArray(elementTypeIndex, nElements, info, typeIndex DEBUG_ARG(nestingLevel + 1))) return false; } else if (fields[i].NativeSize() <= TARGET_POINTER_SIZE) { if (typeIndex >= 2) + { + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s * too many fields\n", + nestingLevel * 4, "")); return false; + } + + bool isFloating = (category == NativeFieldCategory::FLOAT); - StructFloatFieldInfoFlags type = StructFloatFieldInfoFlags( - (category == NativeFieldCategory::FLOAT ? STRUCT_FLOAT_FIELD_FIRST : 0) | - (fields[i].NativeSize() == TARGET_POINTER_SIZE ? STRUCT_FIRST_FIELD_SIZE_IS8 : 0)); - types[typeIndex++] = type; + SetFpStructInRegistersInfoField(info, typeIndex++, + isFloating, fields[i].NativeSize(), offset + fields[i].GetExternalOffset()); + + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s * found field %s [%i..%i), type: %s\n", + nestingLevel * 4, "", fields[i].GetFieldDesc()->GetDebugName(), + fields[i].GetExternalOffset(), fields[i].GetExternalOffset() + fields[i].NativeSize(), categoryNames[(int)category])); } else { + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s " + " * field %s, type: %s, is too big (%i bytes)\n", + nestingLevel * 4, "", fields[i].GetFieldDesc()->GetDebugName(), + categoryNames[(int)category], fields[i].NativeSize())); return false; } } } return true; } -#endif -#if defined(TARGET_LOONGARCH64) -int MethodTable::GetLoongArch64PassStructInRegisterFlags(TypeHandle th) +FpStructInRegistersInfo MethodTable::GetFpStructInRegistersInfo(TypeHandle th) { if (th.GetSize() > ENREGISTERED_PARAMTYPE_MAXSIZE) - return STRUCT_NO_FLOAT_FIELD; + { + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo: struct %s (%u bytes) is too big\n", + (!th.IsTypeDesc() ? th.AsMethodTable() : th.AsNativeValueType())->GetDebugClassName(), th.GetSize())); + return FpStructInRegistersInfo{}; + } - StructFloatFieldInfoFlags types[2] = {STRUCT_NO_FLOAT_FIELD, STRUCT_NO_FLOAT_FIELD}; + FpStructInRegistersInfo info = {}; int nFields = 0; - if (!FlattenFieldTypes(th, types, nFields) || nFields == 0) - return STRUCT_NO_FLOAT_FIELD; - - assert(nFields == 1 || nFields == 2); - - static_assert((STRUCT_FLOAT_FIELD_SECOND | STRUCT_SECOND_FIELD_SIZE_IS8) - == (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FIRST_FIELD_SIZE_IS8) << 1, - "SECOND flags need to be FIRST shifted by 1"); - int flags = types[0] | (types[1] << 1); + if (!FlattenFields(th, 0, info, nFields DEBUG_ARG(0))) + return FpStructInRegistersInfo{}; - static const int bothFloat = STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND; - if ((flags & bothFloat) == 0) - return STRUCT_NO_FLOAT_FIELD; - - if ((flags & bothFloat) == bothFloat) - { - assert(nFields == 2); - flags ^= (bothFloat | STRUCT_FLOAT_FIELD_ONLY_TWO); // replace bothFloat with ONLY_TWO - } - else if (nFields == 1) + using namespace FpStruct; + if ((info.flags & (FloatInt | IntFloat)) == 0) { - assert((flags & STRUCT_FLOAT_FIELD_FIRST) != 0); - flags ^= (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_ONLY_ONE); // replace FIRST with ONLY_ONE + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo: struct %s (%u bytes) has no floating fields\n", + (!th.IsTypeDesc() ? th.AsMethodTable() : th.AsNativeValueType())->GetDebugClassName(), th.GetSize())); + return FpStructInRegistersInfo{}; } - return flags; -} -#endif - -#if defined(TARGET_RISCV64) -int MethodTable::GetRiscV64PassStructInRegisterFlags(TypeHandle th) -{ - if (th.GetSize() > ENREGISTERED_PARAMTYPE_MAXSIZE) - return STRUCT_NO_FLOAT_FIELD; - - StructFloatFieldInfoFlags types[2] = {STRUCT_NO_FLOAT_FIELD, STRUCT_NO_FLOAT_FIELD}; - int nFields = 0; - if (!FlattenFieldTypes(th, types, nFields) || nFields == 0) - return STRUCT_NO_FLOAT_FIELD; - assert(nFields == 1 || nFields == 2); - static_assert((STRUCT_FLOAT_FIELD_SECOND | STRUCT_SECOND_FIELD_SIZE_IS8) - == (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FIRST_FIELD_SIZE_IS8) << 1, - "SECOND flags need to be FIRST shifted by 1"); - int flags = types[0] | (types[1] << 1); - - static const int bothFloat = STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND; - if ((flags & bothFloat) == 0) - return STRUCT_NO_FLOAT_FIELD; - - if ((flags & bothFloat) == bothFloat) + if ((info.flags & (FloatInt | IntFloat)) == (FloatInt | IntFloat)) { assert(nFields == 2); - flags ^= (bothFloat | STRUCT_FLOAT_FIELD_ONLY_TWO); // replace bothFloat with ONLY_TWO + info.flags = FpStruct::Flags(info.flags ^ (FloatInt | IntFloat | BothFloat)); // replace (FloatInt | IntFloat) with BothFloat } else if (nFields == 1) { - assert((flags & STRUCT_FLOAT_FIELD_FIRST) != 0); - flags ^= (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_ONLY_ONE); // replace FIRST with ONLY_ONE - } - - return flags; -} -#endif + assert((info.flags & FloatInt) != 0); + assert((info.flags & (IntFloat | SizeShift2ndMask)) == 0); + assert(info.offset2nd == 0); + info.flags = FpStruct::Flags(info.flags ^ (FloatInt | OnlyOne)); // replace FloatInt with OnlyOne + } + assert(nFields == ((info.flags & OnlyOne) != 0 ? 1 : 2)); + int floatFlags = info.flags & (OnlyOne | BothFloat | FloatInt | IntFloat); + assert(floatFlags != 0); + assert((floatFlags & (floatFlags - 1)) == 0); // there can be only one of (OnlyOne | BothFloat | FloatInt | IntFloat) + if (nFields == 2) + { + unsigned end1st = info.offset1st + info.Size1st(); + unsigned end2nd = info.offset2nd + info.Size2nd(); + assert(end1st <= info.offset2nd || end2nd <= info.offset1st); // fields must not overlap + } + assert(info.offset1st + info.Size1st() <= th.GetSize()); + assert(info.offset2nd + info.Size2nd() <= th.GetSize()); + + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo: " + "struct %s (%u bytes) can be passed with floating-point calling convention, flags=%#03x; " + "%s, sizes={%u, %u}, offsets={%u, %u}\n", + (!th.IsTypeDesc() ? th.AsMethodTable() : th.AsNativeValueType())->GetDebugClassName(), th.GetSize(), info.flags, + info.FlagName(), info.Size1st(), info.Size2nd(), info.offset1st, info.offset2nd + )); + return info; +} +#endif // defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) #if !defined(DACCESS_COMPILE) namespace diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index b3177ef4c989c..48d27b3e54430 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -801,6 +801,102 @@ typedef DPTR(SystemVStructRegisterPassingHelper) SystemVStructRegisterPassingHel #endif // UNIX_AMD64_ABI_ITF +#if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) +// 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)); + } +}; +#endif // defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) + //=============================================================================================== // // GC data appears before the beginning of the MethodTable @@ -1058,10 +1154,8 @@ class MethodTable // during object construction. void CheckRunClassInitAsIfConstructingThrowing(); -#if defined(TARGET_LOONGARCH64) - static int GetLoongArch64PassStructInRegisterFlags(TypeHandle th); -#elif defined(TARGET_RISCV64) - static int GetRiscV64PassStructInRegisterFlags(TypeHandle th); +#if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) + static FpStructInRegistersInfo GetFpStructInRegistersInfo(TypeHandle th); #endif #if defined(UNIX_AMD64_ABI_ITF) diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index eeef84d45df0e..165507c7ef9a3 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -497,7 +497,13 @@ FCIMPL4(Object*, RuntimeMethodHandle::InvokeMethod, #ifdef CALLDESCR_REGTYPEMAP callDescrData.dwRegTypeMap = 0; #endif +#if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) + // Temporary conversion to old flags, CallDescrWorker needs to be overhauled anyway + // to work with arbitrary field offsets and sizes, and support struct size > 16 on RISC-V. + callDescrData.fpReturnSize = FpStructInRegistersInfo{FpStruct::Flags(argit.GetFPReturnSize())}.ToOldFlags(); +#else callDescrData.fpReturnSize = argit.GetFPReturnSize(); +#endif // This is duplicated logic from MethodDesc::GetCallTarget PCODE pTarget; @@ -632,7 +638,6 @@ FCIMPL4(Object*, RuntimeMethodHandle::InvokeMethod, UINT structSize = argit.GetArgSize(); - bool needsStackCopy = false; ArgDestination argDest(pTransitionBlock, ofs, argit.GetArgLocDescForStructInRegs()); #ifdef ENREGISTERED_PARAMTYPE_MAXSIZE diff --git a/src/coreclr/vm/riscv64/profiler.cpp b/src/coreclr/vm/riscv64/profiler.cpp index fc8eff4845638..b4dd3e7152b78 100644 --- a/src/coreclr/vm/riscv64/profiler.cpp +++ b/src/coreclr/vm/riscv64/profiler.cpp @@ -112,12 +112,12 @@ LPVOID ProfileArgIterator::CopyStructFromRegisters(const ArgLocDesc* sir) PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast(m_handle); struct { bool isFloat, is8; } fields[] = { - { (bool) (sir->m_structFields & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_ONLY_TWO | STRUCT_FLOAT_FIELD_ONLY_ONE)), - (bool) (sir->m_structFields & STRUCT_FIRST_FIELD_SIZE_IS8) }, - { (bool) (sir->m_structFields & (STRUCT_FLOAT_FIELD_SECOND | STRUCT_FLOAT_FIELD_ONLY_TWO)), - (bool) (sir->m_structFields & STRUCT_SECOND_FIELD_SIZE_IS8) }, + { (bool) (sir->m_structFields.flags & (FpStruct::FloatInt | FpStruct::BothFloat | FpStruct::OnlyOne)), + (sir->m_structFields.SizeShift1st() == 3) }, + { (bool) (sir->m_structFields.flags & (FpStruct::IntFloat | FpStruct::BothFloat)), + (sir->m_structFields.SizeShift2nd() == 3) }, }; - int fieldCount = (sir->m_structFields & STRUCT_FLOAT_FIELD_ONLY_ONE) ? 1 : 2; + int fieldCount = (sir->m_structFields.flags & FpStruct::OnlyOne) ? 1 : 2; UINT64 bufferPosBegin = m_bufferPos; const double *fRegBegin = &pData->floatArgumentRegisters.f[sir->m_idxFloatReg], *fReg = fRegBegin; const double *fRegEnd = &pData->floatArgumentRegisters.f[0] + NUM_FLOAT_ARGUMENT_REGISTERS; @@ -185,7 +185,7 @@ LPVOID ProfileArgIterator::GetNextArgAddr() // If both fields are in registers of same kind (either float or general) and both are 8 bytes, no need to copy. // We can get away with returning a ptr to argumentRegisters since the struct would have the same layout. if ((sir->m_cFloatReg ^ sir->m_cGenReg) != 2 || - (sir->m_structFields & STRUCT_HAS_8BYTES_FIELDS_MASK) != STRUCT_HAS_8BYTES_FIELDS_MASK) + (sir->m_structFields.SizeShift1st() < 3 || sir->m_structFields.SizeShift2nd() < 3)) { return CopyStructFromRegisters(sir); } @@ -302,11 +302,11 @@ LPVOID ProfileArgIterator::GetReturnBufferAddr(void) return (LPVOID)pData->argumentRegisters.a[0]; } - UINT fpReturnSize = m_argIterator.GetFPReturnSize(); - if (fpReturnSize) + FpStructInRegistersInfo info = {(FpStruct::Flags)m_argIterator.GetFPReturnSize()}; + if (info.flags != FpStruct::UseIntCallConv) { - if ((fpReturnSize & STRUCT_HAS_8BYTES_FIELDS_MASK) == STRUCT_HAS_8BYTES_FIELDS_MASK || - (fpReturnSize & STRUCT_FLOAT_FIELD_ONLY_ONE)) + if (((info.flags & FpStruct::BothFloat) && info.SizeShift1st() == 3 && info.SizeShift2nd() == 3) || + (info.flags & FpStruct::OnlyOne)) { return &pData->floatArgumentRegisters.f[0]; } @@ -317,7 +317,7 @@ LPVOID ProfileArgIterator::GetReturnBufferAddr(void) sir.m_cGenReg = -1; sir.m_byteStackIndex = 0; sir.m_byteStackSize = -1; - sir.m_structFields = fpReturnSize; + sir.m_structFields = info; return CopyStructFromRegisters(&sir); }