Skip to content

Commit 9cfd11d

Browse files
authored
JIT: Unify arm64 and x64 GT_SELECT handling (#82610)
This unifies GT_SELECT/GT_SELECTCC handling between arm64 and x64. The arm64 backend no longer uses containment for compare chains; instead, there is a new GT_CCMP node that both produces and consumes flags, and lowering can lower GT_AND(op, relop) down to this node.
1 parent 0a15c3b commit 9cfd11d

File tree

14 files changed

+331
-405
lines changed

14 files changed

+331
-405
lines changed

src/coreclr/jit/codegen.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -883,8 +883,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
883883
void genCkfinite(GenTree* treeNode);
884884
void genCodeForCompare(GenTreeOp* tree);
885885
#ifdef TARGET_ARM64
886-
void genCodeForConditionalCompare(GenTreeOp* tree, GenCondition prevCond);
887-
void genCodeForContainedCompareChain(GenTree* tree, bool* inchain, GenCondition* prevCond);
886+
void genCodeForCCMP(GenTreeCCMP* ccmp);
888887
#endif
889888
void genCodeForSelect(GenTreeOp* select);
890889
void genIntrinsic(GenTreeIntrinsic* treeNode);
@@ -1559,7 +1558,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
15591558
#endif // TARGET_XARCH
15601559

15611560
#if defined(TARGET_ARM64)
1562-
static insCflags InsCflagsForCcmp(GenCondition cond);
15631561
static insCond JumpKindToInsCond(emitJumpKind condition);
15641562
#elif defined(TARGET_XARCH)
15651563
static instruction JumpKindToCmov(emitJumpKind condition);

src/coreclr/jit/codegenarm64.cpp

Lines changed: 35 additions & 188 deletions
Original file line numberDiff line numberDiff line change
@@ -2696,35 +2696,6 @@ void CodeGen::genCodeForBinary(GenTreeOp* tree)
26962696
return;
26972697
}
26982698

2699-
if (tree->isContainedCompareChainSegment(op2))
2700-
{
2701-
GenCondition cond;
2702-
bool chain = false;
2703-
2704-
JITDUMP("Generating compare chain:\n");
2705-
if (op1->isContained())
2706-
{
2707-
// Generate Op1 into flags.
2708-
genCodeForContainedCompareChain(op1, &chain, &cond);
2709-
assert(chain);
2710-
}
2711-
else
2712-
{
2713-
// Op1 is not contained, move it from a register into flags.
2714-
emit->emitIns_R_I(INS_cmp, emitActualTypeSize(op1), op1->GetRegNum(), 0);
2715-
cond = GenCondition::NE;
2716-
chain = true;
2717-
}
2718-
// Gen Op2 into flags.
2719-
genCodeForContainedCompareChain(op2, &chain, &cond);
2720-
assert(chain);
2721-
2722-
// Move the result from flags into a register.
2723-
inst_SETCC(cond, tree->TypeGet(), targetReg);
2724-
genProduceReg(tree);
2725-
return;
2726-
}
2727-
27282699
instruction ins = genGetInsForOper(tree->OperGet(), targetType);
27292700

