]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/match.pd
* doc/extend.texi (Common Function Attributes): Clarify
[thirdparty/gcc.git] / gcc / match.pd
index 5e4a4dc51a2b7e3eda0d31bf692d1e6358507166..f8e35e96d22036bb0b96fbdbe2c7a346f4695067 100644 (file)
@@ -152,6 +152,28 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
  (if (fold_real_zero_addition_p (type, @1, 1))
   (non_lvalue @0)))
 
+/* Even if the fold_real_zero_addition_p can't simplify X + 0.0
+   into X, we can optimize (X + 0.0) + 0.0 or (X + 0.0) - 0.0
+   or (X - 0.0) + 0.0 into X + 0.0 and (X - 0.0) - 0.0 into X - 0.0
+   if not -frounding-math.  For sNaNs the first operation would raise
+   exceptions but turn the result into qNan, so the second operation
+   would not raise it.   */
+(for inner_op (plus minus)
+ (for outer_op (plus minus)
+  (simplify
+   (outer_op (inner_op@3 @0 REAL_CST@1) REAL_CST@2)
+    (if (real_zerop (@1)
+        && real_zerop (@2)
+        && !HONOR_SIGN_DEPENDENT_ROUNDING (type))
+     (with { bool inner_plus = ((inner_op == PLUS_EXPR)
+                               ^ REAL_VALUE_MINUS_ZERO (TREE_REAL_CST (@1)));
+            bool outer_plus
+              = ((outer_op == PLUS_EXPR)
+                 ^ REAL_VALUE_MINUS_ZERO (TREE_REAL_CST (@2))); }
+      (if (outer_plus && !inner_plus)
+       (outer_op @0 @2)
+       @3))))))
+
 /* Simplify x - x.
    This is unsafe for certain floats even in non-IEEE formats.
    In IEEE, it is unsafe because it does wrong for NaNs.
@@ -195,6 +217,14 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
            || !COMPLEX_FLOAT_TYPE_P (type)))
    (negate @0)))
 
+/* Transform { 0 or 1 } * { 0 or 1 } into { 0 or 1 } & { 0 or 1 } */
+(simplify
+ (mult SSA_NAME@1 SSA_NAME@2)
+  (if (INTEGRAL_TYPE_P (type)
+       && get_nonzero_bits (@1) == 1
+       && get_nonzero_bits (@2) == 1)
+   (bit_and @1 @2)))
+
 /* Transform x * { 0 or 1, 0 or 1, ... } into x & { 0 or -1, 0 or -1, ...},
    unless the target has native support for the former but not the latter.  */
 (simplify
@@ -1473,9 +1503,26 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
 /* X / 4 < Y / 4 iff X < Y when the division is known to be exact.  */
 (for cmp (simple_comparison)
  (simplify
-  (cmp (exact_div @0 INTEGER_CST@2) (exact_div @1 @2))
-  (if (wi::gt_p (wi::to_wide (@2), 0, TYPE_SIGN (TREE_TYPE (@2))))
-   (cmp @0 @1))))
+  (cmp (convert?@3 (exact_div @0 INTEGER_CST@2)) (convert? (exact_div @1 @2)))
+  (if (element_precision (@3) >= element_precision (@0)
+       && types_match (@0, @1))
+   (if (wi::lt_p (wi::to_wide (@2), 0, TYPE_SIGN (TREE_TYPE (@2))))
+    (if (!TYPE_UNSIGNED (TREE_TYPE (@3)))
+     (cmp @1 @0)
+     (if (tree_expr_nonzero_p (@0) && tree_expr_nonzero_p (@1))
+      (with
+       {
+       tree utype = unsigned_type_for (TREE_TYPE (@0));
+       }
+       (cmp (convert:utype @1) (convert:utype @0)))))
+    (if (wi::gt_p (wi::to_wide (@2), 1, TYPE_SIGN (TREE_TYPE (@2))))
+     (if (TYPE_UNSIGNED (TREE_TYPE (@0)) || !TYPE_UNSIGNED (TREE_TYPE (@3)))
+      (cmp @0 @1)
+      (with
+       {
+       tree utype = unsigned_type_for (TREE_TYPE (@0));
+       }
+       (cmp (convert:utype @0) (convert:utype @1)))))))))
 
 /* X / C1 op C2 into a simple range test.  */
 (for cmp (simple_comparison)
@@ -2896,6 +2943,21 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
   (if (integer_zerop (@0))
    @2)))
 
