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