@@ -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//
1427814278GenTree* 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