On Fri, 18 Sep 2026, Tamar Christina wrote:
> Fold integer VEC_UNPACK_LO_EXPR and VEC_UNPACK_HI_EXPR for suitable
> variable-length VECTOR_CSTs.
>
> 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 selftests cover the foldable low/high cases and cases that should not
> fold.
>
> 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.
>
> Bootstrapped Regtested on aarch64-none-linux-gnu,
> arm-none-linux-gnueabihf, x86_64-pc-linux-gnu
> -m32, -m64 and no issues.
>
> OK for master?
Can you try follow Richards comments as to how to keep the
constant/non-constant paths unified that lead to r17-3738-gaaa0f6b1e87bc6?
Thanks,
Richard.
> 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
> 4c4b535d44041ba9fd035ac3841d6862ab5700d1..289c5d5580b4793001ddce55a4a0a0f498db9e53
> 100644
> --- a/gcc/fold-const.cc
> +++ b/gcc/fold-const.cc
> @@ -1956,7 +1956,53 @@ const_unop (enum tree_code code, tree type, tree arg0)
> return NULL_TREE;
>
> if (!VECTOR_CST_NELTS (arg0).is_constant (&in_nelts))
> - return NULL_TREE;
> + {
> + if (code != VEC_UNPACK_LO_EXPR && code != VEC_UNPACK_HI_EXPR)
> + return NULL_TREE;
> + if (!known_eq (VECTOR_CST_NELTS (arg0),
> + TYPE_VECTOR_SUBPARTS (type) * 2)
> + || VECTOR_CST_STEPPED_P (arg0))
> + return NULL_TREE;
> +
> + unsigned int min_out_nelts
> + = constant_lower_bound (TYPE_VECTOR_SUBPARTS (type));
> + tree fill = fold_convert_const (NOP_EXPR, TREE_TYPE (type),
> + vector_cst_elt (arg0,
> + min_out_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 (NOP_EXPR, TREE_TYPE (type),
> + vector_cst_elt
> + (arg0,
> + min_out_nelts + i));
> + if (elt == NULL_TREE
> + || !CONSTANT_CLASS_P (elt)
> + || !operand_equal_p (elt, fill, 0))
> + return NULL_TREE;
> + }
> +
> + if ((!BYTES_BIG_ENDIAN) ^ (code == VEC_UNPACK_LO_EXPR))
> + return build_vector_from_val (type, fill);
> +
> + tree_vector_builder elts (type, min_out_nelts, 2);
> + for (i = 0; i < min_out_nelts; ++i)
> + {
> + tree elt = fold_convert_const (NOP_EXPR, TREE_TYPE (type),
> + vector_cst_elt (arg0, i));
> + if (elt == NULL_TREE || !CONSTANT_CLASS_P (elt))
> + return NULL_TREE;
> + elts.quick_push (elt);
> + }
> + for (i = 0; i < min_out_nelts; ++i)
> + elts.quick_push (fill);
> + return elts.build ();
> + }
> out_nelts = in_nelts / 2;
> gcc_assert (known_eq (out_nelts, TYPE_VECTOR_SUBPARTS (type)));
>
> @@ -17753,6 +17799,96 @@ 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);
> + tree out_type = build_vector_type (out_inner_type,
> + exact_div (in_nelts, 2));
> +
> + 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 ();
> +
> + 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));
> + }
> +
> + 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 ()));
> +
> + 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 ()));
> +
> + 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));
> + ASSERT_EQ (NULL_TREE, const_unop (VEC_UNPACK_FLOAT_LO_EXPR, out_type,
> + arg));
> + return;
> + }
> +}
> +
> /* Run all of the selftests within this file. */
>
> void
> @@ -17761,6 +17897,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 ();
> }
>
>
>
--
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Stefan Gaiser, Jochen Jaser, Abhinav Puri; (HRB 36809, AG Nuernberg)