Skip to content

Inline assembly register reuse weirdness #20106

@canndrew

Description

@canndrew

I'm not sure whether this is a bug or whether it's just my own ignorance when it comes to writing inline assembly but I'm having issues passing rust values into assembly code. I'm using Linux x86_64 and I've written a simple program that calls the exit syscall with the value 23

#![feature(asm)]

fn main() {
  unsafe {
    asm!(
      "
      mov $$60, %rax
      mov $$23, %rdi
      syscall
      "
      :
      :
      : "%rax", "%rdi"
    );
  }
}

This program works fine, I can rustc exit.rs; ./exit; echo $? and get 23. Now I want to make the asm code interface with rust. So I pass the 23 in from rust rather than hardcoding it in the assembly.

#![feature(asm)]

fn main() {
  unsafe {
    asm!(
      "
      mov $$60, %rax
      mov $0, %rdi
      syscall
      "
      :
      : "r"(23)
      : "%rax", "%rdi"
    );
  }
}

Once again, this works. Now I try to pass 23 in using a variable.

#![feature(asm)]

fn main() {
  let x = 23i;
  unsafe {
    asm!(
      "
      mov $$60, %rax
      mov $0, %rdi
      syscall
      "
      :
      : "r"(x)
      : "%rax", "%rdi"
    );
  }
}

And this breaks. It exits with 60 rather than 23. Looking at the generated assembly shows why:

movq    $23, (%rsp)
movq    (%rsp), %rax
#APP

movq    $60, %rax
movq    %rax, %rdi
syscall

The compiler tries to save 23 into %rax but then %rax gets overwritten by 60 in the inline assembly. So is this a compiler bug? Or I am supposed to specifically tell rustc not to use %rax for for x? If it's useful, the LLVM IR for the failing code is:

; ModuleID = 'exit.0.rs'
target datalayout = "e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

; Function Attrs: uwtable
define internal void @_ZN4main20h2dd3717eeddb6da6eaaE() unnamed_addr #0 {
entry-block:
  %x = alloca i64
  store i64 23, i64* %x
  %0 = load i64* %x
  call void asm "\0A      mov $$60, %rax\0A      mov $0, %rdi\0A      syscall\0A      ", "r,~{%rax},~{%rdi},~{dirflag},~{fpsr},~{flags}"(i64 %0), !srcloc !0
  ret void
}

define i64 @main(i64, i8**) unnamed_addr #1 {
top:
  %2 = call i64 @_ZN2rt10lang_start20h6ebacfb5a732c9b9PfyE(i8* bitcast (void ()* @_ZN4main20h2dd3717eeddb6da6eaaE to i8*), i64 %0, i8** %1)
  ret i64 %2
}

declare i64 @_ZN2rt10lang_start20h6ebacfb5a732c9b9PfyE(i8*, i64, i8**) unnamed_addr #1

attributes #0 = { uwtable "split-stack" }
attributes #1 = { "split-stack" }

!0 = metadata !{i32 21}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions