Fold integer VEC_UNPACK_LO_EXPR and VEC_UNPACK_HI_EXPR for suitable
variable-length VECTOR_CSTs.

The patch adds support for other _LO, _HI expressions but it's expected
that those be almost never use but the code unifies the VLA and VLS
paths.

The fixed-length case already folds.  This extends the same idea to VLA
constants when the input has twice as many elements as the output and the
selected half can be represented as a valid VECTOR_CST.  This helps fold
known predicate constants before RTL expansion, avoiding unnecessary
predicate unpack instructions.

The idea here is that the VEC_CST should be a repeating pattern with a
fill above the minimum VL.

i.e. {a, b, a, b, ..., c, c, c} has the requirement that the minimum
vector length consume the entire repeating pattern such that the _hi
folds contain only {c,c,..}.

This show up often with known iteration loops and folding these in gimple
allows us to fold away unrolled masks which end up being empty.

The selftests cover the foldable low/high cases and cases that should not
fold.


Bootstrapped Regtested on aarch64-none-linux-gnu,
arm-none-linux-gnueabihf, x86_64-pc-linux-gnu
-m32, -m64 and no issues.

OK for master?

Thanks,
Tamar

gcc/ChangeLog:

        * fold-const.cc (const_unop): Fold VEC_UNPACK_LO_EXPR and
        VEC_UNPACK_HI_EXPR for suitable variable-length VECTOR_CSTs.
        (selftest::test_vec_unpack_folding): New function.
        (selftest::fold_const_cc_tests): Call it.

---
diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
index 
b9262b34f76683fa75e4c5f7ce938753e9c695a2..f919dbab4390127b604302fad06c7a672e7f9876
 100644
--- a/gcc/fold-const.cc
+++ b/gcc/fold-const.cc
@@ -1948,22 +1948,16 @@ const_unop (enum tree_code code, tree type, tree arg0)
     case VEC_UNPACK_FIX_TRUNC_LO_EXPR:
     case VEC_UNPACK_FIX_TRUNC_HI_EXPR:
       {
-       unsigned HOST_WIDE_INT out_nelts, in_nelts, i;
+       poly_uint64 out_nelts = TYPE_VECTOR_SUBPARTS (type);
+       unsigned HOST_WIDE_INT const_out_nelts;
+       unsigned int encoded_nelts, i, offset = 0;
        enum tree_code subcode;
 
        if (TREE_CODE (arg0) != VECTOR_CST)
          return NULL_TREE;
 
-       if (!VECTOR_CST_NELTS (arg0).is_constant (&in_nelts))
+       if (!known_eq (VECTOR_CST_NELTS (arg0), out_nelts * 2))
          return NULL_TREE;
-       out_nelts = in_nelts / 2;
-       gcc_assert (known_eq (out_nelts, TYPE_VECTOR_SUBPARTS (type)));
-
-       unsigned int offset = 0;
-       if ((!BYTES_BIG_ENDIAN) ^ (code == VEC_UNPACK_LO_EXPR
-                                  || code == VEC_UNPACK_FLOAT_LO_EXPR
-                                  || code == VEC_UNPACK_FIX_TRUNC_LO_EXPR))
-         offset = out_nelts;
 
        if (code == VEC_UNPACK_LO_EXPR || code == VEC_UNPACK_HI_EXPR)
          subcode = NOP_EXPR;
@@ -1973,8 +1967,57 @@ const_unop (enum tree_code code, tree type, tree arg0)
        else
          subcode = FIX_TRUNC_EXPR;
 
-       tree_vector_builder elts (type, out_nelts, 1);
-       for (i = 0; i < out_nelts; i++)
+       bool low_p = (code == VEC_UNPACK_LO_EXPR
+                     || code == VEC_UNPACK_FLOAT_LO_EXPR
+                     || code == VEC_UNPACK_FIX_TRUNC_LO_EXPR);
+       bool use_second_encoded_half_p = (!BYTES_BIG_ENDIAN) ^ low_p;
+
+       unsigned int nelts_per_pattern = 1;
+       tree fill = NULL_TREE;
+       if (out_nelts.is_constant (&const_out_nelts))
+         {
+           encoded_nelts = const_out_nelts;
+           if (use_second_encoded_half_p)
+             offset = encoded_nelts;
+         }
+       else
+         {
+           if (VECTOR_CST_STEPPED_P (arg0))
+             return NULL_TREE;
+
+           unsigned int min_out_nelts = constant_lower_bound (out_nelts);
+           encoded_nelts = min_out_nelts;
+           fill = fold_convert_const (subcode, TREE_TYPE (type),
+                                      vector_cst_elt (arg0, encoded_nelts));
+           if (fill == NULL_TREE || !CONSTANT_CLASS_P (fill))
+             return NULL_TREE;
+
+           /* Check that the selected high half is a single repeated
+              value.  Since the input is not stepped, checking one full
+              pattern is enough to prove the scalable tail.  */
+           for (i = 1; i < VECTOR_CST_NPATTERNS (arg0); ++i)
+             {
+               tree elt
+                 = fold_convert_const (subcode, TREE_TYPE (type),
+                                       vector_cst_elt (arg0,
+                                                       encoded_nelts + i));
+               if (elt == NULL_TREE
+                   || !CONSTANT_CLASS_P (elt)
+                   || !operand_equal_p (elt, fill))
+                 return NULL_TREE;
+             }
+
+           if (use_second_encoded_half_p)
+             {
+               encoded_nelts = 1;
+               offset = min_out_nelts;
+             }
+           else
+             nelts_per_pattern = 2;
+         }
+
+       tree_vector_builder elts (type, encoded_nelts, nelts_per_pattern);
+       for (i = 0; i < encoded_nelts; i++)
          {
            tree elt = fold_convert_const (subcode, TREE_TYPE (type),
                                           VECTOR_CST_ELT (arg0, i + offset));
@@ -1983,6 +2026,10 @@ const_unop (enum tree_code code, tree type, tree arg0)
            elts.quick_push (elt);
          }
 
+       if (nelts_per_pattern == 2)
+         for (i = 0; i < encoded_nelts; ++i)
+           elts.quick_push (fill);
+
        return elts.build ();
       }
 