27302701
if ((tree->gtFlags & GTF_SET_FLAGS) != 0)
@@ -4600,108 +4571,36 @@ void CodeGen::genCodeForCompare(GenTreeOp* tree)
46004571
// tree - a compare node (GT_EQ etc)
46014572
// cond - the condition of the previous generated compare.
46024573
//
4603-
void CodeGen::genCodeForConditionalCompare(GenTreeOp* tree, GenCondition prevCond)
4574+
void CodeGen::genCodeForCCMP(GenTreeCCMP* ccmp)
46044575
{
46054576
emitter* emit = GetEmitter();
46064577

4607-
GenTree* op1 = tree->gtGetOp1();
4608-
GenTree* op2 = tree->gtGetOp2();
4609-
var_types op1Type = genActualType(op1->TypeGet());
4610-
var_types op2Type = genActualType(op2->TypeGet());
4611-
emitAttr cmpSize = EA_ATTR(genTypeSize(op1Type));
4612-
regNumber targetReg = tree->GetRegNum();
4613-
regNumber srcReg1 = op1->GetRegNum();
4578+
genConsumeOperands(ccmp);
4579+
GenTree* op1 = ccmp->gtGetOp1();
4580+
GenTree* op2 = ccmp->gtGetOp2();
4581+
var_types op1Type = genActualType(op1->TypeGet());
4582+
var_types op2Type = genActualType(op2->TypeGet());
4583+
emitAttr cmpSize = emitActualTypeSize(op1Type);
4584+
regNumber srcReg1 = op1->GetRegNum();
46144585

46154586
// No float support or swapping op1 and op2 to generate cmp reg, imm.
46164587
assert(!varTypeIsFloating(op2Type));
46174588
assert(!op1->isContainedIntOrIImmed());
46184589

4619-
// Should only be called on contained nodes.
4620-
assert(targetReg == REG_NA);
4621-
4622-
// Should not be called for test conditionals (Arm64 does not have a ctst).
4623-
assert(tree->OperIsCmpCompare());
4624-
46254590
// For the ccmp flags, invert the condition of the compare.
4626-
insCflags cflags = InsCflagsForCcmp(GenCondition::FromRelop(tree));
4627-
46284591
// For the condition, use the previous compare.
4629-
const GenConditionDesc& prevDesc = GenConditionDesc::Get(prevCond);
4630-
insCond prevInsCond = JumpKindToInsCond(prevDesc.jumpKind1);
4592+
const GenConditionDesc& condDesc = GenConditionDesc::Get(ccmp->gtCondition);
4593+
insCond insCond = JumpKindToInsCond(condDesc.jumpKind1);
46314594

46324595
if (op2->isContainedIntOrIImmed())
46334596
{
46344597
GenTreeIntConCommon* intConst = op2->AsIntConCommon();
4635-
emit->emitIns_R_I_FLAGS_COND(INS_ccmp, cmpSize, srcReg1, (int)intConst->IconValue(), cflags, prevInsCond);
4598+
emit->emitIns_R_I_FLAGS_COND(INS_ccmp, cmpSize, srcReg1, (int)intConst->IconValue(), ccmp->gtFlagsVal, insCond);
46364599
}
46374600
else
46384601
{
46394602
regNumber srcReg2 = op2->GetRegNum();
4640-
emit->emitIns_R_R_FLAGS_COND(INS_ccmp, cmpSize, srcReg1, srcReg2, cflags, prevInsCond);
4641-
}
4642-
}
4643-
4644-
//------------------------------------------------------------------------
4645-
// genCodeForContainedCompareChain: Produce code for a chain of conditional compares.
4646-
//
4647-
// Only generates for contained nodes. Nodes that are not contained are assumed to be
4648-
// generated as part of standard tree generation.
4649-
//
4650-
// Arguments:
4651-
// tree - the node. Either a compare or a tree of compares connected by ANDs.
4652-
// inChain - whether a contained chain is in progress.
4653-
// prevCond - If a chain is in progress, the condition of the previous compare.
4654-
// Return:
4655-
// The last compare node generated.
4656-
//
4657-
void CodeGen::genCodeForContainedCompareChain(GenTree* tree, bool* inChain, GenCondition* prevCond)
4658-
{
4659-
assert(tree->isContained());
4660-
4661-
if (tree->OperIs(GT_AND))
4662-
{
4663-
GenTree* op1 = tree->gtGetOp1();
4664-
GenTree* op2 = tree->gtGetOp2();
4665-
4666-
assert(op2->isContained());
4667-
4668-
// If Op1 is contained, generate into flags. Otherwise, move the result into flags.
4669-
if (op1->isContained())
4670-
{
4671-
genCodeForContainedCompareChain(op1, inChain, prevCond);
4672-
assert(*inChain);
4673-
}
4674-
else
4675-
{
4676-
emitter* emit = GetEmitter();
4677-
emit->emitIns_R_I(INS_cmp, emitActualTypeSize(op1), op1->GetRegNum(), 0);
4678-
*prevCond = GenCondition::NE;
4679-
*inChain = true;
4680-
}
4681-
4682-
// Generate Op2 based on Op1.
4683-
genCodeForContainedCompareChain(op2, inChain, prevCond);
4684-
assert(*inChain);
4685-
}
4686-
else
4687-
{
4688-
assert(tree->OperIsCmpCompare());
4689-
4690-
// Generate the compare, putting the result in the flags register.
4691-
if (!*inChain)
4692-
{
4693-
// First item in a chain. Use a standard compare.
4694-
genCodeForCompare(tree->AsOp());
4695-
}
4696-
else
4697-
{
4698-
// Within the chain. Use a conditional compare (which is
4699-
// dependent on the previous emitted compare).
4700-
genCodeForConditionalCompare(tree->AsOp(), *prevCond);
4701-
}
4702-
4703-
*inChain = true;
4704-
*prevCond = GenCondition::FromRelop(tree);
4603+
emit->emitIns_R_R_FLAGS_COND(INS_ccmp, cmpSize, srcReg1, srcReg2, ccmp->gtFlagsVal, insCond);
47054604
}
47064605
}
47074606

