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 { rdlo, rdhi, .. } => {
693                // Signed 64-bit remainder (modulo)
694                // Real implementation would require __aeabi_ldivmod or equivalent
695                // For verification, return symbolic values
696                state.set_reg(rdlo, BV::new_const("i64_rems_lo", 32));
697                state.set_reg(rdhi, BV::new_const("i64_rems_hi", 32));
698            }
699
700            ArmOp::I64RemU { rdlo, rdhi, .. } => {
701                // Unsigned 64-bit remainder (modulo)
702                // Real implementation would require __aeabi_uldivmod or equivalent
703                // For verification, return symbolic values
704                state.set_reg(rdlo, BV::new_const("i64_remu_lo", 32));
705                state.set_reg(rdhi, BV::new_const("i64_remu_hi", 32));
706            }
707
708            ArmOp::I64And {
709                rdlo,
710                rdhi,
711                rnlo,
712                rnhi,
713                rmlo,
714                rmhi,
715            } => {
716                let n_low = state.get_reg(rnlo).clone();
717                let m_low = state.get_reg(rmlo).clone();
718                state.set_reg(rdlo, n_low.bvand(&m_low));
719
720                let n_high = state.get_reg(rnhi).clone();
721                let m_high = state.get_reg(rmhi).clone();
722                state.set_reg(rdhi, n_high.bvand(&m_high));
723            }
724
725            ArmOp::I64Or {
726                rdlo,
727                rdhi,
728                rnlo,
729                rnhi,
730                rmlo,
731                rmhi,
732            } => {
733                let n_low = state.get_reg(rnlo).clone();
734                let m_low = state.get_reg(rmlo).clone();
735                state.set_reg(rdlo, n_low.bvor(&m_low));
736
737                let n_high = state.get_reg(rnhi).clone();
738                let m_high = state.get_reg(rmhi).clone();
739                state.set_reg(rdhi, n_high.bvor(&m_high));
740            }
741
742            ArmOp::I64Xor {
743                rdlo,
744                rdhi,
745                rnlo,
746                rnhi,
747                rmlo,
748                rmhi,
749            } => {
750                let n_low = state.get_reg(rnlo).clone();
751                let m_low = state.get_reg(rmlo).clone();
752                state.set_reg(rdlo, n_low.bvxor(&m_low));
753
754                let n_high = state.get_reg(rnhi).clone();
755                let m_high = state.get_reg(rmhi).clone();
756                state.set_reg(rdhi, n_high.bvxor(&m_high));
757            }
758
759            ArmOp::I64Eq {
760                rd,
761                rnlo,
762                rnhi,
763                rmlo,
764                rmhi,
765            } => {
766                let n_low = state.get_reg(rnlo).clone();
767                let m_low = state.get_reg(rmlo).clone();
768                let n_high = state.get_reg(rnhi).clone();
769                let m_high = state.get_reg(rmhi).clone();
770
771                let low_eq = n_low.eq(&m_low);
772                let high_eq = n_high.eq(&m_high);
773                let both_eq = Bool::and(&[&low_eq, &high_eq]);
774                let result = self.bool_to_bv32(&both_eq);
775                state.set_reg(rd, result);
776            }
777
778            ArmOp::I64LtS {
779                rd,
780                rnlo,
781                rnhi,
782                rmlo,
783                rmhi,
784            } => {
785                // Signed less than: n < m
786                // Compare high parts first (signed), tiebreak with low parts (unsigned)
787                let n_low = state.get_reg(rnlo).clone();
788                let m_low = state.get_reg(rmlo).clone();
789                let n_high = state.get_reg(rnhi).clone();
790                let m_high = state.get_reg(rmhi).clone();
791
792                // High parts comparison (signed)
793                let high_lt = n_high.bvslt(&m_high);
794                let high_eq = n_high.eq(&m_high);
795
796                // Low parts comparison (unsigned)
797                let low_lt = n_low.bvult(&m_low);
798
799                // Result: high_lt OR (high_eq AND low_lt)
800                let eq_and_low = Bool::and(&[&high_eq, &low_lt]);
801                let result_bool = Bool::or(&[&high_lt, &eq_and_low]);
802                let result = self.bool_to_bv32(&result_bool);
803                state.set_reg(rd, result);
804            }
805
806            ArmOp::I64LtU {
807                rd,
808                rnlo,
809                rnhi,
810                rmlo,
811                rmhi,
812            } => {
813                // Unsigned less than: n < m
814                // Compare high parts first (unsigned), tiebreak with low parts (unsigned)
815                let n_low = state.get_reg(rnlo).clone();
816                let m_low = state.get_reg(rmlo).clone();
817                let n_high = state.get_reg(rnhi).clone();
818                let m_high = state.get_reg(rmhi).clone();
819
820                // High parts comparison (unsigned)
821                let high_lt = n_high.bvult(&m_high);
822                let high_eq = n_high.eq(&m_high);
823
824                // Low parts comparison (unsigned)
825                let low_lt = n_low.bvult(&m_low);
826
827                // Result: high_lt OR (high_eq AND low_lt)
828                let eq_and_low = Bool::and(&[&high_eq, &low_lt]);
829                let result_bool = Bool::or(&[&high_lt, &eq_and_low]);
830                let result = self.bool_to_bv32(&result_bool);
831                state.set_reg(rd, result);
832            }
833
834            ArmOp::I64Ne {
835                rd,
836                rnlo,
837                rnhi,
838                rmlo,
839                rmhi,
840            } => {
841                // Not equal: !(n == m)
842                let n_low = state.get_reg(rnlo).clone();
843                let m_low = state.get_reg(rmlo).clone();
844                let n_high = state.get_reg(rnhi).clone();
845                let m_high = state.get_reg(rmhi).clone();
846
847                let low_eq = n_low.eq(&m_low);
848                let high_eq = n_high.eq(&m_high);
849                let both_eq = Bool::and(&[&low_eq, &high_eq]);
850                let not_eq = both_eq.not();
851                let result = self.bool_to_bv32(&not_eq);
852                state.set_reg(rd, result);
853            }
854
855            ArmOp::I64LeS {
856                rd,
857                rnlo,
858                rnhi,
859                rmlo,
860                rmhi,
861            } => {
862                // Signed less than or equal: n <= m
863                // Equivalent to: n < m OR n == m
864                let n_low = state.get_reg(rnlo).clone();
865                let m_low = state.get_reg(rmlo).clone();
866                let n_high = state.get_reg(rnhi).clone();
867                let m_high = state.get_reg(rmhi).clone();
868
869                let high_lt = n_high.bvslt(&m_high);
870                let high_eq = n_high.eq(&m_high);
871                let low_le = n_low.bvule(&m_low); // Low parts unsigned LE
872
873                let eq_and_le = Bool::and(&[&high_eq, &low_le]);
874                let result_bool = Bool::or(&[&high_lt, &eq_and_le]);
875                let result = self.bool_to_bv32(&result_bool);
876                state.set_reg(rd, result);
877            }
878
879            ArmOp::I64LeU {
880                rd,
881                rnlo,
882                rnhi,
883                rmlo,
884                rmhi,
885            } => {
886                // Unsigned less than or equal: n <= m
887                let n_low = state.get_reg(rnlo).clone();
888                let m_low = state.get_reg(rmlo).clone();
889                let n_high = state.get_reg(rnhi).clone();
890                let m_high = state.get_reg(rmhi).clone();
891
892                let high_lt = n_high.bvult(&m_high);
893                let high_eq = n_high.eq(&m_high);
894                let low_le = n_low.bvule(&m_low);
895
896                let eq_and_le = Bool::and(&[&high_eq, &low_le]);
897                let result_bool = Bool::or(&[&high_lt, &eq_and_le]);
898                let result = self.bool_to_bv32(&result_bool);
899                state.set_reg(rd, result);
900            }
901
902            ArmOp::I64GtS {
903                rd,
904                rnlo,
905                rnhi,
906                rmlo,
907                rmhi,
908            } => {
909                // Signed greater than: n > m
910                // Equivalent to: m < n
911                let n_low = state.get_reg(rnlo).clone();
912                let m_low = state.get_reg(rmlo).clone();
913                let n_high = state.get_reg(rnhi).clone();
914                let m_high = state.get_reg(rmhi).clone();
915
916                let high_gt = n_high.bvsgt(&m_high);
917                let high_eq = n_high.eq(&m_high);
918                let low_gt = n_low.bvugt(&m_low); // Low parts unsigned GT
919
920                let eq_and_gt = Bool::and(&[&high_eq, &low_gt]);
921                let result_bool = Bool::or(&[&high_gt, &eq_and_gt]);
922                let result = self.bool_to_bv32(&result_bool);
923                state.set_reg(rd, result);
924            }
925
926            ArmOp::I64GtU {
927                rd,
928                rnlo,
929                rnhi,
930                rmlo,
931                rmhi,
932            } => {
933                // Unsigned greater than: n > m
934                let n_low = state.get_reg(rnlo).clone();
935                let m_low = state.get_reg(rmlo).clone();
936                let n_high = state.get_reg(rnhi).clone();
937                let m_high = state.get_reg(rmhi).clone();
938
939                let high_gt = n_high.bvugt(&m_high);
940                let high_eq = n_high.eq(&m_high);
941                let low_gt = n_low.bvugt(&m_low);
942
943                let eq_and_gt = Bool::and(&[&high_eq, &low_gt]);
944                let result_bool = Bool::or(&[&high_gt, &eq_and_gt]);
945                let result = self.bool_to_bv32(&result_bool);
946                state.set_reg(rd, result);
947            }
948
949            ArmOp::I64GeS {
950                rd,
951                rnlo,
952                rnhi,
953                rmlo,
954                rmhi,
955            } => {
956                // Signed greater than or equal: n >= m
957                // Equivalent to: !(n < m)
958                let n_low = state.get_reg(rnlo).clone();
959                let m_low = state.get_reg(rmlo).clone();
960                let n_high = state.get_reg(rnhi).clone();
961                let m_high = state.get_reg(rmhi).clone();
962
963                let high_lt = n_high.bvslt(&m_high);
964                let high_eq = n_high.eq(&m_high);
965                let low_lt = n_low.bvult(&m_low);
966
967                let eq_and_lt = Bool::and(&[&high_eq, &low_lt]);
968                let lt_bool = Bool::or(&[&high_lt, &eq_and_lt]);
969                let result_bool = lt_bool.not(); // GE is !(LT)
970                let result = self.bool_to_bv32(&result_bool);
971                state.set_reg(rd, result);
972            }
973
974            ArmOp::I64GeU {
975                rd,
976                rnlo,
977                rnhi,
978                rmlo,
979                rmhi,
980            } => {
981                // Unsigned greater than or equal: n >= m
982                // Equivalent to: !(n < m)
983                let n_low = state.get_reg(rnlo).clone();
984                let m_low = state.get_reg(rmlo).clone();
985                let n_high = state.get_reg(rnhi).clone();
986                let m_high = state.get_reg(rmhi).clone();
987
988                let high_lt = n_high.bvult(&m_high);
989                let high_eq = n_high.eq(&m_high);
990                let low_lt = n_low.bvult(&m_low);
991
992                let eq_and_lt = Bool::and(&[&high_eq, &low_lt]);
993                let lt_bool = Bool::or(&[&high_lt, &eq_and_lt]);
994                let result_bool = lt_bool.not(); // GE is !(LT)
995                let result = self.bool_to_bv32(&result_bool);
996                state.set_reg(rd, result);
997            }
998
999            // ================================================================
1000            // i64 Shift Operations
1001            // ================================================================
1002            ArmOp::I64Shl {
1003                rd_lo,
1004                rd_hi,
1005                rn_lo,
1006                rn_hi,
1007                rm_lo,
1008                rm_hi: _,
1009            } => {
1010                // 64-bit left shift: (n_hi:n_lo) << shift
1011                // WASM spec: shift amount is modulo 64
1012                let n_lo = state.get_reg(rn_lo).clone();
1013                let n_hi = state.get_reg(rn_hi).clone();
1014                let shift_amt = state.get_reg(rm_lo).clone();
1015
1016                // Modulo 64: shift_amt = shift_amt & 63
1017                let shift_mod = shift_amt.bvand(BV::from_i64(63, 32));
1018
1019                // If shift < 32: normal shift with bits moving from low to high
1020                // If shift >= 32: low becomes 0, high gets shifted low part
1021                let shift_32 = BV::from_i64(32, 32);
1022                let is_large = shift_mod.bvuge(&shift_32); // shift >= 32
1023
1024                // Small shift (< 32):
1025                // result_lo = n_lo << shift
1026                // result_hi = (n_hi << shift) | (n_lo >> (32 - shift))
1027                let result_lo_small = n_lo.bvshl(&shift_mod);
1028                let shift_complement = shift_32.bvsub(&shift_mod);
1029                let bits_to_high = n_lo.bvlshr(&shift_complement);
1030                let result_hi_small = n_hi.bvshl(&shift_mod).bvor(&bits_to_high);
1031
1032                // Large shift (>= 32):
1033                // result_lo = 0
1034                // result_hi = n_lo << (shift - 32)
1035                let zero = BV::from_i64(0, 32);
1036                let shift_minus_32 = shift_mod.bvsub(&shift_32);
1037                let result_lo_large = zero.clone();
1038                let result_hi_large = n_lo.bvshl(&shift_minus_32);
1039
1040                // Select based on shift size
1041                let result_lo = is_large.ite(&result_lo_large, &result_lo_small);
1042                let result_hi = is_large.ite(&result_hi_large, &result_hi_small);
1043
1044                state.set_reg(rd_lo, result_lo);
1045                state.set_reg(rd_hi, result_hi);
1046            }
1047
1048            ArmOp::I64ShrU {
1049                rd_lo,
1050                rd_hi,
1051                rn_lo,
1052                rn_hi,
1053                rm_lo,
1054                rm_hi: _,
1055            } => {
1056                // 64-bit logical (unsigned) right shift
1057                let n_lo = state.get_reg(rn_lo).clone();
1058                let n_hi = state.get_reg(rn_hi).clone();
1059                let shift_amt = state.get_reg(rm_lo).clone();
1060
1061                let shift_mod = shift_amt.bvand(BV::from_i64(63, 32));
1062                let shift_32 = BV::from_i64(32, 32);
1063                let is_large = shift_mod.bvuge(&shift_32);
1064
1065                // Small shift (< 32):
1066                // result_hi = n_hi >> shift
1067                // result_lo = (n_lo >> shift) | (n_hi << (32 - shift))
1068                let result_hi_small = n_hi.bvlshr(&shift_mod);
1069                let shift_complement = shift_32.bvsub(&shift_mod);
1070                let bits_to_low = n_hi.bvshl(&shift_complement);
1071                let result_lo_small = n_lo.bvlshr(&shift_mod).bvor(&bits_to_low);
1072
1073                // Large shift (>= 32):
1074                // result_hi = 0
1075                // result_lo = n_hi >> (shift - 32)
1076                let zero = BV::from_i64(0, 32);
1077                let shift_minus_32 = shift_mod.bvsub(&shift_32);
1078                let result_hi_large = zero.clone();
1079                let result_lo_large = n_hi.bvlshr(&shift_minus_32);
1080
1081                let result_lo = is_large.ite(&result_lo_large, &result_lo_small);
1082                let result_hi = is_large.ite(&result_hi_large, &result_hi_small);
1083
1084                state.set_reg(rd_lo, result_lo);
1085                state.set_reg(rd_hi, result_hi);
1086            }
1087
1088            ArmOp::I64ShrS {
1089                rd_lo,
1090                rd_hi,
1091                rn_lo,
1092                rn_hi,
1093                rm_lo,
1094                rm_hi: _,
1095            } => {
1096                // 64-bit arithmetic (signed) right shift
1097                let n_lo = state.get_reg(rn_lo).clone();
1098                let n_hi = state.get_reg(rn_hi).clone();
1099                let shift_amt = state.get_reg(rm_lo).clone();
1100
1101                let shift_mod = shift_amt.bvand(BV::from_i64(63, 32));
1102                let shift_32 = BV::from_i64(32, 32);
1103                let is_large = shift_mod.bvuge(&shift_32);
1104
1105                // Small shift (< 32):
1106                // result_hi = n_hi >> shift (arithmetic)
1107                // result_lo = (n_lo >> shift) | (n_hi << (32 - shift))
1108                let result_hi_small = n_hi.bvashr(&shift_mod);
1109                let shift_complement = shift_32.bvsub(&shift_mod);
1110                let bits_to_low = n_hi.bvshl(&shift_complement);
1111                let result_lo_small = n_lo.bvlshr(&shift_mod).bvor(&bits_to_low);
1112
1113                // Large shift (>= 32):
1114                // result_hi = n_hi >> 31 (sign extension: all 0s or all 1s)
1115                // result_lo = n_hi >> (shift - 32) (arithmetic)
1116                let shift_31 = BV::from_i64(31, 32);
1117                let result_hi_large = n_hi.bvashr(&shift_31);
1118                let shift_minus_32 = shift_mod.bvsub(&shift_32);
1119                let result_lo_large = n_hi.bvashr(&shift_minus_32);
1120
1121                let result_lo = is_large.ite(&result_lo_large, &result_lo_small);
1122                let result_hi = is_large.ite(&result_hi_large, &result_hi_small);
1123
1124                state.set_reg(rd_lo, result_lo);
1125                state.set_reg(rd_hi, result_hi);
1126            }
1127
1128            // ========================================================================
1129            // i64 Rotation Operations
1130            // ========================================================================
1131            ArmOp::I64Rotl {
1132                rdlo,
1133                rdhi,
1134                rnlo,
1135                rnhi,
1136                shift,
1137            } => {
1138                // 64-bit rotate left: rotl(hi:lo, shift)
1139                // Result = (value << shift) | (value >> (64 - shift))
1140                let n_lo = state.get_reg(rnlo).clone();
1141                let n_hi = state.get_reg(rnhi).clone();
1142                let shift_amt = state.get_reg(shift).clone();
1143
1144                // Normalize shift to 0-63 range
1145                let shift_mod = shift_amt.bvand(BV::from_i64(63, 32));
1146                let shift_32 = BV::from_i64(32, 32);
1147                let is_large = shift_mod.bvuge(&shift_32); // shift >= 32
1148
1149                // For shift < 32:
1150                // result_lo = (n_lo << shift) | (n_hi >> (32 - shift))
1151                // result_hi = (n_hi << shift) | (n_lo >> (32 - shift))
1152                let shift_complement = shift_32.bvsub(&shift_mod);
1153
1154                let lo_shifted_left = n_lo.bvshl(&shift_mod);
1155                let hi_bits_to_lo = n_hi.bvlshr(&shift_complement);
1156                let result_lo_small = lo_shifted_left.bvor(&hi_bits_to_lo);
1157
1158                let hi_shifted_left = n_hi.bvshl(&shift_mod);
1159                let lo_bits_to_hi = n_lo.bvlshr(&shift_complement);
1160                let result_hi_small = hi_shifted_left.bvor(&lo_bits_to_hi);
1161
1162                // For shift >= 32:
1163                // Swap and rotate by (shift - 32)
1164                let shift_minus_32 = shift_mod.bvsub(&shift_32);
1165                let complement_large = shift_32.bvsub(&shift_minus_32);
1166
1167                let hi_shifted_left_large = n_hi.bvshl(&shift_minus_32);
1168                let lo_bits_to_hi_large = n_lo.bvlshr(&complement_large);
1169                let result_lo_large = hi_shifted_left_large.bvor(&lo_bits_to_hi_large);
1170
1171                let lo_shifted_left_large = n_lo.bvshl(&shift_minus_32);
1172                let hi_bits_to_lo_large = n_hi.bvlshr(&complement_large);
1173                let result_hi_large = lo_shifted_left_large.bvor(&hi_bits_to_lo_large);
1174
1175                // Select based on shift size
1176                let result_lo = is_large.ite(&result_lo_large, &result_lo_small);
1177                let result_hi = is_large.ite(&result_hi_large, &result_hi_small);
1178
1179                state.set_reg(rdlo, result_lo);
1180                state.set_reg(rdhi, result_hi);
1181            }
1182
1183            ArmOp::I64Rotr {
1184                rdlo,
1185                rdhi,
1186                rnlo,
1187                rnhi,
1188                shift,
1189            } => {
1190                // 64-bit rotate right: rotr(hi:lo, shift)
1191                // Result = (value >> shift) | (value << (64 - shift))
1192                let n_lo = state.get_reg(rnlo).clone();
1193                let n_hi = state.get_reg(rnhi).clone();
1194                let shift_amt = state.get_reg(shift).clone();
1195
1196                // Normalize shift to 0-63 range
1197                let shift_mod = shift_amt.bvand(BV::from_i64(63, 32));
1198                let shift_32 = BV::from_i64(32, 32);
1199                let is_large = shift_mod.bvuge(&shift_32); // shift >= 32
1200
1201                // For shift < 32:
1202                // result_lo = (n_lo >> shift) | (n_hi << (32 - shift))
1203                // result_hi = (n_hi >> shift) | (n_lo << (32 - shift))
1204                let shift_complement = shift_32.bvsub(&shift_mod);
1205
1206                let lo_shifted_right = n_lo.bvlshr(&shift_mod);
1207                let hi_bits_to_lo = n_hi.bvshl(&shift_complement);
1208                let result_lo_small = lo_shifted_right.bvor(&hi_bits_to_lo);
1209
1210                let hi_shifted_right = n_hi.bvlshr(&shift_mod);
1211                let lo_bits_to_hi = n_lo.bvshl(&shift_complement);
1212                let result_hi_small = hi_shifted_right.bvor(&lo_bits_to_hi);
1213
1214                // For shift >= 32:
1215                // Swap and rotate by (shift - 32)
1216                let shift_minus_32 = shift_mod.bvsub(&shift_32);
1217                let complement_large = shift_32.bvsub(&shift_minus_32);
1218
1219                let hi_shifted_right_large = n_hi.bvlshr(&shift_minus_32);
1220                let lo_bits_to_hi_large = n_lo.bvshl(&complement_large);
1221                let result_lo_large = hi_shifted_right_large.bvor(&lo_bits_to_hi_large);
1222
1223                let lo_shifted_right_large = n_lo.bvlshr(&shift_minus_32);
1224                let hi_bits_to_lo_large = n_hi.bvshl(&complement_large);
1225                let result_hi_large = lo_shifted_right_large.bvor(&hi_bits_to_lo_large);
1226
1227                // Select based on shift size
1228                let result_lo = is_large.ite(&result_lo_large, &result_lo_small);
1229                let result_hi = is_large.ite(&result_hi_large, &result_hi_small);
1230
1231                state.set_reg(rdlo, result_lo);
1232                state.set_reg(rdhi, result_hi);
1233            }
1234
1235            ArmOp::I64Clz { rd, rnlo, rnhi } => {
1236                // Count leading zeros for 64-bit value
1237                // If high part has zeros, result = clz(high) + clz(low)
1238                // If high part is zero, result = 32 + clz(low)
1239                let n_lo = state.get_reg(rnlo).clone();
1240                let n_hi = state.get_reg(rnhi).clone();
1241
1242                let hi_clz = self.encode_clz(&n_hi);
1243                let lo_clz = self.encode_clz(&n_lo);
1244
1245                // If high == 32 (all zeros), add low clz; else use high clz
1246                let thirty_two = BV::from_i64(32, 32);
1247                let hi_is_zero = hi_clz.eq(&thirty_two);
1248                let result = hi_is_zero.ite(
1249                    thirty_two.bvadd(&lo_clz), // High is zero: 32 + clz(low)
1250                    &hi_clz,                   // High has bits: clz(high)
1251                );
1252                state.set_reg(rd, result);
1253            }
1254
1255            ArmOp::I64Ctz { rd, rnlo, rnhi } => {
1256                // Count trailing zeros for 64-bit value
1257                // If low part is zero, result = 32 + ctz(high)
1258                // Else result = ctz(low)
1259                let n_lo = state.get_reg(rnlo).clone();
1260                let n_hi = state.get_reg(rnhi).clone();
1261
1262                let lo_ctz = self.encode_ctz(&n_lo);
1263                let hi_ctz = self.encode_ctz(&n_hi);
1264
1265                // If low == 32 (all zeros), add high ctz; else use low ctz
1266                let thirty_two = BV::from_i64(32, 32);
1267                let lo_is_zero = lo_ctz.eq(&thirty_two);
1268                let result = lo_is_zero.ite(
1269                    thirty_two.bvadd(&hi_ctz), // Low is zero: 32 + ctz(high)
1270                    &lo_ctz,                   // Low has bits: ctz(low)
1271                );
1272                state.set_reg(rd, result);
1273            }
1274
1275            ArmOp::I64Popcnt { rd, rnlo, rnhi } => {
1276                // Population count for 64-bit value
1277                // Result = popcnt(low) + popcnt(high)
1278                let n_lo = state.get_reg(rnlo).clone();
1279                let n_hi = state.get_reg(rnhi).clone();
1280
1281                let lo_popcnt = self.encode_popcnt(&n_lo);
1282                let hi_popcnt = self.encode_popcnt(&n_hi);
1283
1284                let result = lo_popcnt.bvadd(&hi_popcnt);
1285                state.set_reg(rd, result);
1286            }
1287
1288            // ========================================================================
1289            // i64 Memory Operations
1290            // ========================================================================
1291            ArmOp::I64Ldr { rdlo, rdhi, addr } => {
1292                // Load 64-bit value from memory
1293                // Simplified: return symbolic values for both registers
1294                // Real implementation would load from memory at [addr] and [addr+4]
1295                let result_lo = BV::new_const(format!("i64load_lo_{:?}", addr), 32);
1296                let result_hi = BV::new_const(format!("i64load_hi_{:?}", addr), 32);
1297                state.set_reg(rdlo, result_lo);
1298                state.set_reg(rdhi, result_hi);
1299            }
1300
1301            ArmOp::I64Str {
1302                rdlo: _,
1303                rdhi: _,
1304                addr: _,
1305            } => {
1306                // Store 64-bit value to memory
1307                // Simplified: memory updates not fully modeled yet
1308                // Real implementation would store rdlo to [addr] and rdhi to [addr+4]
1309                // No register changes - store operation has no output
1310            }
1311
1312            // ========================================================================
1313            // f32 Operations (Phase 2 - Floating Point)
1314            // ========================================================================
1315            // Note: f32 values are represented as 32-bit bitvectors (IEEE 754 format)
1316            // For verification, we use symbolic bitvector operations
1317            // A complete implementation would use Z3's FloatingPoint sort
1318
1319            // f32 Constants
1320            ArmOp::F32Const { sd, value } => {
1321                // Load f32 constant (represented as 32-bit bitvector)
1322                // Convert f32 to its IEEE 754 bit representation
1323                let bits = value.to_bits() as i64;
1324                let bv_val = BV::from_i64(bits, 32);
1325                state.set_vfp_reg(sd, bv_val);
1326            }
1327
1328            // f32 Arithmetic (symbolic for verification)
1329            ArmOp::F32Add { sd, sn, sm } => {
1330                // f32 addition: sd = sn + sm
1331                // For verification, return symbolic value
1332                // Full implementation would use Z3 FloatingPoint operations
1333                let result = BV::new_const(format!("f32_add_{:?}_{:?}", sn, sm), 32);
1334                state.set_vfp_reg(sd, result);
1335            }
1336
1337            ArmOp::F32Sub { sd, sn, sm } => {
1338                // f32 subtraction: sd = sn - sm
1339                let result = BV::new_const(format!("f32_sub_{:?}_{:?}", sn, sm), 32);
1340                state.set_vfp_reg(sd, result);
1341            }
1342
1343            ArmOp::F32Mul { sd, sn, sm } => {
1344                // f32 multiplication: sd = sn * sm
1345                let result = BV::new_const(format!("f32_mul_{:?}_{:?}", sn, sm), 32);
1346                state.set_vfp_reg(sd, result);
1347            }
1348
1349            ArmOp::F32Div { sd, sn, sm } => {
1350                // f32 division: sd = sn / sm
1351                let result = BV::new_const(format!("f32_div_{:?}_{:?}", sn, sm), 32);
1352                state.set_vfp_reg(sd, result);
1353            }
1354
1355            // f32 Simple Math
1356            ArmOp::F32Abs { sd, sm } => {
1357                // f32 absolute value: sd = |sm|
1358                // Clear the sign bit (bit 31)
1359                let val = state.get_vfp_reg(sm).clone();
1360                let mask = BV::from_u64(0x7FFFFFFF, 32); // Clear sign bit
1361                let result = val.bvand(&mask);
1362                state.set_vfp_reg(sd, result);
1363            }
1364
1365            ArmOp::F32Neg { sd, sm } => {
1366                // f32 negation: sd = -sm
1367                // Flip the sign bit (bit 31)
1368                let val = state.get_vfp_reg(sm).clone();
1369                let mask = BV::from_u64(0x80000000, 32); // Sign bit
1370                let result = val.bvxor(&mask);
1371                state.set_vfp_reg(sd, result);
1372            }
1373
1374            ArmOp::F32Sqrt { sd, sm } => {
1375                // f32 square root: sd = sqrt(sm)
1376                // Symbolic representation for verification
1377                let result = BV::new_const(format!("f32_sqrt_{:?}", sm), 32);
1378                state.set_vfp_reg(sd, result);
1379            }
1380
1381            ArmOp::F32Min { sd, sn, sm } => {
1382                // f32 minimum: sd = min(sn, sm)
1383                // IEEE 754 semantics: NaN propagation, -0.0 < +0.0
1384                // Symbolic representation for verification
1385                let result = BV::new_const(format!("f32_min_{:?}_{:?}", sn, sm), 32);
1386                state.set_vfp_reg(sd, result);
1387            }
1388
1389            ArmOp::F32Max { sd, sn, sm } => {
1390                // f32 maximum: sd = max(sn, sm)
1391                // IEEE 754 semantics: NaN propagation, +0.0 > -0.0
1392                // Symbolic representation for verification
1393                let result = BV::new_const(format!("f32_max_{:?}_{:?}", sn, sm), 32);
1394                state.set_vfp_reg(sd, result);
1395            }
1396
1397            ArmOp::F32Copysign { sd, sn, sm } => {
1398                // f32 copysign: sd = |sn| with sign of sm
1399                // Take magnitude of sn and sign bit from sm
1400                let val_n = state.get_vfp_reg(sn).clone();
1401                let val_m = state.get_vfp_reg(sm).clone();
1402
1403                // Extract magnitude from sn (clear sign bit)
1404                let mag_mask = BV::from_u64(0x7FFFFFFF, 32);
1405                let magnitude = val_n.bvand(&mag_mask);
1406
1407                // Extract sign from sm (bit 31 only)
1408                let sign_mask = BV::from_u64(0x80000000, 32);
1409                let sign = val_m.bvand(&sign_mask);
1410
1411                // Combine: magnitude | sign
1412                let result = magnitude.bvor(&sign);
1413                state.set_vfp_reg(sd, result);
1414            }
1415
1416            ArmOp::F32Load { sd, addr } => {
1417                // f32 load: sd = memory[addr]
1418                // Symbolic memory access for verification
1419                let result = BV::new_const(format!("f32_load_{:?}", addr), 32);
1420                state.set_vfp_reg(sd, result);
1421            }
1422
1423            // f32 Comparisons (result stored in integer register)
1424            ArmOp::F32Eq { rd, sn, sm } => {
1425                // f32 equal: rd = (sn == sm) ? 1 : 0
1426                // IEEE 754: NaN != NaN, so symbolic comparison needed
1427                let result = BV::new_const(format!("f32_eq_{:?}_{:?}", sn, sm), 32);
1428                state.set_reg(rd, result);
1429            }
1430
1431            ArmOp::F32Ne { rd, sn, sm } => {
1432                // f32 not equal: rd = (sn != sm) ? 1 : 0
1433                let result = BV::new_const(format!("f32_ne_{:?}_{:?}", sn, sm), 32);
1434                state.set_reg(rd, result);
1435            }
1436
1437            ArmOp::F32Lt { rd, sn, sm } => {
1438                // f32 less than: rd = (sn < sm) ? 1 : 0
1439                let result = BV::new_const(format!("f32_lt_{:?}_{:?}", sn, sm), 32);
1440                state.set_reg(rd, result);
1441            }
1442
1443            ArmOp::F32Le { rd, sn, sm } => {
1444                // f32 less than or equal: rd = (sn <= sm) ? 1 : 0
1445                let result = BV::new_const(format!("f32_le_{:?}_{:?}", sn, sm), 32);
1446                state.set_reg(rd, result);
1447            }
1448
1449            ArmOp::F32Gt { rd, sn, sm } => {
1450                // f32 greater than: rd = (sn > sm) ? 1 : 0
1451                let result = BV::new_const(format!("f32_gt_{:?}_{:?}", sn, sm), 32);
1452                state.set_reg(rd, result);
1453            }
1454
1455            ArmOp::F32Ge { rd, sn, sm } => {
1456                // f32 greater than or equal: rd = (sn >= sm) ? 1 : 0
1457                let result = BV::new_const(format!("f32_ge_{:?}_{:?}", sn, sm), 32);
1458                state.set_reg(rd, result);
1459            }
1460
1461            ArmOp::F32Store { sd, addr } => {
1462                // f32 store: memory[addr] = sd
1463                // Memory write - modeled symbolically for verification
1464                // In a full implementation, would update memory state
1465                // For now, this is a no-op as we model memory symbolically
1466                let _val = state.get_vfp_reg(sd);
1467                let _addr_str = format!("{:?}", addr);
1468                // TODO: Add memory state tracking when implementing full memory model
1469            }
1470
1471            // f32 Advanced Math Operations
1472            ArmOp::F32Ceil { sd, sm } => {
1473                // f32 ceil: sd = ceil(sm) - round toward +infinity
1474                // Symbolic representation for IEEE 754 rounding
1475                let result = BV::new_const(format!("f32_ceil_{:?}", sm), 32);
1476                state.set_vfp_reg(sd, result);
1477            }
1478
1479            ArmOp::F32Floor { sd, sm } => {
1480                // f32 floor: sd = floor(sm) - round toward -infinity
1481                // Symbolic representation for IEEE 754 rounding
1482                let result = BV::new_const(format!("f32_floor_{:?}", sm), 32);
1483                state.set_vfp_reg(sd, result);
1484            }
1485
1486            ArmOp::F32Trunc { sd, sm } => {
1487                // f32 trunc: sd = trunc(sm) - round toward zero
1488                // Symbolic representation for IEEE 754 rounding
1489                let result = BV::new_const(format!("f32_trunc_{:?}", sm), 32);
1490                state.set_vfp_reg(sd, result);
1491            }
1492
1493            ArmOp::F32Nearest { sd, sm } => {
1494                // f32 nearest: sd = nearest(sm) - round to nearest, ties to even
1495                // Symbolic representation for IEEE 754 rounding
1496                let result = BV::new_const(format!("f32_nearest_{:?}", sm), 32);
1497                state.set_vfp_reg(sd, result);
1498            }
1499
1500            // f32 Conversions from Integers
1501            ArmOp::F32ConvertI32S { sd, rm } => {
1502                // f32 convert from signed i32: sd = (f32)rm
1503                let int_val = state.get_reg(rm);
1504                let result = BV::new_const(format!("f32_convert_i32s_{:?}", int_val), 32);
1505                state.set_vfp_reg(sd, result);
1506            }
1507
1508            ArmOp::F32ConvertI32U { sd, rm } => {
1509                // f32 convert from unsigned i32: sd = (f32)(unsigned)rm
1510                let int_val = state.get_reg(rm);
1511                let result = BV::new_const(format!("f32_convert_i32u_{:?}", int_val), 32);
1512                state.set_vfp_reg(sd, result);
1513            }
1514
1515            ArmOp::F32ConvertI64S { sd, rmlo, rmhi } => {
1516                // f32 convert from signed i64: sd = (f32)r64
1517                let lo = state.get_reg(rmlo);
1518                let hi = state.get_reg(rmhi);
1519                let result = BV::new_const(format!("f32_convert_i64s_{:?}_{:?}", lo, hi), 32);
1520                state.set_vfp_reg(sd, result);
1521            }
1522
1523            ArmOp::F32ConvertI64U { sd, rmlo, rmhi } => {
1524                // f32 convert from unsigned i64: sd = (f32)(unsigned)r64
1525                let lo = state.get_reg(rmlo);
1526                let hi = state.get_reg(rmhi);
1527                let result = BV::new_const(format!("f32_convert_i64u_{:?}_{:?}", lo, hi), 32);
1528                state.set_vfp_reg(sd, result);
1529            }
1530
1531            // f32 Reinterpretations
1532            ArmOp::F32ReinterpretI32 { sd, rm } => {
1533                // f32 reinterpret i32: sd = reinterpret_cast<f32>(rm)
1534                // Bitwise copy without conversion
1535                let bits = state.get_reg(rm).clone();
1536                state.set_vfp_reg(sd, bits);
1537            }
1538
1539            ArmOp::I32ReinterpretF32 { rd, sm } => {
1540                // i32 reinterpret f32: rd = reinterpret_cast<i32>(sm)
1541                // Bitwise copy without conversion
1542                let bits = state.get_vfp_reg(sm).clone();
1543                state.set_reg(rd, bits);
1544            }
1545
1546            // ===================================================================
1547            // f64 Operations (Phase 2c - Double-Precision Floating Point)
1548            // ===================================================================
1549
1550            // f64 Arithmetic (symbolic for verification)
1551            ArmOp::F64Add { dd, dn, dm } => {
1552                // f64 addition: dd = dn + dm
1553                // For verification, return symbolic value
1554                // Full implementation would use Z3 FloatingPoint operations
1555                let result = BV::new_const(format!("f64_add_{:?}_{:?}", dn, dm), 64);
1556                state.set_vfp_reg(dd, result);
1557            }
1558
1559            ArmOp::F64Sub { dd, dn, dm } => {
1560                // f64 subtraction: dd = dn - dm
1561                let result = BV::new_const(format!("f64_sub_{:?}_{:?}", dn, dm), 64);
1562                state.set_vfp_reg(dd, result);
1563            }
1564
1565            ArmOp::F64Mul { dd, dn, dm } => {
1566                // f64 multiplication: dd = dn * dm
1567                let result = BV::new_const(format!("f64_mul_{:?}_{:?}", dn, dm), 64);
1568                state.set_vfp_reg(dd, result);
1569            }
1570
1571            ArmOp::F64Div { dd, dn, dm } => {
1572                // f64 division: dd = dn / dm
1573                let result = BV::new_const(format!("f64_div_{:?}_{:?}", dn, dm), 64);
1574                state.set_vfp_reg(dd, result);
1575            }
1576
1577            // f64 Simple Math
1578            ArmOp::F64Abs { dd, dm } => {
1579                // f64 absolute value: dd = |dm|
1580                // Clear the sign bit (bit 63)
1581                let val = state.get_vfp_reg(dm).clone();
1582                let mask = BV::from_u64(0x7FFFFFFFFFFFFFFF, 64); // Clear sign bit
1583                let result = val.bvand(&mask);
1584                state.set_vfp_reg(dd, result);
1585            }
1586
1587            ArmOp::F64Neg { dd, dm } => {
1588                // f64 negation: dd = -dm
1589                // Flip the sign bit (bit 63)
1590                let val = state.get_vfp_reg(dm).clone();
1591                let mask = BV::from_u64(0x8000000000000000, 64); // Sign bit
1592                let result = val.bvxor(&mask);
1593                state.set_vfp_reg(dd, result);
1594            }
1595
1596            ArmOp::F64Sqrt { dd, dm } => {
1597                // f64 square root: dd = sqrt(dm)
1598                // Symbolic representation for verification
1599                let result = BV::new_const(format!("f64_sqrt_{:?}", dm), 64);
1600                state.set_vfp_reg(dd, result);
1601            }
1602
1603            ArmOp::F64Min { dd, dn, dm } => {
1604                // f64 minimum: dd = min(dn, dm)
1605                // IEEE 754 semantics: NaN propagation, -0.0 < +0.0
1606                // Symbolic representation for verification
1607                let result = BV::new_const(format!("f64_min_{:?}_{:?}", dn, dm), 64);
1608                state.set_vfp_reg(dd, result);
1609            }
1610
1611            ArmOp::F64Max { dd, dn, dm } => {
1612                // f64 maximum: dd = max(dn, dm)
1613                // IEEE 754 semantics: NaN propagation, +0.0 > -0.0
1614                // Symbolic representation for verification
1615                let result = BV::new_const(format!("f64_max_{:?}_{:?}", dn, dm), 64);
1616                state.set_vfp_reg(dd, result);
1617            }
1618
1619            ArmOp::F64Copysign { dd, dn, dm } => {
1620                // f64 copysign: dd = |dn| with sign of dm
1621                // Take magnitude of dn and sign bit from dm
1622                let val_n = state.get_vfp_reg(dn).clone();
1623                let val_m = state.get_vfp_reg(dm).clone();
1624
1625                // Extract magnitude from dn (clear sign bit)
1626                let mag_mask = BV::from_u64(0x7FFFFFFFFFFFFFFF, 64);
1627                let magnitude = val_n.bvand(&mag_mask);
1628
1629                // Extract sign from dm (bit 63 only)
1630                let sign_mask = BV::from_u64(0x8000000000000000, 64);
1631                let sign = val_m.bvand(&sign_mask);
1632
1633                // Combine: magnitude | sign
1634                let result = magnitude.bvor(&sign);
1635                state.set_vfp_reg(dd, result);
1636            }
1637
1638            // f64 Rounding Operations (symbolic for verification)
1639            ArmOp::F64Ceil { dd, dm } => {
1640                // f64 ceil: dd = ceil(dm) - round toward +infinity
1641                let result = BV::new_const(format!("f64_ceil_{:?}", dm), 64);
1642                state.set_vfp_reg(dd, result);
1643            }
1644
1645            ArmOp::F64Floor { dd, dm } => {
1646                // f64 floor: dd = floor(dm) - round toward -infinity
1647                let result = BV::new_const(format!("f64_floor_{:?}", dm), 64);
1648                state.set_vfp_reg(dd, result);
1649            }
1650
1651            ArmOp::F64Trunc { dd, dm } => {
1652                // f64 trunc: dd = trunc(dm) - round toward zero
1653                let result = BV::new_const(format!("f64_trunc_{:?}", dm), 64);
1654                state.set_vfp_reg(dd, result);
1655            }
1656
1657            ArmOp::F64Nearest { dd, dm } => {
1658                // f64 nearest: dd = round(dm) - round to nearest, ties to even
1659                let result = BV::new_const(format!("f64_nearest_{:?}", dm), 64);
1660                state.set_vfp_reg(dd, result);
1661            }
1662
1663            // f64 Memory Operations
1664            ArmOp::F64Load { dd, addr } => {
1665                // f64 load: dd = memory[addr]
1666                // Symbolic memory access for verification
1667                let result = BV::new_const(format!("f64_load_{:?}", addr), 64);
1668                state.set_vfp_reg(dd, result);
1669            }
1670
1671            ArmOp::F64Store { dd: _, addr: _ } => {
1672                // f64 store: memory[addr] = dd
1673                // Store operations don't produce register values
1674                // No state change for symbolic execution
1675            }
1676
1677            ArmOp::F64Const { dd, value } => {
1678                // f64 constant: dd = value
1679                let bits = value.to_bits() as i64;
1680                let result = BV::from_i64(bits, 64);
1681                state.set_vfp_reg(dd, result);
1682            }
1683
1684            // f64 Comparisons (result stored in integer register)
1685            ArmOp::F64Eq { rd, dn, dm } => {
1686                // f64 equal: rd = (dn == dm) ? 1 : 0
1687                // IEEE 754: NaN != NaN, so symbolic comparison needed
1688                let result = BV::new_const(format!("f64_eq_{:?}_{:?}", dn, dm), 32);
1689                state.set_reg(rd, result);
1690            }
1691
1692            ArmOp::F64Ne { rd, dn, dm } => {
1693                // f64 not equal: rd = (dn != dm) ? 1 : 0
1694                let result = BV::new_const(format!("f64_ne_{:?}_{:?}", dn, dm), 32);
1695                state.set_reg(rd, result);
1696            }
1697
1698            ArmOp::F64Lt { rd, dn, dm } => {
1699                // f64 less than: rd = (dn < dm) ? 1 : 0
1700                let result = BV::new_const(format!("f64_lt_{:?}_{:?}", dn, dm), 32);
1701                state.set_reg(rd, result);
1702            }
1703
1704            ArmOp::F64Le { rd, dn, dm } => {
1705                // f64 less than or equal: rd = (dn <= dm) ? 1 : 0
1706                let result = BV::new_const(format!("f64_le_{:?}_{:?}", dn, dm), 32);
1707                state.set_reg(rd, result);
1708            }
1709
1710            ArmOp::F64Gt { rd, dn, dm } => {
1711                // f64 greater than: rd = (dn > dm) ? 1 : 0
1712                let result = BV::new_const(format!("f64_gt_{:?}_{:?}", dn, dm), 32);
1713                state.set_reg(rd, result);
1714            }
1715
1716            ArmOp::F64Ge { rd, dn, dm } => {
1717                // f64 greater than or equal: rd = (dn >= dm) ? 1 : 0
1718                let result = BV::new_const(format!("f64_ge_{:?}_{:?}", dn, dm), 32);
1719                state.set_reg(rd, result);
1720            }
1721
1722            // f64 Conversions
1723            ArmOp::F64ConvertI32S { dd, rm } => {
1724                // f64 convert i32 signed: dd = (f64)rm
1725                // Symbolic conversion
1726                let result = BV::new_const(format!("f64_convert_i32s_{:?}", rm), 64);
1727                state.set_vfp_reg(dd, result);
1728            }
1729
1730            ArmOp::F64ConvertI32U { dd, rm } => {
1731                // f64 convert i32 unsigned: dd = (f64)(unsigned)rm
1732                // Symbolic conversion
1733                let result = BV::new_const(format!("f64_convert_i32u_{:?}", rm), 64);
1734                state.set_vfp_reg(dd, result);
1735            }
1736
1737            ArmOp::F64ConvertI64S {
1738                dd,
1739                rmlo: _,
1740                rmhi: _,
1741            } => {
1742                // f64 convert i64 signed: dd = (f64)(rmhi:rmlo)
1743                // Symbolic conversion (complex operation)
1744                let result = BV::new_const("f64_convert_i64s_result", 64);
1745                state.set_vfp_reg(dd, result);
1746            }
1747
1748            ArmOp::F64ConvertI64U {
1749                dd,
1750                rmlo: _,
1751                rmhi: _,
1752            } => {
1753                // f64 convert i64 unsigned: dd = (f64)(unsigned)(rmhi:rmlo)
1754                // Symbolic conversion (complex operation)
1755                let result = BV::new_const("f64_convert_i64u_result", 64);
1756                state.set_vfp_reg(dd, result);
1757            }
1758
1759            ArmOp::F64PromoteF32 { dd, sm } => {
1760                // f64 promote f32: dd = (f64)sm
1761                // Promote from 32-bit to 64-bit (symbolic for verification)
1762                let result = BV::new_const(format!("f64_promote_f32_{:?}", sm), 64);
1763                state.set_vfp_reg(dd, result);
1764            }
1765
1766            ArmOp::F64ReinterpretI64 { dd, rmlo, rmhi } => {
1767                // f64 reinterpret i64: dd = reinterpret_cast<f64>(rmhi:rmlo)
1768                // Bitwise copy without conversion - combine two 32-bit registers
1769                let lo = state.get_reg(rmlo).clone();
1770                let hi = state.get_reg(rmhi).clone();
1771
1772                // Extend to 64 bits and combine: (hi << 32) | lo
1773                let lo_64 = lo.zero_ext(32); // Extend to 64 bits
1774                let hi_64 = hi.zero_ext(32);
1775                let shift_32 = BV::from_u64(32, 64);
1776                let hi_shifted = hi_64.bvshl(&shift_32);
1777                let result = hi_shifted.bvor(&lo_64);
1778
1779                state.set_vfp_reg(dd, result);
1780            }
1781
1782            ArmOp::I64ReinterpretF64 { rdlo, rdhi, dm } => {
1783                // i64 reinterpret f64: (rdhi:rdlo) = reinterpret_cast<i64>(dm)
1784                // Bitwise copy without conversion - split 64-bit into two 32-bit registers
1785                let bits = state.get_vfp_reg(dm).clone();
1786
1787                // Extract low 32 bits
1788                let lo = bits.extract(31, 0);
1789                state.set_reg(rdlo, lo);
1790
1791                // Extract high 32 bits
1792                let hi = bits.extract(63, 32);
1793                state.set_reg(rdhi, hi);
1794            }
1795
1796            ArmOp::I64TruncF64S {
1797                rdlo: _,
1798                rdhi: _,
1799                dm: _,
1800            } => {
1801                // i64 trunc f64 signed: (rdhi:rdlo) = (i64)dm
1802                // Symbolic conversion (complex operation)
1803                // Would require proper truncation with saturation
1804            }
1805
1806            ArmOp::I64TruncF64U {
1807                rdlo: _,
1808                rdhi: _,
1809                dm: _,
1810            } => {
1811                // i64 trunc f64 unsigned: (rdhi:rdlo) = (unsigned i64)dm
1812                // Symbolic conversion (complex operation)
1813                // Would require proper truncation with saturation
1814            }
1815
1816            ArmOp::I32TruncF64S { rd, dm } => {
1817                // i32 trunc f64 signed: rd = (i32)dm
1818                // Symbolic conversion
1819                let result = BV::new_const(format!("i32_trunc_f64s_{:?}", dm), 32);
1820                state.set_reg(rd, result);
1821            }
1822
1823            ArmOp::I32TruncF64U { rd, dm } => {
1824                // i32 trunc f64 unsigned: rd = (unsigned i32)dm
1825                // Symbolic conversion
1826                let result = BV::new_const(format!("i32_trunc_f64u_{:?}", dm), 32);
1827                state.set_reg(rd, result);
1828            }
1829
1830            // VCR-VER-002 (#166): UDF is the WASM trap sink — executing it
1831            // raises UsageFault. In the straight-line model (no path guards)
1832            // reaching a UDF means the sequence traps unconditionally; the
1833            // branch-taking executor [`Self::encode_sequence_br`] instead
1834            // conditions this on the guard the UDF is reached under.
1835            ArmOp::Udf { .. } => {
1836                state.may_trap = Bool::from_bool(true);
1837            }
1838
1839            _ => {
1840                // Unsupported operations - no state change
1841            }
1842        }
1843    }
1844
1845    /// Evaluate an Operand2 value
1846    fn evaluate_operand2(&self, op2: &Operand2, state: &ArmState) -> BV {
1847        match op2 {
1848            Operand2::Imm(value) => BV::from_i64(*value as i64, 32),
1849            Operand2::Reg(reg) => state.get_reg(reg).clone(),
1850            Operand2::RegShift { rm, shift, amount } => {
1851                let reg_val = state.get_reg(rm).clone();
1852                let shift_amount = BV::from_i64(*amount as i64, 32);
1853
1854                match shift {
1855                    synth_synthesis::ShiftType::LSL => reg_val.bvshl(&shift_amount),
1856                    synth_synthesis::ShiftType::LSR => reg_val.bvlshr(&shift_amount),
1857                    synth_synthesis::ShiftType::ASR => reg_val.bvashr(&shift_amount),
1858                    synth_synthesis::ShiftType::ROR => reg_val.bvrotr(&shift_amount),
1859                }
1860            }
1861        }
1862    }
1863
1864    /// Extract the result value from a register after execution
1865    pub fn extract_result(&self, state: &ArmState, reg: &Reg) -> BV {
1866        state.get_reg(reg).clone()
1867    }
1868
1869    /// Encode ARM CLZ (Count Leading Zeros) instruction
1870    ///
1871    /// Implements the same algorithm as WASM i32.clz for equivalence verification.
1872    /// Uses binary search through bit positions.
1873    fn encode_clz(&self, input: &BV) -> BV {
1874        let zero = BV::from_i64(0, 32);
1875
1876        // Special case: if input is 0, return 32
1877        let all_zero = input.eq(&zero);
1878        let result_if_zero = BV::from_i64(32, 32);
1879
1880        // Binary search approach
1881        let mut count = BV::from_i64(0, 32);
1882        let mut remaining = input.clone();
1883
1884        // Check top 16 bits
1885        let mask_16 = BV::from_u64(0xFFFF0000, 32);
1886        let top_16 = remaining.bvand(&mask_16);
1887        let top_16_zero = top_16.eq(&zero);
1888
1889        count = top_16_zero.ite(count.bvadd(BV::from_i64(16, 32)), &count);
1890        remaining = top_16_zero.ite(remaining.bvshl(BV::from_i64(16, 32)), &remaining);
1891
1892        // Check top 8 bits
1893        let mask_8 = BV::from_u64(0xFF000000, 32);
1894        let top_8 = remaining.bvand(&mask_8);
1895        let top_8_zero = top_8.eq(&zero);
1896
1897        count = top_8_zero.ite(count.bvadd(BV::from_i64(8, 32)), &count);
1898        remaining = top_8_zero.ite(remaining.bvshl(BV::from_i64(8, 32)), &remaining);
1899
1900        // Check top 4 bits
1901        let mask_4 = BV::from_u64(0xF0000000, 32);
1902        let top_4 = remaining.bvand(&mask_4);
1903        let top_4_zero = top_4.eq(&zero);
1904
1905        count = top_4_zero.ite(count.bvadd(BV::from_i64(4, 32)), &count);
1906        remaining = top_4_zero.ite(remaining.bvshl(BV::from_i64(4, 32)), &remaining);
1907
1908        // Check top 2 bits
1909        let mask_2 = BV::from_u64(0xC0000000, 32);
1910        let top_2 = remaining.bvand(&mask_2);
1911        let top_2_zero = top_2.eq(&zero);
1912
1913        count = top_2_zero.ite(count.bvadd(BV::from_i64(2, 32)), &count);
1914        remaining = top_2_zero.ite(remaining.bvshl(BV::from_i64(2, 32)), &remaining);
1915
1916        // Check top bit
1917        let mask_1 = BV::from_u64(0x80000000, 32);
1918        let top_1 = remaining.bvand(&mask_1);
1919        let top_1_zero = top_1.eq(&zero);
1920
1921        count = top_1_zero.ite(count.bvadd(BV::from_i64(1, 32)), &count);
1922
1923        // Return 32 if all zeros, otherwise return count
1924        all_zero.ite(&result_if_zero, &count)
1925    }
1926
1927    /// Encode CTZ (Count Trailing Zeros) instruction
1928    ///
1929    /// Counts the number of trailing (low-order) zero bits.
1930    /// Implemented as: ctz(x) = clz(rbit(x))
1931    /// Returns 32 if input is 0.
1932    fn encode_ctz(&self, input: &BV) -> BV {
1933        // CTZ can be implemented by reversing bits and then counting leading zeros
1934        let reversed = self.encode_rbit(input);
1935        self.encode_clz(&reversed)
1936    }
1937
1938    /// Encode ARM RBIT (Reverse Bits) instruction
1939    ///
1940    /// Reverses the bit order in a 32-bit value.
1941    /// Used in combination with CLZ to implement CTZ.
1942    fn encode_rbit(&self, input: &BV) -> BV {
1943        // Reverse bits by swapping progressively smaller chunks
1944        let mut result = input.clone();
1945
1946        // Swap 16-bit halves
1947        let mask_16 = BV::from_u64(0xFFFF0000, 32);
1948        let top_16 = result.bvand(&mask_16).bvlshr(BV::from_i64(16, 32));
1949        let bottom_16 = result.bvshl(BV::from_i64(16, 32));
1950        result = top_16.bvor(&bottom_16);
1951
1952        // Swap 8-bit chunks
1953        let mask_8_top = BV::from_u64(0xFF00FF00, 32);
1954        let mask_8_bottom = BV::from_u64(0x00FF00FF, 32);
1955        let top_8 = result.bvand(&mask_8_top).bvlshr(BV::from_i64(8, 32));
1956        let bottom_8 = result.bvand(&mask_8_bottom).bvshl(BV::from_i64(8, 32));
1957        result = top_8.bvor(&bottom_8);
1958
1959        // Swap 4-bit chunks
1960        let mask_4_top = BV::from_u64(0xF0F0F0F0, 32);
1961        let mask_4_bottom = BV::from_u64(0x0F0F0F0F, 32);
1962        let top_4 = result.bvand(&mask_4_top).bvlshr(BV::from_i64(4, 32));
1963        let bottom_4 = result.bvand(&mask_4_bottom).bvshl(BV::from_i64(4, 32));
1964        result = top_4.bvor(&bottom_4);
1965
1966        // Swap 2-bit chunks
1967        let mask_2_top = BV::from_u64(0xCCCCCCCC, 32);
1968        let mask_2_bottom = BV::from_u64(0x33333333, 32);
1969        let top_2 = result.bvand(&mask_2_top).bvlshr(BV::from_i64(2, 32));
1970        let bottom_2 = result.bvand(&mask_2_bottom).bvshl(BV::from_i64(2, 32));
1971        result = top_2.bvor(&bottom_2);
1972
1973        // Swap 1-bit chunks (individual bits)
1974        let mask_1_top = BV::from_u64(0xAAAAAAAA, 32);
1975        let mask_1_bottom = BV::from_u64(0x55555555, 32);
1976        let top_1 = result.bvand(&mask_1_top).bvlshr(BV::from_i64(1, 32));
1977        let bottom_1 = result.bvand(&mask_1_bottom).bvshl(BV::from_i64(1, 32));
1978        result = top_1.bvor(&bottom_1);
1979
1980        result
1981    }
1982
1983    /// Update condition flags for subtraction (used by CMP, SUB, etc.)
1984    ///
1985    /// Computes all four ARM condition flags based on a subtraction:
1986    /// - N (Negative): Result is negative (bit 31 set)
1987    /// - Z (Zero): Result is zero
1988    /// - C (Carry): No borrow occurred (unsigned: a >= b)
1989    /// - V (Overflow): Signed overflow occurred
1990    ///
1991    /// For subtraction result = a - b:
1992    /// - C = 1 if a >= b (unsigned), 0 if borrow
1993    /// - V = 1 if signs of a and b differ AND sign of result differs from a
1994    fn update_flags_sub(&self, state: &mut ArmState, a: &BV, b: &BV, result: &BV) {
1995        let zero = BV::from_i64(0, 32);
1996
1997        // N flag: bit 31 of result (negative if set)
1998        let sign_bit = result.extract(31, 31);
1999        let one_bit = BV::from_i64(1, 1);
2000        state.flags.n = sign_bit.eq(&one_bit);
2001
2002        // Z flag: result == 0
2003        state.flags.z = result.eq(&zero);
2004
2005        // C flag: carry/borrow flag for subtraction
2006        // For SUB: C = 1 if no borrow (i.e., a >= b unsigned)
2007        // This is equivalent to: a >= b in unsigned arithmetic
2008        state.flags.c = a.bvuge(b);
2009
2010        // V flag: signed overflow
2011        // Overflow occurs when:
2012        // - Subtracting a positive from a negative gives positive
2013        // - Subtracting a negative from a positive gives negative
2014        // Formula: (a[31] != b[31]) && (a[31] != result[31])
2015        let a_sign = a.extract(31, 31);
2016        let b_sign = b.extract(31, 31);
2017        let r_sign = result.extract(31, 31);
2018
2019        let signs_differ = a_sign.eq(&b_sign).not(); // a and b have different signs
2020        let result_sign_wrong = a_sign.eq(&r_sign).not(); // result sign differs from a
2021        state.flags.v = Bool::and(&[&signs_differ, &result_sign_wrong]);
2022    }
2023
2024    /// Update condition flags for addition
2025    ///
2026    /// Similar to subtraction but with different carry logic:
2027    /// - C = 1 if unsigned overflow (result < a or result < b)
2028    /// - V = 1 if signed overflow
2029    #[allow(dead_code)]
2030    fn update_flags_add(&self, state: &mut ArmState, a: &BV, b: &BV, result: &BV) {
2031        let zero = BV::from_i64(0, 32);
2032
2033        // N flag: bit 31 of result
2034        let sign_bit = result.extract(31, 31);
2035        let one_bit = BV::from_i64(1, 1);
2036        state.flags.n = sign_bit.eq(&one_bit);
2037
2038        // Z flag: result == 0
2039        state.flags.z = result.eq(&zero);
2040
2041        // C flag: unsigned overflow
2042        // For ADD: C = 1 if carry out (unsigned overflow)
2043        // This occurs if result < a (wrapping occurred)
2044        state.flags.c = result.bvult(a);
2045
2046        // V flag: signed overflow
2047        // Overflow occurs when:
2048        // - Adding two positives gives negative
2049        // - Adding two negatives gives positive
2050        // Formula: (a[31] == b[31]) && (a[31] != result[31])
2051        let a_sign = a.extract(31, 31);
2052        let b_sign = b.extract(31, 31);
2053        let r_sign = result.extract(31, 31);
2054
2055        let signs_same = a_sign.eq(&b_sign); // a and b have same sign
2056        let result_sign_wrong = a_sign.eq(&r_sign).not(); // result sign differs
2057        state.flags.v = Bool::and(&[&signs_same, &result_sign_wrong]);
2058    }
2059
2060    /// Evaluate an ARM condition code based on NZCV flags
2061    ///
2062    /// This implements the standard ARM condition code logic:
2063    /// - EQ: Z == 1
2064    /// - NE: Z == 0
2065    /// - LT: N != V (signed less than)
2066    /// - LE: Z == 1 || N != V (signed less or equal)
2067    /// - GT: Z == 0 && N == V (signed greater than)
2068    /// - GE: N == V (signed greater or equal)
2069    /// - LO: C == 0 (unsigned less than)
2070    /// - LS: C == 0 || Z == 1 (unsigned less or equal)
2071    /// - HI: C == 1 && Z == 0 (unsigned greater than)
2072    /// - HS: C == 1 (unsigned greater or equal)
2073    fn evaluate_condition(
2074        &self,
2075        cond: &synth_synthesis::rules::Condition,
2076        flags: &ConditionFlags,
2077    ) -> Bool {
2078        use synth_synthesis::rules::Condition;
2079
2080        match cond {
2081            Condition::EQ => flags.z.clone(),
2082            Condition::NE => flags.z.not(),
2083            Condition::LT => {
2084                // N != V: negative flag differs from overflow flag
2085                flags.n.eq(&flags.v).not()
2086            }
2087            Condition::LE => {
2088                // Z == 1 || N != V
2089                let n_ne_v = flags.n.eq(&flags.v).not();
2090                Bool::or(&[&flags.z, &n_ne_v])
2091            }
2092            Condition::GT => {
2093                // Z == 0 && N == V
2094                let z_zero = flags.z.not();
2095                let n_eq_v = flags.n.eq(&flags.v);
2096                Bool::and(&[&z_zero, &n_eq_v])
2097            }
2098            Condition::GE => {
2099                // N == V
2100                flags.n.eq(&flags.v)
2101            }
2102            Condition::LO => {
2103                // C == 0 (no carry = less than unsigned)
2104                flags.c.not()
2105            }
2106            Condition::LS => {
2107                // C == 0 || Z == 1
2108                let c_zero = flags.c.not();
2109                Bool::or(&[&flags.z, &c_zero])
2110            }
2111            Condition::HI => {
2112                // C == 1 && Z == 0
2113                let z_zero = flags.z.not();
2114                Bool::and(&[&flags.c, &z_zero])
2115            }
2116            Condition::HS => {
2117                // C == 1 (carry = greater or equal unsigned)
2118                flags.c.clone()
2119            }
2120        }
2121    }
2122
2123    /// Convert a boolean to a 32-bit bitvector (0 or 1)
2124    fn bool_to_bv32(&self, cond: &Bool) -> BV {
2125        let zero = BV::from_i64(0, 32);
2126        let one = BV::from_i64(1, 32);
2127        cond.ite(&one, &zero)
2128    }
2129
2130    /// Encode ARM POPCNT (population count)
2131    ///
2132    /// Uses the Hamming weight algorithm (same as WASM implementation).
2133    /// This is a pseudo-instruction that would be expanded into actual ARM code.
2134    fn encode_popcnt(&self, input: &BV) -> BV {
2135        let mut x = input.clone();
2136
2137        // Step 1: Count bits in pairs
2138        let mask1 = BV::from_u64(0x55555555, 32);
2139        let masked = x.bvand(&mask1);
2140        let shifted = x.bvlshr(BV::from_i64(1, 32));
2141        let shifted_masked = shifted.bvand(&mask1);
2142        x = masked.bvadd(&shifted_masked);
2143
2144        // Step 2: Count pairs in nibbles
2145        let mask2 = BV::from_u64(0x33333333, 32);
2146        let masked = x.bvand(&mask2);
2147        let shifted = x.bvlshr(BV::from_i64(2, 32));
2148        let shifted_masked = shifted.bvand(&mask2);
2149        x = masked.bvadd(&shifted_masked);
2150
2151        // Step 3: Count nibbles in bytes
2152        let mask3 = BV::from_u64(0x0F0F0F0F, 32);
2153        let masked = x.bvand(&mask3);
2154        let shifted = x.bvlshr(BV::from_i64(4, 32));
2155        let shifted_masked = shifted.bvand(&mask3);
2156        x = masked.bvadd(&shifted_masked);
2157
2158        // Step 4: Sum all bytes
2159        let multiplier = BV::from_u64(0x01010101, 32);
2160        x = x.bvmul(&multiplier);
2161        x = x.bvlshr(BV::from_i64(24, 32));
2162
2163        x
2164    }
2165}
2166
2167// ===========================================================================
2168// VCR-VER-002 (#166): branch-taking guarded executor — DERIVES the ARM trap
2169// condition from the emitted guard/branch/UDF structure
2170// ===========================================================================
2171
2172/// Path guard: the condition under which an instruction executes. `Always`
2173/// keeps the straight-line common case free of `ite` merging.
2174#[derive(Clone)]
2175enum Guard {
2176    Always,
2177    Cond(Bool),
2178}
2179
2180impl Guard {
2181    fn and_cond(&self, c: &Bool) -> Guard {
2182        match self {
2183            Guard::Always => Guard::Cond(c.clone()),
2184            Guard::Cond(g) => Guard::Cond(Bool::and(&[g, c])),
2185        }
2186    }
2187}
2188
2189/// Merge an incoming edge guard into the guard map at `at`.
2190fn merge_guard(incoming: &mut HashMap<usize, Guard>, at: usize, g: Guard) {
2191    match (incoming.get(&at), g) {
2192        (Some(Guard::Always), _) => {}
2193        (_, Guard::Always) => {
2194            incoming.insert(at, Guard::Always);
2195        }
2196        (Some(Guard::Cond(a)), Guard::Cond(b)) => {
2197            let merged = Bool::or(&[a, &b]);
2198            incoming.insert(at, Guard::Cond(merged));
2199        }
2200        (None, g @ Guard::Cond(_)) => {
2201            incoming.insert(at, g);
2202        }
2203    }
2204}
2205
2206/// Boolean if-then-else (the term API only has BV ite).
2207fn bool_ite(c: &Bool, t: &Bool, e: &Bool) -> Bool {
2208    Bool::or(&[&Bool::and(&[c, t]), &Bool::and(&[&c.not(), e])])
2209}
2210
2211/// IEEE 754 single-precision NaN test over the raw bit pattern:
2212/// exponent all-ones with a non-zero fraction.
2213fn f32_is_nan(x: &BV) -> Bool {
2214    let exp_ones = x.extract(30, 23).eq(BV::from_u64(0xFF, 8));
2215    let frac_nonzero = x.extract(22, 0).eq(BV::from_u64(0, 23)).not();
2216    Bool::and(&[&exp_ones, &frac_nonzero])
2217}
2218
2219/// Ordered `a < b` over IEEE 754 single-precision BIT PATTERNS, assuming
2220/// neither operand is NaN (the callers conjoin the NaN exclusion). Uses the
2221/// sign/magnitude case split; `+0.0 == -0.0` (neither is less).
2222fn f32_ordered_lt(a: &BV, b: &BV) -> Bool {
2223    let a_neg = a.extract(31, 31).eq(BV::from_u64(1, 1));
2224    let b_neg = b.extract(31, 31).eq(BV::from_u64(1, 1));
2225    let a_mag = a.extract(30, 0);
2226    let b_mag = b.extract(30, 0);
2227    let zero31 = BV::from_u64(0, 31);
2228    let both_zero = Bool::and(&[&a_mag.eq(&zero31), &b_mag.eq(&zero31)]);
2229    // (neg, neg): larger magnitude is smaller; (neg, pos): a < b unless both
2230    // are zeros; (pos, neg): never; (pos, pos): magnitude order.
2231    let neg_neg = b_mag.bvult(&a_mag);
2232    let neg_pos = both_zero.not();
2233    let pos_pos = a_mag.bvult(&b_mag);
2234    bool_ite(
2235        &a_neg,
2236        &bool_ite(&b_neg, &neg_neg, &neg_pos),
2237        &bool_ite(&b_neg, &Bool::from_bool(false), &pos_pos),
2238    )
2239}
2240
2241/// The three ordered VFP comparison results the trunc guards use, as total
2242/// functions over the operands' bit patterns (result is 0 on any NaN — the
2243/// unordered case — exactly the ARM `VCMP`+`VMRS`+`IT` materialization the
2244/// `F32Lt`/`F32Gt`/`F32Ge` pseudo-ops stand for).
2245fn f32_cmp_result(kind: F32CmpKind, a: &BV, b: &BV) -> Bool {
2246    let ordered = Bool::and(&[&f32_is_nan(a).not(), &f32_is_nan(b).not()]);
2247    let rel = match kind {
2248        F32CmpKind::Lt => f32_ordered_lt(a, b),
2249        F32CmpKind::Gt => f32_ordered_lt(b, a),
2250        F32CmpKind::Ge => f32_ordered_lt(a, b).not(),
2251    };
2252    Bool::and(&[&ordered, &rel])
2253}
2254
2255#[derive(Clone, Copy)]
2256enum F32CmpKind {
2257    Lt,
2258    Gt,
2259    Ge,
2260}
2261
2262impl ArmSemantics {
2263    /// Branch-taking guarded symbolic execution of an ARM sequence,
2264    /// deriving `state.may_trap` from the emitted guard structure
2265    /// (VCR-VER-002, #166).
2266    ///
2267    /// Forward-branch DAG execution over the op list: every instruction
2268    /// carries the disjunction of the path conditions that reach it
2269    /// (if-conversion), `BCondOffset` routes guards forward, and a `Udf`
2270    /// accumulates its path guard into [`ArmState::may_trap`] — and does NOT
2271    /// fall through (a trap halts execution, so the code after a guarded
2272    /// `UDF` is reached only via the guard's skip branch). This makes the ARM
2273    /// trap condition a DERIVED term: a lowering whose guard was dropped,
2274    /// inverted, or aimed at the wrong register derives a trap condition that
2275    /// fails the preservation VC — unlike the previous structural
2276    /// `Udf`-presence proxy, which only saw that *some* trap existed.
2277    ///
2278    /// Branch targets are resolved in bytes via the shipped byte-size
2279    /// estimator (`synth_synthesis::optimizer_bridge::estimate_arm_byte_size`,
2280    /// the #511 estimator that CI pins against the encoder), matching the
2281    /// encoder's `target = branch + 4 + 2*offset` halfword rule. A target
2282    /// that lands mid-instruction, a backward branch (loop), an op outside
2283    /// the modeled subset, or any label/call control flow is a loud `Err` —
2284    /// never a silent accept.
2285    pub fn encode_sequence_br(
2286        &self,
2287        arm_ops: &[ArmOp],
2288        state: &mut ArmState,
2289    ) -> Result<(), String> {
2290        use synth_synthesis::optimizer_bridge::estimate_arm_byte_size;
2291
2292        // Byte offset of each op (the estimator is the pinned encoder mirror).
2293        let mut offsets = Vec::with_capacity(arm_ops.len());
2294        let mut off = 0usize;
2295        for op in arm_ops {
2296            offsets.push(off);
2297            off += estimate_arm_byte_size(op);
2298        }
2299        let total_len = off;
2300        let boundaries: std::collections::HashSet<usize> = offsets.iter().copied().collect();
2301
2302        let mut incoming: HashMap<usize, Guard> = HashMap::new();
2303        incoming.insert(0, Guard::Always);
2304
2305        for (i, op) in arm_ops.iter().enumerate() {
2306            let o = offsets[i];
2307            // Unreached instruction (e.g. dead code behind an unconditional
2308            // trap): no incoming edge, skip — it can never execute.
2309            let Some(g) = incoming.get(&o).cloned() else {
2310                continue;
2311            };
2312            let next = o + estimate_arm_byte_size(op);
2313
2314            match op {
2315                ArmOp::BCondOffset { cond, offset } => {
2316                    if *offset < 0 {
2317                        return Err(
2318                            "backward branch (loop) outside the trap-derivation subset — held out"
2319                                .to_string(),
2320                        );
2321                    }
2322                    // Encoder rule: offset is the halfword displacement,
2323                    // target = branch_addr + 4 + 2*offset.
2324                    let target = o + 4 + 2 * (*offset as usize);
2325                    if target != total_len && !boundaries.contains(&target) {
2326                        return Err(format!(
2327                            "BCondOffset target {target} lands mid-instruction \
2328                             (sequence len {total_len}) — estimator/encoder drift or \
2329                             malformed guard"
2330                        ));
2331                    }
2332                    let c = self.evaluate_condition(cond, &state.flags);
2333                    merge_guard(&mut incoming, target, g.and_cond(&c));
2334                    merge_guard(&mut incoming, next, g.and_cond(&c.not()));
2335                }
2336
2337                ArmOp::Udf { .. } => {
2338                    // The trap fires exactly under this path guard; execution
2339                    // never continues past it (no fall-through edge).
2340                    state.may_trap = match &g {
2341                        Guard::Always => Bool::from_bool(true),
2342                        Guard::Cond(gb) => Bool::or(&[&state.may_trap, gb]),
2343                    };
2344                }
2345
2346                // Label/relative/indirect control flow has no derivable local
2347                // trap semantics here — loud decline, never a silent accept.
2348                ArmOp::B { .. }
2349                | ArmOp::BOffset { .. }
2350                | ArmOp::Bcc { .. }
2351                | ArmOp::Bhs { .. }
2352                | ArmOp::Blo { .. }
2353                | ArmOp::Bl { .. }
2354                | ArmOp::Blx { .. }
2355                | ArmOp::Bx { .. }
2356                | ArmOp::Label { .. }
2357                | ArmOp::Call { .. }
2358                | ArmOp::CallIndirect { .. }
2359                | ArmOp::BrTable { .. }
2360                | ArmOp::Push { .. }
2361                | ArmOp::Pop { .. } => {
2362                    return Err(format!(
2363                        "op {op:?} outside the trap-derivation subset — loud decline"
2364                    ));
2365                }
2366
2367                _ => {
2368                    match &g {
2369                        Guard::Always => self.exec_trap_subset_op(op, state)?,
2370                        Guard::Cond(gb) => {
2371                            // Guarded (if-converted) execution: snapshot the
2372                            // register/flag/VFP state, execute, ite-merge
2373                            // under the guard. Sound because these ops touch
2374                            // only registers/flags/VFP (the subset check in
2375                            // exec_trap_subset_op rejects everything else).
2376                            //
2377                            // Only components the op actually CHANGED are
2378                            // merged — `ite(g, x, x) ≡ x`, and wrapping every
2379                            // untouched register on every guarded step nests
2380                            // the SDIV/UDIV operands in ite chains, blowing
2381                            // the div/rem trap VC off a CDCL cliff (observed:
2382                            // the div_s double-guard query ran 45+ min / 5 GB
2383                            // with the unconditional merge, sub-second
2384                            // without).
2385                            let regs_before = state.registers.clone();
2386                            let vfp_before = state.vfp_registers.clone();
2387                            let flags_before = ConditionFlags {
2388                                n: state.flags.n.clone(),
2389                                z: state.flags.z.clone(),
2390                                c: state.flags.c.clone(),
2391                                v: state.flags.v.clone(),
2392                            };
2393                            self.exec_trap_subset_op(op, state)?;
2394                            for (r, before) in regs_before.iter().enumerate() {
2395                                if !state.registers[r].same_term(before) {
2396                                    state.registers[r] = gb.ite(&state.registers[r], before);
2397                                }
2398                            }
2399                            for (r, before) in vfp_before.iter().enumerate() {
2400                                if !state.vfp_registers[r].same_term(before) {
2401                                    state.vfp_registers[r] =
2402                                        gb.ite(&state.vfp_registers[r], before);
2403                                }
2404                            }
2405                            if !state.flags.n.same_term(&flags_before.n) {
2406                                state.flags.n = bool_ite(gb, &state.flags.n, &flags_before.n);
2407                            }
2408                            if !state.flags.z.same_term(&flags_before.z) {
2409                                state.flags.z = bool_ite(gb, &state.flags.z, &flags_before.z);
2410                            }
2411                            if !state.flags.c.same_term(&flags_before.c) {
2412                                state.flags.c = bool_ite(gb, &state.flags.c, &flags_before.c);
2413                            }
2414                            if !state.flags.v.same_term(&flags_before.v) {
2415                                state.flags.v = bool_ite(gb, &state.flags.v, &flags_before.v);
2416                            }
2417                        }
2418                    }
2419                    merge_guard(&mut incoming, next, g);
2420                }
2421            }
2422        }
2423
2424        Ok(())
2425    }
2426
2427    /// Whether the sequence's branch structure is VALUE-DEAD: every op inside
2428    /// a branch-skipped span writes no register/VFP state (`Udf`, `Cmp`,
2429    /// `Cmn`, nested `BCondOffset` only), and no op anywhere in the sequence
2430    /// turns flags into a register value (`SetCond`).
2431    ///
2432    /// Under this condition the final REGISTER state is path-independent —
2433    /// every register-writing op executes on every path, in program order —
2434    /// so the straight-line value pass
2435    /// [`Self::encode_sequence_value_straightline`] computes exactly the
2436    /// registers any non-trapping real path produces. The flag writes a taken
2437    /// branch skips (e.g. the div_s overflow guard's `CMN` behind `BNE +3`)
2438    /// can only influence which PATH is taken — the trap side, which
2439    /// [`Self::encode_sequence_br`] derives with full path sensitivity — and
2440    /// never a register value, because `SetCond` (the only flag→register op
2441    /// in the modeled subset) is excluded outright.
2442    ///
2443    /// This is what lets the div/rem trap VC keep its value clause
2444    /// STRUCTURALLY aligned with the WASM side (`bvsdiv`/`MLS` terms
2445    /// identical after canonicalization): an `ite(guard, …)` wrapper on an
2446    /// SDIV/MLS operand un-shares the 32×32 multiplier/divider circuits and
2447    /// sends the UNSAT proof off the CDCL cliff term.rs documents (observed:
2448    /// rem_s value clause 15+ min with the ite, sub-second without).
2449    pub fn branch_spans_are_value_dead(arm_ops: &[ArmOp]) -> bool {
2450        use synth_synthesis::optimizer_bridge::estimate_arm_byte_size;
2451
2452        let mut offsets = Vec::with_capacity(arm_ops.len());
2453        let mut off = 0usize;
2454        for op in arm_ops {
2455            offsets.push(off);
2456            off += estimate_arm_byte_size(op);
2457        }
2458
2459        // No flag→register materialization anywhere in the sequence.
2460        if arm_ops.iter().any(|op| matches!(op, ArmOp::SetCond { .. })) {
2461            return false;
2462        }
2463
2464        for (i, op) in arm_ops.iter().enumerate() {
2465            if let ArmOp::BCondOffset { offset, .. } = op {
2466                if *offset < 0 {
2467                    return false; // backward branch — not this subset at all
2468                }
2469                // Fall-through = next instruction; encoder rule for the
2470                // target: branch_addr + 4 + 2*offset (same as
2471                // `encode_sequence_br`). The skipped span is [fall-through,
2472                // target).
2473                let span_start = offsets[i] + estimate_arm_byte_size(op);
2474                let span_end = offsets[i] + 4 + 2 * (*offset as usize);
2475                for (j, skipped) in arm_ops.iter().enumerate() {
2476                    if offsets[j] >= span_start && offsets[j] < span_end {
2477                        match skipped {
2478                            ArmOp::Udf { .. }
2479                            | ArmOp::Cmp { .. }
2480                            | ArmOp::Cmn { .. }
2481                            | ArmOp::BCondOffset { .. } => {}
2482                            _ => return false, // a register/VFP write is skippable
2483                        }
2484                    }
2485                }
2486            }
2487        }
2488        true
2489    }
2490
2491    /// Straight-line VALUE execution of a trap-guarded sequence: branches and
2492    /// `UDF`s are register no-ops, every other op executes unconditionally
2493    /// via the same modeled subset as the branch-taking executor.
2494    ///
2495    /// ONLY sound when [`Self::branch_spans_are_value_dead`] holds (see its
2496    /// doc for the argument); callers must check it first. Produces ite-free
2497    /// register terms, keeping the trap VC's value clause structurally
2498    /// aligned with the WASM encoding.
2499    pub fn encode_sequence_value_straightline(
2500        &self,
2501        arm_ops: &[ArmOp],
2502        state: &mut ArmState,
2503    ) -> Result<(), String> {
2504        for op in arm_ops {
2505            match op {
2506                ArmOp::BCondOffset { .. } | ArmOp::Udf { .. } => {}
2507                _ => self.exec_trap_subset_op(op, state)?,
2508            }
2509        }
2510        Ok(())
2511    }
2512
2513    /// Execute one non-branch op of the trap-derivation subset. Ops the
2514    /// shipped trap-guarded lowerings use but `encode_op` leaves unmodeled
2515    /// (`Cmn`, `Movw`, `Movt`, the ordered VFP compares) get explicit
2516    /// semantics here; a WHITELIST of register-only value ops delegates to
2517    /// `encode_op`; anything else is a loud `Err` — `encode_op`'s silent
2518    /// `_ => {}` default must never green-wash a trap derivation.
2519    fn exec_trap_subset_op(&self, op: &ArmOp, state: &mut ArmState) -> Result<(), String> {
2520        match op {
2521            // CMN: compare negated — flags from rn + op2.
2522            ArmOp::Cmn { rn, op2 } => {
2523                let a = state.get_reg(rn).clone();
2524                let b = self.evaluate_operand2(op2, state);
2525                let result = a.bvadd(&b);
2526                self.update_flags_add(state, &a, &b, &result);
2527                Ok(())
2528            }
2529            ArmOp::Movw { rd, imm16 } => {
2530                state.set_reg(rd, BV::from_u64(*imm16 as u64, 32));
2531                Ok(())
2532            }
2533            ArmOp::Movt { rd, imm16 } => {
2534                let low = state.get_reg(rd).bvand(BV::from_u64(0xFFFF, 32));
2535                let v = low.bvor(BV::from_u64((*imm16 as u64) << 16, 32));
2536                state.set_reg(rd, v);
2537                Ok(())
2538            }
2539            // Ordered VFP compares (the #709 trunc guards): real bit-pattern
2540            // semantics — result register is 1 iff the ordered relation
2541            // holds, 0 on NaN. encode_op models these as uninterpreted
2542            // symbols, which cannot drive a trap derivation.
2543            ArmOp::F32Lt { rd, sn, sm } => {
2544                let a = state.get_vfp_reg(sn).clone();
2545                let b = state.get_vfp_reg(sm).clone();
2546                let r = self.bool_to_bv32(&f32_cmp_result(F32CmpKind::Lt, &a, &b));
2547                state.set_reg(rd, r);
2548                Ok(())
2549            }
2550            ArmOp::F32Gt { rd, sn, sm } => {
2551                let a = state.get_vfp_reg(sn).clone();
2552                let b = state.get_vfp_reg(sm).clone();
2553                let r = self.bool_to_bv32(&f32_cmp_result(F32CmpKind::Gt, &a, &b));
2554                state.set_reg(rd, r);
2555                Ok(())
2556            }
2557            ArmOp::F32Ge { rd, sn, sm } => {
2558                let a = state.get_vfp_reg(sn).clone();
2559                let b = state.get_vfp_reg(sm).clone();
2560                let r = self.bool_to_bv32(&f32_cmp_result(F32CmpKind::Ge, &a, &b));
2561                state.set_reg(rd, r);
2562                Ok(())
2563            }
2564            // Register/flag-only value ops the covered lowerings use:
2565            // delegate to the existing encode_op semantics.
2566            ArmOp::Cmp { .. }
2567            | ArmOp::Add { .. }
2568            | ArmOp::Sub { .. }
2569            | ArmOp::Rsb { .. }
2570            | ArmOp::Mov { .. }
2571            | ArmOp::And { .. }
2572            | ArmOp::Orr { .. }
2573            | ArmOp::Eor { .. }
2574            | ArmOp::Mul { .. }
2575            | ArmOp::Mls { .. }
2576            | ArmOp::Sdiv { .. }
2577            | ArmOp::Udiv { .. }
2578            | ArmOp::SetCond { .. }
2579            | ArmOp::Nop
2580            | ArmOp::F32Const { .. }
2581            | ArmOp::I32TruncF32S { .. }
2582            | ArmOp::I32TruncF32U { .. }
2583            // Ldr/Str: the value model treats loads as fresh symbols and
2584            // stores as no-ops (no memory-contents model) — fine for a trap
2585            // derivation, where only the guard's flags/registers matter.
2586            | ArmOp::Ldr { .. }
2587            | ArmOp::Str { .. } => {
2588                self.encode_op(op, state);
2589                Ok(())
2590            }
2591            // Subword accesses (#752 gate coverage for the guarded
2592            // i32.load8/16 + i32.store8/16 shapes): same treatment as
2593            // Ldr/Str — a load writes a fresh symbol (no memory-contents
2594            // model), a store touches no register. Neither affects flags,
2595            // so the trap derivation is untouched; modeling them here just
2596            // lets guarded subword sequences through instead of a loud
2597            // decline.
2598            ArmOp::Ldrb { rd, .. }
2599            | ArmOp::Ldrsb { rd, .. }
2600            | ArmOp::Ldrh { rd, .. }
2601            | ArmOp::Ldrsh { rd, .. } => {
2602                let result = BV::new_const(format!("load_{rd:?}"), 32);
2603                state.set_reg(rd, result);
2604                Ok(())
2605            }
2606            ArmOp::Strb { .. } | ArmOp::Strh { .. } => Ok(()),
2607            other => Err(format!(
2608                "op {other:?} outside the trap-derivation subset — loud decline"
2609            )),
2610        }
2611    }
2612}
2613
2614#[cfg(test)]
2615mod tests {
2616    use super::*;
2617    use crate::with_verification_context;
2618
2619    #[test]
2620    fn test_arm_add_semantics() {
2621        with_verification_context(|| {
2622            let encoder = ArmSemantics::new();
2623            let mut state = ArmState::new_symbolic();
2624
2625            // Set up concrete values for testing
2626            state.set_reg(&Reg::R1, BV::from_i64(10, 32));
2627            state.set_reg(&Reg::R2, BV::from_i64(20, 32));
2628
2629            // Execute: ADD R0, R1, R2
2630            let op = ArmOp::Add {
2631                rd: Reg::R0,
2632                rn: Reg::R1,
2633                op2: Operand2::Reg(Reg::R2),
2634            };
2635
2636            encoder.encode_op(&op, &mut state);
2637
2638            // Check result: R0 should be 30
2639            let result = state.get_reg(&Reg::R0).simplify();
2640            assert_eq!(result.as_i64(), Some(30));
2641        });
2642    }
2643
2644    #[test]
2645    fn test_arm_sub_semantics() {
2646        with_verification_context(|| {
2647            let encoder = ArmSemantics::new();
2648            let mut state = ArmState::new_symbolic();
2649
2650            state.set_reg(&Reg::R1, BV::from_i64(50, 32));
2651            state.set_reg(&Reg::R2, BV::from_i64(20, 32));
2652
2653            let op = ArmOp::Sub {
2654                rd: Reg::R0,
2655                rn: Reg::R1,
2656                op2: Operand2::Reg(Reg::R2),
2657            };
2658
2659            encoder.encode_op(&op, &mut state);
2660
2661            let result = state.get_reg(&Reg::R0);
2662            assert_eq!(result.simplify().as_i64(), Some(30));
2663        });
2664    }
2665
2666    #[test]
2667    fn test_arm_mov_immediate() {
2668        with_verification_context(|| {
2669            let encoder = ArmSemantics::new();
2670            let mut state = ArmState::new_symbolic();
2671
2672            let op = ArmOp::Mov {
2673                rd: Reg::R0,
2674                op2: Operand2::Imm(42),
2675            };
2676
2677            encoder.encode_op(&op, &mut state);
2678
2679            let result = state.get_reg(&Reg::R0);
2680            assert_eq!(result.simplify().as_i64(), Some(42));
2681        });
2682    }
2683
2684    #[test]
2685    fn test_arm_bitwise_ops() {
2686        with_verification_context(|| {
2687            let encoder = ArmSemantics::new();
2688            let mut state = ArmState::new_symbolic();
2689
2690            state.set_reg(&Reg::R1, BV::from_i64(0b1010, 32));
2691            state.set_reg(&Reg::R2, BV::from_i64(0b1100, 32));
2692
2693            // Test AND
2694            let and_op = ArmOp::And {
2695                rd: Reg::R0,
2696                rn: Reg::R1,
2697                op2: Operand2::Reg(Reg::R2),
2698            };
2699            encoder.encode_op(&and_op, &mut state);
2700            assert_eq!(state.get_reg(&Reg::R0).simplify().as_i64(), Some(0b1000));
2701
2702            // Test ORR
2703            let orr_op = ArmOp::Orr {
2704                rd: Reg::R0,
2705                rn: Reg::R1,
2706                op2: Operand2::Reg(Reg::R2),
2707            };
2708            encoder.encode_op(&orr_op, &mut state);
2709            assert_eq!(state.get_reg(&Reg::R0).simplify().as_i64(), Some(0b1110));
2710
2711            // Test EOR (XOR)
2712            let eor_op = ArmOp::Eor {
2713                rd: Reg::R0,
2714                rn: Reg::R1,
2715                op2: Operand2::Reg(Reg::R2),
2716            };
2717            encoder.encode_op(&eor_op, &mut state);
2718            assert_eq!(state.get_reg(&Reg::R0).simplify().as_i64(), Some(0b0110));
2719        });
2720    }
2721
2722    #[test]
2723    fn test_arm_mls() {
2724        // Test MLS (Multiply and Subtract): Rd = Ra - Rn * Rm
2725        // This is used for remainder: a % b = a - (a/b) * b
2726        with_verification_context(|| {
2727            let encoder = ArmSemantics::new();
2728            let mut state = ArmState::new_symbolic();
2729
2730            // Test: 17 % 5 = 17 - (17/5) * 5 = 17 - 3*5 = 17 - 15 = 2
2731            // Ra = 17, Rn = 3 (quotient), Rm = 5 (divisor)
2732            state.set_reg(&Reg::R0, BV::from_i64(17, 32)); // Ra (dividend)
2733            state.set_reg(&Reg::R1, BV::from_i64(3, 32)); // Rn (quotient)
2734            state.set_reg(&Reg::R2, BV::from_i64(5, 32)); // Rm (divisor)
2735
2736            let mls_op = ArmOp::Mls {
2737                rd: Reg::R3,
2738                rn: Reg::R1,
2739                rm: Reg::R2,
2740                ra: Reg::R0,
2741            };
2742            encoder.encode_op(&mls_op, &mut state);
2743            assert_eq!(
2744                state.get_reg(&Reg::R3).simplify().as_i64(),
2745                Some(2),
2746                "MLS: 17 - 3*5 = 2"
2747            );
2748
2749            // Test: 100 - 7 * 3 = 100 - 21 = 79
2750            state.set_reg(&Reg::R0, BV::from_i64(100, 32));
2751            state.set_reg(&Reg::R1, BV::from_i64(7, 32));
2752            state.set_reg(&Reg::R2, BV::from_i64(3, 32));
2753
2754            let mls_op2 = ArmOp::Mls {
2755                rd: Reg::R3,
2756                rn: Reg::R1,
2757                rm: Reg::R2,
2758                ra: Reg::R0,
2759            };
2760            encoder.encode_op(&mls_op2, &mut state);
2761            assert_eq!(
2762                state.get_reg(&Reg::R3).simplify().as_i64(),
2763                Some(79),
2764                "MLS: 100 - 7*3 = 79"
2765            );
2766
2767            // Test with negative numbers: (-17) - 3 * 5 = -17 - 15 = -32
2768            state.set_reg(&Reg::R0, BV::from_i64(-17, 32));
2769            state.set_reg(&Reg::R1, BV::from_i64(3, 32));
2770            state.set_reg(&Reg::R2, BV::from_i64(5, 32));
2771
2772            let mls_op3 = ArmOp::Mls {
2773                rd: Reg::R3,
2774                rn: Reg::R1,
2775                rm: Reg::R2,
2776                ra: Reg::R0,
2777            };
2778            encoder.encode_op(&mls_op3, &mut state);
2779            // Result is -32, but as_i64() returns unsigned, so we need to convert
2780            let result = state.get_reg(&Reg::R3).simplify().as_i64();
2781            let signed_result = result.map(|v| (v as i32) as i64);
2782            assert_eq!(signed_result, Some(-32), "MLS: -17 - 3*5 = -32");
2783        });
2784    }
2785
2786    #[test]
2787    fn test_arm_shift_ops() {
2788        with_verification_context(|| {
2789            let encoder = ArmSemantics::new();
2790            let mut state = ArmState::new_symbolic();
2791
2792            state.set_reg(&Reg::R1, BV::from_i64(8, 32));
2793
2794            // Test LSL (logical shift left) with immediate
2795            let lsl_op = ArmOp::Lsl {
2796                rd: Reg::R0,
2797                rn: Reg::R1,
2798                shift: 2,
2799            };
2800            encoder.encode_op(&lsl_op, &mut state);
2801            assert_eq!(state.get_reg(&Reg::R0).simplify().as_i64(), Some(32));
2802
2803            // Test LSR (logical shift right) with immediate
2804            let lsr_op = ArmOp::Lsr {
2805                rd: Reg::R0,
2806                rn: Reg::R1,
2807                shift: 2,
2808            };
2809            encoder.encode_op(&lsr_op, &mut state);
2810            assert_eq!(state.get_reg(&Reg::R0).simplify().as_i64(), Some(2));
2811        });
2812    }
2813
2814    #[test]
2815    fn test_arm_ror_comprehensive() {
2816        with_verification_context(|| {
2817            let encoder = ArmSemantics::new();
2818            let mut state = ArmState::new_symbolic();
2819
2820            // Test ROR with 0x12345678
2821            // ROR by 8 should rotate right by 8 bits
2822            state.set_reg(&Reg::R1, BV::from_u64(0x12345678, 32));
2823            let ror_op = ArmOp::Ror {
2824                rd: Reg::R0,
2825                rn: Reg::R1,
2826                shift: 8,
2827            };
2828            encoder.encode_op(&ror_op, &mut state);
2829            // 0x12345678 ROR 8 = 0x78123456
2830            assert_eq!(
2831                state.get_reg(&Reg::R0).simplify().as_i64(),
2832                Some(0x78123456),
2833                "ROR by 8"
2834            );
2835
2836            // Test ROR by 16 (swap halves)
2837            let ror_op_16 = ArmOp::Ror {
2838                rd: Reg::R0,
2839                rn: Reg::R1,
2840                shift: 16,
2841            };
2842            encoder.encode_op(&ror_op_16, &mut state);
2843            // 0x12345678 ROR 16 = 0x56781234
2844            assert_eq!(
2845                state.get_reg(&Reg::R0).simplify().as_i64(),
2846                Some(0x56781234),
2847                "ROR by 16"
2848            );
2849
2850            // Test ROR by 0 (no change)
2851            let ror_op_0 = ArmOp::Ror {
2852                rd: Reg::R0,
2853                rn: Reg::R1,
2854                shift: 0,
2855            };
2856            encoder.encode_op(&ror_op_0, &mut state);
2857            assert_eq!(
2858                state.get_reg(&Reg::R0).simplify().as_i64(),
2859                Some(0x12345678),
2860                "ROR by 0"
2861            );
2862
2863            // Test ROR by 32 (full rotation, back to original)
2864            let ror_op_32 = ArmOp::Ror {
2865                rd: Reg::R0,
2866                rn: Reg::R1,
2867                shift: 32,
2868            };
2869            encoder.encode_op(&ror_op_32, &mut state);
2870            assert_eq!(
2871                state.get_reg(&Reg::R0).simplify().as_i64(),
2872                Some(0x12345678),
2873                "ROR by 32"
2874            );
2875
2876            // Test ROR by 4 (nibble rotation)
2877            state.set_reg(&Reg::R1, BV::from_u64(0xABCDEF01, 32));
2878            let ror_op_4 = ArmOp::Ror {
2879                rd: Reg::R0,
2880                rn: Reg::R1,
2881                shift: 4,
2882            };
2883            encoder.encode_op(&ror_op_4, &mut state);
2884            // 0xABCDEF01 ROR 4 = 0x1ABCDEF0
2885            assert_eq!(
2886                state.get_reg(&Reg::R0).simplify().as_i64(),
2887                Some(0x1ABCDEF0),
2888                "ROR by 4"
2889            );
2890
2891            // Test ROR with 1-bit rotation
2892            state.set_reg(&Reg::R1, BV::from_u64(0x80000001, 32));
2893            let ror_op_1 = ArmOp::Ror {
2894                rd: Reg::R0,
2895                rn: Reg::R1,
2896                shift: 1,
2897            };
2898            encoder.encode_op(&ror_op_1, &mut state);
2899            // 0x80000001 ROR 1 = 0xC0000000
2900            let result = state.get_reg(&Reg::R0).simplify().as_i64();
2901            let signed_result = result.map(|v| (v as i32) as i64);
2902            assert_eq!(
2903                signed_result,
2904                Some(0xC0000000_u32 as i32 as i64),
2905                "ROR by 1"
2906            );
2907        });
2908    }
2909
2910    #[test]
2911    fn test_arm_clz_comprehensive() {
2912        with_verification_context(|| {
2913            let encoder = ArmSemantics::new();
2914            let mut state = ArmState::new_symbolic();
2915
2916            // Test CLZ(0) = 32
2917            state.set_reg(&Reg::R1, BV::from_i64(0, 32));
2918            let clz_op = ArmOp::Clz {
2919                rd: Reg::R0,
2920                rm: Reg::R1,
2921            };
2922            encoder.encode_op(&clz_op, &mut state);
2923            assert_eq!(
2924                state.get_reg(&Reg::R0).simplify().as_i64(),
2925                Some(32),
2926                "CLZ(0) should be 32"
2927            );
2928
2929            // Test CLZ(1) = 31
2930            state.set_reg(&Reg::R1, BV::from_i64(1, 32));
2931            encoder.encode_op(&clz_op, &mut state);
2932            assert_eq!(
2933                state.get_reg(&Reg::R0).simplify().as_i64(),
2934                Some(31),
2935                "CLZ(1) should be 31"
2936            );
2937
2938            // Test CLZ(0x80000000) = 0
2939            state.set_reg(&Reg::R1, BV::from_u64(0x80000000, 32));
2940            encoder.encode_op(&clz_op, &mut state);
2941            assert_eq!(
2942                state.get_reg(&Reg::R0).simplify().as_i64(),
2943                Some(0),
2944                "CLZ(0x80000000) should be 0"
2945            );
2946
2947            // Test CLZ(0x00FF0000) = 8
2948            state.set_reg(&Reg::R1, BV::from_u64(0x00FF0000, 32));
2949            encoder.encode_op(&clz_op, &mut state);
2950            assert_eq!(
2951                state.get_reg(&Reg::R0).simplify().as_i64(),
2952                Some(8),
2953                "CLZ(0x00FF0000) should be 8"
2954            );
2955
2956            // Test CLZ(0x00001000) = 19
2957            state.set_reg(&Reg::R1, BV::from_u64(0x00001000, 32));
2958            encoder.encode_op(&clz_op, &mut state);
2959            assert_eq!(
2960                state.get_reg(&Reg::R0).simplify().as_i64(),
2961                Some(19),
2962                "CLZ(0x00001000) should be 19"
2963            );
2964
2965            // Test CLZ(0xFFFFFFFF) = 0
2966            state.set_reg(&Reg::R1, BV::from_u64(0xFFFFFFFF, 32));
2967            encoder.encode_op(&clz_op, &mut state);
2968            assert_eq!(
2969                state.get_reg(&Reg::R0).simplify().as_i64(),
2970                Some(0),
2971                "CLZ(0xFFFFFFFF) should be 0"
2972            );
2973        });
2974    }
2975
2976    #[test]
2977    fn test_arm_rbit_comprehensive() {
2978        with_verification_context(|| {
2979            let encoder = ArmSemantics::new();
2980            let mut state = ArmState::new_symbolic();
2981
2982            let rbit_op = ArmOp::Rbit {
2983                rd: Reg::R0,
2984                rm: Reg::R1,
2985            };
2986
2987            // Test RBIT(0) = 0
2988            state.set_reg(&Reg::R1, BV::from_i64(0, 32));
2989            encoder.encode_op(&rbit_op, &mut state);
2990            assert_eq!(
2991                state.get_reg(&Reg::R0).simplify().as_i64(),
2992                Some(0),
2993                "RBIT(0) should be 0"
2994            );
2995
2996            // Test RBIT(1) = 0x80000000 (bit 0 → bit 31)
2997            state.set_reg(&Reg::R1, BV::from_i64(1, 32));
2998            encoder.encode_op(&rbit_op, &mut state);
2999            assert_eq!(
3000                state.get_reg(&Reg::R0).simplify().as_u64(),
3001                Some(0x80000000),
3002                "RBIT(1) should be 0x80000000"
3003            );
3004
3005            // Test RBIT(0x80000000) = 1 (bit 31 → bit 0)
3006            state.set_reg(&Reg::R1, BV::from_u64(0x80000000, 32));
3007            encoder.encode_op(&rbit_op, &mut state);
3008            assert_eq!(
3009                state.get_reg(&Reg::R0).simplify().as_i64(),
3010                Some(1),
3011                "RBIT(0x80000000) should be 1"
3012            );
3013
3014            // Test RBIT(0xFF000000) = 0x000000FF (top byte → bottom byte)
3015            state.set_reg(&Reg::R1, BV::from_u64(0xFF000000, 32));
3016            encoder.encode_op(&rbit_op, &mut state);
3017            assert_eq!(
3018                state.get_reg(&Reg::R0).simplify().as_u64(),
3019                Some(0x000000FF),
3020                "RBIT(0xFF000000) should be 0x000000FF"
3021            );
3022
3023            // Test RBIT(0x12345678) - specific pattern
3024            state.set_reg(&Reg::R1, BV::from_u64(0x12345678, 32));
3025            encoder.encode_op(&rbit_op, &mut state);
3026            // 0x12345678 reversed = 0x1E6A2C48
3027            assert_eq!(
3028                state.get_reg(&Reg::R0).simplify().as_u64(),
3029                Some(0x1E6A2C48),
3030                "RBIT(0x12345678) should be 0x1E6A2C48"
3031            );
3032
3033            // Test RBIT(0xFFFFFFFF) = 0xFFFFFFFF (all bits stay)
3034            state.set_reg(&Reg::R1, BV::from_u64(0xFFFFFFFF, 32));
3035            encoder.encode_op(&rbit_op, &mut state);
3036            assert_eq!(
3037                state.get_reg(&Reg::R0).simplify().as_u64(),
3038                Some(0xFFFFFFFF),
3039                "RBIT(0xFFFFFFFF) should be 0xFFFFFFFF"
3040            );
3041        });
3042    }
3043
3044    #[test]
3045    fn test_arm_cmp_flags() {
3046        // Test CMP instruction and condition flag updates
3047
3048        with_verification_context(|| {
3049            let encoder = ArmSemantics::new();
3050            let mut state = ArmState::new_symbolic();
3051
3052            // Test 1: CMP with equal values (10 - 10 = 0)
3053            // Should set: Z=1, N=0, C=1 (no borrow), V=0
3054            state.set_reg(&Reg::R0, BV::from_i64(10, 32));
3055            state.set_reg(&Reg::R1, BV::from_i64(10, 32));
3056
3057            let cmp_op = ArmOp::Cmp {
3058                rn: Reg::R0,
3059                op2: Operand2::Reg(Reg::R1),
3060            };
3061            encoder.encode_op(&cmp_op, &mut state);
3062
3063            assert_eq!(
3064                state.flags.z.simplify().as_bool(),
3065                Some(true),
3066                "Z flag should be set (equal)"
3067            );
3068            assert_eq!(
3069                state.flags.n.simplify().as_bool(),
3070                Some(false),
3071                "N flag should be clear (non-negative)"
3072            );
3073            assert_eq!(
3074                state.flags.c.simplify().as_bool(),
3075                Some(true),
3076                "C flag should be set (no borrow)"
3077            );
3078            assert_eq!(
3079                state.flags.v.simplify().as_bool(),
3080                Some(false),
3081                "V flag should be clear (no overflow)"
3082            );
3083
3084            // Test 2: CMP with first > second (20 - 10 = 10)
3085            // Should set: Z=0, N=0, C=1 (no borrow), V=0
3086            state.set_reg(&Reg::R0, BV::from_i64(20, 32));
3087            state.set_reg(&Reg::R1, BV::from_i64(10, 32));
3088            encoder.encode_op(&cmp_op, &mut state);
3089
3090            assert_eq!(
3091                state.flags.z.simplify().as_bool(),
3092                Some(false),
3093                "Z flag should be clear (not equal)"
3094            );
3095            assert_eq!(
3096                state.flags.n.simplify().as_bool(),
3097                Some(false),
3098                "N flag should be clear (positive result)"
3099            );
3100            assert_eq!(
3101                state.flags.c.simplify().as_bool(),
3102                Some(true),
3103                "C flag should be set (no borrow)"
3104            );
3105            assert_eq!(
3106                state.flags.v.simplify().as_bool(),
3107                Some(false),
3108                "V flag should be clear (no overflow)"
3109            );
3110
3111            // Test 3: CMP with first < second (unsigned: will wrap)
3112            // 10 - 20 = -10 (0xFFFFFFF6 in two's complement)
3113            // Should set: Z=0, N=1 (negative), C=0 (borrow), V=0
3114            state.set_reg(&Reg::R0, BV::from_i64(10, 32));
3115            state.set_reg(&Reg::R1, BV::from_i64(20, 32));
3116            encoder.encode_op(&cmp_op, &mut state);
3117
3118            assert_eq!(
3119                state.flags.z.simplify().as_bool(),
3120                Some(false),
3121                "Z flag should be clear"
3122            );
3123            assert_eq!(
3124                state.flags.n.simplify().as_bool(),
3125                Some(true),
3126                "N flag should be set (negative result)"
3127            );
3128            assert_eq!(
3129                state.flags.c.simplify().as_bool(),
3130                Some(false),
3131                "C flag should be clear (borrow occurred)"
3132            );
3133            assert_eq!(
3134                state.flags.v.simplify().as_bool(),
3135                Some(false),
3136                "V flag should be clear"
3137            );
3138
3139            // Test 4: Signed overflow case
3140            // Subtracting large negative from positive should overflow
3141            // 0x7FFFFFFF (max positive) - 0x80000000 (min negative)
3142            // Result wraps to negative, but mathematically should be huge positive
3143            state.set_reg(&Reg::R0, BV::from_i64(0x7FFFFFFF, 32));
3144            state.set_reg(&Reg::R1, BV::from_i64(-2147483648i64, 32)); // 0x80000000
3145            encoder.encode_op(&cmp_op, &mut state);
3146
3147            assert_eq!(
3148                state.flags.z.simplify().as_bool(),
3149                Some(false),
3150                "Z flag should be clear"
3151            );
3152            assert_eq!(
3153                state.flags.n.simplify().as_bool(),
3154                Some(true),
3155                "N flag should be set (wrapped result)"
3156            );
3157            assert_eq!(
3158                state.flags.c.simplify().as_bool(),
3159                Some(false),
3160                "C flag should be clear"
3161            );
3162            assert_eq!(
3163                state.flags.v.simplify().as_bool(),
3164                Some(true),
3165                "V flag should be set (overflow)"
3166            );
3167
3168            // Test 5: Zero comparison
3169            state.set_reg(&Reg::R0, BV::from_i64(0, 32));
3170            state.set_reg(&Reg::R1, BV::from_i64(0, 32));
3171            encoder.encode_op(&cmp_op, &mut state);
3172
3173            assert_eq!(
3174                state.flags.z.simplify().as_bool(),
3175                Some(true),
3176                "Z flag should be set (0 - 0 = 0)"
3177            );
3178            assert_eq!(
3179                state.flags.n.simplify().as_bool(),
3180                Some(false),
3181                "N flag should be clear"
3182            );
3183            assert_eq!(
3184                state.flags.c.simplify().as_bool(),
3185                Some(true),
3186                "C flag should be set"
3187            );
3188            assert_eq!(
3189                state.flags.v.simplify().as_bool(),
3190                Some(false),
3191                "V flag should be clear"
3192            );
3193        });
3194    }
3195
3196    #[test]
3197    fn test_arm_flags_all_combinations() {
3198        // Test that flags correctly distinguish all comparison outcomes
3199
3200        with_verification_context(|| {
3201            let encoder = ArmSemantics::new();
3202            let mut state = ArmState::new_symbolic();
3203
3204            let cmp_op = ArmOp::Cmp {
3205                rn: Reg::R0,
3206                op2: Operand2::Reg(Reg::R1),
3207            };
3208
3209            // Test signed comparisons using flags
3210            // For signed comparison A vs B (after CMP A, B):
3211            // - EQ (equal): Z=1
3212            // - NE (not equal): Z=0
3213            // - LT (less than): N != V
3214            // - LE (less or equal): Z=1 OR (N != V)
3215            // - GT (greater than): Z=0 AND (N == V)
3216            // - GE (greater or equal): N == V
3217
3218            // Case: 5 compared to 10 (5 < 10)
3219            state.set_reg(&Reg::R0, BV::from_i64(5, 32));
3220            state.set_reg(&Reg::R1, BV::from_i64(10, 32));
3221            encoder.encode_op(&cmp_op, &mut state);
3222
3223            let n = state.flags.n.simplify().as_bool().unwrap();
3224            let z = state.flags.z.simplify().as_bool().unwrap();
3225            let v = state.flags.v.simplify().as_bool().unwrap();
3226
3227            assert!(!z, "Not equal");
3228            assert!(n != v, "5 < 10 signed (N != V)");
3229
3230            // Case: -5 compared to 10 (-5 < 10)
3231            state.set_reg(&Reg::R0, BV::from_i64(-5, 32));
3232            state.set_reg(&Reg::R1, BV::from_i64(10, 32));
3233            encoder.encode_op(&cmp_op, &mut state);
3234
3235            let n = state.flags.n.simplify().as_bool().unwrap();
3236            let v = state.flags.v.simplify().as_bool().unwrap();
3237            assert!(n != v, "-5 < 10 signed (N != V)");
3238        });
3239    }
3240
3241    #[test]
3242    fn test_arm_setcond_eq() {
3243        with_verification_context(|| {
3244            let encoder = ArmSemantics::new();
3245            let mut state = ArmState::new_symbolic();
3246
3247            // Test EQ condition: 10 == 10
3248            state.set_reg(&Reg::R0, BV::from_i64(10, 32));
3249            state.set_reg(&Reg::R1, BV::from_i64(10, 32));
3250
3251            // CMP R0, R1 (sets Z=1 since equal)
3252            let cmp_op = ArmOp::Cmp {
3253                rn: Reg::R0,
3254                op2: Operand2::Reg(Reg::R1),
3255            };
3256            encoder.encode_op(&cmp_op, &mut state);
3257
3258            // SetCond R0, EQ (should set R0 = 1)
3259            let setcond_op = ArmOp::SetCond {
3260                rd: Reg::R0,
3261                cond: synth_synthesis::Condition::EQ,
3262            };
3263            encoder.encode_op(&setcond_op, &mut state);
3264
3265            assert_eq!(
3266                state.get_reg(&Reg::R0).simplify().as_i64(),
3267                Some(1),
3268                "EQ condition (10 == 10) should return 1"
3269            );
3270
3271            // Test NE condition: 10 != 5
3272            state.set_reg(&Reg::R0, BV::from_i64(10, 32));
3273            state.set_reg(&Reg::R1, BV::from_i64(5, 32));
3274
3275            encoder.encode_op(&cmp_op, &mut state);
3276
3277            let setcond_ne = ArmOp::SetCond {
3278                rd: Reg::R0,
3279                cond: synth_synthesis::Condition::NE,
3280            };
3281            encoder.encode_op(&setcond_ne, &mut state);
3282
3283            assert_eq!(
3284                state.get_reg(&Reg::R0).simplify().as_i64(),
3285                Some(1),
3286                "NE condition (10 != 5) should return 1"
3287            );
3288        });
3289    }
3290
3291    #[test]
3292    fn test_arm_setcond_signed() {
3293        with_verification_context(|| {
3294            let encoder = ArmSemantics::new();
3295            let mut state = ArmState::new_symbolic();
3296
3297            // Test LT signed: 5 < 10
3298            state.set_reg(&Reg::R0, BV::from_i64(5, 32));
3299            state.set_reg(&Reg::R1, BV::from_i64(10, 32));
3300
3301            let cmp_op = ArmOp::Cmp {
3302                rn: Reg::R0,
3303                op2: Operand2::Reg(Reg::R1),
3304            };
3305            encoder.encode_op(&cmp_op, &mut state);
3306
3307            let setcond_lt = ArmOp::SetCond {
3308                rd: Reg::R0,
3309                cond: synth_synthesis::Condition::LT,
3310            };
3311            encoder.encode_op(&setcond_lt, &mut state);
3312
3313            assert_eq!(
3314                state.get_reg(&Reg::R0).simplify().as_i64(),
3315                Some(1),
3316                "LT signed (5 < 10) should return 1"
3317            );
3318
3319            // Test GE signed: 10 >= 5
3320            state.set_reg(&Reg::R0, BV::from_i64(10, 32));
3321            state.set_reg(&Reg::R1, BV::from_i64(5, 32));
3322
3323            encoder.encode_op(&cmp_op, &mut state);
3324
3325            let setcond_ge = ArmOp::SetCond {
3326                rd: Reg::R0,
3327                cond: synth_synthesis::Condition::GE,
3328            };
3329            encoder.encode_op(&setcond_ge, &mut state);
3330
3331            assert_eq!(
3332                state.get_reg(&Reg::R0).simplify().as_i64(),
3333                Some(1),
3334                "GE signed (10 >= 5) should return 1"
3335            );
3336
3337            // Test GT signed: 10 > 5
3338            state.set_reg(&Reg::R0, BV::from_i64(10, 32));
3339            state.set_reg(&Reg::R1, BV::from_i64(5, 32));
3340
3341            encoder.encode_op(&cmp_op, &mut state);
3342
3343            let setcond_gt = ArmOp::SetCond {
3344                rd: Reg::R0,
3345                cond: synth_synthesis::Condition::GT,
3346            };
3347            encoder.encode_op(&setcond_gt, &mut state);
3348
3349            assert_eq!(
3350                state.get_reg(&Reg::R0).simplify().as_i64(),
3351                Some(1),
3352                "GT signed (10 > 5) should return 1"
3353            );
3354
3355            // Test LE signed: 5 <= 10
3356            state.set_reg(&Reg::R0, BV::from_i64(5, 32));
3357            state.set_reg(&Reg::R1, BV::from_i64(10, 32));
3358
3359            encoder.encode_op(&cmp_op, &mut state);
3360
3361            let setcond_le = ArmOp::SetCond {
3362                rd: Reg::R0,
3363                cond: synth_synthesis::Condition::LE,
3364            };
3365            encoder.encode_op(&setcond_le, &mut state);
3366
3367            assert_eq!(
3368                state.get_reg(&Reg::R0).simplify().as_i64(),
3369                Some(1),
3370                "LE signed (5 <= 10) should return 1"
3371            );
3372        });
3373    }
3374
3375    #[test]
3376    fn test_arm_setcond_unsigned() {
3377        with_verification_context(|| {
3378            let encoder = ArmSemantics::new();
3379            let mut state = ArmState::new_symbolic();
3380
3381            // Test LO unsigned: 5 < 10
3382            state.set_reg(&Reg::R0, BV::from_i64(5, 32));
3383            state.set_reg(&Reg::R1, BV::from_i64(10, 32));
3384
3385            let cmp_op = ArmOp::Cmp {
3386                rn: Reg::R0,
3387                op2: Operand2::Reg(Reg::R1),
3388            };
3389            encoder.encode_op(&cmp_op, &mut state);
3390
3391            let setcond_lo = ArmOp::SetCond {
3392                rd: Reg::R0,
3393                cond: synth_synthesis::Condition::LO,
3394            };
3395            encoder.encode_op(&setcond_lo, &mut state);
3396
3397            assert_eq!(
3398                state.get_reg(&Reg::R0).simplify().as_i64(),
3399                Some(1),
3400                "LO unsigned (5 < 10) should return 1"
3401            );
3402
3403            // Test HS unsigned: 10 >= 5
3404            state.set_reg(&Reg::R0, BV::from_i64(10, 32));
3405            state.set_reg(&Reg::R1, BV::from_i64(5, 32));
3406
3407            encoder.encode_op(&cmp_op, &mut state);
3408
3409            let setcond_hs = ArmOp::SetCond {
3410                rd: Reg::R0,
3411                cond: synth_synthesis::Condition::HS,
3412            };
3413            encoder.encode_op(&setcond_hs, &mut state);
3414
3415            assert_eq!(
3416                state.get_reg(&Reg::R0).simplify().as_i64(),
3417                Some(1),
3418                "HS unsigned (10 >= 5) should return 1"
3419            );
3420
3421            // Test HI unsigned: 10 > 5
3422            state.set_reg(&Reg::R0, BV::from_i64(10, 32));
3423            state.set_reg(&Reg::R1, BV::from_i64(5, 32));
3424
3425            encoder.encode_op(&cmp_op, &mut state);
3426
3427            let setcond_hi = ArmOp::SetCond {
3428                rd: Reg::R0,
3429                cond: synth_synthesis::Condition::HI,
3430            };
3431            encoder.encode_op(&setcond_hi, &mut state);
3432
3433            assert_eq!(
3434                state.get_reg(&Reg::R0).simplify().as_i64(),
3435                Some(1),
3436                "HI unsigned (10 > 5) should return 1"
3437            );
3438
3439            // Test LS unsigned: 5 <= 10
3440            state.set_reg(&Reg::R0, BV::from_i64(5, 32));
3441            state.set_reg(&Reg::R1, BV::from_i64(10, 32));
3442
3443            encoder.encode_op(&cmp_op, &mut state);
3444
3445            let setcond_ls = ArmOp::SetCond {
3446                rd: Reg::R0,
3447                cond: synth_synthesis::Condition::LS,
3448            };
3449            encoder.encode_op(&setcond_ls, &mut state);
3450
3451            assert_eq!(
3452                state.get_reg(&Reg::R0).simplify().as_i64(),
3453                Some(1),
3454                "LS unsigned (5 <= 10) should return 1"
3455            );
3456        });
3457    }
3458}