Skip to main content

synth_verify/
arm_semantics.rs

1//! ARM Semantics Encoding to SMT
2//!
3//! Encodes ARM operation semantics as SMT bitvector formulas.
4//! Each ARM operation is translated to a mathematical formula that precisely
5//! captures its behavior, including register updates and condition flags.
6
7use crate::term::{BV, Bool};
8use std::collections::HashMap;
9use synth_synthesis::rules::{ArmOp, Operand2, Reg, VfpReg};
10
11/// ARM processor state representation in SMT
12///
13/// Z3 0.19 uses thread-local context -- no lifetime parameters needed.
14pub struct ArmState {
15    /// General purpose registers R0-R15
16    pub registers: Vec<BV>,
17    /// Condition flags (N, Z, C, V)
18    pub flags: ConditionFlags,
19    /// VFP (floating-point) registers
20    pub vfp_registers: Vec<BV>,
21    /// Memory model (simplified for bounded verification)
22    pub memory: Vec<BV>,
23    /// Local variables (for WASM verification)
24    pub locals: Vec<BV>,
25    /// Global variables (for WASM verification)
26    pub globals: Vec<BV>,
27    /// VCR-VER-002 (#166): the accumulated condition under which the executed
28    /// sequence TRAPS (reaches a `UDF`). `false` in a fresh state; `encode_op`
29    /// sets it unconditionally on a `Udf`, and the branch-taking executor
30    /// [`ArmSemantics::encode_sequence_br`] conditions it on the path guard the
31    /// `UDF` is reached under — this is the ARM-side trap term the
32    /// trap-preservation VC compares against the WASM op's trap condition.
33    pub may_trap: Bool,
34}
35
36/// ARM condition flags
37pub struct ConditionFlags {
38    pub n: Bool, // Negative
39    pub z: Bool, // Zero
40    pub c: Bool, // Carry
41    pub v: Bool, // Overflow
42}
43
44impl ArmState {
45    /// Create a new ARM state with symbolic values
46    pub fn new_symbolic() -> Self {
47        let registers = (0..16)
48            .map(|i| BV::new_const(format!("r{}", i), 32))
49            .collect();
50
51        let flags = ConditionFlags {
52            n: Bool::new_const("flag_n"),
53            z: Bool::new_const("flag_z"),
54            c: Bool::new_const("flag_c"),
55            v: Bool::new_const("flag_v"),
56        };
57
58        let memory = (0..256)
59            .map(|i| BV::new_const(format!("mem_{}", i), 32))
60            .collect();
61
62        let locals = (0..32)
63            .map(|i| BV::new_const(format!("local_{}", i), 32))
64            .collect();
65
66        let globals = (0..16)
67            .map(|i| BV::new_const(format!("global_{}", i), 32))
68            .collect();
69
70        let vfp_registers = (0..48)
71            .map(|i| BV::new_const(format!("vfp_{}", i), 32))
72            .collect();
73
74        Self {
75            registers,
76            flags,
77            vfp_registers,
78            memory,
79            locals,
80            globals,
81            may_trap: Bool::from_bool(false),
82        }
83    }
84
85    /// Get register value
86    pub fn get_reg(&self, reg: &Reg) -> &BV {
87        let index = reg_to_index(reg);
88        &self.registers[index]
89    }
90
91    /// Set register value
92    pub fn set_reg(&mut self, reg: &Reg, value: BV) {
93        let index = reg_to_index(reg);
94        self.registers[index] = value;
95    }
96
97    /// Get VFP register value
98    pub fn get_vfp_reg(&self, reg: &VfpReg) -> &BV {
99        let index = vfp_reg_to_index(reg);
100        &self.vfp_registers[index]
101    }
102
103    /// Set VFP register value
104    pub fn set_vfp_reg(&mut self, reg: &VfpReg, value: BV) {
105        let index = vfp_reg_to_index(reg);
106        self.vfp_registers[index] = value;
107    }
108}
109
110/// Convert register enum to index
111fn reg_to_index(reg: &Reg) -> usize {
112    match reg {
113        Reg::R0 => 0,
114        Reg::R1 => 1,
115        Reg::R2 => 2,
116        Reg::R3 => 3,
117        Reg::R4 => 4,
118        Reg::R5 => 5,
119        Reg::R6 => 6,
120        Reg::R7 => 7,
121        Reg::R8 => 8,
122        Reg::R9 => 9,
123        Reg::R10 => 10,
124        Reg::R11 => 11,
125        Reg::R12 => 12,
126        Reg::SP => 13,
127        Reg::LR => 14,
128        Reg::PC => 15,
129    }
130}
131
132/// Convert VFP register enum to index
133fn vfp_reg_to_index(reg: &VfpReg) -> usize {
134    match reg {
135        // Single-precision registers S0-S31 (indices 0-31)
136        VfpReg::S0 => 0,
137        VfpReg::S1 => 1,
138        VfpReg::S2 => 2,
139        VfpReg::S3 => 3,
140        VfpReg::S4 => 4,
141        VfpReg::S5 => 5,
142        VfpReg::S6 => 6,
143        VfpReg::S7 => 7,
144        VfpReg::S8 => 8,
145        VfpReg::S9 => 9,
146        VfpReg::S10 => 10,
147        VfpReg::S11 => 11,
148        VfpReg::S12 => 12,
149        VfpReg::S13 => 13,
150        VfpReg::S14 => 14,
151        VfpReg::S15 => 15,
152        VfpReg::S16 => 16,
153        VfpReg::S17 => 17,
154        VfpReg::S18 => 18,
155        VfpReg::S19 => 19,
156        VfpReg::S20 => 20,
157        VfpReg::S21 => 21,
158        VfpReg::S22 => 22,
159        VfpReg::S23 => 23,
160        VfpReg::S24 => 24,
161        VfpReg::S25 => 25,
162        VfpReg::S26 => 26,
163        VfpReg::S27 => 27,
164        VfpReg::S28 => 28,
165        VfpReg::S29 => 29,
166        VfpReg::S30 => 30,
167        VfpReg::S31 => 31,
168        // Double-precision registers D0-D15 (indices 32-47)
169        // Note: D0 = S0:S1, D1 = S2:S3, etc.
170        // We store the "low" part of each D register
171        VfpReg::D0 => 32,
172        VfpReg::D1 => 33,
173        VfpReg::D2 => 34,
174        VfpReg::D3 => 35,
175        VfpReg::D4 => 36,
176        VfpReg::D5 => 37,
177        VfpReg::D6 => 38,
178        VfpReg::D7 => 39,
179        VfpReg::D8 => 40,
180        VfpReg::D9 => 41,
181        VfpReg::D10 => 42,
182        VfpReg::D11 => 43,
183        VfpReg::D12 => 44,
184        VfpReg::D13 => 45,
185        VfpReg::D14 => 46,
186        VfpReg::D15 => 47,
187    }
188}
189
190/// ARM semantics encoder
191///
192/// Z3 0.19 uses thread-local context -- no lifetime parameters needed.
193pub struct ArmSemantics;
194
195impl Default for ArmSemantics {
196    fn default() -> Self {
197        Self::new()
198    }
199}
200
201impl ArmSemantics {
202    /// Create a new ARM semantics encoder
203    pub fn new() -> Self {
204        Self
205    }
206
207    /// Encode an ARM operation and return the resulting state
208    ///
209    /// This models the effect of executing the ARM instruction on the processor state.
210    pub fn encode_op(&self, op: &ArmOp, state: &mut ArmState) {
211        match op {
212            ArmOp::Add { rd, rn, op2 } => {
213                let rn_val = state.get_reg(rn).clone();
214                let op2_val = self.evaluate_operand2(op2, state);
215                let result = rn_val.bvadd(&op2_val);
216                state.set_reg(rd, result);
217            }
218
219            ArmOp::Sub { rd, rn, op2 } => {
220                let rn_val = state.get_reg(rn).clone();
221                let op2_val = self.evaluate_operand2(op2, state);
222                let result = rn_val.bvsub(&op2_val);
223                state.set_reg(rd, result);
224            }
225
226            ArmOp::Mul { rd, rn, rm } => {
227                let rn_val = state.get_reg(rn).clone();
228                let rm_val = state.get_reg(rm).clone();
229                let result = rn_val.bvmul(&rm_val);
230                state.set_reg(rd, result);
231            }
232
233            ArmOp::Umull { rdlo, rdhi, rn, rm } => {
234                // {rdhi:rdlo} = zext64(rn) * zext64(rm); rdhi = high 32 bits.
235                let rn64 = state.get_reg(rn).zero_ext(32);
236                let rm64 = state.get_reg(rm).zero_ext(32);
237                let prod = rn64.bvmul(&rm64);
238                state.set_reg(rdlo, prod.extract(31, 0));
239                state.set_reg(rdhi, prod.extract(63, 32));
240            }
241
242            ArmOp::Sdiv { rd, rn, rm } => {
243                let rn_val = state.get_reg(rn).clone();
244                let rm_val = state.get_reg(rm).clone();
245                let result = rn_val.bvsdiv(&rm_val);
246                state.set_reg(rd, result);
247            }
248
249            ArmOp::Udiv { rd, rn, rm } => {
250                let rn_val = state.get_reg(rn).clone();
251                let rm_val = state.get_reg(rm).clone();
252                let result = rn_val.bvudiv(&rm_val);
253                state.set_reg(rd, result);
254            }
255
256            ArmOp::Mls { rd, rn, rm, ra } => {
257                // MLS (Multiply and Subtract): Rd = Ra - Rn * Rm
258                // Used for remainder operations: a % b = a - (a/b) * b
259                let rn_val = state.get_reg(rn).clone();
260                let rm_val = state.get_reg(rm).clone();
261                let ra_val = state.get_reg(ra).clone();
262                let product = rn_val.bvmul(&rm_val);
263                let result = ra_val.bvsub(&product);
264                state.set_reg(rd, result);
265            }
266
267            ArmOp::And { rd, rn, op2 } => {
268                let rn_val = state.get_reg(rn).clone();
269                let op2_val = self.evaluate_operand2(op2, state);
270                let result = rn_val.bvand(&op2_val);
271                state.set_reg(rd, result);
272            }
273
274            ArmOp::Orr { rd, rn, op2 } => {
275                let rn_val = state.get_reg(rn).clone();
276                let op2_val = self.evaluate_operand2(op2, state);
277                let result = rn_val.bvor(&op2_val);
278                state.set_reg(rd, result);
279            }
280
281            ArmOp::Eor { rd, rn, op2 } => {
282                let rn_val = state.get_reg(rn).clone();
283                let op2_val = self.evaluate_operand2(op2, state);
284                let result = rn_val.bvxor(&op2_val);
285                state.set_reg(rd, result);
286            }
287
288            ArmOp::Lsl { rd, rn, shift } => {
289                let rn_val = state.get_reg(rn).clone();
290                let shift_val = BV::from_i64(*shift as i64, 32);
291                let result = rn_val.bvshl(&shift_val);
292                state.set_reg(rd, result);
293            }
294
295            ArmOp::Lsr { rd, rn, shift } => {
296                let rn_val = state.get_reg(rn).clone();
297                let shift_val = BV::from_i64(*shift as i64, 32);
298                let result = rn_val.bvlshr(&shift_val);
299                state.set_reg(rd, result);
300            }
301
302            ArmOp::Asr { rd, rn, shift } => {
303                let rn_val = state.get_reg(rn).clone();
304                let shift_val = BV::from_i64(*shift as i64, 32);
305                let result = rn_val.bvashr(&shift_val);
306                state.set_reg(rd, result);
307            }
308
309            ArmOp::Ror { rd, rn, shift } => {
310                // Rotate right - ARM ROR instruction
311                // ROR(x, n) rotates x right by n positions
312                let rn_val = state.get_reg(rn).clone();
313                let shift_val = BV::from_i64(*shift as i64, 32);
314                let result = rn_val.bvrotr(&shift_val);
315                state.set_reg(rd, result);
316            }
317
318            ArmOp::Mov { rd, op2 } => {
319                let op2_val = self.evaluate_operand2(op2, state);
320                state.set_reg(rd, op2_val);
321            }
322
323            ArmOp::Mvn { rd, op2 } => {
324                let op2_val = self.evaluate_operand2(op2, state);
325                let result = op2_val.bvnot();
326                state.set_reg(rd, result);
327            }
328
329            ArmOp::Cmp { rn, op2 } => {
330                // Compare sets flags but doesn't write to a register
331                // CMP performs: Rn - Op2 and updates all condition flags
332                let rn_val = state.get_reg(rn).clone();
333                let op2_val = self.evaluate_operand2(op2, state);
334
335                // Compute result of subtraction
336                let result = rn_val.bvsub(&op2_val);
337
338                // Update all condition flags
339                self.update_flags_sub(state, &rn_val, &op2_val, &result);
340            }
341
342            ArmOp::Clz { rd, rm } => {
343                // Count leading zeros - ARM CLZ instruction
344                // Uses binary search algorithm matching WASM i32.clz semantics
345                let input = state.get_reg(rm).clone();
346                let result = self.encode_clz(&input);
347                state.set_reg(rd, result);
348            }
349
350            ArmOp::Rbit { rd, rm } => {
351                // Reverse bits - ARM RBIT instruction
352                // Reverses the bit order in a 32-bit value
353                let input = state.get_reg(rm).clone();
354                let result = self.encode_rbit(&input);
355                state.set_reg(rd, result);
356            }
357
358            ArmOp::Popcnt { rd, rm } => {
359                // Population count - count number of 1 bits
360                // This is a pseudo-instruction for verification
361                let input = state.get_reg(rm).clone();
362                let result = self.encode_popcnt(&input);
363                state.set_reg(rd, result);
364            }
365
366            ArmOp::Nop => {
367                // No operation - state unchanged
368            }
369
370            ArmOp::SetCond { rd, cond } => {
371                // SetCond evaluates a condition based on NZCV flags and sets rd to 0 or 1
372                // This is a pseudo-instruction for verification purposes
373                let cond_result = self.evaluate_condition(cond, &state.flags);
374                let result = self.bool_to_bv32(&cond_result);
375                state.set_reg(rd, result);
376            }
377
378            ArmOp::Select {
379                rd,
380                rval1,
381                rval2,
382                rcond,
383            } => {
384                // Select operation: if rcond != 0, select rval1, else rval2
385                // This is a pseudo-instruction for verification purposes
386                let val1 = state.get_reg(rval1).clone();
387                let val2 = state.get_reg(rval2).clone();
388                let cond = state.get_reg(rcond).clone();
389                let zero = BV::from_i64(0, 32);
390                let cond_bool = cond.eq(&zero).not(); // cond != 0
391                let result = cond_bool.ite(&val1, &val2);
392                state.set_reg(rd, result);
393            }
394
395            // Memory operations simplified for now
396            ArmOp::Ldr { rd, addr: _ } => {
397                // Load from memory
398                // Simplified: return symbolic value
399                let result = BV::new_const(format!("load_{:?}", rd), 32);
400                state.set_reg(rd, result);
401            }
402
403            ArmOp::Str { rd: _, addr: _ } => {
404                // Store to memory
405                // Simplified: memory updates not fully modeled yet
406            }
407
408            // Control flow operations
409            ArmOp::B { label: _ } => {
410                // Branch - would update PC in full model
411                // For bounded verification, we treat this symbolically
412            }
413
414            ArmOp::Bl { label: _ } => {
415                // Branch with link - would update PC and LR
416            }
417
418            ArmOp::Bx { rm: _ } => {
419                // Branch and exchange - would update PC
420            }
421
422            // Local/Global variable access (pseudo-instructions for verification)
423            ArmOp::LocalGet { rd, index } => {
424                // Load local variable into register
425                let value = state
426                    .locals
427                    .get(*index as usize)
428                    .cloned()
429                    .unwrap_or_else(|| BV::new_const(format!("local_{}", index), 32));
430                state.set_reg(rd, value);
431            }
432
433            ArmOp::LocalSet { rs, index } => {
434                // Store register into local variable
435                let value = state.get_reg(rs).clone();
436                if let Some(local) = state.locals.get_mut(*index as usize) {
437                    *local = value;
438                }
439            }
440
441            ArmOp::LocalTee { rd, rs, index } => {
442                // Store register into local variable and also copy to destination
443                let value = state.get_reg(rs).clone();
444                if let Some(local) = state.locals.get_mut(*index as usize) {
445                    *local = value.clone();
446                }
447                state.set_reg(rd, value);
448            }
449
450            ArmOp::GlobalGet { rd, index } => {
451                // Load global variable into register
452                let value = state
453                    .globals
454                    .get(*index as usize)
455                    .cloned()
456                    .unwrap_or_else(|| BV::new_const(format!("global_{}", index), 32));
457                state.set_reg(rd, value);
458            }
459
460            ArmOp::GlobalSet { rs, index } => {
461                // Store register into global variable
462                let value = state.get_reg(rs).clone();
463                if let Some(global) = state.globals.get_mut(*index as usize) {
464                    *global = value;
465                }
466            }
467
468            ArmOp::BrTable {
469                rd,
470                index_reg,
471                targets,
472                default,
473            } => {
474                // Multi-way branch based on index
475                // For verification, we model the control flow symbolically
476                let _index = state.get_reg(index_reg).clone();
477                let result = BV::new_const(format!("br_table_{}_{}", targets.len(), default), 32);
478                state.set_reg(rd, result);
479            }
480
481            ArmOp::Call { rd, func_idx } => {
482                // Function call - model result symbolically
483                let result = BV::new_const(format!("call_{}", func_idx), 32);
484                state.set_reg(rd, result);
485            }
486
487            ArmOp::CallIndirect {
488                rd,
489                type_idx,
490                table_index_reg,
491                // #642: the bounds guard is a control-flow effect (trap), not
492                // modeled by the symbolic call result. #650: the table base
493                // offset only changes WHICH pointer is loaded, not the
494                // symbolic result shape. #664: the null check is likewise a
495                // trap (control-flow effect) on the loaded pointer.
496                table_size: _,
497                table_byte_offset: _,
498                null_check: _,
499                // #676: the runtime type check is likewise a trap
500                // (control-flow effect) on the sidecar-loaded class id.
501                type_check: _,
502            } => {
503                // Indirect function call through table
504                let _table_index = state.get_reg(table_index_reg).clone();
505                let result = BV::new_const(format!("call_indirect_{}", type_idx), 32);
506                state.set_reg(rd, result);
507            }
508
509            // ================================================================
510            // i64 Operations (Phase 2) - Simplified implementation
511            // ================================================================
512            // These use register pairs on ARM32 but simplified to single
513            // registers for initial implementation
514            ArmOp::I64Const { rdlo, rdhi, value } => {
515                // Load 64-bit constant into register pair
516                let low32 = (*value as u32) as i64;
517                let high32 = *value >> 32;
518                state.set_reg(rdlo, BV::from_i64(low32, 32));
519                state.set_reg(rdhi, BV::from_i64(high32, 32));
520            }
521
522            ArmOp::I64Add {
523                rdlo,
524                rdhi,
525                rnlo,
526                rnhi,
527                rmlo,
528                rmhi,
529            } => {
530                // 64-bit addition with register pairs and carry propagation
531                // ARM: ADDS rdlo, rnlo, rmlo  ; Add low parts, set carry
532                //      ADC  rdhi, rnhi, rmhi  ; Add high parts with carry
533
534                let n_low = state.get_reg(rnlo).clone();
535                let m_low = state.get_reg(rmlo).clone();
536                let n_high = state.get_reg(rnhi).clone();
537                let m_high = state.get_reg(rmhi).clone();
538
539                // Low part: simple addition
540                let result_low = n_low.bvadd(&m_low);
541                state.set_reg(rdlo, result_low.clone());
542
543                // Detect carry: overflow occurred if result < either operand
544                // For unsigned: carry = (result_low < n_low)
545                let carry = result_low.bvult(&n_low);
546                let carry_bv = carry.ite(BV::from_i64(1, 32), BV::from_i64(0, 32));
547
548                // High part: add with carry
549                let high_sum = n_high.bvadd(&m_high);
550                let result_high = high_sum.bvadd(&carry_bv);
551                state.set_reg(rdhi, result_high);
552            }
553
554            ArmOp::I64Eqz { rd, rnlo, rnhi } => {
555                // Check if 64-bit value is zero
556                // True if both low and high parts are zero
557                let zero = BV::from_i64(0, 32);
558                let low_zero = state.get_reg(rnlo).eq(&zero);
559                let high_zero = state.get_reg(rnhi).eq(&zero);
560                let both_zero = Bool::and(&[&low_zero, &high_zero]);
561                let result = self.bool_to_bv32(&both_zero);
562                state.set_reg(rd, result);
563            }
564
565            ArmOp::I32WrapI64 { rd, rnlo } => {
566                // Wrap 64-bit to 32-bit (take low 32 bits)
567                let low_val = state.get_reg(rnlo).clone();
568                state.set_reg(rd, low_val);
569            }
570
571            ArmOp::I64ExtendI32S { rdlo, rdhi, rn } => {
572                // Sign-extend 32-bit to 64-bit
573                let value = state.get_reg(rn).clone();
574                state.set_reg(rdlo, value.clone());
575
576                // High part is sign extension (all 0s or all 1s based on sign bit)
577                let sign_bit = value.extract(31, 31); // Extract bit 31
578                let all_ones = BV::from_i64(-1, 32);
579                let zero = BV::from_i64(0, 32);
580                // If sign bit is 1, high = 0xFFFFFFFF, else high = 0
581                let high_val = sign_bit.eq(BV::from_i64(1, 1)).ite(&all_ones, &zero);
582                state.set_reg(rdhi, high_val);
583            }
584
585            ArmOp::I64ExtendI32U { rdlo, rdhi, rn } => {
586                // Zero-extend 32-bit to 64-bit
587                let value = state.get_reg(rn).clone();
588                state.set_reg(rdlo, value);
589                // High part is always zero for unsigned extend
590                state.set_reg(rdhi, BV::from_i64(0, 32));
591            }
592
593            ArmOp::I64Sub {
594                rdlo,
595                rdhi,
596                rnlo,
597                rnhi,
598                rmlo,
599                rmhi,
600            } => {
601                // 64-bit subtraction with register pairs and borrow propagation
602                // ARM: SUBS rdlo, rnlo, rmlo  ; Subtract low parts, set borrow
603                //      SBC  rdhi, rnhi, rmhi  ; Subtract high parts with borrow
604
605                let n_low = state.get_reg(rnlo).clone();
606                let m_low = state.get_reg(rmlo).clone();
607                let n_high = state.get_reg(rnhi).clone();
608                let m_high = state.get_reg(rmhi).clone();
609
610                // Low part: simple subtraction
611                let result_low = n_low.bvsub(&m_low);
612                state.set_reg(rdlo, result_low.clone());
613
614                // Detect borrow: borrow occurred if n_low < m_low (unsigned)
615                let borrow = n_low.bvult(&m_low);
616                let borrow_bv = borrow.ite(BV::from_i64(1, 32), BV::from_i64(0, 32));
617
618                // High part: subtract with borrow
619                let high_diff = n_high.bvsub(&m_high);
620                let result_high = high_diff.bvsub(&borrow_bv);
621                state.set_reg(rdhi, result_high);
622            }
623
624            ArmOp::I64Mul {
625                rd_lo,
626                rd_hi,
627                rn_lo,
628                rn_hi,
629                rm_lo,
630                rm_hi,
631            } => {
632                // 64-bit multiplication: (a_hi:a_lo) * (b_hi:b_lo) → (result_hi:result_lo)
633                // Algorithm for 64x64→64 bit multiplication:
634                // result = (a_hi * b_lo * 2^32) + (a_lo * b_hi * 2^32) + (a_lo * b_lo)
635                // Only the low 64 bits are kept
636
637                let a_lo = state.get_reg(rn_lo).clone();
638                let a_hi = state.get_reg(rn_hi).clone();
639                let b_lo = state.get_reg(rm_lo).clone();
640                let b_hi = state.get_reg(rm_hi).clone();
641
642                // Low part: a_lo * b_lo (32x32→64, we need both parts)
643                // For SMT, we can use bvmul which gives 32-bit result (truncated)
644                let lo_lo = a_lo.bvmul(&b_lo);
645                state.set_reg(rd_lo, lo_lo.clone());
646
647                // For the high part, we need to handle overflow from a_lo * b_lo
648                // and add the cross products: a_hi * b_lo + a_lo * b_hi
649                //
650                // Simplified approach: use symbolic representation for now
651                // TODO: Implement full 64-bit multiplication with proper overflow handling
652                // This requires 64-bit bitvector intermediate computations
653
654                // Cross products (take low 32 bits of each)
655                let hi_lo = a_hi.bvmul(&b_lo); // a_hi * b_lo (low 32 bits)
656                let lo_hi = a_lo.bvmul(&b_hi); // a_lo * b_hi (low 32 bits)
657
658                // High part approximation (missing carry from a_lo * b_lo)
659                // result_hi ≈ hi_lo + lo_hi
660                let hi_sum = hi_lo.bvadd(&lo_hi);
661                state.set_reg(rd_hi, hi_sum);
662
663                // Note: This is a simplified implementation. A complete implementation
664                // would need to:
665                // 1. Extract high 32 bits of (a_lo * b_lo)
666                // 2. Add that to the cross products
667                // 3. Handle carries properly
668            }
669
670            // ========================================================================
671            // i64 Division and Remainder
672            // ========================================================================
673            // Note: Full 64-bit division on ARM32 requires library calls or
674            // very complex multi-instruction sequences. For verification, we model
675            // the results symbolically.
676            ArmOp::I64DivS { rdlo, rdhi, .. } => {
677                // Signed 64-bit division
678                // Real implementation would require __aeabi_ldivmod or equivalent
679                // For verification, return symbolic values
680                state.set_reg(rdlo, BV::new_const("i64_divs_lo", 32));
681                state.set_reg(rdhi, BV::new_const("i64_divs_hi", 32));
682            }
683
684            ArmOp::I64DivU { rdlo, rdhi, .. } => {
685                // Unsigned 64-bit division
686                // Real implementation would require __aeabi_uldivmod or equivalent
687                // For verification, return symbolic values
688                state.set_reg(rdlo, BV::new_const("i64_divu_lo", 32));
689                state.set_reg(rdhi, BV::new_const("i64_divu_hi", 32));
690            }
691
692            ArmOp::I64RemS {
693                rdlo,
694                rdhi,
695                rnlo,
696                rnhi,
697                rmlo,
698                rmhi,
699                ..
700            } => {
701                // Signed 64-bit remainder (modulo). Same shape as I64RemU but
702                // the SIGNED remainder (`bvsrem`, SMT-LIB sign-of-dividend). The
703                // shipped lowering is an `__aeabi_ldivmod` library call; the
704                // value it must produce is exactly the native 64-bit signed
705                // remainder. The value VC (`verify_i64_rem_value_preservation`)
706                // asserts the R0:R1 pair equals it on the non-trapping path.
707                // rem_s traps ONLY on ÷0 (`rem_s(INT64_MIN,-1) == 0`, no
708                // overflow trap).
709                let n_lo = state.get_reg(rnlo).clone();
710                let n_hi = state.get_reg(rnhi).clone();
711                let m_lo = state.get_reg(rmlo).clone();
712                let m_hi = state.get_reg(rmhi).clone();
713
714                let dividend = n_hi.concat(&n_lo); // 64-bit: n_hi:n_lo
715                let divisor = m_hi.concat(&m_lo); // 64-bit: m_hi:m_lo
716                let rem = dividend.bvsrem(&divisor); // native signed rem, 64-bit
717
718                state.set_reg(rdlo, rem.extract(31, 0));
719                state.set_reg(rdhi, rem.extract(63, 32));
720            }
721
722            ArmOp::I64RemU {
723                rdlo,
724                rdhi,
725                rnlo,
726                rnhi,
727                rmlo,
728                rmhi,
729                ..
730            } => {
731                // Unsigned 64-bit remainder (modulo). ARM32 has no 64-bit
732                // divide instruction — the shipped lowering expands this
733                // pseudo-op to an `__aeabi_uldivmod` library call — but for
734                // translation-validation the *value* the call must produce is
735                // exactly the native 64-bit unsigned remainder. Model it with
736                // the native `BvTerm::Urem` (ordeal 0.12, plumbed via
737                // `BV::bvurem`) instead of a HAVOC constant, so the value VC
738                // (`verify_i64_rem_value_preservation`) proves the register
739                // pair actually equals `dividend % divisor`.
740                //
741                // Compose the 64-bit operands from their register halves
742                // (`concat` puts self in the HIGH bits), take the 64-bit
743                // unsigned remainder, and split back to lo/hi. On a zero
744                // divisor SMT-LIB `bvurem` is total (returns the dividend),
745                // but WASM traps — the value clause is asserted only on the
746                // non-trapping path by the trap-guarded value VC, so this
747                // total model is sound.
748                let n_lo = state.get_reg(rnlo).clone();
749                let n_hi = state.get_reg(rnhi).clone();
750                let m_lo = state.get_reg(rmlo).clone();
751                let m_hi = state.get_reg(rmhi).clone();
752
753                let dividend = n_hi.concat(&n_lo); // 64-bit: n_hi:n_lo
754                let divisor = m_hi.concat(&m_lo); // 64-bit: m_hi:m_lo
755                let rem = dividend.bvurem(&divisor); // native bvurem, 64-bit
756
757                state.set_reg(rdlo, rem.extract(31, 0)); // low 32 bits
758                state.set_reg(rdhi, rem.extract(63, 32)); // high 32 bits
759            }
760
761            ArmOp::I64And {
762                rdlo,
763                rdhi,
764                rnlo,
765                rnhi,
766                rmlo,
767                rmhi,
768            } => {
769                let n_low = state.get_reg(rnlo).clone();
770                let m_low = state.get_reg(rmlo).clone();
771                state.set_reg(rdlo, n_low.bvand(&m_low));
772
773                let n_high = state.get_reg(rnhi).clone();
774                let m_high = state.get_reg(rmhi).clone();
775                state.set_reg(rdhi, n_high.bvand(&m_high));
776            }
777
778            ArmOp::I64Or {
779                rdlo,
780                rdhi,
781                rnlo,
782                rnhi,
783                rmlo,
784                rmhi,
785            } => {
786                let n_low = state.get_reg(rnlo).clone();
787                let m_low = state.get_reg(rmlo).clone();
788                state.set_reg(rdlo, n_low.bvor(&m_low));
789
790                let n_high = state.get_reg(rnhi).clone();
791                let m_high = state.get_reg(rmhi).clone();
792                state.set_reg(rdhi, n_high.bvor(&m_high));
793            }
794
795            ArmOp::I64Xor {
796                rdlo,
797                rdhi,
798                rnlo,
799                rnhi,
800                rmlo,
801                rmhi,
802            } => {
803                let n_low = state.get_reg(rnlo).clone();
804                let m_low = state.get_reg(rmlo).clone();
805                state.set_reg(rdlo, n_low.bvxor(&m_low));
806
807                let n_high = state.get_reg(rnhi).clone();
808                let m_high = state.get_reg(rmhi).clone();
809                state.set_reg(rdhi, n_high.bvxor(&m_high));
810            }
811
812            ArmOp::I64Eq {
813                rd,
814                rnlo,
815                rnhi,
816                rmlo,
817                rmhi,
818            } => {
819                let n_low = state.get_reg(rnlo).clone();
820                let m_low = state.get_reg(rmlo).clone();
821                let n_high = state.get_reg(rnhi).clone();
822                let m_high = state.get_reg(rmhi).clone();
823
824                let low_eq = n_low.eq(&m_low);
825                let high_eq = n_high.eq(&m_high);
826                let both_eq = Bool::and(&[&low_eq, &high_eq]);
827                let result = self.bool_to_bv32(&both_eq);
828                state.set_reg(rd, result);
829            }
830
831            ArmOp::I64LtS {
832                rd,
833                rnlo,
834                rnhi,
835                rmlo,
836                rmhi,
837            } => {
838                // Signed less than: n < m
839                // Compare high parts first (signed), tiebreak with low parts (unsigned)
840                let n_low = state.get_reg(rnlo).clone();
841                let m_low = state.get_reg(rmlo).clone();
842                let n_high = state.get_reg(rnhi).clone();
843                let m_high = state.get_reg(rmhi).clone();
844
845                // High parts comparison (signed)
846                let high_lt = n_high.bvslt(&m_high);
847                let high_eq = n_high.eq(&m_high);
848
849                // Low parts comparison (unsigned)
850                let low_lt = n_low.bvult(&m_low);
851
852                // Result: high_lt OR (high_eq AND low_lt)
853                let eq_and_low = Bool::and(&[&high_eq, &low_lt]);
854                let result_bool = Bool::or(&[&high_lt, &eq_and_low]);
855                let result = self.bool_to_bv32(&result_bool);
856                state.set_reg(rd, result);
857            }
858
859            ArmOp::I64LtU {
860                rd,
861                rnlo,
862                rnhi,
863                rmlo,
864                rmhi,
865            } => {
866                // Unsigned less than: n < m
867                // Compare high parts first (unsigned), tiebreak with low parts (unsigned)
868                let n_low = state.get_reg(rnlo).clone();
869                let m_low = state.get_reg(rmlo).clone();
870                let n_high = state.get_reg(rnhi).clone();
871                let m_high = state.get_reg(rmhi).clone();
872
873                // High parts comparison (unsigned)
874                let high_lt = n_high.bvult(&m_high);
875                let high_eq = n_high.eq(&m_high);
876
877                // Low parts comparison (unsigned)
878                let low_lt = n_low.bvult(&m_low);
879
880                // Result: high_lt OR (high_eq AND low_lt)
881                let eq_and_low = Bool::and(&[&high_eq, &low_lt]);
882                let result_bool = Bool::or(&[&high_lt, &eq_and_low]);
883                let result = self.bool_to_bv32(&result_bool);
884                state.set_reg(rd, result);
885            }
886
887            ArmOp::I64Ne {
888                rd,
889                rnlo,
890                rnhi,
891                rmlo,
892                rmhi,
893            } => {
894                // Not equal: !(n == m)
895                let n_low = state.get_reg(rnlo).clone();
896                let m_low = state.get_reg(rmlo).clone();
897                let n_high = state.get_reg(rnhi).clone();
898                let m_high = state.get_reg(rmhi).clone();
899
900                let low_eq = n_low.eq(&m_low);
901                let high_eq = n_high.eq(&m_high);
902                let both_eq = Bool::and(&[&low_eq, &high_eq]);
903                let not_eq = both_eq.not();
904                let result = self.bool_to_bv32(&not_eq);
905                state.set_reg(rd, result);
906            }
907
908            ArmOp::I64LeS {
909                rd,
910                rnlo,
911                rnhi,
912                rmlo,
913                rmhi,
914            } => {
915                // Signed less than or equal: n <= m
916                // Equivalent to: n < m OR n == m
917                let n_low = state.get_reg(rnlo).clone();
918                let m_low = state.get_reg(rmlo).clone();
919                let n_high = state.get_reg(rnhi).clone();
920                let m_high = state.get_reg(rmhi).clone();
921
922                let high_lt = n_high.bvslt(&m_high);
923                let high_eq = n_high.eq(&m_high);
924                let low_le = n_low.bvule(&m_low); // Low parts unsigned LE
925
926                let eq_and_le = Bool::and(&[&high_eq, &low_le]);
927                let result_bool = Bool::or(&[&high_lt, &eq_and_le]);
928                let result = self.bool_to_bv32(&result_bool);
929                state.set_reg(rd, result);
930            }
931
932            ArmOp::I64LeU {
933                rd,
934                rnlo,
935                rnhi,
936                rmlo,
937                rmhi,
938            } => {
939                // Unsigned less than or equal: n <= m
940                let n_low = state.get_reg(rnlo).clone();
941                let m_low = state.get_reg(rmlo).clone();
942                let n_high = state.get_reg(rnhi).clone();
943                let m_high = state.get_reg(rmhi).clone();
944
945                let high_lt = n_high.bvult(&m_high);
946                let high_eq = n_high.eq(&m_high);
947                let low_le = n_low.bvule(&m_low);
948
949                let eq_and_le = Bool::and(&[&high_eq, &low_le]);
950                let result_bool = Bool::or(&[&high_lt, &eq_and_le]);
951                let result = self.bool_to_bv32(&result_bool);
952                state.set_reg(rd, result);
953            }
954
955            ArmOp::I64GtS {
956                rd,
957                rnlo,
958                rnhi,
959                rmlo,
960                rmhi,
961            } => {
962                // Signed greater than: n > m
963                // Equivalent to: m < n
964                let n_low = state.get_reg(rnlo).clone();
965                let m_low = state.get_reg(rmlo).clone();
966                let n_high = state.get_reg(rnhi).clone();
967                let m_high = state.get_reg(rmhi).clone();
968
969                let high_gt = n_high.bvsgt(&m_high);
970                let high_eq = n_high.eq(&m_high);
971                let low_gt = n_low.bvugt(&m_low); // Low parts unsigned GT
972
973                let eq_and_gt = Bool::and(&[&high_eq, &low_gt]);
974                let result_bool = Bool::or(&[&high_gt, &eq_and_gt]);
975                let result = self.bool_to_bv32(&result_bool);
976                state.set_reg(rd, result);
977            }
978
979            ArmOp::I64GtU {
980                rd,
981                rnlo,
982                rnhi,
983                rmlo,
984                rmhi,
985            } => {
986                // Unsigned greater than: n > m
987                let n_low = state.get_reg(rnlo).clone();
988                let m_low = state.get_reg(rmlo).clone();
989                let n_high = state.get_reg(rnhi).clone();
990                let m_high = state.get_reg(rmhi).clone();
991
992                let high_gt = n_high.bvugt(&m_high);
993                let high_eq = n_high.eq(&m_high);
994                let low_gt = n_low.bvugt(&m_low);
995
996                let eq_and_gt = Bool::and(&[&high_eq, &low_gt]);
997                let result_bool = Bool::or(&[&high_gt, &eq_and_gt]);
998                let result = self.bool_to_bv32(&result_bool);
999                state.set_reg(rd, result);
1000            }
1001
1002            ArmOp::I64GeS {
1003                rd,
1004                rnlo,
1005                rnhi,
1006                rmlo,
1007                rmhi,
1008            } => {
1009                // Signed greater than or equal: n >= m
1010                // Equivalent to: !(n < m)
1011                let n_low = state.get_reg(rnlo).clone();
1012                let m_low = state.get_reg(rmlo).clone();
1013                let n_high = state.get_reg(rnhi).clone();
1014                let m_high = state.get_reg(rmhi).clone();
1015
1016                let high_lt = n_high.bvslt(&m_high);
1017                let high_eq = n_high.eq(&m_high);
1018                let low_lt = n_low.bvult(&m_low);
1019
1020                let eq_and_lt = Bool::and(&[&high_eq, &low_lt]);
1021                let lt_bool = Bool::or(&[&high_lt, &eq_and_lt]);
1022                let result_bool = lt_bool.not(); // GE is !(LT)
1023                let result = self.bool_to_bv32(&result_bool);
1024                state.set_reg(rd, result);
1025            }
1026
1027            ArmOp::I64GeU {
1028                rd,
1029                rnlo,
1030                rnhi,
1031                rmlo,
1032                rmhi,
1033            } => {
1034                // Unsigned greater than or equal: n >= m
1035                // Equivalent to: !(n < m)
1036                let n_low = state.get_reg(rnlo).clone();
1037                let m_low = state.get_reg(rmlo).clone();
1038                let n_high = state.get_reg(rnhi).clone();
1039                let m_high = state.get_reg(rmhi).clone();
1040
1041                let high_lt = n_high.bvult(&m_high);
1042                let high_eq = n_high.eq(&m_high);
1043                let low_lt = n_low.bvult(&m_low);
1044
1045                let eq_and_lt = Bool::and(&[&high_eq, &low_lt]);
1046                let lt_bool = Bool::or(&[&high_lt, &eq_and_lt]);
1047                let result_bool = lt_bool.not(); // GE is !(LT)
1048                let result = self.bool_to_bv32(&result_bool);
1049                state.set_reg(rd, result);
1050            }
1051
1052            // ================================================================
1053            // i64 Shift Operations
1054            // ================================================================
1055            ArmOp::I64Shl {
1056                rd_lo,
1057                rd_hi,
1058                rn_lo,
1059                rn_hi,
1060                rm_lo,
1061                rm_hi: _,
1062            } => {
1063                // 64-bit left shift: (n_hi:n_lo) << shift
1064                // WASM spec: shift amount is modulo 64
1065                let n_lo = state.get_reg(rn_lo).clone();
1066                let n_hi = state.get_reg(rn_hi).clone();
1067                let shift_amt = state.get_reg(rm_lo).clone();
1068
1069                // Modulo 64: shift_amt = shift_amt & 63
1070                let shift_mod = shift_amt.bvand(BV::from_i64(63, 32));
1071
1072                // If shift < 32: normal shift with bits moving from low to high
1073                // If shift >= 32: low becomes 0, high gets shifted low part
1074                let shift_32 = BV::from_i64(32, 32);
1075                let is_large = shift_mod.bvuge(&shift_32); // shift >= 32
1076
1077                // Small shift (< 32):
1078                // result_lo = n_lo << shift
1079                // result_hi = (n_hi << shift) | (n_lo >> (32 - shift))
1080                let result_lo_small = n_lo.bvshl(&shift_mod);
1081                let shift_complement = shift_32.bvsub(&shift_mod);
1082                let bits_to_high = n_lo.bvlshr(&shift_complement);
1083                let result_hi_small = n_hi.bvshl(&shift_mod).bvor(&bits_to_high);
1084
1085                // Large shift (>= 32):
1086                // result_lo = 0
1087                // result_hi = n_lo << (shift - 32)
1088                let zero = BV::from_i64(0, 32);
1089                let shift_minus_32 = shift_mod.bvsub(&shift_32);
1090                let result_lo_large = zero.clone();
1091                let result_hi_large = n_lo.bvshl(&shift_minus_32);
1092
1093                // Select based on shift size
1094                let result_lo = is_large.ite(&result_lo_large, &result_lo_small);
1095                let result_hi = is_large.ite(&result_hi_large, &result_hi_small);
1096
1097                state.set_reg(rd_lo, result_lo);
1098                state.set_reg(rd_hi, result_hi);
1099            }
1100
1101            ArmOp::I64ShrU {
1102                rd_lo,
1103                rd_hi,
1104                rn_lo,
1105                rn_hi,
1106                rm_lo,
1107                rm_hi: _,
1108            } => {
1109                // 64-bit logical (unsigned) right shift
1110                let n_lo = state.get_reg(rn_lo).clone();
1111                let n_hi = state.get_reg(rn_hi).clone();
1112                let shift_amt = state.get_reg(rm_lo).clone();
1113
1114                let shift_mod = shift_amt.bvand(BV::from_i64(63, 32));
1115                let shift_32 = BV::from_i64(32, 32);
1116                let is_large = shift_mod.bvuge(&shift_32);
1117
1118                // Small shift (< 32):
1119                // result_hi = n_hi >> shift
1120                // result_lo = (n_lo >> shift) | (n_hi << (32 - shift))
1121                let result_hi_small = n_hi.bvlshr(&shift_mod);
1122                let shift_complement = shift_32.bvsub(&shift_mod);
1123                let bits_to_low = n_hi.bvshl(&shift_complement);
1124                let result_lo_small = n_lo.bvlshr(&shift_mod).bvor(&bits_to_low);
1125
1126                // Large shift (>= 32):
1127                // result_hi = 0
1128                // result_lo = n_hi >> (shift - 32)
1129                let zero = BV::from_i64(0, 32);
1130                let shift_minus_32 = shift_mod.bvsub(&shift_32);
1131                let result_hi_large = zero.clone();
1132                let result_lo_large = n_hi.bvlshr(&shift_minus_32);
1133
1134                let result_lo = is_large.ite(&result_lo_large, &result_lo_small);
1135                let result_hi = is_large.ite(&result_hi_large, &result_hi_small);
1136
1137                state.set_reg(rd_lo, result_lo);
1138                state.set_reg(rd_hi, result_hi);
1139            }
1140
1141            ArmOp::I64ShrS {
1142                rd_lo,
1143                rd_hi,
1144                rn_lo,
1145                rn_hi,
1146                rm_lo,
1147                rm_hi: _,
1148            } => {
1149                // 64-bit arithmetic (signed) right shift
1150                let n_lo = state.get_reg(rn_lo).clone();
1151                let n_hi = state.get_reg(rn_hi).clone();
1152                let shift_amt = state.get_reg(rm_lo).clone();
1153
1154                let shift_mod = shift_amt.bvand(BV::from_i64(63, 32));
1155                let shift_32 = BV::from_i64(32, 32);
1156                let is_large = shift_mod.bvuge(&shift_32);
1157
1158                // Small shift (< 32):
1159                // result_hi = n_hi >> shift (arithmetic)
1160                // result_lo = (n_lo >> shift) | (n_hi << (32 - shift))
1161                let result_hi_small = n_hi.bvashr(&shift_mod);
1162                let shift_complement = shift_32.bvsub(&shift_mod);
1163                let bits_to_low = n_hi.bvshl(&shift_complement);
1164                let result_lo_small = n_lo.bvlshr(&shift_mod).bvor(&bits_to_low);
1165
1166                // Large shift (>= 32):
1167                // result_hi = n_hi >> 31 (sign extension: all 0s or all 1s)
1168                // result_lo = n_hi >> (shift - 32) (arithmetic)
1169                let shift_31 = BV::from_i64(31, 32);
1170                let result_hi_large = n_hi.bvashr(&shift_31);
1171                let shift_minus_32 = shift_mod.bvsub(&shift_32);
1172                let result_lo_large = n_hi.bvashr(&shift_minus_32);
1173
1174                let result_lo = is_large.ite(&result_lo_large, &result_lo_small);
1175                let result_hi = is_large.ite(&result_hi_large, &result_hi_small);
1176
1177                state.set_reg(rd_lo, result_lo);
1178                state.set_reg(rd_hi, result_hi);
1179            }
1180
1181            // ========================================================================
1182            // i64 Rotation Operations
1183            // ========================================================================
1184            ArmOp::I64Rotl {
1185                rdlo,
1186                rdhi,
1187                rnlo,
1188                rnhi,
1189                shift,
1190            } => {
1191                // 64-bit rotate left: rotl(hi:lo, shift)
1192                // Result = (value << shift) | (value >> (64 - shift))
1193                let n_lo = state.get_reg(rnlo).clone();
1194                let n_hi = state.get_reg(rnhi).clone();
1195                let shift_amt = state.get_reg(shift).clone();
1196
1197                // Normalize shift to 0-63 range
1198                let shift_mod = shift_amt.bvand(BV::from_i64(63, 32));
1199                let shift_32 = BV::from_i64(32, 32);
1200                let is_large = shift_mod.bvuge(&shift_32); // shift >= 32
1201
1202                // For shift < 32:
1203                // result_lo = (n_lo << shift) | (n_hi >> (32 - shift))
1204                // result_hi = (n_hi << shift) | (n_lo >> (32 - shift))
1205                let shift_complement = shift_32.bvsub(&shift_mod);
1206
1207                let lo_shifted_left = n_lo.bvshl(&shift_mod);
1208                let hi_bits_to_lo = n_hi.bvlshr(&shift_complement);
1209                let result_lo_small = lo_shifted_left.bvor(&hi_bits_to_lo);
1210
1211                let hi_shifted_left = n_hi.bvshl(&shift_mod);
1212                let lo_bits_to_hi = n_lo.bvlshr(&shift_complement);
1213                let result_hi_small = hi_shifted_left.bvor(&lo_bits_to_hi);
1214
1215                // For shift >= 32:
1216                // Swap and rotate by (shift - 32)
1217                let shift_minus_32 = shift_mod.bvsub(&shift_32);
1218                let complement_large = shift_32.bvsub(&shift_minus_32);
1219
1220                let hi_shifted_left_large = n_hi.bvshl(&shift_minus_32);
1221                let lo_bits_to_hi_large = n_lo.bvlshr(&complement_large);
1222                let result_lo_large = hi_shifted_left_large.bvor(&lo_bits_to_hi_large);
1223
1224                let lo_shifted_left_large = n_lo.bvshl(&shift_minus_32);
1225                let hi_bits_to_lo_large = n_hi.bvlshr(&complement_large);
1226                let result_hi_large = lo_shifted_left_large.bvor(&hi_bits_to_lo_large);
1227
1228                // Select based on shift size
1229                let result_lo = is_large.ite(&result_lo_large, &result_lo_small);
1230                let result_hi = is_large.ite(&result_hi_large, &result_hi_small);
1231
1232                state.set_reg(rdlo, result_lo);
1233                state.set_reg(rdhi, result_hi);
1234            }
1235
1236            ArmOp::I64Rotr {
1237                rdlo,
1238                rdhi,
1239                rnlo,
1240                rnhi,
1241                shift,
1242            } => {
1243                // 64-bit rotate right: rotr(hi:lo, shift)
1244                // Result = (value >> shift) | (value << (64 - shift))
1245                let n_lo = state.get_reg(rnlo).clone();
1246                let n_hi = state.get_reg(rnhi).clone();
1247                let shift_amt = state.get_reg(shift).clone();
1248
1249                // Normalize shift to 0-63 range
1250                let shift_mod = shift_amt.bvand(BV::from_i64(63, 32));
1251                let shift_32 = BV::from_i64(32, 32);
1252                let is_large = shift_mod.bvuge(&shift_32); // shift >= 32
1253
1254                // For shift < 32:
1255                // result_lo = (n_lo >> shift) | (n_hi << (32 - shift))
1256                // result_hi = (n_hi >> shift) | (n_lo << (32 - shift))
1257                let shift_complement = shift_32.bvsub(&shift_mod);
1258
1259                let lo_shifted_right = n_lo.bvlshr(&shift_mod);
1260                let hi_bits_to_lo = n_hi.bvshl(&shift_complement);
1261                let result_lo_small = lo_shifted_right.bvor(&hi_bits_to_lo);
1262
1263                let hi_shifted_right = n_hi.bvlshr(&shift_mod);
1264                let lo_bits_to_hi = n_lo.bvshl(&shift_complement);
1265                let result_hi_small = hi_shifted_right.bvor(&lo_bits_to_hi);
1266
1267                // For shift >= 32:
1268                // Swap and rotate by (shift - 32)
1269                let shift_minus_32 = shift_mod.bvsub(&shift_32);
1270                let complement_large = shift_32.bvsub(&shift_minus_32);
1271
1272                let hi_shifted_right_large = n_hi.bvlshr(&shift_minus_32);
1273                let lo_bits_to_hi_large = n_lo.bvshl(&complement_large);
1274                let result_lo_large = hi_shifted_right_large.bvor(&lo_bits_to_hi_large);
1275
1276                let lo_shifted_right_large = n_lo.bvlshr(&shift_minus_32);
1277                let hi_bits_to_lo_large = n_hi.bvshl(&complement_large);
1278                let result_hi_large = lo_shifted_right_large.bvor(&hi_bits_to_lo_large);
1279
1280                // Select based on shift size
1281                let result_lo = is_large.ite(&result_lo_large, &result_lo_small);
1282                let result_hi = is_large.ite(&result_hi_large, &result_hi_small);
1283
1284                state.set_reg(rdlo, result_lo);
1285                state.set_reg(rdhi, result_hi);
1286            }
1287
1288            ArmOp::I64Clz { rd, rnlo, rnhi } => {
1289                // Count leading zeros for 64-bit value
1290                // If high part has zeros, result = clz(high) + clz(low)
1291                // If high part is zero, result = 32 + clz(low)
1292                let n_lo = state.get_reg(rnlo).clone();
1293                let n_hi = state.get_reg(rnhi).clone();
1294
1295                let hi_clz = self.encode_clz(&n_hi);
1296                let lo_clz = self.encode_clz(&n_lo);
1297
1298                // If high == 32 (all zeros), add low clz; else use high clz
1299                let thirty_two = BV::from_i64(32, 32);
1300                let hi_is_zero = hi_clz.eq(&thirty_two);
1301                let result = hi_is_zero.ite(
1302                    thirty_two.bvadd(&lo_clz), // High is zero: 32 + clz(low)
1303                    &hi_clz,                   // High has bits: clz(high)
1304                );
1305                state.set_reg(rd, result);
1306            }
1307
1308            ArmOp::I64Ctz { rd, rnlo, rnhi } => {
1309                // Count trailing zeros for 64-bit value
1310                // If low part is zero, result = 32 + ctz(high)
1311                // Else result = ctz(low)
1312                let n_lo = state.get_reg(rnlo).clone();
1313                let n_hi = state.get_reg(rnhi).clone();
1314
1315                let lo_ctz = self.encode_ctz(&n_lo);
1316                let hi_ctz = self.encode_ctz(&n_hi);
1317
1318                // If low == 32 (all zeros), add high ctz; else use low ctz
1319                let thirty_two = BV::from_i64(32, 32);
1320                let lo_is_zero = lo_ctz.eq(&thirty_two);
1321                let result = lo_is_zero.ite(
1322                    thirty_two.bvadd(&hi_ctz), // Low is zero: 32 + ctz(high)
1323                    &lo_ctz,                   // Low has bits: ctz(low)
1324                );
1325                state.set_reg(rd, result);
1326            }
1327
1328            ArmOp::I64Popcnt { rd, rnlo, rnhi } => {
1329                // Population count for 64-bit value
1330                // Result = popcnt(low) + popcnt(high)
1331                let n_lo = state.get_reg(rnlo).clone();
1332                let n_hi = state.get_reg(rnhi).clone();
1333
1334                let lo_popcnt = self.encode_popcnt(&n_lo);
1335                let hi_popcnt = self.encode_popcnt(&n_hi);
1336
1337                let result = lo_popcnt.bvadd(&hi_popcnt);
1338                state.set_reg(rd, result);
1339            }
1340
1341            // ========================================================================
1342            // i64 Memory Operations
1343            // ========================================================================
1344            ArmOp::I64Ldr { rdlo, rdhi, addr } => {
1345                // Load 64-bit value from memory
1346                // Simplified: return symbolic values for both registers
1347                // Real implementation would load from memory at [addr] and [addr+4]
1348                let result_lo = BV::new_const(format!("i64load_lo_{:?}", addr), 32);
1349                let result_hi = BV::new_const(format!("i64load_hi_{:?}", addr), 32);
1350                state.set_reg(rdlo, result_lo);
1351                state.set_reg(rdhi, result_hi);
1352            }
1353
1354            ArmOp::I64Str {
1355                rdlo: _,
1356                rdhi: _,
1357                addr: _,
1358            } => {
1359                // Store 64-bit value to memory
1360                // Simplified: memory updates not fully modeled yet
1361                // Real implementation would store rdlo to [addr] and rdhi to [addr+4]
1362                // No register changes - store operation has no output
1363            }
1364
1365            // ========================================================================
1366            // f32 Operations (Phase 2 - Floating Point)
1367            // ========================================================================
1368            // Note: f32 values are represented as 32-bit bitvectors (IEEE 754 format)
1369            // For verification, we use symbolic bitvector operations
1370            // A complete implementation would use Z3's FloatingPoint sort
1371
1372            // f32 Constants
1373            ArmOp::F32Const { sd, value } => {
1374                // Load f32 constant (represented as 32-bit bitvector)
1375                // Convert f32 to its IEEE 754 bit representation
1376                let bits = value.to_bits() as i64;
1377                let bv_val = BV::from_i64(bits, 32);
1378                state.set_vfp_reg(sd, bv_val);
1379            }
1380
1381            // f32 Arithmetic (symbolic for verification)
1382            ArmOp::F32Add { sd, sn, sm } => {
1383                // f32 addition: sd = sn + sm
1384                // For verification, return symbolic value
1385                // Full implementation would use Z3 FloatingPoint operations
1386                let result = BV::new_const(format!("f32_add_{:?}_{:?}", sn, sm), 32);
1387                state.set_vfp_reg(sd, result);
1388            }
1389
1390            ArmOp::F32Sub { sd, sn, sm } => {
1391                // f32 subtraction: sd = sn - sm
1392                let result = BV::new_const(format!("f32_sub_{:?}_{:?}", sn, sm), 32);
1393                state.set_vfp_reg(sd, result);
1394            }
1395
1396            ArmOp::F32Mul { sd, sn, sm } => {
1397                // f32 multiplication: sd = sn * sm
1398                let result = BV::new_const(format!("f32_mul_{:?}_{:?}", sn, sm), 32);
1399                state.set_vfp_reg(sd, result);
1400            }
1401
1402            ArmOp::F32Div { sd, sn, sm } => {
1403                // f32 division: sd = sn / sm
1404                let result = BV::new_const(format!("f32_div_{:?}_{:?}", sn, sm), 32);
1405                state.set_vfp_reg(sd, result);
1406            }
1407
1408            // f32 Simple Math
1409            ArmOp::F32Abs { sd, sm } => {
1410                // f32 absolute value: sd = |sm|
1411                // Clear the sign bit (bit 31)
1412                let val = state.get_vfp_reg(sm).clone();
1413                let mask = BV::from_u64(0x7FFFFFFF, 32); // Clear sign bit
1414                let result = val.bvand(&mask);
1415                state.set_vfp_reg(sd, result);
1416            }
1417
1418            ArmOp::F32Neg { sd, sm } => {
1419                // f32 negation: sd = -sm
1420                // Flip the sign bit (bit 31)
1421                let val = state.get_vfp_reg(sm).clone();
1422                let mask = BV::from_u64(0x80000000, 32); // Sign bit
1423                let result = val.bvxor(&mask);
1424                state.set_vfp_reg(sd, result);
1425            }
1426
1427            ArmOp::F32Sqrt { sd, sm } => {
1428                // f32 square root: sd = sqrt(sm)
1429                // Symbolic representation for verification
1430                let result = BV::new_const(format!("f32_sqrt_{:?}", sm), 32);
1431                state.set_vfp_reg(sd, result);
1432            }
1433
1434            ArmOp::F32Min { sd, sn, sm } => {
1435                // f32 minimum: sd = min(sn, sm)
1436                // IEEE 754 semantics: NaN propagation, -0.0 < +0.0
1437                // Symbolic representation for verification
1438                let result = BV::new_const(format!("f32_min_{:?}_{:?}", sn, sm), 32);
1439                state.set_vfp_reg(sd, result);
1440            }
1441
1442            ArmOp::F32Max { sd, sn, sm } => {
1443                // f32 maximum: sd = max(sn, sm)
1444                // IEEE 754 semantics: NaN propagation, +0.0 > -0.0
1445                // Symbolic representation for verification
1446                let result = BV::new_const(format!("f32_max_{:?}_{:?}", sn, sm), 32);
1447                state.set_vfp_reg(sd, result);
1448            }
1449
1450            ArmOp::F32Copysign { sd, sn, sm } => {
1451                // f32 copysign: sd = |sn| with sign of sm
1452                // Take magnitude of sn and sign bit from sm
1453                let val_n = state.get_vfp_reg(sn).clone();
1454                let val_m = state.get_vfp_reg(sm).clone();
1455
1456                // Extract magnitude from sn (clear sign bit)
1457                let mag_mask = BV::from_u64(0x7FFFFFFF, 32);
1458                let magnitude = val_n.bvand(&mag_mask);
1459
1460                // Extract sign from sm (bit 31 only)
1461                let sign_mask = BV::from_u64(0x80000000, 32);
1462                let sign = val_m.bvand(&sign_mask);
1463
1464                // Combine: magnitude | sign
1465                let result = magnitude.bvor(&sign);
1466                state.set_vfp_reg(sd, result);
1467            }
1468
1469            ArmOp::F32Load { sd, addr } => {
1470                // f32 load: sd = memory[addr]
1471                // Symbolic memory access for verification
1472                let result = BV::new_const(format!("f32_load_{:?}", addr), 32);
1473                state.set_vfp_reg(sd, result);
1474            }
1475
1476            // f32 Comparisons (result stored in integer register)
1477            ArmOp::F32Eq { rd, sn, sm } => {
1478                // f32 equal: rd = (sn == sm) ? 1 : 0
1479                // IEEE 754: NaN != NaN, so symbolic comparison needed
1480                let result = BV::new_const(format!("f32_eq_{:?}_{:?}", sn, sm), 32);
1481                state.set_reg(rd, result);
1482            }
1483
1484            ArmOp::F32Ne { rd, sn, sm } => {
1485                // f32 not equal: rd = (sn != sm) ? 1 : 0
1486                let result = BV::new_const(format!("f32_ne_{:?}_{:?}", sn, sm), 32);
1487                state.set_reg(rd, result);
1488            }
1489
1490            ArmOp::F32Lt { rd, sn, sm } => {
1491                // f32 less than: rd = (sn < sm) ? 1 : 0
1492                let result = BV::new_const(format!("f32_lt_{:?}_{:?}", sn, sm), 32);
1493                state.set_reg(rd, result);
1494            }
1495
1496            ArmOp::F32Le { rd, sn, sm } => {
1497                // f32 less than or equal: rd = (sn <= sm) ? 1 : 0
1498                let result = BV::new_const(format!("f32_le_{:?}_{:?}", sn, sm), 32);
1499                state.set_reg(rd, result);
1500            }
1501
1502            ArmOp::F32Gt { rd, sn, sm } => {
1503                // f32 greater than: rd = (sn > sm) ? 1 : 0
1504                let result = BV::new_const(format!("f32_gt_{:?}_{:?}", sn, sm), 32);
1505                state.set_reg(rd, result);
1506            }
1507
1508            ArmOp::F32Ge { rd, sn, sm } => {
1509                // f32 greater than or equal: rd = (sn >= sm) ? 1 : 0
1510                let result = BV::new_const(format!("f32_ge_{:?}_{:?}", sn, sm), 32);
1511                state.set_reg(rd, result);
1512            }
1513
1514            ArmOp::F32Store { sd, addr } => {
1515                // f32 store: memory[addr] = sd
1516                // Memory write - modeled symbolically for verification
1517                // In a full implementation, would update memory state
1518                // For now, this is a no-op as we model memory symbolically
1519                let _val = state.get_vfp_reg(sd);
1520                let _addr_str = format!("{:?}", addr);
1521                // TODO: Add memory state tracking when implementing full memory model
1522            }
1523
1524            // f32 Advanced Math Operations
1525            ArmOp::F32Ceil { sd, sm } => {
1526                // f32 ceil: sd = ceil(sm) - round toward +infinity
1527                // Symbolic representation for IEEE 754 rounding
1528                let result = BV::new_const(format!("f32_ceil_{:?}", sm), 32);
1529                state.set_vfp_reg(sd, result);
1530            }
1531
1532            ArmOp::F32Floor { sd, sm } => {
1533                // f32 floor: sd = floor(sm) - round toward -infinity
1534                // Symbolic representation for IEEE 754 rounding
1535                let result = BV::new_const(format!("f32_floor_{:?}", sm), 32);
1536                state.set_vfp_reg(sd, result);
1537            }
1538
1539            ArmOp::F32Trunc { sd, sm } => {
1540                // f32 trunc: sd = trunc(sm) - round toward zero
1541                // Symbolic representation for IEEE 754 rounding
1542                let result = BV::new_const(format!("f32_trunc_{:?}", sm), 32);
1543                state.set_vfp_reg(sd, result);
1544            }
1545
1546            ArmOp::F32Nearest { sd, sm } => {
1547                // f32 nearest: sd = nearest(sm) - round to nearest, ties to even
1548                // Symbolic representation for IEEE 754 rounding
1549                let result = BV::new_const(format!("f32_nearest_{:?}", sm), 32);
1550                state.set_vfp_reg(sd, result);
1551            }
1552
1553            // f32 Conversions from Integers
1554            ArmOp::F32ConvertI32S { sd, rm } => {
1555                // f32 convert from signed i32: sd = (f32)rm
1556                let int_val = state.get_reg(rm);
1557                let result = BV::new_const(format!("f32_convert_i32s_{:?}", int_val), 32);
1558                state.set_vfp_reg(sd, result);
1559            }
1560
1561            ArmOp::F32ConvertI32U { sd, rm } => {
1562                // f32 convert from unsigned i32: sd = (f32)(unsigned)rm
1563                let int_val = state.get_reg(rm);
1564                let result = BV::new_const(format!("f32_convert_i32u_{:?}", int_val), 32);
1565                state.set_vfp_reg(sd, result);
1566            }
1567
1568            ArmOp::F32ConvertI64S { sd, rmlo, rmhi } => {
1569                // f32 convert from signed i64: sd = (f32)r64
1570                let lo = state.get_reg(rmlo);
1571                let hi = state.get_reg(rmhi);
1572                let result = BV::new_const(format!("f32_convert_i64s_{:?}_{:?}", lo, hi), 32);
1573                state.set_vfp_reg(sd, result);
1574            }
1575
1576            ArmOp::F32ConvertI64U { sd, rmlo, rmhi } => {
1577                // f32 convert from unsigned i64: sd = (f32)(unsigned)r64
1578                let lo = state.get_reg(rmlo);
1579                let hi = state.get_reg(rmhi);
1580                let result = BV::new_const(format!("f32_convert_i64u_{:?}_{:?}", lo, hi), 32);
1581                state.set_vfp_reg(sd, result);
1582            }
1583
1584            // f32 Reinterpretations
1585            ArmOp::F32ReinterpretI32 { sd, rm } => {
1586                // f32 reinterpret i32: sd = reinterpret_cast<f32>(rm)
1587                // Bitwise copy without conversion
1588                let bits = state.get_reg(rm).clone();
1589                state.set_vfp_reg(sd, bits);
1590            }
1591
1592            ArmOp::I32ReinterpretF32 { rd, sm } => {
1593                // i32 reinterpret f32: rd = reinterpret_cast<i32>(sm)
1594                // Bitwise copy without conversion
1595                let bits = state.get_vfp_reg(sm).clone();
1596                state.set_reg(rd, bits);
1597            }
1598
1599            // ===================================================================
1600            // f64 Operations (Phase 2c - Double-Precision Floating Point)
1601            // ===================================================================
1602
1603            // f64 Arithmetic (symbolic for verification)
1604            ArmOp::F64Add { dd, dn, dm } => {
1605                // f64 addition: dd = dn + dm
1606                // For verification, return symbolic value
1607                // Full implementation would use Z3 FloatingPoint operations
1608                let result = BV::new_const(format!("f64_add_{:?}_{:?}", dn, dm), 64);
1609                state.set_vfp_reg(dd, result);
1610            }
1611
1612            ArmOp::F64Sub { dd, dn, dm } => {
1613                // f64 subtraction: dd = dn - dm
1614                let result = BV::new_const(format!("f64_sub_{:?}_{:?}", dn, dm), 64);
1615                state.set_vfp_reg(dd, result);
1616            }
1617
1618            ArmOp::F64Mul { dd, dn, dm } => {
1619                // f64 multiplication: dd = dn * dm
1620                let result = BV::new_const(format!("f64_mul_{:?}_{:?}", dn, dm), 64);
1621                state.set_vfp_reg(dd, result);
1622            }
1623
1624            ArmOp::F64Div { dd, dn, dm } => {
1625                // f64 division: dd = dn / dm
1626                let result = BV::new_const(format!("f64_div_{:?}_{:?}", dn, dm), 64);
1627                state.set_vfp_reg(dd, result);
1628            }
1629
1630            // f64 Simple Math
1631            ArmOp::F64Abs { dd, dm } => {
1632                // f64 absolute value: dd = |dm|
1633                // Clear the sign bit (bit 63)
1634                let val = state.get_vfp_reg(dm).clone();
1635                let mask = BV::from_u64(0x7FFFFFFFFFFFFFFF, 64); // Clear sign bit
1636                let result = val.bvand(&mask);
1637                state.set_vfp_reg(dd, result);
1638            }
1639
1640            ArmOp::F64Neg { dd, dm } => {
1641                // f64 negation: dd = -dm
1642                // Flip the sign bit (bit 63)
1643                let val = state.get_vfp_reg(dm).clone();
1644                let mask = BV::from_u64(0x8000000000000000, 64); // Sign bit
1645                let result = val.bvxor(&mask);
1646                state.set_vfp_reg(dd, result);
1647            }
1648
1649            ArmOp::F64Sqrt { dd, dm } => {
1650                // f64 square root: dd = sqrt(dm)
1651                // Symbolic representation for verification
1652                let result = BV::new_const(format!("f64_sqrt_{:?}", dm), 64);
1653                state.set_vfp_reg(dd, result);
1654            }
1655
1656            ArmOp::F64Min { dd, dn, dm } => {
1657                // f64 minimum: dd = min(dn, dm)
1658                // IEEE 754 semantics: NaN propagation, -0.0 < +0.0
1659                // Symbolic representation for verification
1660                let result = BV::new_const(format!("f64_min_{:?}_{:?}", dn, dm), 64);
1661                state.set_vfp_reg(dd, result);
1662            }
1663
1664            ArmOp::F64Max { dd, dn, dm } => {
1665                // f64 maximum: dd = max(dn, dm)
1666                // IEEE 754 semantics: NaN propagation, +0.0 > -0.0
1667                // Symbolic representation for verification
1668                let result = BV::new_const(format!("f64_max_{:?}_{:?}", dn, dm), 64);
1669                state.set_vfp_reg(dd, result);
1670            }
1671
1672            ArmOp::F64Copysign { dd, dn, dm } => {
1673                // f64 copysign: dd = |dn| with sign of dm
1674                // Take magnitude of dn and sign bit from dm
1675                let val_n = state.get_vfp_reg(dn).clone();
1676                let val_m = state.get_vfp_reg(dm).clone();
1677
1678                // Extract magnitude from dn (clear sign bit)
1679                let mag_mask = BV::from_u64(0x7FFFFFFFFFFFFFFF, 64);
1680                let magnitude = val_n.bvand(&mag_mask);
1681
1682                // Extract sign from dm (bit 63 only)
1683                let sign_mask = BV::from_u64(0x8000000000000000, 64);
1684                let sign = val_m.bvand(&sign_mask);
1685
1686                // Combine: magnitude | sign
1687                let result = magnitude.bvor(&sign);
1688                state.set_vfp_reg(dd, result);
1689            }
1690
1691            // f64 Rounding Operations (symbolic for verification)
1692            ArmOp::F64Ceil { dd, dm } => {
1693                // f64 ceil: dd = ceil(dm) - round toward +infinity
1694                let result = BV::new_const(format!("f64_ceil_{:?}", dm), 64);
1695                state.set_vfp_reg(dd, result);
1696            }
1697
1698            ArmOp::F64Floor { dd, dm } => {
1699                // f64 floor: dd = floor(dm) - round toward -infinity
1700                let result = BV::new_const(format!("f64_floor_{:?}", dm), 64);
1701                state.set_vfp_reg(dd, result);
1702            }
1703
1704            ArmOp::F64Trunc { dd, dm } => {
1705                // f64 trunc: dd = trunc(dm) - round toward zero
1706                let result = BV::new_const(format!("f64_trunc_{:?}", dm), 64);
1707                state.set_vfp_reg(dd, result);
1708            }
1709
1710            ArmOp::F64Nearest { dd, dm } => {
1711                // f64 nearest: dd = round(dm) - round to nearest, ties to even
1712                let result = BV::new_const(format!("f64_nearest_{:?}", dm), 64);
1713                state.set_vfp_reg(dd, result);
1714            }
1715
1716            // f64 Memory Operations
1717            ArmOp::F64Load { dd, addr } => {
1718                // f64 load: dd = memory[addr]
1719                // Symbolic memory access for verification
1720                let result = BV::new_const(format!("f64_load_{:?}", addr), 64);
1721                state.set_vfp_reg(dd, result);
1722            }
1723
1724            ArmOp::F64Store { dd: _, addr: _ } => {
1725                // f64 store: memory[addr] = dd
1726                // Store operations don't produce register values
1727                // No state change for symbolic execution
1728            }
1729
1730            ArmOp::F64Const { dd, value } => {
1731                // f64 constant: dd = value
1732                let bits = value.to_bits() as i64;
1733                let result = BV::from_i64(bits, 64);
1734                state.set_vfp_reg(dd, result);
1735            }
1736
1737            // f64 Comparisons (result stored in integer register)
1738            ArmOp::F64Eq { rd, dn, dm } => {
1739                // f64 equal: rd = (dn == dm) ? 1 : 0
1740                // IEEE 754: NaN != NaN, so symbolic comparison needed
1741                let result = BV::new_const(format!("f64_eq_{:?}_{:?}", dn, dm), 32);
1742                state.set_reg(rd, result);
1743            }
1744
1745            ArmOp::F64Ne { rd, dn, dm } => {
1746                // f64 not equal: rd = (dn != dm) ? 1 : 0
1747                let result = BV::new_const(format!("f64_ne_{:?}_{:?}", dn, dm), 32);
1748                state.set_reg(rd, result);
1749            }
1750
1751            ArmOp::F64Lt { rd, dn, dm } => {
1752                // f64 less than: rd = (dn < dm) ? 1 : 0
1753                let result = BV::new_const(format!("f64_lt_{:?}_{:?}", dn, dm), 32);
1754                state.set_reg(rd, result);
1755            }
1756
1757            ArmOp::F64Le { rd, dn, dm } => {
1758                // f64 less than or equal: rd = (dn <= dm) ? 1 : 0
1759                let result = BV::new_const(format!("f64_le_{:?}_{:?}", dn, dm), 32);
1760                state.set_reg(rd, result);
1761            }
1762
1763            ArmOp::F64Gt { rd, dn, dm } => {
1764                // f64 greater than: rd = (dn > dm) ? 1 : 0
1765                let result = BV::new_const(format!("f64_gt_{:?}_{:?}", dn, dm), 32);
1766                state.set_reg(rd, result);
1767            }
1768
1769            ArmOp::F64Ge { rd, dn, dm } => {
1770                // f64 greater than or equal: rd = (dn >= dm) ? 1 : 0
1771                let result = BV::new_const(format!("f64_ge_{:?}_{:?}", dn, dm), 32);
1772                state.set_reg(rd, result);
1773            }
1774
1775            // f64 Conversions
1776            ArmOp::F64ConvertI32S { dd, rm } => {
1777                // f64 convert i32 signed: dd = (f64)rm
1778                // Symbolic conversion
1779                let result = BV::new_const(format!("f64_convert_i32s_{:?}", rm), 64);
1780                state.set_vfp_reg(dd, result);
1781            }
1782
1783            ArmOp::F64ConvertI32U { dd, rm } => {
1784                // f64 convert i32 unsigned: dd = (f64)(unsigned)rm
1785                // Symbolic conversion
1786                let result = BV::new_const(format!("f64_convert_i32u_{:?}", rm), 64);
1787                state.set_vfp_reg(dd, result);
1788            }
1789
1790            ArmOp::F64ConvertI64S {
1791                dd,
1792                rmlo: _,
1793                rmhi: _,
1794            } => {
1795                // f64 convert i64 signed: dd = (f64)(rmhi:rmlo)
1796                // Symbolic conversion (complex operation)
1797                let result = BV::new_const("f64_convert_i64s_result", 64);
1798                state.set_vfp_reg(dd, result);
1799            }
1800
1801            ArmOp::F64ConvertI64U {
1802                dd,
1803                rmlo: _,
1804                rmhi: _,
1805            } => {
1806                // f64 convert i64 unsigned: dd = (f64)(unsigned)(rmhi:rmlo)
1807                // Symbolic conversion (complex operation)
1808                let result = BV::new_const("f64_convert_i64u_result", 64);
1809                state.set_vfp_reg(dd, result);
1810            }
1811
1812            ArmOp::F64PromoteF32 { dd, sm } => {
1813                // f64 promote f32: dd = (f64)sm
1814                // Promote from 32-bit to 64-bit (symbolic for verification)
1815                let result = BV::new_const(format!("f64_promote_f32_{:?}", sm), 64);
1816                state.set_vfp_reg(dd, result);
1817            }
1818
1819            ArmOp::F64ReinterpretI64 { dd, rmlo, rmhi } => {
1820                // f64 reinterpret i64: dd = reinterpret_cast<f64>(rmhi:rmlo)
1821                // Bitwise copy without conversion - combine two 32-bit registers
1822                let lo = state.get_reg(rmlo).clone();
1823                let hi = state.get_reg(rmhi).clone();
1824
1825                // Extend to 64 bits and combine: (hi << 32) | lo
1826                let lo_64 = lo.zero_ext(32); // Extend to 64 bits
1827                let hi_64 = hi.zero_ext(32);
1828                let shift_32 = BV::from_u64(32, 64);
1829                let hi_shifted = hi_64.bvshl(&shift_32);
1830                let result = hi_shifted.bvor(&lo_64);
1831
1832                state.set_vfp_reg(dd, result);
1833            }
1834
1835            ArmOp::I64ReinterpretF64 { rdlo, rdhi, dm } => {
1836                // i64 reinterpret f64: (rdhi:rdlo) = reinterpret_cast<i64>(dm)
1837                // Bitwise copy without conversion - split 64-bit into two 32-bit registers
1838                let bits = state.get_vfp_reg(dm).clone();
1839
1840                // Extract low 32 bits
1841                let lo = bits.extract(31, 0);
1842                state.set_reg(rdlo, lo);
1843
1844                // Extract high 32 bits
1845                let hi = bits.extract(63, 32);
1846                state.set_reg(rdhi, hi);
1847            }
1848
1849            ArmOp::I64TruncF64S {
1850                rdlo: _,
1851                rdhi: _,
1852                dm: _,
1853            } => {
1854                // i64 trunc f64 signed: (rdhi:rdlo) = (i64)dm
1855                // Symbolic conversion (complex operation)
1856                // Would require proper truncation with saturation
1857            }
1858
1859            ArmOp::I64TruncF64U {
1860                rdlo: _,
1861                rdhi: _,
1862                dm: _,
1863            } => {
1864                // i64 trunc f64 unsigned: (rdhi:rdlo) = (unsigned i64)dm
1865                // Symbolic conversion (complex operation)
1866                // Would require proper truncation with saturation
1867            }
1868
1869            ArmOp::I32TruncF64S { rd, dm } => {
1870                // i32 trunc f64 signed: rd = (i32)dm
1871                // Symbolic conversion
1872                let result = BV::new_const(format!("i32_trunc_f64s_{:?}", dm), 32);
1873                state.set_reg(rd, result);
1874            }
1875
1876            ArmOp::I32TruncF64U { rd, dm } => {
1877                // i32 trunc f64 unsigned: rd = (unsigned i32)dm
1878                // Symbolic conversion
1879                let result = BV::new_const(format!("i32_trunc_f64u_{:?}", dm), 32);
1880                state.set_reg(rd, result);
1881            }
1882
1883            // VCR-VER-002 (#166): UDF is the WASM trap sink — executing it
1884            // raises UsageFault. In the straight-line model (no path guards)
1885            // reaching a UDF means the sequence traps unconditionally; the
1886            // branch-taking executor [`Self::encode_sequence_br`] instead
1887            // conditions this on the guard the UDF is reached under.
1888            ArmOp::Udf { .. } => {
1889                state.may_trap = Bool::from_bool(true);
1890            }
1891
1892            _ => {
1893                // Unsupported operations - no state change
1894            }
1895        }
1896    }
1897
1898    /// Evaluate an Operand2 value
1899    fn evaluate_operand2(&self, op2: &Operand2, state: &ArmState) -> BV {
1900        match op2 {
1901            Operand2::Imm(value) => BV::from_i64(*value as i64, 32),
1902            Operand2::Reg(reg) => state.get_reg(reg).clone(),
1903            Operand2::RegShift { rm, shift, amount } => {
1904                let reg_val = state.get_reg(rm).clone();
1905                let shift_amount = BV::from_i64(*amount as i64, 32);
1906
1907                match shift {
1908                    synth_synthesis::ShiftType::LSL => reg_val.bvshl(&shift_amount),
1909                    synth_synthesis::ShiftType::LSR => reg_val.bvlshr(&shift_amount),
1910                    synth_synthesis::ShiftType::ASR => reg_val.bvashr(&shift_amount),
1911                    synth_synthesis::ShiftType::ROR => reg_val.bvrotr(&shift_amount),
1912                }
1913            }
1914        }
1915    }
1916
1917    /// Extract the result value from a register after execution
1918    pub fn extract_result(&self, state: &ArmState, reg: &Reg) -> BV {
1919        state.get_reg(reg).clone()
1920    }
1921
1922    /// Encode ARM CLZ (Count Leading Zeros) instruction
1923    ///
1924    /// Implements the same algorithm as WASM i32.clz for equivalence verification.
1925    /// Uses binary search through bit positions.
1926    fn encode_clz(&self, input: &BV) -> BV {
1927        let zero = BV::from_i64(0, 32);
1928
1929        // Special case: if input is 0, return 32
1930        let all_zero = input.eq(&zero);
1931        let result_if_zero = BV::from_i64(32, 32);
1932
1933        // Binary search approach
1934        let mut count = BV::from_i64(0, 32);
1935        let mut remaining = input.clone();
1936
1937        // Check top 16 bits
1938        let mask_16 = BV::from_u64(0xFFFF0000, 32);
1939        let top_16 = remaining.bvand(&mask_16);
1940        let top_16_zero = top_16.eq(&zero);
1941
1942        count = top_16_zero.ite(count.bvadd(BV::from_i64(16, 32)), &count);
1943        remaining = top_16_zero.ite(remaining.bvshl(BV::from_i64(16, 32)), &remaining);
1944
1945        // Check top 8 bits
1946        let mask_8 = BV::from_u64(0xFF000000, 32);
1947        let top_8 = remaining.bvand(&mask_8);
1948        let top_8_zero = top_8.eq(&zero);
1949
1950        count = top_8_zero.ite(count.bvadd(BV::from_i64(8, 32)), &count);
1951        remaining = top_8_zero.ite(remaining.bvshl(BV::from_i64(8, 32)), &remaining);
1952
1953        // Check top 4 bits
1954        let mask_4 = BV::from_u64(0xF0000000, 32);
1955        let top_4 = remaining.bvand(&mask_4);
1956        let top_4_zero = top_4.eq(&zero);
1957
1958        count = top_4_zero.ite(count.bvadd(BV::from_i64(4, 32)), &count);
1959        remaining = top_4_zero.ite(remaining.bvshl(BV::from_i64(4, 32)), &remaining);
1960
1961        // Check top 2 bits
1962        let mask_2 = BV::from_u64(0xC0000000, 32);
1963        let top_2 = remaining.bvand(&mask_2);
1964        let top_2_zero = top_2.eq(&zero);
1965
1966        count = top_2_zero.ite(count.bvadd(BV::from_i64(2, 32)), &count);
1967        remaining = top_2_zero.ite(remaining.bvshl(BV::from_i64(2, 32)), &remaining);
1968
1969        // Check top bit
1970        let mask_1 = BV::from_u64(0x80000000, 32);
1971        let top_1 = remaining.bvand(&mask_1);
1972        let top_1_zero = top_1.eq(&zero);
1973
1974        count = top_1_zero.ite(count.bvadd(BV::from_i64(1, 32)), &count);
1975
1976        // Return 32 if all zeros, otherwise return count
1977        all_zero.ite(&result_if_zero, &count)
1978    }
1979
1980    /// Encode CTZ (Count Trailing Zeros) instruction
1981    ///
1982    /// Counts the number of trailing (low-order) zero bits.
1983    /// Implemented as: ctz(x) = clz(rbit(x))
1984    /// Returns 32 if input is 0.
1985    fn encode_ctz(&self, input: &BV) -> BV {
1986        // CTZ can be implemented by reversing bits and then counting leading zeros
1987        let reversed = self.encode_rbit(input);
1988        self.encode_clz(&reversed)
1989    }
1990
1991    /// Encode ARM RBIT (Reverse Bits) instruction
1992    ///
1993    /// Reverses the bit order in a 32-bit value.
1994    /// Used in combination with CLZ to implement CTZ.
1995    fn encode_rbit(&self, input: &BV) -> BV {
1996        // Reverse bits by swapping progressively smaller chunks
1997        let mut result = input.clone();
1998
1999        // Swap 16-bit halves
2000        let mask_16 = BV::from_u64(0xFFFF0000, 32);
2001        let top_16 = result.bvand(&mask_16).bvlshr(BV::from_i64(16, 32));
2002        let bottom_16 = result.bvshl(BV::from_i64(16, 32));
2003        result = top_16.bvor(&bottom_16);
2004
2005        // Swap 8-bit chunks
2006        let mask_8_top = BV::from_u64(0xFF00FF00, 32);
2007        let mask_8_bottom = BV::from_u64(0x00FF00FF, 32);
2008        let top_8 = result.bvand(&mask_8_top).bvlshr(BV::from_i64(8, 32));
2009        let bottom_8 = result.bvand(&mask_8_bottom).bvshl(BV::from_i64(8, 32));
2010        result = top_8.bvor(&bottom_8);
2011
2012        // Swap 4-bit chunks
2013        let mask_4_top = BV::from_u64(0xF0F0F0F0, 32);
2014        let mask_4_bottom = BV::from_u64(0x0F0F0F0F, 32);
2015        let top_4 = result.bvand(&mask_4_top).bvlshr(BV::from_i64(4, 32));
2016        let bottom_4 = result.bvand(&mask_4_bottom).bvshl(BV::from_i64(4, 32));
2017        result = top_4.bvor(&bottom_4);
2018
2019        // Swap 2-bit chunks
2020        let mask_2_top = BV::from_u64(0xCCCCCCCC, 32);
2021        let mask_2_bottom = BV::from_u64(0x33333333, 32);
2022        let top_2 = result.bvand(&mask_2_top).bvlshr(BV::from_i64(2, 32));
2023        let bottom_2 = result.bvand(&mask_2_bottom).bvshl(BV::from_i64(2, 32));
2024        result = top_2.bvor(&bottom_2);
2025
2026        // Swap 1-bit chunks (individual bits)
2027        let mask_1_top = BV::from_u64(0xAAAAAAAA, 32);
2028        let mask_1_bottom = BV::from_u64(0x55555555, 32);
2029        let top_1 = result.bvand(&mask_1_top).bvlshr(BV::from_i64(1, 32));
2030        let bottom_1 = result.bvand(&mask_1_bottom).bvshl(BV::from_i64(1, 32));
2031        result = top_1.bvor(&bottom_1);
2032
2033        result
2034    }
2035
2036    /// Update condition flags for subtraction (used by CMP, SUB, etc.)
2037    ///
2038    /// Computes all four ARM condition flags based on a subtraction:
2039    /// - N (Negative): Result is negative (bit 31 set)
2040    /// - Z (Zero): Result is zero
2041    /// - C (Carry): No borrow occurred (unsigned: a >= b)
2042    /// - V (Overflow): Signed overflow occurred
2043    ///
2044    /// For subtraction result = a - b:
2045    /// - C = 1 if a >= b (unsigned), 0 if borrow
2046    /// - V = 1 if signs of a and b differ AND sign of result differs from a
2047    fn update_flags_sub(&self, state: &mut ArmState, a: &BV, b: &BV, result: &BV) {
2048        let zero = BV::from_i64(0, 32);
2049
2050        // N flag: bit 31 of result (negative if set)
2051        let sign_bit = result.extract(31, 31);
2052        let one_bit = BV::from_i64(1, 1);
2053        state.flags.n = sign_bit.eq(&one_bit);
2054
2055        // Z flag: result == 0
2056        state.flags.z = result.eq(&zero);
2057
2058        // C flag: carry/borrow flag for subtraction
2059        // For SUB: C = 1 if no borrow (i.e., a >= b unsigned)
2060        // This is equivalent to: a >= b in unsigned arithmetic
2061        state.flags.c = a.bvuge(b);
2062
2063        // V flag: signed overflow
2064        // Overflow occurs when:
2065        // - Subtracting a positive from a negative gives positive
2066        // - Subtracting a negative from a positive gives negative
2067        // Formula: (a[31] != b[31]) && (a[31] != result[31])
2068        let a_sign = a.extract(31, 31);
2069        let b_sign = b.extract(31, 31);
2070        let r_sign = result.extract(31, 31);
2071
2072        let signs_differ = a_sign.eq(&b_sign).not(); // a and b have different signs
2073        let result_sign_wrong = a_sign.eq(&r_sign).not(); // result sign differs from a
2074        state.flags.v = Bool::and(&[&signs_differ, &result_sign_wrong]);
2075    }
2076
2077    /// Update condition flags for addition
2078    ///
2079    /// Similar to subtraction but with different carry logic:
2080    /// - C = 1 if unsigned overflow (result < a or result < b)
2081    /// - V = 1 if signed overflow
2082    #[allow(dead_code)]
2083    fn update_flags_add(&self, state: &mut ArmState, a: &BV, b: &BV, result: &BV) {
2084        let zero = BV::from_i64(0, 32);
2085
2086        // N flag: bit 31 of result
2087        let sign_bit = result.extract(31, 31);
2088        let one_bit = BV::from_i64(1, 1);
2089        state.flags.n = sign_bit.eq(&one_bit);
2090
2091        // Z flag: result == 0
2092        state.flags.z = result.eq(&zero);
2093
2094        // C flag: unsigned overflow
2095        // For ADD: C = 1 if carry out (unsigned overflow)
2096        // This occurs if result < a (wrapping occurred)
2097        state.flags.c = result.bvult(a);
2098
2099        // V flag: signed overflow
2100        // Overflow occurs when:
2101        // - Adding two positives gives negative
2102        // - Adding two negatives gives positive
2103        // Formula: (a[31] == b[31]) && (a[31] != result[31])
2104        let a_sign = a.extract(31, 31);
2105        let b_sign = b.extract(31, 31);
2106        let r_sign = result.extract(31, 31);
2107
2108        let signs_same = a_sign.eq(&b_sign); // a and b have same sign
2109        let result_sign_wrong = a_sign.eq(&r_sign).not(); // result sign differs
2110        state.flags.v = Bool::and(&[&signs_same, &result_sign_wrong]);
2111    }
2112
2113    /// Evaluate an ARM condition code based on NZCV flags
2114    ///
2115    /// This implements the standard ARM condition code logic:
2116    /// - EQ: Z == 1
2117    /// - NE: Z == 0
2118    /// - LT: N != V (signed less than)
2119    /// - LE: Z == 1 || N != V (signed less or equal)
2120    /// - GT: Z == 0 && N == V (signed greater than)
2121    /// - GE: N == V (signed greater or equal)
2122    /// - LO: C == 0 (unsigned less than)
2123    /// - LS: C == 0 || Z == 1 (unsigned less or equal)
2124    /// - HI: C == 1 && Z == 0 (unsigned greater than)
2125    /// - HS: C == 1 (unsigned greater or equal)
2126    fn evaluate_condition(
2127        &self,
2128        cond: &synth_synthesis::rules::Condition,
2129        flags: &ConditionFlags,
2130    ) -> Bool {
2131        use synth_synthesis::rules::Condition;
2132
2133        match cond {
2134            Condition::EQ => flags.z.clone(),
2135            Condition::NE => flags.z.not(),
2136            Condition::LT => {
2137                // N != V: negative flag differs from overflow flag
2138                flags.n.eq(&flags.v).not()
2139            }
2140            Condition::LE => {
2141                // Z == 1 || N != V
2142                let n_ne_v = flags.n.eq(&flags.v).not();
2143                Bool::or(&[&flags.z, &n_ne_v])
2144            }
2145            Condition::GT => {
2146                // Z == 0 && N == V
2147                let z_zero = flags.z.not();
2148                let n_eq_v = flags.n.eq(&flags.v);
2149                Bool::and(&[&z_zero, &n_eq_v])
2150            }
2151            Condition::GE => {
2152                // N == V
2153                flags.n.eq(&flags.v)
2154            }
2155            Condition::LO => {
2156                // C == 0 (no carry = less than unsigned)
2157                flags.c.not()
2158            }
2159            Condition::LS => {
2160                // C == 0 || Z == 1
2161                let c_zero = flags.c.not();
2162                Bool::or(&[&flags.z, &c_zero])
2163            }
2164            Condition::HI => {
2165                // C == 1 && Z == 0
2166                let z_zero = flags.z.not();
2167                Bool::and(&[&flags.c, &z_zero])
2168            }
2169            Condition::HS => {
2170                // C == 1 (carry = greater or equal unsigned)
2171                flags.c.clone()
2172            }
2173        }
2174    }
2175
2176    /// Convert a boolean to a 32-bit bitvector (0 or 1)
2177    fn bool_to_bv32(&self, cond: &Bool) -> BV {
2178        let zero = BV::from_i64(0, 32);
2179        let one = BV::from_i64(1, 32);
2180        cond.ite(&one, &zero)
2181    }
2182
2183    /// Encode ARM POPCNT (population count)
2184    ///
2185    /// Uses the Hamming weight algorithm (same as WASM implementation).
2186    /// This is a pseudo-instruction that would be expanded into actual ARM code.
2187    fn encode_popcnt(&self, input: &BV) -> BV {
2188        let mut x = input.clone();
2189
2190        // Step 1: Count bits in pairs
2191        let mask1 = BV::from_u64(0x55555555, 32);
2192        let masked = x.bvand(&mask1);
2193        let shifted = x.bvlshr(BV::from_i64(1, 32));
2194        let shifted_masked = shifted.bvand(&mask1);
2195        x = masked.bvadd(&shifted_masked);
2196
2197        // Step 2: Count pairs in nibbles
2198        let mask2 = BV::from_u64(0x33333333, 32);
2199        let masked = x.bvand(&mask2);
2200        let shifted = x.bvlshr(BV::from_i64(2, 32));
2201        let shifted_masked = shifted.bvand(&mask2);
2202        x = masked.bvadd(&shifted_masked);
2203
2204        // Step 3: Count nibbles in bytes
2205        let mask3 = BV::from_u64(0x0F0F0F0F, 32);
2206        let masked = x.bvand(&mask3);
2207        let shifted = x.bvlshr(BV::from_i64(4, 32));
2208        let shifted_masked = shifted.bvand(&mask3);
2209        x = masked.bvadd(&shifted_masked);
2210
2211        // Step 4: Sum all bytes
2212        let multiplier = BV::from_u64(0x01010101, 32);
2213        x = x.bvmul(&multiplier);
2214        x = x.bvlshr(BV::from_i64(24, 32));
2215
2216        x
2217    }
2218}
2219
2220// ===========================================================================
2221// VCR-VER-002 (#166): branch-taking guarded executor — DERIVES the ARM trap
2222// condition from the emitted guard/branch/UDF structure
2223// ===========================================================================
2224
2225/// Path guard: the condition under which an instruction executes. `Always`
2226/// keeps the straight-line common case free of `ite` merging.
2227#[derive(Clone)]
2228enum Guard {
2229    Always,
2230    Cond(Bool),
2231}
2232
2233impl Guard {
2234    fn and_cond(&self, c: &Bool) -> Guard {
2235        match self {
2236            Guard::Always => Guard::Cond(c.clone()),
2237            Guard::Cond(g) => Guard::Cond(Bool::and(&[g, c])),
2238        }
2239    }
2240}
2241
2242/// Merge an incoming edge guard into the guard map at `at`.
2243fn merge_guard(incoming: &mut HashMap<usize, Guard>, at: usize, g: Guard) {
2244    match (incoming.get(&at), g) {
2245        (Some(Guard::Always), _) => {}
2246        (_, Guard::Always) => {
2247            incoming.insert(at, Guard::Always);
2248        }
2249        (Some(Guard::Cond(a)), Guard::Cond(b)) => {
2250            let merged = Bool::or(&[a, &b]);
2251            incoming.insert(at, Guard::Cond(merged));
2252        }
2253        (None, g @ Guard::Cond(_)) => {
2254            incoming.insert(at, g);
2255        }
2256    }
2257}
2258
2259/// Boolean if-then-else (the term API only has BV ite).
2260fn bool_ite(c: &Bool, t: &Bool, e: &Bool) -> Bool {
2261    Bool::or(&[&Bool::and(&[c, t]), &Bool::and(&[&c.not(), e])])
2262}
2263
2264/// IEEE 754 single-precision NaN test over the raw bit pattern:
2265/// exponent all-ones with a non-zero fraction.
2266fn f32_is_nan(x: &BV) -> Bool {
2267    let exp_ones = x.extract(30, 23).eq(BV::from_u64(0xFF, 8));
2268    let frac_nonzero = x.extract(22, 0).eq(BV::from_u64(0, 23)).not();
2269    Bool::and(&[&exp_ones, &frac_nonzero])
2270}
2271
2272/// Ordered `a < b` over IEEE 754 single-precision BIT PATTERNS, assuming
2273/// neither operand is NaN (the callers conjoin the NaN exclusion). Uses the
2274/// sign/magnitude case split; `+0.0 == -0.0` (neither is less).
2275fn f32_ordered_lt(a: &BV, b: &BV) -> Bool {
2276    let a_neg = a.extract(31, 31).eq(BV::from_u64(1, 1));
2277    let b_neg = b.extract(31, 31).eq(BV::from_u64(1, 1));
2278    let a_mag = a.extract(30, 0);
2279    let b_mag = b.extract(30, 0);
2280    let zero31 = BV::from_u64(0, 31);
2281    let both_zero = Bool::and(&[&a_mag.eq(&zero31), &b_mag.eq(&zero31)]);
2282    // (neg, neg): larger magnitude is smaller; (neg, pos): a < b unless both
2283    // are zeros; (pos, neg): never; (pos, pos): magnitude order.
2284    let neg_neg = b_mag.bvult(&a_mag);
2285    let neg_pos = both_zero.not();
2286    let pos_pos = a_mag.bvult(&b_mag);
2287    bool_ite(
2288        &a_neg,
2289        &bool_ite(&b_neg, &neg_neg, &neg_pos),
2290        &bool_ite(&b_neg, &Bool::from_bool(false), &pos_pos),
2291    )
2292}
2293
2294/// The three ordered VFP comparison results the trunc guards use, as total
2295/// functions over the operands' bit patterns (result is 0 on any NaN — the
2296/// unordered case — exactly the ARM `VCMP`+`VMRS`+`IT` materialization the
2297/// `F32Lt`/`F32Gt`/`F32Ge` pseudo-ops stand for).
2298fn f32_cmp_result(kind: F32CmpKind, a: &BV, b: &BV) -> Bool {
2299    let ordered = Bool::and(&[&f32_is_nan(a).not(), &f32_is_nan(b).not()]);
2300    let rel = match kind {
2301        F32CmpKind::Lt => f32_ordered_lt(a, b),
2302        F32CmpKind::Gt => f32_ordered_lt(b, a),
2303        F32CmpKind::Ge => f32_ordered_lt(a, b).not(),
2304    };
2305    Bool::and(&[&ordered, &rel])
2306}
2307
2308#[derive(Clone, Copy)]
2309enum F32CmpKind {
2310    Lt,
2311    Gt,
2312    Ge,
2313}
2314
2315/// IEEE 754 double-precision NaN test over the raw bit pattern:
2316/// exponent all-ones (bits 62..52) with a non-zero fraction (bits 51..0).
2317/// The 64-bit twin of [`f32_is_nan`], for the #709/#756 f64→i32 trunc guards.
2318fn f64_is_nan(x: &BV) -> Bool {
2319    let exp_ones = x.extract(62, 52).eq(BV::from_u64(0x7FF, 11));
2320    let frac_nonzero = x.extract(51, 0).eq(BV::from_u64(0, 52)).not();
2321    Bool::and(&[&exp_ones, &frac_nonzero])
2322}
2323
2324/// Ordered `a < b` over IEEE 754 double-precision BIT PATTERNS, assuming
2325/// neither operand is NaN (the callers conjoin the NaN exclusion). The 64-bit
2326/// twin of [`f32_ordered_lt`]: sign bit 63, magnitude bits 62..0; `+0.0 == -0.0`
2327/// (neither is less).
2328fn f64_ordered_lt(a: &BV, b: &BV) -> Bool {
2329    let a_neg = a.extract(63, 63).eq(BV::from_u64(1, 1));
2330    let b_neg = b.extract(63, 63).eq(BV::from_u64(1, 1));
2331    let a_mag = a.extract(62, 0);
2332    let b_mag = b.extract(62, 0);
2333    let zero63 = BV::from_u64(0, 63);
2334    let both_zero = Bool::and(&[&a_mag.eq(&zero63), &b_mag.eq(&zero63)]);
2335    // (neg, neg): larger magnitude is smaller; (neg, pos): a < b unless both
2336    // are zeros; (pos, neg): never; (pos, pos): magnitude order.
2337    let neg_neg = b_mag.bvult(&a_mag);
2338    let neg_pos = both_zero.not();
2339    let pos_pos = a_mag.bvult(&b_mag);
2340    bool_ite(
2341        &a_neg,
2342        &bool_ite(&b_neg, &neg_neg, &neg_pos),
2343        &bool_ite(&b_neg, &Bool::from_bool(false), &pos_pos),
2344    )
2345}
2346
2347/// The three ordered VFP.F64 comparison results the f64 trunc guards use, as
2348/// total functions over the operands' bit patterns (result is 0 on any NaN —
2349/// the unordered case — exactly the ARM `VCMP.F64`+`VMRS`+`IT` materialization
2350/// the `F64Lt`/`F64Gt`/`F64Ge` pseudo-ops stand for). The 64-bit twin of
2351/// [`f32_cmp_result`].
2352fn f64_cmp_result(kind: F32CmpKind, a: &BV, b: &BV) -> Bool {
2353    let ordered = Bool::and(&[&f64_is_nan(a).not(), &f64_is_nan(b).not()]);
2354    let rel = match kind {
2355        F32CmpKind::Lt => f64_ordered_lt(a, b),
2356        F32CmpKind::Gt => f64_ordered_lt(b, a),
2357        F32CmpKind::Ge => f64_ordered_lt(a, b).not(),
2358    };
2359    Bool::and(&[&ordered, &rel])
2360}
2361
2362impl ArmSemantics {
2363    /// Branch-taking guarded symbolic execution of an ARM sequence,
2364    /// deriving `state.may_trap` from the emitted guard structure
2365    /// (VCR-VER-002, #166).
2366    ///
2367    /// Forward-branch DAG execution over the op list: every instruction
2368    /// carries the disjunction of the path conditions that reach it
2369    /// (if-conversion), `BCondOffset` routes guards forward, and a `Udf`
2370    /// accumulates its path guard into [`ArmState::may_trap`] — and does NOT
2371    /// fall through (a trap halts execution, so the code after a guarded
2372    /// `UDF` is reached only via the guard's skip branch). This makes the ARM
2373    /// trap condition a DERIVED term: a lowering whose guard was dropped,
2374    /// inverted, or aimed at the wrong register derives a trap condition that
2375    /// fails the preservation VC — unlike the previous structural
2376    /// `Udf`-presence proxy, which only saw that *some* trap existed.
2377    ///
2378    /// Branch targets are resolved in bytes via the shipped byte-size
2379    /// estimator (`synth_synthesis::optimizer_bridge::estimate_arm_byte_size`,
2380    /// the #511 estimator that CI pins against the encoder), matching the
2381    /// encoder's `target = branch + 4 + 2*offset` halfword rule. A target
2382    /// that lands mid-instruction, a backward branch (loop), an op outside
2383    /// the modeled subset, or any label/call control flow is a loud `Err` —
2384    /// never a silent accept.
2385    pub fn encode_sequence_br(
2386        &self,
2387        arm_ops: &[ArmOp],
2388        state: &mut ArmState,
2389    ) -> Result<(), String> {
2390        use synth_synthesis::optimizer_bridge::estimate_arm_byte_size;
2391
2392        // Byte offset of each op (the estimator is the pinned encoder mirror).
2393        let mut offsets = Vec::with_capacity(arm_ops.len());
2394        let mut off = 0usize;
2395        for op in arm_ops {
2396            offsets.push(off);
2397            off += estimate_arm_byte_size(op);
2398        }
2399        let total_len = off;
2400        let boundaries: std::collections::HashSet<usize> = offsets.iter().copied().collect();
2401
2402        let mut incoming: HashMap<usize, Guard> = HashMap::new();
2403        incoming.insert(0, Guard::Always);
2404
2405        for (i, op) in arm_ops.iter().enumerate() {
2406            let o = offsets[i];
2407            // Unreached instruction (e.g. dead code behind an unconditional
2408            // trap): no incoming edge, skip — it can never execute.
2409            let Some(g) = incoming.get(&o).cloned() else {
2410                continue;
2411            };
2412            let next = o + estimate_arm_byte_size(op);
2413
2414            match op {
2415                ArmOp::BCondOffset { cond, offset } => {
2416                    if *offset < 0 {
2417                        return Err(
2418                            "backward branch (loop) outside the trap-derivation subset — held out"
2419                                .to_string(),
2420                        );
2421                    }
2422                    // Encoder rule: offset is the halfword displacement,
2423                    // target = branch_addr + 4 + 2*offset.
2424                    let target = o + 4 + 2 * (*offset as usize);
2425                    if target != total_len && !boundaries.contains(&target) {
2426                        return Err(format!(
2427                            "BCondOffset target {target} lands mid-instruction \
2428                             (sequence len {total_len}) — estimator/encoder drift or \
2429                             malformed guard"
2430                        ));
2431                    }
2432                    let c = self.evaluate_condition(cond, &state.flags);
2433                    merge_guard(&mut incoming, target, g.and_cond(&c));
2434                    merge_guard(&mut incoming, next, g.and_cond(&c.not()));
2435                }
2436
2437                ArmOp::Udf { .. } => {
2438                    // The trap fires exactly under this path guard; execution
2439                    // never continues past it (no fall-through edge).
2440                    state.may_trap = match &g {
2441                        Guard::Always => Bool::from_bool(true),
2442                        Guard::Cond(gb) => Bool::or(&[&state.may_trap, gb]),
2443                    };
2444                }
2445
2446                // Label/relative/indirect control flow has no derivable local
2447                // trap semantics here — loud decline, never a silent accept.
2448                ArmOp::B { .. }
2449                | ArmOp::BOffset { .. }
2450                | ArmOp::Bcc { .. }
2451                | ArmOp::Bhs { .. }
2452                | ArmOp::Blo { .. }
2453                | ArmOp::Bl { .. }
2454                | ArmOp::Blx { .. }
2455                | ArmOp::Bx { .. }
2456                | ArmOp::Label { .. }
2457                | ArmOp::Call { .. }
2458                | ArmOp::CallIndirect { .. }
2459                | ArmOp::BrTable { .. }
2460                | ArmOp::Push { .. }
2461                | ArmOp::Pop { .. } => {
2462                    return Err(format!(
2463                        "op {op:?} outside the trap-derivation subset — loud decline"
2464                    ));
2465                }
2466
2467                _ => {
2468                    match &g {
2469                        Guard::Always => self.exec_trap_subset_op(op, state)?,
2470                        Guard::Cond(gb) => {
2471                            // Guarded (if-converted) execution: snapshot the
2472                            // register/flag/VFP state, execute, ite-merge
2473                            // under the guard. Sound because these ops touch
2474                            // only registers/flags/VFP (the subset check in
2475                            // exec_trap_subset_op rejects everything else).
2476                            //
2477                            // Only components the op actually CHANGED are
2478                            // merged — `ite(g, x, x) ≡ x`, and wrapping every
2479                            // untouched register on every guarded step nests
2480                            // the SDIV/UDIV operands in ite chains, blowing
2481                            // the div/rem trap VC off a CDCL cliff (observed:
2482                            // the div_s double-guard query ran 45+ min / 5 GB
2483                            // with the unconditional merge, sub-second
2484                            // without).
2485                            let regs_before = state.registers.clone();
2486                            let vfp_before = state.vfp_registers.clone();
2487                            let flags_before = ConditionFlags {
2488                                n: state.flags.n.clone(),
2489                                z: state.flags.z.clone(),
2490                                c: state.flags.c.clone(),
2491                                v: state.flags.v.clone(),
2492                            };
2493                            self.exec_trap_subset_op(op, state)?;
2494                            for (r, before) in regs_before.iter().enumerate() {
2495                                if !state.registers[r].same_term(before) {
2496                                    state.registers[r] = gb.ite(&state.registers[r], before);
2497                                }
2498                            }
2499                            for (r, before) in vfp_before.iter().enumerate() {
2500                                if !state.vfp_registers[r].same_term(before) {
2501                                    state.vfp_registers[r] =
2502                                        gb.ite(&state.vfp_registers[r], before);
2503                                }
2504                            }
2505                            if !state.flags.n.same_term(&flags_before.n) {
2506                                state.flags.n = bool_ite(gb, &state.flags.n, &flags_before.n);
2507                            }
2508                            if !state.flags.z.same_term(&flags_before.z) {
2509                                state.flags.z = bool_ite(gb, &state.flags.z, &flags_before.z);
2510                            }
2511                            if !state.flags.c.same_term(&flags_before.c) {
2512                                state.flags.c = bool_ite(gb, &state.flags.c, &flags_before.c);
2513                            }
2514                            if !state.flags.v.same_term(&flags_before.v) {
2515                                state.flags.v = bool_ite(gb, &state.flags.v, &flags_before.v);
2516                            }
2517                        }
2518                    }
2519                    merge_guard(&mut incoming, next, g);
2520                }
2521            }
2522        }
2523
2524        Ok(())
2525    }
2526
2527    /// Whether the sequence's branch structure is VALUE-DEAD: every op inside
2528    /// a branch-skipped span writes no register/VFP state (`Udf`, `Cmp`,
2529    /// `Cmn`, nested `BCondOffset` only), and no op anywhere in the sequence
2530    /// turns flags into a register value (`SetCond`).
2531    ///
2532    /// Under this condition the final REGISTER state is path-independent —
2533    /// every register-writing op executes on every path, in program order —
2534    /// so the straight-line value pass
2535    /// [`Self::encode_sequence_value_straightline`] computes exactly the
2536    /// registers any non-trapping real path produces. The flag writes a taken
2537    /// branch skips (e.g. the div_s overflow guard's `CMN` behind `BNE +3`)
2538    /// can only influence which PATH is taken — the trap side, which
2539    /// [`Self::encode_sequence_br`] derives with full path sensitivity — and
2540    /// never a register value, because `SetCond` (the only flag→register op
2541    /// in the modeled subset) is excluded outright.
2542    ///
2543    /// This is what lets the div/rem trap VC keep its value clause
2544    /// STRUCTURALLY aligned with the WASM side (`bvsdiv`/`MLS` terms
2545    /// identical after canonicalization): an `ite(guard, …)` wrapper on an
2546    /// SDIV/MLS operand un-shares the 32×32 multiplier/divider circuits and
2547    /// sends the UNSAT proof off the CDCL cliff term.rs documents (observed:
2548    /// rem_s value clause 15+ min with the ite, sub-second without).
2549    pub fn branch_spans_are_value_dead(arm_ops: &[ArmOp]) -> bool {
2550        use synth_synthesis::optimizer_bridge::estimate_arm_byte_size;
2551
2552        let mut offsets = Vec::with_capacity(arm_ops.len());
2553        let mut off = 0usize;
2554        for op in arm_ops {
2555            offsets.push(off);
2556            off += estimate_arm_byte_size(op);
2557        }
2558
2559        // No flag→register materialization anywhere in the sequence.
2560        if arm_ops.iter().any(|op| matches!(op, ArmOp::SetCond { .. })) {
2561            return false;
2562        }
2563
2564        for (i, op) in arm_ops.iter().enumerate() {
2565            if let ArmOp::BCondOffset { offset, .. } = op {
2566                if *offset < 0 {
2567                    return false; // backward branch — not this subset at all
2568                }
2569                // Fall-through = next instruction; encoder rule for the
2570                // target: branch_addr + 4 + 2*offset (same as
2571                // `encode_sequence_br`). The skipped span is [fall-through,
2572                // target).
2573                let span_start = offsets[i] + estimate_arm_byte_size(op);
2574                let span_end = offsets[i] + 4 + 2 * (*offset as usize);
2575                for (j, skipped) in arm_ops.iter().enumerate() {
2576                    if offsets[j] >= span_start && offsets[j] < span_end {
2577                        match skipped {
2578                            ArmOp::Udf { .. }
2579                            | ArmOp::Cmp { .. }
2580                            | ArmOp::Cmn { .. }
2581                            | ArmOp::BCondOffset { .. } => {}
2582                            _ => return false, // a register/VFP write is skippable
2583                        }
2584                    }
2585                }
2586            }
2587        }
2588        true
2589    }
2590
2591    /// Straight-line VALUE execution of a trap-guarded sequence: branches and
2592    /// `UDF`s are register no-ops, every other op executes unconditionally
2593    /// via the same modeled subset as the branch-taking executor.
2594    ///
2595    /// ONLY sound when [`Self::branch_spans_are_value_dead`] holds (see its
2596    /// doc for the argument); callers must check it first. Produces ite-free
2597    /// register terms, keeping the trap VC's value clause structurally
2598    /// aligned with the WASM encoding.
2599    pub fn encode_sequence_value_straightline(
2600        &self,
2601        arm_ops: &[ArmOp],
2602        state: &mut ArmState,
2603    ) -> Result<(), String> {
2604        for op in arm_ops {
2605            match op {
2606                ArmOp::BCondOffset { .. } | ArmOp::Udf { .. } => {}
2607                _ => self.exec_trap_subset_op(op, state)?,
2608            }
2609        }
2610        Ok(())
2611    }
2612
2613    /// Execute one non-branch op of the trap-derivation subset. Ops the
2614    /// shipped trap-guarded lowerings use but `encode_op` leaves unmodeled
2615    /// (`Cmn`, `Movw`, `Movt`, the ordered VFP compares) get explicit
2616    /// semantics here; a WHITELIST of register-only value ops delegates to
2617    /// `encode_op`; anything else is a loud `Err` — `encode_op`'s silent
2618    /// `_ => {}` default must never green-wash a trap derivation.
2619    fn exec_trap_subset_op(&self, op: &ArmOp, state: &mut ArmState) -> Result<(), String> {
2620        match op {
2621            // CMN: compare negated — flags from rn + op2.
2622            ArmOp::Cmn { rn, op2 } => {
2623                let a = state.get_reg(rn).clone();
2624                let b = self.evaluate_operand2(op2, state);
2625                let result = a.bvadd(&b);
2626                self.update_flags_add(state, &a, &b, &result);
2627                Ok(())
2628            }
2629            ArmOp::Movw { rd, imm16 } => {
2630                state.set_reg(rd, BV::from_u64(*imm16 as u64, 32));
2631                Ok(())
2632            }
2633            ArmOp::Movt { rd, imm16 } => {
2634                let low = state.get_reg(rd).bvand(BV::from_u64(0xFFFF, 32));
2635                let v = low.bvor(BV::from_u64((*imm16 as u64) << 16, 32));
2636                state.set_reg(rd, v);
2637                Ok(())
2638            }
2639            // Ordered VFP compares (the #709 trunc guards): real bit-pattern
2640            // semantics — result register is 1 iff the ordered relation
2641            // holds, 0 on NaN. encode_op models these as uninterpreted
2642            // symbols, which cannot drive a trap derivation.
2643            ArmOp::F32Lt { rd, sn, sm } => {
2644                let a = state.get_vfp_reg(sn).clone();
2645                let b = state.get_vfp_reg(sm).clone();
2646                let r = self.bool_to_bv32(&f32_cmp_result(F32CmpKind::Lt, &a, &b));
2647                state.set_reg(rd, r);
2648                Ok(())
2649            }
2650            ArmOp::F32Gt { rd, sn, sm } => {
2651                let a = state.get_vfp_reg(sn).clone();
2652                let b = state.get_vfp_reg(sm).clone();
2653                let r = self.bool_to_bv32(&f32_cmp_result(F32CmpKind::Gt, &a, &b));
2654                state.set_reg(rd, r);
2655                Ok(())
2656            }
2657            ArmOp::F32Ge { rd, sn, sm } => {
2658                let a = state.get_vfp_reg(sn).clone();
2659                let b = state.get_vfp_reg(sm).clone();
2660                let r = self.bool_to_bv32(&f32_cmp_result(F32CmpKind::Ge, &a, &b));
2661                state.set_reg(rd, r);
2662                Ok(())
2663            }
2664            // Ordered VFP.F64 compares (the #756 f64→i32 trunc guards): real
2665            // bit-pattern semantics over the 64-bit D-register operands — 1 iff
2666            // the ordered relation holds, 0 on NaN. encode_op models these as
2667            // uninterpreted symbols, which cannot drive a trap derivation.
2668            ArmOp::F64Lt { rd, dn, dm } => {
2669                let a = state.get_vfp_reg(dn).clone();
2670                let b = state.get_vfp_reg(dm).clone();
2671                let r = self.bool_to_bv32(&f64_cmp_result(F32CmpKind::Lt, &a, &b));
2672                state.set_reg(rd, r);
2673                Ok(())
2674            }
2675            ArmOp::F64Gt { rd, dn, dm } => {
2676                let a = state.get_vfp_reg(dn).clone();
2677                let b = state.get_vfp_reg(dm).clone();
2678                let r = self.bool_to_bv32(&f64_cmp_result(F32CmpKind::Gt, &a, &b));
2679                state.set_reg(rd, r);
2680                Ok(())
2681            }
2682            ArmOp::F64Ge { rd, dn, dm } => {
2683                let a = state.get_vfp_reg(dn).clone();
2684                let b = state.get_vfp_reg(dm).clone();
2685                let r = self.bool_to_bv32(&f64_cmp_result(F32CmpKind::Ge, &a, &b));
2686                state.set_reg(rd, r);
2687                Ok(())
2688            }
2689            // Register/flag-only value ops the covered lowerings use:
2690            // delegate to the existing encode_op semantics.
2691            ArmOp::Cmp { .. }
2692            | ArmOp::Add { .. }
2693            | ArmOp::Sub { .. }
2694            | ArmOp::Rsb { .. }
2695            | ArmOp::Mov { .. }
2696            | ArmOp::And { .. }
2697            | ArmOp::Orr { .. }
2698            | ArmOp::Eor { .. }
2699            | ArmOp::Mul { .. }
2700            | ArmOp::Mls { .. }
2701            | ArmOp::Sdiv { .. }
2702            | ArmOp::Udiv { .. }
2703            | ArmOp::SetCond { .. }
2704            | ArmOp::Nop
2705            | ArmOp::F32Const { .. }
2706            | ArmOp::I32TruncF32S { .. }
2707            | ArmOp::I32TruncF32U { .. }
2708            // f64 trunc guards (#756): F64Const sets the D-reg to the real
2709            // 64-bit float bit pattern (load-bearing for the derived compare);
2710            // the saturating VCVT pseudo-ops write only the RESULT register,
2711            // which the trap derivation ignores.
2712            | ArmOp::F64Const { .. }
2713            | ArmOp::I32TruncF64S { .. }
2714            | ArmOp::I32TruncF64U { .. }
2715            | ArmOp::I64TruncF64S { .. }
2716            | ArmOp::I64TruncF64U { .. }
2717            // Ldr/Str: the value model treats loads as fresh symbols and
2718            // stores as no-ops (no memory-contents model) — fine for a trap
2719            // derivation, where only the guard's flags/registers matter.
2720            | ArmOp::Ldr { .. }
2721            | ArmOp::Str { .. } => {
2722                self.encode_op(op, state);
2723                Ok(())
2724            }
2725            // Subword accesses (#752 gate coverage for the guarded
2726            // i32.load8/16 + i32.store8/16 shapes): same treatment as
2727            // Ldr/Str — a load writes a fresh symbol (no memory-contents
2728            // model), a store touches no register. Neither affects flags,
2729            // so the trap derivation is untouched; modeling them here just
2730            // lets guarded subword sequences through instead of a loud
2731            // decline.
2732            ArmOp::Ldrb { rd, .. }
2733            | ArmOp::Ldrsb { rd, .. }
2734            | ArmOp::Ldrh { rd, .. }
2735            | ArmOp::Ldrsh { rd, .. } => {
2736                let result = BV::new_const(format!("load_{rd:?}"), 32);
2737                state.set_reg(rd, result);
2738                Ok(())
2739            }
2740            ArmOp::Strb { .. } | ArmOp::Strh { .. } => Ok(()),
2741            // i64 rem_u/rem_s pseudo-ops (VCR-VER, #825/#836): register-only
2742            // (they set the `rd*` pair), so they belong in this subset — the
2743            // executor delegates to the general value model, which now builds a
2744            // real `BvTerm::Urem`/`bvsrem` term. The ÷0 TRAP is NOT derived
2745            // here (the bare pseudo-op carries no `UDF`); the VC reconstructs
2746            // it from the pseudo-op's `elide_zero_guard` field, exactly like
2747            // the i64 trap-only VC. div_u/div_s stay OUT (their 64-bit quotient
2748            // value model is havoc — no value VC consumes it).
2749            ArmOp::I64RemU { .. } | ArmOp::I64RemS { .. } => {
2750                self.encode_op(op, state);
2751                Ok(())
2752            }
2753            other => Err(format!(
2754                "op {other:?} outside the trap-derivation subset — loud decline"
2755            )),
2756        }
2757    }
2758}
2759
2760#[cfg(test)]
2761mod tests {
2762    use super::*;
2763    use crate::with_verification_context;
2764
2765    #[test]
2766    fn test_arm_add_semantics() {
2767        with_verification_context(|| {
2768            let encoder = ArmSemantics::new();
2769            let mut state = ArmState::new_symbolic();
2770
2771            // Set up concrete values for testing
2772            state.set_reg(&Reg::R1, BV::from_i64(10, 32));
2773            state.set_reg(&Reg::R2, BV::from_i64(20, 32));
2774
2775            // Execute: ADD R0, R1, R2
2776            let op = ArmOp::Add {
2777                rd: Reg::R0,
2778                rn: Reg::R1,
2779                op2: Operand2::Reg(Reg::R2),
2780            };
2781
2782            encoder.encode_op(&op, &mut state);
2783
2784            // Check result: R0 should be 30
2785            let result = state.get_reg(&Reg::R0).simplify();
2786            assert_eq!(result.as_i64(), Some(30));
2787        });
2788    }
2789
2790    #[test]
2791    fn test_arm_sub_semantics() {
2792        with_verification_context(|| {
2793            let encoder = ArmSemantics::new();
2794            let mut state = ArmState::new_symbolic();
2795
2796            state.set_reg(&Reg::R1, BV::from_i64(50, 32));
2797            state.set_reg(&Reg::R2, BV::from_i64(20, 32));
2798
2799            let op = ArmOp::Sub {
2800                rd: Reg::R0,
2801                rn: Reg::R1,
2802                op2: Operand2::Reg(Reg::R2),
2803            };
2804
2805            encoder.encode_op(&op, &mut state);
2806
2807            let result = state.get_reg(&Reg::R0);
2808            assert_eq!(result.simplify().as_i64(), Some(30));
2809        });
2810    }
2811
2812    #[test]
2813    fn test_arm_mov_immediate() {
2814        with_verification_context(|| {
2815            let encoder = ArmSemantics::new();
2816            let mut state = ArmState::new_symbolic();
2817
2818            let op = ArmOp::Mov {
2819                rd: Reg::R0,
2820                op2: Operand2::Imm(42),
2821            };
2822
2823            encoder.encode_op(&op, &mut state);
2824
2825            let result = state.get_reg(&Reg::R0);
2826            assert_eq!(result.simplify().as_i64(), Some(42));
2827        });
2828    }
2829
2830    #[test]
2831    fn test_arm_bitwise_ops() {
2832        with_verification_context(|| {
2833            let encoder = ArmSemantics::new();
2834            let mut state = ArmState::new_symbolic();
2835
2836            state.set_reg(&Reg::R1, BV::from_i64(0b1010, 32));
2837            state.set_reg(&Reg::R2, BV::from_i64(0b1100, 32));
2838
2839            // Test AND
2840            let and_op = ArmOp::And {
2841                rd: Reg::R0,
2842                rn: Reg::R1,
2843                op2: Operand2::Reg(Reg::R2),
2844            };
2845            encoder.encode_op(&and_op, &mut state);
2846            assert_eq!(state.get_reg(&Reg::R0).simplify().as_i64(), Some(0b1000));
2847
2848            // Test ORR
2849            let orr_op = ArmOp::Orr {
2850                rd: Reg::R0,
2851                rn: Reg::R1,
2852                op2: Operand2::Reg(Reg::R2),
2853            };
2854            encoder.encode_op(&orr_op, &mut state);
2855            assert_eq!(state.get_reg(&Reg::R0).simplify().as_i64(), Some(0b1110));
2856
2857            // Test EOR (XOR)
2858            let eor_op = ArmOp::Eor {
2859                rd: Reg::R0,
2860                rn: Reg::R1,
2861                op2: Operand2::Reg(Reg::R2),
2862            };
2863            encoder.encode_op(&eor_op, &mut state);
2864            assert_eq!(state.get_reg(&Reg::R0).simplify().as_i64(), Some(0b0110));
2865        });
2866    }
2867
2868    #[test]
2869    fn test_arm_mls() {
2870        // Test MLS (Multiply and Subtract): Rd = Ra - Rn * Rm
2871        // This is used for remainder: a % b = a - (a/b) * b
2872        with_verification_context(|| {
2873            let encoder = ArmSemantics::new();
2874            let mut state = ArmState::new_symbolic();
2875
2876            // Test: 17 % 5 = 17 - (17/5) * 5 = 17 - 3*5 = 17 - 15 = 2
2877            // Ra = 17, Rn = 3 (quotient), Rm = 5 (divisor)
2878            state.set_reg(&Reg::R0, BV::from_i64(17, 32)); // Ra (dividend)
2879            state.set_reg(&Reg::R1, BV::from_i64(3, 32)); // Rn (quotient)
2880            state.set_reg(&Reg::R2, BV::from_i64(5, 32)); // Rm (divisor)
2881
2882            let mls_op = ArmOp::Mls {
2883                rd: Reg::R3,
2884                rn: Reg::R1,
2885                rm: Reg::R2,
2886                ra: Reg::R0,
2887            };
2888            encoder.encode_op(&mls_op, &mut state);
2889            assert_eq!(
2890                state.get_reg(&Reg::R3).simplify().as_i64(),
2891                Some(2),
2892                "MLS: 17 - 3*5 = 2"
2893            );
2894
2895            // Test: 100 - 7 * 3 = 100 - 21 = 79
2896            state.set_reg(&Reg::R0, BV::from_i64(100, 32));
2897            state.set_reg(&Reg::R1, BV::from_i64(7, 32));
2898            state.set_reg(&Reg::R2, BV::from_i64(3, 32));
2899
2900            let mls_op2 = ArmOp::Mls {
2901                rd: Reg::R3,
2902                rn: Reg::R1,
2903                rm: Reg::R2,
2904                ra: Reg::R0,
2905            };
2906            encoder.encode_op(&mls_op2, &mut state);
2907            assert_eq!(
2908                state.get_reg(&Reg::R3).simplify().as_i64(),
2909                Some(79),
2910                "MLS: 100 - 7*3 = 79"
2911            );
2912
2913            // Test with negative numbers: (-17) - 3 * 5 = -17 - 15 = -32
2914            state.set_reg(&Reg::R0, BV::from_i64(-17, 32));
2915            state.set_reg(&Reg::R1, BV::from_i64(3, 32));
2916            state.set_reg(&Reg::R2, BV::from_i64(5, 32));
2917
2918            let mls_op3 = ArmOp::Mls {
2919                rd: Reg::R3,
2920                rn: Reg::R1,
2921                rm: Reg::R2,
2922                ra: Reg::R0,
2923            };
2924            encoder.encode_op(&mls_op3, &mut state);
2925            // Result is -32, but as_i64() returns unsigned, so we need to convert
2926            let result = state.get_reg(&Reg::R3).simplify().as_i64();
2927            let signed_result = result.map(|v| (v as i32) as i64);
2928            assert_eq!(signed_result, Some(-32), "MLS: -17 - 3*5 = -32");
2929        });
2930    }
2931
2932    #[test]
2933    fn test_arm_shift_ops() {
2934        with_verification_context(|| {
2935            let encoder = ArmSemantics::new();
2936            let mut state = ArmState::new_symbolic();
2937
2938            state.set_reg(&Reg::R1, BV::from_i64(8, 32));
2939
2940            // Test LSL (logical shift left) with immediate
2941            let lsl_op = ArmOp::Lsl {
2942                rd: Reg::R0,
2943                rn: Reg::R1,
2944                shift: 2,
2945            };
2946            encoder.encode_op(&lsl_op, &mut state);
2947            assert_eq!(state.get_reg(&Reg::R0).simplify().as_i64(), Some(32));
2948
2949            // Test LSR (logical shift right) with immediate
2950            let lsr_op = ArmOp::Lsr {
2951                rd: Reg::R0,
2952                rn: Reg::R1,
2953                shift: 2,
2954            };
2955            encoder.encode_op(&lsr_op, &mut state);
2956            assert_eq!(state.get_reg(&Reg::R0).simplify().as_i64(), Some(2));
2957        });
2958    }
2959
2960    #[test]
2961    fn test_arm_ror_comprehensive() {
2962        with_verification_context(|| {
2963            let encoder = ArmSemantics::new();
2964            let mut state = ArmState::new_symbolic();
2965
2966            // Test ROR with 0x12345678
2967            // ROR by 8 should rotate right by 8 bits
2968            state.set_reg(&Reg::R1, BV::from_u64(0x12345678, 32));
2969            let ror_op = ArmOp::Ror {
2970                rd: Reg::R0,
2971                rn: Reg::R1,
2972                shift: 8,
2973            };
2974            encoder.encode_op(&ror_op, &mut state);
2975            // 0x12345678 ROR 8 = 0x78123456
2976            assert_eq!(
2977                state.get_reg(&Reg::R0).simplify().as_i64(),
2978                Some(0x78123456),
2979                "ROR by 8"
2980            );
2981
2982            // Test ROR by 16 (swap halves)
2983            let ror_op_16 = ArmOp::Ror {
2984                rd: Reg::R0,
2985                rn: Reg::R1,
2986                shift: 16,
2987            };
2988            encoder.encode_op(&ror_op_16, &mut state);
2989            // 0x12345678 ROR 16 = 0x56781234
2990            assert_eq!(
2991                state.get_reg(&Reg::R0).simplify().as_i64(),
2992                Some(0x56781234),
2993                "ROR by 16"
2994            );
2995
2996            // Test ROR by 0 (no change)
2997            let ror_op_0 = ArmOp::Ror {
2998                rd: Reg::R0,
2999                rn: Reg::R1,
3000                shift: 0,
3001            };
3002            encoder.encode_op(&ror_op_0, &mut state);
3003            assert_eq!(
3004                state.get_reg(&Reg::R0).simplify().as_i64(),
3005                Some(0x12345678),
3006                "ROR by 0"
3007            );
3008
3009            // Test ROR by 32 (full rotation, back to original)
3010            let ror_op_32 = ArmOp::Ror {
3011                rd: Reg::R0,
3012                rn: Reg::R1,
3013                shift: 32,
3014            };
3015            encoder.encode_op(&ror_op_32, &mut state);
3016            assert_eq!(
3017                state.get_reg(&Reg::R0).simplify().as_i64(),
3018                Some(0x12345678),
3019                "ROR by 32"
3020            );
3021
3022            // Test ROR by 4 (nibble rotation)
3023            state.set_reg(&Reg::R1, BV::from_u64(0xABCDEF01, 32));
3024            let ror_op_4 = ArmOp::Ror {
3025                rd: Reg::R0,
3026                rn: Reg::R1,
3027                shift: 4,
3028            };
3029            encoder.encode_op(&ror_op_4, &mut state);
3030            // 0xABCDEF01 ROR 4 = 0x1ABCDEF0
3031            assert_eq!(
3032                state.get_reg(&Reg::R0).simplify().as_i64(),
3033                Some(0x1ABCDEF0),
3034                "ROR by 4"
3035            );
3036
3037            // Test ROR with 1-bit rotation
3038            state.set_reg(&Reg::R1, BV::from_u64(0x80000001, 32));
3039            let ror_op_1 = ArmOp::Ror {
3040                rd: Reg::R0,
3041                rn: Reg::R1,
3042                shift: 1,
3043            };
3044            encoder.encode_op(&ror_op_1, &mut state);
3045            // 0x80000001 ROR 1 = 0xC0000000
3046            let result = state.get_reg(&Reg::R0).simplify().as_i64();
3047            let signed_result = result.map(|v| (v as i32) as i64);
3048            assert_eq!(
3049                signed_result,
3050                Some(0xC0000000_u32 as i32 as i64),
3051                "ROR by 1"
3052            );
3053        });
3054    }
3055
3056    #[test]
3057    fn test_arm_clz_comprehensive() {
3058        with_verification_context(|| {
3059            let encoder = ArmSemantics::new();
3060            let mut state = ArmState::new_symbolic();
3061
3062            // Test CLZ(0) = 32
3063            state.set_reg(&Reg::R1, BV::from_i64(0, 32));
3064            let clz_op = ArmOp::Clz {
3065                rd: Reg::R0,
3066                rm: Reg::R1,
3067            };
3068            encoder.encode_op(&clz_op, &mut state);
3069            assert_eq!(
3070                state.get_reg(&Reg::R0).simplify().as_i64(),
3071                Some(32),
3072                "CLZ(0) should be 32"
3073            );
3074
3075            // Test CLZ(1) = 31
3076            state.set_reg(&Reg::R1, BV::from_i64(1, 32));
3077            encoder.encode_op(&clz_op, &mut state);
3078            assert_eq!(
3079                state.get_reg(&Reg::R0).simplify().as_i64(),
3080                Some(31),
3081                "CLZ(1) should be 31"
3082            );
3083
3084            // Test CLZ(0x80000000) = 0
3085            state.set_reg(&Reg::R1, BV::from_u64(0x80000000, 32));
3086            encoder.encode_op(&clz_op, &mut state);
3087            assert_eq!(
3088                state.get_reg(&Reg::R0).simplify().as_i64(),
3089                Some(0),
3090                "CLZ(0x80000000) should be 0"
3091            );
3092
3093            // Test CLZ(0x00FF0000) = 8
3094            state.set_reg(&Reg::R1, BV::from_u64(0x00FF0000, 32));
3095            encoder.encode_op(&clz_op, &mut state);
3096            assert_eq!(
3097                state.get_reg(&Reg::R0).simplify().as_i64(),
3098                Some(8),
3099                "CLZ(0x00FF0000) should be 8"
3100            );
3101
3102            // Test CLZ(0x00001000) = 19
3103            state.set_reg(&Reg::R1, BV::from_u64(0x00001000, 32));
3104            encoder.encode_op(&clz_op, &mut state);
3105            assert_eq!(
3106                state.get_reg(&Reg::R0).simplify().as_i64(),
3107                Some(19),
3108                "CLZ(0x00001000) should be 19"
3109            );
3110
3111            // Test CLZ(0xFFFFFFFF) = 0
3112            state.set_reg(&Reg::R1, BV::from_u64(0xFFFFFFFF, 32));
3113            encoder.encode_op(&clz_op, &mut state);
3114            assert_eq!(
3115                state.get_reg(&Reg::R0).simplify().as_i64(),
3116                Some(0),
3117                "CLZ(0xFFFFFFFF) should be 0"
3118            );
3119        });
3120    }
3121
3122    #[test]
3123    fn test_arm_rbit_comprehensive() {
3124        with_verification_context(|| {
3125            let encoder = ArmSemantics::new();
3126            let mut state = ArmState::new_symbolic();
3127
3128            let rbit_op = ArmOp::Rbit {
3129                rd: Reg::R0,
3130                rm: Reg::R1,
3131            };
3132
3133            // Test RBIT(0) = 0
3134            state.set_reg(&Reg::R1, BV::from_i64(0, 32));
3135            encoder.encode_op(&rbit_op, &mut state);
3136            assert_eq!(
3137                state.get_reg(&Reg::R0).simplify().as_i64(),
3138                Some(0),
3139                "RBIT(0) should be 0"
3140            );
3141
3142            // Test RBIT(1) = 0x80000000 (bit 0 → bit 31)
3143            state.set_reg(&Reg::R1, BV::from_i64(1, 32));
3144            encoder.encode_op(&rbit_op, &mut state);
3145            assert_eq!(
3146                state.get_reg(&Reg::R0).simplify().as_u64(),
3147                Some(0x80000000),
3148                "RBIT(1) should be 0x80000000"
3149            );
3150
3151            // Test RBIT(0x80000000) = 1 (bit 31 → bit 0)
3152            state.set_reg(&Reg::R1, BV::from_u64(0x80000000, 32));
3153            encoder.encode_op(&rbit_op, &mut state);
3154            assert_eq!(
3155                state.get_reg(&Reg::R0).simplify().as_i64(),
3156                Some(1),
3157                "RBIT(0x80000000) should be 1"
3158            );
3159
3160            // Test RBIT(0xFF000000) = 0x000000FF (top byte → bottom byte)
3161            state.set_reg(&Reg::R1, BV::from_u64(0xFF000000, 32));
3162            encoder.encode_op(&rbit_op, &mut state);
3163            assert_eq!(
3164                state.get_reg(&Reg::R0).simplify().as_u64(),
3165                Some(0x000000FF),
3166                "RBIT(0xFF000000) should be 0x000000FF"
3167            );
3168
3169            // Test RBIT(0x12345678) - specific pattern
3170            state.set_reg(&Reg::R1, BV::from_u64(0x12345678, 32));
3171            encoder.encode_op(&rbit_op, &mut state);
3172            // 0x12345678 reversed = 0x1E6A2C48
3173            assert_eq!(
3174                state.get_reg(&Reg::R0).simplify().as_u64(),
3175                Some(0x1E6A2C48),
3176                "RBIT(0x12345678) should be 0x1E6A2C48"
3177            );
3178
3179            // Test RBIT(0xFFFFFFFF) = 0xFFFFFFFF (all bits stay)
3180            state.set_reg(&Reg::R1, BV::from_u64(0xFFFFFFFF, 32));
3181            encoder.encode_op(&rbit_op, &mut state);
3182            assert_eq!(
3183                state.get_reg(&Reg::R0).simplify().as_u64(),
3184                Some(0xFFFFFFFF),
3185                "RBIT(0xFFFFFFFF) should be 0xFFFFFFFF"
3186            );
3187        });
3188    }
3189
3190    #[test]
3191    fn test_arm_cmp_flags() {
3192        // Test CMP instruction and condition flag updates
3193
3194        with_verification_context(|| {
3195            let encoder = ArmSemantics::new();
3196            let mut state = ArmState::new_symbolic();
3197
3198            // Test 1: CMP with equal values (10 - 10 = 0)
3199            // Should set: Z=1, N=0, C=1 (no borrow), V=0
3200            state.set_reg(&Reg::R0, BV::from_i64(10, 32));
3201            state.set_reg(&Reg::R1, BV::from_i64(10, 32));
3202
3203            let cmp_op = ArmOp::Cmp {
3204                rn: Reg::R0,
3205                op2: Operand2::Reg(Reg::R1),
3206            };
3207            encoder.encode_op(&cmp_op, &mut state);
3208
3209            assert_eq!(
3210                state.flags.z.simplify().as_bool(),
3211                Some(true),
3212                "Z flag should be set (equal)"
3213            );
3214            assert_eq!(
3215                state.flags.n.simplify().as_bool(),
3216                Some(false),
3217                "N flag should be clear (non-negative)"
3218            );
3219            assert_eq!(
3220                state.flags.c.simplify().as_bool(),
3221                Some(true),
3222                "C flag should be set (no borrow)"
3223            );
3224            assert_eq!(
3225                state.flags.v.simplify().as_bool(),
3226                Some(false),
3227                "V flag should be clear (no overflow)"
3228            );
3229
3230            // Test 2: CMP with first > second (20 - 10 = 10)
3231            // Should set: Z=0, N=0, C=1 (no borrow), V=0
3232            state.set_reg(&Reg::R0, BV::from_i64(20, 32));
3233            state.set_reg(&Reg::R1, BV::from_i64(10, 32));
3234            encoder.encode_op(&cmp_op, &mut state);
3235
3236            assert_eq!(
3237                state.flags.z.simplify().as_bool(),
3238                Some(false),
3239                "Z flag should be clear (not equal)"
3240            );
3241            assert_eq!(
3242                state.flags.n.simplify().as_bool(),
3243                Some(false),
3244                "N flag should be clear (positive result)"
3245            );
3246            assert_eq!(
3247                state.flags.c.simplify().as_bool(),
3248                Some(true),
3249                "C flag should be set (no borrow)"
3250            );
3251            assert_eq!(
3252                state.flags.v.simplify().as_bool(),
3253                Some(false),
3254                "V flag should be clear (no overflow)"
3255            );
3256
3257            // Test 3: CMP with first < second (unsigned: will wrap)
3258            // 10 - 20 = -10 (0xFFFFFFF6 in two's complement)
3259            // Should set: Z=0, N=1 (negative), C=0 (borrow), V=0
3260            state.set_reg(&Reg::R0, BV::from_i64(10, 32));
3261            state.set_reg(&Reg::R1, BV::from_i64(20, 32));
3262            encoder.encode_op(&cmp_op, &mut state);
3263
3264            assert_eq!(
3265                state.flags.z.simplify().as_bool(),
3266                Some(false),
3267                "Z flag should be clear"
3268            );
3269            assert_eq!(
3270                state.flags.n.simplify().as_bool(),
3271                Some(true),
3272                "N flag should be set (negative result)"
3273            );
3274            assert_eq!(
3275                state.flags.c.simplify().as_bool(),
3276                Some(false),
3277                "C flag should be clear (borrow occurred)"
3278            );
3279            assert_eq!(
3280                state.flags.v.simplify().as_bool(),
3281                Some(false),
3282                "V flag should be clear"
3283            );
3284
3285            // Test 4: Signed overflow case
3286            // Subtracting large negative from positive should overflow
3287            // 0x7FFFFFFF (max positive) - 0x80000000 (min negative)
3288            // Result wraps to negative, but mathematically should be huge positive
3289            state.set_reg(&Reg::R0, BV::from_i64(0x7FFFFFFF, 32));
3290            state.set_reg(&Reg::R1, BV::from_i64(-2147483648i64, 32)); // 0x80000000
3291            encoder.encode_op(&cmp_op, &mut state);
3292
3293            assert_eq!(
3294                state.flags.z.simplify().as_bool(),
3295                Some(false),
3296                "Z flag should be clear"
3297            );
3298            assert_eq!(
3299                state.flags.n.simplify().as_bool(),
3300                Some(true),
3301                "N flag should be set (wrapped result)"
3302            );
3303            assert_eq!(
3304                state.flags.c.simplify().as_bool(),
3305                Some(false),
3306                "C flag should be clear"
3307            );
3308            assert_eq!(
3309                state.flags.v.simplify().as_bool(),
3310                Some(true),
3311                "V flag should be set (overflow)"
3312            );
3313
3314            // Test 5: Zero comparison
3315            state.set_reg(&Reg::R0, BV::from_i64(0, 32));
3316            state.set_reg(&Reg::R1, BV::from_i64(0, 32));
3317            encoder.encode_op(&cmp_op, &mut state);
3318
3319            assert_eq!(
3320                state.flags.z.simplify().as_bool(),
3321                Some(true),
3322                "Z flag should be set (0 - 0 = 0)"
3323            );
3324            assert_eq!(
3325                state.flags.n.simplify().as_bool(),
3326                Some(false),
3327                "N flag should be clear"
3328            );
3329            assert_eq!(
3330                state.flags.c.simplify().as_bool(),
3331                Some(true),
3332                "C flag should be set"
3333            );
3334            assert_eq!(
3335                state.flags.v.simplify().as_bool(),
3336                Some(false),
3337                "V flag should be clear"
3338            );
3339        });
3340    }
3341
3342    #[test]
3343    fn test_arm_flags_all_combinations() {
3344        // Test that flags correctly distinguish all comparison outcomes
3345
3346        with_verification_context(|| {
3347            let encoder = ArmSemantics::new();
3348            let mut state = ArmState::new_symbolic();
3349
3350            let cmp_op = ArmOp::Cmp {
3351                rn: Reg::R0,
3352                op2: Operand2::Reg(Reg::R1),
3353            };
3354
3355            // Test signed comparisons using flags
3356            // For signed comparison A vs B (after CMP A, B):
3357            // - EQ (equal): Z=1
3358            // - NE (not equal): Z=0
3359            // - LT (less than): N != V
3360            // - LE (less or equal): Z=1 OR (N != V)
3361            // - GT (greater than): Z=0 AND (N == V)
3362            // - GE (greater or equal): N == V
3363
3364            // Case: 5 compared to 10 (5 < 10)
3365            state.set_reg(&Reg::R0, BV::from_i64(5, 32));
3366            state.set_reg(&Reg::R1, BV::from_i64(10, 32));
3367            encoder.encode_op(&cmp_op, &mut state);
3368
3369            let n = state.flags.n.simplify().as_bool().unwrap();
3370            let z = state.flags.z.simplify().as_bool().unwrap();
3371            let v = state.flags.v.simplify().as_bool().unwrap();
3372
3373            assert!(!z, "Not equal");
3374            assert!(n != v, "5 < 10 signed (N != V)");
3375
3376            // Case: -5 compared to 10 (-5 < 10)
3377            state.set_reg(&Reg::R0, BV::from_i64(-5, 32));
3378            state.set_reg(&Reg::R1, BV::from_i64(10, 32));
3379            encoder.encode_op(&cmp_op, &mut state);
3380
3381            let n = state.flags.n.simplify().as_bool().unwrap();
3382            let v = state.flags.v.simplify().as_bool().unwrap();
3383            assert!(n != v, "-5 < 10 signed (N != V)");
3384        });
3385    }
3386
3387    #[test]
3388    fn test_arm_setcond_eq() {
3389        with_verification_context(|| {
3390            let encoder = ArmSemantics::new();
3391            let mut state = ArmState::new_symbolic();
3392
3393            // Test EQ condition: 10 == 10
3394            state.set_reg(&Reg::R0, BV::from_i64(10, 32));
3395            state.set_reg(&Reg::R1, BV::from_i64(10, 32));
3396
3397            // CMP R0, R1 (sets Z=1 since equal)
3398            let cmp_op = ArmOp::Cmp {
3399                rn: Reg::R0,
3400                op2: Operand2::Reg(Reg::R1),
3401            };
3402            encoder.encode_op(&cmp_op, &mut state);
3403
3404            // SetCond R0, EQ (should set R0 = 1)
3405            let setcond_op = ArmOp::SetCond {
3406                rd: Reg::R0,
3407                cond: synth_synthesis::Condition::EQ,
3408            };
3409            encoder.encode_op(&setcond_op, &mut state);
3410
3411            assert_eq!(
3412                state.get_reg(&Reg::R0).simplify().as_i64(),
3413                Some(1),
3414                "EQ condition (10 == 10) should return 1"
3415            );
3416
3417            // Test NE condition: 10 != 5
3418            state.set_reg(&Reg::R0, BV::from_i64(10, 32));
3419            state.set_reg(&Reg::R1, BV::from_i64(5, 32));
3420
3421            encoder.encode_op(&cmp_op, &mut state);
3422
3423            let setcond_ne = ArmOp::SetCond {
3424                rd: Reg::R0,
3425                cond: synth_synthesis::Condition::NE,
3426            };
3427            encoder.encode_op(&setcond_ne, &mut state);
3428
3429            assert_eq!(
3430                state.get_reg(&Reg::R0).simplify().as_i64(),
3431                Some(1),
3432                "NE condition (10 != 5) should return 1"
3433            );
3434        });
3435    }
3436
3437    #[test]
3438    fn test_arm_setcond_signed() {
3439        with_verification_context(|| {
3440            let encoder = ArmSemantics::new();
3441            let mut state = ArmState::new_symbolic();
3442
3443            // Test LT signed: 5 < 10
3444            state.set_reg(&Reg::R0, BV::from_i64(5, 32));
3445            state.set_reg(&Reg::R1, BV::from_i64(10, 32));
3446
3447            let cmp_op = ArmOp::Cmp {
3448                rn: Reg::R0,
3449                op2: Operand2::Reg(Reg::R1),
3450            };
3451            encoder.encode_op(&cmp_op, &mut state);
3452
3453            let setcond_lt = ArmOp::SetCond {
3454                rd: Reg::R0,
3455                cond: synth_synthesis::Condition::LT,
3456            };
3457            encoder.encode_op(&setcond_lt, &mut state);
3458
3459            assert_eq!(
3460                state.get_reg(&Reg::R0).simplify().as_i64(),
3461                Some(1),
3462                "LT signed (5 < 10) should return 1"
3463            );
3464
3465            // Test GE signed: 10 >= 5
3466            state.set_reg(&Reg::R0, BV::from_i64(10, 32));
3467            state.set_reg(&Reg::R1, BV::from_i64(5, 32));
3468
3469            encoder.encode_op(&cmp_op, &mut state);
3470
3471            let setcond_ge = ArmOp::SetCond {
3472                rd: Reg::R0,
3473                cond: synth_synthesis::Condition::GE,
3474            };
3475            encoder.encode_op(&setcond_ge, &mut state);
3476
3477            assert_eq!(
3478                state.get_reg(&Reg::R0).simplify().as_i64(),
3479                Some(1),
3480                "GE signed (10 >= 5) should return 1"
3481            );
3482
3483            // Test GT signed: 10 > 5
3484            state.set_reg(&Reg::R0, BV::from_i64(10, 32));
3485            state.set_reg(&Reg::R1, BV::from_i64(5, 32));
3486
3487            encoder.encode_op(&cmp_op, &mut state);
3488
3489            let setcond_gt = ArmOp::SetCond {
3490                rd: Reg::R0,
3491                cond: synth_synthesis::Condition::GT,
3492            };
3493            encoder.encode_op(&setcond_gt, &mut state);
3494
3495            assert_eq!(
3496                state.get_reg(&Reg::R0).simplify().as_i64(),
3497                Some(1),
3498                "GT signed (10 > 5) should return 1"
3499            );
3500
3501            // Test LE signed: 5 <= 10
3502            state.set_reg(&Reg::R0, BV::from_i64(5, 32));
3503            state.set_reg(&Reg::R1, BV::from_i64(10, 32));
3504
3505            encoder.encode_op(&cmp_op, &mut state);
3506
3507            let setcond_le = ArmOp::SetCond {
3508                rd: Reg::R0,
3509                cond: synth_synthesis::Condition::LE,
3510            };
3511            encoder.encode_op(&setcond_le, &mut state);
3512
3513            assert_eq!(
3514                state.get_reg(&Reg::R0).simplify().as_i64(),
3515                Some(1),
3516                "LE signed (5 <= 10) should return 1"
3517            );
3518        });
3519    }
3520
3521    #[test]
3522    fn test_arm_setcond_unsigned() {
3523        with_verification_context(|| {
3524            let encoder = ArmSemantics::new();
3525            let mut state = ArmState::new_symbolic();
3526
3527            // Test LO unsigned: 5 < 10
3528            state.set_reg(&Reg::R0, BV::from_i64(5, 32));
3529            state.set_reg(&Reg::R1, BV::from_i64(10, 32));
3530
3531            let cmp_op = ArmOp::Cmp {
3532                rn: Reg::R0,
3533                op2: Operand2::Reg(Reg::R1),
3534            };
3535            encoder.encode_op(&cmp_op, &mut state);
3536
3537            let setcond_lo = ArmOp::SetCond {
3538                rd: Reg::R0,
3539                cond: synth_synthesis::Condition::LO,
3540            };
3541            encoder.encode_op(&setcond_lo, &mut state);
3542
3543            assert_eq!(
3544                state.get_reg(&Reg::R0).simplify().as_i64(),
3545                Some(1),
3546                "LO unsigned (5 < 10) should return 1"
3547            );
3548
3549            // Test HS unsigned: 10 >= 5
3550            state.set_reg(&Reg::R0, BV::from_i64(10, 32));
3551            state.set_reg(&Reg::R1, BV::from_i64(5, 32));
3552
3553            encoder.encode_op(&cmp_op, &mut state);
3554
3555            let setcond_hs = ArmOp::SetCond {
3556                rd: Reg::R0,
3557                cond: synth_synthesis::Condition::HS,
3558            };
3559            encoder.encode_op(&setcond_hs, &mut state);
3560
3561            assert_eq!(
3562                state.get_reg(&Reg::R0).simplify().as_i64(),
3563                Some(1),
3564                "HS unsigned (10 >= 5) should return 1"
3565            );
3566
3567            // Test HI unsigned: 10 > 5
3568            state.set_reg(&Reg::R0, BV::from_i64(10, 32));
3569            state.set_reg(&Reg::R1, BV::from_i64(5, 32));
3570
3571            encoder.encode_op(&cmp_op, &mut state);
3572
3573            let setcond_hi = ArmOp::SetCond {
3574                rd: Reg::R0,
3575                cond: synth_synthesis::Condition::HI,
3576            };
3577            encoder.encode_op(&setcond_hi, &mut state);
3578
3579            assert_eq!(
3580                state.get_reg(&Reg::R0).simplify().as_i64(),
3581                Some(1),
3582                "HI unsigned (10 > 5) should return 1"
3583            );
3584
3585            // Test LS unsigned: 5 <= 10
3586            state.set_reg(&Reg::R0, BV::from_i64(5, 32));
3587            state.set_reg(&Reg::R1, BV::from_i64(10, 32));
3588
3589            encoder.encode_op(&cmp_op, &mut state);
3590
3591            let setcond_ls = ArmOp::SetCond {
3592                rd: Reg::R0,
3593                cond: synth_synthesis::Condition::LS,
3594            };
3595            encoder.encode_op(&setcond_ls, &mut state);
3596
3597            assert_eq!(
3598                state.get_reg(&Reg::R0).simplify().as_i64(),
3599                Some(1),
3600                "LS unsigned (5 <= 10) should return 1"
3601            );
3602        });
3603    }
3604}