Skip to main content

synth_backend/
arm_encoder.rs

1//! ARM Code Encoder - Converts ARM instructions to binary machine code
2//!
3//! Generates ARM32/Thumb-2 machine code from ARM instruction structures
4
5use synth_core::Result;
6use synth_core::target::FPUPrecision;
7use synth_synthesis::contracts::encoding as encoding_contracts;
8use synth_synthesis::{ArmOp, MemAddr, MveSize, Operand2, QReg, Reg, VfpReg};
9
10/// ARM instruction encoding
11pub struct ArmEncoder {
12    /// Use Thumb mode (vs ARM mode)
13    thumb_mode: bool,
14    /// FPU capability for VFP instruction encoding
15    #[allow(dead_code)]
16    fpu: Option<FPUPrecision>,
17}
18
19impl ArmEncoder {
20    /// Create a new ARM encoder in ARM32 mode
21    pub fn new_arm32() -> Self {
22        Self {
23            thumb_mode: false,
24            fpu: None,
25        }
26    }
27
28    /// Create a new ARM encoder in Thumb-2 mode
29    pub fn new_thumb2() -> Self {
30        Self {
31            thumb_mode: true,
32            fpu: None,
33        }
34    }
35
36    /// Create a new Thumb-2 encoder with FPU capability
37    pub fn new_thumb2_with_fpu(fpu: Option<FPUPrecision>) -> Self {
38        Self {
39            thumb_mode: true,
40            fpu,
41        }
42    }
43
44    /// Encode a single ARM instruction to bytes
45    pub fn encode(&self, op: &ArmOp) -> Result<Vec<u8>> {
46        if self.thumb_mode {
47            self.encode_thumb(op)
48        } else {
49            self.encode_arm(op)
50        }
51    }
52
53    /// Encode an ARM instruction in ARM32 mode (32-bit instructions)
54    fn encode_arm(&self, op: &ArmOp) -> Result<Vec<u8>> {
55        let instr: u32 = match op {
56            // Data processing instructions
57            ArmOp::Add { rd, rn, op2 } => {
58                let rd_bits = reg_to_bits(rd);
59                let rn_bits = reg_to_bits(rn);
60                let (op2_bits, i_flag) = encode_operand2(op2);
61
62                // ADD encoding: cond(4) | 00 | I(1) | 0100 | S(1) | Rn(4) | Rd(4) | operand2(12)
63                0xE0800000 // condition=always(E), opcode=ADD(0100), S=0
64                    | (i_flag << 25)
65                    | (rn_bits << 16)
66                    | (rd_bits << 12)
67                    | op2_bits
68            }
69
70            ArmOp::Sub { rd, rn, op2 } => {
71                let rd_bits = reg_to_bits(rd);
72                let rn_bits = reg_to_bits(rn);
73                let (op2_bits, i_flag) = encode_operand2(op2);
74
75                // SUB encoding: opcode=0010
76                0xE0400000 | (i_flag << 25) | (rn_bits << 16) | (rd_bits << 12) | op2_bits
77            }
78
79            // i64 support: ADDS, ADC, SUBS, SBC for ARM32
80            ArmOp::Adds { rd, rn, op2 } => {
81                let rd_bits = reg_to_bits(rd);
82                let rn_bits = reg_to_bits(rn);
83                let (op2_bits, i_flag) = encode_operand2(op2);
84
85                // ADDS encoding: opcode=0100, S=1
86                0xE0900000 | (i_flag << 25) | (rn_bits << 16) | (rd_bits << 12) | op2_bits
87            }
88
89            ArmOp::Adc { rd, rn, op2 } => {
90                let rd_bits = reg_to_bits(rd);
91                let rn_bits = reg_to_bits(rn);
92                let (op2_bits, i_flag) = encode_operand2(op2);
93
94                // ADC encoding: opcode=0101
95                0xE0A00000 | (i_flag << 25) | (rn_bits << 16) | (rd_bits << 12) | op2_bits
96            }
97
98            ArmOp::Subs { rd, rn, op2 } => {
99                let rd_bits = reg_to_bits(rd);
100                let rn_bits = reg_to_bits(rn);
101                let (op2_bits, i_flag) = encode_operand2(op2);
102
103                // SUBS encoding: opcode=0010, S=1
104                0xE0500000 | (i_flag << 25) | (rn_bits << 16) | (rd_bits << 12) | op2_bits
105            }
106
107            ArmOp::Sbc { rd, rn, op2 } => {
108                let rd_bits = reg_to_bits(rd);
109                let rn_bits = reg_to_bits(rn);
110                let (op2_bits, i_flag) = encode_operand2(op2);
111
112                // SBC encoding: opcode=0110
113                0xE0C00000 | (i_flag << 25) | (rn_bits << 16) | (rd_bits << 12) | op2_bits
114            }
115
116            ArmOp::Mul { rd, rn, rm } => {
117                let rd_bits = reg_to_bits(rd);
118                let rn_bits = reg_to_bits(rn);
119                let rm_bits = reg_to_bits(rm);
120
121                // MUL encoding: cond(4) | 000000 | A(1) | S(1) | Rd(4) | Rn(4) | Rs(4) | 1001 | Rm(4)
122                0xE0000090 | (rd_bits << 16) | (rn_bits << 8) | rm_bits
123            }
124
125            ArmOp::Sdiv { rd, rn, rm } => {
126                let rd_bits = reg_to_bits(rd);
127                let rn_bits = reg_to_bits(rn);
128                let rm_bits = reg_to_bits(rm);
129
130                // SDIV encoding: cond(4) | 01110001 | Rd(4) | 1111 | Rm(4) | 0001 | Rn(4)
131                // ARMv7-M and above
132                0xE710F010 | (rd_bits << 16) | (rm_bits << 8) | rn_bits
133            }
134
135            ArmOp::Udiv { rd, rn, rm } => {
136                let rd_bits = reg_to_bits(rd);
137                let rn_bits = reg_to_bits(rn);
138                let rm_bits = reg_to_bits(rm);
139
140                // UDIV encoding: cond(4) | 01110011 | Rd(4) | 1111 | Rm(4) | 0001 | Rn(4)
141                // ARMv7-M and above
142                0xE730F010 | (rd_bits << 16) | (rm_bits << 8) | rn_bits
143            }
144
145            ArmOp::Mls { rd, rn, rm, ra } => {
146                let rd_bits = reg_to_bits(rd);
147                let rn_bits = reg_to_bits(rn);
148                let rm_bits = reg_to_bits(rm);
149                let ra_bits = reg_to_bits(ra);
150
151                // MLS encoding: cond(4) | 00000110 | Rd(4) | Ra(4) | Rm(4) | 1001 | Rn(4)
152                // Rd = Ra - (Rn * Rm)
153                0xE0600090 | (rd_bits << 16) | (ra_bits << 12) | (rm_bits << 8) | rn_bits
154            }
155
156            ArmOp::And { rd, rn, op2 } => {
157                let rd_bits = reg_to_bits(rd);
158                let rn_bits = reg_to_bits(rn);
159                let (op2_bits, i_flag) = encode_operand2(op2);
160
161                // AND encoding: opcode=0000
162                0xE0000000 | (i_flag << 25) | (rn_bits << 16) | (rd_bits << 12) | op2_bits
163            }
164
165            ArmOp::Orr { rd, rn, op2 } => {
166                let rd_bits = reg_to_bits(rd);
167                let rn_bits = reg_to_bits(rn);
168                let (op2_bits, i_flag) = encode_operand2(op2);
169
170                // ORR encoding: opcode=1100
171                0xE1800000 | (i_flag << 25) | (rn_bits << 16) | (rd_bits << 12) | op2_bits
172            }
173
174            ArmOp::Eor { rd, rn, op2 } => {
175                let rd_bits = reg_to_bits(rd);
176                let rn_bits = reg_to_bits(rn);
177                let (op2_bits, i_flag) = encode_operand2(op2);
178
179                // EOR encoding: opcode=0001
180                0xE0200000 | (i_flag << 25) | (rn_bits << 16) | (rd_bits << 12) | op2_bits
181            }
182
183            // Shift instructions
184            ArmOp::Lsl { rd, rn, shift } => {
185                let rd_bits = reg_to_bits(rd);
186                let rn_bits = reg_to_bits(rn);
187                let shift_bits = *shift & 0x1F;
188
189                // LSL encoding: MOV with shift
190                0xE1A00000 | (rd_bits << 12) | (shift_bits << 7) | rn_bits
191            }
192
193            ArmOp::Lsr { rd, rn, shift } => {
194                let rd_bits = reg_to_bits(rd);
195                let rn_bits = reg_to_bits(rn);
196                let shift_bits = *shift & 0x1F;
197
198                // LSR encoding
199                0xE1A00020 | (rd_bits << 12) | (shift_bits << 7) | rn_bits
200            }
201
202            ArmOp::Asr { rd, rn, shift } => {
203                let rd_bits = reg_to_bits(rd);
204                let rn_bits = reg_to_bits(rn);
205                let shift_bits = *shift & 0x1F;
206
207                // ASR encoding
208                0xE1A00040 | (rd_bits << 12) | (shift_bits << 7) | rn_bits
209            }
210
211            ArmOp::Ror { rd, rn, shift } => {
212                let rd_bits = reg_to_bits(rd);
213                let rn_bits = reg_to_bits(rn);
214                let shift_bits = *shift & 0x1F;
215
216                // ROR encoding: MOV with ROR shift
217                0xE1A00060 | (rd_bits << 12) | (shift_bits << 7) | rn_bits
218            }
219
220            // Register-based shifts (ARM32)
221            // LSL Rd, Rn, Rm: cond 0001101S 0000 Rd Rs 0001 Rn
222            ArmOp::LslReg { rd, rn, rm } => {
223                let rd_bits = reg_to_bits(rd);
224                let rn_bits = reg_to_bits(rn);
225                let rm_bits = reg_to_bits(rm);
226                0xE1A00010 | (rd_bits << 12) | (rm_bits << 8) | rn_bits
227            }
228            ArmOp::LsrReg { rd, rn, rm } => {
229                let rd_bits = reg_to_bits(rd);
230                let rn_bits = reg_to_bits(rn);
231                let rm_bits = reg_to_bits(rm);
232                0xE1A00030 | (rd_bits << 12) | (rm_bits << 8) | rn_bits
233            }
234            ArmOp::AsrReg { rd, rn, rm } => {
235                let rd_bits = reg_to_bits(rd);
236                let rn_bits = reg_to_bits(rn);
237                let rm_bits = reg_to_bits(rm);
238                0xE1A00050 | (rd_bits << 12) | (rm_bits << 8) | rn_bits
239            }
240            ArmOp::RorReg { rd, rn, rm } => {
241                let rd_bits = reg_to_bits(rd);
242                let rn_bits = reg_to_bits(rn);
243                let rm_bits = reg_to_bits(rm);
244                0xE1A00070 | (rd_bits << 12) | (rm_bits << 8) | rn_bits
245            }
246
247            // RSB (Reverse Subtract): Rd = imm - Rn
248            ArmOp::Rsb { rd, rn, imm } => {
249                let rd_bits = reg_to_bits(rd);
250                let rn_bits = reg_to_bits(rn);
251                // RSB encoding: cond(4) | 00 1 0011 S | Rn(4) | Rd(4) | imm12
252                // Opcode for RSB = 0011, I=1 (immediate), S=0
253                0xE2600000 | (rn_bits << 16) | (rd_bits << 12) | (*imm & 0xFF)
254            }
255
256            // Bit manipulation instructions
257            ArmOp::Clz { rd, rm } => {
258                let rd_bits = reg_to_bits(rd);
259                let rm_bits = reg_to_bits(rm);
260
261                // CLZ encoding: cond(4) | 00010110 | 1111 | Rd(4) | 1111 | 0001 | Rm(4)
262                // ARMv5T and above
263                0xE16F0F10 | (rd_bits << 12) | rm_bits
264            }
265
266            ArmOp::Rbit { rd, rm } => {
267                let rd_bits = reg_to_bits(rd);
268                let rm_bits = reg_to_bits(rm);
269
270                // RBIT encoding: cond(4) | 01101111 | 1111 | Rd(4) | 1111 | 0011 | Rm(4)
271                // ARMv6T2 and above
272                0xE6FF0F30 | (rd_bits << 12) | rm_bits
273            }
274
275            ArmOp::Sxtb { rd, rm } => {
276                let rd_bits = reg_to_bits(rd);
277                let rm_bits = reg_to_bits(rm);
278
279                // SXTB encoding: cond(4) | 01101010 | 1111 | Rd(4) | rotate(2) | 00 | 0111 | Rm(4)
280                // ARMv6 and above. rotate=00 for no rotation
281                0xE6AF0070 | (rd_bits << 12) | rm_bits
282            }
283
284            ArmOp::Sxth { rd, rm } => {
285                let rd_bits = reg_to_bits(rd);
286                let rm_bits = reg_to_bits(rm);
287
288                // SXTH encoding: cond(4) | 01101011 | 1111 | Rd(4) | rotate(2) | 00 | 0111 | Rm(4)
289                // ARMv6 and above. rotate=00 for no rotation
290                0xE6BF0070 | (rd_bits << 12) | rm_bits
291            }
292
293            // Move instructions
294            ArmOp::Mov { rd, op2 } => {
295                let rd_bits = reg_to_bits(rd);
296                let (op2_bits, i_flag) = encode_operand2(op2);
297
298                // MOV encoding: opcode=1101
299                0xE1A00000 | (i_flag << 25) | (rd_bits << 12) | op2_bits
300            }
301
302            ArmOp::Mvn { rd, op2 } => {
303                let rd_bits = reg_to_bits(rd);
304                let (op2_bits, i_flag) = encode_operand2(op2);
305
306                // MVN encoding: opcode=1111
307                0xE1E00000 | (i_flag << 25) | (rd_bits << 12) | op2_bits
308            }
309
310            // MOVW - Move Wide (ARM32)
311            // Encoding: cond(4) | 0011 0000 | imm4(4) | Rd(4) | imm12(12)
312            ArmOp::Movw { rd, imm16 } => {
313                let rd_bits = reg_to_bits(rd);
314                let imm4 = ((*imm16 as u32) >> 12) & 0xF;
315                let imm12 = (*imm16 as u32) & 0xFFF;
316                0xE3000000 | (imm4 << 16) | (rd_bits << 12) | imm12
317            }
318
319            // MOVT - Move Top (ARM32)
320            // Encoding: cond(4) | 0011 0100 | imm4(4) | Rd(4) | imm12(12)
321            ArmOp::Movt { rd, imm16 } => {
322                let rd_bits = reg_to_bits(rd);
323                let imm4 = ((*imm16 as u32) >> 12) & 0xF;
324                let imm12 = (*imm16 as u32) & 0xFFF;
325                0xE3400000 | (imm4 << 16) | (rd_bits << 12) | imm12
326            }
327
328            // Compare
329            ArmOp::Cmp { rn, op2 } => {
330                let rn_bits = reg_to_bits(rn);
331                let (op2_bits, i_flag) = encode_operand2(op2);
332
333                // CMP encoding: opcode=1010, S=1
334                0xE1500000 | (i_flag << 25) | (rn_bits << 16) | op2_bits
335            }
336
337            // Compare Negative (CMN) - computes Rn + op2 and sets flags
338            ArmOp::Cmn { rn, op2 } => {
339                let rn_bits = reg_to_bits(rn);
340                let (op2_bits, i_flag) = encode_operand2(op2);
341
342                // CMN encoding: opcode=1011, S=1
343                0xE1700000 | (i_flag << 25) | (rn_bits << 16) | op2_bits
344            }
345
346            // Load/Store
347            ArmOp::Ldr { rd, addr } => {
348                let rd_bits = reg_to_bits(rd);
349                let (base_bits, offset_bits) = encode_mem_addr(addr);
350
351                // LDR encoding: cond(4) | 01 | I(1) | P(1) | U(1) | B(1) | W(1) | L(1) | Rn(4) | Rd(4) | offset(12)
352                // P=1 (pre-indexed), U=1 (add offset), L=1 (load)
353                0xE5900000 | (base_bits << 16) | (rd_bits << 12) | offset_bits
354            }
355
356            ArmOp::Str { rd, addr } => {
357                let rd_bits = reg_to_bits(rd);
358                let (base_bits, offset_bits) = encode_mem_addr(addr);
359
360                // STR encoding: L=0 (store)
361                0xE5800000 | (base_bits << 16) | (rd_bits << 12) | offset_bits
362            }
363
364            // Sub-word loads (ARM32 encoding)
365            ArmOp::Ldrb { rd, addr } => {
366                let rd_bits = reg_to_bits(rd);
367                let (base_bits, offset_bits) = encode_mem_addr(addr);
368                // LDRB: LDR with B=1 (byte): cond|01|I|P|U|1|W|L|Rn|Rd|offset
369                0xE5D00000 | (base_bits << 16) | (rd_bits << 12) | offset_bits
370            }
371
372            ArmOp::Ldrsb { rd, addr } => {
373                let rd_bits = reg_to_bits(rd);
374                let (base_bits, offset_bits) = encode_mem_addr(addr);
375                // LDRSB (misc load): cond|000|P|U|1|W|1|Rn|Rd|imm4H|1101|imm4L
376                // Simplified with immediate offset
377                let offset_val = offset_bits & 0xFF;
378                let imm4h = (offset_val >> 4) & 0xF;
379                let imm4l = offset_val & 0xF;
380                0xE1D000D0 | (base_bits << 16) | (rd_bits << 12) | (imm4h << 8) | imm4l
381            }
382
383            ArmOp::Ldrh { rd, addr } => {
384                let rd_bits = reg_to_bits(rd);
385                let (base_bits, offset_bits) = encode_mem_addr(addr);
386                // LDRH (misc load): cond|000|P|U|1|W|1|Rn|Rd|imm4H|1011|imm4L
387                let offset_val = offset_bits & 0xFF;
388                let imm4h = (offset_val >> 4) & 0xF;
389                let imm4l = offset_val & 0xF;
390                0xE1D000B0 | (base_bits << 16) | (rd_bits << 12) | (imm4h << 8) | imm4l
391            }
392
393            ArmOp::Ldrsh { rd, addr } => {
394                let rd_bits = reg_to_bits(rd);
395                let (base_bits, offset_bits) = encode_mem_addr(addr);
396                // LDRSH (misc load): cond|000|P|U|1|W|1|Rn|Rd|imm4H|1111|imm4L
397                let offset_val = offset_bits & 0xFF;
398                let imm4h = (offset_val >> 4) & 0xF;
399                let imm4l = offset_val & 0xF;
400                0xE1D000F0 | (base_bits << 16) | (rd_bits << 12) | (imm4h << 8) | imm4l
401            }
402
403            // Sub-word stores (ARM32 encoding)
404            ArmOp::Strb { rd, addr } => {
405                let rd_bits = reg_to_bits(rd);
406                let (base_bits, offset_bits) = encode_mem_addr(addr);
407                // STRB: STR with B=1 (byte): cond|01|I|P|U|1|W|0|Rn|Rd|offset
408                0xE5C00000 | (base_bits << 16) | (rd_bits << 12) | offset_bits
409            }
410
411            ArmOp::Strh { rd, addr } => {
412                let rd_bits = reg_to_bits(rd);
413                let (base_bits, offset_bits) = encode_mem_addr(addr);
414                // STRH (misc store): cond|000|P|U|1|W|0|Rn|Rd|imm4H|1011|imm4L
415                let offset_val = offset_bits & 0xFF;
416                let imm4h = (offset_val >> 4) & 0xF;
417                let imm4l = offset_val & 0xF;
418                0xE1C000B0 | (base_bits << 16) | (rd_bits << 12) | (imm4h << 8) | imm4l
419            }
420
421            // Memory management (ARM32 encoding)
422            ArmOp::MemorySize { rd } => {
423                let rd_bits = reg_to_bits(rd);
424                // MOV rd, R10, LSR #16  (memory size in bytes / 65536 = pages)
425                // cond|000|1101|S|0000|Rd|shift5|type|0|Rm
426                // LSR #16: shift5=10000, type=01
427                0xE1A00820 | (rd_bits << 12) | 0x0A // Rm=R10, shift=16, LSR
428            }
429
430            ArmOp::MemoryGrow { rd, .. } => {
431                let rd_bits = reg_to_bits(rd);
432                // On embedded, always fail: MOV rd, #-1
433                0xE3E00000 | (rd_bits << 12) // MVN rd, #0 = MOV rd, #-1
434            }
435
436            // Label pseudo-instruction: emits no machine code
437            ArmOp::Label { .. } => {
438                return Ok(Vec::new());
439            }
440
441            // Branch instructions
442            ArmOp::B { label: _ } => {
443                // B encoding: cond(4) | 1010 | offset(24)
444                // Simplified: branch to offset 0 (will be patched by linker/resolver)
445                0xEA000000
446            }
447
448            // Conditional branch to label (generic)
449            ArmOp::Bcc { cond, label: _ } => {
450                use synth_synthesis::Condition;
451                let cond_bits: u32 = match cond {
452                    Condition::EQ => 0x0,
453                    Condition::NE => 0x1,
454                    Condition::HS => 0x2,
455                    Condition::LO => 0x3,
456                    Condition::HI => 0x8,
457                    Condition::LS => 0x9,
458                    Condition::GE => 0xA,
459                    Condition::LT => 0xB,
460                    Condition::GT => 0xC,
461                    Condition::LE => 0xD,
462                };
463                // B<cond> with offset 0 (will be patched)
464                (cond_bits << 28) | 0x0A000000
465            }
466
467            // BHS (Branch if Higher or Same) - used for bounds checking
468            ArmOp::Bhs { label: _ } => {
469                // BHS encoding: cond(2=HS) | 1010 | offset(24)
470                0x2A000000 // BHS with offset 0
471            }
472
473            // BLO (Branch if Lower) - complementary to BHS
474            ArmOp::Blo { label: _ } => {
475                // BLO encoding: cond(3=LO) | 1010 | offset(24)
476                0x3A000000 // BLO with offset 0
477            }
478
479            // Branch with numeric offset (in instructions)
480            // ARM32 B instruction: offset is in instructions, stored as words
481            // The offset is relative to PC+8 (due to ARM pipeline)
482            ArmOp::BOffset { offset } => {
483                // B encoding: cond(4) | 1010 | offset(24)
484                // Offset is signed, in words (4-byte units)
485                // ARM adds PC+8 to the offset, so we need to adjust:
486                // target = PC + 8 + (offset * 4)
487                // For backward branch of N instructions: offset = -(N + 2)
488                let adjusted_offset = *offset - 2; // Account for PC+8
489                let offset_bits = (adjusted_offset as u32) & 0x00FFFFFF;
490                0xEA000000 | offset_bits
491            }
492
493            // Conditional branch with numeric offset
494            ArmOp::BCondOffset { cond, offset } => {
495                use synth_synthesis::Condition;
496                let cond_bits: u32 = match cond {
497                    Condition::EQ => 0x0,
498                    Condition::NE => 0x1,
499                    Condition::HS => 0x2,
500                    Condition::LO => 0x3,
501                    Condition::HI => 0x8,
502                    Condition::LS => 0x9,
503                    Condition::GE => 0xA,
504                    Condition::LT => 0xB,
505                    Condition::GT => 0xC,
506                    Condition::LE => 0xD,
507                };
508                // B<cond> encoding: cond(4) | 1010 | offset(24)
509                let adjusted_offset = *offset - 2; // Account for PC+8
510                let offset_bits = (adjusted_offset as u32) & 0x00FFFFFF;
511                (cond_bits << 28) | 0x0A000000 | offset_bits
512            }
513
514            ArmOp::Bl { label: _ } => {
515                // BL encoding: cond(4) | 1011 | offset(24)
516                0xEB000000
517            }
518
519            ArmOp::Bx { rm } => {
520                let rm_bits = reg_to_bits(rm);
521
522                // BX encoding: cond(4) | 000100101111111111110001 | Rm(4)
523                0xE12FFF10 | rm_bits
524            }
525
526            ArmOp::Blx { rm } => {
527                let rm_bits = reg_to_bits(rm);
528
529                // BLX (register) encoding: cond(4) | 000100101111111111110011 | Rm(4)
530                0xE12FFF30 | rm_bits
531            }
532
533            ArmOp::Push { regs } => {
534                // STMDB SP!, {regs} encoding: cond(4) | 100100 | 10 | 1101 | register_list(16)
535                let mut reg_list: u32 = 0;
536                for r in regs {
537                    reg_list |= 1 << reg_to_bits(r);
538                }
539                0xE92D0000 | reg_list
540            }
541
542            ArmOp::Pop { regs } => {
543                // LDMIA SP!, {regs} encoding: cond(4) | 100010 | 11 | 1101 | register_list(16)
544                let mut reg_list: u32 = 0;
545                for r in regs {
546                    reg_list |= 1 << reg_to_bits(r);
547                }
548                0xE8BD0000 | reg_list
549            }
550
551            ArmOp::Nop => {
552                // NOP encoding: MOV R0, R0
553                0xE1A00000
554            }
555
556            ArmOp::Udf { imm } => {
557                // UDF (Undefined) encoding in ARM: 0xE7F000F0 | (imm12_hi << 8) | imm4_lo
558                // We only use imm8, so split into imm4_hi and imm4_lo
559                let imm8 = *imm as u32;
560                0xE7F000F0 | ((imm8 & 0xF0) << 4) | (imm8 & 0x0F)
561            }
562
563            // Pseudo-instructions for verification - encode as NOP
564            // These are used in formal verification but not actual code generation
565            ArmOp::Popcnt { .. } => {
566                // Population count pseudo-instruction
567                // Not a real ARM instruction, would be expanded to actual code
568                0xE1A00000 // NOP for now
569            }
570
571            ArmOp::SetCond { .. } => {
572                // Condition evaluation pseudo-instruction
573                // Not a real ARM instruction, would be expanded to actual code
574                0xE1A00000 // NOP for now
575            }
576
577            ArmOp::SelectMove { .. } => {
578                // Conditional move pseudo-instruction for ARM32
579                // Would use MOV{cond} instruction
580                0xE1A00000 // NOP for now
581            }
582
583            ArmOp::Select { .. } => {
584                // Select pseudo-instruction
585                // Not a real ARM instruction, would be expanded to conditional moves
586                0xE1A00000 // NOP for now
587            }
588
589            ArmOp::LocalGet { .. } => {
590                // Local variable get pseudo-instruction
591                // Not a real ARM instruction, would be expanded to memory access
592                0xE1A00000 // NOP for now
593            }
594
595            ArmOp::LocalSet { .. } => {
596                // Local variable set pseudo-instruction
597                // Not a real ARM instruction, would be expanded to memory access
598                0xE1A00000 // NOP for now
599            }
600
601            ArmOp::LocalTee { .. } => {
602                // Local variable tee pseudo-instruction
603                // Not a real ARM instruction, would be expanded to memory access
604                0xE1A00000 // NOP for now
605            }
606
607            ArmOp::GlobalGet { .. } => {
608                // Global variable get pseudo-instruction
609                // Not a real ARM instruction, would be expanded to memory access
610                0xE1A00000 // NOP for now
611            }
612
613            ArmOp::GlobalSet { .. } => {
614                // Global variable set pseudo-instruction
615                // Not a real ARM instruction, would be expanded to memory access
616                0xE1A00000 // NOP for now
617            }
618
619            ArmOp::BrTable { .. } => {
620                // Branch table pseudo-instruction
621                // Not a real ARM instruction, would be expanded to jump table
622                0xE1A00000 // NOP for now
623            }
624
625            ArmOp::Call { .. } => {
626                // Function call pseudo-instruction
627                // Not a real ARM instruction, would be expanded to BL
628                0xE1A00000 // NOP for now
629            }
630
631            ArmOp::CallIndirect { .. } => {
632                // Indirect function call pseudo-instruction
633                // Not a real ARM instruction, would be expanded to indirect branch
634                0xE1A00000 // NOP for now
635            }
636
637            // i64 pseudo-instructions (Phase 2) - encode as NOP for now
638            // Real compiler would expand these to multi-instruction sequences
639            ArmOp::I64Add { .. } => 0xE1A00000,        // NOP
640            ArmOp::I64Sub { .. } => 0xE1A00000,        // NOP
641            ArmOp::I64DivS { .. } => 0xE1A00000,       // NOP
642            ArmOp::I64DivU { .. } => 0xE1A00000,       // NOP
643            ArmOp::I64RemS { .. } => 0xE1A00000,       // NOP
644            ArmOp::I64RemU { .. } => 0xE1A00000,       // NOP
645            ArmOp::I64Clz { .. } => 0xE1A00000,        // NOP
646            ArmOp::I64Ctz { .. } => 0xE1A00000,        // NOP
647            ArmOp::I64Popcnt { .. } => 0xE1A00000,     // NOP
648            ArmOp::I64And { .. } => 0xE1A00000,        // NOP
649            ArmOp::I64Or { .. } => 0xE1A00000,         // NOP
650            ArmOp::I64Xor { .. } => 0xE1A00000,        // NOP
651            ArmOp::I64Eqz { .. } => 0xE1A00000,        // NOP
652            ArmOp::I64Eq { .. } => 0xE1A00000,         // NOP
653            ArmOp::I64Ne { .. } => 0xE1A00000,         // NOP
654            ArmOp::I64LtS { .. } => 0xE1A00000,        // NOP
655            ArmOp::I64LtU { .. } => 0xE1A00000,        // NOP
656            ArmOp::I64LeS { .. } => 0xE1A00000,        // NOP
657            ArmOp::I64LeU { .. } => 0xE1A00000,        // NOP
658            ArmOp::I64GtS { .. } => 0xE1A00000,        // NOP
659            ArmOp::I64GtU { .. } => 0xE1A00000,        // NOP
660            ArmOp::I64GeS { .. } => 0xE1A00000,        // NOP
661            ArmOp::I64GeU { .. } => 0xE1A00000,        // NOP
662            ArmOp::I64Const { .. } => 0xE1A00000,      // NOP
663            ArmOp::I64Ldr { .. } => 0xE1A00000,        // NOP
664            ArmOp::I64Str { .. } => 0xE1A00000,        // NOP
665            ArmOp::I64ExtendI32S { .. } => 0xE1A00000, // NOP
666            ArmOp::I64ExtendI32U { .. } => 0xE1A00000, // NOP
667            ArmOp::I64Extend8S { .. } => 0xE1A00000,   // NOP (Thumb-2 only)
668            ArmOp::I64Extend16S { .. } => 0xE1A00000,  // NOP (Thumb-2 only)
669            ArmOp::I64Extend32S { .. } => 0xE1A00000,  // NOP (Thumb-2 only)
670            ArmOp::I32WrapI64 { .. } => 0xE1A00000,    // NOP
671
672            // f32 VFP single-precision instructions
673            ArmOp::F32Add { sd, sn, sm } => encode_vfp_3reg(0xEE300A00, sd, sn, sm)?,
674            ArmOp::F32Sub { sd, sn, sm } => encode_vfp_3reg(0xEE300A40, sd, sn, sm)?,
675            ArmOp::F32Mul { sd, sn, sm } => encode_vfp_3reg(0xEE200A00, sd, sn, sm)?,
676            ArmOp::F32Div { sd, sn, sm } => encode_vfp_3reg(0xEE800A00, sd, sn, sm)?,
677            ArmOp::F32Abs { sd, sm } => encode_vfp_2reg(0xEEB00AC0, sd, sm)?,
678            ArmOp::F32Neg { sd, sm } => encode_vfp_2reg(0xEEB10A40, sd, sm)?,
679            ArmOp::F32Sqrt { sd, sm } => encode_vfp_2reg(0xEEB10AC0, sd, sm)?,
680
681            // f32 pseudo-ops — multi-instruction sequences
682            // FPSCR RMode: 00=nearest, 01=+inf(ceil), 10=-inf(floor), 11=zero(trunc)
683            ArmOp::F32Ceil { sd, sm } => {
684                return self.encode_arm_f32_rounding(sd, sm, 0b01); // Round toward +Inf
685            }
686            ArmOp::F32Floor { sd, sm } => {
687                return self.encode_arm_f32_rounding(sd, sm, 0b10); // Round toward -Inf
688            }
689            ArmOp::F32Trunc { sd, sm } => {
690                return self.encode_arm_f32_rounding(sd, sm, 0b11); // VCVT toward zero
691            }
692            ArmOp::F32Nearest { sd, sm } => {
693                return self.encode_arm_f32_rounding(sd, sm, 0b00); // VCVT to nearest
694            }
695            ArmOp::F32Min { sd, sn, sm } => {
696                return self.encode_arm_f32_minmax(sd, sn, sm, true);
697            }
698            ArmOp::F32Max { sd, sn, sm } => {
699                return self.encode_arm_f32_minmax(sd, sn, sm, false);
700            }
701            ArmOp::F32Copysign { sd, sn, sm } => {
702                return self.encode_arm_f32_copysign(sd, sn, sm);
703            }
704
705            // f32 comparisons — multi-instruction: VCMP + VMRS + conditional MOV
706            ArmOp::F32Eq { rd, sn, sm } => {
707                return self.encode_arm_f32_compare(rd, sn, sm, 0x0); // EQ
708            }
709            ArmOp::F32Ne { rd, sn, sm } => {
710                return self.encode_arm_f32_compare(rd, sn, sm, 0x1); // NE
711            }
712            ArmOp::F32Lt { rd, sn, sm } => {
713                return self.encode_arm_f32_compare(rd, sn, sm, 0x4); // MI (less than)
714            }
715            ArmOp::F32Le { rd, sn, sm } => {
716                return self.encode_arm_f32_compare(rd, sn, sm, 0x9); // LS (less or same)
717            }
718            ArmOp::F32Gt { rd, sn, sm } => {
719                return self.encode_arm_f32_compare(rd, sn, sm, 0xC); // GT
720            }
721            ArmOp::F32Ge { rd, sn, sm } => {
722                return self.encode_arm_f32_compare(rd, sn, sm, 0xA); // GE
723            }
724
725            // f32 const — multi-instruction: MOVW + MOVT + VMOV
726            ArmOp::F32Const { sd, value } => {
727                return self.encode_arm_f32_const(sd, *value);
728            }
729
730            ArmOp::F32Load { sd, addr } => encode_vfp_ldst(0xED900A00, sd, addr)?,
731            ArmOp::F32Store { sd, addr } => encode_vfp_ldst(0xED800A00, sd, addr)?,
732
733            // f32 conversions — multi-instruction sequences
734            ArmOp::F32ConvertI32S { sd, rm } => {
735                return self.encode_arm_f32_convert_i32(sd, rm, true);
736            }
737            ArmOp::F32ConvertI32U { sd, rm } => {
738                return self.encode_arm_f32_convert_i32(sd, rm, false);
739            }
740            ArmOp::F32ConvertI64S { .. } | ArmOp::F32ConvertI64U { .. } => {
741                return Err(synth_core::Error::synthesis(
742                    "F32 i64 conversion not supported (requires register pairs on 32-bit ARM)",
743                ));
744            }
745            ArmOp::F32ReinterpretI32 { sd, rm } => encode_vmov_core_sreg(true, sd, rm)?,
746            ArmOp::I32ReinterpretF32 { rd, sm } => encode_vmov_core_sreg(false, sm, rd)?,
747            ArmOp::I32TruncF32S { rd, sm } => {
748                return self.encode_arm_i32_trunc_f32(rd, sm, true);
749            }
750            ArmOp::I32TruncF32U { rd, sm } => {
751                return self.encode_arm_i32_trunc_f32(rd, sm, false);
752            }
753
754            // f64 VFP double-precision instructions (ARM32)
755            // F64 arithmetic: same as F32 but with sz=1 (bit 8 = 1, cp11 = 0xB)
756            ArmOp::F64Add { dd, dn, dm } => encode_vfp_3reg_f64(0xEE300B00, dd, dn, dm)?,
757            ArmOp::F64Sub { dd, dn, dm } => encode_vfp_3reg_f64(0xEE300B40, dd, dn, dm)?,
758            ArmOp::F64Mul { dd, dn, dm } => encode_vfp_3reg_f64(0xEE200B00, dd, dn, dm)?,
759            ArmOp::F64Div { dd, dn, dm } => encode_vfp_3reg_f64(0xEE800B00, dd, dn, dm)?,
760            ArmOp::F64Abs { dd, dm } => encode_vfp_2reg_f64(0xEEB00BC0, dd, dm)?,
761            ArmOp::F64Neg { dd, dm } => encode_vfp_2reg_f64(0xEEB10B40, dd, dm)?,
762            ArmOp::F64Sqrt { dd, dm } => encode_vfp_2reg_f64(0xEEB10BC0, dd, dm)?,
763
764            // f64 pseudo-ops
765            // FPSCR RMode: 00=nearest, 01=+inf(ceil), 10=-inf(floor), 11=zero(trunc)
766            ArmOp::F64Ceil { dd, dm } => {
767                return self.encode_arm_f64_rounding(dd, dm, 0b01);
768            }
769            ArmOp::F64Floor { dd, dm } => {
770                return self.encode_arm_f64_rounding(dd, dm, 0b10);
771            }
772            ArmOp::F64Trunc { dd, dm } => {
773                return self.encode_arm_f64_rounding(dd, dm, 0b11);
774            }
775            ArmOp::F64Nearest { dd, dm } => {
776                return self.encode_arm_f64_rounding(dd, dm, 0b00);
777            }
778            ArmOp::F64Min { dd, dn, dm } => {
779                return self.encode_arm_f64_minmax(dd, dn, dm, true);
780            }
781            ArmOp::F64Max { dd, dn, dm } => {
782                return self.encode_arm_f64_minmax(dd, dn, dm, false);
783            }
784            ArmOp::F64Copysign { dd, dn, dm } => {
785                return self.encode_arm_f64_copysign(dd, dn, dm);
786            }
787
788            // f64 comparisons
789            ArmOp::F64Eq { rd, dn, dm } => {
790                return self.encode_arm_f64_compare(rd, dn, dm, 0x0);
791            }
792            ArmOp::F64Ne { rd, dn, dm } => {
793                return self.encode_arm_f64_compare(rd, dn, dm, 0x1);
794            }
795            ArmOp::F64Lt { rd, dn, dm } => {
796                return self.encode_arm_f64_compare(rd, dn, dm, 0x4);
797            }
798            ArmOp::F64Le { rd, dn, dm } => {
799                return self.encode_arm_f64_compare(rd, dn, dm, 0x9);
800            }
801            ArmOp::F64Gt { rd, dn, dm } => {
802                return self.encode_arm_f64_compare(rd, dn, dm, 0xC);
803            }
804            ArmOp::F64Ge { rd, dn, dm } => {
805                return self.encode_arm_f64_compare(rd, dn, dm, 0xA);
806            }
807
808            ArmOp::F64Const { dd, value } => {
809                return self.encode_arm_f64_const(dd, *value);
810            }
811
812            ArmOp::F64Load { dd, addr } => encode_vfp_ldst_f64(0xED900B00, dd, addr)?,
813            ArmOp::F64Store { dd, addr } => encode_vfp_ldst_f64(0xED800B00, dd, addr)?,
814
815            ArmOp::F64ConvertI32S { dd, rm } => {
816                return self.encode_arm_f64_convert_i32(dd, rm, true);
817            }
818            ArmOp::F64ConvertI32U { dd, rm } => {
819                return self.encode_arm_f64_convert_i32(dd, rm, false);
820            }
821            ArmOp::F64ConvertI64S { .. } | ArmOp::F64ConvertI64U { .. } => {
822                return Err(synth_core::Error::synthesis(
823                    "F64 i64 conversion not supported (requires register pairs on 32-bit ARM)",
824                ));
825            }
826            ArmOp::F64PromoteF32 { dd, sm } => {
827                return self.encode_arm_f64_promote_f32(dd, sm);
828            }
829            ArmOp::F64ReinterpretI64 { dd, rmlo, rmhi } => {
830                encode_vmov_core_dreg(true, dd, rmlo, rmhi)?
831            }
832            ArmOp::I64ReinterpretF64 { rdlo, rdhi, dm } => {
833                encode_vmov_core_dreg(false, dm, rdlo, rdhi)?
834            }
835            ArmOp::I64TruncF64S { .. } | ArmOp::I64TruncF64U { .. } => {
836                return Err(synth_core::Error::synthesis(
837                    "i64 truncation from F64 not supported (requires i64 register pairs on 32-bit ARM)",
838                ));
839            }
840            ArmOp::I32TruncF64S { rd, dm } => {
841                return self.encode_arm_i32_trunc_f64(rd, dm, true);
842            }
843            ArmOp::I32TruncF64U { rd, dm } => {
844                return self.encode_arm_i32_trunc_f64(rd, dm, false);
845            }
846            // Multi-instruction sequences - only meaningful in Thumb-2 mode
847            ArmOp::I64SetCond { .. }
848            | ArmOp::I64SetCondZ { .. }
849            | ArmOp::I64Mul { .. }
850            | ArmOp::I64Shl { .. }
851            | ArmOp::I64ShrS { .. }
852            | ArmOp::I64ShrU { .. }
853            | ArmOp::I64Rotl { .. }
854            | ArmOp::I64Rotr { .. } => 0xE1A00000, // NOP (Thumb-2 only)
855
856            // MVE instructions — Thumb-2 only (Cortex-M55 is always Thumb-2)
857            ArmOp::MveLoad { .. }
858            | ArmOp::MveStore { .. }
859            | ArmOp::MveConst { .. }
860            | ArmOp::MveAnd { .. }
861            | ArmOp::MveOrr { .. }
862            | ArmOp::MveEor { .. }
863            | ArmOp::MveMvn { .. }
864            | ArmOp::MveBic { .. }
865            | ArmOp::MveAddI { .. }
866            | ArmOp::MveSubI { .. }
867            | ArmOp::MveMulI { .. }
868            | ArmOp::MveNegI { .. }
869            | ArmOp::MveCmpEqI { .. }
870            | ArmOp::MveCmpNeI { .. }
871            | ArmOp::MveCmpLtS { .. }
872            | ArmOp::MveCmpLtU { .. }
873            | ArmOp::MveCmpGtS { .. }
874            | ArmOp::MveCmpGtU { .. }
875            | ArmOp::MveCmpLeS { .. }
876            | ArmOp::MveCmpLeU { .. }
877            | ArmOp::MveCmpGeS { .. }
878            | ArmOp::MveCmpGeU { .. }
879            | ArmOp::MveDup { .. }
880            | ArmOp::MveExtractLane { .. }
881            | ArmOp::MveInsertLane { .. }
882            | ArmOp::MveAddF32 { .. }
883            | ArmOp::MveSubF32 { .. }
884            | ArmOp::MveMulF32 { .. }
885            | ArmOp::MveNegF32 { .. }
886            | ArmOp::MveAbsF32 { .. }
887            | ArmOp::MveCmpEqF32 { .. }
888            | ArmOp::MveCmpNeF32 { .. }
889            | ArmOp::MveCmpLtF32 { .. }
890            | ArmOp::MveCmpLeF32 { .. }
891            | ArmOp::MveCmpGtF32 { .. }
892            | ArmOp::MveCmpGeF32 { .. }
893            | ArmOp::MveDupF32 { .. }
894            | ArmOp::MveExtractLaneF32 { .. }
895            | ArmOp::MveReplaceLaneF32 { .. }
896            | ArmOp::MveDivF32 { .. }
897            | ArmOp::MveSqrtF32 { .. } => 0xE1A00000, // NOP (MVE = Thumb-2 only)
898        };
899
900        // ARM32 instructions are little-endian
901        Ok(instr.to_le_bytes().to_vec())
902    }
903
904    // === ARM32 VFP multi-instruction helpers ===
905
906    /// Encode F32 comparison as ARM32: VCMP.F32 + VMRS + MOV rd,#0 + MOVcond rd,#1
907    fn encode_arm_f32_compare(
908        &self,
909        rd: &Reg,
910        sn: &VfpReg,
911        sm: &VfpReg,
912        cond_code: u32,
913    ) -> Result<Vec<u8>> {
914        let mut bytes = Vec::new();
915
916        // VCMP.F32 Sn, Sm: 0xEEB40A40 with Sn in Vd position, Sm in Vm position
917        let sn_num = vfp_sreg_to_num(sn)?;
918        let sm_num = vfp_sreg_to_num(sm)?;
919        let (vd, d) = encode_sreg(sn_num);
920        let (vm, m) = encode_sreg(sm_num);
921        let vcmp = 0xEEB40A40 | (d << 22) | (vd << 12) | (m << 5) | vm;
922        bytes.extend_from_slice(&vcmp.to_le_bytes());
923
924        // VMRS APSR_nzcv, FPSCR: 0xEEF1FA10
925        bytes.extend_from_slice(&0xEEF1FA10u32.to_le_bytes());
926
927        // MOV rd, #0: 0xE3A0_0000 | (rd << 12)
928        let rd_bits = reg_to_bits(rd);
929        let mov_zero = 0xE3A00000 | (rd_bits << 12);
930        bytes.extend_from_slice(&mov_zero.to_le_bytes());
931
932        // MOVcond rd, #1: cond(4) | 0011 1010 0000 rd(4) 0000 0000 0001
933        let mov_one = (cond_code << 28) | 0x03A00001 | (rd_bits << 12);
934        bytes.extend_from_slice(&mov_one.to_le_bytes());
935
936        Ok(bytes)
937    }
938
939    /// Encode F32 constant load as ARM32: MOVW Rt,#lo16 + MOVT Rt,#hi16 + VMOV Sd,Rt
940    fn encode_arm_f32_const(&self, sd: &VfpReg, value: f32) -> Result<Vec<u8>> {
941        let mut bytes = Vec::new();
942        let bits = value.to_bits();
943
944        // Use R12 as temp register for constant loading
945        let rt: u32 = 12; // R12/IP
946
947        // MOVW R12, #lo16: 0xE300_C000 | (imm4 << 16) | imm12
948        let lo16 = bits & 0xFFFF;
949        let movw = 0xE3000000 | (rt << 12) | ((lo16 >> 12) << 16) | (lo16 & 0xFFF);
950        bytes.extend_from_slice(&movw.to_le_bytes());
951
952        // MOVT R12, #hi16: 0xE340_C000 | (imm4 << 16) | imm12
953        let hi16 = (bits >> 16) & 0xFFFF;
954        let movt = 0xE3400000 | (rt << 12) | ((hi16 >> 12) << 16) | (hi16 & 0xFFF);
955        bytes.extend_from_slice(&movt.to_le_bytes());
956
957        // VMOV Sd, R12
958        let vmov = encode_vmov_core_sreg(true, sd, &Reg::R12)?;
959        bytes.extend_from_slice(&vmov.to_le_bytes());
960
961        Ok(bytes)
962    }
963
964    /// Encode VMOV + VCVT.F32.S32/U32 as ARM32
965    fn encode_arm_f32_convert_i32(&self, sd: &VfpReg, rm: &Reg, signed: bool) -> Result<Vec<u8>> {
966        let mut bytes = Vec::new();
967
968        // VMOV Sd, Rm — move integer to VFP register
969        let vmov = encode_vmov_core_sreg(true, sd, rm)?;
970        bytes.extend_from_slice(&vmov.to_le_bytes());
971
972        // VCVT.F32.S32 Sd, Sd (signed) or VCVT.F32.U32 Sd, Sd (unsigned)
973        // Base: 0xEEB80A40 (signed) or 0xEEB80AC0 (unsigned)
974        let sd_num = vfp_sreg_to_num(sd)?;
975        let (vd, d) = encode_sreg(sd_num);
976        let (vm, m) = encode_sreg(sd_num); // same register as source
977        let base = if signed { 0xEEB80A40 } else { 0xEEB80AC0 };
978        let vcvt = base | (d << 22) | (vd << 12) | (m << 5) | vm;
979        bytes.extend_from_slice(&vcvt.to_le_bytes());
980
981        Ok(bytes)
982    }
983
984    /// Encode F32 rounding pseudo-op as ARM32 via VCVT to integer and back.
985    /// mode: 0b00=nearest, 0b01=floor(-Inf), 0b10=ceil(+Inf), 0b11=trunc(zero)
986    /// Strategy: VCVT.S32.F32 Sd, Sm (toward zero), then VCVT.F32.S32 Sd, Sd
987    /// For ceil/floor/nearest, we use VCVTR (round toward mode) + convert back.
988    /// Simplified: convert to int (toward zero for trunc) then back to float.
989    /// Encode F32 rounding as ARM32.
990    /// `mode`: FPSCR RMode — 0b00=nearest, 0b01=+inf(ceil), 0b10=-inf(floor), 0b11=zero(trunc)
991    ///
992    /// For trunc (mode=0b11): uses VCVTR.S32.F32 (always rounds toward zero).
993    /// For ceil/floor/nearest: sets FPSCR rounding mode, uses VCVT.S32.F32 (non-R variant
994    /// which honours FPSCR rmode), then restores FPSCR.
995    fn encode_arm_f32_rounding(&self, sd: &VfpReg, sm: &VfpReg, mode: u8) -> Result<Vec<u8>> {
996        let mut bytes = Vec::new();
997        let sm_num = vfp_sreg_to_num(sm)?;
998        let sd_num = vfp_sreg_to_num(sd)?;
999        let (vd_s, d_s) = encode_sreg(sd_num);
1000        let (vm_s, m_s) = encode_sreg(sm_num);
1001
1002        if mode == 0b11 {
1003            // Trunc (toward zero): VCVTR.S32.F32 — the "R" variant always truncates.
1004            // 0xEEBD0AC0: bit[7]=1 => round toward zero regardless of FPSCR
1005            let vcvt_to_int = 0xEEBD0AC0 | (d_s << 22) | (vd_s << 12) | (m_s << 5) | vm_s;
1006            bytes.extend_from_slice(&vcvt_to_int.to_le_bytes());
1007        } else {
1008            // ceil/floor/nearest: manipulate FPSCR rounding mode
1009            let rt: u32 = 12; // R12/IP as temp
1010
1011            // VMRS R12, FPSCR
1012            let vmrs = 0xEEF10A10 | (rt << 12);
1013            bytes.extend_from_slice(&vmrs.to_le_bytes());
1014
1015            // BIC R12, R12, #(3 << 22) — clear RMode bits [23:22]
1016            // 3<<22 = 0x00C00000. ARM rotated imm: 0x03 ror 10 (rotation=5, imm8=0x03)
1017            let bic = 0xE3CC0000 | (rt << 12) | (0x05 << 8) | 0x03;
1018            bytes.extend_from_slice(&bic.to_le_bytes());
1019
1020            // ORR R12, R12, #(mode << 22) — set desired rounding mode
1021            if mode != 0 {
1022                // mode<<22: rotation=5, imm8=mode
1023                let orr = 0xE38C0000 | (rt << 12) | (0x05 << 8) | (mode as u32);
1024                bytes.extend_from_slice(&orr.to_le_bytes());
1025            }
1026
1027            // VMSR FPSCR, R12
1028            let vmsr = 0xEEE10A10 | (rt << 12);
1029            bytes.extend_from_slice(&vmsr.to_le_bytes());
1030
1031            // VCVT.S32.F32 Sd, Sm — non-R variant (bit[7]=0), uses FPSCR rounding mode
1032            let vcvt_to_int = 0xEEBD0A40 | (d_s << 22) | (vd_s << 12) | (m_s << 5) | vm_s;
1033            bytes.extend_from_slice(&vcvt_to_int.to_le_bytes());
1034
1035            // Restore FPSCR: clear rmode bits back to nearest (default)
1036            bytes.extend_from_slice(&vmrs.to_le_bytes());
1037            bytes.extend_from_slice(&bic.to_le_bytes());
1038            bytes.extend_from_slice(&vmsr.to_le_bytes());
1039        }
1040
1041        // VCVT.F32.S32 Sd, Sd (convert integer result back to float)
1042        let (vd2, d2) = encode_sreg(sd_num);
1043        let vcvt_to_float = 0xEEB80A40 | (d2 << 22) | (vd2 << 12) | (d_s << 5) | vd_s;
1044        bytes.extend_from_slice(&vcvt_to_float.to_le_bytes());
1045
1046        Ok(bytes)
1047    }
1048
1049    /// Encode F32 min/max as ARM32: VCMP + VMRS + conditional VMOV
1050    fn encode_arm_f32_minmax(
1051        &self,
1052        sd: &VfpReg,
1053        sn: &VfpReg,
1054        sm: &VfpReg,
1055        is_min: bool,
1056    ) -> Result<Vec<u8>> {
1057        let mut bytes = Vec::new();
1058        let sn_num = vfp_sreg_to_num(sn)?;
1059        let sm_num = vfp_sreg_to_num(sm)?;
1060        let sd_num = vfp_sreg_to_num(sd)?;
1061
1062        // VMOV Sd, Sn (start with first operand)
1063        let (vd, d) = encode_sreg(sd_num);
1064        let (vn, n) = encode_sreg(sn_num);
1065        let vmov_sn = 0xEEB00A40 | (d << 22) | (vd << 12) | (n << 5) | vn;
1066        bytes.extend_from_slice(&vmov_sn.to_le_bytes());
1067
1068        // VCMP.F32 Sn, Sm
1069        let (vm, m) = encode_sreg(sm_num);
1070        let vcmp = 0xEEB40A40 | (n << 22) | (vn << 12) | (m << 5) | vm;
1071        bytes.extend_from_slice(&vcmp.to_le_bytes());
1072
1073        // VMRS APSR_nzcv, FPSCR
1074        bytes.extend_from_slice(&0xEEF1FA10u32.to_le_bytes());
1075
1076        // For min: if Sn > Sm (GT), use Sm. Condition = GT (0xC)
1077        // For max: if Sn < Sm (MI/LT), use Sm. Condition = MI (0x4)
1078        let cond = if is_min { 0xCu32 } else { 0x4u32 };
1079
1080        // VMOV{cond} Sd, Sm — conditional VMOV
1081        let vmov_cond = (cond << 28) | 0x0EB00A40 | (d << 22) | (vd << 12) | (m << 5) | vm;
1082        bytes.extend_from_slice(&vmov_cond.to_le_bytes());
1083
1084        Ok(bytes)
1085    }
1086
1087    /// Encode F32 copysign as ARM32: extract sign from Sm, magnitude from Sn
1088    fn encode_arm_f32_copysign(&self, sd: &VfpReg, sn: &VfpReg, sm: &VfpReg) -> Result<Vec<u8>> {
1089        let mut bytes = Vec::new();
1090
1091        // VMOV R12, Sm (get sign source bits)
1092        let vmov_sm = encode_vmov_core_sreg(false, sm, &Reg::R12)?;
1093        bytes.extend_from_slice(&vmov_sm.to_le_bytes());
1094
1095        // VMOV R0, Sn (get magnitude source bits) — use R0 as temp
1096        let vmov_sn = encode_vmov_core_sreg(false, sn, &Reg::R0)?;
1097        bytes.extend_from_slice(&vmov_sn.to_le_bytes());
1098
1099        // AND R12, R12, #0x80000000 (keep only sign bit)
1100        // Thumb-2 constant 0x80000000 needs special encoding; in ARM32 use rotated imm
1101        // 0x80000000 = 0x02 rotated right by 2 (rotation=1, imm8=0x02)
1102        let and_sign = 0xE2000000u32 | (12 << 16) | (12 << 12) | (1 << 8) | 0x02;
1103        bytes.extend_from_slice(&and_sign.to_le_bytes());
1104
1105        // BIC R0, R0, #0x80000000 (clear sign bit from magnitude)
1106        // R0 = register 0, so Rn and Rd fields are 0
1107        let bic_sign = 0xE3C00000u32 | (1 << 8) | 0x02;
1108        bytes.extend_from_slice(&bic_sign.to_le_bytes());
1109
1110        // ORR R0, R0, R12 (combine sign + magnitude)
1111        // R0 = register 0, so Rn and Rd fields are 0
1112        let orr = 0xE1800000u32 | 12;
1113        bytes.extend_from_slice(&orr.to_le_bytes());
1114
1115        // VMOV Sd, R0
1116        let vmov_result = encode_vmov_core_sreg(true, sd, &Reg::R0)?;
1117        bytes.extend_from_slice(&vmov_result.to_le_bytes());
1118
1119        Ok(bytes)
1120    }
1121
1122    /// Encode F64 comparison as ARM32: VCMP.F64 + VMRS + MOV rd,#0 + MOVcond rd,#1
1123    fn encode_arm_f64_compare(
1124        &self,
1125        rd: &Reg,
1126        dn: &VfpReg,
1127        dm: &VfpReg,
1128        cond_code: u32,
1129    ) -> Result<Vec<u8>> {
1130        let mut bytes = Vec::new();
1131
1132        // VCMP.F64 Dn, Dm: 0xEEB40B40 with Dn in Vd position, Dm in Vm position
1133        let dn_num = vfp_dreg_to_num(dn)?;
1134        let dm_num = vfp_dreg_to_num(dm)?;
1135        let (vd, d) = encode_dreg(dn_num);
1136        let (vm, m) = encode_dreg(dm_num);
1137        let vcmp = 0xEEB40B40 | (d << 22) | (vd << 12) | (m << 5) | vm;
1138        bytes.extend_from_slice(&vcmp.to_le_bytes());
1139
1140        // VMRS APSR_nzcv, FPSCR
1141        bytes.extend_from_slice(&0xEEF1FA10u32.to_le_bytes());
1142
1143        // MOV rd, #0
1144        let rd_bits = reg_to_bits(rd);
1145        let mov_zero = 0xE3A00000 | (rd_bits << 12);
1146        bytes.extend_from_slice(&mov_zero.to_le_bytes());
1147
1148        // MOVcond rd, #1
1149        let mov_one = (cond_code << 28) | 0x03A00001 | (rd_bits << 12);
1150        bytes.extend_from_slice(&mov_one.to_le_bytes());
1151
1152        Ok(bytes)
1153    }
1154
1155    /// Encode F64 constant load as ARM32: MOVW + MOVT + MOVW + MOVT + VMOV
1156    fn encode_arm_f64_const(&self, dd: &VfpReg, value: f64) -> Result<Vec<u8>> {
1157        let mut bytes = Vec::new();
1158        let bits = value.to_bits();
1159        let lo32 = bits as u32;
1160        let hi32 = (bits >> 32) as u32;
1161
1162        // Load low 32 bits into R0 (Rd field = 0 for R0)
1163        let lo16 = lo32 & 0xFFFF;
1164        let movw_r0 = 0xE3000000 | ((lo16 >> 12) << 16) | (lo16 & 0xFFF);
1165        bytes.extend_from_slice(&movw_r0.to_le_bytes());
1166        let hi16 = (lo32 >> 16) & 0xFFFF;
1167        let movt_r0 = 0xE3400000 | ((hi16 >> 12) << 16) | (hi16 & 0xFFF);
1168        bytes.extend_from_slice(&movt_r0.to_le_bytes());
1169
1170        // Load high 32 bits into R12
1171        let lo16 = hi32 & 0xFFFF;
1172        let movw_r12 = 0xE3000000 | ((lo16 >> 12) << 16) | (12 << 12) | (lo16 & 0xFFF);
1173        bytes.extend_from_slice(&movw_r12.to_le_bytes());
1174        let hi16 = (hi32 >> 16) & 0xFFFF;
1175        let movt_r12 = 0xE3400000 | ((hi16 >> 12) << 16) | (12 << 12) | (hi16 & 0xFFF);
1176        bytes.extend_from_slice(&movt_r12.to_le_bytes());
1177
1178        // VMOV Dd, R0, R12
1179        let vmov = encode_vmov_core_dreg(true, dd, &Reg::R0, &Reg::R12)?;
1180        bytes.extend_from_slice(&vmov.to_le_bytes());
1181
1182        Ok(bytes)
1183    }
1184
1185    /// Encode VMOV Sd, Rm + VCVT.F64.S32/U32 Dd, Sd as ARM32
1186    fn encode_arm_f64_convert_i32(&self, dd: &VfpReg, rm: &Reg, signed: bool) -> Result<Vec<u8>> {
1187        let mut bytes = Vec::new();
1188
1189        // Use S0 as intermediate: VMOV S0, Rm
1190        let vmov = encode_vmov_core_sreg(true, &VfpReg::S0, rm)?;
1191        bytes.extend_from_slice(&vmov.to_le_bytes());
1192
1193        // VCVT.F64.S32 Dd, S0 (signed) or VCVT.F64.U32 Dd, S0 (unsigned)
1194        // Base: 0xEEB80B40 (signed) or 0xEEB80BC0 (unsigned)
1195        let dd_num = vfp_dreg_to_num(dd)?;
1196        let (vd, d) = encode_dreg(dd_num);
1197        let base = if signed { 0xEEB80B40 } else { 0xEEB80BC0 };
1198        // S0 is register 0: Vm=0, M=0
1199        let vcvt = base | (d << 22) | (vd << 12);
1200        bytes.extend_from_slice(&vcvt.to_le_bytes());
1201
1202        Ok(bytes)
1203    }
1204
1205    /// Encode VCVT.F64.F32 Dd, Sm as ARM32 (f32 to f64 promotion)
1206    fn encode_arm_f64_promote_f32(&self, dd: &VfpReg, sm: &VfpReg) -> Result<Vec<u8>> {
1207        let dd_num = vfp_dreg_to_num(dd)?;
1208        let sm_num = vfp_sreg_to_num(sm)?;
1209        let (vd, d) = encode_dreg(dd_num);
1210        let (vm, m) = encode_sreg(sm_num);
1211
1212        // VCVT.F64.F32 Dd, Sm: 0xEEB70AC0
1213        let vcvt = 0xEEB70AC0 | (d << 22) | (vd << 12) | (m << 5) | vm;
1214        Ok(vcvt.to_le_bytes().to_vec())
1215    }
1216
1217    /// Encode VCVT.S32/U32.F64 Sd, Dm + VMOV Rd, Sd as ARM32
1218    fn encode_arm_i32_trunc_f64(&self, rd: &Reg, dm: &VfpReg, signed: bool) -> Result<Vec<u8>> {
1219        let mut bytes = Vec::new();
1220        let dm_num = vfp_dreg_to_num(dm)?;
1221        let (vm, m) = encode_dreg(dm_num);
1222
1223        // VCVT.S32.F64 S0, Dm (toward zero) or VCVT.U32.F64 S0, Dm
1224        // S0: Vd=0, D=0
1225        let base = if signed { 0xEEBD0BC0 } else { 0xEEBC0BC0 };
1226        let vcvt = base | (m << 5) | vm;
1227        bytes.extend_from_slice(&vcvt.to_le_bytes());
1228
1229        // VMOV Rd, S0
1230        let vmov = encode_vmov_core_sreg(false, &VfpReg::S0, rd)?;
1231        bytes.extend_from_slice(&vmov.to_le_bytes());
1232
1233        Ok(bytes)
1234    }
1235
1236    /// Encode F64 rounding pseudo-op as ARM32 via VCVT to integer and back.
1237    /// Encode F64 rounding as ARM32.
1238    /// `mode`: FPSCR RMode — 0b00=nearest, 0b01=+inf(ceil), 0b10=-inf(floor), 0b11=zero(trunc)
1239    ///
1240    /// For trunc: uses VCVTR.S32.F64 (always truncates).
1241    /// For ceil/floor/nearest: sets FPSCR rounding mode, uses VCVT.S32.F64 (non-R variant),
1242    /// then restores FPSCR.
1243    fn encode_arm_f64_rounding(&self, dd: &VfpReg, dm: &VfpReg, mode: u8) -> Result<Vec<u8>> {
1244        let mut bytes = Vec::new();
1245        let dm_num = vfp_dreg_to_num(dm)?;
1246        let dd_num = vfp_dreg_to_num(dd)?;
1247        let (vm, m) = encode_dreg(dm_num);
1248        let (vd, d) = encode_dreg(dd_num);
1249
1250        if mode == 0b11 {
1251            // Trunc (toward zero): VCVTR.S32.F64 — bit[7]=1, always truncates
1252            let vcvt_to_int = 0xEEBD0BC0 | (m << 5) | vm;
1253            bytes.extend_from_slice(&vcvt_to_int.to_le_bytes());
1254        } else {
1255            // ceil/floor/nearest: manipulate FPSCR rounding mode
1256            let rt: u32 = 12;
1257
1258            // VMRS R12, FPSCR
1259            let vmrs = 0xEEF10A10 | (rt << 12);
1260            bytes.extend_from_slice(&vmrs.to_le_bytes());
1261
1262            // BIC R12, R12, #(3 << 22)
1263            let bic = 0xE3CC0000 | (rt << 12) | (0x05 << 8) | 0x03;
1264            bytes.extend_from_slice(&bic.to_le_bytes());
1265
1266            // ORR R12, R12, #(mode << 22)
1267            if mode != 0 {
1268                let orr = 0xE38C0000 | (rt << 12) | (0x05 << 8) | (mode as u32);
1269                bytes.extend_from_slice(&orr.to_le_bytes());
1270            }
1271
1272            // VMSR FPSCR, R12
1273            let vmsr = 0xEEE10A10 | (rt << 12);
1274            bytes.extend_from_slice(&vmsr.to_le_bytes());
1275
1276            // VCVT.S32.F64 S0, Dm — non-R variant (bit[7]=0), uses FPSCR rmode
1277            let vcvt_to_int = 0xEEBD0B40 | (m << 5) | vm;
1278            bytes.extend_from_slice(&vcvt_to_int.to_le_bytes());
1279
1280            // Restore FPSCR
1281            bytes.extend_from_slice(&vmrs.to_le_bytes());
1282            bytes.extend_from_slice(&bic.to_le_bytes());
1283            bytes.extend_from_slice(&vmsr.to_le_bytes());
1284        }
1285
1286        // VCVT.F64.S32 Dd, S0 (convert back to double)
1287        let vcvt_to_float = 0xEEB80B40 | (d << 22) | (vd << 12);
1288        bytes.extend_from_slice(&vcvt_to_float.to_le_bytes());
1289
1290        Ok(bytes)
1291    }
1292
1293    /// Encode F64 min/max as ARM32: VMOV + VCMP + VMRS + conditional VMOV
1294    fn encode_arm_f64_minmax(
1295        &self,
1296        dd: &VfpReg,
1297        dn: &VfpReg,
1298        dm: &VfpReg,
1299        is_min: bool,
1300    ) -> Result<Vec<u8>> {
1301        let mut bytes = Vec::new();
1302        let dn_num = vfp_dreg_to_num(dn)?;
1303        let dm_num = vfp_dreg_to_num(dm)?;
1304        let dd_num = vfp_dreg_to_num(dd)?;
1305
1306        // VMOV.F64 Dd, Dn (start with first operand)
1307        let (vd, d) = encode_dreg(dd_num);
1308        let (vn, n) = encode_dreg(dn_num);
1309        let vmov_dn = 0xEEB00B40 | (d << 22) | (vd << 12) | (n << 5) | vn;
1310        bytes.extend_from_slice(&vmov_dn.to_le_bytes());
1311
1312        // VCMP.F64 Dn, Dm
1313        let (vm, m) = encode_dreg(dm_num);
1314        let vcmp = 0xEEB40B40 | (n << 22) | (vn << 12) | (m << 5) | vm;
1315        bytes.extend_from_slice(&vcmp.to_le_bytes());
1316
1317        // VMRS APSR_nzcv, FPSCR
1318        bytes.extend_from_slice(&0xEEF1FA10u32.to_le_bytes());
1319
1320        let cond = if is_min { 0xCu32 } else { 0x4u32 };
1321        let vmov_cond = (cond << 28) | 0x0EB00B40 | (d << 22) | (vd << 12) | (m << 5) | vm;
1322        bytes.extend_from_slice(&vmov_cond.to_le_bytes());
1323
1324        Ok(bytes)
1325    }
1326
1327    /// Encode F64 copysign as ARM32
1328    fn encode_arm_f64_copysign(&self, dd: &VfpReg, dn: &VfpReg, dm: &VfpReg) -> Result<Vec<u8>> {
1329        let mut bytes = Vec::new();
1330
1331        // VMOV R0, R12, Dm (get sign source bits)
1332        let vmov_dm = encode_vmov_core_dreg(false, dm, &Reg::R0, &Reg::R12)?;
1333        bytes.extend_from_slice(&vmov_dm.to_le_bytes());
1334
1335        // VMOV R1, R2, Dn (get magnitude source bits)
1336        // We use R1 (lo) and R2 (hi) for the magnitude
1337        let vmov_dn = encode_vmov_core_dreg(false, dn, &Reg::R1, &Reg::R2)?;
1338        bytes.extend_from_slice(&vmov_dn.to_le_bytes());
1339
1340        // AND R12, R12, #0x80000000 (keep only sign bit from hi word)
1341        let and_sign = 0xE2000000u32 | (12 << 16) | (12 << 12) | (1 << 8) | 0x02;
1342        bytes.extend_from_slice(&and_sign.to_le_bytes());
1343
1344        // BIC R2, R2, #0x80000000 (clear sign bit from magnitude hi word)
1345        let bic_sign = 0xE3C00000u32 | (2 << 16) | (2 << 12) | (1 << 8) | 0x02;
1346        bytes.extend_from_slice(&bic_sign.to_le_bytes());
1347
1348        // ORR R2, R2, R12 (combine sign + magnitude)
1349        let orr = 0xE1800000u32 | (2 << 16) | (2 << 12) | 12;
1350        bytes.extend_from_slice(&orr.to_le_bytes());
1351
1352        // VMOV Dd, R1, R2
1353        let vmov_result = encode_vmov_core_dreg(true, dd, &Reg::R1, &Reg::R2)?;
1354        bytes.extend_from_slice(&vmov_result.to_le_bytes());
1355
1356        Ok(bytes)
1357    }
1358
1359    /// Encode VCVT.S32/U32.F32 + VMOV as ARM32
1360    fn encode_arm_i32_trunc_f32(&self, rd: &Reg, sm: &VfpReg, signed: bool) -> Result<Vec<u8>> {
1361        let mut bytes = Vec::new();
1362
1363        // VCVT.S32.F32 Sd, Sm (toward zero) or VCVT.U32.F32 Sd, Sm
1364        // We use Sm as both source and destination for the intermediate result
1365        let sm_num = vfp_sreg_to_num(sm)?;
1366        let (vd, d) = encode_sreg(sm_num);
1367        let (vm, m) = encode_sreg(sm_num);
1368        let base = if signed { 0xEEBD0AC0 } else { 0xEEBC0AC0 };
1369        let vcvt = base | (d << 22) | (vd << 12) | (m << 5) | vm;
1370        bytes.extend_from_slice(&vcvt.to_le_bytes());
1371
1372        // VMOV Rd, Sm — move result back to core register
1373        let vmov = encode_vmov_core_sreg(false, sm, rd)?;
1374        bytes.extend_from_slice(&vmov.to_le_bytes());
1375
1376        Ok(bytes)
1377    }
1378
1379    /// Encode an ARM instruction in Thumb-2 mode (16-bit or 32-bit instructions)
1380    fn encode_thumb(&self, op: &ArmOp) -> Result<Vec<u8>> {
1381        // Thumb-2 supports both 16-bit and 32-bit instructions
1382        // 32-bit instructions are encoded as two 16-bit halfwords (big-endian order)
1383        match op {
1384            // === 16-bit Thumb encodings ===
1385            ArmOp::Add { rd, rn, op2 } => {
1386                let rd_bits = reg_to_bits(rd) as u16;
1387                let rn_bits = reg_to_bits(rn) as u16;
1388
1389                if let Operand2::Reg(rm) = op2 {
1390                    let rm_bits = reg_to_bits(rm) as u16;
1391                    // ADDS Rd, Rn, Rm (16-bit): 0001 100 Rm Rn Rd
1392                    let instr: u16 = 0x1800 | (rm_bits << 6) | (rn_bits << 3) | rd_bits;
1393                    Ok(instr.to_le_bytes().to_vec())
1394                } else if let Operand2::Imm(imm) = op2 {
1395                    if *imm <= 7 && rd_bits < 8 && rn_bits < 8 {
1396                        // ADDS Rd, Rn, #imm3 (16-bit): 0001 110 imm3 Rn Rd
1397                        let instr: u16 = 0x1C00 | ((*imm as u16) << 6) | (rn_bits << 3) | rd_bits;
1398                        Ok(instr.to_le_bytes().to_vec())
1399                    } else {
1400                        // Use 32-bit ADD for larger immediates
1401                        self.encode_thumb32_add(rd, rn, *imm as u32)
1402                    }
1403                } else {
1404                    // Fallback to 32-bit encoding
1405                    self.encode_thumb32_add(rd, rn, 0)
1406                }
1407            }
1408
1409            ArmOp::Sub { rd, rn, op2 } => {
1410                let rd_bits = reg_to_bits(rd) as u16;
1411                let rn_bits = reg_to_bits(rn) as u16;
1412
1413                if let Operand2::Reg(rm) = op2 {
1414                    let rm_bits = reg_to_bits(rm) as u16;
1415                    // 16-bit SUBS can only use low registers (R0-R7)
1416                    if rd_bits < 8 && rn_bits < 8 && rm_bits < 8 {
1417                        // SUBS Rd, Rn, Rm (16-bit): 0001 101 Rm Rn Rd
1418                        let instr: u16 = 0x1A00 | (rm_bits << 6) | (rn_bits << 3) | rd_bits;
1419                        Ok(instr.to_le_bytes().to_vec())
1420                    } else {
1421                        // Use 32-bit SUB.W for high registers
1422                        self.encode_thumb32_sub_reg_raw(
1423                            rd_bits as u32,
1424                            rn_bits as u32,
1425                            rm_bits as u32,
1426                        )
1427                    }
1428                } else if let Operand2::Imm(imm) = op2 {
1429                    if *imm <= 7 && rd_bits < 8 && rn_bits < 8 {
1430                        // SUBS Rd, Rn, #imm3 (16-bit): 0001 111 imm3 Rn Rd
1431                        let instr: u16 = 0x1E00 | ((*imm as u16) << 6) | (rn_bits << 3) | rd_bits;
1432                        Ok(instr.to_le_bytes().to_vec())
1433                    } else {
1434                        self.encode_thumb32_sub(rd, rn, *imm as u32)
1435                    }
1436                } else {
1437                    self.encode_thumb32_sub(rd, rn, 0)
1438                }
1439            }
1440
1441            ArmOp::Mov { rd, op2 } => {
1442                let rd_bits = reg_to_bits(rd) as u16;
1443
1444                if let Operand2::Imm(imm) = op2 {
1445                    if *imm <= 255 && rd_bits < 8 {
1446                        // MOVS Rd, #imm8 (16-bit): 0010 0 Rd imm8
1447                        let imm_bits = (*imm as u16) & 0xFF;
1448                        let instr: u16 = 0x2000 | (rd_bits << 8) | imm_bits;
1449                        Ok(instr.to_le_bytes().to_vec())
1450                    } else {
1451                        // Use 32-bit MOVW for larger immediates
1452                        self.encode_thumb32_movw(rd, *imm as u32)
1453                    }
1454                } else if let Operand2::Reg(rm) = op2 {
1455                    let rm_bits = reg_to_bits(rm) as u16;
1456                    // MOV Rd, Rm (16-bit): 0100 0110 D Rm Rd[2:0]
1457                    // D = Rd[3], Rd[2:0] in lower bits
1458                    let d_bit = (rd_bits >> 3) & 1;
1459                    let instr: u16 = 0x4600 | (d_bit << 7) | (rm_bits << 3) | (rd_bits & 0x7);
1460                    Ok(instr.to_le_bytes().to_vec())
1461                } else {
1462                    let instr: u16 = 0xBF00; // NOP fallback
1463                    Ok(instr.to_le_bytes().to_vec())
1464                }
1465            }
1466
1467            ArmOp::Push { regs } => {
1468                // Thumb-2 PUSH encoding:
1469                // If all regs in R0-R7 + LR, use 16-bit: 1011 010 M rrrrrrrr
1470                // Otherwise use 32-bit: STMDB SP!, {regs} = 1110 1001 0010 1101 | 0M0 reglist(13)
1471                let mut reg_list: u16 = 0;
1472                let mut need_32bit = false;
1473                for r in regs {
1474                    let bit = reg_to_bits(r);
1475                    if bit >= 8 && *r != Reg::LR {
1476                        need_32bit = true;
1477                    }
1478                    reg_list |= 1 << bit;
1479                }
1480                if !need_32bit {
1481                    // 16-bit PUSH: 1011 010 M rrrrrrrr
1482                    let m_bit = if reg_list & (1 << 14) != 0 {
1483                        1u16
1484                    } else {
1485                        0u16
1486                    };
1487                    let low_regs = reg_list & 0xFF;
1488                    let instr: u16 = 0xB400 | (m_bit << 8) | low_regs;
1489                    Ok(instr.to_le_bytes().to_vec())
1490                } else {
1491                    // 32-bit STMDB SP!, {regs}: E92D | reglist(16)
1492                    let hw1: u16 = 0xE92D;
1493                    let hw2: u16 = reg_list;
1494                    let mut bytes = hw1.to_le_bytes().to_vec();
1495                    bytes.extend_from_slice(&hw2.to_le_bytes());
1496                    Ok(bytes)
1497                }
1498            }
1499
1500            ArmOp::Pop { regs } => {
1501                // Thumb-2 POP encoding:
1502                // If all regs in R0-R7 + PC, use 16-bit: 1011 110 P rrrrrrrr
1503                // Otherwise use 32-bit: LDMIA SP!, {regs} = 1110 1000 1011 1101 | PM0 reglist(13)
1504                let mut reg_list: u16 = 0;
1505                let mut need_32bit = false;
1506                for r in regs {
1507                    let bit = reg_to_bits(r);
1508                    if bit >= 8 && *r != Reg::PC {
1509                        need_32bit = true;
1510                    }
1511                    reg_list |= 1 << bit;
1512                }
1513                if !need_32bit {
1514                    // 16-bit POP: 1011 110 P rrrrrrrr
1515                    let p_bit = if reg_list & (1 << 15) != 0 {
1516                        1u16
1517                    } else {
1518                        0u16
1519                    };
1520                    let low_regs = reg_list & 0xFF;
1521                    let instr: u16 = 0xBC00 | (p_bit << 8) | low_regs;
1522                    Ok(instr.to_le_bytes().to_vec())
1523                } else {
1524                    // 32-bit LDMIA SP!, {regs}: E8BD | reglist(16)
1525                    let hw1: u16 = 0xE8BD;
1526                    let hw2: u16 = reg_list;
1527                    let mut bytes = hw1.to_le_bytes().to_vec();
1528                    bytes.extend_from_slice(&hw2.to_le_bytes());
1529                    Ok(bytes)
1530                }
1531            }
1532
1533            ArmOp::Nop => {
1534                let instr: u16 = 0xBF00; // NOP in Thumb-2
1535                Ok(instr.to_le_bytes().to_vec())
1536            }
1537
1538            ArmOp::Udf { imm } => {
1539                // UDF (Undefined) in Thumb-2: 16-bit encoding is 0xDE00 | imm8
1540                // This triggers UsageFault/HardFault, used for WASM traps
1541                let instr: u16 = 0xDE00 | (*imm as u16);
1542                let bytes = instr.to_le_bytes().to_vec();
1543                encoding_contracts::verify_thumb16(&bytes);
1544                Ok(bytes)
1545            }
1546
1547            // i64 support: ADDS, ADC, SUBS, SBC for register pair arithmetic
1548            // ADDS sets flags (carry), ADC uses carry from previous ADDS
1549            ArmOp::Adds { rd, rn, op2 } => {
1550                let rd_bits = reg_to_bits(rd) as u16;
1551                let rn_bits = reg_to_bits(rn) as u16;
1552
1553                if let Operand2::Reg(rm) = op2 {
1554                    let rm_bits = reg_to_bits(rm) as u16;
1555                    // ADDS Rd, Rn, Rm (16-bit): 0001 100 Rm Rn Rd
1556                    let instr: u16 = 0x1800 | (rm_bits << 6) | (rn_bits << 3) | rd_bits;
1557                    Ok(instr.to_le_bytes().to_vec())
1558                } else {
1559                    // 32-bit Thumb-2 ADDS with immediate
1560                    self.encode_thumb32_adds(rd, rn, 0)
1561                }
1562            }
1563
1564            // ADC: Add with Carry (Thumb-2 32-bit)
1565            // ADC.W Rd, Rn, Rm: EB40 Rn | 00 Rd 00 Rm
1566            ArmOp::Adc { rd, rn, op2 } => {
1567                let rd_bits = reg_to_bits(rd);
1568                let rn_bits = reg_to_bits(rn);
1569
1570                if let Operand2::Reg(rm) = op2 {
1571                    let rm_bits = reg_to_bits(rm);
1572                    // ADC.W Rd, Rn, Rm (T2): 1110 1011 0100 Rn | 0 000 Rd 00 00 Rm
1573                    let hw1: u16 = (0xEB40 | rn_bits) as u16;
1574                    let hw2: u16 = ((rd_bits << 8) | rm_bits) as u16;
1575
1576                    let mut bytes = hw1.to_le_bytes().to_vec();
1577                    bytes.extend_from_slice(&hw2.to_le_bytes());
1578                    Ok(bytes)
1579                } else {
1580                    // ADC with immediate - use 32-bit encoding
1581                    let hw1: u16 = (0xF140 | rn_bits) as u16;
1582                    let hw2: u16 = (rd_bits << 8) as u16;
1583                    let mut bytes = hw1.to_le_bytes().to_vec();
1584                    bytes.extend_from_slice(&hw2.to_le_bytes());
1585                    Ok(bytes)
1586                }
1587            }
1588
1589            // SUBS sets flags (borrow), SBC uses borrow from previous SUBS
1590            ArmOp::Subs { rd, rn, op2 } => {
1591                let rd_bits = reg_to_bits(rd) as u16;
1592                let rn_bits = reg_to_bits(rn) as u16;
1593
1594                if let Operand2::Reg(rm) = op2 {
1595                    let rm_bits = reg_to_bits(rm) as u16;
1596                    // SUBS Rd, Rn, Rm (16-bit): 0001 101 Rm Rn Rd
1597                    let instr: u16 = 0x1A00 | (rm_bits << 6) | (rn_bits << 3) | rd_bits;
1598                    Ok(instr.to_le_bytes().to_vec())
1599                } else {
1600                    // 32-bit Thumb-2 SUBS with immediate
1601                    self.encode_thumb32_subs(rd, rn, 0)
1602                }
1603            }
1604
1605            // SBC: Subtract with Carry (Thumb-2 32-bit)
1606            // SBC.W Rd, Rn, Rm: EB60 Rn | 00 Rd 00 Rm
1607            ArmOp::Sbc { rd, rn, op2 } => {
1608                let rd_bits = reg_to_bits(rd);
1609                let rn_bits = reg_to_bits(rn);
1610
1611                if let Operand2::Reg(rm) = op2 {
1612                    let rm_bits = reg_to_bits(rm);
1613                    // SBC.W Rd, Rn, Rm (T2): 1110 1011 0110 Rn | 0 000 Rd 00 00 Rm
1614                    let hw1: u16 = (0xEB60 | rn_bits) as u16;
1615                    let hw2: u16 = ((rd_bits << 8) | rm_bits) as u16;
1616
1617                    let mut bytes = hw1.to_le_bytes().to_vec();
1618                    bytes.extend_from_slice(&hw2.to_le_bytes());
1619                    Ok(bytes)
1620                } else {
1621                    // SBC with immediate - use 32-bit encoding
1622                    let hw1: u16 = (0xF160 | rn_bits) as u16;
1623                    let hw2: u16 = (rd_bits << 8) as u16;
1624                    let mut bytes = hw1.to_le_bytes().to_vec();
1625                    bytes.extend_from_slice(&hw2.to_le_bytes());
1626                    Ok(bytes)
1627                }
1628            }
1629
1630            // === 32-bit Thumb-2 encodings ===
1631
1632            // SDIV: 11111011 1001 Rn 1111 Rd 1111 Rm
1633            ArmOp::Sdiv { rd, rn, rm } => {
1634                let rd_bits = reg_to_bits(rd);
1635                let rn_bits = reg_to_bits(rn);
1636                let rm_bits = reg_to_bits(rm);
1637                encoding_contracts::verify_reg_bits(rd_bits);
1638                encoding_contracts::verify_reg_bits(rn_bits);
1639                encoding_contracts::verify_reg_bits(rm_bits);
1640
1641                // Thumb-2 SDIV: FB90 F0F0 | Rn<<16 | Rd<<8 | Rm
1642                // First halfword: 1111 1011 1001 Rn = 0xFB90 | Rn
1643                // Second halfword: 1111 Rd 1111 Rm = 0xF0F0 | Rd<<8 | Rm
1644                let hw1: u16 = (0xFB90 | rn_bits) as u16;
1645                let hw2: u16 = (0xF0F0 | (rd_bits << 8) | rm_bits) as u16;
1646
1647                // Thumb-2 32-bit instructions: first halfword, then second halfword (little-endian each)
1648                let mut bytes = hw1.to_le_bytes().to_vec();
1649                bytes.extend_from_slice(&hw2.to_le_bytes());
1650                encoding_contracts::verify_thumb32(&bytes);
1651                Ok(bytes)
1652            }
1653
1654            // UDIV: 11111011 1011 Rn 1111 Rd 1111 Rm
1655            ArmOp::Udiv { rd, rn, rm } => {
1656                let rd_bits = reg_to_bits(rd);
1657                let rn_bits = reg_to_bits(rn);
1658                let rm_bits = reg_to_bits(rm);
1659                encoding_contracts::verify_reg_bits(rd_bits);
1660                encoding_contracts::verify_reg_bits(rn_bits);
1661                encoding_contracts::verify_reg_bits(rm_bits);
1662
1663                // Thumb-2 UDIV: FBB0 F0F0 | Rn<<16 | Rd<<8 | Rm
1664                let hw1: u16 = (0xFBB0 | rn_bits) as u16;
1665                let hw2: u16 = (0xF0F0 | (rd_bits << 8) | rm_bits) as u16;
1666
1667                let mut bytes = hw1.to_le_bytes().to_vec();
1668                bytes.extend_from_slice(&hw2.to_le_bytes());
1669                encoding_contracts::verify_thumb32(&bytes);
1670                Ok(bytes)
1671            }
1672
1673            // MUL (Thumb-2 32-bit): MUL Rd, Rn, Rm
1674            ArmOp::Mul { rd, rn, rm } => {
1675                let rd_bits = reg_to_bits(rd);
1676                let rn_bits = reg_to_bits(rn);
1677                let rm_bits = reg_to_bits(rm);
1678
1679                // Thumb-2 MUL: FB00 F000 | Rn | Rd<<8 | Rm
1680                // 11111011 0000 Rn | 1111 Rd 0000 Rm
1681                let hw1: u16 = (0xFB00 | rn_bits) as u16;
1682                let hw2: u16 = (0xF000 | (rd_bits << 8) | rm_bits) as u16;
1683
1684                let mut bytes = hw1.to_le_bytes().to_vec();
1685                bytes.extend_from_slice(&hw2.to_le_bytes());
1686                Ok(bytes)
1687            }
1688
1689            // MLS: Rd = Ra - Rn * Rm
1690            ArmOp::Mls { rd, rn, rm, ra } => {
1691                let rd_bits = reg_to_bits(rd);
1692                let rn_bits = reg_to_bits(rn);
1693                let rm_bits = reg_to_bits(rm);
1694                let ra_bits = reg_to_bits(ra);
1695
1696                // Thumb-2 MLS: FB00 Rn | Ra Rd 0001 Rm
1697                // 11111011 0000 Rn | Ra Rd 0001 Rm
1698                let hw1: u16 = (0xFB00 | rn_bits) as u16;
1699                let hw2: u16 = ((ra_bits << 12) | (rd_bits << 8) | 0x10 | rm_bits) as u16;
1700
1701                let mut bytes = hw1.to_le_bytes().to_vec();
1702                bytes.extend_from_slice(&hw2.to_le_bytes());
1703                Ok(bytes)
1704            }
1705
1706            // AND (Thumb-2 32-bit)
1707            ArmOp::And { rd, rn, op2 } => {
1708                if let Operand2::Reg(rm) = op2 {
1709                    let rd_bits = reg_to_bits(rd);
1710                    let rn_bits = reg_to_bits(rn);
1711                    let rm_bits = reg_to_bits(rm);
1712
1713                    // Thumb-2 AND register: EA00 Rn | 0 Rd 00 00 Rm
1714                    let hw1: u16 = (0xEA00 | rn_bits) as u16;
1715                    let hw2: u16 = ((rd_bits << 8) | rm_bits) as u16;
1716
1717                    let mut bytes = hw1.to_le_bytes().to_vec();
1718                    bytes.extend_from_slice(&hw2.to_le_bytes());
1719                    Ok(bytes)
1720                } else if let Operand2::Imm(imm) = op2 {
1721                    let rd_bits = reg_to_bits(rd);
1722                    let rn_bits = reg_to_bits(rn);
1723                    let imm_val = *imm as u32;
1724
1725                    // Thumb-2 AND.W immediate T1: 11110 i 0 0000 S Rn | 0 imm3 Rd imm8
1726                    let i_bit = (imm_val >> 11) & 1;
1727                    let imm3 = (imm_val >> 8) & 0x7;
1728                    let imm8 = imm_val & 0xFF;
1729
1730                    let hw1: u16 = (0xF000 | (i_bit << 10) | rn_bits) as u16;
1731                    let hw2: u16 = ((imm3 << 12) | (rd_bits << 8) | imm8) as u16;
1732
1733                    let mut bytes = hw1.to_le_bytes().to_vec();
1734                    bytes.extend_from_slice(&hw2.to_le_bytes());
1735                    Ok(bytes)
1736                } else {
1737                    // RegShift variant - fallback to NOP
1738                    let instr: u16 = 0xBF00;
1739                    Ok(instr.to_le_bytes().to_vec())
1740                }
1741            }
1742
1743            // ORR (Thumb-2 32-bit)
1744            ArmOp::Orr { rd, rn, op2 } => {
1745                if let Operand2::Reg(rm) = op2 {
1746                    let rd_bits = reg_to_bits(rd);
1747                    let rn_bits = reg_to_bits(rn);
1748                    let rm_bits = reg_to_bits(rm);
1749
1750                    // Thumb-2 ORR: EA40 Rn | 0 Rd 00 00 Rm
1751                    let hw1: u16 = (0xEA40 | rn_bits) as u16;
1752                    let hw2: u16 = ((rd_bits << 8) | rm_bits) as u16;
1753
1754                    let mut bytes = hw1.to_le_bytes().to_vec();
1755                    bytes.extend_from_slice(&hw2.to_le_bytes());
1756                    Ok(bytes)
1757                } else {
1758                    let instr: u16 = 0xBF00;
1759                    Ok(instr.to_le_bytes().to_vec())
1760                }
1761            }
1762
1763            // EOR (Thumb-2 32-bit)
1764            ArmOp::Eor { rd, rn, op2 } => {
1765                if let Operand2::Reg(rm) = op2 {
1766                    let rd_bits = reg_to_bits(rd);
1767                    let rn_bits = reg_to_bits(rn);
1768                    let rm_bits = reg_to_bits(rm);
1769
1770                    // Thumb-2 EOR: EA80 Rn | 0 Rd 00 00 Rm
1771                    let hw1: u16 = (0xEA80 | rn_bits) as u16;
1772                    let hw2: u16 = ((rd_bits << 8) | rm_bits) as u16;
1773
1774                    let mut bytes = hw1.to_le_bytes().to_vec();
1775                    bytes.extend_from_slice(&hw2.to_le_bytes());
1776                    Ok(bytes)
1777                } else {
1778                    let instr: u16 = 0xBF00;
1779                    Ok(instr.to_le_bytes().to_vec())
1780                }
1781            }
1782
1783            // Shift operations (16-bit for low registers)
1784            ArmOp::Lsl { rd, rn, shift } => {
1785                let rd_bits = reg_to_bits(rd) as u16;
1786                let rn_bits = reg_to_bits(rn) as u16;
1787                let shift_bits = (*shift as u16) & 0x1F;
1788
1789                if rd_bits < 8 && rn_bits < 8 {
1790                    // LSLS Rd, Rm, #imm5 (16-bit): 0000 0 imm5 Rm Rd
1791                    let instr: u16 = (shift_bits << 6) | (rn_bits << 3) | rd_bits;
1792                    Ok(instr.to_le_bytes().to_vec())
1793                } else {
1794                    // Use 32-bit encoding for high registers
1795                    self.encode_thumb32_shift(rd, rn, *shift, 0b00) // LSL type
1796                }
1797            }
1798
1799            ArmOp::Lsr { rd, rn, shift } => {
1800                let rd_bits = reg_to_bits(rd) as u16;
1801                let rn_bits = reg_to_bits(rn) as u16;
1802                let shift_bits = (*shift as u16) & 0x1F;
1803
1804                if rd_bits < 8 && rn_bits < 8 && shift_bits > 0 {
1805                    // LSRS Rd, Rm, #imm5 (16-bit): 0000 1 imm5 Rm Rd
1806                    let instr: u16 = 0x0800 | (shift_bits << 6) | (rn_bits << 3) | rd_bits;
1807                    Ok(instr.to_le_bytes().to_vec())
1808                } else {
1809                    self.encode_thumb32_shift(rd, rn, *shift, 0b01) // LSR type
1810                }
1811            }
1812
1813            ArmOp::Asr { rd, rn, shift } => {
1814                let rd_bits = reg_to_bits(rd) as u16;
1815                let rn_bits = reg_to_bits(rn) as u16;
1816                let shift_bits = (*shift as u16) & 0x1F;
1817
1818                if rd_bits < 8 && rn_bits < 8 && shift_bits > 0 {
1819                    // ASRS Rd, Rm, #imm5 (16-bit): 0001 0 imm5 Rm Rd
1820                    let instr: u16 = 0x1000 | (shift_bits << 6) | (rn_bits << 3) | rd_bits;
1821                    Ok(instr.to_le_bytes().to_vec())
1822                } else {
1823                    self.encode_thumb32_shift(rd, rn, *shift, 0b10) // ASR type
1824                }
1825            }
1826
1827            ArmOp::Ror { rd, rn, shift } => {
1828                // ROR doesn't have a 16-bit immediate form, use 32-bit
1829                self.encode_thumb32_shift(rd, rn, *shift, 0b11) // ROR type
1830            }
1831
1832            // Register-based shifts (Thumb-2 32-bit)
1833            // Encoding: 11111010 0xxS Rn 1111 Rd 0000 Rm
1834            // xx = shift type: 00=LSL, 01=LSR, 10=ASR, 11=ROR
1835            ArmOp::LslReg { rd, rn, rm } => self.encode_thumb32_shift_reg(rd, rn, rm, 0b00),
1836            ArmOp::LsrReg { rd, rn, rm } => self.encode_thumb32_shift_reg(rd, rn, rm, 0b01),
1837            ArmOp::AsrReg { rd, rn, rm } => self.encode_thumb32_shift_reg(rd, rn, rm, 0b10),
1838            ArmOp::RorReg { rd, rn, rm } => self.encode_thumb32_shift_reg(rd, rn, rm, 0b11),
1839
1840            // RSB (Reverse Subtract): Rd = imm - Rn
1841            // Thumb-2 T2 encoding: 11110 i 0 1110 S Rn | 0 imm3 Rd imm8
1842            ArmOp::Rsb { rd, rn, imm } => {
1843                let rd_bits = reg_to_bits(rd);
1844                let rn_bits = reg_to_bits(rn);
1845                let imm_val = *imm;
1846
1847                let i_bit = (imm_val >> 11) & 1;
1848                let imm3 = (imm_val >> 8) & 0x7;
1849                let imm8 = imm_val & 0xFF;
1850
1851                // hw1: 11110 i 01110 0 Rn  (S=0)
1852                let hw1: u16 = (0xF1C0 | (i_bit << 10) | rn_bits) as u16;
1853                // hw2: 0 imm3 Rd imm8
1854                let hw2: u16 = ((imm3 << 12) | (rd_bits << 8) | imm8) as u16;
1855
1856                let mut bytes = hw1.to_le_bytes().to_vec();
1857                bytes.extend_from_slice(&hw2.to_le_bytes());
1858                Ok(bytes)
1859            }
1860
1861            // CLZ (Thumb-2 32-bit)
1862            ArmOp::Clz { rd, rm } => {
1863                let rd_bits = reg_to_bits(rd);
1864                let rm_bits = reg_to_bits(rm);
1865
1866                // Thumb-2 CLZ: FAB0 Rm | F8 Rd Rm
1867                // 11111010 1011 Rm | 1111 1000 Rd Rm
1868                let hw1: u16 = (0xFAB0 | rm_bits) as u16;
1869                let hw2: u16 = (0xF080 | (rd_bits << 8) | rm_bits) as u16;
1870
1871                let mut bytes = hw1.to_le_bytes().to_vec();
1872                bytes.extend_from_slice(&hw2.to_le_bytes());
1873                Ok(bytes)
1874            }
1875
1876            // RBIT (Thumb-2 32-bit)
1877            ArmOp::Rbit { rd, rm } => {
1878                let rd_bits = reg_to_bits(rd);
1879                let rm_bits = reg_to_bits(rm);
1880
1881                // Thumb-2 RBIT: FA90 Rm | F0 Rd A0 Rm
1882                // 11111010 1001 Rm | 1111 Rd 1010 Rm
1883                let hw1: u16 = (0xFA90 | rm_bits) as u16;
1884                let hw2: u16 = (0xF0A0 | (rd_bits << 8) | rm_bits) as u16;
1885
1886                let mut bytes = hw1.to_le_bytes().to_vec();
1887                bytes.extend_from_slice(&hw2.to_le_bytes());
1888                Ok(bytes)
1889            }
1890
1891            // SXTB (16-bit for low registers)
1892            ArmOp::Sxtb { rd, rm } => {
1893                let rd_bits = reg_to_bits(rd) as u16;
1894                let rm_bits = reg_to_bits(rm) as u16;
1895
1896                if rd_bits < 8 && rm_bits < 8 {
1897                    // SXTB Rd, Rm (16-bit): 1011 0010 01 Rm Rd
1898                    let instr: u16 = 0xB240 | (rm_bits << 3) | rd_bits;
1899                    Ok(instr.to_le_bytes().to_vec())
1900                } else {
1901                    // Thumb-2 SXTB.W: FA4F F(rd)80 (rm)
1902                    // 11111010 0100 1111 | 1111 Rd 10 rotate Rm
1903                    let rd_bits32 = rd_bits as u32;
1904                    let rm_bits32 = rm_bits as u32;
1905                    let hw1: u16 = 0xFA4F;
1906                    let hw2: u16 = (0xF080 | (rd_bits32 << 8) | rm_bits32) as u16;
1907                    let mut bytes = hw1.to_le_bytes().to_vec();
1908                    bytes.extend_from_slice(&hw2.to_le_bytes());
1909                    Ok(bytes)
1910                }
1911            }
1912
1913            // SXTH (16-bit for low registers)
1914            ArmOp::Sxth { rd, rm } => {
1915                let rd_bits = reg_to_bits(rd) as u16;
1916                let rm_bits = reg_to_bits(rm) as u16;
1917
1918                if rd_bits < 8 && rm_bits < 8 {
1919                    // SXTH Rd, Rm (16-bit): 1011 0010 00 Rm Rd
1920                    let instr: u16 = 0xB200 | (rm_bits << 3) | rd_bits;
1921                    Ok(instr.to_le_bytes().to_vec())
1922                } else {
1923                    // Thumb-2 SXTH.W: FA0F F(rd)80 (rm)
1924                    // 11111010 0000 1111 | 1111 Rd 10 rotate Rm
1925                    let rd_bits32 = rd_bits as u32;
1926                    let rm_bits32 = rm_bits as u32;
1927                    let hw1: u16 = 0xFA0F;
1928                    let hw2: u16 = (0xF080 | (rd_bits32 << 8) | rm_bits32) as u16;
1929                    let mut bytes = hw1.to_le_bytes().to_vec();
1930                    bytes.extend_from_slice(&hw2.to_le_bytes());
1931                    Ok(bytes)
1932                }
1933            }
1934
1935            // CMP (can be 16-bit for low registers)
1936            ArmOp::Cmp { rn, op2 } => {
1937                let rn_bits = reg_to_bits(rn) as u16;
1938
1939                if let Operand2::Imm(imm) = op2 {
1940                    // Only use 16-bit encoding for non-negative immediates 0-255
1941                    // Negative immediates must use 32-bit encoding
1942                    if *imm >= 0 && *imm <= 255 && rn_bits < 8 {
1943                        // CMP Rn, #imm8 (16-bit): 0010 1 Rn imm8
1944                        let instr: u16 = 0x2800 | (rn_bits << 8) | (*imm as u16 & 0xFF);
1945                        Ok(instr.to_le_bytes().to_vec())
1946                    } else {
1947                        self.encode_thumb32_cmp_imm(rn, *imm as u32)
1948                    }
1949                } else if let Operand2::Reg(rm) = op2 {
1950                    let rm_bits = reg_to_bits(rm) as u16;
1951                    if rn_bits < 8 && rm_bits < 8 {
1952                        // CMP Rn, Rm (16-bit low): 0100 0010 10 Rm Rn
1953                        let instr: u16 = 0x4280 | (rm_bits << 3) | rn_bits;
1954                        Ok(instr.to_le_bytes().to_vec())
1955                    } else {
1956                        // CMP Rn, Rm (16-bit high): 0100 0101 N Rm Rn[2:0]
1957                        let n_bit = (rn_bits >> 3) & 1;
1958                        let instr: u16 = 0x4500 | (n_bit << 7) | (rm_bits << 3) | (rn_bits & 0x7);
1959                        Ok(instr.to_le_bytes().to_vec())
1960                    }
1961                } else {
1962                    let instr: u16 = 0xBF00;
1963                    Ok(instr.to_le_bytes().to_vec())
1964                }
1965            }
1966
1967            // CMN (Compare Negative) - computes Rn + op2 and sets flags
1968            // CMN Rn, #1 sets Z flag if Rn == -1 (since -1 + 1 = 0)
1969            ArmOp::Cmn { rn, op2 } => {
1970                let rn_bits = reg_to_bits(rn) as u16;
1971
1972                if let Operand2::Imm(imm) = op2 {
1973                    // CMN.W Rn, #imm (32-bit encoding)
1974                    // Encoding: F110 Rn | 0F00 imm8 (for small immediates 0-255)
1975                    if *imm >= 0 && *imm <= 255 {
1976                        let imm8 = *imm as u16 & 0xFF;
1977                        let hw1: u16 = 0xF110 | rn_bits;
1978                        let hw2: u16 = 0x0F00 | imm8;
1979                        let mut bytes = hw1.to_le_bytes().to_vec();
1980                        bytes.extend_from_slice(&hw2.to_le_bytes());
1981                        Ok(bytes)
1982                    } else {
1983                        // For other immediates, fallback to NOP (should not happen in our use case)
1984                        Ok(vec![0xBF, 0x00])
1985                    }
1986                } else if let Operand2::Reg(rm) = op2 {
1987                    let rm_bits = reg_to_bits(rm) as u16;
1988                    // CMN Rn, Rm (16-bit): 0100 0010 11 Rm Rn
1989                    let instr: u16 = 0x42C0 | (rm_bits << 3) | rn_bits;
1990                    Ok(instr.to_le_bytes().to_vec())
1991                } else {
1992                    Ok(vec![0xBF, 0x00])
1993                }
1994            }
1995
1996            // LDR (can be 16-bit for simple cases)
1997            ArmOp::Ldr { rd, addr } => {
1998                let rd_bits = reg_to_bits(rd);
1999                let base_bits = reg_to_bits(&addr.base);
2000
2001                // Handle register offset mode [base, Roff] or [base, Roff, #imm]
2002                if let Some(offset_reg) = &addr.offset_reg {
2003                    let rm_bits = reg_to_bits(offset_reg);
2004
2005                    // If there's also an immediate offset, we need to ADD it first
2006                    if addr.offset != 0 {
2007                        // Use R12 (IP) as scratch to avoid clobbering the address register
2008                        // ADD R12, Rm, #offset; LDR Rd, [base, R12]
2009                        let scratch = Reg::R12;
2010                        let mut bytes =
2011                            self.encode_thumb32_add_imm(&scratch, offset_reg, addr.offset as u32)?;
2012                        bytes.extend(self.encode_thumb32_ldr_reg(rd, &addr.base, &scratch)?);
2013                        return Ok(bytes);
2014                    }
2015
2016                    // Simple register offset: LDR Rd, [Rn, Rm]
2017                    // 16-bit: only if Rd, Rn, Rm < R8
2018                    if rd_bits < 8 && base_bits < 8 && rm_bits < 8 {
2019                        // LDR Rd, [Rn, Rm] (16-bit): 0101 100 Rm Rn Rd
2020                        let instr: u16 = 0x5800
2021                            | ((rm_bits as u16) << 6)
2022                            | ((base_bits as u16) << 3)
2023                            | (rd_bits as u16);
2024                        return Ok(instr.to_le_bytes().to_vec());
2025                    }
2026
2027                    // 32-bit register offset
2028                    return self.encode_thumb32_ldr_reg(rd, &addr.base, offset_reg);
2029                }
2030
2031                // Immediate offset mode [base, #imm]
2032                let offset = addr.offset as u32;
2033
2034                if rd_bits < 8 && base_bits < 8 && (offset & 0x3) == 0 && offset <= 124 {
2035                    // LDR Rd, [Rn, #imm5*4] (16-bit): 0110 1 imm5 Rn Rd
2036                    let imm5 = (offset >> 2) as u16;
2037                    let instr: u16 =
2038                        0x6800 | (imm5 << 6) | ((base_bits as u16) << 3) | (rd_bits as u16);
2039                    Ok(instr.to_le_bytes().to_vec())
2040                } else {
2041                    self.encode_thumb32_ldr(rd, &addr.base, offset)
2042                }
2043            }
2044
2045            // STR (can be 16-bit for simple cases)
2046            ArmOp::Str { rd, addr } => {
2047                let rd_bits = reg_to_bits(rd);
2048                let base_bits = reg_to_bits(&addr.base);
2049
2050                // Handle register offset mode [base, Roff] or [base, Roff, #imm]
2051                if let Some(offset_reg) = &addr.offset_reg {
2052                    let rm_bits = reg_to_bits(offset_reg);
2053
2054                    // If there's also an immediate offset, we need to ADD it first
2055                    if addr.offset != 0 {
2056                        // Use R12 (IP) as scratch to avoid clobbering the address register
2057                        // ADD R12, Rm, #offset; STR Rd, [base, R12]
2058                        let scratch = Reg::R12;
2059                        let mut bytes =
2060                            self.encode_thumb32_add_imm(&scratch, offset_reg, addr.offset as u32)?;
2061                        bytes.extend(self.encode_thumb32_str_reg(rd, &addr.base, &scratch)?);
2062                        return Ok(bytes);
2063                    }
2064
2065                    // Simple register offset: STR Rd, [Rn, Rm]
2066                    // 16-bit: only if Rd, Rn, Rm < R8
2067                    if rd_bits < 8 && base_bits < 8 && rm_bits < 8 {
2068                        // STR Rd, [Rn, Rm] (16-bit): 0101 000 Rm Rn Rd
2069                        let instr: u16 = 0x5000
2070                            | ((rm_bits as u16) << 6)
2071                            | ((base_bits as u16) << 3)
2072                            | (rd_bits as u16);
2073                        return Ok(instr.to_le_bytes().to_vec());
2074                    }
2075
2076                    // 32-bit register offset
2077                    return self.encode_thumb32_str_reg(rd, &addr.base, offset_reg);
2078                }
2079
2080                // Immediate offset mode [base, #imm]
2081                let offset = addr.offset as u32;
2082
2083                if rd_bits < 8 && base_bits < 8 && (offset & 0x3) == 0 && offset <= 124 {
2084                    // STR Rd, [Rn, #imm5*4] (16-bit): 0110 0 imm5 Rn Rd
2085                    let imm5 = (offset >> 2) as u16;
2086                    let instr: u16 =
2087                        0x6000 | (imm5 << 6) | ((base_bits as u16) << 3) | (rd_bits as u16);
2088                    Ok(instr.to_le_bytes().to_vec())
2089                } else {
2090                    self.encode_thumb32_str(rd, &addr.base, offset)
2091                }
2092            }
2093
2094            // LDRB (Thumb-2)
2095            ArmOp::Ldrb { rd, addr } => {
2096                let rd_bits = reg_to_bits(rd);
2097                let base_bits = reg_to_bits(&addr.base);
2098
2099                if let Some(offset_reg) = &addr.offset_reg {
2100                    if addr.offset != 0 {
2101                        let scratch = Reg::R12;
2102                        let mut bytes =
2103                            self.encode_thumb32_add_imm(&scratch, offset_reg, addr.offset as u32)?;
2104                        bytes.extend(self.encode_thumb32_ldrb_reg(rd, &addr.base, &scratch)?);
2105                        return Ok(bytes);
2106                    }
2107                    return self.encode_thumb32_ldrb_reg(rd, &addr.base, offset_reg);
2108                }
2109
2110                let offset = addr.offset as u32;
2111                if rd_bits < 8 && base_bits < 8 && offset <= 31 {
2112                    // LDRB Rd, [Rn, #imm5] (16-bit): 0111 1 imm5 Rn Rd
2113                    let instr: u16 = 0x7800
2114                        | ((offset as u16) << 6)
2115                        | ((base_bits as u16) << 3)
2116                        | (rd_bits as u16);
2117                    Ok(instr.to_le_bytes().to_vec())
2118                } else {
2119                    self.encode_thumb32_ldrb_imm(rd, &addr.base, offset)
2120                }
2121            }
2122
2123            // LDRSB (Thumb-2)
2124            ArmOp::Ldrsb { rd, addr } => {
2125                let rd_bits = reg_to_bits(rd);
2126                let base_bits = reg_to_bits(&addr.base);
2127
2128                if let Some(offset_reg) = &addr.offset_reg {
2129                    if addr.offset != 0 {
2130                        let scratch = Reg::R12;
2131                        let mut bytes =
2132                            self.encode_thumb32_add_imm(&scratch, offset_reg, addr.offset as u32)?;
2133                        bytes.extend(self.encode_thumb32_ldrsb_reg(rd, &addr.base, &scratch)?);
2134                        return Ok(bytes);
2135                    }
2136                    return self.encode_thumb32_ldrsb_reg(rd, &addr.base, offset_reg);
2137                }
2138
2139                let offset = addr.offset as u32;
2140                // LDRSB has no 16-bit immediate form (only register)
2141                // For 16-bit reg form: only if Rd, Rn, Rm < R8
2142                if rd_bits < 8 && base_bits < 8 && offset == 0 {
2143                    // No immediate 16-bit encoding for LDRSB; use 32-bit
2144                    self.encode_thumb32_ldrsb_imm(rd, &addr.base, offset)
2145                } else {
2146                    self.encode_thumb32_ldrsb_imm(rd, &addr.base, offset)
2147                }
2148            }
2149
2150            // LDRH (Thumb-2)
2151            ArmOp::Ldrh { rd, addr } => {
2152                let rd_bits = reg_to_bits(rd);
2153                let base_bits = reg_to_bits(&addr.base);
2154
2155                if let Some(offset_reg) = &addr.offset_reg {
2156                    if addr.offset != 0 {
2157                        let scratch = Reg::R12;
2158                        let mut bytes =
2159                            self.encode_thumb32_add_imm(&scratch, offset_reg, addr.offset as u32)?;
2160                        bytes.extend(self.encode_thumb32_ldrh_reg(rd, &addr.base, &scratch)?);
2161                        return Ok(bytes);
2162                    }
2163                    return self.encode_thumb32_ldrh_reg(rd, &addr.base, offset_reg);
2164                }
2165
2166                let offset = addr.offset as u32;
2167                if rd_bits < 8 && base_bits < 8 && (offset & 0x1) == 0 && offset <= 62 {
2168                    // LDRH Rd, [Rn, #imm5*2] (16-bit): 1000 1 imm5 Rn Rd
2169                    let imm5 = (offset >> 1) as u16;
2170                    let instr: u16 =
2171                        0x8800 | (imm5 << 6) | ((base_bits as u16) << 3) | (rd_bits as u16);
2172                    Ok(instr.to_le_bytes().to_vec())
2173                } else {
2174                    self.encode_thumb32_ldrh_imm(rd, &addr.base, offset)
2175                }
2176            }
2177
2178            // LDRSH (Thumb-2)
2179            ArmOp::Ldrsh { rd, addr } => {
2180                if let Some(offset_reg) = &addr.offset_reg {
2181                    if addr.offset != 0 {
2182                        let scratch = Reg::R12;
2183                        let mut bytes =
2184                            self.encode_thumb32_add_imm(&scratch, offset_reg, addr.offset as u32)?;
2185                        bytes.extend(self.encode_thumb32_ldrsh_reg(rd, &addr.base, &scratch)?);
2186                        return Ok(bytes);
2187                    }
2188                    return self.encode_thumb32_ldrsh_reg(rd, &addr.base, offset_reg);
2189                }
2190
2191                let offset = addr.offset as u32;
2192                self.encode_thumb32_ldrsh_imm(rd, &addr.base, offset)
2193            }
2194
2195            // STRB (Thumb-2)
2196            ArmOp::Strb { rd, addr } => {
2197                let rd_bits = reg_to_bits(rd);
2198                let base_bits = reg_to_bits(&addr.base);
2199
2200                if let Some(offset_reg) = &addr.offset_reg {
2201                    if addr.offset != 0 {
2202                        let scratch = Reg::R12;
2203                        let mut bytes =
2204                            self.encode_thumb32_add_imm(&scratch, offset_reg, addr.offset as u32)?;
2205                        bytes.extend(self.encode_thumb32_strb_reg(rd, &addr.base, &scratch)?);
2206                        return Ok(bytes);
2207                    }
2208                    return self.encode_thumb32_strb_reg(rd, &addr.base, offset_reg);
2209                }
2210
2211                let offset = addr.offset as u32;
2212                if rd_bits < 8 && base_bits < 8 && offset <= 31 {
2213                    // STRB Rd, [Rn, #imm5] (16-bit): 0111 0 imm5 Rn Rd
2214                    let instr: u16 = 0x7000
2215                        | ((offset as u16) << 6)
2216                        | ((base_bits as u16) << 3)
2217                        | (rd_bits as u16);
2218                    Ok(instr.to_le_bytes().to_vec())
2219                } else {
2220                    self.encode_thumb32_strb_imm(rd, &addr.base, offset)
2221                }
2222            }
2223
2224            // STRH (Thumb-2)
2225            ArmOp::Strh { rd, addr } => {
2226                let rd_bits = reg_to_bits(rd);
2227                let base_bits = reg_to_bits(&addr.base);
2228
2229                if let Some(offset_reg) = &addr.offset_reg {
2230                    if addr.offset != 0 {
2231                        let scratch = Reg::R12;
2232                        let mut bytes =
2233                            self.encode_thumb32_add_imm(&scratch, offset_reg, addr.offset as u32)?;
2234                        bytes.extend(self.encode_thumb32_strh_reg(rd, &addr.base, &scratch)?);
2235                        return Ok(bytes);
2236                    }
2237                    return self.encode_thumb32_strh_reg(rd, &addr.base, offset_reg);
2238                }
2239
2240                let offset = addr.offset as u32;
2241                if rd_bits < 8 && base_bits < 8 && (offset & 0x1) == 0 && offset <= 62 {
2242                    // STRH Rd, [Rn, #imm5*2] (16-bit): 1000 0 imm5 Rn Rd
2243                    let imm5 = (offset >> 1) as u16;
2244                    let instr: u16 =
2245                        0x8000 | (imm5 << 6) | ((base_bits as u16) << 3) | (rd_bits as u16);
2246                    Ok(instr.to_le_bytes().to_vec())
2247                } else {
2248                    self.encode_thumb32_strh_imm(rd, &addr.base, offset)
2249                }
2250            }
2251
2252            // MemorySize (Thumb-2)
2253            ArmOp::MemorySize { rd } => {
2254                // LSR rd, R10, #16 — memory size in bytes / 65536 = pages
2255                // Thumb-2 16-bit: LSRS Rd, Rm, #imm5 — 0000 1 imm5 Rm Rd
2256                let rd_bits = reg_to_bits(rd);
2257                let r10_bits = reg_to_bits(&Reg::R10);
2258                if rd_bits < 8 && r10_bits < 8 {
2259                    let instr: u16 =
2260                        0x0800 | (16u16 << 6) | ((r10_bits as u16) << 3) | (rd_bits as u16);
2261                    Ok(instr.to_le_bytes().to_vec())
2262                } else {
2263                    // Thumb-2 32-bit LSR: 1110 1010 010 0 1111 | 0 imm3 Rd imm2 01 Rm
2264                    let imm5: u32 = 16;
2265                    let imm3 = (imm5 >> 2) & 0x7;
2266                    let imm2 = imm5 & 0x3;
2267                    let hw1: u16 = 0xEA4F;
2268                    let hw2: u16 =
2269                        ((imm3 << 12) | (rd_bits << 8) | (imm2 << 6) | 0x10 | r10_bits) as u16;
2270                    let mut bytes = hw1.to_le_bytes().to_vec();
2271                    bytes.extend_from_slice(&hw2.to_le_bytes());
2272                    Ok(bytes)
2273                }
2274            }
2275
2276            // MemoryGrow (Thumb-2)
2277            ArmOp::MemoryGrow { rd, .. } => {
2278                // On embedded with fixed memory, always return -1 (failure)
2279                // MVN rd, #0 → MOV rd, #-1
2280                // Thumb-2 32-bit: MVN: 1111 0 i 0 0 0 1 1 0 1111 | 0 imm3 Rd imm8
2281                let rd_bits = reg_to_bits(rd);
2282                let hw1: u16 = 0xF06F; // MVN with i=0
2283                let hw2: u16 = (rd_bits << 8) as u16; // imm8=0 → ~0 = 0xFFFFFFFF = -1
2284                let mut bytes = hw1.to_le_bytes().to_vec();
2285                bytes.extend_from_slice(&hw2.to_le_bytes());
2286                Ok(bytes)
2287            }
2288
2289            // BX (16-bit)
2290            ArmOp::Bx { rm } => {
2291                let rm_bits = reg_to_bits(rm) as u16;
2292                // BX Rm (16-bit): 0100 0111 0 Rm 000
2293                let instr: u16 = 0x4700 | (rm_bits << 3);
2294                Ok(instr.to_le_bytes().to_vec())
2295            }
2296
2297            // BLX (16-bit) - Branch with Link and Exchange
2298            // BLX Rm: 0100 0111 1 Rm 000
2299            ArmOp::Blx { rm } => {
2300                let rm_bits = reg_to_bits(rm) as u16;
2301                let instr: u16 = 0x4780 | (rm_bits << 3);
2302                Ok(instr.to_le_bytes().to_vec())
2303            }
2304
2305            // CallIndirect - indirect function call via table lookup
2306            // table_index_reg contains the table index
2307            // Generates: LSL R12, idx, #2; LDR R12, [R12, table_base]; BLX R12
2308            ArmOp::CallIndirect {
2309                rd: _,
2310                type_idx: _,
2311                table_index_reg,
2312            } => {
2313                let idx_reg = reg_to_bits(table_index_reg);
2314                let mut bytes = Vec::new();
2315
2316                // For now, we generate code that:
2317                // 1. Multiplies index by 4 (function pointer size)
2318                // 2. Loads function pointer from table (assumes table base in R11)
2319                // 3. Calls the function via BLX
2320                //
2321                // Table base setup must be done by caller/runtime.
2322                // This is a simplified implementation - full support needs:
2323                // - Table base address resolution
2324                // - Type signature checking
2325                // - Bounds checking
2326
2327                // LSL R12, idx_reg, #2 (multiply index by 4)
2328                // Thumb-2 MOV with shift: 11101010 010 S 1111 | 0 imm3 Rd imm2 type Rm
2329                // LSL: type=00, imm5=2 -> imm3=0, imm2=10
2330                let hw1: u16 = 0xEA4F_u16; // MOV.W R12, Rm, LSL #2
2331                let hw2: u16 = ((0x0C00 | (0b10 << 4)) | idx_reg) as u16;
2332                bytes.extend_from_slice(&hw1.to_le_bytes());
2333                bytes.extend_from_slice(&hw2.to_le_bytes());
2334
2335                // LDR R12, [R11, R12] - load function pointer
2336                // Thumb-2 LDR (register): 1111 1000 0101 Rn | Rt 0000 00 imm2 Rm
2337                // Rn=R11, Rt=R12, Rm=R12, imm2=00 (no shift)
2338                let ldr_hw1: u16 = 0xF85B; // LDR.W Rt, [R11, Rm]
2339                let ldr_hw2: u16 = 0xC00C; // Rt=R12, imm2=00, Rm=R12
2340                bytes.extend_from_slice(&ldr_hw1.to_le_bytes());
2341                bytes.extend_from_slice(&ldr_hw2.to_le_bytes());
2342
2343                // BLX R12 (call function indirectly)
2344                // BLX Rm (16-bit): 0100 0111 1 Rm 000
2345                let blx: u16 = 0x47E0; // BLX R12
2346                bytes.extend_from_slice(&blx.to_le_bytes());
2347
2348                Ok(bytes)
2349            }
2350
2351            // Label pseudo-instruction: emits no machine code
2352            ArmOp::Label { .. } => Ok(Vec::new()),
2353
2354            // Conditional branch to label (generic) - offset 0, will be patched
2355            ArmOp::Bcc { cond, label: _ } => {
2356                use synth_synthesis::Condition;
2357                let cond_bits: u16 = match cond {
2358                    Condition::EQ => 0x0,
2359                    Condition::NE => 0x1,
2360                    Condition::HS => 0x2,
2361                    Condition::LO => 0x3,
2362                    Condition::HI => 0x8,
2363                    Condition::LS => 0x9,
2364                    Condition::GE => 0xA,
2365                    Condition::LT => 0xB,
2366                    Condition::GT => 0xC,
2367                    Condition::LE => 0xD,
2368                };
2369                // 16-bit B<cond> with offset 0: 1101 cond imm8
2370                let instr: u16 = 0xD000 | (cond_bits << 8);
2371                Ok(instr.to_le_bytes().to_vec())
2372            }
2373
2374            // Branch instructions
2375            ArmOp::B { label: _ } => {
2376                // Simplified: B.N with offset 0
2377                // For real usage, would need label resolution
2378                let instr: u16 = 0xE000; // B.N #0
2379                Ok(instr.to_le_bytes().to_vec())
2380            }
2381
2382            // BHS (Branch if Higher or Same) - used for bounds checking
2383            // Condition code: 0x2 (C set)
2384            ArmOp::Bhs { label: _ } => {
2385                // 16-bit B<cond> with offset 0: 1101 cond imm8
2386                // cond = 0x2 (HS)
2387                let instr: u16 = 0xD200; // BHS.N #0
2388                Ok(instr.to_le_bytes().to_vec())
2389            }
2390
2391            // BLO (Branch if Lower) - complementary to BHS
2392            // Condition code: 0x3 (C clear)
2393            ArmOp::Blo { label: _ } => {
2394                // 16-bit B<cond> with offset 0: 1101 cond imm8
2395                // cond = 0x3 (LO)
2396                let instr: u16 = 0xD300; // BLO.N #0
2397                Ok(instr.to_le_bytes().to_vec())
2398            }
2399
2400            // Branch with numeric offset (Thumb-2)
2401            // Thumb-2 B.W instruction: 32-bit with +-16MB range
2402            ArmOp::BOffset { offset } => {
2403                // offset is already the halfword displacement: (target - branch - 4) / 2
2404                // This is the raw encoded value, accounting for variable-length instructions
2405                let halfword_offset = *offset;
2406
2407                // 16-bit B.N encoding: 1110 0 imm11 (11-bit signed halfword offset)
2408                // Range: -1024 to +1022 halfwords
2409                if (-1024..=1022).contains(&halfword_offset) {
2410                    // 16-bit B.N encoding: 1110 0 imm11
2411                    let imm11 = (halfword_offset as u16) & 0x7FF;
2412                    let instr: u16 = 0xE000 | imm11;
2413                    Ok(instr.to_le_bytes().to_vec())
2414                } else {
2415                    // 32-bit B.W encoding for larger offsets
2416                    // First halfword: 1111 0 S imm10
2417                    // Second halfword: 10 J1 0 J2 imm11
2418                    // Total offset = SignExtend(S:I1:I2:imm10:imm11:0)
2419                    // where I1 = NOT(J1 XOR S), I2 = NOT(J2 XOR S)
2420
2421                    // The B.W (T4) encoding packs the signed offset as:
2422                    //   S:I1:I2:imm10:imm11:0  (25-bit signed, halfword-aligned)
2423                    // where J1 = NOT(I1 XOR S), J2 = NOT(I2 XOR S)
2424                    // Input halfword_offset already equals (target - PC - 4) / 2,
2425                    // so the full byte offset = halfword_offset << 1.
2426                    // The encoding fields split that 25-bit signed value (including the
2427                    // implicit trailing zero) as: S | imm10 | imm11
2428                    // with I1 = bit 23 and I2 = bit 22 of the signed offset.
2429                    let signed_offset = halfword_offset << 1; // byte offset
2430                    let s = if signed_offset < 0 { 1u32 } else { 0u32 };
2431                    let uoffset = signed_offset as u32;
2432                    let imm10 = (uoffset >> 12) & 0x3FF; // bits [21:12]
2433                    let imm11 = (uoffset >> 1) & 0x7FF; // bits [11:1]
2434                    let i1 = (uoffset >> 23) & 1; // bit 23
2435                    let i2 = (uoffset >> 22) & 1; // bit 22
2436                    let j1 = (!(i1 ^ s)) & 1; // J1 = NOT(I1 XOR S)
2437                    let j2 = (!(i2 ^ s)) & 1; // J2 = NOT(I2 XOR S)
2438
2439                    let hw1: u16 = (0xF000 | (s << 10) | imm10) as u16;
2440                    let hw2: u16 = (0x9000 | (j1 << 13) | (j2 << 11) | imm11) as u16;
2441
2442                    let mut bytes = hw1.to_le_bytes().to_vec();
2443                    bytes.extend_from_slice(&hw2.to_le_bytes());
2444                    Ok(bytes)
2445                }
2446            }
2447
2448            // Conditional branch with numeric offset (Thumb-2)
2449            ArmOp::BCondOffset { cond, offset } => {
2450                use synth_synthesis::Condition;
2451                let cond_bits: u16 = match cond {
2452                    Condition::EQ => 0x0,
2453                    Condition::NE => 0x1,
2454                    Condition::HS => 0x2,
2455                    Condition::LO => 0x3,
2456                    Condition::HI => 0x8,
2457                    Condition::LS => 0x9,
2458                    Condition::GE => 0xA,
2459                    Condition::LT => 0xB,
2460                    Condition::GT => 0xC,
2461                    Condition::LE => 0xD,
2462                };
2463
2464                // offset is already the halfword displacement: (target - branch - 4) / 2
2465                // This is the raw imm8 value for 16-bit B<cond> encoding
2466                let halfword_offset = *offset;
2467
2468                // 16-bit B<cond> encoding: 1101 cond imm8
2469                // Range: -256 to +254 halfwords (imm8 is sign-extended and shifted left 1)
2470                if (-128..=127).contains(&halfword_offset) {
2471                    let imm8 = (halfword_offset as u16) & 0xFF;
2472                    let instr: u16 = 0xD000 | (cond_bits << 8) | imm8;
2473                    Ok(instr.to_le_bytes().to_vec())
2474                } else {
2475                    // 32-bit B<cond>.W for larger offsets
2476                    // First halfword: 1111 0 S cond imm6
2477                    // Second halfword: 10 J1 0 J2 imm11
2478                    let offset = halfword_offset >> 1;
2479                    let s = if offset < 0 { 1u32 } else { 0u32 };
2480                    let imm6 = ((offset >> 11) as u32) & 0x3F;
2481                    let imm11 = (offset as u32) & 0x7FF;
2482                    let j1 = if s == 1 { 1 } else { 0 };
2483                    let j2 = if s == 1 { 1 } else { 0 };
2484
2485                    let hw1: u16 = (0xF000 | (s << 10) | ((cond_bits as u32) << 6) | imm6) as u16;
2486                    let hw2: u16 = (0x8000 | (j1 << 13) | (j2 << 11) | imm11) as u16;
2487
2488                    let mut bytes = hw1.to_le_bytes().to_vec();
2489                    bytes.extend_from_slice(&hw2.to_le_bytes());
2490                    Ok(bytes)
2491                }
2492            }
2493
2494            ArmOp::Bl { label: _ } => {
2495                // BL is always 32-bit in Thumb-2, encoded here as a relocatable
2496                // placeholder; an R_ARM_THM_CALL relocation patches the target
2497                // (see arm_backend.rs). The placeholder must carry an embedded
2498                // addend of -4 so the relocation nets to exactly the symbol S.
2499                //
2500                // Thumb BL computes `target = (P + 4) + signed_offset`. Under
2501                // R_ARM_THM_CALL the linker resolves using the in-place addend;
2502                // a 0xF800 placeholder (addend 0) lands at S+4 — every call one
2503                // instruction past the callee entry (#174). The correct
2504                // placeholder is what `gas` emits for `bl <extern>`:
2505                //   f7ff fffe  ->  `bl <self>`  (S=1, J1=J2=1, imm = -4 addend),
2506                // i.e. hw1=0xF7FF, hw2=0xFFFE. This nets to S, not S+4.
2507                // (The earlier 0xD000 was worse still — a ~+0x600000 addend,
2508                // the garbage `bl c0000c` and "truncated to fit" of #167.)
2509                let hw1: u16 = 0xF7FF;
2510                let hw2: u16 = 0xFFFE;
2511                let mut bytes = hw1.to_le_bytes().to_vec();
2512                bytes.extend_from_slice(&hw2.to_le_bytes());
2513                Ok(bytes)
2514            }
2515
2516            // MVN
2517            ArmOp::Mvn { rd, op2 } => {
2518                if let Operand2::Reg(rm) = op2 {
2519                    let rd_bits = reg_to_bits(rd) as u16;
2520                    let rm_bits = reg_to_bits(rm) as u16;
2521
2522                    if rd_bits < 8 && rm_bits < 8 {
2523                        // MVNS Rd, Rm (16-bit): 0100 0011 11 Rm Rd
2524                        let instr: u16 = 0x43C0 | (rm_bits << 3) | rd_bits;
2525                        Ok(instr.to_le_bytes().to_vec())
2526                    } else {
2527                        // 32-bit MVN
2528                        let hw1: u16 = 0xEA6F_u16;
2529                        let hw2: u16 = ((reg_to_bits(rd) << 8) | reg_to_bits(rm)) as u16;
2530                        let mut bytes = hw1.to_le_bytes().to_vec();
2531                        bytes.extend_from_slice(&hw2.to_le_bytes());
2532                        Ok(bytes)
2533                    }
2534                } else {
2535                    let instr: u16 = 0xBF00;
2536                    Ok(instr.to_le_bytes().to_vec())
2537                }
2538            }
2539
2540            // MOVW - Move Wide (Thumb-2 32-bit)
2541            ArmOp::Movw { rd, imm16 } => {
2542                self.encode_thumb32_movw_raw(reg_to_bits(rd), *imm16 as u32)
2543            }
2544
2545            // MOVT - Move Top (Thumb-2 32-bit)
2546            ArmOp::Movt { rd, imm16 } => {
2547                self.encode_thumb32_movt_raw(reg_to_bits(rd), *imm16 as u32)
2548            }
2549
2550            // SetCond: Materialize condition flag into register (0 or 1)
2551            // Strategy: ITE <cond>; MOV Rd, #1; MOV Rd, #0
2552            // IMPORTANT: Must use ITE (If-Then-Else) because 16-bit Thumb MOV
2553            // always sets flags (MOVS). We need to evaluate the condition BEFORE
2554            // any MOV instruction clobbers the flags from CMP.
2555            ArmOp::SetCond { rd, cond } => {
2556                let rd_bits = reg_to_bits(rd) as u16;
2557
2558                // Condition code encoding for IT block
2559                use synth_synthesis::Condition;
2560                let cond_bits: u16 = match cond {
2561                    Condition::EQ => 0x0,
2562                    Condition::NE => 0x1,
2563                    Condition::LT => 0xB,
2564                    Condition::LE => 0xD,
2565                    Condition::GT => 0xC,
2566                    Condition::GE => 0xA,
2567                    Condition::LO => 0x3, // CC/LO (unsigned <)
2568                    Condition::LS => 0x9, // LS (unsigned <=)
2569                    Condition::HI => 0x8, // HI (unsigned >)
2570                    Condition::HS => 0x2, // CS/HS (unsigned >=)
2571                };
2572
2573                // ITE <cond>: encodes If-Then-Else block
2574                // The mask field depends on firstcond[0]:
2575                // - If firstcond[0] = 0: mask = 0xC for TE pattern (ITE EQ = BF0C)
2576                // - If firstcond[0] = 1: mask = 0x4 for TE pattern (ITE NE = BF14)
2577                let mask = if (cond_bits & 1) == 0 { 0xC } else { 0x4 };
2578                let ite_instr: u16 = 0xBF00 | (cond_bits << 4) | mask;
2579
2580                // MOV Rd, #1 (Then branch - condition true)
2581                let mov_one: u16 = 0x2001 | (rd_bits << 8);
2582
2583                // MOV Rd, #0 (Else branch - condition false)
2584                let mov_zero: u16 = 0x2000 | (rd_bits << 8);
2585
2586                // Emit: ITE, MOV #1 (Then), MOV #0 (Else)
2587                let mut bytes = ite_instr.to_le_bytes().to_vec();
2588                bytes.extend_from_slice(&mov_one.to_le_bytes());
2589                bytes.extend_from_slice(&mov_zero.to_le_bytes());
2590                Ok(bytes)
2591            }
2592
2593            // I64SetCond: Compare two i64 register pairs, result 0/1 in rd
2594            // EQ/NE: CMP lo,lo; IT EQ; CMPEQ hi,hi; ITE <cond>; MOV 1; MOV 0
2595            // LT: CMP lo,lo; SBCS rd,hi,hi; ITE LT; MOV 1; MOV 0
2596            // GT: CMP lo,lo (swapped); SBCS rd,hi,hi (swapped); ITE LT; MOV 1; MOV 0
2597            ArmOp::I64SetCond {
2598                rd,
2599                rn_lo,
2600                rn_hi,
2601                rm_lo,
2602                rm_hi,
2603                cond,
2604            } => {
2605                use synth_synthesis::Condition;
2606                let rd_bits = reg_to_bits(rd) as u16;
2607                let mut bytes = Vec::new();
2608
2609                // Helper: encode CMP Rn, Rm (16-bit)
2610                let encode_cmp_reg = |rn: &synth_synthesis::Reg,
2611                                      rm: &synth_synthesis::Reg|
2612                 -> Vec<u8> {
2613                    let rn_bits = reg_to_bits(rn) as u16;
2614                    let rm_bits = reg_to_bits(rm) as u16;
2615                    if rn_bits < 8 && rm_bits < 8 {
2616                        let instr: u16 = 0x4280 | (rm_bits << 3) | rn_bits;
2617                        instr.to_le_bytes().to_vec()
2618                    } else {
2619                        let n_bit = (rn_bits >> 3) & 1;
2620                        let instr: u16 = 0x4500 | (n_bit << 7) | (rm_bits << 3) | (rn_bits & 0x7);
2621                        instr.to_le_bytes().to_vec()
2622                    }
2623                };
2624
2625                // Helper: encode ITE <cond> (2 bytes)
2626                let encode_ite = |cond_bits: u16| -> Vec<u8> {
2627                    let mask = if (cond_bits & 1) == 0 { 0xC } else { 0x4 };
2628                    let ite_instr: u16 = 0xBF00 | (cond_bits << 4) | mask;
2629                    ite_instr.to_le_bytes().to_vec()
2630                };
2631
2632                // Helper: encode SetCond (ITE + MOV #1 + MOV #0) for given condition
2633                let encode_setcond = |cond_bits: u16, rd_bits: u16| -> Vec<u8> {
2634                    let mut b = encode_ite(cond_bits);
2635                    let mov_one: u16 = 0x2001 | (rd_bits << 8);
2636                    let mov_zero: u16 = 0x2000 | (rd_bits << 8);
2637                    b.extend_from_slice(&mov_one.to_le_bytes());
2638                    b.extend_from_slice(&mov_zero.to_le_bytes());
2639                    b
2640                };
2641
2642                match cond {
2643                    Condition::EQ | Condition::NE => {
2644                        // CMP rn_lo, rm_lo (compare low words)
2645                        bytes.extend_from_slice(&encode_cmp_reg(rn_lo, rm_lo));
2646
2647                        // IT EQ (execute next instruction only if Z=1)
2648                        let it_eq: u16 = 0xBF08; // IT EQ: cond=0000, mask=1000
2649                        bytes.extend_from_slice(&it_eq.to_le_bytes());
2650
2651                        // CMPEQ rn_hi, rm_hi (compare high words, only if low equal)
2652                        bytes.extend_from_slice(&encode_cmp_reg(rn_hi, rm_hi));
2653
2654                        // ITE <cond>; MOV rd, #1; MOV rd, #0
2655                        let cond_bits: u16 = match cond {
2656                            Condition::EQ => 0x0,
2657                            Condition::NE => 0x1,
2658                            _ => unreachable!(),
2659                        };
2660                        bytes.extend_from_slice(&encode_setcond(cond_bits, rd_bits));
2661                    }
2662
2663                    Condition::LT => {
2664                        // CMP rn_lo, rm_lo (sets C flag for borrow)
2665                        bytes.extend_from_slice(&encode_cmp_reg(rn_lo, rm_lo));
2666
2667                        // SBCS rd, rn_hi, rm_hi (subtract with carry, sets N,V flags)
2668                        // SBCS.W Rd, Rn, Rm: EB70 Rn | 0000 Rd 0000 Rm
2669                        let rn_hi_bits = reg_to_bits(rn_hi);
2670                        let rm_hi_bits = reg_to_bits(rm_hi);
2671                        let hw1: u16 = (0xEB70 | rn_hi_bits) as u16;
2672                        let hw2: u16 = ((rd_bits as u32) << 8 | rm_hi_bits) as u16;
2673                        bytes.extend_from_slice(&hw1.to_le_bytes());
2674                        bytes.extend_from_slice(&hw2.to_le_bytes());
2675
2676                        // ITE LT; MOV rd, #1; MOV rd, #0
2677                        bytes.extend_from_slice(&encode_setcond(0xB, rd_bits)); // LT = 0xB
2678                    }
2679
2680                    Condition::GT => {
2681                        // GT(a,b) = LT(b,a): swap operands
2682                        // CMP rm_lo, rn_lo (swapped)
2683                        bytes.extend_from_slice(&encode_cmp_reg(rm_lo, rn_lo));
2684
2685                        // SBCS rd, rm_hi, rn_hi (swapped)
2686                        let rm_hi_bits = reg_to_bits(rm_hi);
2687                        let rn_hi_bits = reg_to_bits(rn_hi);
2688                        let hw1: u16 = (0xEB70 | rm_hi_bits) as u16;
2689                        let hw2: u16 = ((rd_bits as u32) << 8 | rn_hi_bits) as u16;
2690                        bytes.extend_from_slice(&hw1.to_le_bytes());
2691                        bytes.extend_from_slice(&hw2.to_le_bytes());
2692
2693                        // ITE LT; MOV rd, #1; MOV rd, #0
2694                        bytes.extend_from_slice(&encode_setcond(0xB, rd_bits)); // LT = 0xB
2695                    }
2696
2697                    Condition::LE => {
2698                        // LE(a,b) = !GT(a,b): use GT logic but invert result
2699                        // GT(a,b) = LT(b,a): so we do CMP(b,a) and check LT, then invert
2700                        // CMP rm_lo, rn_lo (swapped, same as GT)
2701                        bytes.extend_from_slice(&encode_cmp_reg(rm_lo, rn_lo));
2702
2703                        // SBCS rd, rm_hi, rn_hi (swapped)
2704                        let rm_hi_bits = reg_to_bits(rm_hi);
2705                        let rn_hi_bits = reg_to_bits(rn_hi);
2706                        let hw1: u16 = (0xEB70 | rm_hi_bits) as u16;
2707                        let hw2: u16 = ((rd_bits as u32) << 8 | rn_hi_bits) as u16;
2708                        bytes.extend_from_slice(&hw1.to_le_bytes());
2709                        bytes.extend_from_slice(&hw2.to_le_bytes());
2710
2711                        // ITE GE; MOV rd, #1; MOV rd, #0 (GE is !LT, so inverting GT result)
2712                        bytes.extend_from_slice(&encode_setcond(0xA, rd_bits)); // GE = 0xA
2713                    }
2714
2715                    Condition::GE => {
2716                        // GE(a,b) = !LT(a,b): use LT logic but invert result
2717                        // CMP rn_lo, rm_lo (same as LT)
2718                        bytes.extend_from_slice(&encode_cmp_reg(rn_lo, rm_lo));
2719
2720                        // SBCS rd, rn_hi, rm_hi (same as LT)
2721                        let rn_hi_bits = reg_to_bits(rn_hi);
2722                        let rm_hi_bits = reg_to_bits(rm_hi);
2723                        let hw1: u16 = (0xEB70 | rn_hi_bits) as u16;
2724                        let hw2: u16 = ((rd_bits as u32) << 8 | rm_hi_bits) as u16;
2725                        bytes.extend_from_slice(&hw1.to_le_bytes());
2726                        bytes.extend_from_slice(&hw2.to_le_bytes());
2727
2728                        // ITE GE; MOV rd, #1; MOV rd, #0 (GE is !LT)
2729                        bytes.extend_from_slice(&encode_setcond(0xA, rd_bits)); // GE = 0xA
2730                    }
2731
2732                    // Unsigned comparisons - same instruction sequence, different conditions
2733                    Condition::LO => {
2734                        // LO (unsigned LT): CMP lo, SBCS hi, check C=0
2735                        bytes.extend_from_slice(&encode_cmp_reg(rn_lo, rm_lo));
2736                        let rn_hi_bits = reg_to_bits(rn_hi);
2737                        let rm_hi_bits = reg_to_bits(rm_hi);
2738                        let hw1: u16 = (0xEB70 | rn_hi_bits) as u16;
2739                        let hw2: u16 = ((rd_bits as u32) << 8 | rm_hi_bits) as u16;
2740                        bytes.extend_from_slice(&hw1.to_le_bytes());
2741                        bytes.extend_from_slice(&hw2.to_le_bytes());
2742                        bytes.extend_from_slice(&encode_setcond(0x3, rd_bits)); // LO = 0x3 (CC)
2743                    }
2744
2745                    Condition::HI => {
2746                        // HI (unsigned GT): swap operands and check LO
2747                        bytes.extend_from_slice(&encode_cmp_reg(rm_lo, rn_lo));
2748                        let rm_hi_bits = reg_to_bits(rm_hi);
2749                        let rn_hi_bits = reg_to_bits(rn_hi);
2750                        let hw1: u16 = (0xEB70 | rm_hi_bits) as u16;
2751                        let hw2: u16 = ((rd_bits as u32) << 8 | rn_hi_bits) as u16;
2752                        bytes.extend_from_slice(&hw1.to_le_bytes());
2753                        bytes.extend_from_slice(&hw2.to_le_bytes());
2754                        bytes.extend_from_slice(&encode_setcond(0x3, rd_bits)); // LO = 0x3 (CC)
2755                    }
2756
2757                    Condition::LS => {
2758                        // LS (unsigned LE): !(a > b) = !(HI), so do HI and invert
2759                        bytes.extend_from_slice(&encode_cmp_reg(rm_lo, rn_lo));
2760                        let rm_hi_bits = reg_to_bits(rm_hi);
2761                        let rn_hi_bits = reg_to_bits(rn_hi);
2762                        let hw1: u16 = (0xEB70 | rm_hi_bits) as u16;
2763                        let hw2: u16 = ((rd_bits as u32) << 8 | rn_hi_bits) as u16;
2764                        bytes.extend_from_slice(&hw1.to_le_bytes());
2765                        bytes.extend_from_slice(&hw2.to_le_bytes());
2766                        bytes.extend_from_slice(&encode_setcond(0x2, rd_bits)); // HS = 0x2 (CS) = !LO
2767                    }
2768
2769                    Condition::HS => {
2770                        // HS (unsigned GE): !(a < b) = !(LO)
2771                        bytes.extend_from_slice(&encode_cmp_reg(rn_lo, rm_lo));
2772                        let rn_hi_bits = reg_to_bits(rn_hi);
2773                        let rm_hi_bits = reg_to_bits(rm_hi);
2774                        let hw1: u16 = (0xEB70 | rn_hi_bits) as u16;
2775                        let hw2: u16 = ((rd_bits as u32) << 8 | rm_hi_bits) as u16;
2776                        bytes.extend_from_slice(&hw1.to_le_bytes());
2777                        bytes.extend_from_slice(&hw2.to_le_bytes());
2778                        bytes.extend_from_slice(&encode_setcond(0x2, rd_bits)); // HS = 0x2 (CS) = !LO
2779                    }
2780                }
2781
2782                Ok(bytes)
2783            }
2784
2785            // I64SetCondZ: Test if i64 register pair is zero, result 0/1 in rd
2786            // ORR.W rd, rn_lo, rn_hi; CMP rd, #0; ITE EQ; MOV 1; MOV 0
2787            ArmOp::I64SetCondZ { rd, rn_lo, rn_hi } => {
2788                let rd_bits = reg_to_bits(rd);
2789                let rn_lo_bits = reg_to_bits(rn_lo);
2790                let rn_hi_bits = reg_to_bits(rn_hi);
2791                let mut bytes = Vec::new();
2792
2793                // ORR.W rd, rn_lo, rn_hi: EA40 rn_lo | 0000 rd 0000 rn_hi
2794                let hw1: u16 = (0xEA40 | rn_lo_bits) as u16;
2795                let hw2: u16 = ((rd_bits << 8) | rn_hi_bits) as u16;
2796                bytes.extend_from_slice(&hw1.to_le_bytes());
2797                bytes.extend_from_slice(&hw2.to_le_bytes());
2798
2799                // CMP rd, #0 (16-bit): 0010 1 Rd 0000 0000
2800                let cmp_instr: u16 = 0x2800 | ((rd_bits as u16) << 8);
2801                bytes.extend_from_slice(&cmp_instr.to_le_bytes());
2802
2803                // ITE EQ; MOV rd, #1; MOV rd, #0
2804                let mask = 0xC_u16; // ITE EQ mask: firstcond[0]=0, mask=0xC
2805                let ite_instr: u16 = 0xBF00 | mask;
2806                bytes.extend_from_slice(&ite_instr.to_le_bytes());
2807                let mov_one: u16 = 0x2001 | ((rd_bits as u16) << 8);
2808                let mov_zero: u16 = 0x2000 | ((rd_bits as u16) << 8);
2809                bytes.extend_from_slice(&mov_one.to_le_bytes());
2810                bytes.extend_from_slice(&mov_zero.to_le_bytes());
2811
2812                Ok(bytes)
2813            }
2814
2815            // I64Mul: 64-bit multiply using UMULL + MLA cross products
2816            // Formula: result = (a_lo * b_lo) + ((a_lo * b_hi + a_hi * b_lo) << 32)
2817            // Uses R12 as scratch register
2818            ArmOp::I64Mul {
2819                rd_lo,
2820                rd_hi,
2821                rn_lo,
2822                rn_hi,
2823                rm_lo,
2824                rm_hi,
2825            } => {
2826                let rd_lo_bits = reg_to_bits(rd_lo);
2827                let rd_hi_bits = reg_to_bits(rd_hi);
2828                let rn_lo_bits = reg_to_bits(rn_lo);
2829                let rn_hi_bits = reg_to_bits(rn_hi);
2830                let rm_lo_bits = reg_to_bits(rm_lo);
2831                let rm_hi_bits = reg_to_bits(rm_hi);
2832                let r12: u32 = 12; // IP scratch register
2833                let mut bytes = Vec::new();
2834
2835                // 1. MUL R12, rn_lo, rm_hi  (R12 = a_lo * b_hi)
2836                // Thumb-2 MUL: hw1=0xFB00|Rn, hw2=0xF000|(Rd<<8)|Rm
2837                let hw1: u16 = (0xFB00 | rn_lo_bits) as u16;
2838                let hw2: u16 = (0xF000 | (r12 << 8) | rm_hi_bits) as u16;
2839                bytes.extend_from_slice(&hw1.to_le_bytes());
2840                bytes.extend_from_slice(&hw2.to_le_bytes());
2841
2842                // 2. MLA R12, rn_hi, rm_lo, R12  (R12 += a_hi * b_lo)
2843                // Thumb-2 MLA: hw1=0xFB00|Rn, hw2=(Ra<<12)|(Rd<<8)|Rm
2844                let hw1: u16 = (0xFB00 | rn_hi_bits) as u16;
2845                let hw2: u16 = ((r12 << 12) | (r12 << 8) | rm_lo_bits) as u16;
2846                bytes.extend_from_slice(&hw1.to_le_bytes());
2847                bytes.extend_from_slice(&hw2.to_le_bytes());
2848
2849                // 3. UMULL rd_lo, rd_hi, rn_lo, rm_lo  (rd_lo:rd_hi = a_lo * b_lo)
2850                // Thumb-2 UMULL: hw1=0xFBA0|Rn, hw2=(RdLo<<12)|(RdHi<<8)|Rm
2851                let hw1: u16 = (0xFBA0 | rn_lo_bits) as u16;
2852                let hw2: u16 = ((rd_lo_bits << 12) | (rd_hi_bits << 8) | rm_lo_bits) as u16;
2853                bytes.extend_from_slice(&hw1.to_le_bytes());
2854                bytes.extend_from_slice(&hw2.to_le_bytes());
2855
2856                // 4. ADD rd_hi, R12  (rd_hi += cross products)
2857                // 16-bit high reg ADD: 01000100 D Rm Rdn[2:0]
2858                let d_bit = (rd_hi_bits >> 3) & 1;
2859                let add_instr: u16 =
2860                    (0x4400 | (d_bit << 7) | (r12 << 3) | (rd_hi_bits & 0x7)) as u16;
2861                bytes.extend_from_slice(&add_instr.to_le_bytes());
2862
2863                Ok(bytes)
2864            }
2865
2866            // I64Shl: 64-bit shift left with branch for n<32 vs n>=32
2867            // rm_hi (R3) is used as temp register
2868            ArmOp::I64Shl {
2869                rd_lo,
2870                rd_hi,
2871                rn_lo,
2872                rn_hi,
2873                rm_lo,
2874                rm_hi,
2875            } => {
2876                let rd_lo_bits = reg_to_bits(rd_lo);
2877                let rd_hi_bits = reg_to_bits(rd_hi);
2878                let rn_lo_bits = reg_to_bits(rn_lo);
2879                let rn_hi_bits = reg_to_bits(rn_hi);
2880                let rm_lo_bits = reg_to_bits(rm_lo);
2881                let rm_hi_bits = reg_to_bits(rm_hi); // temp
2882                let mut bytes = Vec::new();
2883
2884                // AND.W rm_lo, rm_lo, #63  (mask shift amount to 6 bits)
2885                let hw1: u16 = (0xF000 | rm_lo_bits) as u16;
2886                let hw2: u16 = ((rm_lo_bits << 8) | 0x3F) as u16;
2887                bytes.extend_from_slice(&hw1.to_le_bytes());
2888                bytes.extend_from_slice(&hw2.to_le_bytes());
2889
2890                // SUBS.W rm_hi, rm_lo, #32  (rm_hi = n-32, sets flags)
2891                let hw1: u16 = (0xF1B0 | rm_lo_bits) as u16;
2892                let hw2: u16 = ((rm_hi_bits << 8) | 0x20) as u16;
2893                bytes.extend_from_slice(&hw1.to_le_bytes());
2894                bytes.extend_from_slice(&hw2.to_le_bytes());
2895
2896                // BPL .large (branch if n >= 32, offset = +10 halfwords)
2897                let bpl: u16 = 0xD50A;
2898                bytes.extend_from_slice(&bpl.to_le_bytes());
2899
2900                // --- Small shift (n < 32) ---
2901                // RSB.W rm_hi, rm_lo, #32  (rm_hi = 32-n)
2902                let hw1: u16 = (0xF1C0 | rm_lo_bits) as u16;
2903                let hw2: u16 = ((rm_hi_bits << 8) | 0x20) as u16;
2904                bytes.extend_from_slice(&hw1.to_le_bytes());
2905                bytes.extend_from_slice(&hw2.to_le_bytes());
2906
2907                // LSR.W rm_hi, rn_lo, rm_hi  (rm_hi = lo >> (32-n), overflow bits)
2908                let hw1: u16 = (0xFA20 | rn_lo_bits) as u16;
2909                let hw2: u16 = (0xF000 | (rm_hi_bits << 8) | rm_hi_bits) as u16;
2910                bytes.extend_from_slice(&hw1.to_le_bytes());
2911                bytes.extend_from_slice(&hw2.to_le_bytes());
2912
2913                // LSL.W rd_hi, rn_hi, rm_lo  (hi <<= n)
2914                let hw1: u16 = (0xFA00 | rn_hi_bits) as u16;
2915                let hw2: u16 = (0xF000 | (rd_hi_bits << 8) | rm_lo_bits) as u16;
2916                bytes.extend_from_slice(&hw1.to_le_bytes());
2917                bytes.extend_from_slice(&hw2.to_le_bytes());
2918
2919                // ORR.W rd_hi, rd_hi, rm_hi  (hi |= overflow bits from lo)
2920                let hw1: u16 = (0xEA40 | rd_hi_bits) as u16;
2921                let hw2: u16 = ((rd_hi_bits << 8) | rm_hi_bits) as u16;
2922                bytes.extend_from_slice(&hw1.to_le_bytes());
2923                bytes.extend_from_slice(&hw2.to_le_bytes());
2924
2925                // LSL.W rd_lo, rn_lo, rm_lo  (lo <<= n)
2926                let hw1: u16 = (0xFA00 | rn_lo_bits) as u16;
2927                let hw2: u16 = (0xF000 | (rd_lo_bits << 8) | rm_lo_bits) as u16;
2928                bytes.extend_from_slice(&hw1.to_le_bytes());
2929                bytes.extend_from_slice(&hw2.to_le_bytes());
2930
2931                // B .done (skip large shift: +2 halfwords)
2932                let b_done: u16 = 0xE002;
2933                bytes.extend_from_slice(&b_done.to_le_bytes());
2934
2935                // --- Large shift (n >= 32) ---
2936                // LSL.W rd_hi, rn_lo, rm_hi  (hi = lo << (n-32))
2937                let hw1: u16 = (0xFA00 | rn_lo_bits) as u16;
2938                let hw2: u16 = (0xF000 | (rd_hi_bits << 8) | rm_hi_bits) as u16;
2939                bytes.extend_from_slice(&hw1.to_le_bytes());
2940                bytes.extend_from_slice(&hw2.to_le_bytes());
2941
2942                // MOV rd_lo, #0
2943                let mov_zero: u16 = 0x2000 | ((rd_lo_bits as u16) << 8);
2944                bytes.extend_from_slice(&mov_zero.to_le_bytes());
2945
2946                Ok(bytes) // Total: 38 bytes
2947            }
2948
2949            // I64ShrU: 64-bit logical shift right with branch for n<32 vs n>=32
2950            ArmOp::I64ShrU {
2951                rd_lo,
2952                rd_hi,
2953                rn_lo,
2954                rn_hi,
2955                rm_lo,
2956                rm_hi,
2957            } => {
2958                let rd_lo_bits = reg_to_bits(rd_lo);
2959                let rd_hi_bits = reg_to_bits(rd_hi);
2960                let rn_lo_bits = reg_to_bits(rn_lo);
2961                let rn_hi_bits = reg_to_bits(rn_hi);
2962                let rm_lo_bits = reg_to_bits(rm_lo);
2963                let rm_hi_bits = reg_to_bits(rm_hi); // temp
2964                let mut bytes = Vec::new();
2965
2966                // AND.W rm_lo, rm_lo, #63
2967                let hw1: u16 = (0xF000 | rm_lo_bits) as u16;
2968                let hw2: u16 = ((rm_lo_bits << 8) | 0x3F) as u16;
2969                bytes.extend_from_slice(&hw1.to_le_bytes());
2970                bytes.extend_from_slice(&hw2.to_le_bytes());
2971
2972                // SUBS.W rm_hi, rm_lo, #32
2973                let hw1: u16 = (0xF1B0 | rm_lo_bits) as u16;
2974                let hw2: u16 = ((rm_hi_bits << 8) | 0x20) as u16;
2975                bytes.extend_from_slice(&hw1.to_le_bytes());
2976                bytes.extend_from_slice(&hw2.to_le_bytes());
2977
2978                // BPL .large (+10 halfwords)
2979                let bpl: u16 = 0xD50A;
2980                bytes.extend_from_slice(&bpl.to_le_bytes());
2981
2982                // --- Small shift (n < 32) ---
2983                // RSB.W rm_hi, rm_lo, #32  (rm_hi = 32-n)
2984                let hw1: u16 = (0xF1C0 | rm_lo_bits) as u16;
2985                let hw2: u16 = ((rm_hi_bits << 8) | 0x20) as u16;
2986                bytes.extend_from_slice(&hw1.to_le_bytes());
2987                bytes.extend_from_slice(&hw2.to_le_bytes());
2988
2989                // LSL.W rm_hi, rn_hi, rm_hi  (rm_hi = hi << (32-n), bits flowing to lo)
2990                let hw1: u16 = (0xFA00 | rn_hi_bits) as u16;
2991                let hw2: u16 = (0xF000 | (rm_hi_bits << 8) | rm_hi_bits) as u16;
2992                bytes.extend_from_slice(&hw1.to_le_bytes());
2993                bytes.extend_from_slice(&hw2.to_le_bytes());
2994
2995                // LSR.W rd_lo, rn_lo, rm_lo  (lo >>= n)
2996                let hw1: u16 = (0xFA20 | rn_lo_bits) as u16;
2997                let hw2: u16 = (0xF000 | (rd_lo_bits << 8) | rm_lo_bits) as u16;
2998                bytes.extend_from_slice(&hw1.to_le_bytes());
2999                bytes.extend_from_slice(&hw2.to_le_bytes());
3000
3001                // ORR.W rd_lo, rd_lo, rm_hi  (lo |= overflow from hi)
3002                let hw1: u16 = (0xEA40 | rd_lo_bits) as u16;
3003                let hw2: u16 = ((rd_lo_bits << 8) | rm_hi_bits) as u16;
3004                bytes.extend_from_slice(&hw1.to_le_bytes());
3005                bytes.extend_from_slice(&hw2.to_le_bytes());
3006
3007                // LSR.W rd_hi, rn_hi, rm_lo  (hi >>= n, logical)
3008                let hw1: u16 = (0xFA20 | rn_hi_bits) as u16;
3009                let hw2: u16 = (0xF000 | (rd_hi_bits << 8) | rm_lo_bits) as u16;
3010                bytes.extend_from_slice(&hw1.to_le_bytes());
3011                bytes.extend_from_slice(&hw2.to_le_bytes());
3012
3013                // B .done (+2 halfwords)
3014                let b_done: u16 = 0xE002;
3015                bytes.extend_from_slice(&b_done.to_le_bytes());
3016
3017                // --- Large shift (n >= 32) ---
3018                // LSR.W rd_lo, rn_hi, rm_hi  (lo = hi >> (n-32))
3019                let hw1: u16 = (0xFA20 | rn_hi_bits) as u16;
3020                let hw2: u16 = (0xF000 | (rd_lo_bits << 8) | rm_hi_bits) as u16;
3021                bytes.extend_from_slice(&hw1.to_le_bytes());
3022                bytes.extend_from_slice(&hw2.to_le_bytes());
3023
3024                // MOV rd_hi, #0
3025                let mov_zero: u16 = 0x2000 | ((rd_hi_bits as u16) << 8);
3026                bytes.extend_from_slice(&mov_zero.to_le_bytes());
3027
3028                Ok(bytes) // Total: 38 bytes
3029            }
3030
3031            // I64ShrS: 64-bit arithmetic shift right with branch for n<32 vs n>=32
3032            ArmOp::I64ShrS {
3033                rd_lo,
3034                rd_hi,
3035                rn_lo,
3036                rn_hi,
3037                rm_lo,
3038                rm_hi,
3039            } => {
3040                let rd_lo_bits = reg_to_bits(rd_lo);
3041                let rd_hi_bits = reg_to_bits(rd_hi);
3042                let rn_lo_bits = reg_to_bits(rn_lo);
3043                let rn_hi_bits = reg_to_bits(rn_hi);
3044                let rm_lo_bits = reg_to_bits(rm_lo);
3045                let rm_hi_bits = reg_to_bits(rm_hi); // temp
3046                let mut bytes = Vec::new();
3047
3048                // AND.W rm_lo, rm_lo, #63
3049                let hw1: u16 = (0xF000 | rm_lo_bits) as u16;
3050                let hw2: u16 = ((rm_lo_bits << 8) | 0x3F) as u16;
3051                bytes.extend_from_slice(&hw1.to_le_bytes());
3052                bytes.extend_from_slice(&hw2.to_le_bytes());
3053
3054                // SUBS.W rm_hi, rm_lo, #32
3055                let hw1: u16 = (0xF1B0 | rm_lo_bits) as u16;
3056                let hw2: u16 = ((rm_hi_bits << 8) | 0x20) as u16;
3057                bytes.extend_from_slice(&hw1.to_le_bytes());
3058                bytes.extend_from_slice(&hw2.to_le_bytes());
3059
3060                // BPL .large (+10 halfwords)
3061                let bpl: u16 = 0xD50A;
3062                bytes.extend_from_slice(&bpl.to_le_bytes());
3063
3064                // --- Small shift (n < 32) ---
3065                // RSB.W rm_hi, rm_lo, #32
3066                let hw1: u16 = (0xF1C0 | rm_lo_bits) as u16;
3067                let hw2: u16 = ((rm_hi_bits << 8) | 0x20) as u16;
3068                bytes.extend_from_slice(&hw1.to_le_bytes());
3069                bytes.extend_from_slice(&hw2.to_le_bytes());
3070
3071                // LSL.W rm_hi, rn_hi, rm_hi  (rm_hi = hi << (32-n), bits flowing to lo)
3072                let hw1: u16 = (0xFA00 | rn_hi_bits) as u16;
3073                let hw2: u16 = (0xF000 | (rm_hi_bits << 8) | rm_hi_bits) as u16;
3074                bytes.extend_from_slice(&hw1.to_le_bytes());
3075                bytes.extend_from_slice(&hw2.to_le_bytes());
3076
3077                // LSR.W rd_lo, rn_lo, rm_lo  (lo >>= n, logical for lo word)
3078                let hw1: u16 = (0xFA20 | rn_lo_bits) as u16;
3079                let hw2: u16 = (0xF000 | (rd_lo_bits << 8) | rm_lo_bits) as u16;
3080                bytes.extend_from_slice(&hw1.to_le_bytes());
3081                bytes.extend_from_slice(&hw2.to_le_bytes());
3082
3083                // ORR.W rd_lo, rd_lo, rm_hi  (lo |= overflow from hi)
3084                let hw1: u16 = (0xEA40 | rd_lo_bits) as u16;
3085                let hw2: u16 = ((rd_lo_bits << 8) | rm_hi_bits) as u16;
3086                bytes.extend_from_slice(&hw1.to_le_bytes());
3087                bytes.extend_from_slice(&hw2.to_le_bytes());
3088
3089                // ASR.W rd_hi, rn_hi, rm_lo  (hi >>= n, arithmetic/sign-extending)
3090                let hw1: u16 = (0xFA40 | rn_hi_bits) as u16;
3091                let hw2: u16 = (0xF000 | (rd_hi_bits << 8) | rm_lo_bits) as u16;
3092                bytes.extend_from_slice(&hw1.to_le_bytes());
3093                bytes.extend_from_slice(&hw2.to_le_bytes());
3094
3095                // B .done (+3 halfwords, large shift is 8 bytes)
3096                let b_done: u16 = 0xE003;
3097                bytes.extend_from_slice(&b_done.to_le_bytes());
3098
3099                // --- Large shift (n >= 32) ---
3100                // ASR.W rd_lo, rn_hi, rm_hi  (lo = hi >>> (n-32))
3101                let hw1: u16 = (0xFA40 | rn_hi_bits) as u16;
3102                let hw2: u16 = (0xF000 | (rd_lo_bits << 8) | rm_hi_bits) as u16;
3103                bytes.extend_from_slice(&hw1.to_le_bytes());
3104                bytes.extend_from_slice(&hw2.to_le_bytes());
3105
3106                // ASR.W rd_hi, rn_hi, #31  (hi = sign extension, all 0s or all 1s)
3107                // Thumb-2 ASR immediate: hw1=0xEA4F, hw2=imm3:Rd:imm2:10:Rm
3108                // imm5=31=11111 → imm3=111, imm2=11
3109                let hw1: u16 = 0xEA4F;
3110                let hw2: u16 = (0x7000 | (rd_hi_bits << 8) | 0x00E0 | rn_hi_bits) as u16;
3111                bytes.extend_from_slice(&hw1.to_le_bytes());
3112                bytes.extend_from_slice(&hw2.to_le_bytes());
3113
3114                Ok(bytes) // Total: 40 bytes
3115            }
3116
3117            // I64Rotl: 64-bit rotate left
3118            // For n < 32: new_hi = (hi << n) | (lo >> (32-n)), new_lo = (lo << n) | (hi >> (32-n))
3119            // For n >= 32: same formula but with lo/hi conceptually swapped, shift by (n-32)
3120            // Uses R4 (saved/restored) and R12 as scratch
3121            ArmOp::I64Rotl {
3122                rdlo,
3123                rdhi,
3124                rnlo,
3125                rnhi,
3126                shift,
3127            } => {
3128                let rd_lo_bits = reg_to_bits(rdlo);
3129                let rd_hi_bits = reg_to_bits(rdhi);
3130                let rn_lo_bits = reg_to_bits(rnlo);
3131                let rn_hi_bits = reg_to_bits(rnhi);
3132                let shift_bits = reg_to_bits(shift);
3133                let r12: u32 = 12; // IP scratch
3134                let r3: u32 = 3; // Scratch (high word of shift amount, unused)
3135                let r4: u32 = 4; // Scratch (saved/restored)
3136                let mut bytes = Vec::new();
3137
3138                // PUSH {R4}
3139                bytes.extend_from_slice(&0xB410u16.to_le_bytes());
3140
3141                // AND.W shift, shift, #63 (mask to 6 bits)
3142                let hw1: u16 = (0xF000 | shift_bits) as u16;
3143                let hw2: u16 = ((shift_bits << 8) | 0x3F) as u16;
3144                bytes.extend_from_slice(&hw1.to_le_bytes());
3145                bytes.extend_from_slice(&hw2.to_le_bytes());
3146
3147                // SUBS.W R3, shift, #32 (R3 = n-32, sets flags)
3148                let hw1: u16 = (0xF1B0 | shift_bits) as u16;
3149                let hw2: u16 = ((r3 << 8) | 0x20) as u16;
3150                bytes.extend_from_slice(&hw1.to_le_bytes());
3151                bytes.extend_from_slice(&hw2.to_le_bytes());
3152
3153                // BPL .large (branch if n >= 32, offset = +14 halfwords)
3154                let bpl: u16 = 0xD50E;
3155                bytes.extend_from_slice(&bpl.to_le_bytes());
3156
3157                // === Small rotation (n < 32) ===
3158                // RSB.W R3, shift, #32 (R3 = 32-n)
3159                let hw1: u16 = (0xF1C0 | shift_bits) as u16;
3160                let hw2: u16 = ((r3 << 8) | 0x20) as u16;
3161                bytes.extend_from_slice(&hw1.to_le_bytes());
3162                bytes.extend_from_slice(&hw2.to_le_bytes());
3163
3164                // LSR.W R4, rn_lo, R3 (R4 = lo >> (32-n), will go to new_hi)
3165                let hw1: u16 = (0xFA20 | rn_lo_bits) as u16;
3166                let hw2: u16 = (0xF000 | (r4 << 8) | r3) as u16;
3167                bytes.extend_from_slice(&hw1.to_le_bytes());
3168                bytes.extend_from_slice(&hw2.to_le_bytes());
3169
3170                // LSR.W R12, rn_hi, R3 (R12 = hi >> (32-n), will go to new_lo)
3171                let hw1: u16 = (0xFA20 | rn_hi_bits) as u16;
3172                let hw2: u16 = (0xF000 | (r12 << 8) | r3) as u16;
3173                bytes.extend_from_slice(&hw1.to_le_bytes());
3174                bytes.extend_from_slice(&hw2.to_le_bytes());
3175
3176                // LSL.W rd_hi, rn_hi, shift (rd_hi = hi << n)
3177                let hw1: u16 = (0xFA00 | rn_hi_bits) as u16;
3178                let hw2: u16 = (0xF000 | (rd_hi_bits << 8) | shift_bits) as u16;
3179                bytes.extend_from_slice(&hw1.to_le_bytes());
3180                bytes.extend_from_slice(&hw2.to_le_bytes());
3181
3182                // ORR.W rd_hi, rd_hi, R4 (rd_hi = (hi << n) | (lo >> (32-n)))
3183                let hw1: u16 = (0xEA40 | rd_hi_bits) as u16;
3184                let hw2: u16 = ((rd_hi_bits << 8) | r4) as u16;
3185                bytes.extend_from_slice(&hw1.to_le_bytes());
3186                bytes.extend_from_slice(&hw2.to_le_bytes());
3187
3188                // LSL.W rd_lo, rn_lo, shift (rd_lo = lo << n)
3189                let hw1: u16 = (0xFA00 | rn_lo_bits) as u16;
3190                let hw2: u16 = (0xF000 | (rd_lo_bits << 8) | shift_bits) as u16;
3191                bytes.extend_from_slice(&hw1.to_le_bytes());
3192                bytes.extend_from_slice(&hw2.to_le_bytes());
3193
3194                // ORR.W rd_lo, rd_lo, R12 (rd_lo = (lo << n) | (hi >> (32-n)))
3195                let hw1: u16 = (0xEA40 | rd_lo_bits) as u16;
3196                let hw2: u16 = ((rd_lo_bits << 8) | r12) as u16;
3197                bytes.extend_from_slice(&hw1.to_le_bytes());
3198                bytes.extend_from_slice(&hw2.to_le_bytes());
3199
3200                // B .done (skip large block, offset = +14 halfwords)
3201                let b_done: u16 = 0xE00E;
3202                bytes.extend_from_slice(&b_done.to_le_bytes());
3203
3204                // === Large rotation (n >= 32) ===
3205                // R3 already has n-32 from the SUBS
3206                // RSB.W R4, R3, #32 (R4 = 32-(n-32) = 64-n)
3207                let hw1: u16 = (0xF1C0 | r3) as u16;
3208                let hw2: u16 = ((r4 << 8) | 0x20) as u16;
3209                bytes.extend_from_slice(&hw1.to_le_bytes());
3210                bytes.extend_from_slice(&hw2.to_le_bytes());
3211
3212                // LSR.W R12, rn_hi, R4 (R12 = hi >> (64-n), goes to new_hi low bits)
3213                let hw1: u16 = (0xFA20 | rn_hi_bits) as u16;
3214                let hw2: u16 = (0xF000 | (r12 << 8) | r4) as u16;
3215                bytes.extend_from_slice(&hw1.to_le_bytes());
3216                bytes.extend_from_slice(&hw2.to_le_bytes());
3217
3218                // LSR.W R4, rn_lo, R4 (R4 = lo >> (64-n), goes to new_lo low bits)
3219                let hw1: u16 = (0xFA20 | rn_lo_bits) as u16;
3220                let hw2: u16 = (0xF000 | (r4 << 8) | r4) as u16;
3221                bytes.extend_from_slice(&hw1.to_le_bytes());
3222                bytes.extend_from_slice(&hw2.to_le_bytes());
3223
3224                // LSL.W shift, rn_lo, R3 (shift = lo << (n-32), new_hi high bits)
3225                let hw1: u16 = (0xFA00 | rn_lo_bits) as u16;
3226                let hw2: u16 = (0xF000 | (shift_bits << 8) | r3) as u16;
3227                bytes.extend_from_slice(&hw1.to_le_bytes());
3228                bytes.extend_from_slice(&hw2.to_le_bytes());
3229
3230                // ORR.W shift, shift, R12 (shift = (lo << (n-32)) | (hi >> (64-n)) = new_hi)
3231                let hw1: u16 = (0xEA40 | shift_bits) as u16;
3232                let hw2: u16 = ((shift_bits << 8) | r12) as u16;
3233                bytes.extend_from_slice(&hw1.to_le_bytes());
3234                bytes.extend_from_slice(&hw2.to_le_bytes());
3235
3236                // LSL.W rd_lo, rn_hi, R3 (rd_lo = hi << (n-32), new_lo high bits)
3237                let hw1: u16 = (0xFA00 | rn_hi_bits) as u16;
3238                let hw2: u16 = (0xF000 | (rd_lo_bits << 8) | r3) as u16;
3239                bytes.extend_from_slice(&hw1.to_le_bytes());
3240                bytes.extend_from_slice(&hw2.to_le_bytes());
3241
3242                // ORR.W rd_lo, rd_lo, R4 (rd_lo = (hi << (n-32)) | (lo >> (64-n)) = new_lo)
3243                let hw1: u16 = (0xEA40 | rd_lo_bits) as u16;
3244                let hw2: u16 = ((rd_lo_bits << 8) | r4) as u16;
3245                bytes.extend_from_slice(&hw1.to_le_bytes());
3246                bytes.extend_from_slice(&hw2.to_le_bytes());
3247
3248                // MOV rd_hi, shift (rd_hi = new_hi)
3249                let d_bit = (rd_hi_bits >> 3) & 1;
3250                let mov_instr: u16 =
3251                    (0x4600 | (d_bit << 7) | (shift_bits << 3) | (rd_hi_bits & 0x7)) as u16;
3252                bytes.extend_from_slice(&mov_instr.to_le_bytes());
3253
3254                // POP {R4}
3255                bytes.extend_from_slice(&0xBC10u16.to_le_bytes());
3256
3257                Ok(bytes) // Total: 74 bytes
3258            }
3259
3260            // I64Rotr: 64-bit rotate right
3261            // rotr(x, n) = rotl(x, 64-n)
3262            // For n < 32: new_lo = (lo >> n) | (hi << (32-n)), new_hi = (hi >> n) | (lo << (32-n))
3263            // For n >= 32: same formula but with lo/hi swapped, shift by (n-32)
3264            ArmOp::I64Rotr {
3265                rdlo,
3266                rdhi,
3267                rnlo,
3268                rnhi,
3269                shift,
3270            } => {
3271                let rd_lo_bits = reg_to_bits(rdlo);
3272                let rd_hi_bits = reg_to_bits(rdhi);
3273                let rn_lo_bits = reg_to_bits(rnlo);
3274                let rn_hi_bits = reg_to_bits(rnhi);
3275                let shift_bits = reg_to_bits(shift);
3276                let r12: u32 = 12;
3277                let r3: u32 = 3;
3278                let r4: u32 = 4;
3279                let mut bytes = Vec::new();
3280
3281                // PUSH {R4}
3282                bytes.extend_from_slice(&0xB410u16.to_le_bytes());
3283
3284                // AND.W shift, shift, #63
3285                let hw1: u16 = (0xF000 | shift_bits) as u16;
3286                let hw2: u16 = ((shift_bits << 8) | 0x3F) as u16;
3287                bytes.extend_from_slice(&hw1.to_le_bytes());
3288                bytes.extend_from_slice(&hw2.to_le_bytes());
3289
3290                // SUBS.W R3, shift, #32
3291                let hw1: u16 = (0xF1B0 | shift_bits) as u16;
3292                let hw2: u16 = ((r3 << 8) | 0x20) as u16;
3293                bytes.extend_from_slice(&hw1.to_le_bytes());
3294                bytes.extend_from_slice(&hw2.to_le_bytes());
3295
3296                // BPL .large (+14 halfwords)
3297                let bpl: u16 = 0xD50E;
3298                bytes.extend_from_slice(&bpl.to_le_bytes());
3299
3300                // === Small rotation (n < 32) ===
3301                // RSB.W R3, shift, #32 (R3 = 32-n)
3302                let hw1: u16 = (0xF1C0 | shift_bits) as u16;
3303                let hw2: u16 = ((r3 << 8) | 0x20) as u16;
3304                bytes.extend_from_slice(&hw1.to_le_bytes());
3305                bytes.extend_from_slice(&hw2.to_le_bytes());
3306
3307                // LSL.W R4, rn_hi, R3 (R4 = hi << (32-n), will go to new_lo)
3308                let hw1: u16 = (0xFA00 | rn_hi_bits) as u16;
3309                let hw2: u16 = (0xF000 | (r4 << 8) | r3) as u16;
3310                bytes.extend_from_slice(&hw1.to_le_bytes());
3311                bytes.extend_from_slice(&hw2.to_le_bytes());
3312
3313                // LSL.W R12, rn_lo, R3 (R12 = lo << (32-n), will go to new_hi)
3314                let hw1: u16 = (0xFA00 | rn_lo_bits) as u16;
3315                let hw2: u16 = (0xF000 | (r12 << 8) | r3) as u16;
3316                bytes.extend_from_slice(&hw1.to_le_bytes());
3317                bytes.extend_from_slice(&hw2.to_le_bytes());
3318
3319                // LSR.W rd_lo, rn_lo, shift (rd_lo = lo >> n)
3320                let hw1: u16 = (0xFA20 | rn_lo_bits) as u16;
3321                let hw2: u16 = (0xF000 | (rd_lo_bits << 8) | shift_bits) as u16;
3322                bytes.extend_from_slice(&hw1.to_le_bytes());
3323                bytes.extend_from_slice(&hw2.to_le_bytes());
3324
3325                // ORR.W rd_lo, rd_lo, R4 (rd_lo = (lo >> n) | (hi << (32-n)))
3326                let hw1: u16 = (0xEA40 | rd_lo_bits) as u16;
3327                let hw2: u16 = ((rd_lo_bits << 8) | r4) as u16;
3328                bytes.extend_from_slice(&hw1.to_le_bytes());
3329                bytes.extend_from_slice(&hw2.to_le_bytes());
3330
3331                // LSR.W rd_hi, rn_hi, shift (rd_hi = hi >> n)
3332                let hw1: u16 = (0xFA20 | rn_hi_bits) as u16;
3333                let hw2: u16 = (0xF000 | (rd_hi_bits << 8) | shift_bits) as u16;
3334                bytes.extend_from_slice(&hw1.to_le_bytes());
3335                bytes.extend_from_slice(&hw2.to_le_bytes());
3336
3337                // ORR.W rd_hi, rd_hi, R12 (rd_hi = (hi >> n) | (lo << (32-n)))
3338                let hw1: u16 = (0xEA40 | rd_hi_bits) as u16;
3339                let hw2: u16 = ((rd_hi_bits << 8) | r12) as u16;
3340                bytes.extend_from_slice(&hw1.to_le_bytes());
3341                bytes.extend_from_slice(&hw2.to_le_bytes());
3342
3343                // B .done (+14 halfwords)
3344                let b_done: u16 = 0xE00E;
3345                bytes.extend_from_slice(&b_done.to_le_bytes());
3346
3347                // === Large rotation (n >= 32) ===
3348                // RSB.W R4, R3, #32 (R4 = 64-n)
3349                let hw1: u16 = (0xF1C0 | r3) as u16;
3350                let hw2: u16 = ((r4 << 8) | 0x20) as u16;
3351                bytes.extend_from_slice(&hw1.to_le_bytes());
3352                bytes.extend_from_slice(&hw2.to_le_bytes());
3353
3354                // LSL.W R12, rn_lo, R4 (R12 = lo << (64-n), goes to new_lo low bits)
3355                let hw1: u16 = (0xFA00 | rn_lo_bits) as u16;
3356                let hw2: u16 = (0xF000 | (r12 << 8) | r4) as u16;
3357                bytes.extend_from_slice(&hw1.to_le_bytes());
3358                bytes.extend_from_slice(&hw2.to_le_bytes());
3359
3360                // LSL.W R4, rn_hi, R4 (R4 = hi << (64-n), goes to new_hi low bits)
3361                let hw1: u16 = (0xFA00 | rn_hi_bits) as u16;
3362                let hw2: u16 = (0xF000 | (r4 << 8) | r4) as u16;
3363                bytes.extend_from_slice(&hw1.to_le_bytes());
3364                bytes.extend_from_slice(&hw2.to_le_bytes());
3365
3366                // LSR.W shift, rn_hi, R3 (shift = hi >> (n-32), new_lo high bits)
3367                let hw1: u16 = (0xFA20 | rn_hi_bits) as u16;
3368                let hw2: u16 = (0xF000 | (shift_bits << 8) | r3) as u16;
3369                bytes.extend_from_slice(&hw1.to_le_bytes());
3370                bytes.extend_from_slice(&hw2.to_le_bytes());
3371
3372                // ORR.W shift, shift, R12 (shift = (hi >> (n-32)) | (lo << (64-n)) = new_lo)
3373                let hw1: u16 = (0xEA40 | shift_bits) as u16;
3374                let hw2: u16 = ((shift_bits << 8) | r12) as u16;
3375                bytes.extend_from_slice(&hw1.to_le_bytes());
3376                bytes.extend_from_slice(&hw2.to_le_bytes());
3377
3378                // LSR.W rd_hi, rn_lo, R3 (rd_hi = lo >> (n-32), new_hi high bits)
3379                let hw1: u16 = (0xFA20 | rn_lo_bits) as u16;
3380                let hw2: u16 = (0xF000 | (rd_hi_bits << 8) | r3) as u16;
3381                bytes.extend_from_slice(&hw1.to_le_bytes());
3382                bytes.extend_from_slice(&hw2.to_le_bytes());
3383
3384                // ORR.W rd_hi, rd_hi, R4 (rd_hi = (lo >> (n-32)) | (hi << (64-n)) = new_hi)
3385                let hw1: u16 = (0xEA40 | rd_hi_bits) as u16;
3386                let hw2: u16 = ((rd_hi_bits << 8) | r4) as u16;
3387                bytes.extend_from_slice(&hw1.to_le_bytes());
3388                bytes.extend_from_slice(&hw2.to_le_bytes());
3389
3390                // MOV rd_lo, shift (rd_lo = new_lo)
3391                let d_bit = (rd_lo_bits >> 3) & 1;
3392                let mov_instr: u16 =
3393                    (0x4600 | (d_bit << 7) | (shift_bits << 3) | (rd_lo_bits & 0x7)) as u16;
3394                bytes.extend_from_slice(&mov_instr.to_le_bytes());
3395
3396                // POP {R4}
3397                bytes.extend_from_slice(&0xBC10u16.to_le_bytes());
3398
3399                Ok(bytes) // Total: 74 bytes
3400            }
3401
3402            // I64Clz: Count leading zeros in 64-bit value
3403            // If hi != 0: result = CLZ(hi)
3404            // If hi == 0: result = 32 + CLZ(lo)
3405            //
3406            // Layout (using CMP+BNE approach for consistency):
3407            // 0: CMP.W rnhi, #0 (4 bytes)
3408            // 4: BEQ .hi_zero (2 bytes) - branch forward to offset 14
3409            // 6: CLZ.W rd, rnhi (4 bytes)
3410            // 10: B .done (2 bytes) - branch forward to offset 22
3411            // 12: NOP (2 bytes) - padding for alignment
3412            // 14: .hi_zero: CLZ.W rd, rnlo (4 bytes)
3413            // 18: ADD.W rd, rd, #32 (4 bytes)
3414            // 22: .done
3415            ArmOp::I64Clz { rd, rnlo, rnhi } => {
3416                let rd_bits = reg_to_bits(rd);
3417                let rn_lo_bits = reg_to_bits(rnlo);
3418                let rn_hi_bits = reg_to_bits(rnhi);
3419                let mut bytes = Vec::new();
3420
3421                // CMP.W rnhi, #0 (4 bytes at offset 0)
3422                let hw1: u16 = (0xF1B0 | rn_hi_bits) as u16;
3423                let hw2: u16 = 0x0F00;
3424                bytes.extend_from_slice(&hw1.to_le_bytes());
3425                bytes.extend_from_slice(&hw2.to_le_bytes());
3426
3427                // BEQ .hi_zero (2 bytes at offset 4)
3428                // PC = 4 + 4 = 8, target = 14, offset = 6, imm8 = 3
3429                let beq: u16 = 0xD003;
3430                bytes.extend_from_slice(&beq.to_le_bytes());
3431
3432                // CLZ.W rd, rnhi (4 bytes at offset 6)
3433                // CLZ T1: hw1 = 0xFAB<Rm>, hw2 = 0xF<Rd>8<Rm>
3434                let hw1: u16 = (0xFAB0 | rn_hi_bits) as u16;
3435                let hw2: u16 = (0xF080 | (rd_bits << 8) | rn_hi_bits) as u16;
3436                bytes.extend_from_slice(&hw1.to_le_bytes());
3437                bytes.extend_from_slice(&hw2.to_le_bytes());
3438
3439                // B .done (2 bytes at offset 10)
3440                // PC = 10 + 4 = 14, target = 22, offset = 8, imm11 = 4
3441                let b_done: u16 = 0xE004;
3442                bytes.extend_from_slice(&b_done.to_le_bytes());
3443
3444                // NOP (2 bytes at offset 12) - padding
3445                bytes.extend_from_slice(&0xBF00u16.to_le_bytes());
3446
3447                // .hi_zero: (offset 14)
3448                // CLZ.W rd, rnlo (4 bytes)
3449                // CLZ T1: hw1 = 0xFAB<Rm>, hw2 = 0xF<Rd>8<Rm>
3450                let hw1: u16 = (0xFAB0 | rn_lo_bits) as u16;
3451                let hw2: u16 = (0xF080 | (rd_bits << 8) | rn_lo_bits) as u16;
3452                bytes.extend_from_slice(&hw1.to_le_bytes());
3453                bytes.extend_from_slice(&hw2.to_le_bytes());
3454
3455                // ADD.W rd, rd, #32 (4 bytes at offset 18)
3456                let hw1: u16 = (0xF100 | rd_bits) as u16;
3457                let hw2: u16 = ((rd_bits << 8) | 0x20) as u16;
3458                bytes.extend_from_slice(&hw1.to_le_bytes());
3459                bytes.extend_from_slice(&hw2.to_le_bytes());
3460
3461                // .done: (offset 22)
3462                // i64.clz returns i64, so clear high word: MOV rnhi, #0 (2 bytes)
3463                // MOVS Rn, #0: 0010 0 Rn 00000000
3464                let mov0: u16 = (0x2000 | (rn_hi_bits << 8)) as u16;
3465                bytes.extend_from_slice(&mov0.to_le_bytes());
3466
3467                Ok(bytes)
3468            }
3469
3470            // I64Ctz: Count trailing zeros in 64-bit value
3471            // If lo != 0: result = CTZ(lo) = CLZ(RBIT(lo))
3472            // If lo == 0: result = 32 + CTZ(hi) = 32 + CLZ(RBIT(hi))
3473            //
3474            // Layout:
3475            // 0: CMP.W rnlo, #0 (4 bytes)
3476            // 4: BEQ .lo_zero (2 bytes) - branch to offset 18
3477            // 6: RBIT.W rd, rnlo (4 bytes)
3478            // 10: CLZ.W rd, rd (4 bytes)
3479            // 14: B .done (2 bytes) - branch to offset 30
3480            // 16: NOP (2 bytes) - padding
3481            // 18: .lo_zero: RBIT.W rd, rnhi (4 bytes)
3482            // 22: CLZ.W rd, rd (4 bytes)
3483            // 26: ADD.W rd, rd, #32 (4 bytes)
3484            // 30: .done
3485            ArmOp::I64Ctz { rd, rnlo, rnhi } => {
3486                let rd_bits = reg_to_bits(rd);
3487                let rn_lo_bits = reg_to_bits(rnlo);
3488                let rn_hi_bits = reg_to_bits(rnhi);
3489                let mut bytes = Vec::new();
3490
3491                // CMP.W rnlo, #0 (4 bytes at offset 0)
3492                let hw1: u16 = (0xF1B0 | rn_lo_bits) as u16;
3493                let hw2: u16 = 0x0F00;
3494                bytes.extend_from_slice(&hw1.to_le_bytes());
3495                bytes.extend_from_slice(&hw2.to_le_bytes());
3496
3497                // BEQ .lo_zero (2 bytes at offset 4)
3498                // PC = 4 + 4 = 8, target = 18, offset = 10, imm8 = 5
3499                let beq: u16 = 0xD005;
3500                bytes.extend_from_slice(&beq.to_le_bytes());
3501
3502                // RBIT.W rd, rnlo (4 bytes at offset 6)
3503                // RBIT T1: hw1 = 0xFA9<Rm>, hw2 = 0xF<Rd>A<Rm>
3504                let hw1: u16 = (0xFA90 | rn_lo_bits) as u16;
3505                let hw2: u16 = (0xF0A0 | (rd_bits << 8) | rn_lo_bits) as u16;
3506                bytes.extend_from_slice(&hw1.to_le_bytes());
3507                bytes.extend_from_slice(&hw2.to_le_bytes());
3508
3509                // CLZ.W rd, rd (4 bytes at offset 10)
3510                // CLZ T1: hw1 = 0xFAB<Rm>, hw2 = 0xF<Rd>8<Rm>
3511                let hw1: u16 = (0xFAB0 | rd_bits) as u16;
3512                let hw2: u16 = (0xF080 | (rd_bits << 8) | rd_bits) as u16;
3513                bytes.extend_from_slice(&hw1.to_le_bytes());
3514                bytes.extend_from_slice(&hw2.to_le_bytes());
3515
3516                // B .done (2 bytes at offset 14)
3517                // PC = 14 + 4 = 18, target = 30, offset = 12, imm11 = 6
3518                let b_done: u16 = 0xE006;
3519                bytes.extend_from_slice(&b_done.to_le_bytes());
3520
3521                // NOP (2 bytes at offset 16) - padding
3522                bytes.extend_from_slice(&0xBF00u16.to_le_bytes());
3523
3524                // .lo_zero: (offset 18)
3525                // RBIT.W rd, rnhi (4 bytes)
3526                // RBIT T1: hw1 = 0xFA9<Rm>, hw2 = 0xF<Rd>A<Rm>
3527                let hw1: u16 = (0xFA90 | rn_hi_bits) as u16;
3528                let hw2: u16 = (0xF0A0 | (rd_bits << 8) | rn_hi_bits) as u16;
3529                bytes.extend_from_slice(&hw1.to_le_bytes());
3530                bytes.extend_from_slice(&hw2.to_le_bytes());
3531
3532                // CLZ.W rd, rd (4 bytes at offset 22)
3533                // CLZ T1: hw1 = 0xFAB<Rm>, hw2 = 0xF<Rd>8<Rm>
3534                let hw1: u16 = (0xFAB0 | rd_bits) as u16;
3535                let hw2: u16 = (0xF080 | (rd_bits << 8) | rd_bits) as u16;
3536                bytes.extend_from_slice(&hw1.to_le_bytes());
3537                bytes.extend_from_slice(&hw2.to_le_bytes());
3538
3539                // ADD.W rd, rd, #32 (4 bytes at offset 26)
3540                let hw1: u16 = (0xF100 | rd_bits) as u16;
3541                let hw2: u16 = ((rd_bits << 8) | 0x20) as u16;
3542                bytes.extend_from_slice(&hw1.to_le_bytes());
3543                bytes.extend_from_slice(&hw2.to_le_bytes());
3544
3545                // .done: (offset 30)
3546                // i64.ctz returns i64, so clear high word: MOV rnhi, #0 (2 bytes)
3547                let mov0: u16 = (0x2000 | (rn_hi_bits << 8)) as u16;
3548                bytes.extend_from_slice(&mov0.to_le_bytes());
3549
3550                Ok(bytes)
3551            }
3552
3553            // I64Popcnt: Population count of 64-bit value
3554            // result = POPCNT(lo) + POPCNT(hi)
3555            // Using SIMD-style parallel bit counting algorithm
3556            ArmOp::I64Popcnt { rd, rnlo, rnhi } => {
3557                let rd_bits = reg_to_bits(rd);
3558                let rn_lo_bits = reg_to_bits(rnlo);
3559                let rn_hi_bits = reg_to_bits(rnhi);
3560                let r12: u32 = 12; // IP scratch
3561                let r3: u32 = 3; // Scratch for hi popcnt result
3562                let mut bytes = Vec::new();
3563
3564                // PUSH {R3, R4, R5} - save scratch registers
3565                bytes.extend_from_slice(&0xB438u16.to_le_bytes());
3566
3567                // Strategy: compute popcnt(lo) -> R4, popcnt(hi) -> R5, add them -> rd
3568                // Using lookup table approach for each byte would be too large
3569                // Using shift-and-add approach instead
3570
3571                // For simplicity and correctness, use the efficient parallel algorithm
3572                // but implement it as a series of inline operations
3573
3574                // MOV R4, rnlo
3575                let d_bit: u32 = 0; // R4 < 8, so high bit is 0
3576                let mov: u16 = (0x4600 | (d_bit << 7) | (rn_lo_bits << 3) | (4 & 0x7)) as u16;
3577                bytes.extend_from_slice(&mov.to_le_bytes());
3578
3579                // MOV R5, rnhi
3580                let d_bit: u32 = 0; // R5 < 8, so high bit is 0
3581                let mov: u16 = (0x4600 | (d_bit << 7) | (rn_hi_bits << 3) | (5 & 0x7)) as u16;
3582                bytes.extend_from_slice(&mov.to_le_bytes());
3583
3584                // --- POPCNT for R4 (lo word) ---
3585                // Step 1: x = x - ((x >> 1) & 0x55555555)
3586                // LSR.W R12, R4, #1
3587                let hw1: u16 = 0xEA4F;
3588                let hw2: u16 = ((r12 << 8) | 0x50 | 4) as u16;
3589                bytes.extend_from_slice(&hw1.to_le_bytes());
3590                bytes.extend_from_slice(&hw2.to_le_bytes());
3591
3592                // Load 0x55555555 into R3 using MOVW/MOVT
3593                // MOVW R3, #0x5555
3594                bytes.extend_from_slice(&0xF245u16.to_le_bytes());
3595                bytes.extend_from_slice(&0x5355u16.to_le_bytes());
3596                // MOVT R3, #0x5555
3597                bytes.extend_from_slice(&0xF2C5u16.to_le_bytes());
3598                bytes.extend_from_slice(&0x5355u16.to_le_bytes());
3599
3600                // AND.W R12, R12, R3
3601                let hw1: u16 = (0xEA00 | r12) as u16;
3602                let hw2: u16 = ((r12 << 8) | r3) as u16;
3603                bytes.extend_from_slice(&hw1.to_le_bytes());
3604                bytes.extend_from_slice(&hw2.to_le_bytes());
3605
3606                // SUB.W R4, R4, R12
3607                let hw1: u16 = (0xEBA0 | 4) as u16;
3608                let hw2: u16 = ((4 << 8) | r12) as u16;
3609                bytes.extend_from_slice(&hw1.to_le_bytes());
3610                bytes.extend_from_slice(&hw2.to_le_bytes());
3611
3612                // Step 2: x = (x & 0x33333333) + ((x >> 2) & 0x33333333)
3613                // Load 0x33333333 into R3
3614                // MOVW R3, #0x3333
3615                bytes.extend_from_slice(&0xF243u16.to_le_bytes());
3616                bytes.extend_from_slice(&0x3333u16.to_le_bytes());
3617                // MOVT R3, #0x3333
3618                bytes.extend_from_slice(&0xF2C3u16.to_le_bytes());
3619                bytes.extend_from_slice(&0x3333u16.to_le_bytes());
3620
3621                // AND.W R12, R4, R3
3622                let hw1: u16 = (0xEA00 | 4) as u16;
3623                let hw2: u16 = ((r12 << 8) | r3) as u16;
3624                bytes.extend_from_slice(&hw1.to_le_bytes());
3625                bytes.extend_from_slice(&hw2.to_le_bytes());
3626
3627                // LSR.W R4, R4, #2
3628                let hw1: u16 = 0xEA4F;
3629                let hw2: u16 = ((4 << 8) | 0x90 | 4) as u16;
3630                bytes.extend_from_slice(&hw1.to_le_bytes());
3631                bytes.extend_from_slice(&hw2.to_le_bytes());
3632
3633                // AND.W R4, R4, R3
3634                let hw1: u16 = (0xEA00 | 4) as u16;
3635                let hw2: u16 = ((4 << 8) | r3) as u16;
3636                bytes.extend_from_slice(&hw1.to_le_bytes());
3637                bytes.extend_from_slice(&hw2.to_le_bytes());
3638
3639                // ADD.W R4, R4, R12
3640                let hw1: u16 = (0xEB00 | 4) as u16;
3641                let hw2: u16 = ((4 << 8) | r12) as u16;
3642                bytes.extend_from_slice(&hw1.to_le_bytes());
3643                bytes.extend_from_slice(&hw2.to_le_bytes());
3644
3645                // Step 3: x = (x + (x >> 4)) & 0x0F0F0F0F
3646                // LSR.W R12, R4, #4
3647                // hw2 = (imm3 << 12) | (Rd << 8) | (imm2 << 6) | (type << 4) | Rm
3648                // imm5=4=00100 → imm3=1, imm2=0, type=01(LSR)
3649                let hw1: u16 = 0xEA4F;
3650                let hw2: u16 = (0x1000 | (r12 << 8) | 0x10 | 4) as u16;
3651                bytes.extend_from_slice(&hw1.to_le_bytes());
3652                bytes.extend_from_slice(&hw2.to_le_bytes());
3653
3654                // ADD.W R4, R4, R12
3655                let hw1: u16 = (0xEB00 | 4) as u16;
3656                let hw2: u16 = ((4 << 8) | r12) as u16;
3657                bytes.extend_from_slice(&hw1.to_le_bytes());
3658                bytes.extend_from_slice(&hw2.to_le_bytes());
3659
3660                // Load 0x0F0F0F0F into R3
3661                // MOVW R3, #0x0F0F (imm4=0, i=1, imm3=7, imm8=0x0F)
3662                // hw1 = 11110 1 10 0100 0000 = 0xF640
3663                // hw2 = 0 111 0011 00001111 = 0x730F
3664                bytes.extend_from_slice(&0xF640u16.to_le_bytes());
3665                bytes.extend_from_slice(&0x730Fu16.to_le_bytes());
3666                // MOVT R3, #0x0F0F
3667                bytes.extend_from_slice(&0xF6C0u16.to_le_bytes());
3668                bytes.extend_from_slice(&0x730Fu16.to_le_bytes());
3669
3670                // AND.W R4, R4, R3
3671                let hw1: u16 = (0xEA00 | 4) as u16;
3672                let hw2: u16 = ((4 << 8) | r3) as u16;
3673                bytes.extend_from_slice(&hw1.to_le_bytes());
3674                bytes.extend_from_slice(&hw2.to_le_bytes());
3675
3676                // Step 4: x = x * 0x01010101 >> 24
3677                // Load 0x01010101 into R3
3678                // MOVW R3, #0x0101
3679                bytes.extend_from_slice(&0xF240u16.to_le_bytes());
3680                bytes.extend_from_slice(&0x1301u16.to_le_bytes());
3681                // MOVT R3, #0x0101
3682                bytes.extend_from_slice(&0xF2C0u16.to_le_bytes());
3683                bytes.extend_from_slice(&0x1301u16.to_le_bytes());
3684
3685                // MUL R4, R4, R3
3686                // MUL T2: hw1 = 0xFB00|Rn, hw2 = 0xF000|(Rd<<8)|Rm
3687                let hw1: u16 = (0xFB00 | 4) as u16;
3688                let hw2: u16 = (0xF000 | (4 << 8) | r3) as u16;
3689                bytes.extend_from_slice(&hw1.to_le_bytes());
3690                bytes.extend_from_slice(&hw2.to_le_bytes());
3691
3692                // LSR.W R4, R4, #24
3693                // imm5=24=11000 → imm3=6, imm2=0, type=01(LSR)
3694                let hw1: u16 = 0xEA4F;
3695                let hw2: u16 = (0x6000 | (4 << 8) | 0x10 | 4) as u16;
3696                bytes.extend_from_slice(&hw1.to_le_bytes());
3697                bytes.extend_from_slice(&hw2.to_le_bytes());
3698
3699                // --- POPCNT for R5 (hi word) - same algorithm ---
3700                // Step 1
3701                let hw1: u16 = 0xEA4F;
3702                let hw2: u16 = ((r12 << 8) | 0x50 | 5) as u16;
3703                bytes.extend_from_slice(&hw1.to_le_bytes());
3704                bytes.extend_from_slice(&hw2.to_le_bytes());
3705
3706                // Load 0x55555555 into R3
3707                bytes.extend_from_slice(&0xF245u16.to_le_bytes());
3708                bytes.extend_from_slice(&0x5355u16.to_le_bytes());
3709                bytes.extend_from_slice(&0xF2C5u16.to_le_bytes());
3710                bytes.extend_from_slice(&0x5355u16.to_le_bytes());
3711
3712                let hw1: u16 = (0xEA00 | r12) as u16;
3713                let hw2: u16 = ((r12 << 8) | r3) as u16;
3714                bytes.extend_from_slice(&hw1.to_le_bytes());
3715                bytes.extend_from_slice(&hw2.to_le_bytes());
3716
3717                let hw1: u16 = (0xEBA0 | 5) as u16;
3718                let hw2: u16 = ((5 << 8) | r12) as u16;
3719                bytes.extend_from_slice(&hw1.to_le_bytes());
3720                bytes.extend_from_slice(&hw2.to_le_bytes());
3721
3722                // Step 2
3723                bytes.extend_from_slice(&0xF243u16.to_le_bytes());
3724                bytes.extend_from_slice(&0x3333u16.to_le_bytes());
3725                bytes.extend_from_slice(&0xF2C3u16.to_le_bytes());
3726                bytes.extend_from_slice(&0x3333u16.to_le_bytes());
3727
3728                let hw1: u16 = (0xEA00 | 5) as u16;
3729                let hw2: u16 = ((r12 << 8) | r3) as u16;
3730                bytes.extend_from_slice(&hw1.to_le_bytes());
3731                bytes.extend_from_slice(&hw2.to_le_bytes());
3732
3733                let hw1: u16 = 0xEA4F;
3734                let hw2: u16 = ((5 << 8) | 0x90 | 5) as u16;
3735                bytes.extend_from_slice(&hw1.to_le_bytes());
3736                bytes.extend_from_slice(&hw2.to_le_bytes());
3737
3738                let hw1: u16 = (0xEA00 | 5) as u16;
3739                let hw2: u16 = ((5 << 8) | r3) as u16;
3740                bytes.extend_from_slice(&hw1.to_le_bytes());
3741                bytes.extend_from_slice(&hw2.to_le_bytes());
3742
3743                let hw1: u16 = (0xEB00 | 5) as u16;
3744                let hw2: u16 = ((5 << 8) | r12) as u16;
3745                bytes.extend_from_slice(&hw1.to_le_bytes());
3746                bytes.extend_from_slice(&hw2.to_le_bytes());
3747
3748                // Step 3: LSR.W R12, R5, #4
3749                // imm5=4=00100 → imm3=1, imm2=0, type=01(LSR)
3750                let hw1: u16 = 0xEA4F;
3751                let hw2: u16 = (0x1000 | (r12 << 8) | 0x10 | 5) as u16;
3752                bytes.extend_from_slice(&hw1.to_le_bytes());
3753                bytes.extend_from_slice(&hw2.to_le_bytes());
3754
3755                let hw1: u16 = (0xEB00 | 5) as u16;
3756                let hw2: u16 = ((5 << 8) | r12) as u16;
3757                bytes.extend_from_slice(&hw1.to_le_bytes());
3758                bytes.extend_from_slice(&hw2.to_le_bytes());
3759
3760                // Load 0x0F0F0F0F into R3 (for hi-word)
3761                bytes.extend_from_slice(&0xF640u16.to_le_bytes());
3762                bytes.extend_from_slice(&0x730Fu16.to_le_bytes());
3763                bytes.extend_from_slice(&0xF6C0u16.to_le_bytes());
3764                bytes.extend_from_slice(&0x730Fu16.to_le_bytes());
3765
3766                let hw1: u16 = (0xEA00 | 5) as u16;
3767                let hw2: u16 = ((5 << 8) | r3) as u16;
3768                bytes.extend_from_slice(&hw1.to_le_bytes());
3769                bytes.extend_from_slice(&hw2.to_le_bytes());
3770
3771                // Step 4
3772                bytes.extend_from_slice(&0xF240u16.to_le_bytes());
3773                bytes.extend_from_slice(&0x1301u16.to_le_bytes());
3774                bytes.extend_from_slice(&0xF2C0u16.to_le_bytes());
3775                bytes.extend_from_slice(&0x1301u16.to_le_bytes());
3776
3777                // MUL R5, R5, R3
3778                // MUL T2: hw1 = 0xFB00|Rn, hw2 = 0xF000|(Rd<<8)|Rm
3779                let hw1: u16 = (0xFB00 | 5) as u16;
3780                let hw2: u16 = (0xF000 | (5 << 8) | r3) as u16;
3781                bytes.extend_from_slice(&hw1.to_le_bytes());
3782                bytes.extend_from_slice(&hw2.to_le_bytes());
3783
3784                // LSR.W R5, R5, #24
3785                // imm5=24=11000 → imm3=6, imm2=0, type=01(LSR)
3786                let hw1: u16 = 0xEA4F;
3787                let hw2: u16 = (0x6000 | (5 << 8) | 0x10 | 5) as u16;
3788                bytes.extend_from_slice(&hw1.to_le_bytes());
3789                bytes.extend_from_slice(&hw2.to_le_bytes());
3790
3791                // ADD rd, R4, R5 (combine lo and hi counts)
3792                // ADDS Rd, Rn, Rm (T1): 0001 100 Rm Rn Rd = 0x1800 | (Rm<<6) | (Rn<<3) | Rd
3793                let rd_bits_u16 = rd_bits as u16;
3794                let instr: u16 = 0x1800 | (5 << 6) | (4 << 3) | rd_bits_u16;
3795                bytes.extend_from_slice(&instr.to_le_bytes());
3796
3797                // POP {R3, R4, R5}
3798                bytes.extend_from_slice(&0xBC38u16.to_le_bytes());
3799
3800                // i64.popcnt returns i64, so clear high word: MOV rnhi, #0 (2 bytes)
3801                let mov0: u16 = (0x2000 | (rn_hi_bits << 8)) as u16;
3802                bytes.extend_from_slice(&mov0.to_le_bytes());
3803
3804                Ok(bytes)
3805            }
3806
3807            // I64Extend8S: Sign-extend low 8 bits to 64 bits
3808            // Result: rdlo = sign_extend_8(rnlo), rdhi = rdlo >> 31
3809            ArmOp::I64Extend8S { rdlo, rdhi, rnlo } => {
3810                let rdlo_bits = reg_to_bits(rdlo);
3811                let rdhi_bits = reg_to_bits(rdhi);
3812                let rnlo_bits = reg_to_bits(rnlo);
3813                let mut bytes = Vec::new();
3814
3815                // SXTB.W rdlo, rnlo (sign-extend byte to 32-bit)
3816                // SXTB T2: hw1 = 0xFA4F, hw2 = 0xF0<Rd><Rm>
3817                let hw1: u16 = 0xFA4F_u16;
3818                let hw2: u16 = (0xF080 | (rdlo_bits << 8) | rnlo_bits) as u16;
3819                bytes.extend_from_slice(&hw1.to_le_bytes());
3820                bytes.extend_from_slice(&hw2.to_le_bytes());
3821
3822                // ASR.W rdhi, rdlo, #31 (sign-extend to high word)
3823                // ASR (immediate): hw1 = 0xEA4F, hw2 = imm3:Rd:imm2:type:Rm
3824                // For imm5=31: imm3=111, imm2=11, type=10 (ASR)
3825                // hw2 = (7 << 12) | (rdhi << 8) | (3 << 6) | (2 << 4) | rdlo
3826                let hw1: u16 = 0xEA4F;
3827                let hw2: u16 = (0x70E0 | (rdhi_bits << 8) | rdlo_bits) as u16;
3828                bytes.extend_from_slice(&hw1.to_le_bytes());
3829                bytes.extend_from_slice(&hw2.to_le_bytes());
3830
3831                Ok(bytes)
3832            }
3833
3834            // I64Extend16S: Sign-extend low 16 bits to 64 bits
3835            // Result: rdlo = sign_extend_16(rnlo), rdhi = rdlo >> 31
3836            ArmOp::I64Extend16S { rdlo, rdhi, rnlo } => {
3837                let rdlo_bits = reg_to_bits(rdlo);
3838                let rdhi_bits = reg_to_bits(rdhi);
3839                let rnlo_bits = reg_to_bits(rnlo);
3840                let mut bytes = Vec::new();
3841
3842                // SXTH.W rdlo, rnlo (sign-extend halfword to 32-bit)
3843                // SXTH T2: hw1 = 0xFA0F, hw2 = 0xF0<Rd><Rm>
3844                let hw1: u16 = 0xFA0F_u16;
3845                let hw2: u16 = (0xF080 | (rdlo_bits << 8) | rnlo_bits) as u16;
3846                bytes.extend_from_slice(&hw1.to_le_bytes());
3847                bytes.extend_from_slice(&hw2.to_le_bytes());
3848
3849                // ASR.W rdhi, rdlo, #31 (sign-extend to high word)
3850                let hw1: u16 = 0xEA4F;
3851                let hw2: u16 = (0x70E0 | (rdhi_bits << 8) | rdlo_bits) as u16;
3852                bytes.extend_from_slice(&hw1.to_le_bytes());
3853                bytes.extend_from_slice(&hw2.to_le_bytes());
3854
3855                Ok(bytes)
3856            }
3857
3858            // I64Extend32S: Sign-extend low 32 bits to 64 bits
3859            // Result: rdlo = rnlo, rdhi = rnlo >> 31
3860            ArmOp::I64Extend32S { rdlo, rdhi, rnlo } => {
3861                let rdlo_bits = reg_to_bits(rdlo);
3862                let rdhi_bits = reg_to_bits(rdhi);
3863                let rnlo_bits = reg_to_bits(rnlo);
3864                let mut bytes = Vec::new();
3865
3866                // MOV rdlo, rnlo (if different)
3867                if rdlo_bits != rnlo_bits {
3868                    // MOV Rd, Rm (16-bit): 0100 0110 D Rm Rd[2:0]
3869                    let d_bit = ((rdlo_bits >> 3) & 1) as u16;
3870                    let mov: u16 = 0x4600
3871                        | (d_bit << 7)
3872                        | ((rnlo_bits as u16) << 3)
3873                        | ((rdlo_bits & 0x7) as u16);
3874                    bytes.extend_from_slice(&mov.to_le_bytes());
3875                }
3876
3877                // ASR.W rdhi, rnlo, #31 (sign-extend to high word)
3878                let hw1: u16 = 0xEA4F;
3879                let hw2: u16 = (0x70E0 | (rdhi_bits << 8) | rnlo_bits) as u16;
3880                bytes.extend_from_slice(&hw1.to_le_bytes());
3881                bytes.extend_from_slice(&hw2.to_le_bytes());
3882
3883                Ok(bytes)
3884            }
3885
3886            // SelectMove: IT <cond>; MOV{cond} rd, rm
3887            // Conditional move: only execute MOV if condition is true
3888            ArmOp::SelectMove { rd, rm, cond } => {
3889                let rd_bits = reg_to_bits(rd) as u16;
3890                let rm_bits = reg_to_bits(rm) as u16;
3891
3892                // Condition code encoding for IT block
3893                use synth_synthesis::Condition;
3894                let cond_bits: u16 = match cond {
3895                    Condition::EQ => 0x0, // Equal
3896                    Condition::NE => 0x1, // Not equal
3897                    Condition::HS => 0x2, // Higher or same (unsigned >=)
3898                    Condition::LO => 0x3, // Lower (unsigned <)
3899                    Condition::HI => 0x8, // Higher (unsigned >)
3900                    Condition::LS => 0x9, // Lower or same (unsigned <=)
3901                    Condition::GE => 0xA, // Greater or equal (signed)
3902                    Condition::LT => 0xB, // Less than (signed)
3903                    Condition::GT => 0xC, // Greater than (signed)
3904                    Condition::LE => 0xD, // Less or equal (signed)
3905                };
3906
3907                // IT <cond>: single Then block (mask = 0x8 for T only)
3908                // IT instruction: 1011 1111 firstcond mask
3909                let it_instr: u16 = 0xBF00 | (cond_bits << 4) | 0x8;
3910
3911                // MOV Rd, Rm (16-bit): 0100 0110 D Rm Rd[2:0]
3912                // This MOV will only execute if condition is true due to IT block
3913                let d_bit = (rd_bits >> 3) & 1;
3914                let mov_instr: u16 = 0x4600 | (d_bit << 7) | (rm_bits << 3) | (rd_bits & 0x7);
3915
3916                // Emit: IT <cond>, MOV rd, rm
3917                let mut bytes = it_instr.to_le_bytes().to_vec();
3918                bytes.extend_from_slice(&mov_instr.to_le_bytes());
3919                Ok(bytes)
3920            }
3921
3922            // Popcnt: Population count (count set bits)
3923            // ARM Cortex-M has no native POPCNT, so we implement the bit manipulation algorithm:
3924            // x = x - ((x >> 1) & 0x55555555);
3925            // x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
3926            // x = (x + (x >> 4)) & 0x0F0F0F0F;
3927            // x = x + (x >> 8);
3928            // x = x + (x >> 16);
3929            // return x & 0x3F;
3930            //
3931            // Uses rd as working register and R12 as scratch for constants
3932            ArmOp::Popcnt { rd, rm } => {
3933                let mut bytes = Vec::new();
3934
3935                // First, move rm to rd if they're different
3936                if rd != rm {
3937                    let rd_bits = reg_to_bits(rd) as u16;
3938                    let rm_bits = reg_to_bits(rm) as u16;
3939                    // MOV Rd, Rm (16-bit): 0100 0110 D Rm Rd[2:0]
3940                    let d_bit = (rd_bits >> 3) & 1;
3941                    let mov_instr: u16 = 0x4600 | (d_bit << 7) | (rm_bits << 3) | (rd_bits & 0x7);
3942                    bytes.extend_from_slice(&mov_instr.to_le_bytes());
3943                }
3944
3945                // Step 1: x = x - ((x >> 1) & 0x55555555)
3946                // Load 0x55555555 into R12
3947                bytes.extend_from_slice(&self.encode_thumb32_movw_raw(12, 0x5555)?);
3948                bytes.extend_from_slice(&self.encode_thumb32_movt_raw(12, 0x5555)?);
3949
3950                // R12_temp = rd >> 1
3951                // We need a second scratch register. Use R11.
3952                bytes.extend_from_slice(&self.encode_thumb32_lsr_raw(11, reg_to_bits(rd), 1)?);
3953
3954                // R11 = R11 & R12 (R11 = (x >> 1) & 0x55555555)
3955                bytes.extend_from_slice(&self.encode_thumb32_and_reg_raw(11, 11, 12)?);
3956
3957                // rd = rd - R11
3958                bytes.extend_from_slice(&self.encode_thumb32_sub_reg_raw(
3959                    reg_to_bits(rd),
3960                    reg_to_bits(rd),
3961                    11,
3962                )?);
3963
3964                // Step 2: x = (x & 0x33333333) + ((x >> 2) & 0x33333333)
3965                // Load 0x33333333 into R12
3966                bytes.extend_from_slice(&self.encode_thumb32_movw_raw(12, 0x3333)?);
3967                bytes.extend_from_slice(&self.encode_thumb32_movt_raw(12, 0x3333)?);
3968
3969                // R11 = rd & R12
3970                bytes.extend_from_slice(&self.encode_thumb32_and_reg_raw(
3971                    11,
3972                    reg_to_bits(rd),
3973                    12,
3974                )?);
3975
3976                // rd = rd >> 2
3977                bytes.extend_from_slice(&self.encode_thumb32_lsr_raw(
3978                    reg_to_bits(rd),
3979                    reg_to_bits(rd),
3980                    2,
3981                )?);
3982
3983                // rd = rd & R12
3984                bytes.extend_from_slice(&self.encode_thumb32_and_reg_raw(
3985                    reg_to_bits(rd),
3986                    reg_to_bits(rd),
3987                    12,
3988                )?);
3989
3990                // rd = rd + R11
3991                bytes.extend_from_slice(&self.encode_thumb32_add_reg_raw(
3992                    reg_to_bits(rd),
3993                    reg_to_bits(rd),
3994                    11,
3995                )?);
3996
3997                // Step 3: x = (x + (x >> 4)) & 0x0F0F0F0F
3998                // R11 = rd >> 4
3999                bytes.extend_from_slice(&self.encode_thumb32_lsr_raw(11, reg_to_bits(rd), 4)?);
4000
4001                // rd = rd + R11
4002                bytes.extend_from_slice(&self.encode_thumb32_add_reg_raw(
4003                    reg_to_bits(rd),
4004                    reg_to_bits(rd),
4005                    11,
4006                )?);
4007
4008                // Load 0x0F0F0F0F into R12
4009                bytes.extend_from_slice(&self.encode_thumb32_movw_raw(12, 0x0F0F)?);
4010                bytes.extend_from_slice(&self.encode_thumb32_movt_raw(12, 0x0F0F)?);
4011
4012                // rd = rd & R12
4013                bytes.extend_from_slice(&self.encode_thumb32_and_reg_raw(
4014                    reg_to_bits(rd),
4015                    reg_to_bits(rd),
4016                    12,
4017                )?);
4018
4019                // Step 4: x = x + (x >> 8)
4020                // R11 = rd >> 8
4021                bytes.extend_from_slice(&self.encode_thumb32_lsr_raw(11, reg_to_bits(rd), 8)?);
4022
4023                // rd = rd + R11
4024                bytes.extend_from_slice(&self.encode_thumb32_add_reg_raw(
4025                    reg_to_bits(rd),
4026                    reg_to_bits(rd),
4027                    11,
4028                )?);
4029
4030                // Step 5: x = x + (x >> 16)
4031                // R11 = rd >> 16
4032                bytes.extend_from_slice(&self.encode_thumb32_lsr_raw(11, reg_to_bits(rd), 16)?);
4033
4034                // rd = rd + R11
4035                bytes.extend_from_slice(&self.encode_thumb32_add_reg_raw(
4036                    reg_to_bits(rd),
4037                    reg_to_bits(rd),
4038                    11,
4039                )?);
4040
4041                // Step 6: return x & 0x3F
4042                // AND with 0x3F (small immediate, can use BIC or AND with immediate)
4043                bytes.extend_from_slice(&self.encode_thumb32_and_imm_raw(
4044                    reg_to_bits(rd),
4045                    reg_to_bits(rd),
4046                    0x3F,
4047                )?);
4048
4049                Ok(bytes)
4050            }
4051
4052            // I64DivU: 64-bit unsigned division using binary long division
4053            // Input: R0:R1 = dividend, R2:R3 = divisor
4054            // Output: R0:R1 = quotient
4055            // Uses: R4-R7, R12 as loop counter (avoid R8 for Renode compatibility)
4056            ArmOp::I64DivU {
4057                rdlo: _,
4058                rdhi: _,
4059                rnlo: _,
4060                rnhi: _,
4061                rmlo: _,
4062                rmhi: _,
4063            } => {
4064                let mut bytes = Vec::new();
4065
4066                // PUSH {R4-R7} - save scratch registers (NO LR — this is inline code)
4067                // 16-bit PUSH: 1011 010 M rrrrrrrr where M=0 (no LR), r=R4-R7 = 0xF0
4068                // Encoding: 1011 0100 1111 0000 = 0xB4F0
4069                bytes.extend_from_slice(&0xB4F0u16.to_le_bytes());
4070
4071                // Initialize quotient (R4:R5) = 0
4072                bytes.extend_from_slice(&0x2400u16.to_le_bytes()); // MOV R4, #0
4073                bytes.extend_from_slice(&0x2500u16.to_le_bytes()); // MOV R5, #0
4074
4075                // Initialize remainder (R6:R7) = 0
4076                bytes.extend_from_slice(&0x2600u16.to_le_bytes()); // MOV R6, #0
4077                bytes.extend_from_slice(&0x2700u16.to_le_bytes()); // MOV R7, #0
4078
4079                // Initialize loop counter R12 = 64 (use R12 scratch instead of R8)
4080                // MOV.W R12, #64: F04F 0C40
4081                bytes.extend_from_slice(&0xF04Fu16.to_le_bytes());
4082                bytes.extend_from_slice(&0x0C40u16.to_le_bytes());
4083
4084                // Loop start
4085                let loop_start = bytes.len();
4086
4087                // === Loop body: process one bit ===
4088
4089                // 1. Shift quotient R4:R5 left by 1
4090                // LSLS R5, R5, #1 (16-bit: 0000 0010 1010 1101 = 0x006D -> actually 0x002D for LSL R5,R5,#1)
4091                // LSL Rd, Rm, #imm5: 000 00 imm5 Rm Rd = 000 00 00001 101 101 = 0x006D
4092                bytes.extend_from_slice(&0x006Du16.to_le_bytes()); // LSLS R5, R5, #1
4093                // Get carry from R4 into R5: ORR R5, R5, R4 LSR #31
4094                // Thumb-2 ORR with shifted register: EA45 75D4 = ORR.W R5, R5, R4, LSR #31
4095                // 11101010 010 S Rn | 0 imm3 Rd imm2 type Rm
4096                // type=01 (LSR), imm5=31 (imm3=111, imm2=11)
4097                bytes.extend_from_slice(&0xEA45u16.to_le_bytes());
4098                bytes.extend_from_slice(&0x75D4u16.to_le_bytes()); // ORR.W R5, R5, R4, LSR #31
4099                // LSLS R4, R4, #1: 000 00 00001 100 100 = 0x0064
4100                bytes.extend_from_slice(&0x0064u16.to_le_bytes()); // LSLS R4, R4, #1
4101
4102                // 2. Shift remainder R6:R7 left by 1, OR in MSB of dividend R1
4103                // LSLS R7, R7, #1
4104                bytes.extend_from_slice(&0x007Fu16.to_le_bytes()); // LSLS R7, R7, #1
4105                // ORR.W R7, R7, R6, LSR #31
4106                bytes.extend_from_slice(&0xEA47u16.to_le_bytes());
4107                bytes.extend_from_slice(&0x77D6u16.to_le_bytes());
4108                // LSLS R6, R6, #1
4109                bytes.extend_from_slice(&0x0076u16.to_le_bytes()); // LSLS R6, R6, #1
4110                // ORR.W R6, R6, R1, LSR #31 (bring in MSB of dividend high)
4111                bytes.extend_from_slice(&0xEA46u16.to_le_bytes());
4112                bytes.extend_from_slice(&0x76D1u16.to_le_bytes());
4113
4114                // 3. Shift dividend R0:R1 left by 1
4115                // LSLS R1, R1, #1
4116                bytes.extend_from_slice(&0x0049u16.to_le_bytes()); // LSLS R1, R1, #1
4117                // ORR.W R1, R1, R0, LSR #31
4118                bytes.extend_from_slice(&0xEA41u16.to_le_bytes());
4119                bytes.extend_from_slice(&0x71D0u16.to_le_bytes());
4120                // LSLS R0, R0, #1
4121                bytes.extend_from_slice(&0x0040u16.to_le_bytes()); // LSLS R0, R0, #1
4122
4123                // 4. Compare remainder >= divisor (64-bit unsigned comparison)
4124                // Compare high words first: CMP R7, R3
4125                // CMP Rn, Rm encoding: 0x4280 | (Rm << 3) | Rn
4126                bytes.extend_from_slice(&0x429Fu16.to_le_bytes()); // CMP R7, R3 (16-bit)
4127                // BHI means R7 > R3 (unsigned) - definitely subtract
4128                // BLO means R7 < R3 - definitely don't subtract
4129                // BEQ means need to check low words
4130
4131                // If high > divisor high: branch to subtract (forward +offset)
4132                // BHI.N +6 (skip CMP, skip BLO, do subtract)
4133                // BHI: 1101 1000 offset8 where cond=1000 (HI)
4134                bytes.extend_from_slice(&0xD802u16.to_le_bytes()); // BHI +4 (to subtract block)
4135
4136                // If high < divisor high: branch past subtract
4137                // BLO.N +10 (skip to decrement)
4138                bytes.extend_from_slice(&0xD306u16.to_le_bytes()); // BLO/BCC +12 (past subtract)
4139
4140                // High words equal, compare low: CMP R6, R2
4141                bytes.extend_from_slice(&0x4296u16.to_le_bytes()); // CMP R6, R2 (16-bit)
4142                // BLO/BCC past subtract (skip SUBS+SBC.W+ORR.W = 10 bytes = 4 halfwords from PC+4)
4143                bytes.extend_from_slice(&0xD304u16.to_le_bytes()); // BCC +4 halfwords (past subtract)
4144
4145                // === Subtract block: remainder -= divisor, quotient |= 1 ===
4146                // SUBS R6, R6, R2
4147                bytes.extend_from_slice(&0x1AB6u16.to_le_bytes()); // SUBS R6, R6, R2 (16-bit)
4148                // SBC R7, R7, R3 (with borrow)
4149                // Thumb-2 SBC.W: EB67 0703 = SBC.W R7, R7, R3
4150                bytes.extend_from_slice(&0xEB67u16.to_le_bytes());
4151                bytes.extend_from_slice(&0x0703u16.to_le_bytes());
4152                // ORR R4, R4, #1 (set bit 0 of quotient low)
4153                bytes.extend_from_slice(&0xF044u16.to_le_bytes()); // ORR.W R4, R4, #1
4154                bytes.extend_from_slice(&0x0401u16.to_le_bytes());
4155
4156                // === Decrement counter and loop ===
4157                // SUBS.W R12, R12, #1 (decrement loop counter)
4158                // SUBS.W R12, R12, #1: F1BC 0C01
4159                bytes.extend_from_slice(&0xF1BCu16.to_le_bytes());
4160                bytes.extend_from_slice(&0x0C01u16.to_le_bytes());
4161
4162                // BNE back to loop_start
4163                let branch_offset_bytes = bytes.len() - loop_start + 4; // +4 for pipeline
4164                let offset_halfwords = -((branch_offset_bytes / 2) as i16);
4165                let bne_encoding = 0xD100u16 | ((offset_halfwords as u16) & 0xFF);
4166                bytes.extend_from_slice(&bne_encoding.to_le_bytes());
4167
4168                // === Loop done, move quotient to R0:R1 ===
4169                bytes.extend_from_slice(&0x4620u16.to_le_bytes()); // MOV R0, R4
4170                bytes.extend_from_slice(&0x4629u16.to_le_bytes()); // MOV R1, R5
4171
4172                // POP {R4-R7} - restore scratch registers (NO PC — inline code continues)
4173                // 16-bit POP: 1011 110 P rrrrrrrr where P=0 (no PC), r=R4-R7 = 0xF0
4174                // Encoding: 1011 1100 1111 0000 = 0xBCF0
4175                bytes.extend_from_slice(&0xBCF0u16.to_le_bytes());
4176
4177                Ok(bytes)
4178            }
4179
4180            // I64DivS: 64-bit signed division
4181            // Converts to unsigned, divides, then applies sign
4182            // Input: R0:R1 = dividend (signed), R2:R3 = divisor (signed)
4183            // Output: R0:R1 = quotient (signed)
4184            ArmOp::I64DivS {
4185                rdlo: _,
4186                rdhi: _,
4187                rnlo: _,
4188                rnhi: _,
4189                rmlo: _,
4190                rmhi: _,
4191            } => {
4192                let mut bytes = Vec::new();
4193
4194                // PUSH {R4-R11} - save scratch registers (NO LR — inline code)
4195                bytes.extend_from_slice(&0xE92Du16.to_le_bytes());
4196                bytes.extend_from_slice(&0x0FF0u16.to_le_bytes());
4197
4198                // Save result sign in R9: R9 = R1 XOR R3 (sign bit = MSB)
4199                // EOR.W R9, R1, R3
4200                bytes.extend_from_slice(&0xEA81u16.to_le_bytes());
4201                bytes.extend_from_slice(&0x0903u16.to_le_bytes());
4202
4203                // If dividend negative (R1 MSB set), negate it
4204                // TST R1, R1 (check sign)
4205                bytes.extend_from_slice(&0x4209u16.to_le_bytes()); // TST R1, R1
4206                // BPL skip_neg_dividend (+10 bytes = 5 halfwords)
4207                bytes.extend_from_slice(&0xD504u16.to_le_bytes()); // BPL +8
4208
4209                // Negate R0:R1 (64-bit): RSBS R0, R0, #0; SBC R1, R1, R1 LSL #1
4210                // Actually: MVN R0, R0; MVN R1, R1; ADDS R0, R0, #1; ADC R1, R1, #0
4211                bytes.extend_from_slice(&0x43C0u16.to_le_bytes()); // MVNS R0, R0
4212                bytes.extend_from_slice(&0x43C9u16.to_le_bytes()); // MVNS R1, R1
4213                bytes.extend_from_slice(&0x1C40u16.to_le_bytes()); // ADDS R0, R0, #1
4214                bytes.extend_from_slice(&0xF141u16.to_le_bytes()); // ADC.W R1, R1, #0
4215                bytes.extend_from_slice(&0x0100u16.to_le_bytes());
4216
4217                // If divisor negative (R3 MSB set), negate it
4218                bytes.extend_from_slice(&0x421Bu16.to_le_bytes()); // TST R3, R3
4219                bytes.extend_from_slice(&0xD504u16.to_le_bytes()); // BPL +8
4220
4221                // Negate R2:R3
4222                bytes.extend_from_slice(&0x43D2u16.to_le_bytes()); // MVNS R2, R2
4223                bytes.extend_from_slice(&0x43DBu16.to_le_bytes()); // MVNS R3, R3
4224                bytes.extend_from_slice(&0x1C52u16.to_le_bytes()); // ADDS R2, R2, #1
4225                bytes.extend_from_slice(&0xF143u16.to_le_bytes()); // ADC.W R3, R3, #0
4226                bytes.extend_from_slice(&0x0300u16.to_le_bytes());
4227
4228                // === Now do unsigned division (same as I64DivU) ===
4229                // Initialize quotient (R4:R5) = 0
4230                bytes.extend_from_slice(&0x2400u16.to_le_bytes());
4231                bytes.extend_from_slice(&0x2500u16.to_le_bytes());
4232                // Initialize remainder (R6:R7) = 0
4233                bytes.extend_from_slice(&0x2600u16.to_le_bytes());
4234                bytes.extend_from_slice(&0x2700u16.to_le_bytes());
4235                // Initialize loop counter R8 = 64
4236                bytes.extend_from_slice(&0xF04Fu16.to_le_bytes());
4237                bytes.extend_from_slice(&0x0840u16.to_le_bytes());
4238
4239                let loop_start = bytes.len();
4240
4241                // Shift quotient left
4242                bytes.extend_from_slice(&0x006Du16.to_le_bytes()); // LSLS R5, R5, #1
4243                bytes.extend_from_slice(&0xEA45u16.to_le_bytes()); // ORR.W R5, R5, R4, LSR #31
4244                bytes.extend_from_slice(&0x75D4u16.to_le_bytes());
4245                bytes.extend_from_slice(&0x0064u16.to_le_bytes()); // LSLS R4, R4, #1
4246
4247                // Shift remainder left, OR in MSB of dividend
4248                bytes.extend_from_slice(&0x007Fu16.to_le_bytes()); // LSLS R7, R7, #1
4249                bytes.extend_from_slice(&0xEA47u16.to_le_bytes()); // ORR.W R7, R7, R6, LSR #31
4250                bytes.extend_from_slice(&0x77D6u16.to_le_bytes());
4251                bytes.extend_from_slice(&0x0076u16.to_le_bytes()); // LSLS R6, R6, #1
4252                bytes.extend_from_slice(&0xEA46u16.to_le_bytes()); // ORR.W R6, R6, R1, LSR #31
4253                bytes.extend_from_slice(&0x76D1u16.to_le_bytes());
4254
4255                // Shift dividend left
4256                bytes.extend_from_slice(&0x0049u16.to_le_bytes()); // LSLS R1, R1, #1
4257                bytes.extend_from_slice(&0xEA41u16.to_le_bytes()); // ORR.W R1, R1, R0, LSR #31
4258                bytes.extend_from_slice(&0x71D0u16.to_le_bytes());
4259                bytes.extend_from_slice(&0x0040u16.to_le_bytes()); // LSLS R0, R0, #1
4260
4261                // Compare and conditionally subtract
4262                bytes.extend_from_slice(&0x429Fu16.to_le_bytes()); // CMP R7, R3
4263                bytes.extend_from_slice(&0xD802u16.to_le_bytes()); // BHI +4
4264                bytes.extend_from_slice(&0xD306u16.to_le_bytes()); // BCC +12
4265                bytes.extend_from_slice(&0x4296u16.to_le_bytes()); // CMP R6, R2
4266                bytes.extend_from_slice(&0xD304u16.to_le_bytes()); // BCC +4 halfwords
4267
4268                // Subtract and set quotient bit
4269                bytes.extend_from_slice(&0x1AB6u16.to_le_bytes()); // SUBS R6, R6, R2
4270                bytes.extend_from_slice(&0xEB67u16.to_le_bytes()); // SBC.W R7, R7, R3
4271                bytes.extend_from_slice(&0x0703u16.to_le_bytes());
4272                bytes.extend_from_slice(&0xF044u16.to_le_bytes()); // ORR.W R4, R4, #1
4273                bytes.extend_from_slice(&0x0401u16.to_le_bytes());
4274
4275                // Decrement and loop
4276                bytes.extend_from_slice(&0xF1B8u16.to_le_bytes()); // SUB.W R8, R8, #1
4277                bytes.extend_from_slice(&0x0801u16.to_le_bytes());
4278
4279                let branch_offset_bytes = bytes.len() - loop_start + 4;
4280                let offset_halfwords = -((branch_offset_bytes / 2) as i16);
4281                let bne_encoding = 0xD100u16 | ((offset_halfwords as u16) & 0xFF);
4282                bytes.extend_from_slice(&bne_encoding.to_le_bytes());
4283
4284                // Move quotient to R0:R1
4285                bytes.extend_from_slice(&0x4620u16.to_le_bytes()); // MOV R0, R4
4286                bytes.extend_from_slice(&0x4629u16.to_le_bytes()); // MOV R1, R5
4287
4288                // If result should be negative (R9 MSB set), negate R0:R1
4289                bytes.extend_from_slice(&0xF1B9u16.to_le_bytes()); // TST.W R9, R9 (check MSB)
4290                bytes.extend_from_slice(&0x0F00u16.to_le_bytes());
4291                bytes.extend_from_slice(&0xD504u16.to_le_bytes()); // BPL +8 (skip negation)
4292
4293                // Negate result R0:R1
4294                bytes.extend_from_slice(&0x43C0u16.to_le_bytes()); // MVNS R0, R0
4295                bytes.extend_from_slice(&0x43C9u16.to_le_bytes()); // MVNS R1, R1
4296                bytes.extend_from_slice(&0x1C40u16.to_le_bytes()); // ADDS R0, R0, #1
4297                bytes.extend_from_slice(&0xF141u16.to_le_bytes()); // ADC.W R1, R1, #0
4298                bytes.extend_from_slice(&0x0100u16.to_le_bytes());
4299
4300                // POP {R4-R11} - restore scratch registers (NO PC — inline code continues)
4301                bytes.extend_from_slice(&0xE8BDu16.to_le_bytes());
4302                bytes.extend_from_slice(&0x0FF0u16.to_le_bytes());
4303
4304                Ok(bytes)
4305            }
4306
4307            // I64RemU: 64-bit unsigned remainder using binary long division
4308            // Same algorithm as I64DivU but returns remainder instead of quotient
4309            // Input: R0:R1 = dividend, R2:R3 = divisor
4310            // Output: R0:R1 = remainder
4311            ArmOp::I64RemU {
4312                rdlo: _,
4313                rdhi: _,
4314                rnlo: _,
4315                rnhi: _,
4316                rmlo: _,
4317                rmhi: _,
4318            } => {
4319                let mut bytes = Vec::new();
4320
4321                // PUSH {R4-R8} - save scratch registers (NO LR — inline code)
4322                bytes.extend_from_slice(&0xE92Du16.to_le_bytes());
4323                bytes.extend_from_slice(&0x01F0u16.to_le_bytes());
4324
4325                // Initialize quotient (R4:R5) = 0 (computed but not returned)
4326                bytes.extend_from_slice(&0x2400u16.to_le_bytes());
4327                bytes.extend_from_slice(&0x2500u16.to_le_bytes());
4328                // Initialize remainder (R6:R7) = 0
4329                bytes.extend_from_slice(&0x2600u16.to_le_bytes());
4330                bytes.extend_from_slice(&0x2700u16.to_le_bytes());
4331                // Initialize loop counter R8 = 64
4332                bytes.extend_from_slice(&0xF04Fu16.to_le_bytes());
4333                bytes.extend_from_slice(&0x0840u16.to_le_bytes());
4334
4335                let loop_start = bytes.len();
4336
4337                // Shift quotient left (not needed for result, but keeps algorithm same)
4338                bytes.extend_from_slice(&0x006Du16.to_le_bytes()); // LSLS R5, R5, #1
4339                bytes.extend_from_slice(&0xEA45u16.to_le_bytes()); // ORR.W R5, R5, R4, LSR #31
4340                bytes.extend_from_slice(&0x75D4u16.to_le_bytes());
4341                bytes.extend_from_slice(&0x0064u16.to_le_bytes()); // LSLS R4, R4, #1
4342
4343                // Shift remainder left, OR in MSB of dividend
4344                bytes.extend_from_slice(&0x007Fu16.to_le_bytes()); // LSLS R7, R7, #1
4345                bytes.extend_from_slice(&0xEA47u16.to_le_bytes()); // ORR.W R7, R7, R6, LSR #31
4346                bytes.extend_from_slice(&0x77D6u16.to_le_bytes());
4347                bytes.extend_from_slice(&0x0076u16.to_le_bytes()); // LSLS R6, R6, #1
4348                bytes.extend_from_slice(&0xEA46u16.to_le_bytes()); // ORR.W R6, R6, R1, LSR #31
4349                bytes.extend_from_slice(&0x76D1u16.to_le_bytes());
4350
4351                // Shift dividend left
4352                bytes.extend_from_slice(&0x0049u16.to_le_bytes()); // LSLS R1, R1, #1
4353                bytes.extend_from_slice(&0xEA41u16.to_le_bytes()); // ORR.W R1, R1, R0, LSR #31
4354                bytes.extend_from_slice(&0x71D0u16.to_le_bytes());
4355                bytes.extend_from_slice(&0x0040u16.to_le_bytes()); // LSLS R0, R0, #1
4356
4357                // Compare and conditionally subtract
4358                bytes.extend_from_slice(&0x429Fu16.to_le_bytes()); // CMP R7, R3
4359                bytes.extend_from_slice(&0xD802u16.to_le_bytes()); // BHI +4
4360                bytes.extend_from_slice(&0xD306u16.to_le_bytes()); // BCC +12
4361                bytes.extend_from_slice(&0x4296u16.to_le_bytes()); // CMP R6, R2
4362                bytes.extend_from_slice(&0xD304u16.to_le_bytes()); // BCC +4 halfwords
4363
4364                // Subtract and set quotient bit
4365                bytes.extend_from_slice(&0x1AB6u16.to_le_bytes()); // SUBS R6, R6, R2
4366                bytes.extend_from_slice(&0xEB67u16.to_le_bytes()); // SBC.W R7, R7, R3
4367                bytes.extend_from_slice(&0x0703u16.to_le_bytes());
4368                bytes.extend_from_slice(&0xF044u16.to_le_bytes()); // ORR.W R4, R4, #1
4369                bytes.extend_from_slice(&0x0401u16.to_le_bytes());
4370
4371                // Decrement and loop
4372                bytes.extend_from_slice(&0xF1B8u16.to_le_bytes()); // SUB.W R8, R8, #1
4373                bytes.extend_from_slice(&0x0801u16.to_le_bytes());
4374
4375                let branch_offset_bytes = bytes.len() - loop_start + 4;
4376                let offset_halfwords = -((branch_offset_bytes / 2) as i16);
4377                let bne_encoding = 0xD100u16 | ((offset_halfwords as u16) & 0xFF);
4378                bytes.extend_from_slice(&bne_encoding.to_le_bytes());
4379
4380                // Move REMAINDER to R0:R1 (difference from I64DivU)
4381                bytes.extend_from_slice(&0x4630u16.to_le_bytes()); // MOV R0, R6
4382                bytes.extend_from_slice(&0x4639u16.to_le_bytes()); // MOV R1, R7
4383
4384                // POP {R4-R8} - restore scratch registers (NO PC — inline code continues)
4385                bytes.extend_from_slice(&0xE8BDu16.to_le_bytes());
4386                bytes.extend_from_slice(&0x01F0u16.to_le_bytes());
4387
4388                Ok(bytes)
4389            }
4390
4391            // I64RemS: 64-bit signed remainder
4392            // Remainder sign follows dividend sign (not quotient rule)
4393            // Input: R0:R1 = dividend (signed), R2:R3 = divisor (signed)
4394            // Output: R0:R1 = remainder (signed, same sign as dividend)
4395            ArmOp::I64RemS {
4396                rdlo: _,
4397                rdhi: _,
4398                rnlo: _,
4399                rnhi: _,
4400                rmlo: _,
4401                rmhi: _,
4402            } => {
4403                let mut bytes = Vec::new();
4404
4405                // PUSH {R4-R11} - save scratch registers (NO LR — inline code)
4406                bytes.extend_from_slice(&0xE92Du16.to_le_bytes());
4407                bytes.extend_from_slice(&0x0FF0u16.to_le_bytes());
4408
4409                // Save dividend sign in R9 (remainder sign = dividend sign)
4410                // MOV R9, R1 (just need the sign bit)
4411                bytes.extend_from_slice(&0x4689u16.to_le_bytes()); // MOV R9, R1
4412
4413                // If dividend negative (R1 MSB set), negate it
4414                bytes.extend_from_slice(&0x4209u16.to_le_bytes()); // TST R1, R1
4415                bytes.extend_from_slice(&0xD504u16.to_le_bytes()); // BPL +8
4416
4417                // Negate R0:R1
4418                bytes.extend_from_slice(&0x43C0u16.to_le_bytes()); // MVNS R0, R0
4419                bytes.extend_from_slice(&0x43C9u16.to_le_bytes()); // MVNS R1, R1
4420                bytes.extend_from_slice(&0x1C40u16.to_le_bytes()); // ADDS R0, R0, #1
4421                bytes.extend_from_slice(&0xF141u16.to_le_bytes()); // ADC.W R1, R1, #0
4422                bytes.extend_from_slice(&0x0100u16.to_le_bytes());
4423
4424                // If divisor negative (R3 MSB set), negate it
4425                bytes.extend_from_slice(&0x421Bu16.to_le_bytes()); // TST R3, R3
4426                bytes.extend_from_slice(&0xD504u16.to_le_bytes()); // BPL +8
4427
4428                // Negate R2:R3
4429                bytes.extend_from_slice(&0x43D2u16.to_le_bytes()); // MVNS R2, R2
4430                bytes.extend_from_slice(&0x43DBu16.to_le_bytes()); // MVNS R3, R3
4431                bytes.extend_from_slice(&0x1C52u16.to_le_bytes()); // ADDS R2, R2, #1
4432                bytes.extend_from_slice(&0xF143u16.to_le_bytes()); // ADC.W R3, R3, #0
4433                bytes.extend_from_slice(&0x0300u16.to_le_bytes());
4434
4435                // === Unsigned division algorithm ===
4436                // Initialize quotient (R4:R5) = 0
4437                bytes.extend_from_slice(&0x2400u16.to_le_bytes());
4438                bytes.extend_from_slice(&0x2500u16.to_le_bytes());
4439                // Initialize remainder (R6:R7) = 0
4440                bytes.extend_from_slice(&0x2600u16.to_le_bytes());
4441                bytes.extend_from_slice(&0x2700u16.to_le_bytes());
4442                // Initialize loop counter R8 = 64
4443                bytes.extend_from_slice(&0xF04Fu16.to_le_bytes());
4444                bytes.extend_from_slice(&0x0840u16.to_le_bytes());
4445
4446                let loop_start = bytes.len();
4447
4448                // Shift quotient left
4449                bytes.extend_from_slice(&0x006Du16.to_le_bytes()); // LSLS R5, R5, #1
4450                bytes.extend_from_slice(&0xEA45u16.to_le_bytes()); // ORR.W R5, R5, R4, LSR #31
4451                bytes.extend_from_slice(&0x75D4u16.to_le_bytes());
4452                bytes.extend_from_slice(&0x0064u16.to_le_bytes()); // LSLS R4, R4, #1
4453
4454                // Shift remainder left, OR in MSB of dividend
4455                bytes.extend_from_slice(&0x007Fu16.to_le_bytes()); // LSLS R7, R7, #1
4456                bytes.extend_from_slice(&0xEA47u16.to_le_bytes()); // ORR.W R7, R7, R6, LSR #31
4457                bytes.extend_from_slice(&0x77D6u16.to_le_bytes());
4458                bytes.extend_from_slice(&0x0076u16.to_le_bytes()); // LSLS R6, R6, #1
4459                bytes.extend_from_slice(&0xEA46u16.to_le_bytes()); // ORR.W R6, R6, R1, LSR #31
4460                bytes.extend_from_slice(&0x76D1u16.to_le_bytes());
4461
4462                // Shift dividend left
4463                bytes.extend_from_slice(&0x0049u16.to_le_bytes()); // LSLS R1, R1, #1
4464                bytes.extend_from_slice(&0xEA41u16.to_le_bytes()); // ORR.W R1, R1, R0, LSR #31
4465                bytes.extend_from_slice(&0x71D0u16.to_le_bytes());
4466                bytes.extend_from_slice(&0x0040u16.to_le_bytes()); // LSLS R0, R0, #1
4467
4468                // Compare and conditionally subtract
4469                bytes.extend_from_slice(&0x429Fu16.to_le_bytes()); // CMP R7, R3
4470                bytes.extend_from_slice(&0xD802u16.to_le_bytes()); // BHI +4
4471                bytes.extend_from_slice(&0xD306u16.to_le_bytes()); // BCC +12
4472                bytes.extend_from_slice(&0x4296u16.to_le_bytes()); // CMP R6, R2
4473                bytes.extend_from_slice(&0xD304u16.to_le_bytes()); // BCC +4 halfwords
4474
4475                // Subtract and set quotient bit
4476                bytes.extend_from_slice(&0x1AB6u16.to_le_bytes()); // SUBS R6, R6, R2
4477                bytes.extend_from_slice(&0xEB67u16.to_le_bytes()); // SBC.W R7, R7, R3
4478                bytes.extend_from_slice(&0x0703u16.to_le_bytes());
4479                bytes.extend_from_slice(&0xF044u16.to_le_bytes()); // ORR.W R4, R4, #1
4480                bytes.extend_from_slice(&0x0401u16.to_le_bytes());
4481
4482                // Decrement and loop
4483                bytes.extend_from_slice(&0xF1B8u16.to_le_bytes()); // SUB.W R8, R8, #1
4484                bytes.extend_from_slice(&0x0801u16.to_le_bytes());
4485
4486                let branch_offset_bytes = bytes.len() - loop_start + 4;
4487                let offset_halfwords = -((branch_offset_bytes / 2) as i16);
4488                let bne_encoding = 0xD100u16 | ((offset_halfwords as u16) & 0xFF);
4489                bytes.extend_from_slice(&bne_encoding.to_le_bytes());
4490
4491                // Move remainder to R0:R1
4492                bytes.extend_from_slice(&0x4630u16.to_le_bytes()); // MOV R0, R6
4493                bytes.extend_from_slice(&0x4639u16.to_le_bytes()); // MOV R1, R7
4494
4495                // If original dividend was negative (R9 MSB set), negate remainder
4496                bytes.extend_from_slice(&0xF1B9u16.to_le_bytes()); // TST.W R9, R9
4497                bytes.extend_from_slice(&0x0F00u16.to_le_bytes());
4498                bytes.extend_from_slice(&0xD504u16.to_le_bytes()); // BPL +8
4499
4500                // Negate result R0:R1
4501                bytes.extend_from_slice(&0x43C0u16.to_le_bytes()); // MVNS R0, R0
4502                bytes.extend_from_slice(&0x43C9u16.to_le_bytes()); // MVNS R1, R1
4503                bytes.extend_from_slice(&0x1C40u16.to_le_bytes()); // ADDS R0, R0, #1
4504                bytes.extend_from_slice(&0xF141u16.to_le_bytes()); // ADC.W R1, R1, #0
4505                bytes.extend_from_slice(&0x0100u16.to_le_bytes());
4506
4507                // POP {R4-R11} - restore scratch registers (NO PC — inline code continues)
4508                bytes.extend_from_slice(&0xE8BDu16.to_le_bytes());
4509                bytes.extend_from_slice(&0x0FF0u16.to_le_bytes());
4510
4511                Ok(bytes)
4512            }
4513
4514            // === F32 VFP single-precision Thumb-2 encodings ===
4515            // VFP instruction words are identical to ARM32; emit as two LE halfwords.
4516            ArmOp::F32Add { sd, sn, sm } => {
4517                Ok(vfp_to_thumb_bytes(encode_vfp_3reg(0xEE300A00, sd, sn, sm)?))
4518            }
4519            ArmOp::F32Sub { sd, sn, sm } => {
4520                Ok(vfp_to_thumb_bytes(encode_vfp_3reg(0xEE300A40, sd, sn, sm)?))
4521            }
4522            ArmOp::F32Mul { sd, sn, sm } => {
4523                Ok(vfp_to_thumb_bytes(encode_vfp_3reg(0xEE200A00, sd, sn, sm)?))
4524            }
4525            ArmOp::F32Div { sd, sn, sm } => {
4526                Ok(vfp_to_thumb_bytes(encode_vfp_3reg(0xEE800A00, sd, sn, sm)?))
4527            }
4528            ArmOp::F32Abs { sd, sm } => {
4529                Ok(vfp_to_thumb_bytes(encode_vfp_2reg(0xEEB00AC0, sd, sm)?))
4530            }
4531            ArmOp::F32Neg { sd, sm } => {
4532                Ok(vfp_to_thumb_bytes(encode_vfp_2reg(0xEEB10A40, sd, sm)?))
4533            }
4534            ArmOp::F32Sqrt { sd, sm } => {
4535                Ok(vfp_to_thumb_bytes(encode_vfp_2reg(0xEEB10AC0, sd, sm)?))
4536            }
4537
4538            // f32 pseudo-ops — multi-instruction sequences
4539            // FPSCR RMode: 00=nearest, 01=+inf(ceil), 10=-inf(floor), 11=zero(trunc)
4540            ArmOp::F32Ceil { sd, sm } => self.encode_thumb_f32_rounding(sd, sm, 0b01),
4541            ArmOp::F32Floor { sd, sm } => self.encode_thumb_f32_rounding(sd, sm, 0b10),
4542            ArmOp::F32Trunc { sd, sm } => self.encode_thumb_f32_rounding(sd, sm, 0b11),
4543            ArmOp::F32Nearest { sd, sm } => self.encode_thumb_f32_rounding(sd, sm, 0b00),
4544            ArmOp::F32Min { sd, sn, sm } => self.encode_thumb_f32_minmax(sd, sn, sm, true),
4545            ArmOp::F32Max { sd, sn, sm } => self.encode_thumb_f32_minmax(sd, sn, sm, false),
4546            ArmOp::F32Copysign { sd, sn, sm } => self.encode_thumb_f32_copysign(sd, sn, sm),
4547
4548            // f32 comparisons — VCMP + VMRS + MOV #0 + IT + MOV #1
4549            ArmOp::F32Eq { rd, sn, sm } => self.encode_thumb_f32_compare(rd, sn, sm, 0x0),
4550            ArmOp::F32Ne { rd, sn, sm } => self.encode_thumb_f32_compare(rd, sn, sm, 0x1),
4551            ArmOp::F32Lt { rd, sn, sm } => self.encode_thumb_f32_compare(rd, sn, sm, 0x4),
4552            ArmOp::F32Le { rd, sn, sm } => self.encode_thumb_f32_compare(rd, sn, sm, 0x9),
4553            ArmOp::F32Gt { rd, sn, sm } => self.encode_thumb_f32_compare(rd, sn, sm, 0xC),
4554            ArmOp::F32Ge { rd, sn, sm } => self.encode_thumb_f32_compare(rd, sn, sm, 0xA),
4555
4556            ArmOp::F32Const { sd, value } => self.encode_thumb_f32_const(sd, *value),
4557
4558            ArmOp::F32Load { sd, addr } => {
4559                Ok(vfp_to_thumb_bytes(encode_vfp_ldst(0xED900A00, sd, addr)?))
4560            }
4561            ArmOp::F32Store { sd, addr } => {
4562                Ok(vfp_to_thumb_bytes(encode_vfp_ldst(0xED800A00, sd, addr)?))
4563            }
4564
4565            ArmOp::F32ConvertI32S { sd, rm } => self.encode_thumb_f32_convert_i32(sd, rm, true),
4566            ArmOp::F32ConvertI32U { sd, rm } => self.encode_thumb_f32_convert_i32(sd, rm, false),
4567            ArmOp::F32ConvertI64S { .. } | ArmOp::F32ConvertI64U { .. } => {
4568                Err(synth_core::Error::synthesis(
4569                    "F32 i64 conversion not supported (requires register pairs on 32-bit ARM)",
4570                ))
4571            }
4572            ArmOp::F32ReinterpretI32 { sd, rm } => {
4573                Ok(vfp_to_thumb_bytes(encode_vmov_core_sreg(true, sd, rm)?))
4574            }
4575            ArmOp::I32ReinterpretF32 { rd, sm } => {
4576                Ok(vfp_to_thumb_bytes(encode_vmov_core_sreg(false, sm, rd)?))
4577            }
4578            ArmOp::I32TruncF32S { rd, sm } => self.encode_thumb_i32_trunc_f32(rd, sm, true),
4579            ArmOp::I32TruncF32U { rd, sm } => self.encode_thumb_i32_trunc_f32(rd, sm, false),
4580
4581            // === F64 VFP double-precision Thumb-2 encodings ===
4582            // VFP instruction words are identical to ARM32; emit as two LE halfwords.
4583            ArmOp::F64Add { dd, dn, dm } => Ok(vfp_to_thumb_bytes(encode_vfp_3reg_f64(
4584                0xEE300B00, dd, dn, dm,
4585            )?)),
4586            ArmOp::F64Sub { dd, dn, dm } => Ok(vfp_to_thumb_bytes(encode_vfp_3reg_f64(
4587                0xEE300B40, dd, dn, dm,
4588            )?)),
4589            ArmOp::F64Mul { dd, dn, dm } => Ok(vfp_to_thumb_bytes(encode_vfp_3reg_f64(
4590                0xEE200B00, dd, dn, dm,
4591            )?)),
4592            ArmOp::F64Div { dd, dn, dm } => Ok(vfp_to_thumb_bytes(encode_vfp_3reg_f64(
4593                0xEE800B00, dd, dn, dm,
4594            )?)),
4595            ArmOp::F64Abs { dd, dm } => {
4596                Ok(vfp_to_thumb_bytes(encode_vfp_2reg_f64(0xEEB00BC0, dd, dm)?))
4597            }
4598            ArmOp::F64Neg { dd, dm } => {
4599                Ok(vfp_to_thumb_bytes(encode_vfp_2reg_f64(0xEEB10B40, dd, dm)?))
4600            }
4601            ArmOp::F64Sqrt { dd, dm } => {
4602                Ok(vfp_to_thumb_bytes(encode_vfp_2reg_f64(0xEEB10BC0, dd, dm)?))
4603            }
4604
4605            // f64 pseudo-ops
4606            // FPSCR RMode: 00=nearest, 01=+inf(ceil), 10=-inf(floor), 11=zero(trunc)
4607            ArmOp::F64Ceil { dd, dm } => self.encode_thumb_f64_rounding(dd, dm, 0b01),
4608            ArmOp::F64Floor { dd, dm } => self.encode_thumb_f64_rounding(dd, dm, 0b10),
4609            ArmOp::F64Trunc { dd, dm } => self.encode_thumb_f64_rounding(dd, dm, 0b11),
4610            ArmOp::F64Nearest { dd, dm } => self.encode_thumb_f64_rounding(dd, dm, 0b00),
4611            ArmOp::F64Min { dd, dn, dm } => self.encode_thumb_f64_minmax(dd, dn, dm, true),
4612            ArmOp::F64Max { dd, dn, dm } => self.encode_thumb_f64_minmax(dd, dn, dm, false),
4613            ArmOp::F64Copysign { dd, dn, dm } => self.encode_thumb_f64_copysign(dd, dn, dm),
4614
4615            // f64 comparisons
4616            ArmOp::F64Eq { rd, dn, dm } => self.encode_thumb_f64_compare(rd, dn, dm, 0x0),
4617            ArmOp::F64Ne { rd, dn, dm } => self.encode_thumb_f64_compare(rd, dn, dm, 0x1),
4618            ArmOp::F64Lt { rd, dn, dm } => self.encode_thumb_f64_compare(rd, dn, dm, 0x4),
4619            ArmOp::F64Le { rd, dn, dm } => self.encode_thumb_f64_compare(rd, dn, dm, 0x9),
4620            ArmOp::F64Gt { rd, dn, dm } => self.encode_thumb_f64_compare(rd, dn, dm, 0xC),
4621            ArmOp::F64Ge { rd, dn, dm } => self.encode_thumb_f64_compare(rd, dn, dm, 0xA),
4622
4623            ArmOp::F64Const { dd, value } => self.encode_thumb_f64_const(dd, *value),
4624
4625            ArmOp::F64Load { dd, addr } => Ok(vfp_to_thumb_bytes(encode_vfp_ldst_f64(
4626                0xED900B00, dd, addr,
4627            )?)),
4628            ArmOp::F64Store { dd, addr } => Ok(vfp_to_thumb_bytes(encode_vfp_ldst_f64(
4629                0xED800B00, dd, addr,
4630            )?)),
4631
4632            ArmOp::F64ConvertI32S { dd, rm } => self.encode_thumb_f64_convert_i32(dd, rm, true),
4633            ArmOp::F64ConvertI32U { dd, rm } => self.encode_thumb_f64_convert_i32(dd, rm, false),
4634            ArmOp::F64ConvertI64S { .. } | ArmOp::F64ConvertI64U { .. } => {
4635                Err(synth_core::Error::synthesis(
4636                    "F64 i64 conversion not supported (requires register pairs on 32-bit ARM)",
4637                ))
4638            }
4639            ArmOp::F64PromoteF32 { dd, sm } => self.encode_thumb_f64_promote_f32(dd, sm),
4640            ArmOp::F64ReinterpretI64 { dd, rmlo, rmhi } => Ok(vfp_to_thumb_bytes(
4641                encode_vmov_core_dreg(true, dd, rmlo, rmhi)?,
4642            )),
4643            ArmOp::I64ReinterpretF64 { rdlo, rdhi, dm } => Ok(vfp_to_thumb_bytes(
4644                encode_vmov_core_dreg(false, dm, rdlo, rdhi)?,
4645            )),
4646            ArmOp::I64TruncF64S { .. } | ArmOp::I64TruncF64U { .. } => {
4647                Err(synth_core::Error::synthesis(
4648                    "i64 truncation from F64 not supported (requires i64 register pairs on 32-bit ARM)",
4649                ))
4650            }
4651            ArmOp::I32TruncF64S { rd, dm } => self.encode_thumb_i32_trunc_f64(rd, dm, true),
4652            ArmOp::I32TruncF64U { rd, dm } => self.encode_thumb_i32_trunc_f64(rd, dm, false),
4653
4654            // ===== i64 operations: encode as multi-instruction Thumb-2 sequences =====
4655
4656            // I64Add: ADDS rdlo, rnlo, rmlo; ADC.W rdhi, rnhi, rmhi
4657            ArmOp::I64Add {
4658                rdlo,
4659                rdhi,
4660                rnlo,
4661                rnhi,
4662                rmlo,
4663                rmhi,
4664            } => {
4665                let mut bytes = Vec::new();
4666                // ADDS rdlo, rnlo, rmlo (16-bit)
4667                bytes.extend_from_slice(&self.encode_thumb(&ArmOp::Adds {
4668                    rd: *rdlo,
4669                    rn: *rnlo,
4670                    op2: Operand2::Reg(*rmlo),
4671                })?);
4672                // ADC.W rdhi, rnhi, rmhi (32-bit)
4673                bytes.extend_from_slice(&self.encode_thumb(&ArmOp::Adc {
4674                    rd: *rdhi,
4675                    rn: *rnhi,
4676                    op2: Operand2::Reg(*rmhi),
4677                })?);
4678                Ok(bytes)
4679            }
4680
4681            // I64Sub: SUBS rdlo, rnlo, rmlo; SBC.W rdhi, rnhi, rmhi
4682            ArmOp::I64Sub {
4683                rdlo,
4684                rdhi,
4685                rnlo,
4686                rnhi,
4687                rmlo,
4688                rmhi,
4689            } => {
4690                let mut bytes = Vec::new();
4691                // SUBS rdlo, rnlo, rmlo (16-bit)
4692                bytes.extend_from_slice(&self.encode_thumb(&ArmOp::Subs {
4693                    rd: *rdlo,
4694                    rn: *rnlo,
4695                    op2: Operand2::Reg(*rmlo),
4696                })?);
4697                // SBC.W rdhi, rnhi, rmhi (32-bit)
4698                bytes.extend_from_slice(&self.encode_thumb(&ArmOp::Sbc {
4699                    rd: *rdhi,
4700                    rn: *rnhi,
4701                    op2: Operand2::Reg(*rmhi),
4702                })?);
4703                Ok(bytes)
4704            }
4705
4706            // I64And: AND rdlo, rnlo, rmlo; AND rdhi, rnhi, rmhi
4707            ArmOp::I64And {
4708                rdlo,
4709                rdhi,
4710                rnlo,
4711                rnhi,
4712                rmlo,
4713                rmhi,
4714            } => {
4715                let mut bytes = Vec::new();
4716                bytes.extend_from_slice(&self.encode_thumb(&ArmOp::And {
4717                    rd: *rdlo,
4718                    rn: *rnlo,
4719                    op2: Operand2::Reg(*rmlo),
4720                })?);
4721                bytes.extend_from_slice(&self.encode_thumb(&ArmOp::And {
4722                    rd: *rdhi,
4723                    rn: *rnhi,
4724                    op2: Operand2::Reg(*rmhi),
4725                })?);
4726                Ok(bytes)
4727            }
4728
4729            // I64Or: ORR rdlo, rnlo, rmlo; ORR rdhi, rnhi, rmhi
4730            ArmOp::I64Or {
4731                rdlo,
4732                rdhi,
4733                rnlo,
4734                rnhi,
4735                rmlo,
4736                rmhi,
4737            } => {
4738                let mut bytes = Vec::new();
4739                bytes.extend_from_slice(&self.encode_thumb(&ArmOp::Orr {
4740                    rd: *rdlo,
4741                    rn: *rnlo,
4742                    op2: Operand2::Reg(*rmlo),
4743                })?);
4744                bytes.extend_from_slice(&self.encode_thumb(&ArmOp::Orr {
4745                    rd: *rdhi,
4746                    rn: *rnhi,
4747                    op2: Operand2::Reg(*rmhi),
4748                })?);
4749                Ok(bytes)
4750            }
4751
4752            // I64Xor: EOR rdlo, rnlo, rmlo; EOR rdhi, rnhi, rmhi
4753            ArmOp::I64Xor {
4754                rdlo,
4755                rdhi,
4756                rnlo,
4757                rnhi,
4758                rmlo,
4759                rmhi,
4760            } => {
4761                let mut bytes = Vec::new();
4762                bytes.extend_from_slice(&self.encode_thumb(&ArmOp::Eor {
4763                    rd: *rdlo,
4764                    rn: *rnlo,
4765                    op2: Operand2::Reg(*rmlo),
4766                })?);
4767                bytes.extend_from_slice(&self.encode_thumb(&ArmOp::Eor {
4768                    rd: *rdhi,
4769                    rn: *rnhi,
4770                    op2: Operand2::Reg(*rmhi),
4771                })?);
4772                Ok(bytes)
4773            }
4774
4775            // I64Eqz: ORR scratch, lo, hi; ITE EQ; MOV rd, #1; MOV rd, #0
4776            ArmOp::I64Eqz { rd, rnlo, rnhi } => self.encode_thumb(&ArmOp::I64SetCondZ {
4777                rd: *rd,
4778                rn_lo: *rnlo,
4779                rn_hi: *rnhi,
4780            }),
4781
4782            // I64 comparisons: delegate to I64SetCond
4783            ArmOp::I64Eq {
4784                rd,
4785                rnlo,
4786                rnhi,
4787                rmlo,
4788                rmhi,
4789            } => self.encode_thumb(&ArmOp::I64SetCond {
4790                rd: *rd,
4791                rn_lo: *rnlo,
4792                rn_hi: *rnhi,
4793                rm_lo: *rmlo,
4794                rm_hi: *rmhi,
4795                cond: synth_synthesis::Condition::EQ,
4796            }),
4797
4798            ArmOp::I64Ne {
4799                rd,
4800                rnlo,
4801                rnhi,
4802                rmlo,
4803                rmhi,
4804            } => self.encode_thumb(&ArmOp::I64SetCond {
4805                rd: *rd,
4806                rn_lo: *rnlo,
4807                rn_hi: *rnhi,
4808                rm_lo: *rmlo,
4809                rm_hi: *rmhi,
4810                cond: synth_synthesis::Condition::NE,
4811            }),
4812
4813            ArmOp::I64LtS {
4814                rd,
4815                rnlo,
4816                rnhi,
4817                rmlo,
4818                rmhi,
4819            } => self.encode_thumb(&ArmOp::I64SetCond {
4820                rd: *rd,
4821                rn_lo: *rnlo,
4822                rn_hi: *rnhi,
4823                rm_lo: *rmlo,
4824                rm_hi: *rmhi,
4825                cond: synth_synthesis::Condition::LT,
4826            }),
4827
4828            ArmOp::I64LtU {
4829                rd,
4830                rnlo,
4831                rnhi,
4832                rmlo,
4833                rmhi,
4834            } => self.encode_thumb(&ArmOp::I64SetCond {
4835                rd: *rd,
4836                rn_lo: *rnlo,
4837                rn_hi: *rnhi,
4838                rm_lo: *rmlo,
4839                rm_hi: *rmhi,
4840                cond: synth_synthesis::Condition::LO,
4841            }),
4842
4843            ArmOp::I64LeS {
4844                rd,
4845                rnlo,
4846                rnhi,
4847                rmlo,
4848                rmhi,
4849            } => self.encode_thumb(&ArmOp::I64SetCond {
4850                rd: *rd,
4851                rn_lo: *rnlo,
4852                rn_hi: *rnhi,
4853                rm_lo: *rmlo,
4854                rm_hi: *rmhi,
4855                cond: synth_synthesis::Condition::LE,
4856            }),
4857
4858            ArmOp::I64LeU {
4859                rd,
4860                rnlo,
4861                rnhi,
4862                rmlo,
4863                rmhi,
4864            } => self.encode_thumb(&ArmOp::I64SetCond {
4865                rd: *rd,
4866                rn_lo: *rnlo,
4867                rn_hi: *rnhi,
4868                rm_lo: *rmlo,
4869                rm_hi: *rmhi,
4870                cond: synth_synthesis::Condition::LS,
4871            }),
4872
4873            ArmOp::I64GtS {
4874                rd,
4875                rnlo,
4876                rnhi,
4877                rmlo,
4878                rmhi,
4879            } => self.encode_thumb(&ArmOp::I64SetCond {
4880                rd: *rd,
4881                rn_lo: *rnlo,
4882                rn_hi: *rnhi,
4883                rm_lo: *rmlo,
4884                rm_hi: *rmhi,
4885                cond: synth_synthesis::Condition::GT,
4886            }),
4887
4888            ArmOp::I64GtU {
4889                rd,
4890                rnlo,
4891                rnhi,
4892                rmlo,
4893                rmhi,
4894            } => self.encode_thumb(&ArmOp::I64SetCond {
4895                rd: *rd,
4896                rn_lo: *rnlo,
4897                rn_hi: *rnhi,
4898                rm_lo: *rmlo,
4899                rm_hi: *rmhi,
4900                cond: synth_synthesis::Condition::HI,
4901            }),
4902
4903            ArmOp::I64GeS {
4904                rd,
4905                rnlo,
4906                rnhi,
4907                rmlo,
4908                rmhi,
4909            } => self.encode_thumb(&ArmOp::I64SetCond {
4910                rd: *rd,
4911                rn_lo: *rnlo,
4912                rn_hi: *rnhi,
4913                rm_lo: *rmlo,
4914                rm_hi: *rmhi,
4915                cond: synth_synthesis::Condition::GE,
4916            }),
4917
4918            ArmOp::I64GeU {
4919                rd,
4920                rnlo,
4921                rnhi,
4922                rmlo,
4923                rmhi,
4924            } => self.encode_thumb(&ArmOp::I64SetCond {
4925                rd: *rd,
4926                rn_lo: *rnlo,
4927                rn_hi: *rnhi,
4928                rm_lo: *rmlo,
4929                rm_hi: *rmhi,
4930                cond: synth_synthesis::Condition::HS,
4931            }),
4932
4933            // I64Const: MOVW rdlo, lo16; MOVT rdlo, hi16; MOVW rdhi, lo16_hi; MOVT rdhi, hi16_hi
4934            ArmOp::I64Const { rdlo, rdhi, value } => {
4935                let lo32 = *value as u32;
4936                let hi32 = (*value >> 32) as u32;
4937                let mut bytes = Vec::new();
4938                // Load low 32 bits into rdlo
4939                bytes.extend_from_slice(
4940                    &self.encode_thumb32_movw_raw(reg_to_bits(rdlo), lo32 & 0xFFFF)?,
4941                );
4942                if lo32 > 0xFFFF {
4943                    bytes.extend_from_slice(
4944                        &self.encode_thumb32_movt_raw(reg_to_bits(rdlo), lo32 >> 16)?,
4945                    );
4946                }
4947                // Load high 32 bits into rdhi
4948                bytes.extend_from_slice(
4949                    &self.encode_thumb32_movw_raw(reg_to_bits(rdhi), hi32 & 0xFFFF)?,
4950                );
4951                if hi32 > 0xFFFF {
4952                    bytes.extend_from_slice(
4953                        &self.encode_thumb32_movt_raw(reg_to_bits(rdhi), hi32 >> 16)?,
4954                    );
4955                }
4956                Ok(bytes)
4957            }
4958
4959            // I64Ldr: LDR rdlo, [base, offset]; LDR rdhi, [base, offset+4]
4960            ArmOp::I64Ldr { rdlo, rdhi, addr } => {
4961                let mut bytes = Vec::new();
4962                let offset = if addr.offset < 0 {
4963                    0u32
4964                } else {
4965                    addr.offset as u32
4966                };
4967                bytes.extend_from_slice(&self.encode_thumb32_ldr(rdlo, &addr.base, offset)?);
4968                bytes.extend_from_slice(&self.encode_thumb32_ldr(
4969                    rdhi,
4970                    &addr.base,
4971                    offset.wrapping_add(4),
4972                )?);
4973                Ok(bytes)
4974            }
4975
4976            // I64Str: STR rdlo, [base, offset]; STR rdhi, [base, offset+4]
4977            ArmOp::I64Str { rdlo, rdhi, addr } => {
4978                let mut bytes = Vec::new();
4979                let offset = if addr.offset < 0 {
4980                    0u32
4981                } else {
4982                    addr.offset as u32
4983                };
4984                bytes.extend_from_slice(&self.encode_thumb32_str(rdlo, &addr.base, offset)?);
4985                bytes.extend_from_slice(&self.encode_thumb32_str(
4986                    rdhi,
4987                    &addr.base,
4988                    offset.wrapping_add(4),
4989                )?);
4990                Ok(bytes)
4991            }
4992
4993            // I64ExtendI32S: MOV rdlo, rn; ASR rdhi, rdlo, #31 (sign-extend)
4994            ArmOp::I64ExtendI32S { rdlo, rdhi, rn } => {
4995                let mut bytes = Vec::new();
4996                if rdlo != rn {
4997                    // MOV rdlo, rn (16-bit)
4998                    bytes.extend_from_slice(&self.encode_thumb(&ArmOp::Mov {
4999                        rd: *rdlo,
5000                        op2: Operand2::Reg(*rn),
5001                    })?);
5002                }
5003                // ASR rdhi, rdlo, #31 (sign-extend: fill high word with sign bit)
5004                bytes.extend_from_slice(
5005                    &self.encode_thumb32_shift(rdhi, rdlo, 31, 0b10)?, // ASR type
5006                );
5007                Ok(bytes)
5008            }
5009
5010            // I64ExtendI32U: MOV rdlo, rn; MOV rdhi, #0
5011            ArmOp::I64ExtendI32U { rdlo, rdhi, rn } => {
5012                let mut bytes = Vec::new();
5013                if rdlo != rn {
5014                    // MOV rdlo, rn
5015                    bytes.extend_from_slice(&self.encode_thumb(&ArmOp::Mov {
5016                        rd: *rdlo,
5017                        op2: Operand2::Reg(*rn),
5018                    })?);
5019                }
5020                // MOV rdhi, #0 (16-bit: MOVS Rd, #0)
5021                let rdhi_bits = reg_to_bits(rdhi) as u16;
5022                let instr: u16 = 0x2000 | (rdhi_bits << 8);
5023                bytes.extend_from_slice(&instr.to_le_bytes());
5024                Ok(bytes)
5025            }
5026
5027            // I32WrapI64: MOV rd, rnlo (just take low 32 bits)
5028            ArmOp::I32WrapI64 { rd, rnlo } => {
5029                if rd == rnlo {
5030                    // No-op: already in the right register
5031                    let instr: u16 = 0xBF00; // NOP
5032                    Ok(instr.to_le_bytes().to_vec())
5033                } else {
5034                    // MOV rd, rnlo
5035                    self.encode_thumb(&ArmOp::Mov {
5036                        rd: *rd,
5037                        op2: Operand2::Reg(*rnlo),
5038                    })
5039                }
5040            }
5041
5042            // ===== Helium MVE operations (Thumb-2 encoding) =====
5043            ArmOp::MveLoad { qd, addr } => Ok(vfp_to_thumb_bytes(encode_mve_vldrw(qd, addr))),
5044            ArmOp::MveStore { qd, addr } => Ok(vfp_to_thumb_bytes(encode_mve_vstrw(qd, addr))),
5045            ArmOp::MveConst { qd, bytes } => self.encode_thumb_mve_const(qd, bytes),
5046            ArmOp::MveAnd { qd, qn, qm } => Ok(vfp_to_thumb_bytes(encode_mve_3reg_bitwise(
5047                0xEF000150, qd, qn, qm,
5048            ))),
5049            ArmOp::MveOrr { qd, qn, qm } => Ok(vfp_to_thumb_bytes(encode_mve_3reg_bitwise(
5050                0xEF200150, qd, qn, qm,
5051            ))),
5052            ArmOp::MveEor { qd, qn, qm } => Ok(vfp_to_thumb_bytes(encode_mve_3reg_bitwise(
5053                0xFF000150, qd, qn, qm,
5054            ))),
5055            ArmOp::MveMvn { qd, qm } => {
5056                // VMVN Qd, Qm: 0xFFB005C0 | Qd<<12 | Qm
5057                let qd_enc = qreg_to_num(qd);
5058                let qm_enc = qreg_to_num(qm);
5059                let instr: u32 = 0xFFB005C0 | ((qd_enc * 2) << 12) | (qm_enc * 2);
5060                Ok(vfp_to_thumb_bytes(instr))
5061            }
5062            ArmOp::MveBic { qd, qn, qm } => Ok(vfp_to_thumb_bytes(encode_mve_3reg_bitwise(
5063                0xEF100150, qd, qn, qm,
5064            ))),
5065            ArmOp::MveAddI { qd, qn, qm, size } => {
5066                let sz = mve_size_bits(size);
5067                let base: u32 = 0xEF000840 | (sz << 20);
5068                Ok(vfp_to_thumb_bytes(encode_mve_3reg(base, qd, qn, qm)))
5069            }
5070            ArmOp::MveSubI { qd, qn, qm, size } => {
5071                let sz = mve_size_bits(size);
5072                let base: u32 = 0xFF000840 | (sz << 20);
5073                Ok(vfp_to_thumb_bytes(encode_mve_3reg(base, qd, qn, qm)))
5074            }
5075            ArmOp::MveMulI { qd, qn, qm, size } => {
5076                let sz = mve_size_bits(size);
5077                let base: u32 = 0xEF000950 | (sz << 20);
5078                Ok(vfp_to_thumb_bytes(encode_mve_3reg(base, qd, qn, qm)))
5079            }
5080            ArmOp::MveNegI { qd, qm, size } => {
5081                let sz = mve_size_bits(size);
5082                // VNEG.Sx Qd, Qm
5083                let qd_enc = qreg_to_num(qd);
5084                let qm_enc = qreg_to_num(qm);
5085                let base: u32 = 0xFFB103C0 | (sz << 18);
5086                let instr = base | ((qd_enc * 2) << 12) | (qm_enc * 2);
5087                Ok(vfp_to_thumb_bytes(instr))
5088            }
5089            ArmOp::MveDup { qd, rn, size } => {
5090                let sz = mve_size_bits(size);
5091                let qd_enc = qreg_to_num(qd);
5092                let rn_bits = reg_to_bits(rn);
5093                // VDUP.sz Qd, Rn: EEA0 0B10 variant
5094                // size encoding: 00=32, 01=16, 10=8
5095                let be = match sz {
5096                    0 => 0b00u32, // 8-bit
5097                    1 => 0b01,    // 16-bit
5098                    _ => 0b00,    // 32-bit (default)
5099                };
5100                let instr: u32 = 0xEEA00B10 | ((qd_enc * 2) << 16) | (rn_bits << 12) | (be << 5);
5101                Ok(vfp_to_thumb_bytes(instr))
5102            }
5103            ArmOp::MveExtractLane { rd, qn, lane, size } => {
5104                let qn_enc = qreg_to_num(qn);
5105                let rd_bits = reg_to_bits(rd);
5106                // VMOV.sz Rd, Dn[x] — extract from Q-register lane
5107                // For 32-bit: VMOV Rd, Dn — where Dn is the appropriate D-register
5108                let d_reg = qn_enc * 2 + ((*lane as u32) >> 1);
5109                let lane_in_d = (*lane as u32) & 1;
5110                let _sz = mve_size_bits(size);
5111                // VMOV Rd, Dn[x]: EE10 0B10 for 32-bit
5112                let instr: u32 = 0xEE100B10 | (d_reg << 16) | (rd_bits << 12) | (lane_in_d << 21);
5113                Ok(vfp_to_thumb_bytes(instr))
5114            }
5115            ArmOp::MveInsertLane { qd, rn, lane, size } => {
5116                let qd_enc = qreg_to_num(qd);
5117                let rn_bits = reg_to_bits(rn);
5118                let d_reg = qd_enc * 2 + ((*lane as u32) >> 1);
5119                let lane_in_d = (*lane as u32) & 1;
5120                let _sz = mve_size_bits(size);
5121                // VMOV Dn[x], Rn: EE00 0B10 for 32-bit
5122                let instr: u32 = 0xEE000B10 | (d_reg << 16) | (rn_bits << 12) | (lane_in_d << 21);
5123                Ok(vfp_to_thumb_bytes(instr))
5124            }
5125
5126            // MVE float comparisons — emit VCMP + VPSEL sequence (simplified: just VCMP)
5127            ArmOp::MveCmpEqI { qd, qn, qm, size }
5128            | ArmOp::MveCmpNeI { qd, qn, qm, size }
5129            | ArmOp::MveCmpLtS { qd, qn, qm, size }
5130            | ArmOp::MveCmpLtU { qd, qn, qm, size }
5131            | ArmOp::MveCmpGtS { qd, qn, qm, size }
5132            | ArmOp::MveCmpGtU { qd, qn, qm, size }
5133            | ArmOp::MveCmpLeS { qd, qn, qm, size }
5134            | ArmOp::MveCmpLeU { qd, qn, qm, size }
5135            | ArmOp::MveCmpGeS { qd, qn, qm, size }
5136            | ArmOp::MveCmpGeU { qd, qn, qm, size } => {
5137                // Encode as VADD (placeholder encoding — real implementation
5138                // would use VCMP + VPSEL pair)
5139                let sz = mve_size_bits(size);
5140                let base: u32 = 0xEF000840 | (sz << 20);
5141                Ok(vfp_to_thumb_bytes(encode_mve_3reg(base, qd, qn, qm)))
5142            }
5143
5144            // f32x4 MVE arithmetic
5145            ArmOp::MveAddF32 { qd, qn, qm } => {
5146                // VADD.F32 Qd, Qn, Qm (MVE): 0xEF000D40
5147                Ok(vfp_to_thumb_bytes(encode_mve_3reg(0xEF000D40, qd, qn, qm)))
5148            }
5149            ArmOp::MveSubF32 { qd, qn, qm } => {
5150                // VSUB.F32 Qd, Qn, Qm (MVE): 0xEF200D40
5151                Ok(vfp_to_thumb_bytes(encode_mve_3reg(0xEF200D40, qd, qn, qm)))
5152            }
5153            ArmOp::MveMulF32 { qd, qn, qm } => {
5154                // VMUL.F32 Qd, Qn, Qm (MVE): 0xFF000D50
5155                Ok(vfp_to_thumb_bytes(encode_mve_3reg(0xFF000D50, qd, qn, qm)))
5156            }
5157            ArmOp::MveNegF32 { qd, qm } => {
5158                let qd_enc = qreg_to_num(qd);
5159                let qm_enc = qreg_to_num(qm);
5160                // VNEG.F32 Qd, Qm: FFB907C0
5161                let instr: u32 = 0xFFB907C0 | ((qd_enc * 2) << 12) | (qm_enc * 2);
5162                Ok(vfp_to_thumb_bytes(instr))
5163            }
5164            ArmOp::MveAbsF32 { qd, qm } => {
5165                let qd_enc = qreg_to_num(qd);
5166                let qm_enc = qreg_to_num(qm);
5167                // VABS.F32 Qd, Qm: FFB90740
5168                let instr: u32 = 0xFFB90740 | ((qd_enc * 2) << 12) | (qm_enc * 2);
5169                Ok(vfp_to_thumb_bytes(instr))
5170            }
5171            ArmOp::MveCmpEqF32 { qd, qn, qm }
5172            | ArmOp::MveCmpNeF32 { qd, qn, qm }
5173            | ArmOp::MveCmpLtF32 { qd, qn, qm }
5174            | ArmOp::MveCmpLeF32 { qd, qn, qm }
5175            | ArmOp::MveCmpGtF32 { qd, qn, qm }
5176            | ArmOp::MveCmpGeF32 { qd, qn, qm } => {
5177                // Placeholder: encode as VADD.F32 (real impl needs VCMP.F32 + VPSEL)
5178                Ok(vfp_to_thumb_bytes(encode_mve_3reg(0xEF000D40, qd, qn, qm)))
5179            }
5180            ArmOp::MveDupF32 { qd, rn } => {
5181                let qd_enc = qreg_to_num(qd);
5182                let rn_bits = reg_to_bits(rn);
5183                // VDUP.32 Qd, Rn (same encoding as integer VDUP.32)
5184                let instr: u32 = 0xEEA00B10 | ((qd_enc * 2) << 16) | (rn_bits << 12);
5185                Ok(vfp_to_thumb_bytes(instr))
5186            }
5187            ArmOp::MveExtractLaneF32 { rd, qn, lane } => {
5188                let qn_enc = qreg_to_num(qn);
5189                let rd_bits = reg_to_bits(rd);
5190                // VMOV Rd, Sn where Sn = Q*4 + lane
5191                let s_num = qn_enc * 4 + (*lane as u32);
5192                let (vn, n) = encode_sreg(s_num);
5193                let instr: u32 = 0xEE100A10 | (vn << 16) | (rd_bits << 12) | (n << 7);
5194                Ok(vfp_to_thumb_bytes(instr))
5195            }
5196            ArmOp::MveReplaceLaneF32 { qd, rn, lane } => {
5197                let qd_enc = qreg_to_num(qd);
5198                let rn_bits = reg_to_bits(rn);
5199                // VMOV Sn, Rn where Sn = Q*4 + lane
5200                let s_num = qd_enc * 4 + (*lane as u32);
5201                let (vn, n) = encode_sreg(s_num);
5202                let instr: u32 = 0xEE000A10 | (vn << 16) | (rn_bits << 12) | (n << 7);
5203                Ok(vfp_to_thumb_bytes(instr))
5204            }
5205            ArmOp::MveDivF32 { qd, qn, qm } => {
5206                // Lane-wise: extract 4 S-regs, VDIV, insert back
5207                self.encode_thumb_mve_lane_wise_f32_binop(qd, qn, qm, 0xEE800A00)
5208            }
5209            ArmOp::MveSqrtF32 { qd, qm } => {
5210                // Lane-wise: extract 4 S-regs, VSQRT, insert back
5211                self.encode_thumb_mve_lane_wise_f32_sqrt(qd, qm)
5212            }
5213
5214            // Catch-all for any remaining ops
5215            _ => {
5216                let instr: u16 = 0xBF00; // NOP
5217                Ok(instr.to_le_bytes().to_vec())
5218            }
5219        }
5220    }
5221
5222    // === Thumb-2 VFP multi-instruction helpers ===
5223
5224    /// Encode F32 comparison as Thumb-2: VCMP.F32 + VMRS + MOVS rd,#0 + IT + MOV rd,#1
5225    fn encode_thumb_f32_compare(
5226        &self,
5227        rd: &Reg,
5228        sn: &VfpReg,
5229        sm: &VfpReg,
5230        cond_code: u32,
5231    ) -> Result<Vec<u8>> {
5232        let mut bytes = Vec::new();
5233        let rd_bits = reg_to_bits(rd);
5234
5235        // VCMP.F32 Sn, Sm
5236        let sn_num = vfp_sreg_to_num(sn)?;
5237        let sm_num = vfp_sreg_to_num(sm)?;
5238        let (vd, d) = encode_sreg(sn_num);
5239        let (vm, m) = encode_sreg(sm_num);
5240        let vcmp = 0xEEB40A40 | (d << 22) | (vd << 12) | (m << 5) | vm;
5241        bytes.extend_from_slice(&vfp_to_thumb_bytes(vcmp));
5242
5243        // VMRS APSR_nzcv, FPSCR: 0xEEF1FA10
5244        bytes.extend_from_slice(&vfp_to_thumb_bytes(0xEEF1FA10));
5245
5246        // MOVS Rd, #0 (16-bit): 0010 0 Rd(3) 0000 0000
5247        if rd_bits < 8 {
5248            let movs_zero: u16 = 0x2000 | ((rd_bits as u16) << 8);
5249            bytes.extend_from_slice(&movs_zero.to_le_bytes());
5250        } else {
5251            // MOV.W Rd, #0 (32-bit Thumb-2)
5252            let hw1: u16 = 0xF04F;
5253            let hw2: u16 = (rd_bits as u16) << 8;
5254            bytes.extend_from_slice(&hw1.to_le_bytes());
5255            bytes.extend_from_slice(&hw2.to_le_bytes());
5256        }
5257
5258        // IT<cond> — If-Then for conditional MOV
5259        // IT encoding: 1011 1111 cond(4) mask(4)
5260        // mask = 0x8 for single "then" (IT)
5261        let it: u16 = 0xBF00 | ((cond_code as u16) << 4) | 0x8;
5262        bytes.extend_from_slice(&it.to_le_bytes());
5263
5264        // MOV Rd, #1 (16-bit, conditional due to IT): 0010 0 Rd(3) 0000 0001
5265        if rd_bits < 8 {
5266            let mov_one: u16 = 0x2001 | ((rd_bits as u16) << 8);
5267            bytes.extend_from_slice(&mov_one.to_le_bytes());
5268        } else {
5269            // MOV.W Rd, #1 (32-bit)
5270            let hw1: u16 = 0xF04F;
5271            let hw2: u16 = ((rd_bits as u16) << 8) | 0x01;
5272            bytes.extend_from_slice(&hw1.to_le_bytes());
5273            bytes.extend_from_slice(&hw2.to_le_bytes());
5274        }
5275
5276        Ok(bytes)
5277    }
5278
5279    /// Encode F32 constant load as Thumb-2: MOVW + MOVT + VMOV
5280    fn encode_thumb_f32_const(&self, sd: &VfpReg, value: f32) -> Result<Vec<u8>> {
5281        let mut bytes = Vec::new();
5282        let bits = value.to_bits();
5283        let rt: u32 = 12; // R12/IP as temp
5284
5285        // MOVW R12, #lo16
5286        // Thumb-2 MOVW: 11110 i 10 0100 imm4 | 0 imm3 Rd imm8
5287        let lo16 = bits & 0xFFFF;
5288        let imm4 = (lo16 >> 12) & 0xF;
5289        let i_bit = (lo16 >> 11) & 1;
5290        let imm3 = (lo16 >> 8) & 0x7;
5291        let imm8 = lo16 & 0xFF;
5292        let hw1: u16 = (0xF240 | (i_bit << 10) | imm4) as u16;
5293        let hw2: u16 = ((imm3 << 12) | (rt << 8) | imm8) as u16;
5294        bytes.extend_from_slice(&hw1.to_le_bytes());
5295        bytes.extend_from_slice(&hw2.to_le_bytes());
5296
5297        // MOVT R12, #hi16
5298        let hi16 = (bits >> 16) & 0xFFFF;
5299        let imm4 = (hi16 >> 12) & 0xF;
5300        let i_bit = (hi16 >> 11) & 1;
5301        let imm3 = (hi16 >> 8) & 0x7;
5302        let imm8 = hi16 & 0xFF;
5303        let hw1: u16 = (0xF2C0 | (i_bit << 10) | imm4) as u16;
5304        let hw2: u16 = ((imm3 << 12) | (rt << 8) | imm8) as u16;
5305        bytes.extend_from_slice(&hw1.to_le_bytes());
5306        bytes.extend_from_slice(&hw2.to_le_bytes());
5307
5308        // VMOV Sd, R12
5309        let vmov = encode_vmov_core_sreg(true, sd, &Reg::R12)?;
5310        bytes.extend_from_slice(&vfp_to_thumb_bytes(vmov));
5311
5312        Ok(bytes)
5313    }
5314
5315    /// Encode VMOV + VCVT.F32.xS32 as Thumb-2
5316    fn encode_thumb_f32_convert_i32(&self, sd: &VfpReg, rm: &Reg, signed: bool) -> Result<Vec<u8>> {
5317        let mut bytes = Vec::new();
5318
5319        // VMOV Sd, Rm
5320        let vmov = encode_vmov_core_sreg(true, sd, rm)?;
5321        bytes.extend_from_slice(&vfp_to_thumb_bytes(vmov));
5322
5323        // VCVT.F32.S32/U32 Sd, Sd
5324        let sd_num = vfp_sreg_to_num(sd)?;
5325        let (vd, d) = encode_sreg(sd_num);
5326        let (vm, m) = encode_sreg(sd_num);
5327        let base = if signed { 0xEEB80A40 } else { 0xEEB80AC0 };
5328        let vcvt = base | (d << 22) | (vd << 12) | (m << 5) | vm;
5329        bytes.extend_from_slice(&vfp_to_thumb_bytes(vcvt));
5330
5331        Ok(bytes)
5332    }
5333
5334    /// Encode F32 rounding pseudo-op as Thumb-2 via VCVT to integer and back
5335    /// Encode F32 rounding as Thumb-2.
5336    /// `mode`: FPSCR RMode — 0b00=nearest, 0b01=+inf(ceil), 0b10=-inf(floor), 0b11=zero(trunc)
5337    ///
5338    /// For trunc: uses VCVTR.S32.F32 (always truncates).
5339    /// For ceil/floor/nearest: sets FPSCR rounding mode, uses VCVT.S32.F32 (non-R variant),
5340    /// then restores FPSCR.
5341    fn encode_thumb_f32_rounding(&self, sd: &VfpReg, sm: &VfpReg, mode: u8) -> Result<Vec<u8>> {
5342        let mut bytes = Vec::new();
5343        let sm_num = vfp_sreg_to_num(sm)?;
5344        let sd_num = vfp_sreg_to_num(sd)?;
5345        let (vd_s, d_s) = encode_sreg(sd_num);
5346        let (vm_s, m_s) = encode_sreg(sm_num);
5347
5348        if mode == 0b11 {
5349            // Trunc (toward zero): VCVTR.S32.F32 — bit[7]=1, always truncates
5350            let vcvt_to_int = 0xEEBD0AC0 | (d_s << 22) | (vd_s << 12) | (m_s << 5) | vm_s;
5351            bytes.extend_from_slice(&vfp_to_thumb_bytes(vcvt_to_int));
5352        } else {
5353            // ceil/floor/nearest: manipulate FPSCR rounding mode
5354            let rt: u32 = 12; // R12/IP as temp
5355
5356            // VMRS R12, FPSCR
5357            let vmrs = 0xEEF10A10 | (rt << 12);
5358            bytes.extend_from_slice(&vfp_to_thumb_bytes(vmrs));
5359
5360            // BIC.W R12, R12, #(3 << 22) — clear RMode bits [23:22]
5361            // Thumb-2 modified immediate for 3<<22 = 0x00C00000:
5362            // BIC.W encoding: 11110 i 0 0001 S Rn | 0 imm3 Rd imm8
5363            // 0x00C00000 = 0x03 shifted left by 22 => Thumb mod-imm: i=0, imm3=0b101, imm8=0x03
5364            let bic_hw1: u16 = 0xF020 | ((rt as u16) & 0xF); // BIC, Rn=R12
5365            let bic_hw2: u16 = (0x05 << 12) | ((rt as u16) << 8) | 0x03;
5366            bytes.extend_from_slice(&bic_hw1.to_le_bytes());
5367            bytes.extend_from_slice(&bic_hw2.to_le_bytes());
5368
5369            // ORR.W R12, R12, #(mode << 22)
5370            if mode != 0 {
5371                let orr_hw1: u16 = 0xF040 | ((rt as u16) & 0xF); // ORR, Rn=R12
5372                let orr_hw2: u16 = (0x05 << 12) | ((rt as u16) << 8) | (mode as u16);
5373                bytes.extend_from_slice(&orr_hw1.to_le_bytes());
5374                bytes.extend_from_slice(&orr_hw2.to_le_bytes());
5375            }
5376
5377            // VMSR FPSCR, R12
5378            let vmsr = 0xEEE10A10 | (rt << 12);
5379            bytes.extend_from_slice(&vfp_to_thumb_bytes(vmsr));
5380
5381            // VCVT.S32.F32 Sd, Sm — non-R variant (bit[7]=0), uses FPSCR rmode
5382            let vcvt_to_int = 0xEEBD0A40 | (d_s << 22) | (vd_s << 12) | (m_s << 5) | vm_s;
5383            bytes.extend_from_slice(&vfp_to_thumb_bytes(vcvt_to_int));
5384
5385            // Restore FPSCR: clear rmode bits back to nearest (default)
5386            bytes.extend_from_slice(&vfp_to_thumb_bytes(vmrs));
5387            bytes.extend_from_slice(&bic_hw1.to_le_bytes());
5388            bytes.extend_from_slice(&bic_hw2.to_le_bytes());
5389            bytes.extend_from_slice(&vfp_to_thumb_bytes(vmsr));
5390        }
5391
5392        // VCVT.F32.S32 Sd, Sd (convert integer result back to float)
5393        let (vd2, d2) = encode_sreg(sd_num);
5394        let vcvt_to_float = 0xEEB80A40 | (d2 << 22) | (vd2 << 12) | (d_s << 5) | vd_s;
5395        bytes.extend_from_slice(&vfp_to_thumb_bytes(vcvt_to_float));
5396
5397        Ok(bytes)
5398    }
5399
5400    /// Encode F32 min/max as Thumb-2: VMOV + VCMP + VMRS + IT + VMOV
5401    fn encode_thumb_f32_minmax(
5402        &self,
5403        sd: &VfpReg,
5404        sn: &VfpReg,
5405        sm: &VfpReg,
5406        is_min: bool,
5407    ) -> Result<Vec<u8>> {
5408        let mut bytes = Vec::new();
5409        let sn_num = vfp_sreg_to_num(sn)?;
5410        let sm_num = vfp_sreg_to_num(sm)?;
5411        let sd_num = vfp_sreg_to_num(sd)?;
5412
5413        // VMOV.F32 Sd, Sn
5414        let (vd, d) = encode_sreg(sd_num);
5415        let (vn, n) = encode_sreg(sn_num);
5416        let vmov_sn = 0xEEB00A40 | (d << 22) | (vd << 12) | (n << 5) | vn;
5417        bytes.extend_from_slice(&vfp_to_thumb_bytes(vmov_sn));
5418
5419        // VCMP.F32 Sn, Sm
5420        let (vm, m) = encode_sreg(sm_num);
5421        let vcmp = 0xEEB40A40 | (n << 22) | (vn << 12) | (m << 5) | vm;
5422        bytes.extend_from_slice(&vfp_to_thumb_bytes(vcmp));
5423
5424        // VMRS APSR_nzcv, FPSCR
5425        bytes.extend_from_slice(&vfp_to_thumb_bytes(0xEEF1FA10));
5426
5427        // IT GT (for min) or IT MI (for max)
5428        let cond: u16 = if is_min { 0xC } else { 0x4 };
5429        let it: u16 = 0xBF00 | (cond << 4) | 0x8;
5430        bytes.extend_from_slice(&it.to_le_bytes());
5431
5432        // VMOV{cond}.F32 Sd, Sm — conditional VMOV in IT block
5433        let vmov_sm = 0xEEB00A40 | (d << 22) | (vd << 12) | (m << 5) | vm;
5434        bytes.extend_from_slice(&vfp_to_thumb_bytes(vmov_sm));
5435
5436        Ok(bytes)
5437    }
5438
5439    /// Encode F32 copysign as Thumb-2
5440    fn encode_thumb_f32_copysign(&self, sd: &VfpReg, sn: &VfpReg, sm: &VfpReg) -> Result<Vec<u8>> {
5441        let mut bytes = Vec::new();
5442
5443        // VMOV R12, Sm (get sign source bits)
5444        bytes.extend_from_slice(&vfp_to_thumb_bytes(encode_vmov_core_sreg(
5445            false,
5446            sm,
5447            &Reg::R12,
5448        )?));
5449
5450        // VMOV R0, Sn (get magnitude source bits)
5451        bytes.extend_from_slice(&vfp_to_thumb_bytes(encode_vmov_core_sreg(
5452            false,
5453            sn,
5454            &Reg::R0,
5455        )?));
5456
5457        // AND.W R12, R12, #0x80000000
5458        // Thumb-2 modified immediate: 0x80000000 = constant 0x80 with rotation
5459        // Using T1 encoding: 11110 i 0 0000 S Rn | 0 imm3 Rd imm8
5460        // 0x80000000: i=0, imm3=0b001, imm8=0x00 (rotation=4, value=0x80)
5461        // Actually encoding #0x80000000 as modified constant:
5462        // bit pattern 1 followed by 31 zeros: enc = 0b0100_00000000 = 0x0100? No.
5463        // ARM modified immediate: abcdefgh rotated. 0x80000000 = 0x80 ROR 2 = enc 0x0102
5464        // Actually: value = abcdefgh ROR (2*rot). 0x80 = 10000000, ROR 2 gives 0x20000000.
5465        // For 0x80000000: 0x02 ROR 2 = 0x80000000. So imm12 = (1<<8) | 0x02 = 0x102
5466        let hw1: u16 = 0xF000 | 12; // AND.W R12, R12, #modified_const (i=0, Rn=R12)
5467        let hw2: u16 = (0x1 << 12) | (12 << 8) | 0x02; // imm3=1, Rd=R12, imm8=0x02
5468        bytes.extend_from_slice(&hw1.to_le_bytes());
5469        bytes.extend_from_slice(&hw2.to_le_bytes());
5470
5471        // BIC.W R0, R0, #0x80000000 (R0 = register 0, fields are zero)
5472        let hw1: u16 = 0xF020; // BIC.W R0, R0, #modified_const (i=0, Rn=R0)
5473        let hw2: u16 = (0x1 << 12) | 0x02; // imm3=1, Rd=R0, imm8=0x02
5474        bytes.extend_from_slice(&hw1.to_le_bytes());
5475        bytes.extend_from_slice(&hw2.to_le_bytes());
5476
5477        // ORR.W R0, R0, R12 (R0 = register 0)
5478        let hw1: u16 = 0xEA40; // ORR.W R0, R0, R12 (Rn=R0)
5479        let hw2: u16 = 12; // Rd=R0, Rm=R12
5480        bytes.extend_from_slice(&hw1.to_le_bytes());
5481        bytes.extend_from_slice(&hw2.to_le_bytes());
5482
5483        // VMOV Sd, R0
5484        bytes.extend_from_slice(&vfp_to_thumb_bytes(encode_vmov_core_sreg(
5485            true,
5486            sd,
5487            &Reg::R0,
5488        )?));
5489
5490        Ok(bytes)
5491    }
5492
5493    /// Encode F64 comparison as Thumb-2: VCMP.F64 + VMRS + MOV #0 + IT + MOV #1
5494    fn encode_thumb_f64_compare(
5495        &self,
5496        rd: &Reg,
5497        dn: &VfpReg,
5498        dm: &VfpReg,
5499        cond_code: u32,
5500    ) -> Result<Vec<u8>> {
5501        let mut bytes = Vec::new();
5502        let rd_bits = reg_to_bits(rd);
5503
5504        // VCMP.F64 Dn, Dm
5505        let dn_num = vfp_dreg_to_num(dn)?;
5506        let dm_num = vfp_dreg_to_num(dm)?;
5507        let (vd, d) = encode_dreg(dn_num);
5508        let (vm, m) = encode_dreg(dm_num);
5509        let vcmp = 0xEEB40B40 | (d << 22) | (vd << 12) | (m << 5) | vm;
5510        bytes.extend_from_slice(&vfp_to_thumb_bytes(vcmp));
5511
5512        // VMRS APSR_nzcv, FPSCR
5513        bytes.extend_from_slice(&vfp_to_thumb_bytes(0xEEF1FA10));
5514
5515        // MOVS Rd, #0
5516        if rd_bits < 8 {
5517            let movs_zero: u16 = 0x2000 | ((rd_bits as u16) << 8);
5518            bytes.extend_from_slice(&movs_zero.to_le_bytes());
5519        } else {
5520            let hw1: u16 = 0xF04F;
5521            let hw2: u16 = (rd_bits as u16) << 8;
5522            bytes.extend_from_slice(&hw1.to_le_bytes());
5523            bytes.extend_from_slice(&hw2.to_le_bytes());
5524        }
5525
5526        // IT<cond>
5527        let it: u16 = 0xBF00 | ((cond_code as u16) << 4) | 0x8;
5528        bytes.extend_from_slice(&it.to_le_bytes());
5529
5530        // MOV Rd, #1
5531        if rd_bits < 8 {
5532            let mov_one: u16 = 0x2001 | ((rd_bits as u16) << 8);
5533            bytes.extend_from_slice(&mov_one.to_le_bytes());
5534        } else {
5535            let hw1: u16 = 0xF04F;
5536            let hw2: u16 = ((rd_bits as u16) << 8) | 0x01;
5537            bytes.extend_from_slice(&hw1.to_le_bytes());
5538            bytes.extend_from_slice(&hw2.to_le_bytes());
5539        }
5540
5541        Ok(bytes)
5542    }
5543
5544    /// Encode F64 constant load as Thumb-2: MOVW+MOVT (lo32 into R0) + MOVW+MOVT (hi32 into R12) + VMOV Dd, R0, R12
5545    fn encode_thumb_f64_const(&self, dd: &VfpReg, value: f64) -> Result<Vec<u8>> {
5546        let mut bytes = Vec::new();
5547        let bits = value.to_bits();
5548        let lo32 = bits as u32;
5549        let hi32 = (bits >> 32) as u32;
5550
5551        // MOVW R0, #lo16(lo32)
5552        let lo16 = lo32 & 0xFFFF;
5553        bytes.extend_from_slice(&self.encode_thumb32_movw_raw(0, lo16)?);
5554
5555        // MOVT R0, #hi16(lo32)
5556        let hi16 = (lo32 >> 16) & 0xFFFF;
5557        bytes.extend_from_slice(&self.encode_thumb32_movt_raw(0, hi16)?);
5558
5559        // MOVW R12, #lo16(hi32)
5560        let lo16 = hi32 & 0xFFFF;
5561        bytes.extend_from_slice(&self.encode_thumb32_movw_raw(12, lo16)?);
5562
5563        // MOVT R12, #hi16(hi32)
5564        let hi16 = (hi32 >> 16) & 0xFFFF;
5565        bytes.extend_from_slice(&self.encode_thumb32_movt_raw(12, hi16)?);
5566
5567        // VMOV Dd, R0, R12
5568        let vmov = encode_vmov_core_dreg(true, dd, &Reg::R0, &Reg::R12)?;
5569        bytes.extend_from_slice(&vfp_to_thumb_bytes(vmov));
5570
5571        Ok(bytes)
5572    }
5573
5574    /// Encode VMOV Sd, Rm + VCVT.F64.S32/U32 Dd, Sd as Thumb-2
5575    fn encode_thumb_f64_convert_i32(&self, dd: &VfpReg, rm: &Reg, signed: bool) -> Result<Vec<u8>> {
5576        let mut bytes = Vec::new();
5577
5578        // VMOV S0, Rm
5579        let vmov = encode_vmov_core_sreg(true, &VfpReg::S0, rm)?;
5580        bytes.extend_from_slice(&vfp_to_thumb_bytes(vmov));
5581
5582        // VCVT.F64.S32 Dd, S0 or VCVT.F64.U32 Dd, S0
5583        let dd_num = vfp_dreg_to_num(dd)?;
5584        let (vd, d) = encode_dreg(dd_num);
5585        let base = if signed { 0xEEB80B40 } else { 0xEEB80BC0 };
5586        let vcvt = base | (d << 22) | (vd << 12);
5587        bytes.extend_from_slice(&vfp_to_thumb_bytes(vcvt));
5588
5589        Ok(bytes)
5590    }
5591
5592    /// Encode VCVT.F64.F32 Dd, Sm as Thumb-2
5593    fn encode_thumb_f64_promote_f32(&self, dd: &VfpReg, sm: &VfpReg) -> Result<Vec<u8>> {
5594        let dd_num = vfp_dreg_to_num(dd)?;
5595        let sm_num = vfp_sreg_to_num(sm)?;
5596        let (vd, d) = encode_dreg(dd_num);
5597        let (vm, m) = encode_sreg(sm_num);
5598
5599        let vcvt = 0xEEB70AC0 | (d << 22) | (vd << 12) | (m << 5) | vm;
5600        Ok(vfp_to_thumb_bytes(vcvt))
5601    }
5602
5603    /// Encode VCVT.S32/U32.F64 S0, Dm + VMOV Rd, S0 as Thumb-2
5604    fn encode_thumb_i32_trunc_f64(&self, rd: &Reg, dm: &VfpReg, signed: bool) -> Result<Vec<u8>> {
5605        let mut bytes = Vec::new();
5606        let dm_num = vfp_dreg_to_num(dm)?;
5607        let (vm, m) = encode_dreg(dm_num);
5608
5609        // VCVT.S32.F64 S0, Dm or VCVT.U32.F64 S0, Dm
5610        let base = if signed { 0xEEBD0BC0 } else { 0xEEBC0BC0 };
5611        let vcvt = base | (m << 5) | vm;
5612        bytes.extend_from_slice(&vfp_to_thumb_bytes(vcvt));
5613
5614        // VMOV Rd, S0
5615        let vmov = encode_vmov_core_sreg(false, &VfpReg::S0, rd)?;
5616        bytes.extend_from_slice(&vfp_to_thumb_bytes(vmov));
5617
5618        Ok(bytes)
5619    }
5620
5621    /// Encode F64 rounding pseudo-op as Thumb-2 via VCVT to integer and back
5622    /// Encode F64 rounding as Thumb-2.
5623    /// `mode`: FPSCR RMode — 0b00=nearest, 0b01=+inf(ceil), 0b10=-inf(floor), 0b11=zero(trunc)
5624    fn encode_thumb_f64_rounding(&self, dd: &VfpReg, dm: &VfpReg, mode: u8) -> Result<Vec<u8>> {
5625        let mut bytes = Vec::new();
5626        let dm_num = vfp_dreg_to_num(dm)?;
5627        let dd_num = vfp_dreg_to_num(dd)?;
5628        let (vm, m) = encode_dreg(dm_num);
5629        let (vd, d) = encode_dreg(dd_num);
5630
5631        if mode == 0b11 {
5632            // Trunc: VCVTR.S32.F64 — bit[7]=1, always truncates
5633            let vcvt_to_int = 0xEEBD0BC0 | (m << 5) | vm;
5634            bytes.extend_from_slice(&vfp_to_thumb_bytes(vcvt_to_int));
5635        } else {
5636            let rt: u32 = 12;
5637
5638            // VMRS R12, FPSCR
5639            let vmrs = 0xEEF10A10 | (rt << 12);
5640            bytes.extend_from_slice(&vfp_to_thumb_bytes(vmrs));
5641
5642            // BIC.W R12, R12, #(3 << 22)
5643            let bic_hw1: u16 = 0xF020 | ((rt as u16) & 0xF);
5644            let bic_hw2: u16 = (0x05 << 12) | ((rt as u16) << 8) | 0x03;
5645            bytes.extend_from_slice(&bic_hw1.to_le_bytes());
5646            bytes.extend_from_slice(&bic_hw2.to_le_bytes());
5647
5648            // ORR.W R12, R12, #(mode << 22)
5649            if mode != 0 {
5650                let orr_hw1: u16 = 0xF040 | ((rt as u16) & 0xF);
5651                let orr_hw2: u16 = (0x05 << 12) | ((rt as u16) << 8) | (mode as u16);
5652                bytes.extend_from_slice(&orr_hw1.to_le_bytes());
5653                bytes.extend_from_slice(&orr_hw2.to_le_bytes());
5654            }
5655
5656            // VMSR FPSCR, R12
5657            let vmsr = 0xEEE10A10 | (rt << 12);
5658            bytes.extend_from_slice(&vfp_to_thumb_bytes(vmsr));
5659
5660            // VCVT.S32.F64 S0, Dm — non-R variant (bit[7]=0)
5661            let vcvt_to_int = 0xEEBD0B40 | (m << 5) | vm;
5662            bytes.extend_from_slice(&vfp_to_thumb_bytes(vcvt_to_int));
5663
5664            // Restore FPSCR
5665            bytes.extend_from_slice(&vfp_to_thumb_bytes(vmrs));
5666            bytes.extend_from_slice(&bic_hw1.to_le_bytes());
5667            bytes.extend_from_slice(&bic_hw2.to_le_bytes());
5668            bytes.extend_from_slice(&vfp_to_thumb_bytes(vmsr));
5669        }
5670
5671        // VCVT.F64.S32 Dd, S0
5672        let vcvt_to_float = 0xEEB80B40 | (d << 22) | (vd << 12);
5673        bytes.extend_from_slice(&vfp_to_thumb_bytes(vcvt_to_float));
5674
5675        Ok(bytes)
5676    }
5677
5678    /// Encode F64 min/max as Thumb-2
5679    fn encode_thumb_f64_minmax(
5680        &self,
5681        dd: &VfpReg,
5682        dn: &VfpReg,
5683        dm: &VfpReg,
5684        is_min: bool,
5685    ) -> Result<Vec<u8>> {
5686        let mut bytes = Vec::new();
5687        let dn_num = vfp_dreg_to_num(dn)?;
5688        let dm_num = vfp_dreg_to_num(dm)?;
5689        let dd_num = vfp_dreg_to_num(dd)?;
5690
5691        // VMOV.F64 Dd, Dn
5692        let (vd, d) = encode_dreg(dd_num);
5693        let (vn, n) = encode_dreg(dn_num);
5694        let vmov_dn = 0xEEB00B40 | (d << 22) | (vd << 12) | (n << 5) | vn;
5695        bytes.extend_from_slice(&vfp_to_thumb_bytes(vmov_dn));
5696
5697        // VCMP.F64 Dn, Dm
5698        let (vm, m) = encode_dreg(dm_num);
5699        let vcmp = 0xEEB40B40 | (n << 22) | (vn << 12) | (m << 5) | vm;
5700        bytes.extend_from_slice(&vfp_to_thumb_bytes(vcmp));
5701
5702        // VMRS APSR_nzcv, FPSCR
5703        bytes.extend_from_slice(&vfp_to_thumb_bytes(0xEEF1FA10));
5704
5705        // IT GT (for min) or IT MI (for max)
5706        let cond: u16 = if is_min { 0xC } else { 0x4 };
5707        let it: u16 = 0xBF00 | (cond << 4) | 0x8;
5708        bytes.extend_from_slice(&it.to_le_bytes());
5709
5710        // VMOV{cond}.F64 Dd, Dm
5711        let vmov_dm = 0xEEB00B40 | (d << 22) | (vd << 12) | (m << 5) | vm;
5712        bytes.extend_from_slice(&vfp_to_thumb_bytes(vmov_dm));
5713
5714        Ok(bytes)
5715    }
5716
5717    /// Encode F64 copysign as Thumb-2
5718    fn encode_thumb_f64_copysign(&self, dd: &VfpReg, dn: &VfpReg, dm: &VfpReg) -> Result<Vec<u8>> {
5719        let mut bytes = Vec::new();
5720
5721        // VMOV R0, R12, Dm (get sign source)
5722        bytes.extend_from_slice(&vfp_to_thumb_bytes(encode_vmov_core_dreg(
5723            false,
5724            dm,
5725            &Reg::R0,
5726            &Reg::R12,
5727        )?));
5728
5729        // VMOV R1, R2, Dn (get magnitude source)
5730        bytes.extend_from_slice(&vfp_to_thumb_bytes(encode_vmov_core_dreg(
5731            false,
5732            dn,
5733            &Reg::R1,
5734            &Reg::R2,
5735        )?));
5736
5737        // AND.W R12, R12, #0x80000000 (i=0, Rn=R12)
5738        let hw1: u16 = 0xF000 | 12;
5739        let hw2: u16 = (0x1 << 12) | (12 << 8) | 0x02;
5740        bytes.extend_from_slice(&hw1.to_le_bytes());
5741        bytes.extend_from_slice(&hw2.to_le_bytes());
5742
5743        // BIC.W R2, R2, #0x80000000 (i=0, Rn=R2)
5744        let hw1: u16 = 0xF020 | 2;
5745        let hw2: u16 = (0x1 << 12) | (2 << 8) | 0x02;
5746        bytes.extend_from_slice(&hw1.to_le_bytes());
5747        bytes.extend_from_slice(&hw2.to_le_bytes());
5748
5749        // ORR.W R2, R2, R12
5750        let hw1: u16 = 0xEA40 | 2;
5751        let hw2: u16 = (2 << 8) | 12;
5752        bytes.extend_from_slice(&hw1.to_le_bytes());
5753        bytes.extend_from_slice(&hw2.to_le_bytes());
5754
5755        // VMOV Dd, R1, R2
5756        bytes.extend_from_slice(&vfp_to_thumb_bytes(encode_vmov_core_dreg(
5757            true,
5758            dd,
5759            &Reg::R1,
5760            &Reg::R2,
5761        )?));
5762
5763        Ok(bytes)
5764    }
5765
5766    /// Encode VCVT.S32/U32.F32 + VMOV as Thumb-2
5767    fn encode_thumb_i32_trunc_f32(&self, rd: &Reg, sm: &VfpReg, signed: bool) -> Result<Vec<u8>> {
5768        let mut bytes = Vec::new();
5769
5770        let sm_num = vfp_sreg_to_num(sm)?;
5771        let (vd, d) = encode_sreg(sm_num);
5772        let (vm, m) = encode_sreg(sm_num);
5773        let base = if signed { 0xEEBD0AC0 } else { 0xEEBC0AC0 };
5774        let vcvt = base | (d << 22) | (vd << 12) | (m << 5) | vm;
5775        bytes.extend_from_slice(&vfp_to_thumb_bytes(vcvt));
5776
5777        // VMOV Rd, Sm
5778        let vmov = encode_vmov_core_sreg(false, sm, rd)?;
5779        bytes.extend_from_slice(&vfp_to_thumb_bytes(vmov));
5780
5781        Ok(bytes)
5782    }
5783
5784    // === Thumb-2 32-bit encoding helpers ===
5785
5786    /// Encode Thumb-2 32-bit ADD with immediate
5787    fn encode_thumb32_add(&self, rd: &Reg, rn: &Reg, imm: u32) -> Result<Vec<u8>> {
5788        let rd_bits = reg_to_bits(rd);
5789        let rn_bits = reg_to_bits(rn);
5790
5791        // ADD.W Rd, Rn, #imm12
5792        // First halfword: 1111 0 i 0 1000 S Rn
5793        // Second halfword: 0 imm3 Rd imm8
5794        let i_bit = (imm >> 11) & 1;
5795        let imm3 = (imm >> 8) & 0x7;
5796        let imm8 = imm & 0xFF;
5797
5798        let hw1: u16 = (0xF100 | (i_bit << 10) | rn_bits) as u16;
5799        let hw2: u16 = ((imm3 << 12) | (rd_bits << 8) | imm8) as u16;
5800
5801        let mut bytes = hw1.to_le_bytes().to_vec();
5802        bytes.extend_from_slice(&hw2.to_le_bytes());
5803        Ok(bytes)
5804    }
5805
5806    /// Encode Thumb-2 32-bit SUB with immediate
5807    fn encode_thumb32_sub(&self, rd: &Reg, rn: &Reg, imm: u32) -> Result<Vec<u8>> {
5808        let rd_bits = reg_to_bits(rd);
5809        let rn_bits = reg_to_bits(rn);
5810
5811        let i_bit = (imm >> 11) & 1;
5812        let imm3 = (imm >> 8) & 0x7;
5813        let imm8 = imm & 0xFF;
5814
5815        let hw1: u16 = (0xF1A0 | (i_bit << 10) | rn_bits) as u16;
5816        let hw2: u16 = ((imm3 << 12) | (rd_bits << 8) | imm8) as u16;
5817
5818        let mut bytes = hw1.to_le_bytes().to_vec();
5819        bytes.extend_from_slice(&hw2.to_le_bytes());
5820        Ok(bytes)
5821    }
5822
5823    /// Encode Thumb-2 32-bit ADDS with immediate (sets flags)
5824    fn encode_thumb32_adds(&self, rd: &Reg, rn: &Reg, imm: u32) -> Result<Vec<u8>> {
5825        let rd_bits = reg_to_bits(rd);
5826        let rn_bits = reg_to_bits(rn);
5827
5828        let i_bit = (imm >> 11) & 1;
5829        let imm3 = (imm >> 8) & 0x7;
5830        let imm8 = imm & 0xFF;
5831
5832        // ADDS.W Rd, Rn, #imm (with S=1)
5833        // First halfword: 1111 0 i 0 1000 1 Rn = F110 | i<<10 | Rn
5834        let hw1: u16 = (0xF110 | (i_bit << 10) | rn_bits) as u16;
5835        let hw2: u16 = ((imm3 << 12) | (rd_bits << 8) | imm8) as u16;
5836
5837        let mut bytes = hw1.to_le_bytes().to_vec();
5838        bytes.extend_from_slice(&hw2.to_le_bytes());
5839        Ok(bytes)
5840    }
5841
5842    /// Encode Thumb-2 32-bit SUBS with immediate (sets flags)
5843    fn encode_thumb32_subs(&self, rd: &Reg, rn: &Reg, imm: u32) -> Result<Vec<u8>> {
5844        let rd_bits = reg_to_bits(rd);
5845        let rn_bits = reg_to_bits(rn);
5846
5847        let i_bit = (imm >> 11) & 1;
5848        let imm3 = (imm >> 8) & 0x7;
5849        let imm8 = imm & 0xFF;
5850
5851        // SUBS.W Rd, Rn, #imm (with S=1)
5852        // First halfword: 1111 0 i 0 1101 1 Rn = F1B0 | i<<10 | Rn
5853        let hw1: u16 = (0xF1B0 | (i_bit << 10) | rn_bits) as u16;
5854        let hw2: u16 = ((imm3 << 12) | (rd_bits << 8) | imm8) as u16;
5855
5856        let mut bytes = hw1.to_le_bytes().to_vec();
5857        bytes.extend_from_slice(&hw2.to_le_bytes());
5858        Ok(bytes)
5859    }
5860
5861    /// Encode Thumb-2 32-bit MOVW (16-bit immediate)
5862    ///
5863    /// # Contract (Verus-style)
5864    /// ```text
5865    /// requires rd <= R14
5866    /// ensures result.len() == 4
5867    /// ensures (imm & 0xFFFF) can be reconstructed from the encoding
5868    /// ```
5869    fn encode_thumb32_movw(&self, rd: &Reg, imm: u32) -> Result<Vec<u8>> {
5870        let rd_bits = reg_to_bits(rd);
5871        encoding_contracts::verify_reg_bits(rd_bits);
5872        let imm16 = imm & 0xFFFF;
5873
5874        // MOVW Rd, #imm16
5875        // 1111 0 i 10 0 1 0 0 imm4 | 0 imm3 Rd imm8
5876        let imm4 = (imm16 >> 12) & 0xF;
5877        let i_bit = (imm16 >> 11) & 1;
5878        let imm3 = (imm16 >> 8) & 0x7;
5879        let imm8 = imm16 & 0xFF;
5880
5881        let hw1: u16 = (0xF240 | (i_bit << 10) | imm4) as u16;
5882        let hw2: u16 = ((imm3 << 12) | (rd_bits << 8) | imm8) as u16;
5883
5884        let mut bytes = hw1.to_le_bytes().to_vec();
5885        bytes.extend_from_slice(&hw2.to_le_bytes());
5886        encoding_contracts::verify_thumb32(&bytes);
5887        Ok(bytes)
5888    }
5889
5890    /// Encode Thumb-2 32-bit shift with immediate
5891    ///
5892    /// # Contract (Verus-style)
5893    /// ```text
5894    /// requires rd <= R14, rm <= R14
5895    /// ensures result.len() == 4
5896    /// ```
5897    fn encode_thumb32_shift(
5898        &self,
5899        rd: &Reg,
5900        rm: &Reg,
5901        shift: u32,
5902        shift_type: u8,
5903    ) -> Result<Vec<u8>> {
5904        let rd_bits = reg_to_bits(rd);
5905        let rm_bits = reg_to_bits(rm);
5906        encoding_contracts::verify_reg_bits(rd_bits);
5907        encoding_contracts::verify_reg_bits(rm_bits);
5908        let imm5 = shift & 0x1F;
5909        let imm2 = imm5 & 0x3;
5910        let imm3 = (imm5 >> 2) & 0x7;
5911
5912        // MOV.W Rd, Rm, <shift> #imm
5913        // EA4F 0 imm3 Rd imm2 type Rm
5914        let hw1: u16 = 0xEA4F;
5915        let hw2: u16 =
5916            ((imm3 << 12) | (rd_bits << 8) | (imm2 << 6) | ((shift_type as u32) << 4) | rm_bits)
5917                as u16;
5918
5919        let mut bytes = hw1.to_le_bytes().to_vec();
5920        bytes.extend_from_slice(&hw2.to_le_bytes());
5921        Ok(bytes)
5922    }
5923
5924    /// Encode Thumb-2 32-bit shift by register
5925    /// Encoding: 11111010 0xx0 Rn | 1111 Rd 0000 Rm
5926    /// shift_type: 00=LSL, 01=LSR, 10=ASR, 11=ROR
5927    fn encode_thumb32_shift_reg(
5928        &self,
5929        rd: &Reg,
5930        rn: &Reg,
5931        rm: &Reg,
5932        shift_type: u8,
5933    ) -> Result<Vec<u8>> {
5934        let rd_bits = reg_to_bits(rd);
5935        let rn_bits = reg_to_bits(rn);
5936        let rm_bits = reg_to_bits(rm);
5937
5938        // hw1: 1111 1010 0xx0 Rn
5939        let hw1: u16 = (0xFA00 | ((shift_type as u32) << 5) | rn_bits) as u16;
5940        // hw2: 1111 Rd 0000 Rm
5941        let hw2: u16 = (0xF000 | (rd_bits << 8) | rm_bits) as u16;
5942
5943        let mut bytes = hw1.to_le_bytes().to_vec();
5944        bytes.extend_from_slice(&hw2.to_le_bytes());
5945        Ok(bytes)
5946    }
5947
5948    /// Encode Thumb-2 32-bit CMP with immediate
5949    fn encode_thumb32_cmp_imm(&self, rn: &Reg, imm: u32) -> Result<Vec<u8>> {
5950        let rn_bits = reg_to_bits(rn);
5951
5952        let i_bit = (imm >> 11) & 1;
5953        let imm3 = (imm >> 8) & 0x7;
5954        let imm8 = imm & 0xFF;
5955
5956        // CMP.W Rn, #imm
5957        let hw1: u16 = (0xF1B0 | (i_bit << 10) | rn_bits) as u16;
5958        let hw2: u16 = ((imm3 << 12) | 0x0F00 | imm8) as u16;
5959
5960        let mut bytes = hw1.to_le_bytes().to_vec();
5961        bytes.extend_from_slice(&hw2.to_le_bytes());
5962        Ok(bytes)
5963    }
5964
5965    /// Encode Thumb-2 32-bit LDR
5966    fn encode_thumb32_ldr(&self, rd: &Reg, base: &Reg, offset: u32) -> Result<Vec<u8>> {
5967        let rd_bits = reg_to_bits(rd);
5968        let base_bits = reg_to_bits(base);
5969
5970        // LDR.W Rd, [Rn, #imm12]
5971        let hw1: u16 = (0xF8D0 | base_bits) as u16;
5972        let hw2: u16 = ((rd_bits << 12) | (offset & 0xFFF)) as u16;
5973
5974        let mut bytes = hw1.to_le_bytes().to_vec();
5975        bytes.extend_from_slice(&hw2.to_le_bytes());
5976        Ok(bytes)
5977    }
5978
5979    /// Encode Thumb-2 32-bit STR
5980    fn encode_thumb32_str(&self, rd: &Reg, base: &Reg, offset: u32) -> Result<Vec<u8>> {
5981        let rd_bits = reg_to_bits(rd);
5982        let base_bits = reg_to_bits(base);
5983
5984        // STR.W Rd, [Rn, #imm12]
5985        let hw1: u16 = (0xF8C0 | base_bits) as u16;
5986        let hw2: u16 = ((rd_bits << 12) | (offset & 0xFFF)) as u16;
5987
5988        let mut bytes = hw1.to_le_bytes().to_vec();
5989        bytes.extend_from_slice(&hw2.to_le_bytes());
5990        Ok(bytes)
5991    }
5992
5993    /// Encode Thumb-2 32-bit LDR with register offset: LDR.W Rd, [Rn, Rm]
5994    fn encode_thumb32_ldr_reg(&self, rd: &Reg, base: &Reg, offset_reg: &Reg) -> Result<Vec<u8>> {
5995        let rd_bits = reg_to_bits(rd);
5996        let base_bits = reg_to_bits(base);
5997        let rm_bits = reg_to_bits(offset_reg);
5998
5999        // LDR.W Rd, [Rn, Rm, LSL #0]
6000        // Encoding: 1111 1000 0101 Rn | Rt 0000 00 imm2 Rm
6001        // imm2 = 00 for no shift (LSL #0)
6002        let hw1: u16 = (0xF850 | base_bits) as u16;
6003        let hw2: u16 = ((rd_bits << 12) | rm_bits) as u16;
6004
6005        let mut bytes = hw1.to_le_bytes().to_vec();
6006        bytes.extend_from_slice(&hw2.to_le_bytes());
6007        Ok(bytes)
6008    }
6009
6010    /// Encode Thumb-2 32-bit STR with register offset: STR.W Rd, [Rn, Rm]
6011    fn encode_thumb32_str_reg(&self, rd: &Reg, base: &Reg, offset_reg: &Reg) -> Result<Vec<u8>> {
6012        let rd_bits = reg_to_bits(rd);
6013        let base_bits = reg_to_bits(base);
6014        let rm_bits = reg_to_bits(offset_reg);
6015
6016        // STR.W Rd, [Rn, Rm, LSL #0]
6017        // Encoding: 1111 1000 0100 Rn | Rt 0000 00 imm2 Rm
6018        // imm2 = 00 for no shift (LSL #0)
6019        let hw1: u16 = (0xF840 | base_bits) as u16;
6020        let hw2: u16 = ((rd_bits << 12) | rm_bits) as u16;
6021
6022        let mut bytes = hw1.to_le_bytes().to_vec();
6023        bytes.extend_from_slice(&hw2.to_le_bytes());
6024        Ok(bytes)
6025    }
6026
6027    // === Sub-word load/store Thumb-2 encoding helpers ===
6028
6029    /// Encode Thumb-2 32-bit LDRB with immediate: LDRB.W Rd, [Rn, #imm12]
6030    fn encode_thumb32_ldrb_imm(&self, rd: &Reg, base: &Reg, offset: u32) -> Result<Vec<u8>> {
6031        let rd_bits = reg_to_bits(rd);
6032        let base_bits = reg_to_bits(base);
6033        // LDRB.W Rd, [Rn, #imm12]: 1111 1000 1001 Rn | Rt imm12
6034        let hw1: u16 = (0xF890 | base_bits) as u16;
6035        let hw2: u16 = ((rd_bits << 12) | (offset & 0xFFF)) as u16;
6036        let mut bytes = hw1.to_le_bytes().to_vec();
6037        bytes.extend_from_slice(&hw2.to_le_bytes());
6038        Ok(bytes)
6039    }
6040
6041    /// Encode Thumb-2 32-bit LDRB with register: LDRB.W Rd, [Rn, Rm]
6042    fn encode_thumb32_ldrb_reg(&self, rd: &Reg, base: &Reg, offset_reg: &Reg) -> Result<Vec<u8>> {
6043        let rd_bits = reg_to_bits(rd);
6044        let base_bits = reg_to_bits(base);
6045        let rm_bits = reg_to_bits(offset_reg);
6046        // LDRB.W Rd, [Rn, Rm, LSL #0]: 1111 1000 0001 Rn | Rt 0000 00 imm2 Rm
6047        let hw1: u16 = (0xF810 | base_bits) as u16;
6048        let hw2: u16 = ((rd_bits << 12) | rm_bits) as u16;
6049        let mut bytes = hw1.to_le_bytes().to_vec();
6050        bytes.extend_from_slice(&hw2.to_le_bytes());
6051        Ok(bytes)
6052    }
6053
6054    /// Encode Thumb-2 32-bit LDRSB with immediate: LDRSB.W Rd, [Rn, #imm12]
6055    fn encode_thumb32_ldrsb_imm(&self, rd: &Reg, base: &Reg, offset: u32) -> Result<Vec<u8>> {
6056        let rd_bits = reg_to_bits(rd);
6057        let base_bits = reg_to_bits(base);
6058        // LDRSB.W Rd, [Rn, #imm12]: 1111 1001 1001 Rn | Rt imm12
6059        let hw1: u16 = (0xF990 | base_bits) as u16;
6060        let hw2: u16 = ((rd_bits << 12) | (offset & 0xFFF)) as u16;
6061        let mut bytes = hw1.to_le_bytes().to_vec();
6062        bytes.extend_from_slice(&hw2.to_le_bytes());
6063        Ok(bytes)
6064    }
6065
6066    /// Encode Thumb-2 32-bit LDRSB with register: LDRSB.W Rd, [Rn, Rm]
6067    fn encode_thumb32_ldrsb_reg(&self, rd: &Reg, base: &Reg, offset_reg: &Reg) -> Result<Vec<u8>> {
6068        let rd_bits = reg_to_bits(rd);
6069        let base_bits = reg_to_bits(base);
6070        let rm_bits = reg_to_bits(offset_reg);
6071        // LDRSB.W Rd, [Rn, Rm, LSL #0]: 1111 1001 0001 Rn | Rt 0000 00 imm2 Rm
6072        let hw1: u16 = (0xF910 | base_bits) as u16;
6073        let hw2: u16 = ((rd_bits << 12) | rm_bits) as u16;
6074        let mut bytes = hw1.to_le_bytes().to_vec();
6075        bytes.extend_from_slice(&hw2.to_le_bytes());
6076        Ok(bytes)
6077    }
6078
6079    /// Encode Thumb-2 32-bit LDRH with immediate: LDRH.W Rd, [Rn, #imm12]
6080    fn encode_thumb32_ldrh_imm(&self, rd: &Reg, base: &Reg, offset: u32) -> Result<Vec<u8>> {
6081        let rd_bits = reg_to_bits(rd);
6082        let base_bits = reg_to_bits(base);
6083        // LDRH.W Rd, [Rn, #imm12]: 1111 1000 1011 Rn | Rt imm12
6084        let hw1: u16 = (0xF8B0 | base_bits) as u16;
6085        let hw2: u16 = ((rd_bits << 12) | (offset & 0xFFF)) as u16;
6086        let mut bytes = hw1.to_le_bytes().to_vec();
6087        bytes.extend_from_slice(&hw2.to_le_bytes());
6088        Ok(bytes)
6089    }
6090
6091    /// Encode Thumb-2 32-bit LDRH with register: LDRH.W Rd, [Rn, Rm]
6092    fn encode_thumb32_ldrh_reg(&self, rd: &Reg, base: &Reg, offset_reg: &Reg) -> Result<Vec<u8>> {
6093        let rd_bits = reg_to_bits(rd);
6094        let base_bits = reg_to_bits(base);
6095        let rm_bits = reg_to_bits(offset_reg);
6096        // LDRH.W Rd, [Rn, Rm, LSL #0]: 1111 1000 0011 Rn | Rt 0000 00 imm2 Rm
6097        let hw1: u16 = (0xF830 | base_bits) as u16;
6098        let hw2: u16 = ((rd_bits << 12) | rm_bits) as u16;
6099        let mut bytes = hw1.to_le_bytes().to_vec();
6100        bytes.extend_from_slice(&hw2.to_le_bytes());
6101        Ok(bytes)
6102    }
6103
6104    /// Encode Thumb-2 32-bit LDRSH with immediate: LDRSH.W Rd, [Rn, #imm12]
6105    fn encode_thumb32_ldrsh_imm(&self, rd: &Reg, base: &Reg, offset: u32) -> Result<Vec<u8>> {
6106        let rd_bits = reg_to_bits(rd);
6107        let base_bits = reg_to_bits(base);
6108        // LDRSH.W Rd, [Rn, #imm12]: 1111 1001 1011 Rn | Rt imm12
6109        let hw1: u16 = (0xF9B0 | base_bits) as u16;
6110        let hw2: u16 = ((rd_bits << 12) | (offset & 0xFFF)) as u16;
6111        let mut bytes = hw1.to_le_bytes().to_vec();
6112        bytes.extend_from_slice(&hw2.to_le_bytes());
6113        Ok(bytes)
6114    }
6115
6116    /// Encode Thumb-2 32-bit LDRSH with register: LDRSH.W Rd, [Rn, Rm]
6117    fn encode_thumb32_ldrsh_reg(&self, rd: &Reg, base: &Reg, offset_reg: &Reg) -> Result<Vec<u8>> {
6118        let rd_bits = reg_to_bits(rd);
6119        let base_bits = reg_to_bits(base);
6120        let rm_bits = reg_to_bits(offset_reg);
6121        // LDRSH.W Rd, [Rn, Rm, LSL #0]: 1111 1001 0011 Rn | Rt 0000 00 imm2 Rm
6122        let hw1: u16 = (0xF930 | base_bits) as u16;
6123        let hw2: u16 = ((rd_bits << 12) | rm_bits) as u16;
6124        let mut bytes = hw1.to_le_bytes().to_vec();
6125        bytes.extend_from_slice(&hw2.to_le_bytes());
6126        Ok(bytes)
6127    }
6128
6129    /// Encode Thumb-2 32-bit STRB with immediate: STRB.W Rd, [Rn, #imm12]
6130    fn encode_thumb32_strb_imm(&self, rd: &Reg, base: &Reg, offset: u32) -> Result<Vec<u8>> {
6131        let rd_bits = reg_to_bits(rd);
6132        let base_bits = reg_to_bits(base);
6133        // STRB.W Rd, [Rn, #imm12]: 1111 1000 1000 Rn | Rt imm12
6134        let hw1: u16 = (0xF880 | base_bits) as u16;
6135        let hw2: u16 = ((rd_bits << 12) | (offset & 0xFFF)) as u16;
6136        let mut bytes = hw1.to_le_bytes().to_vec();
6137        bytes.extend_from_slice(&hw2.to_le_bytes());
6138        Ok(bytes)
6139    }
6140
6141    /// Encode Thumb-2 32-bit STRB with register: STRB.W Rd, [Rn, Rm]
6142    fn encode_thumb32_strb_reg(&self, rd: &Reg, base: &Reg, offset_reg: &Reg) -> Result<Vec<u8>> {
6143        let rd_bits = reg_to_bits(rd);
6144        let base_bits = reg_to_bits(base);
6145        let rm_bits = reg_to_bits(offset_reg);
6146        // STRB.W Rd, [Rn, Rm, LSL #0]: 1111 1000 0000 Rn | Rt 0000 00 imm2 Rm
6147        let hw1: u16 = (0xF800 | base_bits) as u16;
6148        let hw2: u16 = ((rd_bits << 12) | rm_bits) as u16;
6149        let mut bytes = hw1.to_le_bytes().to_vec();
6150        bytes.extend_from_slice(&hw2.to_le_bytes());
6151        Ok(bytes)
6152    }
6153
6154    /// Encode Thumb-2 32-bit STRH with immediate: STRH.W Rd, [Rn, #imm12]
6155    fn encode_thumb32_strh_imm(&self, rd: &Reg, base: &Reg, offset: u32) -> Result<Vec<u8>> {
6156        let rd_bits = reg_to_bits(rd);
6157        let base_bits = reg_to_bits(base);
6158        // STRH.W Rd, [Rn, #imm12]: 1111 1000 1010 Rn | Rt imm12
6159        let hw1: u16 = (0xF8A0 | base_bits) as u16;
6160        let hw2: u16 = ((rd_bits << 12) | (offset & 0xFFF)) as u16;
6161        let mut bytes = hw1.to_le_bytes().to_vec();
6162        bytes.extend_from_slice(&hw2.to_le_bytes());
6163        Ok(bytes)
6164    }
6165
6166    /// Encode Thumb-2 32-bit STRH with register: STRH.W Rd, [Rn, Rm]
6167    fn encode_thumb32_strh_reg(&self, rd: &Reg, base: &Reg, offset_reg: &Reg) -> Result<Vec<u8>> {
6168        let rd_bits = reg_to_bits(rd);
6169        let base_bits = reg_to_bits(base);
6170        let rm_bits = reg_to_bits(offset_reg);
6171        // STRH.W Rd, [Rn, Rm, LSL #0]: 1111 1000 0010 Rn | Rt 0000 00 imm2 Rm
6172        let hw1: u16 = (0xF820 | base_bits) as u16;
6173        let hw2: u16 = ((rd_bits << 12) | rm_bits) as u16;
6174        let mut bytes = hw1.to_le_bytes().to_vec();
6175        bytes.extend_from_slice(&hw2.to_le_bytes());
6176        Ok(bytes)
6177    }
6178
6179    /// Encode Thumb-2 32-bit ADD with immediate: ADD.W Rd, Rn, #imm
6180    fn encode_thumb32_add_imm(&self, rd: &Reg, rn: &Reg, imm: u32) -> Result<Vec<u8>> {
6181        let rd_bits = reg_to_bits(rd);
6182        let rn_bits = reg_to_bits(rn);
6183
6184        // For small immediates, use ADD.W Rd, Rn, #imm12
6185        // Encoding: 1111 0 i 0 1 0 0 0 S Rn | 0 imm3 Rd imm8
6186        // S = 0 (don't update flags)
6187        // The 12-bit immediate is encoded as: i:imm3:imm8
6188        // For simplicity, we only support imm <= 0xFFF (direct encoding)
6189        if imm <= 0xFFF {
6190            let i_bit = (imm >> 11) & 1;
6191            let imm3 = (imm >> 8) & 0x7;
6192            let imm8 = imm & 0xFF;
6193
6194            let hw1: u16 = (0xF100 | (i_bit << 10) | rn_bits) as u16;
6195            let hw2: u16 = ((imm3 << 12) | (rd_bits << 8) | imm8) as u16;
6196
6197            let mut bytes = hw1.to_le_bytes().to_vec();
6198            bytes.extend_from_slice(&hw2.to_le_bytes());
6199            Ok(bytes)
6200        } else {
6201            // For larger immediates, would need MOVW/MOVT + ADD
6202            // For now, return error
6203            Err(synth_core::Error::synthesis(
6204                "ADD immediate too large for single instruction",
6205            ))
6206        }
6207    }
6208
6209    // === Raw encoding helpers for POPCNT (take register numbers directly) ===
6210
6211    /// Encode Thumb-2 32-bit MOVW (16-bit immediate) - raw version
6212    ///
6213    /// # Contract (Verus-style)
6214    /// ```text
6215    /// requires rd <= 14, imm16 <= 0xFFFF
6216    /// ensures result.len() == 4
6217    /// ```
6218    fn encode_thumb32_movw_raw(&self, rd: u32, imm16: u32) -> Result<Vec<u8>> {
6219        encoding_contracts::verify_reg_bits(rd);
6220        encoding_contracts::verify_imm16(imm16);
6221        // MOVW Rd, #imm16
6222        // 1111 0 i 10 0 1 0 0 imm4 | 0 imm3 Rd imm8
6223        let imm16 = imm16 & 0xFFFF;
6224        let imm4 = (imm16 >> 12) & 0xF;
6225        let i_bit = (imm16 >> 11) & 1;
6226        let imm3 = (imm16 >> 8) & 0x7;
6227        let imm8 = imm16 & 0xFF;
6228
6229        let hw1: u16 = (0xF240 | (i_bit << 10) | imm4) as u16;
6230        let hw2: u16 = ((imm3 << 12) | (rd << 8) | imm8) as u16;
6231
6232        let mut bytes = hw1.to_le_bytes().to_vec();
6233        bytes.extend_from_slice(&hw2.to_le_bytes());
6234        encoding_contracts::verify_thumb32(&bytes);
6235        Ok(bytes)
6236    }
6237
6238    /// Encode Thumb-2 32-bit MOVT (move top 16 bits) - raw version
6239    ///
6240    /// # Contract (Verus-style)
6241    /// ```text
6242    /// requires rd <= 14, imm16 <= 0xFFFF
6243    /// ensures result.len() == 4
6244    /// ```
6245    fn encode_thumb32_movt_raw(&self, rd: u32, imm16: u32) -> Result<Vec<u8>> {
6246        encoding_contracts::verify_reg_bits(rd);
6247        encoding_contracts::verify_imm16(imm16);
6248        // MOVT Rd, #imm16
6249        // 1111 0 i 10 1 1 0 0 imm4 | 0 imm3 Rd imm8
6250        let imm16 = imm16 & 0xFFFF;
6251        let imm4 = (imm16 >> 12) & 0xF;
6252        let i_bit = (imm16 >> 11) & 1;
6253        let imm3 = (imm16 >> 8) & 0x7;
6254        let imm8 = imm16 & 0xFF;
6255
6256        let hw1: u16 = (0xF2C0 | (i_bit << 10) | imm4) as u16;
6257        let hw2: u16 = ((imm3 << 12) | (rd << 8) | imm8) as u16;
6258
6259        let mut bytes = hw1.to_le_bytes().to_vec();
6260        bytes.extend_from_slice(&hw2.to_le_bytes());
6261        encoding_contracts::verify_thumb32(&bytes);
6262        Ok(bytes)
6263    }
6264
6265    /// Encode Thumb-2 32-bit LSR (logical shift right) with immediate - raw version
6266    fn encode_thumb32_lsr_raw(&self, rd: u32, rm: u32, shift: u32) -> Result<Vec<u8>> {
6267        // MOV.W Rd, Rm, LSR #imm
6268        // EA4F 0 imm3 Rd imm2 01 Rm
6269        let imm5 = shift & 0x1F;
6270        let imm2 = imm5 & 0x3;
6271        let imm3 = (imm5 >> 2) & 0x7;
6272
6273        let hw1: u16 = 0xEA4F;
6274        let hw2: u16 = ((imm3 << 12) | (rd << 8) | (imm2 << 6) | (0b01 << 4) | rm) as u16;
6275
6276        let mut bytes = hw1.to_le_bytes().to_vec();
6277        bytes.extend_from_slice(&hw2.to_le_bytes());
6278        Ok(bytes)
6279    }
6280
6281    /// Encode Thumb-2 32-bit AND (register) - raw version
6282    fn encode_thumb32_and_reg_raw(&self, rd: u32, rn: u32, rm: u32) -> Result<Vec<u8>> {
6283        // AND.W Rd, Rn, Rm
6284        // EA00 Rn | 0 Rd 00 00 Rm
6285        let hw1: u16 = (0xEA00 | rn) as u16;
6286        let hw2: u16 = ((rd << 8) | rm) as u16;
6287
6288        let mut bytes = hw1.to_le_bytes().to_vec();
6289        bytes.extend_from_slice(&hw2.to_le_bytes());
6290        Ok(bytes)
6291    }
6292
6293    /// Encode Thumb-2 32-bit AND with immediate - raw version
6294    fn encode_thumb32_and_imm_raw(&self, rd: u32, rn: u32, imm: u32) -> Result<Vec<u8>> {
6295        // AND.W Rd, Rn, #<modified_immediate>
6296        // For small immediates (0-255), the encoding is simpler
6297        // F0 00 Rn | 0 imm3 Rd imm8
6298        let i_bit = (imm >> 11) & 1;
6299        let imm3 = (imm >> 8) & 0x7;
6300        let imm8 = imm & 0xFF;
6301
6302        let hw1: u16 = (0xF000 | (i_bit << 10) | rn) as u16;
6303        let hw2: u16 = ((imm3 << 12) | (rd << 8) | imm8) as u16;
6304
6305        let mut bytes = hw1.to_le_bytes().to_vec();
6306        bytes.extend_from_slice(&hw2.to_le_bytes());
6307        Ok(bytes)
6308    }
6309
6310    /// Encode Thumb-2 32-bit SUB (register) - raw version
6311    fn encode_thumb32_sub_reg_raw(&self, rd: u32, rn: u32, rm: u32) -> Result<Vec<u8>> {
6312        // SUB.W Rd, Rn, Rm
6313        // EBA0 Rn | 0 Rd 00 00 Rm
6314        let hw1: u16 = (0xEBA0 | rn) as u16;
6315        let hw2: u16 = ((rd << 8) | rm) as u16;
6316
6317        let mut bytes = hw1.to_le_bytes().to_vec();
6318        bytes.extend_from_slice(&hw2.to_le_bytes());
6319        Ok(bytes)
6320    }
6321
6322    /// Encode Thumb-2 32-bit ADD (register) - raw version
6323    fn encode_thumb32_add_reg_raw(&self, rd: u32, rn: u32, rm: u32) -> Result<Vec<u8>> {
6324        // ADD.W Rd, Rn, Rm
6325        // EB00 Rn | 0 Rd 00 00 Rm
6326        let hw1: u16 = (0xEB00 | rn) as u16;
6327        let hw2: u16 = ((rd << 8) | rm) as u16;
6328
6329        let mut bytes = hw1.to_le_bytes().to_vec();
6330        bytes.extend_from_slice(&hw2.to_le_bytes());
6331        Ok(bytes)
6332    }
6333
6334    /// Encode a sequence of ARM instructions
6335    pub fn encode_sequence(&self, ops: &[ArmOp]) -> Result<Vec<u8>> {
6336        let mut code = Vec::new();
6337
6338        for op in ops {
6339            let encoded = self.encode(op)?;
6340            code.extend_from_slice(&encoded);
6341        }
6342
6343        Ok(code)
6344    }
6345}
6346
6347/// Convert register to bit encoding (0-15)
6348fn reg_to_bits(reg: &Reg) -> u32 {
6349    match reg {
6350        Reg::R0 => 0,
6351        Reg::R1 => 1,
6352        Reg::R2 => 2,
6353        Reg::R3 => 3,
6354        Reg::R4 => 4,
6355        Reg::R5 => 5,
6356        Reg::R6 => 6,
6357        Reg::R7 => 7,
6358        Reg::R8 => 8,
6359        Reg::R9 => 9,
6360        Reg::R10 => 10,
6361        Reg::R11 => 11,
6362        Reg::R12 => 12,
6363        Reg::SP => 13,
6364        Reg::LR => 14,
6365        Reg::PC => 15,
6366    }
6367}
6368
6369/// Try to encode a 32-bit value as an ARM rotated immediate (imm8 ROR 2*rot4).
6370/// Returns Some((encoded_bits, 1)) if representable, None otherwise.
6371fn try_encode_rotated_imm(val: u32) -> Option<(u32, u32)> {
6372    if val == 0 {
6373        return Some((0, 1));
6374    }
6375    for rot in 0..16u32 {
6376        let shift = rot * 2;
6377        // Rotate left by shift (undo the ROR) to see if result fits in 8 bits
6378        let unrotated = val.rotate_left(shift);
6379        if unrotated <= 0xFF {
6380            // Encoded as: rot4(4 bits) | imm8(8 bits) = rotate_imm << 8 | imm8
6381            return Some(((rot << 8) | unrotated, 1));
6382        }
6383    }
6384    None
6385}
6386
6387/// Encode operand2 field and return (bits, immediate_flag).
6388/// For ARM32 mode, immediates use the rotated-immediate encoding (imm8 ROR 2*rot4).
6389/// Panics if an immediate value cannot be represented. Callers that need large
6390/// immediates should use MOVW/MOVT instead of Operand2::Imm.
6391fn encode_operand2(op2: &Operand2) -> (u32, u32) {
6392    match op2 {
6393        Operand2::Imm(val) => {
6394            let uval = *val as u32;
6395            // Attempt rotated-immediate encoding (ARM32 Operand2)
6396            if let Some(encoded) = try_encode_rotated_imm(uval) {
6397                encoded
6398            } else {
6399                // Fallback: mask to 8 bits (legacy behavior for values that
6400                // cannot be represented). This should not be reached for
6401                // correctly-selected instructions; the instruction selector
6402                // must use MOVW/MOVT for large constants.
6403                let imm = uval & 0xFF;
6404                (imm, 1)
6405            }
6406        }
6407
6408        Operand2::Reg(reg) => {
6409            let reg_bits = reg_to_bits(reg);
6410            (reg_bits, 0) // I=0 for register
6411        }
6412
6413        Operand2::RegShift {
6414            rm,
6415            shift: _,
6416            amount,
6417        } => {
6418            // Simplified encoding with shift
6419            let rm_bits = reg_to_bits(rm);
6420            let shift_bits = (*amount & 0x1F) << 7;
6421            (shift_bits | rm_bits, 0)
6422        }
6423    }
6424}
6425
6426/// Encode memory address to (base_reg, offset)
6427fn encode_mem_addr(addr: &MemAddr) -> (u32, u32) {
6428    let base_bits = reg_to_bits(&addr.base);
6429    let offset_bits = (addr.offset as u32) & 0xFFF; // 12-bit offset
6430    (base_bits, offset_bits)
6431}
6432
6433/// S-register number: S0=0, S1=1, ..., S31=31
6434fn vfp_sreg_to_num(reg: &VfpReg) -> Result<u32> {
6435    match reg {
6436        VfpReg::S0 => Ok(0),
6437        VfpReg::S1 => Ok(1),
6438        VfpReg::S2 => Ok(2),
6439        VfpReg::S3 => Ok(3),
6440        VfpReg::S4 => Ok(4),
6441        VfpReg::S5 => Ok(5),
6442        VfpReg::S6 => Ok(6),
6443        VfpReg::S7 => Ok(7),
6444        VfpReg::S8 => Ok(8),
6445        VfpReg::S9 => Ok(9),
6446        VfpReg::S10 => Ok(10),
6447        VfpReg::S11 => Ok(11),
6448        VfpReg::S12 => Ok(12),
6449        VfpReg::S13 => Ok(13),
6450        VfpReg::S14 => Ok(14),
6451        VfpReg::S15 => Ok(15),
6452        VfpReg::S16 => Ok(16),
6453        VfpReg::S17 => Ok(17),
6454        VfpReg::S18 => Ok(18),
6455        VfpReg::S19 => Ok(19),
6456        VfpReg::S20 => Ok(20),
6457        VfpReg::S21 => Ok(21),
6458        VfpReg::S22 => Ok(22),
6459        VfpReg::S23 => Ok(23),
6460        VfpReg::S24 => Ok(24),
6461        VfpReg::S25 => Ok(25),
6462        VfpReg::S26 => Ok(26),
6463        VfpReg::S27 => Ok(27),
6464        VfpReg::S28 => Ok(28),
6465        VfpReg::S29 => Ok(29),
6466        VfpReg::S30 => Ok(30),
6467        VfpReg::S31 => Ok(31),
6468        // D-registers are not used in F32 single-precision encodings
6469        _ => Err(synth_core::Error::SynthesisError(
6470            "D-register not supported in single-precision VFP encoding".to_string(),
6471        )),
6472    }
6473}
6474
6475/// D-register number: D0=0, D1=1, ..., D15=15
6476fn vfp_dreg_to_num(reg: &VfpReg) -> Result<u32> {
6477    match reg {
6478        VfpReg::D0 => Ok(0),
6479        VfpReg::D1 => Ok(1),
6480        VfpReg::D2 => Ok(2),
6481        VfpReg::D3 => Ok(3),
6482        VfpReg::D4 => Ok(4),
6483        VfpReg::D5 => Ok(5),
6484        VfpReg::D6 => Ok(6),
6485        VfpReg::D7 => Ok(7),
6486        VfpReg::D8 => Ok(8),
6487        VfpReg::D9 => Ok(9),
6488        VfpReg::D10 => Ok(10),
6489        VfpReg::D11 => Ok(11),
6490        VfpReg::D12 => Ok(12),
6491        VfpReg::D13 => Ok(13),
6492        VfpReg::D14 => Ok(14),
6493        VfpReg::D15 => Ok(15),
6494        // S-registers are not used in F64 double-precision encodings
6495        _ => Err(synth_core::Error::SynthesisError(
6496            "S-register not supported in double-precision VFP encoding".to_string(),
6497        )),
6498    }
6499}
6500
6501/// Split S-register into (Vx[3:0], qualifier_bit) for VFP encoding.
6502/// For an S-register number s: Vx = s >> 1, qualifier = s & 1.
6503/// The qualifier bit goes to D (bit 22), N (bit 7), or M (bit 5) depending on role.
6504fn encode_sreg(s: u32) -> (u32, u32) {
6505    (s >> 1, s & 1)
6506}
6507
6508/// Split D-register into (Vx[3:0], qualifier_bit) for VFP double-precision encoding.
6509/// For a D-register number d: Vx = d & 0xF, qualifier = (d >> 4) & 1.
6510/// For D0-D15, qualifier is always 0.
6511fn encode_dreg(d: u32) -> (u32, u32) {
6512    (d & 0xF, (d >> 4) & 1)
6513}
6514
6515/// Encode a VFP 3-register arithmetic instruction (VADD.F32, VSUB.F32, VMUL.F32, VDIV.F32).
6516/// Returns the full 32-bit instruction word.
6517///
6518/// VFP encoding: [cond 1110] [D opc1 Vn] [Vd 101 sz] [N opc2 M 0 Vm]
6519/// For single-precision (sz=0), coprocessor = 0xA (bits[11:8]).
6520fn encode_vfp_3reg(base: u32, sd: &VfpReg, sn: &VfpReg, sm: &VfpReg) -> Result<u32> {
6521    let sd_num = vfp_sreg_to_num(sd)?;
6522    let sn_num = vfp_sreg_to_num(sn)?;
6523    let sm_num = vfp_sreg_to_num(sm)?;
6524    let (vd, d) = encode_sreg(sd_num);
6525    let (vn, n) = encode_sreg(sn_num);
6526    let (vm, m) = encode_sreg(sm_num);
6527
6528    Ok(base | (d << 22) | (vn << 16) | (vd << 12) | (n << 7) | (m << 5) | vm)
6529}
6530
6531/// Encode a VFP 2-register instruction (VNEG.F32, VABS.F32, VSQRT.F32).
6532/// Returns the full 32-bit instruction word.
6533fn encode_vfp_2reg(base: u32, sd: &VfpReg, sm: &VfpReg) -> Result<u32> {
6534    let sd_num = vfp_sreg_to_num(sd)?;
6535    let sm_num = vfp_sreg_to_num(sm)?;
6536    let (vd, d) = encode_sreg(sd_num);
6537    let (vm, m) = encode_sreg(sm_num);
6538
6539    Ok(base | (d << 22) | (vd << 12) | (m << 5) | vm)
6540}
6541
6542/// Encode a VFP load/store (VLDR.F32 / VSTR.F32).
6543/// offset is in bytes and must be word-aligned; encoded as imm8 = offset/4.
6544/// U bit (bit 23) controls add/subtract offset.
6545fn encode_vfp_ldst(base: u32, sd: &VfpReg, addr: &MemAddr) -> Result<u32> {
6546    let sd_num = vfp_sreg_to_num(sd)?;
6547    let (vd, d) = encode_sreg(sd_num);
6548    let rn = reg_to_bits(&addr.base);
6549
6550    let offset = addr.offset;
6551    let u_bit = if offset >= 0 { 1u32 } else { 0u32 };
6552    let abs_offset = offset.unsigned_abs();
6553    let imm8 = (abs_offset / 4) & 0xFF;
6554
6555    Ok(base | (u_bit << 23) | (d << 22) | (rn << 16) | (vd << 12) | imm8)
6556}
6557
6558/// Encode VMOV between core register and S-register.
6559/// VMOV Sn, Rt: 0xEE00_0A10 | (Vn << 16) | (N << 7) | (Rt << 12)
6560/// VMOV Rt, Sn: 0xEE10_0A10 | (Vn << 16) | (N << 7) | (Rt << 12)
6561fn encode_vmov_core_sreg(to_sreg: bool, sreg: &VfpReg, core: &Reg) -> Result<u32> {
6562    let s_num = vfp_sreg_to_num(sreg)?;
6563    let (vn, n) = encode_sreg(s_num);
6564    let rt = reg_to_bits(core);
6565
6566    let base = if to_sreg { 0xEE000A10 } else { 0xEE100A10 };
6567    Ok(base | (vn << 16) | (rt << 12) | (n << 7))
6568}
6569
6570/// Encode a VFP 3-register double-precision instruction (VADD.F64, VSUB.F64, etc.).
6571/// For double-precision (sz=1), coprocessor = 0xB (bits[11:8]).
6572/// The base should have bit 8 = 1 for F64 (0xB suffix instead of 0xA).
6573fn encode_vfp_3reg_f64(base: u32, dd: &VfpReg, dn: &VfpReg, dm: &VfpReg) -> Result<u32> {
6574    let dd_num = vfp_dreg_to_num(dd)?;
6575    let dn_num = vfp_dreg_to_num(dn)?;
6576    let dm_num = vfp_dreg_to_num(dm)?;
6577    let (vd, d) = encode_dreg(dd_num);
6578    let (vn, n) = encode_dreg(dn_num);
6579    let (vm, m) = encode_dreg(dm_num);
6580
6581    Ok(base | (d << 22) | (vn << 16) | (vd << 12) | (n << 7) | (m << 5) | vm)
6582}
6583
6584/// Encode a VFP 2-register double-precision instruction (VNEG.F64, VABS.F64, VSQRT.F64).
6585fn encode_vfp_2reg_f64(base: u32, dd: &VfpReg, dm: &VfpReg) -> Result<u32> {
6586    let dd_num = vfp_dreg_to_num(dd)?;
6587    let dm_num = vfp_dreg_to_num(dm)?;
6588    let (vd, d) = encode_dreg(dd_num);
6589    let (vm, m) = encode_dreg(dm_num);
6590
6591    Ok(base | (d << 22) | (vd << 12) | (m << 5) | vm)
6592}
6593
6594/// Encode a VFP load/store for double-precision (VLDR.64 / VSTR.64).
6595/// offset is in bytes and must be word-aligned; encoded as imm8 = offset/4.
6596fn encode_vfp_ldst_f64(base: u32, dd: &VfpReg, addr: &MemAddr) -> Result<u32> {
6597    let dd_num = vfp_dreg_to_num(dd)?;
6598    let (vd, d) = encode_dreg(dd_num);
6599    let rn = reg_to_bits(&addr.base);
6600
6601    let offset = addr.offset;
6602    let u_bit = if offset >= 0 { 1u32 } else { 0u32 };
6603    let abs_offset = offset.unsigned_abs();
6604    let imm8 = (abs_offset / 4) & 0xFF;
6605
6606    Ok(base | (u_bit << 23) | (d << 22) | (rn << 16) | (vd << 12) | imm8)
6607}
6608
6609/// Encode VMOV between two core registers and a D-register.
6610/// VMOV Dm, Rt, Rt2: 0xEC40_0B10 | (Rt2 << 16) | (Rt << 12) | (M << 5) | Vm
6611/// VMOV Rt, Rt2, Dm: 0xEC50_0B10 | (Rt2 << 16) | (Rt << 12) | (M << 5) | Vm
6612fn encode_vmov_core_dreg(
6613    to_dreg: bool,
6614    dreg: &VfpReg,
6615    core_lo: &Reg,
6616    core_hi: &Reg,
6617) -> Result<u32> {
6618    let d_num = vfp_dreg_to_num(dreg)?;
6619    let (vm, m) = encode_dreg(d_num);
6620    let rt = reg_to_bits(core_lo);
6621    let rt2 = reg_to_bits(core_hi);
6622
6623    let base = if to_dreg { 0xEC400B10 } else { 0xEC500B10 };
6624    Ok(base | (rt2 << 16) | (rt << 12) | (m << 5) | vm)
6625}
6626
6627/// Emit a VFP 32-bit instruction as Thumb-2 bytes (two LE halfwords).
6628fn vfp_to_thumb_bytes(instr: u32) -> Vec<u8> {
6629    let hw1 = ((instr >> 16) & 0xFFFF) as u16;
6630    let hw2 = (instr & 0xFFFF) as u16;
6631    let mut bytes = hw1.to_le_bytes().to_vec();
6632    bytes.extend_from_slice(&hw2.to_le_bytes());
6633    bytes
6634}
6635
6636// ============================================================================
6637// Helium MVE encoding helpers
6638// ============================================================================
6639
6640/// Q-register number: Q0=0, Q1=1, ..., Q7=7
6641fn qreg_to_num(reg: &QReg) -> u32 {
6642    match reg {
6643        QReg::Q0 => 0,
6644        QReg::Q1 => 1,
6645        QReg::Q2 => 2,
6646        QReg::Q3 => 3,
6647        QReg::Q4 => 4,
6648        QReg::Q5 => 5,
6649        QReg::Q6 => 6,
6650        QReg::Q7 => 7,
6651    }
6652}
6653
6654/// MVE element size to encoding bits: S8=0b00, S16=0b01, S32=0b10
6655fn mve_size_bits(size: &MveSize) -> u32 {
6656    match size {
6657        MveSize::S8 => 0b00,
6658        MveSize::S16 => 0b01,
6659        MveSize::S32 => 0b10,
6660    }
6661}
6662
6663/// Encode MVE 3-register instruction.
6664/// Q-registers are encoded as D-register pairs: Q0=D0:D1, Q1=D2:D3, etc.
6665/// In NEON/MVE encoding, the Q-register uses D-register number = Qn * 2.
6666fn encode_mve_3reg(base: u32, qd: &QReg, qn: &QReg, qm: &QReg) -> u32 {
6667    let d = qreg_to_num(qd) * 2;
6668    let n = qreg_to_num(qn) * 2;
6669    let m = qreg_to_num(qm) * 2;
6670
6671    // Standard NEON/MVE 3-register encoding:
6672    // D bit (bit 22) = Vd[4], Vd[3:0] = bits [15:12]
6673    // N bit (bit 7)  = Vn[4], Vn[3:0] = bits [19:16]
6674    // M bit (bit 5)  = Vm[4], Vm[3:0] = bits [3:0]
6675    let vd = d & 0xF;
6676    let d_bit = (d >> 4) & 1;
6677    let vn = n & 0xF;
6678    let n_bit = (n >> 4) & 1;
6679    let vm = m & 0xF;
6680    let m_bit = (m >> 4) & 1;
6681
6682    base | (d_bit << 22) | (vn << 16) | (vd << 12) | (n_bit << 7) | (m_bit << 5) | vm
6683}
6684
6685/// Encode MVE 3-register bitwise instruction (VAND, VORR, VEOR, VBIC).
6686fn encode_mve_3reg_bitwise(base: u32, qd: &QReg, qn: &QReg, qm: &QReg) -> u32 {
6687    encode_mve_3reg(base, qd, qn, qm)
6688}
6689
6690/// Encode MVE VLDRW.32 Qd, [Rn, #offset]
6691/// Format: EC9x xxxx - contiguous load, word-sized elements
6692fn encode_mve_vldrw(qd: &QReg, addr: &MemAddr) -> u32 {
6693    let qd_enc = qreg_to_num(qd) * 2;
6694    let rn = reg_to_bits(&addr.base);
6695    let offset = addr.offset;
6696    let u_bit = if offset >= 0 { 1u32 } else { 0u32 };
6697    let abs_offset = offset.unsigned_abs();
6698    let imm7 = (abs_offset / 4) & 0x7F; // 7-bit word-aligned offset
6699
6700    // VLDRW.32 Qd, [Rn, #imm]: ED10 xx80 variant
6701    0xED100E80
6702        | (u_bit << 23)
6703        | ((qd_enc >> 4) << 22)
6704        | (rn << 16)
6705        | ((qd_enc & 0xF) << 12)
6706        | (imm7 & 0x7F)
6707}
6708
6709/// Encode MVE VSTRW.32 Qd, [Rn, #offset]
6710fn encode_mve_vstrw(qd: &QReg, addr: &MemAddr) -> u32 {
6711    let qd_enc = qreg_to_num(qd) * 2;
6712    let rn = reg_to_bits(&addr.base);
6713    let offset = addr.offset;
6714    let u_bit = if offset >= 0 { 1u32 } else { 0u32 };
6715    let abs_offset = offset.unsigned_abs();
6716    let imm7 = (abs_offset / 4) & 0x7F;
6717
6718    0xED000E80
6719        | (u_bit << 23)
6720        | ((qd_enc >> 4) << 22)
6721        | (rn << 16)
6722        | ((qd_enc & 0xF) << 12)
6723        | (imm7 & 0x7F)
6724}
6725
6726impl ArmEncoder {
6727    /// Encode MVE constant load: MOVW+MOVT+VMOV for each 32-bit word, then assemble Q-register
6728    fn encode_thumb_mve_const(&self, qd: &QReg, bytes: &[u8; 16]) -> Result<Vec<u8>> {
6729        let mut result = Vec::new();
6730        let qd_num = qreg_to_num(qd);
6731
6732        // Load each 32-bit word into R12 (temp) then VMOV into S-register
6733        for i in 0..4 {
6734            let word = u32::from_le_bytes([
6735                bytes[i * 4],
6736                bytes[i * 4 + 1],
6737                bytes[i * 4 + 2],
6738                bytes[i * 4 + 3],
6739            ]);
6740            let lo16 = word & 0xFFFF;
6741            let hi16 = (word >> 16) & 0xFFFF;
6742
6743            // MOVW R12, #lo16
6744            result.extend_from_slice(&self.encode_thumb32_movw_raw(12, lo16)?);
6745            // MOVT R12, #hi16
6746            if hi16 != 0 {
6747                result.extend_from_slice(&self.encode_thumb32_movt_raw(12, hi16)?);
6748            }
6749
6750            // VMOV Sn, R12 where Sn = Qd*4 + i
6751            let s_num = qd_num * 4 + i as u32;
6752            let (vn, n) = encode_sreg(s_num);
6753            let vmov: u32 = 0xEE000A10 | (vn << 16) | (12 << 12) | (n << 7);
6754            result.extend_from_slice(&vfp_to_thumb_bytes(vmov));
6755        }
6756
6757        Ok(result)
6758    }
6759
6760    /// Encode lane-wise f32 binary operation (VDIV, etc.) via S-register extraction
6761    fn encode_thumb_mve_lane_wise_f32_binop(
6762        &self,
6763        qd: &QReg,
6764        qn: &QReg,
6765        qm: &QReg,
6766        vfp_base: u32,
6767    ) -> Result<Vec<u8>> {
6768        let mut result = Vec::new();
6769        let qd_num = qreg_to_num(qd);
6770        let qn_num = qreg_to_num(qn);
6771        let qm_num = qreg_to_num(qm);
6772
6773        // For each lane 0..3: use S-registers directly (Q aliasing)
6774        for i in 0..4u32 {
6775            let sd = qd_num * 4 + i;
6776            let sn = qn_num * 4 + i;
6777            let sm = qm_num * 4 + i;
6778
6779            let (vd, d) = encode_sreg(sd);
6780            let (vn, n) = encode_sreg(sn);
6781            let (vm, m) = encode_sreg(sm);
6782
6783            let instr = vfp_base | (d << 22) | (vn << 16) | (vd << 12) | (n << 7) | (m << 5) | vm;
6784            result.extend_from_slice(&vfp_to_thumb_bytes(instr));
6785        }
6786
6787        Ok(result)
6788    }
6789
6790    /// Encode lane-wise f32 VSQRT via S-register extraction
6791    fn encode_thumb_mve_lane_wise_f32_sqrt(&self, qd: &QReg, qm: &QReg) -> Result<Vec<u8>> {
6792        let mut result = Vec::new();
6793        let qd_num = qreg_to_num(qd);
6794        let qm_num = qreg_to_num(qm);
6795
6796        // VSQRT.F32 base: 0xEEB10AC0
6797        for i in 0..4u32 {
6798            let sd = qd_num * 4 + i;
6799            let sm = qm_num * 4 + i;
6800
6801            let (vd, d) = encode_sreg(sd);
6802            let (vm, m) = encode_sreg(sm);
6803
6804            let instr: u32 = 0xEEB10AC0 | (d << 22) | (vd << 12) | (m << 5) | vm;
6805            result.extend_from_slice(&vfp_to_thumb_bytes(instr));
6806        }
6807
6808        Ok(result)
6809    }
6810}
6811
6812#[cfg(test)]
6813mod tests {
6814    use super::*;
6815
6816    #[test]
6817    fn test_encoder_creation() {
6818        let encoder_arm = ArmEncoder::new_arm32();
6819        assert!(!encoder_arm.thumb_mode);
6820
6821        let encoder_thumb = ArmEncoder::new_thumb2();
6822        assert!(encoder_thumb.thumb_mode);
6823    }
6824
6825    #[test]
6826    fn test_encode_nop_arm32() {
6827        let encoder = ArmEncoder::new_arm32();
6828        let code = encoder.encode(&ArmOp::Nop).unwrap();
6829
6830        assert_eq!(code.len(), 4); // ARM32 instructions are 4 bytes
6831        assert_eq!(code, vec![0x00, 0x00, 0xA0, 0xE1]); // MOV R0, R0
6832    }
6833
6834    #[test]
6835    fn test_encode_nop_thumb() {
6836        let encoder = ArmEncoder::new_thumb2();
6837        let code = encoder.encode(&ArmOp::Nop).unwrap();
6838
6839        assert_eq!(code.len(), 2); // Thumb instructions are 2 bytes
6840        assert_eq!(code, vec![0x00, 0xBF]); // NOP
6841    }
6842
6843    #[test]
6844    fn test_encode_mov_immediate_arm32() {
6845        let encoder = ArmEncoder::new_arm32();
6846        let op = ArmOp::Mov {
6847            rd: Reg::R0,
6848            op2: Operand2::Imm(42),
6849        };
6850
6851        let code = encoder.encode(&op).unwrap();
6852        assert_eq!(code.len(), 4);
6853
6854        // Verify it's a MOV instruction (bits should have immediate flag set)
6855        let instr = u32::from_le_bytes([code[0], code[1], code[2], code[3]]);
6856        assert_eq!(instr & 0x0E000000, 0x02000000); // Check I bit is set
6857    }
6858
6859    #[test]
6860    fn test_encode_add_registers_arm32() {
6861        let encoder = ArmEncoder::new_arm32();
6862        let op = ArmOp::Add {
6863            rd: Reg::R0,
6864            rn: Reg::R1,
6865            op2: Operand2::Reg(Reg::R2),
6866        };
6867
6868        let code = encoder.encode(&op).unwrap();
6869        assert_eq!(code.len(), 4);
6870
6871        let instr = u32::from_le_bytes([code[0], code[1], code[2], code[3]]);
6872        // Verify it's an ADD instruction with correct opcode
6873        assert_eq!(instr & 0x0FE00000, 0x00800000);
6874    }
6875
6876    #[test]
6877    fn test_encode_ldr_arm32() {
6878        let encoder = ArmEncoder::new_arm32();
6879        let op = ArmOp::Ldr {
6880            rd: Reg::R0,
6881            addr: MemAddr::imm(Reg::R1, 4),
6882        };
6883
6884        let code = encoder.encode(&op).unwrap();
6885        assert_eq!(code.len(), 4);
6886
6887        let instr = u32::from_le_bytes([code[0], code[1], code[2], code[3]]);
6888        // Verify load bit is set
6889        assert_eq!(instr & 0x00100000, 0x00100000);
6890    }
6891
6892    #[test]
6893    fn test_encode_str_arm32() {
6894        let encoder = ArmEncoder::new_arm32();
6895        let op = ArmOp::Str {
6896            rd: Reg::R0,
6897            addr: MemAddr::imm(Reg::SP, 0),
6898        };
6899
6900        let code = encoder.encode(&op).unwrap();
6901        assert_eq!(code.len(), 4);
6902    }
6903
6904    #[test]
6905    fn test_encode_branch_arm32() {
6906        let encoder = ArmEncoder::new_arm32();
6907        let op = ArmOp::Bl {
6908            label: "main".to_string(),
6909        };
6910
6911        let code = encoder.encode(&op).unwrap();
6912        assert_eq!(code.len(), 4);
6913
6914        let instr = u32::from_le_bytes([code[0], code[1], code[2], code[3]]);
6915        // Verify BL opcode
6916        assert_eq!(instr & 0x0F000000, 0x0B000000);
6917    }
6918
6919    /// Regression test for #167 + #174: the Thumb-2 BL relocatable placeholder
6920    /// must carry a -4 addend so an R_ARM_THM_CALL nets to exactly the symbol S.
6921    /// The correct encoding is what `gas` emits for `bl <extern>`: f7ff fffe
6922    /// (hw1=0xF7FF, hw2=0xFFFE), little-endian bytes FF F7 FE FF.
6923    ///   - 0xD000 (J1=J2=0) → ~+0x600000 garbage addend: `bl c0000c` / truncated
6924    ///     to fit (#167).
6925    ///   - 0xF800 (addend 0) → lands at S+4, one instruction past the callee
6926    ///     entry (#174).
6927    ///   - 0xFFFE (addend -4) → lands at S. Correct.
6928    #[test]
6929    fn test_encode_thumb_bl_placeholder_addend_167_174() {
6930        let encoder = ArmEncoder::new_thumb2();
6931        let op = ArmOp::Bl {
6932            label: "callee".to_string(),
6933        };
6934
6935        let code = encoder.encode(&op).unwrap();
6936        assert_eq!(code.len(), 4, "Thumb-2 BL is 32-bit");
6937
6938        let hw1 = u16::from_le_bytes([code[0], code[1]]);
6939        let hw2 = u16::from_le_bytes([code[2], code[3]]);
6940        assert_eq!(hw1, 0xF7FF, "BL first halfword (matches gas `bl <extern>`)");
6941        assert_eq!(
6942            hw2, 0xFFFE,
6943            "BL second halfword must be 0xFFFE (-4 addend → nets to S), not 0xF800 (→ S+4, #174) or 0xD000 (#167)"
6944        );
6945        assert_ne!(hw2, 0xF800, "0xF800 (addend 0) lands at S+4 (#174)");
6946        assert_ne!(hw2, 0xD000, "0xD000 bakes in a ~+0x600000 addend (#167)");
6947    }
6948
6949    #[test]
6950    fn test_encode_sequence() {
6951        let encoder = ArmEncoder::new_arm32();
6952        let ops = vec![
6953            ArmOp::Mov {
6954                rd: Reg::R0,
6955                op2: Operand2::Imm(42),
6956            },
6957            ArmOp::Mov {
6958                rd: Reg::R1,
6959                op2: Operand2::Imm(10),
6960            },
6961            ArmOp::Add {
6962                rd: Reg::R2,
6963                rn: Reg::R0,
6964                op2: Operand2::Reg(Reg::R1),
6965            },
6966        ];
6967
6968        let code = encoder.encode_sequence(&ops).unwrap();
6969        assert_eq!(code.len(), 12); // 3 instructions * 4 bytes
6970    }
6971
6972    #[test]
6973    fn test_reg_to_bits() {
6974        assert_eq!(reg_to_bits(&Reg::R0), 0);
6975        assert_eq!(reg_to_bits(&Reg::R7), 7);
6976        assert_eq!(reg_to_bits(&Reg::SP), 13);
6977        assert_eq!(reg_to_bits(&Reg::LR), 14);
6978        assert_eq!(reg_to_bits(&Reg::PC), 15);
6979    }
6980
6981    #[test]
6982    fn test_encode_bitwise_operations() {
6983        let encoder = ArmEncoder::new_arm32();
6984
6985        let and_op = ArmOp::And {
6986            rd: Reg::R0,
6987            rn: Reg::R1,
6988            op2: Operand2::Reg(Reg::R2),
6989        };
6990        let and_code = encoder.encode(&and_op).unwrap();
6991        assert_eq!(and_code.len(), 4);
6992
6993        let orr_op = ArmOp::Orr {
6994            rd: Reg::R0,
6995            rn: Reg::R1,
6996            op2: Operand2::Reg(Reg::R2),
6997        };
6998        let orr_code = encoder.encode(&orr_op).unwrap();
6999        assert_eq!(orr_code.len(), 4);
7000
7001        let eor_op = ArmOp::Eor {
7002            rd: Reg::R0,
7003            rn: Reg::R1,
7004            op2: Operand2::Reg(Reg::R2),
7005        };
7006        let eor_code = encoder.encode(&eor_op).unwrap();
7007        assert_eq!(eor_code.len(), 4);
7008    }
7009
7010    // === Thumb-2 32-bit encoding tests ===
7011
7012    #[test]
7013    fn test_encode_sdiv_thumb2() {
7014        let encoder = ArmEncoder::new_thumb2();
7015        let op = ArmOp::Sdiv {
7016            rd: Reg::R0,
7017            rn: Reg::R1,
7018            rm: Reg::R2,
7019        };
7020
7021        let code = encoder.encode(&op).unwrap();
7022        assert_eq!(code.len(), 4); // 32-bit Thumb-2 instruction
7023
7024        // SDIV R0, R1, R2: 0xFB91 0xF0F2
7025        // First halfword: 0xFB90 | Rn(1) = 0xFB91
7026        // Second halfword: 0xF0F0 | Rd(0)<<8 | Rm(2) = 0xF0F2
7027        // Little-endian: [0x91, 0xFB, 0xF2, 0xF0]
7028        assert_eq!(code[0], 0x91);
7029        assert_eq!(code[1], 0xFB);
7030        assert_eq!(code[2], 0xF2);
7031        assert_eq!(code[3], 0xF0);
7032    }
7033
7034    #[test]
7035    fn test_encode_udiv_thumb2() {
7036        let encoder = ArmEncoder::new_thumb2();
7037        let op = ArmOp::Udiv {
7038            rd: Reg::R0,
7039            rn: Reg::R1,
7040            rm: Reg::R2,
7041        };
7042
7043        let code = encoder.encode(&op).unwrap();
7044        assert_eq!(code.len(), 4); // 32-bit Thumb-2 instruction
7045
7046        // UDIV R0, R1, R2: 0xFBB1 0xF0F2
7047        // Little-endian: [0xB1, 0xFB, 0xF2, 0xF0]
7048        assert_eq!(code[0], 0xB1);
7049        assert_eq!(code[1], 0xFB);
7050        assert_eq!(code[2], 0xF2);
7051        assert_eq!(code[3], 0xF0);
7052    }
7053
7054    #[test]
7055    fn test_encode_mul_thumb2() {
7056        let encoder = ArmEncoder::new_thumb2();
7057        let op = ArmOp::Mul {
7058            rd: Reg::R0,
7059            rn: Reg::R1,
7060            rm: Reg::R2,
7061        };
7062
7063        let code = encoder.encode(&op).unwrap();
7064        assert_eq!(code.len(), 4); // 32-bit Thumb-2 instruction
7065    }
7066
7067    #[test]
7068    fn test_encode_and_thumb2() {
7069        let encoder = ArmEncoder::new_thumb2();
7070        let op = ArmOp::And {
7071            rd: Reg::R0,
7072            rn: Reg::R1,
7073            op2: Operand2::Reg(Reg::R2),
7074        };
7075
7076        let code = encoder.encode(&op).unwrap();
7077        assert_eq!(code.len(), 4); // 32-bit Thumb-2 instruction
7078    }
7079
7080    #[test]
7081    fn test_encode_lsl_thumb2_low_regs() {
7082        let encoder = ArmEncoder::new_thumb2();
7083        let op = ArmOp::Lsl {
7084            rd: Reg::R0,
7085            rn: Reg::R1,
7086            shift: 5,
7087        };
7088
7089        let code = encoder.encode(&op).unwrap();
7090        assert_eq!(code.len(), 2); // 16-bit for low registers
7091    }
7092
7093    #[test]
7094    fn test_encode_clz_thumb2() {
7095        let encoder = ArmEncoder::new_thumb2();
7096        let op = ArmOp::Clz {
7097            rd: Reg::R0,
7098            rm: Reg::R1,
7099        };
7100
7101        let code = encoder.encode(&op).unwrap();
7102        assert_eq!(code.len(), 4); // 32-bit Thumb-2 instruction
7103    }
7104
7105    #[test]
7106    fn test_encode_bx_thumb2() {
7107        let encoder = ArmEncoder::new_thumb2();
7108        let op = ArmOp::Bx { rm: Reg::LR };
7109
7110        let code = encoder.encode(&op).unwrap();
7111        assert_eq!(code.len(), 2); // 16-bit instruction
7112
7113        // BX LR: 0x4770
7114        assert_eq!(code, vec![0x70, 0x47]);
7115    }
7116
7117    // ========================================================================
7118    // f32 pseudo-op encoding tests
7119    // ========================================================================
7120
7121    #[test]
7122    fn test_encode_f32_abs_arm32() {
7123        let encoder = ArmEncoder::new_arm32();
7124        let op = ArmOp::F32Abs {
7125            sd: VfpReg::S0,
7126            sm: VfpReg::S2,
7127        };
7128        let code = encoder.encode(&op).unwrap();
7129        assert_eq!(code.len(), 4); // Single VFP instruction
7130    }
7131
7132    #[test]
7133    fn test_encode_f32_neg_arm32() {
7134        let encoder = ArmEncoder::new_arm32();
7135        let op = ArmOp::F32Neg {
7136            sd: VfpReg::S0,
7137            sm: VfpReg::S2,
7138        };
7139        let code = encoder.encode(&op).unwrap();
7140        assert_eq!(code.len(), 4);
7141    }
7142
7143    #[test]
7144    fn test_encode_f32_sqrt_arm32() {
7145        let encoder = ArmEncoder::new_arm32();
7146        let op = ArmOp::F32Sqrt {
7147            sd: VfpReg::S0,
7148            sm: VfpReg::S2,
7149        };
7150        let code = encoder.encode(&op).unwrap();
7151        assert_eq!(code.len(), 4);
7152    }
7153
7154    #[test]
7155    fn test_encode_f32_ceil_arm32() {
7156        let encoder = ArmEncoder::new_arm32();
7157        let op = ArmOp::F32Ceil {
7158            sd: VfpReg::S0,
7159            sm: VfpReg::S2,
7160        };
7161        let code = encoder.encode(&op).unwrap();
7162        // VMRS + BIC + ORR + VMSR + VCVT.S32.F32 + VMRS + BIC + VMSR + VCVT.F32.S32
7163        assert_eq!(code.len(), 36);
7164    }
7165
7166    #[test]
7167    fn test_encode_f32_floor_thumb2() {
7168        let encoder = ArmEncoder::new_thumb2();
7169        let op = ArmOp::F32Floor {
7170            sd: VfpReg::S0,
7171            sm: VfpReg::S2,
7172        };
7173        let code = encoder.encode(&op).unwrap();
7174        // VMRS + BIC.W + ORR.W + VMSR + VCVT + VMRS + BIC.W + VMSR + VCVT.F32.S32
7175        assert_eq!(code.len(), 36);
7176    }
7177
7178    #[test]
7179    fn test_encode_f32_min_arm32() {
7180        let encoder = ArmEncoder::new_arm32();
7181        let op = ArmOp::F32Min {
7182            sd: VfpReg::S0,
7183            sn: VfpReg::S2,
7184            sm: VfpReg::S4,
7185        };
7186        let code = encoder.encode(&op).unwrap();
7187        assert_eq!(code.len(), 16); // VMOV + VCMP + VMRS + conditional VMOV
7188    }
7189
7190    #[test]
7191    fn test_encode_f32_max_thumb2() {
7192        let encoder = ArmEncoder::new_thumb2();
7193        let op = ArmOp::F32Max {
7194            sd: VfpReg::S0,
7195            sn: VfpReg::S2,
7196            sm: VfpReg::S4,
7197        };
7198        let code = encoder.encode(&op).unwrap();
7199        // VMOV(4) + VCMP(4) + VMRS(4) + IT(2) + VMOV(4) = 18
7200        assert_eq!(code.len(), 18);
7201    }
7202
7203    #[test]
7204    fn test_encode_f32_copysign_arm32() {
7205        let encoder = ArmEncoder::new_arm32();
7206        let op = ArmOp::F32Copysign {
7207            sd: VfpReg::S0,
7208            sn: VfpReg::S2,
7209            sm: VfpReg::S4,
7210        };
7211        let code = encoder.encode(&op).unwrap();
7212        // VMOV + VMOV + AND + BIC + ORR + VMOV = 6 * 4 = 24
7213        assert_eq!(code.len(), 24);
7214    }
7215
7216    // ========================================================================
7217    // f64 encoding tests
7218    // ========================================================================
7219
7220    #[test]
7221    fn test_encode_f64_add_arm32() {
7222        let encoder = ArmEncoder::new_arm32();
7223        let op = ArmOp::F64Add {
7224            dd: VfpReg::D0,
7225            dn: VfpReg::D1,
7226            dm: VfpReg::D2,
7227        };
7228        let code = encoder.encode(&op).unwrap();
7229        assert_eq!(code.len(), 4);
7230        // VADD.F64 D0, D1, D2: check coprocessor is cp11 (0xB)
7231        let instr = u32::from_le_bytes([code[0], code[1], code[2], code[3]]);
7232        assert_eq!((instr >> 8) & 0xF, 0xB); // cp11
7233    }
7234
7235    #[test]
7236    fn test_encode_f64_sub_thumb2() {
7237        let encoder = ArmEncoder::new_thumb2();
7238        let op = ArmOp::F64Sub {
7239            dd: VfpReg::D0,
7240            dn: VfpReg::D1,
7241            dm: VfpReg::D2,
7242        };
7243        let code = encoder.encode(&op).unwrap();
7244        assert_eq!(code.len(), 4); // 32-bit VFP as two Thumb halfwords
7245    }
7246
7247    #[test]
7248    fn test_encode_f64_mul_arm32() {
7249        let encoder = ArmEncoder::new_arm32();
7250        let op = ArmOp::F64Mul {
7251            dd: VfpReg::D0,
7252            dn: VfpReg::D1,
7253            dm: VfpReg::D2,
7254        };
7255        let code = encoder.encode(&op).unwrap();
7256        assert_eq!(code.len(), 4);
7257    }
7258
7259    #[test]
7260    fn test_encode_f64_div_arm32() {
7261        let encoder = ArmEncoder::new_arm32();
7262        let op = ArmOp::F64Div {
7263            dd: VfpReg::D0,
7264            dn: VfpReg::D1,
7265            dm: VfpReg::D2,
7266        };
7267        let code = encoder.encode(&op).unwrap();
7268        assert_eq!(code.len(), 4);
7269    }
7270
7271    #[test]
7272    fn test_encode_f64_abs_arm32() {
7273        let encoder = ArmEncoder::new_arm32();
7274        let op = ArmOp::F64Abs {
7275            dd: VfpReg::D0,
7276            dm: VfpReg::D2,
7277        };
7278        let code = encoder.encode(&op).unwrap();
7279        assert_eq!(code.len(), 4);
7280    }
7281
7282    #[test]
7283    fn test_encode_f64_neg_arm32() {
7284        let encoder = ArmEncoder::new_arm32();
7285        let op = ArmOp::F64Neg {
7286            dd: VfpReg::D0,
7287            dm: VfpReg::D2,
7288        };
7289        let code = encoder.encode(&op).unwrap();
7290        assert_eq!(code.len(), 4);
7291    }
7292
7293    #[test]
7294    fn test_encode_f64_sqrt_arm32() {
7295        let encoder = ArmEncoder::new_arm32();
7296        let op = ArmOp::F64Sqrt {
7297            dd: VfpReg::D0,
7298            dm: VfpReg::D2,
7299        };
7300        let code = encoder.encode(&op).unwrap();
7301        assert_eq!(code.len(), 4);
7302    }
7303
7304    #[test]
7305    fn test_encode_f64_load_arm32() {
7306        let encoder = ArmEncoder::new_arm32();
7307        let op = ArmOp::F64Load {
7308            dd: VfpReg::D0,
7309            addr: MemAddr::imm(Reg::R0, 8),
7310        };
7311        let code = encoder.encode(&op).unwrap();
7312        assert_eq!(code.len(), 4);
7313        let instr = u32::from_le_bytes([code[0], code[1], code[2], code[3]]);
7314        assert_eq!((instr >> 8) & 0xF, 0xB); // cp11 for F64
7315        assert_eq!(instr & 0xFF, 2); // offset 8 / 4 = 2
7316    }
7317
7318    #[test]
7319    fn test_encode_f64_store_thumb2() {
7320        let encoder = ArmEncoder::new_thumb2();
7321        let op = ArmOp::F64Store {
7322            dd: VfpReg::D0,
7323            addr: MemAddr::imm(Reg::SP, 0),
7324        };
7325        let code = encoder.encode(&op).unwrap();
7326        assert_eq!(code.len(), 4);
7327    }
7328
7329    #[test]
7330    fn test_encode_f64_compare_arm32() {
7331        let encoder = ArmEncoder::new_arm32();
7332        let op = ArmOp::F64Eq {
7333            rd: Reg::R0,
7334            dn: VfpReg::D0,
7335            dm: VfpReg::D1,
7336        };
7337        let code = encoder.encode(&op).unwrap();
7338        assert_eq!(code.len(), 16); // VCMP + VMRS + MOV #0 + MOVcond #1
7339    }
7340
7341    #[test]
7342    fn test_encode_f64_compare_thumb2() {
7343        let encoder = ArmEncoder::new_thumb2();
7344        let op = ArmOp::F64Lt {
7345            rd: Reg::R0,
7346            dn: VfpReg::D0,
7347            dm: VfpReg::D1,
7348        };
7349        let code = encoder.encode(&op).unwrap();
7350        // VCMP(4) + VMRS(4) + MOVS(2) + IT(2) + MOV(2) = 14
7351        assert_eq!(code.len(), 14);
7352    }
7353
7354    #[test]
7355    fn test_encode_f64_const_arm32() {
7356        let encoder = ArmEncoder::new_arm32();
7357        let op = ArmOp::F64Const {
7358            dd: VfpReg::D0,
7359            value: 3.125,
7360        };
7361        let code = encoder.encode(&op).unwrap();
7362        // MOVW(4) + MOVT(4) + MOVW(4) + MOVT(4) + VMOV(4) = 20
7363        assert_eq!(code.len(), 20);
7364    }
7365
7366    #[test]
7367    fn test_encode_f64_const_thumb2() {
7368        let encoder = ArmEncoder::new_thumb2();
7369        let op = ArmOp::F64Const {
7370            dd: VfpReg::D0,
7371            value: 2.5,
7372        };
7373        let code = encoder.encode(&op).unwrap();
7374        // MOVW(4) + MOVT(4) + MOVW(4) + MOVT(4) + VMOV(4) = 20
7375        assert_eq!(code.len(), 20);
7376    }
7377
7378    #[test]
7379    fn test_encode_f64_convert_i32s_arm32() {
7380        let encoder = ArmEncoder::new_arm32();
7381        let op = ArmOp::F64ConvertI32S {
7382            dd: VfpReg::D0,
7383            rm: Reg::R0,
7384        };
7385        let code = encoder.encode(&op).unwrap();
7386        // VMOV(4) + VCVT(4) = 8
7387        assert_eq!(code.len(), 8);
7388    }
7389
7390    #[test]
7391    fn test_encode_f64_promote_f32_arm32() {
7392        let encoder = ArmEncoder::new_arm32();
7393        let op = ArmOp::F64PromoteF32 {
7394            dd: VfpReg::D0,
7395            sm: VfpReg::S0,
7396        };
7397        let code = encoder.encode(&op).unwrap();
7398        assert_eq!(code.len(), 4); // Single VCVT.F64.F32 instruction
7399    }
7400
7401    #[test]
7402    fn test_encode_f64_promote_f32_thumb2() {
7403        let encoder = ArmEncoder::new_thumb2();
7404        let op = ArmOp::F64PromoteF32 {
7405            dd: VfpReg::D0,
7406            sm: VfpReg::S0,
7407        };
7408        let code = encoder.encode(&op).unwrap();
7409        assert_eq!(code.len(), 4);
7410    }
7411
7412    #[test]
7413    fn test_encode_i32_trunc_f64s_arm32() {
7414        let encoder = ArmEncoder::new_arm32();
7415        let op = ArmOp::I32TruncF64S {
7416            rd: Reg::R0,
7417            dm: VfpReg::D0,
7418        };
7419        let code = encoder.encode(&op).unwrap();
7420        // VCVT(4) + VMOV(4) = 8
7421        assert_eq!(code.len(), 8);
7422    }
7423
7424    #[test]
7425    fn test_encode_f64_reinterpret_i64_arm32() {
7426        let encoder = ArmEncoder::new_arm32();
7427        let op = ArmOp::F64ReinterpretI64 {
7428            dd: VfpReg::D0,
7429            rmlo: Reg::R0,
7430            rmhi: Reg::R1,
7431        };
7432        let code = encoder.encode(&op).unwrap();
7433        assert_eq!(code.len(), 4); // Single VMOV instruction
7434    }
7435
7436    #[test]
7437    fn test_encode_i64_reinterpret_f64_thumb2() {
7438        let encoder = ArmEncoder::new_thumb2();
7439        let op = ArmOp::I64ReinterpretF64 {
7440            rdlo: Reg::R0,
7441            rdhi: Reg::R1,
7442            dm: VfpReg::D0,
7443        };
7444        let code = encoder.encode(&op).unwrap();
7445        assert_eq!(code.len(), 4);
7446    }
7447
7448    #[test]
7449    fn test_encode_f64_trunc_thumb2() {
7450        let encoder = ArmEncoder::new_thumb2();
7451        let op = ArmOp::F64Trunc {
7452            dd: VfpReg::D0,
7453            dm: VfpReg::D1,
7454        };
7455        let code = encoder.encode(&op).unwrap();
7456        // Two VFP instructions via Thumb encoding
7457        assert_eq!(code.len(), 8);
7458    }
7459
7460    #[test]
7461    fn test_encode_f64_min_arm32() {
7462        let encoder = ArmEncoder::new_arm32();
7463        let op = ArmOp::F64Min {
7464            dd: VfpReg::D0,
7465            dn: VfpReg::D1,
7466            dm: VfpReg::D2,
7467        };
7468        let code = encoder.encode(&op).unwrap();
7469        // VMOV + VCMP + VMRS + conditional VMOV = 16
7470        assert_eq!(code.len(), 16);
7471    }
7472
7473    #[test]
7474    fn test_f64_cp11_encoding() {
7475        // Verify that F64 instructions use coprocessor 11 (0xB), not 10 (0xA)
7476        let encoder = ArmEncoder::new_arm32();
7477
7478        // F64Add
7479        let code = encoder
7480            .encode(&ArmOp::F64Add {
7481                dd: VfpReg::D0,
7482                dn: VfpReg::D0,
7483                dm: VfpReg::D0,
7484            })
7485            .unwrap();
7486        let instr = u32::from_le_bytes([code[0], code[1], code[2], code[3]]);
7487        assert_eq!((instr >> 8) & 0xF, 0xB, "F64 should use cp11");
7488
7489        // F32Add for comparison
7490        let code = encoder
7491            .encode(&ArmOp::F32Add {
7492                sd: VfpReg::S0,
7493                sn: VfpReg::S0,
7494                sm: VfpReg::S0,
7495            })
7496            .unwrap();
7497        let instr = u32::from_le_bytes([code[0], code[1], code[2], code[3]]);
7498        assert_eq!((instr >> 8) & 0xF, 0xA, "F32 should use cp10");
7499    }
7500
7501    #[test]
7502    fn test_dreg_encoding_higher_registers() {
7503        let encoder = ArmEncoder::new_arm32();
7504
7505        // Test with D15 (highest register)
7506        let op = ArmOp::F64Add {
7507            dd: VfpReg::D15,
7508            dn: VfpReg::D14,
7509            dm: VfpReg::D13,
7510        };
7511        let code = encoder.encode(&op).unwrap();
7512        assert_eq!(code.len(), 4);
7513
7514        // Verify the register encoding worked (instruction is valid)
7515        let instr = u32::from_le_bytes([code[0], code[1], code[2], code[3]]);
7516        assert_eq!((instr >> 8) & 0xF, 0xB); // cp11
7517    }
7518
7519    // ========================================================================
7520    // Control flow encoding tests
7521    // ========================================================================
7522
7523    #[test]
7524    fn test_encode_label_emits_no_bytes() {
7525        let encoder = ArmEncoder::new_thumb2();
7526        let op = ArmOp::Label {
7527            name: ".Lblock_end_0".to_string(),
7528        };
7529        let code = encoder.encode(&op).unwrap();
7530        assert!(code.is_empty(), "Label should emit zero bytes");
7531
7532        let encoder32 = ArmEncoder::new_arm32();
7533        let code32 = encoder32.encode(&op).unwrap();
7534        assert!(
7535            code32.is_empty(),
7536            "Label should emit zero bytes in ARM32 too"
7537        );
7538    }
7539
7540    #[test]
7541    fn test_encode_bcc_eq_thumb2() {
7542        use synth_synthesis::Condition;
7543        let encoder = ArmEncoder::new_thumb2();
7544        let op = ArmOp::Bcc {
7545            cond: Condition::EQ,
7546            label: "target".to_string(),
7547        };
7548        let code = encoder.encode(&op).unwrap();
7549        assert_eq!(code.len(), 2); // 16-bit conditional branch
7550
7551        // BEQ with offset 0: 0xD000 in little-endian
7552        assert_eq!(code, vec![0x00, 0xD0]);
7553    }
7554
7555    #[test]
7556    fn test_encode_bcc_ne_thumb2() {
7557        use synth_synthesis::Condition;
7558        let encoder = ArmEncoder::new_thumb2();
7559        let op = ArmOp::Bcc {
7560            cond: Condition::NE,
7561            label: "target".to_string(),
7562        };
7563        let code = encoder.encode(&op).unwrap();
7564        assert_eq!(code.len(), 2);
7565
7566        // BNE with offset 0: 0xD100 in little-endian
7567        assert_eq!(code, vec![0x00, 0xD1]);
7568    }
7569
7570    #[test]
7571    fn test_encode_bcc_arm32() {
7572        use synth_synthesis::Condition;
7573        let encoder = ArmEncoder::new_arm32();
7574        let op = ArmOp::Bcc {
7575            cond: Condition::EQ,
7576            label: "target".to_string(),
7577        };
7578        let code = encoder.encode(&op).unwrap();
7579        assert_eq!(code.len(), 4); // 32-bit ARM instruction
7580
7581        let instr = u32::from_le_bytes([code[0], code[1], code[2], code[3]]);
7582        // BEQ: cond=0x0, opcode=0xA, offset=0
7583        assert_eq!(instr & 0xF0000000, 0x00000000); // EQ condition
7584        assert_eq!(instr & 0x0F000000, 0x0A000000); // Branch opcode
7585    }
7586
7587    #[test]
7588    fn test_encode_udf_thumb2() {
7589        let encoder = ArmEncoder::new_thumb2();
7590        let op = ArmOp::Udf { imm: 0 };
7591        let code = encoder.encode(&op).unwrap();
7592        assert_eq!(code.len(), 2); // 16-bit
7593
7594        // UDF #0: 0xDE00 in little-endian
7595        assert_eq!(code, vec![0x00, 0xDE]);
7596    }
7597
7598    #[test]
7599    fn test_encode_nop_thumb2() {
7600        let encoder = ArmEncoder::new_thumb2();
7601        let op = ArmOp::Nop;
7602        let code = encoder.encode(&op).unwrap();
7603        assert_eq!(code.len(), 2); // 16-bit
7604
7605        // NOP: 0xBF00 in little-endian
7606        assert_eq!(code, vec![0x00, 0xBF]);
7607    }
7608
7609    // =========================================================================
7610    // i64 Thumb-2 encoding tests
7611    // =========================================================================
7612
7613    #[test]
7614    fn test_encode_i64_add_thumb2() {
7615        let encoder = ArmEncoder::new_thumb2();
7616        let op = ArmOp::I64Add {
7617            rdlo: Reg::R0,
7618            rdhi: Reg::R1,
7619            rnlo: Reg::R0,
7620            rnhi: Reg::R1,
7621            rmlo: Reg::R2,
7622            rmhi: Reg::R3,
7623        };
7624        let code = encoder.encode(&op).unwrap();
7625        // Should emit ADDS (2 bytes) + ADC.W (4 bytes) = 6 bytes
7626        assert_eq!(code.len(), 6, "I64Add should be 6 bytes (ADDS + ADC.W)");
7627    }
7628
7629    #[test]
7630    fn test_encode_i64_sub_thumb2() {
7631        let encoder = ArmEncoder::new_thumb2();
7632        let op = ArmOp::I64Sub {
7633            rdlo: Reg::R0,
7634            rdhi: Reg::R1,
7635            rnlo: Reg::R0,
7636            rnhi: Reg::R1,
7637            rmlo: Reg::R2,
7638            rmhi: Reg::R3,
7639        };
7640        let code = encoder.encode(&op).unwrap();
7641        // Should emit SUBS (2 bytes) + SBC.W (4 bytes) = 6 bytes
7642        assert_eq!(code.len(), 6, "I64Sub should be 6 bytes (SUBS + SBC.W)");
7643    }
7644
7645    #[test]
7646    fn test_encode_i64_and_thumb2() {
7647        let encoder = ArmEncoder::new_thumb2();
7648        let op = ArmOp::I64And {
7649            rdlo: Reg::R0,
7650            rdhi: Reg::R1,
7651            rnlo: Reg::R0,
7652            rnhi: Reg::R1,
7653            rmlo: Reg::R2,
7654            rmhi: Reg::R3,
7655        };
7656        let code = encoder.encode(&op).unwrap();
7657        // AND.W (4 bytes) + AND.W (4 bytes) = 8 bytes
7658        assert!(code.len() >= 4, "I64And should emit at least 4 bytes");
7659    }
7660
7661    #[test]
7662    fn test_encode_i64_or_thumb2() {
7663        let encoder = ArmEncoder::new_thumb2();
7664        let op = ArmOp::I64Or {
7665            rdlo: Reg::R0,
7666            rdhi: Reg::R1,
7667            rnlo: Reg::R0,
7668            rnhi: Reg::R1,
7669            rmlo: Reg::R2,
7670            rmhi: Reg::R3,
7671        };
7672        let code = encoder.encode(&op).unwrap();
7673        assert!(code.len() >= 4, "I64Or should emit at least 4 bytes");
7674    }
7675
7676    #[test]
7677    fn test_encode_i64_xor_thumb2() {
7678        let encoder = ArmEncoder::new_thumb2();
7679        let op = ArmOp::I64Xor {
7680            rdlo: Reg::R0,
7681            rdhi: Reg::R1,
7682            rnlo: Reg::R0,
7683            rnhi: Reg::R1,
7684            rmlo: Reg::R2,
7685            rmhi: Reg::R3,
7686        };
7687        let code = encoder.encode(&op).unwrap();
7688        assert!(code.len() >= 4, "I64Xor should emit at least 4 bytes");
7689    }
7690
7691    #[test]
7692    fn test_encode_i64_const_small_thumb2() {
7693        let encoder = ArmEncoder::new_thumb2();
7694        // Small constant: only needs MOVW for each half
7695        let op = ArmOp::I64Const {
7696            rdlo: Reg::R0,
7697            rdhi: Reg::R1,
7698            value: 42,
7699        };
7700        let code = encoder.encode(&op).unwrap();
7701        // MOVW R0, #42 (4 bytes) + MOVW R1, #0 (4 bytes) = 8 bytes minimum
7702        assert!(code.len() >= 8, "I64Const should emit at least 8 bytes");
7703    }
7704
7705    #[test]
7706    fn test_encode_i64_const_large_thumb2() {
7707        let encoder = ArmEncoder::new_thumb2();
7708        // Large constant: needs MOVW+MOVT for each half
7709        let op = ArmOp::I64Const {
7710            rdlo: Reg::R0,
7711            rdhi: Reg::R1,
7712            value: 0x1234_5678_9ABC_DEF0_u64 as i64,
7713        };
7714        let code = encoder.encode(&op).unwrap();
7715        // MOVW + MOVT for lo (8 bytes) + MOVW + MOVT for hi (8 bytes) = 16 bytes
7716        assert_eq!(
7717            code.len(),
7718            16,
7719            "I64Const with large value should be 16 bytes"
7720        );
7721    }
7722
7723    #[test]
7724    fn test_encode_i64_extend_i32_s_thumb2() {
7725        let encoder = ArmEncoder::new_thumb2();
7726        let op = ArmOp::I64ExtendI32S {
7727            rdlo: Reg::R0,
7728            rdhi: Reg::R1,
7729            rn: Reg::R0,
7730        };
7731        let code = encoder.encode(&op).unwrap();
7732        // When rdlo == rn, only ASR (4 bytes) is emitted
7733        assert_eq!(
7734            code.len(),
7735            4,
7736            "I64ExtendI32S (same reg) should be 4 bytes (ASR only)"
7737        );
7738    }
7739
7740    #[test]
7741    fn test_encode_i64_extend_i32_s_diff_reg_thumb2() {
7742        let encoder = ArmEncoder::new_thumb2();
7743        let op = ArmOp::I64ExtendI32S {
7744            rdlo: Reg::R0,
7745            rdhi: Reg::R1,
7746            rn: Reg::R2,
7747        };
7748        let code = encoder.encode(&op).unwrap();
7749        // MOV rdlo, rn (2 bytes for low regs) + ASR rdhi, rdlo, #31 (4 bytes) = 6 bytes
7750        assert!(
7751            code.len() >= 6,
7752            "I64ExtendI32S (diff reg) should be at least 6 bytes"
7753        );
7754    }
7755
7756    #[test]
7757    fn test_encode_i64_extend_i32_u_thumb2() {
7758        let encoder = ArmEncoder::new_thumb2();
7759        let op = ArmOp::I64ExtendI32U {
7760            rdlo: Reg::R0,
7761            rdhi: Reg::R1,
7762            rn: Reg::R0,
7763        };
7764        let code = encoder.encode(&op).unwrap();
7765        // When rdlo == rn, only MOV rdhi, #0 (2 bytes) is emitted
7766        assert_eq!(
7767            code.len(),
7768            2,
7769            "I64ExtendI32U (same reg) should be 2 bytes (MOV #0 only)"
7770        );
7771    }
7772
7773    #[test]
7774    fn test_encode_i32_wrap_i64_nop_thumb2() {
7775        let encoder = ArmEncoder::new_thumb2();
7776        // When rd == rnlo, should be a NOP
7777        let op = ArmOp::I32WrapI64 {
7778            rd: Reg::R0,
7779            rnlo: Reg::R0,
7780        };
7781        let code = encoder.encode(&op).unwrap();
7782        assert_eq!(code.len(), 2, "I32WrapI64 same reg should be NOP (2 bytes)");
7783        assert_eq!(code, vec![0x00, 0xBF]); // NOP
7784    }
7785
7786    #[test]
7787    fn test_encode_i32_wrap_i64_diff_reg_thumb2() {
7788        let encoder = ArmEncoder::new_thumb2();
7789        let op = ArmOp::I32WrapI64 {
7790            rd: Reg::R2,
7791            rnlo: Reg::R0,
7792        };
7793        let code = encoder.encode(&op).unwrap();
7794        // MOV R2, R0 (2 or 4 bytes)
7795        assert!(
7796            code.len() >= 2,
7797            "I32WrapI64 diff reg should emit at least 2 bytes"
7798        );
7799    }
7800
7801    #[test]
7802    fn test_encode_i64_eqz_thumb2() {
7803        let encoder = ArmEncoder::new_thumb2();
7804        let op = ArmOp::I64Eqz {
7805            rd: Reg::R0,
7806            rnlo: Reg::R0,
7807            rnhi: Reg::R1,
7808        };
7809        let code = encoder.encode(&op).unwrap();
7810        // Delegates to I64SetCondZ which is already encoded
7811        assert!(
7812            code.len() >= 6,
7813            "I64Eqz should emit at least 6 bytes for ORR+ITE+MOV+MOV"
7814        );
7815    }
7816
7817    #[test]
7818    fn test_encode_i64_eq_thumb2() {
7819        let encoder = ArmEncoder::new_thumb2();
7820        let op = ArmOp::I64Eq {
7821            rd: Reg::R0,
7822            rnlo: Reg::R0,
7823            rnhi: Reg::R1,
7824            rmlo: Reg::R2,
7825            rmhi: Reg::R3,
7826        };
7827        let code = encoder.encode(&op).unwrap();
7828        // Delegates to I64SetCond EQ: CMP lo + IT EQ + CMPEQ hi + ITE EQ + MOV 1 + MOV 0
7829        assert!(code.len() >= 10, "I64Eq should emit at least 10 bytes");
7830    }
7831
7832    #[test]
7833    fn test_encode_i64_ldr_thumb2() {
7834        let encoder = ArmEncoder::new_thumb2();
7835        let op = ArmOp::I64Ldr {
7836            rdlo: Reg::R0,
7837            rdhi: Reg::R1,
7838            addr: MemAddr::imm(Reg::SP, 0),
7839        };
7840        let code = encoder.encode(&op).unwrap();
7841        // Two LDR instructions (lo at offset, hi at offset+4)
7842        assert!(code.len() >= 4, "I64Ldr should emit at least 4 bytes");
7843    }
7844
7845    #[test]
7846    fn test_encode_i64_str_thumb2() {
7847        let encoder = ArmEncoder::new_thumb2();
7848        let op = ArmOp::I64Str {
7849            rdlo: Reg::R0,
7850            rdhi: Reg::R1,
7851            addr: MemAddr::imm(Reg::SP, 0),
7852        };
7853        let code = encoder.encode(&op).unwrap();
7854        // Two STR instructions (lo at offset, hi at offset+4)
7855        assert!(code.len() >= 4, "I64Str should emit at least 4 bytes");
7856    }
7857
7858    #[test]
7859    fn test_encode_i64_all_comparisons_thumb2() {
7860        let encoder = ArmEncoder::new_thumb2();
7861
7862        let ops = vec![
7863            ArmOp::I64Ne {
7864                rd: Reg::R0,
7865                rnlo: Reg::R0,
7866                rnhi: Reg::R1,
7867                rmlo: Reg::R2,
7868                rmhi: Reg::R3,
7869            },
7870            ArmOp::I64LtS {
7871                rd: Reg::R0,
7872                rnlo: Reg::R0,
7873                rnhi: Reg::R1,
7874                rmlo: Reg::R2,
7875                rmhi: Reg::R3,
7876            },
7877            ArmOp::I64LtU {
7878                rd: Reg::R0,
7879                rnlo: Reg::R0,
7880                rnhi: Reg::R1,
7881                rmlo: Reg::R2,
7882                rmhi: Reg::R3,
7883            },
7884            ArmOp::I64LeS {
7885                rd: Reg::R0,
7886                rnlo: Reg::R0,
7887                rnhi: Reg::R1,
7888                rmlo: Reg::R2,
7889                rmhi: Reg::R3,
7890            },
7891            ArmOp::I64LeU {
7892                rd: Reg::R0,
7893                rnlo: Reg::R0,
7894                rnhi: Reg::R1,
7895                rmlo: Reg::R2,
7896                rmhi: Reg::R3,
7897            },
7898            ArmOp::I64GtS {
7899                rd: Reg::R0,
7900                rnlo: Reg::R0,
7901                rnhi: Reg::R1,
7902                rmlo: Reg::R2,
7903                rmhi: Reg::R3,
7904            },
7905            ArmOp::I64GtU {
7906                rd: Reg::R0,
7907                rnlo: Reg::R0,
7908                rnhi: Reg::R1,
7909                rmlo: Reg::R2,
7910                rmhi: Reg::R3,
7911            },
7912            ArmOp::I64GeS {
7913                rd: Reg::R0,
7914                rnlo: Reg::R0,
7915                rnhi: Reg::R1,
7916                rmlo: Reg::R2,
7917                rmhi: Reg::R3,
7918            },
7919            ArmOp::I64GeU {
7920                rd: Reg::R0,
7921                rnlo: Reg::R0,
7922                rnhi: Reg::R1,
7923                rmlo: Reg::R2,
7924                rmhi: Reg::R3,
7925            },
7926        ];
7927
7928        for op in &ops {
7929            let code = encoder.encode(op).unwrap();
7930            assert!(
7931                code.len() >= 8,
7932                "i64 comparison {:?} should emit at least 8 bytes, got {}",
7933                op,
7934                code.len()
7935            );
7936        }
7937    }
7938
7939    #[test]
7940    fn test_encode_i64_const_zero_thumb2() {
7941        let encoder = ArmEncoder::new_thumb2();
7942        let op = ArmOp::I64Const {
7943            rdlo: Reg::R0,
7944            rdhi: Reg::R1,
7945            value: 0,
7946        };
7947        let code = encoder.encode(&op).unwrap();
7948        // MOVW R0, #0 (4 bytes) + MOVW R1, #0 (4 bytes) = 8 bytes
7949        assert_eq!(code.len(), 8, "I64Const(0) should be 8 bytes");
7950    }
7951
7952    #[test]
7953    fn test_encode_i64_const_negative_one_thumb2() {
7954        let encoder = ArmEncoder::new_thumb2();
7955        let op = ArmOp::I64Const {
7956            rdlo: Reg::R0,
7957            rdhi: Reg::R1,
7958            value: -1, // 0xFFFF_FFFF_FFFF_FFFF
7959        };
7960        let code = encoder.encode(&op).unwrap();
7961        // MOVW + MOVT for lo (8 bytes) + MOVW + MOVT for hi (8 bytes) = 16 bytes
7962        assert_eq!(code.len(), 16, "I64Const(-1) should be 16 bytes");
7963    }
7964
7965    // =========================================================================
7966    // Sub-word load/store encoding tests
7967    // =========================================================================
7968
7969    #[test]
7970    fn test_encode_ldrb_arm32() {
7971        let encoder = ArmEncoder::new_arm32();
7972        let op = ArmOp::Ldrb {
7973            rd: Reg::R0,
7974            addr: MemAddr::imm(Reg::R1, 4),
7975        };
7976        let code = encoder.encode(&op).unwrap();
7977        assert_eq!(code.len(), 4, "ARM32 LDRB should be 4 bytes");
7978        // LDRB R0, [R1, #4] = 0xE5D10004
7979        let encoded = u32::from_le_bytes([code[0], code[1], code[2], code[3]]);
7980        assert_eq!(encoded, 0xE5D10004, "Should encode LDRB R0, [R1, #4]");
7981    }
7982
7983    #[test]
7984    fn test_encode_strb_arm32() {
7985        let encoder = ArmEncoder::new_arm32();
7986        let op = ArmOp::Strb {
7987            rd: Reg::R0,
7988            addr: MemAddr::imm(Reg::R1, 0),
7989        };
7990        let code = encoder.encode(&op).unwrap();
7991        assert_eq!(code.len(), 4, "ARM32 STRB should be 4 bytes");
7992        // STRB R0, [R1, #0] = 0xE5C10000
7993        let encoded = u32::from_le_bytes([code[0], code[1], code[2], code[3]]);
7994        assert_eq!(encoded, 0xE5C10000, "Should encode STRB R0, [R1, #0]");
7995    }
7996
7997    #[test]
7998    fn test_encode_ldrh_arm32() {
7999        let encoder = ArmEncoder::new_arm32();
8000        let op = ArmOp::Ldrh {
8001            rd: Reg::R0,
8002            addr: MemAddr::imm(Reg::R1, 2),
8003        };
8004        let code = encoder.encode(&op).unwrap();
8005        assert_eq!(code.len(), 4, "ARM32 LDRH should be 4 bytes");
8006    }
8007
8008    #[test]
8009    fn test_encode_strh_arm32() {
8010        let encoder = ArmEncoder::new_arm32();
8011        let op = ArmOp::Strh {
8012            rd: Reg::R0,
8013            addr: MemAddr::imm(Reg::R1, 0),
8014        };
8015        let code = encoder.encode(&op).unwrap();
8016        assert_eq!(code.len(), 4, "ARM32 STRH should be 4 bytes");
8017    }
8018
8019    #[test]
8020    fn test_encode_ldrsb_arm32() {
8021        let encoder = ArmEncoder::new_arm32();
8022        let op = ArmOp::Ldrsb {
8023            rd: Reg::R0,
8024            addr: MemAddr::imm(Reg::R1, 0),
8025        };
8026        let code = encoder.encode(&op).unwrap();
8027        assert_eq!(code.len(), 4, "ARM32 LDRSB should be 4 bytes");
8028    }
8029
8030    #[test]
8031    fn test_encode_ldrsh_arm32() {
8032        let encoder = ArmEncoder::new_arm32();
8033        let op = ArmOp::Ldrsh {
8034            rd: Reg::R0,
8035            addr: MemAddr::imm(Reg::R1, 0),
8036        };
8037        let code = encoder.encode(&op).unwrap();
8038        assert_eq!(code.len(), 4, "ARM32 LDRSH should be 4 bytes");
8039    }
8040
8041    #[test]
8042    fn test_encode_ldrb_thumb2_16bit() {
8043        let encoder = ArmEncoder::new_thumb2();
8044        let op = ArmOp::Ldrb {
8045            rd: Reg::R0,
8046            addr: MemAddr::imm(Reg::R1, 4),
8047        };
8048        let code = encoder.encode(&op).unwrap();
8049        // Low registers + small offset -> 16-bit encoding
8050        assert_eq!(
8051            code.len(),
8052            2,
8053            "Thumb-2 LDRB with small offset should be 16-bit"
8054        );
8055    }
8056
8057    #[test]
8058    fn test_encode_ldrb_thumb2_32bit() {
8059        let encoder = ArmEncoder::new_thumb2();
8060        let op = ArmOp::Ldrb {
8061            rd: Reg::R0,
8062            addr: MemAddr::imm(Reg::R1, 100), // offset > 31 needs 32-bit
8063        };
8064        let code = encoder.encode(&op).unwrap();
8065        assert_eq!(
8066            code.len(),
8067            4,
8068            "Thumb-2 LDRB with large offset should be 32-bit"
8069        );
8070    }
8071
8072    #[test]
8073    fn test_encode_strb_thumb2_16bit() {
8074        let encoder = ArmEncoder::new_thumb2();
8075        let op = ArmOp::Strb {
8076            rd: Reg::R0,
8077            addr: MemAddr::imm(Reg::R1, 10),
8078        };
8079        let code = encoder.encode(&op).unwrap();
8080        assert_eq!(
8081            code.len(),
8082            2,
8083            "Thumb-2 STRB with small offset should be 16-bit"
8084        );
8085    }
8086
8087    #[test]
8088    fn test_encode_ldrh_thumb2_16bit() {
8089        let encoder = ArmEncoder::new_thumb2();
8090        let op = ArmOp::Ldrh {
8091            rd: Reg::R0,
8092            addr: MemAddr::imm(Reg::R1, 4), // offset aligned to 2, <= 62
8093        };
8094        let code = encoder.encode(&op).unwrap();
8095        assert_eq!(
8096            code.len(),
8097            2,
8098            "Thumb-2 LDRH with small aligned offset should be 16-bit"
8099        );
8100    }
8101
8102    #[test]
8103    fn test_encode_strh_thumb2_16bit() {
8104        let encoder = ArmEncoder::new_thumb2();
8105        let op = ArmOp::Strh {
8106            rd: Reg::R0,
8107            addr: MemAddr::imm(Reg::R1, 4),
8108        };
8109        let code = encoder.encode(&op).unwrap();
8110        assert_eq!(
8111            code.len(),
8112            2,
8113            "Thumb-2 STRH with small aligned offset should be 16-bit"
8114        );
8115    }
8116
8117    #[test]
8118    fn test_encode_ldrsb_thumb2() {
8119        let encoder = ArmEncoder::new_thumb2();
8120        let op = ArmOp::Ldrsb {
8121            rd: Reg::R0,
8122            addr: MemAddr::imm(Reg::R1, 0),
8123        };
8124        let code = encoder.encode(&op).unwrap();
8125        // LDRSB has no 16-bit immediate form, always 32-bit
8126        assert_eq!(code.len(), 4, "Thumb-2 LDRSB should be 32-bit");
8127    }
8128
8129    #[test]
8130    fn test_encode_ldrsh_thumb2() {
8131        let encoder = ArmEncoder::new_thumb2();
8132        let op = ArmOp::Ldrsh {
8133            rd: Reg::R0,
8134            addr: MemAddr::imm(Reg::R1, 0),
8135        };
8136        let code = encoder.encode(&op).unwrap();
8137        assert_eq!(code.len(), 4, "Thumb-2 LDRSH should be 32-bit");
8138    }
8139
8140    #[test]
8141    fn test_encode_memory_size_thumb2() {
8142        let encoder = ArmEncoder::new_thumb2();
8143        let op = ArmOp::MemorySize { rd: Reg::R0 };
8144        let code = encoder.encode(&op).unwrap();
8145        // R0 and R10 are not both low registers, so this needs careful handling
8146        assert!(!code.is_empty(), "MemorySize should produce code");
8147    }
8148
8149    #[test]
8150    fn test_encode_memory_grow_thumb2() {
8151        let encoder = ArmEncoder::new_thumb2();
8152        let op = ArmOp::MemoryGrow {
8153            rd: Reg::R0,
8154            rn: Reg::R0,
8155        };
8156        let code = encoder.encode(&op).unwrap();
8157        assert_eq!(code.len(), 4, "MemoryGrow (MVN) should be 32-bit Thumb-2");
8158    }
8159
8160    #[test]
8161    fn test_encode_subword_reg_offset_thumb2() {
8162        let encoder = ArmEncoder::new_thumb2();
8163
8164        // LDRB with register offset
8165        let op = ArmOp::Ldrb {
8166            rd: Reg::R0,
8167            addr: MemAddr::reg(Reg::R1, Reg::R2),
8168        };
8169        let code = encoder.encode(&op).unwrap();
8170        assert_eq!(
8171            code.len(),
8172            4,
8173            "Thumb-2 LDRB with reg offset should be 32-bit"
8174        );
8175
8176        // STRB with register offset
8177        let op = ArmOp::Strb {
8178            rd: Reg::R0,
8179            addr: MemAddr::reg(Reg::R1, Reg::R2),
8180        };
8181        let code = encoder.encode(&op).unwrap();
8182        assert_eq!(
8183            code.len(),
8184            4,
8185            "Thumb-2 STRB with reg offset should be 32-bit"
8186        );
8187
8188        // LDRH with register offset
8189        let op = ArmOp::Ldrh {
8190            rd: Reg::R0,
8191            addr: MemAddr::reg(Reg::R1, Reg::R2),
8192        };
8193        let code = encoder.encode(&op).unwrap();
8194        assert_eq!(
8195            code.len(),
8196            4,
8197            "Thumb-2 LDRH with reg offset should be 32-bit"
8198        );
8199
8200        // STRH with register offset
8201        let op = ArmOp::Strh {
8202            rd: Reg::R0,
8203            addr: MemAddr::reg(Reg::R1, Reg::R2),
8204        };
8205        let code = encoder.encode(&op).unwrap();
8206        assert_eq!(
8207            code.len(),
8208            4,
8209            "Thumb-2 STRH with reg offset should be 32-bit"
8210        );
8211    }
8212
8213    #[test]
8214    fn test_encode_subword_reg_imm_offset_thumb2() {
8215        let encoder = ArmEncoder::new_thumb2();
8216
8217        // LDRB with both register and immediate offset
8218        let op = ArmOp::Ldrb {
8219            rd: Reg::R0,
8220            addr: MemAddr::reg_imm(Reg::R1, Reg::R2, 4),
8221        };
8222        let code = encoder.encode(&op).unwrap();
8223        // ADD R12, R2, #4 (4 bytes) + LDRB R0, [R1, R12] (4 bytes) = 8 bytes
8224        assert_eq!(
8225            code.len(),
8226            8,
8227            "Thumb-2 LDRB with reg+imm offset should be 8 bytes"
8228        );
8229    }
8230
8231    // ========================================================================
8232    // Helium MVE encoding tests
8233    // ========================================================================
8234
8235    #[test]
8236    fn test_encode_mve_addi32_thumb2() {
8237        let encoder = ArmEncoder::new_thumb2();
8238        let op = ArmOp::MveAddI {
8239            qd: QReg::Q0,
8240            qn: QReg::Q1,
8241            qm: QReg::Q2,
8242            size: MveSize::S32,
8243        };
8244        let code = encoder.encode(&op).unwrap();
8245        assert_eq!(
8246            code.len(),
8247            4,
8248            "MVE VADD.I32 should be 4 bytes (Thumb-2 32-bit)"
8249        );
8250    }
8251
8252    #[test]
8253    fn test_encode_mve_subi16_thumb2() {
8254        let encoder = ArmEncoder::new_thumb2();
8255        let op = ArmOp::MveSubI {
8256            qd: QReg::Q0,
8257            qn: QReg::Q1,
8258            qm: QReg::Q2,
8259            size: MveSize::S16,
8260        };
8261        let code = encoder.encode(&op).unwrap();
8262        assert_eq!(code.len(), 4, "MVE VSUB.I16 should be 4 bytes");
8263    }
8264
8265    #[test]
8266    fn test_encode_mve_muli8_thumb2() {
8267        let encoder = ArmEncoder::new_thumb2();
8268        let op = ArmOp::MveMulI {
8269            qd: QReg::Q0,
8270            qn: QReg::Q1,
8271            qm: QReg::Q2,
8272            size: MveSize::S8,
8273        };
8274        let code = encoder.encode(&op).unwrap();
8275        assert_eq!(code.len(), 4, "MVE VMUL.I8 should be 4 bytes");
8276    }
8277
8278    #[test]
8279    fn test_encode_mve_bitwise_thumb2() {
8280        let encoder = ArmEncoder::new_thumb2();
8281
8282        let ops = vec![
8283            ArmOp::MveAnd {
8284                qd: QReg::Q0,
8285                qn: QReg::Q1,
8286                qm: QReg::Q2,
8287            },
8288            ArmOp::MveOrr {
8289                qd: QReg::Q0,
8290                qn: QReg::Q1,
8291                qm: QReg::Q2,
8292            },
8293            ArmOp::MveEor {
8294                qd: QReg::Q0,
8295                qn: QReg::Q1,
8296                qm: QReg::Q2,
8297            },
8298            ArmOp::MveBic {
8299                qd: QReg::Q0,
8300                qn: QReg::Q1,
8301                qm: QReg::Q2,
8302            },
8303        ];
8304        for op in ops {
8305            let code = encoder.encode(&op).unwrap();
8306            assert_eq!(code.len(), 4, "MVE bitwise op should be 4 bytes");
8307        }
8308    }
8309
8310    #[test]
8311    fn test_encode_mve_mvn_thumb2() {
8312        let encoder = ArmEncoder::new_thumb2();
8313        let op = ArmOp::MveMvn {
8314            qd: QReg::Q0,
8315            qm: QReg::Q1,
8316        };
8317        let code = encoder.encode(&op).unwrap();
8318        assert_eq!(code.len(), 4, "MVE VMVN should be 4 bytes");
8319    }
8320
8321    #[test]
8322    fn test_encode_mve_load_store_thumb2() {
8323        let encoder = ArmEncoder::new_thumb2();
8324
8325        let load = ArmOp::MveLoad {
8326            qd: QReg::Q0,
8327            addr: MemAddr::imm(Reg::R0, 16),
8328        };
8329        let code = encoder.encode(&load).unwrap();
8330        assert_eq!(code.len(), 4, "MVE VLDRW.32 should be 4 bytes");
8331
8332        let store = ArmOp::MveStore {
8333            qd: QReg::Q1,
8334            addr: MemAddr::imm(Reg::R1, 0),
8335        };
8336        let code = encoder.encode(&store).unwrap();
8337        assert_eq!(code.len(), 4, "MVE VSTRW.32 should be 4 bytes");
8338    }
8339
8340    #[test]
8341    fn test_encode_mve_const_thumb2() {
8342        let encoder = ArmEncoder::new_thumb2();
8343        let op = ArmOp::MveConst {
8344            qd: QReg::Q0,
8345            bytes: [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0],
8346        };
8347        let code = encoder.encode(&op).unwrap();
8348        // Should be 4 words of (MOVW R12 + VMOV Sn) = 4 * (4+4) = 32 bytes min
8349        // Some words with hi16=0 skip MOVT, so length varies
8350        assert!(
8351            code.len() >= 24,
8352            "MVE const should produce multiple instructions"
8353        );
8354    }
8355
8356    #[test]
8357    fn test_encode_mve_dup_thumb2() {
8358        let encoder = ArmEncoder::new_thumb2();
8359        let op = ArmOp::MveDup {
8360            qd: QReg::Q0,
8361            rn: Reg::R0,
8362            size: MveSize::S32,
8363        };
8364        let code = encoder.encode(&op).unwrap();
8365        assert_eq!(code.len(), 4, "MVE VDUP.32 should be 4 bytes");
8366    }
8367
8368    #[test]
8369    fn test_encode_mve_extract_lane_thumb2() {
8370        let encoder = ArmEncoder::new_thumb2();
8371        let op = ArmOp::MveExtractLane {
8372            rd: Reg::R0,
8373            qn: QReg::Q1,
8374            lane: 2,
8375            size: MveSize::S32,
8376        };
8377        let code = encoder.encode(&op).unwrap();
8378        assert_eq!(code.len(), 4, "MVE extract lane should be 4 bytes");
8379    }
8380
8381    #[test]
8382    fn test_encode_mve_insert_lane_thumb2() {
8383        let encoder = ArmEncoder::new_thumb2();
8384        let op = ArmOp::MveInsertLane {
8385            qd: QReg::Q0,
8386            rn: Reg::R1,
8387            lane: 3,
8388            size: MveSize::S32,
8389        };
8390        let code = encoder.encode(&op).unwrap();
8391        assert_eq!(code.len(), 4, "MVE insert lane should be 4 bytes");
8392    }
8393
8394    #[test]
8395    fn test_encode_mve_addf32_thumb2() {
8396        let encoder = ArmEncoder::new_thumb2();
8397        let op = ArmOp::MveAddF32 {
8398            qd: QReg::Q0,
8399            qn: QReg::Q1,
8400            qm: QReg::Q2,
8401        };
8402        let code = encoder.encode(&op).unwrap();
8403        assert_eq!(code.len(), 4, "MVE VADD.F32 should be 4 bytes");
8404    }
8405
8406    #[test]
8407    fn test_encode_mve_divf32_thumb2() {
8408        let encoder = ArmEncoder::new_thumb2();
8409        let op = ArmOp::MveDivF32 {
8410            qd: QReg::Q0,
8411            qn: QReg::Q1,
8412            qm: QReg::Q2,
8413        };
8414        let code = encoder.encode(&op).unwrap();
8415        // Lane-wise: 4 x VDIV.F32 = 4 x 4 = 16 bytes
8416        assert_eq!(
8417            code.len(),
8418            16,
8419            "MVE VDIV.F32 (lane-wise) should be 16 bytes"
8420        );
8421    }
8422
8423    #[test]
8424    fn test_encode_mve_sqrtf32_thumb2() {
8425        let encoder = ArmEncoder::new_thumb2();
8426        let op = ArmOp::MveSqrtF32 {
8427            qd: QReg::Q0,
8428            qm: QReg::Q1,
8429        };
8430        let code = encoder.encode(&op).unwrap();
8431        // Lane-wise: 4 x VSQRT.F32 = 4 x 4 = 16 bytes
8432        assert_eq!(
8433            code.len(),
8434            16,
8435            "MVE VSQRT.F32 (lane-wise) should be 16 bytes"
8436        );
8437    }
8438
8439    #[test]
8440    fn test_encode_mve_negf32_thumb2() {
8441        let encoder = ArmEncoder::new_thumb2();
8442        let op = ArmOp::MveNegF32 {
8443            qd: QReg::Q0,
8444            qm: QReg::Q1,
8445        };
8446        let code = encoder.encode(&op).unwrap();
8447        assert_eq!(code.len(), 4, "MVE VNEG.F32 should be 4 bytes");
8448    }
8449
8450    #[test]
8451    fn test_encode_mve_absf32_thumb2() {
8452        let encoder = ArmEncoder::new_thumb2();
8453        let op = ArmOp::MveAbsF32 {
8454            qd: QReg::Q0,
8455            qm: QReg::Q1,
8456        };
8457        let code = encoder.encode(&op).unwrap();
8458        assert_eq!(code.len(), 4, "MVE VABS.F32 should be 4 bytes");
8459    }
8460
8461    #[test]
8462    fn test_encode_mve_different_qregs() {
8463        let encoder = ArmEncoder::new_thumb2();
8464
8465        // Test that different Q-register numbers produce different encodings
8466        let op1 = ArmOp::MveAddI {
8467            qd: QReg::Q0,
8468            qn: QReg::Q0,
8469            qm: QReg::Q0,
8470            size: MveSize::S32,
8471        };
8472        let op2 = ArmOp::MveAddI {
8473            qd: QReg::Q3,
8474            qn: QReg::Q5,
8475            qm: QReg::Q7,
8476            size: MveSize::S32,
8477        };
8478        let code1 = encoder.encode(&op1).unwrap();
8479        let code2 = encoder.encode(&op2).unwrap();
8480        assert_ne!(
8481            code1, code2,
8482            "Different Q-registers should produce different encodings"
8483        );
8484    }
8485
8486    #[test]
8487    fn test_encode_mve_arm32_nop() {
8488        // MVE instructions on ARM32 encoder should produce NOP (only Thumb-2 supported)
8489        let encoder = ArmEncoder::new_arm32();
8490        let op = ArmOp::MveAddI {
8491            qd: QReg::Q0,
8492            qn: QReg::Q1,
8493            qm: QReg::Q2,
8494            size: MveSize::S32,
8495        };
8496        let code = encoder.encode(&op).unwrap();
8497        assert_eq!(code.len(), 4, "ARM32 MVE should be 4 bytes (NOP)");
8498        // NOP in ARM32 is 0xE1A00000 (MOV R0, R0)
8499        let instr = u32::from_le_bytes([code[0], code[1], code[2], code[3]]);
8500        assert_eq!(instr, 0xE1A00000, "ARM32 MVE should encode as NOP");
8501    }
8502}