Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit d92b629

Browse files
committed
Consider spilled lcl var as contained memory operands for codegen purpose.
1 parent 652f1f0 commit d92b629

File tree

9 files changed

+617
-113
lines changed

9 files changed

+617
-113
lines changed

src/jit/codegenxarch.cpp

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1238,8 +1238,13 @@ void CodeGen::genCodeForDivMod(GenTreeOp* treeNode)
12381238
genConsumeOperands(treeNode->AsOp());
12391239
if (varTypeIsFloating(targetType))
12401240
{
1241-
// divisor is not contained or if contained is a memory op
1242-
assert(!divisor->isContained() || divisor->isMemoryOp() || divisor->IsCnsFltOrDbl());
1241+
// divisor is not contained or if contained is a memory op.
1242+
// Note that a reg optional operand is a treated as a memory op
1243+
// if no register is allocated to it.
1244+
assert(!divisor->isContained() ||
1245+
divisor->isMemoryOp() ||
1246+
divisor->IsCnsFltOrDbl() ||
1247+
divisor->IsRegOptional());
12431248

12441249
// Floating point div/rem operation
12451250
assert(oper == GT_DIV || oper == GT_MOD);
@@ -1357,7 +1362,10 @@ void CodeGen::genCodeForBinary(GenTree* treeNode)
13571362
if (op1->isContained())
13581363
{
13591364
assert(treeNode->OperIsCommutative());
1360-
assert(op1->isMemoryOp() || op1->IsCnsNonZeroFltOrDbl() || op1->IsIntCnsFitsInI32());
1365+
assert(op1->isMemoryOp() ||
1366+
op1->IsCnsNonZeroFltOrDbl() ||
1367+
op1->IsIntCnsFitsInI32() ||
1368+
op1->IsRegOptional());
13611369

13621370
op1 = treeNode->gtGetOp2();
13631371
op2 = treeNode->gtGetOp1();
@@ -2203,8 +2211,8 @@ CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
22032211
bool isUnsignedMultiply = ((treeNode->gtFlags & GTF_UNSIGNED) != 0);
22042212
bool requiresOverflowCheck = treeNode->gtOverflowEx();
22052213

2206-
GenTree *op1 = treeNode->gtOp.gtOp1;
2207-
GenTree *op2 = treeNode->gtOp.gtOp2;
2214+
GenTree* op1 = treeNode->gtGetOp1();
2215+
GenTree* op2 = treeNode->gtGetOp2();
22082216

22092217
// there are 3 forms of x64 multiply:
22102218
// 1-op form with 128 result: RDX:RAX = RAX * rm
@@ -4958,6 +4966,18 @@ void CodeGen::genConsumeRegs(GenTree* tree)
49584966
// Now we need to consume the operands of the GT_AND node.
49594967
genConsumeOperands(tree->AsOp());
49604968
}
4969+
else if (tree->OperGet() == GT_LCL_VAR)
4970+
{
4971+
// A contained lcl var must be living on stack and marked as reg optional.
4972+
unsigned varNum = tree->AsLclVarCommon()->GetLclNum();
4973+
LclVarDsc* varDsc = compiler->lvaTable + varNum;
4974+
4975+
noway_assert(varDsc->lvRegNum == REG_STK);
4976+
noway_assert(tree->IsRegOptional());
4977+
4978+
// Update the life of reg optional lcl var.
4979+
genUpdateLife(tree);
4980+
}
49614981
else
49624982
{
49634983
assert(tree->OperIsLeaf());
@@ -5488,10 +5508,15 @@ void CodeGen::genStoreInd(GenTreePtr node)
54885508
rmwDst = data->gtGetOp2();
54895509
rmwSrc = data->gtGetOp1();
54905510
}
5511+
5512+
genConsumeRegs(rmwSrc);
54915513
}
54925514
else
54935515
{
5494-
// For unary RMW ops, src and dst of RMW memory op is the same.
5516+
// *(p) = oper *(p): Here addr = p, rmwsrc=rmwDst = *(p) i.e. GT_IND(p)
5517+
// For unary RMW ops, src and dst of RMW memory op is the same. Lower
5518+
// clears operand counts on rmwSrc and we don't need to perform a
5519+
// genConsumeReg() on it.
54955520
assert(storeInd->IsRMWDstOp1());
54965521
rmwSrc = data->gtGetOp1();
54975522
rmwDst = data->gtGetOp1();
@@ -5500,9 +5525,7 @@ void CodeGen::genStoreInd(GenTreePtr node)
55005525

55015526
assert(rmwSrc != nullptr);
55025527
assert(rmwDst != nullptr);
5503-
assert(Lowering::IndirsAreEquivalent(rmwDst, storeInd));
5504-
5505-
genConsumeRegs(rmwSrc);
5528+
assert(Lowering::IndirsAreEquivalent(rmwDst, storeInd));
55065529
}
55075530
else
55085531
{

src/jit/emitxarch.cpp

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2845,17 +2845,22 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G
28452845
{
28462846
// dst can only be a reg or modrm
28472847
assert(!dst->isContained() ||
2848-
dst->isContainedIndir() ||
2849-
dst->isContainedLclField() ||
2848+
dst->isContainedMemoryOp() ||
28502849
instrIs3opImul(ins)); // dst on these isn't really the dst
28512850

2851+
#ifdef DEBUG
28522852
// src can be anything but both src and dst cannot be addr modes
28532853
// or at least cannot be contained addr modes
2854-
if (dst->isContainedIndir())
2855-
assert(!src->isContainedIndir());
2854+
if (dst->isContainedMemoryOp())
2855+
{
2856+
assert(!src->isContainedMemoryOp());
2857+
}
28562858

2857-
if (src->isContainedLclField())
2858-
assert(!dst->isContained());
2859+
if (src->isContainedMemoryOp())
2860+
{
2861+
assert(!dst->isContainedMemoryOp());
2862+
}
2863+
#endif
28592864

28602865
// find which operand is a memory op (if any)
28612866
// and what its base is
@@ -2890,7 +2895,7 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G
28902895
}
28912896

28922897
// find local field if any
2893-
GenTreeLclFld* lclField = nullptr;
2898+
GenTreeLclFld* lclField = nullptr;
28942899
if (src->isContainedLclField())
28952900
{
28962901
lclField = src->AsLclFld();
@@ -2900,9 +2905,22 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G
29002905
lclField = dst->AsLclFld();
29012906
}
29022907

2908+
// find contained lcl var if any
2909+
GenTreeLclVar* lclVar = nullptr;
2910+
if (src->isContainedLclVar())
2911+
{
2912+
assert(src->IsRegOptional());
2913+
lclVar = src->AsLclVar();
2914+
}
2915+
else if (dst->isContainedLclVar())
2916+
{
2917+
assert(dst->IsRegOptional());
2918+
lclVar = dst->AsLclVar();
2919+
}
2920+
29032921
// First handle the simple non-memory cases
29042922
//
2905-
if ((mem == nullptr) && (lclField == nullptr))
2923+
if ((mem == nullptr) && (lclField == nullptr) && (lclVar == nullptr))
29062924
{
29072925
if (intConst != nullptr)
29082926
{
@@ -2938,15 +2956,27 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G
29382956
return dst->gtRegNum;
29392957
}
29402958

2941-
// Next handle the cases where we have a stack based local memory operand
2959+
// Next handle the cases where we have a stack based local memory operand.
29422960
//
2943-
if (lclField)
2961+
unsigned varNum = BAD_VAR_NUM;
2962+
unsigned offset = (unsigned) -1;
2963+
2964+
if (lclField != nullptr)
29442965
{
2945-
unsigned offset = lclField->gtLclFld.gtLclOffs;
2946-
unsigned varNum = lclField->gtLclVarCommon.gtLclNum;
2966+
varNum = lclField->AsLclVarCommon()->GetLclNum();
2967+
offset = lclField->gtLclFld.gtLclOffs;
2968+
}
2969+
else if (lclVar != nullptr)
2970+
{
2971+
varNum = lclVar->AsLclVarCommon()->GetLclNum();
2972+
offset = 0;
2973+
}
29472974

2975+
if (varNum != BAD_VAR_NUM)
2976+
{
29482977
// Is the memory op in the source position?
2949-
if (src->isContainedLclField())
2978+
if (src->isContainedLclField() ||
2979+
src->isContainedLclVar())
29502980
{
29512981
if (instrHasImplicitRegPairDest(ins))
29522982
{
@@ -2964,6 +2994,7 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G
29642994
else // The memory op is in the dest position.
29652995
{
29662996
assert(dst->gtRegNum == REG_NA);
2997+
29672998
// src could be int or reg
29682999
if (src->isContainedIntOrIImmed())
29693000
{

src/jit/gentree.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12869,6 +12869,19 @@ BasicBlock* BasicBlock::GetSucc(unsigned i, Compiler * comp)
1286912869
}
1287012870
}
1287112871

12872+
// -------------------------------------------------------------------------
12873+
// IsRegOptional: Returns true if this gentree node is marked by lowering to
12874+
// indicate that codegen can still generate code even if it wasn't allocated
12875+
// a register.
12876+
bool GenTree::IsRegOptional() const
12877+
{
12878+
#ifdef LEGACY_BACKEND
12879+
return false;
12880+
#else
12881+
return gtLsraInfo.regOptional;
12882+
#endif
12883+
}
12884+
1287212885
bool GenTree::IsPhiDefn()
1287312886
{
1287412887
bool res =

src/jit/gentree.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -495,11 +495,16 @@ struct GenTree
495495

496496
bool isContainedLclField() const { return isContained() && isLclField(); }
497497

498+
bool isContainedLclVar() const { return isContained() && (OperGet() == GT_LCL_VAR); }
499+
498500
// Indicates whether it is a memory op.
499501
// Right now it includes Indir and LclField ops.
500502
bool isMemoryOp() const { return isIndir() || isLclField(); }
501503

502-
bool isContainedMemoryOp() const { return isContained() && isMemoryOp(); }
504+
bool isContainedMemoryOp() const
505+
{
506+
return (isContained() && isMemoryOp()) || isContainedLclVar();
507+
}
503508

504509
regNumber GetRegNum() const
505510
{
@@ -1621,6 +1626,11 @@ struct GenTree
16211626
inline var_types CastFromType();
16221627
inline var_types& CastToType();
16231628

1629+
// Returns true if this gentree node is marked by lowering to indicate
1630+
// that codegen can still generate code even if it wasn't allocated a
1631+
// register.
1632+
bool IsRegOptional() const;
1633+
16241634
// Returns "true" iff "*this" is an assignment (GT_ASG) tree that defines an SSA name (lcl = phi(...));
16251635
bool IsPhiDefn();
16261636

src/jit/lower.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ class Lowering : public Phase
145145
void TreeNodeInfoInit(GenTreePtr* tree, GenTree* parent);
146146
#if defined(_TARGET_XARCH_)
147147
void TreeNodeInfoInitSimple(GenTree* tree);
148+
void SetRegOptionalForBinOp(GenTree* tree);
149+
void TryToSetRegOptional(GenTree* operand);
148150
#endif // defined(_TARGET_XARCH_)
149151
void TreeNodeInfoInitReturn(GenTree* tree);
150152
void TreeNodeInfoInitShiftRotate(GenTree* tree);

0 commit comments

Comments
 (0)