return false;
}
+//------------------------------------------------------------------------
+// IsLclVarUpdateTree: Determine whether this is an assignment tree of the
+// form Vn = Vn 'oper' 'otherTree' where Vn is a lclVar
+//
+// Arguments:
+// pOtherTree - An "out" argument in which 'otherTree' will be returned.
+// pOper - An "out" argument in which 'oper' will be returned.
+//
+// Return Value:
+// If the tree is of the above form, the lclNum of the variable being
+// updated is returned, and 'pOtherTree' and 'pOper' are set.
+// Otherwise, returns BAD_VAR_NUM.
+//
+// Notes:
+// 'otherTree' can have any shape.
+// We avoid worrying about whether the op is commutative by only considering the
+// first operand of the rhs. It is expected that most trees of this form will
+// already have the lclVar on the lhs.
+// TODO-CQ: Evaluate whether there are missed opportunities due to this, or
+// whether gtSetEvalOrder will already have put the lclVar on the lhs in
+// the cases of interest.
+
+unsigned
+GenTree::IsLclVarUpdateTree(GenTree** pOtherTree, genTreeOps *pOper)
+{
+ unsigned lclNum = BAD_VAR_NUM;
+ if (OperIsAssignment())
+ {
+ GenTree* lhs = gtOp.gtOp1;
+ if (lhs->OperGet() == GT_LCL_VAR)
+ {
+ unsigned lhsLclNum = lhs->AsLclVarCommon()->gtLclNum;
+ if (gtOper == GT_ASG)
+ {
+ GenTree* rhs = gtOp.gtOp2;
+ if (rhs->OperIsBinary() &&
+ (rhs->gtOp.gtOp1->gtOper == GT_LCL_VAR) &&
+ (rhs->gtOp.gtOp1->AsLclVarCommon()->gtLclNum == lhsLclNum))
+ {
+ lclNum = lhsLclNum;
+ *pOtherTree = rhs->gtOp.gtOp2;
+ *pOper = rhs->gtOper;
+ }
+ }
+ else
+ {
+ lclNum = lhsLclNum;
+ *pOper = GenTree::OpAsgToOper(gtOper);
+ *pOtherTree = gtOp.gtOp2;
+ }
+ }
+ }
+ return lclNum;
+}
+
// return true if this tree node is a subcomponent of parent for codegen purposes
// (essentially, will be rolled into the same instruction)
// Note that this method relies upon the value of gtRegNum field to determine
// yields an address into a local
GenTreeLclVarCommon* IsLocalAddrExpr();
+ // Determine whether this is an assignment tree of the form X = X (op) Y,
+ // where Y is an arbitrary tree, and X is a lclVar.
+ unsigned IsLclVarUpdateTree(GenTree** otherTree, genTreeOps *updateOper);
+
// If returns "true", "this" may represent the address of a static or instance field
// (or a field of such a field, in the case of an object field of type struct).
// If returns "true", then either "*pObj" is set to the object reference,
//
unsigned Compiler::optIsLoopIncrTree(GenTreePtr incr)
{
- switch (incr->gtOper)
- {
- case GT_ASG_ADD:
- case GT_ASG_SUB:
- case GT_ASG_MUL:
- case GT_ASG_RSH:
- case GT_ASG_LSH:
- case GT_ASG:
- break;
- default:
- return BAD_VAR_NUM;
- }
-
- unsigned iterVar;
- GenTreePtr incrVal;
- if (incr->gtOper == GT_ASG)
+ GenTree* incrVal;
+ genTreeOps updateOper;
+ unsigned iterVar = incr->IsLclVarUpdateTree(&incrVal, &updateOper);
+ if (iterVar != BAD_VAR_NUM)
{
- // We have v = v + 1 type asg node.
- GenTreePtr lhs = incr->gtOp.gtOp1;
- GenTreePtr rhs = incr->gtOp.gtOp2;
- switch (rhs->gtOper)
+ // We have v = v op y type asg node.
+ switch (updateOper)
{
case GT_ADD:
case GT_SUB:
default:
return BAD_VAR_NUM;
}
- GenTreePtr rhsOp1 = rhs->gtOp.gtOp1;
- incrVal = rhs->gtOp.gtOp2;
- // Make sure lhs and rhs have the right variable numbers.
- if (lhs->gtOper != GT_LCL_VAR || rhsOp1->gtOper != GT_LCL_VAR || rhsOp1->gtLclVarCommon.gtLclNum != lhs->gtLclVarCommon.gtLclNum)
+ // Increment should be by a const int.
+ // TODO-CQ: CLONE: allow variable increments.
+ if ((incrVal->gtOper != GT_CNS_INT) || (incrVal->TypeGet() != TYP_INT))
{
return BAD_VAR_NUM;
}
- iterVar = rhsOp1->gtLclVarCommon.gtLclNum;
- }
- else
- {
- // We have op=
- GenTreePtr lhs = incr->gtOp.gtOp1;
- incrVal = incr->gtOp.gtOp2;
- if (lhs->gtOper != GT_LCL_VAR)
- {
- return BAD_VAR_NUM;
- }
- iterVar = lhs->gtLclVarCommon.gtLclNum;
- }
-
- // Increment should be by a const int.
- // TODO-CQ: CLONE: allow variable increments.
- if (incrVal->gtOper != GT_CNS_INT || incrVal->TypeGet() != TYP_INT)
- {
- return BAD_VAR_NUM;
}
return iterVar;