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

Commit 34e1626

Browse files
authored
Merge pull request #6326 from sivarv/phase2
Support for reg optional tree temps.
2 parents 4529b0a + 0a817e0 commit 34e1626

File tree

10 files changed

+418
-120
lines changed

10 files changed

+418
-120
lines changed

src/jit/codegencommon.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,6 +1398,29 @@ regNumber CodeGenInterface::genGetThisArgReg(GenTreePtr call)
13981398
return REG_ARG_0;
13991399
}
14001400

1401+
//----------------------------------------------------------------------
1402+
// getSpillTempDsc: get the TempDsc corresponding to a spilled tree.
1403+
//
1404+
// Arguments:
1405+
// tree - spilled GenTree node
1406+
//
1407+
// Return Value:
1408+
// TempDsc corresponding to tree
1409+
TempDsc* CodeGenInterface::getSpillTempDsc(GenTree* tree)
1410+
{
1411+
// tree must be in spilled state.
1412+
assert((tree->gtFlags & GTF_SPILLED) != 0);
1413+
1414+
// Get the tree's SpillDsc.
1415+
RegSet::SpillDsc* prevDsc;
1416+
RegSet::SpillDsc* spillDsc = regSet.rsGetSpillInfo(tree, tree->gtRegNum, &prevDsc);
1417+
assert(spillDsc != nullptr);
1418+
1419+
// Get the temp desc.
1420+
TempDsc* temp = regSet.rsGetSpillTempWord(tree->gtRegNum, spillDsc, prevDsc);
1421+
return temp;
1422+
}
1423+
14011424
#ifdef _TARGET_XARCH_
14021425

14031426
#ifdef _TARGET_AMD64_

src/jit/codegeninterface.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,9 @@ class CodeGenInterface
313313
void SpillFloat (regNumber reg, bool bIsCall = false);
314314
#endif // LEGACY_BACKEND
315315

316+
// The following method is used by xarch emitter for handling contained tree temps.
317+
TempDsc* getSpillTempDsc(GenTree* tree);
318+
316319
public:
317320
emitter* getEmitter() { return m_cgEmitter; }
318321
protected:

