Skip to content

Expand "return condition" into "return condition ? true : false" #107499

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

Merged
merged 45 commits into from
Mar 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
04e531f
Expand "return condition" into "return condition ? true : false"
EgorBo Sep 7, 2024
5b17833
remove unnecessary limitation
EgorBo Sep 7, 2024
8ca7a22
clean up
EgorBo Sep 7, 2024
c1b907d
test
EgorBo Sep 7, 2024
15cb4c0
fix diffs
EgorBo Sep 7, 2024
09bb50d
run on isPhase
EgorBo Sep 7, 2024
9f71716
Fix some size regressions
EgorBo Sep 7, 2024
5d3dba9
fix bug
EgorBo Sep 7, 2024
679a68b
disable on jit32 gc encoder
EgorBo Sep 7, 2024
a7240d9
mitigate some regressions
EgorBo Sep 7, 2024
69005b7
clean up
EgorBo Sep 7, 2024
152ad31
fix size regressions
EgorBo Sep 8, 2024
4775b50
move to a separate method
EgorBo Sep 8, 2024
7334bd8
fix reverse
EgorBo Sep 8, 2024
1944f90
Merge branch 'main' into expand-return-cond
EgorBo Sep 10, 2024
34c687e
Update fgopt.cpp
EgorBo Sep 10, 2024
8a434f5
Update fgopt.cpp
EgorBo Sep 11, 2024
1a319a9
Update fgopt.cpp
EgorBo Sep 11, 2024
4a36ebd
Merge branch 'main' into expand-return-cond
EgorBo Oct 10, 2024
45b1457
Update fgopt.cpp
EgorBo Oct 10, 2024
60d4b7d
Merge branch 'main' into expand-return-cond
stephentoub Nov 9, 2024
a76902b
Merge branch 'main' into expand-return-cond
EgorBo Nov 21, 2024
47548f3
Merge branch 'main' of github.com:dotnet/runtime into expand-return-cond
EgorBo Jan 31, 2025
042e53c
clean up
EgorBo Jan 31, 2025
2977ea5
clean up
EgorBo Jan 31, 2025
4039806
merge returns back together
EgorBo Jan 31, 2025
eadf625
clean up
EgorBo Jan 31, 2025
9153940
Comment out tail duplication condition
EgorBo Jan 31, 2025
3490707
Merge branch 'main' into expand-return-cond
EgorBo Feb 9, 2025
29d8139
Merge branch 'main' into expand-return-cond
EgorBo Mar 20, 2025
3628d32
Enable tail duplication for return blocks
EgorBo Mar 20, 2025
d94011a
Merge branch 'main' of https://github.com/dotnet/runtime into expand-…
EgorBo Mar 23, 2025
f5535ac
Cleanup
EgorBo Mar 23, 2025
a85ee6d
remove comment
EgorBo Mar 23, 2025
e441ed7
Cleanup
EgorBo Mar 23, 2025
76cdec5
Clean up
EgorBo Mar 23, 2025
8d32cff
fix build
EgorBo Mar 23, 2025
a04750b
fix build
EgorBo Mar 23, 2025
8430840
Comment out conditional block in optimizebools.cpp
EgorBo Mar 23, 2025
4b9df07
fix issue with arm32
EgorBo Mar 23, 2025
aea34d2
Apply suggestions from code review
EgorBo Mar 24, 2025
8e5980d
Address feedback
EgorBo Mar 24, 2025
68a7aea
Address feedback
EgorBo Mar 24, 2025
aa74f08
Update src/coreclr/jit/optimizebools.cpp
EgorBo Mar 24, 2025
ce10e5c
Address feedback
EgorBo Mar 24, 2025
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
4 changes: 4 additions & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -5384,6 +5384,8 @@ class Compiler

FoldResult fgFoldConditional(BasicBlock* block);

bool fgFoldCondToReturnBlock(BasicBlock* block);

struct MorphUnreachableInfo
{
MorphUnreachableInfo(Compiler* comp);
Expand Down Expand Up @@ -6225,6 +6227,8 @@ class Compiler

PhaseStatus fgDetermineFirstColdBlock();

bool fgDedupReturnComparison(BasicBlock* block);

bool fgIsForwardBranch(BasicBlock* bJump, BasicBlock* bDest, BasicBlock* bSrc = nullptr);

bool fgUpdateFlowGraph(bool doTailDup = false, bool isPhase = false);
Expand Down
15 changes: 13 additions & 2 deletions src/coreclr/jit/fgdiagnostic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3278,9 +3278,20 @@ void Compiler::fgDebugCheckTypes(GenTree* tree)
assert(!"TYP_ULONG and TYP_UINT are not legal in IR");
}

if (node->OperIs(GT_NOP))
switch (node->OperGet())
{
assert(node->TypeIs(TYP_VOID) && "GT_NOP should be TYP_VOID.");
case GT_NOP:
case GT_JTRUE:
case GT_BOUNDS_CHECK:
if (!node->TypeIs(TYP_VOID))
{
m_compiler->gtDispTree(node);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be an assert here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, oops, indeed

assert("The tree is expected to be of TYP_VOID type");
}
break;

default:
break;
}

