@@ -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,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