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

Enregister multireg lclVars #36862

Merged
merged 10 commits into from
Jun 11, 2020
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/coreclr/src/jit/codegen.h
Original file line number Diff line number Diff line change
Expand Up @@ -1117,7 +1117,9 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
void genUnspillLocal(
unsigned varNum, var_types type, GenTreeLclVar* lclNode, regNumber regNum, bool reSpill, bool isLastUse);
void genUnspillRegIfNeeded(GenTree* tree);
void genUnspillRegIfNeeded(GenTree* tree, unsigned multiRegIndex);
regNumber genConsumeReg(GenTree* tree);
regNumber genConsumeReg(GenTree* tree, unsigned multiRegIndex);
void genCopyRegIfNeeded(GenTree* tree, regNumber needReg);
void genConsumeRegAndCopy(GenTree* tree, regNumber needReg);

Expand All @@ -1130,6 +1132,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
}

void genRegCopy(GenTree* tree);
regNumber genRegCopy(GenTree* tree, unsigned multiRegIndex);
void genTransferRegGCState(regNumber dst, regNumber src);
void genConsumeAddress(GenTree* addr);
void genConsumeAddrMode(GenTreeAddrMode* mode);
Expand Down Expand Up @@ -1278,7 +1281,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
void genEHFinallyOrFilterRet(BasicBlock* block);
#endif // !FEATURE_EH_FUNCLETS

void genMultiRegStoreToLocal(GenTree* treeNode);
void genMultiRegStoreToSIMDLocal(GenTreeLclVar* lclNode);
void genMultiRegStoreToLocal(GenTreeLclVar* lclNode);

// Codegen for multi-register struct returns.
bool isStructReturn(GenTree* treeNode);
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/src/jit/codegenarm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1048,7 +1048,7 @@ void CodeGen::genCodeForStoreLclVar(GenTreeLclVar* tree)

// var = call, where call returns a multi-reg return value
// case is handled separately.
if (data->gtSkipReloadOrCopy()->IsMultiRegCall())
if (data->gtSkipReloadOrCopy()->IsMultiRegNode())
{
genMultiRegStoreToLocal(tree);
}
Expand Down
52 changes: 34 additions & 18 deletions src/coreclr/src/jit/codegenarm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1919,33 +1919,49 @@ void CodeGen::genCodeForStoreLclFld(GenTreeLclFld* tree)
// genCodeForStoreLclVar: Produce code for a GT_STORE_LCL_VAR node.
//
// Arguments:
// tree - the GT_STORE_LCL_VAR node
// lclNode - the GT_STORE_LCL_VAR node
//
void CodeGen::genCodeForStoreLclVar(GenTreeLclVar* tree)
void CodeGen::genCodeForStoreLclVar(GenTreeLclVar* lclNode)
{
GenTree* data = tree->gtOp1;
GenTree* data = lclNode->gtOp1;

// Stores from a multi-reg source are handled separately.
if (data->gtSkipReloadOrCopy()->IsMultiRegNode())
{
genMultiRegStoreToLocal(lclNode);
return;
}

// var = call, where call returns a multi-reg return value
// case is handled separately.
if (data->gtSkipReloadOrCopy()->IsMultiRegCall())
LclVarDsc* varDsc = compiler->lvaGetDesc(lclNode);
if (lclNode->IsMultiReg())
{
genMultiRegStoreToLocal(tree);
// This is the case of storing to a multi-reg HFA local from a fixed-size SIMD type.
assert(varTypeIsSIMD(data) && varDsc->lvIsHfa() && (varDsc->GetHfaType() == TYP_FLOAT));
regNumber operandReg = genConsumeReg(data);
unsigned int regCount = varDsc->lvFieldCnt;
for (unsigned i = 0; i < regCount; ++i)
{
regNumber varReg = lclNode->GetRegByIndex(i);
assert(varReg != REG_NA);
unsigned fieldLclNum = varDsc->lvFieldLclStart + i;
LclVarDsc* fieldVarDsc = compiler->lvaGetDesc(fieldLclNum);
assert(fieldVarDsc->TypeGet() == TYP_FLOAT);
GetEmitter()->emitIns_R_R_I(INS_dup, emitTypeSize(TYP_FLOAT), varReg, operandReg, i);
}
}
else
{
regNumber targetReg = tree->GetRegNum();
regNumber targetReg = lclNode->GetRegNum();
emitter* emit = GetEmitter();

unsigned varNum = tree->GetLclNum();
assert(varNum < compiler->lvaCount);
LclVarDsc* varDsc = compiler->lvaGetDesc(varNum);
var_types targetType = varDsc->GetRegisterType(tree);
unsigned varNum = lclNode->GetLclNum();
var_types targetType = varDsc->GetRegisterType(lclNode);

#ifdef FEATURE_SIMD
// storing of TYP_SIMD12 (i.e. Vector3) field
if (targetType == TYP_SIMD12)
{
genStoreLclTypeSIMD12(tree);
genStoreLclTypeSIMD12(lclNode);
return;
}
#endif // FEATURE_SIMD
Expand All @@ -1963,7 +1979,7 @@ void CodeGen::genCodeForStoreLclVar(GenTreeLclVar* tree)
if (targetReg != REG_NA)
{
emit->emitIns_R_I(INS_movi, emitActualTypeSize(targetType), targetReg, 0x00, INS_OPTS_16B);
genProduceReg(tree);
genProduceReg(lclNode);
}
else
{
Expand All @@ -1976,7 +1992,7 @@ void CodeGen::genCodeForStoreLclVar(GenTreeLclVar* tree)
assert(targetType == TYP_SIMD8);
GetEmitter()->emitIns_S_R(INS_str, EA_8BYTE, REG_ZR, varNum, 0);
}
genUpdateLife(tree);
genUpdateLife(lclNode);
}
return;
}
Expand All @@ -1992,14 +2008,14 @@ void CodeGen::genCodeForStoreLclVar(GenTreeLclVar* tree)

