Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Arm64: Combine if conditions into compare chains #79283

Merged
merged 32 commits into from
Mar 21, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
3aa62af
Create AND chains in bool optimizer pass
a74nh Dec 5, 2022
48b68ec
Remove contained and morph checks
a74nh Jan 26, 2023
56ff631
Add GT_ANDFLAGS node
a74nh Jan 26, 2023
a919a13
Fix X64 build
a74nh Jan 30, 2023
893cbb5
Review fixups
a74nh Feb 1, 2023
31c8560
Add CCMP_EQ/CCMP_NE nodes
a74nh Feb 3, 2023
60341f3
Various cleanups
a74nh Feb 6, 2023
177c92a
remove fgopt changes
a74nh Feb 6, 2023
62dabf4
restore lsraarm64 GT_AND change
a74nh Feb 7, 2023
ca38951
Make GT_CCMP_ into conditional nodes
a74nh Feb 14, 2023
2360f26
Merge branch main
a74nh Mar 8, 2023
8323f71
Remove lowering/codegen changes
a74nh Mar 8, 2023
bef86f0
update header
a74nh Mar 8, 2023
3bdceeb
Add costing with stress overrides
a74nh Mar 9, 2023
adf6b99
Better cbz comment
a74nh Mar 9, 2023
5b99a4d
Use fgRemoveRefPred
a74nh Mar 9, 2023
e8ebd2a
Allow reversed conditions in tests
a74nh Mar 9, 2023
748252f
Move optimize bools pass to a new file
a74nh Mar 9, 2023
6a94a6e
Improve cbz detection
a74nh Mar 9, 2023
8846bc3
Move optbools back into optimizer
a74nh Mar 13, 2023
1350849
Forward iterate through the blocks
a74nh Mar 13, 2023
f056765
Fixup comment block
a74nh Mar 13, 2023
a690be6
Improve scanning for existing chains
a74nh Mar 13, 2023
02bbafe
Fix formatting
a74nh Mar 13, 2023
8fcef67
Allow main optimize bools loop to run again
a74nh Mar 14, 2023
e7f4369
Check for tbz conditions generated from pow2 values
a74nh Mar 15, 2023
7f9fdb8
Fix and expand test cases
a74nh Mar 15, 2023
3d8ed4f
Minor fixups
a74nh Mar 15, 2023
191a650
Allow wider range of conditions
a74nh Mar 16, 2023
6cf9cd3
Minor cleanups
a74nh Mar 17, 2023
18ea05c
Reduce max allowed cost
a74nh Mar 17, 2023
238d810
Fix formatting
a74nh Mar 17, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/coreclr/jit/codegen.h
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
void genCodeForContainedCompareChain(GenTree* tree, bool* inchain, GenCondition* prevCond);
#endif
void genCodeForSelect(GenTreeOp* select);
void genCodeForConditionalCompare(GenTreeOp* select);
void genIntrinsic(GenTreeIntrinsic* treeNode);
void genPutArgStk(GenTreePutArgStk* treeNode);
void genPutArgReg(GenTreeOp* tree);
Expand Down
104 changes: 99 additions & 5 deletions src/coreclr/jit/codegenarm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4664,7 +4664,7 @@ void CodeGen::genCodeForContainedCompareChain(GenTree* tree, bool* inChain, GenC
{
assert(tree->isContained());

if (tree->OperIs(GT_AND))
if (tree->OperIs(GT_AND) || tree->OperIsConditionalCompare())
{
GenTree* op1 = tree->gtGetOp1();
GenTree* op2 = tree->gtGetOp2();
Expand Down Expand Up @@ -4734,28 +4734,53 @@ void CodeGen::genCodeForSelect(GenTreeOp* tree)
assert(genTypeSize(op1Type) == genTypeSize(op2Type));

GenCondition prevCond;
genConsumeRegs(opcond);
if (opcond->isContained())
{
// Generate the contained condition.
if (opcond->OperIsCompare())
{
genConsumeRegs(opcond);
genCodeForCompare(opcond->AsOp());
prevCond = GenCondition::FromRelop(opcond);
}
else
{
// Condition is a compare chain. Try to contain it.
assert(opcond->OperIs(GT_AND));
assert(opcond->OperIsConditionalCompare());

// Condition is a compare chain. Generate it.
bool chain = false;
JITDUMP("Generating compare chain:\n");
genCodeForContainedCompareChain(opcond, &chain, &prevCond);

GenTree* op1 = opcond->gtGetOp1();
GenTree* op2 = opcond->gtGetOp2();

genConsumeRegs(op1);
genConsumeRegs(op2);

// If Op1 is contained, generate it into flags.
if (op1->isContained())
{
genCodeForContainedCompareChain(op1, &chain, &prevCond);
assert(chain);
assert(op2->isContained());
}

// Generate op2 into flags.
assert(op2->isContained());
genCodeForContainedCompareChain(op2, &chain, &prevCond);
assert(chain);

// Reverse condition for NE.
if (opcond->OperIs(GT_CCMP_NE))
{
prevCond = GenCondition::Reverse(prevCond);
}
}
}
else
{
// Condition has been generated into a register - move it into flags.
genConsumeRegs(opcond);
emit->emitIns_R_I(INS_cmp, emitActualTypeSize(opcond), opcond->GetRegNum(), 0);
prevCond = GenCondition::NE;
}
Expand Down Expand Up @@ -4784,6 +4809,75 @@ void CodeGen::genCodeForSelect(GenTreeOp* tree)
genProduceReg(tree);
}

//------------------------------------------------------------------------
// genCodeForConditionalCompare: Generates code for CCMP node.
//
// Arguments:
// tree - the node
//
void CodeGen::genCodeForConditionalCompare(GenTreeOp* tree)
{
var_types targetType = tree->TypeGet();
emitter* emit = GetEmitter();

assert(tree->OperIsConditionalCompare());

GenTree* op1 = tree->gtGetOp1();
GenTree* op2 = tree->gtGetOp2();

assert(tree->isContainedCompareChainSegment(op2));

GenCondition cond;
bool chain = false;

JITDUMP("Generating compare chain:\n");
if (op1->isContained())
{
// Generate Op1 into flags.
genCodeForContainedCompareChain(op1, &chain, &cond);
assert(chain);
}
else
{
// Op1 is not contained, move it from a register into flags.
emit->emitIns_R_I(INS_cmp, emitActualTypeSize(op1), op1->GetRegNum(), 0);
cond = GenCondition::NE;
chain = true;
}

// AHTODO: not sure this is always true
assert(op2->isContained());

// Gen Op2 into flags.
genCodeForContainedCompareChain(op2, &chain, &cond);
assert(chain);

// Are we evaluating this into a register?
regNumber targetReg = tree->GetRegNum();
if (targetReg != REG_NA)
{
// AHTODO: merge this into helper function with genCodeForJumpTrue()
// Find the last contained compare in the chain.
GenCondition condition;
GenTreeOp* lastCompare = tree->gtGetOp2()->AsOp();
assert(lastCompare->isContained());
while (!lastCompare->OperIsCompare())
{
assert(lastCompare->OperIs(GT_AND) || lastCompare->OperIsConditionalCompare());
lastCompare = lastCompare->gtGetOp2()->AsOp();
assert(lastCompare->isContained());
}
condition = GenCondition::FromRelop(lastCompare);
if (tree->OperIs(GT_CCMP_NE))
{
condition = GenCondition::Reverse(condition);
}

inst_SETCC(condition, tree->TypeGet(), targetReg);
genProduceReg(tree);
}
}

//------------------------------------------------------------------------
// genCodeForJumpCompare: Generates code for jmpCompare statement.
//
Expand Down
6 changes: 6 additions & 0 deletions src/coreclr/jit/codegenarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,12 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
case GT_SELECT:
genCodeForSelect(treeNode->AsConditional());
break;

case GT_CCMP_EQ:
case GT_CCMP_NE:
genConsumeOperands(treeNode->AsOp());
genCodeForConditionalCompare(treeNode->AsOp());
break;
#endif

case GT_JTRUE:
Expand Down
37 changes: 33 additions & 4 deletions src/coreclr/jit/codegenlinear.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1610,10 +1610,10 @@ void CodeGen::genConsumeRegs(GenTree* tree)
assert(cast->isContained());
genConsumeAddress(cast->CastOp());
}
else if (tree->OperIsCompare() || tree->OperIs(GT_AND))
else if (tree->OperIsCompare() || tree->OperIs(GT_AND) || tree->OperIsConditionalCompare())
{
// Compares can be contained by a SELECT.
// ANDs and Cmp Compares may be contained in a chain.
// Compares, ANDs and conditional compares may be contained in a chain.
genConsumeRegs(tree->gtGetOp1());
genConsumeRegs(tree->gtGetOp2());
}
Expand Down Expand Up @@ -2592,8 +2592,37 @@ void CodeGen::genCodeForJumpTrue(GenTreeOp* jtrue)
assert(compiler->compCurBB->bbJumpKind == BBJ_COND);
assert(jtrue->OperIs(GT_JTRUE));

GenTreeOp* relop = jtrue->gtGetOp1()->AsOp();
GenCondition condition = GenCondition::FromRelop(relop);
GenTreeOp* relop = jtrue->gtGetOp1()->AsOp();
GenCondition condition;

// Operands should never be contained inside a jtrue.
assert(!relop->isContained());

#if defined(TARGET_ARM64)
if (relop->OperIsConditionalCompare())
{
// Find the last contained compare in the chain.
assert(relop->gtType == TYP_VOID);
GenTreeOp* lastCompare = relop->gtGetOp2()->AsOp();
assert(lastCompare->isContained());
while (!lastCompare->OperIsCompare())
{
assert(lastCompare->OperIs(GT_AND) || lastCompare->OperIsConditionalCompare());
lastCompare = lastCompare->gtGetOp2()->AsOp();
assert(lastCompare->isContained());
}
condition = GenCondition::FromRelop(lastCompare);
if (relop->OperIs(GT_CCMP_NE))
{
condition = GenCondition::Reverse(condition);
}
}
else
#endif
{
assert(relop->OperIsCompare());
condition = GenCondition::FromRelop(relop);
}

if (condition.PreferSwap())
{
Expand Down
13 changes: 11 additions & 2 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3454,6 +3454,16 @@ GenTree* Compiler::gtReverseCond(GenTree* tree)
// tbz <=> tbnz
tree->gtFlags ^= GTF_JCMP_EQ;
}
#if defined(TARGET_ARM64)
else if (tree->OperIs(GT_CCMP_EQ))
{
tree->SetOper(GT_CCMP_NE);
}
else if (tree->OperIs(GT_CCMP_NE))
{
tree->SetOper(GT_CCMP_EQ);
}
#endif
else
{
tree = gtNewOperNode(GT_NOT, TYP_INT, tree);
Expand Down Expand Up @@ -17132,8 +17142,7 @@ bool GenTree::canBeContained() const
return false;
}

// It is not possible for nodes that do not produce values or that are not containable values to be contained.
if (!IsValue() || ((DebugOperKind() & DBK_NOCONTAIN) != 0) || (OperIsHWIntrinsic() && !isContainableHWIntrinsic()))
if (((DebugOperKind() & DBK_NOCONTAIN) != 0) || (OperIsHWIntrinsic() && !isContainableHWIntrinsic()))
{
return false;
}
Expand Down
21 changes: 19 additions & 2 deletions src/coreclr/jit/gentree.h
Original file line number Diff line number Diff line change
Expand Up @@ -910,11 +910,14 @@ struct GenTree
return isContained() && IsCnsIntOrI() && !isUsedFromSpillTemp();
}

