"Robin Dapp" <[email protected]> writes:
>>> I suppose the question isn't so much "is this a tuple mode?" but more
>>> "does this vector occupy multiple registers?".  REGMODE_NATURAL_SIZE
>>> seems like the right thing to test for that.  The problem is that both
>>> AArch64 and RISC-V only define REGMODE_NATURAL_SIZE in the natural way
>>> for variable-length vectors:
>>>
>>>   /* ??? For now, only do this for variable-width SVE registers.
>>>      Doing it for constant-sized registers breaks lower-subreg.cc.  */
>>>
>>> So I suppose we'd need to fix that first (which would be a good thing,
>>> but a bit of tangent).
>>
>> This has bitten me several times already but I never bothered to
>> investigate.
>> Agreed that it would be a good thing to fix.  I'll have a look (but
>> not right
>> away).
>>
>>> Then related_vector_mode could be used to get the associated vector mode.
>
>
> I couldn't resist even though I have tons of other things to do :/
> But as one of my mid-term goals is to rework vec-vec extracts (for which we 
> still rely on subregs surviving to the target), it seemed a reasonable fit.
>
> The attached patch/idea defines REGMODE_NATURAL_SIZE for VLS vectors as well 
> and tries to handle the resulting fallout.  At least for riscv, lower-subreg 
> wasn't actually the problem but rather the arteries around emit_move, 
> operand_force_subreg etc.
>
> Would an approach like the attached have any way forward?  If so, a more 
> thorough auditing of the various paths will be necessary.  Obviously, the 
> aarch64 changes are just ad-hoc.  A previous version of it was 
> bootstrapped/regtested as usual FWIW.

Thanks for trying this!

