1
0
Fork 0
mirror of https://github.com/VSadov/Satori.git synced 2025-06-10 01:50:53 +09:00

[LoongArch64] add new ABI classifier. (#101224)

* add new ABI classifier.

* share `CodeGen::genHomeRegisterParams` with XARCH-ARMARCH.
This commit is contained in:
Qiao Pengcheng 2024-04-18 22:13:17 +08:00 committed by GitHub
parent b0118bebe3
commit 588b7753a7
Signed by: github
GPG key ID: B5690EEEBB952194
5 changed files with 234 additions and 544 deletions

View file

@ -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 @@ public:
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

View file

@ -2802,7 +2802,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
*/
#if !defined(TARGET_LOONGARCH64) && !defined(TARGET_RISCV64)
#if !defined(TARGET_RISCV64)
struct RegNode;
struct RegNodeEdge

View file

@ -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.

View file

@ -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)
{
PlatformClassifier classifier(cInfo);
lvaClassifyParameterABI(classifier);
}
#endif
#ifdef DEBUG
if (lvaParameterPassingInfo == nullptr)

View file

@ -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