Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RISC-V] Use common CodeGen::genHomeRegisterParams #101288

Merged
merged 16 commits into from Apr 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 8 additions & 6 deletions src/coreclr/jit/abi.cpp
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
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
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
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();
Comment on lines +4162 to +4163
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: for the future, we generally restrict our ternary use to very simple cases. I think the existing code was easier to read.
https://github.com/dotnet/runtime/blob/main/docs/coding-guidelines/clr-jit-coding-conventions.md#1519-ternary-operators

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

modified it within #101656

#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