#if defined(TARGET_ARM64)
// Node and its child in isolation form a contained compare chain.
bool isContainedCompareChainSegment(GenTree* child) const
{
return (OperIs(GT_AND) && child->isContained() && (child->OperIs(GT_AND) || child->OperIsCmpCompare()));
return ((OperIs(GT_AND) || OperIsConditionalCompare()) && child->isContained() &&
(child->OperIs(GT_AND) || child->OperIsCmpCompare() || child->OperIsConditionalCompare()));
}
#endif

bool isContainedFltOrDblImmed() const
{
Expand Down Expand Up @@ -1090,7 +1093,7 @@ struct GenTree
{
// These are the only operators which can produce either VOID or non-VOID results.
assert(OperIs(GT_NOP, GT_CALL, GT_COMMA) || OperIsCompare() || OperIsLong() || OperIsHWIntrinsic() ||
IsCnsVec());
IsCnsVec() || OperIsConditionalCompare());
return false;
}

Expand Down Expand Up @@ -1376,6 +1379,20 @@ struct GenTree
return OperIsConditional(OperGet());
}

static bool OperIsConditionalCompare(genTreeOps gtOper)
{
#if defined(TARGET_ARM64)
return (GT_CCMP_EQ == gtOper || GT_CCMP_NE == gtOper);
#else
return false;
#endif
}