@@ -17752,6 +17799,175 @@ test_vec_duplicate_folding ()
   ASSERT_TRUE (operand_equal_p (dup5_expr, dup5_cst, 0));
 }
 
+/* Verify folding of VEC_UNPACK_{LO,HI}_EXPRs.  */
+
+static void
+test_vec_unpack_folding ()
+{
+  machine_mode vmode;
+  FOR_EACH_MODE_IN_CLASS (vmode, MODE_VECTOR_INT)
+    {
+      poly_uint64 in_nelts = GET_MODE_NUNITS (vmode);
+      if (!test_fold_vec_perm_cst::is_simple_vla_size (in_nelts)
+         || in_nelts.coeffs[0] < 2
+         || !targetm.vector_mode_supported_p (vmode))
+       continue;
+
+      unsigned int in_bits = GET_MODE_UNIT_BITSIZE (vmode);
+      if (in_bits >= HOST_BITS_PER_WIDE_INT)
+       continue;
+
+      unsigned int min_out_nelts = in_nelts.coeffs[0] / 2;
+      tree in_inner_type = lang_hooks.types.type_for_mode
+       (GET_MODE_INNER (vmode), 1);
+      tree out_inner_type = build_nonstandard_integer_type (in_bits * 2, 1);
+      tree in_type = build_vector_type_for_mode (in_inner_type, vmode);
+      poly_uint64 out_nelts = exact_div (in_nelts, 2);
+      tree out_type = build_vector_type (out_inner_type, out_nelts);
+
+      tree_vector_builder builder (in_type, in_nelts.coeffs[0], 2);
+      for (unsigned int i = 0; i < in_nelts.coeffs[0]; ++i)
+       builder.quick_push (build_int_cst (in_inner_type, i + 1));
+      for (unsigned int i = 0; i < in_nelts.coeffs[0]; ++i)
+       builder.quick_push (build_int_cst (in_inner_type,
+                                          i < min_out_nelts ? 100 + i : 77));
+      tree arg = builder.build ();
+
+      /* Check integer unpacking with a duplicated scalable high half.  */
+      tree lo = const_unop (VEC_UNPACK_LO_EXPR, out_type, arg);
+      ASSERT_TRUE (lo != NULL_TREE);
+      tree hi = const_unop (VEC_UNPACK_HI_EXPR, out_type, arg);
+      ASSERT_TRUE (hi != NULL_TREE);
+
+      tree prefix = (!BYTES_BIG_ENDIAN ? lo : hi);
+      tree dup = (!BYTES_BIG_ENDIAN ? hi : lo);
+
+      ASSERT_EQ (VECTOR_CST, TREE_CODE (prefix));
+      ASSERT_EQ (VECTOR_CST, TREE_CODE (dup));
+      ASSERT_KNOWN_EQ (TYPE_VECTOR_SUBPARTS (out_type),
+                      VECTOR_CST_NELTS (prefix));
+      ASSERT_KNOWN_EQ (TYPE_VECTOR_SUBPARTS (out_type),
+                      VECTOR_CST_NELTS (dup));
+      ASSERT_EQ (min_out_nelts, VECTOR_CST_NPATTERNS (prefix));
+      ASSERT_EQ (2, VECTOR_CST_NELTS_PER_PATTERN (prefix));
+      ASSERT_EQ (1, VECTOR_CST_NPATTERNS (dup));
+      ASSERT_EQ (1, VECTOR_CST_NELTS_PER_PATTERN (dup));
+
+      for (unsigned int i = 0; i < min_out_nelts; ++i)
+       {
+         tree elt = build_int_cst (out_inner_type, i + 1);
+         ASSERT_TRUE (operand_equal_p (VECTOR_CST_ELT (prefix, i),
+                                       elt, 0));
+         tree fill = build_int_cst (out_inner_type, 77);
+         ASSERT_TRUE (operand_equal_p (VECTOR_CST_ELT
+                                         (prefix, min_out_nelts + i),
+                                       fill, 0));
+         ASSERT_TRUE (operand_equal_p (VECTOR_CST_ELT (dup, i),
+                                       fill, 0));
+       }
+
+      /* Check int-to-float unpacking with the same scalable shape.  */
+      tree float_out_type = build_vector_type (float_type_node, out_nelts);
+      tree float_lo = const_unop (VEC_UNPACK_FLOAT_LO_EXPR, float_out_type,
+                                 arg);
+      ASSERT_TRUE (float_lo != NULL_TREE);
+      tree float_hi = const_unop (VEC_UNPACK_FLOAT_HI_EXPR, float_out_type,
+                                 arg);
+      ASSERT_TRUE (float_hi != NULL_TREE);
+
+      tree float_prefix = (!BYTES_BIG_ENDIAN ? float_lo : float_hi);
+      tree float_dup = (!BYTES_BIG_ENDIAN ? float_hi : float_lo);
+      ASSERT_EQ (VECTOR_CST, TREE_CODE (float_prefix));
+      ASSERT_EQ (VECTOR_CST, TREE_CODE (float_dup));
+      ASSERT_EQ (min_out_nelts, VECTOR_CST_NPATTERNS (float_prefix));
+      ASSERT_EQ (2, VECTOR_CST_NELTS_PER_PATTERN (float_prefix));
+      ASSERT_EQ (1, VECTOR_CST_NPATTERNS (float_dup));
+      ASSERT_EQ (1, VECTOR_CST_NELTS_PER_PATTERN (float_dup));
+
+      for (unsigned int i = 0; i < min_out_nelts; ++i)
+       {
+         tree elt = fold_convert_const (FLOAT_EXPR, float_type_node,
+                                        build_int_cst (in_inner_type, i + 1));
+         ASSERT_TRUE (operand_equal_p (VECTOR_CST_ELT (float_prefix, i),
+                                       elt, 0));
+         tree fill = fold_convert_const (FLOAT_EXPR, float_type_node,
+                                         build_int_cst (in_inner_type, 77));
+         ASSERT_TRUE (operand_equal_p (VECTOR_CST_ELT
+                                         (float_prefix, min_out_nelts + i),
+                                       fill, 0));
+         ASSERT_TRUE (operand_equal_p (VECTOR_CST_ELT (float_dup, i),
+                                       fill, 0));
+       }
+
+      /* Check float-to-int unpacking with the same scalable shape.  */
+      tree float_in_type = build_vector_type (float_type_node, in_nelts);
+      tree_vector_builder fix_builder (float_in_type, in_nelts.coeffs[0], 2);
+      for (unsigned int i = 0; i < in_nelts.coeffs[0]; ++i)
+       fix_builder.quick_push
+         (build_real_from_int_cst (float_type_node,
+                                   build_int_cst (integer_type_node, i + 1)));
+      for (unsigned int i = 0; i < in_nelts.coeffs[0]; ++i)
+       fix_builder.quick_push
+         (build_real_from_int_cst (float_type_node,
+                                   build_int_cst (integer_type_node,
+                                                  i < min_out_nelts
+                                                  ? 100 + i : 77)));
+      tree fix_arg = fix_builder.build ();
+      tree fix_out_type = build_vector_type (integer_type_node, out_nelts);
+      tree fix_lo = const_unop (VEC_UNPACK_FIX_TRUNC_LO_EXPR, fix_out_type,
+                               fix_arg);
+      ASSERT_TRUE (fix_lo != NULL_TREE);
+      tree fix_hi = const_unop (VEC_UNPACK_FIX_TRUNC_HI_EXPR, fix_out_type,
+                               fix_arg);
+      ASSERT_TRUE (fix_hi != NULL_TREE);
+
+      tree fix_prefix = (!BYTES_BIG_ENDIAN ? fix_lo : fix_hi);
+      tree fix_dup = (!BYTES_BIG_ENDIAN ? fix_hi : fix_lo);
+      ASSERT_EQ (VECTOR_CST, TREE_CODE (fix_prefix));
+      ASSERT_EQ (VECTOR_CST, TREE_CODE (fix_dup));
+      ASSERT_EQ (min_out_nelts, VECTOR_CST_NPATTERNS (fix_prefix));
+      ASSERT_EQ (2, VECTOR_CST_NELTS_PER_PATTERN (fix_prefix));
+      ASSERT_EQ (1, VECTOR_CST_NPATTERNS (fix_dup));
+      ASSERT_EQ (1, VECTOR_CST_NELTS_PER_PATTERN (fix_dup));
+
+      for (unsigned int i = 0; i < min_out_nelts; ++i)
+       {
+         tree elt = build_int_cst (integer_type_node, i + 1);
+         ASSERT_TRUE (operand_equal_p (VECTOR_CST_ELT (fix_prefix, i),
+                                       elt, 0));
+         tree fill = build_int_cst (integer_type_node, 77);
+         ASSERT_TRUE (operand_equal_p (VECTOR_CST_ELT
+                                         (fix_prefix, min_out_nelts + i),
+                                       fill, 0));
+         ASSERT_TRUE (operand_equal_p (VECTOR_CST_ELT (fix_dup, i),
+                                       fill, 0));
+       }
+
+      /* Reject scalable constants whose high half is not duplicated.  */
+      tree_vector_builder varied (in_type, in_nelts.coeffs[0], 2);
+      for (unsigned int i = 0; i < in_nelts.coeffs[0]; ++i)
+       varied.quick_push (build_int_cst (in_inner_type, i + 1));
+      for (unsigned int i = 0; i < in_nelts.coeffs[0]; ++i)
+       varied.quick_push (build_int_cst (in_inner_type, 100 + i));
+      ASSERT_EQ (NULL_TREE, const_unop (VEC_UNPACK_LO_EXPR, out_type,
+                                       varied.build ()));
+
+      /* Reject stepped scalable constants.  */
+      tree_vector_builder stepped (in_type, 1, 3);
+      stepped.quick_push (build_int_cst (in_inner_type, 1));
+      stepped.quick_push (build_int_cst (in_inner_type, 2));
+      stepped.quick_push (build_int_cst (in_inner_type, 3));
+      ASSERT_EQ (NULL_TREE, const_unop (VEC_UNPACK_LO_EXPR, out_type,
+                                       stepped.build ()));
+
+      /* Reject result types with the wrong number of elements.  */
+      tree wrong_out_type = build_vector_type (out_inner_type, in_nelts);
+      ASSERT_EQ (NULL_TREE, const_unop (VEC_UNPACK_LO_EXPR, wrong_out_type,
+                                       arg));
+      return;
+    }
+}
+
 /* Run all of the selftests within this file.  */
 
 void
