use synth_backend::ArmEncoder;
use synth_synthesis::{ArmOp, InstructionSelector, Reg, RuleDatabase, WasmOp};
#[test]
fn test_signed_division() {
let wasm_ops = vec![
WasmOp::I32Const(100),
WasmOp::I32Const(7),
WasmOp::I32DivS, ];
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_sdiv = arm_instrs
.iter()
.any(|i| matches!(i.op, ArmOp::Sdiv { .. }));
assert!(has_sdiv, "Should generate SDIV instruction");
}
#[test]
fn test_unsigned_division() {
let wasm_ops = vec![
WasmOp::I32Const(100),
WasmOp::I32Const(7),
WasmOp::I32DivU, ];
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_udiv = arm_instrs
.iter()
.any(|i| matches!(i.op, ArmOp::Udiv { .. }));
assert!(has_udiv, "Should generate UDIV instruction");
}
#[test]
fn test_signed_remainder() {
let wasm_ops = vec![
WasmOp::I32Const(100),
WasmOp::I32Const(7),
WasmOp::I32RemS, ];
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());
}
#[test]
fn test_unsigned_remainder() {
let wasm_ops = vec![
WasmOp::I32Const(100),
WasmOp::I32Const(7),
WasmOp::I32RemU, ];
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());
}
#[test]
fn test_sdiv_encoding() {
let encoder = ArmEncoder::new_arm32();
let sdiv_op = ArmOp::Sdiv {
rd: Reg::R0,
rn: Reg::R1,
rm: Reg::R2,
};
let code = encoder.encode(&sdiv_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, 0xE710F211, "SDIV R0, R1, R2 encoding");
}
#[test]
fn test_udiv_encoding() {
let encoder = ArmEncoder::new_arm32();
let udiv_op = ArmOp::Udiv {
rd: Reg::R0,
rn: Reg::R1,
rm: Reg::R2,
};
let code = encoder.encode(&udiv_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, 0xE730F211, "UDIV R0, R1, R2 encoding");
}
#[test]
fn test_mls_encoding() {
let encoder = ArmEncoder::new_arm32();
let mls_op = ArmOp::Mls {
rd: Reg::R0,
rn: Reg::R1,
rm: Reg::R2,
ra: Reg::R3,
};
let code = encoder.encode(&mls_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_division_by_constant() {
let wasm_ops = vec![
WasmOp::I32Const(1000),
WasmOp::I32Const(8), WasmOp::I32DivU, ];
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());
}
#[test]
fn test_division_embedded_use_case() {
let wasm_ops = vec![
WasmOp::I32Const(10),
WasmOp::I32Const(20),
WasmOp::I32Add,
WasmOp::I32Const(30),
WasmOp::I32Add,
WasmOp::I32Const(40),
WasmOp::I32Add, WasmOp::I32Const(4),
WasmOp::I32DivU, ];
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_modulo_embedded_use_case() {
let wasm_ops = vec![
WasmOp::I32Const(15), WasmOp::I32Const(1),
WasmOp::I32Add, WasmOp::I32Const(16), WasmOp::I32RemU, ];
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_negative_division() {
let wasm_ops = vec![
WasmOp::I32Const(-100),
WasmOp::I32Const(7),
WasmOp::I32DivS, ];
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 has_sdiv = arm_instrs
.iter()
.any(|i| matches!(i.op, ArmOp::Sdiv { .. }));
assert!(has_sdiv);
}