[PATCH/RFC] combine: Tweak the condition of last_set invalidation

Kewen.Lin linkw@linux.ibm.com
Fri Jan 15 08:06:17 GMT 2021


Hi Segher,

Thanks for the comments!

on 2021/1/15 上午8:22, Segher Boessenkool wrote:
> Hi!
> 
> On Wed, Dec 16, 2020 at 04:49:49PM +0800, Kewen.Lin wrote:
>> When I was investigating unsigned int vec_init issue on Power,
>> I happened to find there seems something we can enhance in how
>> combine pass invalidate last_set (set last_set_invalid nonzero).
>>
>> Currently we have the check:
>>
>>       if (!insn
>> 	  || (value && rsp->last_set_table_tick >= label_tick_ebb_start))
>> 	rsp->last_set_invalid = 1; 
>>
>> which means if we want to record some value for some reg and
>> this reg got refered before in a valid scope, we invalidate the
>> set of reg (last_set_invalid to 1).  It avoids to find the wrong
>> set for one reg reference, such as the case like:
>>
>>    ... op regX  // this regX could find wrong last_set below
>>    regX = ...   // if we think this set is valid
>>    ... op regX
>>
>> But because of retry's existence, the last_set_table_tick could
> 
> It is not just because of retry: combine can change other insns than
> just i2 and i3, too.  And even changing i2 requires this!
> 

Ah, thanks for the information!  Here retry is one example for that
we can revisit one instruction but meanwhile the stored information
for reg reference can be from that instruction after the current
one but visited before.

> The whole reg_stat stuff is an ugly hack that does not work well.  For
> example, as in your example, some "known" value can be invalidated
> before the combination that wants to know that value is tried.
> 
> We need to have this outside of combine, in a dataflow(-like) thing
> for example.  This could take the place of REG_EQ* as well probably
> (which is good, there are various problems with that as well).
> 

Good point, but IIUC we still need to keep updating(tracking)
information like what we put into reg_stat stuff, it's not static
since as you pointed out above, combine can change i2/i3 etc,
we need to update the information for the changes.  Anyway, it's not
what this patch tries to solve.  :-P

>> This proposal is to check whether the last_set_table safely happens
>> after the current set, make the set still valid if so.
> 
> I don't think this is safe to do like this, unfortunately.  There are
> more places that set last_set_invalid (well, one more), so at the very
> minimum this needs a lot more justification.
> 

Let me try to explain it more.
* Background *

There are two places which set last_set_invalid to 1. 

CASE 1:

```
  if (CALL_P (insn))
    {
      HARD_REG_SET callee_clobbers
	= insn_callee_abi (insn).full_and_partial_reg_clobbers ();
      hard_reg_set_iterator hrsi;
      EXECUTE_IF_SET_IN_HARD_REG_SET (callee_clobbers, 0, i, hrsi)
	{
	  reg_stat_type *rsp;

	  /* ??? We could try to preserve some information from the last
	     set of register I if the call doesn't actually clobber
	     (reg:last_set_mode I), which might be true for ABIs with
	     partial clobbers.  However, it would be difficult to
	     update last_set_nonzero_bits and last_sign_bit_copies
	     to account for the part of I that actually was clobbered.
	     It wouldn't help much anyway, since we rarely see this
	     situation before RA.  */
	  rsp = &reg_stat[i];
	  rsp->last_set_invalid = 1;
```

The justification for this part is that if ABI defines some callee
clobberred registers, we need to invalidate their sets to avoid
later references to use the unexpected values.

CASE 2:

```
  for (i = regno; i < endregno; i++)
    {
      rsp = &reg_stat[i];
      rsp->last_set_label = label_tick;
      if (!insn
	  || (value && rsp->last_set_table_tick >= label_tick_ebb_start))
	rsp->last_set_invalid = 1;
      else
	rsp->last_set_invalid = 0;
    }
```

The justification here is that: if the insn is NULL, it's simply to
invalidate this reg set, go for it; if the value is NULL, it's simply
to say the reg clobberred, invalidate it; if the reference of this
reg being set have been seen, let's invalidate it.

The last part follows the comments:

  /* Now update the status of each register being set.
     If someone is using this register in this block, set this register
     to invalid since we will get confused between the two lives in this
     basic block.  This makes using this register always invalid.  In cse, we
     scan the table to invalidate all entries using this register, but this
     is too much work for us.  */

It's understandable to invalidate it to avoid the case:

   ... op regX    // (a)
   regX = ...     // (b)
   ... op regX    // (c)
 
When we are revisiting (a), it avoids to use the reg set in (b).

* Problem *

Firstly, the problem that this patch is trying to solve is mainly related
to case 2, so it doesn't touch CASE 1.

In the context of CASE 2, the current condition

  (rsp->last_set_table_tick >= label_tick_ebb_start)

is completely safe but too conservative to over-kill some case as below:

   regX = ...     // (d)
   ... op regX    // (e)

At the beginning, the visiting sequence looks like: (d) -> (e),
assuming (d) joins in some successful combination, then re-visiting (d). 
At that time of re-visiting (d), the last_set_table_tick has been set
according to (e) since we found regX's reference when visiting (e) before.
Then it will invalidate the set for regX.

This set (d) is actually safe since the recorded last_set_table_tick is
from one later reference (e) after this set.

* Solution *

The patch is trying to distinguish some safe cases like (d)(e)(d),
the basic points are:

  1) obtain the earliest reference of set reg.
     
     We have one assertion that the later references should have
     the same or higher label_tick, so if we already have one
     lower label_tick, we won't update the last_set_table_tick.

  2) record the luid of the earliest reference.
     
     When we meet one reference, if the label_tick are the same
     as the kept one, check the luid and choose the earliest one.

     If both label_ticks of reg set and reg ref are different, we
     have to take it as unsafe conservatively.  If they are the
     same, we can simply compare the luid to ensure the reg set
     happens before reg ref.

BR,
Kewen


More information about the Gcc-patches mailing list