@@ -17760,6 +17976,7 @@ fold_const_cc_tests ()
   test_arithmetic_folding ();
   test_vector_folding ();
   test_vec_duplicate_folding ();
+  test_vec_unpack_folding ();
   test_fold_vec_perm_cst::test ();
   test_operand_equality::test ();
 }


-- 
diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
index b9262b34f76683fa75e4c5f7ce938753e9c695a2..f919dbab4390127b604302fad06c7a672e7f9876 100644
--- a/gcc/fold-const.cc
+++ b/gcc/fold-const.cc
@@ -1948,22 +1948,16 @@ const_unop (enum tree_code code, tree type, tree arg0)
     case VEC_UNPACK_FIX_TRUNC_LO_EXPR:
     case VEC_UNPACK_FIX_TRUNC_HI_EXPR:
       {
-	unsigned HOST_WIDE_INT out_nelts, in_nelts, i;
+	poly_uint64 out_nelts = TYPE_VECTOR_SUBPARTS (type);
+	unsigned HOST_WIDE_INT const_out_nelts;
+	unsigned int encoded_nelts, i, offset = 0;
 	enum tree_code subcode;
 
 	if (TREE_CODE (arg0) != VECTOR_CST)
 	  return NULL_TREE;
 
-	if (!VECTOR_CST_NELTS (arg0).is_constant (&in_nelts))
+	if (!known_eq (VECTOR_CST_NELTS (arg0), out_nelts * 2))
 	  return NULL_TREE;
-	out_nelts = in_nelts / 2;
-	gcc_assert (known_eq (out_nelts, TYPE_VECTOR_SUBPARTS (type)));
-
-	unsigned int offset = 0;
-	if ((!BYTES_BIG_ENDIAN) ^ (code == VEC_UNPACK_LO_EXPR
-				   || code == VEC_UNPACK_FLOAT_LO_EXPR
-				   || code == VEC_UNPACK_FIX_TRUNC_LO_EXPR))
-	  offset = out_nelts;
 
 	if (code == VEC_UNPACK_LO_EXPR || code == VEC_UNPACK_HI_EXPR)
 	  subcode = NOP_EXPR;
@@ -1973,8 +1967,57 @@ const_unop (enum tree_code code, tree type, tree arg0)
 	else
 	  subcode = FIX_TRUNC_EXPR;
 
-	tree_vector_builder elts (type, out_nelts, 1);
-	for (i = 0; i < out_nelts; i++)
+	bool low_p = (code == VEC_UNPACK_LO_EXPR
+		      || code == VEC_UNPACK_FLOAT_LO_EXPR
+		      || code == VEC_UNPACK_FIX_TRUNC_LO_EXPR);
+	bool use_second_encoded_half_p = (!BYTES_BIG_ENDIAN) ^ low_p;
+
+	unsigned int nelts_per_pattern = 1;
+	tree fill = NULL_TREE;
+	if (out_nelts.is_constant (&const_out_nelts))
+	  {
+	    encoded_nelts = const_out_nelts;
+	    if (use_second_encoded_half_p)
+	      offset = encoded_nelts;
+	  }
+	else
+	  {
+	    if (VECTOR_CST_STEPPED_P (arg0))
+	      return NULL_TREE;
+
+	    unsigned int min_out_nelts = constant_lower_bound (out_nelts);
+	    encoded_nelts = min_out_nelts;
+	    fill = fold_convert_const (subcode, TREE_TYPE (type),
+				       vector_cst_elt (arg0, encoded_nelts));
+	    if (fill == NULL_TREE || !CONSTANT_CLASS_P (fill))
+	      return NULL_TREE;
+
+	    /* Check that the selected high half is a single repeated
+	       value.  Since the input is not stepped, checking one full
+	       pattern is enough to prove the scalable tail.  */
+	    for (i = 1; i < VECTOR_CST_NPATTERNS (arg0); ++i)
+	      {
+		tree elt
+		  = fold_convert_const (subcode, TREE_TYPE (type),
+					vector_cst_elt (arg0,
+							encoded_nelts + i));
+		if (elt == NULL_TREE
+		    || !CONSTANT_CLASS_P (elt)
+		    || !operand_equal_p (elt, fill))
+		  return NULL_TREE;
+	      }
+
+	    if (use_second_encoded_half_p)
+	      {
+		encoded_nelts = 1;
+		offset = min_out_nelts;
+	      }
+	    else
+	      nelts_per_pattern = 2;
+	  }
+
+	tree_vector_builder elts (type, encoded_nelts, nelts_per_pattern);
+	for (i = 0; i < encoded_nelts; i++)
 	  {
 	    tree elt = fold_convert_const (subcode, TREE_TYPE (type),
 					   VECTOR_CST_ELT (arg0, i + offset));
@@ -1983,6 +2026,10 @@ const_unop (enum tree_code code, tree type, tree arg0)
 	    elts.quick_push (elt);
 	  }
 
+	if (nelts_per_pattern == 2)
+	  for (i = 0; i < encoded_nelts; ++i)
+	    elts.quick_push (fill);
+
 	return elts.build ();
       }
 
