| // Compiler: | |
| // | |
| // Run-time: | |
| // status: 0 | |
| #[cfg(target_arch = "x86_64")] | |
| use std::arch::{asm, global_asm}; | |
| #[cfg(target_arch = "x86_64")] | |
| global_asm!( | |
| " | |
| .global add_asm | |
| add_asm: | |
| mov rax, rdi | |
| add rax, rsi | |
| ret" | |
| ); | |
| #[cfg(target_arch = "x86_64")] | |
| extern "C" { | |
| fn add_asm(a: i64, b: i64) -> i64; | |
| } | |
| #[cfg(target_arch = "x86_64")] | |
| pub unsafe fn mem_cpy(dst: *mut u8, src: *const u8, len: usize) { | |
| asm!( | |
| "rep movsb", | |
| inout("rdi") dst => _, | |
| inout("rsi") src => _, | |
| inout("rcx") len => _, | |
| options(preserves_flags, nostack) | |
| ); | |
| } | |
| #[cfg(target_arch = "x86_64")] | |
| fn asm() { | |
| unsafe { | |
| asm!("nop"); | |
| } | |
| let x: u64; | |
| unsafe { | |
| asm!("mov $5, {}", | |
| out(reg) x, | |
| options(att_syntax) | |
| ); | |
| } | |
| assert_eq!(x, 5); | |
| let x: u64; | |
| let input: u64 = 42; | |
| unsafe { | |
| asm!("mov {input}, {output}", | |
| "add $1, {output}", | |
| input = in(reg) input, | |
| output = out(reg) x, | |
| options(att_syntax) | |
| ); | |
| } | |
| assert_eq!(x, 43); | |
| let x: u64; | |
| unsafe { | |
| asm!("mov {}, 6", | |
| out(reg) x, | |
| ); | |
| } | |
| assert_eq!(x, 6); | |
| let x: u64; | |
| let input: u64 = 42; | |
| unsafe { | |
| asm!("mov {output}, {input}", | |
| "add {output}, 1", | |
| input = in(reg) input, | |
| output = out(reg) x, | |
| ); | |
| } | |
| assert_eq!(x, 43); | |
| // check inout(reg_class) x | |
| let mut x: u64 = 42; | |
| unsafe { | |
| asm!("add {0}, {0}", | |
| inout(reg) x | |
| ); | |
| } | |
| assert_eq!(x, 84); | |
| // check inout("reg") x | |
| let mut x: u64 = 42; | |
| unsafe { | |
| asm!("add r11, r11", | |
| inout("r11") x | |
| ); | |
| } | |
| assert_eq!(x, 84); | |
| // check a mix of | |
| // in("reg") | |
| // inout(class) x => y | |
| // inout (class) x | |
| let x: u64 = 702; | |
| let y: u64 = 100; | |
| let res: u64; | |
| let mut rem: u64 = 0; | |
| unsafe { | |
| asm!("div r11", | |
| in("r11") y, | |
| inout("eax") x => res, | |
| inout("edx") rem, | |
| ); | |
| } | |
| assert_eq!(res, 7); | |
| assert_eq!(rem, 2); | |
| // check const | |
| let mut x: u64 = 42; | |
| unsafe { | |
| asm!("add {}, {}", | |
| inout(reg) x, | |
| const 1 | |
| ); | |
| } | |
| assert_eq!(x, 43); | |
| // check const (ATT syntax) | |
| let mut x: u64 = 42; | |
| unsafe { | |
| asm!("add ${}, {}", | |
| const 1, | |
| inout(reg) x, | |
| options(att_syntax) | |
| ); | |
| } | |
| assert_eq!(x, 43); | |
| // check sym fn | |
| extern "C" fn foo() -> u64 { | |
| 42 | |
| } | |
| let x: u64; | |
| unsafe { | |
| asm!("call {}", sym foo, lateout("rax") x); | |
| } | |
| assert_eq!(x, 42); | |
| // check sym fn (ATT syntax) | |
| let x: u64; | |
| unsafe { | |
| asm!("call {}", sym foo, lateout("rax") x, options(att_syntax)); | |
| } | |
| assert_eq!(x, 42); | |
| // check sym static | |
| static FOO: u64 = 42; | |
| let x: u64; | |
| unsafe { | |
| asm!("mov {1}, qword ptr [rip + {0}]", sym FOO, lateout(reg) x); | |
| } | |
| assert_eq!(x, 42); | |
| // check sym static (ATT syntax) | |
| let x: u64; | |
| unsafe { | |
| asm!("movq {0}(%rip), {1}", sym FOO, lateout(reg) x, options(att_syntax)); | |
| } | |
| assert_eq!(x, 42); | |
| assert_eq!(unsafe { add_asm(40, 2) }, 42); | |
| let array1 = [1u8, 2, 3]; | |
| let mut array2 = [0u8, 0, 0]; | |
| unsafe { | |
| mem_cpy(array2.as_mut_ptr(), array1.as_ptr(), 3); | |
| } | |
| assert_eq!(array1, array2); | |
| // in and clobber registers cannot overlap. This tests that the lateout register without an | |
| // output place (indicated by the `_`) is not added to the list of clobbered registers | |
| let x = 8; | |
| let y: i32; | |
| unsafe { | |
| asm!( | |
| "mov rax, rdi", | |
| in("rdi") x, | |
| lateout("rdi") _, | |
| out("rax") y, | |
| ); | |
| } | |
| assert_eq!((x, y), (8, 8)); | |
| // sysv64 is the default calling convention on unix systems. The rdi register is | |
| // used to pass arguments in the sysv64 calling convention, so this register will be clobbered | |
| #[cfg(unix)] | |
| { | |
| let x = 16; | |
| let y: i32; | |
| unsafe { | |
| asm!( | |
| "mov rax, rdi", | |
| in("rdi") x, | |
| out("rax") y, | |
| clobber_abi("sysv64"), | |
| ); | |
| } | |
| assert_eq!((x, y), (16, 16)); | |
| } | |
| // the `b` suffix for registers in the `reg_byte` register class is not supported in GCC | |
| // and needs to be stripped in order to use these registers. | |
| unsafe { | |
| core::arch::asm!( | |
| "", | |
| out("al") _, | |
| out("cl") _, | |
| out("dl") _, | |
| out("sil") _, | |
| out("dil") _, | |
| out("r8b") _, | |
| out("r9b") _, | |
| out("r10b") _, | |
| out("r11b") _, | |
| out("r12b") _, | |
| out("r13b") _, | |
| out("r14b") _, | |
| out("r15b") _, | |
| ); | |
| } | |
| } | |
| #[cfg(not(target_arch = "x86_64"))] | |
| fn asm() {} | |
| fn main() { | |
| asm(); | |
| } |