Skip to content

Commit 86ce145

Browse files
Use cinv and cneg instead of csel when possible (#84926)
* Use cinv instead of csel when possible * Fix when the operands of conditional select should be reversed * Scope down the cinv optimisation to local vars * Use more generic CSINV instead of CINV instruction * Add support for CSNEG and refactor conditional selects * Fix initialisation of srcReg2 * Add RequiresProcessIsolation property to fix test failures * Address review comments - Part 1 * Remove output type from conditional negate tests * Ensure operands of cneg/cinv are invariants while optimising * Update tests to the new format * Use annotations to run tests through the framework * Fix conditional negate tests * Handle conditional negate/invert of shifted operands
1 parent 66644e3 commit 86ce145

16 files changed

+628
-313
lines changed

src/coreclr/jit/codegen.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -901,7 +901,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
901901
void genCodeForCompare(GenTreeOp* tree);
902902
#ifdef TARGET_ARM64
903903
void genCodeForCCMP(GenTreeCCMP* ccmp);
904-
void genCodeForCinc(GenTreeOp* cinc);
905904
#endif
906905
void genCodeForSelect(GenTreeOp* select);
907906
void genIntrinsic(GenTreeIntrinsic* treeNode);
@@ -1250,7 +1249,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
12501249
#if defined(TARGET_ARM64)
12511250
void genCodeForJumpCompare(GenTreeOpCC* tree);
12521251
void genCodeForBfiz(GenTreeOp* tree);
1253-
void genCodeForCond(GenTreeOp* tree);
12541252
#endif // TARGET_ARM64
12551253

12561254
#if defined(FEATURE_EH_FUNCLETS)

src/coreclr/jit/codegenarm64.cpp

Lines changed: 64 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -4683,32 +4683,49 @@ void CodeGen::genCodeForCCMP(GenTreeCCMP* ccmp)
46834683
}
46844684

46854685
//------------------------------------------------------------------------
4686-
// genCodeForSelect: Produce code for a GT_SELECT node.
4686+
// genCodeForSelect: Produce code for a GT_SELECT/GT_SELECT_INV/GT_SELECT_NEG node.
46874687
//
46884688
// Arguments:
46894689
// tree - the node
46904690
//
46914691
void CodeGen::genCodeForSelect(GenTreeOp* tree)
46924692
{
4693-
assert(tree->OperIs(GT_SELECT, GT_SELECTCC));
4694-
GenTree* opcond = nullptr;
4695-
if (tree->OperIs(GT_SELECT))
4693+
assert(tree->OperIs(GT_SELECT, GT_SELECTCC, GT_SELECT_INC, GT_SELECT_INCCC, GT_SELECT_INV, GT_SELECT_INVCC,
4694+
GT_SELECT_NEG, GT_SELECT_NEGCC));
4695+
GenTree* opcond = nullptr;
4696+
instruction ins = INS_csel;
4697+
GenTree* op1 = tree->gtOp1;
4698+
GenTree* op2 = tree->gtOp2;
4699+
4700+
if (tree->OperIs(GT_SELECT_INV, GT_SELECT_INVCC))
4701+
{
4702+
ins = (op2 == nullptr) ? INS_cinv : INS_csinv;
4703+
}
4704+
else if (tree->OperIs(GT_SELECT_NEG, GT_SELECT_NEGCC))
4705+
{
4706+
ins = (op2 == nullptr) ? INS_cneg : INS_csneg;
4707+
}
4708+
else if (tree->OperIs(GT_SELECT_INC, GT_SELECT_INCCC))
4709+
{
4710+
ins = (op2 == nullptr) ? INS_cinc : INS_csinc;
4711+
}
4712+
4713+
if (tree->OperIs(GT_SELECT, GT_SELECT_INV, GT_SELECT_NEG))
46964714
{
46974715
opcond = tree->AsConditional()->gtCond;
46984716
genConsumeRegs(opcond);
46994717
}
47004718

4701-
emitter* emit = GetEmitter();
4702-
4703-
GenTree* op1 = tree->gtOp1;
4704-
GenTree* op2 = tree->gtOp2;
4705-
var_types op1Type = genActualType(op1);
4706-
var_types op2Type = genActualType(op2);
4707-
emitAttr attr = emitActualTypeSize(tree);
4719+
if (op2 != nullptr)
4720+
{
4721+
var_types op1Type = genActualType(op1);
4722+
var_types op2Type = genActualType(op2);
4723+
assert(genTypeSize(op1Type) == genTypeSize(op2Type));
4724+
}
47084725

47094726
assert(!op1->isUsedFromMemory());
4710-
assert(genTypeSize(op1Type) == genTypeSize(op2Type));
47114727

4728+
emitter* emit = GetEmitter();
47124729
GenCondition cond;
47134730

47144731
if (opcond != nullptr)
@@ -4719,92 +4736,62 @@ void CodeGen::genCodeForSelect(GenTreeOp* tree)
47194736
}
47204737
else
47214738
{
4722-
assert(tree->OperIs(GT_SELECTCC));
4739+
assert(tree->OperIs(GT_SELECTCC, GT_SELECT_INCCC, GT_SELECT_INVCC, GT_SELECT_NEGCC));
47234740
cond = tree->AsOpCC()->gtCondition;
47244741
}
47254742

