| //@ add-minicore |
| //@ assembly-output: emit-asm |
| //@ compile-flags: --target xtensa-esp32-none-elf -Zmerge-functions=disabled |
| //@ min-llvm-version: 22 |
| //@ needs-llvm-components: xtensa |
| |
| #![feature(no_core, lang_items, rustc_attrs, repr_simd, asm_experimental_arch)] |
| #![crate_type = "rlib"] |
| #![no_core] |
| #![allow(asm_sub_register, non_camel_case_types)] |
| |
| extern crate minicore; |
| use minicore::*; |
| |
| type ptr = *mut u8; |
| |
| extern "C" { |
| fn extern_func(); |
| } |
| |
| // CHECK-LABEL: sym_fn: |
| // CHECK: #APP |
| // CHECK: call4 extern_func |
| // CHECK: #NO_APP |
| #[no_mangle] |
| pub unsafe fn sym_fn() { |
| asm!("call4 {}", sym extern_func); |
| } |
| |
| macro_rules! check_general_reg { |
| ($func:ident $ty:ident $class:ident $mov:literal) => { |
| #[no_mangle] |
| pub unsafe fn $func(x: $ty) -> $ty { |
| let y; |
| asm!(concat!($mov, " {}, {}"), out($class) y, in($class) x); |
| y |
| } |
| }; |
| } |
| |
| // CHECK-LABEL: reg_i8: |
| // CHECK: #APP |
| // CHECK: or a{{[0-9]+}}, a{{[0-9]+}}, a{{[0-9]+}} |
| // CHECK: #NO_APP |
| check_general_reg!(reg_i8 i8 reg "mov"); |
| |
| // CHECK-LABEL: reg_i16: |
| // CHECK: #APP |
| // CHECK: or a{{[0-9]+}}, a{{[0-9]+}}, a{{[0-9]+}} |
| // CHECK: #NO_APP |
| check_general_reg!(reg_i16 i16 reg "mov"); |
| |
| // CHECK-LABEL: reg_i32: |
| // CHECK: #APP |
| // CHECK: or a{{[0-9]+}}, a{{[0-9]+}}, a{{[0-9]+}} |
| // CHECK: #NO_APP |
| check_general_reg!(reg_i32 i32 reg "mov"); |
| |
| // CHECK-LABEL: reg_ptr: |
| // CHECK: #APP |
| // CHECK: or a{{[0-9]+}}, a{{[0-9]+}}, a{{[0-9]+}} |
| // CHECK: #NO_APP |
| check_general_reg!(reg_ptr ptr reg "mov"); |
| |
| macro_rules! check_explicit_reg { |
| ($func:ident $ty:ident $reg:tt $mov:literal) => { |
| #[no_mangle] |
| pub unsafe fn $func(x: $ty) -> $ty { |
| let y; |
| asm!(concat!($mov, " ", $reg, ", ", $reg), lateout($reg) y, in($reg) x); |
| y |
| } |
| }; |
| } |
| |
| // CHECK-LABEL: a2_i32: |
| // CHECK: #APP |
| // CHECK: or a2, a2, a2 |
| // CHECK: #NO_APP |
| check_explicit_reg!(a2_i32 i32 "a2" "mov"); |
| |
| // CHECK-LABEL: a5_i8: |
| // CHECK: #APP |
| // CHECK: or a5, a5, a5 |
| // CHECK: #NO_APP |
| check_explicit_reg!(a5_i8 i8 "a5" "mov"); |
| |
| // CHECK-LABEL: a5_i16: |
| // CHECK: #APP |
| // CHECK: or a5, a5, a5 |
| // CHECK: #NO_APP |
| check_explicit_reg!(a5_i16 i16 "a5" "mov"); |
| |
| // CHECK-LABEL: a5_i32: |
| // CHECK: #APP |
| // CHECK: or a5, a5, a5 |
| // CHECK: #NO_APP |
| check_explicit_reg!(a5_i32 i32 "a5" "mov"); |
| |
| // CHECK-LABEL: a5_ptr: |
| // CHECK: #APP |
| // CHECK: or a5, a5, a5 |
| // CHECK: #NO_APP |
| check_explicit_reg!(a5_ptr ptr "a5" "mov"); |
| |
| // CHECK-LABEL: a14_i32: |
| // CHECK: #APP |
| // CHECK: or a14, a14, a14 |
| // CHECK: #NO_APP |
| check_explicit_reg!(a14_i32 i32 "a14" "mov"); |
| |
| // a15 is the frame pointer under CALL0, but usable under the windowed ABI |
| // (this test target). |
| // CHECK-LABEL: a15_i32: |
| // CHECK: #APP |
| // CHECK: or a15, a15, a15 |
| // CHECK: #NO_APP |
| check_explicit_reg!(a15_i32 i32 "a15" "mov"); |
| |
| // CHECK-LABEL: f0_f32: |
| // CHECK: #APP |
| // CHECK: mov.s f0, f0 |
| // CHECK: #NO_APP |
| check_explicit_reg!(f0_f32 f32 "f0" "mov.s"); |
| |
| // CHECK-LABEL: f7_f32: |
| // CHECK: #APP |
| // CHECK: mov.s f7, f7 |
| // CHECK: #NO_APP |
| check_explicit_reg!(f7_f32 f32 "f7" "mov.s"); |
| |
| // CHECK-LABEL: f15_f32: |
| // CHECK: #APP |
| // CHECK: mov.s f15, f15 |
| // CHECK: #NO_APP |
| check_explicit_reg!(f15_f32 f32 "f15" "mov.s"); |
| |
| // Special/Boolean registers are clobber-only. |
| macro_rules! check_clobber { |
| ($func:ident $reg:tt $insn:literal) => { |
| #[no_mangle] |
| pub unsafe fn $func(x: i32) { |
| asm!($insn, in(reg) x, out($reg) _); |
| } |
| }; |
| } |
| |
| // CHECK-LABEL: sar_clobber: |
| // CHECK: #APP |
| // CHECK: wsr a{{[0-9]+}}, sar |
| // CHECK: #NO_APP |
| check_clobber!(sar_clobber "sar" "wsr {0}, sar"); |
| |
| // CHECK-LABEL: scompare1_clobber: |
| // CHECK: #APP |
| // CHECK: wsr a{{[0-9]+}}, scompare1 |
| // CHECK: #NO_APP |
| check_clobber!(scompare1_clobber "scompare1" "wsr {0}, scompare1"); |
| |
| // CHECK-LABEL: lbeg_clobber: |
| // CHECK: #APP |
| // CHECK: wsr a{{[0-9]+}}, lbeg |
| // CHECK: #NO_APP |
| check_clobber!(lbeg_clobber "lbeg" "wsr {0}, lbeg"); |
| |
| // CHECK-LABEL: lend_clobber: |
| // CHECK: #APP |
| // CHECK: wsr a{{[0-9]+}}, lend |
| // CHECK: #NO_APP |
| check_clobber!(lend_clobber "lend" "wsr {0}, lend"); |
| |
| // CHECK-LABEL: lcount_clobber: |
| // CHECK: #APP |
| // CHECK: wsr a{{[0-9]+}}, lcount |
| // CHECK: #NO_APP |
| check_clobber!(lcount_clobber "lcount" "wsr {0}, lcount"); |
| |
| // CHECK-LABEL: acclo_clobber: |
| // CHECK: #APP |
| // CHECK: wsr a{{[0-9]+}}, acclo |
| // CHECK: #NO_APP |
| check_clobber!(acclo_clobber "acclo" "wsr {0}, acclo"); |
| |
| // CHECK-LABEL: acchi_clobber: |
| // CHECK: #APP |
| // CHECK: wsr a{{[0-9]+}}, acchi |
| // CHECK: #NO_APP |
| check_clobber!(acchi_clobber "acchi" "wsr {0}, acchi"); |
| |
| // CHECK-LABEL: m0_clobber: |
| // CHECK: #APP |
| // CHECK: wsr a{{[0-9]+}}, m0 |
| // CHECK: #NO_APP |
| check_clobber!(m0_clobber "m0" "wsr {0}, m0"); |
| |
| // CHECK-LABEL: m3_clobber: |
| // CHECK: #APP |
| // CHECK: wsr a{{[0-9]+}}, m3 |
| // CHECK: #NO_APP |
| check_clobber!(m3_clobber "m3" "wsr {0}, m3"); |
| |
| // Boolean registers are bits of BR; write them via `andb`. |
| macro_rules! check_breg_clobber { |
| ($func:ident $reg:tt) => { |
| #[no_mangle] |
| pub unsafe fn $func() { |
| asm!(concat!("andb ", $reg, ", ", $reg, ", ", $reg), out($reg) _); |
| } |
| }; |
| } |
| |
| // CHECK-LABEL: b0_clobber: |
| // CHECK: #APP |
| // CHECK: andb b0, b0, b0 |
| // CHECK: #NO_APP |
| check_breg_clobber!(b0_clobber "b0"); |
| |
| // CHECK-LABEL: b7_clobber: |
| // CHECK: #APP |
| // CHECK: andb b7, b7, b7 |
| // CHECK: #NO_APP |
| check_breg_clobber!(b7_clobber "b7"); |
| |
| // CHECK-LABEL: b15_clobber: |
| // CHECK: #APP |
| // CHECK: andb b15, b15, b15 |
| // CHECK: #NO_APP |
| check_breg_clobber!(b15_clobber "b15"); |