Skip to content

Commit 33be29e

Browse files
authored
Arm64: Add If conversion pass (#73472)
* Arm64: Add If conversion pass * Minor review fixups * Return a PhaseStatus * Fix formatting * Check for side effects on NOPs * Add function block comments for the phase * Remove creation of AND chains from if conversion pass * Update middleBlock flow * Check for order side effects * Remove COLON_COND check * Remove flag toggling * Move the conditional assignment to the JTRUE block * Fix formatting * Allow conditions with side effects * Fix formatting * Correct all moved SSA statements * Add size costing check * Only move middle block ssa defs * Fix formatting * Fewer SSA assumptions * Use implicit func for value numbering * Update header for gtFoldExprConditional * Cost based on speed * Add Stress mode for inner loops * Rework costings * Check for invalid VNs * Ignore compares against zero * Ensure float compares are contained * Allow if conversion of test compares * Do not contain test compares within compare chains * Add float versions of the JIT/opt/Compares tests * Fix formatting * Compare chains use CmpCompares, selects use Compares * Fix flow checking for empty blocks * Fix to contexts setting JitStdOutFile * Fix attr and reg producing in select generation
1 parent 3429533 commit 33be29e

15 files changed

+923
-48
lines changed

src/coreclr/jit/codegenarm64.cpp

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4613,6 +4613,9 @@ void CodeGen::genCodeForConditionalCompare(GenTreeOp* tree, GenCondition prevCon
46134613
// Should only be called on contained nodes.
46144614
assert(targetReg == REG_NA);
46154615

4616+
// Should not be called for test conditionals (Arm64 does not have a ctst).
4617+
assert(tree->OperIsCmpCompare());
4618+
46164619
// For the ccmp flags, invert the condition of the compare.
46174620
insCflags cflags = InsCflagsForCcmp(GenCondition::FromRelop(tree));
46184621

@@ -4711,7 +4714,7 @@ void CodeGen::genCodeForSelect(GenTreeConditional* tree)
47114714
GenTree* op2 = tree->gtOp2;
47124715
var_types op1Type = genActualType(op1->TypeGet());
47134716
var_types op2Type = genActualType(op2->TypeGet());
4714-
emitAttr cmpSize = EA_ATTR(genTypeSize(op1Type));
4717+
emitAttr attr = emitActualTypeSize(tree->TypeGet());
47154718

47164719
assert(!op1->isUsedFromMemory());
47174720
assert(genTypeSize(op1Type) == genTypeSize(op2Type));
@@ -4721,10 +4724,20 @@ void CodeGen::genCodeForSelect(GenTreeConditional* tree)
47214724
if (opcond->isContained())
47224725
{
47234726
// Generate the contained condition.
4724-
bool chain = false;
4725-
JITDUMP("Generating compare chain:\n");
4726-
genCodeForContainedCompareChain(opcond, &chain, &prevCond);
4727-
assert(chain);
4727+
if (opcond->OperIsCompare())
4728+
{
4729+
genCodeForCompare(opcond->AsOp());
4730+
prevCond = GenCondition::FromRelop(opcond);
4731+
}
4732+
else
4733+
{
4734+
// Condition is a compare chain. Try to contain it.
4735+
assert(opcond->OperIs(GT_AND));
4736+
bool chain = false;
4737+
JITDUMP("Generating compare chain:\n");
4738+
genCodeForContainedCompareChain(opcond, &chain, &prevCond);
4739+
assert(chain);
4740+
}
47284741
}
47294742
else
47304743
{
@@ -4738,8 +4751,9 @@ void CodeGen::genCodeForSelect(GenTreeConditional* tree)
47384751
regNumber srcReg2 = genConsumeReg(op2);
47394752
const GenConditionDesc& prevDesc = GenConditionDesc::Get(prevCond);
47404753

4741-
emit->emitIns_R_R_R_COND(INS_csel, cmpSize, targetReg, srcReg1, srcReg2, JumpKindToInsCond(prevDesc.jumpKind1));
4754+
emit->emitIns_R_R_R_COND(INS_csel, attr, targetReg, srcReg1, srcReg2, JumpKindToInsCond(prevDesc.jumpKind1));
47424755
regSet.verifyRegUsed(targetReg);
4756+
genProduceReg(tree);
47434757
}
47444758

47454759
//------------------------------------------------------------------------

src/coreclr/jit/codegenlinear.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1634,9 +1634,10 @@ void CodeGen::genConsumeRegs(GenTree* tree)
16341634
assert(cast->isContained());
16351635
genConsumeAddress(cast->CastOp());
16361636
}
1637-
else if (tree->OperIsCmpCompare() || tree->OperIs(GT_AND))
1637+
else if (tree->OperIsCompare() || tree->OperIs(GT_AND))
16381638
{
1639-
// Compares and ANDs may be contained in a conditional chain.
1639+
// Compares can be contained by a SELECT.
1640+
// ANDs and Cmp Compares may be contained in a chain.
16401641
genConsumeRegs(tree->gtGetOp1());
16411642
genConsumeRegs(tree->gtGetOp2());
16421643
}

src/coreclr/jit/compiler.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4741,6 +4741,7 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
47414741
bool doCse = true;
47424742
bool doAssertionProp = true;
47434743
bool doRangeAnalysis = true;
4744+
bool doIfConversion = true;
47444745
int iterations = 1;
47454746

47464747
#if defined(OPT_CONFIG)
@@ -4753,6 +4754,7 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
47534754
doCse = doValueNum;
47544755
doAssertionProp = doValueNum && (JitConfig.JitDoAssertionProp() != 0);
47554756
doRangeAnalysis = doAssertionProp && (JitConfig.JitDoRangeAnalysis() != 0);
4757+
doIfConversion = doIfConversion && (JitConfig.JitDoIfConversion() != 0);
47564758

47574759
if (opts.optRepeat)
47584760
{
@@ -4834,6 +4836,13 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
48344836
DoPhase(this, PHASE_ASSERTION_PROP_MAIN, &Compiler::optAssertionPropMain);
48354837
}
48364838

4839+
if (doIfConversion)
4840+
{
4841+
// If conversion
4842+
//
4843+
DoPhase(this, PHASE_IF_CONVERSION, &Compiler::optIfConversion);
4844+
}
4845+
48374846
if (doRangeAnalysis)
48384847
{
48394848
// Bounds check elimination via range analysis

src/coreclr/jit/compiler.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2392,6 +2392,9 @@ class Compiler
23922392
GenTreeLclVar* gtNewLclVarAddrNode(unsigned lclNum, var_types type = TYP_I_IMPL);
23932393
GenTreeLclFld* gtNewLclFldAddrNode(unsigned lclNum, unsigned lclOffs, var_types type = TYP_I_IMPL);
23942394

2395+
GenTreeConditional* gtNewConditionalNode(
2396+
genTreeOps oper, GenTree* cond, GenTree* op1, GenTree* op2, var_types type);
2397+
23952398
#ifdef FEATURE_SIMD
23962399
GenTreeSIMD* gtNewSIMDNode(
23972400
var_types type, GenTree* op1, SIMDIntrinsicID simdIntrinsicID, CorInfoType simdBaseJitType, unsigned simdSize);
@@ -2831,6 +2834,7 @@ class Compiler
28312834
GenTree* gtFoldExprSpecial(GenTree* tree);
28322835
GenTree* gtFoldBoxNullable(GenTree* tree);
28332836
GenTree* gtFoldExprCompare(GenTree* tree);
2837+
GenTree* gtFoldExprConditional(GenTree* tree);
28342838
GenTree* gtCreateHandleCompare(genTreeOps oper,
28352839
GenTree* op1,
28362840
GenTree* op2,
@@ -4869,6 +4873,9 @@ class Compiler
48694873
// Does value-numbering for a block assignment.
48704874
void fgValueNumberBlockAssignment(GenTree* tree);
48714875

4876+
// Does value-numbering for a variable definition that has SSA.
4877+
void fgValueNumberSsaVarDef(GenTreeLclVarCommon* lcl);
4878+
48724879
// Does value-numbering for a cast tree.
48734880
void fgValueNumberCastTree(GenTree* tree);
48744881

@@ -6082,6 +6089,7 @@ class Compiler
60826089
void optEnsureUniqueHead(unsigned loopInd, weight_t ambientWeight);
60836090
PhaseStatus optUnrollLoops(); // Unrolls loops (needs to have cost info)
60846091
void optRemoveRedundantZeroInits();
6092+
PhaseStatus optIfConversion(); // If conversion
60856093

60866094
protected:
60876095
// This enumeration describes what is killed by a call.
@@ -6470,6 +6478,7 @@ class Compiler
64706478
OptInvertCountTreeInfoType optInvertCountTreeInfo(GenTree* tree);
64716479

64726480
bool optInvertWhileLoop(BasicBlock* block);
6481+
bool optIfConvert(BasicBlock* block);
64736482

64746483
private:
64756484
static bool optIterSmallOverflow(int iterAtExit, var_types incrType);
@@ -7378,6 +7387,7 @@ class Compiler
73787387
GenTree* optAssertionProp_Cast(ASSERT_VALARG_TP assertions, GenTreeCast* cast, Statement* stmt);
73797388
GenTree* optAssertionProp_Call(ASSERT_VALARG_TP assertions, GenTreeCall* call, Statement* stmt);
73807389
GenTree* optAssertionProp_RelOp(ASSERT_VALARG_TP assertions, GenTree* tree, Statement* stmt);
7390+
GenTree* optAssertionProp_ConditionalOp(ASSERT_VALARG_TP assertions, GenTree* tree, Statement* stmt);
73817391
GenTree* optAssertionProp_Comma(ASSERT_VALARG_TP assertions, GenTree* tree, Statement* stmt);
73827392
GenTree* optAssertionProp_BndsChk(ASSERT_VALARG_TP assertions, GenTree* tree, Statement* stmt);
73837393
GenTree* optAssertionPropGlobal_RelOp(ASSERT_VALARG_TP assertions, GenTree* tree, Statement* stmt);
@@ -9576,6 +9586,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
95769586
STRESS_MODE(EMITTER) \
95779587
STRESS_MODE(CHK_REIMPORT) \
95789588
STRESS_MODE(GENERIC_CHECK) \
9589+
STRESS_MODE(IF_CONVERSION_COST) \
9590+
STRESS_MODE(IF_CONVERSION_INNER_LOOPS) \
95799591
STRESS_MODE(COUNT)
95809592

95819593
enum compStressArea

src/coreclr/jit/compphases.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ CompPhaseNameMacro(PHASE_OPTIMIZE_VALNUM_CSES, "Optimize Valnum CSEs",
8787
CompPhaseNameMacro(PHASE_VN_COPY_PROP, "VN based copy prop", false, -1, false)
8888
CompPhaseNameMacro(PHASE_OPTIMIZE_BRANCHES, "Redundant branch opts", false, -1, false)
8989
CompPhaseNameMacro(PHASE_ASSERTION_PROP_MAIN, "Assertion prop", false, -1, false)
90+
CompPhaseNameMacro(PHASE_IF_CONVERSION, "If conversion", false, -1, false)
9091
CompPhaseNameMacro(PHASE_OPT_UPDATE_FLOW_GRAPH, "Update flow graph opt pass", false, -1, false)
9192
CompPhaseNameMacro(PHASE_COMPUTE_EDGE_WEIGHTS2, "Compute edge weights (2, false)",false, -1, false)
9293
CompPhaseNameMacro(PHASE_INSERT_GC_POLLS, "Insert GC Polls", false, -1, true)

src/coreclr/jit/gentree.cpp

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5847,6 +5847,9 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree)
58475847
level = max(level, lvl2);
58485848
costEx += tree->AsConditional()->gtOp2->GetCostEx();
58495849
costSz += tree->AsConditional()->gtOp2->GetCostSz();
5850+
5851+
costEx += 1;
5852+
costSz += 1;
58505853
break;
58515854

58525855
default:
@@ -7387,6 +7390,17 @@ GenTreeLclFld* Compiler::gtNewLclFldAddrNode(unsigned lclNum, unsigned lclOffs,
73877390
return node;
73887391
}
73897392

7393+
GenTreeConditional* Compiler::gtNewConditionalNode(
7394+
genTreeOps oper, GenTree* cond, GenTree* op1, GenTree* op2, var_types type)
7395+
{
7396+
assert(GenTree::OperIsConditional(oper));
7397+
GenTreeConditional* node = new (this, oper) GenTreeConditional(oper, type, cond, op1, op2);
7398+
node->gtFlags |= (cond->gtFlags & GTF_ALL_EFFECT);
7399+
node->gtFlags |= (op1->gtFlags & GTF_ALL_EFFECT);
7400+
node->gtFlags |= (op2->gtFlags & GTF_ALL_EFFECT);
7401+
return node;
7402+
}
7403+
73907404
GenTreeLclFld* Compiler::gtNewLclFldNode(unsigned lnum, var_types type, unsigned offset)
73917405
{
73927406
GenTreeLclFld* node = new (this, GT_LCL_FLD) GenTreeLclFld(GT_LCL_FLD, type, lnum, offset);
@@ -12643,6 +12657,10 @@ GenTree* Compiler::gtFoldExpr(GenTree* tree)
1264312657

1264412658
if (!(kind & GTK_SMPOP))
1264512659
{
12660+
if (tree->OperIsConditional())
12661+
{
12662+
return gtFoldExprConditional(tree);
12663+
}
1264612664
return tree;
1264712665
}
1264812666

@@ -12911,6 +12929,118 @@ GenTree* Compiler::gtFoldExprCompare(GenTree* tree)
1291112929
return cons;
1291212930
}
1291312931

12932+
//------------------------------------------------------------------------
12933+
// gtFoldExprConditional: see if a conditional is foldable
12934+
//
12935+
// Arguments:
12936+
// tree - condition to examine
12937+
//
12938+
// Returns:
12939+
// The original call if no folding happened.
12940+
// An alternative tree if folding happens.
12941+
//
12942+
// Notes:
12943+
// Supporting foldings are:
12944+
// SELECT TRUE X Y -> X
12945+
// SELECT FALSE X Y -> Y
12946+
// SELECT COND X X -> X
12947+
//
12948+
GenTree* Compiler::gtFoldExprConditional(GenTree* tree)
12949+
{
12950+
GenTree* cond = tree->AsConditional()->gtCond;
12951+
GenTree* op1 = tree->AsConditional()->gtOp1;
12952+
GenTree* op2 = tree->AsConditional()->gtOp2;
12953+
12954+
assert(tree->OperIsConditional());
12955+
12956+
// Check for a constant conditional
12957+
if (cond->OperIsConst())
12958+
{
12959+
// Constant conditions must be folded away.
12960+
12961+
JITDUMP("\nFolding conditional op with constant condition:\n");
12962+
DISPTREE(tree);
12963+
12964+
assert(cond->TypeIs(TYP_INT));
12965+
assert((tree->gtFlags & GTF_SIDE_EFFECT & ~GTF_ASG) == 0);
12966+
assert((tree->gtFlags & GTF_ORDER_SIDEEFF) == 0);
12967+
12968+
GenTree* replacement = nullptr;
12969+
if (cond->IsIntegralConst(0))
12970+
{
12971+
JITDUMP("Bashed to false path:\n");
12972+
replacement = op2;
12973+
}
12974+
else
12975+
{
12976+
// Condition should never be a constant other than 0 or 1
12977+
assert(cond->IsIntegralConst(1));
12978+
JITDUMP("Bashed to true path:\n");
12979+
replacement = op1;
12980+
}
12981+
12982+
if (fgGlobalMorph)
12983+
{
12984+
fgMorphTreeDone(replacement);
12985+
}
12986+
else
12987+
{
12988+
replacement->gtNext = tree->gtNext;
12989+
replacement->gtPrev = tree->gtPrev;
12990+
}
12991+
DISPTREE(replacement);
12992+
JITDUMP("\n");
12993+
12994+
// If we bashed to a compare, try to fold that.
12995+
if (replacement->OperIsCompare())
12996+
{
12997+
return gtFoldExprCompare(replacement);
12998+
}
12999+
13000+
return replacement;
13001+
}
13002+
13003+
assert(cond->OperIsCompare());
13004+
13005+
if (((tree->gtFlags & GTF_SIDE_EFFECT) != 0) || !GenTree::Compare(op1, op2, true))
13006+
{
13007+
// No folding.
13008+
return tree;
13009+
}
13010+
13011+
// GTF_ORDER_SIDEEFF here may indicate volatile subtrees.
13012+
// Or it may indicate a non-null assertion prop into an indir subtree.
13013+
if ((tree->gtFlags & GTF_ORDER_SIDEEFF) != 0)
13014+
{
13015+
// If op1 is "volatile" and op2 is not, we can still fold.
13016+
const bool op1MayBeVolatile = (op1->gtFlags & GTF_ORDER_SIDEEFF) != 0;
13017+
const bool op2MayBeVolatile = (op2->gtFlags & GTF_ORDER_SIDEEFF) != 0;
13018+
13019+
if (!op1MayBeVolatile || op2MayBeVolatile)
13020+
{
13021+
// No folding.
13022+
return tree;
13023+
}
13024+
}
13025+
13026+
JITDUMP("Bashed to first of two identical paths:\n");
13027+
GenTree* replacement = op1;
13028+
13029+
if (fgGlobalMorph)
13030+
{
13031+
fgMorphTreeDone(replacement);
13032+
}
13033+
else
13034+
{
13035+
replacement->gtNext = tree->gtNext;
13036+
replacement->gtPrev = tree->gtPrev;
13037+
}
13038+
DISPTREE(replacement);
13039+
JITDUMP("\n");
13040+
13041+
return replacement;
13042+
}
13043+
1291413044
//------------------------------------------------------------------------
1291513045
// gtCreateHandleCompare: generate a type handle comparison
1291613046
//

src/coreclr/jit/jitconfigvalues.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,7 @@ CONFIG_INTEGER(JitDoValueNumber, W("JitDoValueNumber"), 1) // Perform value numb
426426

427427
CONFIG_METHODSET(JitOptRepeat, W("JitOptRepeat")) // Runs optimizer multiple times on the method
428428
CONFIG_INTEGER(JitOptRepeatCount, W("JitOptRepeatCount"), 2) // Number of times to repeat opts when repeating
429+
CONFIG_INTEGER(JitDoIfConversion, W("JitDoIfConversion"), 1) // Perform If conversion
429430
#endif // defined(OPT_CONFIG)
430431

431432
CONFIG_INTEGER(JitTelemetry, W("JitTelemetry"), 1) // If non-zero, gather JIT telemetry data

src/coreclr/jit/lowerarmarch.cpp

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2327,8 +2327,7 @@ bool Lowering::ContainCheckCompareChain(GenTree* child, GenTree* parent, GenTree
23272327
child->SetContained();
23282328
return true;
23292329
}
2330-
else if (child->OperIsCmpCompare() && varTypeIsIntegral(child->gtGetOp1()) &&
2331-
varTypeIsIntegral(child->gtGetOp2()))
2330+
else if (child->OperIsCmpCompare())
23322331
{
23332332
child->AsOp()->SetContained();
23342333

@@ -2422,17 +2421,28 @@ void Lowering::ContainCheckSelect(GenTreeConditional* node)
24222421
return;
24232422
}
24242423

2425-
// Check if the compare does not need to be generated into a register.
2426-
GenTree* startOfChain = nullptr;
2427-
ContainCheckCompareChain(node->gtCond, node, &startOfChain);
2428-
2429-
if (startOfChain != nullptr)
2424+
if (node->gtCond->OperIsCompare())
2425+
{
2426+
// All compare node types (including TEST_) are containable.
2427+
if (IsSafeToContainMem(node, node->gtCond))
2428+
{
2429+
node->gtCond->AsOp()->SetContained();
2430+
}
2431+
}
2432+
else
24302433
{
2431-
// The earliest node in the chain will be generated as a standard compare.
2432-
assert(startOfChain->OperIsCmpCompare());
2433-
startOfChain->AsOp()->gtGetOp1()->ClearContained();
2434-
startOfChain->AsOp()->gtGetOp2()->ClearContained();
2435-
ContainCheckCompare(startOfChain->AsOp());
2434+
// Check for a compare chain and try to contain it.
2435+
GenTree* startOfChain = nullptr;
2436+
ContainCheckCompareChain(node->gtCond, node, &startOfChain);
2437+
2438+
if (startOfChain != nullptr)
2439+
{
2440+
// The earliest node in the chain will be generated as a standard compare.
2441+
assert(startOfChain->OperIsCmpCompare());
2442+
startOfChain->AsOp()->gtGetOp1()->ClearContained();
2443+
startOfChain->AsOp()->gtGetOp2()->ClearContained();
2444+
ContainCheckCompare(startOfChain->AsOp());
2445+
}
24362446
}
24372447
}
24382448

src/coreclr/jit/lsrabuild.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3226,10 +3226,11 @@ int LinearScan::BuildOperandUses(GenTree* node, regMaskTP candidates)
32263226
}
32273227
#endif // FEATURE_HW_INTRINSICS
32283228
#ifdef TARGET_ARM64
3229-
if (node->OperIs(GT_MUL) || node->OperIsCmpCompare() || node->OperIs(GT_AND))
3229+
if (node->OperIs(GT_MUL) || node->OperIsCompare() || node->OperIs(GT_AND))
32303230
{
32313231
// MUL can be contained for madd or msub on arm64.
3232-
// Compare and AND may be contained due to If Conversion.
3232+
// Compares can be contained by a SELECT.
3233+
// ANDs and Cmp Compares may be contained in a chain.
32333234
return BuildBinaryUses(node->AsOp(), candidates);
32343235
}
32353236
if (node->OperIs(GT_NEG, GT_CAST, GT_LSH, GT_RSH, GT_RSZ))

0 commit comments

Comments
 (0)