https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127841

            Bug ID: 127841
           Summary: m68k: subdi_dishl32 emits memory-to-memory sub.l
           Product: gcc
           Version: 16.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: target
          Assignee: unassigned at gcc dot gnu.org
          Reporter: bernie at codewiz dot org
  Target Milestone: ---

The subdi_dishl32 pattern in m68k.md accepts a memory destination together
with a memory source:

    (define_insn "subdi_dishl32"
      [(set (match_operand:DI 0 "nonimmediate_operand" "+ro")
        (minus:DI (match_dup 0)
            (ashift:DI (match_operand:DI 1 "general_operand" "ro")
                (const_int 32))))]

This can output a memory-to-memory sub.l, which m68k doesn't support.


Seen with the m68k-amigaos port of GCC 16.2, which carries the pattern
unchanged:

    $ cat t.c
    void f (unsigned long long *p, unsigned long long *q) { *p -= *q << 32; }
    $ m68k-amigaos-gcc -O2 -c t.c
    Error: operands mismatch -- statement `sub.l 4(a1),(a0)' ignored

It fails with every -mcpu (68000 to 68060). With register pressure it also
shows up as `sub.l 56(sp),44(sp)` on stack slots; that is how a user hit it
in real code: https://github.com/AmigaPorts/m68k-amigaos-gcc/issues/129

adddi_dishl32, the matching addition pattern, already avoids this with two
alternatives: a register destination takes any source, a memory destination
needs a data register source.

The proposed fix gives subdi_dishl32 the same alternatives:

    --- a/gcc/config/m68k/m68k.md
    +++ b/gcc/config/m68k/m68k.md
    @@ (define_insn "subdi_dishl32"
    -  [(set (match_operand:DI 0 "nonimmediate_operand" "+ro")
    +  [(set (match_operand:DI 0 "nonimmediate_operand" "+r,o")
         (minus:DI (match_dup 0)
    -        (ashift:DI (match_operand:DI 1 "general_operand" "ro")
    +        (ashift:DI (match_operand:DI 1 "general_operand" "ro,d")
                 (const_int 32))))]

With it, f compiles to

    move.l 4(sp),a0
    move.l 8(sp),a1
    move.l (a1),d0
    move.l 4(a1),d1
    sub.l d1,(a0)
    rts

(the load of the unused high word into d0 is a missed optimization, not a
correctness issue).

Proposed testcase:

    /* { dg-do run } */
    /* { dg-options "-O2" } */
    extern void abort (void);
    __attribute__((noipa)) void
    f (unsigned long long *p, unsigned long long *q)
    {
      *p -= *q << 32;
    }
    int
    main (void)
    {
      unsigned long long a = 0x0000000500000007ULL;
      unsigned long long b = 0x1234567800000002ULL;
      f (&a, &b);
      if (a != 0x0000000300000007ULL)
        abort ();
      return 0;
    }

Initial reported created by arczi84 on the m68k-amigaos-gcc tracker:
https://github.com/AmigaPorts/m68k-amigaos-gcc/issues/129

Reply via email to