@@ -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,256 @@ 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
+ // 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
+
14633
14888
//------------------------------------------------------------------------
14634
14889
// gtFoldBoxNullable -- optimize a boxed nullable feeding a compare to zero
14635
14890
//
0 commit comments