bool OperIsConditionalCompare() const
{
return OperIsConditionalCompare(OperGet());
}

static bool OperIsCC(genTreeOps gtOper)
{
return (gtOper == GT_JCC) || (gtOper == GT_SETCC);
Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/jit/gtlist.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,11 @@ GTNODE(SETCC , GenTreeCC ,0,GTK_LEAF|DBK_NOTHIR)
// The XARCH BT instruction. Like CMP, this sets the condition flags (CF to be precise) and does not produce a value.
GTNODE(BT , GenTreeOp ,0,(GTK_BINOP|GTK_NOVALUE|DBK_NOTHIR))
#endif
// Sets the condition flags according to the combined results of its children.
#if defined(TARGET_ARM64)
GTNODE(CCMP_EQ , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR)
GTNODE(CCMP_NE , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR)
a74nh marked this conversation as resolved.
Show resolved Hide resolved
#endif

//-----------------------------------------------------------------------------
// Other nodes that look like unary/binary operators:
Expand Down
37 changes: 24 additions & 13 deletions src/coreclr/jit/lower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2868,15 +2868,6 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp)
GenTreeIntCon* op2 = cmp->gtGetOp2()->AsIntCon();
ssize_t op2Value = op2->IconValue();

#ifdef TARGET_ARM64
// Do not optimise further if op1 has a contained chain.
if (op1->OperIs(GT_AND) &&
(op1->isContainedCompareChainSegment(op1->gtGetOp1()) || op1->isContainedCompareChainSegment(op1->gtGetOp2())))
{
return cmp;
}
#endif

