#![cfg(target_arch = "x86_64")]
use crate::arch::x86::flags::Flags;
use std::arch::asm;
const VALS8: &[u8] = &[0x00, 0x01, 0x80, 0x81, 0xff, 0x7f, 0xaa, 0x55, 0x0f, 0xf0];
const COUNTS: &[u8] = &[0, 1, 2, 7, 8, 15, 16, 17, 31];
fn check(op: &str, val: u64, count: u8, rf: u64, f: &mut Flags) {
f.materialize_lazy();
if count == 0 {
return; }
let cpu_cf = rf & (1 << 0) != 0;
let cpu_pf = rf & (1 << 2) != 0;
let cpu_zf = rf & (1 << 6) != 0;
let cpu_sf = rf & (1 << 7) != 0;
let cpu_of = rf & (1 << 11) != 0;
assert_eq!(f.f_cf, cpu_cf, "{op}({val:#x}, {count}) CF");
assert_eq!(f.f_zf, cpu_zf, "{op}({val:#x}, {count}) ZF");
assert_eq!(f.f_sf, cpu_sf, "{op}({val:#x}, {count}) SF");
assert_eq!(f.f_pf, cpu_pf, "{op}({val:#x}, {count}) PF");
if count == 1 {
assert_eq!(f.f_of, cpu_of, "{op}({val:#x}, {count}) OF");
}
}
#[test]
fn shl8_matches_cpu() {
for &v in VALS8 {
for &c in COUNTS {
let (res_cpu, rf): (u8, u64);
unsafe {
asm!("shl {a}, cl", "pushfq", "pop {rf}",
a = inout(reg_byte) v => res_cpu, in("cl") c, rf = out(reg) rf);
}
let mut f = Flags::new();
let res_m = f.shl2p8(v as u64, c as u64) as u8;
assert_eq!(res_cpu, res_m, "shl8({v:#x},{c}) result");
check("shl8", v as u64, c, rf, &mut f);
}
}
}
#[test]
fn shr8_matches_cpu() {
for &v in VALS8 {
for &c in COUNTS {
let (res_cpu, rf): (u8, u64);
unsafe {
asm!("shr {a}, cl", "pushfq", "pop {rf}",
a = inout(reg_byte) v => res_cpu, in("cl") c, rf = out(reg) rf);
}
let mut f = Flags::new();
let res_m = f.shr2p8(v as u64, c as u64) as u8;
assert_eq!(res_cpu, res_m, "shr8({v:#x},{c}) result");
check("shr8", v as u64, c, rf, &mut f);
}
}
}
#[test]
fn sar8_matches_cpu() {
for &v in VALS8 {
for &c in COUNTS {
let (res_cpu, rf): (u8, u64);
unsafe {
asm!("sar {a}, cl", "pushfq", "pop {rf}",
a = inout(reg_byte) v => res_cpu, in("cl") c, rf = out(reg) rf);
}
let mut f = Flags::new();
let res_m = f.sar2p8(v as u64, c as u64) as u8;
assert_eq!(res_cpu, res_m, "sar8({v:#x},{c}) result");
check("sar8", v as u64, c, rf, &mut f);
}
}
}
const VALS16: &[u16] = &[0, 1, 0x8000, 0x7fff, 0xffff, 0xaaaa, 0x00ff, 0x0080];
#[test]
fn shl16_matches_cpu() {
for &v in VALS16 {
for &c in COUNTS {
let (res_cpu, rf): (u16, u64);
unsafe {
asm!("shl {a:x}, cl", "pushfq", "pop {rf}",
a = inout(reg) v => res_cpu, in("cl") c, rf = out(reg) rf);
}
let mut f = Flags::new();
let res_m = f.shl2p16(v as u64, c as u64) as u16;
assert_eq!(res_cpu, res_m, "shl16({v:#x},{c}) result");
check("shl16", v as u64, c, rf, &mut f);
}
}
}
#[test]
fn shr16_matches_cpu() {
for &v in VALS16 {
for &c in COUNTS {
let (res_cpu, rf): (u16, u64);
unsafe {
asm!("shr {a:x}, cl", "pushfq", "pop {rf}",
a = inout(reg) v => res_cpu, in("cl") c, rf = out(reg) rf);
}
let mut f = Flags::new();
let res_m = f.shr2p16(v as u64, c as u64) as u16;
assert_eq!(res_cpu, res_m, "shr16({v:#x},{c}) result");
check("shr16", v as u64, c, rf, &mut f);
}
}
}
#[test]
fn sar16_matches_cpu() {
for &v in VALS16 {
for &c in COUNTS {
let (res_cpu, rf): (u16, u64);
unsafe {
asm!("sar {a:x}, cl", "pushfq", "pop {rf}",
a = inout(reg) v => res_cpu, in("cl") c, rf = out(reg) rf);
}
let mut f = Flags::new();
let res_m = f.sar2p16(v as u64, c as u64) as u16;
assert_eq!(res_cpu, res_m, "sar16({v:#x},{c}) result");
check("sar16", v as u64, c, rf, &mut f);
}
}
}
const VALS32: &[u32] = &[
0,
1,
0x8000_0000,
0x7fff_ffff,
0xffff_ffff,
0xaaaa_aaaa,
0x0000_00ff,
];
#[test]
fn shl32_matches_cpu() {
for &v in VALS32 {
for &c in COUNTS {
let (res_cpu, rf): (u32, u64);
unsafe {
asm!("shl {a:e}, cl", "pushfq", "pop {rf}",
a = inout(reg) v => res_cpu, in("cl") c, rf = out(reg) rf);
}
let mut f = Flags::new();
let res_m = f.shl2p32(v as u64, c as u64) as u32;
assert_eq!(res_cpu, res_m, "shl32({v:#x},{c}) result");
check("shl32", v as u64, c, rf, &mut f);
}
}
}
#[test]
fn shr32_matches_cpu() {
for &v in VALS32 {
for &c in COUNTS {
let (res_cpu, rf): (u32, u64);
unsafe {
asm!("shr {a:e}, cl", "pushfq", "pop {rf}",
a = inout(reg) v => res_cpu, in("cl") c, rf = out(reg) rf);
}
let mut f = Flags::new();
let res_m = f.shr2p32(v as u64, c as u64) as u32;
assert_eq!(res_cpu, res_m, "shr32({v:#x},{c}) result");
check("shr32", v as u64, c, rf, &mut f);
}
}
}
#[test]
fn sar32_matches_cpu() {
for &v in VALS32 {
for &c in COUNTS {
let (res_cpu, rf): (u32, u64);
unsafe {
asm!("sar {a:e}, cl", "pushfq", "pop {rf}",
a = inout(reg) v => res_cpu, in("cl") c, rf = out(reg) rf);
}
let mut f = Flags::new();
let res_m = f.sar2p32(v as u64, c as u64) as u32;
assert_eq!(res_cpu, res_m, "sar32({v:#x},{c}) result");
check("sar32", v as u64, c, rf, &mut f);
}
}
}
const DBL32: &[(u32, u32)] = &[
(0, 0),
(1, 0),
(0x8000_0000, 0xffff_ffff),
(0xdead_beef, 0x1234_5678),
(0xffff_ffff, 0),
(0, 0xffff_ffff),
(0x7fff_ffff, 0x8000_0000),
(0xaaaa_aaaa, 0x5555_5555),
];
#[test]
fn shld32_matches_cpu() {
for &(v0, v1) in DBL32 {
for &c in COUNTS {
let (res_cpu, rf): (u32, u64);
unsafe {
asm!("shld {d:e}, {s:e}, cl", "pushfq", "pop {rf}",
d = inout(reg) v0 => res_cpu, s = in(reg) v1, in("cl") c, rf = out(reg) rf);
}
let mut f = Flags::new();
let res_m = f.shld(v0 as u64, v1 as u64, c as u64, 32) as u32;
assert_eq!(res_cpu, res_m, "shld32({v0:#x},{v1:#x},{c}) result");
check("shld32", v0 as u64, c, rf, &mut f);
}
}
}
#[test]
fn shrd32_matches_cpu() {
for &(v0, v1) in DBL32 {
for &c in COUNTS {
let (res_cpu, rf): (u32, u64);
unsafe {
asm!("shrd {d:e}, {s:e}, cl", "pushfq", "pop {rf}",
d = inout(reg) v0 => res_cpu, s = in(reg) v1, in("cl") c, rf = out(reg) rf);
}
let mut f = Flags::new();
let res_m = f.shrd(v0 as u64, v1 as u64, c as u64, 32) as u32;
assert_eq!(res_cpu, res_m, "shrd32({v0:#x},{v1:#x},{c}) result");
check("shrd32", v0 as u64, c, rf, &mut f);
}
}
}
const DBL64: &[(u64, u64)] = &[
(0, 0),
(1, 0),
(0x8000_0000_0000_0000, u64::MAX),
(0xdead_beef_cafe_babe, 0x0123_4567_89ab_cdef),
(u64::MAX, 0),
(0, u64::MAX),
];
const COUNTS64: &[u8] = &[0, 1, 2, 31, 32, 33, 63];
#[test]
fn shld64_matches_cpu() {
for &(v0, v1) in DBL64 {
for &c in COUNTS64 {
let (res_cpu, rf): (u64, u64);
unsafe {
asm!("shld {d:r}, {s:r}, cl", "pushfq", "pop {rf}",
d = inout(reg) v0 => res_cpu, s = in(reg) v1, in("cl") c, rf = out(reg) rf);
}
let mut f = Flags::new();
let res_m = f.shld(v0, v1, c as u64, 64);
assert_eq!(res_cpu, res_m, "shld64({v0:#x},{v1:#x},{c}) result");
check("shld64", v0, c, rf, &mut f);
}
}
}
#[test]
fn shrd64_matches_cpu() {
for &(v0, v1) in DBL64 {
for &c in COUNTS64 {
let (res_cpu, rf): (u64, u64);
unsafe {
asm!("shrd {d:r}, {s:r}, cl", "pushfq", "pop {rf}",
d = inout(reg) v0 => res_cpu, s = in(reg) v1, in("cl") c, rf = out(reg) rf);
}
let mut f = Flags::new();
let res_m = f.shrd(v0, v1, c as u64, 64);
assert_eq!(res_cpu, res_m, "shrd64({v0:#x},{v1:#x},{c}) result");
check("shrd64", v0, c, rf, &mut f);
}
}
}