1use crate::arm_semantics::{ArmSemantics, ArmState};
22use crate::solver::{CheckOutcome, new_solver};
23use crate::term::BV;
24use crate::wasm_semantics::WasmSemantics;
25use synth_core::WasmOp;
26use synth_synthesis::{ArmOp, Reg, SynthesisRule};
27use thiserror::Error;
28
29#[derive(Debug, Error)]
31pub enum VerificationError {
32 #[error("Translation is incorrect: counterexample found")]
33 CounterexampleFound {
34 wasm_result: String,
35 arm_result: String,
36 inputs: Vec<String>,
37 },
38
39 #[error("Verification timeout after {0}ms")]
40 Timeout(u64),
41
42 #[error("Unsupported operation: {0}")]
43 UnsupportedOperation(String),
44
45 #[error("SMT solver error: {0}")]
46 SolverError(String),
47
48 #[error("Invalid synthesis rule: {0}")]
49 InvalidRule(String),
50}
51
52#[derive(Debug, Clone, PartialEq)]
54pub enum ValidationResult {
55 Verified,
57
58 Invalid { counterexample: Vec<(String, i64)> },
60
61 Unknown { reason: String },
63}
64
65pub struct TranslationValidator {
69 wasm_encoder: WasmSemantics,
70 arm_encoder: ArmSemantics,
71 timeout_ms: u64,
72}
73
74impl Default for TranslationValidator {
75 fn default() -> Self {
76 Self::new()
77 }
78}
79
80impl TranslationValidator {
81 pub fn new() -> Self {
83 Self {
84 wasm_encoder: WasmSemantics::new(),
85 arm_encoder: ArmSemantics::new(),
86 timeout_ms: 30000, }
88 }
89
90 pub fn set_timeout(&mut self, timeout_ms: u64) {
92 self.timeout_ms = timeout_ms;
93 }
94
95 pub fn verify_rule(&self, rule: &SynthesisRule) -> Result<ValidationResult, VerificationError> {
100 let wasm_op = match &rule.pattern {
102 synth_synthesis::Pattern::WasmInstr(op) => op,
103 _ => {
104 return Err(VerificationError::UnsupportedOperation(
105 "Only single WASM instruction patterns are supported".to_string(),
106 ));
107 }
108 };
109
110 let arm_ops = match &rule.replacement {
112 synth_synthesis::Replacement::ArmInstr(op) => vec![op.clone()],
113 synth_synthesis::Replacement::ArmSequence(ops) => ops.clone(),
114 _ => {
115 return Err(VerificationError::UnsupportedOperation(
116 "Only ARM instruction replacements are supported".to_string(),
117 ));
118 }
119 };
120
121 self.verify_equivalence(wasm_op, &arm_ops)
122 }
123
124 pub fn verify_equivalence(
126 &self,
127 wasm_op: &WasmOp,
128 arm_ops: &[ArmOp],
129 ) -> Result<ValidationResult, VerificationError> {
130 self.verify_equivalence_parameterized(wasm_op, arm_ops, &[])
131 }
132
133 pub fn verify_equivalence_parameterized(
135 &self,
136 wasm_op: &WasmOp,
137 arm_ops: &[ArmOp],
138 concrete_params: &[(usize, i64)],
139 ) -> Result<ValidationResult, VerificationError> {
140 let mut solver = new_solver();
141
142 let num_inputs = self.get_num_inputs(wasm_op);
144 let mut inputs: Vec<BV> = Vec::new();
145
146 for i in 0..num_inputs {
147 let input = if let Some((_, value)) = concrete_params.iter().find(|(idx, _)| *idx == i)
148 {
149 BV::from_i64(*value, 32)
151 } else {
152 BV::new_const(format!("input_{}", i), 32)
154 };
155 inputs.push(input);
156 }
157
158 let wasm_result = self.wasm_encoder.encode_op(wasm_op, &inputs);
160
161 let arm_result = self.encode_arm_sequence(arm_ops, &inputs)?;
163
164 solver.assert(&wasm_result.eq(&arm_result).not());
168
169 match solver.check() {
170 CheckOutcome::Unsat => {
171 Ok(ValidationResult::Verified)
173 }
174
175 CheckOutcome::Sat => {
176 let mut counterexample = Vec::new();
180 for (i, input) in inputs.iter().enumerate() {
181 if let Some(value) = solver.value(input)
182 && let Ok(int_val) = i64::try_from(value)
183 {
184 counterexample.push((format!("input_{}", i), int_val));
185 }
186 }
187
188 Ok(ValidationResult::Invalid { counterexample })
189 }
190
191 CheckOutcome::Unknown(reason) => {
192 Ok(ValidationResult::Unknown {
194 reason: format!("SMT solver returned unknown: {reason}"),
195 })
196 }
197 }
198 }
199
200 fn encode_arm_sequence(
202 &self,
203 arm_ops: &[ArmOp],
204 inputs: &[BV],
205 ) -> Result<BV, VerificationError> {
206 let mut state = ArmState::new_symbolic();
207
208 for (i, input) in inputs.iter().enumerate() {
210 let reg = match i {
211 0 => Reg::R0,
212 1 => Reg::R1,
213 2 => Reg::R2,
214 _ => {
215 return Err(VerificationError::UnsupportedOperation(format!(
216 "Too many inputs: {}",
217 inputs.len()
218 )));
219 }
220 };
221 state.set_reg(®, input.clone());
222 }
223
224 for arm_op in arm_ops {
226 self.arm_encoder.encode_op(arm_op, &mut state);
227 }
228
229 Ok(self.arm_encoder.extract_result(&state, &Reg::R0))
231 }
232
233 pub fn verify_parameterized_range<F>(
235 &self,
236 wasm_op: &WasmOp,
237 create_arm_ops: F,
238 param_index: usize,
239 range: std::ops::Range<i64>,
240 ) -> Result<ValidationResult, VerificationError>
241 where
242 F: Fn(i64) -> Vec<ArmOp>,
243 {
244 for value in range {
245 let arm_ops = create_arm_ops(value);
246 let result =
247 self.verify_equivalence_parameterized(wasm_op, &arm_ops, &[(param_index, value)])?;
248
249 match result {
250 ValidationResult::Verified => continue,
251 ValidationResult::Invalid { counterexample } => {
252 return Ok(ValidationResult::Invalid {
253 counterexample: counterexample
254 .into_iter()
255 .map(|(k, v)| (format!("{} (param={})", k, value), v))
256 .collect(),
257 });
258 }
259 ValidationResult::Unknown { reason } => {
260 return Ok(ValidationResult::Unknown {
261 reason: format!("Failed at param={}: {}", value, reason),
262 });
263 }
264 }
265 }
266
267 Ok(ValidationResult::Verified)
268 }
269
270 fn get_num_inputs(&self, wasm_op: &WasmOp) -> usize {
272 use WasmOp::*;
273 match wasm_op {
274 I32Add | I32Sub | I32Mul | I32DivS | I32DivU | I32RemS | I32RemU | I32And | I32Or
276 | I32Xor | I32Shl | I32ShrS | I32ShrU | I32Rotl | I32Rotr | I32Eq | I32Ne | I32LtS
277 | I32LtU | I32LeS | I32LeU | I32GtS | I32GtU | I32GeS | I32GeU => 2,
278
279 I32Clz | I32Ctz | I32Popcnt | I32Eqz => 1,
281
282 I32Const(_) => 0,
284
285 I32Load { .. } => 1, I32Store { .. } => 2, LocalGet(_) | GlobalGet(_) => 0,
291 LocalSet(_) | GlobalSet(_) | LocalTee(_) => 1,
292 Br(_) | BrIf(_) | Return => 0,
293
294 Drop => 1,
296 Select => 3, Nop | Unreachable | Block | Loop | If | Else | End => 0,
298
299 _ => 0,
301 }
302 }
303
304 pub fn verify_rules(
306 &self,
307 rules: &[SynthesisRule],
308 ) -> Vec<(String, Result<ValidationResult, VerificationError>)> {
309 rules
310 .iter()
311 .map(|rule| {
312 let result = self.verify_rule(rule);
313 (rule.name.clone(), result)
314 })
315 .collect()
316 }
317}
318
319#[cfg(test)]
320mod tests {
321 use super::*;
322 use crate::with_verification_context;
323 use synth_synthesis::{Cost, Operand2, Pattern, Replacement};
324
325 fn create_test_rule(wasm_op: WasmOp, arm_op: ArmOp) -> SynthesisRule {
326 SynthesisRule {
327 name: format!("{:?}", wasm_op),
328 priority: 0,
329 pattern: Pattern::WasmInstr(wasm_op),
330 replacement: Replacement::ArmInstr(arm_op),
331 cost: Cost {
332 cycles: 1,
333 code_size: 4,
334 registers: 2,
335 },
336 }
337 }
338
339 #[test]
340 fn test_verify_add_correct() {
341 with_verification_context(|| {
342 let validator = TranslationValidator::new();
343
344 let rule = create_test_rule(
345 WasmOp::I32Add,
346 ArmOp::Add {
347 rd: Reg::R0,
348 rn: Reg::R0,
349 op2: Operand2::Reg(Reg::R1),
350 },
351 );
352
353 let result = validator.verify_rule(&rule).unwrap();
354 assert_eq!(result, ValidationResult::Verified);
355 });
356 }
357
358 #[test]
359 fn test_verify_sub_correct() {
360 with_verification_context(|| {
361 let validator = TranslationValidator::new();
362
363 let rule = create_test_rule(
364 WasmOp::I32Sub,
365 ArmOp::Sub {
366 rd: Reg::R0,
367 rn: Reg::R0,
368 op2: Operand2::Reg(Reg::R1),
369 },
370 );
371
372 let result = validator.verify_rule(&rule).unwrap();
373 assert_eq!(result, ValidationResult::Verified);
374 });
375 }
376
377 #[test]
378 fn test_verify_mul_correct() {
379 with_verification_context(|| {
380 let validator = TranslationValidator::new();
381
382 let rule = create_test_rule(
383 WasmOp::I32Mul,
384 ArmOp::Mul {
385 rd: Reg::R0,
386 rn: Reg::R0,
387 rm: Reg::R1,
388 },
389 );
390
391 let result = validator.verify_rule(&rule).unwrap();
392 assert_eq!(result, ValidationResult::Verified);
393 });
394 }
395
396 #[test]
397 fn test_verify_and_correct() {
398 with_verification_context(|| {
399 let validator = TranslationValidator::new();
400
401 let rule = create_test_rule(
402 WasmOp::I32And,
403 ArmOp::And {
404 rd: Reg::R0,
405 rn: Reg::R0,
406 op2: Operand2::Reg(Reg::R1),
407 },
408 );
409
410 let result = validator.verify_rule(&rule).unwrap();
411 assert_eq!(result, ValidationResult::Verified);
412 });
413 }
414
415 #[test]
416 fn test_verify_incorrect_rule() {
417 with_verification_context(|| {
418 let validator = TranslationValidator::new();
419
420 let rule = create_test_rule(
422 WasmOp::I32Add,
423 ArmOp::Sub {
424 rd: Reg::R0,
425 rn: Reg::R0,
426 op2: Operand2::Reg(Reg::R1),
427 },
428 );
429
430 let result = validator.verify_rule(&rule).unwrap();
431
432 match result {
433 ValidationResult::Invalid { counterexample } => {
434 assert!(!counterexample.is_empty());
435 }
436 _ => panic!("Expected counterexample but got: {:?}", result),
437 }
438 });
439 }
440
441 #[test]
442 fn test_verify_bitwise_ops() {
443 with_verification_context(|| {
444 let validator = TranslationValidator::new();
445
446 let or_rule = create_test_rule(
448 WasmOp::I32Or,
449 ArmOp::Orr {
450 rd: Reg::R0,
451 rn: Reg::R0,
452 op2: Operand2::Reg(Reg::R1),
453 },
454 );
455 assert_eq!(
456 validator.verify_rule(&or_rule).unwrap(),
457 ValidationResult::Verified
458 );
459
460 let xor_rule = create_test_rule(
462 WasmOp::I32Xor,
463 ArmOp::Eor {
464 rd: Reg::R0,
465 rn: Reg::R0,
466 op2: Operand2::Reg(Reg::R1),
467 },
468 );
469 assert_eq!(
470 validator.verify_rule(&xor_rule).unwrap(),
471 ValidationResult::Verified
472 );
473 });
474 }
475
476 #[test]
477 fn test_verify_shift_ops() {
478 }
483}