+/* Sink unary operations to constant branches, but only if we do fold it to
+   constants.  */
+(for op (negate bit_not abs absu)
+ (simplify
+  (op (vec_cond @0 VECTOR_CST@1 VECTOR_CST@2))
+  (with
+   {
+     tree cst1, cst2;
+     cst1 = const_unop (op, type, @1);
+     if (cst1)
+       cst2 = const_unop (op, type, @2);
+   }
+   (if (cst1 && cst2)
+    (vec_cond @0 { cst1; } { cst2; })))))
+
 /* Simplification moved from fold_cond_expr_with_comparison.  It may also
    be extended.  */
 /* This pattern implements two kinds simplification:
@@ -3611,6 +3673,33 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
                              != (cmp == LT_EXPR || cmp == LE_EXPR), type); }
      (cmp @0 { wide_int_to_tree (TREE_TYPE (@0), prod); }))))))
 
+/* Fold (size_t)(A /[ex] B) CMP C to (size_t)A CMP (size_t)B * C or A CMP' 0.
+
+   For small C (less than max/B), this is (size_t)A CMP (size_t)B * C.
+   For large C (more than min/B+2^size), this is also true, with the
+   multiplication computed modulo 2^size.
+   For intermediate C, this just tests the sign of A.  */
+(for cmp  (lt le gt ge)
+     cmp2 (ge ge lt lt)
+ (simplify
+  (cmp (convert (exact_div @0 INTEGER_CST@1)) INTEGER_CST@2)
+  (if (tree_nop_conversion_p (TREE_TYPE (@0), TREE_TYPE (@2))
+       && TYPE_UNSIGNED (TREE_TYPE (@2)) && !TYPE_UNSIGNED (TREE_TYPE (@0))
+       && wi::gt_p (wi::to_wide (@1), 0, TYPE_SIGN (TREE_TYPE (@1))))
+   (with
+    {
+      tree utype = TREE_TYPE (@2);
+      wide_int denom = wi::to_wide (@1);
+      wide_int right = wi::to_wide (@2);
+      wide_int smax = wi::sdiv_trunc (wi::max_value (TREE_TYPE (@0)), denom);
+      wide_int smin = wi::sdiv_trunc (wi::min_value (TREE_TYPE (@0)), denom);
+      bool small = wi::leu_p (right, smax);
+      bool large = wi::geu_p (right, smin);
+    }
+    (if (small || large)
+     (cmp (convert:utype @0) (mult @2 (convert @1)))
+     (cmp2 @0 { build_zero_cst (TREE_TYPE (@0)); }))))))
+
 /* Unordered tests if either argument is a NaN.  */
 (simplify
  (bit_ior (unordered @0 @0) (unordered @1 @1))
@@ -5352,3 +5441,130 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
       (bit_and:elt_type
        (BIT_FIELD_REF:elt_type @0 { size; } { pos; })
        { elt; })))))))