if (varTypeIsSmall(node))
Expand Down
67 changes: 67 additions & 0 deletions src/coreclr/jit/fgopt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4259,6 +4259,64 @@ PhaseStatus Compiler::fgUpdateFlowGraphPhase()
return madeChanges ? PhaseStatus::MODIFIED_EVERYTHING : PhaseStatus::MODIFIED_NOTHING;
}

//-------------------------------------------------------------
// fgDedupReturnComparison: Expands BBJ_RETURN <relop> into BBJ_COND <relop> with two
// BBJ_RETURN blocks ("return true" and "return false"). Such transformation
// helps other phases to focus only on BBJ_COND <relop> (normalization).
//
// Arguments:
// block - the BBJ_RETURN block to convert into BBJ_COND <relop>
//
// Returns:
// true if the block was converted into BBJ_COND <relop>
//
bool Compiler::fgDedupReturnComparison(BasicBlock* block)
{
#ifdef JIT32_GCENCODER
// JIT32_GCENCODER has a hard limit on the number of epilogues, let's not add more.
return false;
#endif

assert(block->KindIs(BBJ_RETURN));

// We're only interested in boolean returns
if ((info.compRetType != TYP_UBYTE) || (block == genReturnBB) || (block->lastStmt() == nullptr))
{
return false;
}

GenTree* rootNode = block->lastStmt()->GetRootNode();
if (!rootNode->OperIs(GT_RETURN) || !rootNode->gtGetOp1()->OperIsCmpCompare())
{
return false;
}

GenTree* cmp = rootNode->gtGetOp1();
cmp->gtFlags |= (GTF_RELOP_JMP_USED | GTF_DONT_CSE);
rootNode->ChangeOper(GT_JTRUE);
rootNode->ChangeType(TYP_VOID);

GenTree* retTrue = gtNewOperNode(GT_RETURN, TYP_INT, gtNewTrue());
GenTree* retFalse = gtNewOperNode(GT_RETURN, TYP_INT, gtNewFalse());

// Create RETURN 1/0 blocks. We expect fgHeadTailMerge to handle them if there are similar returns.
DebugInfo dbgInfo = block->lastStmt()->GetDebugInfo();
BasicBlock* retTrueBb = fgNewBBFromTreeAfter(BBJ_RETURN, block, retTrue, dbgInfo);
BasicBlock* retFalseBb = fgNewBBFromTreeAfter(BBJ_RETURN, block, retFalse, dbgInfo);

FlowEdge* trueEdge = fgAddRefPred(retTrueBb, block);
FlowEdge* falseEdge = fgAddRefPred(retFalseBb, block);
block->SetCond(trueEdge, falseEdge);

// We might want to instrument 'return <cond>' too in the future. For now apply 50%/50%.
trueEdge->setLikelihood(0.5);
falseEdge->setLikelihood(0.5);
retTrueBb->inheritWeightPercentage(block, 50);
retFalseBb->inheritWeightPercentage(block, 50);

return true;
}

//-------------------------------------------------------------
// fgUpdateFlowGraph: Removes any empty blocks, unreachable blocks, and redundant jumps.
// Most of those appear after dead store removal and folding of conditionals.
Expand Down Expand Up @@ -4354,6 +4412,15 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication /* = false */, bool isPh
bDest = nullptr;
bFalseDest = nullptr;

// Expand BBJ_RETURN <relop> into BBJ_COND <relop> when doTailDuplication is enabled
if (doTailDuplication && block->KindIs(BBJ_RETURN) && fgDedupReturnComparison(block))
{
assert(block->KindIs(BBJ_COND));
change = true;
modified = true;
bNext = block->Next();
}

if (block->KindIs(BBJ_ALWAYS))
{
bDest = block->GetTarget();
Expand Down
24 changes: 21 additions & 3 deletions src/coreclr/jit/ifconversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -704,9 +704,27 @@ bool OptIfConversionDsc::optIfConvert()
selectType = genActualType(m_thenOperation.node);
}

// Create a select node.
GenTreeConditional* select =
m_comp->gtNewConditionalNode(GT_SELECT, m_cond, selectTrueInput, selectFalseInput, selectType);
GenTree* select = nullptr;
if (selectTrueInput->TypeIs(TYP_INT) && selectFalseInput->TypeIs(TYP_INT))
{
if (selectTrueInput->IsIntegralConst(1) && selectFalseInput->IsIntegralConst(0))
{
// compare ? true : false --> compare
select = m_cond;
}
else if (selectTrueInput->IsIntegralConst(0) && selectFalseInput->IsIntegralConst(1))
{
// compare ? false : true --> reversed_compare
select = m_comp->gtReverseCond(m_cond);
}
}

if (select == nullptr)
{
// Create a select node
select = m_comp->gtNewConditionalNode(GT_SELECT, m_cond, selectTrueInput, selectFalseInput, selectType);
}

m_thenOperation.node->AddAllEffectsFlags(select);

// Use the select as the source of the Then operation.
Expand Down
Loading
Loading