Skip to content

Commit

Permalink
Enregister multireg lclVars (#36862)
Browse files Browse the repository at this point in the history
* Enregister multireg lclVars

Allow struct lclVars that are returned in multiple registers to be
enregistered, as long as the fields are a match for the registers.

Fix #34105
  • Loading branch information
CarolEidt committed Jun 11, 2020
1 parent f68bb5b commit 7ef49af
Show file tree
Hide file tree
Showing 27 changed files with 1,661 additions and 690 deletions.
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
// 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

0 comments on commit 7ef49af

Please sign in to comment.