ack, I am interested in this patch and will review this in the next few days.

On Fri, Dec 12, 2025 at 10:22 PM Robin Dapp <[email protected]> wrote:
>
> Hi,
>
> As discussed in the patchwork sync this patch adds a dynamic LMUL mode
> that sets the LMUL to the ratio of largest/smallest type size in a loop,
> with the maximum being LMUL8.
>
> This is supposed to imitate what other architectures implicitly do by
> vec_unpack_hi/lo.  I have done cursory testing and obviously more
> coverage would be preferred.
>
> Regtested on rv64gcv_zvl512b.
>
> Regards
>  Robin
>
>         PR target/122846
>
> gcc/ChangeLog:
>
>         * config/riscv/riscv-opts.h (enum rvv_max_lmul_enum): Add
>         RVV_CONV_DYNAMIC.
>         (TARGET_MAX_LMUL): Ditto.
>         * config/riscv/riscv-string.cc (use_vector_stringop_p): Use
>         LMUL1 for RVV_CONV_DYNAMIC.
>         (expand_rawmemchr): Ditto.
>         (expand_strcmp): Ditto.
>         (check_vectorise_memory_operation): Ditto.
>         * config/riscv/riscv-vector-costs.cc (get_smallest_mode):
>         New function.
>         (compute_lmul_from_conversion_ratio): Calculate LMUL from
>         largest/smallest type.
>         (costs::has_unexpected_spills_p): Split.
>         (costs::compute_live_ranges_and_lmul): Compute smallest type and
>         call new function.
>         (costs::cleanup_live_range_data): New function.
>         (costs::compute_conversion_dynamic_lmul): New function.
>         (costs::record_potential_unexpected_spills): Use new function.
>         (costs::better_main_loop_than_p): Allow appropriate LMUL.
>         * config/riscv/riscv-vector-costs.h: Declare.
>         * config/riscv/riscv.opt: New option
>         -mrvv-max-lmul=conv-dynamic.
>
> gcc/testsuite/ChangeLog:
>
>         * gcc.target/riscv/rvv/autovec/dyn-lmul-conv-1.c: New test.
>         * gcc.target/riscv/rvv/autovec/dyn-lmul-conv-2.c: New test.
>         * gcc.target/riscv/rvv/autovec/pr122846.c: New test.
> ---
>  gcc/config/riscv/riscv-opts.h                 |   7 +-
>  gcc/config/riscv/riscv-string.cc              |  26 +-
>  gcc/config/riscv/riscv-vector-costs.cc        | 226 ++++++++++++++----
>  gcc/config/riscv/riscv-vector-costs.h         |  17 +-
>  gcc/config/riscv/riscv.opt                    |   3 +
>  .../riscv/rvv/autovec/dyn-lmul-conv-1.c       |  42 ++++
>  .../riscv/rvv/autovec/dyn-lmul-conv-2.c       |  43 ++++
>  .../gcc.target/riscv/rvv/autovec/pr122846.c   |  14 ++
>  8 files changed, 320 insertions(+), 58 deletions(-)
>  create mode 100644 
> gcc/testsuite/gcc.target/riscv/rvv/autovec/dyn-lmul-conv-1.c
>  create mode 100644 
> gcc/testsuite/gcc.target/riscv/rvv/autovec/dyn-lmul-conv-2.c
>  create mode 100644 gcc/testsuite/gcc.target/riscv/rvv/autovec/pr122846.c
>
> diff --git a/gcc/config/riscv/riscv-opts.h b/gcc/config/riscv/riscv-opts.h
> index 9b92a965e27..c6a09d59620 100644
> --- a/gcc/config/riscv/riscv-opts.h
> +++ b/gcc/config/riscv/riscv-opts.h
> @@ -86,7 +86,9 @@ enum rvv_max_lmul_enum {
>    RVV_M4 = 4,
>    RVV_M8 = 8,
>    /* For dynamic LMUL, we compare COST start with LMUL8.  */
> -  RVV_DYNAMIC = 9
> +  RVV_DYNAMIC = 9,
> +  /* For dynamic LMUL based on conversions, set LMUL based on type size 
> ratio.  */
> +  RVV_CONV_DYNAMIC = 10
>  };
>
>  enum riscv_multilib_select_kind {
> @@ -155,7 +157,8 @@ enum rvv_vector_bits_enum {
>
>  /* The maximum LMUL according to user configuration.  */
>  #define TARGET_MAX_LMUL                                                      
>   \
> -  (int) (rvv_max_lmul == RVV_DYNAMIC ? RVV_M8 : rvv_max_lmul)
> +  (int) ((rvv_max_lmul == RVV_DYNAMIC || rvv_max_lmul == RVV_CONV_DYNAMIC) \
> +        ? RVV_M8 : rvv_max_lmul)
>
>  /* TLS types.  */
>  enum riscv_tls_type {
> diff --git a/gcc/config/riscv/riscv-string.cc 
> b/gcc/config/riscv/riscv-string.cc
> index c5710e4c896..ac9b19213a0 100644
> --- a/gcc/config/riscv/riscv-string.cc
> +++ b/gcc/config/riscv/riscv-string.cc
> @@ -1089,13 +1089,17 @@ use_vector_stringop_p (struct stringop_info &info, 
> HOST_WIDE_INT max_ew,
>    if (!TARGET_VECTOR || !(stringop_strategy & STRATEGY_VECTOR))
>      return false;
>
> +  int max_lmul = TARGET_MAX_LMUL;
> +  if (rvv_max_lmul == RVV_CONV_DYNAMIC)
> +    max_lmul = RVV_M1;
> +
>    if (CONST_INT_P (length_in))
>      {
>        HOST_WIDE_INT length = INTVAL (length_in);
>
>        /* If the VLEN and preferred LMUL allow the entire block to be copied 
> in
>          one go then no loop is needed.  */
> -      if (known_le (length, BYTES_PER_RISCV_VECTOR * TARGET_MAX_LMUL))
> +      if (known_le (length, BYTES_PER_RISCV_VECTOR * max_lmul))
>         {
>           need_loop = false;
>
> @@ -1130,10 +1134,10 @@ use_vector_stringop_p (struct stringop_info &info, 
> HOST_WIDE_INT max_ew,
>           poly_int64 nunits;
>
>           if (need_loop)
> -           per_iter = BYTES_PER_RISCV_VECTOR * TARGET_MAX_LMUL;
> +           per_iter = BYTES_PER_RISCV_VECTOR * max_lmul;
>           else
>             per_iter = length;
> -         /* BYTES_PER_RISCV_VECTOR * TARGET_MAX_LMUL may not be divisible by
> +         /* BYTES_PER_RISCV_VECTOR * MAX_LMUL may not be divisible by
>              this potential_ew.  */
>           if (!multiple_p (per_iter, potential_ew, &nunits))
>             continue;
> @@ -1164,7 +1168,7 @@ use_vector_stringop_p (struct stringop_info &info, 
> HOST_WIDE_INT max_ew,
>                  pointless.
>                  Still, by choosing a lower LMUL factor that still allows
>                  an entire transfer, we can reduce register pressure.  */
> -             for (unsigned lmul = 1; lmul < TARGET_MAX_LMUL; lmul <<= 1)
> +             for (int lmul = 1; lmul < max_lmul; lmul <<= 1)
>                 if (known_le (length * BITS_PER_UNIT, TARGET_MIN_VLEN * lmul)
>                     && multiple_p (BYTES_PER_RISCV_VECTOR * lmul, 
> potential_ew,
>                                    &mode_units)
> @@ -1177,9 +1181,9 @@ use_vector_stringop_p (struct stringop_info &info, 
> HOST_WIDE_INT max_ew,
>           if (vmode != VOIDmode)
>             break;
>
> -         /* BYTES_PER_RISCV_VECTOR * TARGET_MAX_LMUL will at least be 
> divisible
> +         /* BYTES_PER_RISCV_VECTOR * MAX_LMUL will at least be divisible
>              by potential_ew 1, so this should succeed eventually.  */
> -         if (multiple_p (BYTES_PER_RISCV_VECTOR * TARGET_MAX_LMUL,
> +         if (multiple_p (BYTES_PER_RISCV_VECTOR * max_lmul,
>                           potential_ew, &mode_units)
>               && riscv_vector::get_vector_mode (elem_mode,
>                                                 mode_units).exists (&vmode))
> @@ -1195,7 +1199,7 @@ use_vector_stringop_p (struct stringop_info &info, 
> HOST_WIDE_INT max_ew,
>      }
>    else
>      {
> -      gcc_assert (get_lmul_mode (QImode, TARGET_MAX_LMUL).exists (&vmode));
> +      gcc_assert (get_lmul_mode (QImode, max_lmul).exists (&vmode));
>      }
>
>    /* A memcpy libcall in the worst case takes 3 instructions to prepare the
> @@ -1356,6 +1360,8 @@ expand_rawmemchr (machine_mode mode, rtx dst, rtx 
> haystack, rtx needle,
>
>    unsigned int isize = GET_MODE_SIZE (mode).to_constant ();
>    int lmul = TARGET_MAX_LMUL;
> +  if (rvv_max_lmul == RVV_CONV_DYNAMIC)
> +    lmul = RVV_M1;
>    poly_int64 nunits = exact_div (BYTES_PER_RISCV_VECTOR * lmul, isize);
>
>    machine_mode vmode;
> @@ -1455,6 +1461,8 @@ expand_strcmp (rtx result, rtx src1, rtx src2, rtx 
> nbytes,
>    machine_mode mode = E_QImode;
>    unsigned int isize = GET_MODE_SIZE (mode).to_constant ();
>    int lmul = TARGET_MAX_LMUL;
> +  if (rvv_max_lmul == RVV_CONV_DYNAMIC)
> +    lmul = RVV_M1;
>    poly_int64 nunits = exact_div (BYTES_PER_RISCV_VECTOR * lmul, isize);
>
>    machine_mode vmode;
> @@ -1606,7 +1614,9 @@ check_vectorise_memory_operation (rtx length_in, 
> HOST_WIDE_INT &lmul_out)
>    if (rvv_max_lmul != RVV_DYNAMIC)
>      {
>        lmul_out = TARGET_MAX_LMUL;
> -      return (length <= ((TARGET_MAX_LMUL * TARGET_MIN_VLEN) / 8));
> +      if (rvv_max_lmul == RVV_CONV_DYNAMIC)
> +       lmul_out = RVV_M1;
> +      return (length <= ((lmul_out * TARGET_MIN_VLEN) / 8));
>      }
>
>    /* Find smallest lmul large enough for entire op.  */
> diff --git a/gcc/config/riscv/riscv-vector-costs.cc 
> b/gcc/config/riscv/riscv-vector-costs.cc
> index 27ced61e815..41b4e4860b0 100644
> --- a/gcc/config/riscv/riscv-vector-costs.cc
> +++ b/gcc/config/riscv/riscv-vector-costs.cc
> @@ -258,6 +258,14 @@ get_biggest_mode (machine_mode mode1, machine_mode mode2)
>    return mode1_size >= mode2_size ? mode1 : mode2;
>  }
>
> +static machine_mode
> +get_smallest_mode (machine_mode mode1, machine_mode mode2)
> +{
> +  unsigned int mode1_size = GET_MODE_BITSIZE (mode1).to_constant ();
> +  unsigned int mode2_size = GET_MODE_BITSIZE (mode2).to_constant ();
> +  return mode1_size <= mode2_size ? mode1 : mode2;
> +}
> +
>  /* Return true if OP is invariant.  */
>
>  static bool
> @@ -361,9 +369,11 @@ machine_mode
>  costs::compute_local_live_ranges (
>    loop_vec_info loop_vinfo,
>    const hash_map<basic_block, vec<stmt_point>> &program_points_per_bb,
> -  hash_map<basic_block, hash_map<tree, pair>> &live_ranges_per_bb)
> +  hash_map<basic_block, hash_map<tree, pair>> &live_ranges_per_bb,
> +  machine_mode *smallest_mode_out)
>  {
>    machine_mode biggest_mode = QImode;
> +  machine_mode smallest_mode = TImode;
>    class loop *loop = LOOP_VINFO_LOOP (loop_vinfo);
>    if (!program_points_per_bb.is_empty ())
>      {
> @@ -396,6 +406,8 @@ costs::compute_local_live_ranges (
>                 {
>                   biggest_mode = get_biggest_mode (biggest_mode,
>                                                    TYPE_MODE (TREE_TYPE 
> (lhs)));
> +                 smallest_mode = get_smallest_mode (smallest_mode,
> +                                                    TYPE_MODE (TREE_TYPE 
> (lhs)));
>                   bool existed_p = false;
>                   pair &live_range
>                     = live_ranges->get_or_insert (lhs, &existed_p);
> @@ -415,6 +427,9 @@ costs::compute_local_live_ranges (
>                       biggest_mode
>                         = get_biggest_mode (biggest_mode,
>                                             TYPE_MODE (TREE_TYPE (var)));
> +                     smallest_mode
> +                       = get_smallest_mode (smallest_mode,
> +                                            TYPE_MODE (TREE_TYPE (var)));
>                       bool existed_p = false;
>                       pair &live_range
>                         = live_ranges->get_or_insert (var, &existed_p);
> @@ -445,6 +460,8 @@ costs::compute_local_live_ranges (
>                                   (*r).second = MAX (point, (*r).second);
>                                   biggest_mode = get_biggest_mode (
>                                     biggest_mode, TYPE_MODE (TREE_TYPE 
> (arg)));
> +                                 smallest_mode = get_smallest_mode (
> +                                   smallest_mode, TYPE_MODE (TREE_TYPE 
> (arg)));
>                                 }
>                             }
>                           else
> @@ -464,8 +481,14 @@ costs::compute_local_live_ranges (
>         }
>      }
>    if (dump_enabled_p ())
> -    dump_printf_loc (MSG_NOTE, vect_location, "Biggest mode = %s\n",
> -                    GET_MODE_NAME (biggest_mode));
> +    {
> +      dump_printf_loc (MSG_NOTE, vect_location, "Biggest mode = %s\n",
> +                      GET_MODE_NAME (biggest_mode));
> +      dump_printf_loc (MSG_NOTE, vect_location, "Smallest mode = %s\n",
> +                      GET_MODE_NAME (smallest_mode));
> +    }
> +  if (smallest_mode_out)
> +    *smallest_mode_out = smallest_mode;
>    return biggest_mode;
>  }
>
> @@ -639,6 +662,25 @@ compute_estimated_lmul (loop_vec_info loop_vinfo, 
> machine_mode mode)
>    return 0;
>  }
>
> +/* Compute LMUL based on the ratio of biggest to smallest type size.
> +   This is used for RVV_CONV_DYNAMIC.  */
> +static int
> +compute_lmul_from_conversion_ratio (machine_mode biggest_mode,
> +                                   machine_mode smallest_mode)
> +{
> +  gcc_assert (GET_MODE_BITSIZE (biggest_mode).is_constant ());
> +  gcc_assert (GET_MODE_BITSIZE (smallest_mode).is_constant ());
> +
> +  unsigned int biggest_size = GET_MODE_BITSIZE (biggest_mode).to_constant ();
> +  unsigned int smallest_size = GET_MODE_BITSIZE (smallest_mode).to_constant 
> ();
> +
> +  int lmul = biggest_size / smallest_size;
> +  lmul = std::min (lmul, (int) RVV_M8);
> +  lmul = std::max (lmul, (int) RVV_M1);
> +
> +  return lmul;
> +}
> +
>  /* Update the live ranges according PHI.
>
>     Loop:
> @@ -825,56 +867,37 @@ costs::update_local_live_ranges (
>      }
>  }
>
> -/* Compute the maximum live V_REGS.  */
> -bool
> -costs::has_unexpected_spills_p (loop_vec_info loop_vinfo)
> +/* Helper to compute live ranges, modes, and LMUL.  */
> +void
> +costs::compute_live_ranges_and_lmul (loop_vec_info loop_vinfo,
> +  hash_map<basic_block, vec<stmt_point>> &program_points_per_bb,
> +  hash_map<basic_block, hash_map<tree, pair>> &live_ranges_per_bb,
> +  machine_mode &biggest_mode, machine_mode &smallest_mode, int &lmul)
>  {
> -  /* Compute local program points.
> -     It's a fast and effective computation.  */
> -  hash_map<basic_block, vec<stmt_point>> program_points_per_bb;
>    compute_local_program_points (loop_vinfo, program_points_per_bb);
>
> -  /* Compute local live ranges.  */
> -  hash_map<basic_block, hash_map<tree, pair>> live_ranges_per_bb;
> -  machine_mode biggest_mode
> -    = compute_local_live_ranges (loop_vinfo, program_points_per_bb,
> -                                live_ranges_per_bb);
> +  smallest_mode = TImode;
> +  biggest_mode = compute_local_live_ranges (loop_vinfo, 
> program_points_per_bb,
> +                                           live_ranges_per_bb, 
> &smallest_mode);
>
> -  /* Update live ranges according to PHI.  */
>    update_local_live_ranges (loop_vinfo, program_points_per_bb,
>                             live_ranges_per_bb, &biggest_mode);
>
> -  int lmul = compute_estimated_lmul (loop_vinfo, biggest_mode);
> +  if (rvv_max_lmul == RVV_CONV_DYNAMIC)
> +    lmul = compute_lmul_from_conversion_ratio (biggest_mode, smallest_mode);
> +  else
> +    lmul = compute_estimated_lmul (loop_vinfo, biggest_mode);
> +
>    gcc_assert (lmul <= RVV_M8);
> -  /* TODO: We calculate the maximum live vars base on current STMTS
> -     sequence.  We can support live range shrink if it can give us
> -     big improvement in the future.  */
> -  if (lmul > RVV_M1)
> -    {
> -      if (!live_ranges_per_bb.is_empty ())
> -       {
> -         unsigned int max_nregs = 0;
> -         for (hash_map<basic_block, hash_map<tree, pair>>::iterator iter
> -              = live_ranges_per_bb.begin ();
> -              iter != live_ranges_per_bb.end (); ++iter)
> -           {
> -             basic_block bb = (*iter).first;
> -             unsigned int max_point
> -               = (*program_points_per_bb.get (bb)).length () + 1;
> -             if ((*iter).second.is_empty ())
> -               continue;
> -             /* We prefer larger LMUL unless it causes register spillings. */
> -             unsigned int nregs
> -               = max_number_of_live_regs (loop_vinfo, bb, (*iter).second,
> -                                          max_point, biggest_mode, lmul);
> -             if (nregs > max_nregs)
> -               max_nregs = nregs;
> -           }
> -         live_ranges_per_bb.empty ();
> -         if (max_nregs > V_REG_NUM)
> -           return true;
> -       }
> -    }
> +}
> +
> +/* Helper to clean up live range data structures.  */
> +void
> +costs::cleanup_live_range_data (hash_map<basic_block, vec<stmt_point>>
> +                               &program_points_per_bb,
> +                               hash_map<basic_block, hash_map<tree, pair>>
> +                               &live_ranges_per_bb)
> +{
>    if (!program_points_per_bb.is_empty ())
>      {
>        for (hash_map<basic_block, vec<stmt_point>>::iterator iter
> @@ -887,7 +910,72 @@ costs::has_unexpected_spills_p (loop_vec_info loop_vinfo)
>         }
>        program_points_per_bb.empty ();
>      }
> -  return false;
> +  live_ranges_per_bb.empty ();
> +}
> +
> +/* Compute LMUL for RVV_CONV_DYNAMIC mode based on conversion ratio.  */
> +void
> +costs::compute_conversion_dynamic_lmul (loop_vec_info loop_vinfo)
> +{
> +  hash_map<basic_block, vec<stmt_point>> program_points_per_bb;
> +  hash_map<basic_block, hash_map<tree, pair>> live_ranges_per_bb;
> +  machine_mode biggest_mode, smallest_mode;
> +  int lmul;
> +
> +  compute_live_ranges_and_lmul (loop_vinfo, program_points_per_bb,
> +                               live_ranges_per_bb, biggest_mode,
> +                               smallest_mode, lmul);
> +
> +  /* Store the computed LMUL and biggest mode for later comparison
> +     in cost model.  */
> +  m_computed_lmul_from_conv = lmul;
> +  m_biggest_mode_for_conv = biggest_mode;
> +
> +  cleanup_live_range_data (program_points_per_bb, live_ranges_per_bb);
> +}
> +
> +/* Compute the maximum live V_REGS and check for unexpected spills.  */
> +bool
> +costs::has_unexpected_spills_p (loop_vec_info loop_vinfo)
> +{
> +  hash_map<basic_block, vec<stmt_point>> program_points_per_bb;
> +  hash_map<basic_block, hash_map<tree, pair>> live_ranges_per_bb;
> +  machine_mode biggest_mode, smallest_mode;
> +  int lmul;
> +
> +  compute_live_ranges_and_lmul (loop_vinfo, program_points_per_bb,
> +                               live_ranges_per_bb, biggest_mode,
> +                               smallest_mode, lmul);
> +
> +  /* TODO: We calculate the maximum live vars base on current STMTS
> +     sequence.  We can support live range shrink if it can give us
> +     big improvement in the future.  */
> +  bool has_spills = false;
> +  if (lmul > RVV_M1 && !live_ranges_per_bb.is_empty ())
> +    {
> +      unsigned int max_nregs = 0;
> +      for (hash_map<basic_block, hash_map<tree, pair>>::iterator iter
> +          = live_ranges_per_bb.begin ();
> +          iter != live_ranges_per_bb.end (); ++iter)
> +       {
> +         basic_block bb = (*iter).first;
> +         unsigned int max_point
> +           = (*program_points_per_bb.get (bb)).length () + 1;
> +         if ((*iter).second.is_empty ())
> +           continue;
> +         /* We prefer larger LMUL unless it causes register spillings.  */
> +         unsigned int nregs
> +           = max_number_of_live_regs (loop_vinfo, bb, (*iter).second,
> +                                      max_point, biggest_mode, lmul);
> +         if (nregs > max_nregs)
> +           max_nregs = nregs;
> +       }
> +      if (max_nregs > V_REG_NUM)
> +       has_spills = true;
> +    }
> +
> +  cleanup_live_range_data (program_points_per_bb, live_ranges_per_bb);
> +  return has_spills;
>  }
>
>  costs::costs (vec_info *vinfo, bool costing_for_scalar)
> @@ -937,6 +1025,8 @@ costs::record_potential_unexpected_spills (loop_vec_info 
> loop_vinfo)
>        if (!post_dom_available_p)
>         free_dominance_info (CDI_POST_DOMINATORS);
>      }
> +  else if (rvv_max_lmul == RVV_CONV_DYNAMIC)
> +    compute_conversion_dynamic_lmul (loop_vinfo);
>  }
>
>  /* Decide whether to use the unrolling heuristic described above
> @@ -1033,6 +1123,50 @@ costs::better_main_loop_than_p (const vector_costs 
> *uncast_other) const
>           return other_prefer_unrolled;
>         }
>      }
> +  else if (rvv_max_lmul == RVV_CONV_DYNAMIC)
> +    {
> +      if (this->m_computed_lmul_from_conv > 0
> +         && other->m_computed_lmul_from_conv > 0
> +         && this->m_biggest_mode_for_conv != VOIDmode)
> +       {
> +         int this_vf = vect_vf_for_cost (this_loop_vinfo);
> +         int other_vf = vect_vf_for_cost (other_loop_vinfo);
> +
> +         /* Get element size from the biggest mode.  */
> +         unsigned int element_bits
> +           = GET_MODE_BITSIZE (this->m_biggest_mode_for_conv).to_constant ();
> +
> +         /* Estimate LMUL from VF * element_size / MIN_VLEN.  */
> +         int this_lmul = (this_vf * element_bits) / TARGET_MIN_VLEN;
> +         int other_lmul = (other_vf * element_bits) / TARGET_MIN_VLEN;
> +
> +         /* Clamp to valid LMUL range.  */
> +         this_lmul = MAX (1, MIN (this_lmul, 8));
> +         other_lmul = MAX (1, MIN (other_lmul, 8));
> +
> +         int target_lmul = this->m_computed_lmul_from_conv;
> +
> +         /* Prefer the LMUL that exactly matches our computed ratio.  */
> +         if (this_lmul == target_lmul && other_lmul != target_lmul)
> +           {
> +             if (dump_enabled_p ())
> +               dump_printf_loc (MSG_NOTE, vect_location,
> +                                "Preferring LMUL=%d loop because it matches"
> +                                " conversion ratio (other LMUL=%d)\n",
> +                                this_lmul, other_lmul);
> +             return true;
> +           }
> +         else if (this_lmul != target_lmul && other_lmul == target_lmul)
> +           {
> +             if (dump_enabled_p ())
> +               dump_printf_loc (MSG_NOTE, vect_location,
> +                                "Preferring other LMUL=%d loop because it 
> matches"
> +                                " conversion ratio (this LMUL=%d)\n",
> +                                other_lmul, this_lmul);
> +             return false;
> +           }
> +       }
> +    }
>    else if (rvv_max_lmul == RVV_DYNAMIC)
>      {
>        if (other->m_has_unexpected_spills_p)
> diff --git a/gcc/config/riscv/riscv-vector-costs.h 
> b/gcc/config/riscv/riscv-vector-costs.h
> index b84ceb1d3cf..89f813c3d98 100644
> --- a/gcc/config/riscv/riscv-vector-costs.h
> +++ b/gcc/config/riscv/riscv-vector-costs.h
> @@ -106,6 +106,11 @@ private:
>    bool m_has_unexpected_spills_p = false;
>    void record_potential_unexpected_spills (loop_vec_info);
>
> +  /* For RVV_DYNAMIC_CONV mode, store the LMUL computed from conversion ratio
> +     and the biggest mode used in the computation.  */
> +  int m_computed_lmul_from_conv = 0;
> +  machine_mode m_biggest_mode_for_conv = VOIDmode;
> +
>    void compute_local_program_points (vec_info *,
>                                      hash_map<basic_block, vec<stmt_point>> 
> &);
>    void update_local_live_ranges (vec_info *,
> @@ -114,9 +119,17 @@ private:
>                                  machine_mode *);
>    machine_mode compute_local_live_ranges
>      (loop_vec_info, const hash_map<basic_block, vec<stmt_point>> &,
> -     hash_map<basic_block, hash_map<tree, pair>> &);
> -
> +     hash_map<basic_block, hash_map<tree, pair>> &,
> +     machine_mode * = nullptr);
> +
> +  void compute_live_ranges_and_lmul (loop_vec_info,
> +                                    hash_map<basic_block, vec<stmt_point>> &,
> +                                    hash_map<basic_block, hash_map<tree, 
> pair>> &,
> +                                    machine_mode &, machine_mode &, int &);
> +  void cleanup_live_range_data (hash_map<basic_block, vec<stmt_point>> &,
> +                               hash_map<basic_block, hash_map<tree, pair>> 
> &);
>    bool has_unexpected_spills_p (loop_vec_info);
> +  void compute_conversion_dynamic_lmul (loop_vec_info);
>    bool need_additional_vector_vars_p (stmt_vec_info, slp_tree);
>
>    void adjust_vect_cost_per_loop (loop_vec_info);
> diff --git a/gcc/config/riscv/riscv.opt b/gcc/config/riscv/riscv.opt
> index 452062c6500..de7730a8961 100644
> --- a/gcc/config/riscv/riscv.opt
> +++ b/gcc/config/riscv/riscv.opt
> @@ -313,6 +313,9 @@ Enum(rvv_max_lmul) String(m8) Value(RVV_M8)
>  EnumValue
>  Enum(rvv_max_lmul) String(dynamic) Value(RVV_DYNAMIC)
>
> +EnumValue
> +Enum(rvv_max_lmul) String(conv-dynamic) Value(RVV_CONV_DYNAMIC)
> +
>  mrvv-max-lmul=
>  Target RejectNegative Joined Enum(rvv_max_lmul) Var(rvv_max_lmul) 
> Init(RVV_M1)
>  -mrvv-max-lmul=<string>        Set the RVV LMUL of auto-vectorization.
> diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/dyn-lmul-conv-1.c 
> b/gcc/testsuite/gcc.target/riscv/rvv/autovec/dyn-lmul-conv-1.c
> new file mode 100644
> index 00000000000..b07bd86f76e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/dyn-lmul-conv-1.c
> @@ -0,0 +1,42 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O3 -march=rv64gcv -mabi=lp64d -mrvv-max-lmul=conv-dynamic" 
> } */
> +
> +void foo2x1 (short *restrict a, char *restrict b, int n)
> +{
> +  for (int i = 0; i < n; i++)
> +    a[i] = b[i];
> +}
> +
> +void foo2x2 (int *restrict a, short *restrict b, int n)
> +{
> +  for (int i = 0; i < n; i++)
> +    a[i] = b[i];
> +}
> +
> +void foo2x3 (long *restrict a, int *restrict b, int n)
> +{
> +  for (int i = 0; i < n; i++)
> +    a[i] = b[i];
> +}
> +
> +void foo4x1 (int *restrict a, char *restrict b, int n)
> +{
> +  for (int i = 0; i < n; i++)
> +    a[i] = b[i];
> +}
> +
> +void foo4x2 (long *restrict a, short *restrict b, int n)
> +{
> +  for (int i = 0; i < n; i++)
> +    a[i] = b[i];
> +}
> +
> +void foo8x (long *restrict a, char *restrict b, int n)
> +{
> +  for (int i = 0; i < n; i++)
> +    a[i] = b[i];
> +}
> +
> +/* { dg-final { scan-assembler-times ",m2," 3 } } */
> +/* { dg-final { scan-assembler-times ",m4," 2 } } */
> +/* { dg-final { scan-assembler-times ",m8," 1 } } */
> diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/dyn-lmul-conv-2.c 
> b/gcc/testsuite/gcc.target/riscv/rvv/autovec/dyn-lmul-conv-2.c
> new file mode 100644
> index 00000000000..c37e4dd63f2
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/dyn-lmul-conv-2.c
> @@ -0,0 +1,43 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O3 -march=rv64gcv -mabi=lp64d -mrvv-max-lmul=conv-dynamic" 
> } */
> +
> +void foo2x1 (unsigned char *restrict a, unsigned short *restrict b, int n)
> +{
> +  for (int i = 0; i < n; i++)
> +    a[i] = b[i];
> +}
> +
> +void foo2x2 (unsigned short *restrict a, unsigned int *restrict b, int n)
> +{
> +  for (int i = 0; i < n; i++)
> +    a[i] = b[i];
> +}
> +
> +void foo2x3 (unsigned int *restrict a, unsigned long *restrict b, int n)
> +{
> +  for (int i = 0; i < n; i++)
> +    a[i] = b[i];
> +}
> +
> +void foo4x1 (unsigned char *restrict a, unsigned int *restrict b, int n)
> +{
> +  for (int i = 0; i < n; i++)
> +    a[i] = b[i];
> +}
> +
> +void foo4x2 (unsigned short *restrict a, unsigned long *restrict b, int n)
> +{
> +  for (int i = 0; i < n; i++)
> +    a[i] = b[i];
> +}
> +
> +void foo8x (unsigned char *restrict a, unsigned long *restrict b, int n)
> +{
> +  for (int i = 0; i < n; i++)
> +    a[i] = b[i];
> +}
> +
> +/* { dg-final { scan-assembler-times ",m1," 6 } } */
> +/* { dg-final { scan-assembler-times ",m2," 3 } } */
> +/* { dg-final { scan-assembler-times ",m4," 1 } } */
> +/* { dg-final { scan-assembler-not ",mf2," } } */
> diff --git a/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr122846.c 
> b/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr122846.c
> new file mode 100644
> index 00000000000..7753a66cd96
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/riscv/rvv/autovec/pr122846.c
> @@ -0,0 +1,14 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O3 -march=rv64gcv -mabi=lp64d -mrvv-max-lmul=conv-dynamic" 
> } */
> +
> +int
> +foo (const char *x, const char *y)
> +{
> +  int sum = 0;
> +  for (int i = 0; i < 1024; i++)
> +    sum += x[i] * y[i];
> +  return sum;
> +}
> +
> +/* One for the initial value, one for the reduction.  */
> +/* { dg-final { scan-assembler-times ",m4," 2 } } */
> --
> 2.51.1
>

Reply via email to