From 10e9b7957b6a9d80c2cf6c33f654dbb5b56b3d48 Mon Sep 17 00:00:00 2001 From: qiaopengcheng Date: Thu, 18 Apr 2024 12:09:05 +0800 Subject: [PATCH 1/2] add new ABI classifier. --- src/coreclr/jit/abi.h | 22 ++- src/coreclr/jit/lclvars.cpp | 2 +- src/coreclr/jit/targetloongarch64.cpp | 212 ++++++++++++++++++++++++++ 3 files changed, 234 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/abi.h b/src/coreclr/jit/abi.h index 97232bf911c4a3..ac0ad5090dcf2b 100644 --- a/src/coreclr/jit/abi.h +++ b/src/coreclr/jit/abi.h @@ -48,7 +48,9 @@ struct ABIPassingInformation // multiple register segments and a struct segment. // - On Windows x64, all parameters always fit into one stack slot or // register, and thus always have NumSegments == 1 - // - On RISC-V, structs can be split out over 2 segments, each can be an integer/float register or a stack slot + // - On loongarch64/riscv64, structs can be passed in two registers or + // can be split out over register and stack, giving + // multiple register segments and a struct segment. unsigned NumSegments = 0; ABIPassingSegment* Segments = nullptr; @@ -202,6 +204,22 @@ class RiscV64Classifier WellKnownArg wellKnownParam); }; +class LoongArch64Classifier +{ + const ClassifierInfo& m_info; + RegisterQueue m_intRegs; + RegisterQueue m_floatRegs; + unsigned m_stackArgSize = 0; + +public: + LoongArch64Classifier(const ClassifierInfo& info); + + ABIPassingInformation Classify(Compiler* comp, + var_types type, + ClassLayout* structLayout, + WellKnownArg wellKnownParam); +}; + #if defined(TARGET_X86) typedef X86Classifier PlatformClassifier; #elif defined(WINDOWS_AMD64_ABI) @@ -214,6 +232,8 @@ typedef Arm64Classifier PlatformClassifier; typedef Arm32Classifier PlatformClassifier; #elif defined(TARGET_RISCV64) typedef RiscV64Classifier PlatformClassifier; +#elif defined(TARGET_LOONGARCH64) +typedef LoongArch64Classifier PlatformClassifier; #endif #ifdef SWIFT_SUPPORT diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 5eb39435a51543..de8f1883e4c193 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -1805,7 +1805,7 @@ void Compiler::lvaClassifyParameterABI() else #endif #if defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_ARM) || \ - defined(TARGET_RISCV64) + defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) { PlatformClassifier classifier(cInfo); lvaClassifyParameterABI(classifier); diff --git a/src/coreclr/jit/targetloongarch64.cpp b/src/coreclr/jit/targetloongarch64.cpp index e0097a1b62a1c9..47f74094fab9dc 100644 --- a/src/coreclr/jit/targetloongarch64.cpp +++ b/src/coreclr/jit/targetloongarch64.cpp @@ -24,4 +24,216 @@ const regNumber fltArgRegs [] = {REG_F0, REG_F1, REG_F2, REG_F3, REG_F4, REG_F5, const regMaskTP fltArgMasks[] = {RBM_F0, RBM_F1, RBM_F2, RBM_F3, RBM_F4, RBM_F5, RBM_F6, RBM_F7 }; // clang-format on +//----------------------------------------------------------------------------- +// LoongArch64Classifier: +// Construct a new instance of the LoongArch64 ABI classifier. +// +// Parameters: +// info - Info about the method being classified. +// +LoongArch64Classifier::LoongArch64Classifier(const ClassifierInfo& info) + : m_info(info) + , m_intRegs(intArgRegs, ArrLen(intArgRegs)) + , m_floatRegs(fltArgRegs, ArrLen(fltArgRegs)) +{ +} + +//----------------------------------------------------------------------------- +// Classify: +// Classify a parameter for the LoongArch64 ABI. +// +// Parameters: +// comp - Compiler instance +// type - The type of the parameter +// structLayout - The layout of the struct. Expected to be non-null if +// varTypeIsStruct(type) is true. +// wellKnownParam - Well known type of the parameter (if it may affect its ABI classification) +// +// Returns: +// Classification information for the parameter. +// +ABIPassingInformation LoongArch64Classifier::Classify(Compiler* comp, + var_types type, + ClassLayout* structLayout, + WellKnownArg wellKnownParam) +{ + assert(!m_info.IsVarArgs); + + unsigned passedSize; + unsigned slots = 0; + var_types argRegTypeInStruct1 = TYP_UNKNOWN; + var_types argRegTypeInStruct2 = TYP_UNKNOWN; + + bool canPassArgInRegisters = false; + if (varTypeIsStruct(type)) + { + passedSize = structLayout->GetSize(); + if (passedSize > MAX_PASS_MULTIREG_BYTES) + { + slots = 1; // Passed by implicit byref + passedSize = TARGET_POINTER_SIZE; + canPassArgInRegisters = m_intRegs.Count() > 0; + } + else + { + assert(!structLayout->IsBlockLayout()); + + uint32_t floatFlags; + CORINFO_CLASS_HANDLE typeHnd = structLayout->GetClassHandle(); + + floatFlags = comp->info.compCompHnd->getLoongArch64PassStructInRegisterFlags(typeHnd); + + if ((floatFlags & STRUCT_HAS_FLOAT_FIELDS_MASK) != 0) + { + if ((floatFlags & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) + { + assert(passedSize <= TARGET_POINTER_SIZE); + + 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; + } + else if ((floatFlags & STRUCT_FLOAT_FIELD_FIRST) != 0) + { + slots = 1; + 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) + { + slots = 1; + 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((slots == 1) || (slots == 2)); + + if (!canPassArgInRegisters) + { + m_floatRegs.Clear(); + slots = (passedSize + TARGET_POINTER_SIZE - 1) / TARGET_POINTER_SIZE; + // On LoongArch64, if there aren't any remaining floating-point registers to pass the argument, + // integer registers (if any) are used instead. + canPassArgInRegisters = m_intRegs.Count() >= slots; + + argRegTypeInStruct1 = TYP_UNKNOWN; + argRegTypeInStruct2 = TYP_UNKNOWN; + } + } + else + { + slots = (passedSize + TARGET_POINTER_SIZE - 1) / TARGET_POINTER_SIZE; + canPassArgInRegisters = m_intRegs.Count() >= slots; + } + + if (!canPassArgInRegisters && (slots == 2)) + { + // Here a struct-arg which needs two registers but only one integer register available, + // it has to be split. + if (m_intRegs.Count() > 0) + { + canPassArgInRegisters = true; + } + } + } + } + else + { + assert(genTypeSize(type) <= TARGET_POINTER_SIZE); + + slots = 1; + passedSize = genTypeSize(type); + if (varTypeIsFloating(type)) + { + canPassArgInRegisters = m_floatRegs.Count() > 0; + if (!canPassArgInRegisters) + { + m_floatRegs.Clear(); + canPassArgInRegisters = m_intRegs.Count() > 0; + } + } + else + { + canPassArgInRegisters = m_intRegs.Count() > 0; + } + } + + ABIPassingInformation info; + if (canPassArgInRegisters) + { + info.NumSegments = slots; + info.Segments = new (comp, CMK_ABI) ABIPassingSegment[slots]; + if (argRegTypeInStruct1 != TYP_UNKNOWN) + { + RegisterQueue* regs = varTypeIsFloating(argRegTypeInStruct1) ? &m_floatRegs : &m_intRegs; + assert(regs->Count() > 0); + + passedSize = genTypeSize(argRegTypeInStruct1); + info.Segments[0] = ABIPassingSegment::InRegister(regs->Dequeue(), 0, passedSize); + + if (argRegTypeInStruct2 != TYP_UNKNOWN) + { + unsigned slotSize = genTypeSize(argRegTypeInStruct2); + + regs = varTypeIsFloating(argRegTypeInStruct2) ? &m_floatRegs : &m_intRegs; + assert(regs->Count() > 0); + + passedSize = max(passedSize, slotSize); + info.Segments[1] = ABIPassingSegment::InRegister(regs->Dequeue(), passedSize, slotSize); + } + } + else + { + RegisterQueue* regs = varTypeIsFloating(type) ? &m_floatRegs : &m_intRegs; + unsigned slotSize = min(passedSize, (unsigned)TARGET_POINTER_SIZE); + info.Segments[0] = ABIPassingSegment::InRegister(regs->Dequeue(), 0, slotSize); + if (slots == 2) + { + assert(varTypeIsStruct(type)); + assert(passedSize > TARGET_POINTER_SIZE); + unsigned tailSize = passedSize - slotSize; + if (m_intRegs.Count() > 0) + { + info.Segments[1] = ABIPassingSegment::InRegister(m_intRegs.Dequeue(), slotSize, tailSize); + } + else + { + assert(m_intRegs.Count() == 0); + assert(m_stackArgSize == 0); + info.Segments[1] = ABIPassingSegment::OnStack(0, TARGET_POINTER_SIZE, tailSize); + m_stackArgSize += TARGET_POINTER_SIZE; + } + } + } + } + else + { + assert((m_stackArgSize % TARGET_POINTER_SIZE) == 0); + + info = ABIPassingInformation::FromSegment(comp, ABIPassingSegment::OnStack(m_stackArgSize, 0, passedSize)); + + m_stackArgSize += roundUp(passedSize, TARGET_POINTER_SIZE); + + // As soon as we pass something on stack we cannot go back and + // enregister something else. + // The float had been cleared before and only integer type go here. + m_intRegs.Clear(); + } + + return info; +} + #endif // TARGET_LOONGARCH64 From 3018cf692a12b25977d7124347a921bf4934797e Mon Sep 17 00:00:00 2001 From: qiaopengcheng Date: Thu, 18 Apr 2024 15:39:04 +0800 Subject: [PATCH 2/2] share `CodeGen::genHomeRegisterParams` with XARCH-ARMARCH. --- src/coreclr/jit/codegencommon.cpp | 2 +- src/coreclr/jit/codegenloongarch64.cpp | 539 ------------------------- src/coreclr/jit/lclvars.cpp | 3 - 3 files changed, 1 insertion(+), 543 deletions(-) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index b982ae640e5556..417a7e6f31695d 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -2802,7 +2802,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ -#if !defined(TARGET_LOONGARCH64) && !defined(TARGET_RISCV64) +#if !defined(TARGET_RISCV64) struct RegNode; struct RegNodeEdge diff --git a/src/coreclr/jit/codegenloongarch64.cpp b/src/coreclr/jit/codegenloongarch64.cpp index fdb99f1c29f5c9..2954a989c74668 100644 --- a/src/coreclr/jit/codegenloongarch64.cpp +++ b/src/coreclr/jit/codegenloongarch64.cpp @@ -7983,545 +7983,6 @@ void CodeGen::genPopCalleeSavedRegisters(bool jmpEpilog) } } -void CodeGen::genHomeRegisterParams(regNumber initReg, bool* initRegStillZeroed) -{ - *initRegStillZeroed = false; - assert(!(intRegState.rsCalleeRegArgMaskLiveIn & floatRegState.rsCalleeRegArgMaskLiveIn)); - - regMaskTP regArgMaskLive = intRegState.rsCalleeRegArgMaskLiveIn | floatRegState.rsCalleeRegArgMaskLiveIn; - -#ifdef DEBUG - if (verbose) - { - printf("*************** In genFnPrologCalleeRegArgs() LOONGARCH64:0x%llx.\n", regArgMaskLive); - } -#endif - - // We should be generating the prolog block when we are called - assert(compiler->compGeneratingProlog); - - // We expect to have some registers of the type we are doing, that are LiveIn, otherwise we don't need to be called. - noway_assert(regArgMaskLive != 0); - - unsigned varNum; - unsigned regArgMaskIsInt = 0; - int regArgNum = 0; - // Process any circular dependencies - unsigned regArg[MAX_REG_ARG * 2] = {0}; - unsigned regArgInit[MAX_REG_ARG * 2] = {0}; - for (varNum = 0; varNum < compiler->lvaCount; ++varNum) - { - LclVarDsc* varDsc = compiler->lvaTable + varNum; - - // Is this variable a register arg? - if (!varDsc->lvIsParam) - { - continue; - } - - if (!varDsc->lvIsRegArg) - { - continue; - } - - if (varDsc->lvIsInReg()) - { - assert(genIsValidIntReg(varDsc->GetArgReg()) || genIsValidFloatReg(varDsc->GetArgReg())); - assert(!(genIsValidIntReg(varDsc->GetOtherArgReg()) || genIsValidFloatReg(varDsc->GetOtherArgReg()))); - if (varDsc->GetArgInitReg() != varDsc->GetArgReg()) - { - if (genIsValidIntReg(varDsc->GetArgInitReg())) - { - if (varDsc->GetArgInitReg() > REG_ARG_LAST) - { - bool isSkip; - instruction ins; - emitAttr size; - if (genIsValidIntReg(varDsc->GetArgReg())) - { - ins = INS_mov; - if (varDsc->TypeGet() == TYP_INT) - { - size = EA_4BYTE; - isSkip = false; - } - else - { - size = EA_PTRSIZE; - isSkip = true; - } - } - else - { - ins = INS_movfr2gr_d; - size = EA_PTRSIZE; - isSkip = true; - } - GetEmitter()->emitIns_Mov(ins, size, varDsc->GetArgInitReg(), varDsc->GetArgReg(), isSkip); - regArgMaskLive &= ~genRegMask(varDsc->GetArgReg()); - } - else - { - if (genIsValidIntReg(varDsc->GetArgReg())) - { - assert(varDsc->GetArgReg() >= REG_ARG_FIRST); - regArg[varDsc->GetArgReg() - REG_ARG_FIRST] = varDsc->GetArgReg(); - regArgInit[varDsc->GetArgReg() - REG_ARG_FIRST] = varDsc->GetArgInitReg(); - if (varDsc->TypeGet() == TYP_INT) - { - regArgMaskIsInt = 1 << (unsigned)varDsc->GetArgReg(); - } - } - else - { - assert(genIsValidFloatReg(varDsc->GetArgReg())); - regArg[(varDsc->GetArgReg() & 7) | 0x8] = varDsc->GetArgReg(); - regArgInit[(varDsc->GetArgReg() & 7) | 0x8] = varDsc->GetArgInitReg(); - } - regArgNum++; - } - } - else - { - assert(genIsValidFloatReg(varDsc->GetArgInitReg())); - if (genIsValidIntReg(varDsc->GetArgReg())) - { - GetEmitter()->emitIns_Mov(INS_movgr2fr_d, EA_PTRSIZE, varDsc->GetArgInitReg(), - varDsc->GetArgReg(), false); - regArgMaskLive &= ~genRegMask(varDsc->GetArgReg()); - } - else if (varDsc->GetArgInitReg() > REG_ARG_FP_LAST) - { - GetEmitter()->emitIns_Mov(INS_fmov_d, EA_PTRSIZE, varDsc->GetArgInitReg(), varDsc->GetArgReg(), - true); - regArgMaskLive &= ~genRegMask(varDsc->GetArgReg()); - } - else - { - assert(genIsValidFloatReg(varDsc->GetArgReg())); - regArg[(varDsc->GetArgReg() & 7) | 0x8] = varDsc->GetArgReg(); - regArgInit[(varDsc->GetArgReg() & 7) | 0x8] = varDsc->GetArgInitReg(); - regArgNum++; - } - } - } - else - { - // TODO for LoongArch64: should delete this by optimization "struct {long a; int32_t b;};" - // liking AMD64_ABI within morph. - if (genIsValidIntReg(varDsc->GetArgReg()) && (varDsc->TypeGet() == TYP_INT)) - { - GetEmitter()->emitIns_Mov(INS_mov, EA_4BYTE, varDsc->GetArgInitReg(), varDsc->GetArgReg(), false); - } - regArgMaskLive &= ~genRegMask(varDsc->GetArgReg()); - } -#ifdef USING_SCOPE_INFO - psiMoveToReg(varNum); -#endif // USING_SCOPE_INFO - if (!varDsc->lvLiveInOutOfHndlr) - { - continue; - } - } - - // When we have a promoted struct we have two possible LclVars that can represent the incoming argument - // in the regArgTab[], either the original TYP_STRUCT argument or the introduced lvStructField. - // We will use the lvStructField if we have a TYPE_INDEPENDENT promoted struct field otherwise - // use the original TYP_STRUCT argument. - // - if (varDsc->lvPromoted || varDsc->lvIsStructField) - { - LclVarDsc* parentVarDsc = varDsc; - if (varDsc->lvIsStructField) - { - assert(!varDsc->lvPromoted); - parentVarDsc = &compiler->lvaTable[varDsc->lvParentLcl]; - } - - Compiler::lvaPromotionType promotionType = compiler->lvaGetPromotionType(parentVarDsc); - - if (promotionType == Compiler::PROMOTION_TYPE_INDEPENDENT) - { - // For register arguments that are independent promoted structs we put the promoted field varNum - // in the regArgTab[] - if (varDsc->lvPromoted) - { - continue; - } - } - else - { - // For register arguments that are not independent promoted structs we put the parent struct varNum - // in the regArgTab[] - if (varDsc->lvIsStructField) - { - continue; - } - } - } - - var_types storeType = TYP_UNDEF; - int slotSize = TARGET_POINTER_SIZE; - - if (varTypeIsStruct(varDsc)) - { - if (emitter::isFloatReg(varDsc->GetArgReg())) - { - storeType = varDsc->lvIs4Field1 ? TYP_FLOAT : TYP_DOUBLE; - } - else - { - assert(emitter::isGeneralRegister(varDsc->GetArgReg())); - if (varDsc->lvIs4Field1) - { - storeType = TYP_INT; - } - else - { - storeType = varDsc->GetLayout()->GetGCPtrType(0); - } - } - slotSize = (int)EA_SIZE(emitActualTypeSize(storeType)); - -#if FEATURE_MULTIREG_ARGS - // Must be <= MAX_PASS_MULTIREG_BYTES or else it wouldn't be passed in registers - noway_assert(varDsc->lvSize() <= MAX_PASS_MULTIREG_BYTES); -#endif - } - else // Not a struct type - { - storeType = compiler->mangleVarArgsType(genActualType(varDsc->TypeGet())); - if (emitter::isFloatReg(varDsc->GetArgReg()) != varTypeIsFloating(storeType)) - { - assert(varTypeIsFloating(storeType)); - storeType = storeType == TYP_DOUBLE ? TYP_I_IMPL : TYP_INT; - } - } - emitAttr size = emitActualTypeSize(storeType); - - regNumber srcRegNum = varDsc->GetArgReg(); - - // Stack argument - if the ref count is 0 don't care about it - if (!varDsc->lvOnFrame) - { - noway_assert(varDsc->lvRefCnt() == 0); - regArgMaskLive &= ~genRegMask(varDsc->GetArgReg()); - if (varDsc->GetOtherArgReg() < REG_STK) - { - regArgMaskLive &= ~genRegMask(varDsc->GetOtherArgReg()); - } - } - else - { - assert(srcRegNum != varDsc->GetOtherArgReg()); - - regNumber tmp_reg = REG_NA; - - bool FPbased; - int baseOffset = compiler->lvaFrameAddress(varNum, &FPbased); - - // First store the `varDsc->GetArgReg()` on stack. - if (emitter::isValidSimm12(baseOffset)) - { - GetEmitter()->emitIns_S_R(ins_Store(storeType), size, srcRegNum, varNum, 0); - } - else - { - assert(tmp_reg == REG_NA); - - tmp_reg = REG_R21; - GetEmitter()->emitIns_I_la(EA_PTRSIZE, REG_R21, baseOffset); - // The last parameter `int offs` of the `emitIns_S_R` is negtive, - // it means the offset imm had been stored within the `REG_R21`. - GetEmitter()->emitIns_S_R(ins_Store(storeType, true), size, srcRegNum, varNum, -8); - } - - regArgMaskLive &= ~genRegMask(srcRegNum); - - // Then check if varDsc is a struct arg - if (varTypeIsStruct(varDsc)) - { - if (emitter::isFloatReg(varDsc->GetOtherArgReg())) - { - srcRegNum = varDsc->GetOtherArgReg(); - storeType = varDsc->lvIs4Field2 ? TYP_FLOAT : TYP_DOUBLE; - size = EA_SIZE(emitActualTypeSize(storeType)); - - slotSize = slotSize < (int)size ? (int)size : slotSize; - } - else if (emitter::isGeneralRegister(varDsc->GetOtherArgReg())) - { - if (varDsc->lvIs4Field2) - { - storeType = TYP_INT; - } - else - { - storeType = varDsc->GetLayout()->GetGCPtrType(1); - } - - srcRegNum = varDsc->GetOtherArgReg(); - size = emitActualTypeSize(storeType); - - slotSize = slotSize < (int)EA_SIZE(size) ? (int)EA_SIZE(size) : slotSize; - } - baseOffset += slotSize; - - // if the struct passed by two register, then store the second register `varDsc->GetOtherArgReg()`. - if (srcRegNum == varDsc->GetOtherArgReg()) - { - if (emitter::isValidSimm12(baseOffset)) - { - GetEmitter()->emitIns_S_R(ins_Store(storeType), size, srcRegNum, varNum, slotSize); - } - else - { - if (tmp_reg == REG_NA) - { - GetEmitter()->emitIns_I_la(EA_PTRSIZE, REG_R21, baseOffset); - // The last parameter `int offs` of the `emitIns_S_R` is negtive, - // it means the offset imm had been stored within the `REG_R21`. - GetEmitter()->emitIns_S_R(ins_Store(storeType, true), size, srcRegNum, varNum, - -slotSize - 8); - } - else - { - GetEmitter()->emitIns_R_R_I(INS_addi_d, EA_PTRSIZE, REG_R21, REG_R21, slotSize); - GetEmitter()->emitIns_S_R(ins_Store(storeType, true), size, srcRegNum, varNum, - -slotSize - 8); - } - } - regArgMaskLive &= ~genRegMask(srcRegNum); // maybe do this later is better! - } - else if (varDsc->lvIsSplit) - { - // the struct is a split struct. - assert(varDsc->GetArgReg() == REG_ARG_LAST && varDsc->GetOtherArgReg() == REG_STK); - - // For the LA's ABI, the split struct arg will be passed via `A7` and a stack slot on caller. - // But if the `A7` is stored on stack on the callee side, the whole split struct should be - // stored continuous on the stack on the callee side. - // So, after we save `A7` on the stack in prolog, it has to copy the stack slot of the split struct - // which was passed by the caller. Here we load the stack slot to `REG_SCRATCH`, and save it - // on the stack following the `A7` in prolog. - if (emitter::isValidSimm12(genTotalFrameSize())) - { - GetEmitter()->emitIns_R_R_I(INS_ld_d, size, REG_SCRATCH, REG_SPBASE, genTotalFrameSize()); - } - else - { - assert(!EA_IS_RELOC(size)); - GetEmitter()->emitIns_I_la(size, REG_SCRATCH, genTotalFrameSize()); - GetEmitter()->emitIns_R_R_R(INS_ldx_d, size, REG_SCRATCH, REG_SPBASE, REG_SCRATCH); - } - - if (emitter::isValidSimm12(baseOffset)) - { - GetEmitter()->emitIns_S_R(INS_st_d, size, REG_SCRATCH, varNum, TARGET_POINTER_SIZE); - } - else - { - if (tmp_reg == REG_NA) - { - GetEmitter()->emitIns_I_la(EA_PTRSIZE, REG_R21, baseOffset); - // The last parameter `int offs` of the `emitIns_S_R` is negtive, - // it means the offset imm had been stored within the `REG_R21`. - GetEmitter()->emitIns_S_R(INS_stx_d, size, REG_SCRATCH, varNum, -8); - } - else - { - GetEmitter()->emitIns_R_R_I(INS_addi_d, EA_PTRSIZE, REG_R21, REG_R21, TARGET_POINTER_SIZE); - GetEmitter()->emitIns_S_R(INS_stx_d, size, REG_SCRATCH, varNum, -slotSize - 8); - } - } - } - } - -#ifdef USING_SCOPE_INFO - { - psiMoveToStack(varNum); - } -#endif // USING_SCOPE_INFO - } - } - - if (regArgNum > 0) - { - instruction ins; - for (int i = MAX_REG_ARG - 1; i >= 0; i--) - { - if (regArg[i] > 0) - { - assert(genIsValidIntReg((regNumber)regArg[i])); - assert(genIsValidIntReg((regNumber)regArgInit[i])); - - unsigned tmpRegs[MAX_REG_ARG] = {0}; - - unsigned tmpArg = regArg[i]; - unsigned nextReg = regArgInit[i] - REG_ARG_FIRST; - - assert(tmpArg <= REG_ARG_LAST); - assert(nextReg < MAX_REG_ARG); - assert(nextReg != (unsigned)i); - - regArg[i] = 0; - int count = 0; - - while (regArg[nextReg] != 0) - { - tmpRegs[count] = nextReg; - nextReg = regArgInit[nextReg] - REG_ARG_FIRST; - assert(nextReg < MAX_REG_ARG); - - for (int count2 = 0; count2 < count; count2++) - { - if (nextReg == tmpRegs[count2]) - { - NYI_LOONGARCH64("-----------CodeGen::genFnPrologCalleeRegArgs() error: intRegs!"); - } - } - - count++; - } - - if (nextReg == (unsigned)i) - { - GetEmitter()->emitIns_R_R_I(INS_ori, EA_PTRSIZE, REG_R21, (regNumber)tmpArg, 0); - regArgMaskLive &= ~genRegMask((regNumber)tmpArg); - assert(count > 0); - } - else if (count == 0) - { - tmpRegs[0] = (unsigned)i; - regArg[i] = tmpArg; - } - else - { - count--; - } - - do - { - tmpArg = tmpRegs[count]; - - instruction ins = (regArgMaskIsInt & (1 << regArg[tmpArg])) != 0 ? INS_slli_w : INS_ori; - GetEmitter()->emitIns_R_R_I(ins, EA_PTRSIZE, (regNumber)regArgInit[tmpArg], - (regNumber)regArg[tmpArg], 0); - - regArgMaskLive &= ~genRegMask((regNumber)regArg[tmpArg]); - regArg[tmpArg] = 0; - count--; - regArgNum--; - assert(regArgNum >= 0); - } while (count >= 0); - - if (nextReg == (unsigned)i) - { - instruction ins = (regArgMaskIsInt & (1 << regArg[i])) != 0 ? INS_slli_w : INS_ori; - GetEmitter()->emitIns_R_R_I(ins, EA_PTRSIZE, (regNumber)regArgInit[i], REG_R21, 0); - regArgNum--; - assert(regArgNum >= 0); - } - else if (tmpRegs[0] != (unsigned)i) - { - instruction ins = (regArgMaskIsInt & (1 << (i + REG_ARG_FIRST))) != 0 ? INS_slli_w : INS_ori; - GetEmitter()->emitIns_R_R_I(ins, EA_PTRSIZE, (regNumber)regArgInit[i], - (regNumber)(i + REG_ARG_FIRST), 0); - - regArgMaskLive &= ~genRegMask((regNumber)(i + REG_ARG_FIRST)); - regArgNum--; - } - assert(regArgNum >= 0); - } - } - - if (regArgNum > 0) - { - for (int i = MAX_REG_ARG + MAX_FLOAT_REG_ARG - 1; i >= MAX_REG_ARG; i--) - { - if (regArg[i] > 0) - { - assert(genIsValidFloatReg((regNumber)regArg[i])); - - instruction ins = genIsValidIntReg((regNumber)regArgInit[i]) ? INS_movfr2gr_d : INS_fmov_d; - - regArgNum--; - regArgMaskLive &= ~genRegMask((regNumber)regArg[i]); - if (regArgNum == 0) - { - GetEmitter()->emitIns_Mov(ins, EA_PTRSIZE, (regNumber)regArgInit[i], (regNumber)regArg[i], - true); - break; - } - else if (regArgInit[i] > regArg[i]) - { - GetEmitter()->emitIns_R_R(INS_fmov_d, EA_PTRSIZE, (regNumber)regArgInit[i], - (regNumber)regArg[i]); - } - else - { - assert(i > MAX_REG_ARG); - assert(regArgNum > 0); - - int j = genIsValidIntReg((regNumber)regArgInit[i]) ? (regArgInit[i] - REG_ARG_FIRST) - : ((regArgInit[i] & 0x7) | 0x8); - if (regArg[j] == 0) - { - GetEmitter()->emitIns_Mov(ins, EA_PTRSIZE, (regNumber)regArgInit[i], (regNumber)regArg[i], - true); - } - else - { - // NOTE: Not support the int-register case. - assert(genIsValidFloatReg((regNumber)regArg[j])); - assert(genIsValidFloatReg((regNumber)regArgInit[j])); - - int k = (regArgInit[j] & 0x7) | 0x8; - if ((regArg[k] == 0) || (k > i)) - { - GetEmitter()->emitIns_R_R(INS_fmov_d, EA_PTRSIZE, (regNumber)regArgInit[j], - (regNumber)regArg[j]); - GetEmitter()->emitIns_R_R(INS_fmov_d, EA_PTRSIZE, (regNumber)regArgInit[i], - (regNumber)regArg[i]); - regArgNum--; - regArgMaskLive &= ~genRegMask((regNumber)regArg[j]); - regArg[j] = 0; - if (regArgNum == 0) - { - break; - } - } - else if (k == i) - { - GetEmitter()->emitIns_R_R(INS_fmov_d, EA_PTRSIZE, REG_SCRATCH_FLT, - (regNumber)regArg[i]); - GetEmitter()->emitIns_R_R(INS_fmov_d, EA_PTRSIZE, (regNumber)regArgInit[j], - (regNumber)regArg[j]); - GetEmitter()->emitIns_R_R(INS_fmov_d, EA_PTRSIZE, (regNumber)regArgInit[i], - REG_SCRATCH_FLT); - regArgNum--; - regArgMaskLive &= ~genRegMask((regNumber)regArg[j]); - regArg[j] = 0; - if (regArgNum == 0) - { - break; - } - } - else - { - NYI_LOONGARCH64("-----------CodeGen::genFnPrologCalleeRegArgs() error!--"); - } - } - } - } - } - } - assert(regArgNum == 0); - } - - assert(!regArgMaskLive); -} - #ifdef PROFILING_SUPPORTED //----------------------------------------------------------------------------------- // genProfilingEnterCallback: Generate the profiling function enter callback. diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index de8f1883e4c193..c101955fdea7ed 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -1804,13 +1804,10 @@ void Compiler::lvaClassifyParameterABI() } else #endif -#if defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_ARM) || \ - defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) { PlatformClassifier classifier(cInfo); lvaClassifyParameterABI(classifier); } -#endif #ifdef DEBUG if (lvaParameterPassingInfo == nullptr)