Skip to content

Commit

Permalink
JIT: Generalize parameter register to local mapping in the backend (#…
Browse files Browse the repository at this point in the history
…110795)

This PR generalizes the backend's treatment of which parameter registers are
mapped to which locals. Before this PR, parameter locals implicitly are defined
from the incoming parameter registers/stack. For old promotion, the backend then
knows that some registers can be mapped cleanly into the fields of those
promoted locals, allowing for efficient handling of structs passed in registers.

This PR adds an explicit data structure that indicates if the backend needs to
make any special mappings for parameter registers. The data structure maps
incoming register segments to locals. The PR changes the backend to use this
data structure as follows:
- Lowering is changed to create these mapping for old promoted local struct
  fields
- LSRA is changed to consult the mappings when building definitions for
  parameters
- Codegen is changed to consult the mappings when physically generating the code
  that moves incoming parameter registers to their corresponding local

In the future, the lowering part can be changed to recognize and optimize IR in
the initial block when it looks like creating more mappings will be beneficial.
The IR physical promotion creates will allow this optimization to kick in.
  • Loading branch information
jakobbotsch authored Dec 20, 2024
1 parent aa190d6 commit b6038a4
Show file tree
Hide file tree
Showing 12 changed files with 334 additions and 348 deletions.
25 changes: 17 additions & 8 deletions src/coreclr/jit/abi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -465,15 +465,24 @@ void ABIPassingInformation::Dump() const
}

const ABIPassingSegment& seg = Segment(i);
seg.Dump();
printf("\n");
}
}

if (seg.IsPassedInRegister())
{
printf("[%02u..%02u) reg %s\n", seg.Offset, seg.Offset + seg.Size, getRegName(seg.GetRegister()));
}
else
{
printf("[%02u..%02u) stack @ +%02u\n", seg.Offset, seg.Offset + seg.Size, seg.GetStackOffset());
}
//-----------------------------------------------------------------------------
// Dump:
// Dump the ABIPassingSegment to stdout.
//
void ABIPassingSegment::Dump() const
{
if (IsPassedInRegister())
{
printf("[%02u..%02u) reg %s", Offset, Offset + Size, getRegName(GetRegister()));
}
else
{
printf("[%02u..%02u) stack @ +%02u", Offset, Offset + Size, GetStackOffset());
}
}
#endif
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/jit/abi.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ class ABIPassingSegment

static ABIPassingSegment InRegister(regNumber reg, unsigned offset, unsigned size);
static ABIPassingSegment OnStack(unsigned stackOffset, unsigned offset, unsigned size);

#ifdef DEBUG
void Dump() const;
#endif
};

class ABIPassingSegmentIterator
Expand Down
7 changes: 4 additions & 3 deletions src/coreclr/jit/codegen.h
Original file line number Diff line number Diff line change
Expand Up @@ -260,9 +260,10 @@ class CodeGen final : public CodeGenInterface
regMaskTP genGetParameterHomingTempRegisterCandidates();