47264743
assert(!op1->isContained() || op1->IsIntegralConst(0));
4727-
assert(!op2->isContained() || op2->IsIntegralConst(0));
4744+
assert(op2 == nullptr || !op2->isContained() || op2->IsIntegralConst(0));
47284745

47294746
regNumber targetReg = tree->GetRegNum();
47304747
regNumber srcReg1 = op1->IsIntegralConst(0) ? REG_ZR : genConsumeReg(op1);
4731-
regNumber srcReg2 = op2->IsIntegralConst(0) ? REG_ZR : genConsumeReg(op2);
47324748
const GenConditionDesc& prevDesc = GenConditionDesc::Get(cond);
4749+
emitAttr attr = emitActualTypeSize(tree);
4750+
regNumber srcReg2;
47334751

4734-
emit->emitIns_R_R_R_COND(INS_csel, attr, targetReg, srcReg1, srcReg2, JumpKindToInsCond(prevDesc.jumpKind1));
4735-
4736-
// Some conditions require an additional condition check.
4737-
if (prevDesc.oper == GT_OR)
4738-
{
4739-
emit->emitIns_R_R_R_COND(INS_csel, attr, targetReg, srcReg1, targetReg, JumpKindToInsCond(prevDesc.jumpKind2));
4740-
}
4741-
else if (prevDesc.oper == GT_AND)
4742-
{
4743-
emit->emitIns_R_R_R_COND(INS_csel, attr, targetReg, targetReg, srcReg2, JumpKindToInsCond(prevDesc.jumpKind2));
4744-
}
4745-
4746-
regSet.verifyRegUsed(targetReg);
4747-
genProduceReg(tree);
4748-
}
4749-
4750-
//------------------------------------------------------------------------
4751-
// genCodeForCinc: Produce code for a GT_CINC/GT_CINCCC node.
4752-
//
4753-
// Arguments:
4754-
// tree - the node
4755-
//
4756-
void CodeGen::genCodeForCinc(GenTreeOp* cinc)
4757-
{
4758-
assert(cinc->OperIs(GT_CINC, GT_CINCCC));
4759-
4760-
GenTree* opcond = nullptr;
4761-
GenTree* op = cinc->gtOp1;
4762-
if (cinc->OperIs(GT_CINC))
4752+
if (op2 == nullptr)
47634753
{
4764-
opcond = cinc->gtOp1;
4765-
op = cinc->gtOp2;
4766-
genConsumeRegs(opcond);
4767-
}
4768-
4769-
emitter* emit = GetEmitter();
4770-
var_types opType = genActualType(op->TypeGet());
4771-
emitAttr attr = emitActualTypeSize(cinc->TypeGet());
4772-
4773-
assert(!op->isUsedFromMemory());
4774-
genConsumeRegs(op);
4775-
4776-
GenCondition cond;
4777-
4778-
if (cinc->OperIs(GT_CINC))
4779-
{
4780-
assert(!opcond->isContained());
4781-
// Condition has been generated into a register - move it into flags.
4782-
emit->emitIns_R_I(INS_cmp, emitActualTypeSize(opcond), opcond->GetRegNum(), 0);
4783-
cond = GenCondition::NE;
4754+
srcReg2 = srcReg1;
4755+
emit->emitIns_R_R_COND(ins, attr, targetReg, srcReg1, JumpKindToInsCond(prevDesc.jumpKind1));
47844756
}
47854757
else
47864758
{
4787-
assert(cinc->OperIs(GT_CINCCC));
4788-
cond = cinc->AsOpCC()->gtCondition;
4759+
srcReg2 = (op2->IsIntegralConst(0) ? REG_ZR : genConsumeReg(op2));
4760+
emit->emitIns_R_R_R_COND(ins, attr, targetReg, srcReg1, srcReg2, JumpKindToInsCond(prevDesc.jumpKind1));
47894761
}
4790-
const GenConditionDesc& prevDesc = GenConditionDesc::Get(cond);
4791-
regNumber targetReg = cinc->GetRegNum();
4792-
regNumber srcReg;
47934762

4794-
if (op->isContained())
4763+
// Some floating point comparision conditions require an additional condition check.
4764+
// These checks are emitted as a subsequent check using GT_AND or GT_OR nodes.
4765+
// e.g., using GT_OR => `dest = (cond1 || cond2) ? src1 : src2`
4766+
// GT_AND => `dest = (cond1 && cond2) ? src1 : src2`
4767+
// The GT_OR case results in emitting the following sequence of two csel instructions.
4768+
// csel dest, src1, src2, cond1 # emitted previously
4769+
// csel dest, src1, dest, cond2
4770+
//
4771+
if (prevDesc.oper == GT_AND)
47954772
{
4796-
assert(op->IsIntegralConst(0));
4797-
srcReg = REG_ZR;
4773+
// To ensure correctness with invert and negate variants of conditional select, the second instruction needs to
4774+
// be csinv or csneg respectively.
4775+
// dest = (cond1 && cond2) ? src1 : ~src2
4776+
// csinv dest, src1, src2, cond1
4777+
// csinv dest, dest, src2, cond2
4778+
//
4779+
// However, the other variants - increment and select, the second instruction needs to be csel.
4780+
// dest = (cond1 && cond2) ? src1 : src2++
4781+
// csinc dest, src1, src2, cond1
4782+
// csel dest, dest, src1 cond2
4783+
ins = ((ins == INS_csinv) || (ins == INS_csneg)) ? ins : INS_csel;
4784+
emit->emitIns_R_R_R_COND(ins, attr, targetReg, targetReg, srcReg2, JumpKindToInsCond(prevDesc.jumpKind2));
47984785
}
4799-
else
4786+
else if (prevDesc.oper == GT_OR)
48004787
{
4801-
srcReg = op->GetRegNum();
4788+
// Similarly, the second instruction needs to be csinc while emitting conditional increment.
4789+
ins = (ins == INS_csinc) ? ins : INS_csel;
4790+
emit->emitIns_R_R_R_COND(ins, attr, targetReg, srcReg1, targetReg, JumpKindToInsCond(prevDesc.jumpKind2));
48024791
}
48034792

4804-
assert(prevDesc.oper != GT_OR && prevDesc.oper != GT_AND);
4805-
emit->emitIns_R_R_COND(INS_cinc, attr, targetReg, srcReg, JumpKindToInsCond(prevDesc.jumpKind1));
48064793
regSet.verifyRegUsed(targetReg);
4807-
genProduceReg(cinc);
4794+
genProduceReg(tree);
48084795
}
48094796

