Skip to content

Inline assembly syntax rejected only in a module asm block by LLVM on a Windows host while being accepted on a Linux host #162043

@Fulgen301

Description

@Fulgen301

This is rust-lang/rust#147267

Rust lowers naked functions to a module asm block - the following Rust code

#[unsafe(no_mangle)]
extern "C" fn foo() {
    unsafe {
        core::arch::asm!("mov rax, QWORD PTR gs:[60h]");
    }
}

#[unsafe(naked)]
#[unsafe(no_mangle)]
extern "C" fn foo2() {
    core::arch::naked_asm!("mov rax, QWORD PTR gs:[60h]")
}

ends up as

LLVM IR

; ModuleID = 'test.881695139e95559a-cgu.0'
source_filename = "test.881695139e95559a-cgu.0"
target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-windows-msvc"

module asm ".intel_syntax"
module asm ".pushsection .text.foo2,\22xr\22"
module asm ".balign 4"
module asm ".globl foo2"
module asm ".def foo2"
module asm ".scl 2"
module asm ".type 32"
module asm ".endef"
module asm "foo2:"
module asm "mov rax, QWORD PTR gs:[60h]"
module asm ".popsection"
module asm ""
module asm ".att_syntax"

@vtable.0 = private unnamed_addr constant <{ [24 x i8], ptr, ptr, ptr }> <{ [24 x i8] c"\00\00\00\00\00\00\00\00\08\00\00\00\00\00\00\00\08\00\00\00\00\00\00\00", ptr @"_ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17hd1c75b492168a55dE", ptr @"_ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17h4262cf38e2e29668E", ptr @"_ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17h4262cf38e2e29668E" }>, align 8

; std::rt::lang_start
; Function Attrs: uwtable
define hidden i64 @_ZN3std2rt10lang_start17h433cd31365191fd0E(ptr %main, i64 %argc, ptr %argv, i8 %sigpipe) unnamed_addr #0 {
start:
  %_7 = alloca [8 x i8], align 8
  store ptr %main, ptr %_7, align 8
; call std::rt::lang_start_internal
  %_0 = call i64 @_ZN3std2rt19lang_start_internal17h4a182455c3ab4a29E(ptr align 1 %_7, ptr align 8 @vtable.0, i64 %argc, ptr %argv, i8 %sigpipe)
  ret i64 %_0
}

; std::rt::lang_start::{{closure}}
; Function Attrs: inlinehint uwtable
define internal i32 @"_ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17h4262cf38e2e29668E"(ptr align 8 %_1) unnamed_addr #1 {
start:
  %_4 = load ptr, ptr %_1, align 8
; call std::sys::backtrace::__rust_begin_short_backtrace
  call void @_ZN3std3sys9backtrace28__rust_begin_short_backtrace17hc9806c74f494fc26E(ptr %_4)
; call <() as std::process::Termination>::report
  %self = call i32 @"_ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17h264280877e7cdf26E"()
  ret i32 %self
}

; std::sys::backtrace::__rust_begin_short_backtrace
; Function Attrs: noinline uwtable
define internal void @_ZN3std3sys9backtrace28__rust_begin_short_backtrace17hc9806c74f494fc26E(ptr %f) unnamed_addr #2 {
start:
; call core::ops::function::FnOnce::call_once
  call void @_ZN4core3ops8function6FnOnce9call_once17h2ca7e55b5cd60158E(ptr %f)
  call void asm sideeffect "", "~{memory}"(), !srcloc !3
  ret void
}

; core::ops::function::FnOnce::call_once{{vtable.shim}}
; Function Attrs: inlinehint uwtable
define internal i32 @"_ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17hd1c75b492168a55dE"(ptr %_1) unnamed_addr #1 {
start:
  %_2 = alloca [0 x i8], align 1
  %0 = load ptr, ptr %_1, align 8
; call core::ops::function::FnOnce::call_once
  %_0 = call i32 @_ZN4core3ops8function6FnOnce9call_once17h3de95b96d12b004dE(ptr %0)
  ret i32 %_0
}

; core::ops::function::FnOnce::call_once
; Function Attrs: inlinehint uwtable
define internal void @_ZN4core3ops8function6FnOnce9call_once17h2ca7e55b5cd60158E(ptr %_1) unnamed_addr #1 {
start:
  %_2 = alloca [0 x i8], align 1
  call void %_1()
  ret void
}

; core::ops::function::FnOnce::call_once
; Function Attrs: inlinehint uwtable
define internal i32 @_ZN4core3ops8function6FnOnce9call_once17h3de95b96d12b004dE(ptr %0) unnamed_addr #1 personality ptr @__CxxFrameHandler3 {
start:
  %_2 = alloca [0 x i8], align 1
  %_1 = alloca [8 x i8], align 8
  store ptr %0, ptr %_1, align 8
