Convert llvm_asm to asm

Summary:
The `llvm_asm` nightly feature was removed in Rust 1.59 and `asm!()` was simultaneously stabilized, so we need to make the switch so things continue compiling.

This retains the `llvm_asm` code so that it can continue compiling on old llvm versions. This will be removed when that is updated.

Reviewed By: johnhurt

Differential Revision: D34561246

fbshipit-source-id: 42cd2c7e83cea9a5c21bd5871463bb1a42be139d
This commit is contained in:
Jason White 2022-03-10 12:05:42 -08:00 committed by Facebook GitHub Bot
parent 75e875fe28
commit 193965dfe5
6 changed files with 143 additions and 15 deletions

View file

@ -36,7 +36,7 @@
#![feature(async_closure)] #![feature(async_closure)]
#![feature(internal_output_capture)] #![feature(internal_output_capture)]
#![feature(never_type)] #![feature(never_type)]
#![feature(llvm_asm)] #![cfg_attr(feature = "llvm_asm", feature(llvm_asm))]
#![feature(map_first_last)] #![feature(map_first_last)]
#![feature(bench_black_box)] #![feature(bench_black_box)]

View file

@ -569,10 +569,30 @@ macro_rules! ret_without_perf {
/// Perform exactly `count+1` conditional branch instructions. Useful for /// Perform exactly `count+1` conditional branch instructions. Useful for
/// testing timer-related code. /// testing timer-related code.
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
#[cfg(not(feature = "llvm_asm"))]
#[inline(never)]
pub fn do_branches(mut count: u64) {
// Anything but assembly is unreliable between debug and release
unsafe {
// Loop until carry flag is set, indicating underflow
core::arch::asm!(
"2:",
"sub {0}, 1",
"jnc 2b",
inout(reg) count,
)
}
assert_eq!(count, u64::MAX);
}
/// Perform exactly `count+1` conditional branch instructions. Useful for
/// testing timer-related code.
#[cfg(target_arch = "x86_64")]
#[cfg(feature = "llvm_asm")]
#[inline(never)] #[inline(never)]
pub fn do_branches(count: u64) { pub fn do_branches(count: u64) {
// Anything but assembly is unreliable between debug and release // Anything but assembly is unreliable between debug and release
// TODO: Switch to `asm!()` when our LLVM version supports it.
#[allow(deprecated)] #[allow(deprecated)]
unsafe { unsafe {
// Loop until carry flag is set, indicating underflow // Loop until carry flag is set, indicating underflow

View file

@ -13,7 +13,6 @@ use core::mem;
use perf_event_open_sys::bindings as perf; use perf_event_open_sys::bindings as perf;
use raw_cpuid::{CpuId, FeatureInfo}; use raw_cpuid::{CpuId, FeatureInfo};
use reverie::Errno; use reverie::Errno;
use std::hint::black_box;
use thiserror::Error; use thiserror::Error;
use tracing::{error, warn}; use tracing::{error, warn};
@ -68,6 +67,7 @@ pub(crate) enum PmuValidationError {
#[error("Intel Kvm-In-Txcp bug found")] #[error("Intel Kvm-In-Txcp bug found")]
IntelKvmInTxcpBugDetected, IntelKvmInTxcpBugDetected,
#[cfg(feature = "llvm_asm")]
#[error("Overcount triggered by PMU interrupts detected due to Xen PMU virtualization bug")] #[error("Overcount triggered by PMU interrupts detected due to Xen PMU virtualization bug")]
IntelXenPmiBugDetected, IntelXenPmiBugDetected,
} }
@ -361,7 +361,7 @@ fn is_amd_zen(cpu_feature: FeatureInfo) -> bool {
/// This is a transcription of the function with the same name in Mozilla-RR it will /// This is a transcription of the function with the same name in Mozilla-RR it will
/// check for bugs specific to cpu architectures /// check for bugs specific to cpu architectures
fn check_for_arch_bugs(precise_ip: bool) -> Result<(), PmuValidationError> { fn check_for_arch_bugs(_precise_ip: bool) -> Result<(), PmuValidationError> {
let c = CpuId::new(); let c = CpuId::new();
let vendor = c.get_vendor_info().unwrap(); let vendor = c.get_vendor_info().unwrap();
let feature_info = c let feature_info = c
@ -371,7 +371,12 @@ fn check_for_arch_bugs(precise_ip: bool) -> Result<(), PmuValidationError> {
match vendor_str { match vendor_str {
AMD_VENDOR if is_amd_zen(feature_info) => check_for_zen_speclockmap(), AMD_VENDOR if is_amd_zen(feature_info) => check_for_zen_speclockmap(),
INTEL_VENDOR => check_for_kvm_in_txcp_bug().and(check_for_xen_pmi_bug(precise_ip)), INTEL_VENDOR => {
check_for_kvm_in_txcp_bug()?;
#[cfg(feature = "llvm_asm")]
check_for_xen_pmi_bug(_precise_ip)?;
Ok(())
}
s => panic!("Unknown CPU vendor: {}", s), s => panic!("Unknown CPU vendor: {}", s),
} }
} }
@ -395,6 +400,18 @@ fn check_for_zen_speclockmap() -> Result<(), PmuValidationError> {
let count = read_counter(&fd)?; let count = read_counter(&fd)?;
// A lock add is known to increase the perf counter we're looking at. // A lock add is known to increase the perf counter we're looking at.
#[cfg(not(feature = "llvm_asm"))]
unsafe {
let _prev: usize;
core::arch::asm!(
"lock",
"xadd [{}], {}",
in(reg) val,
inout(reg) to_add => _prev,
)
}
#[cfg(feature = "llvm_asm")]
#[allow(deprecated)] #[allow(deprecated)]
unsafe { unsafe {
let _prev: usize; let _prev: usize;
@ -437,6 +454,8 @@ fn check_for_kvm_in_txcp_bug() -> Result<(), PmuValidationError> {
} }
} }
// FIXME: Convert this big block of llvm_asm over to the new asm syntax.
#[cfg(feature = "llvm_asm")]
fn check_for_xen_pmi_bug(precise_ip: bool) -> Result<(), PmuValidationError> { fn check_for_xen_pmi_bug(precise_ip: bool) -> Result<(), PmuValidationError> {
#[allow(unused_assignments)] #[allow(unused_assignments)]
let mut count: i32 = -1; let mut count: i32 = -1;
@ -451,7 +470,7 @@ fn check_for_xen_pmi_bug(precise_ip: bool) -> Result<(), PmuValidationError> {
20764791 20764791
} }
let mut accumulator = black_box(make_accumulator_seed()); let mut accumulator = core::hint::black_box(make_accumulator_seed());
let mut expected_accumulator = accumulator; let mut expected_accumulator = accumulator;
// reproduce the assembly here to calculate what the final accumulator value should be // reproduce the assembly here to calculate what the final accumulator value should be

View file

@ -6,7 +6,7 @@
* This source code is licensed under the BSD-style license found in the * This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
#![feature(llvm_asm)] #![cfg_attr(feature = "llvm_asm", feature(llvm_asm))]
//! Basic tests that don't fall into some other category. //! Basic tests that don't fall into some other category.
@ -221,12 +221,27 @@ fn i_should_segfault() {
fn i_should_segfault_2() { fn i_should_segfault_2() {
use nix::sys::signal::Signal::SIGSEGV; use nix::sys::signal::Signal::SIGSEGV;
use reverie_ptrace::testing::test_fn; use reverie_ptrace::testing::test_fn;
let (output, _) = test_fn::<NoopTool, _>(|| unsafe {
#[inline]
#[cfg(not(feature = "llvm_asm"))]
unsafe fn do_segfault() {
let null_ptr: *const usize = core::ptr::null();
asm!(
"jmp {0}",
in(reg) null_ptr,
)
}
#[inline]
#[cfg(feature = "llvm_asm")]
#[allow(deprecated)]
unsafe fn do_segfault() {
llvm_asm!(r#"mov $$0, %rax llvm_asm!(r#"mov $$0, %rax
jmpq *%rax jmpq *%rax
"#:::"rax") "#:::"rax")
}) }
.unwrap();
let (output, _) = test_fn::<NoopTool, _>(|| unsafe { do_segfault() }).unwrap();
assert_eq!(output.status, ExitStatus::Signaled(SIGSEGV, true),); assert_eq!(output.status, ExitStatus::Signaled(SIGSEGV, true),);
} }

View file

@ -6,7 +6,7 @@
* This source code is licensed under the BSD-style license found in the * This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
#![feature(llvm_asm)] #![cfg_attr(feature = "llvm_asm", feature(llvm_asm))]
// when we convert syscall, such as open -> openat, the old syscall // when we convert syscall, such as open -> openat, the old syscall
// args should not be clobbered, even with the conversion. // args should not be clobbered, even with the conversion.
@ -61,7 +61,64 @@ mod tests {
use reverie_ptrace::testing::check_fn; use reverie_ptrace::testing::check_fn;
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
#[cfg(not(feature = "llvm_asm"))]
#[allow(unused_mut)] #[allow(unused_mut)]
unsafe fn open_syscall_sanity_check() -> i32 {
let path = b"/dev/null\0".as_ptr() as usize;
let flags: usize = 0x8000; // O_LARGEFILE
let mode: usize = 0o644;
let mut ret: usize;
// // The following asm block does this:
// let ret = open("/dev/null", 0x8000, 0644);
// if ret >= -4095 as u64 { exit_group(1) }
// // Sanity check input registers to ensure they didn't change.
// if %rsi != 0x8000 { exit_group(1) }
// if %rdx != 0644 { exit_roup(1) }
// return fd
core::arch::asm!(
"mov r8, {arg1}",
"syscall",
// if (ret >= -4095 as u64) goto 1
"cmp 0xfffffffffffff001"
"jae 2f",
// if (rax != r8) goto label1;
"cmp rdi, r8",
"jne 2f",
// if (rsi != 0x8000) goto label1;
"cmp rsi, 0x8000",
"jne 2f",
// if (rdx != 0644) goto label1;
"cmp rdx, 0x1a4",
"jne 2f",
// Otherwise, we're successful.
"jmp 3f",
"2:",
// Set syscall arg1 to label1
"mov rdi, 0x1",
// Set syscall to exit_group
"mov rax, {sys_exit_group}",
// Do the syscall
"syscall",
"3:",
lateout("rax") ret,
in("rax") n,
arg1 = inlateout("rdi") path, // Reused for the exit_group syscall.
in("rsi") flags,
in("rdx") mode,
out("r8") _, // Clobbered
out("rcx") _, // rcx is used to store old rip
out("r11") _, // r11 is used to store old rflags
);
ret
}
#[cfg(target_arch = "x86_64")]
#[cfg(feature = "llvm_asm")]
#[allow(unused_mut)]
#[allow(deprecated)]
unsafe fn open_syscall_sanity_check() -> i32 { unsafe fn open_syscall_sanity_check() -> i32 {
let mut ret; let mut ret;
let path = b"/dev/null\0"; let path = b"/dev/null\0";

View file

@ -12,8 +12,7 @@
//! Syscalls are abused to communicate from the guest to the tool instructions //! Syscalls are abused to communicate from the guest to the tool instructions
//! necessary to carry out the test, such as setting timers or reading clocks. //! necessary to carry out the test, such as setting timers or reading clocks.
#![feature(llvm_asm)] #![cfg_attr(feature = "llvm_asm", feature(llvm_asm))]
use core::arch::x86_64::{__cpuid, __rdtscp, _rdtsc}; use core::arch::x86_64::{__cpuid, __rdtscp, _rdtsc};
use libc; use libc;
use reverie::{ use reverie::{
@ -189,7 +188,25 @@ async fn raise_sigwinch<T: Guest<LocalState>>(guest: &mut T) -> Tgkill {
.with_sig(libc::SIGWINCH) .with_sig(libc::SIGWINCH)
} }
// FIXME: Use the syscalls crate for doing this when it switches to using the
// `asm!()` macro instead of asm inside of a C file.
#[cfg(all(target_os = "linux", target_arch = "x86_64"))] #[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[cfg(not(feature = "llvm_asm"))]
unsafe fn syscall_no_branches(no: libc::c_long, arg1: libc::c_long) {
let mut ret: u64;
core::arch::asm!(
"syscall",
lateout("rax") ret,
in("rax") no,
in("rdi") arg1,
out("rcx") _, // rcx is used to store old rip
out("r11") _, // r11 is used to store old rflags
);
}
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
#[cfg(feature = "llvm_asm")]
#[allow(deprecated)]
unsafe fn syscall_no_branches(no: libc::c_long, arg1: libc::c_long) { unsafe fn syscall_no_branches(no: libc::c_long, arg1: libc::c_long) {
llvm_asm!(" llvm_asm!("
mov $0, %rax mov $0, %rax