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

            Bug ID: 85185
           Summary: Wider-than-expected load for struct member used as
                    operand of inline-asm with memory clobber at -Og
           Product: gcc
           Version: 7.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: inline-asm
          Assignee: unassigned at gcc dot gnu.org
          Reporter: zev+gccbug at bewilderbeest dot net
  Target Milestone: ---

Created attachment 43832
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=43832&action=edit
Minimized reproducer

In the attached test case, compiling with -Og generates a wider memory access
than expected (4 bytes instead of 2, on the LP64 targets I'm dealing with) to
load sub.a on line 12.

--
$ nl -ba min.c
     1  struct sub {
     2    short a, b;
     3  };
     4
     5  struct {
     6    int i;
     7    struct sub P;
     8  } S;
     9
    10  static inline void inner(struct sub sub)
    11  {
    12    asm("magic %0" :: "r" (sub.a) : "memory");
    13  }
    14
    15  void outer(void)
    16  {
    17    inner(S.P);
    18  }
$ gcc -S -o - -Og min.c
        .file   "min.c"
        .text
        .globl  outer
        .type   outer, @function
outer:
.LFB1:
        .cfi_startproc
        movl    4+S(%rip), %eax
#APP
# 12 "min.c" 1
        magic %ax
# 0 "" 2
#NO_APP
        ret
        .cfi_endproc
.LFE1:
        .size   outer, .-outer
        .comm   S,8,8
        .ident  "GCC: (GNU) 7.3.0"
        .section        .note.GNU-stack,"",@progbits
--

On x86-64 it ends up being relatively harmless because it uses a 16-bit
register (%ax) as the actual operand used in the instruction, but on targets
that only have full-width registers (riscv64 being the one at hand for me) this
results in the instruction receiving a very different operand than desired
(containing additional bytes from the neighboring struct member):

--
$ riscv64-unknown-elf-gcc -S -o - -Og min.c
        .file   "min.c"
        .option nopic
        .text
        .align  2
        .globl  outer
        .type   outer, @function
outer:
        addi    sp,sp,-16
        lui     a5,%hi(S)
        addi    a5,a5,%lo(S)
        lw      a5,4(a5)
        sw      a5,8(sp)
 #APP
# 17 "min.c" 1
        magic a5
# 0 "" 2
 #NO_APP
        addi    sp,sp,16
        jr      ra
        .size   outer, .-outer
        .comm   S,8,8
        .ident  "GCC: (GNU) 7.2.0"
--

This only occurs when compiling with -Og; -O0, -O1, -O2, and -O3 all generate
the expected two-byte load (movzwl instead of movl on x86-64, lh or lhu instead
of lw on riscv64).  I've also found via a bit of haphazard guess-based
experimentation that adding -ftree-sra to -Og produces a two-byte load as well.

I've observed these same patterns (4-byte load at -Og, 2-byte load at all other
-O levels or with -ftree-sra) on a number of different GCCs:

 - x86-64: 4.8.2 (Red Hat), 5.1.0 (source), 5.4.0 (Ubuntu), 7.2.0 (source),
7.3.0 (Void)
 - riscv64: 6.1.0 and 7.2.0 (both from source; I realize the former predates
official upstream riscv support but is perhaps illustrative nonetheless)

I've filed this on release 7.2.0 because that's the version where this is
actually biting me -- and as mentioned above, is only actually causing problems
on riscv, but the very similar code generation on x86 makes me suspect the
underlying problem is target-independent (and has apparently existed for a
while).

Reply via email to