{
genConsumeAddress(tree->AsIndir()->Addr());
}
- else if (tree->OperGet() == GT_AND)
- {
- // This is the special contained GT_AND that we created in Lowering::TreeNodeInfoInitCmp()
- // Now we need to consume the operands of the GT_AND node.
- genConsumeOperands(tree->AsOp());
- }
#ifdef _TARGET_XARCH_
else if (tree->OperGet() == GT_LCL_VAR)
{
GenTreePtr op2 = tree->gtOp2;
var_types op1Type = op1->TypeGet();
var_types op2Type = op2->TypeGet();
- regNumber targetReg = treeNode->gtRegNum;
+ regNumber targetReg = tree->gtRegNum;
// Case of op1 == 0 or op1 != 0:
// Optimize generation of 'test' instruction if op1 sets flags.
genConsumeOperands(tree);
- instruction ins;
- emitAttr cmpAttr;
-
// TODO-CQ: We should be able to support swapping op1 and op2 to generate cmp reg, imm.
// https://github.com/dotnet/coreclr/issues/7270
assert(!op1->isContainedIntOrIImmed()); // We no longer support
assert(!varTypeIsLong(op1Type) && !varTypeIsLong(op2Type));
#endif // _TARGET_X86_
- // By default we use an int32 sized cmp instruction
+ // By default we use an int32 sized cmp/test instruction
//
- ins = ((treeNode->OperGet() == GT_TEST_EQ) || (treeNode->OperGet() == GT_TEST_NE)) ? INS_test : INS_cmp;
- var_types cmpType = TYP_INT;
+ instruction ins = ((tree->OperGet() == GT_TEST_EQ) || (tree->OperGet() == GT_TEST_NE)) ? INS_test : INS_cmp;
+ var_types cmpType = TYP_INT;
// In the if/then/else statement below we may change the
// 'cmpType' and/or 'ins' to generate a smaller instruction
// If we have two different int64 types we need to use a long compare
cmpType = TYP_LONG;
}
-
- cmpAttr = emitTypeSize(cmpType);
}
else // Here we know that (op1Type != op2Type)
{
}
}
#endif // _TARGET_AMD64_
-
- cmpAttr = emitTypeSize(cmpType);
}
// See if we can generate a "test" instruction instead of a "cmp".
// For this to generate the correct conditional branch we must have
// a compare against zero.
//
- if (op2->IsIntegralConst(0))
+ if ((ins == INS_cmp) && !op1->isContained() && op2->IsIntegralConst(0))
{
- if (!op1->isUsedFromReg())
- {
- // op1 can be a contained memory op
- // or the special contained GT_AND that we created in Lowering::TreeNodeInfoInitCmp()
- //
- if ((op1->OperGet() == GT_AND) && op1->gtGetOp2()->isContainedIntOrIImmed() &&
- ((tree->OperGet() == GT_EQ) || (tree->OperGet() == GT_NE)))
- {
- ins = INS_test; // we will generate "test andOp1, andOp2CnsVal"
- op2 = op1->gtOp.gtOp2; // must assign op2 before we overwrite op1
- op1 = op1->gtOp.gtOp1; // overwrite op1
-
- if (op1->isUsedFromMemory())
- {
- // use the size andOp1 if it is a contained memoryop.
- cmpAttr = emitTypeSize(op1->TypeGet());
- }
- // fallthrough to emit->emitInsBinary(ins, cmpAttr, op1, op2);
- }
- }
- else // op1 is not contained thus it must be in a register
- {
- ins = INS_test;
- op2 = op1; // we will generate "test reg1,reg1"
- // fallthrough to emit->emitInsBinary(ins, cmpAttr, op1, op2);
- }
+ // op1 is not contained thus it must be in a register
+ ins = INS_test;
+ op2 = op1; // we will generate "test reg1,reg1"
+ // fallthrough to emit->emitInsBinary(ins, cmpAttr, op1, op2);
}
- getEmitter()->emitInsBinary(ins, cmpAttr, op1, op2);
+ getEmitter()->emitInsBinary(ins, emitTypeSize(cmpType), op1, op2);
// Are we evaluating this into a register?
if (targetReg != REG_NA)
// TODO-XArch-CQ: factor out cmp optimization in 'genCondSetFlags' to be used here
// or in other backend.
- bool hasShortCast = false;
if (CheckImmedAndMakeContained(tree, op2))
{
// If the types are the same, or if the constant is of the correct size,
// we can treat the isMemoryOp as contained.
- bool op1CanBeContained = (genTypeSize(op1Type) == genTypeSize(op2Type));
-
- if (op1CanBeContained)
+ if (genTypeSize(op1Type) == genTypeSize(op2Type))
{
if (op1->isMemoryOp())
{
MakeSrcContained(tree, op1);
}
+ // If op1 codegen sets ZF and SF flags and ==/!= against
+ // zero, we don't need to generate test instruction,
+ // provided we don't have another GenTree node between op1
+ // and tree that could potentially modify flags.
+ //
+ // TODO-CQ: right now the below peep is inexpensive and
+ // gets the benefit in most of cases because in majority
+ // of cases op1, op2 and tree would be in that order in
+ // execution. In general we should be able to check that all
+ // the nodes that come after op1 in execution order do not
+ // modify the flags so that it is safe to avoid generating a
+ // test instruction. Such a check requires that on each
+ // GenTree node we need to set the info whether its codegen
+ // will modify flags.
+ //
+ // TODO-CQ: We can optimize compare against zero in the
+ // following cases by generating the branch as indicated
+ // against each case.
+ // 1) unsigned compare
+ // < 0 - always FALSE
+ // <= 0 - ZF=1 and jne
+ // > 0 - ZF=0 and je
+ // >= 0 - always TRUE
+ //
+ // 2) signed compare
+ // < 0 - SF=1 and js
+ // >= 0 - SF=0 and jns
+ else if (((tree->gtOper == GT_EQ) || (tree->gtOper == GT_NE)) && op1->gtSetZSFlags() &&
+ op2->IsIntegralConst(0) && (op1->gtNext == op2) && (op2->gtNext == tree))
+ {
+ // Require codegen of op1 to set the flags.
+ assert(!op1->gtSetFlags());
+ op1->gtFlags |= GTF_SET_FLAGS;
+ }
else
{
- bool op1IsMadeContained = false;
- bool isEqualityCompare = (tree->gtOper == GT_EQ || tree->gtOper == GT_NE);
-
- // If not made contained, op1 can be marked as reg-optional.
- if (!op1IsMadeContained)
- {
- SetRegOptional(op1);
-
- // If op1 codegen sets ZF and SF flags and ==/!= against
- // zero, we don't need to generate test instruction,
- // provided we don't have another GenTree node between op1
- // and tree that could potentially modify flags.
- //
- // TODO-CQ: right now the below peep is inexpensive and
- // gets the benefit in most of cases because in majority
- // of cases op1, op2 and tree would be in that order in
- // execution. In general we should be able to check that all
- // the nodes that come after op1 in execution order do not
- // modify the flags so that it is safe to avoid generating a
- // test instruction. Such a check requires that on each
- // GenTree node we need to set the info whether its codegen
- // will modify flags.
- //
- // TODO-CQ: We can optimize compare against zero in the
- // following cases by generating the branch as indicated
- // against each case.
- // 1) unsigned compare
- // < 0 - always FALSE
- // <= 0 - ZF=1 and jne
- // > 0 - ZF=0 and je
- // >= 0 - always TRUE
- //
- // 2) signed compare
- // < 0 - SF=1 and js
- // >= 0 - SF=0 and jns
- if (isEqualityCompare && op1->gtSetZSFlags() && op2->IsIntegralConst(0) && (op1->gtNext == op2) &&
- (op2->gtNext == tree))
- {
- // Require codegen of op1 to set the flags.
- assert(!op1->gtSetFlags());
- op1->gtFlags |= GTF_SET_FLAGS;
- }
- }
+ SetRegOptional(op1);
}
}
}