@@ -13786,7 +13786,7 @@ GenTree* Compiler::gtFoldExprCompare(GenTree* tree)
13786
13786
13787
13787
/* Filter out cases that cannot be folded here */
13788
13788
13789
- /* Do not fold floats or doubles (e.g. NaN != Nan ) */
13789
+ /* Do not fold floats or doubles (e.g. NaN != NaN ) */
13790
13790
13791
13791
if (varTypeIsFloating(op1->TypeGet()))
13792
13792
{
@@ -14277,6 +14277,7 @@ CORINFO_CLASS_HANDLE Compiler::gtGetHelperArgClassHandle(GenTree* tree)
14277
14277
//
14278
14278
GenTree* Compiler::gtFoldExprSpecial(GenTree* tree)
14279
14279
{
14280
+ var_types type = tree->TypeGet();
14280
14281
GenTree* op1 = tree->AsOp()->gtOp1;
14281
14282
GenTree* op2 = tree->AsOp()->gtOp2;
14282
14283
genTreeOps oper = tree->OperGet();
@@ -14296,8 +14297,12 @@ GenTree* Compiler::gtFoldExprSpecial(GenTree* tree)
14296
14297
/* We only consider TYP_INT for folding
14297
14298
* Do not fold pointer arithmetic (e.g. addressing modes!) */
14298
14299
14299
- if (oper != GT_QMARK && !varTypeIsIntOrI(tree->gtType ))
14300
+ if (oper != GT_QMARK && !varTypeIsIntOrI(type ))
14300
14301
{
14302
+ if (varTypeIsFloating(type))
14303
+ {
14304
+ return gtFoldExprSpecialFloating(tree);
14305
+ }
14301
14306
return tree;
14302
14307
}
14303
14308
@@ -14630,6 +14635,233 @@ GenTree* Compiler::gtFoldExprSpecial(GenTree* tree)
14630
14635
return op;
14631
14636
}
14632
14637
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
+
14633
14865
//------------------------------------------------------------------------
14634
14866
// gtFoldBoxNullable -- optimize a boxed nullable feeding a compare to zero
14635
14867
//
0 commit comments