cse: Fix handling of fake vec_select sets [PR111702]
commit08ab2293dcce442e139b163ba22f65a32e7ebd6c
authorRichard Sandiford <richard.sandiford@arm.com>
Wed, 20 Dec 2023 19:07:35 +0000 (20 19:07 +0000)
committerRichard Sandiford <richard.sandiford@arm.com>
Wed, 20 Dec 2023 19:07:35 +0000 (20 19:07 +0000)
treef8181c4ffb329514fba5c643c315390a0dcc0fa5
parentd07d0e992232d668ba6c27e15433f7616a69e5b5
cse: Fix handling of fake vec_select sets [PR111702]

If cse sees:

  (set (reg R) (const_vector [A B ...]))

it creates fake sets of the form:

  (set R[0] A)
  (set R[1] B)
  ...

(with R[n] replaced by appropriate rtl) and then adds them to the tables
in the same way as for normal sets.  This allows a sequence like:

  (set (reg R2) A)
  ...(reg R2)...

to try to use R[0] instead of (reg R2).

But the pass was taking the analogy too far, and was trying to simplify
these fake sets based on costs.  That is, if there was an earlier:

  (set (reg T) A)

the pass would go to considerable effort trying to work out whether:

  (set R[0] A)

or:

  (set R[0] (reg T))

was more profitable.  This included running validate*_change on the sets,
which has no meaning given that the sets are not part of the insn.

In this example, the equivalence A == T is already known, and the
purpose of the fake sets is to add A == T == R[0].  We can do that
just as easily (or, as the PR shows, more easily) if we keep the
original form of the fake set, with A instead of T.

The problem in the PR occurred if we had:

(1) something that establishes an equivalence between a vector V1 of
    M-bit scalar integers and a hard register H

(2) something that establishes an equivalence between a vector V2 of
    N-bit scalar integers, where N<M and where V2 contains at least 2
    instances of V1[0]

(1) established an equivalence between V1[0] and H in M bits.
(2) then triggered a search for an equivalence of V1[0] in N bits.
This included:

      /* See if we have a CONST_INT that is already in a register in a
 wider mode.  */

which (correctly) found that the low N bits of H contain the right value.
But because it came from a wider mode, this equivalence between N-bit H
and N-bit V1[0] was not yet in the hash table.  It therefore survived
the purge in:

      /* At this point, ELT, if nonzero, points to a class of expressions
         equivalent to the source of this SET and SRC, SRC_EQV, SRC_FOLDED,
 and SRC_RELATED, if nonzero, each contain additional equivalent
 expressions.  Prune these latter expressions by deleting expressions
 already in the equivalence class.

And since more than 1 set found the same N-bit equivalence between
H and V1[0], the pass tried to add it more than once.

Things were already wrong at this stage, but an ICE was only triggered
later when trying to merge this N-bit equivalence with another one.

We could avoid the double registration by adding:

  for (elt = classp; elt; elt = elt->next_same_value)
    if (rtx_equal_p (elt->exp, x))
      return elt;

to insert_with_costs, or by making cse_insn check whether previous
sets have recorded the same equivalence.  The latter seems more
appealing from a compile-time perspective.  But in this case,
doing that would be adding yet more spurious work to the handling
of fake sets.

The handling of fake sets therefore seems like the more fundamental bug.

While there, the patch also makes sure that we don't apply REG_EQUAL
notes to these fake sets.  They only describe the "real" (first) set.

gcc/
PR rtl-optimization/111702
* cse.cc (set::mode): Move earlier.
(set::src_in_memory, set::src_volatile): Convert to bitfields.
(set::is_fake_set): New member variable.
(add_to_set): Add an is_fake_set parameter.
(find_sets_in_insn): Update calls accordingly.
(cse_insn): Do not apply REG_EQUAL notes to fake sets.  Do not
try to optimize them either, or validate changes to them.

gcc/testsuite/
PR rtl-optimization/111702
* gcc.dg/rtl/aarch64/pr111702.c: New test.
gcc/cse.cc
gcc/testsuite/gcc.dg/rtl/aarch64/pr111702.c [new file with mode: 0644]