asm goto miscompilation · Issue #74483 · llvm/llvm-project (original) (raw)

As far as I understand asm goto now supports output arguments on all paths. If I am reading this correctly, in this case asm goto corrupts local state (output variable has incorrect value). The code is:

#include <stdio.h>

long foo(long a, long b) {
    long x, y, z;
    asm goto(
      "movq %[a], %[x]; testq %[a], %[a]; jnz %l[label1]"
        : [x] "=&r"(x), [y] "=&r"(y), [z] "=&r"(z)
        : [a]"r"(a), [b]"r"(b)
        : "memory" : label1, label2);
    return 0;
    {
        label1:
        return x;
    }
    label2:
    return 1;
}

int main() {
  printf("%lu\n", foo(11, 22));
}
foo(long, long):                               # @foo(long, long)
        pushq   %rbp
        movq    %rsp, %rbp
        movq    %rdi, -16(%rbp)
        movq    %rsi, -24(%rbp)
        movq    -16(%rbp), %rsi
        movq    -24(%rbp), %rdi
        movq    %rsi, %rax
        testq   %rsi, %rsi
        jne     .LBB0_2
        movq    %rdx, -96(%rbp)                 # 8-byte Spill
        movq    %rcx, -88(%rbp)                 # 8-byte Spill
        movq    %rax, -80(%rbp)                 # 8-byte Spill
        movq    %rdx, -72(%rbp)                 # 8-byte Spill
        movq    %rcx, -64(%rbp)                 # 8-byte Spill
        movq    %rax, -56(%rbp)                 # 8-byte Spill
        jmp     .LBB0_1
.LBB0_1:
        movq    -72(%rbp), %rax                 # 8-byte Reload
        movq    -64(%rbp), %rcx                 # 8-byte Reload
        movq    -56(%rbp), %rdx                 # 8-byte Reload
        movq    %rdx, -32(%rbp)
        movq    %rcx, -40(%rbp)
        movq    %rax, -48(%rbp)
        movq    $0, -8(%rbp)
        jmp     .LBB0_6
.LBB0_2:                                # Block address taken
        movq    %rdx, -96(%rbp)                 # 8-byte Spill
        movq    -80(%rbp), %rdx                 # 8-byte Reload
        movq    %rcx, -88(%rbp)                 # 8-byte Spill
        movq    -88(%rbp), %rcx                 # 8-byte Reload
        movq    %rax, -80(%rbp)                 # 8-byte Spill
        movq    -96(%rbp), %rax                 # 8-byte Reload
        movq    %rdx, -32(%rbp)
        movq    %rcx, -40(%rbp)
        movq    %rax, -48(%rbp)
        jmp     .LBB0_4
.LBB0_3:                                # Block address taken
        movq    %rdx, -96(%rbp)                 # 8-byte Spill
        movq    -80(%rbp), %rdx                 # 8-byte Reload
        movq    %rcx, -88(%rbp)                 # 8-byte Spill
        movq    -88(%rbp), %rcx                 # 8-byte Reload
        movq    %rax, -80(%rbp)                 # 8-byte Spill
        movq    -96(%rbp), %rax                 # 8-byte Reload
        movq    %rdx, -32(%rbp)
        movq    %rcx, -40(%rbp)
        movq    %rax, -48(%rbp)
        jmp     .LBB0_5
.LBB0_4:
        movq    -32(%rbp), %rax
        movq    %rax, -8(%rbp)
        jmp     .LBB0_6
.LBB0_5:
        movq    $1, -8(%rbp)
.LBB0_6:
        movq    -8(%rbp), %rax
        popq    %rbp
        retq

If we track the x variable, it's spilled to -80(%rbp), but then loaded from -32(%rbp):

foo(long, long):                               # @foo(long, long)
...
        movq    %rsi, %rax
...
        jne     .LBB0_2
...
.LBB0_2:                                # Block address taken
...
        movq    %rax, -80(%rbp)                 # 8-byte Spill
...
        jmp     .LBB0_4
...
.LBB0_4:
        movq    -32(%rbp), %rax
        movq    %rax, -8(%rbp)
        jmp     .LBB0_6
...
.LBB0_6:
        movq    -8(%rbp), %rax
        popq    %rbp
        retq