; invoke std::rt::lang_start::{{closure}}
  %_0 = invoke i32 @"_ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17h4262cf38e2e29668E"(ptr align 8 %_1)
          to label %bb1 unwind label %funclet_bb3

bb3:                                              ; preds = %funclet_bb3
  cleanupret from %cleanuppad unwind to caller

funclet_bb3:                                      ; preds = %start
  %cleanuppad = cleanuppad within none []
  br label %bb3

bb1:                                              ; preds = %start
  ret i32 %_0
}

; <() as std::process::Termination>::report
; Function Attrs: inlinehint uwtable
define internal i32 @"_ZN54_$LT$$LP$$RP$$u20$as$u20$std..process..Termination$GT$6report17h264280877e7cdf26E"() unnamed_addr #1 {
start:
  ret i32 0
}

; Function Attrs: nounwind uwtable
define dso_local void @foo() unnamed_addr #3 {
start:
  call void asm sideeffect alignstack inteldialect "mov rax, QWORD PTR gs:[60h]", "~{dirflag},~{fpsr},~{flags},~{memory}"(), !srcloc !4
  ret void
}

; test::main
; Function Attrs: uwtable
define hidden void @_ZN4test4main17h31abd93402dadb48E() unnamed_addr #0 {
start:
  ret void
}

; std::rt::lang_start_internal
; Function Attrs: uwtable
declare i64 @_ZN3std2rt19lang_start_internal17h4a182455c3ab4a29E(ptr align 1, ptr align 8, i64, ptr, i8) unnamed_addr #0

declare i32 @__CxxFrameHandler3(...) unnamed_addr #4

; Function Attrs: noinline nounwind uwtable
declare void @foo2() unnamed_addr #5

define i32 @main(i32 %0, ptr %1) unnamed_addr #4 {
top:
  %2 = sext i32 %0 to i64
; call std::rt::lang_start
  %3 = call i64 @_ZN3std2rt10lang_start17h433cd31365191fd0E(ptr @_ZN4test4main17h31abd93402dadb48E, i64 %2, ptr %1, i8 0)
  %4 = trunc i64 %3 to i32
  ret i32 %4
}

attributes #0 = { uwtable "target-cpu"="x86-64" "target-features"="+cx16,+sse3,+sahf" }
attributes #1 = { inlinehint uwtable "target-cpu"="x86-64" "target-features"="+cx16,+sse3,+sahf" }
attributes #2 = { noinline uwtable "target-cpu"="x86-64" "target-features"="+cx16,+sse3,+sahf" }
attributes #3 = { nounwind uwtable "target-cpu"="x86-64" "target-features"="+cx16,+sse3,+sahf" }
attributes #4 = { "target-cpu"="x86-64" }
attributes #5 = { noinline nounwind uwtable "target-cpu"="x86-64" "target-features"="+cx16,+sse3,+sahf" }

!llvm.module.flags = !{!0, !1}
!llvm.ident = !{!2}

!0 = !{i32 8, !"PIC Level", i32 2}
!1 = !{i32 7, !"PIE Level", i32 2}
!2 = !{!"rustc version 1.90.0 (1159e78c4 2025-09-14)"}
!3 = !{i64 2898316946459614}
!4 = !{i64 433791696970

However, LLVM with x86_64-pc-windows-msvc as host tools does not accept mov rax, QWORD PTR gs:[60h] in module asm:

PS> rustc test.rs
error: unknown token in expression
   |
note: instantiated into assembly here
  --> <inline asm>:10:27
   |
10 | mov rax, QWORD PTR gs:[60h]
   |                           ^

error: aborting due to 1 previous error

PS> rustc --emit=llvm-ir test.rs && clang test.ll
warning: overriding the module target triple with x86_64-pc-windows-msvc19.44.35217 [-Woverride-module]
<inline asm>:10:27: error: unknown token in expression
mov rax, QWORD PTR gs:[60h]
                          ^
error: cannot compile inline asm
1 warning and 1 error generated

The error goes away if I pass -masm=intel to Clang, or use [0x96] instead of [60h]. The error also does not appear if the code is compiled with Linux host tools, such as on godbolt. It is also accepted by regular inline assembly (call void asm).

Reduced:

source_filename = "short.ll"
target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-windows-msvc"


module asm ".intel_syntax"
module asm ".pushsection .text"
module asm "foo2:"
module asm "mov rax, QWORD PTR gs:[60h]"
module asm ".popsection"
module asm ""
module asm ".att_syntax"

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions