Skip to content

Commit

Permalink
[RISC-V] Use common CodeGen::genHomeRegisterParams (dotnet#101288)
Browse files Browse the repository at this point in the history
* Code review from dotnet#101114

* Use common genHomeRegisterParams on RISC-V

* Make passSlot integer-only because we know hardware floating-point calling convention passes in registers only

* Make a RISC-V specific routine for homing stack parts of split parameters.

* Move genHomeStackPartOfSplitParameter out of genHomeSwiftStructParameters, share stack segment homing with Swift code
  • Loading branch information
tomeksowi authored and michaelgsharp committed May 8, 2024
1 parent ea33b18 commit f9fac27
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 538 deletions.
14 changes: 8 additions & 6 deletions src/coreclr/jit/abi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

//-----------------------------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/jit/abi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/jit/codegen.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
154 changes: 101 additions & 53 deletions src/coreclr/jit/codegencommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2802,7 +2802,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
*/

#if !defined(TARGET_RISCV64)
struct RegNode;

struct RegNodeEdge
Expand Down Expand Up @@ -3346,8 +3345,6 @@ void CodeGen::genHomeRegisterParams(regNumber initReg, bool* initRegStillZeroed)
}
}

#endif

// -----------------------------------------------------------------------------
// genGetParameterHomingTempRegisterCandidates: Get the registers that are
// usable during register homing.
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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
}

/*-----------------------------------------------------------------------------
*
Expand Down Expand Up @@ -5547,6 +5593,8 @@ void CodeGen::genFnProlog()
{
compiler->lvaUpdateArgsWithInitialReg();

genHomeStackPartOfSplitParameter(initReg, &initRegZeroed);

if ((intRegState.rsCalleeRegArgMaskLiveIn | floatRegState.rsCalleeRegArgMaskLiveIn) != RBM_NONE)
{
genHomeRegisterParams(initReg, &initRegZeroed);
Expand Down
Loading

0 comments on commit f9fac27

Please sign in to comment.