GenTree* op1 = node->gtOp.gtOp1;
GenTree* op2 = node->gtOp.gtOp2;
+ bool isSafeToContainOp1 = true;
+ bool isSafeToContainOp2 = true;
+
// Case of float/double mul.
if (varTypeIsFloating(node->TypeGet()))
{
assert(node->OperGet() == GT_MUL);
- if (IsContainableMemoryOp(op2) || op2->IsCnsNonZeroFltOrDbl())
+ if (op2->IsCnsNonZeroFltOrDbl())
{
MakeSrcContained(node, op2);
}
- else if (op1->IsCnsNonZeroFltOrDbl() || (IsContainableMemoryOp(op1) && IsSafeToContainMem(node, op1)))
+ else if (IsContainableMemoryOp(op2))
+ {
+ isSafeToContainOp2 = IsSafeToContainMem(node, op2);
+ if (isSafeToContainOp2)
+ {
+ MakeSrcContained(node, op2);
+ }
+ }
+
+ if (!op2->isContained())
{
// Since GT_MUL is commutative, we will try to re-order operands if it is safe to
// generate more efficient code sequence for the case of GT_MUL(op1=memOp, op2=non-memOp)
- MakeSrcContained(node, op1);
+ if (op1->IsCnsNonZeroFltOrDbl())
+ {
+ MakeSrcContained(node, op1);
+ }
+ else if (IsContainableMemoryOp(op1))
+ {
+ isSafeToContainOp1 = IsSafeToContainMem(node, op1);
+ if (isSafeToContainOp1)
+ {
+ MakeSrcContained(node, op1);
+ }
+ }
}
- else
+
+ if (!op1->isContained() && !op2->isContained())
{
// If there are no containable operands, we can make an operand reg optional.
- SetRegOptionalForBinOp(node);
+ // IsSafeToContainMem is expensive so we call it at most once for each operand
+ // in this method. If we already called IsSafeToContainMem, it must have returned false;
+ // otherwise, the corresponding operand (op1 or op2) would be contained.
+ isSafeToContainOp1 = isSafeToContainOp1 && IsSafeToContainMem(node, op1);
+ isSafeToContainOp2 = isSafeToContainOp2 && IsSafeToContainMem(node, op2);
+ SetRegOptionalForBinOp(node, isSafeToContainOp1, isSafeToContainOp2);
}
return;
}
//
if (memOp == nullptr)
{
- if (IsContainableMemoryOp(op2) && (op2->TypeGet() == node->TypeGet()) && IsSafeToContainMem(node, op2))
+ if ((op2->TypeGet() == node->TypeGet()) && IsContainableMemoryOp(op2))
{
- memOp = op2;
+ isSafeToContainOp2 = IsSafeToContainMem(node, op2);
+ if (isSafeToContainOp2)
+ {
+ memOp = op2;
+ }
}
- else if (IsContainableMemoryOp(op1) && (op1->TypeGet() == node->TypeGet()) && IsSafeToContainMem(node, op1))
+
+ if ((memOp == nullptr) && (op1->TypeGet() == node->TypeGet()) && IsContainableMemoryOp(op1))
{
- memOp = op1;
+ isSafeToContainOp1 = IsSafeToContainMem(node, op1);
+ if (isSafeToContainOp1)
+ {
+ memOp = op1;
+ }
}
}
else
{
- if ((memOp->TypeGet() != node->TypeGet()) || !IsSafeToContainMem(node, memOp))
+ if ((memOp->TypeGet() != node->TypeGet()))
{
memOp = nullptr;
}
+ else if (!IsSafeToContainMem(node, memOp))
+ {
+ if (memOp == op1)
+ {
+ isSafeToContainOp1 = false;
+ }
+ else
+ {
+ isSafeToContainOp2 = false;
+ }
+ memOp = nullptr;
+ }
}
// To generate an LEA we need to force memOp into a register
// so don't allow memOp to be 'contained'
{
MakeSrcContained(node, memOp);
}
- else if (imm != nullptr)
- {
- // Has a contained immediate operand.
- // Only 'other' operand can be marked as reg optional.
- assert(other != nullptr);
- other->SetRegOptional();
- }
- else if (hasImpliedFirstOperand)
- {
- // Only op2 can be marke as reg optional.
- op2->SetRegOptional();
- }
else
{
- // If there are no containable operands, we can make either of op1 or op2
- // as reg optional.
- SetRegOptionalForBinOp(node);
+ // IsSafeToContainMem is expensive so we call it at most once for each operand
+ // in this method. If we already called IsSafeToContainMem, it must have returned false;
+ // otherwise, memOp would be set to the corresponding operand (op1 or op2).
+ if (imm != nullptr)
+ {
+ // Has a contained immediate operand.
+ // Only 'other' operand can be marked as reg optional.
+ assert(other != nullptr);
+
+ isSafeToContainOp1 = ((other == op1) && isSafeToContainOp1 && IsSafeToContainMem(node, op1));
+ isSafeToContainOp2 = ((other == op2) && isSafeToContainOp2 && IsSafeToContainMem(node, op2));
+ }
+ else if (hasImpliedFirstOperand)
+ {
+ // Only op2 can be marked as reg optional.
+ isSafeToContainOp1 = false;
+ isSafeToContainOp2 = isSafeToContainOp2 && IsSafeToContainMem(node, op2);
+ }
+ else
+ {
+ // If there are no containable operands, we can make either of op1 or op2
+ // as reg optional.
+ isSafeToContainOp1 = isSafeToContainOp1 && IsSafeToContainMem(node, op1);
+ isSafeToContainOp2 = isSafeToContainOp2 && IsSafeToContainMem(node, op2);
+ }
+ SetRegOptionalForBinOp(node, isSafeToContainOp1, isSafeToContainOp2);
}
}
}
}
assert(otherOp != nullptr);
+ bool isSafeToContainOtherOp = true;
if (otherOp->IsCnsNonZeroFltOrDbl())
{
MakeSrcContained(cmp, otherOp);
}
- else if (IsContainableMemoryOp(otherOp) && ((otherOp == op2) || IsSafeToContainMem(cmp, otherOp)))
+ else if (IsContainableMemoryOp(otherOp))
{
- MakeSrcContained(cmp, otherOp);
+ isSafeToContainOtherOp = IsSafeToContainMem(cmp, otherOp);
+ if (isSafeToContainOtherOp)
+ {
+ MakeSrcContained(cmp, otherOp);
+ }
}
- else
+
+ if (!otherOp->isContained() && isSafeToContainOtherOp && IsSafeToContainMem(cmp, otherOp))
{
// SSE2 allows only otherOp to be a memory-op. Since otherOp is not
// contained, we can mark it reg-optional.
+ // IsSafeToContainMem is expensive so we call it at most once for otherOp.
+ // If we already called IsSafeToContainMem, it must have returned false;
+ // otherwise, otherOp would be contained.
otherOp->SetRegOptional();
}
// Note that TEST does not have a r,rm encoding like CMP has but we can still
// contain the second operand because the emitter maps both r,rm and rm,r to
// the same instruction code. This avoids the need to special case TEST here.
+
+ bool isSafeToContainOp1 = true;
+ bool isSafeToContainOp2 = true;
+
if (IsContainableMemoryOp(op2))
{
- MakeSrcContained(cmp, op2);
- }
- else if (IsContainableMemoryOp(op1) && IsSafeToContainMem(cmp, op1))
- {
- MakeSrcContained(cmp, op1);
+ isSafeToContainOp2 = IsSafeToContainMem(cmp, op2);
+ if (isSafeToContainOp2)
+ {
+ MakeSrcContained(cmp, op2);
+ }
}
- else if (op1->IsCnsIntOrI())
+
+ if (!op2->isContained() && IsContainableMemoryOp(op1))
{
- op2->SetRegOptional();
+ isSafeToContainOp1 = IsSafeToContainMem(cmp, op1);
+ if (isSafeToContainOp1)
+ {
+ MakeSrcContained(cmp, op1);
+ }
}
- else
+
+ if (!op1->isContained() && !op2->isContained())
{
// One of op1 or op2 could be marked as reg optional
// to indicate that codegen can still generate code
// if one of them is on stack.
- PreferredRegOptionalOperand(cmp)->SetRegOptional();
+ GenTree* regOptionalCandidate = op1->IsCnsIntOrI() ? op2 : PreferredRegOptionalOperand(cmp);
+
+ // IsSafeToContainMem is expensive so we call it at most once for each operand
+ // in this method. If we already called IsSafeToContainMem, it must have returned false;
+ // otherwise, the corresponding operand (op1 or op2) would be contained.
+ bool setRegOptional = (regOptionalCandidate == op1) ? isSafeToContainOp1 && IsSafeToContainMem(cmp, op1)
+ : isSafeToContainOp2 && IsSafeToContainMem(cmp, op2);
+ if (setRegOptional)
+ {
+ regOptionalCandidate->SetRegOptional();
+ }
}
}
}
return;
}
- // We're not marking a constant hanging on the left of an add
- // as containable so we assign it to a register having CQ impact.
- // TODO-XArch-CQ: Detect this case and support both generating a single instruction
- // for GT_ADD(Constant, SomeTree)
-
GenTree* op1 = node->gtOp1;
GenTree* op2 = node->gtOp2;
// In case of memory-op, we can encode it directly provided its type matches with 'tree' type.
// This is because during codegen, type of 'tree' is used to determine emit Type size. If the types
// do not match, they get normalized (i.e. sign/zero extended) on load into a register.
- bool directlyEncodable = false;
- bool binOpInRMW = false;
- GenTree* operand = nullptr;
+ bool directlyEncodable = false;
+ bool binOpInRMW = false;
+ GenTree* operand = nullptr;
+ bool isSafeToContainOp1 = true;
+ bool isSafeToContainOp2 = true;
if (IsContainableImmed(node, op2))
{
if (!binOpInRMW)
{
const unsigned operatorSize = genTypeSize(node->TypeGet());
- if (IsContainableMemoryOp(op2) && (genTypeSize(op2->TypeGet()) == operatorSize))
+ if ((genTypeSize(op2->TypeGet()) == operatorSize) && IsContainableMemoryOp(op2))
{
- directlyEncodable = true;
- operand = op2;
+ isSafeToContainOp2 = IsSafeToContainMem(node, op2);
+ if (isSafeToContainOp2)
+ {
+ directlyEncodable = true;
+ operand = op2;
+ }
}
- else if (node->OperIsCommutative())
+
+ if ((operand == nullptr) && node->OperIsCommutative())
{
- if (IsContainableImmed(node, op1) ||
- (IsContainableMemoryOp(op1) && (genTypeSize(op1->TypeGet()) == operatorSize) &&
- IsSafeToContainMem(node, op1)))
+ // If it is safe, we can reverse the order of operands of commutative operations for efficient
+ // codegen
+ if (IsContainableImmed(node, op1))
{
- // If it is safe, we can reverse the order of operands of commutative operations for efficient
- // codegen
directlyEncodable = true;
operand = op1;
}
+ else if ((genTypeSize(op1->TypeGet()) == operatorSize) && IsContainableMemoryOp(op1))
+ {
+ isSafeToContainOp1 = IsSafeToContainMem(node, op1);
+ if (isSafeToContainOp1)
+ {
+ directlyEncodable = true;
+ operand = op1;
+ }
+ }
}
}
}
// If this binary op neither has contained operands, nor is a
// Read-Modify-Write (RMW) operation, we can mark its operands
// as reg optional.
- SetRegOptionalForBinOp(node);
+
+ // IsSafeToContainMem is expensive so we call it at most once for each operand
+ // in this method. If we already called IsSafeToContainMem, it must have returned false;
+ // otherwise, directlyEncodable would be true.
+ isSafeToContainOp1 = isSafeToContainOp1 && IsSafeToContainMem(node, op1);
+ isSafeToContainOp2 = isSafeToContainOp2 && IsSafeToContainMem(node, op2);
+
+ SetRegOptionalForBinOp(node, isSafeToContainOp1, isSafeToContainOp2);
}
}
// everything is made explicit by adding casts.
assert(op1->TypeGet() == op2->TypeGet());
- if (IsContainableMemoryOp(op2) || op2->IsCnsNonZeroFltOrDbl())
+ bool isSafeToContainOp1 = true;
+ bool isSafeToContainOp2 = true;
+
+ if (op2->IsCnsNonZeroFltOrDbl())
{
MakeSrcContained(node, op2);
}
- else if (node->OperIsCommutative() &&
- (op1->IsCnsNonZeroFltOrDbl() || (IsContainableMemoryOp(op1) && IsSafeToContainMem(node, op1))))
+ else if (IsContainableMemoryOp(op2))
+ {
+ isSafeToContainOp2 = IsSafeToContainMem(node, op2);
+ if (isSafeToContainOp2)
+ {
+ MakeSrcContained(node, op2);
+ }
+ }
+
+ if (!op2->isContained() && node->OperIsCommutative())
{
// Though we have GT_ADD(op1=memOp, op2=non-memOp, we try to reorder the operands
// as long as it is safe so that the following efficient code sequence is generated:
// Instead of
// movss op1Reg, [memOp]; addss/sd targetReg, Op2Reg (if op1Reg == targetReg) OR
// movss op1Reg, [memOp]; movaps targetReg, op1Reg, addss/sd targetReg, Op2Reg
- MakeSrcContained(node, op1);
+
+ if (op1->IsCnsNonZeroFltOrDbl())
+ {
+ MakeSrcContained(node, op1);
+ }
+ else if (IsContainableMemoryOp(op1))
+ {
+ isSafeToContainOp1 = IsSafeToContainMem(node, op1);
+ if (isSafeToContainOp1)
+ {
+ MakeSrcContained(node, op1);
+ }
+ }
}
- else
+
+ if (!op1->isContained() && !op2->isContained())
{
// If there are no containable operands, we can make an operand reg optional.
- SetRegOptionalForBinOp(node);
+ // IsSafeToContainMem is expensive so we call it at most once for each operand
+ // in this method. If we already called IsSafeToContainMem, it must have returned false;
+ // otherwise, the corresponding operand (op1 or op2) would be contained.
+ isSafeToContainOp1 = isSafeToContainOp1 && IsSafeToContainMem(node, op1);
+ isSafeToContainOp2 = isSafeToContainOp2 && IsSafeToContainMem(node, op2);
+ SetRegOptionalForBinOp(node, isSafeToContainOp1, isSafeToContainOp2);
}
}