var_types genParamStackType(LclVarDsc* dsc, const ABIPassingSegment& seg);
void genSpillOrAddRegisterParam(unsigned lclNum, class RegGraph* graph);
void genSpillOrAddNonStandardRegisterParam(unsigned lclNum, regNumber sourceReg, class RegGraph* graph);
void genEnregisterIncomingStackArgs();
void genSpillOrAddRegisterParam(
unsigned lclNum, unsigned offset, unsigned paramLclNum, const ABIPassingSegment& seg, class RegGraph* graph);
void genSpillOrAddNonStandardRegisterParam(unsigned lclNum, regNumber sourceReg, class RegGraph* graph);
void genEnregisterIncomingStackArgs();
#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
void genEnregisterOSRArgsAndLocals(regNumber initReg, bool* pInitRegZeroed);
#else
Expand Down
146 changes: 72 additions & 74 deletions src/coreclr/jit/codegencommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3239,83 +3239,70 @@ var_types CodeGen::genParamStackType(LclVarDsc* dsc, const ABIPassingSegment& se
// to stack immediately, or by adding it to the register graph.
//
// Parameters:
// lclNum - Parameter local (or field of it)
// graph - The register graph to add to
//
void CodeGen::genSpillOrAddRegisterParam(unsigned lclNum, RegGraph* graph)
// lclNum - Target local
// offset - Offset into the target local
// paramLclNum - Local that is the actual parameter that has the incoming register
// segment - Register segment to either spill or put in the register graph
// graph - The register graph to add to
//
void CodeGen::genSpillOrAddRegisterParam(
unsigned lclNum, unsigned offset, unsigned paramLclNum, const ABIPassingSegment& segment, RegGraph* graph)
{
regMaskTP paramRegs = intRegState.rsCalleeRegArgMaskLiveIn | floatRegState.rsCalleeRegArgMaskLiveIn;
LclVarDsc* varDsc = compiler->lvaGetDesc(lclNum);
regMaskTP paramRegs = intRegState.rsCalleeRegArgMaskLiveIn | floatRegState.rsCalleeRegArgMaskLiveIn;

unsigned baseOffset = varDsc->lvIsStructField ? varDsc->lvFldOffset : 0;
unsigned size = varDsc->lvExactSize();
if (!segment.IsPassedInRegister() || ((paramRegs & genRegMask(segment.GetRegister())) == 0))
{
return;
}

unsigned paramLclNum = varDsc->lvIsStructField ? varDsc->lvParentLcl : lclNum;
LclVarDsc* paramVarDsc = compiler->lvaGetDesc(paramLclNum);
const ABIPassingInformation& abiInfo = compiler->lvaGetParameterABIInfo(paramLclNum);
for (const ABIPassingSegment& seg : abiInfo.Segments())
LclVarDsc* varDsc = compiler->lvaGetDesc(lclNum);
if (varDsc->lvOnFrame && (!varDsc->lvIsInReg() || varDsc->lvLiveInOutOfHndlr))
{
if (!seg.IsPassedInRegister() || ((paramRegs & genRegMask(seg.GetRegister())) == 0))
{
continue;
}
LclVarDsc* paramVarDsc = compiler->lvaGetDesc(paramLclNum);

if (seg.Offset + seg.Size <= baseOffset)
var_types storeType = genParamStackType(paramVarDsc, segment);
if ((varDsc->TypeGet() != TYP_STRUCT) && (genTypeSize(genActualType(varDsc)) < genTypeSize(storeType)))
{
continue;
// Can happen for struct fields due to padding.
storeType = genActualType(varDsc);
}

if (baseOffset + size <= seg.Offset)
{
continue;
}

if (varDsc->lvOnFrame && (!varDsc->lvIsInReg() || varDsc->lvLiveInOutOfHndlr))
{
var_types storeType = genParamStackType(paramVarDsc, seg);
if ((varDsc->TypeGet() != TYP_STRUCT) && (genTypeSize(genActualType(varDsc)) < genTypeSize(storeType)))
{
// Can happen for struct fields due to padding.
storeType = genActualType(varDsc);
}

GetEmitter()->emitIns_S_R(ins_Store(storeType), emitActualTypeSize(storeType), seg.GetRegister(), lclNum,
seg.Offset - baseOffset);
}
GetEmitter()->emitIns_S_R(ins_Store(storeType), emitActualTypeSize(storeType), segment.GetRegister(), lclNum,
offset);
}

if (!varDsc->lvIsInReg())
{
continue;
}
if (!varDsc->lvIsInReg())
{
return;
}

var_types edgeType = genActualType(varDsc->GetRegisterType());
// Some parameters can be passed in multiple registers but enregistered
// in a single one (e.g. SIMD types on arm64). In this case the edges
// we add here represent insertions of each element.
if (seg.Size < genTypeSize(edgeType))
{
edgeType = seg.GetRegisterType();
}
var_types edgeType = genActualType(varDsc->GetRegisterType());
// Some parameters can be passed in multiple registers but enregistered
// in a single one (e.g. SIMD types on arm64). In this case the edges
// we add here represent insertions of each element.
if (segment.Size < genTypeSize(edgeType))
{
edgeType = segment.GetRegisterType();
}

RegNode* sourceReg = graph->GetOrAdd(seg.GetRegister());
RegNode* destReg = graph->GetOrAdd(varDsc->GetRegNum());
RegNode* sourceReg = graph->GetOrAdd(segment.GetRegister());
RegNode* destReg = graph->GetOrAdd(varDsc->GetRegNum());

if ((sourceReg != destReg) || (baseOffset != seg.Offset))
{
if ((sourceReg != destReg) || (offset != 0))
{
#ifdef TARGET_ARM
if (edgeType == TYP_DOUBLE)
{
assert(seg.Offset == baseOffset);
graph->AddEdge(sourceReg, destReg, TYP_FLOAT, 0);
if (edgeType == TYP_DOUBLE)
{
assert(offset == 0);
graph->AddEdge(sourceReg, destReg, TYP_FLOAT, 0);

sourceReg = graph->GetOrAdd(REG_NEXT(sourceReg->reg));
destReg = graph->GetOrAdd(REG_NEXT(destReg->reg));
graph->AddEdge(sourceReg, destReg, TYP_FLOAT, 0);
continue;
}
#endif
graph->AddEdge(sourceReg, destReg, edgeType, seg.Offset - baseOffset);
sourceReg = graph->GetOrAdd(REG_NEXT(sourceReg->reg));
destReg = graph->GetOrAdd(REG_NEXT(destReg->reg));
graph->AddEdge(sourceReg, destReg, TYP_FLOAT, 0);
return;
}
#endif
graph->AddEdge(sourceReg, destReg, edgeType, offset);
}
}

Expand Down Expand Up @@ -3386,7 +3373,8 @@ void CodeGen::genHomeRegisterParams(regNumber initReg, bool* initRegStillZeroed)
}
}

