Skip to content

Commit ccd5bad

Browse files
committed
Adding some constant folding support for basic floating-point operations
1 parent b5948bf commit ccd5bad

File tree

3 files changed

+692
-215
lines changed

3 files changed

+692
-215
lines changed

src/coreclr/jit/compiler.h

+1
Original file line numberDiff line numberDiff line change
@@ -3639,6 +3639,7 @@ class Compiler
36393639
GenTree* gtFoldExprConst(GenTree* tree);
36403640
GenTree* gtFoldIndirConst(GenTreeIndir* indir);
36413641
GenTree* gtFoldExprSpecial(GenTree* tree);
3642+
GenTree* gtFoldExprSpecialFloating(GenTree* tree);
36423643
GenTree* gtFoldBoxNullable(GenTree* tree);
36433644
GenTree* gtFoldExprCompare(GenTree* tree);
36443645
GenTree* gtFoldExprConditional(GenTree* tree);

src/coreclr/jit/gentree.cpp

+257-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,256 @@ 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+
// Transforms that would drop op cannot be performed if op has side effects
14687+
bool opHasSideEffects = (op->gtFlags & GTF_SIDE_EFFECT) != 0;
14688+
14689+
// Helper function that creates a new IntCon node and morphs it, if required
14690+
auto NewMorphedIntConNode = [&](int value) -> GenTreeIntCon* {
14691+
GenTreeIntCon* icon = gtNewIconNode(value);
14692+
if (fgGlobalMorph)
14693+
{
14694+
fgMorphTreeDone(icon);
14695+
}
14696+
return icon;
14697+
};
14698+
14699+
// Here `op` is the non-constant operand, `cons` is the constant operand
14700+
// and `val` is the constant value.
14701+
14702+
switch (oper)
14703+
{
14704+
case GT_ADD:
14705+
{
14706+
// Handle `x + NaN == NaN` and `NaN + x == NaN`
14707+
// This is safe for all floats since we do not fault for sNaN
14708+
14709+
if (FloatingPointUtils::isNaN(val))
14710+
{
14711+
if (opHasSideEffects)
14712+
{
14713+
break;
14714+
}
14715+
goto DONE_FOLD;
14716+
}
14717+
14718+
// Handle `x + -0 == x` and `-0 + x == x`
14719+
14720+
if (FloatingPointUtils::isNegativeZero(val))
14721+
{
14722+
goto DONE_FOLD;
14723+
}
14724+
14725+
// We cannot handle `x + 0 == x` or `0 + x == x` since `-0 + 0 == 0`
14726+
break;
14727+
}
14728+
14729+
case GT_DIV:
14730+
{
14731+
// Handle `x / NaN == NaN` and `NaN / x == NaN`
14732+
// This is safe for all floats since we do not fault for sNaN
14733+
14734+
if (FloatingPointUtils::isNaN(val))
14735+
{
14736+
if (opHasSideEffects)
14737+
{
14738+
break;
14739+
}
14740+
goto DONE_FOLD;
14741+
}
14742+
14743+
// Handle `x / 1 == x`.
14744+
// This is safe for all floats since we do not fault for sNaN
14745+
14746+
if ((op2 == cons) && (val == 1.0))
14747+
{
14748+
goto DONE_FOLD;
14749+
}
14750+
break;
14751+
}
14752+
14753+
case GT_EQ:
14754+
{
14755+
assert((tree->gtFlags & GTF_RELOP_NAN_UN) == 0);
14756+
14757+
if (FloatingPointUtils::isNaN(val))
14758+
{
14759+
if (opHasSideEffects)
14760+
{
14761+
break;
14762+
}
14763+
14764+
// Comparison with NaN is always false
14765+
op = NewMorphedIntConNode(0);
14766+
goto DONE_FOLD;
14767+
}
14768+
break;
14769+
}
14770+
14771+
case GT_GE:
14772+
case GT_GT:
14773+
case GT_LE:
14774+
case GT_LT:
14775+
{
14776+
if (FloatingPointUtils::isNaN(val))
14777+
{
14778+
if (opHasSideEffects)
14779+
{
14780+
break;
14781+
}
14782+
14783+
if ((tree->gtFlags & GTF_RELOP_NAN_UN) != 0)
14784+
{
14785+
// Unordered comparison with NaN is always true
14786+
op = NewMorphedIntConNode(1);
14787+
}
14788+
else
14789+
{
14790+
// Comparison with NaN is always false
14791+
op = NewMorphedIntConNode(0);
14792+
}
14793+
goto DONE_FOLD;
14794+
}
14795+
break;
14796+
}
14797+
14798+
case GT_MUL:
14799+
{
14800+
// Handle `x * NaN == NaN` and `NaN * x == NaN`
14801+
// This is safe for all floats since we do not fault for sNaN
14802+
14803+
if (FloatingPointUtils::isNaN(val))
14804+
{
14805+
if (opHasSideEffects)
14806+
{
14807+
break;
14808+
}
14809+
goto DONE_FOLD;
14810+
}
14811+
14812+
// Handle `x * 1 == x` and `1 * x == x`
14813+
// This is safe for all floats since we do not fault for sNaN
14814+
14815+
if (val == 1.0)
14816+
{
14817+
goto DONE_FOLD;
14818+
}
14819+
14820+
// We cannot handle `x * 0 == 0` or ` 0 * x == 0` since `-0 * 0 == -0`
14821+
// We cannot handle `x * -0 == -0` or `-0 * x == -0` since `-0 * -0 == 0`
14822+
break;
14823+
}
14824+
14825+
case GT_NE:
14826+
{
14827+
assert((tree->gtFlags & GTF_RELOP_NAN_UN) == 0);
14828+
14829+
if (FloatingPointUtils::isNaN(val))
14830+
{
14831+
if (opHasSideEffects)
14832+
{
14833+
break;
14834+
}
14835+
14836+
// Comparison with NaN is always true
14837+
op = NewMorphedIntConNode(1);
14838+
goto DONE_FOLD;
14839+
}
14840+
break;
14841+
}
14842+
14843+
case GT_SUB:
14844+
{
14845+
// Handle `x - NaN == NaN` and `NaN - x == NaN`
14846+
// This is safe for all floats since we do not fault for sNaN
14847+
14848+
if (FloatingPointUtils::isNaN(val))
14849+
{
14850+
if (opHasSideEffects)
14851+
{
14852+
break;
14853+
}
14854+
goto DONE_FOLD;
14855+
}
14856+
14857+
// Handle `x - 0 == x`
14858+
14859+
if ((cons == op2) && FloatingPointUtils::isPositiveZero(val))
14860+
{
14861+
goto DONE_FOLD;
14862+
}
14863+
14864+
// We cannot handle `x - -0 == x` since `-0 - -0 == 0`
14865+
break;
14866+
}
14867+
14868+
default:
14869+
{
14870+
break;
14871+
}
14872+
}
14873+
14874+
/* The node is not foldable */
14875+
14876+
return tree;
14877+
14878+
DONE_FOLD:
14879+
14880+
JITDUMP("\nFolding binary operator with a constant operand:\n");
14881+
DISPTREE(tree);
14882+
JITDUMP("Transformed into:\n");
14883+
DISPTREE(op);
14884+
14885+
return op;
14886+
}
14887+
1463314888
//------------------------------------------------------------------------
1463414889
// gtFoldBoxNullable -- optimize a boxed nullable feeding a compare to zero
1463514890
//

0 commit comments

Comments
 (0)