diff --git a/src/coreclr/jit/abi.cpp b/src/coreclr/jit/abi.cpp index c6dbd8aa9202fd..7a594f77f39121 100644 --- a/src/coreclr/jit/abi.cpp +++ b/src/coreclr/jit/abi.cpp @@ -258,14 +258,16 @@ bool ABIPassingInformation::HasExactlyOneStackSegment() const // bool ABIPassingInformation::IsSplitAcrossRegistersAndStack() const { - bool anyReg = false; - bool anyStack = false; - for (unsigned i = 0; i < NumSegments; i++) + if (NumSegments < 2) + return false; + + bool isFirstInReg = Segments[0].IsPassedInRegister(); + for (unsigned i = 1; i < NumSegments; i++) { - anyReg |= Segments[i].IsPassedInRegister(); - anyStack |= Segments[i].IsPassedOnStack(); + if (isFirstInReg != Segments[i].IsPassedInRegister()) + return true; } - return anyReg && anyStack; + return false; } //----------------------------------------------------------------------------- diff --git a/src/coreclr/jit/abi.h b/src/coreclr/jit/abi.h index ac0ad5090dcf2b..37268ce3effd9f 100644 --- a/src/coreclr/jit/abi.h +++ b/src/coreclr/jit/abi.h @@ -51,8 +51,8 @@ struct ABIPassingInformation // - 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; + unsigned NumSegments; + ABIPassingSegment* Segments; ABIPassingInformation(unsigned numSegments = 0, ABIPassingSegment* segments = nullptr) : NumSegments(numSegments) diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 441db5f6b50d47..2a2ede6bae9ac0 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -284,7 +284,9 @@ class CodeGen final : public CodeGenInterface void genEnregisterOSRArgsAndLocals(); #endif + void genHomeStackSegment(unsigned lclNum, const ABIPassingSegment& seg, regNumber initReg, bool* pInitRegZeroed); void genHomeSwiftStructParameters(bool handleStack); + void genHomeStackPartOfSplitParameter(regNumber initReg, bool* initRegStillZeroed); void genCheckUseBlockInit(); #if defined(UNIX_AMD64_ABI) && defined(FEATURE_SIMD) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 9f3120d274d57d..c3608402d27ec2 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -2802,7 +2802,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ -#if !defined(TARGET_RISCV64) struct RegNode; struct RegNodeEdge @@ -3346,8 +3345,6 @@ void CodeGen::genHomeRegisterParams(regNumber initReg, bool* initRegStillZeroed) } } -#endif - // ----------------------------------------------------------------------------- // genGetParameterHomingTempRegisterCandidates: Get the registers that are // usable during register homing. @@ -4122,15 +4119,69 @@ void CodeGen::genEnregisterOSRArgsAndLocals() } } +#if defined(SWIFT_SUPPORT) || defined(TARGET_RISCV64) +//----------------------------------------------------------------------------- +// genHomeSwiftStructParameters: Move the incoming segment to the local stack frame. +// +// Arguments: +// lclNum - Number of local variable to home +// seg - Stack segment of the local variable to home +// initReg - Scratch register to use if needed +// initRegStillZeroed - Set to false if the scratch register was needed +// +void CodeGen::genHomeStackSegment(unsigned lclNum, + const ABIPassingSegment& seg, + regNumber initReg, + bool* initRegStillZeroed) +{ + var_types loadType = TYP_UNDEF; + switch (seg.Size) + { + case 1: + loadType = TYP_UBYTE; + break; + case 2: + loadType = TYP_USHORT; + break; + case 3: + case 4: + loadType = TYP_INT; + break; + case 5: + case 6: + case 7: + case 8: + loadType = TYP_LONG; + break; + default: + assert(!"Unexpected segment size for struct parameter not passed implicitly by ref"); + return; + } + emitAttr size = emitTypeSize(loadType); + + int loadOffset = + -(isFramePointerUsed() ? genCallerSPtoFPdelta() : genCallerSPtoInitialSPdelta()) + (int)seg.GetStackOffset(); +#ifdef TARGET_XARCH + GetEmitter()->emitIns_R_AR(ins_Load(loadType), size, initReg, genFramePointerReg(), loadOffset); +#else + genInstrWithConstant(ins_Load(loadType), size, initReg, genFramePointerReg(), loadOffset, initReg); +#endif + GetEmitter()->emitIns_S_R(ins_Store(loadType), size, initReg, lclNum, seg.Offset); + + if (initRegStillZeroed) + *initRegStillZeroed = false; +} +#endif // defined(SWIFT_SUPPORT) || defined(TARGET_RISCV64) + #ifdef SWIFT_SUPPORT //----------------------------------------------------------------------------- // genHomeSwiftStructParameters: -// Reassemble Swift struct parameters if necessary. +// Reassemble Swift struct parameters if necessary. // -// Parameters: -// handleStack - If true, reassemble the segments that were passed on the stack. -// If false, reassemble the segments that were passed in registers. +// Arguments: +// handleStack - If true, reassemble the segments that were passed on the stack. +// If false, reassemble the segments that were passed in registers. // void CodeGen::genHomeSwiftStructParameters(bool handleStack) { @@ -4176,59 +4227,54 @@ void CodeGen::genHomeSwiftStructParameters(bool handleStack) } else { - var_types loadType = TYP_UNDEF; - switch (seg.Size) - { - case 1: - loadType = TYP_UBYTE; - break; - case 2: - loadType = TYP_USHORT; - break; - case 3: - case 4: - loadType = TYP_INT; - break; - case 5: - case 6: - case 7: - case 8: - loadType = TYP_LONG; - break; - default: - assert(!"Unexpected segment size for struct parameter not passed implicitly by ref"); - continue; - } + // We can use REG_SCRATCH as a temporary register here as we ensured that during LSRA build. + genHomeStackSegment(lclNum, seg, REG_SCRATCH, nullptr); + } + } + } +} +#endif - int offset; - if (isFramePointerUsed()) - { - offset = -genCallerSPtoFPdelta(); - } - else - { - offset = -genCallerSPtoInitialSPdelta(); - } +//----------------------------------------------------------------------------- +// genHomeStackPartOfSplitParameter: Home the tail (stack) portion of a split parameter next to where the head +// (register) portion is homed. +// +// Arguments: +// initReg - scratch register to use if needed +// initRegStillZeroed - set to false if scratch register was needed +// +// Notes: +// No-op on platforms where argument registers are already homed to form a contiguous space with incoming stack. +// +void CodeGen::genHomeStackPartOfSplitParameter(regNumber initReg, bool* initRegStillZeroed) +{ +#ifdef TARGET_RISCV64 + unsigned lclNum = 0; + for (; lclNum < compiler->info.compArgsCount; lclNum++) + { + LclVarDsc* var = compiler->lvaGetDesc(lclNum); + if (!var->lvIsSplit || !var->lvOnFrame) + continue; - offset += (int)seg.GetStackOffset(); + JITDUMP("Homing stack part of split parameter V%02u\n", lclNum); - // Move the incoming segment to the local stack frame. We can - // use REG_SCRATCH as a temporary register here as we ensured - // that during LSRA build. -#ifdef TARGET_XARCH - GetEmitter()->emitIns_R_AR(ins_Load(loadType), emitTypeSize(loadType), REG_SCRATCH, - genFramePointerReg(), offset); -#else - genInstrWithConstant(ins_Load(loadType), emitTypeSize(loadType), REG_SCRATCH, genFramePointerReg(), - offset, REG_SCRATCH); -#endif + assert(varTypeIsStruct(var)); + assert(!compiler->lvaIsImplicitByRefLocal(lclNum)); + const ABIPassingInformation& abiInfo = compiler->lvaGetParameterABIInfo(lclNum); + assert(abiInfo.NumSegments == 2); + assert(abiInfo.Segments[0].GetRegister() == REG_ARG_LAST); + const ABIPassingSegment& seg = abiInfo.Segments[1]; - GetEmitter()->emitIns_S_R(ins_Store(loadType), emitTypeSize(loadType), REG_SCRATCH, lclNum, seg.Offset); - } + genHomeStackSegment(lclNum, seg, initReg, initRegStillZeroed); + + for (lclNum += 1; lclNum < compiler->info.compArgsCount; lclNum++) + { + assert(!compiler->lvaGetDesc(lclNum)->lvIsSplit); // There should be only one split parameter } + break; } -} #endif +} /*----------------------------------------------------------------------------- * @@ -5547,6 +5593,8 @@ void CodeGen::genFnProlog() { compiler->lvaUpdateArgsWithInitialReg(); + genHomeStackPartOfSplitParameter(initReg, &initRegZeroed); + if ((intRegState.rsCalleeRegArgMaskLiveIn | floatRegState.rsCalleeRegArgMaskLiveIn) != RBM_NONE) { genHomeRegisterParams(initReg, &initRegZeroed); diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index b7c119be94987c..0fa8bdb5874f40 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -7962,449 +7962,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() RISCV64: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 regArgNum = 0; - // Process any rearrangements including circular dependencies. - regNumber regArg[MAX_REG_ARG + MAX_FLOAT_REG_ARG]; - regNumber regArgInit[MAX_REG_ARG + MAX_FLOAT_REG_ARG]; - emitAttr regArgAttr[MAX_REG_ARG + MAX_FLOAT_REG_ARG]; - - for (int i = 0; i < MAX_REG_ARG + MAX_FLOAT_REG_ARG; i++) - { - regArg[i] = REG_NA; - -#ifdef DEBUG - regArgInit[i] = REG_NA; - regArgAttr[i] = EA_UNKNOWN; -#endif - } - - 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_fmv_x_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(isValidIntArgReg(varDsc->GetArgReg(), compiler->info.compCallConv)); - regArg[varDsc->GetArgReg() - REG_ARG_FIRST] = varDsc->GetArgReg(); - regArgInit[varDsc->GetArgReg() - REG_ARG_FIRST] = varDsc->GetArgInitReg(); - regArgAttr[varDsc->GetArgReg() - REG_ARG_FIRST] = - varDsc->TypeGet() == TYP_INT ? EA_4BYTE : EA_PTRSIZE; - } - else - { - assert(isValidFloatArgReg(varDsc->GetArgReg())); - regArg[varDsc->GetArgReg() - REG_ARG_FP_FIRST + MAX_REG_ARG] = varDsc->GetArgReg(); - regArgInit[varDsc->GetArgReg() - REG_ARG_FP_FIRST + MAX_REG_ARG] = varDsc->GetArgInitReg(); - regArgAttr[varDsc->GetArgReg() - REG_ARG_FP_FIRST + MAX_REG_ARG] = - varDsc->TypeGet() == TYP_FLOAT ? EA_4BYTE : EA_PTRSIZE; - } - regArgNum++; - } - } - else - { - assert(genIsValidFloatReg(varDsc->GetArgInitReg())); - if (genIsValidIntReg(varDsc->GetArgReg())) - { - emitAttr size = (varDsc->TypeGet() == TYP_FLOAT) ? EA_4BYTE : EA_PTRSIZE; - GetEmitter()->emitIns_Mov(size, varDsc->GetArgInitReg(), varDsc->GetArgReg(), false); - regArgMaskLive &= ~genRegMask(varDsc->GetArgReg()); - } - else if (varDsc->GetArgInitReg() > REG_ARG_FP_LAST) - { - GetEmitter()->emitIns_Mov(INS_fsgnj_d, EA_PTRSIZE, varDsc->GetArgInitReg(), varDsc->GetArgReg(), - true); - regArgMaskLive &= ~genRegMask(varDsc->GetArgReg()); - } - else - { - assert(isValidFloatArgReg(varDsc->GetArgReg())); - regArg[varDsc->GetArgReg() - REG_ARG_FP_FIRST + MAX_REG_ARG] = varDsc->GetArgReg(); - regArgInit[varDsc->GetArgReg() - REG_ARG_FP_FIRST + MAX_REG_ARG] = varDsc->GetArgInitReg(); - regArgAttr[varDsc->GetArgReg() - REG_ARG_FP_FIRST + MAX_REG_ARG] = - varDsc->TypeGet() == TYP_FLOAT ? EA_4BYTE : EA_PTRSIZE; - regArgNum++; - } - } - } - else - { - // TODO-RISCV64: 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 tmpReg = 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(tmpReg == REG_NA); - - tmpReg = REG_RA; - // Prepare tmpReg to possible future use - GetEmitter()->emitLoadImmediate(EA_PTRSIZE, tmpReg, baseOffset); - GetEmitter()->emitIns_R_R_R(INS_add, EA_PTRSIZE, tmpReg, tmpReg, FPbased ? REG_FPBASE : REG_SPBASE); - GetEmitter()->emitIns_S_R_R(ins_Store(storeType), size, srcRegNum, tmpReg, varNum, 0); - } - - 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()) - { - GetEmitter()->emitIns_S_R_R(ins_Store(storeType), size, srcRegNum, tmpReg, varNum, slotSize); - 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 RISCV64'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, size, REG_SCRATCH, REG_SPBASE, genTotalFrameSize()); - } - else - { - assert(!EA_IS_RELOC(size)); - GetEmitter()->emitLoadImmediate(size, REG_SCRATCH, genTotalFrameSize()); - GetEmitter()->emitIns_R_R_R(INS_add, size, REG_SCRATCH, REG_SCRATCH, REG_SPBASE); - GetEmitter()->emitIns_R_R_I(INS_ld, size, REG_SCRATCH, REG_SCRATCH, 0); - } - - GetEmitter()->emitIns_S_R_R(ins_Store(storeType), size, REG_SCRATCH, tmpReg, varNum, slotSize); - } - } - -#ifdef USING_SCOPE_INFO - { - psiMoveToStack(varNum); - } -#endif // USING_SCOPE_INFO - } - } - - if (regArgNum > 0) - { - for (int i = MAX_REG_ARG + MAX_FLOAT_REG_ARG - 1; i >= 0; i--) - { - if (regArg[i] != REG_NA && !isValidIntArgReg(regArgInit[i], compiler->info.compCallConv) && - !isValidFloatArgReg(regArgInit[i])) - { - assert(regArg[i] != regArgInit[i]); - assert(isValidIntArgReg(regArg[i], compiler->info.compCallConv) || isValidFloatArgReg(regArg[i])); - - GetEmitter()->emitIns_Mov(regArgAttr[i], regArgInit[i], regArg[i], false); - - regArgMaskLive &= ~genRegMask(regArg[i]); - regArg[i] = REG_NA; - regArgNum -= 1; - } - } - } - - if (regArgNum > 0) - { - for (int i = MAX_REG_ARG + MAX_FLOAT_REG_ARG - 1; i >= 0; i--) - { - if (regArg[i] != REG_NA) - { - assert(regArg[i] != regArgInit[i]); - - // regArg indexes list - unsigned indexList[MAX_REG_ARG + MAX_FLOAT_REG_ARG]; - int count = 0; // Number of nodes in list - bool loop = false; // List has a loop - - for (unsigned cur = i; regArg[cur] != REG_NA; count++) - { - if (cur == i && count > 0) - { - loop = true; - break; - } - - indexList[count] = cur; - - for (int count2 = 0; count2 < count; count2++) - { - // The list could not have backlinks except last to first case which handled above. - assert(cur != indexList[count2] && "Attempt to move several values on same register."); - } - assert(cur < MAX_REG_ARG + MAX_FLOAT_REG_ARG); - assert(isValidIntArgReg(regArg[cur], compiler->info.compCallConv) || - isValidFloatArgReg(regArg[cur])); - - if (isValidIntArgReg(regArgInit[cur], compiler->info.compCallConv)) - { - cur = regArgInit[cur] - REG_ARG_FIRST; - } - else if (isValidFloatArgReg(regArgInit[cur])) - { - cur = regArgInit[cur] - REG_ARG_FP_FIRST + MAX_REG_ARG; - } - else - { - assert(!"Argument register is neither valid float nor valid int argument register"); - } - } - - if (loop) - { - unsigned tmpArg = indexList[count - 1]; - - GetEmitter()->emitIns_Mov(regArgAttr[tmpArg], rsGetRsvdReg(), regArg[tmpArg], false); - count--; // Decrease count to not access last node which regArgInit points to start node i - assert(count > 0); - } - - for (int cur = count - 1; cur >= 0; cur--) - { - unsigned tmpArg = indexList[cur]; - - GetEmitter()->emitIns_Mov(regArgAttr[tmpArg], regArgInit[tmpArg], regArg[tmpArg], false); - - regArgMaskLive &= ~genRegMask(regArg[tmpArg]); - regArg[tmpArg] = REG_NA; - regArgNum--; - assert(regArgNum >= 0); - }; - - if (loop) - { - unsigned tmpArg = indexList[count]; // count was decreased for loop case - - GetEmitter()->emitIns_Mov(regArgAttr[i], regArg[tmpArg], rsGetRsvdReg(), false); - - regArgMaskLive &= ~genRegMask(regArg[tmpArg]); - regArg[tmpArg] = REG_NA; - regArgNum--; - } - assert(regArgNum >= 0); - } - } - } - - assert(regArgNum == 0); - assert(!regArgMaskLive); -} - #ifdef PROFILING_SUPPORTED //----------------------------------------------------------------------------------- // genProfilingEnterCallback: Generate the profiling function enter callback. diff --git a/src/coreclr/jit/targetriscv64.cpp b/src/coreclr/jit/targetriscv64.cpp index 29f71dc76d8fa2..581087b5cc63f8 100644 --- a/src/coreclr/jit/targetriscv64.cpp +++ b/src/coreclr/jit/targetriscv64.cpp @@ -36,6 +36,7 @@ RiscV64Classifier::RiscV64Classifier(const ClassifierInfo& info) , m_intRegs(intArgRegs, ArrLen(intArgRegs)) , m_floatRegs(fltArgRegs, ArrLen(fltArgRegs)) { + assert(!m_info.IsVarArgs); // TODO: varargs currently not supported on RISC-V } //----------------------------------------------------------------------------- @@ -57,8 +58,6 @@ ABIPassingInformation RiscV64Classifier::Classify(Compiler* comp, ClassLayout* structLayout, WellKnownArg /*wellKnownParam*/) { - assert(!m_info.IsVarArgs); // TODO: varargs currently not supported on RISC-V - StructFloatFieldInfoFlags flags = STRUCT_NO_FLOAT_FIELD; unsigned intFields = 0, floatFields = 0; unsigned passedSize; @@ -93,36 +92,13 @@ ABIPassingInformation RiscV64Classifier::Classify(Compiler* comp, } else { - assert(genTypeSize(type) <= TARGET_POINTER_SIZE); - - if (varTypeIsFloating(type)) - floatFields = 1; - passedSize = genTypeSize(type); + assert(passedSize <= TARGET_POINTER_SIZE); + floatFields = varTypeIsFloating(type) ? 1 : 0; } assert((floatFields > 0) || (intFields == 0)); - auto PassSlot = [this](bool inFloatReg, unsigned offset, unsigned size) -> ABIPassingSegment { - assert(size > 0); - assert(size <= TARGET_POINTER_SIZE); - if (inFloatReg) - { - return ABIPassingSegment::InRegister(m_floatRegs.Dequeue(), offset, size); - } - else if (m_intRegs.Count() > 0) - { - return ABIPassingSegment::InRegister(m_intRegs.Dequeue(), offset, size); - } - else - { - assert((m_stackArgSize % TARGET_POINTER_SIZE) == 0); - ABIPassingSegment seg = ABIPassingSegment::OnStack(m_stackArgSize, offset, size); - m_stackArgSize += TARGET_POINTER_SIZE; - return seg; - } - }; - if ((floatFields > 0) && (m_floatRegs.Count() >= floatFields) && (m_intRegs.Count() >= intFields)) { // Hardware floating-point calling convention @@ -151,27 +127,45 @@ ABIPassingInformation RiscV64Classifier::Classify(Compiler* comp, bool isSecondFloat = (flags & (STRUCT_FLOAT_FIELD_ONLY_TWO | STRUCT_FLOAT_FIELD_SECOND)) != 0; assert(isFirstFloat || isSecondFloat); - return {2, new (comp, CMK_ABI) ABIPassingSegment[]{PassSlot(isFirstFloat, 0, firstSize), - PassSlot(isSecondFloat, offset, secondSize)}}; + regNumber firstReg = (isFirstFloat ? m_floatRegs : m_intRegs).Dequeue(); + regNumber secondReg = (isSecondFloat ? m_floatRegs : m_intRegs).Dequeue(); + + return {2, new (comp, CMK_ABI) + ABIPassingSegment[]{ABIPassingSegment::InRegister(firstReg, 0, firstSize), + ABIPassingSegment::InRegister(secondReg, offset, secondSize)}}; } } else { // Integer calling convention + auto passSlot = [this](unsigned offset, unsigned size) -> ABIPassingSegment { + assert(size > 0); + assert(size <= TARGET_POINTER_SIZE); + if (m_intRegs.Count() > 0) + { + return ABIPassingSegment::InRegister(m_intRegs.Dequeue(), offset, size); + } + else + { + assert((m_stackArgSize % TARGET_POINTER_SIZE) == 0); + ABIPassingSegment seg = ABIPassingSegment::OnStack(m_stackArgSize, offset, size); + m_stackArgSize += TARGET_POINTER_SIZE; + return seg; + } + }; + if (passedSize <= TARGET_POINTER_SIZE) { - return ABIPassingInformation::FromSegment(comp, PassSlot(false, 0, passedSize)); + return ABIPassingInformation::FromSegment(comp, passSlot(0, passedSize)); } else { assert(varTypeIsStruct(type)); return {2, new (comp, CMK_ABI) - ABIPassingSegment[]{PassSlot(false, 0, TARGET_POINTER_SIZE), - PassSlot(false, TARGET_POINTER_SIZE, passedSize - TARGET_POINTER_SIZE)}}; + ABIPassingSegment[]{passSlot(0, TARGET_POINTER_SIZE), + passSlot(TARGET_POINTER_SIZE, passedSize - TARGET_POINTER_SIZE)}}; } } - - unreached(); } #endif // TARGET_RISCV64