RISC-V: Do not delete fused vsetvl if it has uses [PR119115].

In PR119115 we end up with an orphaned
	vsetvli	zero,t1,e16,m1,ta,ma.
t1 originally came from another vsetvl that was fused from
	vsetvli	a4,a3,e8,mf2,ta,ma
	vsetvli	t1,a3,e8,mf2,ta,ma   (1)
to
	vsetvli	zero,a3,e16,m1,ta,ma.

This patch checks if t1, the VL operand of (1), has AVL uses and does
not delete the vsetvl if so.  While doing so, it also wraps the search
for VL uses into two new functions reg_used and reg_single_use_in_avl.

	PR target/119115

gcc/ChangeLog:

	* config/riscv/riscv-vsetvl.cc (reg_used): New function.
	(reg_single_use_in_avl): Ditto.
	(pre_vsetvl::fuse_local_vsetvl_info): Use reg_single_use_in_avl
	when checking if vsetvl can be deleted.

gcc/testsuite/ChangeLog:

	* gcc.target/riscv/rvv/base/pr119115.c: New test.
This commit is contained in:
Robin Dapp 2025-03-05 18:16:57 +01:00
parent f043ef2b6a
commit 77ef91d715
2 changed files with 131 additions and 23 deletions

View file

@ -780,6 +780,36 @@ enum class avl_demand_type : unsigned
ignore_avl = demand_flags::DEMAND_EMPTY_P,
};
/* Go through all uses of INSN looking for a single use of register REG.
Return true if we find
- Uses in a non-RVV insn
- More than one use in an RVV insn
- A single use in the VL operand of an RVV insn
and false otherwise.
A single use in the AVL operand does not count as use as we take care of
those separately in the pass. */
static bool
reg_used (insn_info *insn, rtx reg)
{
unsigned int regno = REGNO (reg);
const hash_set<use_info *> vl_uses = get_all_real_uses (insn, regno);
for (use_info *use : vl_uses)
{
gcc_assert (use->insn ()->is_real ());
rtx_insn *rinsn = use->insn ()->rtl ();
if (!has_vl_op (rinsn)
|| count_regno_occurrences (rinsn, regno) != 1)
return true;
rtx avl = ::get_avl (rinsn);
if (!avl || !REG_P (avl) || regno != REGNO (avl))
return true;
}
return false;
}
class vsetvl_info
{
private:
@ -1142,27 +1172,7 @@ public:
/* Determine if dest operand(vl) has been used by non-RVV instructions. */
if (dest_vl)
{
const hash_set<use_info *> vl_uses
= get_all_real_uses (get_insn (), REGNO (dest_vl));
for (use_info *use : vl_uses)
{
gcc_assert (use->insn ()->is_real ());
rtx_insn *rinsn = use->insn ()->rtl ();
if (!has_vl_op (rinsn)
|| count_regno_occurrences (rinsn, REGNO (dest_vl)) != 1)
{
m_vl_used_by_non_rvv_insn = true;
break;
}
rtx avl = ::get_avl (rinsn);
if (!avl || !REG_P (avl) || REGNO (dest_vl) != REGNO (avl))
{
m_vl_used_by_non_rvv_insn = true;
break;
}
}
}
m_vl_used_by_non_rvv_insn = reg_used (get_insn (), dest_vl);
/* Collect the read vl insn for the fault-only-first rvv loads. */
if (fault_first_load_p (insn->rtl ()))
@ -1369,6 +1379,35 @@ public:
void set_empty_info () { global_info.set_empty (); }
};
/* Same as REG_USED () but looks for a single use in an RVV insn's AVL
operand. */
static bool
reg_single_use_in_avl (insn_info *insn, rtx reg)
{
if (!reg)
return false;
unsigned int regno = REGNO (reg);
const hash_set<use_info *> vl_uses = get_all_real_uses (insn, regno);
for (use_info *use : vl_uses)
{
gcc_assert (use->insn ()->is_real ());
rtx_insn *rinsn = use->insn ()->rtl ();
if (!has_vl_op (rinsn)
|| count_regno_occurrences (rinsn, regno) != 1)
return false;
vsetvl_info info = vsetvl_info (use->insn ());
if (!info.has_nonvlmax_reg_avl ())
return false;
rtx avl = info.get_avl ();
if (avl && REG_P (avl) && regno == REGNO (avl))
return true;
}
return false;
}
/* Demand system is the RVV-based VSETVL info analysis tools wrapper.
It defines compatible rules for SEW/LMUL, POLICY and AVL.
Also, it provides 3 interfaces available_p, compatible_p and
@ -2797,8 +2836,18 @@ pre_vsetvl::fuse_local_vsetvl_info ()
64 into 32. */
prev_info.set_max_sew (
MIN (prev_info.get_max_sew (), curr_info.get_max_sew ()));
if (!curr_info.vl_used_by_non_rvv_insn_p ()
&& vsetvl_insn_p (curr_info.get_insn ()->rtl ()))
/* If we fuse and the current, to be deleted vsetvl has uses
of its VL as AVL operand in other vsetvls those will be
orphaned. Therefore only delete if that's not the case.
*/
rtx cur_dest = curr_info.has_vl ()
? curr_info.get_vl ()
: NULL_RTX;
if (vsetvl_insn_p (curr_info.get_insn ()->rtl ())
&& !reg_single_use_in_avl (curr_info.get_insn (),
cur_dest))
m_delete_list.safe_push (curr_info);
if (curr_info.get_read_vl_insn ())

View file

@ -0,0 +1,59 @@
/* { dg-do run } */
/* { dg-require-effective-target rv64 } */
/* { dg-require-effective-target rvv_zvl256b_ok } */
/* { dg-additional-options "-march=rv64gcv_zvl256b -mabi=lp64d -O3 -fsigned-char -fwrapv -mrvv-vector-bits=zvl" } */
short a[4][14][14];
void
b (short d, short e, _Bool f, short g, int h, int i, char j, int k, int l,
short m[][14], char n[], unsigned short o[][14][14], short p[])
{
for (unsigned q = 0; q < (char) i; q++)
for (unsigned char r = 0; r < (char) (-1748723647U * m[q][q]) - 20; r++)
a[q][q][r] = p[q] || o[q][q][r];
}
__attribute__ ((noipa))
void
check (long long val)
{
if (val != 0)
__builtin_abort ();
}
long long seed;
short d;
short e;
_Bool f;
short g;
int h;
int i = 15963650;
char j;
int k;
int l;
short m[4][14];
char n[4];
unsigned short o[4][14][14];
short p[4];
int
main ()
{
for (int q = 0; q < 4; ++q)
{
p[q] = 4084;
for (int r = 0; r < 4; ++r)
{
m[q][r] = 24482;
for (int u = 0; u < 4; ++u)
a[q][r][u] = 81;
}
}
b (d, e, f, g, h, i, j, k, l, m, n, o, p);
for (int q = 0; q < 4; ++q)
for (int r = 0; r < 4; ++r)
for (int u = 0; u < 14; ++u)
seed ^= a[q][r][u] + (seed >> 2);
check (seed);
}