Sorry for the slow reply!

Jeff Law <jeffreya...@gmail.com> writes:
> We don't really have a good place for discussions anymore other than 
> gcc-patches, but this really isn't a patch..  Oh well.
>
>
> I'm debugging a failure of ext-dce on a big endian target (m68k) and I 
> can't help but think it's actually exposed a latent bug in subreg 
> handling.  Your input would be appreciated.
>
>
> So we have this insn:
>
>> (insn 14 13 15 2 (set (reg:DI 55 [ _6 ])
>>         (zero_extend:DI (subreg:SI (reg:DI 35 [ _5 ]) 0))) "j.c":21:27 85 
>> {*zero_extendsidi2}
>>      (expr_list:REG_DEAD (reg:DI 35 [ _5 ])
>>         (nil)))
> So bits 32..63 from (reg:DI 35) and shove them into bits 0..31 of 
> (reg:DI 55) with zero extension.
>
> ext-dce has correctly determined that we only need bits 0..31 of (reg:DI 
> 55).  It it conceptually wants to transform that into:
>
> (set (reg:DI 55) (subreg:DI (subreg:SI (reg:DI 35) 0) 0))
>
> Of course we don't want nested subregs, so we actually use 
> simplify_gen_subreg.
>
> It's simplifying that to...
>
> (set (reg:DI 55) (reg:DI 35))
>
> Which seems wrong for the SET_SRC expression.
>
> This code looks suspicious to me (simplify-rtx.cc):
>
>
>
>>   /* Changing mode twice with SUBREG => just change it once,
>>      or not at all if changing back op starting mode.  */
>>   if (GET_CODE (op) == SUBREG)
>>     {
>>       machine_mode innermostmode = GET_MODE (SUBREG_REG (op));
>>       poly_uint64 innermostsize = GET_MODE_SIZE (innermostmode);
>>       rtx newx;
>> 
>>       /* Make sure that the relationship between the two subregs is
>>          known at compile time.  */
>>       if (!ordered_p (outersize, innermostsize))
>>         return NULL_RTX;
>> 
>>       if (outermode == innermostmode
>>           && known_eq (byte, 0U)
>>           && known_eq (SUBREG_BYTE (op), 0))
>>         return SUBREG_REG (op);
>
>
> That test just seems wrong on a big endian target.  If fact if I take 
> out that early exit and let the rest of the code run we get into this:
>
>>       /* Work out the memory offset of the final OUTERMODE value relative
>>          to the inner value of OP.  */
>>       poly_int64 mem_offset = subreg_memory_offset (outermode,
>>                                                     innermode, byte);
>>       poly_int64 op_mem_offset = subreg_memory_offset (op);
>>       poly_int64 final_offset = mem_offset + op_mem_offset;
>> 
>>       /* See whether resulting subreg will be paradoxical.  */
>>       if (!paradoxical_subreg_p (outermode, innermostmode))
>>         {
>>           /* Bail out in case resulting subreg would be incorrect.  */
>>           if (maybe_lt (final_offset, 0)
>>               || maybe_ge (poly_uint64 (final_offset), innermostsize)
>>               || !multiple_p (final_offset, outersize))
>>             return NULL_RTX;
>>         }
>
> OUTERMODE and INNERMOSTMODE are both DImode, so it's not paradoxical at 
> this point.  But FINAL_OFFSET is -4, which triggers rejecting the 
> simplification.
>
> Am I totally offbase here?   Or should we just not have called 
> gen_simplify_subreg in the way we did?

No, I agree it looks like a bug.  Seems like:

       if (outermode == innermostmode
           && known_eq (byte, 0U)
           && known_eq (SUBREG_BYTE (op), 0))
         return SUBREG_REG (op);

should be testing for subreg_lowpart_offset in both cases.

Richard

Reply via email to