Skip to content

Commit

Permalink
JIT: Remove LclVarDsc::GetArgReg and LclVarDsc::GetOtherArgReg us…
Browse files Browse the repository at this point in the history
…es (dotnet#112652)

Switch these to use new ABI info.

The only uses left are in logging and in the old ABI classification
happening during `lvaInitUserArgs`. Once we remove uses of all the
information stored there we can get rid of the fields entirely.
  • Loading branch information
jakobbotsch authored Feb 19, 2025
1 parent 4dde471 commit 0ddd0c4
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 116 deletions.
15 changes: 9 additions & 6 deletions src/coreclr/jit/codegencommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4582,9 +4582,10 @@ void CodeGen::genReportGenericContextArg(regNumber initReg, bool* pInitRegZeroed
#endif

// Load from the argument register only if it is not prespilled.
if (compiler->lvaIsRegArgument(contextArg) && !isPrespilledForProfiling)
const ABIPassingInformation& abiInfo = compiler->lvaGetParameterABIInfo(contextArg);
if (abiInfo.HasExactlyOneRegisterSegment() && !isPrespilledForProfiling)
{
reg = varDsc->GetArgReg();
reg = abiInfo.Segment(0).GetRegister();
}
else
{
Expand Down Expand Up @@ -6265,13 +6266,15 @@ unsigned CodeGen::getFirstArgWithStackSlot()
// that's passed on the stack.
for (unsigned i = 0; i < compiler->info.compArgsCount; i++)
{
LclVarDsc* varDsc = compiler->lvaGetDesc(i);

// We should have found a stack parameter (and broken out of this loop) before
// we find any non-parameters.
assert(varDsc->lvIsParam);
assert(compiler->lvaGetDesc(i)->lvIsParam);

const ABIPassingInformation& abiInfo = compiler->lvaGetParameterABIInfo(i);
// We do not expect to need this function in ambiguous cases.
assert(!abiInfo.IsSplitAcrossRegistersAndStack());

if (varDsc->GetArgReg() == REG_STK)
if (abiInfo.HasAnyStackSegment())
{
return i;
}
Expand Down
38 changes: 13 additions & 25 deletions src/coreclr/jit/codegenxarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4210,7 +4210,9 @@ void CodeGen::genClearStackVec3ArgUpperBits()
{
// Assume that for x64 linux, an argument is fully in registers
// or fully on stack.
regNumber argReg = varDsc->GetOtherArgReg();
const ABIPassingInformation& abiInfo = compiler->lvaGetParameterABIInfo(varNum);
assert((abiInfo.NumSegments == 2) && !abiInfo.HasAnyStackSegment());
regNumber argReg = abiInfo.Segment(1).GetRegister();
genSimd12UpperClear(argReg);
}
}
Expand Down Expand Up @@ -7920,25 +7922,6 @@ unsigned CodeGen::getBaseVarForPutArgStk(GenTree* treeNode)
{
// See the note in the function header re: finding the first stack passed argument.
baseVarNum = getFirstArgWithStackSlot();
assert(baseVarNum != BAD_VAR_NUM);

#ifdef DEBUG
// This must be a fast tail call.
assert(treeNode->AsPutArgStk()->gtCall->AsCall()->IsFastTailCall());

// Since it is a fast tail call, the existence of first incoming arg is guaranteed
// because fast tail call requires that in-coming arg area of caller is >= out-going
// arg area required for tail call.
LclVarDsc* varDsc = compiler->lvaGetDesc(baseVarNum);
assert(varDsc != nullptr);

#ifdef UNIX_AMD64_ABI
assert(!varDsc->lvIsRegArg && varDsc->GetArgReg() == REG_STK);
#else // !UNIX_AMD64_ABI
// On Windows this assert is always true. The first argument will always be in REG_ARG_0 or REG_FLTARG_0.
assert(varDsc->lvIsRegArg && (varDsc->GetArgReg() == REG_ARG_0 || varDsc->GetArgReg() == REG_FLTARG_0));
#endif // !UNIX_AMD64_ABI
#endif // !DEBUG
}
else
{
Expand Down Expand Up @@ -9744,7 +9727,7 @@ void CodeGen::genProfilingEnterCallback(regNumber initReg, bool* pInitRegZeroed)
return;
}

#if !defined(UNIX_AMD64_ABI)
#if defined(WINDOWS_AMD64_ABI)

unsigned varNum;
LclVarDsc* varDsc;
Expand All @@ -9766,13 +9749,15 @@ void CodeGen::genProfilingEnterCallback(regNumber initReg, bool* pInitRegZeroed)
{
noway_assert(varDsc->lvIsParam);

if (!varDsc->lvIsRegArg)
const ABIPassingInformation& abiInfo = compiler->lvaGetParameterABIInfo(varNum);
if (!abiInfo.HasExactlyOneRegisterSegment())
{
assert(!abiInfo.HasAnyRegisterSegment());
continue;
}

var_types storeType = varDsc->GetRegisterType();
regNumber argReg = varDsc->GetArgReg();
regNumber argReg = abiInfo.Segment(0).GetRegister();

instruction store_ins = ins_Store(storeType);

Expand Down Expand Up @@ -9827,13 +9812,16 @@ void CodeGen::genProfilingEnterCallback(regNumber initReg, bool* pInitRegZeroed)
{
noway_assert(varDsc->lvIsParam);

if (!varDsc->lvIsRegArg)
const ABIPassingInformation& abiInfo = compiler->lvaGetParameterABIInfo(varNum);

if (!abiInfo.HasExactlyOneRegisterSegment())
{
assert(!abiInfo.HasAnyRegisterSegment());
continue;
}

var_types loadType = varDsc->GetRegisterType();
regNumber argReg = varDsc->GetArgReg();
regNumber argReg = abiInfo.Segment(0).GetRegister();

instruction load_ins = ins_Load(loadType);

Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,7 @@ class LclVarDsc
var_types lvType : 5; // TYP_INT/LONG/FLOAT/DOUBLE/REF

unsigned char lvIsParam : 1; // is this a parameter?
unsigned char lvIsRegArg : 1; // is this an argument that was passed by register?
unsigned char lvIsRegArg : 1; // is any part of this parameter passed in a register?
unsigned char lvIsParamRegTarget : 1; // is this the target of a param reg to local mapping?
unsigned char lvFramePointerBased : 1; // 0 = off of REG_SPBASE (e.g., ESP), 1 = off of REG_FPBASE (e.g., EBP)

Expand Down
48 changes: 40 additions & 8 deletions src/coreclr/jit/lclvars.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5858,8 +5858,21 @@ void Compiler::lvaFixVirtualFrameOffsets()
#ifdef TARGET_ARM
bool Compiler::lvaIsPreSpilled(unsigned lclNum, regMaskTP preSpillMask)
{
const LclVarDsc& desc = lvaTable[lclNum];
return desc.lvIsRegArg && (preSpillMask & genRegMask(desc.GetArgReg()));
LclVarDsc* dsc = lvaGetDesc(lclNum);
if (dsc->lvIsStructField)
{
lclNum = dsc->lvParentLcl;
}
const ABIPassingInformation& abiInfo = lvaGetParameterABIInfo(lclNum);
for (const ABIPassingSegment& segment : abiInfo.Segments())
{
if (segment.IsPassedInRegister() && ((preSpillMask & segment.GetRegisterMask()) != RBM_NONE))
{
return true;
}
}

return false;
}
#endif // TARGET_ARM

Expand Down Expand Up @@ -6620,12 +6633,31 @@ void Compiler::lvaAssignVirtualFrameOffsetsToLocals()
if (varDsc->lvIsParam)
{
#ifdef TARGET_ARM64
if (info.compIsVarArgs && varDsc->lvIsRegArg &&
(varDsc->GetArgReg() != theFixedRetBuffReg(info.compCallConv)))
if (info.compIsVarArgs && varDsc->lvIsRegArg && (lclNum != info.compRetBuffArg))
{
// Stack offset to varargs (parameters) should point to home area which will be preallocated.
const unsigned regArgNum = genMapIntRegNumToRegArgNum(varDsc->GetArgReg(), info.compCallConv);
varDsc->SetStackOffset(-initialStkOffs + regArgNum * REGSIZE_BYTES);
const ABIPassingInformation& abiInfo =
lvaGetParameterABIInfo(varDsc->lvIsStructField ? varDsc->lvParentLcl : lclNum);
bool found = false;
for (const ABIPassingSegment& segment : abiInfo.Segments())
{
if (!segment.IsPassedInRegister())
{
continue;
}

if (varDsc->lvIsStructField && (segment.Offset != varDsc->lvFldOffset))
{
continue;
}

found = true;
// Stack offset to varargs (parameters) should point to home area which will be preallocated.
const unsigned regArgNum = genMapIntRegNumToRegArgNum(segment.GetRegister(), info.compCallConv);
varDsc->SetStackOffset(-initialStkOffs + regArgNum * REGSIZE_BYTES);
break;
}

assert(found);
continue;
}
#endif
Expand Down Expand Up @@ -6932,7 +6964,7 @@ bool Compiler::lvaParamHasLocalStackSpace(unsigned lclNum)
// On ARM we spill the registers in codeGen->regSet.rsMaskPreSpillRegArg
// in the prolog, thus they don't need stack frame space.
//
if ((codeGen->regSet.rsMaskPreSpillRegs(false) & genRegMask(varDsc->GetArgReg())) != 0)
if (lvaIsPreSpilled(lclNum, codeGen->regSet.rsMaskPreSpillRegs(false)))
{
assert(varDsc->GetStackOffset() != BAD_STK_OFFS);
return false;
Expand Down
8 changes: 1 addition & 7 deletions src/coreclr/jit/lsra.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1621,13 +1621,7 @@ bool LinearScan::isRegCandidate(LclVarDsc* varDsc)
// vars will have `lvMustInit` set, because emitter has poor support for struct liveness,
// but if the variable is tracked the prolog generator would expect it to be in liveIn set,
// so an assert in `genFnProlog` will fire.
bool isRegCandidate = compiler->compEnregStructLocals() && !varDsc->HasGCPtr();
#if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
// The LoongArch64's ABI which the float args within a struct maybe passed by integer register
// when no float register left but free integer register.
isRegCandidate &= !genIsValidFloatReg(varDsc->GetOtherArgReg());
#endif
return isRegCandidate;
return compiler->compEnregStructLocals() && !varDsc->HasGCPtr();
}

case TYP_UNDEF:
Expand Down
92 changes: 23 additions & 69 deletions src/coreclr/jit/scopeinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1702,86 +1702,40 @@ void CodeGen::psiBegProlog()
}
siVarLoc varLocation;

if (lclVarDsc->lvIsRegArg)
regNumber reg1 = REG_NA;
regNumber reg2 = REG_NA;

const ABIPassingInformation& abiInfo = compiler->lvaGetParameterABIInfo(varScope->vsdVarNum);
for (const ABIPassingSegment& segment : abiInfo.Segments())
{
bool isStructHandled = false;
#if defined(UNIX_AMD64_ABI)
SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc;
if (varTypeIsStruct(lclVarDsc))
if (segment.IsPassedInRegister())
{
CORINFO_CLASS_HANDLE typeHnd = lclVarDsc->GetLayout()->GetClassHandle();
assert(typeHnd != nullptr);
compiler->eeGetSystemVAmd64PassStructInRegisterDescriptor(typeHnd, &structDesc);
if (structDesc.passedInRegisters)
if (reg1 == REG_NA)
{
regNumber regNum = REG_NA;
regNumber otherRegNum = REG_NA;
for (unsigned nCnt = 0; nCnt < structDesc.eightByteCount; nCnt++)
{
if (nCnt == 0)
{
regNum = lclVarDsc->GetArgReg();
}
else if (nCnt == 1)
{
otherRegNum = lclVarDsc->GetOtherArgReg();
}
else
{
assert(false && "Invalid eightbyte number.");
}
}

varLocation.storeVariableInRegisters(regNum, otherRegNum);
reg1 = segment.GetRegister();
}
else
{
// Stack passed argument. Get the offset from the caller's frame.
varLocation.storeVariableOnStack(REG_SPBASE, psiGetVarStackOffset(lclVarDsc));
reg2 = segment.GetRegister();
break;
}

isStructHandled = true;
}
#endif // !defined(UNIX_AMD64_ABI)
if (!isStructHandled)
else
{
#ifdef DEBUG
#if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
var_types regType;
if (varTypeIsStruct(lclVarDsc))
{
// Must be <= 16 bytes or else it wouldn't be passed in registers,
// which can be bigger (and is handled above).
noway_assert(EA_SIZE_IN_BYTES(lclVarDsc->lvSize()) <= 16);
if (emitter::isFloatReg(lclVarDsc->GetArgReg()))
{
regType = TYP_DOUBLE;
}
else
{
regType = lclVarDsc->GetLayout()->GetGCPtrType(0);
}
}
else
{
regType = compiler->mangleVarArgsType(lclVarDsc->TypeGet());
if (emitter::isGeneralRegisterOrR0(lclVarDsc->GetArgReg()) && isFloatRegType(regType))
{
// For LoongArch64 and RISCV64's ABI, the float args may be passed by integer register.
regType = TYP_LONG;
}
}
#else
var_types regType = compiler->mangleVarArgsType(lclVarDsc->TypeGet());
if (lclVarDsc->lvIsHfaRegArg())
{
regType = lclVarDsc->GetHfaType();
}
#endif // defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
#endif // DEBUG
varLocation.storeVariableInRegisters(lclVarDsc->GetArgReg(), REG_NA);
break;
}
}

// We only report multiple registers on SysV ABI. On other ABIs we
// report only the first register.
#ifndef UNIX_AMD64_ABI
reg2 = REG_NA;
#endif

if (reg1 != REG_NA)
{
varLocation.storeVariableInRegisters(reg1, reg2);
}
else
{
varLocation.storeVariableOnStack(REG_SPBASE, psiGetVarStackOffset(lclVarDsc));
Expand Down

0 comments on commit 0ddd0c4

Please sign in to comment.