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::Offset, AddressingMode::Offset) => true,
551            (MemoryAddressingConstraintSpec::PreIndex, AddressingMode::PreIndex) => true,
552            (MemoryAddressingConstraintSpec::PostIndex, AddressingMode::PostIndex(_)) => true,
553            (_, _) => false,
554        },
555        _ => true,
556    })
557}
558
559#[derive(Debug, Copy, Clone, PartialEq, Eq)]
560struct SplitImmediatePlan {
561    first_slot: usize,
562    second_slot: usize,
563    kind: SplitImmediateKind,
564}
565
566#[derive(Debug, Copy, Clone, PartialEq, Eq)]
567enum SplitImmediateKind {
568    AdrLike {
569        immlo_field_idx: usize,
570        immhi_field_idx: usize,
571        scale: i64,
572    },
573    BitIndex6 {
574        b5_field_idx: usize,
575        b40_field_idx: usize,
576    },
577}
578
579#[inline]
580fn spec_split_immediate_plan(spec: &EncodingSpec) -> Option<SplitImmediatePlan> {
581    spec.split_immediate_plan.map(|plan| SplitImmediatePlan {
582        first_slot: usize::from(plan.first_slot),
583        second_slot: usize::from(plan.second_slot),
584        kind: match plan.kind {
585            SplitImmediateKindSpec::AdrLike {
586                immlo_field_index,
587                immhi_field_index,
588                scale,
589            } => SplitImmediateKind::AdrLike {
590                immlo_field_idx: usize::from(immlo_field_index),
591                immhi_field_idx: usize::from(immhi_field_index),
592                scale,
593            },
594            SplitImmediateKindSpec::BitIndex6 {
595                b5_field_index,
596                b40_field_index,
597            } => SplitImmediateKind::BitIndex6 {
598                b5_field_idx: usize::from(b5_field_index),
599                b40_field_idx: usize::from(b40_field_index),
600            },
601        },
602    })
603}
604
605#[inline]
606fn spec_maybe_split_immediate(spec: &EncodingSpec) -> bool {
607    spec.split_immediate_plan.is_some()
608}
609
610fn scale_immediate(field: &'static str, value: i64, scale: i64) -> Result<i64, EncodeError> {
611    if scale <= 1 {
612        return Ok(value);
613    }
614    if value % scale != 0 {
615        return Err(EncodeError::ImmediateNotAligned {
616            field,
617            value,
618            scale,
619        });
620    }
621    Ok(value / scale)
622}
623
624fn normalize_field_value(
625    spec: &EncodingSpec,
626    field: BitFieldSpec,
627    field_idx: usize,
628    value: i64,
629) -> Result<i64, EncodeError> {
630    let scale = spec.field_scales.get(field_idx).copied().unwrap_or(1);
631    scale_immediate(field.name, value, i64::from(scale))
632}
633
634#[inline]
635fn encode_field(field: BitFieldSpec, value: i64) -> Result<u32, EncodeError> {
636    let raw = if field.signed {
637        if field.width == 0 || field.width > 32 {
638            return Err(EncodeError::OperandOutOfRange {
639                field: field.name,
640                value,
641                width: field.width,
642                signed: true,
643            });
644        }
645
646        let min = -(1i64 << (field.width - 1));
647        let max = (1i64 << (field.width - 1)) - 1;
648        if value < min || value > max {
649            return Err(EncodeError::OperandOutOfRange {
650                field: field.name,
651                value,
652                width: field.width,
653                signed: true,
654            });
655        }
656
657        (value as i32 as u32) & field_mask(field.width)
658    } else {
659        if value < 0 {
660            return Err(EncodeError::OperandOutOfRange {
661                field: field.name,
662                value,
663                width: field.width,
664                signed: false,
665            });
666        }
667
668        let max = i64::from(field_mask(field.width));
669        if value > max {
670            return Err(EncodeError::OperandOutOfRange {
671                field: field.name,
672                value,
673                width: field.width,
674                signed: false,
675            });
676        }
677        value as u32
678    };
679
680    Ok(raw << field.lsb)
681}
682
683/// Encodes one instruction variant using structured operands in asm-like order.
684///
685/// Field mapping and kind checks are driven by generated metadata stored in
686/// [`EncodingSpec::operand_order`], [`EncodingSpec::operand_kinds`] and
687/// [`EncodingSpec::implicit_defaults`].
688///
689/// # Errors
690///
691/// Returns [`EncodeError`] when operand count, kind, or ranges are invalid.
692#[inline]
693fn encode_flat_ordered(
694    spec: &EncodingSpec,
695    selected_flat: &[FlatOperand],
696    selected_len: usize,
697    split_plan: Option<SplitImmediatePlan>,
698) -> Result<InstructionCode, EncodeError> {
699    // Values are collected by field index first, then emitted in field order.
700    // This keeps split-immediate and implicit-default handling centralized.
701    let mut values = [0i64; 64];
702    let mut filled_mask = 0u64;
703    let mut input_idx = 0usize;
704    let mut slot = 0usize;
705    while slot < spec.operand_order.len() {
706        if let Some(plan) = split_plan {
707            if slot == plan.first_slot {
708                if input_idx >= selected_len {
709                    return Err(EncodeError::OperandCountMismatch);
710                }
711
712                let first_field_idx = spec.operand_order[slot] as usize;
713                let first_field = spec.fields[first_field_idx];
714                let expected = spec.operand_kinds[slot];
715                let actual = selected_flat[input_idx];
716                if !kind_matches_for_slot(spec, slot, expected, actual.kind) {
717                    return Err(EncodeError::InvalidOperandKind {
718                        field: first_field.name,
719                        expected: expected_kind_name(expected),
720                        got: expected_kind_name(actual.kind),
721                    });
722                }
723
724                match plan.kind {
725                    SplitImmediateKind::AdrLike {
726                        immlo_field_idx,
727                        immhi_field_idx,
728                        scale,
729                    } => {
730                        let encoded = scale_immediate(first_field.name, actual.value, scale)?;
731                        if immlo_field_idx >= values.len() || immhi_field_idx >= values.len() {
732                            return Err(EncodeError::OperandCountMismatch);
733                        }
734                        values[immlo_field_idx] = encoded & 0b11;
735                        values[immhi_field_idx] = encoded >> 2;
736                        filled_mask |= 1u64 << immlo_field_idx;
737                        filled_mask |= 1u64 << immhi_field_idx;
738                    }
739                    SplitImmediateKind::BitIndex6 {
740                        b5_field_idx,
741                        b40_field_idx,
742                    } => {
743                        if b5_field_idx >= values.len() || b40_field_idx >= values.len() {
744                            return Err(EncodeError::OperandCountMismatch);
745                        }
746                        values[b5_field_idx] = actual.value >> 5;
747                        values[b40_field_idx] = actual.value & 0x1f;
748                        filled_mask |= 1u64 << b5_field_idx;
749                        filled_mask |= 1u64 << b40_field_idx;
750                    }
751                }
752                input_idx += 1;
753                slot += 1;
754                continue;
755            }
756
757            if slot == plan.second_slot {
758                slot += 1;
759                continue;
760            }
761        }
762
763        if input_idx >= selected_len {
764            return Err(EncodeError::OperandCountMismatch);
765        }
766
767        let field_idx = spec.operand_order[slot] as usize;
768        if field_idx >= spec.fields.len() || field_idx >= values.len() {
769            return Err(EncodeError::OperandCountMismatch);
770        }
771
772        let field = spec.fields[field_idx];
773        let expected = spec.operand_kinds[slot];
774        let actual = selected_flat[input_idx];
775        if !kind_matches_for_slot(spec, slot, expected, actual.kind) {
776            return Err(EncodeError::InvalidOperandKind {
777                field: field.name,
778                expected: expected_kind_name(expected),
779                got: expected_kind_name(actual.kind),
780            });
781        }
782        values[field_idx] = actual.value;
783        filled_mask |= 1u64 << field_idx;
784        input_idx += 1;
785        slot += 1;
786    }
787
788    if input_idx != selected_len {
789        return Err(EncodeError::OperandCountMismatch);
790    }
791
792    for implicit in spec.implicit_defaults {
793        let idx = implicit.field_index as usize;
794        if idx < spec.fields.len() && idx < values.len() && ((filled_mask >> idx) & 1) == 0 {
795            values[idx] = implicit.value;
796            filled_mask |= 1u64 << idx;
797        }
798    }
799
800    let mut args = [0i64; 64];
801    for idx in 0..spec.fields.len() {
802        if idx >= values.len() || ((filled_mask >> idx) & 1) == 0 {
803            return Err(EncodeError::OperandCountMismatch);
804        }
805        let raw = values[idx];
806        args[idx] = normalize_field_value(spec, spec.fields[idx], idx, raw)?;
807    }
808
809    encode_by_spec(spec, &args[..spec.fields.len()])
810}
811
812fn spec_has_arrangement_lane(spec: &EncodingSpec) -> bool {
813    spec.operand_kinds.iter().any(|kind| {
814        matches!(
815            kind,
816            OperandConstraintKind::Arrangement | OperandConstraintKind::Lane
817        )
818    })
819}
820
821fn flat_has_arrangement_lane(flat: &[FlatOperand], len: usize) -> bool {
822    flat[..len].iter().any(|operand| {
823        matches!(
824            operand.kind,
825            OperandConstraintKind::Arrangement | OperandConstraintKind::Lane
826        )
827    })
828}
829
830fn reorder_flat_arrangement_lane_for_expected(
831    spec: &EncodingSpec,
832    flat: &[FlatOperand],
833    len: usize,
834    out: &mut [FlatOperand; 64],
835) -> Option<usize> {
836    let mut expected = [OperandConstraintKind::Immediate; 64];
837    let expected_len = expected_user_operand_kinds(spec, &mut expected);
838    if expected_len != len {
839        return None;
840    }
841
842    let mut non_arr = [FlatOperand {
843        kind: OperandConstraintKind::Immediate,
844        value: 0,
845        optional: false,
846    }; 64];
847    let mut arr = [FlatOperand {
848        kind: OperandConstraintKind::Immediate,
849        value: 0,
850        optional: false,
851    }; 64];
852    let mut non_arr_len = 0usize;
853    let mut arr_len = 0usize;
854
855    for operand in &flat[..len] {
856        if matches!(
857            operand.kind,
858            OperandConstraintKind::Arrangement | OperandConstraintKind::Lane
859        ) {
860            arr[arr_len] = *operand;
861            arr_len += 1;
862        } else {
863            non_arr[non_arr_len] = *operand;
864            non_arr_len += 1;
865        }
866    }
867
868    let mut non_idx = 0usize;
869    let mut arr_idx = 0usize;
870    let mut out_len = 0usize;
871    for expected_kind in expected[..expected_len].iter().copied() {
872        if matches!(
873            expected_kind,
874            OperandConstraintKind::Arrangement | OperandConstraintKind::Lane
875        ) {
876            if arr_idx >= arr_len {
877                return None;
878            }
879            if arr[arr_idx].kind != expected_kind {
880                let Some(rel_idx) = arr[arr_idx..arr_len]
881                    .iter()
882                    .position(|operand| operand.kind == expected_kind)
883                else {
884                    return None;
885                };
886                arr.swap(arr_idx, arr_idx + rel_idx);
887            }
888            out[out_len] = arr[arr_idx];
889            arr_idx += 1;
890            out_len += 1;
891        } else {
892            if non_idx >= non_arr_len {
893                return None;
894            }
895            out[out_len] = non_arr[non_idx];
896            non_idx += 1;
897            out_len += 1;
898        }
899    }
900
901    if non_idx != non_arr_len || arr_idx != arr_len {
902        return None;
903    }
904    Some(out_len)
905}
906
907#[inline]
908fn encode_flat(
909    spec: &EncodingSpec,
910    selected_flat: &[FlatOperand],
911    selected_len: usize,
912    split_plan: Option<SplitImmediatePlan>,
913) -> Result<InstructionCode, EncodeError> {
914    // Fast path: direct ordered encoding. If that fails and both expected/actual
915    // streams contain arrangement/lane tokens, retry with a stable reorder that
916    // aligns trailing arrangement/lane decorations to the expected slots.
917    let direct = encode_flat_ordered(spec, selected_flat, selected_len, split_plan);
918    if direct.is_ok()
919        || !spec_has_arrangement_lane(spec)
920        || !flat_has_arrangement_lane(selected_flat, selected_len)
921    {
922        return direct;
923    }
924
925    let mut reordered = [FlatOperand {
926        kind: OperandConstraintKind::Immediate,
927        value: 0,
928        optional: false,
929    }; 64];
930    let Some(reordered_len) = reorder_flat_arrangement_lane_for_expected(
931        spec,
932        selected_flat,
933        selected_len,
934        &mut reordered,
935    ) else {
936        return direct;
937    };
938    if reordered_len != selected_len || reordered[..selected_len] == selected_flat[..selected_len] {
939        return direct;
940    }
941
942    match encode_flat_ordered(spec, &reordered, reordered_len, split_plan) {
943        Ok(code) => Ok(code),
944        Err(_) => direct,
945    }
946}
947
948fn diagnostic_priority(err: &EncodeError) -> u8 {
949    match err {
950        EncodeError::OperandOutOfRange { .. } | EncodeError::ImmediateNotAligned { .. } => 0,
951        EncodeError::InvalidOperandKind { .. } => 1,
952        EncodeError::OperandCountMismatch | EncodeError::OperandCountRange { .. } => 2,
953        EncodeError::NoMatchingVariant | EncodeError::NoMatchingVariantHint { .. } => 3,
954        EncodeError::AmbiguousVariant | EncodeError::UnknownMnemonic => 4,
955    }
956}
957
958fn prefer_diagnostic_error(lhs: EncodeError, rhs: EncodeError) -> EncodeError {
959    if diagnostic_priority(&rhs) < diagnostic_priority(&lhs) {
960        rhs
961    } else {
962        lhs
963    }
964}
965
966#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
967struct CandidateScore {
968    fixed_bits: u16,
969    kind_specificity: u16,
970    immediate_narrowness: u16,
971    explicit_operands: u8,
972    implicit_penalty: u8,
973}
974
975#[inline]
976fn kind_specificity(kind: OperandConstraintKind) -> u16 {
977    match kind {
978        OperandConstraintKind::GprRegister => 3,
979        OperandConstraintKind::Gpr32Register
980        | OperandConstraintKind::Gpr64Register
981        | OperandConstraintKind::SimdRegister
982        | OperandConstraintKind::SveZRegister
983        | OperandConstraintKind::PredicateRegister => 4,
984        OperandConstraintKind::Immediate => 1,
985        OperandConstraintKind::Condition
986        | OperandConstraintKind::ShiftKind
987        | OperandConstraintKind::ExtendKind
988        | OperandConstraintKind::SysRegPart
989        | OperandConstraintKind::Arrangement
990        | OperandConstraintKind::Lane => 2,
991    }
992}
993
994fn candidate_score(spec: &EncodingSpec) -> CandidateScore {
995    // Tie-break candidates deterministically so "best" means:
996    // - more fixed opcode bits,
997    // - more specific operand kinds,
998    // - narrower immediate fields,
999    // - fewer implicit defaults.
1000    let fixed_bits = spec.opcode_mask.count_ones().min(u32::from(u16::MAX)) as u16;
1001
1002    let mut kind_specificity_sum = 0u16;
1003    let mut immediate_narrowness = 0u16;
1004    for (slot, kind) in spec.operand_kinds.iter().copied().enumerate() {
1005        kind_specificity_sum = kind_specificity_sum.saturating_add(kind_specificity(kind));
1006        if kind == OperandConstraintKind::Immediate {
1007            let Some(field_idx) = spec.operand_order.get(slot).copied() else {
1008                continue;
1009            };
1010            let field_idx = field_idx as usize;
1011            if let Some(field) = spec.fields.get(field_idx) {
1012                immediate_narrowness =
1013                    immediate_narrowness.saturating_add((64u16).saturating_sub(field.width as u16));
1014            }
1015        }
1016    }
1017
1018    let explicit_operands = spec
1019        .operand_order
1020        .len()
1021        .saturating_sub(spec.implicit_defaults.len())
1022        .min(usize::from(u8::MAX)) as u8;
1023
1024    let implicit_penalty = (u8::MAX as usize)
1025        .saturating_sub(spec.implicit_defaults.len().min(usize::from(u8::MAX)))
1026        as u8;
1027
1028    CandidateScore {
1029        fixed_bits,
1030        kind_specificity: kind_specificity_sum,
1031        immediate_narrowness,
1032        explicit_operands,
1033        implicit_penalty,
1034    }
1035}
1036
1037#[inline]
1038fn encode_by_spec_from_flattened(
1039    spec: &EncodingSpec,
1040    flat: &[FlatOperand],
1041    flat_len: usize,
1042) -> Result<InstructionCode, EncodeError> {
1043    if spec.fields.len() > 64 {
1044        return Err(EncodeError::OperandCountMismatch);
1045    }
1046    let split_before_materialize = if flat_len + 1 == spec.operand_order.len() {
1047        if spec_maybe_split_immediate(spec) {
1048            spec_split_immediate_plan(spec)
1049        } else {
1050            None
1051        }
1052    } else {
1053        None
1054    };
1055    let expected_flat_len = if split_before_materialize.is_some() {
1056        spec.operand_order
1057            .len()
1058            .checked_sub(1)
1059            .ok_or(EncodeError::OperandCountMismatch)?
1060    } else {
1061        spec.operand_order.len()
1062    };
1063    let mut selected_flat = [FlatOperand {
1064        kind: OperandConstraintKind::Immediate,
1065        value: 0,
1066        optional: false,
1067    }; 64];
1068    let selected_len = materialize_flat_for_expected_len(
1069        expected_flat_len,
1070        &flat[..flat_len],
1071        &mut selected_flat,
1072    )?;
1073    encode_flat(spec, &selected_flat, selected_len, split_before_materialize)
1074}
1075
1076/// Returns [`EncodeError`] when operand count, kind, or ranges are invalid.
1077#[must_use]
1078pub fn encode_by_spec_operands(
1079    spec: &EncodingSpec,
1080    operands: &[Operand],
1081) -> Result<InstructionCode, EncodeError> {
1082    if spec.fields.len() > 64 {
1083        return Err(EncodeError::OperandCountMismatch);
1084    }
1085    if spec.operand_order.len() != spec.operand_kinds.len() {
1086        return Err(EncodeError::OperandCountMismatch);
1087    }
1088
1089    let mut flat = [FlatOperand {
1090        kind: OperandConstraintKind::Immediate,
1091        value: 0,
1092        optional: false,
1093    }; 64];
1094    let flat_len = flatten_operands(operands, &mut flat)?;
1095    encode_by_spec_from_flattened(spec, &flat, flat_len)
1096}
1097
1098/// Encodes one mnemonic using a generated shortlist of candidate variant indices.
1099///
1100/// The shortlist is expected to contain only variants for the same mnemonic.
1101/// This function keeps strict validation and ambiguity handling while avoiding
1102/// a full linear scan over all mnemonic variants.
1103///
1104/// # Errors
1105///
1106/// Returns [`EncodeError`] when no candidate can be encoded unambiguously.
1107#[must_use]
1108pub fn encode_candidates(
1109    specs: &[EncodingSpec],
1110    candidate_indices: &[u16],
1111    operands: &[Operand],
1112) -> Result<InstructionCode, EncodeError> {
1113    if candidate_indices.is_empty() {
1114        return Err(EncodeError::NoMatchingVariant);
1115    }
1116
1117    let mut flat_operands = [FlatOperand {
1118        kind: OperandConstraintKind::Immediate,
1119        value: 0,
1120        optional: false,
1121    }; 64];
1122    let flat_len = flatten_operands(operands, &mut flat_operands)?;
1123
1124    let mut best_detail_error: Option<EncodeError> = None;
1125    let mut selected: Option<(InstructionCode, CandidateScore)> = None;
1126    let mut selected_mask = 0u32;
1127    let mut selected_bits = 0u32;
1128    let mut saw_count_mismatch = false;
1129    let mut saw_shape_mismatch = false;
1130
1131    for candidate_index in candidate_indices.iter().copied() {
1132        let Some(spec) = specs.get(usize::from(candidate_index)) else {
1133            continue;
1134        };
1135        if !spec_matches_memory_addressing(spec, operands) {
1136            saw_shape_mismatch = true;
1137            continue;
1138        }
1139
1140        match encode_by_spec_from_flattened(spec, &flat_operands, flat_len) {
1141            Ok(code) => {
1142                let overlap = selected_mask & spec.opcode_mask;
1143                if ((selected_bits ^ spec.opcode) & overlap) != 0 {
1144                    return Err(EncodeError::AmbiguousVariant);
1145                }
1146                let next_mask = selected_mask | spec.opcode_mask;
1147                let next_bits = (selected_bits & !spec.opcode_mask) | spec.opcode;
1148                let score = candidate_score(spec);
1149                let code_matches_combined = (code.unpack() & next_mask) == next_bits;
1150                if let Some((prev_code, prev_score)) = selected {
1151                    let prev_matches_combined = (prev_code.unpack() & next_mask) == next_bits;
1152                    if !prev_matches_combined && !code_matches_combined {
1153                        return Err(EncodeError::AmbiguousVariant);
1154                    }
1155                    selected_mask = next_mask;
1156                    selected_bits = next_bits;
1157                    if prev_code.unpack() == code.unpack() {
1158                        if score > prev_score {
1159                            selected = Some((code, score));
1160                        }
1161                        continue;
1162                    }
1163                    if !code_matches_combined {
1164                        continue;
1165                    }
1166                    if !prev_matches_combined {
1167                        selected = Some((code, score));
1168                        continue;
1169                    }
1170                    match score.cmp(&prev_score) {
1171                        core::cmp::Ordering::Greater => {
1172                            selected = Some((code, score));
1173                        }
1174                        core::cmp::Ordering::Less => {}
1175                        core::cmp::Ordering::Equal => {
1176                            return Err(EncodeError::AmbiguousVariant);
1177                        }
1178                    }
1179                    continue;
1180                }
1181                selected_mask = next_mask;
1182                selected_bits = next_bits;
1183                if !code_matches_combined {
1184                    return Err(EncodeError::AmbiguousVariant);
1185                }
1186                selected = Some((code, score));
1187            }
1188            Err(EncodeError::OperandCountMismatch) => {
1189                saw_count_mismatch = true;
1190            }
1191            Err(
1192                err @ (EncodeError::OperandOutOfRange { .. }
1193                | EncodeError::ImmediateNotAligned { .. }
1194                | EncodeError::InvalidOperandKind { .. }
1195                | EncodeError::NoMatchingVariant
1196                | EncodeError::NoMatchingVariantHint { .. }),
1197            ) => {
1198                saw_shape_mismatch = true;
1199                best_detail_error = Some(match best_detail_error {
1200                    Some(current) => prefer_diagnostic_error(current, err),
1201                    None => err,
1202                });
1203            }
1204            Err(err) => return Err(err),
1205        }
1206    }
1207
1208    if let Some((code, _)) = selected {
1209        return Ok(code);
1210    }
1211    if let Some(detail) = best_detail_error {
1212        return Err(detail);
1213    }
1214    if saw_shape_mismatch || saw_count_mismatch {
1215        return Err(EncodeError::NoMatchingVariant);
1216    }
1217    Err(EncodeError::NoMatchingVariant)
1218}
1219
1220/// Computes packed operand-shape keys for all materialized flattenings of `operands`.
1221///
1222/// Some operands can contribute optional flattened fields (for example bare `[base]`
1223/// memory offsets). This function returns one key per valid materialized shape.
1224///
1225/// # Errors
1226///
1227/// Returns [`EncodeError`] when flattening fails or `out` is too small.
1228#[must_use]
1229pub fn operand_shape_keys(
1230    operands: &[Operand],
1231    out: &mut [OperandShapeKey],
1232) -> Result<usize, EncodeError> {
1233    let memory_shape_code = memory_shape_code_from_operands(operands);
1234    let mut flat = [FlatOperand {
1235        kind: OperandConstraintKind::Immediate,
1236        value: 0,
1237        optional: false,
1238    }; 64];
1239    let flat_len = flatten_operands(operands, &mut flat)?;
1240    let mut required_len = 0usize;
1241    let mut optional_len = 0usize;
1242    for operand in &flat[..flat_len] {
1243        if operand.optional {
1244            optional_len += 1;
1245        } else {
1246            required_len += 1;
1247        }
1248    }
1249
1250    let mut selected = [FlatOperand {
1251        kind: OperandConstraintKind::Immediate,
1252        value: 0,
1253        optional: false,
1254    }; 64];
1255    let mut out_len = 0usize;
1256    let mut kind_codes = [0u8; 64];
1257    for expected_len in required_len..=required_len + optional_len {
1258        let selected_len =
1259            materialize_flat_for_expected_len(expected_len, &flat[..flat_len], &mut selected)?;
1260        for (idx, operand) in selected[..selected_len].iter().enumerate() {
1261            kind_codes[idx] = operand_shape_code(operand.kind);
1262        }
1263
1264        let mut key_len = selected_len;
1265        if memory_shape_code.is_some() {
1266            key_len = key_len.saturating_add(1);
1267        }
1268        if key_len > 30 {
1269            continue;
1270        }
1271        let mut key = key_len as OperandShapeKey;
1272        for (slot, code) in kind_codes[..selected_len].iter().copied().enumerate() {
1273            let shift = 8 + (slot * 4);
1274            key |= OperandShapeKey::from(code) << shift;
1275        }
1276        if let Some(memory_shape_code) = memory_shape_code {
1277            let shift = 8 + (selected_len * 4);
1278            key |= OperandShapeKey::from(memory_shape_code) << shift;
1279        }
1280
1281        if out[..out_len].contains(&key) {
1282            continue;
1283        }
1284        if out_len >= out.len() {
1285            return Err(EncodeError::OperandCountMismatch);
1286        }
1287        out[out_len] = key;
1288        out_len += 1;
1289    }
1290    Ok(out_len)
1291}
1292
1293/// Encodes one instruction variant by spec.
1294///
1295/// # Errors
1296///
1297/// Returns [`EncodeError`] when operand count or operand ranges are invalid.
1298#[must_use]
1299pub fn encode_by_spec(spec: &EncodingSpec, args: &[i64]) -> Result<InstructionCode, EncodeError> {
1300    if spec.fields.len() != args.len() {
1301        return Err(EncodeError::OperandCountMismatch);
1302    }
1303
1304    let mut word = spec.opcode;
1305    for (field, arg) in spec.fields.iter().copied().zip(args.iter().copied()) {
1306        let encoded = encode_field(field, arg)?;
1307        word |= encoded;
1308    }
1309
1310    Ok(InstructionCode::from_u32(word))
1311}
1312
1313fn mnemonic_has_memory_immediate_variants(specs: &[EncodingSpec], mnemonic: &str) -> bool {
1314    specs.iter().any(|spec| {
1315        spec.mnemonic == mnemonic
1316            && spec.memory_addressing != MemoryAddressingConstraintSpec::None
1317            && (spec_has_field(spec, "imm12")
1318                || spec_has_field(spec, "imm9")
1319                || spec_has_field(spec, "imm7"))
1320            && spec_has_field(spec, "rn")
1321            && spec_has_field(spec, "rt")
1322    })
1323}
1324
1325fn operands_have_bare_memory_offset(operands: &[Operand]) -> bool {
1326    operands.iter().any(|operand| {
1327        matches!(
1328            operand,
1329            Operand::Memory(MemoryOperand {
1330                offset: MemoryOffset::None,
1331                addressing: AddressingMode::Offset,
1332                ..
1333            })
1334        )
1335    })
1336}
1337
1338fn operands_have_mixed_gpr_width(operands: &[Operand]) -> bool {
1339    let mut saw_w = false;
1340    let mut saw_x = false;
1341    for operand in operands {
1342        if let Operand::Register(reg) = operand {
1343            match reg.class {
1344                RegClass::W | RegClass::Wsp => saw_w = true,
1345                RegClass::X | RegClass::Xsp => saw_x = true,
1346                _ => {}
1347            }
1348        }
1349    }
1350    saw_w && saw_x
1351}
1352
1353fn expected_user_operand_kinds(
1354    spec: &EncodingSpec,
1355    out: &mut [OperandConstraintKind; 64],
1356) -> usize {
1357    let split = spec_split_immediate_plan(spec);
1358    let mut out_len = 0usize;
1359    let mut slot = 0usize;
1360    while slot < spec.operand_kinds.len() {
1361        if let Some(plan) = split {
1362            if slot == plan.first_slot {
1363                out[out_len] = OperandConstraintKind::Immediate;
1364                out_len += 1;
1365                slot = slot.saturating_add(2);
1366                continue;
1367            }
1368        }
1369
1370        out[out_len] = spec.operand_kinds[slot];
1371        out_len += 1;
1372        slot += 1;
1373    }
1374    out_len
1375}
1376
1377fn infer_no_matching_hint(
1378    specs: &[EncodingSpec],
1379    mnemonic: &str,
1380    operands: &[Operand],
1381) -> Option<CoreNoMatchHint> {
1382    // Keep this heuristic-only and data-driven by operand forms/fields.
1383    // No opcode-name based acceptance logic is introduced here.
1384    if operands_have_bare_memory_offset(operands)
1385        && mnemonic_has_memory_immediate_variants(specs, mnemonic)
1386    {
1387        return Some(CoreNoMatchHint::MemoryMayRequireExplicitZeroOffset);
1388    }
1389
1390    if operands_have_mixed_gpr_width(operands) {
1391        return Some(CoreNoMatchHint::RegisterWidthMismatch);
1392    }
1393
1394    None
1395}
1396
1397fn operand_shape_tag(operand: &Operand) -> OperandShapeTag {
1398    match operand {
1399        Operand::Register(reg) => match reg.class {
1400            RegClass::W | RegClass::Wsp => OperandShapeTag::Gpr32,
1401            RegClass::X | RegClass::Xsp => OperandShapeTag::Gpr64,
1402            RegClass::V | RegClass::B | RegClass::H | RegClass::S | RegClass::D | RegClass::Q => {
1403                OperandShapeTag::Simd
1404            }
1405            RegClass::Z => OperandShapeTag::SveZ,
1406            RegClass::P => OperandShapeTag::Predicate,
1407        },
1408        Operand::Immediate(_) => OperandShapeTag::Immediate,
1409        Operand::Memory(_) => OperandShapeTag::Memory,
1410        Operand::Shift(_) => OperandShapeTag::Shift,
1411        Operand::Extend(_) => OperandShapeTag::Extend,
1412        Operand::Condition(_) => OperandShapeTag::Condition,
1413        Operand::RegisterList(_) => OperandShapeTag::RegisterList,
1414        Operand::SysReg(_) => OperandShapeTag::SysReg,
1415    }
1416}
1417
1418fn build_input_shape_signature(operands: &[Operand]) -> OperandShapeSignature {
1419    let mut slots = Vec::with_capacity(operands.len());
1420    for operand in operands {
1421        slots.push(operand_shape_tag(operand));
1422    }
1423    OperandShapeSignature {
1424        slots: slots.into_boxed_slice(),
1425    }
1426}
1427
1428fn expected_signature_from_spec(spec: &EncodingSpec) -> OperandConstraintSignature {
1429    let mut kinds = [OperandConstraintKind::Immediate; 64];
1430    let len = expected_user_operand_kinds(spec, &mut kinds);
1431    OperandConstraintSignature {
1432        slots: kinds[..len].to_vec().into_boxed_slice(),
1433    }
1434}
1435
1436fn build_shape_mismatch_hint(
1437    specs: &[EncodingSpec],
1438    mnemonic: &str,
1439    operands: &[Operand],
1440) -> Option<CoreNoMatchHint> {
1441    let mut expected = Vec::<OperandConstraintSignature>::new();
1442    for spec in specs {
1443        if spec.mnemonic != mnemonic {
1444            continue;
1445        }
1446        let signature = expected_signature_from_spec(spec);
1447        if expected.iter().any(|existing| existing == &signature) {
1448            continue;
1449        }
1450        expected.push(signature);
1451    }
1452
1453    if expected.is_empty() {
1454        return None;
1455    }
1456
1457    expected.sort_by(|lhs, rhs| lhs.slots.len().cmp(&rhs.slots.len()));
1458    let shown = expected.len().min(4);
1459    let expected_additional = expected
1460        .len()
1461        .saturating_sub(shown)
1462        .min(usize::from(u16::MAX)) as u16;
1463
1464    Some(CoreNoMatchHint::ShapeMismatch {
1465        expected: expected
1466            .into_iter()
1467            .take(shown)
1468            .collect::<Vec<_>>()
1469            .into_boxed_slice(),
1470        expected_additional,
1471        got: build_input_shape_signature(operands),
1472    })
1473}
1474
1475fn operand_hint_kind(operand: &Operand) -> Option<OperandConstraintKind> {
1476    match operand {
1477        Operand::Register(reg) => Some(reg_constraint_for_class(reg.class)),
1478        Operand::Immediate(_) => Some(OperandConstraintKind::Immediate),
1479        Operand::Condition(_) => Some(OperandConstraintKind::Condition),
1480        Operand::Shift(_) => Some(OperandConstraintKind::ShiftKind),
1481        Operand::Extend(_) => Some(OperandConstraintKind::ExtendKind),
1482        Operand::Memory(_) | Operand::RegisterList(_) | Operand::SysReg(_) => None,
1483    }
1484}
1485
1486fn push_unique_expected_kind(
1487    out: &mut Vec<OperandConstraintKind>,
1488    expected: OperandConstraintKind,
1489) {
1490    if !out.contains(&expected) {
1491        out.push(expected);
1492    }
1493}
1494
1495fn build_operand_delta_hint(
1496    specs: &[EncodingSpec],
1497    mnemonic: &str,
1498    operands: &[Operand],
1499) -> Option<CoreNoMatchHint> {
1500    // Produce the earliest actionable delta:
1501    // - missing operand at a given slot, or
1502    // - single-slot kind mismatch.
1503    let actual = operands
1504        .iter()
1505        .map(operand_hint_kind)
1506        .collect::<Vec<Option<OperandConstraintKind>>>();
1507
1508    let mut missing_slot: Option<usize> = None;
1509    let mut missing_expected = Vec::<OperandConstraintKind>::new();
1510    let mut mismatch_slot: Option<usize> = None;
1511    let mut mismatch_expected = Vec::<OperandConstraintKind>::new();
1512
1513    for spec in specs {
1514        if spec.mnemonic != mnemonic {
1515            continue;
1516        }
1517
1518        let mut expected = [OperandConstraintKind::Immediate; 64];
1519        let expected_len = expected_user_operand_kinds(spec, &mut expected);
1520
1521        if operands.len() + 1 == expected_len {
1522            let mut compatible_prefix = true;
1523            for idx in 0..operands.len() {
1524                let Some(actual_kind) = actual[idx] else {
1525                    compatible_prefix = false;
1526                    break;
1527                };
1528                if !kind_matches(expected[idx], actual_kind) {
1529                    compatible_prefix = false;
1530                    break;
1531                }
1532            }
1533
1534            if compatible_prefix {
1535                let idx = expected_len - 1;
1536                if match missing_slot {
1537                    None => true,
1538                    Some(current) => idx < current,
1539                } {
1540                    missing_slot = Some(idx);
1541                    missing_expected.clear();
1542                }
1543                if missing_slot == Some(idx) {
1544                    push_unique_expected_kind(&mut missing_expected, expected[idx]);
1545                }
1546            }
1547        }
1548
1549        if operands.len() == expected_len {
1550            let mut mismatch_idx: Option<usize> = None;
1551            let mut valid = true;
1552            for idx in 0..operands.len() {
1553                let Some(actual_kind) = actual[idx] else {
1554                    valid = false;
1555                    break;
1556                };
1557                if !kind_matches(expected[idx], actual_kind) {
1558                    if mismatch_idx.is_some() {
1559                        valid = false;
1560                        break;
1561                    }
1562                    mismatch_idx = Some(idx);
1563                }
1564            }
1565
1566            if valid && let Some(idx) = mismatch_idx {
1567                if match mismatch_slot {
1568                    None => true,
1569                    Some(current) => idx < current,
1570                } {
1571                    mismatch_slot = Some(idx);
1572                    mismatch_expected.clear();
1573                }
1574                if mismatch_slot == Some(idx) {
1575                    push_unique_expected_kind(&mut mismatch_expected, expected[idx]);
1576                }
1577            }
1578        }
1579    }
1580
1581    if let Some(idx) = missing_slot
1582        && !missing_expected.is_empty()
1583    {
1584        return Some(CoreNoMatchHint::OperandMissing {
1585            index: (idx + 1).min(usize::from(u8::MAX)) as u8,
1586            expected: missing_expected.into_boxed_slice(),
1587        });
1588    }
1589
1590    if let Some(idx) = mismatch_slot
1591        && !mismatch_expected.is_empty()
1592        && idx < operands.len()
1593    {
1594        return Some(CoreNoMatchHint::OperandKindMismatch {
1595            index: (idx + 1).min(usize::from(u8::MAX)) as u8,
1596            expected: mismatch_expected.into_boxed_slice(),
1597            got: operand_shape_tag(&operands[idx]),
1598        });
1599    }
1600
1601    None
1602}
1603
1604fn spec_expected_user_operand_count(spec: &EncodingSpec) -> usize {
1605    if spec_split_immediate_plan(spec).is_some() {
1606        spec.operand_order.len().saturating_sub(1)
1607    } else {
1608        spec.operand_order.len()
1609    }
1610}
1611
1612/// Selects a variant by mnemonic and typed operands and encodes it.
1613///
1614/// # Errors
1615///
1616/// Returns [`EncodeError`] if no variant can be selected and encoded unambiguously.
1617#[must_use]
1618pub fn encode(
1619    specs: &[EncodingSpec],
1620    mnemonic: &str,
1621    operands: &[Operand],
1622) -> Result<InstructionCode, EncodeError> {
1623    let mut saw_mnemonic = false;
1624    let mut saw_count_mismatch = false;
1625    let mut saw_shape_mismatch = false;
1626    let mut best_detail_error: Option<EncodeError> = None;
1627    let mut selected: Option<(InstructionCode, CandidateScore)> = None;
1628    let mut selected_mask = 0u32;
1629    let mut selected_bits = 0u32;
1630    let mut flat_operands = [FlatOperand {
1631        kind: OperandConstraintKind::Immediate,
1632        value: 0,
1633        optional: false,
1634    }; 64];
1635    let mut flat_len = 0usize;
1636    let mut flat_attempted = false;
1637    let mut flat_error: Option<EncodeError> = None;
1638    let mut expected_min_count = usize::MAX;
1639    let mut expected_max_count = 0usize;
1640
1641    for spec in specs {
1642        if spec.mnemonic != mnemonic {
1643            continue;
1644        }
1645        saw_mnemonic = true;
1646        let expected_count = spec_expected_user_operand_count(spec);
1647        if expected_count < expected_min_count {
1648            expected_min_count = expected_count;
1649        }
1650        if expected_count > expected_max_count {
1651            expected_max_count = expected_count;
1652        }
1653
1654        if !spec_matches_memory_addressing(spec, operands) {
1655            saw_shape_mismatch = true;
1656            continue;
1657        }
1658
1659        if !flat_attempted {
1660            flat_attempted = true;
1661            match flatten_operands(operands, &mut flat_operands) {
1662                Ok(len) => flat_len = len,
1663                Err(err) => flat_error = Some(err),
1664            }
1665        }
1666
1667        let encode_result = if let Some(err) = flat_error.clone() {
1668            Err(err)
1669        } else {
1670            encode_by_spec_from_flattened(spec, &flat_operands, flat_len)
1671        };
1672
1673        match encode_result {
1674            Ok(code) => {
1675                let overlap = selected_mask & spec.opcode_mask;
1676                if ((selected_bits ^ spec.opcode) & overlap) != 0 {
1677                    return Err(EncodeError::AmbiguousVariant);
1678                }
1679                let next_mask = selected_mask | spec.opcode_mask;
1680                let next_bits = (selected_bits & !spec.opcode_mask) | spec.opcode;
1681                let score = candidate_score(spec);
1682                let code_matches_combined = (code.unpack() & next_mask) == next_bits;
1683                if let Some((prev_code, prev_score)) = selected {
1684                    let prev_matches_combined = (prev_code.unpack() & next_mask) == next_bits;
1685                    if !prev_matches_combined && !code_matches_combined {
1686                        return Err(EncodeError::AmbiguousVariant);
1687                    }
1688                    selected_mask = next_mask;
1689                    selected_bits = next_bits;
1690                    if prev_code.unpack() == code.unpack() {
1691                        if score > prev_score {
1692                            selected = Some((code, score));
1693                        }
1694                        continue;
1695                    }
1696                    if !code_matches_combined {
1697                        continue;
1698                    }
1699                    if !prev_matches_combined {
1700                        selected = Some((code, score));
1701                        continue;
1702                    }
1703                    match score.cmp(&prev_score) {
1704                        core::cmp::Ordering::Greater => {
1705                            selected = Some((code, score));
1706                        }
1707                        core::cmp::Ordering::Less => {}
1708                        core::cmp::Ordering::Equal => {
1709                            return Err(EncodeError::AmbiguousVariant);
1710                        }
1711                    }
1712                    continue;
1713                }
1714                selected_mask = next_mask;
1715                selected_bits = next_bits;
1716                if !code_matches_combined {
1717                    return Err(EncodeError::AmbiguousVariant);
1718                }
1719                selected = Some((code, score));
1720            }
1721            Err(EncodeError::OperandCountMismatch) => {
1722                saw_count_mismatch = true;
1723            }
1724            Err(
1725                err @ (EncodeError::OperandOutOfRange { .. }
1726                | EncodeError::ImmediateNotAligned { .. }
1727                | EncodeError::InvalidOperandKind { .. }
1728                | EncodeError::NoMatchingVariant
1729                | EncodeError::NoMatchingVariantHint { .. }),
1730            ) => {
1731                saw_shape_mismatch = true;
1732                best_detail_error = Some(match best_detail_error {
1733                    Some(current) => prefer_diagnostic_error(current, err),
1734                    None => err,
1735                });
1736            }
1737            Err(err) => return Err(err),
1738        }
1739    }
1740
1741    if let Some((code, _)) = selected {
1742        return Ok(code);
1743    }
1744    if !saw_mnemonic {
1745        return Err(EncodeError::UnknownMnemonic);
1746    }
1747    if let Some(detail) = best_detail_error {
1748        return Err(detail);
1749    }
1750    if saw_shape_mismatch {
1751        if let Some(hint) = infer_no_matching_hint(specs, mnemonic, operands) {
1752            return Err(EncodeError::NoMatchingVariantHint {
1753                hint: NoMatchingHint::Core(hint),
1754            });
1755        }
1756        if let Some(hint) = build_operand_delta_hint(specs, mnemonic, operands) {
1757            return Err(EncodeError::NoMatchingVariantHint {
1758                hint: NoMatchingHint::Core(hint),
1759            });
1760        }
1761        if let Some(hint) = build_shape_mismatch_hint(specs, mnemonic, operands) {
1762            return Err(EncodeError::NoMatchingVariantHint {
1763                hint: NoMatchingHint::Core(hint),
1764            });
1765        }
1766        return Err(EncodeError::NoMatchingVariant);
1767    }
1768    if saw_count_mismatch {
1769        if expected_min_count != usize::MAX {
1770            let min = expected_min_count.min(usize::from(u8::MAX)) as u8;
1771            let max = expected_max_count.min(usize::from(u8::MAX)) as u8;
1772            let got = operands.len().min(usize::from(u8::MAX)) as u8;
1773            return Err(EncodeError::OperandCountRange { min, max, got });
1774        }
1775        return Err(EncodeError::OperandCountMismatch);
1776    }
1777
1778    Err(EncodeError::NoMatchingVariant)
1779}
1780
1781#[cfg(test)]
1782mod tests {
1783    use super::*;
1784    use alloc::vec;
1785
1786    const ADD_FIELDS: &[BitFieldSpec] = &[
1787        BitFieldSpec {
1788            name: "sh",
1789            lsb: 22,
1790            width: 1,
1791            signed: false,
1792        },
1793        BitFieldSpec {
1794            name: "imm12",
1795            lsb: 10,
1796            width: 12,
1797            signed: false,
1798        },
1799        BitFieldSpec {
1800            name: "Rn",
1801            lsb: 5,
1802            width: 5,
1803            signed: false,
1804        },
1805        BitFieldSpec {
1806            name: "Rd",
1807            lsb: 0,
1808            width: 5,
1809            signed: false,
1810        },
1811    ];
1812
1813    const ADD_SPEC: EncodingSpec = EncodingSpec {
1814        mnemonic: "add",
1815        variant: "ADD_64_addsub_imm",
1816        opcode: 0b100100010u32 << 23,
1817        opcode_mask: 0b111111111u32 << 23,
1818        fields: ADD_FIELDS,
1819        operand_order: &[3, 2, 1],
1820        operand_kinds: &[
1821            OperandConstraintKind::Gpr64Register,
1822            OperandConstraintKind::Gpr64Register,
1823            OperandConstraintKind::Immediate,
1824        ],
1825        implicit_defaults: &[ImplicitField {
1826            field_index: 0,
1827            value: 0,
1828        }],
1829        memory_addressing: MemoryAddressingConstraintSpec::None,
1830        field_scales: &[1, 1, 1, 1],
1831        split_immediate_plan: None,
1832        gpr32_extend_compatibility: 0,
1833    };
1834
1835    const SEL_P_FIELDS: &[BitFieldSpec] = &[
1836        BitFieldSpec {
1837            name: "Pm",
1838            lsb: 16,
1839            width: 4,
1840            signed: false,
1841        },
1842        BitFieldSpec {
1843            name: "Pg",
1844            lsb: 10,
1845            width: 4,
1846            signed: false,
1847        },
1848        BitFieldSpec {
1849            name: "Pn",
1850            lsb: 5,
1851            width: 4,
1852            signed: false,
1853        },
1854        BitFieldSpec {
1855            name: "Pd",
1856            lsb: 0,
1857            width: 4,
1858            signed: false,
1859        },
1860    ];
1861
1862    const SEL_P_SPEC: EncodingSpec = EncodingSpec {
1863        mnemonic: "sel",
1864        variant: "sel_p_p_pp_",
1865        opcode: 0x2500_4210,
1866        opcode_mask: 0xfff0_c210,
1867        fields: SEL_P_FIELDS,
1868        operand_order: &[3, 1, 2, 0],
1869        operand_kinds: &[
1870            OperandConstraintKind::PredicateRegister,
1871            OperandConstraintKind::PredicateRegister,
1872            OperandConstraintKind::PredicateRegister,
1873            OperandConstraintKind::PredicateRegister,
1874        ],
1875        implicit_defaults: &[],
1876        memory_addressing: MemoryAddressingConstraintSpec::None,
1877        field_scales: &[1, 1, 1, 1],
1878        split_immediate_plan: None,
1879        gpr32_extend_compatibility: 0,
1880    };
1881
1882    const STR_64_POS_FIELDS: &[BitFieldSpec] = &[
1883        BitFieldSpec {
1884            name: "imm12",
1885            lsb: 10,
1886            width: 12,
1887            signed: false,
1888        },
1889        BitFieldSpec {
1890            name: "Rn",
1891            lsb: 5,
1892            width: 5,
1893            signed: false,
1894        },
1895        BitFieldSpec {
1896            name: "Rt",
1897            lsb: 0,
1898            width: 5,
1899            signed: false,
1900        },
1901    ];
1902
1903    const STR_64_POS_SPEC: EncodingSpec = EncodingSpec {
1904        mnemonic: "str",
1905        variant: "STR_64_ldst_pos",
1906        opcode: 0xf900_0000,
1907        opcode_mask: 0xffc0_0000,
1908        fields: STR_64_POS_FIELDS,
1909        operand_order: &[2, 1, 0],
1910        operand_kinds: &[
1911            OperandConstraintKind::Gpr64Register,
1912            OperandConstraintKind::Gpr64Register,
1913            OperandConstraintKind::Immediate,
1914        ],
1915        implicit_defaults: &[],
1916        memory_addressing: MemoryAddressingConstraintSpec::Offset,
1917        field_scales: &[8, 1, 1],
1918        split_immediate_plan: None,
1919        gpr32_extend_compatibility: 0,
1920    };
1921
1922    const STP_64_PRE_FIELDS: &[BitFieldSpec] = &[
1923        BitFieldSpec {
1924            name: "imm7",
1925            lsb: 15,
1926            width: 7,
1927            signed: true,
1928        },
1929        BitFieldSpec {
1930            name: "Rt2",
1931            lsb: 10,
1932            width: 5,
1933            signed: false,
1934        },
1935        BitFieldSpec {
1936            name: "Rn",
1937            lsb: 5,
1938            width: 5,
1939            signed: false,
1940        },
1941        BitFieldSpec {
1942            name: "Rt",
1943            lsb: 0,
1944            width: 5,
1945            signed: false,
1946        },
1947    ];
1948
1949    const STP_64_PRE_SPEC: EncodingSpec = EncodingSpec {
1950        mnemonic: "stp",
1951        variant: "STP_64_ldstpair_pre",
1952        opcode: 0xa980_0000,
1953        opcode_mask: 0xffc0_0000,
1954        fields: STP_64_PRE_FIELDS,
1955        operand_order: &[3, 1, 2, 0],
1956        operand_kinds: &[
1957            OperandConstraintKind::Gpr64Register,
1958            OperandConstraintKind::Gpr64Register,
1959            OperandConstraintKind::Gpr64Register,
1960            OperandConstraintKind::Immediate,
1961        ],
1962        implicit_defaults: &[],
1963        memory_addressing: MemoryAddressingConstraintSpec::PreIndex,
1964        field_scales: &[8, 1, 1, 1],
1965        split_immediate_plan: None,
1966        gpr32_extend_compatibility: 0,
1967    };
1968
1969    const B_IMM_SPEC: EncodingSpec = EncodingSpec {
1970        mnemonic: "b",
1971        variant: "B_only_branch_imm",
1972        opcode: 0x1400_0000,
1973        opcode_mask: 0xfc00_0000,
1974        fields: &[BitFieldSpec {
1975            name: "imm26",
1976            lsb: 0,
1977            width: 26,
1978            signed: true,
1979        }],
1980        operand_order: &[0],
1981        operand_kinds: &[OperandConstraintKind::Immediate],
1982        implicit_defaults: &[],
1983        memory_addressing: MemoryAddressingConstraintSpec::None,
1984        field_scales: &[4],
1985        split_immediate_plan: None,
1986        gpr32_extend_compatibility: 0,
1987    };
1988
1989    const ADR_SPEC: EncodingSpec = EncodingSpec {
1990        mnemonic: "adr",
1991        variant: "ADR_only_pcreladdr",
1992        opcode: 0x1000_0000,
1993        opcode_mask: 0x9f00_0000,
1994        fields: &[
1995            BitFieldSpec {
1996                name: "immlo",
1997                lsb: 29,
1998                width: 2,
1999                signed: false,
2000            },
2001            BitFieldSpec {
2002                name: "immhi",
2003                lsb: 5,
2004                width: 19,
2005                signed: true,
2006            },
2007            BitFieldSpec {
2008                name: "Rd",
2009                lsb: 0,
2010                width: 5,
2011                signed: false,
2012            },
2013        ],
2014        operand_order: &[2, 0, 1],
2015        operand_kinds: &[
2016            OperandConstraintKind::Gpr64Register,
2017            OperandConstraintKind::Immediate,
2018            OperandConstraintKind::Immediate,
2019        ],
2020        implicit_defaults: &[],
2021        memory_addressing: MemoryAddressingConstraintSpec::None,
2022        field_scales: &[1, 1, 1],
2023        split_immediate_plan: Some(SplitImmediatePlanSpec {
2024            first_slot: 1,
2025            second_slot: 2,
2026            kind: SplitImmediateKindSpec::AdrLike {
2027                immlo_field_index: 0,
2028                immhi_field_index: 1,
2029                scale: 1,
2030            },
2031        }),
2032        gpr32_extend_compatibility: 0,
2033    };
2034
2035    const ADRP_SPEC: EncodingSpec = EncodingSpec {
2036        mnemonic: "adrp",
2037        variant: "ADRP_only_pcreladdr",
2038        opcode: 0x9000_0000,
2039        opcode_mask: 0x9f00_0000,
2040        fields: &[
2041            BitFieldSpec {
2042                name: "immlo",
2043                lsb: 29,
2044                width: 2,
2045                signed: false,
2046            },
2047            BitFieldSpec {
2048                name: "immhi",
2049                lsb: 5,
2050                width: 19,
2051                signed: true,
2052            },
2053            BitFieldSpec {
2054                name: "Rd",
2055                lsb: 0,
2056                width: 5,
2057                signed: false,
2058            },
2059        ],
2060        operand_order: &[2, 0, 1],
2061        operand_kinds: &[
2062            OperandConstraintKind::Gpr64Register,
2063            OperandConstraintKind::Immediate,
2064            OperandConstraintKind::Immediate,
2065        ],
2066        implicit_defaults: &[],
2067        memory_addressing: MemoryAddressingConstraintSpec::None,
2068        field_scales: &[1, 1, 1],
2069        split_immediate_plan: Some(SplitImmediatePlanSpec {
2070            first_slot: 1,
2071            second_slot: 2,
2072            kind: SplitImmediateKindSpec::AdrLike {
2073                immlo_field_index: 0,
2074                immhi_field_index: 1,
2075                scale: 4096,
2076            },
2077        }),
2078        gpr32_extend_compatibility: 0,
2079    };
2080
2081    const ADD_EXT_64_SPEC: EncodingSpec = EncodingSpec {
2082        mnemonic: "add",
2083        variant: "ADD_64_addsub_ext",
2084        opcode: 0x8b20_0000,
2085        opcode_mask: 0xffe0_0000,
2086        fields: &[
2087            BitFieldSpec {
2088                name: "Rm",
2089                lsb: 16,
2090                width: 5,
2091                signed: false,
2092            },
2093            BitFieldSpec {
2094                name: "option",
2095                lsb: 13,
2096                width: 3,
2097                signed: false,
2098            },
2099            BitFieldSpec {
2100                name: "imm3",
2101                lsb: 10,
2102                width: 3,
2103                signed: false,
2104            },
2105            BitFieldSpec {
2106                name: "Rn",
2107                lsb: 5,
2108                width: 5,
2109                signed: false,
2110            },
2111            BitFieldSpec {
2112                name: "Rd",
2113                lsb: 0,
2114                width: 5,
2115                signed: false,
2116            },
2117        ],
2118        operand_order: &[4, 3, 0, 1, 2],
2119        operand_kinds: &[
2120            OperandConstraintKind::Gpr64Register,
2121            OperandConstraintKind::Gpr64Register,
2122            OperandConstraintKind::Gpr64Register,
2123            OperandConstraintKind::ExtendKind,
2124            OperandConstraintKind::Immediate,
2125        ],
2126        implicit_defaults: &[],
2127        memory_addressing: MemoryAddressingConstraintSpec::None,
2128        field_scales: &[1, 1, 1, 1, 1],
2129        split_immediate_plan: None,
2130        gpr32_extend_compatibility: 0b100,
2131    };
2132
2133    const LDR_64_UNSIGNED_SPEC: EncodingSpec = EncodingSpec {
2134        mnemonic: "ldr",
2135        variant: "LDR_64_ldst_pos",
2136        opcode: 0xf940_0000,
2137        opcode_mask: 0xffc0_0000,
2138        fields: &[
2139            BitFieldSpec {
2140                name: "imm12",
2141                lsb: 10,
2142                width: 12,
2143                signed: false,
2144            },
2145            BitFieldSpec {
2146                name: "Rn",
2147                lsb: 5,
2148                width: 5,
2149                signed: false,
2150            },
2151            BitFieldSpec {
2152                name: "Rt",
2153                lsb: 0,
2154                width: 5,
2155                signed: false,
2156            },
2157        ],
2158        operand_order: &[2, 1, 0],
2159        operand_kinds: &[
2160            OperandConstraintKind::Gpr64Register,
2161            OperandConstraintKind::Gpr64Register,
2162            OperandConstraintKind::Immediate,
2163        ],
2164        implicit_defaults: &[],
2165        memory_addressing: MemoryAddressingConstraintSpec::Offset,
2166        field_scales: &[8, 1, 1],
2167        split_immediate_plan: None,
2168        gpr32_extend_compatibility: 0,
2169    };
2170
2171    const LDAR_64_SPEC: EncodingSpec = EncodingSpec {
2172        mnemonic: "ldar",
2173        variant: "LDAR_LR64_ldstord",
2174        opcode: 0xc8df_fc00,
2175        opcode_mask: 0xffff_fc00,
2176        fields: &[
2177            BitFieldSpec {
2178                name: "Rn",
2179                lsb: 5,
2180                width: 5,
2181                signed: false,
2182            },
2183            BitFieldSpec {
2184                name: "Rt",
2185                lsb: 0,
2186                width: 5,
2187                signed: false,
2188            },
2189        ],
2190        operand_order: &[1, 0],
2191        operand_kinds: &[
2192            OperandConstraintKind::Gpr64Register,
2193            OperandConstraintKind::Gpr64Register,
2194        ],
2195        implicit_defaults: &[],
2196        memory_addressing: MemoryAddressingConstraintSpec::None,
2197        field_scales: &[1, 1],
2198        split_immediate_plan: None,
2199        gpr32_extend_compatibility: 0,
2200    };
2201
2202    const TBNZ_SPEC: EncodingSpec = EncodingSpec {
2203        mnemonic: "tbnz",
2204        variant: "TBNZ_only_testbranch",
2205        opcode: 0x3700_0000,
2206        opcode_mask: 0x7f00_0000,
2207        fields: &[
2208            BitFieldSpec {
2209                name: "b5",
2210                lsb: 31,
2211                width: 1,
2212                signed: false,
2213            },
2214            BitFieldSpec {
2215                name: "b40",
2216                lsb: 19,
2217                width: 5,
2218                signed: false,
2219            },
2220            BitFieldSpec {
2221                name: "imm14",
2222                lsb: 5,
2223                width: 14,
2224                signed: true,
2225            },
2226            BitFieldSpec {
2227                name: "Rt",
2228                lsb: 0,
2229                width: 5,
2230                signed: false,
2231            },
2232        ],
2233        operand_order: &[3, 0, 1, 2],
2234        operand_kinds: &[
2235            OperandConstraintKind::Gpr64Register,
2236            OperandConstraintKind::Immediate,
2237            OperandConstraintKind::Immediate,
2238            OperandConstraintKind::Immediate,
2239        ],
2240        implicit_defaults: &[],
2241        memory_addressing: MemoryAddressingConstraintSpec::None,
2242        field_scales: &[1, 1, 4, 1],
2243        split_immediate_plan: Some(SplitImmediatePlanSpec {
2244            first_slot: 1,
2245            second_slot: 2,
2246            kind: SplitImmediateKindSpec::BitIndex6 {
2247                b5_field_index: 0,
2248                b40_field_index: 1,
2249            },
2250        }),
2251        gpr32_extend_compatibility: 0,
2252    };
2253
2254    const MRS_SPEC: EncodingSpec = EncodingSpec {
2255        mnemonic: "mrs",
2256        variant: "MRS_RS_systemmove",
2257        opcode: 0xd530_0000,
2258        opcode_mask: 0xfff0_0000,
2259        fields: &[
2260            BitFieldSpec {
2261                name: "o0",
2262                lsb: 19,
2263                width: 1,
2264                signed: false,
2265            },
2266            BitFieldSpec {
2267                name: "op1",
2268                lsb: 16,
2269                width: 3,
2270                signed: false,
2271            },
2272            BitFieldSpec {
2273                name: "CRn",
2274                lsb: 12,
2275                width: 4,
2276                signed: false,
2277            },
2278            BitFieldSpec {
2279                name: "CRm",
2280                lsb: 8,
2281                width: 4,
2282                signed: false,
2283            },
2284            BitFieldSpec {
2285                name: "op2",
2286                lsb: 5,
2287                width: 3,
2288                signed: false,
2289            },
2290            BitFieldSpec {
2291                name: "Rt",
2292                lsb: 0,
2293                width: 5,
2294                signed: false,
2295            },
2296        ],
2297        operand_order: &[5, 1, 2, 3, 4, 0],
2298        operand_kinds: &[
2299            OperandConstraintKind::Gpr64Register,
2300            OperandConstraintKind::SysRegPart,
2301            OperandConstraintKind::SysRegPart,
2302            OperandConstraintKind::SysRegPart,
2303            OperandConstraintKind::SysRegPart,
2304            OperandConstraintKind::Immediate,
2305        ],
2306        implicit_defaults: &[],
2307        memory_addressing: MemoryAddressingConstraintSpec::None,
2308        field_scales: &[1, 1, 1, 1, 1, 1],
2309        split_immediate_plan: None,
2310        gpr32_extend_compatibility: 0,
2311    };
2312
2313    #[test]
2314    fn encode_add_imm() {
2315        let code = encode_by_spec(&ADD_SPEC, &[0, 1, 2, 1]).expect("encode should succeed");
2316        assert_eq!(code.unpack(), 0x91000441);
2317    }
2318
2319    #[test]
2320    fn rejects_imm_overflow() {
2321        let err =
2322            encode_by_spec(&ADD_SPEC, &[0, 0x2000, 2, 1]).expect_err("should reject overflow");
2323        assert_eq!(
2324            err,
2325            EncodeError::OperandOutOfRange {
2326                field: "imm12",
2327                value: 0x2000,
2328                width: 12,
2329                signed: false,
2330            }
2331        );
2332    }
2333
2334    #[test]
2335    fn unknown_mnemonic() {
2336        let err = encode(
2337            &[ADD_SPEC],
2338            "sub",
2339            &[
2340                Operand::Register(RegisterOperand {
2341                    code: 1,
2342                    class: RegClass::X,
2343                    arrangement: None,
2344                    lane: None,
2345                }),
2346                Operand::Register(RegisterOperand {
2347                    code: 2,
2348                    class: RegClass::X,
2349                    arrangement: None,
2350                    lane: None,
2351                }),
2352                Operand::Immediate(1),
2353            ],
2354        )
2355        .expect_err("must fail");
2356        assert_eq!(err, EncodeError::UnknownMnemonic);
2357    }
2358
2359    #[test]
2360    fn encode_add_imm_asm_operands() {
2361        let code = encode_by_spec_operands(
2362            &ADD_SPEC,
2363            &[
2364                Operand::Register(RegisterOperand {
2365                    code: 1,
2366                    class: RegClass::X,
2367                    arrangement: None,
2368                    lane: None,
2369                }),
2370                Operand::Register(RegisterOperand {
2371                    code: 2,
2372                    class: RegClass::X,
2373                    arrangement: None,
2374                    lane: None,
2375                }),
2376                Operand::Immediate(1),
2377            ],
2378        )
2379        .expect("encode should succeed");
2380        assert_eq!(code.unpack(), 0x91000441);
2381    }
2382
2383    #[test]
2384    fn invalid_kind_rejected() {
2385        let err = encode_by_spec_operands(
2386            &ADD_SPEC,
2387            &[
2388                Operand::Immediate(1),
2389                Operand::Register(RegisterOperand {
2390                    code: 2,
2391                    class: RegClass::X,
2392                    arrangement: None,
2393                    lane: None,
2394                }),
2395                Operand::Immediate(1),
2396            ],
2397        )
2398        .expect_err("must fail");
2399        assert_eq!(
2400            err,
2401            EncodeError::InvalidOperandKind {
2402                field: "Rd",
2403                expected: "64-bit general-purpose register",
2404                got: "immediate",
2405            }
2406        );
2407    }
2408
2409    #[test]
2410    fn encode_predicate_operands() {
2411        let code = encode_by_spec_operands(
2412            &SEL_P_SPEC,
2413            &[
2414                Operand::Register(RegisterOperand {
2415                    code: 1,
2416                    class: RegClass::P,
2417                    arrangement: None,
2418                    lane: None,
2419                }),
2420                Operand::Register(RegisterOperand {
2421                    code: 2,
2422                    class: RegClass::P,
2423                    arrangement: None,
2424                    lane: None,
2425                }),
2426                Operand::Register(RegisterOperand {
2427                    code: 3,
2428                    class: RegClass::P,
2429                    arrangement: None,
2430                    lane: None,
2431                }),
2432                Operand::Register(RegisterOperand {
2433                    code: 4,
2434                    class: RegClass::P,
2435                    arrangement: None,
2436                    lane: None,
2437                }),
2438            ],
2439        )
2440        .expect("predicate encoding should succeed");
2441        assert_eq!(
2442            code.unpack(),
2443            0x2500_4210u32 | (4u32 << 16) | (2u32 << 10) | (3u32 << 5) | 1u32
2444        );
2445    }
2446
2447    #[test]
2448    fn reject_wrong_register_family() {
2449        let err = encode_by_spec_operands(
2450            &SEL_P_SPEC,
2451            &[
2452                Operand::Register(RegisterOperand {
2453                    code: 1,
2454                    class: RegClass::X,
2455                    arrangement: None,
2456                    lane: None,
2457                }),
2458                Operand::Register(RegisterOperand {
2459                    code: 2,
2460                    class: RegClass::P,
2461                    arrangement: None,
2462                    lane: None,
2463                }),
2464                Operand::Register(RegisterOperand {
2465                    code: 3,
2466                    class: RegClass::P,
2467                    arrangement: None,
2468                    lane: None,
2469                }),
2470                Operand::Register(RegisterOperand {
2471                    code: 4,
2472                    class: RegClass::P,
2473                    arrangement: None,
2474                    lane: None,
2475                }),
2476            ],
2477        )
2478        .expect_err("mismatched family should fail");
2479        assert_eq!(
2480            err,
2481            EncodeError::InvalidOperandKind {
2482                field: "Pd",
2483                expected: "predicate register",
2484                got: "64-bit general-purpose register",
2485            }
2486        );
2487    }
2488
2489    #[test]
2490    fn memory_imm12_is_scaled_from_bytes() {
2491        let code = encode_by_spec_operands(
2492            &STR_64_POS_SPEC,
2493            &[
2494                Operand::Register(RegisterOperand {
2495                    code: 30,
2496                    class: RegClass::X,
2497                    arrangement: None,
2498                    lane: None,
2499                }),
2500                Operand::Memory(MemoryOperand {
2501                    base: RegisterOperand {
2502                        code: 31,
2503                        class: RegClass::Xsp,
2504                        arrangement: None,
2505                        lane: None,
2506                    },
2507                    offset: MemoryOffset::Immediate(16),
2508                    addressing: AddressingMode::Offset,
2509                }),
2510            ],
2511        )
2512        .expect("str should encode");
2513        assert_eq!(code.unpack(), 0xf900_0bfe);
2514    }
2515
2516    #[test]
2517    fn memory_imm7_pair_is_scaled_from_bytes() {
2518        let code = encode_by_spec_operands(
2519            &STP_64_PRE_SPEC,
2520            &[
2521                Operand::Register(RegisterOperand {
2522                    code: 27,
2523                    class: RegClass::X,
2524                    arrangement: None,
2525                    lane: None,
2526                }),
2527                Operand::Register(RegisterOperand {
2528                    code: 28,
2529                    class: RegClass::X,
2530                    arrangement: None,
2531                    lane: None,
2532                }),
2533                Operand::Memory(MemoryOperand {
2534                    base: RegisterOperand {
2535                        code: 31,
2536                        class: RegClass::Xsp,
2537                        arrangement: None,
2538                        lane: None,
2539                    },
2540                    offset: MemoryOffset::Immediate(-32),
2541                    addressing: AddressingMode::PreIndex,
2542                }),
2543            ],
2544        )
2545        .expect("stp should encode");
2546        assert_eq!(code.unpack(), 0xa9be_73fb);
2547    }
2548
2549    #[test]
2550    fn misaligned_memory_immediate_is_rejected() {
2551        let err = encode_by_spec_operands(
2552            &STR_64_POS_SPEC,
2553            &[
2554                Operand::Register(RegisterOperand {
2555                    code: 30,
2556                    class: RegClass::X,
2557                    arrangement: None,
2558                    lane: None,
2559                }),
2560                Operand::Memory(MemoryOperand {
2561                    base: RegisterOperand {
2562                        code: 31,
2563                        class: RegClass::Xsp,
2564                        arrangement: None,
2565                        lane: None,
2566                    },
2567                    offset: MemoryOffset::Immediate(10),
2568                    addressing: AddressingMode::Offset,
2569                }),
2570            ],
2571        )
2572        .expect_err("misalignment must fail");
2573        assert_eq!(
2574            err,
2575            EncodeError::ImmediateNotAligned {
2576                field: "imm12",
2577                value: 10,
2578                scale: 8,
2579            }
2580        );
2581    }
2582
2583    #[test]
2584    fn branch_immediate_is_scaled_from_bytes() {
2585        let code =
2586            encode_by_spec_operands(&B_IMM_SPEC, &[Operand::Immediate(8)]).expect("b encode");
2587        assert_eq!(code.unpack(), 0x1400_0002);
2588    }
2589
2590    #[test]
2591    fn branch_immediate_alignment_is_enforced() {
2592        let err = encode_by_spec_operands(&B_IMM_SPEC, &[Operand::Immediate(6)])
2593            .expect_err("misaligned branch immediate must fail");
2594        assert_eq!(
2595            err,
2596            EncodeError::ImmediateNotAligned {
2597                field: "imm26",
2598                value: 6,
2599                scale: 4,
2600            }
2601        );
2602    }
2603
2604    #[test]
2605    fn adr_accepts_single_pc_relative_immediate() {
2606        let code = encode_by_spec_operands(
2607            &ADR_SPEC,
2608            &[
2609                Operand::Register(RegisterOperand {
2610                    code: 0,
2611                    class: RegClass::X,
2612                    arrangement: None,
2613                    lane: None,
2614                }),
2615                Operand::Immediate(4),
2616            ],
2617        )
2618        .expect("adr must encode with single immediate");
2619        assert_eq!(code.unpack(), 0x1000_0020);
2620    }
2621
2622    #[test]
2623    fn adrp_immediate_uses_page_scale() {
2624        let code = encode_by_spec_operands(
2625            &ADRP_SPEC,
2626            &[
2627                Operand::Register(RegisterOperand {
2628                    code: 0,
2629                    class: RegClass::X,
2630                    arrangement: None,
2631                    lane: None,
2632                }),
2633                Operand::Immediate(4096),
2634            ],
2635        )
2636        .expect("adrp must encode with page immediate");
2637        assert_eq!(code.unpack(), 0xb000_0000);
2638    }
2639
2640    #[test]
2641    fn adrp_immediate_alignment_is_enforced() {
2642        let err = encode_by_spec_operands(
2643            &ADRP_SPEC,
2644            &[
2645                Operand::Register(RegisterOperand {
2646                    code: 0,
2647                    class: RegClass::X,
2648                    arrangement: None,
2649                    lane: None,
2650                }),
2651                Operand::Immediate(2048),
2652            ],
2653        )
2654        .expect_err("misaligned adrp immediate must fail");
2655        assert_eq!(
2656            err,
2657            EncodeError::ImmediateNotAligned {
2658                field: "immlo",
2659                value: 2048,
2660                scale: 4096,
2661            }
2662        );
2663    }
2664
2665    #[test]
2666    fn add_ext_64_accepts_w_register_source() {
2667        let w_source = encode_by_spec_operands(
2668            &ADD_EXT_64_SPEC,
2669            &[
2670                Operand::Register(RegisterOperand {
2671                    code: 3,
2672                    class: RegClass::X,
2673                    arrangement: None,
2674                    lane: None,
2675                }),
2676                Operand::Register(RegisterOperand {
2677                    code: 4,
2678                    class: RegClass::X,
2679                    arrangement: None,
2680                    lane: None,
2681                }),
2682                Operand::Register(RegisterOperand {
2683                    code: 5,
2684                    class: RegClass::W,
2685                    arrangement: None,
2686                    lane: None,
2687                }),
2688                Operand::Extend(ExtendOperand {
2689                    kind: ExtendKind::Uxtw,
2690                    amount: Some(2),
2691                }),
2692            ],
2693        )
2694        .expect("64-bit add ext form should accept Wm");
2695
2696        let x_source = encode_by_spec_operands(
2697            &ADD_EXT_64_SPEC,
2698            &[
2699                Operand::Register(RegisterOperand {
2700                    code: 3,
2701                    class: RegClass::X,
2702                    arrangement: None,
2703                    lane: None,
2704                }),
2705                Operand::Register(RegisterOperand {
2706                    code: 4,
2707                    class: RegClass::X,
2708                    arrangement: None,
2709                    lane: None,
2710                }),
2711                Operand::Register(RegisterOperand {
2712                    code: 5,
2713                    class: RegClass::X,
2714                    arrangement: None,
2715                    lane: None,
2716                }),
2717                Operand::Extend(ExtendOperand {
2718                    kind: ExtendKind::Uxtw,
2719                    amount: Some(2),
2720                }),
2721            ],
2722        )
2723        .expect("64-bit add ext form should accept Xm as encoded source register");
2724
2725        assert_eq!(w_source.unpack(), x_source.unpack());
2726    }
2727
2728    #[test]
2729    fn tbnz_accepts_single_bit_index_operand() {
2730        let code = encode_by_spec_operands(
2731            &TBNZ_SPEC,
2732            &[
2733                Operand::Register(RegisterOperand {
2734                    code: 1,
2735                    class: RegClass::X,
2736                    arrangement: None,
2737                    lane: None,
2738                }),
2739                Operand::Immediate(3),
2740                Operand::Immediate(8),
2741            ],
2742        )
2743        .expect("tbnz with split bit-index should encode");
2744
2745        let expected = encode_by_spec(&TBNZ_SPEC, &[0, 3, 2, 1]).expect("expected split encoding");
2746        assert_eq!(code.unpack(), expected.unpack());
2747    }
2748
2749    #[test]
2750    fn bare_memory_operand_maps_to_zero_or_no_offset_forms() {
2751        let ldr = encode_by_spec_operands(
2752            &LDR_64_UNSIGNED_SPEC,
2753            &[
2754                Operand::Register(RegisterOperand {
2755                    code: 6,
2756                    class: RegClass::X,
2757                    arrangement: None,
2758                    lane: None,
2759                }),
2760                Operand::Memory(MemoryOperand {
2761                    base: RegisterOperand {
2762                        code: 7,
2763                        class: RegClass::X,
2764                        arrangement: None,
2765                        lane: None,
2766                    },
2767                    offset: MemoryOffset::None,
2768                    addressing: AddressingMode::Offset,
2769                }),
2770            ],
2771        )
2772        .expect("ldr [xn] should map to imm12=#0 form");
2773        let expected_ldr = encode_by_spec(&LDR_64_UNSIGNED_SPEC, &[0, 7, 6]).expect("ldr #0");
2774        assert_eq!(ldr.unpack(), expected_ldr.unpack());
2775
2776        let ldar = encode_by_spec_operands(
2777            &LDAR_64_SPEC,
2778            &[
2779                Operand::Register(RegisterOperand {
2780                    code: 8,
2781                    class: RegClass::X,
2782                    arrangement: None,
2783                    lane: None,
2784                }),
2785                Operand::Memory(MemoryOperand {
2786                    base: RegisterOperand {
2787                        code: 9,
2788                        class: RegClass::X,
2789                        arrangement: None,
2790                        lane: None,
2791                    },
2792                    offset: MemoryOffset::None,
2793                    addressing: AddressingMode::Offset,
2794                }),
2795            ],
2796        )
2797        .expect("ldar [xn] should match no-offset form");
2798        let expected_ldar = encode_by_spec(&LDAR_64_SPEC, &[9, 8]).expect("ldar");
2799        assert_eq!(ldar.unpack(), expected_ldar.unpack());
2800    }
2801
2802    #[test]
2803    fn sysreg_operand_converts_arch_op0_to_encoded_o0() {
2804        let code = encode_by_spec_operands(
2805            &MRS_SPEC,
2806            &[
2807                Operand::Register(RegisterOperand {
2808                    code: 8,
2809                    class: RegClass::X,
2810                    arrangement: None,
2811                    lane: None,
2812                }),
2813                Operand::SysReg(SysRegOperand {
2814                    op0: 3,
2815                    op1: 0,
2816                    crn: 15,
2817                    crm: 2,
2818                    op2: 0,
2819                }),
2820            ],
2821        )
2822        .expect("mrs sysreg form should encode");
2823
2824        let expected = encode_by_spec(&MRS_SPEC, &[1, 0, 15, 2, 0, 8]).expect("mrs expected");
2825        assert_eq!(code.unpack(), expected.unpack());
2826    }
2827
2828    #[test]
2829    fn operand_count_range_reports_expected_bounds() {
2830        let err = encode(
2831            &[ADD_SPEC],
2832            "add",
2833            &[Operand::Register(RegisterOperand {
2834                code: 1,
2835                class: RegClass::X,
2836                arrangement: None,
2837                lane: None,
2838            })],
2839        )
2840        .expect_err("must reject operand count");
2841        assert_eq!(
2842            err,
2843            EncodeError::OperandCountRange {
2844                min: 3,
2845                max: 3,
2846                got: 1,
2847            }
2848        );
2849    }
2850
2851    #[test]
2852    fn operand_count_range_uses_user_visible_split_immediate_count() {
2853        let err = encode(
2854            &[TBNZ_SPEC],
2855            "tbnz",
2856            &[
2857                Operand::Register(RegisterOperand {
2858                    code: 1,
2859                    class: RegClass::X,
2860                    arrangement: None,
2861                    lane: None,
2862                }),
2863                Operand::Immediate(3),
2864            ],
2865        )
2866        .expect_err("must reject missing branch immediate");
2867        assert_eq!(
2868            err,
2869            EncodeError::OperandCountRange {
2870                min: 3,
2871                max: 3,
2872                got: 2,
2873            }
2874        );
2875    }
2876
2877    #[test]
2878    fn no_matching_variant_hint_includes_expected_shapes() {
2879        let err = encode(
2880            &[STR_64_POS_SPEC],
2881            "str",
2882            &[
2883                Operand::Register(RegisterOperand {
2884                    code: 0,
2885                    class: RegClass::X,
2886                    arrangement: None,
2887                    lane: None,
2888                }),
2889                Operand::Memory(MemoryOperand {
2890                    base: RegisterOperand {
2891                        code: 1,
2892                        class: RegClass::X,
2893                        arrangement: None,
2894                        lane: None,
2895                    },
2896                    offset: MemoryOffset::Immediate(16),
2897                    addressing: AddressingMode::PreIndex,
2898                }),
2899            ],
2900        )
2901        .expect_err("shape mismatch should return hint");
2902
2903        match err {
2904            EncodeError::NoMatchingVariantHint {
2905                hint:
2906                    NoMatchingHint::Core(CoreNoMatchHint::ShapeMismatch {
2907                        expected,
2908                        expected_additional,
2909                        got,
2910                    }),
2911            } => {
2912                assert_eq!(expected_additional, 0);
2913                assert_eq!(expected.len(), 1);
2914                assert_eq!(
2915                    expected[0].slots.as_ref(),
2916                    &[
2917                        OperandConstraintKind::Gpr64Register,
2918                        OperandConstraintKind::Gpr64Register,
2919                        OperandConstraintKind::Immediate,
2920                    ]
2921                );
2922                assert_eq!(
2923                    got.slots.as_ref(),
2924                    &[OperandShapeTag::Gpr64, OperandShapeTag::Memory]
2925                );
2926            }
2927            other => panic!("unexpected error: {other:?}"),
2928        }
2929    }
2930
2931    #[test]
2932    fn operand_delta_hint_reports_missing_trailing_operand() {
2933        let hint = build_operand_delta_hint(
2934            &[ADD_SPEC],
2935            "add",
2936            &[
2937                Operand::Register(RegisterOperand {
2938                    code: 0,
2939                    class: RegClass::X,
2940                    arrangement: None,
2941                    lane: None,
2942                }),
2943                Operand::Register(RegisterOperand {
2944                    code: 1,
2945                    class: RegClass::X,
2946                    arrangement: None,
2947                    lane: None,
2948                }),
2949            ],
2950        )
2951        .expect("missing operand hint should be available");
2952        assert_eq!(
2953            hint,
2954            CoreNoMatchHint::OperandMissing {
2955                index: 3,
2956                expected: vec![OperandConstraintKind::Immediate].into_boxed_slice(),
2957            }
2958        );
2959    }
2960
2961    #[test]
2962    fn operand_delta_hint_reports_single_slot_kind_mismatch() {
2963        let hint = build_operand_delta_hint(
2964            &[ADD_SPEC],
2965            "add",
2966            &[
2967                Operand::Register(RegisterOperand {
2968                    code: 0,
2969                    class: RegClass::X,
2970                    arrangement: None,
2971                    lane: None,
2972                }),
2973                Operand::Immediate(7),
2974                Operand::Immediate(9),
2975            ],
2976        )
2977        .expect("kind mismatch hint should be available");
2978        assert_eq!(
2979            hint,
2980            CoreNoMatchHint::OperandKindMismatch {
2981                index: 2,
2982                expected: vec![OperandConstraintKind::Gpr64Register].into_boxed_slice(),
2983                got: OperandShapeTag::Immediate,
2984            }
2985        );
2986    }
2987
2988    #[test]
2989    fn mnemonic_selection_reports_ambiguous_variant_for_conflicting_fixed_bits() {
2990        const WITH_DEFAULT: EncodingSpec = EncodingSpec {
2991            mnemonic: "pick",
2992            variant: "PICK_64_defaulted",
2993            opcode: 0x1000_0000,
2994            opcode_mask: 0xff00_0000,
2995            fields: &[
2996                BitFieldSpec {
2997                    name: "sh",
2998                    lsb: 22,
2999                    width: 1,
3000                    signed: false,
3001                },
3002                BitFieldSpec {
3003                    name: "imm12",
3004                    lsb: 10,
3005                    width: 12,
3006                    signed: false,
3007                },
3008                BitFieldSpec {
3009                    name: "Rn",
3010                    lsb: 5,
3011                    width: 5,
3012                    signed: false,
3013                },
3014                BitFieldSpec {
3015                    name: "Rd",
3016                    lsb: 0,
3017                    width: 5,
3018                    signed: false,
3019                },
3020            ],
3021            operand_order: &[3, 2, 1],
3022            operand_kinds: &[
3023                OperandConstraintKind::Gpr64Register,
3024                OperandConstraintKind::Gpr64Register,
3025                OperandConstraintKind::Immediate,
3026            ],
3027            implicit_defaults: &[ImplicitField {
3028                field_index: 0,
3029                value: 0,
3030            }],
3031            memory_addressing: MemoryAddressingConstraintSpec::None,
3032            field_scales: &[1, 1, 1, 1],
3033            split_immediate_plan: None,
3034            gpr32_extend_compatibility: 0,
3035        };
3036
3037        const NO_DEFAULT: EncodingSpec = EncodingSpec {
3038            mnemonic: "pick",
3039            variant: "PICK_64_direct",
3040            opcode: 0x2000_0000,
3041            opcode_mask: 0xff00_0000,
3042            fields: &[
3043                BitFieldSpec {
3044                    name: "imm12",
3045                    lsb: 10,
3046                    width: 12,
3047                    signed: false,
3048                },
3049                BitFieldSpec {
3050                    name: "Rn",
3051                    lsb: 5,
3052                    width: 5,
3053                    signed: false,
3054                },
3055                BitFieldSpec {
3056                    name: "Rd",
3057                    lsb: 0,
3058                    width: 5,
3059                    signed: false,
3060                },
3061            ],
3062            operand_order: &[2, 1, 0],
3063            operand_kinds: &[
3064                OperandConstraintKind::Gpr64Register,
3065                OperandConstraintKind::Gpr64Register,
3066                OperandConstraintKind::Immediate,
3067            ],
3068            implicit_defaults: &[],
3069            memory_addressing: MemoryAddressingConstraintSpec::None,
3070            field_scales: &[1, 1, 1],
3071            split_immediate_plan: None,
3072            gpr32_extend_compatibility: 0,
3073        };
3074
3075        let code = encode(
3076            &[WITH_DEFAULT, NO_DEFAULT],
3077            "pick",
3078            &[
3079                Operand::Register(RegisterOperand {
3080                    code: 1,
3081                    class: RegClass::X,
3082                    arrangement: None,
3083                    lane: None,
3084                }),
3085                Operand::Register(RegisterOperand {
3086                    code: 2,
3087                    class: RegClass::X,
3088                    arrangement: None,
3089                    lane: None,
3090                }),
3091                Operand::Immediate(7),
3092            ],
3093        )
3094        .expect_err("conflicting fixed bits must stay ambiguous");
3095        assert_eq!(code, EncodeError::AmbiguousVariant);
3096    }
3097
3098    #[test]
3099    fn mnemonic_selection_prefers_specific_score_when_fixed_bits_are_compatible() {
3100        const PICK_NARROW: EncodingSpec = EncodingSpec {
3101            mnemonic: "pickcompat",
3102            variant: "PICKCOMPAT_narrow",
3103            opcode: 0x1000_0000,
3104            opcode_mask: 0xf000_0000,
3105            fields: &[
3106                BitFieldSpec {
3107                    name: "imm1",
3108                    lsb: 0,
3109                    width: 1,
3110                    signed: false,
3111                },
3112                BitFieldSpec {
3113                    name: "Rn",
3114                    lsb: 5,
3115                    width: 5,
3116                    signed: false,
3117                },
3118                BitFieldSpec {
3119                    name: "Rd",
3120                    lsb: 10,
3121                    width: 5,
3122                    signed: false,
3123                },
3124            ],
3125            operand_order: &[2, 1, 0],
3126            operand_kinds: &[
3127                OperandConstraintKind::Gpr64Register,
3128                OperandConstraintKind::Gpr64Register,
3129                OperandConstraintKind::Immediate,
3130            ],
3131            implicit_defaults: &[],
3132            memory_addressing: MemoryAddressingConstraintSpec::None,
3133            field_scales: &[1, 1, 1],
3134            split_immediate_plan: None,
3135            gpr32_extend_compatibility: 0,
3136        };
3137
3138        const PICK_WIDE_DEFAULTED: EncodingSpec = EncodingSpec {
3139            mnemonic: "pickcompat",
3140            variant: "PICKCOMPAT_wide_defaulted",
3141            opcode: 0x1000_0000,
3142            opcode_mask: 0xf000_0000,
3143            fields: &[
3144                BitFieldSpec {
3145                    name: "sh",
3146                    lsb: 22,
3147                    width: 1,
3148                    signed: false,
3149                },
3150                BitFieldSpec {
3151                    name: "imm12",
3152                    lsb: 10,
3153                    width: 12,
3154                    signed: false,
3155                },
3156                BitFieldSpec {
3157                    name: "Rn",
3158                    lsb: 5,
3159                    width: 5,
3160                    signed: false,
3161                },
3162                BitFieldSpec {
3163                    name: "Rd",
3164                    lsb: 0,
3165                    width: 5,
3166                    signed: false,
3167                },
3168            ],
3169            operand_order: &[3, 2, 1],
3170            operand_kinds: &[
3171                OperandConstraintKind::Gpr64Register,
3172                OperandConstraintKind::Gpr64Register,
3173                OperandConstraintKind::Immediate,
3174            ],
3175            implicit_defaults: &[ImplicitField {
3176                field_index: 0,
3177                value: 0,
3178            }],
3179            memory_addressing: MemoryAddressingConstraintSpec::None,
3180            field_scales: &[1, 1, 1, 1],
3181            split_immediate_plan: None,
3182            gpr32_extend_compatibility: 0,
3183        };
3184
3185        let operands = [
3186            Operand::Register(RegisterOperand {
3187                code: 1,
3188                class: RegClass::X,
3189                arrangement: None,
3190                lane: None,
3191            }),
3192            Operand::Register(RegisterOperand {
3193                code: 2,
3194                class: RegClass::X,
3195                arrangement: None,
3196                lane: None,
3197            }),
3198            Operand::Immediate(1),
3199        ];
3200
3201        let selected = encode(&[PICK_WIDE_DEFAULTED, PICK_NARROW], "pickcompat", &operands)
3202            .expect("compatible fixed bits should allow score-based selection");
3203        let expected = encode_by_spec_operands(&PICK_NARROW, &operands)
3204            .expect("narrow immediate form should encode");
3205        assert_eq!(selected.unpack(), expected.unpack());
3206    }
3207
3208    #[test]
3209    fn mnemonic_selection_reports_ambiguous_variant_on_score_tie() {
3210        const PICK_A: EncodingSpec = EncodingSpec {
3211            mnemonic: "picktie",
3212            variant: "PICKTIE_64_a",
3213            opcode: 0x4000_0000,
3214            opcode_mask: 0xf000_0000,
3215            fields: &[
3216                BitFieldSpec {
3217                    name: "Rn",
3218                    lsb: 5,
3219                    width: 5,
3220                    signed: false,
3221                },
3222                BitFieldSpec {
3223                    name: "Rd",
3224                    lsb: 0,
3225                    width: 5,
3226                    signed: false,
3227                },
3228            ],
3229            operand_order: &[1, 0],
3230            operand_kinds: &[
3231                OperandConstraintKind::Gpr64Register,
3232                OperandConstraintKind::Gpr64Register,
3233            ],
3234            implicit_defaults: &[],
3235            memory_addressing: MemoryAddressingConstraintSpec::None,
3236            field_scales: &[1, 1],
3237            split_immediate_plan: None,
3238            gpr32_extend_compatibility: 0,
3239        };
3240
3241        const PICK_B: EncodingSpec = EncodingSpec {
3242            mnemonic: "picktie",
3243            variant: "PICKTIE_64_b",
3244            opcode: 0x4000_0000,
3245            opcode_mask: 0xf000_0000,
3246            fields: PICK_A.fields,
3247            operand_order: &[0, 1],
3248            operand_kinds: PICK_A.operand_kinds,
3249            implicit_defaults: PICK_A.implicit_defaults,
3250            memory_addressing: PICK_A.memory_addressing,
3251            field_scales: PICK_A.field_scales,
3252            split_immediate_plan: PICK_A.split_immediate_plan,
3253            gpr32_extend_compatibility: PICK_A.gpr32_extend_compatibility,
3254        };
3255
3256        let err = encode(
3257            &[PICK_A, PICK_B],
3258            "picktie",
3259            &[
3260                Operand::Register(RegisterOperand {
3261                    code: 1,
3262                    class: RegClass::X,
3263                    arrangement: None,
3264                    lane: None,
3265                }),
3266                Operand::Register(RegisterOperand {
3267                    code: 2,
3268                    class: RegClass::X,
3269                    arrangement: None,
3270                    lane: None,
3271                }),
3272            ],
3273        )
3274        .expect_err("equal-score distinct encodings must stay ambiguous");
3275        assert_eq!(err, EncodeError::AmbiguousVariant);
3276    }
3277}