+
+(simplify
+ (vec_perm @0 @1 VECTOR_CST@2)
+ (with
+  {
+    tree op0 = @0, op1 = @1, op2 = @2;
+
+    /* Build a vector of integers from the tree mask.  */
+    vec_perm_builder builder;
+    if (!tree_to_vec_perm_builder (&builder, op2))
+      return NULL_TREE;
+
+    /* Create a vec_perm_indices for the integer vector.  */
+    poly_uint64 nelts = TYPE_VECTOR_SUBPARTS (type);
+    bool single_arg = (op0 == op1);
+    vec_perm_indices sel (builder, single_arg ? 1 : 2, nelts);
+  }
+  (if (sel.series_p (0, 1, 0, 1))
+   { op0; }
+   (if (sel.series_p (0, 1, nelts, 1))
+    { op1; }
+    (with
+     {
+       if (!single_arg)
+         {
+          if (sel.all_from_input_p (0))
+            op1 = op0;
+          else if (sel.all_from_input_p (1))
+            {
+              op0 = op1;
+              sel.rotate_inputs (1);
+            }
+          else if (known_ge (poly_uint64 (sel[0]), nelts))
+            {
+              std::swap (op0, op1);
+              sel.rotate_inputs (1);
+            }
+         }
+       gassign *def;
+       tree cop0 = op0, cop1 = op1;
+       if (TREE_CODE (op0) == SSA_NAME
+           && (def = dyn_cast <gassign *> (SSA_NAME_DEF_STMT (op0)))
+          && gimple_assign_rhs_code (def) == CONSTRUCTOR)
+        cop0 = gimple_assign_rhs1 (def);
+       if (TREE_CODE (op1) == SSA_NAME
+           && (def = dyn_cast <gassign *> (SSA_NAME_DEF_STMT (op1)))
+          && gimple_assign_rhs_code (def) == CONSTRUCTOR)
+        cop1 = gimple_assign_rhs1 (def);
+
+       tree t;
+    }
+    (if ((TREE_CODE (cop0) == VECTOR_CST
+         || TREE_CODE (cop0) == CONSTRUCTOR)
+        && (TREE_CODE (cop1) == VECTOR_CST
+            || TREE_CODE (cop1) == CONSTRUCTOR)
+        && (t = fold_vec_perm (type, cop0, cop1, sel)))
+     { t; }
+     (with
+      {
+       bool changed = (op0 == op1 && !single_arg);
+       tree ins = NULL_TREE;
+       unsigned at = 0;
+
+       /* See if the permutation is performing a single element
+          insert from a CONSTRUCTOR or constant and use a BIT_INSERT_EXPR
+          in that case.  But only if the vector mode is supported,
+          otherwise this is invalid GIMPLE.  */
+        if (TYPE_MODE (type) != BLKmode
+           && (TREE_CODE (cop0) == VECTOR_CST
+               || TREE_CODE (cop0) == CONSTRUCTOR
+               || TREE_CODE (cop1) == VECTOR_CST
+               || TREE_CODE (cop1) == CONSTRUCTOR))
+          {
+           if (sel.series_p (1, 1, nelts + 1, 1))
+             {
+               /* After canonicalizing the first elt to come from the
+                  first vector we only can insert the first elt from
+                  the first vector.  */
+               at = 0;
+               if ((ins = fold_read_from_vector (cop0, 0)))
+                 op0 = op1;
+             }
+           else
+             {
+               unsigned int encoded_nelts = sel.encoding ().encoded_nelts ();
+               for (at = 0; at < encoded_nelts; ++at)
+                 if (maybe_ne (sel[at], at))
+                   break;
+               if (at < encoded_nelts && sel.series_p (at + 1, 1, at + 1, 1))
+                 {
+                   if (known_lt (at, nelts))
+                     ins = fold_read_from_vector (cop0, sel[at]);
+                   else
+                     ins = fold_read_from_vector (cop1, sel[at] - nelts);
+                 }
+             }
+         }
+
+       /* Generate a canonical form of the selector.  */
+       if (!ins && sel.encoding () != builder)
+         {
+           /* Some targets are deficient and fail to expand a single
+              argument permutation while still allowing an equivalent
+              2-argument version.  */
+           tree oldop2 = op2;
+           if (sel.ninputs () == 2
+              || can_vec_perm_const_p (TYPE_MODE (type), sel, false))
+             op2 = vec_perm_indices_to_tree (TREE_TYPE (op2), sel);
+           else
+             {
+               vec_perm_indices sel2 (builder, 2, nelts);
+               if (can_vec_perm_const_p (TYPE_MODE (type), sel2, false))
+                 op2 = vec_perm_indices_to_tree (TREE_TYPE (op2), sel2);
+               else
+                 /* Not directly supported with either encoding,
+                    so use the preferred form.  */
+                 op2 = vec_perm_indices_to_tree (TREE_TYPE (op2), sel);
+             }
+           if (!operand_equal_p (op2, oldop2, 0))
+             changed = true;
+         }
+      }
+      (if (ins)
+       (bit_insert { op0; } { ins; }
+         { bitsize_int (at * tree_to_uhwi (TYPE_SIZE (TREE_TYPE (type)))); })
+       (if (changed)
+        (vec_perm { op0; } { op1; } { op2; }))))))))))