-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
JIT: Generalize parameter register to local mapping in the backend #110795
Changes from all commits
794536f
db15f94
8e8c913
e504b4b
bf196bd
559baf0
6d1b57e
31b10ee
a4fa310
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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); | ||
} | ||
} | ||
|
||
|
@@ -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) | ||
Comment on lines
+3376
to
+3377
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change was required because of the unification of the code in LSRA that figures out which registers are live-in. That code marks all registers assigned to untracked locals as live-in in |
||
{ | ||
GetEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, REG_SECRET_STUB_PARAM, | ||
compiler->lvaStubArgumentVar, 0); | ||
|
@@ -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); | ||
} | ||
} | ||
|
||
|
@@ -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), | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4221,6 +4221,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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i am assuming an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, the number of entries is inherently limited because of the small number of registers used for parameters, so not much searching is required here. |
||
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 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
@@ -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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Eventually I'd like to get rid of this field entirely and require that all mappings are cleanly mapped. However, the cases described in the comment above will need to be handled in a different way than they do today then. One way would be to create new locals in lowering and insert IR in the init BB to reconstruct the destination local from the multiple locals when we detect these cases. |
||
|
||
ParameterRegisterLocalMapping(const ABIPassingSegment* segment, unsigned lclNum, unsigned offset) | ||
: RegisterSegment(segment) | ||
, LclNum(lclNum) | ||
, Offset(offset) | ||
{ | ||
} | ||
}; | ||
|
||
/* | ||
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX | ||
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX | ||
|
@@ -8258,8 +8285,6 @@ class Compiler | |
*/ | ||
|
||
public: | ||
regNumber raUpdateRegStateForArg(RegState* regState, LclVarDsc* argDsc); | ||
|
||
void raMarkStkVars(); | ||
|
||
#if FEATURE_PARTIAL_SIMD_CALLEE_SAVE | ||
|
@@ -8291,8 +8316,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 | ||
|
@@ -8307,7 +8338,6 @@ class Compiler | |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX | ||
*/ | ||
|
||
public: | ||
// Get handles | ||
|
||
void eeGetCallInfo(CORINFO_RESOLVED_TOKEN* pResolvedToken, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No actual diffs here, just refactoring the function to take the segment directly so that we can get it from the new data structure in the caller.