Skip to content

Commit 2c540e5

Browse files
Adding some constant folding support for basic floating-point operations (#103206)
* Adding some constant folding support for basic floating-point operations * Use gtWrapWithSideEffects and respond to PR feedback * Make sure we set DEBUG_NODE_MORPHED on the comma
1 parent f453865 commit 2c540e5

File tree

3 files changed

+669
-215
lines changed

3 files changed

+669
-215
lines changed

src/coreclr/jit/compiler.h

+1
Original file line numberDiff line numberDiff line change
@@ -3641,6 +3641,7 @@ class Compiler
36413641
GenTree* gtFoldExprConst(GenTree* tree);
36423642
GenTree* gtFoldIndirConst(GenTreeIndir* indir);
36433643
GenTree* gtFoldExprSpecial(GenTree* tree);
3644+
GenTree* gtFoldExprSpecialFloating(GenTree* tree);
36443645
GenTree* gtFoldBoxNullable(GenTree* tree);
36453646
GenTree* gtFoldExprCompare(GenTree* tree);
36463647
GenTree* gtFoldExprConditional(GenTree* tree);

src/coreclr/jit/gentree.cpp

+234-2
Original file line numberDiff line numberDiff line change
@@ -13786,7 +13786,7 @@ GenTree* Compiler::gtFoldExprCompare(GenTree* tree)
1378613786

1378713787
/* Filter out cases that cannot be folded here */
1378813788

13789-
/* Do not fold floats or doubles (e.g. NaN != Nan) */
13789+
/* Do not fold floats or doubles (e.g. NaN != NaN) */
1379013790

1379113791
if (varTypeIsFloating(op1->TypeGet()))
1379213792
{
@@ -14277,6 +14277,7 @@ CORINFO_CLASS_HANDLE Compiler::gtGetHelperArgClassHandle(GenTree* tree)
1427714277
//
1427814278
GenTree* Compiler::gtFoldExprSpecial(GenTree* tree)
1427914279
{
14280+
var_types type = tree->TypeGet();
1428014281
GenTree* op1 = tree->AsOp()->gtOp1;
1428114282
GenTree* op2 = tree->AsOp()->gtOp2;
1428214283
genTreeOps oper = tree->OperGet();
@@ -14296,8 +14297,12 @@ GenTree* Compiler::gtFoldExprSpecial(GenTree* tree)
1429614297
/* We only consider TYP_INT for folding
1429714298
* Do not fold pointer arithmetic (e.g. addressing modes!) */
1429814299

14299-
if (oper != GT_QMARK && !varTypeIsIntOrI(tree->gtType))
14300+
if (oper != GT_QMARK && !varTypeIsIntOrI(type))
1430014301
{
14302+
if (varTypeIsFloating(type))
14303+
{
14304+
return gtFoldExprSpecialFloating(tree);
14305+
}
1430114306
return tree;
1430214307
}
1430314308

@@ -14630,6 +14635,233 @@ GenTree* Compiler::gtFoldExprSpecial(GenTree* tree)
1463014635
return op;
1463114636
}
1463214637

14638+
//------------------------------------------------------------------------
14639+
// gtFoldExprSpecialFloating -- optimize floating-point binary ops with one constant operand
14640+
//
14641+
// Arguments:
14642+
// tree - tree to optimize
14643+
//
14644+
// Return value:
14645+
// Tree (possibly modified at root or below), or a new tree
14646+
// Any new tree is fully morphed, if necessary.
14647+
//
14648+
GenTree* Compiler::gtFoldExprSpecialFloating(GenTree* tree)
14649+
{
14650+
assert(varTypeIsFloating(tree->TypeGet()));
14651+
assert(tree->OperKind() & GTK_BINOP);
14652+
14653+
GenTree* op1 = tree->AsOp()->gtOp1;
14654+
GenTree* op2 = tree->AsOp()->gtOp2;
14655+
genTreeOps oper = tree->OperGet();
14656+
14657+
GenTree* op;
14658+
GenTree* cons;
14659+
double val;
14660+
14661+
/* Filter out operators that cannot be folded here */
14662+
if (oper == GT_CAST)
14663+
{
14664+
return tree;
14665+
}
14666+
14667+
/* Find out which is the constant node */
14668+
if (op1->IsCnsFltOrDbl())
14669+
{
14670+
op = op2;
14671+
cons = op1;
14672+
}
14673+
else if (op2->IsCnsFltOrDbl())
14674+
{
14675+
op = op1;
14676+
cons = op2;
14677+
}
14678+
else
14679+
{
14680+
return tree;
14681+
}
14682+
14683+
/* Get the constant value */
14684+
val = cons->AsDblCon()->DconValue();
14685+
14686+
// Helper function that creates a new IntCon node and morphs it, if required
14687+
auto NewMorphedIntConNode = [&](int value) -> GenTreeIntCon* {
14688+
GenTreeIntCon* icon = gtNewIconNode(value);
14689+
if (fgGlobalMorph)
14690+
{
14691+
INDEBUG(icon->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED);
14692+
}
14693+
return icon;
14694+
};
14695+
14696+
// Here `op` is the non-constant operand, `cons` is the constant operand
14697+
// and `val` is the constant value.
14698+
14699+
switch (oper)
14700+
{
14701+
case GT_ADD:
14702+
{
14703+
// Handle `x + NaN == NaN` and `NaN + x == NaN`
14704+
// This is safe for all floats since we do not fault for sNaN
14705+
14706+
if (FloatingPointUtils::isNaN(val))
14707+
{
14708+
op = gtWrapWithSideEffects(cons, op, GTF_ALL_EFFECT);
14709+
goto DONE_FOLD;
14710+
}
14711+
14712+
// Handle `x + -0 == x` and `-0 + x == x`
14713+
14714+
if (FloatingPointUtils::isNegativeZero(val))
14715+
{
14716+
goto DONE_FOLD;
14717+
}
14718+
14719+
// We cannot handle `x + 0 == x` or `0 + x == x` since `-0 + 0 == 0`
14720+
break;
14721+
}
14722+
14723+
case GT_DIV:
14724+
{
14725+
// Handle `x / NaN == NaN` and `NaN / x == NaN`
14726+
// This is safe for all floats since we do not fault for sNaN
14727+
14728+
if (FloatingPointUtils::isNaN(val))
14729+
{
14730+
op = gtWrapWithSideEffects(cons, op, GTF_ALL_EFFECT);
14731+
goto DONE_FOLD;
14732+
}
14733+
14734+
// Handle `x / 1 == x`.
14735+
// This is safe for all floats since we do not fault for sNaN
14736+
14737+
if ((op2 == cons) && (val == 1.0))
14738+
{
14739+
goto DONE_FOLD;
14740+
}
14741+
break;
14742+
}
14743+
14744+
case GT_EQ:
14745+
{
14746+
assert((tree->gtFlags & GTF_RELOP_NAN_UN) == 0);
14747+
14748+
if (FloatingPointUtils::isNaN(val))
14749+
{
14750+
// Comparison with NaN is always false
14751+
op = gtWrapWithSideEffects(NewMorphedIntConNode(0), op, GTF_ALL_EFFECT);
14752+
goto DONE_FOLD;
14753+
}
14754+
break;
14755+
}
14756+
14757+
case GT_GE:
14758+
case GT_GT:
14759+
case GT_LE:
14760+
case GT_LT:
14761+
{
14762+
if (FloatingPointUtils::isNaN(val))
14763+
{
14764+
if ((tree->gtFlags & GTF_RELOP_NAN_UN) != 0)
14765+
{
14766+
// Unordered comparison with NaN is always true
14767+
op = gtWrapWithSideEffects(NewMorphedIntConNode(1), op, GTF_ALL_EFFECT);
14768+
}
14769+
else
14770+
{
14771+
// Comparison with NaN is always false
14772+
op = gtWrapWithSideEffects(NewMorphedIntConNode(0), op, GTF_ALL_EFFECT);
14773+
}
14774+
goto DONE_FOLD;
14775+
}
14776+
break;
14777+
}
14778+
14779+
case GT_MUL:
14780+
{
14781+
// Handle `x * NaN == NaN` and `NaN * x == NaN`
14782+
// This is safe for all floats since we do not fault for sNaN
14783+
14784+
if (FloatingPointUtils::isNaN(val))
14785+
{
14786+
op = gtWrapWithSideEffects(cons, op, GTF_ALL_EFFECT);
14787+
goto DONE_FOLD;
14788+
}
14789+
14790+
// Handle `x * 1 == x` and `1 * x == x`
14791+
// This is safe for all floats since we do not fault for sNaN
14792+
14793+
if (val == 1.0)
14794+
{
14795+
goto DONE_FOLD;
14796+
}
14797+
14798+
// We cannot handle `x * 0 == 0` or ` 0 * x == 0` since `-0 * 0 == -0`
14799+
// We cannot handle `x * -0 == -0` or `-0 * x == -0` since `-0 * -0 == 0`
14800+
break;
14801+
}
14802+
14803+
case GT_NE:
14804+
{
14805+
assert((tree->gtFlags & GTF_RELOP_NAN_UN) == 0);
14806+
14807+
if (FloatingPointUtils::isNaN(val))
14808+
{
14809+
// Comparison with NaN is always true
14810+
op = gtWrapWithSideEffects(NewMorphedIntConNode(1), op, GTF_ALL_EFFECT);
14811+
goto DONE_FOLD;
14812+
}
14813+
break;
14814+
}
14815+
14816+
case GT_SUB:
14817+
{
14818+
// Handle `x - NaN == NaN` and `NaN - x == NaN`
14819+
// This is safe for all floats since we do not fault for sNaN
14820+
14821+
if (FloatingPointUtils::isNaN(val))
14822+
{
14823+
op = gtWrapWithSideEffects(cons, op, GTF_ALL_EFFECT);
14824+
goto DONE_FOLD;
14825+
}
14826+
14827+
// Handle `x - 0 == x`
14828+
14829+
if ((cons == op2) && FloatingPointUtils::isPositiveZero(val))
14830+
{
14831+
goto DONE_FOLD;
14832+
}
14833+
14834+
// We cannot handle `x - -0 == x` since `-0 - -0 == 0`
14835+
break;
14836+
}
14837+
14838+
default:
14839+
{
14840+
break;
14841+
}
14842+
}
14843+
14844+
/* The node is not foldable */
14845+
14846+
return tree;
14847+
14848+
DONE_FOLD:
14849+
14850+
JITDUMP("\nFolding binary operator with a constant operand:\n");
14851+
DISPTREE(tree);
14852+
JITDUMP("Transformed into:\n");
14853+
DISPTREE(op);
14854+
14855+
if (fgGlobalMorph)
14856+
{
14857+
// We can sometimes produce a comma over the constant if the original op
14858+
// had a side effect, so just ensure we set the flag (which will be already
14859+
// set for the operands otherwise).
14860+
INDEBUG(op->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED);
14861+
}
14862+
return op;
14863+
}
14864+
1463314865
//------------------------------------------------------------------------
1463414866
// gtFoldBoxNullable -- optimize a boxed nullable feeding a compare to zero
1463514867
//

0 commit comments

Comments
 (0)