#ifdef TARGET_XARCH
var_types op1Type = op1->TypeGet();
if (IsContainableMemoryOp(op1) && varTypeIsSmall(op1Type) && FitsIn(op1Type, op2Value))
Expand Down Expand Up @@ -7164,10 +7155,30 @@ void Lowering::ContainCheckRet(GenTreeUnOp* ret)
//
void Lowering::ContainCheckJTrue(GenTreeOp* node)
{
// The compare does not need to be generated into a register.
GenTree* cmp = node->gtGetOp1();
cmp->gtType = TYP_VOID;
cmp->gtFlags |= GTF_SET_FLAGS;
GenTree* op1 = node->gtGetOp1();

if (op1->OperIsCompare())
{
// The compare does not need to be generated into a register.
op1->gtType = TYP_VOID;
op1->gtFlags |= GTF_SET_FLAGS;
}
#if defined(TARGET_ARM64)
else if (op1->OperIsConditionalCompare())
{
// If the second op of the CCMP is contained, then the CCMP does not need to be generated
// into a register.
if (op1->gtGetOp2()->isContained())
{
op1->gtType = TYP_VOID;
op1->gtFlags |= GTF_SET_FLAGS;
}
}
#endif
else
{
unreached();
}
}

//------------------------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/jit/lower.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ class Lowering final : public Phase
#ifdef TARGET_ARM64
bool IsValidCompareChain(GenTree* child, GenTree* parent);
bool ContainCheckCompareChain(GenTree* child, GenTree* parent, GenTree** earliestValid);
void ContainCheckCompareChainForAnd(GenTree* tree);
void ContainCheckConditionalCompare(GenTreeOp* cmp);
bool ContainCheckCompareChainForAnd(GenTree* tree);
void ContainCheckChainedCompare(GenTreeOp* cmp);
void ContainCheckNeg(GenTreeOp* neg);
#endif
void ContainCheckSelect(GenTreeOp* select);
Expand Down
Loading