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?

Thanks in advance,
jeff


Reply via email to