if (targetReg == REG_NA) // store into stack based LclVar
{
inst_set_SV_var(tree);
inst_set_SV_var(lclNode);

instruction ins = ins_Store(targetType);
emitAttr attr = emitActualTypeSize(targetType);

emit->emitIns_S_R(ins, attr, dataReg, varNum, /* offset */ 0);

genUpdateLife(tree);
genUpdateLife(lclNode);

varDsc->SetRegNum(REG_STK);
}
Expand All @@ -2010,7 +2026,7 @@ void CodeGen::genCodeForStoreLclVar(GenTreeLclVar* tree)
// Assign into targetReg when dataReg (from op1) is not the same register
inst_RV_RV(ins_Copy(targetType), targetReg, dataReg, targetType);
}
genProduceReg(tree);
genProduceReg(lclNode);
}
}
}
Expand Down
131 changes: 42 additions & 89 deletions src/coreclr/src/jit/codegenarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1354,116 +1354,69 @@ void CodeGen::genPutArgSplit(GenTreePutArgSplit* treeNode)
}
#endif // FEATURE_ARG_SPLIT

#ifdef FEATURE_SIMD
//----------------------------------------------------------------------------------
// genMultiRegStoreToLocal: store multi-reg return value of a call node to a local
// genMultiRegStoreToSIMDLocal: store multi-reg value to a single-reg SIMD local
//
// Arguments:
// treeNode - Gentree of GT_STORE_LCL_VAR
// lclNode - GentreeLclVar of GT_STORE_LCL_VAR
//
// Return Value:
// None
//
// Assumption:
// The child of store is a multi-reg node.
//
void CodeGen::genMultiRegStoreToLocal(GenTree* treeNode)
void CodeGen::genMultiRegStoreToSIMDLocal(GenTreeLclVar* lclNode)
{
assert(treeNode->OperGet() == GT_STORE_LCL_VAR);
assert(varTypeIsStruct(treeNode) || varTypeIsMultiReg(treeNode));
GenTree* op1 = treeNode->gtGetOp1();
GenTree* actualOp1 = op1->gtSkipReloadOrCopy();
regNumber dst = lclNode->GetRegNum();
GenTree* op1 = lclNode->gtGetOp1();
GenTree* actualOp1 = op1->gtSkipReloadOrCopy();
unsigned regCount =
actualOp1->IsMultiRegLclVar() ? actualOp1->AsLclVar()->GetFieldCount(compiler) : actualOp1->GetMultiRegCount();
assert(op1->IsMultiRegNode());
unsigned regCount = actualOp1->GetMultiRegCount();

// Assumption: current implementation requires that a multi-reg
// var in 'var = call' is flagged as lvIsMultiRegRet to prevent it from
// being promoted.
unsigned lclNum = treeNode->AsLclVarCommon()->GetLclNum();
LclVarDsc* varDsc = compiler->lvaGetDesc(lclNum);
if (op1->OperIs(GT_CALL))
{
assert(regCount <= MAX_RET_REG_COUNT);
noway_assert(varDsc->lvIsMultiRegRet);
}

genConsumeRegs(op1);

int offset = 0;

// Check for the case of an enregistered SIMD type that's returned in multiple registers.
if (varDsc->lvIsRegCandidate() && treeNode->GetRegNum() != REG_NA)
// Treat dst register as a homogenous vector with element size equal to the src size
// Insert pieces in reverse order
for (int i = regCount - 1; i >= 0; --i)
{
assert(varTypeIsSIMD(treeNode));
assert(regCount != 0);

regNumber dst = treeNode->GetRegNum();

// Treat dst register as a homogenous vector with element size equal to the src size
// Insert pieces in reverse order
for (int i = regCount - 1; i >= 0; --i)
var_types type = op1->gtSkipReloadOrCopy()->GetRegTypeByIndex(i);
regNumber reg = op1->GetRegByIndex(i);
if (op1->IsCopyOrReload())
{
var_types type = op1->gtSkipReloadOrCopy()->GetRegTypeByIndex(i);
regNumber reg = op1->GetRegByIndex(i);
if (op1->IsCopyOrReload())
{
// GT_COPY/GT_RELOAD will have valid reg for those positions
// that need to be copied or reloaded.
regNumber reloadReg = op1->AsCopyOrReload()->GetRegNumByIdx(i);
if (reloadReg != REG_NA)
{
reg = reloadReg;
}
}

assert(reg != REG_NA);
if (varTypeIsFloating(type))
// GT_COPY/GT_RELOAD will have valid reg for those positions
// that need to be copied or reloaded.
regNumber reloadReg = op1->AsCopyOrReload()->GetRegNumByIdx(i);
if (reloadReg != REG_NA)
{
// If the register piece was passed in a floating point register
// Use a vector mov element instruction
// src is not a vector, so it is in the first element reg[0]
// mov dst[i], reg[0]
// This effectively moves from `reg[0]` to `dst[i]`, leaving other dst bits unchanged till further
// iterations
// For the case where reg == dst, if we iterate so that we write dst[0] last, we eliminate the need for
// a temporary
GetEmitter()->emitIns_R_R_I_I(INS_mov, emitTypeSize(type), dst, reg, i, 0);
}
else
{
// If the register piece was passed in an integer register
// Use a vector mov from general purpose register instruction
// mov dst[i], reg
// This effectively moves from `reg` to `dst[i]`
GetEmitter()->emitIns_R_R_I(INS_mov, emitTypeSize(type), dst, reg, i);
reg = reloadReg;
}
}

genProduceReg(treeNode);
}
else
{
for (unsigned i = 0; i < regCount; ++i)
assert(reg != REG_NA);
if (varTypeIsFloating(type))
{
var_types type = actualOp1->GetRegTypeByIndex(i);
regNumber reg = op1->GetRegByIndex(i);
if (reg == REG_NA)
{
// GT_COPY/GT_RELOAD will have valid reg only for those positions
// that need to be copied or reloaded.
assert(op1->IsCopyOrReload());
reg = actualOp1->GetRegByIndex(i);
}

assert(reg != REG_NA);
GetEmitter()->emitIns_S_R(ins_Store(type), emitTypeSize(type), reg, lclNum, offset);
offset += genTypeSize(type);
// If the register piece was passed in a floating point register
// Use a vector mov element instruction
// src is not a vector, so it is in the first element reg[0]
// mov dst[i], reg[0]
// This effectively moves from `reg[0]` to `dst[i]`, leaving other dst bits unchanged till further
// iterations
// For the case where reg == dst, if we iterate so that we write dst[0] last, we eliminate the need for
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we save a mov when reg == dst?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Possibly, but this is pre-existing code (factored out of genMultiRegStoreToLocal) so I'd prefer not to make that change here.

// a temporary
GetEmitter()->emitIns_R_R_I_I(INS_mov, emitTypeSize(type), dst, reg, i, 0);
}
else
{
// If the register piece was passed in an integer register
// Use a vector mov from general purpose register instruction
// mov dst[i], reg
// This effectively moves from `reg` to `dst[i]`
GetEmitter()->emitIns_R_R_I(INS_mov, emitTypeSize(type), dst, reg, i);
}

// Update variable liveness.
genUpdateLife(treeNode);
varDsc->SetRegNum(REG_STK);
}

genProduceReg(lclNode);
}
#endif // FEATURE_SIMD

//------------------------------------------------------------------------
// genRangeCheck: generate code for GT_ARR_BOUNDS_CHECK node.
Expand Down
Loading