src/jit/codegenxarch.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1341,7 +1341,7 @@ void CodeGen::genCodeForDivMod(GenTreeOp* treeNode)
13411341
{
13421342
emit->emitInsBinary(genGetInsForOper(treeNode->gtOper, targetType), size, treeNode, divisor);
13431343
}
1344-
else if (divisor->gtRegNum == targetReg)
1344+
else if (!divisor->isContained() && divisor->gtRegNum == targetReg)
13451345
{
13461346
// It is not possible to generate 2-operand divss or divsd where reg2 = reg1 / reg2
13471347
// because divss/divsd reg1, reg2 will over-write reg1. Therefore, in case of AMD64
@@ -1466,8 +1466,8 @@ void CodeGen::genCodeForBinary(GenTree* treeNode)
14661466
// The arithmetic node must be sitting in a register (since it's not contained)
14671467
noway_assert(targetReg != REG_NA);
14681468

1469-
regNumber op1reg = op1->gtRegNum;
1470-
regNumber op2reg = op2->gtRegNum;
1469+
regNumber op1reg = op1->isContained() ? REG_NA: op1->gtRegNum;
1470+
regNumber op2reg = op2->isContained() ? REG_NA: op2->gtRegNum;
14711471

14721472
GenTreePtr dst;
14731473
GenTreePtr src;
@@ -5148,7 +5148,11 @@ void CodeGen::genConsumeRegs(GenTree* tree)
51485148

51495149
if (tree->isContained())
51505150
{
5151-
if (tree->isIndir())
5151+
if (tree->isContainedSpillTemp())
5152+
{
5153+
// spill temps are un-tracked and hence no need to update life
5154+
}
5155+
else if (tree->isIndir())
51525156
{
51535157
genConsumeAddress(tree->AsIndir()->Addr());
51545158
}

src/jit/emitxarch.cpp

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2893,7 +2893,7 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G
28932893
{
28942894
dblConst = src->AsDblCon();
28952895
}
2896-
2896+
28972897
// find local field if any
28982898
GenTreeLclFld* lclField = nullptr;
28992899
if (src->isContainedLclField())
@@ -2918,9 +2918,25 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G
29182918
lclVar = dst->AsLclVar();
29192919
}
29202920

2921+
// find contained spill tmp if any
2922+
TempDsc* tmpDsc = nullptr;
2923+
if (src->isContainedSpillTemp())
2924+
{
2925+
assert(src->IsRegOptional());
2926+
tmpDsc = codeGen->getSpillTempDsc(src);
2927+
}
2928+
else if (dst->isContainedSpillTemp())
2929+
{
2930+
assert(dst->IsRegOptional());
2931+
tmpDsc = codeGen->getSpillTempDsc(dst);
2932+
}
2933+
29212934
// First handle the simple non-memory cases
29222935
//
2923-
if ((mem == nullptr) && (lclField == nullptr) && (lclVar == nullptr))
2936+
if ((mem == nullptr) &&
2937+
(lclField == nullptr) &&
2938+
(lclVar == nullptr) &&
2939+
(tmpDsc == nullptr))
29242940
{
29252941
if (intConst != nullptr)
29262942
{
@@ -2959,7 +2975,7 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G
29592975
// Next handle the cases where we have a stack based local memory operand.
29602976
//
29612977
unsigned varNum = BAD_VAR_NUM;
2962-
unsigned offset = (unsigned) -1;
2978+
unsigned offset = (unsigned)-1;
29632979

29642980
if (lclField != nullptr)
29652981
{
@@ -2971,12 +2987,22 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G
29712987
varNum = lclVar->AsLclVarCommon()->GetLclNum();
29722988
offset = 0;
29732989
}
2990+
else if (tmpDsc != nullptr)
2991+
{
2992+
varNum = tmpDsc->tdTempNum();
2993+
offset = 0;
2994+
}
29742995

2975-
if (varNum != BAD_VAR_NUM)
2996+
// Spill temp numbers are negative and start with -1
2997+
// which also happens to be BAD_VAR_NUM. For this reason
2998+
// we also need to check 'tmpDsc != nullptr' here.
2999+
if (varNum != BAD_VAR_NUM ||
3000+
tmpDsc != nullptr)
29763001
{
29773002
// Is the memory op in the source position?
29783003
if (src->isContainedLclField() ||
2979-
src->isContainedLclVar())
3004+
src->isContainedLclVar() ||
3005+
src->isContainedSpillTemp())
29803006
{
29813007
if (instrHasImplicitRegPairDest(ins))
29823008
{
@@ -2993,7 +3019,7 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G
29933019
}
29943020
else // The memory op is in the dest position.
29953021
{
2996-
assert(dst->gtRegNum == REG_NA);
3022+
assert(dst->gtRegNum == REG_NA || dst->IsRegOptional());
29973023

29983024
// src could be int or reg
29993025
if (src->isContainedIntOrIImmed())
@@ -3011,6 +3037,11 @@ regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, G
30113037
}
30123038
}
30133039

3040+
if (tmpDsc != nullptr)
3041+
{
3042+
emitComp->tmpRlsTemp(tmpDsc);
3043+
}
3044+
30143045
return dst->gtRegNum;
30153046
}
30163047

src/jit/gentree.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13306,13 +13306,22 @@ GenTree::IsLclVarUpdateTree(GenTree** pOtherTree, genTreeOps *pOper)
1330613306
// until after the LSRA phase has allocated physical registers to the treenodes.
1330713307
bool GenTree::isContained() const
1330813308
{
13309+
if (isContainedSpillTemp())
13310+
{
13311+
return true;
13312+
}
13313+
1330913314
if (gtHasReg())
13315+
{
1331013316
return false;
13317+
}
1331113318

1331213319
// these actually produce a register (the flags reg, we just don't model it)
1331313320
// and are a separate instruction from the branch that consumes the result
1331413321
if (OperKind() & GTK_RELOP)
13322+
{
1331513323
return false;
13324+
}
1331613325

1331713326
// TODO-Cleanup : this is not clean, would be nice to have some way of marking this.
1331813327
switch (OperGet())

src/jit/gentree.h

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -495,15 +495,17 @@ struct GenTree
495495

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

498-
bool isContainedLclVar() const { return isContained() && (OperGet() == GT_LCL_VAR); }
498+
bool isContainedLclVar() const { return isContained() && (OperGet() == GT_LCL_VAR); }
499+
500+
bool isContainedSpillTemp() const;
499501

500502
// Indicates whether it is a memory op.
501503
// Right now it includes Indir and LclField ops.
502504
bool isMemoryOp() const { return isIndir() || isLclField(); }
503505

504506
bool isContainedMemoryOp() const
505507
{
506-
return (isContained() && isMemoryOp()) || isContainedLclVar();
508+
return (isContained() && isMemoryOp()) || isContainedLclVar() || isContainedSpillTemp();
507509
}
508510

509511
regNumber GetRegNum() const
@@ -665,11 +667,13 @@ struct GenTree
665667
#define GTF_REVERSE_OPS 0x00000020 // operand op2 should be evaluated before op1 (normally, op1 is evaluated first and op2 is evaluated second)
666668
#define GTF_REG_VAL 0x00000040 // operand is sitting in a register (or part of a TYP_LONG operand is sitting in a register)
667669

668-
#define GTF_SPILLED 0x00000080 // the value has been spilled
669-
#define GTF_SPILLED_OPER 0x00000100 // op1 has been spilled
670+
#define GTF_SPILLED 0x00000080 // the value has been spilled
670671

671672
#ifdef LEGACY_BACKEND
672-
#define GTF_SPILLED_OP2 0x00000200 // op2 has been spilled
673+
#define GTF_SPILLED_OPER 0x00000100 // op1 has been spilled
674+
#define GTF_SPILLED_OP2 0x00000200 // op2 has been spilled
675+
#else
676+
#define GTF_NOREG_AT_USE 0x00000100 // tree node is in memory at the point of use
673677
#endif // LEGACY_BACKEND
674678

675679
#define GTF_REDINDEX_CHECK 0x00000100 // Used for redundant range checks. Disjoint from GTF_SPILLED_OPER
@@ -4497,6 +4501,19 @@ GenTreeBlkOp::HasGCPtr()
44974501
return false;
44984502
}
44994503

4504+
inline bool GenTree::isContainedSpillTemp() const
4505+
{
4506+
#if !defined(LEGACY_BACKEND)
4507+
// If spilled and no reg at use, then it is treated as contained.
4508+
if (((gtFlags & GTF_SPILLED) != 0) &&
4509+
((gtFlags & GTF_NOREG_AT_USE) != 0))
4510+
{
4511+
return true;
4512+
}
4513+
#endif //!LEGACY_BACKEND
4514+
4515+
return false;
4516+
}
45004517

45014518
/*****************************************************************************/
45024519

src/jit/lower.h

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,62 @@ class Lowering : public Phase
143143
void TreeNodeInfoInit(GenTreePtr* tree, GenTree* parent);
144144
#if defined(_TARGET_XARCH_)
145145
void TreeNodeInfoInitSimple(GenTree* tree);
146-
void SetRegOptionalForBinOp(GenTree* tree);
147-
void TryToSetRegOptional(GenTree* operand);
146+
147+
//----------------------------------------------------------------------
148+
// SetRegOptional - sets a bit to indicate to LSRA that register
149+
// for a given tree node is optional for codegen purpose. If no
150+
// register is allocated to such a tree node, its parent node treats
151+
// it as a contained memory operand during codegen.
152+
//
153+
// Arguments:
154+
// tree - GenTree node
155+
//
156+
// Returns
157+
// None
158+
void SetRegOptional(GenTree* tree)
159+
{
160+
tree->gtLsraInfo.regOptional = true;
161+
}
162+
163+
GenTree* PreferredRegOptionalOperand(GenTree* tree);
164+
165+
// ------------------------------------------------------------------
166+
// SetRegOptionalBinOp - Indicates which of the operands of a bin-op
167+
// register requirement is optional. Xarch instruction set allows
168+
// either of op1 or op2 of binary operation (e.g. add, mul etc) to be
169+
// a memory operand. This routine provides info to register allocator
170+
// which of its operands optionally require a register. Lsra might not
171+
// allocate a register to RefTypeUse positions of such operands if it
172+
// is beneficial. In such a case codegen will treat them as memory
173+
// operands.
174+
//
175+
// Arguments:
176+
// tree - Gentree of a bininary operation.
177+
//
178+
// Returns
179+
// None.
180+
//
181+
// Note: On xarch at most only one of the operands will be marked as
182+
// reg optional, even when both operands could be considered register
183+
// optional.
184+
void SetRegOptionalForBinOp(GenTree* tree)
185+
{
186+
assert(GenTree::OperIsBinary(tree->OperGet()));
187+
188+
GenTree* op1 = tree->gtGetOp1();
189+
GenTree* op2 = tree->gtGetOp2();
190+
191+
if (tree->OperIsCommutative() &&
192+
tree->TypeGet() == op1->TypeGet())
193+
{
194+
GenTree* preferredOp = PreferredRegOptionalOperand(tree);
195+
SetRegOptional(preferredOp);
196+
}
197+
else if (tree->TypeGet() == op2->TypeGet())
198+
{
199+
SetRegOptional(op2);
200+
}
201+
}
148202
#endif // defined(_TARGET_XARCH_)
149203
void TreeNodeInfoInitReturn(GenTree* tree);
150204
void TreeNodeInfoInitShiftRotate(GenTree* tree);

0 commit comments

Comments
 (0)