//@ only-aarch64 //@ only-linux //@ needs-asm-support //@ run-pass #![feature(thread_local)] use std::arch::asm; extern "C" fn f1() -> i32 { 111 } // The compiler will generate a shim to hide the caller location parameter. #[track_caller] fn f2() -> i32 { 222 } macro_rules! call { ($func:path) => { unsafe { let result: i32; asm!("bl {}", sym $func, out("w0") result, out("x20") _, out("x21") _, out("x22") _, out("x23") _, out("x24") _, out("x25") _, out("x26") _, out("x27") _, out("x28") _, ); result } } } macro_rules! static_addr { ($s:expr) => { unsafe { let result: *const u32; asm!( // ADRP gives the address of a 4KB page from a PC-relative address "adrp {out}, {sym}", // We then add the remaining lower 12 bits "add {out}, {out}, #:lo12:{sym}", out = out(reg) result, sym = sym $s); result } } } macro_rules! static_tls_addr { ($s:expr) => { unsafe { let result: *const u32; asm!( // Load the thread pointer register "mrs {out}, TPIDR_EL0", // Add the top 12 bits of the symbol's offset "add {out}, {out}, :tprel_hi12:{sym}", // And the bottom 12 bits "add {out}, {out}, :tprel_lo12_nc:{sym}", out = out(reg) result, sym = sym $s ); result } } } static S1: u32 = 111; #[thread_local] static S2: u32 = 222; fn main() { assert_eq!(call!(f1), 111); assert_eq!(call!(f2), 222); assert_eq!(static_addr!(S1), &S1 as *const u32); assert_eq!(static_tls_addr!(S2), &S2 as *const u32); std::thread::spawn(|| { assert_eq!(static_addr!(S1), &S1 as *const u32); assert_eq!(static_tls_addr!(S2), &S2 as *const u32); }) .join() .unwrap(); }