if (compiler->info.compPublishStubParam && ((paramRegs & RBM_SECRET_STUB_PARAM) != RBM_NONE))
if (compiler->info.compPublishStubParam && ((paramRegs & RBM_SECRET_STUB_PARAM) != RBM_NONE) &&
compiler->lvaGetDesc(compiler->lvaStubArgumentVar)->lvOnFrame)
{
GetEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, REG_SECRET_STUB_PARAM,
compiler->lvaStubArgumentVar, 0);
Expand All @@ -3408,19 +3396,27 @@ void CodeGen::genHomeRegisterParams(regNumber initReg, bool* initRegStillZeroed)

for (unsigned lclNum = 0; lclNum < compiler->info.compArgsCount; lclNum++)
{
LclVarDsc* lclDsc = compiler->lvaGetDesc(lclNum);
LclVarDsc* lclDsc = compiler->lvaGetDesc(lclNum);
const ABIPassingInformation& abiInfo = compiler->lvaGetParameterABIInfo(lclNum);

if (compiler->lvaGetPromotionType(lclNum) == Compiler::PROMOTION_TYPE_INDEPENDENT)
for (const ABIPassingSegment& segment : abiInfo.Segments())
{
for (unsigned fld = 0; fld < lclDsc->lvFieldCnt; fld++)
if (!segment.IsPassedInRegister())
{
unsigned fieldLclNum = lclDsc->lvFieldLclStart + fld;
genSpillOrAddRegisterParam(fieldLclNum, &graph);
continue;
}

const ParameterRegisterLocalMapping* mapping =
compiler->FindParameterRegisterLocalMappingByRegister(segment.GetRegister());

if (mapping != nullptr)
{
genSpillOrAddRegisterParam(mapping->LclNum, mapping->Offset, lclNum, segment, &graph);
}
else
{
genSpillOrAddRegisterParam(lclNum, segment.Offset, lclNum, segment, &graph);
}
}
else
{
genSpillOrAddRegisterParam(lclNum, &graph);
}
}

Expand Down Expand Up @@ -5711,14 +5707,16 @@ void CodeGen::genFnProlog()
if (compiler->info.compCallConv == CorInfoCallConvExtension::Swift)
{
if ((compiler->lvaSwiftSelfArg != BAD_VAR_NUM) &&
((intRegState.rsCalleeRegArgMaskLiveIn & RBM_SWIFT_SELF) != 0))
((intRegState.rsCalleeRegArgMaskLiveIn & RBM_SWIFT_SELF) != 0) &&
compiler->lvaGetDesc(compiler->lvaSwiftSelfArg)->lvOnFrame)
{
GetEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, REG_SWIFT_SELF, compiler->lvaSwiftSelfArg, 0);
intRegState.rsCalleeRegArgMaskLiveIn &= ~RBM_SWIFT_SELF;
}

if ((compiler->lvaSwiftIndirectResultArg != BAD_VAR_NUM) &&
((intRegState.rsCalleeRegArgMaskLiveIn & theFixedRetBuffMask(CorInfoCallConvExtension::Swift)) != 0))
((intRegState.rsCalleeRegArgMaskLiveIn & theFixedRetBuffMask(CorInfoCallConvExtension::Swift)) != 0) &&
compiler->lvaGetDesc(compiler->lvaSwiftIndirectResultArg)->lvOnFrame)
{
GetEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE,
theFixedRetBuffReg(CorInfoCallConvExtension::Swift),
Expand Down
59 changes: 59 additions & 0 deletions src/coreclr/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4224,6 +4224,65 @@ bool Compiler::compRsvdRegCheck(FrameLayoutState curState)
}
#endif // TARGET_ARMARCH || TARGET_RISCV64

//------------------------------------------------------------------------
// FindParameterRegisterLocalMappingByRegister:
// Try to find a mapping that maps a particular parameter register to an
// incoming defined local.
//
// Returns:
// The mapping, or nullptr if no mapping was found for this register.
//
const ParameterRegisterLocalMapping* Compiler::FindParameterRegisterLocalMappingByRegister(regNumber reg)
{
if (m_paramRegLocalMappings == nullptr)
{
return nullptr;
}

for (int i = 0; i < m_paramRegLocalMappings->Height(); i++)
{
const ParameterRegisterLocalMapping& mapping = m_paramRegLocalMappings->BottomRef(i);
if (mapping.RegisterSegment->GetRegister() == reg)
{
return &mapping;
}
}

return nullptr;
}

//------------------------------------------------------------------------
// FindParameterRegisterLocalMappingByLocal:
// Try to find a mapping that maps a particular local from an incoming
// parameter register.
//
// Parameters:
// lclNum - The local to find a mapping for
// offset - The offset that the mapping maps to in the local
//
// Returns:
// The mapping, or nullptr if no mapping was found for this local.
//
const ParameterRegisterLocalMapping* Compiler::FindParameterRegisterLocalMappingByLocal(unsigned lclNum,
unsigned offset)
{
if (m_paramRegLocalMappings == nullptr)
{
return nullptr;
}

for (int i = 0; i < m_paramRegLocalMappings->Height(); i++)
{
const ParameterRegisterLocalMapping& mapping = m_paramRegLocalMappings->BottomRef(i);
if ((mapping.LclNum == lclNum) && (mapping.Offset == offset))
{
return &mapping;
}
}

return nullptr;
}

//------------------------------------------------------------------------
// compGetTieringName: get a string describing tiered compilation settings
// for this method
Expand Down
40 changes: 35 additions & 5 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,7 @@ class LclVarDsc

unsigned char lvIsParam : 1; // is this a parameter?
unsigned char lvIsRegArg : 1; // is this an argument that was passed by 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)

unsigned char lvOnFrame : 1; // (part of) the variable lives on the frame
Expand Down Expand Up @@ -2580,6 +2581,32 @@ struct CloneTryInfo
bool ScaleOriginalBlockProfile = false;
};

//------------------------------------------------------------------------
// ParameterRegisterLocalMapping:
// Contains mappings between a parameter register segment and a corresponding
// local. Used by the backend to know which locals are expected to contain
// which register parameters after the prolog.
//
struct ParameterRegisterLocalMapping
{
const ABIPassingSegment* RegisterSegment;
unsigned LclNum;
// Offset at which the register is inserted into the local. Used e.g. for
// HFAs on arm64 that might have been promoted as a single local (e.g.
// System.Numerics.Plane is passed in 3 float regs but enregistered as
// TYP_SIMD12).
// SysV 64 also see similar situations e.g. Vector3 being passed in
// xmm0[0..8), xmm1[8..12), but enregistered as one register.
unsigned Offset;

ParameterRegisterLocalMapping(const ABIPassingSegment* segment, unsigned lclNum, unsigned offset)
: RegisterSegment(segment)
, LclNum(lclNum)
, Offset(offset)
{
}
};

/*
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Expand Down Expand Up @@ -8262,8 +8289,6 @@ class Compiler
*/

public:
regNumber raUpdateRegStateForArg(RegState* regState, LclVarDsc* argDsc);

void raMarkStkVars();

#if FEATURE_PARTIAL_SIMD_CALLEE_SAVE
Expand Down Expand Up @@ -8295,8 +8320,14 @@ class Compiler
bool rpMustCreateEBPFrame(INDEBUG(const char** wbReason));

private:
Lowering* m_pLowering; // Lowering; needed to Lower IR that's added or modified after Lowering.
LinearScanInterface* m_pLinearScan; // Linear Scan allocator
Lowering* m_pLowering = nullptr; // Lowering; needed to Lower IR that's added or modified after Lowering.
LinearScanInterface* m_pLinearScan = nullptr; // Linear Scan allocator

public:
ArrayStack<ParameterRegisterLocalMapping>* m_paramRegLocalMappings = nullptr;

const ParameterRegisterLocalMapping* FindParameterRegisterLocalMappingByRegister(regNumber reg);
const ParameterRegisterLocalMapping* FindParameterRegisterLocalMappingByLocal(unsigned lclNum, unsigned offset);

/*
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Expand All @@ -8311,7 +8342,6 @@ class Compiler
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
*/

public:
// Get handles

void eeGetCallInfo(CORINFO_RESOLVED_TOKEN* pResolvedToken,
Expand Down
Loading

0 comments on commit b6038a4

Please sign in to comment.