access-softek / llvm-project

Other
0 stars 0 forks source link

Inefficient argument passing even at -Os optimization level #30

Closed atrosinenko closed 11 months ago

atrosinenko commented 4 years ago

When some arguments of a function should be placed on a stack (see Sections 3.3.3 and 3.3.4 of MSP430 EABI for example), LLVM generates correct but inefficient instruction sequences inside a calling function. Consider the following program based on examples from EABI:

void f(int x, long y, long z);
void g(int x, long long y);

extern int i;
extern long j, k;
extern long long ll;

void call_f(void) {
  f(i, j, k);
}

void call_g(void) {
  g(i, ll);
}

When compiled with GCC with -Os -S options, it produces (with non-instruction lines omitted):

call_f:
        SUB.W   #2, R1
        MOV.W   &k+2, @R1
        MOV.W   &k, R15
        MOV.W   &j, R13
        MOV.W   &j+2, R14
        MOV.W   &i, R12
        CALL    #f
        ADD.W   #2, R1
        RET
call_g:
        SUB.W   #8, R1
        MOV.W   &ll, @R1
        MOV.W   &ll+2, 2(R1)
        MOV.W   &ll+4, 4(R1)
        MOV.W   &ll+6, 6(R1)
        MOV.W   &i, R12
        CALL    #g
        ADD.W   #8, R1
        RET

And LLVM:

call_f:
        sub     #2, r1
        mov     &k, r15
        mov     &j+2, r14
        mov     &j, r13
        mov     &i, r12
        mov     &k+2, r11
        mov     r11, 0(r1)
        call    #f
        add     #2, r1
        ret
call_g:
        sub     #8, r1
        mov     &i, r12
        mov     &ll, r13
        mov     &ll+2, r14
        mov     &ll+4, r15
        mov     &ll+6, r11
        mov     r11, 6(r1)
        mov     r15, 4(r1)
        mov     r14, 2(r1)
        mov     r13, 0(r1)
        call    #g
        add     #8, r1
        ret

Disassembly for GCC:

00000000 call_f:
       0: 21 83                         decd    r1
       2: 91 42 00 00 00 00             mov     &0, 0(r1)
       8: 1f 42 00 00                   mov     &0, r15
       c: 1d 42 00 00                   mov     &0, r13
      10: 1e 42 00 00                   mov     &0, r14
      14: 1c 42 00 00                   mov     &0, r12
      18: b0 12 00 00                   call    #0
      1c: 21 53                         incd    r1
      1e: 30 41                         ret

00000020 call_g:
      20: 31 82                         sub     #8, r1
      22: 91 42 00 00 00 00             mov     &0, 0(r1)
      28: 91 42 00 00 02 00             mov     &0, 2(r1)
      2e: 91 42 00 00 04 00             mov     &0, 4(r1)
      34: 91 42 00 00 06 00             mov     &0, 6(r1)
      3a: 1c 42 00 00                   mov     &0, r12
      3e: b0 12 00 00                   call    #0
      42: 31 52                         add     #8, r1
      44: 30 41                         ret

And for LLVM:

00000000 call_f:
       0: 31 80 02 00                   sub     #2, r1
       4: 1f 42 00 00                   mov     &0, r15
       8: 1e 42 00 00                   mov     &0, r14
       c: 1d 42 00 00                   mov     &0, r13
      10: 1c 42 00 00                   mov     &0, r12
      14: 1b 42 00 00                   mov     &0, r11
      18: 81 4b 00 00                   mov     r11, 0(r1)
      1c: b0 12 00 00                   call    #0
      20: 31 50 02 00                   add     #2, r1
      24: 30 41                         ret

00000026 call_g:
      26: 31 80 08 00                   sub     #8, r1
      2a: 1c 42 00 00                   mov     &0, r12
      2e: 1d 42 00 00                   mov     &0, r13
      32: 1e 42 00 00                   mov     &0, r14
      36: 1f 42 00 00                   mov     &0, r15
      3a: 1b 42 00 00                   mov     &0, r11
      3e: 81 4b 06 00                   mov     r11, 6(r1)
      42: 81 4f 04 00                   mov     r15, 4(r1)
      46: 81 4e 02 00                   mov     r14, 2(r1)
      4a: 81 4d 00 00                   mov     r13, 0(r1)
      4e: b0 12 00 00                   call    #0
      52: 31 50 08 00                   add     #8, r1
      56: 30 41                         ret

That is, addr -> r11 + r11 -> 6(r1) takes 2*4 bytes while addr -> 6(r1) takes 6 bytes only.

atrosinenko commented 4 years ago

Meanwhile, LLVM seems to generate better code for trivial f() and g() themselves while GCC seems to always emit prologue and epilogue separately. :)