> diff --git a/gcc/config/riscv/riscv-v.cc b/gcc/config/riscv/riscv-v.cc
> index 431aaa1e761..3794f91c1cb 100644
> --- a/gcc/config/riscv/riscv-v.cc
> +++ b/gcc/config/riscv/riscv-v.cc
> @@ -4018,7 +4018,9 @@ shuffle_even_odd_patterns (struct expand_vec_perm_d *d)
>       now, we only do this when the mode size is no greater than the natural 
> size
>       of the register.  Once this is fixed, the condition should be replaced 
> by
>       the ELEN condition.  */
> -  if (known_le (GET_MODE_SIZE (vmode), riscv_regmode_natural_size (vmode)))
> +  unsigned int max_elen = TARGET_VECTOR_ELEN_64 ? 64 : 32;
> +  if (known_le (GET_MODE_SIZE (vmode), riscv_regmode_natural_size (vmode))
> +      && GET_MODE_BITSIZE (GET_MODE_INNER (vmode)) * 2 <= max_elen)
>      {
>        unsigned int elen = GET_MODE_BITSIZE (GET_MODE_INNER (vmode));
>        unsigned int elen2x = elen * 2;

FWIW, I suppose the comment above the check would need to be updated
as well.  It sounds like the comment was anticipating that no
riscv_regmode_natural_size check would be needed after the fix,
but perhaps I misunderstood.

> diff --git a/gcc/expmed.cc b/gcc/expmed.cc
> index da1b5b63287..3e005958839 100644
> --- a/gcc/expmed.cc
> +++ b/gcc/expmed.cc
> @@ -858,12 +858,15 @@ store_bit_field_1 (rtx str_rtx, poly_uint64 bitsize, 
> poly_uint64 bitnum,
>       valid for integral modes.  */
>    opt_scalar_int_mode op0_mode = int_mode_for_mode (GET_MODE (op0));
>    scalar_int_mode imode;
> +  bool need_stack_p = false;
>    if (!op0_mode.exists (&imode) || imode != GET_MODE (op0))
>      {
>        if (MEM_P (op0))
>       op0 = adjust_bitfield_address_size (op0, op0_mode.else_blk (),
>                                           0, MEM_SIZE (op0));
> -      else if (!op0_mode.exists ())
> +      else if (!op0_mode.exists ()
> +            || maybe_lt ((unsigned) UNITS_PER_WORD,
> +                         REGMODE_NATURAL_SIZE (GET_MODE (op0))))
>       {
>         if (ibitnum == 0
>             && known_eq (ibitsize, GET_MODE_BITSIZE (GET_MODE (op0)))
> @@ -876,17 +879,31 @@ store_bit_field_1 (rtx str_rtx, poly_uint64 bitsize, 
> poly_uint64 bitnum,
>           }
>         if (!fallback_p)
>           return false;
> -       rtx temp = assign_stack_temp (GET_MODE (op0),
> -                                     GET_MODE_SIZE (GET_MODE (op0)));
> -       emit_move_insn (temp, op0);
> -       store_bit_field_1 (temp, bitsize, bitnum, 0, 0, fieldmode, value,
> -                          reverse, fallback_p, undefined_p);
> -       emit_move_insn (op0, temp);
> -       return true;
> +       need_stack_p = true;
>       }
>        else
>       op0 = gen_lowpart (op0_mode.require (), op0);
>      }
> +  else
> +    {
> +      if (maybe_lt ((unsigned) UNITS_PER_WORD,
> +                 REGMODE_NATURAL_SIZE (GET_MODE (op0))))
> +     need_stack_p = true;
> +    }
> +
> +  /* With or without punning we might be faced with a mode that we cannot
> +     split into words.  If so, spill OP0 to the stack and recurse.
> +     This happens at most once.  */
> +  if (need_stack_p)
> +    {
> +      rtx temp = assign_stack_temp (GET_MODE (op0),
> +                                 GET_MODE_SIZE (GET_MODE (op0)));
> +      emit_move_insn (temp, op0);
> +      store_bit_field_1 (temp, bitsize, bitnum, 0, 0, fieldmode, value,
> +                      reverse, fallback_p, undefined_p);
> +      emit_move_insn (op0, temp);
> +      return true;
> +    }
>  
>    return store_integral_bit_field (op0, op0_mode, ibitsize, ibitnum,
>                                  bitregion_start, bitregion_end,
> @@ -1008,7 +1025,9 @@ store_integral_bit_field (rtx op0, opt_scalar_int_mode 
> op0_mode,
>             in BLKmode to handle unaligned memory references and to shift the
>             last chunk right on big-endian machines if need be.  */
>         rtx value_word
> -         = fieldmode == BLKmode
> +         = (fieldmode == BLKmode
> +            || maybe_lt ((unsigned) UNITS_PER_WORD,
> +                         REGMODE_NATURAL_SIZE (value_mode)))
>             ? extract_bit_field (value, new_bitsize, wordnum * BITS_PER_WORD,
>                                  1, NULL_RTX, word_mode, word_mode, false,
>                                  NULL)

Long-term, I think both store_bit_field and extract_bit_field should
be taught to split into REGMODE_NATURAL_SIZE chunks of op0.  The same
applies to users of operand_subword_force in general.

But clearly that would be a lot of work.  And we'd need the value_mode
check in store_integral_bit_field even then.

So IMO the expmed changes above are OK.

> @@ -1834,12 +1853,15 @@ extract_bit_field_1 (rtx str_rtx, poly_uint64 
> bitsize, poly_uint64 bitnum,
>       if we aren't.  */
>    opt_scalar_int_mode op0_mode = int_mode_for_mode (GET_MODE (op0));
>    scalar_int_mode imode;
> +  bool need_stack_p = false;
>    if (!op0_mode.exists (&imode) || imode != GET_MODE (op0))
>      {
>        if (MEM_P (op0))
>       op0 = adjust_bitfield_address_size (op0, op0_mode.else_blk (),
>                                           0, MEM_SIZE (op0));
> -      else if (op0_mode.exists (&imode))
> +      else if (op0_mode.exists (&imode)
> +            && known_ge ((unsigned) UNITS_PER_WORD,
> +                         REGMODE_NATURAL_SIZE (GET_MODE (op0))))
>       {
>         op0 = gen_lowpart (imode, op0);
>  
> @@ -1849,12 +1871,21 @@ extract_bit_field_1 (rtx str_rtx, poly_uint64 
> bitsize, poly_uint64 bitnum,
>           op0 = force_reg (imode, op0);
>       }
>        else
> -     {
> -       poly_int64 size = GET_MODE_SIZE (GET_MODE (op0));
> -       rtx mem = assign_stack_temp (GET_MODE (op0), size);
> -       emit_move_insn (mem, op0);
> -       op0 = adjust_bitfield_address_size (mem, BLKmode, 0, size);
> -     }
> +     need_stack_p = true;
> +    }
> +  else
> +    {
> +      if (maybe_lt ((unsigned) UNITS_PER_WORD,
> +                 REGMODE_NATURAL_SIZE (GET_MODE (op0))))
> +     need_stack_p = true;
> +    }
> +
> +  if (need_stack_p)
> +    {
> +      poly_int64 size = GET_MODE_SIZE (GET_MODE (op0));
> +      rtx mem = assign_stack_temp (GET_MODE (op0), size);
> +      emit_move_insn (mem, op0);
> +      op0 = adjust_bitfield_address_size (mem, BLKmode, 0, size);
>      }
>  
>    /* ??? We currently assume TARGET is at least as big as BITSIZE.

I wonder how important it is in practice that we avoid forcing to memory
when the bitfield occupies the low word of a register.  That would be
relatively complex to test, though, and I think any time spent on that
would be better spent on making extract_integral_bit_field handle
REGMODE_NATURAL_SIZE properly.

So IMO the extract_bit_field_1 change is OK as well.

> diff --git a/gcc/expr.cc b/gcc/expr.cc
> index de73215ccc6..5e0c659cad6 100644
> --- a/gcc/expr.cc
> +++ b/gcc/expr.cc
> @@ -4173,8 +4173,20 @@ read_complex_part (rtx cplx, bool imag_p)
>        if (ret)
>          return ret;
>        else
> -     /* simplify_gen_subreg may fail for sub-word MEMs.  */
> -     gcc_assert (MEM_P (cplx) && ibitsize < BITS_PER_WORD);
> +     {
> +       /* Get the original reg we might be 'subreg'ing from.
> +          If that has a vector mode we can fall back to
> +          extract_bit_field.
> +
> +          For scalar modes, simplify_gen_subreg may fail for
> +          sub-word MEMs.  */
> +       rtx reg = cplx;
> +       if (SUBREG_P (reg))
> +         reg = SUBREG_REG (reg);
> +       /* simplify_gen_subreg may fail for sub-word MEMs.  */
> +       gcc_assert (VECTOR_MODE_P (GET_MODE (reg))
> +                   || (MEM_P (cplx) && ibitsize < BITS_PER_WORD));
> +     }
>      }
>  
>    return extract_bit_field (cplx, ibitsize, imag_p ? ibitsize : 0,

I'd prefer to remove the assert here.  I'm not sure what purpose
it serves.  There are multiple valid reasons for simplify_gen_subreg
to return null and extract_bit_field is the correct fallback when
it does.  extract_bit_field will itself assert if the combination
is really badly formed.

> @@ -4709,6 +4721,21 @@ emit_move_insn (rtx x, rtx y)
>        y = y_inner;
>        mode = GET_MODE (x_inner);
>      }
> +  /* If we don't have a move for MODE, emit_move_insn_1 will fall back to
> +     emit_move_multi_word.  The latter will call operand_subword_force,
> +     which calls emit_move_insn again, without making progress.
> +     Therefore, spill Y to memory and let the memory path below handle
> +     it.  */
> +  else if (optab_handler (mov_optab, mode) == CODE_FOR_nothing
> +        && y_inner && !MEM_P (x)
> +        && maybe_gt (REGMODE_NATURAL_SIZE (GET_MODE (y_inner)),
> +                     (unsigned) UNITS_PER_WORD))
> +    {
> +      rtx mem = assign_stack_temp (GET_MODE (y_inner),
> +                                GET_MODE_SIZE (GET_MODE (y_inner)));
> +      emit_move_insn (mem, y_inner);
> +      y = adjust_address (mem, mode, 0);
> +    }
>  
>    if (CONSTANT_P (y))
>      {

Not sure I understand this one.  Could you go into more detail?

Also, why is this keyed off y_inner being nonnull?  Does the non-subreg
case work correctly?  What about the case where y is a subreg but the
inner register is a different size (and thus y_inner is null)?

The "memory path below" just seems to "validize" the memories and
still falls through to emit_move_insn_1 in the end.

> diff --git a/gcc/lower-subreg.cc b/gcc/lower-subreg.cc
> index 5dee6a0b646..59b99c34554 100644
> --- a/gcc/lower-subreg.cc
> +++ b/gcc/lower-subreg.cc
> @@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "memmodel.h"
>  #include "tm_p.h"
>  #include "expmed.h"
> +#include "regs.h"
>  #include "insn-config.h"
>  #include "emit-rtl.h"
>  #include "recog.h"
> @@ -1636,7 +1637,13 @@ decompose_multiword_subregs (bool decompose_copies)
>        bitmap_clear (sub_blocks);
>  
>        EXECUTE_IF_SET_IN_BITMAP (decomposable_context, 0, regno, iter)
> -     decompose_register (regno);
> +     {
> +       if (maybe_lt
> +           ((unsigned) UNITS_PER_WORD,
> +            REGMODE_NATURAL_SIZE (GET_MODE (regno_reg_rtx[regno]))))
> +         continue;
> +       decompose_register (regno);
> +     }
>  
>        FOR_EACH_BB_FN (bb, cfun)
>       {

It's not obvious to me how safe it is to pull out at this stage.
Did you consider making interesting_mode_p return false instead?

Richard

Reply via email to