// no-system-llvm
// revisions: x86_64 i686
// assembly-output: emit-asm
// compile-flags: -O
//[x86_64] compile-flags: --target x86_64-unknown-linux-gnu
//[i686] compile-flags: --target i686-unknown-linux-gnu
// compile-flags: -C llvm-args=--x86-asm-syntax=intel
// compile-flags: -C target-feature=+avx512bw

#![feature(no_core, lang_items, rustc_attrs)]
#![crate_type = "rlib"]
#![no_core]
#![allow(asm_sub_register)]

#[rustc_builtin_macro]
macro_rules! asm {
    () => {};
}
#[rustc_builtin_macro]
macro_rules! concat {
    () => {};
}
#[rustc_builtin_macro]
macro_rules! stringify {
    () => {};
}

#[lang = "sized"]
trait Sized {}
#[lang = "copy"]
trait Copy {}

impl Copy for i32 {}

macro_rules! check {
    ($func:ident $modifier:literal $reg:ident $mov:literal) => {
        // -O and extern "C" guarantee that the selected register is always ax/xmm0
        #[no_mangle]
        pub unsafe extern "C" fn $func() -> i32 {
            // Hack to avoid function merging
            extern "Rust" {
                fn dont_merge(s: &str);
            }
            dont_merge(stringify!($func));

            let y;
            asm!(concat!($mov, " {0:", $modifier, "}, {0:", $modifier, "}"), out($reg) y);
            y
        }
    };
}

// CHECK-LABEL: reg:
// CHECK: #APP
// x86_64: mov rax, rax
// i686: mov eax, eax
// CHECK: #NO_APP
check!(reg "" reg "mov");

// x86_64-LABEL: reg_l:
// x86_64: #APP
// x86_64: mov al, al
// x86_64: #NO_APP
#[cfg(x86_64)]
check!(reg_l "l" reg "mov");

// CHECK-LABEL: reg_x:
// CHECK: #APP
// CHECK: mov ax, ax
// CHECK: #NO_APP
check!(reg_x "x" reg "mov");

// CHECK-LABEL: reg_e:
// CHECK: #APP
// CHECK: mov eax, eax
// CHECK: #NO_APP
check!(reg_e "e" reg "mov");

// x86_64-LABEL: reg_r:
// x86_64: #APP
// x86_64: mov rax, rax
// x86_64: #NO_APP
#[cfg(x86_64)]
check!(reg_r "r" reg "mov");

// CHECK-LABEL: reg_abcd:
// CHECK: #APP
// x86_64: mov rax, rax
// i686: mov eax, eax
// CHECK: #NO_APP
check!(reg_abcd "" reg_abcd "mov");

// CHECK-LABEL: reg_abcd_l:
// CHECK: #APP
// CHECK: mov al, al
// CHECK: #NO_APP
check!(reg_abcd_l "l" reg_abcd "mov");

// CHECK-LABEL: reg_abcd_h:
// CHECK: #APP
// CHECK: mov ah, ah
// CHECK: #NO_APP
check!(reg_abcd_h "h" reg_abcd "mov");

// CHECK-LABEL: reg_abcd_x:
// CHECK: #APP
// CHECK: mov ax, ax
// CHECK: #NO_APP
check!(reg_abcd_x "x" reg_abcd "mov");

// CHECK-LABEL: reg_abcd_e:
// CHECK: #APP
// CHECK: mov eax, eax
// CHECK: #NO_APP
check!(reg_abcd_e "e" reg_abcd "mov");

// x86_64-LABEL: reg_abcd_r:
// x86_64: #APP
// x86_64: mov rax, rax
// x86_64: #NO_APP
#[cfg(x86_64)]
check!(reg_abcd_r "r" reg_abcd "mov");

// CHECK-LABEL: xmm_reg
// CHECK: #APP
// CHECK: movaps xmm0, xmm0
// CHECK: #NO_APP
check!(xmm_reg "" xmm_reg "movaps");

// CHECK-LABEL: xmm_reg_x
// CHECK: #APP
// CHECK: movaps xmm0, xmm0
// CHECK: #NO_APP
check!(xmm_reg_x "x" xmm_reg "movaps");

// CHECK-LABEL: xmm_reg_y
// CHECK: #APP
// CHECK: vmovaps ymm0, ymm0
// CHECK: #NO_APP
check!(xmm_reg_y "y" xmm_reg "vmovaps");

// CHECK-LABEL: xmm_reg_z
// CHECK: #APP
// CHECK: vmovaps zmm0, zmm0
// CHECK: #NO_APP
check!(xmm_reg_z "z" xmm_reg "vmovaps");

// CHECK-LABEL: ymm_reg
// CHECK: #APP
// CHECK: movaps ymm0, ymm0
// CHECK: #NO_APP
check!(ymm_reg "" ymm_reg "vmovaps");

// CHECK-LABEL: ymm_reg_x
// CHECK: #APP
// CHECK: movaps xmm0, xmm0
// CHECK: #NO_APP
check!(ymm_reg_x "x" ymm_reg "movaps");

// CHECK-LABEL: ymm_reg_y
// CHECK: #APP
// CHECK: vmovaps ymm0, ymm0
// CHECK: #NO_APP
check!(ymm_reg_y "y" ymm_reg "vmovaps");

// CHECK-LABEL: ymm_reg_z
// CHECK: #APP
// CHECK: vmovaps zmm0, zmm0
// CHECK: #NO_APP
check!(ymm_reg_z "z" ymm_reg "vmovaps");

// CHECK-LABEL: zmm_reg
// CHECK: #APP
// CHECK: movaps zmm0, zmm0
// CHECK: #NO_APP
check!(zmm_reg "" zmm_reg "vmovaps");

// CHECK-LABEL: zmm_reg_x
// CHECK: #APP
// CHECK: movaps xmm0, xmm0
// CHECK: #NO_APP
check!(zmm_reg_x "x" zmm_reg "movaps");

// CHECK-LABEL: zmm_reg_y
// CHECK: #APP
// CHECK: vmovaps ymm0, ymm0
// CHECK: #NO_APP
check!(zmm_reg_y "y" zmm_reg "vmovaps");

// CHECK-LABEL: zmm_reg_z
// CHECK: #APP
// CHECK: vmovaps zmm0, zmm0
// CHECK: #NO_APP
check!(zmm_reg_z "z" zmm_reg "vmovaps");

// Note: we don't have any way of ensuring that k1 is actually the register
// chosen by the register allocator, so this check may fail if a different
// register is chosen.

// CHECK-LABEL: kreg:
// CHECK: #APP
// CHECK: kmovb k1, k1
// CHECK: #NO_APP
check!(kreg "" kreg "kmovb");