@@ -17752,6 +17799,175 @@ test_vec_duplicate_folding ()
   ASSERT_TRUE (operand_equal_p (dup5_expr, dup5_cst, 0));
 }
 
+/* Verify folding of VEC_UNPACK_{LO,HI}_EXPRs.  */
+
+static void
+test_vec_unpack_folding ()
+{
+  machine_mode vmode;
+  FOR_EACH_MODE_IN_CLASS (vmode, MODE_VECTOR_INT)
+    {
+      poly_uint64 in_nelts = GET_MODE_NUNITS (vmode);
+      if (!test_fold_vec_perm_cst::is_simple_vla_size (in_nelts)
+	  || in_nelts.coeffs[0] < 2
+	  || !targetm.vector_mode_supported_p (vmode))
+	continue;
+
+      unsigned int in_bits = GET_MODE_UNIT_BITSIZE (vmode);
+      if (in_bits >= HOST_BITS_PER_WIDE_INT)
+	continue;
+
+      unsigned int min_out_nelts = in_nelts.coeffs[0] / 2;
+      tree in_inner_type = lang_hooks.types.type_for_mode
+	(GET_MODE_INNER (vmode), 1);
+      tree out_inner_type = build_nonstandard_integer_type (in_bits * 2, 1);
+      tree in_type = build_vector_type_for_mode (in_inner_type, vmode);
+      poly_uint64 out_nelts = exact_div (in_nelts, 2);
+      tree out_type = build_vector_type (out_inner_type, out_nelts);
+
+      tree_vector_builder builder (in_type, in_nelts.coeffs[0], 2);
+      for (unsigned int i = 0; i < in_nelts.coeffs[0]; ++i)
+	builder.quick_push (build_int_cst (in_inner_type, i + 1));
+      for (unsigned int i = 0; i < in_nelts.coeffs[0]; ++i)
+	builder.quick_push (build_int_cst (in_inner_type,
+					   i < min_out_nelts ? 100 + i : 77));
+      tree arg = builder.build ();
+
+      /* Check integer unpacking with a duplicated scalable high half.  */
+      tree lo = const_unop (VEC_UNPACK_LO_EXPR, out_type, arg);
+      ASSERT_TRUE (lo != NULL_TREE);
+      tree hi = const_unop (VEC_UNPACK_HI_EXPR, out_type, arg);
+      ASSERT_TRUE (hi != NULL_TREE);
+
+      tree prefix = (!BYTES_BIG_ENDIAN ? lo : hi);
+      tree dup = (!BYTES_BIG_ENDIAN ? hi : lo);
+
+      ASSERT_EQ (VECTOR_CST, TREE_CODE (prefix));
+      ASSERT_EQ (VECTOR_CST, TREE_CODE (dup));
+      ASSERT_KNOWN_EQ (TYPE_VECTOR_SUBPARTS (out_type),
+		       VECTOR_CST_NELTS (prefix));
+      ASSERT_KNOWN_EQ (TYPE_VECTOR_SUBPARTS (out_type),
+		       VECTOR_CST_NELTS (dup));
+      ASSERT_EQ (min_out_nelts, VECTOR_CST_NPATTERNS (prefix));
+      ASSERT_EQ (2, VECTOR_CST_NELTS_PER_PATTERN (prefix));
+      ASSERT_EQ (1, VECTOR_CST_NPATTERNS (dup));
+      ASSERT_EQ (1, VECTOR_CST_NELTS_PER_PATTERN (dup));
+
+      for (unsigned int i = 0; i < min_out_nelts; ++i)
+	{
+	  tree elt = build_int_cst (out_inner_type, i + 1);
+	  ASSERT_TRUE (operand_equal_p (VECTOR_CST_ELT (prefix, i),
+					elt, 0));
+	  tree fill = build_int_cst (out_inner_type, 77);
+	  ASSERT_TRUE (operand_equal_p (VECTOR_CST_ELT
+					  (prefix, min_out_nelts + i),
+					fill, 0));
+	  ASSERT_TRUE (operand_equal_p (VECTOR_CST_ELT (dup, i),
+					fill, 0));
+	}
+
+      /* Check int-to-float unpacking with the same scalable shape.  */
+      tree float_out_type = build_vector_type (float_type_node, out_nelts);
+      tree float_lo = const_unop (VEC_UNPACK_FLOAT_LO_EXPR, float_out_type,
+				  arg);
+      ASSERT_TRUE (float_lo != NULL_TREE);
+      tree float_hi = const_unop (VEC_UNPACK_FLOAT_HI_EXPR, float_out_type,
+				  arg);
+      ASSERT_TRUE (float_hi != NULL_TREE);
+
+      tree float_prefix = (!BYTES_BIG_ENDIAN ? float_lo : float_hi);
+      tree float_dup = (!BYTES_BIG_ENDIAN ? float_hi : float_lo);
+      ASSERT_EQ (VECTOR_CST, TREE_CODE (float_prefix));
+      ASSERT_EQ (VECTOR_CST, TREE_CODE (float_dup));
+      ASSERT_EQ (min_out_nelts, VECTOR_CST_NPATTERNS (float_prefix));
+      ASSERT_EQ (2, VECTOR_CST_NELTS_PER_PATTERN (float_prefix));
+      ASSERT_EQ (1, VECTOR_CST_NPATTERNS (float_dup));
+      ASSERT_EQ (1, VECTOR_CST_NELTS_PER_PATTERN (float_dup));
+
+      for (unsigned int i = 0; i < min_out_nelts; ++i)
+	{
+	  tree elt = fold_convert_const (FLOAT_EXPR, float_type_node,
+					 build_int_cst (in_inner_type, i + 1));
+	  ASSERT_TRUE (operand_equal_p (VECTOR_CST_ELT (float_prefix, i),
+					elt, 0));
+	  tree fill = fold_convert_const (FLOAT_EXPR, float_type_node,
+					  build_int_cst (in_inner_type, 77));
+	  ASSERT_TRUE (operand_equal_p (VECTOR_CST_ELT
+					  (float_prefix, min_out_nelts + i),
+					fill, 0));
+	  ASSERT_TRUE (operand_equal_p (VECTOR_CST_ELT (float_dup, i),
+					fill, 0));
+	}
+
+      /* Check float-to-int unpacking with the same scalable shape.  */
+      tree float_in_type = build_vector_type (float_type_node, in_nelts);
+      tree_vector_builder fix_builder (float_in_type, in_nelts.coeffs[0], 2);
+      for (unsigned int i = 0; i < in_nelts.coeffs[0]; ++i)
+	fix_builder.quick_push
+	  (build_real_from_int_cst (float_type_node,
+				    build_int_cst (integer_type_node, i + 1)));
+      for (unsigned int i = 0; i < in_nelts.coeffs[0]; ++i)
+	fix_builder.quick_push
+	  (build_real_from_int_cst (float_type_node,
+				    build_int_cst (integer_type_node,
+						   i < min_out_nelts
+						   ? 100 + i : 77)));
+      tree fix_arg = fix_builder.build ();
+      tree fix_out_type = build_vector_type (integer_type_node, out_nelts);
+      tree fix_lo = const_unop (VEC_UNPACK_FIX_TRUNC_LO_EXPR, fix_out_type,
+				fix_arg);
+      ASSERT_TRUE (fix_lo != NULL_TREE);
+      tree fix_hi = const_unop (VEC_UNPACK_FIX_TRUNC_HI_EXPR, fix_out_type,
+				fix_arg);
+      ASSERT_TRUE (fix_hi != NULL_TREE);
+
+      tree fix_prefix = (!BYTES_BIG_ENDIAN ? fix_lo : fix_hi);
+      tree fix_dup = (!BYTES_BIG_ENDIAN ? fix_hi : fix_lo);
+      ASSERT_EQ (VECTOR_CST, TREE_CODE (fix_prefix));
+      ASSERT_EQ (VECTOR_CST, TREE_CODE (fix_dup));
+      ASSERT_EQ (min_out_nelts, VECTOR_CST_NPATTERNS (fix_prefix));
+      ASSERT_EQ (2, VECTOR_CST_NELTS_PER_PATTERN (fix_prefix));
+      ASSERT_EQ (1, VECTOR_CST_NPATTERNS (fix_dup));
+      ASSERT_EQ (1, VECTOR_CST_NELTS_PER_PATTERN (fix_dup));
+
+      for (unsigned int i = 0; i < min_out_nelts; ++i)
+	{
+	  tree elt = build_int_cst (integer_type_node, i + 1);
+	  ASSERT_TRUE (operand_equal_p (VECTOR_CST_ELT (fix_prefix, i),
+					elt, 0));
+	  tree fill = build_int_cst (integer_type_node, 77);
+	  ASSERT_TRUE (operand_equal_p (VECTOR_CST_ELT
+					  (fix_prefix, min_out_nelts + i),
+					fill, 0));
+	  ASSERT_TRUE (operand_equal_p (VECTOR_CST_ELT (fix_dup, i),
+					fill, 0));
+	}
+
+      /* Reject scalable constants whose high half is not duplicated.  */
+      tree_vector_builder varied (in_type, in_nelts.coeffs[0], 2);
+      for (unsigned int i = 0; i < in_nelts.coeffs[0]; ++i)
+	varied.quick_push (build_int_cst (in_inner_type, i + 1));
+      for (unsigned int i = 0; i < in_nelts.coeffs[0]; ++i)
+	varied.quick_push (build_int_cst (in_inner_type, 100 + i));
+      ASSERT_EQ (NULL_TREE, const_unop (VEC_UNPACK_LO_EXPR, out_type,
+					varied.build ()));
+
+      /* Reject stepped scalable constants.  */
+      tree_vector_builder stepped (in_type, 1, 3);
+      stepped.quick_push (build_int_cst (in_inner_type, 1));
+      stepped.quick_push (build_int_cst (in_inner_type, 2));
+      stepped.quick_push (build_int_cst (in_inner_type, 3));
+      ASSERT_EQ (NULL_TREE, const_unop (VEC_UNPACK_LO_EXPR, out_type,
+					stepped.build ()));
+
+      /* Reject result types with the wrong number of elements.  */
+      tree wrong_out_type = build_vector_type (out_inner_type, in_nelts);
+      ASSERT_EQ (NULL_TREE, const_unop (VEC_UNPACK_LO_EXPR, wrong_out_type,
+					arg));
+      return;
+    }
+}
+
 /* Run all of the selftests within this file.  */
 
 void
@@ -17760,6 +17976,7 @@ fold_const_cc_tests ()
   test_arithmetic_folding ();
   test_vector_folding ();
   test_vec_duplicate_folding ();
+  test_vec_unpack_folding ();
   test_fold_vec_perm_cst::test ();
   test_operand_equality::test ();
 }

Reply via email to