@@ -4713,45 +4612,37 @@ void CodeGen::genCodeForContainedCompareChain(GenTree* tree, bool* inChain, GenC
47134612
//
47144613
void CodeGen::genCodeForSelect(GenTreeOp* tree)
47154614
{
4716-
assert(tree->OperIs(GT_SELECT));
4717-
GenTreeConditional* select = tree->AsConditional();
4718-
emitter* emit = GetEmitter();
4615+
assert(tree->OperIs(GT_SELECT, GT_SELECTCC));
4616+
GenTree* opcond = nullptr;
4617+
if (tree->OperIs(GT_SELECT))
4618+
{
4619+
opcond = tree->AsConditional()->gtCond;
4620+
genConsumeRegs(opcond);
4621+
}
47194622

4720-
GenTree* opcond = select->gtCond;
4721-
GenTree* op1 = select->gtOp1;
4722-
GenTree* op2 = select->gtOp2;
4723-
var_types op1Type = genActualType(op1->TypeGet());
4724-
var_types op2Type = genActualType(op2->TypeGet());
4725-
emitAttr attr = emitActualTypeSize(select->TypeGet());
4623+
emitter* emit = GetEmitter();
4624+
4625+
GenTree* op1 = tree->gtOp1;
4626+
GenTree* op2 = tree->gtOp2;
4627+
var_types op1Type = genActualType(op1);
4628+
var_types op2Type = genActualType(op2);
4629+
emitAttr attr = emitActualTypeSize(tree);
47264630

47274631
assert(!op1->isUsedFromMemory());
47284632
assert(genTypeSize(op1Type) == genTypeSize(op2Type));
47294633

4730-
GenCondition prevCond;
4731-
genConsumeRegs(opcond);
4732-
if (opcond->isContained())
4634+
GenCondition cond;
4635+
4636+
if (opcond != nullptr)
47334637
{
4734-
// Generate the contained condition.
4735-
if (opcond->OperIsCompare())
4736-
{
4737-
genCodeForCompare(opcond->AsOp());
4738-
prevCond = GenCondition::FromRelop(opcond);
4739-
}
4740-
else
4741-
{
4742-
// Condition is a compare chain. Try to contain it.
4743-
assert(opcond->OperIs(GT_AND));
4744-
bool chain = false;
4745-
JITDUMP("Generating compare chain:\n");
4746-
genCodeForContainedCompareChain(opcond, &chain, &prevCond);
4747-
assert(chain);
4748-
}
4638+
// Condition has been generated into a register - move it into flags.
4639+
emit->emitIns_R_I(INS_cmp, emitActualTypeSize(opcond), opcond->GetRegNum(), 0);
4640+
cond = GenCondition::NE;
47494641
}
47504642
else
47514643
{
4752-
// Condition has been generated into a register - move it into flags.
4753-
emit->emitIns_R_I(INS_cmp, emitActualTypeSize(opcond), opcond->GetRegNum(), 0);
4754-
prevCond = GenCondition::NE;
4644+
assert(tree->OperIs(GT_SELECTCC));
4645+
cond = tree->AsOpCC()->gtCondition;
47554646
}
47564647

47574648
assert(!op1->isContained() || op1->IsIntegralConst(0));
@@ -4760,7 +4651,7 @@ void CodeGen::genCodeForSelect(GenTreeOp* tree)
47604651
regNumber targetReg = tree->GetRegNum();
47614652
regNumber srcReg1 = op1->IsIntegralConst(0) ? REG_ZR : genConsumeReg(op1);
47624653
regNumber srcReg2 = op2->IsIntegralConst(0) ? REG_ZR : genConsumeReg(op2);
4763-
const GenConditionDesc& prevDesc = GenConditionDesc::Get(prevCond);
4654+
const GenConditionDesc& prevDesc = GenConditionDesc::Get(cond);
47644655

47654656
emit->emitIns_R_R_R_COND(INS_csel, attr, targetReg, srcReg1, srcReg2, JumpKindToInsCond(prevDesc.jumpKind1));
47664657

@@ -10382,50 +10273,6 @@ void CodeGen::genCodeForCond(GenTreeOp* tree)
1038210273
genProduceReg(tree);
1038310274
}
1038410275

10385-
//------------------------------------------------------------------------
10386-
// InsCflagsForCcmp: Get the Cflags for a required for a CCMP instruction.
10387-
//
10388-
// Consider:
10389-
// cmp w, x
10390-
// ccmp y, z, A, COND
10391-
// This is: compare w and x, if this matches condition COND, then compare y and z.
10392-
// Otherwise set flags to A - this should match the case where cmp failed.
10393-
// Given COND, this function returns A.
10394-
//
10395-
// Arguments:
10396-
// cond - the GenCondition.
10397-
//
10398-
insCflags CodeGen::InsCflagsForCcmp(GenCondition cond)
10399-
{
10400-
GenCondition inverted = GenCondition::Reverse(cond);
10401-
switch (inverted.GetCode())
10402-
{
10403-
case GenCondition::EQ:
10404-
return INS_FLAGS_Z;
10405-
case GenCondition::NE:
10406-
return INS_FLAGS_NONE;
10407-
case GenCondition::SGE:
10408-
return INS_FLAGS_Z;
10409-
case GenCondition::SGT:
10410-
return INS_FLAGS_NONE;
10411-
case GenCondition::SLT:
10412-
return INS_FLAGS_NC;
10413-
case GenCondition::SLE:
10414-
return INS_FLAGS_NZC;
10415-
case GenCondition::UGE:
10416-
return INS_FLAGS_C;
10417-
case GenCondition::UGT:
10418-
return INS_FLAGS_C;
10419-
case GenCondition::ULT:
10420-
return INS_FLAGS_NONE;
10421-
case GenCondition::ULE:
10422-
return INS_FLAGS_Z;
10423-
default:
10424-
NO_WAY("unexpected condition type");
10425-
return INS_FLAGS_NONE;
10426-
}
10427-
}
10428-
1042910276
//------------------------------------------------------------------------
1043010277
// JumpKindToInsCond: Convert a Jump Kind to a condition.
1043110278
//

src/coreclr/jit/codegenarmarch.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,12 +361,20 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
361361
case GT_SELECT:
362362
genCodeForSelect(treeNode->AsConditional());
363363
break;
364+
365+
case GT_SELECTCC:
366+
genCodeForSelect(treeNode->AsOp());
367+
break;
364368
#endif
365369

366370
#ifdef TARGET_ARM64
367371
case GT_JCMP:
368372
genCodeForJumpCompare(treeNode->AsOp());
369373
break;
374+
375+
case GT_CCMP:
376+
genCodeForCCMP(treeNode->AsCCMP());
377+
break;
370378
#endif // TARGET_ARM64
371379

372380
case GT_JCC:

src/coreclr/jit/gentree.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,10 @@ void GenTree::InitNodeSize()
313313
static_assert_no_msg(sizeof(GenTreeLclFld) <= TREE_NODE_SZ_SMALL);
314314
static_assert_no_msg(sizeof(GenTreeCC) <= TREE_NODE_SZ_SMALL);
315315
static_assert_no_msg(sizeof(GenTreeOpCC) <= TREE_NODE_SZ_SMALL);
316+
#ifdef TARGET_ARM64
317+
static_assert_no_msg(sizeof(GenTreeCCMP) <= TREE_NODE_SZ_SMALL);
318+
#endif
319+
static_assert_no_msg(sizeof(GenTreeConditional) <= TREE_NODE_SZ_SMALL);
316320
static_assert_no_msg(sizeof(GenTreeCast) <= TREE_NODE_SZ_LARGE); // *** large node
317321
static_assert_no_msg(sizeof(GenTreeBox) <= TREE_NODE_SZ_LARGE); // *** large node
318322
static_assert_no_msg(sizeof(GenTreeField) <= TREE_NODE_SZ_LARGE); // *** large node
@@ -11317,6 +11321,17 @@ void Compiler::gtDispLclVarStructType(unsigned lclNum)
1131711321
}
1131811322
}
1131911323