48104797
//------------------------------------------------------------------------
@@ -10365,53 +10352,6 @@ void CodeGen::genCodeForBfiz(GenTreeOp* tree)
1036510352
genProduceReg(tree);
1036610353
}
1036710354

10368-
//------------------------------------------------------------------------
10369-
// genCodeForCond: Generates the code sequence for a GenTree node that
10370-
// represents a conditional instruction.
10371-
//
10372-
// Arguments:
10373-
// tree - conditional op
10374-
//
10375-
void CodeGen::genCodeForCond(GenTreeOp* tree)
10376-
{
10377-
assert(tree->OperIs(GT_CSNEG_MI, GT_CNEG_LT));
10378-
assert(!(tree->gtFlags & GTF_SET_FLAGS));
10379-
genConsumeOperands(tree);
10380-
10381-
switch (tree->OperGet())
10382-
{
10383-
case GT_CSNEG_MI:
10384-
{
10385-
instruction ins = INS_csneg;
10386-
insCond cond = INS_COND_MI;
10387-
10388-
regNumber dstReg = tree->GetRegNum();
10389-
regNumber op1Reg = tree->gtGetOp1()->GetRegNum();
10390-
regNumber op2Reg = tree->gtGetOp2()->GetRegNum();
10391-
10392-
GetEmitter()->emitIns_R_R_R_COND(ins, emitActualTypeSize(tree), dstReg, op1Reg, op2Reg, cond);
10393-
break;
10394-
}
10395-
10396-
case GT_CNEG_LT:
10397-
{
10398-
instruction ins = INS_cneg;
10399-
insCond cond = INS_COND_LT;
10400-
10401-
regNumber dstReg = tree->GetRegNum();
10402-
regNumber op1Reg = tree->gtGetOp1()->GetRegNum();
10403-
10404-
GetEmitter()->emitIns_R_R_COND(ins, emitActualTypeSize(tree), dstReg, op1Reg, cond);
10405-
break;
10406-
}
10407-
10408-
default:
10409-
unreached();
10410-
}
10411-
10412-
genProduceReg(tree);
10413-
}
10414-
1041510355
//------------------------------------------------------------------------
1041610356
// JumpKindToInsCond: Convert a Jump Kind to a condition.
1041710357
//

