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