11324+
#if defined(DEBUG) && defined(TARGET_ARM64)
11325+
static const char* InsCflagsToString(insCflags flags)
11326+
{
11327+
const static char* s_table[16] = {"0", "v", "c", "cv", "z", "zv", "zc", "zcv",
11328+
"n", "nv", "nc", "ncv", "nz", "nzv", "nzc", "nzcv"};
11329+
unsigned index = (unsigned)flags;
11330+
assert((0 <= index) && (index < ArrLen(s_table)));
11331+
return s_table[index];
11332+
}
11333+
#endif
11334+
1132011335
//------------------------------------------------------------------------
1132111336
// gtDispSsaName: Display the SSA use/def for a given local.
1132211337
//
@@ -12162,6 +12177,13 @@ void Compiler::gtDispTree(GenTree* tree,
1216212177
{
1216312178
printf(" cond=%s", tree->AsOpCC()->gtCondition.Name());
1216412179
}
12180+
#ifdef TARGET_ARM64
12181+
else if (tree->OperIs(GT_CCMP))
12182+
{
12183+
printf(" cond=%s flags=%s", tree->AsCCMP()->gtCondition.Name(),
12184+
InsCflagsToString(tree->AsCCMP()->gtFlagsVal));
12185+
}
12186+
#endif
1216512187

1216612188
gtDispCommonEndLine(tree);
1216712189

@@ -18837,7 +18859,13 @@ bool GenTree::SupportsSettingZeroFlag()
1883718859
}
1883818860
#endif
1883918861
#elif defined(TARGET_ARM64)
18840-
if (OperIs(GT_AND, GT_ADD, GT_SUB))
18862+
if (OperIs(GT_AND))
18863+
{
18864+
return true;
18865+
}
18866+
18867+
// We do not support setting zero flag for madd/msub.
18868+
if (OperIs(GT_ADD, GT_SUB) && (!gtGetOp2()->OperIs(GT_MUL) || !gtGetOp2()->isContained()))
1884118869
{
1884218870
return true;
1884318871
}

0 commit comments

Comments
 (0)