use synth_backend::ArmEncoder;
use synth_synthesis::{ArmOp, InstructionSelector, Reg, RuleDatabase, WasmOp};
#[test]
fn test_rotate_left() {
let wasm_ops = vec![
WasmOp::I32Const(0x12345678),
WasmOp::I32Const(4),
WasmOp::I32Rotl,
];
let db = RuleDatabase::with_standard_rules();
let mut selector = InstructionSelector::new(db.rules().to_vec());
let arm_instrs = selector.select(&wasm_ops).expect("Failed to select");
assert!(!arm_instrs.is_empty());
let has_rsb = arm_instrs.iter().any(|i| matches!(i.op, ArmOp::Rsb { .. }));
let has_ror = arm_instrs
.iter()
.any(|i| matches!(i.op, ArmOp::RorReg { .. }));
assert!(has_rsb && has_ror, "ROTL should emit RSB + ROR sequence");
}
#[test]
fn test_rotate_right() {
let wasm_ops = vec![
WasmOp::I32Const(0x12345678),
WasmOp::I32Const(4),
WasmOp::I32Rotr,
];
let db = RuleDatabase::with_standard_rules();
let mut selector = InstructionSelector::new(db.rules().to_vec());
let arm_instrs = selector.select(&wasm_ops).expect("Failed to select");
assert!(!arm_instrs.is_empty());
let has_ror = arm_instrs
.iter()
.any(|i| matches!(i.op, ArmOp::RorReg { .. }));
assert!(has_ror);
}
#[test]
fn test_count_leading_zeros() {
let wasm_ops = vec![
WasmOp::I32Const(0x00001000), WasmOp::I32Clz,
];
let db = RuleDatabase::with_standard_rules();
let mut selector = InstructionSelector::new(db.rules().to_vec());
let arm_instrs = selector.select(&wasm_ops).expect("Failed to select");
assert!(!arm_instrs.is_empty());
let has_clz = arm_instrs.iter().any(|i| matches!(i.op, ArmOp::Clz { .. }));
assert!(has_clz);
}
#[test]
fn test_count_trailing_zeros() {
let wasm_ops = vec![
WasmOp::I32Const(0x00001000), WasmOp::I32Ctz,
];
let db = RuleDatabase::with_standard_rules();
let mut selector = InstructionSelector::new(db.rules().to_vec());
let arm_instrs = selector.select(&wasm_ops).expect("Failed to select");
assert!(!arm_instrs.is_empty());
let has_rbit = arm_instrs
.iter()
.any(|i| matches!(i.op, ArmOp::Rbit { .. }));
assert!(has_rbit);
}
#[test]
fn test_population_count() {
let wasm_ops = vec![
WasmOp::I32Const(0x0F0F0F0F), WasmOp::I32Popcnt,
];
let db = RuleDatabase::with_standard_rules();
let mut selector = InstructionSelector::new(db.rules().to_vec());
let result = selector.select(&wasm_ops);
assert!(
result.is_ok(),
"i32.popcnt should succeed via Popcnt pseudo-instruction"
);
let arm_instrs = result.unwrap();
let has_popcnt = arm_instrs
.iter()
.any(|i| matches!(&i.op, ArmOp::Popcnt { .. }));
assert!(has_popcnt, "Should emit Popcnt pseudo-instruction");
}
#[test]
fn test_ror_encoding() {
let encoder = ArmEncoder::new_arm32();
let ror_op = ArmOp::Ror {
rd: Reg::R0,
rn: Reg::R1,
shift: 4,
};
let code = encoder.encode(&ror_op).expect("Failed to encode");
assert_eq!(code.len(), 4);
let instr = u32::from_le_bytes([code[0], code[1], code[2], code[3]]);
assert_ne!(instr, 0);
}
#[test]
fn test_clz_encoding() {
let encoder = ArmEncoder::new_arm32();
let clz_op = ArmOp::Clz {
rd: Reg::R0,
rm: Reg::R1,
};
let code = encoder.encode(&clz_op).expect("Failed to encode");
assert_eq!(code.len(), 4);
let instr = u32::from_le_bytes([code[0], code[1], code[2], code[3]]);
assert_eq!(instr, 0xE16F0F11);
}
#[test]
fn test_rbit_encoding() {
let encoder = ArmEncoder::new_arm32();
let rbit_op = ArmOp::Rbit {
rd: Reg::R0,
rm: Reg::R1,
};
let code = encoder.encode(&rbit_op).expect("Failed to encode");
assert_eq!(code.len(), 4);
let instr = u32::from_le_bytes([code[0], code[1], code[2], code[3]]);
assert_eq!(instr, 0xE6FF0F31);
}
#[test]
fn test_bit_ops_in_real_code() {
let wasm_ops = vec![
WasmOp::I32Const(0x00100000), WasmOp::I32Ctz, ];
let db = RuleDatabase::with_standard_rules();
let mut selector = InstructionSelector::new(db.rules().to_vec());
let arm_instrs = selector.select(&wasm_ops).expect("Failed to select");
let encoder = ArmEncoder::new_arm32();
let ops: Vec<_> = arm_instrs.iter().map(|i| i.op.clone()).collect();
let code = encoder.encode_sequence(&ops).expect("Failed to encode");
assert!(!code.is_empty());
assert_eq!(code.len() % 4, 0); }
#[test]
fn test_bit_ops_embedded_use_case() {
let wasm_ops = vec![
WasmOp::I32Const(16), WasmOp::I32Const(1),
WasmOp::I32Sub, WasmOp::I32Const(16), WasmOp::I32And, WasmOp::I32Eqz, ];
let db = RuleDatabase::with_standard_rules();
let mut selector = InstructionSelector::new(db.rules().to_vec());
let arm_instrs = selector.select(&wasm_ops).expect("Failed to select");
assert!(!arm_instrs.is_empty());
}