}
//------------------------------------------------------------------------------
+// fgIsSafeToClone: If the node is an unaliased local or constant,
+// then it is safe to clone.
+//
+// Arguments:
+// tree - The node to check if it is safe to clone.
+//
+// Return Value:
+// True if the tree is cloneable. False if the tree is not cloneable.
+//
+// Notes:
+// This is conservative as this will return False if the local's address
+// is exposed.
+//
+bool Compiler::fgIsSafeToClone(GenTree* tree)
+{
+ if (tree->IsInvariant())
+ {
+ return true;
+ }
+ else if (tree->IsLocal())
+ {
+ // Can't rely on GTF_GLOB_REF here.
+ //
+ if (!lvaGetDesc(tree->AsLclVarCommon())->IsAddressExposed())
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//------------------------------------------------------------------------------
+// fgMakeTemp: Make a temp variable with a right-hand side expression as the assignment.
+//
+// Arguments:
+// rhs - The right-hand side expression.
+//
+// Return Value:
+// 'TempInfo' data that contains the GT_ASG and GT_LCL_VAR nodes for assignment
+// and variable load respectively.
+//
+TempInfo Compiler::fgMakeTemp(GenTree* rhs, CORINFO_CLASS_HANDLE structType /*= nullptr*/)
+{
+ unsigned lclNum = lvaGrabTemp(true DEBUGARG("fgMakeTemp is creating a new local variable"));
+
+ if (varTypeIsStruct(rhs))
+ {
+ assert(structType != nullptr);
+ lvaSetStruct(lclNum, structType, false);
+ }
+
+ // If rhs->TypeGet() == TYP_STRUCT, gtNewTempAssign() will create a GT_COPYBLK tree.
+ // The type of GT_COPYBLK is TYP_VOID. Therefore, we should use rhs->TypeGet() for
+ // setting type of lcl vars created.
+ GenTree* asg = gtNewTempAssign(lclNum, rhs);
+ GenTree* load = new (this, GT_LCL_VAR) GenTreeLclVar(GT_LCL_VAR, rhs->TypeGet(), lclNum);
+
+ TempInfo tempInfo{};
+ tempInfo.asg = asg;
+ tempInfo.load = load;
+
+ return tempInfo;
+}
+
+//------------------------------------------------------------------------------
// fgMakeMultiUse : If the node is an unaliased local or constant clone it,
// otherwise insert a comma form temp
//
{
GenTree* const tree = *pOp;
- if (tree->IsInvariant())
+ if (fgIsSafeToClone(tree))
{
return gtClone(tree);
}
- else if (tree->IsLocal())
- {
- // Can't rely on GTF_GLOB_REF here.
- //
- if (!lvaGetDesc(tree->AsLclVarCommon())->IsAddressExposed())
- {
- return gtClone(tree);
- }
- }
return fgInsertCommaFormTemp(pOp, structType);
}
{
GenTree* subTree = *ppTree;
- unsigned lclNum = lvaGrabTemp(true DEBUGARG("fgInsertCommaFormTemp is creating a new local variable"));
+ TempInfo tempInfo = fgMakeTemp(subTree, structType);
+ GenTree* asg = tempInfo.asg;
+ GenTree* load = tempInfo.load;
- if (varTypeIsStruct(subTree))
- {
- assert(structType != nullptr);
- lvaSetStruct(lclNum, structType, false);
- }
+ *ppTree = gtNewOperNode(GT_COMMA, subTree->TypeGet(), asg, load);
- // If subTree->TypeGet() == TYP_STRUCT, gtNewTempAssign() will create a GT_COPYBLK tree.
- // The type of GT_COPYBLK is TYP_VOID. Therefore, we should use subTree->TypeGet() for
- // setting type of lcl vars created.
- GenTree* asg = gtNewTempAssign(lclNum, subTree);
-
- GenTree* load = new (this, GT_LCL_VAR) GenTreeLclVar(GT_LCL_VAR, subTree->TypeGet(), lclNum);
-
- GenTree* comma = gtNewOperNode(GT_COMMA, subTree->TypeGet(), asg, load);
-
- *ppTree = comma;
-
- return new (this, GT_LCL_VAR) GenTreeLclVar(GT_LCL_VAR, subTree->TypeGet(), lclNum);
+ return gtClone(load);
}
//------------------------------------------------------------------------
// division will be used, in that case this transform allows CSE to
// eliminate the redundant div from code like "x = a / 3; y = a % 3;".
//
+// Before:
+// * RETURN int
+// \--* MOD int
+// +--* MUL int
+// | +--* LCL_VAR int V00 arg0
+// | \--* LCL_VAR int V00 arg0
+// \--* LCL_VAR int V01 arg1
+// After:
+// * RETURN int
+// \--* COMMA int
+// +--* ASG int
+// | +--* LCL_VAR int V03 tmp1
+// | \--* MUL int
+// | +--* LCL_VAR int V00 arg0
+// | \--* LCL_VAR int V00 arg0
+// \--* SUB int
+// +--* LCL_VAR int V03 tmp1
+// \--* MUL int
+// +--* DIV int
+// | +--* LCL_VAR int V03 tmp1
+// | \--* LCL_VAR int V01 arg1
+// \--* LCL_VAR int V01 arg1
GenTree* Compiler::fgMorphModToSubMulDiv(GenTreeOp* tree)
{
JITDUMP("\nMorphing MOD/UMOD [%06u] to Sub/Mul/Div\n", dspTreeID(tree));
noway_assert(!"Illegal gtOper in fgMorphModToSubMulDiv");
}
- var_types type = tree->gtType;
+ GenTreeOp* const div = tree;
- GenTree* const copyOfNumeratorValue = fgMakeMultiUse(&tree->gtOp1);
- GenTree* const copyOfDenominatorValue = fgMakeMultiUse(&tree->gtOp2);
- GenTree* const mul = gtNewOperNode(GT_MUL, type, tree, copyOfDenominatorValue);
- GenTree* const sub = gtNewOperNode(GT_SUB, type, copyOfNumeratorValue, mul);
+ GenTree* dividend = div->gtGetOp1();
+ GenTree* divisor = div->gtGetOp2();
- // Ensure "sub" does not evaluate "copyOfNumeratorValue" before it is defined by "mul".
- //
- sub->gtFlags |= GTF_REVERSE_OPS;
+ TempInfo tempInfos[2]{};
+ int tempInfoCount = 0;
+
+ if (!fgIsSafeToClone(dividend))
+ {
+ tempInfos[tempInfoCount] = fgMakeTemp(dividend);
+ dividend = tempInfos[tempInfoCount].load;
+ tempInfoCount++;
+ }
+
+ if (!fgIsSafeToClone(divisor))
+ {
+ tempInfos[tempInfoCount] = fgMakeTemp(divisor);
+ divisor = tempInfos[tempInfoCount].load;
+ tempInfoCount++;
+ }
+
+ var_types type = div->gtType;
+
+ div->gtOp1 = gtClone(dividend);
+ div->gtOp2 = gtClone(divisor);
+
+ GenTree* const mul = gtNewOperNode(GT_MUL, type, div, divisor);
+ GenTree* const sub = gtNewOperNode(GT_SUB, type, dividend, mul);
+
+ GenTree* result = sub;
+ // We loop backwards as it is easier to create new commas
+ // within one another for their sequence order.
+ for (int i = tempInfoCount - 1; i >= 0; i--)
+ {
+ result = gtNewOperNode(GT_COMMA, type, tempInfos[i].asg, result);
+ }
#ifdef DEBUG
- sub->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED;
+ result->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED;
#endif
- tree->CheckDivideByConstOptimized(this);
+ div->CheckDivideByConstOptimized(this);
- return sub;
+ return result;
}
//------------------------------------------------------------------------