src/coreclr/jit/codegenarmarch.cpp

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -315,11 +315,6 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
315315
case GT_BFIZ:
316316
genCodeForBfiz(treeNode->AsOp());
317317
break;
318-
319-
case GT_CSNEG_MI:
320-
case GT_CNEG_LT:
321-
genCodeForCond(treeNode->AsOp());
322-
break;
323318
#endif // TARGET_ARM64
324319

325320
case GT_JMP:
@@ -355,15 +350,16 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
355350
break;
356351

357352
#ifdef TARGET_ARM64
353+
case GT_SELECT_NEG:
354+
case GT_SELECT_INV:
355+
case GT_SELECT_INC:
358356
case GT_SELECT:
359357
genCodeForSelect(treeNode->AsConditional());
360358
break;
361359

362-
case GT_CINC:
363-
case GT_CINCCC:
364-
genCodeForCinc(treeNode->AsOp());
365-
break;
366-
360+
case GT_SELECT_NEGCC:
361+
case GT_SELECT_INVCC:
362+
case GT_SELECT_INCCC:
367363
case GT_SELECTCC:
368364
genCodeForSelect(treeNode->AsOp());
369365
break;

src/coreclr/jit/compiler.hpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4290,11 +4290,7 @@ void GenTree::VisitOperands(TVisitor visitor)
42904290
}
42914291
FALLTHROUGH;
42924292

4293-
// Standard unary operators
4294-
#ifdef TARGET_ARM64
4295-
case GT_CNEG_LT:
4296-
case GT_CINCCC:
4297-
#endif // TARGET_ARM64
4293+
// Standard unary operators
42984294
case GT_STORE_LCL_VAR:
42994295
case GT_STORE_LCL_FLD:
43004296
case GT_NOT:

src/coreclr/jit/gentree.cpp

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6361,11 +6361,7 @@ bool GenTree::TryGetUse(GenTree* operand, GenTree*** pUse)
63616361
case GT_IL_OFFSET:
63626362
return false;
63636363

6364-
// Standard unary operators
6365-
#ifdef TARGET_ARM64
6366-
case GT_CNEG_LT:
6367-
case GT_CINCCC:
6368-
#endif // TARGET_ARM64
6364+
// Standard unary operators
63696365
case GT_STORE_LCL_VAR:
63706366
case GT_STORE_LCL_FLD:
63716367
case GT_NOT:
@@ -6554,7 +6550,11 @@ bool GenTree::TryGetUse(GenTree* operand, GenTree*** pUse)
65546550
}
65556551
return false;
65566552
}
6557-
6553+
#ifdef TARGET_ARM64
6554+
case GT_SELECT_NEG:
6555+
case GT_SELECT_INV:
6556+
case GT_SELECT_INC:
6557+
#endif
65586558
case GT_SELECT:
65596559
{
65606560
GenTreeConditional* const conditional = this->AsConditional();
@@ -9713,11 +9713,7 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node)
97139713
m_state = -1;
97149714
return;
97159715

9716-
// Standard unary operators
9717-
#ifdef TARGET_ARM64
9718-
case GT_CNEG_LT:
9719-
case GT_CINCCC:
9720-
#endif // TARGET_ARM64
9716+
// Standard unary operators
97219717
case GT_STORE_LCL_VAR:
97229718
case GT_STORE_LCL_FLD:
97239719
case GT_NOT:
@@ -9829,7 +9825,11 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node)
98299825
m_advance = &GenTreeUseEdgeIterator::AdvanceCall<CALL_ARGS>;
98309826
AdvanceCall<CALL_ARGS>();
98319827
return;
9832-
9828+
#ifdef TARGET_ARM64
9829+
case GT_SELECT_NEG:
9830+
case GT_SELECT_INV:
9831+
case GT_SELECT_INC:
9832+
#endif
98339833
case GT_SELECT:
98349834
m_edge = &m_node->AsConditional()->gtCond;
98359835
assert(*m_edge != nullptr);
@@ -9955,8 +9955,15 @@ void GenTreeUseEdgeIterator::AdvanceConditional()
99559955
switch (m_state)
99569956
{
99579957
case 0:
9958-
m_edge = &conditional->gtOp1;
9959-
m_state = 1;
9958+
m_edge = &conditional->gtOp1;
9959+
if (conditional->gtOp2 == nullptr)
9960+
{
9961+
m_advance = &GenTreeUseEdgeIterator::Terminate;
9962+
}
9963+
else
9964+
{
9965+
m_state = 1;
9966+
}
99609967
break;
99619968
case 1:
99629969
m_edge = &conditional->gtOp2;
@@ -12229,7 +12236,7 @@ void Compiler::gtDispTree(GenTree* tree,
1222912236
printf(" cond=%s", tree->AsOpCC()->gtCondition.Name());
1223012237
}
1223112238
#ifdef TARGET_ARM64
12232-
else if (tree->OperIs(GT_CINCCC))
12239+
else if (tree->OperIs(GT_SELECT_INCCC, GT_SELECT_INVCC, GT_SELECT_NEGCC))
1223312240
{
1223412241
printf(" cond=%s", tree->AsOpCC()->gtCondition.Name());
1223512242
}

0 commit comments

Comments
 (0)