1use 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 kind: OperandConstraintKind,
26 value: i64,
28 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 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 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#[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 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 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 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#[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#[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#[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#[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 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 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#[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}