Skip to main content

jit_core/
engine.rs

1//! Runtime variant matcher and field encoder.
2//!
3//! High-level flow:
4//! 1. Flatten structured operands into a linear kind/value stream.
5//! 2. Materialize optional fields (for example bare `[base]` vs explicit `#0`).
6//! 3. Select candidate variants by shape and validate constraints.
7//! 4. Encode normalized values into bitfields with scale/range checks.
8//! 5. Return precise diagnostics when no unique variant can be chosen.
9
10use alloc::vec::Vec;
11
12use crate::types::*;
13
14fn field_mask(width: u8) -> u32 {
15    if width == 32 {
16        u32::MAX
17    } else {
18        (1u32 << width) - 1
19    }
20}
21
22#[derive(Debug, Copy, Clone, PartialEq, Eq)]
23struct FlatOperand {
24    /// Operand kind token used by the selector.
25    kind: OperandConstraintKind,
26    /// Canonical immediate/register index value after flattening.
27    value: i64,
28    /// Optional tokens can be omitted when matching shorter forms.
29    optional: bool,
30}
31
32#[inline]
33fn reg_constraint_for_class(class: RegClass) -> OperandConstraintKind {
34    match class {
35        RegClass::W | RegClass::Wsp => OperandConstraintKind::Gpr32Register,
36        RegClass::X | RegClass::Xsp => OperandConstraintKind::Gpr64Register,
37        RegClass::V | RegClass::B | RegClass::H | RegClass::S | RegClass::D | RegClass::Q => {
38            OperandConstraintKind::SimdRegister
39        }
40        RegClass::Z => OperandConstraintKind::SveZRegister,
41        RegClass::P => OperandConstraintKind::PredicateRegister,
42    }
43}
44
45#[inline]
46fn condition_code_value(code: ConditionCode) -> i64 {
47    match code {
48        ConditionCode::Eq => 0,
49        ConditionCode::Ne => 1,
50        ConditionCode::Cs => 2,
51        ConditionCode::Cc => 3,
52        ConditionCode::Mi => 4,
53        ConditionCode::Pl => 5,
54        ConditionCode::Vs => 6,
55        ConditionCode::Vc => 7,
56        ConditionCode::Hi => 8,
57        ConditionCode::Ls => 9,
58        ConditionCode::Ge => 10,
59        ConditionCode::Lt => 11,
60        ConditionCode::Gt => 12,
61        ConditionCode::Le => 13,
62        ConditionCode::Al => 14,
63        ConditionCode::Nv => 15,
64    }
65}
66
67#[inline]
68fn shift_kind_value(kind: ShiftKind) -> i64 {
69    match kind {
70        ShiftKind::Lsl => 0,
71        ShiftKind::Lsr => 1,
72        ShiftKind::Asr => 2,
73        ShiftKind::Ror => 3,
74        ShiftKind::Msl => 4,
75    }
76}
77
78#[inline]
79fn extend_kind_value(kind: ExtendKind) -> i64 {
80    match kind {
81        ExtendKind::Uxtb => 0,
82        ExtendKind::Uxth => 1,
83        ExtendKind::Uxtw => 2,
84        ExtendKind::Uxtx => 3,
85        ExtendKind::Sxtb => 4,
86        ExtendKind::Sxth => 5,
87        ExtendKind::Sxtw => 6,
88        ExtendKind::Sxtx => 7,
89    }
90}
91
92#[inline]
93fn arrangement_value(arrangement: VectorArrangement) -> i64 {
94    match arrangement {
95        VectorArrangement::B8 => 0,
96        VectorArrangement::B16 => 1,
97        VectorArrangement::H4 => 2,
98        VectorArrangement::H8 => 3,
99        VectorArrangement::S2 => 4,
100        VectorArrangement::S4 => 5,
101        VectorArrangement::D1 => 6,
102        VectorArrangement::D2 => 7,
103        VectorArrangement::Q1 => 8,
104    }
105}
106
107#[inline]
108fn push_flat(
109    out: &mut [FlatOperand; 64],
110    len: &mut usize,
111    operand: FlatOperand,
112) -> Result<(), EncodeError> {
113    if *len >= out.len() {
114        return Err(EncodeError::OperandCountMismatch);
115    }
116    out[*len] = operand;
117    *len += 1;
118    Ok(())
119}
120
121#[inline]
122fn push_flat_optional(
123    out: &mut [FlatOperand; 64],
124    len: &mut usize,
125    kind: OperandConstraintKind,
126    value: i64,
127) -> Result<(), EncodeError> {
128    push_flat(
129        out,
130        len,
131        FlatOperand {
132            kind,
133            value,
134            optional: true,
135        },
136    )
137}
138
139fn flatten_register(
140    reg: RegisterOperand,
141    out: &mut [FlatOperand; 64],
142    len: &mut usize,
143) -> Result<(), EncodeError> {
144    push_flat(
145        out,
146        len,
147        FlatOperand {
148            kind: reg_constraint_for_class(reg.class),
149            value: i64::from(reg.code),
150            optional: false,
151        },
152    )?;
153
154    if let Some(arrangement) = reg.arrangement {
155        push_flat(
156            out,
157            len,
158            FlatOperand {
159                kind: OperandConstraintKind::Arrangement,
160                value: arrangement_value(arrangement),
161                optional: false,
162            },
163        )?;
164    }
165
166    if let Some(lane) = reg.lane {
167        push_flat(
168            out,
169            len,
170            FlatOperand {
171                kind: OperandConstraintKind::Lane,
172                value: i64::from(lane),
173                optional: false,
174            },
175        )?;
176    }
177
178    Ok(())
179}
180
181fn flatten_modifier(
182    modifier: Modifier,
183    out: &mut [FlatOperand; 64],
184    len: &mut usize,
185) -> Result<(), EncodeError> {
186    match modifier {
187        Modifier::Shift(shift) => {
188            push_flat(
189                out,
190                len,
191                FlatOperand {
192                    kind: OperandConstraintKind::ShiftKind,
193                    value: shift_kind_value(shift.kind),
194                    optional: false,
195                },
196            )?;
197            push_flat(
198                out,
199                len,
200                FlatOperand {
201                    kind: OperandConstraintKind::Immediate,
202                    value: i64::from(shift.amount),
203                    optional: false,
204                },
205            )?;
206        }
207        Modifier::Extend(extend) => {
208            push_flat(
209                out,
210                len,
211                FlatOperand {
212                    kind: OperandConstraintKind::ExtendKind,
213                    value: extend_kind_value(extend.kind),
214                    optional: false,
215                },
216            )?;
217            push_flat(
218                out,
219                len,
220                FlatOperand {
221                    kind: OperandConstraintKind::Immediate,
222                    value: i64::from(extend.amount.unwrap_or(0)),
223                    optional: false,
224                },
225            )?;
226        }
227    }
228    Ok(())
229}
230
231fn flatten_operand(
232    operand: Operand,
233    out: &mut [FlatOperand; 64],
234    len: &mut usize,
235) -> Result<(), EncodeError> {
236    match operand {
237        Operand::Register(reg) => flatten_register(reg, out, len),
238        Operand::Immediate(value) => push_flat(
239            out,
240            len,
241            FlatOperand {
242                kind: OperandConstraintKind::Immediate,
243                value,
244                optional: false,
245            },
246        ),
247        Operand::Condition(cond) => push_flat(
248            out,
249            len,
250            FlatOperand {
251                kind: OperandConstraintKind::Condition,
252                value: condition_code_value(cond),
253                optional: false,
254            },
255        ),
256        Operand::Shift(shift) => flatten_modifier(Modifier::Shift(shift), out, len),
257        Operand::Extend(extend) => flatten_modifier(Modifier::Extend(extend), out, len),
258        Operand::Memory(mem) => {
259            flatten_register(mem.base, out, len)?;
260            match mem.offset {
261                MemoryOffset::None => {
262                    if mem.addressing == AddressingMode::Offset {
263                        // Bare `[base]` can map either to no-offset forms (e.g. LDAR)
264                        // or to encodings with an explicit zero immediate (e.g. LDR).
265                        push_flat_optional(out, len, OperandConstraintKind::Immediate, 0)?;
266                    }
267                }
268                MemoryOffset::Immediate(value) => {
269                    push_flat(
270                        out,
271                        len,
272                        FlatOperand {
273                            kind: OperandConstraintKind::Immediate,
274                            value,
275                            optional: false,
276                        },
277                    )?;
278                }
279                MemoryOffset::Register { reg, modifier } => {
280                    flatten_register(reg, out, len)?;
281                    if let Some(modifier) = modifier {
282                        flatten_modifier(modifier, out, len)?;
283                    }
284                }
285            }
286
287            if let AddressingMode::PostIndex(offset) = mem.addressing {
288                match offset {
289                    PostIndexOffset::Immediate(value) => {
290                        push_flat(
291                            out,
292                            len,
293                            FlatOperand {
294                                kind: OperandConstraintKind::Immediate,
295                                value,
296                                optional: false,
297                            },
298                        )?;
299                    }
300                    PostIndexOffset::Register(reg) => {
301                        flatten_register(reg, out, len)?;
302                    }
303                }
304            }
305
306            Ok(())
307        }
308        Operand::RegisterList(list) => {
309            if list.count == 0 {
310                return Err(EncodeError::OperandCountMismatch);
311            }
312
313            let mut idx = 0u8;
314            while idx < list.count {
315                let code = list.first.code.saturating_add(idx);
316                if code > 31 {
317                    return Err(EncodeError::OperandOutOfRange {
318                        field: "reglist",
319                        value: i64::from(code),
320                        width: 5,
321                        signed: false,
322                    });
323                }
324                flatten_register(
325                    RegisterOperand {
326                        code,
327                        class: list.first.class,
328                        arrangement: list.first.arrangement,
329                        lane: list.first.lane,
330                    },
331                    out,
332                    len,
333                )?;
334                idx = idx.saturating_add(1);
335            }
336            Ok(())
337        }
338        Operand::SysReg(sys) => {
339            let o0 = match sys.op0 {
340                2 | 3 => sys.op0 - 2,
341                _ => {
342                    return Err(EncodeError::OperandOutOfRange {
343                        field: "op0",
344                        value: i64::from(sys.op0),
345                        width: 2,
346                        signed: false,
347                    });
348                }
349            };
350
351            for value in [sys.op1, sys.crn, sys.crm, sys.op2] {
352                push_flat(
353                    out,
354                    len,
355                    FlatOperand {
356                        kind: OperandConstraintKind::SysRegPart,
357                        value: i64::from(value),
358                        optional: false,
359                    },
360                )?;
361            }
362            push_flat(
363                out,
364                len,
365                FlatOperand {
366                    kind: OperandConstraintKind::Immediate,
367                    value: i64::from(o0),
368                    optional: false,
369                },
370            )?;
371            Ok(())
372        }
373    }
374}
375
376fn flatten_operands(
377    operands: &[Operand],
378    out: &mut [FlatOperand; 64],
379) -> Result<usize, EncodeError> {
380    let mut len = 0usize;
381    for operand in operands.iter().copied() {
382        flatten_operand(operand, out, &mut len)?;
383    }
384    Ok(len)
385}
386
387fn materialize_flat_for_expected_len(
388    expected_len: usize,
389    flat: &[FlatOperand],
390    out: &mut [FlatOperand; 64],
391) -> Result<usize, EncodeError> {
392    // A single flattened stream may represent multiple user-visible forms when it
393    // contains optional tokens. This materializer picks one concrete length to
394    // compare against a specific variant signature.
395    let mut required_len = 0usize;
396    let mut optional_len = 0usize;
397    for operand in flat {
398        if operand.optional {
399            optional_len += 1;
400        } else {
401            required_len += 1;
402        }
403    }
404
405    if expected_len < required_len || expected_len > required_len + optional_len {
406        return Err(EncodeError::OperandCountMismatch);
407    }
408
409    let mut include_optional = expected_len - required_len;
410    let mut out_len = 0usize;
411    for operand in flat {
412        if !operand.optional || include_optional > 0 {
413            if out_len >= out.len() {
414                return Err(EncodeError::OperandCountMismatch);
415            }
416            out[out_len] = FlatOperand {
417                kind: operand.kind,
418                value: operand.value,
419                optional: false,
420            };
421            out_len += 1;
422            if operand.optional {
423                include_optional = include_optional.saturating_sub(1);
424            }
425        }
426    }
427
428    if out_len != expected_len {
429        return Err(EncodeError::OperandCountMismatch);
430    }
431    Ok(out_len)
432}
433
434#[inline]
435fn kind_matches(expected: OperandConstraintKind, actual: OperandConstraintKind) -> bool {
436    if expected == actual {
437        return true;
438    }
439
440    matches!(
441        (expected, actual),
442        (
443            OperandConstraintKind::GprRegister,
444            OperandConstraintKind::Gpr32Register
445        ) | (
446            OperandConstraintKind::GprRegister,
447            OperandConstraintKind::Gpr64Register
448        ) | (
449            OperandConstraintKind::SysRegPart,
450            OperandConstraintKind::Immediate
451        )
452    )
453}
454
455#[inline]
456fn kind_matches_for_slot(
457    spec: &EncodingSpec,
458    slot: usize,
459    expected: OperandConstraintKind,
460    actual: OperandConstraintKind,
461) -> bool {
462    if kind_matches(expected, actual) {
463        return true;
464    }
465
466    if !(expected == OperandConstraintKind::Gpr64Register
467        && actual == OperandConstraintKind::Gpr32Register)
468    {
469        return false;
470    }
471    if slot >= u64::BITS as usize {
472        return false;
473    }
474    ((spec.gpr32_extend_compatibility >> slot) & 1) != 0
475}
476
477#[inline]
478fn operand_shape_code(kind: OperandConstraintKind) -> u8 {
479    match kind {
480        OperandConstraintKind::GprRegister => 1,
481        OperandConstraintKind::Gpr32Register => 2,
482        OperandConstraintKind::Gpr64Register => 3,
483        OperandConstraintKind::SimdRegister => 4,
484        OperandConstraintKind::SveZRegister => 5,
485        OperandConstraintKind::PredicateRegister => 6,
486        OperandConstraintKind::Immediate => 7,
487        OperandConstraintKind::Condition => 8,
488        OperandConstraintKind::ShiftKind => 9,
489        OperandConstraintKind::ExtendKind => 10,
490        OperandConstraintKind::SysRegPart => 11,
491        OperandConstraintKind::Arrangement => 12,
492        OperandConstraintKind::Lane => 13,
493    }
494}
495
496#[inline]
497fn memory_shape_code_from_operands(operands: &[Operand]) -> Option<u8> {
498    let mut code = None;
499    for operand in operands {
500        let Operand::Memory(memory) = operand else {
501            continue;
502        };
503        let next = match memory.addressing {
504            AddressingMode::Offset => 14,
505            AddressingMode::PreIndex => 15,
506            AddressingMode::PostIndex(_) => 0,
507        };
508        match code {
509            None => code = Some(next),
510            Some(existing) if existing == next => {}
511            Some(_) => return None,
512        }
513    }
514    code
515}
516
517#[inline]
518fn expected_kind_name(kind: OperandConstraintKind) -> &'static str {
519    match kind {
520        OperandConstraintKind::GprRegister => "general-purpose register",
521        OperandConstraintKind::Gpr32Register => "32-bit general-purpose register",
522        OperandConstraintKind::Gpr64Register => "64-bit general-purpose register",
523        OperandConstraintKind::SimdRegister => "SIMD register",
524        OperandConstraintKind::SveZRegister => "SVE Z register",
525        OperandConstraintKind::PredicateRegister => "predicate register",
526        OperandConstraintKind::Immediate => "immediate",
527        OperandConstraintKind::Condition => "condition code",
528        OperandConstraintKind::ShiftKind => "shift kind",
529        OperandConstraintKind::ExtendKind => "extend kind",
530        OperandConstraintKind::SysRegPart => "system register field",
531        OperandConstraintKind::Arrangement => "vector arrangement",
532        OperandConstraintKind::Lane => "vector lane",
533    }
534}
535
536fn field_name_matches(field: &BitFieldSpec, expected: &str) -> bool {
537    field.name.eq_ignore_ascii_case(expected)
538}
539
540fn spec_has_field(spec: &EncodingSpec, expected: &str) -> bool {
541    spec.fields
542        .iter()
543        .any(|field| field_name_matches(field, expected))
544}
545
546fn spec_matches_memory_addressing(spec: &EncodingSpec, operands: &[Operand]) -> bool {
547    operands.iter().all(|operand| match operand {
548        Operand::Memory(mem) => match (spec.memory_addressing, mem.addressing) {
549            (MemoryAddressingConstraintSpec::None, _) => true,
550            (MemoryAddressingConstraintSpec::NoOffset, AddressingMode::Offset) => {
551                matches!(mem.offset, MemoryOffset::None)
552            }
553            (MemoryAddressingConstraintSpec::Offset, AddressingMode::Offset) => true,
554            (MemoryAddressingConstraintSpec::PreIndex, AddressingMode::PreIndex) => true,
555            (MemoryAddressingConstraintSpec::PostIndex, AddressingMode::PostIndex(_)) => true,
556            (_, _) => false,
557        },
558        _ => true,
559    })
560}
561
562#[derive(Debug, Copy, Clone, PartialEq, Eq)]
563struct SplitImmediatePlan {
564    first_slot: usize,
565    second_slot: usize,
566    kind: SplitImmediateKind,
567}
568
569#[derive(Debug, Copy, Clone, PartialEq, Eq)]
570enum SplitImmediateKind {
571    AdrLike {
572        immlo_field_idx: usize,
573        immhi_field_idx: usize,
574        scale: i64,
575    },
576    BitIndex6 {
577        b5_field_idx: usize,
578        b40_field_idx: usize,
579    },
580    LogicalImmRs {
581        immr_field_idx: usize,
582        imms_field_idx: usize,
583        reg_size: u8,
584    },
585    LogicalImmNrs {
586        n_field_idx: usize,
587        immr_field_idx: usize,
588        imms_field_idx: usize,
589        reg_size: u8,
590    },
591}
592
593#[inline]
594fn spec_split_immediate_plan(spec: &EncodingSpec) -> Option<SplitImmediatePlan> {
595    spec.split_immediate_plan.map(|plan| SplitImmediatePlan {
596        first_slot: usize::from(plan.first_slot),
597        second_slot: usize::from(plan.second_slot),
598        kind: match plan.kind {
599            SplitImmediateKindSpec::AdrLike {
600                immlo_field_index,
601                immhi_field_index,
602                scale,
603            } => SplitImmediateKind::AdrLike {
604                immlo_field_idx: usize::from(immlo_field_index),
605                immhi_field_idx: usize::from(immhi_field_index),
606                scale,
607            },
608            SplitImmediateKindSpec::BitIndex6 {
609                b5_field_index,
610                b40_field_index,
611            } => SplitImmediateKind::BitIndex6 {
612                b5_field_idx: usize::from(b5_field_index),
613                b40_field_idx: usize::from(b40_field_index),
614            },
615            SplitImmediateKindSpec::LogicalImmRs {
616                immr_field_index,
617                imms_field_index,
618                reg_size,
619            } => SplitImmediateKind::LogicalImmRs {
620                immr_field_idx: usize::from(immr_field_index),
621                imms_field_idx: usize::from(imms_field_index),
622                reg_size,
623            },
624            SplitImmediateKindSpec::LogicalImmNrs {
625                n_field_index,
626                immr_field_index,
627                imms_field_index,
628                reg_size,
629            } => SplitImmediateKind::LogicalImmNrs {
630                n_field_idx: usize::from(n_field_index),
631                immr_field_idx: usize::from(immr_field_index),
632                imms_field_idx: usize::from(imms_field_index),
633                reg_size,
634            },
635        },
636    })
637}
638
639#[inline]
640fn spec_maybe_split_immediate(spec: &EncodingSpec) -> bool {
641    spec.split_immediate_plan.is_some()
642}
643
644#[inline]
645fn split_plan_input_span(plan: SplitImmediatePlan) -> usize {
646    match plan.kind {
647        SplitImmediateKind::AdrLike { .. }
648        | SplitImmediateKind::BitIndex6 { .. }
649        | SplitImmediateKind::LogicalImmRs { .. } => 2,
650        SplitImmediateKind::LogicalImmNrs { .. } => 3,
651    }
652}
653
654#[inline]
655fn low_bits_mask(width: u8) -> u64 {
656    if width >= 64 {
657        u64::MAX
658    } else {
659        (1u64 << width) - 1
660    }
661}
662
663#[inline]
664fn rotate_right_with_width(value: u64, amount: u8, width: u8) -> u64 {
665    let mask = low_bits_mask(width);
666    let value = value & mask;
667    let r = amount % width;
668    if r == 0 {
669        value
670    } else {
671        ((value >> r) | (value << (width - r))) & mask
672    }
673}
674
675#[inline]
676fn replicate_pattern_to_width(pattern: u64, element_width: u8, width: u8) -> u64 {
677    let element_mask = low_bits_mask(element_width);
678    let element = pattern & element_mask;
679    let mut out = 0u64;
680    let mut shift = 0u8;
681    while shift < width {
682        out |= element << shift;
683        shift = shift.saturating_add(element_width);
684    }
685    out & low_bits_mask(width)
686}
687
688fn encode_logical_immediate_nrs(value: i64, reg_size: u8) -> Option<(i64, i64, i64)> {
689    if reg_size != 32 && reg_size != 64 {
690        return None;
691    }
692    let mask = low_bits_mask(reg_size);
693    let bits = (value as u64) & mask;
694    if bits == 0 || bits == mask {
695        return None;
696    }
697
698    let mut element_width = 2u8;
699    while element_width <= reg_size {
700        let pattern = bits & low_bits_mask(element_width);
701        if replicate_pattern_to_width(pattern, element_width, reg_size) == bits {
702            let mut ones = 1u8;
703            while ones < element_width {
704                let base = low_bits_mask(ones);
705                let mut immr = 0u8;
706                while immr < element_width {
707                    if rotate_right_with_width(base, immr, element_width) == pattern {
708                        let n = if element_width == 64 { 1i64 } else { 0i64 };
709                        let imms =
710                            ((-(i64::from(element_width) << 1)) | (i64::from(ones) - 1)) & 0x3f;
711                        return Some((n, i64::from(immr), imms));
712                    }
713                    immr = immr.saturating_add(1);
714                }
715                ones = ones.saturating_add(1);
716            }
717        }
718        element_width = element_width.saturating_mul(2);
719    }
720
721    None
722}
723
724fn scale_immediate(field: &'static str, value: i64, scale: i64) -> Result<i64, EncodeError> {
725    if scale <= 1 {
726        return Ok(value);
727    }
728    if value % scale != 0 {
729        return Err(EncodeError::ImmediateNotAligned {
730            field,
731            value,
732            scale,
733        });
734    }
735    Ok(value / scale)
736}
737
738fn normalize_field_value(
739    spec: &EncodingSpec,
740    field: BitFieldSpec,
741    field_idx: usize,
742    value: i64,
743) -> Result<i64, EncodeError> {
744    let scale = spec.field_scales.get(field_idx).copied().unwrap_or(1);
745    scale_immediate(field.name, value, i64::from(scale))
746}
747
748#[inline]
749fn encode_field(field: BitFieldSpec, value: i64) -> Result<u32, EncodeError> {
750    let raw = if field.signed {
751        if field.width == 0 || field.width > 32 {
752            return Err(EncodeError::OperandOutOfRange {
753                field: field.name,
754                value,
755                width: field.width,
756                signed: true,
757            });
758        }
759
760        let min = -(1i64 << (field.width - 1));
761        let max = (1i64 << (field.width - 1)) - 1;
762        if value < min || value > max {
763            return Err(EncodeError::OperandOutOfRange {
764                field: field.name,
765                value,
766                width: field.width,
767                signed: true,
768            });
769        }
770
771        (value as i32 as u32) & field_mask(field.width)
772    } else {
773        if value < 0 {
774            return Err(EncodeError::OperandOutOfRange {
775                field: field.name,
776                value,
777                width: field.width,
778                signed: false,
779            });
780        }
781
782        let max = i64::from(field_mask(field.width));
783        if value > max {
784            return Err(EncodeError::OperandOutOfRange {
785                field: field.name,
786                value,
787                width: field.width,
788                signed: false,
789            });
790        }
791        value as u32
792    };
793
794    Ok(raw << field.lsb)
795}
796
797/// Encodes one instruction variant using structured operands in asm-like order.
798///
799/// Field mapping and kind checks are driven by generated metadata stored in
800/// [`EncodingSpec::operand_order`], [`EncodingSpec::operand_kinds`] and
801/// [`EncodingSpec::implicit_defaults`].
802///
803/// # Errors
804///
805/// Returns [`EncodeError`] when operand count, kind, or ranges are invalid.
806#[inline]
807fn encode_flat_ordered(
808    spec: &EncodingSpec,
809    selected_flat: &[FlatOperand],
810    selected_len: usize,
811    split_plan: Option<SplitImmediatePlan>,
812) -> Result<InstructionCode, EncodeError> {
813    // Values are collected by field index first, then emitted in field order.
814    // This keeps split-immediate and implicit-default handling centralized.
815    let mut values = [0i64; 64];
816    let mut filled_mask = 0u64;
817    let mut input_idx = 0usize;
818    let mut slot = 0usize;
819    while slot < spec.operand_order.len() {
820        if let Some(plan) = split_plan {
821            if slot == plan.first_slot {
822                if input_idx >= selected_len {
823                    return Err(EncodeError::OperandCountMismatch);
824                }
825
826                let first_field_idx = spec.operand_order[slot] as usize;
827                let first_field = spec.fields[first_field_idx];
828                let expected = spec.operand_kinds[slot];
829                let actual = selected_flat[input_idx];
830                if !kind_matches_for_slot(spec, slot, expected, actual.kind) {
831                    return Err(EncodeError::InvalidOperandKind {
832                        field: first_field.name,
833                        expected: expected_kind_name(expected),
834                        got: expected_kind_name(actual.kind),
835                    });
836                }
837
838                match plan.kind {
839                    SplitImmediateKind::AdrLike {
840                        immlo_field_idx,
841                        immhi_field_idx,
842                        scale,
843                    } => {
844                        let encoded = scale_immediate(first_field.name, actual.value, scale)?;
845                        if immlo_field_idx >= values.len() || immhi_field_idx >= values.len() {
846                            return Err(EncodeError::OperandCountMismatch);
847                        }
848                        values[immlo_field_idx] = encoded & 0b11;
849                        values[immhi_field_idx] = encoded >> 2;
850                        filled_mask |= 1u64 << immlo_field_idx;
851                        filled_mask |= 1u64 << immhi_field_idx;
852                    }
853                    SplitImmediateKind::BitIndex6 {
854                        b5_field_idx,
855                        b40_field_idx,
856                    } => {
857                        if b5_field_idx >= values.len() || b40_field_idx >= values.len() {
858                            return Err(EncodeError::OperandCountMismatch);
859                        }
860                        values[b5_field_idx] = actual.value >> 5;
861                        values[b40_field_idx] = actual.value & 0x1f;
862                        filled_mask |= 1u64 << b5_field_idx;
863                        filled_mask |= 1u64 << b40_field_idx;
864                    }
865                    SplitImmediateKind::LogicalImmRs {
866                        immr_field_idx,
867                        imms_field_idx,
868                        reg_size,
869                    } => {
870                        if immr_field_idx >= values.len() || imms_field_idx >= values.len() {
871                            return Err(EncodeError::OperandCountMismatch);
872                        }
873                        let Some((_, immr, imms)) =
874                            encode_logical_immediate_nrs(actual.value, reg_size)
875                        else {
876                            return Err(EncodeError::OperandOutOfRange {
877                                field: first_field.name,
878                                value: actual.value,
879                                width: reg_size,
880                                signed: false,
881                            });
882                        };
883                        values[immr_field_idx] = immr;
884                        values[imms_field_idx] = imms;
885                        filled_mask |= 1u64 << immr_field_idx;
886                        filled_mask |= 1u64 << imms_field_idx;
887                    }
888                    SplitImmediateKind::LogicalImmNrs {
889                        n_field_idx,
890                        immr_field_idx,
891                        imms_field_idx,
892                        reg_size,
893                    } => {
894                        if n_field_idx >= values.len()
895                            || immr_field_idx >= values.len()
896                            || imms_field_idx >= values.len()
897                        {
898                            return Err(EncodeError::OperandCountMismatch);
899                        }
900                        let Some((n, immr, imms)) =
901                            encode_logical_immediate_nrs(actual.value, reg_size)
902                        else {
903                            return Err(EncodeError::OperandOutOfRange {
904                                field: first_field.name,
905                                value: actual.value,
906                                width: reg_size,
907                                signed: false,
908                            });
909                        };
910                        values[n_field_idx] = n;
911                        values[immr_field_idx] = immr;
912                        values[imms_field_idx] = imms;
913                        filled_mask |= 1u64 << n_field_idx;
914                        filled_mask |= 1u64 << immr_field_idx;
915                        filled_mask |= 1u64 << imms_field_idx;
916                    }
917                }
918                input_idx += 1;
919                slot += 1;
920                continue;
921            }
922
923            if slot > plan.first_slot && slot <= plan.second_slot {
924                slot += 1;
925                continue;
926            }
927        }
928
929        if input_idx >= selected_len {
930            return Err(EncodeError::OperandCountMismatch);
931        }
932
933        let field_idx = spec.operand_order[slot] as usize;
934        if field_idx >= spec.fields.len() || field_idx >= values.len() {
935            return Err(EncodeError::OperandCountMismatch);
936        }
937
938        let field = spec.fields[field_idx];
939        let expected = spec.operand_kinds[slot];
940        let actual = selected_flat[input_idx];
941        if !kind_matches_for_slot(spec, slot, expected, actual.kind) {
942            return Err(EncodeError::InvalidOperandKind {
943                field: field.name,
944                expected: expected_kind_name(expected),
945                got: expected_kind_name(actual.kind),
946            });
947        }
948        values[field_idx] = actual.value;
949        filled_mask |= 1u64 << field_idx;
950        input_idx += 1;
951        slot += 1;
952    }
953
954    if input_idx != selected_len {
955        return Err(EncodeError::OperandCountMismatch);
956    }
957
958    for implicit in spec.implicit_defaults {
959        let idx = implicit.field_index as usize;
960        if idx < spec.fields.len() && idx < values.len() && ((filled_mask >> idx) & 1) == 0 {
961            values[idx] = implicit.value;
962            filled_mask |= 1u64 << idx;
963        }
964    }
965
966    let mut args = [0i64; 64];
967    for idx in 0..spec.fields.len() {
968        if idx >= values.len() || ((filled_mask >> idx) & 1) == 0 {
969            return Err(EncodeError::OperandCountMismatch);
970        }
971        let raw = values[idx];
972        args[idx] = normalize_field_value(spec, spec.fields[idx], idx, raw)?;
973    }
974
975    encode_by_spec(spec, &args[..spec.fields.len()])
976}
977
978fn spec_has_arrangement_lane(spec: &EncodingSpec) -> bool {
979    spec.operand_kinds.iter().any(|kind| {
980        matches!(
981            kind,
982            OperandConstraintKind::Arrangement | OperandConstraintKind::Lane
983        )
984    })
985}
986
987fn flat_has_arrangement_lane(flat: &[FlatOperand], len: usize) -> bool {
988    flat[..len].iter().any(|operand| {
989        matches!(
990            operand.kind,
991            OperandConstraintKind::Arrangement | OperandConstraintKind::Lane
992        )
993    })
994}
995
996fn reorder_flat_arrangement_lane_for_expected(
997    spec: &EncodingSpec,
998    flat: &[FlatOperand],
999    len: usize,
1000    out: &mut [FlatOperand; 64],
1001) -> Option<usize> {
1002    let mut expected = [OperandConstraintKind::Immediate; 64];
1003    let expected_len = expected_user_operand_kinds(spec, &mut expected);
1004    if expected_len != len {
1005        return None;
1006    }
1007
1008    let mut non_arr = [FlatOperand {
1009        kind: OperandConstraintKind::Immediate,
1010        value: 0,
1011        optional: false,
1012    }; 64];
1013    let mut arr = [FlatOperand {
1014        kind: OperandConstraintKind::Immediate,
1015        value: 0,
1016        optional: false,
1017    }; 64];
1018    let mut non_arr_len = 0usize;
1019    let mut arr_len = 0usize;
1020
1021    for operand in &flat[..len] {
1022        if matches!(
1023            operand.kind,
1024            OperandConstraintKind::Arrangement | OperandConstraintKind::Lane
1025        ) {
1026            arr[arr_len] = *operand;
1027            arr_len += 1;
1028        } else {
1029            non_arr[non_arr_len] = *operand;
1030            non_arr_len += 1;
1031        }
1032    }
1033
1034    let mut non_idx = 0usize;
1035    let mut arr_idx = 0usize;
1036    let mut out_len = 0usize;
1037    for expected_kind in expected[..expected_len].iter().copied() {
1038        if matches!(
1039            expected_kind,
1040            OperandConstraintKind::Arrangement | OperandConstraintKind::Lane
1041        ) {
1042            if arr_idx >= arr_len {
1043                return None;
1044            }
1045            if arr[arr_idx].kind != expected_kind {
1046                let Some(rel_idx) = arr[arr_idx..arr_len]
1047                    .iter()
1048                    .position(|operand| operand.kind == expected_kind)
1049                else {
1050                    return None;
1051                };
1052                arr.swap(arr_idx, arr_idx + rel_idx);
1053            }
1054            out[out_len] = arr[arr_idx];
1055            arr_idx += 1;
1056            out_len += 1;
1057        } else {
1058            if non_idx >= non_arr_len {
1059                return None;
1060            }
1061            out[out_len] = non_arr[non_idx];
1062            non_idx += 1;
1063            out_len += 1;
1064        }
1065    }
1066
1067    if non_idx != non_arr_len || arr_idx != arr_len {
1068        return None;
1069    }
1070    Some(out_len)
1071}
1072
1073#[inline]
1074fn encode_flat(
1075    spec: &EncodingSpec,
1076    selected_flat: &[FlatOperand],
1077    selected_len: usize,
1078    split_plan: Option<SplitImmediatePlan>,
1079) -> Result<InstructionCode, EncodeError> {
1080    // Fast path: direct ordered encoding. If that fails and both expected/actual
1081    // streams contain arrangement/lane tokens, retry with a stable reorder that
1082    // aligns trailing arrangement/lane decorations to the expected slots.
1083    let direct = encode_flat_ordered(spec, selected_flat, selected_len, split_plan);
1084    if direct.is_ok()
1085        || !spec_has_arrangement_lane(spec)
1086        || !flat_has_arrangement_lane(selected_flat, selected_len)
1087    {
1088        return direct;
1089    }
1090
1091    let mut reordered = [FlatOperand {
1092        kind: OperandConstraintKind::Immediate,
1093        value: 0,
1094        optional: false,
1095    }; 64];
1096    let Some(reordered_len) = reorder_flat_arrangement_lane_for_expected(
1097        spec,
1098        selected_flat,
1099        selected_len,
1100        &mut reordered,
1101    ) else {
1102        return direct;
1103    };
1104    if reordered_len != selected_len || reordered[..selected_len] == selected_flat[..selected_len] {
1105        return direct;
1106    }
1107
1108    match encode_flat_ordered(spec, &reordered, reordered_len, split_plan) {
1109        Ok(code) => Ok(code),
1110        Err(_) => direct,
1111    }
1112}
1113
1114fn diagnostic_priority(err: &EncodeError) -> u8 {
1115    match err {
1116        EncodeError::OperandOutOfRange { .. } | EncodeError::ImmediateNotAligned { .. } => 0,
1117        EncodeError::InvalidOperandKind { .. } => 1,
1118        EncodeError::OperandCountMismatch | EncodeError::OperandCountRange { .. } => 2,
1119        EncodeError::NoMatchingVariant | EncodeError::NoMatchingVariantHint { .. } => 3,
1120        EncodeError::AmbiguousVariant | EncodeError::UnknownMnemonic => 4,
1121    }
1122}
1123
1124fn prefer_diagnostic_error(lhs: EncodeError, rhs: EncodeError) -> EncodeError {
1125    if diagnostic_priority(&rhs) < diagnostic_priority(&lhs) {
1126        rhs
1127    } else {
1128        lhs
1129    }
1130}
1131
1132#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
1133struct CandidateScore {
1134    fixed_bits: u16,
1135    kind_specificity: u16,
1136    immediate_narrowness: u16,
1137    explicit_operands: u8,
1138    implicit_penalty: u8,
1139}
1140
1141#[inline]
1142fn kind_specificity(kind: OperandConstraintKind) -> u16 {
1143    match kind {
1144        OperandConstraintKind::GprRegister => 3,
1145        OperandConstraintKind::Gpr32Register
1146        | OperandConstraintKind::Gpr64Register
1147        | OperandConstraintKind::SimdRegister
1148        | OperandConstraintKind::SveZRegister
1149        | OperandConstraintKind::PredicateRegister => 4,
1150        OperandConstraintKind::Immediate => 1,
1151        OperandConstraintKind::Condition
1152        | OperandConstraintKind::ShiftKind
1153        | OperandConstraintKind::ExtendKind
1154        | OperandConstraintKind::SysRegPart
1155        | OperandConstraintKind::Arrangement
1156        | OperandConstraintKind::Lane => 2,
1157    }
1158}
1159
1160fn candidate_score(spec: &EncodingSpec) -> CandidateScore {
1161    // Tie-break candidates deterministically so "best" means:
1162    // - more fixed opcode bits,
1163    // - more specific operand kinds,
1164    // - narrower immediate fields,
1165    // - fewer implicit defaults.
1166    let fixed_bits = spec.opcode_mask.count_ones().min(u32::from(u16::MAX)) as u16;
1167
1168    let mut kind_specificity_sum = 0u16;
1169    let mut immediate_narrowness = 0u16;
1170    for (slot, kind) in spec.operand_kinds.iter().copied().enumerate() {
1171        kind_specificity_sum = kind_specificity_sum.saturating_add(kind_specificity(kind));
1172        if kind == OperandConstraintKind::Immediate {
1173            let Some(field_idx) = spec.operand_order.get(slot).copied() else {
1174                continue;
1175            };
1176            let field_idx = field_idx as usize;
1177            if let Some(field) = spec.fields.get(field_idx) {
1178                immediate_narrowness =
1179                    immediate_narrowness.saturating_add((64u16).saturating_sub(field.width as u16));
1180            }
1181        }
1182    }
1183
1184    let explicit_operands = spec
1185        .operand_order
1186        .len()
1187        .saturating_sub(spec.implicit_defaults.len())
1188        .min(usize::from(u8::MAX)) as u8;
1189
1190    let implicit_penalty = (u8::MAX as usize)
1191        .saturating_sub(spec.implicit_defaults.len().min(usize::from(u8::MAX)))
1192        as u8;
1193
1194    CandidateScore {
1195        fixed_bits,
1196        kind_specificity: kind_specificity_sum,
1197        immediate_narrowness,
1198        explicit_operands,
1199        implicit_penalty,
1200    }
1201}
1202
1203#[inline]
1204fn should_replace_selected_candidate(
1205    selected_score: CandidateScore,
1206    selected_mask: u32,
1207    selected_opcode: u32,
1208    next_score: CandidateScore,
1209    next_mask: u32,
1210    next_opcode: u32,
1211) -> bool {
1212    match next_score.cmp(&selected_score) {
1213        core::cmp::Ordering::Greater => true,
1214        core::cmp::Ordering::Less => false,
1215        // Deterministic tie-break for equivalent-shape duplicate variants.
1216        core::cmp::Ordering::Equal => (next_mask, next_opcode) > (selected_mask, selected_opcode),
1217    }
1218}
1219
1220#[inline]
1221fn encode_by_spec_from_flattened(
1222    spec: &EncodingSpec,
1223    flat: &[FlatOperand],
1224    flat_len: usize,
1225) -> Result<InstructionCode, EncodeError> {
1226    if spec.fields.len() > 64 {
1227        return Err(EncodeError::OperandCountMismatch);
1228    }
1229    let split_before_materialize = if spec_maybe_split_immediate(spec) {
1230        if let Some(plan) = spec_split_immediate_plan(spec) {
1231            let merge = split_plan_input_span(plan).saturating_sub(1);
1232            if flat_len + merge == spec.operand_order.len() {
1233                Some(plan)
1234            } else {
1235                None
1236            }
1237        } else {
1238            None
1239        }
1240    } else {
1241        None
1242    };
1243    let expected_flat_len = if let Some(plan) = split_before_materialize {
1244        let merge = split_plan_input_span(plan).saturating_sub(1);
1245        spec.operand_order
1246            .len()
1247            .checked_sub(merge)
1248            .ok_or(EncodeError::OperandCountMismatch)?
1249    } else {
1250        spec.operand_order.len()
1251    };
1252    let mut selected_flat = [FlatOperand {
1253        kind: OperandConstraintKind::Immediate,
1254        value: 0,
1255        optional: false,
1256    }; 64];
1257    let selected_len = materialize_flat_for_expected_len(
1258        expected_flat_len,
1259        &flat[..flat_len],
1260        &mut selected_flat,
1261    )?;
1262    encode_flat(spec, &selected_flat, selected_len, split_before_materialize)
1263}
1264
1265/// Returns [`EncodeError`] when operand count, kind, or ranges are invalid.
1266#[must_use]
1267pub fn encode_by_spec_operands(
1268    spec: &EncodingSpec,
1269    operands: &[Operand],
1270) -> Result<InstructionCode, EncodeError> {
1271    if spec.fields.len() > 64 {
1272        return Err(EncodeError::OperandCountMismatch);
1273    }
1274    if spec.operand_order.len() != spec.operand_kinds.len() {
1275        return Err(EncodeError::OperandCountMismatch);
1276    }
1277
1278    let mut flat = [FlatOperand {
1279        kind: OperandConstraintKind::Immediate,
1280        value: 0,
1281        optional: false,
1282    }; 64];
1283    let flat_len = flatten_operands(operands, &mut flat)?;
1284    encode_by_spec_from_flattened(spec, &flat, flat_len)
1285}
1286
1287/// Encodes one mnemonic using a generated shortlist of candidate variant indices.
1288///
1289/// The shortlist is expected to contain only variants for the same mnemonic.
1290/// This function keeps strict validation and ambiguity handling while avoiding
1291/// a full linear scan over all mnemonic variants.
1292///
1293/// # Errors
1294///
1295/// Returns [`EncodeError`] when no candidate can be encoded unambiguously.
1296#[must_use]
1297pub fn encode_candidates(
1298    specs: &[EncodingSpec],
1299    candidate_indices: &[u16],
1300    operands: &[Operand],
1301) -> Result<InstructionCode, EncodeError> {
1302    if candidate_indices.is_empty() {
1303        return Err(EncodeError::NoMatchingVariant);
1304    }
1305
1306    let mut flat_operands = [FlatOperand {
1307        kind: OperandConstraintKind::Immediate,
1308        value: 0,
1309        optional: false,
1310    }; 64];
1311    let flat_len = flatten_operands(operands, &mut flat_operands)?;
1312
1313    let mut best_detail_error: Option<EncodeError> = None;
1314    let mut selected: Option<(InstructionCode, CandidateScore, u32, u32)> = None;
1315    let mut saw_count_mismatch = false;
1316    let mut saw_shape_mismatch = false;
1317
1318    for candidate_index in candidate_indices.iter().copied() {
1319        let Some(spec) = specs.get(usize::from(candidate_index)) else {
1320            continue;
1321        };
1322        if !spec_matches_memory_addressing(spec, operands) {
1323            saw_shape_mismatch = true;
1324            continue;
1325        }
1326
1327        match encode_by_spec_from_flattened(spec, &flat_operands, flat_len) {
1328            Ok(code) => {
1329                let score = candidate_score(spec);
1330                if let Some((_, prev_score, prev_mask, prev_opcode)) = selected {
1331                    if should_replace_selected_candidate(
1332                        prev_score,
1333                        prev_mask,
1334                        prev_opcode,
1335                        score,
1336                        spec.opcode_mask,
1337                        spec.opcode,
1338                    ) {
1339                        selected = Some((code, score, spec.opcode_mask, spec.opcode));
1340                    }
1341                    continue;
1342                }
1343                selected = Some((code, score, spec.opcode_mask, spec.opcode));
1344            }
1345            Err(EncodeError::OperandCountMismatch) => {
1346                saw_count_mismatch = true;
1347            }
1348            Err(
1349                err @ (EncodeError::OperandOutOfRange { .. }
1350                | EncodeError::ImmediateNotAligned { .. }
1351                | EncodeError::InvalidOperandKind { .. }
1352                | EncodeError::NoMatchingVariant
1353                | EncodeError::NoMatchingVariantHint { .. }),
1354            ) => {
1355                saw_shape_mismatch = true;
1356                best_detail_error = Some(match best_detail_error {
1357                    Some(current) => prefer_diagnostic_error(current, err),
1358                    None => err,
1359                });
1360            }
1361            Err(err) => return Err(err),
1362        }
1363    }
1364
1365    if let Some((code, _, _, _)) = selected {
1366        return Ok(code);
1367    }
1368    if let Some(detail) = best_detail_error {
1369        return Err(detail);
1370    }
1371    if saw_shape_mismatch || saw_count_mismatch {
1372        return Err(EncodeError::NoMatchingVariant);
1373    }
1374    Err(EncodeError::NoMatchingVariant)
1375}
1376
1377/// Computes packed operand-shape keys for all materialized flattenings of `operands`.
1378///
1379/// Some operands can contribute optional flattened fields (for example bare `[base]`
1380/// memory offsets). This function returns one key per valid materialized shape.
1381///
1382/// # Errors
1383///
1384/// Returns [`EncodeError`] when flattening fails or `out` is too small.
1385#[must_use]
1386pub fn operand_shape_keys(
1387    operands: &[Operand],
1388    out: &mut [OperandShapeKey],
1389) -> Result<usize, EncodeError> {
1390    let memory_shape_code = memory_shape_code_from_operands(operands);
1391    let mut flat = [FlatOperand {
1392        kind: OperandConstraintKind::Immediate,
1393        value: 0,
1394        optional: false,
1395    }; 64];
1396    let flat_len = flatten_operands(operands, &mut flat)?;
1397    let mut required_len = 0usize;
1398    let mut optional_len = 0usize;
1399    for operand in &flat[..flat_len] {
1400        if operand.optional {
1401            optional_len += 1;
1402        } else {
1403            required_len += 1;
1404        }
1405    }
1406
1407    let mut selected = [FlatOperand {
1408        kind: OperandConstraintKind::Immediate,
1409        value: 0,
1410        optional: false,
1411    }; 64];
1412    let mut out_len = 0usize;
1413    let mut kind_codes = [0u8; 64];
1414    for expected_len in required_len..=required_len + optional_len {
1415        let selected_len =
1416            materialize_flat_for_expected_len(expected_len, &flat[..flat_len], &mut selected)?;
1417        for (idx, operand) in selected[..selected_len].iter().enumerate() {
1418            kind_codes[idx] = operand_shape_code(operand.kind);
1419        }
1420
1421        let mut key_len = selected_len;
1422        if memory_shape_code.is_some() {
1423            key_len = key_len.saturating_add(1);
1424        }
1425        if key_len > 30 {
1426            continue;
1427        }
1428        let mut key = key_len as OperandShapeKey;
1429        for (slot, code) in kind_codes[..selected_len].iter().copied().enumerate() {
1430            let shift = 8 + (slot * 4);
1431            key |= OperandShapeKey::from(code) << shift;
1432        }
1433        if let Some(memory_shape_code) = memory_shape_code {
1434            let shift = 8 + (selected_len * 4);
1435            key |= OperandShapeKey::from(memory_shape_code) << shift;
1436        }
1437
1438        if out[..out_len].contains(&key) {
1439            continue;
1440        }
1441        if out_len >= out.len() {
1442            return Err(EncodeError::OperandCountMismatch);
1443        }
1444        out[out_len] = key;
1445        out_len += 1;
1446    }
1447    Ok(out_len)
1448}
1449
1450/// Encodes one instruction variant by spec.
1451///
1452/// # Errors
1453///
1454/// Returns [`EncodeError`] when operand count or operand ranges are invalid.
1455#[must_use]
1456pub fn encode_by_spec(spec: &EncodingSpec, args: &[i64]) -> Result<InstructionCode, EncodeError> {
1457    if spec.fields.len() != args.len() {
1458        return Err(EncodeError::OperandCountMismatch);
1459    }
1460
1461    let mut word = spec.opcode;
1462    for (field, arg) in spec.fields.iter().copied().zip(args.iter().copied()) {
1463        let encoded = encode_field(field, arg)?;
1464        word |= encoded;
1465    }
1466
1467    Ok(InstructionCode::from_u32(word))
1468}
1469
1470fn mnemonic_has_memory_immediate_variants(specs: &[EncodingSpec], mnemonic: &str) -> bool {
1471    specs.iter().any(|spec| {
1472        spec.mnemonic == mnemonic
1473            && spec.memory_addressing != MemoryAddressingConstraintSpec::None
1474            && (spec_has_field(spec, "imm12")
1475                || spec_has_field(spec, "imm9")
1476                || spec_has_field(spec, "imm7"))
1477            && spec_has_field(spec, "rn")
1478            && spec_has_field(spec, "rt")
1479    })
1480}
1481
1482fn operands_have_bare_memory_offset(operands: &[Operand]) -> bool {
1483    operands.iter().any(|operand| {
1484        matches!(
1485            operand,
1486            Operand::Memory(MemoryOperand {
1487                offset: MemoryOffset::None,
1488                addressing: AddressingMode::Offset,
1489                ..
1490            })
1491        )
1492    })
1493}
1494
1495fn operands_have_mixed_gpr_width(operands: &[Operand]) -> bool {
1496    let mut saw_w = false;
1497    let mut saw_x = false;
1498    for operand in operands {
1499        if let Operand::Register(reg) = operand {
1500            match reg.class {
1501                RegClass::W | RegClass::Wsp => saw_w = true,
1502                RegClass::X | RegClass::Xsp => saw_x = true,
1503                _ => {}
1504            }
1505        }
1506    }
1507    saw_w && saw_x
1508}
1509
1510fn expected_user_operand_kinds(
1511    spec: &EncodingSpec,
1512    out: &mut [OperandConstraintKind; 64],
1513) -> usize {
1514    let split = spec_split_immediate_plan(spec);
1515    let mut out_len = 0usize;
1516    let mut slot = 0usize;
1517    while slot < spec.operand_kinds.len() {
1518        if let Some(plan) = split {
1519            if slot == plan.first_slot {
1520                out[out_len] = OperandConstraintKind::Immediate;
1521                out_len += 1;
1522                slot = slot.saturating_add(split_plan_input_span(plan));
1523                continue;
1524            }
1525        }
1526
1527        out[out_len] = spec.operand_kinds[slot];
1528        out_len += 1;
1529        slot += 1;
1530    }
1531    out_len
1532}
1533
1534fn infer_no_matching_hint(
1535    specs: &[EncodingSpec],
1536    mnemonic: &str,
1537    operands: &[Operand],
1538) -> Option<CoreNoMatchHint> {
1539    // Keep this heuristic-only and data-driven by operand forms/fields.
1540    // No opcode-name based acceptance logic is introduced here.
1541    if operands_have_bare_memory_offset(operands)
1542        && mnemonic_has_memory_immediate_variants(specs, mnemonic)
1543    {
1544        return Some(CoreNoMatchHint::MemoryMayRequireExplicitZeroOffset);
1545    }
1546
1547    if operands_have_mixed_gpr_width(operands) {
1548        return Some(CoreNoMatchHint::RegisterWidthMismatch);
1549    }
1550
1551    None
1552}
1553
1554fn operand_shape_tag(operand: &Operand) -> OperandShapeTag {
1555    match operand {
1556        Operand::Register(reg) => match reg.class {
1557            RegClass::W | RegClass::Wsp => OperandShapeTag::Gpr32,
1558            RegClass::X | RegClass::Xsp => OperandShapeTag::Gpr64,
1559            RegClass::V | RegClass::B | RegClass::H | RegClass::S | RegClass::D | RegClass::Q => {
1560                OperandShapeTag::Simd
1561            }
1562            RegClass::Z => OperandShapeTag::SveZ,
1563            RegClass::P => OperandShapeTag::Predicate,
1564        },
1565        Operand::Immediate(_) => OperandShapeTag::Immediate,
1566        Operand::Memory(_) => OperandShapeTag::Memory,
1567        Operand::Shift(_) => OperandShapeTag::Shift,
1568        Operand::Extend(_) => OperandShapeTag::Extend,
1569        Operand::Condition(_) => OperandShapeTag::Condition,
1570        Operand::RegisterList(_) => OperandShapeTag::RegisterList,
1571        Operand::SysReg(_) => OperandShapeTag::SysReg,
1572    }
1573}
1574
1575fn build_input_shape_signature(operands: &[Operand]) -> OperandShapeSignature {
1576    let mut slots = Vec::with_capacity(operands.len());
1577    for operand in operands {
1578        slots.push(operand_shape_tag(operand));
1579    }
1580    OperandShapeSignature {
1581        slots: slots.into_boxed_slice(),
1582    }
1583}
1584
1585fn expected_signature_from_spec(spec: &EncodingSpec) -> OperandConstraintSignature {
1586    let mut kinds = [OperandConstraintKind::Immediate; 64];
1587    let len = expected_user_operand_kinds(spec, &mut kinds);
1588    OperandConstraintSignature {
1589        slots: kinds[..len].to_vec().into_boxed_slice(),
1590    }
1591}
1592
1593fn build_shape_mismatch_hint(
1594    specs: &[EncodingSpec],
1595    mnemonic: &str,
1596    operands: &[Operand],
1597) -> Option<CoreNoMatchHint> {
1598    let mut expected = Vec::<OperandConstraintSignature>::new();
1599    for spec in specs {
1600        if spec.mnemonic != mnemonic {
1601            continue;
1602        }
1603        let signature = expected_signature_from_spec(spec);
1604        if expected.iter().any(|existing| existing == &signature) {
1605            continue;
1606        }
1607        expected.push(signature);
1608    }
1609
1610    if expected.is_empty() {
1611        return None;
1612    }
1613
1614    expected.sort_by(|lhs, rhs| lhs.slots.len().cmp(&rhs.slots.len()));
1615    let shown = expected.len().min(4);
1616    let expected_additional = expected
1617        .len()
1618        .saturating_sub(shown)
1619        .min(usize::from(u16::MAX)) as u16;
1620
1621    Some(CoreNoMatchHint::ShapeMismatch {
1622        expected: expected
1623            .into_iter()
1624            .take(shown)
1625            .collect::<Vec<_>>()
1626            .into_boxed_slice(),
1627        expected_additional,
1628        got: build_input_shape_signature(operands),
1629    })
1630}
1631
1632fn operand_hint_kind(operand: &Operand) -> Option<OperandConstraintKind> {
1633    match operand {
1634        Operand::Register(reg) => Some(reg_constraint_for_class(reg.class)),
1635        Operand::Immediate(_) => Some(OperandConstraintKind::Immediate),
1636        Operand::Condition(_) => Some(OperandConstraintKind::Condition),
1637        Operand::Shift(_) => Some(OperandConstraintKind::ShiftKind),
1638        Operand::Extend(_) => Some(OperandConstraintKind::ExtendKind),
1639        Operand::Memory(_) | Operand::RegisterList(_) | Operand::SysReg(_) => None,
1640    }
1641}
1642
1643fn push_unique_expected_kind(
1644    out: &mut Vec<OperandConstraintKind>,
1645    expected: OperandConstraintKind,
1646) {
1647    if !out.contains(&expected) {
1648        out.push(expected);
1649    }
1650}
1651
1652fn build_operand_delta_hint(
1653    specs: &[EncodingSpec],
1654    mnemonic: &str,
1655    operands: &[Operand],
1656) -> Option<CoreNoMatchHint> {
1657    // Produce the earliest actionable delta:
1658    // - missing operand at a given slot, or
1659    // - single-slot kind mismatch.
1660    let actual = operands
1661        .iter()
1662        .map(operand_hint_kind)
1663        .collect::<Vec<Option<OperandConstraintKind>>>();
1664
1665    let mut missing_slot: Option<usize> = None;
1666    let mut missing_expected = Vec::<OperandConstraintKind>::new();
1667    let mut mismatch_slot: Option<usize> = None;
1668    let mut mismatch_expected = Vec::<OperandConstraintKind>::new();
1669
1670    for spec in specs {
1671        if spec.mnemonic != mnemonic {
1672            continue;
1673        }
1674
1675        let mut expected = [OperandConstraintKind::Immediate; 64];
1676        let expected_len = expected_user_operand_kinds(spec, &mut expected);
1677
1678        if operands.len() + 1 == expected_len {
1679            let mut compatible_prefix = true;
1680            for idx in 0..operands.len() {
1681                let Some(actual_kind) = actual[idx] else {
1682                    compatible_prefix = false;
1683                    break;
1684                };
1685                if !kind_matches(expected[idx], actual_kind) {
1686                    compatible_prefix = false;
1687                    break;
1688                }
1689            }
1690
1691            if compatible_prefix {
1692                let idx = expected_len - 1;
1693                if match missing_slot {
1694                    None => true,
1695                    Some(current) => idx < current,
1696                } {
1697                    missing_slot = Some(idx);
1698                    missing_expected.clear();
1699                }
1700                if missing_slot == Some(idx) {
1701                    push_unique_expected_kind(&mut missing_expected, expected[idx]);
1702                }
1703            }
1704        }
1705
1706        if operands.len() == expected_len {
1707            let mut mismatch_idx: Option<usize> = None;
1708            let mut valid = true;
1709            for idx in 0..operands.len() {
1710                let Some(actual_kind) = actual[idx] else {
1711                    valid = false;
1712                    break;
1713                };
1714                if !kind_matches(expected[idx], actual_kind) {
1715                    if mismatch_idx.is_some() {
1716                        valid = false;
1717                        break;
1718                    }
1719                    mismatch_idx = Some(idx);
1720                }
1721            }
1722
1723            if valid && let Some(idx) = mismatch_idx {
1724                if match mismatch_slot {
1725                    None => true,
1726                    Some(current) => idx < current,
1727                } {
1728                    mismatch_slot = Some(idx);
1729                    mismatch_expected.clear();
1730                }
1731                if mismatch_slot == Some(idx) {
1732                    push_unique_expected_kind(&mut mismatch_expected, expected[idx]);
1733                }
1734            }
1735        }
1736    }
1737
1738    if let Some(idx) = missing_slot
1739        && !missing_expected.is_empty()
1740    {
1741        return Some(CoreNoMatchHint::OperandMissing {
1742            index: (idx + 1).min(usize::from(u8::MAX)) as u8,
1743            expected: missing_expected.into_boxed_slice(),
1744        });
1745    }
1746
1747    if let Some(idx) = mismatch_slot
1748        && !mismatch_expected.is_empty()
1749        && idx < operands.len()
1750    {
1751        return Some(CoreNoMatchHint::OperandKindMismatch {
1752            index: (idx + 1).min(usize::from(u8::MAX)) as u8,
1753            expected: mismatch_expected.into_boxed_slice(),
1754            got: operand_shape_tag(&operands[idx]),
1755        });
1756    }
1757
1758    None
1759}
1760
1761fn spec_expected_user_operand_count(spec: &EncodingSpec) -> usize {
1762    if let Some(plan) = spec_split_immediate_plan(spec) {
1763        let merge = split_plan_input_span(plan).saturating_sub(1);
1764        spec.operand_order.len().saturating_sub(merge)
1765    } else {
1766        spec.operand_order.len()
1767    }
1768}
1769
1770/// Selects a variant by mnemonic and typed operands and encodes it.
1771///
1772/// # Errors
1773///
1774/// Returns [`EncodeError`] if no variant can be selected and encoded unambiguously.
1775#[must_use]
1776pub fn encode(
1777    specs: &[EncodingSpec],
1778    mnemonic: &str,
1779    operands: &[Operand],
1780) -> Result<InstructionCode, EncodeError> {
1781    let mut saw_mnemonic = false;
1782    let mut saw_count_mismatch = false;
1783    let mut saw_shape_mismatch = false;
1784    let mut best_detail_error: Option<EncodeError> = None;
1785    let mut selected: Option<(InstructionCode, CandidateScore, u32, u32)> = None;
1786    let mut flat_operands = [FlatOperand {
1787        kind: OperandConstraintKind::Immediate,
1788        value: 0,
1789        optional: false,
1790    }; 64];
1791    let mut flat_len = 0usize;
1792    let mut flat_attempted = false;
1793    let mut flat_error: Option<EncodeError> = None;
1794    let mut expected_min_count = usize::MAX;
1795    let mut expected_max_count = 0usize;
1796
1797    for spec in specs {
1798        if spec.mnemonic != mnemonic {
1799            continue;
1800        }
1801        saw_mnemonic = true;
1802        let expected_count = spec_expected_user_operand_count(spec);
1803        if expected_count < expected_min_count {
1804            expected_min_count = expected_count;
1805        }
1806        if expected_count > expected_max_count {
1807            expected_max_count = expected_count;
1808        }
1809
1810        if !spec_matches_memory_addressing(spec, operands) {
1811            saw_shape_mismatch = true;
1812            continue;
1813        }
1814
1815        if !flat_attempted {
1816            flat_attempted = true;
1817            match flatten_operands(operands, &mut flat_operands) {
1818                Ok(len) => flat_len = len,
1819                Err(err) => flat_error = Some(err),
1820            }
1821        }
1822
1823        let encode_result = if let Some(err) = flat_error.clone() {
1824            Err(err)
1825        } else {
1826            encode_by_spec_from_flattened(spec, &flat_operands, flat_len)
1827        };
1828
1829        match encode_result {
1830            Ok(code) => {
1831                let score = candidate_score(spec);
1832                if let Some((_, prev_score, prev_mask, prev_opcode)) = selected {
1833                    if should_replace_selected_candidate(
1834                        prev_score,
1835                        prev_mask,
1836                        prev_opcode,
1837                        score,
1838                        spec.opcode_mask,
1839                        spec.opcode,
1840                    ) {
1841                        selected = Some((code, score, spec.opcode_mask, spec.opcode));
1842                    }
1843                    continue;
1844                }
1845                selected = Some((code, score, spec.opcode_mask, spec.opcode));
1846            }
1847            Err(EncodeError::OperandCountMismatch) => {
1848                saw_count_mismatch = true;
1849            }
1850            Err(
1851                err @ (EncodeError::OperandOutOfRange { .. }
1852                | EncodeError::ImmediateNotAligned { .. }
1853                | EncodeError::InvalidOperandKind { .. }
1854                | EncodeError::NoMatchingVariant
1855                | EncodeError::NoMatchingVariantHint { .. }),
1856            ) => {
1857                saw_shape_mismatch = true;
1858                best_detail_error = Some(match best_detail_error {
1859                    Some(current) => prefer_diagnostic_error(current, err),
1860                    None => err,
1861                });
1862            }
1863            Err(err) => return Err(err),
1864        }
1865    }
1866
1867    if let Some((code, _, _, _)) = selected {
1868        return Ok(code);
1869    }
1870    if !saw_mnemonic {
1871        return Err(EncodeError::UnknownMnemonic);
1872    }
1873    if let Some(detail) = best_detail_error {
1874        return Err(detail);
1875    }
1876    if saw_shape_mismatch {
1877        if let Some(hint) = infer_no_matching_hint(specs, mnemonic, operands) {
1878            return Err(EncodeError::NoMatchingVariantHint {
1879                hint: NoMatchingHint::Core(hint),
1880            });
1881        }
1882    }
1883    if saw_shape_mismatch {
1884        if let Some(hint) = build_operand_delta_hint(specs, mnemonic, operands) {
1885            return Err(EncodeError::NoMatchingVariantHint {
1886                hint: NoMatchingHint::Core(hint),
1887            });
1888        }
1889        if let Some(hint) = build_shape_mismatch_hint(specs, mnemonic, operands) {
1890            return Err(EncodeError::NoMatchingVariantHint {
1891                hint: NoMatchingHint::Core(hint),
1892            });
1893        }
1894        return Err(EncodeError::NoMatchingVariant);
1895    }
1896    if saw_count_mismatch {
1897        if expected_min_count != usize::MAX {
1898            let min = expected_min_count.min(usize::from(u8::MAX)) as u8;
1899            let max = expected_max_count.min(usize::from(u8::MAX)) as u8;
1900            let got = operands.len().min(usize::from(u8::MAX)) as u8;
1901            return Err(EncodeError::OperandCountRange { min, max, got });
1902        }
1903        return Err(EncodeError::OperandCountMismatch);
1904    }
1905
1906    Err(EncodeError::NoMatchingVariant)
1907}
1908
1909#[cfg(test)]
1910mod tests {
1911    use super::*;
1912    use alloc::vec;
1913
1914    const ADD_FIELDS: &[BitFieldSpec] = &[
1915        BitFieldSpec {
1916            name: "sh",
1917            lsb: 22,
1918            width: 1,
1919            signed: false,
1920        },
1921        BitFieldSpec {
1922            name: "imm12",
1923            lsb: 10,
1924            width: 12,
1925            signed: false,
1926        },
1927        BitFieldSpec {
1928            name: "Rn",
1929            lsb: 5,
1930            width: 5,
1931            signed: false,
1932        },
1933        BitFieldSpec {
1934            name: "Rd",
1935            lsb: 0,
1936            width: 5,
1937            signed: false,
1938        },
1939    ];
1940
1941    const ADD_SPEC: EncodingSpec = EncodingSpec {
1942        mnemonic: "add",
1943        variant: "ADD_64_addsub_imm",
1944        opcode: 0b100100010u32 << 23,
1945        opcode_mask: 0b111111111u32 << 23,
1946        fields: ADD_FIELDS,
1947        operand_order: &[3, 2, 1],
1948        operand_kinds: &[
1949            OperandConstraintKind::Gpr64Register,
1950            OperandConstraintKind::Gpr64Register,
1951            OperandConstraintKind::Immediate,
1952        ],
1953        implicit_defaults: &[ImplicitField {
1954            field_index: 0,
1955            value: 0,
1956        }],
1957        memory_addressing: MemoryAddressingConstraintSpec::None,
1958        field_scales: &[1, 1, 1, 1],
1959        split_immediate_plan: None,
1960        gpr32_extend_compatibility: 0,
1961    };
1962
1963    const SEL_P_FIELDS: &[BitFieldSpec] = &[
1964        BitFieldSpec {
1965            name: "Pm",
1966            lsb: 16,
1967            width: 4,
1968            signed: false,
1969        },
1970        BitFieldSpec {
1971            name: "Pg",
1972            lsb: 10,
1973            width: 4,
1974            signed: false,
1975        },
1976        BitFieldSpec {
1977            name: "Pn",
1978            lsb: 5,
1979            width: 4,
1980            signed: false,
1981        },
1982        BitFieldSpec {
1983            name: "Pd",
1984            lsb: 0,
1985            width: 4,
1986            signed: false,
1987        },
1988    ];
1989
1990    const SEL_P_SPEC: EncodingSpec = EncodingSpec {
1991        mnemonic: "sel",
1992        variant: "sel_p_p_pp_",
1993        opcode: 0x2500_4210,
1994        opcode_mask: 0xfff0_c210,
1995        fields: SEL_P_FIELDS,
1996        operand_order: &[3, 1, 2, 0],
1997        operand_kinds: &[
1998            OperandConstraintKind::PredicateRegister,
1999            OperandConstraintKind::PredicateRegister,
2000            OperandConstraintKind::PredicateRegister,
2001            OperandConstraintKind::PredicateRegister,
2002        ],
2003        implicit_defaults: &[],
2004        memory_addressing: MemoryAddressingConstraintSpec::None,
2005        field_scales: &[1, 1, 1, 1],
2006        split_immediate_plan: None,
2007        gpr32_extend_compatibility: 0,
2008    };
2009
2010    const STR_64_POS_FIELDS: &[BitFieldSpec] = &[
2011        BitFieldSpec {
2012            name: "imm12",
2013            lsb: 10,
2014            width: 12,
2015            signed: false,
2016        },
2017        BitFieldSpec {
2018            name: "Rn",
2019            lsb: 5,
2020            width: 5,
2021            signed: false,
2022        },
2023        BitFieldSpec {
2024            name: "Rt",
2025            lsb: 0,
2026            width: 5,
2027            signed: false,
2028        },
2029    ];
2030
2031    const STR_64_POS_SPEC: EncodingSpec = EncodingSpec {
2032        mnemonic: "str",
2033        variant: "STR_64_ldst_pos",
2034        opcode: 0xf900_0000,
2035        opcode_mask: 0xffc0_0000,
2036        fields: STR_64_POS_FIELDS,
2037        operand_order: &[2, 1, 0],
2038        operand_kinds: &[
2039            OperandConstraintKind::Gpr64Register,
2040            OperandConstraintKind::Gpr64Register,
2041            OperandConstraintKind::Immediate,
2042        ],
2043        implicit_defaults: &[],
2044        memory_addressing: MemoryAddressingConstraintSpec::Offset,
2045        field_scales: &[8, 1, 1],
2046        split_immediate_plan: None,
2047        gpr32_extend_compatibility: 0,
2048    };
2049
2050    const STLR_NO_OFFSET_FIELDS: &[BitFieldSpec] = &[
2051        BitFieldSpec {
2052            name: "Rn",
2053            lsb: 5,
2054            width: 5,
2055            signed: false,
2056        },
2057        BitFieldSpec {
2058            name: "Rt",
2059            lsb: 0,
2060            width: 5,
2061            signed: false,
2062        },
2063    ];
2064
2065    const STLR_NO_OFFSET_SPEC: EncodingSpec = EncodingSpec {
2066        mnemonic: "stlr",
2067        variant: "STLR_SL64_ldstord",
2068        opcode: 0xc89f_fc00,
2069        opcode_mask: 0xffff_fc00,
2070        fields: STLR_NO_OFFSET_FIELDS,
2071        operand_order: &[1, 0],
2072        operand_kinds: &[
2073            OperandConstraintKind::Gpr64Register,
2074            OperandConstraintKind::Gpr64Register,
2075        ],
2076        implicit_defaults: &[],
2077        memory_addressing: MemoryAddressingConstraintSpec::NoOffset,
2078        field_scales: &[1, 1],
2079        split_immediate_plan: None,
2080        gpr32_extend_compatibility: 0,
2081    };
2082
2083    const STP_64_PRE_FIELDS: &[BitFieldSpec] = &[
2084        BitFieldSpec {
2085            name: "imm7",
2086            lsb: 15,
2087            width: 7,
2088            signed: true,
2089        },
2090        BitFieldSpec {
2091            name: "Rt2",
2092            lsb: 10,
2093            width: 5,
2094            signed: false,
2095        },
2096        BitFieldSpec {
2097            name: "Rn",
2098            lsb: 5,
2099            width: 5,
2100            signed: false,
2101        },
2102        BitFieldSpec {
2103            name: "Rt",
2104            lsb: 0,
2105            width: 5,
2106            signed: false,
2107        },
2108    ];
2109
2110    const STP_64_PRE_SPEC: EncodingSpec = EncodingSpec {
2111        mnemonic: "stp",
2112        variant: "STP_64_ldstpair_pre",
2113        opcode: 0xa980_0000,
2114        opcode_mask: 0xffc0_0000,
2115        fields: STP_64_PRE_FIELDS,
2116        operand_order: &[3, 1, 2, 0],
2117        operand_kinds: &[
2118            OperandConstraintKind::Gpr64Register,
2119            OperandConstraintKind::Gpr64Register,
2120            OperandConstraintKind::Gpr64Register,
2121            OperandConstraintKind::Immediate,
2122        ],
2123        implicit_defaults: &[],
2124        memory_addressing: MemoryAddressingConstraintSpec::PreIndex,
2125        field_scales: &[8, 1, 1, 1],
2126        split_immediate_plan: None,
2127        gpr32_extend_compatibility: 0,
2128    };
2129
2130    const B_IMM_SPEC: EncodingSpec = EncodingSpec {
2131        mnemonic: "b",
2132        variant: "B_only_branch_imm",
2133        opcode: 0x1400_0000,
2134        opcode_mask: 0xfc00_0000,
2135        fields: &[BitFieldSpec {
2136            name: "imm26",
2137            lsb: 0,
2138            width: 26,
2139            signed: true,
2140        }],
2141        operand_order: &[0],
2142        operand_kinds: &[OperandConstraintKind::Immediate],
2143        implicit_defaults: &[],
2144        memory_addressing: MemoryAddressingConstraintSpec::None,
2145        field_scales: &[4],
2146        split_immediate_plan: None,
2147        gpr32_extend_compatibility: 0,
2148    };
2149
2150    const ADR_SPEC: EncodingSpec = EncodingSpec {
2151        mnemonic: "adr",
2152        variant: "ADR_only_pcreladdr",
2153        opcode: 0x1000_0000,
2154        opcode_mask: 0x9f00_0000,
2155        fields: &[
2156            BitFieldSpec {
2157                name: "immlo",
2158                lsb: 29,
2159                width: 2,
2160                signed: false,
2161            },
2162            BitFieldSpec {
2163                name: "immhi",
2164                lsb: 5,
2165                width: 19,
2166                signed: true,
2167            },
2168            BitFieldSpec {
2169                name: "Rd",
2170                lsb: 0,
2171                width: 5,
2172                signed: false,
2173            },
2174        ],
2175        operand_order: &[2, 0, 1],
2176        operand_kinds: &[
2177            OperandConstraintKind::Gpr64Register,
2178            OperandConstraintKind::Immediate,
2179            OperandConstraintKind::Immediate,
2180        ],
2181        implicit_defaults: &[],
2182        memory_addressing: MemoryAddressingConstraintSpec::None,
2183        field_scales: &[1, 1, 1],
2184        split_immediate_plan: Some(SplitImmediatePlanSpec {
2185            first_slot: 1,
2186            second_slot: 2,
2187            kind: SplitImmediateKindSpec::AdrLike {
2188                immlo_field_index: 0,
2189                immhi_field_index: 1,
2190                scale: 1,
2191            },
2192        }),
2193        gpr32_extend_compatibility: 0,
2194    };
2195
2196    const ADRP_SPEC: EncodingSpec = EncodingSpec {
2197        mnemonic: "adrp",
2198        variant: "ADRP_only_pcreladdr",
2199        opcode: 0x9000_0000,
2200        opcode_mask: 0x9f00_0000,
2201        fields: &[
2202            BitFieldSpec {
2203                name: "immlo",
2204                lsb: 29,
2205                width: 2,
2206                signed: false,
2207            },
2208            BitFieldSpec {
2209                name: "immhi",
2210                lsb: 5,
2211                width: 19,
2212                signed: true,
2213            },
2214            BitFieldSpec {
2215                name: "Rd",
2216                lsb: 0,
2217                width: 5,
2218                signed: false,
2219            },
2220        ],
2221        operand_order: &[2, 0, 1],
2222        operand_kinds: &[
2223            OperandConstraintKind::Gpr64Register,
2224            OperandConstraintKind::Immediate,
2225            OperandConstraintKind::Immediate,
2226        ],
2227        implicit_defaults: &[],
2228        memory_addressing: MemoryAddressingConstraintSpec::None,
2229        field_scales: &[1, 1, 1],
2230        split_immediate_plan: Some(SplitImmediatePlanSpec {
2231            first_slot: 1,
2232            second_slot: 2,
2233            kind: SplitImmediateKindSpec::AdrLike {
2234                immlo_field_index: 0,
2235                immhi_field_index: 1,
2236                scale: 4096,
2237            },
2238        }),
2239        gpr32_extend_compatibility: 0,
2240    };
2241
2242    const EOR_64_LOG_IMM_SPEC: EncodingSpec = EncodingSpec {
2243        mnemonic: "eor",
2244        variant: "EOR_64_log_imm",
2245        opcode: 0xd200_0000,
2246        opcode_mask: 0xff80_0000,
2247        fields: &[
2248            BitFieldSpec {
2249                name: "N",
2250                lsb: 22,
2251                width: 1,
2252                signed: false,
2253            },
2254            BitFieldSpec {
2255                name: "immr",
2256                lsb: 16,
2257                width: 6,
2258                signed: false,
2259            },
2260            BitFieldSpec {
2261                name: "imms",
2262                lsb: 10,
2263                width: 6,
2264                signed: false,
2265            },
2266            BitFieldSpec {
2267                name: "Rn",
2268                lsb: 5,
2269                width: 5,
2270                signed: false,
2271            },
2272            BitFieldSpec {
2273                name: "Rd",
2274                lsb: 0,
2275                width: 5,
2276                signed: false,
2277            },
2278        ],
2279        operand_order: &[4, 3, 0, 1, 2],
2280        operand_kinds: &[
2281            OperandConstraintKind::Gpr64Register,
2282            OperandConstraintKind::Gpr64Register,
2283            OperandConstraintKind::Immediate,
2284            OperandConstraintKind::Immediate,
2285            OperandConstraintKind::Immediate,
2286        ],
2287        implicit_defaults: &[],
2288        memory_addressing: MemoryAddressingConstraintSpec::None,
2289        field_scales: &[1, 1, 1, 1, 1],
2290        split_immediate_plan: Some(SplitImmediatePlanSpec {
2291            first_slot: 2,
2292            second_slot: 4,
2293            kind: SplitImmediateKindSpec::LogicalImmNrs {
2294                n_field_index: 0,
2295                immr_field_index: 1,
2296                imms_field_index: 2,
2297                reg_size: 64,
2298            },
2299        }),
2300        gpr32_extend_compatibility: 0,
2301    };
2302
2303    const EOR_32_LOG_IMM_SPEC: EncodingSpec = EncodingSpec {
2304        mnemonic: "eor",
2305        variant: "EOR_32_log_imm",
2306        opcode: 0x5200_0000,
2307        opcode_mask: 0xffc0_0000,
2308        fields: &[
2309            BitFieldSpec {
2310                name: "immr",
2311                lsb: 16,
2312                width: 6,
2313                signed: false,
2314            },
2315            BitFieldSpec {
2316                name: "imms",
2317                lsb: 10,
2318                width: 6,
2319                signed: false,
2320            },
2321            BitFieldSpec {
2322                name: "Rn",
2323                lsb: 5,
2324                width: 5,
2325                signed: false,
2326            },
2327            BitFieldSpec {
2328                name: "Rd",
2329                lsb: 0,
2330                width: 5,
2331                signed: false,
2332            },
2333        ],
2334        operand_order: &[3, 2, 0, 1],
2335        operand_kinds: &[
2336            OperandConstraintKind::Gpr32Register,
2337            OperandConstraintKind::Gpr32Register,
2338            OperandConstraintKind::Immediate,
2339            OperandConstraintKind::Immediate,
2340        ],
2341        implicit_defaults: &[],
2342        memory_addressing: MemoryAddressingConstraintSpec::None,
2343        field_scales: &[1, 1, 1, 1],
2344        split_immediate_plan: Some(SplitImmediatePlanSpec {
2345            first_slot: 2,
2346            second_slot: 3,
2347            kind: SplitImmediateKindSpec::LogicalImmRs {
2348                immr_field_index: 0,
2349                imms_field_index: 1,
2350                reg_size: 32,
2351            },
2352        }),
2353        gpr32_extend_compatibility: 0,
2354    };
2355
2356    const ADD_EXT_64_SPEC: EncodingSpec = EncodingSpec {
2357        mnemonic: "add",
2358        variant: "ADD_64_addsub_ext",
2359        opcode: 0x8b20_0000,
2360        opcode_mask: 0xffe0_0000,
2361        fields: &[
2362            BitFieldSpec {
2363                name: "Rm",
2364                lsb: 16,
2365                width: 5,
2366                signed: false,
2367            },
2368            BitFieldSpec {
2369                name: "option",
2370                lsb: 13,
2371                width: 3,
2372                signed: false,
2373            },
2374            BitFieldSpec {
2375                name: "imm3",
2376                lsb: 10,
2377                width: 3,
2378                signed: false,
2379            },
2380            BitFieldSpec {
2381                name: "Rn",
2382                lsb: 5,
2383                width: 5,
2384                signed: false,
2385            },
2386            BitFieldSpec {
2387                name: "Rd",
2388                lsb: 0,
2389                width: 5,
2390                signed: false,
2391            },
2392        ],
2393        operand_order: &[4, 3, 0, 1, 2],
2394        operand_kinds: &[
2395            OperandConstraintKind::Gpr64Register,
2396            OperandConstraintKind::Gpr64Register,
2397            OperandConstraintKind::Gpr64Register,
2398            OperandConstraintKind::ExtendKind,
2399            OperandConstraintKind::Immediate,
2400        ],
2401        implicit_defaults: &[],
2402        memory_addressing: MemoryAddressingConstraintSpec::None,
2403        field_scales: &[1, 1, 1, 1, 1],
2404        split_immediate_plan: None,
2405        gpr32_extend_compatibility: 0b100,
2406    };
2407
2408    const LDR_64_UNSIGNED_SPEC: EncodingSpec = EncodingSpec {
2409        mnemonic: "ldr",
2410        variant: "LDR_64_ldst_pos",
2411        opcode: 0xf940_0000,
2412        opcode_mask: 0xffc0_0000,
2413        fields: &[
2414            BitFieldSpec {
2415                name: "imm12",
2416                lsb: 10,
2417                width: 12,
2418                signed: false,
2419            },
2420            BitFieldSpec {
2421                name: "Rn",
2422                lsb: 5,
2423                width: 5,
2424                signed: false,
2425            },
2426            BitFieldSpec {
2427                name: "Rt",
2428                lsb: 0,
2429                width: 5,
2430                signed: false,
2431            },
2432        ],
2433        operand_order: &[2, 1, 0],
2434        operand_kinds: &[
2435            OperandConstraintKind::Gpr64Register,
2436            OperandConstraintKind::Gpr64Register,
2437            OperandConstraintKind::Immediate,
2438        ],
2439        implicit_defaults: &[],
2440        memory_addressing: MemoryAddressingConstraintSpec::Offset,
2441        field_scales: &[8, 1, 1],
2442        split_immediate_plan: None,
2443        gpr32_extend_compatibility: 0,
2444    };
2445
2446    const LDAR_64_SPEC: EncodingSpec = EncodingSpec {
2447        mnemonic: "ldar",
2448        variant: "LDAR_LR64_ldstord",
2449        opcode: 0xc8df_fc00,
2450        opcode_mask: 0xffff_fc00,
2451        fields: &[
2452            BitFieldSpec {
2453                name: "Rn",
2454                lsb: 5,
2455                width: 5,
2456                signed: false,
2457            },
2458            BitFieldSpec {
2459                name: "Rt",
2460                lsb: 0,
2461                width: 5,
2462                signed: false,
2463            },
2464        ],
2465        operand_order: &[1, 0],
2466        operand_kinds: &[
2467            OperandConstraintKind::Gpr64Register,
2468            OperandConstraintKind::Gpr64Register,
2469        ],
2470        implicit_defaults: &[],
2471        memory_addressing: MemoryAddressingConstraintSpec::None,
2472        field_scales: &[1, 1],
2473        split_immediate_plan: None,
2474        gpr32_extend_compatibility: 0,
2475    };
2476
2477    const TBNZ_SPEC: EncodingSpec = EncodingSpec {
2478        mnemonic: "tbnz",
2479        variant: "TBNZ_only_testbranch",
2480        opcode: 0x3700_0000,
2481        opcode_mask: 0x7f00_0000,
2482        fields: &[
2483            BitFieldSpec {
2484                name: "b5",
2485                lsb: 31,
2486                width: 1,
2487                signed: false,
2488            },
2489            BitFieldSpec {
2490                name: "b40",
2491                lsb: 19,
2492                width: 5,
2493                signed: false,
2494            },
2495            BitFieldSpec {
2496                name: "imm14",
2497                lsb: 5,
2498                width: 14,
2499                signed: true,
2500            },
2501            BitFieldSpec {
2502                name: "Rt",
2503                lsb: 0,
2504                width: 5,
2505                signed: false,
2506            },
2507        ],
2508        operand_order: &[3, 0, 1, 2],
2509        operand_kinds: &[
2510            OperandConstraintKind::Gpr64Register,
2511            OperandConstraintKind::Immediate,
2512            OperandConstraintKind::Immediate,
2513            OperandConstraintKind::Immediate,
2514        ],
2515        implicit_defaults: &[],
2516        memory_addressing: MemoryAddressingConstraintSpec::None,
2517        field_scales: &[1, 1, 4, 1],
2518        split_immediate_plan: Some(SplitImmediatePlanSpec {
2519            first_slot: 1,
2520            second_slot: 2,
2521            kind: SplitImmediateKindSpec::BitIndex6 {
2522                b5_field_index: 0,
2523                b40_field_index: 1,
2524            },
2525        }),
2526        gpr32_extend_compatibility: 0,
2527    };
2528
2529    const MRS_SPEC: EncodingSpec = EncodingSpec {
2530        mnemonic: "mrs",
2531        variant: "MRS_RS_systemmove",
2532        opcode: 0xd530_0000,
2533        opcode_mask: 0xfff0_0000,
2534        fields: &[
2535            BitFieldSpec {
2536                name: "o0",
2537                lsb: 19,
2538                width: 1,
2539                signed: false,
2540            },
2541            BitFieldSpec {
2542                name: "op1",
2543                lsb: 16,
2544                width: 3,
2545                signed: false,
2546            },
2547            BitFieldSpec {
2548                name: "CRn",
2549                lsb: 12,
2550                width: 4,
2551                signed: false,
2552            },
2553            BitFieldSpec {
2554                name: "CRm",
2555                lsb: 8,
2556                width: 4,
2557                signed: false,
2558            },
2559            BitFieldSpec {
2560                name: "op2",
2561                lsb: 5,
2562                width: 3,
2563                signed: false,
2564            },
2565            BitFieldSpec {
2566                name: "Rt",
2567                lsb: 0,
2568                width: 5,
2569                signed: false,
2570            },
2571        ],
2572        operand_order: &[5, 1, 2, 3, 4, 0],
2573        operand_kinds: &[
2574            OperandConstraintKind::Gpr64Register,
2575            OperandConstraintKind::SysRegPart,
2576            OperandConstraintKind::SysRegPart,
2577            OperandConstraintKind::SysRegPart,
2578            OperandConstraintKind::SysRegPart,
2579            OperandConstraintKind::Immediate,
2580        ],
2581        implicit_defaults: &[],
2582        memory_addressing: MemoryAddressingConstraintSpec::None,
2583        field_scales: &[1, 1, 1, 1, 1, 1],
2584        split_immediate_plan: None,
2585        gpr32_extend_compatibility: 0,
2586    };
2587
2588    #[test]
2589    fn encode_add_imm() {
2590        let code = encode_by_spec(&ADD_SPEC, &[0, 1, 2, 1]).expect("encode should succeed");
2591        assert_eq!(code.unpack(), 0x91000441);
2592    }
2593
2594    #[test]
2595    fn rejects_imm_overflow() {
2596        let err =
2597            encode_by_spec(&ADD_SPEC, &[0, 0x2000, 2, 1]).expect_err("should reject overflow");
2598        assert_eq!(
2599            err,
2600            EncodeError::OperandOutOfRange {
2601                field: "imm12",
2602                value: 0x2000,
2603                width: 12,
2604                signed: false,
2605            }
2606        );
2607    }
2608
2609    #[test]
2610    fn unknown_mnemonic() {
2611        let err = encode(
2612            &[ADD_SPEC],
2613            "sub",
2614            &[
2615                Operand::Register(RegisterOperand {
2616                    code: 1,
2617                    class: RegClass::X,
2618                    arrangement: None,
2619                    lane: None,
2620                }),
2621                Operand::Register(RegisterOperand {
2622                    code: 2,
2623                    class: RegClass::X,
2624                    arrangement: None,
2625                    lane: None,
2626                }),
2627                Operand::Immediate(1),
2628            ],
2629        )
2630        .expect_err("must fail");
2631        assert_eq!(err, EncodeError::UnknownMnemonic);
2632    }
2633
2634    #[test]
2635    fn encode_add_imm_asm_operands() {
2636        let code = encode_by_spec_operands(
2637            &ADD_SPEC,
2638            &[
2639                Operand::Register(RegisterOperand {
2640                    code: 1,
2641                    class: RegClass::X,
2642                    arrangement: None,
2643                    lane: None,
2644                }),
2645                Operand::Register(RegisterOperand {
2646                    code: 2,
2647                    class: RegClass::X,
2648                    arrangement: None,
2649                    lane: None,
2650                }),
2651                Operand::Immediate(1),
2652            ],
2653        )
2654        .expect("encode should succeed");
2655        assert_eq!(code.unpack(), 0x91000441);
2656    }
2657
2658    #[test]
2659    fn invalid_kind_rejected() {
2660        let err = encode_by_spec_operands(
2661            &ADD_SPEC,
2662            &[
2663                Operand::Immediate(1),
2664                Operand::Register(RegisterOperand {
2665                    code: 2,
2666                    class: RegClass::X,
2667                    arrangement: None,
2668                    lane: None,
2669                }),
2670                Operand::Immediate(1),
2671            ],
2672        )
2673        .expect_err("must fail");
2674        assert_eq!(
2675            err,
2676            EncodeError::InvalidOperandKind {
2677                field: "Rd",
2678                expected: "64-bit general-purpose register",
2679                got: "immediate",
2680            }
2681        );
2682    }
2683
2684    #[test]
2685    fn encode_predicate_operands() {
2686        let code = encode_by_spec_operands(
2687            &SEL_P_SPEC,
2688            &[
2689                Operand::Register(RegisterOperand {
2690                    code: 1,
2691                    class: RegClass::P,
2692                    arrangement: None,
2693                    lane: None,
2694                }),
2695                Operand::Register(RegisterOperand {
2696                    code: 2,
2697                    class: RegClass::P,
2698                    arrangement: None,
2699                    lane: None,
2700                }),
2701                Operand::Register(RegisterOperand {
2702                    code: 3,
2703                    class: RegClass::P,
2704                    arrangement: None,
2705                    lane: None,
2706                }),
2707                Operand::Register(RegisterOperand {
2708                    code: 4,
2709                    class: RegClass::P,
2710                    arrangement: None,
2711                    lane: None,
2712                }),
2713            ],
2714        )
2715        .expect("predicate encoding should succeed");
2716        assert_eq!(
2717            code.unpack(),
2718            0x2500_4210u32 | (4u32 << 16) | (2u32 << 10) | (3u32 << 5) | 1u32
2719        );
2720    }
2721
2722    #[test]
2723    fn reject_wrong_register_family() {
2724        let err = encode_by_spec_operands(
2725            &SEL_P_SPEC,
2726            &[
2727                Operand::Register(RegisterOperand {
2728                    code: 1,
2729                    class: RegClass::X,
2730                    arrangement: None,
2731                    lane: None,
2732                }),
2733                Operand::Register(RegisterOperand {
2734                    code: 2,
2735                    class: RegClass::P,
2736                    arrangement: None,
2737                    lane: None,
2738                }),
2739                Operand::Register(RegisterOperand {
2740                    code: 3,
2741                    class: RegClass::P,
2742                    arrangement: None,
2743                    lane: None,
2744                }),
2745                Operand::Register(RegisterOperand {
2746                    code: 4,
2747                    class: RegClass::P,
2748                    arrangement: None,
2749                    lane: None,
2750                }),
2751            ],
2752        )
2753        .expect_err("mismatched family should fail");
2754        assert_eq!(
2755            err,
2756            EncodeError::InvalidOperandKind {
2757                field: "Pd",
2758                expected: "predicate register",
2759                got: "64-bit general-purpose register",
2760            }
2761        );
2762    }
2763
2764    #[test]
2765    fn memory_imm12_is_scaled_from_bytes() {
2766        let code = encode_by_spec_operands(
2767            &STR_64_POS_SPEC,
2768            &[
2769                Operand::Register(RegisterOperand {
2770                    code: 30,
2771                    class: RegClass::X,
2772                    arrangement: None,
2773                    lane: None,
2774                }),
2775                Operand::Memory(MemoryOperand {
2776                    base: RegisterOperand {
2777                        code: 31,
2778                        class: RegClass::Xsp,
2779                        arrangement: None,
2780                        lane: None,
2781                    },
2782                    offset: MemoryOffset::Immediate(16),
2783                    addressing: AddressingMode::Offset,
2784                }),
2785            ],
2786        )
2787        .expect("str should encode");
2788        assert_eq!(code.unpack(), 0xf900_0bfe);
2789    }
2790
2791    #[test]
2792    fn memory_imm7_pair_is_scaled_from_bytes() {
2793        let code = encode_by_spec_operands(
2794            &STP_64_PRE_SPEC,
2795            &[
2796                Operand::Register(RegisterOperand {
2797                    code: 27,
2798                    class: RegClass::X,
2799                    arrangement: None,
2800                    lane: None,
2801                }),
2802                Operand::Register(RegisterOperand {
2803                    code: 28,
2804                    class: RegClass::X,
2805                    arrangement: None,
2806                    lane: None,
2807                }),
2808                Operand::Memory(MemoryOperand {
2809                    base: RegisterOperand {
2810                        code: 31,
2811                        class: RegClass::Xsp,
2812                        arrangement: None,
2813                        lane: None,
2814                    },
2815                    offset: MemoryOffset::Immediate(-32),
2816                    addressing: AddressingMode::PreIndex,
2817                }),
2818            ],
2819        )
2820        .expect("stp should encode");
2821        assert_eq!(code.unpack(), 0xa9be_73fb);
2822    }
2823
2824    #[test]
2825    fn misaligned_memory_immediate_is_rejected() {
2826        let err = encode_by_spec_operands(
2827            &STR_64_POS_SPEC,
2828            &[
2829                Operand::Register(RegisterOperand {
2830                    code: 30,
2831                    class: RegClass::X,
2832                    arrangement: None,
2833                    lane: None,
2834                }),
2835                Operand::Memory(MemoryOperand {
2836                    base: RegisterOperand {
2837                        code: 31,
2838                        class: RegClass::Xsp,
2839                        arrangement: None,
2840                        lane: None,
2841                    },
2842                    offset: MemoryOffset::Immediate(10),
2843                    addressing: AddressingMode::Offset,
2844                }),
2845            ],
2846        )
2847        .expect_err("misalignment must fail");
2848        assert_eq!(
2849            err,
2850            EncodeError::ImmediateNotAligned {
2851                field: "imm12",
2852                value: 10,
2853                scale: 8,
2854            }
2855        );
2856    }
2857
2858    #[test]
2859    fn branch_immediate_is_scaled_from_bytes() {
2860        let code =
2861            encode_by_spec_operands(&B_IMM_SPEC, &[Operand::Immediate(8)]).expect("b encode");
2862        assert_eq!(code.unpack(), 0x1400_0002);
2863    }
2864
2865    #[test]
2866    fn branch_immediate_alignment_is_enforced() {
2867        let err = encode_by_spec_operands(&B_IMM_SPEC, &[Operand::Immediate(6)])
2868            .expect_err("misaligned branch immediate must fail");
2869        assert_eq!(
2870            err,
2871            EncodeError::ImmediateNotAligned {
2872                field: "imm26",
2873                value: 6,
2874                scale: 4,
2875            }
2876        );
2877    }
2878
2879    #[test]
2880    fn adr_accepts_single_pc_relative_immediate() {
2881        let code = encode_by_spec_operands(
2882            &ADR_SPEC,
2883            &[
2884                Operand::Register(RegisterOperand {
2885                    code: 0,
2886                    class: RegClass::X,
2887                    arrangement: None,
2888                    lane: None,
2889                }),
2890                Operand::Immediate(4),
2891            ],
2892        )
2893        .expect("adr must encode with single immediate");
2894        assert_eq!(code.unpack(), 0x1000_0020);
2895    }
2896
2897    #[test]
2898    fn adrp_immediate_uses_page_scale() {
2899        let code = encode_by_spec_operands(
2900            &ADRP_SPEC,
2901            &[
2902                Operand::Register(RegisterOperand {
2903                    code: 0,
2904                    class: RegClass::X,
2905                    arrangement: None,
2906                    lane: None,
2907                }),
2908                Operand::Immediate(4096),
2909            ],
2910        )
2911        .expect("adrp must encode with page immediate");
2912        assert_eq!(code.unpack(), 0xb000_0000);
2913    }
2914
2915    #[test]
2916    fn adrp_immediate_alignment_is_enforced() {
2917        let err = encode_by_spec_operands(
2918            &ADRP_SPEC,
2919            &[
2920                Operand::Register(RegisterOperand {
2921                    code: 0,
2922                    class: RegClass::X,
2923                    arrangement: None,
2924                    lane: None,
2925                }),
2926                Operand::Immediate(2048),
2927            ],
2928        )
2929        .expect_err("misaligned adrp immediate must fail");
2930        assert_eq!(
2931            err,
2932            EncodeError::ImmediateNotAligned {
2933                field: "immlo",
2934                value: 2048,
2935                scale: 4096,
2936            }
2937        );
2938    }
2939
2940    #[test]
2941    fn eor_64_logical_immediate_encodes_single_bitmask_operand() {
2942        let code = encode_by_spec_operands(
2943            &EOR_64_LOG_IMM_SPEC,
2944            &[
2945                Operand::Register(RegisterOperand {
2946                    code: 0,
2947                    class: RegClass::X,
2948                    arrangement: None,
2949                    lane: None,
2950                }),
2951                Operand::Register(RegisterOperand {
2952                    code: 0,
2953                    class: RegClass::X,
2954                    arrangement: None,
2955                    lane: None,
2956                }),
2957                Operand::Immediate(0x2000_0000),
2958            ],
2959        )
2960        .expect("eor x,x,#imm must encode");
2961        assert_eq!(code.unpack(), 0xd263_0000);
2962    }
2963
2964    #[test]
2965    fn eor_32_logical_immediate_encodes_single_bitmask_operand() {
2966        let code = encode_by_spec_operands(
2967            &EOR_32_LOG_IMM_SPEC,
2968            &[
2969                Operand::Register(RegisterOperand {
2970                    code: 0,
2971                    class: RegClass::W,
2972                    arrangement: None,
2973                    lane: None,
2974                }),
2975                Operand::Register(RegisterOperand {
2976                    code: 0,
2977                    class: RegClass::W,
2978                    arrangement: None,
2979                    lane: None,
2980                }),
2981                Operand::Immediate(0x2000_0000),
2982            ],
2983        )
2984        .expect("eor w,w,#imm must encode");
2985        assert_eq!(code.unpack(), 0x5203_0000);
2986    }
2987
2988    #[test]
2989    fn add_ext_64_accepts_w_register_source() {
2990        let w_source = encode_by_spec_operands(
2991            &ADD_EXT_64_SPEC,
2992            &[
2993                Operand::Register(RegisterOperand {
2994                    code: 3,
2995                    class: RegClass::X,
2996                    arrangement: None,
2997                    lane: None,
2998                }),
2999                Operand::Register(RegisterOperand {
3000                    code: 4,
3001                    class: RegClass::X,
3002                    arrangement: None,
3003                    lane: None,
3004                }),
3005                Operand::Register(RegisterOperand {
3006                    code: 5,
3007                    class: RegClass::W,
3008                    arrangement: None,
3009                    lane: None,
3010                }),
3011                Operand::Extend(ExtendOperand {
3012                    kind: ExtendKind::Uxtw,
3013                    amount: Some(2),
3014                }),
3015            ],
3016        )
3017        .expect("64-bit add ext form should accept Wm");
3018
3019        let x_source = encode_by_spec_operands(
3020            &ADD_EXT_64_SPEC,
3021            &[
3022                Operand::Register(RegisterOperand {
3023                    code: 3,
3024                    class: RegClass::X,
3025                    arrangement: None,
3026                    lane: None,
3027                }),
3028                Operand::Register(RegisterOperand {
3029                    code: 4,
3030                    class: RegClass::X,
3031                    arrangement: None,
3032                    lane: None,
3033                }),
3034                Operand::Register(RegisterOperand {
3035                    code: 5,
3036                    class: RegClass::X,
3037                    arrangement: None,
3038                    lane: None,
3039                }),
3040                Operand::Extend(ExtendOperand {
3041                    kind: ExtendKind::Uxtw,
3042                    amount: Some(2),
3043                }),
3044            ],
3045        )
3046        .expect("64-bit add ext form should accept Xm as encoded source register");
3047
3048        assert_eq!(w_source.unpack(), x_source.unpack());
3049    }
3050
3051    #[test]
3052    fn tbnz_accepts_single_bit_index_operand() {
3053        let code = encode_by_spec_operands(
3054            &TBNZ_SPEC,
3055            &[
3056                Operand::Register(RegisterOperand {
3057                    code: 1,
3058                    class: RegClass::X,
3059                    arrangement: None,
3060                    lane: None,
3061                }),
3062                Operand::Immediate(3),
3063                Operand::Immediate(8),
3064            ],
3065        )
3066        .expect("tbnz with split bit-index should encode");
3067
3068        let expected = encode_by_spec(&TBNZ_SPEC, &[0, 3, 2, 1]).expect("expected split encoding");
3069        assert_eq!(code.unpack(), expected.unpack());
3070    }
3071
3072    #[test]
3073    fn bare_memory_operand_maps_to_zero_or_no_offset_forms() {
3074        let ldr = encode_by_spec_operands(
3075            &LDR_64_UNSIGNED_SPEC,
3076            &[
3077                Operand::Register(RegisterOperand {
3078                    code: 6,
3079                    class: RegClass::X,
3080                    arrangement: None,
3081                    lane: None,
3082                }),
3083                Operand::Memory(MemoryOperand {
3084                    base: RegisterOperand {
3085                        code: 7,
3086                        class: RegClass::X,
3087                        arrangement: None,
3088                        lane: None,
3089                    },
3090                    offset: MemoryOffset::None,
3091                    addressing: AddressingMode::Offset,
3092                }),
3093            ],
3094        )
3095        .expect("ldr [xn] should map to imm12=#0 form");
3096        let expected_ldr = encode_by_spec(&LDR_64_UNSIGNED_SPEC, &[0, 7, 6]).expect("ldr #0");
3097        assert_eq!(ldr.unpack(), expected_ldr.unpack());
3098
3099        let ldar = encode_by_spec_operands(
3100            &LDAR_64_SPEC,
3101            &[
3102                Operand::Register(RegisterOperand {
3103                    code: 8,
3104                    class: RegClass::X,
3105                    arrangement: None,
3106                    lane: None,
3107                }),
3108                Operand::Memory(MemoryOperand {
3109                    base: RegisterOperand {
3110                        code: 9,
3111                        class: RegClass::X,
3112                        arrangement: None,
3113                        lane: None,
3114                    },
3115                    offset: MemoryOffset::None,
3116                    addressing: AddressingMode::Offset,
3117                }),
3118            ],
3119        )
3120        .expect("ldar [xn] should match no-offset form");
3121        let expected_ldar = encode_by_spec(&LDAR_64_SPEC, &[9, 8]).expect("ldar");
3122        assert_eq!(ldar.unpack(), expected_ldar.unpack());
3123    }
3124
3125    #[test]
3126    fn sysreg_operand_converts_arch_op0_to_encoded_o0() {
3127        let code = encode_by_spec_operands(
3128            &MRS_SPEC,
3129            &[
3130                Operand::Register(RegisterOperand {
3131                    code: 8,
3132                    class: RegClass::X,
3133                    arrangement: None,
3134                    lane: None,
3135                }),
3136                Operand::SysReg(SysRegOperand {
3137                    op0: 3,
3138                    op1: 0,
3139                    crn: 15,
3140                    crm: 2,
3141                    op2: 0,
3142                }),
3143            ],
3144        )
3145        .expect("mrs sysreg form should encode");
3146
3147        let expected = encode_by_spec(&MRS_SPEC, &[1, 0, 15, 2, 0, 8]).expect("mrs expected");
3148        assert_eq!(code.unpack(), expected.unpack());
3149    }
3150
3151    #[test]
3152    fn operand_count_range_reports_expected_bounds() {
3153        let err = encode(
3154            &[ADD_SPEC],
3155            "add",
3156            &[Operand::Register(RegisterOperand {
3157                code: 1,
3158                class: RegClass::X,
3159                arrangement: None,
3160                lane: None,
3161            })],
3162        )
3163        .expect_err("must reject operand count");
3164        assert_eq!(
3165            err,
3166            EncodeError::OperandCountRange {
3167                min: 3,
3168                max: 3,
3169                got: 1,
3170            }
3171        );
3172    }
3173
3174    #[test]
3175    fn operand_count_range_uses_user_visible_split_immediate_count() {
3176        let err = encode(
3177            &[TBNZ_SPEC],
3178            "tbnz",
3179            &[
3180                Operand::Register(RegisterOperand {
3181                    code: 1,
3182                    class: RegClass::X,
3183                    arrangement: None,
3184                    lane: None,
3185                }),
3186                Operand::Immediate(3),
3187            ],
3188        )
3189        .expect_err("must reject missing branch immediate");
3190        assert_eq!(
3191            err,
3192            EncodeError::OperandCountRange {
3193                min: 3,
3194                max: 3,
3195                got: 2,
3196            }
3197        );
3198    }
3199
3200    #[test]
3201    fn no_matching_variant_hint_includes_expected_shapes() {
3202        let err = encode(
3203            &[STR_64_POS_SPEC],
3204            "str",
3205            &[
3206                Operand::Register(RegisterOperand {
3207                    code: 0,
3208                    class: RegClass::X,
3209                    arrangement: None,
3210                    lane: None,
3211                }),
3212                Operand::Memory(MemoryOperand {
3213                    base: RegisterOperand {
3214                        code: 1,
3215                        class: RegClass::X,
3216                        arrangement: None,
3217                        lane: None,
3218                    },
3219                    offset: MemoryOffset::Immediate(16),
3220                    addressing: AddressingMode::PreIndex,
3221                }),
3222            ],
3223        )
3224        .expect_err("shape mismatch should return hint");
3225
3226        match err {
3227            EncodeError::NoMatchingVariantHint {
3228                hint:
3229                    NoMatchingHint::Core(CoreNoMatchHint::ShapeMismatch {
3230                        expected,
3231                        expected_additional,
3232                        got,
3233                    }),
3234            } => {
3235                assert_eq!(expected_additional, 0);
3236                assert_eq!(expected.len(), 1);
3237                assert_eq!(
3238                    expected[0].slots.as_ref(),
3239                    &[
3240                        OperandConstraintKind::Gpr64Register,
3241                        OperandConstraintKind::Gpr64Register,
3242                        OperandConstraintKind::Immediate,
3243                    ]
3244                );
3245                assert_eq!(
3246                    got.slots.as_ref(),
3247                    &[OperandShapeTag::Gpr64, OperandShapeTag::Memory]
3248                );
3249            }
3250            other => panic!("unexpected error: {other:?}"),
3251        }
3252    }
3253
3254    #[test]
3255    fn no_offset_memory_constraint_rejects_explicit_zero_offset_without_count_range() {
3256        let err = encode(
3257            &[STLR_NO_OFFSET_SPEC],
3258            "stlr",
3259            &[
3260                Operand::Register(RegisterOperand {
3261                    code: 0,
3262                    class: RegClass::X,
3263                    arrangement: None,
3264                    lane: None,
3265                }),
3266                Operand::Memory(MemoryOperand {
3267                    base: RegisterOperand {
3268                        code: 1,
3269                        class: RegClass::X,
3270                        arrangement: None,
3271                        lane: None,
3272                    },
3273                    offset: MemoryOffset::Immediate(0),
3274                    addressing: AddressingMode::Offset,
3275                }),
3276            ],
3277        )
3278        .expect_err("stlr no-offset form must reject explicit #0");
3279
3280        match err {
3281            EncodeError::NoMatchingVariant | EncodeError::NoMatchingVariantHint { .. } => {}
3282            EncodeError::OperandCountRange { .. } => {
3283                panic!("explicit #0 on no-offset memory form must not map to operand count range")
3284            }
3285            other => panic!("unexpected error: {other:?}"),
3286        }
3287    }
3288
3289    #[test]
3290    fn operand_delta_hint_reports_missing_trailing_operand() {
3291        let hint = build_operand_delta_hint(
3292            &[ADD_SPEC],
3293            "add",
3294            &[
3295                Operand::Register(RegisterOperand {
3296                    code: 0,
3297                    class: RegClass::X,
3298                    arrangement: None,
3299                    lane: None,
3300                }),
3301                Operand::Register(RegisterOperand {
3302                    code: 1,
3303                    class: RegClass::X,
3304                    arrangement: None,
3305                    lane: None,
3306                }),
3307            ],
3308        )
3309        .expect("missing operand hint should be available");
3310        assert_eq!(
3311            hint,
3312            CoreNoMatchHint::OperandMissing {
3313                index: 3,
3314                expected: vec![OperandConstraintKind::Immediate].into_boxed_slice(),
3315            }
3316        );
3317    }
3318
3319    #[test]
3320    fn operand_delta_hint_reports_single_slot_kind_mismatch() {
3321        let hint = build_operand_delta_hint(
3322            &[ADD_SPEC],
3323            "add",
3324            &[
3325                Operand::Register(RegisterOperand {
3326                    code: 0,
3327                    class: RegClass::X,
3328                    arrangement: None,
3329                    lane: None,
3330                }),
3331                Operand::Immediate(7),
3332                Operand::Immediate(9),
3333            ],
3334        )
3335        .expect("kind mismatch hint should be available");
3336        assert_eq!(
3337            hint,
3338            CoreNoMatchHint::OperandKindMismatch {
3339                index: 2,
3340                expected: vec![OperandConstraintKind::Gpr64Register].into_boxed_slice(),
3341                got: OperandShapeTag::Immediate,
3342            }
3343        );
3344    }
3345
3346    #[test]
3347    fn mnemonic_selection_prefers_canonical_variant_for_conflicting_fixed_bits() {
3348        const WITH_DEFAULT: EncodingSpec = EncodingSpec {
3349            mnemonic: "pick",
3350            variant: "PICK_64_defaulted",
3351            opcode: 0x1000_0000,
3352            opcode_mask: 0xff00_0000,
3353            fields: &[
3354                BitFieldSpec {
3355                    name: "sh",
3356                    lsb: 22,
3357                    width: 1,
3358                    signed: false,
3359                },
3360                BitFieldSpec {
3361                    name: "imm12",
3362                    lsb: 10,
3363                    width: 12,
3364                    signed: false,
3365                },
3366                BitFieldSpec {
3367                    name: "Rn",
3368                    lsb: 5,
3369                    width: 5,
3370                    signed: false,
3371                },
3372                BitFieldSpec {
3373                    name: "Rd",
3374                    lsb: 0,
3375                    width: 5,
3376                    signed: false,
3377                },
3378            ],
3379            operand_order: &[3, 2, 1],
3380            operand_kinds: &[
3381                OperandConstraintKind::Gpr64Register,
3382                OperandConstraintKind::Gpr64Register,
3383                OperandConstraintKind::Immediate,
3384            ],
3385            implicit_defaults: &[ImplicitField {
3386                field_index: 0,
3387                value: 0,
3388            }],
3389            memory_addressing: MemoryAddressingConstraintSpec::None,
3390            field_scales: &[1, 1, 1, 1],
3391            split_immediate_plan: None,
3392            gpr32_extend_compatibility: 0,
3393        };
3394
3395        const NO_DEFAULT: EncodingSpec = EncodingSpec {
3396            mnemonic: "pick",
3397            variant: "PICK_64_direct",
3398            opcode: 0x2000_0000,
3399            opcode_mask: 0xff00_0000,
3400            fields: &[
3401                BitFieldSpec {
3402                    name: "imm12",
3403                    lsb: 10,
3404                    width: 12,
3405                    signed: false,
3406                },
3407                BitFieldSpec {
3408                    name: "Rn",
3409                    lsb: 5,
3410                    width: 5,
3411                    signed: false,
3412                },
3413                BitFieldSpec {
3414                    name: "Rd",
3415                    lsb: 0,
3416                    width: 5,
3417                    signed: false,
3418                },
3419            ],
3420            operand_order: &[2, 1, 0],
3421            operand_kinds: &[
3422                OperandConstraintKind::Gpr64Register,
3423                OperandConstraintKind::Gpr64Register,
3424                OperandConstraintKind::Immediate,
3425            ],
3426            implicit_defaults: &[],
3427            memory_addressing: MemoryAddressingConstraintSpec::None,
3428            field_scales: &[1, 1, 1],
3429            split_immediate_plan: None,
3430            gpr32_extend_compatibility: 0,
3431        };
3432
3433        let code = encode(
3434            &[WITH_DEFAULT, NO_DEFAULT],
3435            "pick",
3436            &[
3437                Operand::Register(RegisterOperand {
3438                    code: 1,
3439                    class: RegClass::X,
3440                    arrangement: None,
3441                    lane: None,
3442                }),
3443                Operand::Register(RegisterOperand {
3444                    code: 2,
3445                    class: RegClass::X,
3446                    arrangement: None,
3447                    lane: None,
3448                }),
3449                Operand::Immediate(7),
3450            ],
3451        )
3452        .expect("conflicting fixed bits should resolve to one canonical variant");
3453        let expected = encode_by_spec_operands(
3454            &NO_DEFAULT,
3455            &[
3456                Operand::Register(RegisterOperand {
3457                    code: 1,
3458                    class: RegClass::X,
3459                    arrangement: None,
3460                    lane: None,
3461                }),
3462                Operand::Register(RegisterOperand {
3463                    code: 2,
3464                    class: RegClass::X,
3465                    arrangement: None,
3466                    lane: None,
3467                }),
3468                Operand::Immediate(7),
3469            ],
3470        )
3471        .expect("no-default form should encode");
3472        assert_eq!(code.unpack(), expected.unpack());
3473    }
3474
3475    #[test]
3476    fn mnemonic_selection_prefers_specific_score_when_fixed_bits_are_compatible() {
3477        const PICK_NARROW: EncodingSpec = EncodingSpec {
3478            mnemonic: "pickcompat",
3479            variant: "PICKCOMPAT_narrow",
3480            opcode: 0x1000_0000,
3481            opcode_mask: 0xf000_0000,
3482            fields: &[
3483                BitFieldSpec {
3484                    name: "imm1",
3485                    lsb: 0,
3486                    width: 1,
3487                    signed: false,
3488                },
3489                BitFieldSpec {
3490                    name: "Rn",
3491                    lsb: 5,
3492                    width: 5,
3493                    signed: false,
3494                },
3495                BitFieldSpec {
3496                    name: "Rd",
3497                    lsb: 10,
3498                    width: 5,
3499                    signed: false,
3500                },
3501            ],
3502            operand_order: &[2, 1, 0],
3503            operand_kinds: &[
3504                OperandConstraintKind::Gpr64Register,
3505                OperandConstraintKind::Gpr64Register,
3506                OperandConstraintKind::Immediate,
3507            ],
3508            implicit_defaults: &[],
3509            memory_addressing: MemoryAddressingConstraintSpec::None,
3510            field_scales: &[1, 1, 1],
3511            split_immediate_plan: None,
3512            gpr32_extend_compatibility: 0,
3513        };
3514
3515        const PICK_WIDE_DEFAULTED: EncodingSpec = EncodingSpec {
3516            mnemonic: "pickcompat",
3517            variant: "PICKCOMPAT_wide_defaulted",
3518            opcode: 0x1000_0000,
3519            opcode_mask: 0xf000_0000,
3520            fields: &[
3521                BitFieldSpec {
3522                    name: "sh",
3523                    lsb: 22,
3524                    width: 1,
3525                    signed: false,
3526                },
3527                BitFieldSpec {
3528                    name: "imm12",
3529                    lsb: 10,
3530                    width: 12,
3531                    signed: false,
3532                },
3533                BitFieldSpec {
3534                    name: "Rn",
3535                    lsb: 5,
3536                    width: 5,
3537                    signed: false,
3538                },
3539                BitFieldSpec {
3540                    name: "Rd",
3541                    lsb: 0,
3542                    width: 5,
3543                    signed: false,
3544                },
3545            ],
3546            operand_order: &[3, 2, 1],
3547            operand_kinds: &[
3548                OperandConstraintKind::Gpr64Register,
3549                OperandConstraintKind::Gpr64Register,
3550                OperandConstraintKind::Immediate,
3551            ],
3552            implicit_defaults: &[ImplicitField {
3553                field_index: 0,
3554                value: 0,
3555            }],
3556            memory_addressing: MemoryAddressingConstraintSpec::None,
3557            field_scales: &[1, 1, 1, 1],
3558            split_immediate_plan: None,
3559            gpr32_extend_compatibility: 0,
3560        };
3561
3562        let operands = [
3563            Operand::Register(RegisterOperand {
3564                code: 1,
3565                class: RegClass::X,
3566                arrangement: None,
3567                lane: None,
3568            }),
3569            Operand::Register(RegisterOperand {
3570                code: 2,
3571                class: RegClass::X,
3572                arrangement: None,
3573                lane: None,
3574            }),
3575            Operand::Immediate(1),
3576        ];
3577
3578        let selected = encode(&[PICK_WIDE_DEFAULTED, PICK_NARROW], "pickcompat", &operands)
3579            .expect("compatible fixed bits should allow score-based selection");
3580        let expected = encode_by_spec_operands(&PICK_NARROW, &operands)
3581            .expect("narrow immediate form should encode");
3582        assert_eq!(selected.unpack(), expected.unpack());
3583    }
3584
3585    #[test]
3586    fn mnemonic_selection_uses_stable_order_on_score_tie() {
3587        const PICK_A: EncodingSpec = EncodingSpec {
3588            mnemonic: "picktie",
3589            variant: "PICKTIE_64_a",
3590            opcode: 0x4000_0000,
3591            opcode_mask: 0xf000_0000,
3592            fields: &[
3593                BitFieldSpec {
3594                    name: "Rn",
3595                    lsb: 5,
3596                    width: 5,
3597                    signed: false,
3598                },
3599                BitFieldSpec {
3600                    name: "Rd",
3601                    lsb: 0,
3602                    width: 5,
3603                    signed: false,
3604                },
3605            ],
3606            operand_order: &[1, 0],
3607            operand_kinds: &[
3608                OperandConstraintKind::Gpr64Register,
3609                OperandConstraintKind::Gpr64Register,
3610            ],
3611            implicit_defaults: &[],
3612            memory_addressing: MemoryAddressingConstraintSpec::None,
3613            field_scales: &[1, 1],
3614            split_immediate_plan: None,
3615            gpr32_extend_compatibility: 0,
3616        };
3617
3618        const PICK_B: EncodingSpec = EncodingSpec {
3619            mnemonic: "picktie",
3620            variant: "PICKTIE_64_b",
3621            opcode: 0x4000_0000,
3622            opcode_mask: 0xf000_0000,
3623            fields: PICK_A.fields,
3624            operand_order: &[0, 1],
3625            operand_kinds: PICK_A.operand_kinds,
3626            implicit_defaults: PICK_A.implicit_defaults,
3627            memory_addressing: PICK_A.memory_addressing,
3628            field_scales: PICK_A.field_scales,
3629            split_immediate_plan: PICK_A.split_immediate_plan,
3630            gpr32_extend_compatibility: PICK_A.gpr32_extend_compatibility,
3631        };
3632
3633        let code = encode(
3634            &[PICK_A, PICK_B],
3635            "picktie",
3636            &[
3637                Operand::Register(RegisterOperand {
3638                    code: 1,
3639                    class: RegClass::X,
3640                    arrangement: None,
3641                    lane: None,
3642                }),
3643                Operand::Register(RegisterOperand {
3644                    code: 2,
3645                    class: RegClass::X,
3646                    arrangement: None,
3647                    lane: None,
3648                }),
3649            ],
3650        )
3651        .expect("equal-score candidates should resolve deterministically");
3652        let expected = encode_by_spec_operands(
3653            &PICK_A,
3654            &[
3655                Operand::Register(RegisterOperand {
3656                    code: 1,
3657                    class: RegClass::X,
3658                    arrangement: None,
3659                    lane: None,
3660                }),
3661                Operand::Register(RegisterOperand {
3662                    code: 2,
3663                    class: RegClass::X,
3664                    arrangement: None,
3665                    lane: None,
3666                }),
3667            ],
3668        )
3669        .expect("first tie candidate should encode");
3670        assert_eq!(code.unpack(), expected.unpack());
3671    }
3672}