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:
parent
b0118bebe3
commit
588b7753a7
5 changed files with 234 additions and 544 deletions
|
@ -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
|
||||
|
|
|
@ -2802,7 +2802,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|||
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
*/
|
||||
|
||||
#if !defined(TARGET_LOONGARCH64) && !defined(TARGET_RISCV64)
|
||||
#if !defined(TARGET_RISCV64)
|
||||
struct RegNode;
|
||||
|
||||
struct RegNodeEdge
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue