#![cfg(feature = "std")]
use fARM64::decode::decode;
use fARM64::format::{format_to_string, FmtFormatter};
use fARM64::{encode, FeatureSet};
fn norm(s: &str) -> String {
let s = s.to_ascii_lowercase();
let mut out = String::with_capacity(s.len());
let mut pending = false;
for ch in s.chars() {
if ch.is_whitespace() {
pending = true;
continue;
}
let suppress = matches!(ch, ',' | ']' | '}' | ')');
let after_open = out.ends_with(['[', '{', '(']);
if pending && !out.is_empty() && !suppress && !after_open {
out.push(' ');
}
pending = false;
out.push(ch);
if ch == ',' {
pending = true;
}
}
out.trim().to_string()
}
fn text(word: u32) -> String {
let insn = decode(word, 0, FeatureSet::ALL);
format_to_string(&FmtFormatter::new(), &insn)
}
fn check(word: u32, expected: &str) {
let insn = decode(word, 0, FeatureSet::ALL);
assert!(
!insn.is_invalid(),
"{:08X} decoded Invalid (want `{}`)",
word,
expected
);
assert_eq!(
norm(&text(word)),
norm(expected),
"{:08X} disasm mismatch",
word
);
let enc = encode(&insn).unwrap_or_else(|e| {
panic!(
"{:08X} ({}) encode error {:?}",
word,
insn.mnemonic().name(),
e
)
});
assert_eq!(enc, word, "{:08X} round-trip produced {:08X}", word, enc);
}
fn check_invalid(word: u32) {
let insn = decode(word, 0, FeatureSet::ALL);
assert!(
insn.is_invalid(),
"{:08X} over-decoded as `{}`",
word,
text(word)
);
}
#[test]
fn neon_i8mm_examples() {
check(0x4E80A45F, "smmla v31.4s, v2.16b, v0.16b");
check(0x4E80AC5F, "usmmla v31.4s, v2.16b, v0.16b");
check(0x6E80A45F, "ummla v31.4s, v2.16b, v0.16b");
check(0x4E80A400, "smmla v0.4s, v0.16b, v0.16b");
check(0x4E9FA400, "smmla v0.4s, v0.16b, v31.16b");
check(0x4E81A400, "smmla v0.4s, v0.16b, v1.16b");
check(0x4E80A420, "smmla v0.4s, v1.16b, v0.16b");
}
#[test]
fn sve_matmul_examples() {
check(0x64A0E400, "fmmla z0.s, z0.s, z0.s");
check(0x64E0E400, "fmmla z0.d, z0.d, z0.d");
check(0x64A0E7E0, "fmmla z0.s, z31.s, z0.s");
check(0x64BFE400, "fmmla z0.s, z0.s, z31.s");
check(0x45029820, "smmla z0.s, z1.b, z2.b");
check(0x45829820, "usmmla z0.s, z1.b, z2.b");
check(0x45C29820, "ummla z0.s, z1.b, z2.b");
}
#[test]
fn sve_fp8_mlal_examples() {
check(0x6420518E, "fmlalb z14.h, z12.b, z0.b[0]");
check(0x64A0518E, "fmlalt z14.h, z12.b, z0.b[0]");
check(0x64285C00, "fmlalb z0.h, z0.b, z0.b[7]");
check(0x64385C00, "fmlalb z0.h, z0.b, z0.b[15]");
check(0x64275000, "fmlalb z0.h, z0.b, z7.b[0]");
check(0x6420C000, "fmlallbb z0.s, z0.b, z0.b[0]");
check(0x6460C000, "fmlallbt z0.s, z0.b, z0.b[0]");
check(0x64A0C000, "fmlalltb z0.s, z0.b, z0.b[0]");
check(0x64E0C000, "fmlalltt z0.s, z0.b, z0.b[0]");
}
#[test]
fn sve_bf16_indexed_examples() {
check(0x64220960, "bfmla z0.h, z11.h, z2.h[0]");
check(0x64200800, "bfmla z0.h, z0.h, z0.h[0]");
check(0x64200C00, "bfmls z0.h, z0.h, z0.h[0]");
check(0x64202800, "bfmul z0.h, z0.h, z0.h[0]");
check(0x64780800, "bfmla z0.h, z0.h, z0.h[7]");
check(0x64270800, "bfmla z0.h, z0.h, z7.h[0]");
}
#[test]
fn sme2_multivector_fmul_examples() {
check(
0xC160E798,
"fmul {z24.h, z25.h}, {z28.h, z29.h}, {z0.h, z1.h}",
);
check(0xC160E400, "fmul {z0.h, z1.h}, {z0.h, z1.h}, {z0.h, z1.h}");
check(0xC1A0E400, "fmul {z0.s, z1.s}, {z0.s, z1.s}, {z0.s, z1.s}");
check(0xC1E0E400, "fmul {z0.d, z1.d}, {z0.d, z1.d}, {z0.d, z1.d}");
check(
0xC161E400,
"fmul {z0.h - z3.h}, {z0.h - z3.h}, {z0.h - z3.h}",
);
check(
0xC1A1E400,
"fmul {z0.s - z3.s}, {z0.s - z3.s}, {z0.s - z3.s}",
);
}
fn assert_rt(word: u32) {
let insn = decode(word, 0, FeatureSet::ALL);
assert!(!insn.is_invalid(), "{:08X} Invalid", word);
let enc = encode(&insn).unwrap_or_else(|e| {
panic!(
"{:08X} ({}) encode error {:?}",
word,
insn.mnemonic().name(),
e
)
});
assert_eq!(enc, word, "{:08X} round-trip produced {:08X}", word, enc);
}
#[test]
fn neon_i8mm_roundtrip_sweep() {
for &(u, b) in &[(0u32, 0u32), (0, 1), (1, 0)] {
for rm in [0u32, 1, 15, 31] {
for rn in [0u32, 7, 31] {
for rd in [0u32, 2, 31] {
let w = (0b0100_1110_1000_0000u32 << 16)
| (u << 29)
| (rm << 16)
| (0b1010 << 12)
| (b << 11)
| (1 << 10)
| (rn << 5)
| rd;
assert_rt(w);
}
}
}
}
}
#[test]
fn sve_fp8_mlal_roundtrip_sweep() {
for t in [0u32, 1] {
for idx in 0u32..16 {
for zm in 0u32..8 {
for &(zn, zda) in &[(0u32, 0u32), (1, 2), (31, 31)] {
let w = 0x6420_5000
| (t << 23)
| ((idx >> 2) << 19)
| (zm << 16)
| ((idx & 3) << 10)
| (zn << 5)
| zda;
assert_rt(w);
}
}
}
}
for bt in 0u32..4 {
for idx in [0u32, 1, 7, 15] {
for zm in [0u32, 3, 7] {
let w =
0x6420_C000 | (bt << 22) | ((idx >> 2) << 19) | (zm << 16) | ((idx & 3) << 10);
assert_rt(w);
}
}
}
}
#[test]
fn sve_bf16_indexed_roundtrip_sweep() {
for sub in [0b000010u32, 0b000011, 0b001010] {
for idx in 0u32..8 {
for zm in 0u32..8 {
for &(zn, zda) in &[(0u32, 0u32), (11, 0), (31, 31)] {
let w = (0b0110_0100u32 << 24)
| (1 << 21)
| ((idx >> 2) << 22) | ((idx & 3) << 19) | (zm << 16)
| (sub << 10)
| (zn << 5)
| zda;
assert_rt(w);
}
}
}
}
}
#[test]
fn sme2_multivector_fmul_roundtrip_sweep() {
for size in [1u32, 2, 3] {
for zd in [0u32, 2, 30] {
for zn in [0u32, 4, 28] {
for zm in [0u32, 2, 6] {
let w = 0xC120_E400
| (size << 22)
| ((zm / 2) << 17)
| ((zn / 2) << 6)
| ((zd / 2) << 1);
assert_rt(w);
}
}
}
for zd in [0u32, 4, 28] {
for zn in [0u32, 8, 12] {
for zm in [0u32, 4, 8] {
let w = 0xC121_E400
| (size << 22)
| ((zm / 4) << 18)
| ((zn / 4) << 7)
| ((zd / 4) << 2);
assert_rt(w);
}
}
}
}
}
#[test]
fn no_over_decode() {
check_invalid(0x0E80A400); check_invalid(0x4E00A400); check_invalid(0x4EC0A400); check_invalid(0x4E80A800); check_invalid(0x6E80AC00); check_invalid(0x64605022);
check_invalid(0xC160E420); check_invalid(0xC160E401); check_invalid(0xC163E408); check_invalid(0xC161E440); }