Rust · 319224 bytes Raw Blame History
1 //! ARM64 assembly parser.
2 //!
3 //! Parses tokenized assembly into structured statements: instructions (as `Inst`),
4 //! labels, and directives. Resolves instruction aliases (cmp, mov, tst, etc.)
5 //! to their canonical forms.
6
7 use crate::encode::{AddrExtend, BarrierOpt, Inst, RegExtend, RegShift};
8 use crate::expr::{self, Expr, SymbolModifier};
9 use crate::lex::{LexError, Lexer, Tok, Token};
10 use crate::reg::*;
11
12 use std::collections::BTreeMap;
13 use std::fmt;
14
15 /// A parsed assembly statement.
16 #[derive(Debug, Clone, PartialEq)]
17 pub enum Stmt {
18 Label(String),
19 Instruction(Inst),
20 /// Instruction with a label reference that needs relocation.
21 InstructionWithReloc(Inst, LabelRef),
22 Directive(Directive),
23 }
24
25 /// A label reference in an instruction that needs relocation.
26 #[derive(Debug, Clone, PartialEq)]
27 pub struct LabelRef {
28 pub symbol: String,
29 pub kind: RelocKind,
30 pub addend: i64,
31 }
32
33 /// What kind of relocation is needed.
34 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
35 pub enum RelocKind {
36 /// ADRP — page-relative (ARM64_RELOC_PAGE21)
37 Page21,
38 /// ADRP — page-relative to GOT slot page (ARM64_RELOC_GOT_LOAD_PAGE21)
39 GotLoadPage21,
40 /// ADRP — page-relative to TLVP slot page (ARM64_RELOC_TLVP_LOAD_PAGE21)
41 TlvpLoadPage21,
42 /// ADD/LDR — page offset (ARM64_RELOC_PAGEOFF12)
43 PageOff12,
44 /// LDR — page offset to GOT slot (ARM64_RELOC_GOT_LOAD_PAGEOFF12)
45 GotLoadPageOff12,
46 /// LDR — page offset to TLVP slot (ARM64_RELOC_TLVP_LOAD_PAGEOFF12)
47 TlvpLoadPageOff12,
48 /// B/BL — branch (ARM64_RELOC_BRANCH26)
49 Branch26,
50 /// B.cond / CBZ / CBNZ — assembler-resolved 19-bit branch immediate
51 Branch19,
52 /// TBZ / TBNZ — assembler-resolved 14-bit branch immediate
53 Branch14,
54 /// LDR literal — assembler-resolved 19-bit PC-relative load
55 Literal19,
56 /// ADR — assembler-resolved 21-bit PC-relative address
57 Adr21,
58 }
59
60 /// Assembly directives.
61 #[derive(Debug, Clone, PartialEq)]
62 pub enum Directive {
63 Text,
64 Data,
65 Comm {
66 name: String,
67 size: u64,
68 align_pow2: u8,
69 },
70 Extern(String),
71 Global(String),
72 PrivateExtern(String),
73 WeakReference(String),
74 WeakDefinition(String),
75 Set(String, Expr),
76 Align {
77 power: u32,
78 fill: Option<u8>,
79 max_skip: Option<u64>,
80 },
81 P2Align {
82 power: u32,
83 fill: Option<u8>,
84 max_skip: Option<u64>,
85 },
86 Byte(Vec<Expr>),
87 Short(Vec<Expr>),
88 Word(Vec<Expr>),
89 Quad(Vec<Expr>),
90 Ascii(Vec<u8>),
91 Asciz(Vec<u8>),
92 Space(u64),
93 Fill {
94 repeat: u64,
95 size: u8,
96 value: u64,
97 },
98 Zerofill {
99 segment: String,
100 section: String,
101 symbol: Option<String>,
102 size: u64,
103 align_pow2: u32,
104 },
105 CfiStartProc,
106 CfiEndProc,
107 CfiDefCfa {
108 register: GpReg,
109 offset: i64,
110 },
111 CfiDefCfaOffset(i64),
112 CfiDefCfaRegister(GpReg),
113 CfiOffset {
114 register: GpReg,
115 offset: i64,
116 },
117 CfiRestore(GpReg),
118 CfiAdjustCfaOffset(i64),
119 Section(String, String),
120 SubsectionsViaSymbols,
121 BuildVersion(BuildVersionDirective),
122 LinkerOptimizationHint(LinkerOptimizationHintDirective),
123 }
124
125 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
126 pub struct VersionTriple {
127 pub major: u32,
128 pub minor: u32,
129 pub patch: u32,
130 }
131
132 #[derive(Debug, Clone, PartialEq, Eq)]
133 pub struct BuildVersionDirective {
134 pub platform: String,
135 pub minos: VersionTriple,
136 pub sdk: Option<VersionTriple>,
137 }
138
139 #[derive(Debug, Clone, PartialEq, Eq)]
140 pub struct LinkerOptimizationHintDirective {
141 pub kind: String,
142 pub labels: Vec<String>,
143 }
144
145 fn linker_optimization_hint_label_count(kind: &str) -> Option<usize> {
146 match kind {
147 "AdrpLdrGotLdr" => Some(3),
148 "AdrpAdd" | "AdrpLdr" | "AdrpLdrGot" => Some(2),
149 _ => None,
150 }
151 }
152
153 /// Parse error with source location.
154 #[derive(Debug, Clone)]
155 pub struct ParseError {
156 pub line: u32,
157 pub col: u32,
158 pub msg: String,
159 }
160
161 fn allowed_section_attrs(seg: &str, sect: &str) -> Option<&'static [&'static str]> {
162 let seg = seg.to_ascii_lowercase();
163 let sect = sect.to_ascii_lowercase();
164 match (seg.as_str(), sect.as_str()) {
165 ("__text", "__text") => Some(&["regular", "pure_instructions"]),
166 ("__text", "__cstring") => Some(&["regular", "cstring_literals"]),
167 ("__text", "__literal16") => Some(&["regular", "16byte_literals"]),
168 ("__text", "__const") => Some(&["regular"]),
169 ("__data", "__data") => Some(&["regular"]),
170 ("__data", "__const") => Some(&["regular"]),
171 // Apple `as` accepts either thread-local attr spelling here and
172 // canonicalizes based on the section name.
173 ("__data", "__thread_data") => {
174 Some(&["regular", "thread_local_regular", "thread_local_variables"])
175 }
176 ("__data", "__thread_vars") => {
177 Some(&["regular", "thread_local_regular", "thread_local_variables"])
178 }
179 _ => None,
180 }
181 }
182
183 fn validate_section_attrs(seg: &str, sect: &str, attrs: &[String]) -> Result<(), String> {
184 if attrs.is_empty() {
185 return Ok(());
186 }
187 let Some(allowed) = allowed_section_attrs(seg, sect) else {
188 return Err(format!(
189 "section {},{} does not support explicit attributes",
190 seg, sect
191 ));
192 };
193 let unsupported: Vec<_> = attrs
194 .iter()
195 .filter(|attr| {
196 !allowed
197 .iter()
198 .any(|allowed_attr| attr.eq_ignore_ascii_case(allowed_attr))
199 })
200 .cloned()
201 .collect();
202 if unsupported.is_empty() {
203 if seg.eq_ignore_ascii_case("__TEXT")
204 && sect.eq_ignore_ascii_case("__text")
205 && attrs
206 .iter()
207 .any(|attr| attr.eq_ignore_ascii_case("pure_instructions"))
208 && !attrs
209 .iter()
210 .any(|attr| attr.eq_ignore_ascii_case("regular"))
211 {
212 return Err(format!(
213 "section {},{} requires 'regular' when using 'pure_instructions'",
214 seg, sect
215 ));
216 }
217
218 return Ok(());
219 }
220
221 Err(format!(
222 "unsupported section attributes for {},{}: {} (supported attrs: {})",
223 seg,
224 sect,
225 unsupported.join(", "),
226 allowed.join(", ")
227 ))
228 }
229
230 #[derive(Debug, Clone, PartialEq)]
231 pub struct LocatedStmt {
232 pub stmt: Stmt,
233 pub line: u32,
234 pub col: u32,
235 }
236
237 impl fmt::Display for ParseError {
238 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239 write!(f, "{}:{}: error: {}", self.line, self.col, self.msg)
240 }
241 }
242
243 impl std::error::Error for ParseError {}
244
245 impl From<LexError> for ParseError {
246 fn from(e: LexError) -> Self {
247 ParseError {
248 line: e.line,
249 col: e.col,
250 msg: e.msg,
251 }
252 }
253 }
254
255 /// Parse assembly source text into a list of statements.
256 pub fn parse(src: &str) -> Result<Vec<Stmt>, ParseError> {
257 Ok(parse_with_locations(src)?
258 .into_iter()
259 .map(|stmt| stmt.stmt)
260 .collect())
261 }
262
263 /// Parse assembly source text into statements with source locations.
264 pub fn parse_with_locations(src: &str) -> Result<Vec<LocatedStmt>, ParseError> {
265 let tokens = Lexer::tokenize(src)?;
266 let mut p = Parser::new(&tokens);
267 p.parse_program()
268 }
269
270 struct Parser<'a> {
271 tokens: &'a [Token],
272 pos: usize,
273 absolute_symbols: BTreeMap<String, i64>,
274 numeric_labels: BTreeMap<u32, u32>,
275 }
276
277 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
278 enum NumericLabelDirection {
279 Forward,
280 Backward,
281 }
282
283 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
284 enum AddSubModifier {
285 Shift(RegShift, u8),
286 Extend(RegExtend, u8),
287 }
288
289 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
290 enum GpRegKind {
291 Reg,
292 Sp,
293 Zr,
294 }
295
296 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
297 enum FpMemWidth {
298 B8,
299 H16,
300 S32,
301 D64,
302 Q128,
303 }
304
305 impl FpMemWidth {
306 fn scale(self) -> u8 {
307 match self {
308 FpMemWidth::B8 => 0,
309 FpMemWidth::H16 => 1,
310 FpMemWidth::S32 => 2,
311 FpMemWidth::D64 => 3,
312 FpMemWidth::Q128 => 4,
313 }
314 }
315 }
316
317 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
318 enum SimdLaneWidth {
319 B8,
320 H16,
321 S32,
322 D64,
323 }
324
325 impl SimdLaneWidth {
326 fn max_index(self) -> u8 {
327 match self {
328 SimdLaneWidth::B8 => 15,
329 SimdLaneWidth::H16 => 7,
330 SimdLaneWidth::S32 => 3,
331 SimdLaneWidth::D64 => 1,
332 }
333 }
334 }
335
336 impl<'a> Parser<'a> {
337 fn new(tokens: &'a [Token]) -> Self {
338 Self {
339 tokens,
340 pos: 0,
341 absolute_symbols: BTreeMap::new(),
342 numeric_labels: BTreeMap::new(),
343 }
344 }
345
346 fn peek(&self) -> &Tok {
347 if self.pos < self.tokens.len() {
348 &self.tokens[self.pos].kind
349 } else {
350 &Tok::Eof
351 }
352 }
353
354 fn token_at(&self, pos: usize) -> &Token {
355 &self.tokens[pos.min(self.tokens.len() - 1)]
356 }
357
358 fn cur(&self) -> &Token {
359 self.token_at(self.pos)
360 }
361
362 fn advance(&mut self) -> &Token {
363 let t = &self.tokens[self.pos.min(self.tokens.len() - 1)];
364 if self.pos < self.tokens.len() {
365 self.pos += 1;
366 }
367 t
368 }
369
370 fn expect_ident(&mut self) -> Result<String, ParseError> {
371 match self.peek().clone() {
372 Tok::Ident(s) => {
373 self.advance();
374 Ok(s)
375 }
376 other => Err(self.err(format!("expected identifier, got {}", other))),
377 }
378 }
379
380 fn parse_section_attr(&mut self) -> Result<String, ParseError> {
381 let mut attr = String::new();
382 let mut consumed = false;
383 loop {
384 match self.peek().clone() {
385 Tok::Ident(s) => {
386 self.advance();
387 attr.push_str(&s);
388 consumed = true;
389 }
390 Tok::Integer(n) => {
391 self.advance();
392 attr.push_str(&n.to_string());
393 consumed = true;
394 }
395 _ => break,
396 }
397 }
398 if consumed {
399 Ok(attr)
400 } else {
401 Err(self.err(format!("expected section attribute, got {}", self.peek())))
402 }
403 }
404
405 fn expect(&mut self, kind: &Tok) -> Result<(), ParseError> {
406 if self.peek() == kind {
407 self.advance();
408 Ok(())
409 } else {
410 Err(self.err(format!("expected {}, got {}", kind, self.peek())))
411 }
412 }
413
414 fn eat(&mut self, kind: &Tok) -> bool {
415 if self.peek() == kind {
416 self.advance();
417 true
418 } else {
419 false
420 }
421 }
422
423 fn err(&self, msg: String) -> ParseError {
424 let t = self.cur();
425 ParseError {
426 line: t.line,
427 col: t.col,
428 msg,
429 }
430 }
431
432 fn at_end_of_stmt(&self) -> bool {
433 matches!(self.peek(), Tok::Newline | Tok::Eof)
434 }
435
436 fn skip_newlines(&mut self) {
437 while self.peek() == &Tok::Newline {
438 self.advance();
439 }
440 }
441
442 fn parse_program(&mut self) -> Result<Vec<LocatedStmt>, ParseError> {
443 let mut stmts = Vec::new();
444 self.skip_newlines();
445 while self.peek() != &Tok::Eof {
446 self.parse_line(&mut stmts)?;
447 self.skip_newlines();
448 }
449 Ok(stmts)
450 }
451
452 fn parse_line(&mut self, stmts: &mut Vec<LocatedStmt>) -> Result<(), ParseError> {
453 // A line can be: label, directive, instruction, or empty.
454 match self.peek().clone() {
455 Tok::Ident(ref name) if name.starts_with('.') => {
456 // Could be directive or local label.
457 let start = self.cur().clone();
458 let name = name.clone();
459 self.advance();
460 if self.eat(&Tok::Colon) {
461 // Local label (.Lxxx:)
462 stmts.push(LocatedStmt {
463 stmt: Stmt::Label(name),
464 line: start.line,
465 col: start.col,
466 });
467 } else {
468 // Directive
469 stmts.push(LocatedStmt {
470 stmt: self.parse_directive(&name, start.line, start.col)?,
471 line: start.line,
472 col: start.col,
473 });
474 }
475 }
476 Tok::Ident(_) => {
477 let start = self.cur().clone();
478 let name = if let Tok::Ident(s) = self.peek().clone() {
479 s
480 } else {
481 unreachable!()
482 };
483 self.advance();
484 if self.eat(&Tok::Colon) {
485 // Label
486 stmts.push(LocatedStmt {
487 stmt: Stmt::Label(name),
488 line: start.line,
489 col: start.col,
490 });
491 // There might be an instruction on the same line.
492 if !self.at_end_of_stmt() {
493 self.parse_line(stmts)?;
494 }
495 } else {
496 // Instruction mnemonic — might have a condition suffix.
497 let mnemonic = self.resolve_mnemonic(&name)?;
498 stmts.push(LocatedStmt {
499 stmt: self.parse_instruction(&mnemonic)?,
500 line: start.line,
501 col: start.col,
502 });
503 }
504 }
505 Tok::Integer(_) if self.numeric_label_definition_number().is_some() => {
506 let start = self.cur().clone();
507 let number = self.numeric_label_definition_number().unwrap();
508 self.advance();
509 self.expect(&Tok::Colon)?;
510 stmts.push(LocatedStmt {
511 stmt: Stmt::Label(self.define_numeric_label(number)),
512 line: start.line,
513 col: start.col,
514 });
515 if !self.at_end_of_stmt() {
516 self.parse_line(stmts)?;
517 }
518 }
519 Tok::Newline | Tok::Eof => {}
520 _ => return Err(self.err(format!("unexpected token: {}", self.peek()))),
521 }
522 Ok(())
523 }
524
525 /// Handle conditional branch mnemonics: "b" followed by ".eq", ".ne", etc.
526 fn resolve_mnemonic(&mut self, name: &str) -> Result<String, ParseError> {
527 let lower = name.to_lowercase();
528 if let Some(cond_name) = lower.strip_prefix("b.") {
529 if parse_condition(cond_name).is_some() {
530 return Ok(format!("b.{}", cond_name));
531 }
532 }
533 if lower == "b" {
534 // Check for .cond suffix (e.g., B.EQ, b.ne)
535 if let Tok::Ident(ref cond) = self.peek().clone() {
536 if let Some(cond_name) = cond.strip_prefix('.') {
537 if parse_condition(cond_name).is_none() {
538 return Ok(lower);
539 }
540 let full = format!("b.{}", cond_name.to_lowercase());
541 self.advance();
542 return Ok(full);
543 }
544 }
545 }
546 Ok(lower)
547 }
548
549 fn parse_directive(&mut self, name: &str, line: u32, col: u32) -> Result<Stmt, ParseError> {
550 let dir = match name {
551 ".text" => Directive::Text,
552 ".data" => Directive::Data,
553 ".cstring" => Directive::Section("__TEXT".into(), "__cstring".into()),
554 ".comm" => {
555 let sym = self.expect_ident()?;
556 self.expect(&Tok::Comma)?;
557 let size = self.parse_const_expr("common size expression")? as u64;
558 let align_pow2 = if self.eat(&Tok::Comma) {
559 self.parse_const_expr("common alignment expression")? as u8
560 } else {
561 0
562 };
563 Directive::Comm {
564 name: sym,
565 size,
566 align_pow2,
567 }
568 }
569 ".extern" => {
570 let sym = self.expect_ident()?;
571 Directive::Extern(sym)
572 }
573 ".global" | ".globl" => {
574 let sym = self.expect_ident()?;
575 Directive::Global(sym)
576 }
577 ".private_extern" => {
578 let sym = self.expect_ident()?;
579 Directive::PrivateExtern(sym)
580 }
581 ".weak_reference" => {
582 let sym = self.expect_ident()?;
583 Directive::WeakReference(sym)
584 }
585 ".weak_definition" => {
586 let sym = self.expect_ident()?;
587 Directive::WeakDefinition(sym)
588 }
589 ".set" | ".equ" => {
590 let sym = self.expect_ident()?;
591 self.expect(&Tok::Comma)?;
592 let expr = self.parse_expr()?;
593 if let Ok(value) = expr::eval_with_symbols(&expr, &self.absolute_symbols) {
594 self.absolute_symbols.insert(sym.clone(), value);
595 } else {
596 self.absolute_symbols.remove(&sym);
597 }
598 Directive::Set(sym, expr)
599 }
600 ".align" => {
601 let (power, fill, max_skip) = self.parse_alignment_directive_args()?;
602 Directive::Align {
603 power,
604 fill,
605 max_skip,
606 }
607 }
608 ".p2align" => {
609 let (power, fill, max_skip) = self.parse_alignment_directive_args()?;
610 Directive::P2Align {
611 power,
612 fill,
613 max_skip,
614 }
615 }
616 ".byte" => Directive::Byte(self.parse_expr_list()?),
617 ".short" => Directive::Short(self.parse_expr_list()?),
618 ".word" | ".long" => Directive::Word(self.parse_expr_list()?),
619 ".quad" => Directive::Quad(self.parse_expr_list()?),
620 ".ascii" => {
621 if let Tok::StringLit(s) = self.peek().clone() {
622 self.advance();
623 Directive::Ascii(s)
624 } else {
625 return Err(self.err("expected string after .ascii".into()));
626 }
627 }
628 ".asciz" | ".string" => {
629 if let Tok::StringLit(s) = self.peek().clone() {
630 self.advance();
631 let mut bytes = s;
632 bytes.push(0); // null terminator
633 Directive::Asciz(bytes)
634 } else {
635 return Err(self.err("expected string after .asciz".into()));
636 }
637 }
638 ".space" | ".skip" => {
639 let n = self.parse_const_expr("space expression")? as u64;
640 Directive::Space(n)
641 }
642 ".zero" => {
643 let n = self.parse_const_expr("zero expression")? as u64;
644 Directive::Space(n)
645 }
646 ".fill" => {
647 let repeat = self.parse_const_expr("fill repeat expression")? as u64;
648 self.expect(&Tok::Comma)?;
649 let size = self.parse_const_expr("fill size expression")? as u8;
650 let value = if self.eat(&Tok::Comma) {
651 self.parse_const_expr("fill value expression")? as u64
652 } else {
653 0
654 };
655 Directive::Fill {
656 repeat,
657 size,
658 value,
659 }
660 }
661 ".zerofill" => {
662 let segment = self.expect_ident()?;
663 self.expect(&Tok::Comma)?;
664 let section = self.expect_ident()?;
665 self.expect(&Tok::Comma)?;
666 let symbol = if matches!(self.peek(), Tok::Ident(_)) {
667 Some(self.expect_ident()?)
668 } else {
669 None
670 };
671 self.expect(&Tok::Comma)?;
672 let size = self.parse_const_expr("zerofill size expression")? as u64;
673 let align_pow2 = if self.eat(&Tok::Comma) {
674 self.parse_const_expr("zerofill alignment expression")? as u32
675 } else {
676 0
677 };
678 Directive::Zerofill {
679 segment,
680 section,
681 symbol,
682 size,
683 align_pow2,
684 }
685 }
686 ".tbss" => {
687 let symbol = self.expect_ident()?;
688 self.expect(&Tok::Comma)?;
689 let size = self.parse_const_expr("tbss size expression")? as u64;
690 self.expect(&Tok::Comma)?;
691 let align_pow2 = self.parse_const_expr("tbss alignment expression")? as u32;
692 Directive::Zerofill {
693 segment: "__DATA".into(),
694 section: "__thread_bss".into(),
695 symbol: Some(symbol),
696 size,
697 align_pow2,
698 }
699 }
700 ".cfi_startproc" => Directive::CfiStartProc,
701 ".cfi_endproc" => Directive::CfiEndProc,
702 ".cfi_def_cfa" => {
703 let register = self.parse_cfi_register()?;
704 self.expect(&Tok::Comma)?;
705 let offset = self.parse_const_expr("CFA offset")?;
706 Directive::CfiDefCfa { register, offset }
707 }
708 ".cfi_def_cfa_offset" => {
709 Directive::CfiDefCfaOffset(self.parse_const_expr("CFA offset")?)
710 }
711 ".cfi_def_cfa_register" => Directive::CfiDefCfaRegister(self.parse_cfi_register()?),
712 ".cfi_offset" => {
713 let register = self.parse_cfi_register()?;
714 self.expect(&Tok::Comma)?;
715 let offset = self.parse_const_expr("CFI offset")?;
716 Directive::CfiOffset { register, offset }
717 }
718 ".cfi_restore" => Directive::CfiRestore(self.parse_cfi_register()?),
719 ".cfi_adjust_cfa_offset" => {
720 Directive::CfiAdjustCfaOffset(self.parse_const_expr("CFA adjustment")?)
721 }
722 ".section" => {
723 let seg = self.expect_ident()?;
724 self.expect(&Tok::Comma)?;
725 let sect = self.expect_ident()?;
726 let mut attrs = Vec::new();
727 while self.eat(&Tok::Comma) {
728 attrs.push(self.parse_section_attr()?);
729 }
730 if let Err(msg) = validate_section_attrs(&seg, &sect, &attrs) {
731 return Err(self.err(msg));
732 }
733 Directive::Section(seg, sect)
734 }
735 ".subsections_via_symbols" => Directive::SubsectionsViaSymbols,
736 ".build_version" => {
737 let platform = self.expect_ident()?.to_ascii_lowercase();
738 self.expect(&Tok::Comma)?;
739 let minos = self.parse_version_triple("build version minimum OS")?;
740 let sdk = if self.at_end_of_stmt() {
741 None
742 } else {
743 let keyword = self.expect_ident()?;
744 if !keyword.eq_ignore_ascii_case("sdk_version") {
745 return Err(self.err(format!(
746 "expected sdk_version after .build_version, got {}",
747 keyword
748 )));
749 }
750 Some(self.parse_version_triple("build version SDK")?)
751 };
752 if !self.at_end_of_stmt() {
753 return Err(self.err("unexpected tokens after .build_version".into()));
754 }
755 Directive::BuildVersion(BuildVersionDirective {
756 platform,
757 minos,
758 sdk,
759 })
760 }
761 ".loh" => {
762 let kind = self.expect_ident()?;
763 let Some(expected) = linker_optimization_hint_label_count(&kind) else {
764 return Err(self.err(format!(
765 "unsupported .loh kind '{}' (supported: AdrpAdd, AdrpLdr, AdrpLdrGot, AdrpLdrGotLdr)",
766 kind
767 )));
768 };
769 let mut labels = Vec::new();
770 if !self.at_end_of_stmt() {
771 labels.push(self.expect_ident()?);
772 while !self.at_end_of_stmt() {
773 self.expect(&Tok::Comma)?;
774 labels.push(self.expect_ident()?);
775 }
776 }
777 if labels.len() != expected {
778 return Err(self.err(format!(
779 ".loh {} expects {} label{}, got {}",
780 kind,
781 expected,
782 if expected == 1 { "" } else { "s" },
783 labels.len()
784 )));
785 }
786 Directive::LinkerOptimizationHint(LinkerOptimizationHintDirective { kind, labels })
787 }
788 _ if name.starts_with(".cfi_") => {
789 return Err(ParseError {
790 line,
791 col,
792 msg: format!(
793 "unsupported CFI directive '{}' (supported: .cfi_startproc, .cfi_endproc, .cfi_def_cfa, .cfi_def_cfa_offset, .cfi_def_cfa_register, .cfi_offset, .cfi_restore, .cfi_adjust_cfa_offset)",
794 name
795 )});
796 }
797 _ => {
798 return Err(ParseError {
799 line,
800 col,
801 msg: format!("unsupported directive '{}'", name),
802 });
803 }
804 };
805 Ok(Stmt::Directive(dir))
806 }
807
808 fn parse_expr_list(&mut self) -> Result<Vec<Expr>, ParseError> {
809 let mut vals = vec![self.parse_expr()?];
810 while self.eat(&Tok::Comma) {
811 vals.push(self.parse_expr()?);
812 }
813 Ok(vals)
814 }
815
816 fn parse_alignment_directive_args(
817 &mut self,
818 ) -> Result<(u32, Option<u8>, Option<u64>), ParseError> {
819 let power = self.parse_const_expr("alignment expression")? as u32;
820 let mut fill = None;
821 let mut max_skip = None;
822
823 if self.eat(&Tok::Comma) {
824 if !self.at_end_of_stmt() && self.peek() != &Tok::Comma {
825 fill = Some(self.parse_const_expr("alignment fill expression")? as u8);
826 }
827 if self.eat(&Tok::Comma) {
828 max_skip = Some(self.parse_const_expr("alignment max-skip expression")? as u64);
829 }
830 }
831
832 Ok((power, fill, max_skip))
833 }
834
835 fn parse_cfi_register(&mut self) -> Result<GpReg, ParseError> {
836 let (register, _, kind) = self.parse_gp_reg_with_size_kind()?;
837 if matches!(kind, GpRegKind::Zr) {
838 return Err(self.err("CFI directives do not accept the zero register".into()));
839 }
840 Ok(register)
841 }
842
843 fn parse_version_triple(&mut self, context: &str) -> Result<VersionTriple, ParseError> {
844 let major = self.parse_version_component(context)?;
845 self.expect(&Tok::Comma)?;
846 let minor = self.parse_version_component(context)?;
847 let patch = if self.eat(&Tok::Comma) {
848 self.parse_version_component(context)?
849 } else {
850 0
851 };
852 Ok(VersionTriple {
853 major,
854 minor,
855 patch,
856 })
857 }
858
859 fn parse_version_component(&mut self, context: &str) -> Result<u32, ParseError> {
860 match self.peek().clone() {
861 Tok::Integer(value) if value >= 0 => {
862 self.advance();
863 u32::try_from(value).map_err(|_| {
864 self.err(format!(
865 "{} component {} does not fit in u32",
866 context, value
867 ))
868 })
869 }
870 Tok::Integer(value) => Err(self.err(format!(
871 "{} component must be non-negative, got {}",
872 context, value
873 ))),
874 other => Err(self.err(format!("expected integer for {}, got {}", context, other))),
875 }
876 }
877
878 fn parse_const_expr(&mut self, context: &str) -> Result<i64, ParseError> {
879 let expr = self.parse_expr()?;
880 expr::eval_with_symbols(&expr, &self.absolute_symbols).map_err(|err| {
881 self.err(format!(
882 "{} must be a pure constant expression: {}",
883 context, err
884 ))
885 })
886 }
887
888 fn starts_const_expr(&self) -> bool {
889 if self.numeric_label_ref_at(self.pos).is_some() {
890 return false;
891 }
892 matches!(self.peek(), Tok::Integer(_) | Tok::Minus | Tok::LParen)
893 }
894
895 fn starts_immediate_expr(&self) -> bool {
896 self.peek() == &Tok::Hash || self.starts_const_expr()
897 }
898
899 fn parse_immediate_const_expr(&mut self, context: &str) -> Result<i64, ParseError> {
900 self.eat(&Tok::Hash);
901 self.parse_const_expr(context)
902 }
903
904 fn parse_logical_immediate_value(&mut self, sf: bool) -> Result<u64, ParseError> {
905 let imm = self.parse_immediate_const_expr("logical immediate")?;
906 let raw = if sf { imm as u64 } else { (imm as u32) as u64 };
907 if logical_immediate_encodable(raw, if sf { 64 } else { 32 }) {
908 Ok(raw)
909 } else {
910 Err(self.err(format!(
911 "immediate {:#x} is not encodable as a logical immediate",
912 raw
913 )))
914 }
915 }
916
917 fn parse_expr(&mut self) -> Result<Expr, ParseError> {
918 self.parse_add_sub_expr()
919 }
920
921 fn parse_add_sub_expr(&mut self) -> Result<Expr, ParseError> {
922 let mut expr = self.parse_unary_expr()?;
923 loop {
924 if self.eat(&Tok::Plus) {
925 let rhs = self.parse_unary_expr()?;
926 expr = Expr::Add(Box::new(expr), Box::new(rhs));
927 } else if self.eat(&Tok::Minus) {
928 let rhs = self.parse_unary_expr()?;
929 expr = Expr::Sub(Box::new(expr), Box::new(rhs));
930 } else {
931 break;
932 }
933 }
934 Ok(expr)
935 }
936
937 fn parse_unary_expr(&mut self) -> Result<Expr, ParseError> {
938 if self.eat(&Tok::Minus) {
939 Ok(Expr::UnaryMinus(Box::new(self.parse_unary_expr()?)))
940 } else {
941 self.parse_primary_expr()
942 }
943 }
944
945 fn parse_primary_expr(&mut self) -> Result<Expr, ParseError> {
946 if let Some(symbol) = self.parse_numeric_label_ref()? {
947 return Ok(Expr::Symbol(symbol));
948 }
949 match self.peek().clone() {
950 Tok::Integer(value) => {
951 self.advance();
952 Ok(Expr::Int(value))
953 }
954 Tok::Ident(symbol) => {
955 self.advance();
956 if self.eat(&Tok::At) {
957 let modifier = self.expect_ident()?;
958 let upper = modifier.to_ascii_uppercase();
959 match upper.as_str() {
960 "GOT" => Ok(Expr::ModifiedSymbol {
961 symbol,
962 modifier: SymbolModifier::Got,
963 }),
964 _ => Err(self.err(format!(
965 "unsupported relocation modifier '@{}' in expression",
966 modifier
967 ))),
968 }
969 } else {
970 Ok(Expr::Symbol(symbol))
971 }
972 }
973 Tok::Dot => {
974 self.advance();
975 Ok(Expr::CurrentLocation)
976 }
977 Tok::LParen => {
978 self.advance();
979 let expr = self.parse_expr()?;
980 self.expect(&Tok::RParen)?;
981 Ok(expr)
982 }
983 other => Err(self.err(format!("expected expression, got {}", other))),
984 }
985 }
986
987 fn numeric_label_definition_number(&self) -> Option<u32> {
988 match self.peek() {
989 Tok::Integer(value)
990 if *value >= 0 && matches!(&self.token_at(self.pos + 1).kind, Tok::Colon) =>
991 {
992 u32::try_from(*value).ok()
993 }
994 _ => None,
995 }
996 }
997
998 fn numeric_label_ref_at(&self, pos: usize) -> Option<(u32, NumericLabelDirection)> {
999 let int_tok = self.token_at(pos);
1000 let value = match &int_tok.kind {
1001 Tok::Integer(value) if *value >= 0 => u32::try_from(*value).ok()?,
1002 _ => return None,
1003 };
1004 let suffix_tok = self.token_at(pos + 1);
1005 if suffix_tok.line != int_tok.line {
1006 return None;
1007 }
1008 if suffix_tok.col != int_tok.col + decimal_width(value) {
1009 return None;
1010 }
1011 match &suffix_tok.kind {
1012 Tok::Ident(suffix) if suffix.eq_ignore_ascii_case("f") => {
1013 Some((value, NumericLabelDirection::Forward))
1014 }
1015 Tok::Ident(suffix) if suffix.eq_ignore_ascii_case("b") => {
1016 Some((value, NumericLabelDirection::Backward))
1017 }
1018 _ => None,
1019 }
1020 }
1021
1022 fn parse_numeric_label_ref(&mut self) -> Result<Option<String>, ParseError> {
1023 let Some((number, direction)) = self.numeric_label_ref_at(self.pos) else {
1024 return Ok(None);
1025 };
1026 self.advance();
1027 self.advance();
1028 Ok(Some(self.resolve_numeric_label_ref(number, direction)?))
1029 }
1030
1031 fn parse_label_reference(&mut self) -> Result<String, ParseError> {
1032 if let Some(symbol) = self.parse_numeric_label_ref()? {
1033 Ok(symbol)
1034 } else {
1035 self.expect_ident()
1036 }
1037 }
1038
1039 fn parse_symbol_reloc_modifier(
1040 &mut self,
1041 default: Option<RelocKind>,
1042 allowed: &[(&str, RelocKind)],
1043 context: &str,
1044 ) -> Result<RelocKind, ParseError> {
1045 if !self.eat(&Tok::At) {
1046 return default
1047 .ok_or_else(|| self.err(format!("{} requires a relocation modifier", context)));
1048 }
1049
1050 let modifier = self.expect_ident()?;
1051 let upper = modifier.to_ascii_uppercase();
1052 for (name, kind) in allowed {
1053 if upper == *name {
1054 return Ok(*kind);
1055 }
1056 }
1057
1058 Err(self.err(format!(
1059 "unsupported relocation modifier '@{}' for {}",
1060 modifier, context
1061 )))
1062 }
1063
1064 fn parse_optional_symbol_addend(&mut self) -> Result<i64, ParseError> {
1065 if self.eat(&Tok::Plus) {
1066 self.parse_const_expr("symbol addend")
1067 } else if self.eat(&Tok::Minus) {
1068 Ok(-self.parse_const_expr("symbol addend")?)
1069 } else {
1070 Ok(0)
1071 }
1072 }
1073
1074 fn starts_non_register_symbol_reference(&self) -> bool {
1075 if self.numeric_label_ref_at(self.pos).is_some() {
1076 return true;
1077 }
1078 match self.peek() {
1079 Tok::Ident(name) => !looks_like_gp_register_name(name),
1080 _ => false,
1081 }
1082 }
1083
1084 fn starts_non_register_literal_reference(&self) -> bool {
1085 if self.numeric_label_ref_at(self.pos).is_some() {
1086 return true;
1087 }
1088 match self.peek() {
1089 Tok::Ident(name) => {
1090 !looks_like_gp_register_name(name) && !looks_like_fp_register_name(name)
1091 }
1092 _ => false,
1093 }
1094 }
1095
1096 fn define_numeric_label(&mut self, number: u32) -> String {
1097 let ordinal = self.numeric_labels.entry(number).or_insert(0);
1098 *ordinal += 1;
1099 numeric_label_symbol(number, *ordinal)
1100 }
1101
1102 fn resolve_numeric_label_ref(
1103 &self,
1104 number: u32,
1105 direction: NumericLabelDirection,
1106 ) -> Result<String, ParseError> {
1107 let current = self.numeric_labels.get(&number).copied().unwrap_or(0);
1108 match direction {
1109 NumericLabelDirection::Forward => Ok(numeric_label_symbol(number, current + 1)),
1110 NumericLabelDirection::Backward if current > 0 => {
1111 Ok(numeric_label_symbol(number, current))
1112 }
1113 NumericLabelDirection::Backward => Err(self.err(format!(
1114 "numeric label '{}' has no previous definition",
1115 number
1116 ))),
1117 }
1118 }
1119
1120 fn parse_instruction(&mut self, mnemonic: &str) -> Result<Stmt, ParseError> {
1121 // ADRP and ADD-with-label return Stmt directly (may carry relocation info).
1122 if mnemonic == "adrp" {
1123 return self.parse_adrp();
1124 }
1125 if mnemonic == "add" {
1126 return self.parse_add_sub_stmt(false, false);
1127 }
1128 if mnemonic == "b" {
1129 return self.parse_b();
1130 }
1131 if mnemonic == "bl" {
1132 return self.parse_bl();
1133 }
1134 if mnemonic == "cbz" {
1135 return self.parse_cbz(false);
1136 }
1137 if mnemonic == "cbnz" {
1138 return self.parse_cbz(true);
1139 }
1140 if mnemonic == "tbz" {
1141 return self.parse_tbz(false);
1142 }
1143 if mnemonic == "tbnz" {
1144 return self.parse_tbz(true);
1145 }
1146 if mnemonic == "adr" {
1147 return self.parse_adr();
1148 }
1149 if mnemonic == "ldr" {
1150 return self.parse_ldr_str(true);
1151 }
1152 if mnemonic == "str" {
1153 return self.parse_ldr_str(false);
1154 }
1155 if mnemonic == "ldur" {
1156 return self.parse_ldur_stur(true);
1157 }
1158 if mnemonic == "stur" {
1159 return self.parse_ldur_stur(false);
1160 }
1161 if mnemonic == "ldrsw" {
1162 return self.parse_ldrsw();
1163 }
1164 if mnemonic == "ldaprb" {
1165 return self.parse_ldapr_narrow("ldaprb");
1166 }
1167 if mnemonic == "ldaprh" {
1168 return self.parse_ldapr_narrow("ldaprh");
1169 }
1170 if mnemonic == "ldapr" {
1171 return self.parse_ldapr();
1172 }
1173 if mnemonic == "stlrb" {
1174 return self.parse_stlr_narrow("stlrb");
1175 }
1176 if mnemonic == "stlrh" {
1177 return self.parse_stlr_narrow("stlrh");
1178 }
1179 if mnemonic == "stlr" {
1180 return self.parse_stlr();
1181 }
1182 if mnemonic == "ldaddalb" {
1183 return self.parse_atomic_rmw_narrow("ldaddalb");
1184 }
1185 if mnemonic == "ldaddalh" {
1186 return self.parse_atomic_rmw_narrow("ldaddalh");
1187 }
1188 if mnemonic == "ldaddal" {
1189 return self.parse_ldaddal();
1190 }
1191 if mnemonic == "ldumaxalb" {
1192 return self.parse_atomic_rmw_narrow("ldumaxalb");
1193 }
1194 if mnemonic == "ldumaxalh" {
1195 return self.parse_atomic_rmw_narrow("ldumaxalh");
1196 }
1197 if mnemonic == "ldumaxal" {
1198 return self.parse_atomic_rmw("ldumaxal");
1199 }
1200 if mnemonic == "ldsmaxalb" {
1201 return self.parse_atomic_rmw_narrow("ldsmaxalb");
1202 }
1203 if mnemonic == "ldsmaxalh" {
1204 return self.parse_atomic_rmw_narrow("ldsmaxalh");
1205 }
1206 if mnemonic == "ldsmaxal" {
1207 return self.parse_ldsmaxal();
1208 }
1209 if mnemonic == "lduminalb" {
1210 return self.parse_atomic_rmw_narrow("lduminalb");
1211 }
1212 if mnemonic == "lduminalh" {
1213 return self.parse_atomic_rmw_narrow("lduminalh");
1214 }
1215 if mnemonic == "lduminal" {
1216 return self.parse_atomic_rmw("lduminal");
1217 }
1218 if mnemonic == "ldsminalb" {
1219 return self.parse_atomic_rmw_narrow("ldsminalb");
1220 }
1221 if mnemonic == "ldsminalh" {
1222 return self.parse_atomic_rmw_narrow("ldsminalh");
1223 }
1224 if mnemonic == "ldsminal" {
1225 return self.parse_ldsminal();
1226 }
1227 if mnemonic == "ldclralb" {
1228 return self.parse_atomic_rmw_narrow("ldclralb");
1229 }
1230 if mnemonic == "ldclralh" {
1231 return self.parse_atomic_rmw_narrow("ldclralh");
1232 }
1233 if mnemonic == "ldclral" {
1234 return self.parse_ldclral();
1235 }
1236 if mnemonic == "ldeoralb" {
1237 return self.parse_atomic_rmw_narrow("ldeoralb");
1238 }
1239 if mnemonic == "ldeoralh" {
1240 return self.parse_atomic_rmw_narrow("ldeoralh");
1241 }
1242 if mnemonic == "ldeoral" {
1243 return self.parse_ldeoral();
1244 }
1245 if mnemonic == "ldsetalb" {
1246 return self.parse_atomic_rmw_narrow("ldsetalb");
1247 }
1248 if mnemonic == "ldsetalh" {
1249 return self.parse_atomic_rmw_narrow("ldsetalh");
1250 }
1251 if mnemonic == "ldsetal" {
1252 return self.parse_ldsetal();
1253 }
1254 if mnemonic == "swpalb" {
1255 return self.parse_atomic_rmw_narrow("swpalb");
1256 }
1257 if mnemonic == "swpalh" {
1258 return self.parse_atomic_rmw_narrow("swpalh");
1259 }
1260 if mnemonic == "swpal" {
1261 return self.parse_swpal();
1262 }
1263 if mnemonic == "casalb" {
1264 return self.parse_atomic_rmw_narrow("casalb");
1265 }
1266 if mnemonic == "casalh" {
1267 return self.parse_atomic_rmw_narrow("casalh");
1268 }
1269 if mnemonic == "casal" {
1270 return self.parse_casal();
1271 }
1272 if let Some(cond) = mnemonic.strip_prefix("b.") {
1273 return self.parse_bcond(cond);
1274 }
1275
1276 // All other instructions return Inst, wrapped as Stmt::Instruction.
1277 let inst = match mnemonic {
1278 // ---- Data processing (register + immediate) ----
1279 "sub" => self.parse_add_sub(true, false),
1280 "adds" => self.parse_add_sub(false, true),
1281 "subs" => self.parse_add_sub(true, true),
1282 "cmp" => self.parse_cmp(),
1283 "cmn" => self.parse_cmn(),
1284 "mul" => self.parse_3reg("mul"),
1285 "umull" => self.parse_umull(),
1286 "madd" => self.parse_madd(),
1287 "msub" => self.parse_madd_sub("msub"),
1288 "sdiv" => self.parse_3reg("sdiv"),
1289 "udiv" => self.parse_3reg("udiv"),
1290
1291 // Logic
1292 "and" => self.parse_logic("and"),
1293 "and.16b" => self.parse_simd_logic_16b("and.16b"),
1294 "bic.16b" => self.parse_simd_logic_16b("bic.16b"),
1295 "bif.16b" => self.parse_simd_logic_16b("bif.16b"),
1296 "bit.16b" => self.parse_simd_logic_16b("bit.16b"),
1297 "bsl.16b" => self.parse_simd_logic_16b("bsl.16b"),
1298 "orr" => self.parse_logic("orr"),
1299 "orr.16b" => self.parse_simd_logic_16b("orr.16b"),
1300 "eor" => self.parse_logic("eor"),
1301 "eor.16b" => self.parse_simd_logic_16b("eor.16b"),
1302 "ands" => self.parse_logic("ands"),
1303 "neg" => self.parse_neg(),
1304 "mvn" => self.parse_mvn(),
1305 "tst" => self.parse_tst(),
1306
1307 // Move
1308 "csel" => self.parse_csel(),
1309 "csinc" => self.parse_csinc(),
1310 "csinv" => self.parse_csinv(),
1311 "csneg" => self.parse_csneg(),
1312 "ccmp" => self.parse_ccmp(),
1313 "ccmn" => self.parse_ccmn(),
1314 "cset" => self.parse_cset(),
1315 "csetm" => self.parse_csetm(),
1316 "cinc" => self.parse_cinc(),
1317 "cinv" => self.parse_cinv(),
1318 "cneg" => self.parse_cneg(),
1319 "mov" => self.parse_mov(),
1320 "mov.s" | "mov.d" | "mov.h" | "mov.b" => self.parse_simd_lane_insert(mnemonic),
1321 "umov.h" | "umov.b" => self.parse_simd_lane_extract_gp(mnemonic),
1322 "smov.h" | "smov.b" => self.parse_simd_lane_extract_gp_signed(mnemonic),
1323 "ld1.s" | "ld1.d" | "ld1.h" | "ld1.b" => self.parse_simd_lane_load(mnemonic),
1324 "mov.8b" | "mov.16b" | "mov.4s" | "mov.2d" => self.parse_simd_mov(mnemonic),
1325 "dup.16b" | "dup.8h" | "dup.4s" | "dup.2d" => self.parse_simd_dup(mnemonic),
1326 "tbl.16b" => self.parse_simd_table_lookup("tbl.16b"),
1327 "tbx.16b" => self.parse_simd_table_lookup("tbx.16b"),
1328 "ext.16b" => self.parse_simd_ext_16b(),
1329 "rev64.4s" => self.parse_simd_rev64_4s(),
1330 "zip1.4s" | "zip2.4s" | "uzp1.4s" | "uzp2.4s" | "trn1.4s" | "trn2.4s" => {
1331 self.parse_simd_shuffle_4s(mnemonic)
1332 }
1333 "zip1.2d" | "zip2.2d" | "uzp1.2d" | "uzp2.2d" | "trn1.2d" | "trn2.2d" => {
1334 self.parse_simd_shuffle_2d(mnemonic)
1335 }
1336 "movz" => self.parse_mov_wide("movz"),
1337 "movk" => self.parse_mov_wide("movk"),
1338 "movn" => self.parse_mov_wide("movn"),
1339
1340 // Shifts
1341 "lsl" => self.parse_shift("lsl"),
1342 "lsr" => self.parse_shift("lsr"),
1343 "asr" => self.parse_shift("asr"),
1344 "ubfiz" => self.parse_bitfield_alias("ubfiz"),
1345 "bfi" => self.parse_bitfield_alias("bfi"),
1346 "bfxil" => self.parse_bitfield_alias("bfxil"),
1347
1348 // Branches
1349 "ret" => self.parse_ret(),
1350 "br" => {
1351 let rn = self.parse_gp_reg()?;
1352 Ok(Inst::Br { rn })
1353 }
1354 "blr" => {
1355 let rn = self.parse_gp_reg()?;
1356 Ok(Inst::Blr { rn })
1357 }
1358
1359 // Load/store
1360 "ldrb" => self.parse_ldstb_h("ldrb"),
1361 "ldrsb" => self.parse_ldst_signed_b_h("ldrsb"),
1362 "ldrh" => self.parse_ldstb_h("ldrh"),
1363 "ldrsh" => self.parse_ldst_signed_b_h("ldrsh"),
1364 "strb" => self.parse_ldstb_h("strb"),
1365 "strh" => self.parse_ldstb_h("strh"),
1366 "stp" => self.parse_ldp_stp(false),
1367 "ldp" => self.parse_ldp_stp(true),
1368
1369 // FP arithmetic (double)
1370 "fadd" => self.parse_fp_arith("fadd"),
1371 "addp.16b" | "smaxp.16b" | "sminp.16b" | "umaxp.16b" | "uminp.16b" => {
1372 self.parse_simd_int_arith_16b(mnemonic)
1373 }
1374 "addp.2d" => self.parse_simd_int_arith_2d(mnemonic),
1375 "addp.8h" | "smaxp.8h" | "sminp.8h" | "umaxp.8h" | "uminp.8h" => {
1376 self.parse_simd_int_arith_8h(mnemonic)
1377 }
1378 "add.4s" | "addp.4s" | "sub.4s" | "smax.4s" | "smaxp.4s" | "smin.4s" | "sminp.4s"
1379 | "umax.4s" | "umaxp.4s" | "umin.4s" | "uminp.4s" => {
1380 self.parse_simd_int_arith_4s(mnemonic)
1381 }
1382 "addv.16b" | "umaxv.16b" | "smaxv.16b" | "uminv.16b" | "sminv.16b" => {
1383 self.parse_simd_reduce_16b(mnemonic)
1384 }
1385 "addv.8h" | "umaxv.8h" | "smaxv.8h" | "uminv.8h" | "sminv.8h" => {
1386 self.parse_simd_reduce_8h(mnemonic)
1387 }
1388 "addv.4s" | "faddp.2s" | "fmaxv.4s" | "fminv.4s" | "fmaxnmv.4s" | "fminnmv.4s"
1389 | "smaxv.4s" | "umaxv.4s" | "sminv.4s" | "uminv.4s" => {
1390 self.parse_simd_reduce_4s(mnemonic)
1391 }
1392 "cmeq.4s" | "cmhs.4s" | "cmhi.4s" | "cmge.4s" | "cmgt.4s" | "fcmeq.4s" | "fcmge.4s"
1393 | "fcmgt.4s" => self.parse_simd_compare_4s(mnemonic),
1394 "fcmeq.2d" | "fcmge.2d" | "fcmgt.2d" | "fcmle.2d" | "fcmlt.2d" => {
1395 self.parse_simd_compare_2d(mnemonic)
1396 }
1397 "fadd.4s" | "faddp.4s" | "fmaxp.4s" | "fminp.4s" | "fmaxnmp.4s" | "fminnmp.4s"
1398 | "fmla.4s" | "fmls.4s" | "fsub.4s" | "fmul.4s" | "fdiv.4s" | "fmax.4s" | "fmin.4s"
1399 | "fmaxnm.4s" | "fminnm.4s" | "frecps.4s" | "frsqrts.4s" => {
1400 self.parse_simd_fp_arith_4s(mnemonic)
1401 }
1402 "fadd.2d" | "fsub.2d" | "fmul.2d" | "fdiv.2d" | "fabd.2d" | "fmla.2d" | "fmls.2d"
1403 | "fmax.2d" | "fmin.2d" | "fmaxnm.2d" | "fminnm.2d" | "frecps.2d" | "frsqrts.2d" => {
1404 self.parse_simd_fp_arith_2d(mnemonic)
1405 }
1406 "faddp.2d" | "fmaxp.2d" | "fminp.2d" | "fmaxnmp.2d" | "fminnmp.2d" => {
1407 self.parse_fpairwise_or_reduce_2d(mnemonic)
1408 }
1409 "fabs.4s" | "fneg.4s" | "fsqrt.4s" | "scvtf.4s" | "ucvtf.4s" | "fcvtzs.4s"
1410 | "fcvtzu.4s" | "frecpe.4s" | "frsqrte.4s" | "frintn.4s" | "frintm.4s"
1411 | "frintp.4s" | "frintz.4s" | "frinta.4s" | "frinti.4s" => {
1412 self.parse_simd_fp_unary_4s(mnemonic)
1413 }
1414 "fabs.2d" | "fneg.2d" | "fsqrt.2d" | "scvtf.2d" | "ucvtf.2d" | "fcvtzs.2d"
1415 | "fcvtzu.2d" | "frecpe.2d" | "frsqrte.2d" | "frintn.2d" | "frintm.2d"
1416 | "frintp.2d" | "frintz.2d" | "frinta.2d" | "frinti.2d" => {
1417 self.parse_simd_fp_unary_2d(mnemonic)
1418 }
1419 "fsub" => self.parse_fp_arith("fsub"),
1420 "fmul" => self.parse_fp_arith("fmul"),
1421 "fdiv" => self.parse_fp_arith("fdiv"),
1422 "fneg" => self.parse_fp_unary("fneg"),
1423 "fabs" => self.parse_fp_unary("fabs"),
1424 "fsqrt" => self.parse_fp_unary("fsqrt"),
1425 "fcmp" => self.parse_fcmp(),
1426 "fcsel" => self.parse_fcsel(),
1427 "fmadd" => self.parse_fmadd(),
1428
1429 // FP conversion
1430 "fcvtzs" => self.parse_fcvtzs(),
1431 "scvtf" => self.parse_scvtf(),
1432 "fmov" => self.parse_fmov(),
1433
1434 // System
1435 "svc" => {
1436 let imm = self.parse_immediate_const_expr("svc immediate")? as u16;
1437 Ok(Inst::Svc { imm16: imm })
1438 }
1439 "nop" => Ok(Inst::Nop),
1440 "yield" => Ok(Inst::Yield),
1441 "wfe" => Ok(Inst::Wfe),
1442 "wfi" => Ok(Inst::Wfi),
1443 "sev" => Ok(Inst::Sev),
1444 "sevl" => Ok(Inst::Sevl),
1445 "dmb" => Ok(Inst::Dmb {
1446 option: self.parse_barrier_option("dmb option")?,
1447 }),
1448 "dsb" => Ok(Inst::Dsb {
1449 option: self.parse_barrier_option("dsb option")?,
1450 }),
1451 "isb" => {
1452 let option = if self.at_end_of_stmt() {
1453 BarrierOpt::Sy
1454 } else {
1455 self.parse_barrier_option("isb option")?
1456 };
1457 Ok(Inst::Isb { option })
1458 }
1459 "brk" => {
1460 let imm = self.parse_immediate_const_expr("brk immediate")? as u16;
1461 Ok(Inst::Brk { imm16: imm })
1462 }
1463
1464 _ => Err(self.err(format!("unknown mnemonic: {}", mnemonic))),
1465 }?;
1466 Ok(Stmt::Instruction(inst))
1467 }
1468
1469 // ---- Register parsing helpers ----
1470
1471 fn parse_gp_reg(&mut self) -> Result<GpReg, ParseError> {
1472 let name = self.expect_ident()?;
1473 parse_gp_reg_name(&name)
1474 .ok_or_else(|| self.err(format!("expected GP register, got '{}'", name)))
1475 }
1476
1477 /// Returns (register, is_64bit).
1478 fn parse_gp_reg_with_size(&mut self) -> Result<(GpReg, bool), ParseError> {
1479 let (reg, is_64bit, _kind) = self.parse_gp_reg_with_size_kind()?;
1480 Ok((reg, is_64bit))
1481 }
1482
1483 fn parse_gp_reg_with_size_kind(&mut self) -> Result<(GpReg, bool, GpRegKind), ParseError> {
1484 let name = self.expect_ident()?;
1485 let lower = name.to_lowercase();
1486 self.gp_reg_with_size_kind_from_name(&name, &lower)
1487 }
1488
1489 fn gp_reg_with_size_kind_from_name(
1490 &self,
1491 name: &str,
1492 lower: &str,
1493 ) -> Result<(GpReg, bool, GpRegKind), ParseError> {
1494 if lower == "sp" || lower == "xzr" {
1495 let kind = if lower == "sp" {
1496 GpRegKind::Sp
1497 } else {
1498 GpRegKind::Zr
1499 };
1500 return Ok((parse_gp_reg_name(lower).unwrap(), true, kind));
1501 }
1502 if lower == "wzr" {
1503 return Ok((WZR, false, GpRegKind::Zr));
1504 }
1505 if lower.starts_with('x') {
1506 let reg = parse_gp_reg_name(lower)
1507 .ok_or_else(|| self.err(format!("bad register '{}'", name)))?;
1508 Ok((reg, true, GpRegKind::Reg))
1509 } else if lower.starts_with('w') {
1510 let reg = parse_gp_reg_name(lower)
1511 .ok_or_else(|| self.err(format!("bad register '{}'", name)))?;
1512 Ok((reg, false, GpRegKind::Reg))
1513 } else {
1514 Err(self.err(format!("expected GP register, got '{}'", name)))
1515 }
1516 }
1517
1518 fn parse_lane_gp_reg(
1519 &mut self,
1520 width: SimdLaneWidth,
1521 context: &str,
1522 ) -> Result<GpReg, ParseError> {
1523 let (reg, is_64bit, kind) = self.parse_gp_reg_with_size_kind()?;
1524 if kind == GpRegKind::Sp {
1525 return Err(self.err(format!("{} does not allow SP", context)));
1526 }
1527 let needs_64bit = matches!(width, SimdLaneWidth::D64);
1528 if needs_64bit != is_64bit {
1529 let width_name = if needs_64bit {
1530 "x-register"
1531 } else {
1532 "w-register"
1533 };
1534 return Err(self.err(format!("{} requires a {}", context, width_name)));
1535 }
1536 Ok(reg)
1537 }
1538
1539 fn parse_fp_reg_with_size(&mut self) -> Result<(FpReg, bool), ParseError> {
1540 let name = self.expect_ident()?;
1541 let lower = name.to_lowercase();
1542 if lower.starts_with('d') {
1543 let reg = parse_fp_reg_name(&lower)
1544 .ok_or_else(|| self.err(format!("bad FP register '{}'", name)))?;
1545 Ok((reg, true)) // double
1546 } else if lower.starts_with('s') {
1547 let reg = parse_fp_reg_name(&lower)
1548 .ok_or_else(|| self.err(format!("bad FP register '{}'", name)))?;
1549 Ok((reg, false)) // single
1550 } else {
1551 Err(self.err(format!("expected FP register, got '{}'", name)))
1552 }
1553 }
1554
1555 fn parse_fp_mem_reg_with_width(&mut self) -> Result<(FpReg, FpMemWidth), ParseError> {
1556 let name = self.expect_ident()?;
1557 let lower = name.to_lowercase();
1558 let width = if lower.starts_with('b') {
1559 FpMemWidth::B8
1560 } else if lower.starts_with('h') {
1561 FpMemWidth::H16
1562 } else if lower.starts_with('d') {
1563 FpMemWidth::D64
1564 } else if lower.starts_with('s') {
1565 FpMemWidth::S32
1566 } else if lower.starts_with('q') {
1567 FpMemWidth::Q128
1568 } else {
1569 return Err(self.err(format!("expected FP/SIMD register, got '{}'", name)));
1570 };
1571 let num: u8 = lower[1..]
1572 .parse()
1573 .map_err(|_| self.err(format!("bad FP/SIMD register '{}'", name)))?;
1574 if num > 31 {
1575 return Err(self.err(format!("bad FP/SIMD register '{}'", name)));
1576 }
1577 let reg = FpReg::new(num);
1578 Ok((reg, width))
1579 }
1580
1581 fn parse_atomic_data_reg(&mut self, context: &str) -> Result<(GpReg, bool), ParseError> {
1582 let (reg, sf, kind) = self.parse_gp_reg_with_size_kind()?;
1583 if kind == GpRegKind::Sp {
1584 return Err(self.err(format!("{} does not allow SP as a data register", context)));
1585 }
1586 Ok((reg, sf))
1587 }
1588
1589 fn parse_atomic_wreg(&mut self, context: &str) -> Result<GpReg, ParseError> {
1590 let (reg, sf) = self.parse_atomic_data_reg(context)?;
1591 if sf {
1592 return Err(self.err(format!("{} requires a w-register", context)));
1593 }
1594 Ok(reg)
1595 }
1596
1597 fn parse_atomic_base_reg(&mut self, context: &str) -> Result<GpReg, ParseError> {
1598 self.expect(&Tok::LBracket)?;
1599 let (rn, sf, kind) = self.parse_gp_reg_with_size_kind()?;
1600 if !sf || kind == GpRegKind::Zr {
1601 return Err(self.err(format!("{} expects an [Xn] or [sp] base register", context)));
1602 }
1603 if !self.eat(&Tok::RBracket) {
1604 return Err(self.err(format!("{} expects a simple [Xn] memory operand", context)));
1605 }
1606 if self.eat(&Tok::Bang) {
1607 return Err(self.err(format!("{} does not support pre-index addressing", context)));
1608 }
1609 if self.eat(&Tok::Comma) {
1610 return Err(self.err(format!(
1611 "{} does not support post-index or offset addressing",
1612 context
1613 )));
1614 }
1615 Ok(rn)
1616 }
1617
1618 // ---- Instruction-specific parsers ----
1619
1620 fn parse_ldapr(&mut self) -> Result<Stmt, ParseError> {
1621 let (rt, sf) = self.parse_atomic_data_reg("ldapr")?;
1622 self.expect(&Tok::Comma)?;
1623 let rn = self.parse_atomic_base_reg("ldapr")?;
1624 Ok(Stmt::Instruction(if sf {
1625 Inst::Ldapr64 { rt, rn }
1626 } else {
1627 Inst::Ldapr32 { rt, rn }
1628 }))
1629 }
1630
1631 fn parse_ldapr_narrow(&mut self, mnemonic: &str) -> Result<Stmt, ParseError> {
1632 let rt = self.parse_atomic_wreg(mnemonic)?;
1633 self.expect(&Tok::Comma)?;
1634 let rn = self.parse_atomic_base_reg(mnemonic)?;
1635 Ok(Stmt::Instruction(match mnemonic {
1636 "ldaprb" => Inst::Ldaprb { rt, rn },
1637 "ldaprh" => Inst::Ldaprh { rt, rn },
1638 _ => unreachable!(),
1639 }))
1640 }
1641
1642 fn parse_stlr(&mut self) -> Result<Stmt, ParseError> {
1643 let (rt, sf) = self.parse_atomic_data_reg("stlr")?;
1644 self.expect(&Tok::Comma)?;
1645 let rn = self.parse_atomic_base_reg("stlr")?;
1646 Ok(Stmt::Instruction(if sf {
1647 Inst::Stlr64 { rt, rn }
1648 } else {
1649 Inst::Stlr32 { rt, rn }
1650 }))
1651 }
1652
1653 fn parse_stlr_narrow(&mut self, mnemonic: &str) -> Result<Stmt, ParseError> {
1654 let rt = self.parse_atomic_wreg(mnemonic)?;
1655 self.expect(&Tok::Comma)?;
1656 let rn = self.parse_atomic_base_reg(mnemonic)?;
1657 Ok(Stmt::Instruction(match mnemonic {
1658 "stlrb" => Inst::Stlrb { rt, rn },
1659 "stlrh" => Inst::Stlrh { rt, rn },
1660 _ => unreachable!(),
1661 }))
1662 }
1663
1664 fn parse_atomic_rmw(&mut self, mnemonic: &str) -> Result<Stmt, ParseError> {
1665 let (rs, sf) = self.parse_atomic_data_reg(mnemonic)?;
1666 self.expect(&Tok::Comma)?;
1667 let (rt, rt_sf) = self.parse_atomic_data_reg(mnemonic)?;
1668 if sf != rt_sf {
1669 return Err(self.err(format!(
1670 "{} requires source and destination registers of the same width",
1671 mnemonic
1672 )));
1673 }
1674 self.expect(&Tok::Comma)?;
1675 let rn = self.parse_atomic_base_reg(mnemonic)?;
1676 Ok(Stmt::Instruction(match (mnemonic, sf) {
1677 ("ldaddal", true) => Inst::Ldaddal64 { rs, rt, rn },
1678 ("ldaddal", false) => Inst::Ldaddal32 { rs, rt, rn },
1679 ("ldumaxal", true) => Inst::Ldumaxal64 { rs, rt, rn },
1680 ("ldumaxal", false) => Inst::Ldumaxal32 { rs, rt, rn },
1681 ("ldsmaxal", true) => Inst::Ldsmaxal64 { rs, rt, rn },
1682 ("ldsmaxal", false) => Inst::Ldsmaxal32 { rs, rt, rn },
1683 ("lduminal", true) => Inst::Lduminal64 { rs, rt, rn },
1684 ("lduminal", false) => Inst::Lduminal32 { rs, rt, rn },
1685 ("ldsminal", true) => Inst::Ldsminal64 { rs, rt, rn },
1686 ("ldsminal", false) => Inst::Ldsminal32 { rs, rt, rn },
1687 ("ldclral", true) => Inst::Ldclral64 { rs, rt, rn },
1688 ("ldclral", false) => Inst::Ldclral32 { rs, rt, rn },
1689 ("ldeoral", true) => Inst::Ldeoral64 { rs, rt, rn },
1690 ("ldeoral", false) => Inst::Ldeoral32 { rs, rt, rn },
1691 ("ldsetal", true) => Inst::Ldsetal64 { rs, rt, rn },
1692 ("ldsetal", false) => Inst::Ldsetal32 { rs, rt, rn },
1693 ("swpal", true) => Inst::Swpal64 { rs, rt, rn },
1694 ("swpal", false) => Inst::Swpal32 { rs, rt, rn },
1695 ("casal", true) => Inst::Casal64 { rs, rt, rn },
1696 ("casal", false) => Inst::Casal32 { rs, rt, rn },
1697 _ => unreachable!(),
1698 }))
1699 }
1700
1701 fn parse_atomic_rmw_narrow(&mut self, mnemonic: &str) -> Result<Stmt, ParseError> {
1702 let rs = self.parse_atomic_wreg(mnemonic)?;
1703 self.expect(&Tok::Comma)?;
1704 let rt = self.parse_atomic_wreg(mnemonic)?;
1705 self.expect(&Tok::Comma)?;
1706 let rn = self.parse_atomic_base_reg(mnemonic)?;
1707 Ok(Stmt::Instruction(match mnemonic {
1708 "ldaddalb" => Inst::Ldaddalb { rs, rt, rn },
1709 "ldaddalh" => Inst::Ldaddalh { rs, rt, rn },
1710 "ldumaxalb" => Inst::Ldumaxalb { rs, rt, rn },
1711 "ldumaxalh" => Inst::Ldumaxalh { rs, rt, rn },
1712 "ldsmaxalb" => Inst::Ldsmaxalb { rs, rt, rn },
1713 "ldsmaxalh" => Inst::Ldsmaxalh { rs, rt, rn },
1714 "lduminalb" => Inst::Lduminalb { rs, rt, rn },
1715 "lduminalh" => Inst::Lduminalh { rs, rt, rn },
1716 "ldsminalb" => Inst::Ldsminalb { rs, rt, rn },
1717 "ldsminalh" => Inst::Ldsminalh { rs, rt, rn },
1718 "ldclralb" => Inst::Ldclralb { rs, rt, rn },
1719 "ldclralh" => Inst::Ldclralh { rs, rt, rn },
1720 "ldeoralb" => Inst::Ldeoralb { rs, rt, rn },
1721 "ldeoralh" => Inst::Ldeoralh { rs, rt, rn },
1722 "ldsetalb" => Inst::Ldsetalb { rs, rt, rn },
1723 "ldsetalh" => Inst::Ldsetalh { rs, rt, rn },
1724 "swpalb" => Inst::Swpalb { rs, rt, rn },
1725 "swpalh" => Inst::Swpalh { rs, rt, rn },
1726 "casalb" => Inst::Casalb { rs, rt, rn },
1727 "casalh" => Inst::Casalh { rs, rt, rn },
1728 _ => unreachable!(),
1729 }))
1730 }
1731
1732 fn parse_ldaddal(&mut self) -> Result<Stmt, ParseError> {
1733 self.parse_atomic_rmw("ldaddal")
1734 }
1735
1736 fn parse_ldsmaxal(&mut self) -> Result<Stmt, ParseError> {
1737 self.parse_atomic_rmw("ldsmaxal")
1738 }
1739
1740 fn parse_ldsminal(&mut self) -> Result<Stmt, ParseError> {
1741 self.parse_atomic_rmw("ldsminal")
1742 }
1743
1744 fn parse_ldclral(&mut self) -> Result<Stmt, ParseError> {
1745 self.parse_atomic_rmw("ldclral")
1746 }
1747
1748 fn parse_ldeoral(&mut self) -> Result<Stmt, ParseError> {
1749 self.parse_atomic_rmw("ldeoral")
1750 }
1751
1752 fn parse_ldsetal(&mut self) -> Result<Stmt, ParseError> {
1753 self.parse_atomic_rmw("ldsetal")
1754 }
1755
1756 fn parse_swpal(&mut self) -> Result<Stmt, ParseError> {
1757 self.parse_atomic_rmw("swpal")
1758 }
1759
1760 fn parse_casal(&mut self) -> Result<Stmt, ParseError> {
1761 self.parse_atomic_rmw("casal")
1762 }
1763
1764 fn parse_add_sub(&mut self, is_sub: bool, sets_flags: bool) -> Result<Inst, ParseError> {
1765 let (rd, sf) = self.parse_gp_reg_with_size()?;
1766 self.expect(&Tok::Comma)?;
1767 let (rn, _, rn_kind) = self.parse_gp_reg_with_size_kind()?;
1768 self.expect(&Tok::Comma)?;
1769
1770 if self.starts_immediate_expr() {
1771 let imm = self.parse_immediate_const_expr("add/sub immediate")? as u16;
1772 let shift = self.parse_optional_lsl12()?;
1773 Ok(match (is_sub, sets_flags) {
1774 (false, false) => Inst::AddImm {
1775 rd,
1776 rn,
1777 imm12: imm,
1778 shift,
1779 sf,
1780 },
1781 (true, false) => Inst::SubImm {
1782 rd,
1783 rn,
1784 imm12: imm,
1785 shift,
1786 sf,
1787 },
1788 (false, true) => Inst::AddsImm {
1789 rd,
1790 rn,
1791 imm12: imm,
1792 shift,
1793 sf,
1794 },
1795 (true, true) => Inst::SubsImm {
1796 rd,
1797 rn,
1798 imm12: imm,
1799 shift,
1800 sf,
1801 },
1802 })
1803 } else {
1804 let (rm, rm_is_64bit) = self.parse_gp_reg_with_size()?;
1805 let modifier = self.parse_optional_add_sub_modifier(sf, rm_is_64bit)?;
1806 self.validate_add_sub_extended_base_reg(rn_kind, modifier)?;
1807 Ok(match (is_sub, sets_flags, modifier) {
1808 (false, false, None) => Inst::AddReg { rd, rn, rm, sf },
1809 (true, false, None) => Inst::SubReg { rd, rn, rm, sf },
1810 (false, true, None) => Inst::AddsReg { rd, rn, rm, sf },
1811 (true, true, None) => Inst::SubsReg { rd, rn, rm, sf },
1812 (false, false, Some(AddSubModifier::Shift(shift, amount))) => Inst::AddShiftReg {
1813 rd,
1814 rn,
1815 rm,
1816 shift,
1817 amount,
1818 sf,
1819 },
1820 (true, false, Some(AddSubModifier::Shift(shift, amount))) => Inst::SubShiftReg {
1821 rd,
1822 rn,
1823 rm,
1824 shift,
1825 amount,
1826 sf,
1827 },
1828 (false, true, Some(AddSubModifier::Shift(shift, amount))) => Inst::AddsShiftReg {
1829 rd,
1830 rn,
1831 rm,
1832 shift,
1833 amount,
1834 sf,
1835 },
1836 (true, true, Some(AddSubModifier::Shift(shift, amount))) => Inst::SubsShiftReg {
1837 rd,
1838 rn,
1839 rm,
1840 shift,
1841 amount,
1842 sf,
1843 },
1844 (false, false, Some(AddSubModifier::Extend(extend, amount))) => Inst::AddExtReg {
1845 rd,
1846 rn,
1847 rm,
1848 extend,
1849 amount,
1850 sf,
1851 },
1852 (true, false, Some(AddSubModifier::Extend(extend, amount))) => Inst::SubExtReg {
1853 rd,
1854 rn,
1855 rm,
1856 extend,
1857 amount,
1858 sf,
1859 },
1860 (false, true, Some(AddSubModifier::Extend(extend, amount))) => Inst::AddsExtReg {
1861 rd,
1862 rn,
1863 rm,
1864 extend,
1865 amount,
1866 sf,
1867 },
1868 (true, true, Some(AddSubModifier::Extend(extend, amount))) => Inst::SubsExtReg {
1869 rd,
1870 rn,
1871 rm,
1872 extend,
1873 amount,
1874 sf,
1875 },
1876 })
1877 }
1878 }
1879
1880 /// ADD that can also handle label@PAGEOFF references (for ADRP+ADD pairs).
1881 fn parse_add_sub_stmt(&mut self, is_sub: bool, sets_flags: bool) -> Result<Stmt, ParseError> {
1882 let (rd, sf) = self.parse_gp_reg_with_size()?;
1883 self.expect(&Tok::Comma)?;
1884 let (rn, _, rn_kind) = self.parse_gp_reg_with_size_kind()?;
1885 self.expect(&Tok::Comma)?;
1886
1887 // Check for label@PAGEOFF (identifier or numeric local reference followed by @).
1888 if self.starts_non_register_symbol_reference() {
1889 let label = self.parse_label_reference()?;
1890 let kind = self.parse_symbol_reloc_modifier(
1891 Some(RelocKind::PageOff12),
1892 &[("PAGEOFF", RelocKind::PageOff12)],
1893 "add/sub symbol operand",
1894 )?;
1895 let addend = self.parse_optional_symbol_addend()?;
1896 let inst = Inst::AddImm {
1897 rd,
1898 rn,
1899 imm12: 0,
1900 shift: false,
1901 sf,
1902 };
1903 return Ok(Stmt::InstructionWithReloc(
1904 inst,
1905 LabelRef {
1906 symbol: label,
1907 kind,
1908 addend,
1909 },
1910 ));
1911 }
1912
1913 // Normal add/sub (immediate or register).
1914 let inst = self.parse_add_sub_operand(rd, rn, rn_kind, sf, is_sub, sets_flags)?;
1915 Ok(Stmt::Instruction(inst))
1916 }
1917
1918 /// Parse the third operand of add/sub (immediate or register).
1919 fn parse_add_sub_operand(
1920 &mut self,
1921 rd: GpReg,
1922 rn: GpReg,
1923 rn_kind: GpRegKind,
1924 sf: bool,
1925 is_sub: bool,
1926 sets_flags: bool,
1927 ) -> Result<Inst, ParseError> {
1928 if self.starts_immediate_expr() {
1929 let imm = self.parse_immediate_const_expr("add/sub immediate")? as u16;
1930 let shift = self.parse_optional_lsl12()?;
1931 Ok(match (is_sub, sets_flags) {
1932 (false, false) => Inst::AddImm {
1933 rd,
1934 rn,
1935 imm12: imm,
1936 shift,
1937 sf,
1938 },
1939 (true, false) => Inst::SubImm {
1940 rd,
1941 rn,
1942 imm12: imm,
1943 shift,
1944 sf,
1945 },
1946 (false, true) => Inst::AddsImm {
1947 rd,
1948 rn,
1949 imm12: imm,
1950 shift,
1951 sf,
1952 },
1953 (true, true) => Inst::SubsImm {
1954 rd,
1955 rn,
1956 imm12: imm,
1957 shift,
1958 sf,
1959 },
1960 })
1961 } else {
1962 let (rm, rm_is_64bit) = self.parse_gp_reg_with_size()?;
1963 let modifier = self.parse_optional_add_sub_modifier(sf, rm_is_64bit)?;
1964 self.validate_add_sub_extended_base_reg(rn_kind, modifier)?;
1965 Ok(match (is_sub, sets_flags, modifier) {
1966 (false, false, None) => Inst::AddReg { rd, rn, rm, sf },
1967 (true, false, None) => Inst::SubReg { rd, rn, rm, sf },
1968 (false, true, None) => Inst::AddsReg { rd, rn, rm, sf },
1969 (true, true, None) => Inst::SubsReg { rd, rn, rm, sf },
1970 (false, false, Some(AddSubModifier::Shift(shift, amount))) => Inst::AddShiftReg {
1971 rd,
1972 rn,
1973 rm,
1974 shift,
1975 amount,
1976 sf,
1977 },
1978 (true, false, Some(AddSubModifier::Shift(shift, amount))) => Inst::SubShiftReg {
1979 rd,
1980 rn,
1981 rm,
1982 shift,
1983 amount,
1984 sf,
1985 },
1986 (false, true, Some(AddSubModifier::Shift(shift, amount))) => Inst::AddsShiftReg {
1987 rd,
1988 rn,
1989 rm,
1990 shift,
1991 amount,
1992 sf,
1993 },
1994 (true, true, Some(AddSubModifier::Shift(shift, amount))) => Inst::SubsShiftReg {
1995 rd,
1996 rn,
1997 rm,
1998 shift,
1999 amount,
2000 sf,
2001 },
2002 (false, false, Some(AddSubModifier::Extend(extend, amount))) => Inst::AddExtReg {
2003 rd,
2004 rn,
2005 rm,
2006 extend,
2007 amount,
2008 sf,
2009 },
2010 (true, false, Some(AddSubModifier::Extend(extend, amount))) => Inst::SubExtReg {
2011 rd,
2012 rn,
2013 rm,
2014 extend,
2015 amount,
2016 sf,
2017 },
2018 (false, true, Some(AddSubModifier::Extend(extend, amount))) => Inst::AddsExtReg {
2019 rd,
2020 rn,
2021 rm,
2022 extend,
2023 amount,
2024 sf,
2025 },
2026 (true, true, Some(AddSubModifier::Extend(extend, amount))) => Inst::SubsExtReg {
2027 rd,
2028 rn,
2029 rm,
2030 extend,
2031 amount,
2032 sf,
2033 },
2034 })
2035 }
2036 }
2037
2038 fn parse_cmp(&mut self) -> Result<Inst, ParseError> {
2039 let (rn, sf, rn_kind) = self.parse_gp_reg_with_size_kind()?;
2040 self.expect(&Tok::Comma)?;
2041 if self.starts_immediate_expr() {
2042 let imm = self.parse_immediate_const_expr("cmp immediate")? as u16;
2043 let shift = self.parse_optional_lsl12()?;
2044 Ok(Inst::SubsImm {
2045 rd: XZR,
2046 rn,
2047 imm12: imm,
2048 shift,
2049 sf,
2050 })
2051 } else {
2052 let (rm, rm_is_64bit) = self.parse_gp_reg_with_size()?;
2053 let modifier = self.parse_optional_add_sub_modifier(sf, rm_is_64bit)?;
2054 self.validate_add_sub_extended_base_reg(rn_kind, modifier)?;
2055 if let Some(modifier) = modifier {
2056 Ok(match modifier {
2057 AddSubModifier::Shift(shift, amount) => Inst::SubsShiftReg {
2058 rd: XZR,
2059 rn,
2060 rm,
2061 shift,
2062 amount,
2063 sf,
2064 },
2065 AddSubModifier::Extend(extend, amount) => Inst::SubsExtReg {
2066 rd: XZR,
2067 rn,
2068 rm,
2069 extend,
2070 amount,
2071 sf,
2072 },
2073 })
2074 } else {
2075 Ok(Inst::SubsReg {
2076 rd: XZR,
2077 rn,
2078 rm,
2079 sf,
2080 })
2081 }
2082 }
2083 }
2084
2085 fn parse_cmn(&mut self) -> Result<Inst, ParseError> {
2086 let (rn, sf, rn_kind) = self.parse_gp_reg_with_size_kind()?;
2087 self.expect(&Tok::Comma)?;
2088 let (rm, rm_is_64bit) = self.parse_gp_reg_with_size()?;
2089 let modifier = self.parse_optional_add_sub_modifier(sf, rm_is_64bit)?;
2090 self.validate_add_sub_extended_base_reg(rn_kind, modifier)?;
2091 if let Some(modifier) = modifier {
2092 Ok(match modifier {
2093 AddSubModifier::Shift(shift, amount) => Inst::AddsShiftReg {
2094 rd: XZR,
2095 rn,
2096 rm,
2097 shift,
2098 amount,
2099 sf,
2100 },
2101 AddSubModifier::Extend(extend, amount) => Inst::AddsExtReg {
2102 rd: XZR,
2103 rn,
2104 rm,
2105 extend,
2106 amount,
2107 sf,
2108 },
2109 })
2110 } else {
2111 Ok(Inst::AddsReg {
2112 rd: XZR,
2113 rn,
2114 rm,
2115 sf,
2116 })
2117 }
2118 }
2119
2120 fn parse_tst(&mut self) -> Result<Inst, ParseError> {
2121 let (rn, sf) = self.parse_gp_reg_with_size()?;
2122 self.expect(&Tok::Comma)?;
2123 if self.starts_immediate_expr() {
2124 let imm = self.parse_logical_immediate_value(sf)?;
2125 Ok(Inst::AndsImm {
2126 rd: XZR,
2127 rn,
2128 imm,
2129 sf,
2130 })
2131 } else {
2132 let (rm, _) = self.parse_gp_reg_with_size()?;
2133 Ok(Inst::AndsReg {
2134 rd: XZR,
2135 rn,
2136 rm,
2137 sf,
2138 })
2139 }
2140 }
2141
2142 fn parse_neg(&mut self) -> Result<Inst, ParseError> {
2143 let (rd, sf) = self.parse_gp_reg_with_size()?;
2144 self.expect(&Tok::Comma)?;
2145 let (rm, rm_is_64bit) = self.parse_gp_reg_with_size()?;
2146 let modifier = self.parse_optional_add_sub_modifier(sf, rm_is_64bit)?;
2147 self.validate_add_sub_extended_base_reg(GpRegKind::Zr, modifier)?;
2148 if let Some(modifier) = modifier {
2149 Ok(match modifier {
2150 AddSubModifier::Shift(shift, amount) => Inst::SubShiftReg {
2151 rd,
2152 rn: XZR,
2153 rm,
2154 shift,
2155 amount,
2156 sf,
2157 },
2158 AddSubModifier::Extend(extend, amount) => Inst::SubExtReg {
2159 rd,
2160 rn: XZR,
2161 rm,
2162 extend,
2163 amount,
2164 sf,
2165 },
2166 })
2167 } else {
2168 Ok(Inst::SubReg {
2169 rd,
2170 rn: XZR,
2171 rm,
2172 sf,
2173 })
2174 }
2175 }
2176
2177 fn parse_mvn(&mut self) -> Result<Inst, ParseError> {
2178 let (rd, sf) = self.parse_gp_reg_with_size()?;
2179 self.expect(&Tok::Comma)?;
2180 let (rm, _) = self.parse_gp_reg_with_size()?;
2181 Ok(Inst::OrnReg {
2182 rd,
2183 rn: XZR,
2184 rm,
2185 sf,
2186 })
2187 }
2188
2189 fn parse_cond_select(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
2190 let (rd, sf) = self.parse_gp_reg_with_size()?;
2191 self.expect(&Tok::Comma)?;
2192 let (rn, _) = self.parse_gp_reg_with_size()?;
2193 self.expect(&Tok::Comma)?;
2194 let (rm, _) = self.parse_gp_reg_with_size()?;
2195 self.expect(&Tok::Comma)?;
2196 let cond_name = self.expect_ident()?;
2197 let cond = parse_condition(&cond_name)
2198 .ok_or_else(|| self.err(format!("unknown condition: {}", cond_name)))?;
2199 Ok(match mnemonic {
2200 "csel" => Inst::Csel {
2201 rd,
2202 rn,
2203 rm,
2204 cond,
2205 sf,
2206 },
2207 "csinc" => Inst::Csinc {
2208 rd,
2209 rn,
2210 rm,
2211 cond,
2212 sf,
2213 },
2214 "csinv" => Inst::Csinv {
2215 rd,
2216 rn,
2217 rm,
2218 cond,
2219 sf,
2220 },
2221 "csneg" => Inst::Csneg {
2222 rd,
2223 rn,
2224 rm,
2225 cond,
2226 sf,
2227 },
2228 _ => unreachable!("unsupported conditional select mnemonic"),
2229 })
2230 }
2231
2232 fn parse_cond_select_set_alias(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
2233 let (rd, sf) = self.parse_gp_reg_with_size()?;
2234 self.expect(&Tok::Comma)?;
2235 let cond_name = self.expect_ident()?;
2236 let cond = parse_condition(&cond_name)
2237 .ok_or_else(|| self.err(format!("unknown condition: {}", cond_name)))?;
2238 let cond = invert_condition(cond);
2239 Ok(match mnemonic {
2240 "cset" => Inst::Csinc {
2241 rd,
2242 rn: XZR,
2243 rm: XZR,
2244 cond,
2245 sf,
2246 },
2247 "csetm" => Inst::Csinv {
2248 rd,
2249 rn: XZR,
2250 rm: XZR,
2251 cond,
2252 sf,
2253 },
2254 _ => unreachable!("unsupported conditional set alias"),
2255 })
2256 }
2257
2258 fn parse_cond_select_unary_alias(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
2259 let (rd, sf) = self.parse_gp_reg_with_size()?;
2260 self.expect(&Tok::Comma)?;
2261 let (rn, _) = self.parse_gp_reg_with_size()?;
2262 self.expect(&Tok::Comma)?;
2263 let cond_name = self.expect_ident()?;
2264 let cond = parse_condition(&cond_name)
2265 .ok_or_else(|| self.err(format!("unknown condition: {}", cond_name)))?;
2266 let cond = invert_condition(cond);
2267 Ok(match mnemonic {
2268 "cinc" => Inst::Csinc {
2269 rd,
2270 rn,
2271 rm: rn,
2272 cond,
2273 sf,
2274 },
2275 "cinv" => Inst::Csinv {
2276 rd,
2277 rn,
2278 rm: rn,
2279 cond,
2280 sf,
2281 },
2282 "cneg" => Inst::Csneg {
2283 rd,
2284 rn,
2285 rm: rn,
2286 cond,
2287 sf,
2288 },
2289 _ => unreachable!("unsupported conditional unary alias"),
2290 })
2291 }
2292
2293 fn parse_csel(&mut self) -> Result<Inst, ParseError> {
2294 self.parse_cond_select("csel")
2295 }
2296
2297 fn parse_csinc(&mut self) -> Result<Inst, ParseError> {
2298 self.parse_cond_select("csinc")
2299 }
2300
2301 fn parse_csinv(&mut self) -> Result<Inst, ParseError> {
2302 self.parse_cond_select("csinv")
2303 }
2304
2305 fn parse_csneg(&mut self) -> Result<Inst, ParseError> {
2306 self.parse_cond_select("csneg")
2307 }
2308
2309 fn parse_cond_compare_imm(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
2310 let (rn, sf) = self.parse_gp_reg_with_size()?;
2311 self.expect(&Tok::Comma)?;
2312 let imm5 = self.parse_immediate_const_expr("conditional compare immediate")?;
2313 if !(0..=31).contains(&imm5) {
2314 return Err(self.err(format!(
2315 "conditional compare immediate must be in 0..=31, got {}",
2316 imm5
2317 )));
2318 }
2319 self.expect(&Tok::Comma)?;
2320 let nzcv = self.parse_immediate_const_expr("conditional compare nzcv")?;
2321 if !(0..=15).contains(&nzcv) {
2322 return Err(self.err(format!(
2323 "conditional compare nzcv must be in 0..=15, got {}",
2324 nzcv
2325 )));
2326 }
2327 self.expect(&Tok::Comma)?;
2328 let cond_name = self.expect_ident()?;
2329 let cond = parse_condition(&cond_name)
2330 .ok_or_else(|| self.err(format!("unknown condition: {}", cond_name)))?;
2331 Ok(match mnemonic {
2332 "ccmp" => Inst::CcmpImm {
2333 rn,
2334 imm5: imm5 as u8,
2335 nzcv: nzcv as u8,
2336 cond,
2337 sf,
2338 },
2339 "ccmn" => Inst::CcmnImm {
2340 rn,
2341 imm5: imm5 as u8,
2342 nzcv: nzcv as u8,
2343 cond,
2344 sf,
2345 },
2346 _ => unreachable!("unsupported conditional compare mnemonic"),
2347 })
2348 }
2349
2350 fn parse_ccmp(&mut self) -> Result<Inst, ParseError> {
2351 self.parse_cond_compare_imm("ccmp")
2352 }
2353
2354 fn parse_ccmn(&mut self) -> Result<Inst, ParseError> {
2355 self.parse_cond_compare_imm("ccmn")
2356 }
2357
2358 fn parse_cset(&mut self) -> Result<Inst, ParseError> {
2359 self.parse_cond_select_set_alias("cset")
2360 }
2361
2362 fn parse_csetm(&mut self) -> Result<Inst, ParseError> {
2363 self.parse_cond_select_set_alias("csetm")
2364 }
2365
2366 fn parse_cinc(&mut self) -> Result<Inst, ParseError> {
2367 self.parse_cond_select_unary_alias("cinc")
2368 }
2369
2370 fn parse_cinv(&mut self) -> Result<Inst, ParseError> {
2371 self.parse_cond_select_unary_alias("cinv")
2372 }
2373
2374 fn parse_cneg(&mut self) -> Result<Inst, ParseError> {
2375 self.parse_cond_select_unary_alias("cneg")
2376 }
2377
2378 fn parse_3reg(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
2379 let (rd, sf) = self.parse_gp_reg_with_size()?;
2380 self.expect(&Tok::Comma)?;
2381 let (rn, _) = self.parse_gp_reg_with_size()?;
2382 self.expect(&Tok::Comma)?;
2383 let (rm, _) = self.parse_gp_reg_with_size()?;
2384 Ok(match mnemonic {
2385 "mul" => Inst::Mul { rd, rn, rm, sf },
2386 "sdiv" => Inst::Sdiv { rd, rn, rm, sf },
2387 "udiv" => Inst::Udiv { rd, rn, rm, sf },
2388 _ => unreachable!(),
2389 })
2390 }
2391
2392 fn parse_madd_sub(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
2393 let (rd, sf) = self.parse_gp_reg_with_size()?;
2394 self.expect(&Tok::Comma)?;
2395 let (rn, _) = self.parse_gp_reg_with_size()?;
2396 self.expect(&Tok::Comma)?;
2397 let (rm, _) = self.parse_gp_reg_with_size()?;
2398 self.expect(&Tok::Comma)?;
2399 let (ra, _) = self.parse_gp_reg_with_size()?;
2400 Ok(match mnemonic {
2401 "madd" => Inst::Madd { rd, rn, rm, ra, sf },
2402 "msub" => Inst::Msub { rd, rn, rm, ra, sf },
2403 _ => unreachable!(),
2404 })
2405 }
2406
2407 fn parse_madd(&mut self) -> Result<Inst, ParseError> {
2408 self.parse_madd_sub("madd")
2409 }
2410
2411 fn parse_umull(&mut self) -> Result<Inst, ParseError> {
2412 let (rd, rd_is_64bit) = self.parse_gp_reg_with_size()?;
2413 if !rd_is_64bit {
2414 return Err(self.err("umull destination must be an X register".into()));
2415 }
2416 self.expect(&Tok::Comma)?;
2417 let (rn, rn_is_64bit) = self.parse_gp_reg_with_size()?;
2418 if rn_is_64bit {
2419 return Err(self.err("umull sources must be W registers".into()));
2420 }
2421 self.expect(&Tok::Comma)?;
2422 let (rm, rm_is_64bit) = self.parse_gp_reg_with_size()?;
2423 if rm_is_64bit {
2424 return Err(self.err("umull sources must be W registers".into()));
2425 }
2426 Ok(Inst::Umull { rd, rn, rm })
2427 }
2428
2429 fn parse_logic(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
2430 let (rd, sf) = self.parse_gp_reg_with_size()?;
2431 self.expect(&Tok::Comma)?;
2432 let (rn, _) = self.parse_gp_reg_with_size()?;
2433 self.expect(&Tok::Comma)?;
2434 if self.starts_immediate_expr() {
2435 let imm = self.parse_logical_immediate_value(sf)?;
2436 Ok(match mnemonic {
2437 "and" => Inst::AndImm { rd, rn, imm, sf },
2438 "orr" => Inst::OrrImm { rd, rn, imm, sf },
2439 "eor" => Inst::EorImm { rd, rn, imm, sf },
2440 "ands" => Inst::AndsImm { rd, rn, imm, sf },
2441 _ => unreachable!(),
2442 })
2443 } else {
2444 let (rm, _) = self.parse_gp_reg_with_size()?;
2445 Ok(match mnemonic {
2446 "and" => Inst::AndReg { rd, rn, rm, sf },
2447 "orr" => Inst::OrrReg { rd, rn, rm, sf },
2448 "eor" => Inst::EorReg { rd, rn, rm, sf },
2449 "ands" => Inst::AndsReg { rd, rn, rm, sf },
2450 _ => unreachable!(),
2451 })
2452 }
2453 }
2454
2455 fn parse_mov(&mut self) -> Result<Inst, ParseError> {
2456 if self.peek_is_scalar_fp_reg() {
2457 return self.parse_simd_lane_extract();
2458 }
2459 let (rd, sf, rd_kind) = self.parse_gp_reg_with_size_kind()?;
2460 self.expect(&Tok::Comma)?;
2461 if self.starts_immediate_expr() {
2462 let imm = self.parse_immediate_const_expr("mov immediate")?;
2463 if let Some(inst) = mov_alias_imm(rd, imm, sf) {
2464 Ok(inst)
2465 } else {
2466 Err(self.err(format!(
2467 "immediate {} is not encodable as a single MOV alias, use MOVZ/MOVN/MOVK sequence",
2468 imm
2469 )))
2470 }
2471 } else {
2472 let (rm, _, rm_kind) = self.parse_gp_reg_with_size_kind()?;
2473 if rm_kind == GpRegKind::Sp || rd_kind == GpRegKind::Sp {
2474 // MOV involving SP → ADD Xd, Xn, #0 (SP can't be used in ORR shifted reg)
2475 Ok(Inst::AddImm {
2476 rd,
2477 rn: rm,
2478 imm12: 0,
2479 shift: false,
2480 sf,
2481 })
2482 } else {
2483 // MOV Xd, Xm → ORR Xd, XZR, Xm
2484 Ok(Inst::OrrReg {
2485 rd,
2486 rn: XZR,
2487 rm,
2488 sf,
2489 })
2490 }
2491 }
2492 }
2493
2494 fn parse_simd_lane_extract(&mut self) -> Result<Inst, ParseError> {
2495 let (rd, is_double) = self.parse_fp_reg_with_size()?;
2496 self.expect(&Tok::Comma)?;
2497 let (rn, index) = self.parse_simd_lane_ref(if is_double {
2498 SimdLaneWidth::D64
2499 } else {
2500 SimdLaneWidth::S32
2501 })?;
2502 Ok(if is_double {
2503 Inst::MovFromLaneD { rd, rn, index }
2504 } else {
2505 Inst::MovFromLaneS { rd, rn, index }
2506 })
2507 }
2508
2509 fn parse_simd_lane_extract_gp(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
2510 let width = match mnemonic {
2511 "umov.h" => SimdLaneWidth::H16,
2512 "umov.b" => SimdLaneWidth::B8,
2513 _ => unreachable!(),
2514 };
2515 let rd = self.parse_lane_gp_reg(width, mnemonic)?;
2516 self.expect(&Tok::Comma)?;
2517 let (rn, index) = self.parse_simd_lane_ref(width)?;
2518 Ok(match width {
2519 SimdLaneWidth::H16 => Inst::UmovFromLaneH { rd, rn, index },
2520 SimdLaneWidth::B8 => Inst::UmovFromLaneB { rd, rn, index },
2521 SimdLaneWidth::S32 | SimdLaneWidth::D64 => unreachable!(),
2522 })
2523 }
2524
2525 fn parse_simd_lane_extract_gp_signed(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
2526 let width = match mnemonic {
2527 "smov.h" => SimdLaneWidth::H16,
2528 "smov.b" => SimdLaneWidth::B8,
2529 _ => unreachable!(),
2530 };
2531 let rd = self.parse_lane_gp_reg(width, mnemonic)?;
2532 self.expect(&Tok::Comma)?;
2533 let (rn, index) = self.parse_simd_lane_ref(width)?;
2534 Ok(match width {
2535 SimdLaneWidth::H16 => Inst::SmovFromLaneH { rd, rn, index },
2536 SimdLaneWidth::B8 => Inst::SmovFromLaneB { rd, rn, index },
2537 SimdLaneWidth::S32 | SimdLaneWidth::D64 => unreachable!(),
2538 })
2539 }
2540
2541 fn parse_simd_lane_insert(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
2542 let width = match mnemonic {
2543 "mov.s" => SimdLaneWidth::S32,
2544 "mov.d" => SimdLaneWidth::D64,
2545 "mov.h" => SimdLaneWidth::H16,
2546 "mov.b" => SimdLaneWidth::B8,
2547 _ => unreachable!(),
2548 };
2549 if matches!(width, SimdLaneWidth::S32 | SimdLaneWidth::D64) && self.peek_is_gp_reg() {
2550 let rd = self.parse_lane_gp_reg(width, mnemonic)?;
2551 self.expect(&Tok::Comma)?;
2552 let (rn, index) = self.parse_simd_lane_ref(width)?;
2553 return Ok(match width {
2554 SimdLaneWidth::S32 => Inst::MovFromLaneGpS { rd, rn, index },
2555 SimdLaneWidth::D64 => Inst::MovFromLaneGpD { rd, rn, index },
2556 SimdLaneWidth::B8 | SimdLaneWidth::H16 => unreachable!(),
2557 });
2558 }
2559
2560 let (rd, rd_index) = self.parse_simd_lane_ref(width)?;
2561 self.expect(&Tok::Comma)?;
2562 if self.peek_is_simd_reg() {
2563 let (rn, rn_index) = self.parse_simd_lane_ref(width)?;
2564 Ok(match width {
2565 SimdLaneWidth::S32 => Inst::MovLaneS {
2566 rd,
2567 rd_index,
2568 rn,
2569 rn_index,
2570 },
2571 SimdLaneWidth::D64 => Inst::MovLaneD {
2572 rd,
2573 rd_index,
2574 rn,
2575 rn_index,
2576 },
2577 SimdLaneWidth::H16 => Inst::MovLaneH {
2578 rd,
2579 rd_index,
2580 rn,
2581 rn_index,
2582 },
2583 SimdLaneWidth::B8 => Inst::MovLaneB {
2584 rd,
2585 rd_index,
2586 rn,
2587 rn_index,
2588 },
2589 })
2590 } else {
2591 let rn = self.parse_lane_gp_reg(width, mnemonic)?;
2592 Ok(match width {
2593 SimdLaneWidth::S32 => Inst::MovLaneFromGpS { rd, rd_index, rn },
2594 SimdLaneWidth::D64 => Inst::MovLaneFromGpD { rd, rd_index, rn },
2595 SimdLaneWidth::H16 => Inst::MovLaneFromGpH { rd, rd_index, rn },
2596 SimdLaneWidth::B8 => Inst::MovLaneFromGpB { rd, rd_index, rn },
2597 })
2598 }
2599 }
2600
2601 fn parse_simd_lane_load(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
2602 let width = match mnemonic {
2603 "ld1.s" => SimdLaneWidth::S32,
2604 "ld1.d" => SimdLaneWidth::D64,
2605 "ld1.h" => SimdLaneWidth::H16,
2606 "ld1.b" => SimdLaneWidth::B8,
2607 _ => unreachable!(),
2608 };
2609 self.expect(&Tok::LBrace)?;
2610 let rt = self.parse_simd_reg()?;
2611 self.expect(&Tok::RBrace)?;
2612 self.expect(&Tok::LBracket)?;
2613 let index = self.parse_lane_index(width.max_index())?;
2614 self.expect(&Tok::RBracket)?;
2615 self.expect(&Tok::Comma)?;
2616 self.expect(&Tok::LBracket)?;
2617 let rn = self.parse_gp_reg()?;
2618 self.expect(&Tok::RBracket)?;
2619 Ok(match width {
2620 SimdLaneWidth::S32 => Inst::Ld1LaneS { rt, index, rn },
2621 SimdLaneWidth::D64 => Inst::Ld1LaneD { rt, index, rn },
2622 SimdLaneWidth::H16 => Inst::Ld1LaneH { rt, index, rn },
2623 SimdLaneWidth::B8 => Inst::Ld1LaneB { rt, index, rn },
2624 })
2625 }
2626
2627 fn parse_mov_wide(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
2628 let (rd, sf) = self.parse_gp_reg_with_size()?;
2629 self.expect(&Tok::Comma)?;
2630 let imm = self.parse_immediate_const_expr("mov wide immediate")? as u16;
2631 let shift = self.parse_optional_lsl_amount()?;
2632 Ok(match mnemonic {
2633 "movz" => Inst::Movz {
2634 rd,
2635 imm16: imm,
2636 shift,
2637 sf,
2638 },
2639 "movk" => Inst::Movk {
2640 rd,
2641 imm16: imm,
2642 shift,
2643 sf,
2644 },
2645 "movn" => Inst::Movn {
2646 rd,
2647 imm16: imm,
2648 shift,
2649 sf,
2650 },
2651 _ => unreachable!(),
2652 })
2653 }
2654
2655 fn parse_shift(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
2656 let (rd, sf) = self.parse_gp_reg_with_size()?;
2657 self.expect(&Tok::Comma)?;
2658 let (rn, _) = self.parse_gp_reg_with_size()?;
2659 self.expect(&Tok::Comma)?;
2660 let amount = self.parse_immediate_const_expr("shift amount")? as u8;
2661 Ok(match mnemonic {
2662 "lsl" => Inst::LslImm { rd, rn, amount, sf },
2663 "lsr" => Inst::LsrImm { rd, rn, amount, sf },
2664 "asr" => Inst::AsrImm { rd, rn, amount, sf },
2665 _ => unreachable!(),
2666 })
2667 }
2668
2669 fn parse_bitfield_alias(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
2670 let (rd, sf) = self.parse_gp_reg_with_size()?;
2671 self.expect(&Tok::Comma)?;
2672 let (rn, _) = self.parse_gp_reg_with_size()?;
2673 self.expect(&Tok::Comma)?;
2674 let lsb = self.parse_immediate_const_expr("bitfield lsb")? as u8;
2675 self.expect(&Tok::Comma)?;
2676 let width = self.parse_immediate_const_expr("bitfield width")? as u8;
2677 self.validate_bitfield_alias_args(mnemonic, sf, lsb, width)?;
2678 Ok(match mnemonic {
2679 "ubfiz" => Inst::Ubfiz {
2680 rd,
2681 rn,
2682 lsb,
2683 width,
2684 sf,
2685 },
2686 "bfi" => Inst::Bfi {
2687 rd,
2688 rn,
2689 lsb,
2690 width,
2691 sf,
2692 },
2693 "bfxil" => Inst::Bfxil {
2694 rd,
2695 rn,
2696 lsb,
2697 width,
2698 sf,
2699 },
2700 _ => unreachable!(),
2701 })
2702 }
2703
2704 fn parse_b(&mut self) -> Result<Stmt, ParseError> {
2705 if self.starts_immediate_expr() {
2706 let offset = self.parse_immediate_const_expr("branch offset")? as i32;
2707 Ok(Stmt::Instruction(Inst::B { offset }))
2708 } else {
2709 let label = self.parse_label_reference()?;
2710 let addend = self.parse_optional_symbol_addend()?;
2711 Ok(Stmt::InstructionWithReloc(
2712 Inst::B { offset: 0 },
2713 LabelRef {
2714 symbol: label,
2715 kind: RelocKind::Branch26,
2716 addend,
2717 },
2718 ))
2719 }
2720 }
2721
2722 fn parse_bl(&mut self) -> Result<Stmt, ParseError> {
2723 if self.starts_immediate_expr() {
2724 let offset = self.parse_immediate_const_expr("branch offset")? as i32;
2725 Ok(Stmt::Instruction(Inst::Bl { offset }))
2726 } else {
2727 let label = self.parse_label_reference()?;
2728 let addend = self.parse_optional_symbol_addend()?;
2729 Ok(Stmt::InstructionWithReloc(
2730 Inst::Bl { offset: 0 },
2731 LabelRef {
2732 symbol: label,
2733 kind: RelocKind::Branch26,
2734 addend,
2735 },
2736 ))
2737 }
2738 }
2739
2740 fn parse_bcond(&mut self, cond_str: &str) -> Result<Stmt, ParseError> {
2741 let cond = parse_condition(cond_str)
2742 .ok_or_else(|| self.err(format!("unknown condition: {}", cond_str)))?;
2743 if self.starts_immediate_expr() {
2744 let offset = self.parse_immediate_const_expr("branch offset")? as i32;
2745 Ok(Stmt::Instruction(Inst::BCond { cond, offset }))
2746 } else {
2747 let label = self.parse_label_reference()?;
2748 let addend = self.parse_optional_symbol_addend()?;
2749 Ok(Stmt::InstructionWithReloc(
2750 Inst::BCond { cond, offset: 0 },
2751 LabelRef {
2752 symbol: label,
2753 kind: RelocKind::Branch19,
2754 addend,
2755 },
2756 ))
2757 }
2758 }
2759
2760 fn parse_cbz(&mut self, is_nz: bool) -> Result<Stmt, ParseError> {
2761 let (rt, sf) = self.parse_gp_reg_with_size()?;
2762 self.expect(&Tok::Comma)?;
2763 if self.starts_immediate_expr() {
2764 let offset = self.parse_immediate_const_expr("cbz/cbnz offset")? as i32;
2765 let inst = if is_nz {
2766 Inst::Cbnz { rt, offset, sf }
2767 } else {
2768 Inst::Cbz { rt, offset, sf }
2769 };
2770 Ok(Stmt::Instruction(inst))
2771 } else {
2772 let label = self.parse_label_reference()?;
2773 let addend = self.parse_optional_symbol_addend()?;
2774 let inst = if is_nz {
2775 Inst::Cbnz { rt, offset: 0, sf }
2776 } else {
2777 Inst::Cbz { rt, offset: 0, sf }
2778 };
2779 Ok(Stmt::InstructionWithReloc(
2780 inst,
2781 LabelRef {
2782 symbol: label,
2783 kind: RelocKind::Branch19,
2784 addend,
2785 },
2786 ))
2787 }
2788 }
2789
2790 fn parse_tbz(&mut self, is_nz: bool) -> Result<Stmt, ParseError> {
2791 let (rt, sf) = self.parse_gp_reg_with_size()?;
2792 self.expect(&Tok::Comma)?;
2793 let bit = self.parse_bit_index(
2794 sf,
2795 if is_nz {
2796 "tbnz bit index"
2797 } else {
2798 "tbz bit index"
2799 },
2800 )?;
2801 self.expect(&Tok::Comma)?;
2802 if self.starts_immediate_expr() {
2803 let offset = self.parse_immediate_const_expr("tbz/tbnz offset")? as i32;
2804 let inst = if is_nz {
2805 Inst::Tbnz {
2806 rt,
2807 bit,
2808 offset,
2809 sf,
2810 }
2811 } else {
2812 Inst::Tbz {
2813 rt,
2814 bit,
2815 offset,
2816 sf,
2817 }
2818 };
2819 Ok(Stmt::Instruction(inst))
2820 } else {
2821 let label = self.parse_label_reference()?;
2822 let addend = self.parse_optional_symbol_addend()?;
2823 let inst = if is_nz {
2824 Inst::Tbnz {
2825 rt,
2826 bit,
2827 offset: 0,
2828 sf,
2829 }
2830 } else {
2831 Inst::Tbz {
2832 rt,
2833 bit,
2834 offset: 0,
2835 sf,
2836 }
2837 };
2838 Ok(Stmt::InstructionWithReloc(
2839 inst,
2840 LabelRef {
2841 symbol: label,
2842 kind: RelocKind::Branch14,
2843 addend,
2844 },
2845 ))
2846 }
2847 }
2848
2849 fn parse_ret(&mut self) -> Result<Inst, ParseError> {
2850 if self.at_end_of_stmt() {
2851 Ok(Inst::Ret { rn: X30 })
2852 } else {
2853 let rn = self.parse_gp_reg()?;
2854 Ok(Inst::Ret { rn })
2855 }
2856 }
2857
2858 fn parse_adr(&mut self) -> Result<Stmt, ParseError> {
2859 let rd = self.parse_gp_reg()?;
2860 self.expect(&Tok::Comma)?;
2861 if self.starts_immediate_expr() {
2862 let imm = self.parse_immediate_const_expr("adr immediate")? as i32;
2863 Ok(Stmt::Instruction(Inst::Adr { rd, imm }))
2864 } else {
2865 let label = self.parse_label_reference()?;
2866 let addend = self.parse_optional_symbol_addend()?;
2867 Ok(Stmt::InstructionWithReloc(
2868 Inst::Adr { rd, imm: 0 },
2869 LabelRef {
2870 symbol: label,
2871 kind: RelocKind::Adr21,
2872 addend,
2873 },
2874 ))
2875 }
2876 }
2877
2878 fn parse_adrp(&mut self) -> Result<Stmt, ParseError> {
2879 let rd = self.parse_gp_reg()?;
2880 self.expect(&Tok::Comma)?;
2881 if self.starts_immediate_expr() {
2882 let imm = self.parse_immediate_const_expr("adrp immediate")? as i32;
2883 Ok(Stmt::Instruction(Inst::Adrp { rd, imm }))
2884 } else {
2885 let label = self.parse_label_reference()?;
2886 let kind = self.parse_symbol_reloc_modifier(
2887 Some(RelocKind::Page21),
2888 &[
2889 ("PAGE", RelocKind::Page21),
2890 ("GOTPAGE", RelocKind::GotLoadPage21),
2891 ("TLVPPAGE", RelocKind::TlvpLoadPage21),
2892 ],
2893 "adrp symbol operand",
2894 )?;
2895 let addend = self.parse_optional_symbol_addend()?;
2896 Ok(Stmt::InstructionWithReloc(
2897 Inst::Adrp { rd, imm: 0 },
2898 LabelRef {
2899 symbol: label,
2900 kind,
2901 addend,
2902 },
2903 ))
2904 }
2905 }
2906
2907 fn parse_ldr_str(&mut self, is_load: bool) -> Result<Stmt, ParseError> {
2908 if self.starts_fp_register_like_operand() {
2909 return self.parse_ldr_str_fp(is_load);
2910 }
2911 self.parse_ldr_str_gp(is_load)
2912 }
2913
2914 fn gp_mem_offset_inst(
2915 &self,
2916 is_load: bool,
2917 sf: bool,
2918 rt: GpReg,
2919 rn: GpReg,
2920 offset: i16,
2921 force_unscaled: bool,
2922 ) -> Inst {
2923 if force_unscaled || offset < 0 {
2924 match (is_load, sf) {
2925 (true, true) => Inst::Ldur64 { rt, rn, offset },
2926 (false, true) => Inst::Stur64 { rt, rn, offset },
2927 (true, false) => Inst::Ldur32 { rt, rn, offset },
2928 (false, false) => Inst::Stur32 { rt, rn, offset },
2929 }
2930 } else {
2931 match (is_load, sf) {
2932 (true, true) => Inst::LdrImm64 {
2933 rt,
2934 rn,
2935 offset: offset as u16,
2936 },
2937 (false, true) => Inst::StrImm64 {
2938 rt,
2939 rn,
2940 offset: offset as u16,
2941 },
2942 (true, false) => Inst::LdrImm32 {
2943 rt,
2944 rn,
2945 offset: offset as u16,
2946 },
2947 (false, false) => Inst::StrImm32 {
2948 rt,
2949 rn,
2950 offset: offset as u16,
2951 },
2952 }
2953 }
2954 }
2955
2956 fn gp_mem_pre_inst(&self, is_load: bool, sf: bool, rt: GpReg, rn: GpReg, offset: i16) -> Inst {
2957 match (is_load, sf) {
2958 (true, true) => Inst::LdrPre64 { rt, rn, offset },
2959 (false, true) => Inst::StrPre64 { rt, rn, offset },
2960 (true, false) => Inst::LdrPre32 { rt, rn, offset },
2961 (false, false) => Inst::StrPre32 { rt, rn, offset },
2962 }
2963 }
2964
2965 fn gp_mem_post_inst(&self, is_load: bool, sf: bool, rt: GpReg, rn: GpReg, offset: i16) -> Inst {
2966 match (is_load, sf) {
2967 (true, true) => Inst::LdrPost64 { rt, rn, offset },
2968 (false, true) => Inst::StrPost64 { rt, rn, offset },
2969 (true, false) => Inst::LdrPost32 { rt, rn, offset },
2970 (false, false) => Inst::StrPost32 { rt, rn, offset },
2971 }
2972 }
2973
2974 fn fp_mem_offset_inst(
2975 &self,
2976 is_load: bool,
2977 width: FpMemWidth,
2978 rt: FpReg,
2979 rn: GpReg,
2980 offset: i64,
2981 ) -> Result<Inst, ParseError> {
2982 let scale = 1i64 << width.scale();
2983 let fits_unsigned =
2984 offset >= 0 && offset % scale == 0 && (offset >> width.scale()) <= 0xFFF;
2985 if fits_unsigned {
2986 let offset = offset as u16;
2987 return Ok(match (is_load, width) {
2988 (true, FpMemWidth::H16) => Inst::LdrFpImm16 { rt, rn, offset },
2989 (false, FpMemWidth::H16) => Inst::StrFpImm16 { rt, rn, offset },
2990 (true, FpMemWidth::B8) => Inst::LdrFpImm8 { rt, rn, offset },
2991 (false, FpMemWidth::B8) => Inst::StrFpImm8 { rt, rn, offset },
2992 (true, FpMemWidth::D64) => Inst::LdrFpImm64 { rt, rn, offset },
2993 (false, FpMemWidth::D64) => Inst::StrFpImm64 { rt, rn, offset },
2994 (true, FpMemWidth::S32) => Inst::LdrFpImm32 { rt, rn, offset },
2995 (false, FpMemWidth::S32) => Inst::StrFpImm32 { rt, rn, offset },
2996 (true, FpMemWidth::Q128) => Inst::LdrFpImm128 { rt, rn, offset },
2997 (false, FpMemWidth::Q128) => Inst::StrFpImm128 { rt, rn, offset },
2998 });
2999 }
3000
3001 if (-256..=255).contains(&offset) {
3002 let offset = offset as i16;
3003 return Ok(match (is_load, width) {
3004 (true, FpMemWidth::H16) => Inst::LdurFp16 { rt, rn, offset },
3005 (false, FpMemWidth::H16) => Inst::SturFp16 { rt, rn, offset },
3006 (true, FpMemWidth::B8) => Inst::LdurFp8 { rt, rn, offset },
3007 (false, FpMemWidth::B8) => Inst::SturFp8 { rt, rn, offset },
3008 (true, FpMemWidth::D64) => Inst::LdurFp64 { rt, rn, offset },
3009 (false, FpMemWidth::D64) => Inst::SturFp64 { rt, rn, offset },
3010 (true, FpMemWidth::S32) => Inst::LdurFp32 { rt, rn, offset },
3011 (false, FpMemWidth::S32) => Inst::SturFp32 { rt, rn, offset },
3012 (true, FpMemWidth::Q128) => Inst::LdurFp128 { rt, rn, offset },
3013 (false, FpMemWidth::Q128) => Inst::SturFp128 { rt, rn, offset },
3014 });
3015 }
3016
3017 Err(self.err(format!(
3018 "FP/SIMD memory offset {offset} is out of range for {}",
3019 if is_load { "LDR" } else { "STR" }
3020 )))
3021 }
3022
3023 fn parse_ldur_stur(&mut self, is_load: bool) -> Result<Stmt, ParseError> {
3024 let (rt, sf) = self.parse_gp_reg_with_size()?;
3025 self.expect(&Tok::Comma)?;
3026 self.expect(&Tok::LBracket)?;
3027 let (rn, _) = self.parse_gp_reg_with_size()?;
3028 let offset = if self.eat(&Tok::RBracket) {
3029 0
3030 } else {
3031 self.expect(&Tok::Comma)?;
3032 let offset = self.parse_immediate_const_expr("memory offset")? as i16;
3033 self.expect(&Tok::RBracket)?;
3034 offset
3035 };
3036
3037 if self.eat(&Tok::Bang) {
3038 return Err(self.err("ldur/stur do not support pre-index addressing".into()));
3039 }
3040 if self.eat(&Tok::Comma) {
3041 return Err(self.err("ldur/stur do not support post-index addressing".into()));
3042 }
3043
3044 Ok(Stmt::Instruction(
3045 self.gp_mem_offset_inst(is_load, sf, rt, rn, offset, true),
3046 ))
3047 }
3048
3049 fn parse_ldr_str_gp(&mut self, is_load: bool) -> Result<Stmt, ParseError> {
3050 let (rt, sf) = self.parse_gp_reg_with_size()?;
3051 self.expect(&Tok::Comma)?;
3052
3053 if is_load && self.starts_immediate_expr() {
3054 let offset = self.parse_immediate_const_expr("ldr literal offset")? as i32;
3055 let inst = if sf {
3056 Inst::LdrLit64 { rt, offset }
3057 } else {
3058 Inst::LdrLit32 { rt, offset }
3059 };
3060 return Ok(Stmt::Instruction(inst));
3061 }
3062
3063 if is_load && self.starts_non_register_literal_reference() {
3064 let label = self.parse_label_reference()?;
3065 let addend = self.parse_optional_symbol_addend()?;
3066 let inst = if sf {
3067 Inst::LdrLit64 { rt, offset: 0 }
3068 } else {
3069 Inst::LdrLit32 { rt, offset: 0 }
3070 };
3071 return Ok(Stmt::InstructionWithReloc(
3072 inst,
3073 LabelRef {
3074 symbol: label,
3075 kind: RelocKind::Literal19,
3076 addend,
3077 },
3078 ));
3079 }
3080
3081 if !is_load && self.peek() != &Tok::LBracket {
3082 return Err(self.err("STR expects a bracketed memory operand".into()));
3083 }
3084
3085 self.expect(&Tok::LBracket)?;
3086 let (rn, _) = self.parse_gp_reg_with_size()?;
3087
3088 if self.eat(&Tok::RBracket) {
3089 // [Xn] or [Xn], #off (post-index)
3090 if self.eat(&Tok::Comma) {
3091 let offset = self.parse_immediate_const_expr("post-index offset")? as i16;
3092 return Ok(Stmt::Instruction(
3093 self.gp_mem_post_inst(is_load, sf, rt, rn, offset),
3094 ));
3095 }
3096 return Ok(Stmt::Instruction(
3097 self.gp_mem_offset_inst(is_load, sf, rt, rn, 0, false),
3098 ));
3099 }
3100
3101 self.expect(&Tok::Comma)?;
3102 if self.starts_non_register_symbol_reference() {
3103 let label = self.parse_label_reference()?;
3104 let allowed = if is_load {
3105 &[
3106 ("PAGEOFF", RelocKind::PageOff12),
3107 ("GOTPAGEOFF", RelocKind::GotLoadPageOff12),
3108 ("TLVPPAGEOFF", RelocKind::TlvpLoadPageOff12),
3109 ][..]
3110 } else {
3111 &[("PAGEOFF", RelocKind::PageOff12)][..]
3112 };
3113 let kind = self.parse_symbol_reloc_modifier(None, allowed, "memory symbol operand")?;
3114 let addend = self.parse_optional_symbol_addend()?;
3115 self.expect(&Tok::RBracket)?;
3116 let inst = if sf {
3117 if is_load {
3118 Inst::LdrImm64 { rt, rn, offset: 0 }
3119 } else {
3120 Inst::StrImm64 { rt, rn, offset: 0 }
3121 }
3122 } else {
3123 if is_load {
3124 Inst::LdrImm32 { rt, rn, offset: 0 }
3125 } else {
3126 Inst::StrImm32 { rt, rn, offset: 0 }
3127 }
3128 };
3129 return Ok(Stmt::InstructionWithReloc(
3130 inst,
3131 LabelRef {
3132 symbol: label,
3133 kind,
3134 addend,
3135 },
3136 ));
3137 }
3138
3139 if self.starts_register_like_operand() {
3140 let (rm, extend, shift) = self.parse_reg_offset_operand(if sf { 3 } else { 2 })?;
3141 self.expect(&Tok::RBracket)?;
3142 let inst = if sf {
3143 if is_load {
3144 Inst::LdrReg64 {
3145 rt,
3146 rn,
3147 rm,
3148 extend,
3149 shift,
3150 }
3151 } else {
3152 Inst::StrReg64 {
3153 rt,
3154 rn,
3155 rm,
3156 extend,
3157 shift,
3158 }
3159 }
3160 } else if is_load {
3161 Inst::LdrReg32 {
3162 rt,
3163 rn,
3164 rm,
3165 extend,
3166 shift,
3167 }
3168 } else {
3169 Inst::StrReg32 {
3170 rt,
3171 rn,
3172 rm,
3173 extend,
3174 shift,
3175 }
3176 };
3177 return Ok(Stmt::Instruction(inst));
3178 }
3179
3180 let offset = self.parse_immediate_const_expr("memory offset")? as i16;
3181 self.expect(&Tok::RBracket)?;
3182
3183 if self.eat(&Tok::Bang) {
3184 return Ok(Stmt::Instruction(
3185 self.gp_mem_pre_inst(is_load, sf, rt, rn, offset),
3186 ));
3187 }
3188
3189 Ok(Stmt::Instruction(
3190 self.gp_mem_offset_inst(is_load, sf, rt, rn, offset, false),
3191 ))
3192 }
3193
3194 fn parse_ldr_str_fp(&mut self, is_load: bool) -> Result<Stmt, ParseError> {
3195 let (rt, width) = self.parse_fp_mem_reg_with_width()?;
3196 self.expect(&Tok::Comma)?;
3197 let is_scalar_narrow = matches!(width, FpMemWidth::B8 | FpMemWidth::H16);
3198
3199 if is_load && self.starts_immediate_expr() {
3200 if is_scalar_narrow {
3201 return Err(self.err("narrow FP literal loads are not supported".into()));
3202 }
3203 let offset = self.parse_immediate_const_expr("ldr literal offset")? as i32;
3204 let inst = match width {
3205 FpMemWidth::B8 | FpMemWidth::H16 => unreachable!(),
3206 FpMemWidth::D64 => Inst::LdrFpLit64 { rt, offset },
3207 FpMemWidth::S32 => Inst::LdrFpLit32 { rt, offset },
3208 FpMemWidth::Q128 => Inst::LdrFpLit128 { rt, offset },
3209 };
3210 return Ok(Stmt::Instruction(inst));
3211 }
3212
3213 if is_load && self.starts_non_register_literal_reference() {
3214 if is_scalar_narrow {
3215 return Err(self.err("narrow FP literal loads are not supported".into()));
3216 }
3217 let label = self.parse_label_reference()?;
3218 let addend = self.parse_optional_symbol_addend()?;
3219 let inst = match width {
3220 FpMemWidth::B8 | FpMemWidth::H16 => unreachable!(),
3221 FpMemWidth::D64 => Inst::LdrFpLit64 { rt, offset: 0 },
3222 FpMemWidth::S32 => Inst::LdrFpLit32 { rt, offset: 0 },
3223 FpMemWidth::Q128 => Inst::LdrFpLit128 { rt, offset: 0 },
3224 };
3225 return Ok(Stmt::InstructionWithReloc(
3226 inst,
3227 LabelRef {
3228 symbol: label,
3229 kind: RelocKind::Literal19,
3230 addend,
3231 },
3232 ));
3233 }
3234
3235 if !is_load && self.peek() != &Tok::LBracket {
3236 return Err(self.err("STR expects a bracketed memory operand".into()));
3237 }
3238
3239 self.expect(&Tok::LBracket)?;
3240 let (rn, _) = self.parse_gp_reg_with_size()?;
3241
3242 if self.eat(&Tok::RBracket) {
3243 if self.eat(&Tok::Comma) {
3244 if is_scalar_narrow {
3245 return Err(
3246 self.err("post-index narrow FP loads/stores are not supported".into())
3247 );
3248 }
3249 let offset = self.parse_immediate_const_expr("post-index offset")? as i16;
3250 let inst = match (is_load, width) {
3251 (_, FpMemWidth::B8 | FpMemWidth::H16) => unreachable!(),
3252 (true, FpMemWidth::D64) => Inst::LdrFpPost64 { rt, rn, offset },
3253 (false, FpMemWidth::D64) => Inst::StrFpPost64 { rt, rn, offset },
3254 (true, FpMemWidth::S32) => Inst::LdrFpPost32 { rt, rn, offset },
3255 (false, FpMemWidth::S32) => Inst::StrFpPost32 { rt, rn, offset },
3256 (true, FpMemWidth::Q128) => Inst::LdrFpPost128 { rt, rn, offset },
3257 (false, FpMemWidth::Q128) => Inst::StrFpPost128 { rt, rn, offset },
3258 };
3259 return Ok(Stmt::Instruction(inst));
3260 }
3261 let inst = match (is_load, width) {
3262 (true, FpMemWidth::H16) => Inst::LdrFpImm16 { rt, rn, offset: 0 },
3263 (false, FpMemWidth::H16) => Inst::StrFpImm16 { rt, rn, offset: 0 },
3264 (true, FpMemWidth::B8) => Inst::LdrFpImm8 { rt, rn, offset: 0 },
3265 (false, FpMemWidth::B8) => Inst::StrFpImm8 { rt, rn, offset: 0 },
3266 (true, FpMemWidth::D64) => Inst::LdrFpImm64 { rt, rn, offset: 0 },
3267 (false, FpMemWidth::D64) => Inst::StrFpImm64 { rt, rn, offset: 0 },
3268 (true, FpMemWidth::S32) => Inst::LdrFpImm32 { rt, rn, offset: 0 },
3269 (false, FpMemWidth::S32) => Inst::StrFpImm32 { rt, rn, offset: 0 },
3270 (true, FpMemWidth::Q128) => Inst::LdrFpImm128 { rt, rn, offset: 0 },
3271 (false, FpMemWidth::Q128) => Inst::StrFpImm128 { rt, rn, offset: 0 },
3272 };
3273 return Ok(Stmt::Instruction(inst));
3274 }
3275
3276 self.expect(&Tok::Comma)?;
3277 if self.starts_non_register_symbol_reference() {
3278 if is_scalar_narrow {
3279 return Err(self.err("symbolic narrow FP loads/stores are not supported".into()));
3280 }
3281 let label = self.parse_label_reference()?;
3282 let kind = self.parse_symbol_reloc_modifier(
3283 None,
3284 &[("PAGEOFF", RelocKind::PageOff12)],
3285 "FP/SIMD memory symbol operand",
3286 )?;
3287 let addend = self.parse_optional_symbol_addend()?;
3288 self.expect(&Tok::RBracket)?;
3289 let inst = match (is_load, width) {
3290 (_, FpMemWidth::B8 | FpMemWidth::H16) => unreachable!(),
3291 (true, FpMemWidth::D64) => Inst::LdrFpImm64 { rt, rn, offset: 0 },
3292 (false, FpMemWidth::D64) => Inst::StrFpImm64 { rt, rn, offset: 0 },
3293 (true, FpMemWidth::S32) => Inst::LdrFpImm32 { rt, rn, offset: 0 },
3294 (false, FpMemWidth::S32) => Inst::StrFpImm32 { rt, rn, offset: 0 },
3295 (true, FpMemWidth::Q128) => Inst::LdrFpImm128 { rt, rn, offset: 0 },
3296 (false, FpMemWidth::Q128) => Inst::StrFpImm128 { rt, rn, offset: 0 },
3297 };
3298 return Ok(Stmt::InstructionWithReloc(
3299 inst,
3300 LabelRef {
3301 symbol: label,
3302 kind,
3303 addend,
3304 },
3305 ));
3306 }
3307
3308 if self.starts_register_like_operand() {
3309 if is_scalar_narrow {
3310 return Err(
3311 self.err("register-offset narrow FP loads/stores are not supported".into())
3312 );
3313 }
3314 let (rm, extend, shift) = self.parse_reg_offset_operand(width.scale())?;
3315 self.expect(&Tok::RBracket)?;
3316 let inst = match (is_load, width) {
3317 (_, FpMemWidth::B8 | FpMemWidth::H16) => unreachable!(),
3318 (true, FpMemWidth::D64) => Inst::LdrFpReg64 {
3319 rt,
3320 rn,
3321 rm,
3322 extend,
3323 shift,
3324 },
3325 (false, FpMemWidth::D64) => Inst::StrFpReg64 {
3326 rt,
3327 rn,
3328 rm,
3329 extend,
3330 shift,
3331 },
3332 (true, FpMemWidth::S32) => Inst::LdrFpReg32 {
3333 rt,
3334 rn,
3335 rm,
3336 extend,
3337 shift,
3338 },
3339 (false, FpMemWidth::S32) => Inst::StrFpReg32 {
3340 rt,
3341 rn,
3342 rm,
3343 extend,
3344 shift,
3345 },
3346 (true, FpMemWidth::Q128) => Inst::LdrFpReg128 {
3347 rt,
3348 rn,
3349 rm,
3350 extend,
3351 shift,
3352 },
3353 (false, FpMemWidth::Q128) => Inst::StrFpReg128 {
3354 rt,
3355 rn,
3356 rm,
3357 extend,
3358 shift,
3359 },
3360 };
3361 return Ok(Stmt::Instruction(inst));
3362 }
3363
3364 let offset = self.parse_immediate_const_expr("memory offset")?;
3365 self.expect(&Tok::RBracket)?;
3366
3367 if self.eat(&Tok::Bang) {
3368 if is_scalar_narrow {
3369 return Err(self.err("pre-index narrow FP loads/stores are not supported".into()));
3370 }
3371 let inst = match (is_load, width) {
3372 (_, FpMemWidth::B8 | FpMemWidth::H16) => unreachable!(),
3373 (true, FpMemWidth::D64) => Inst::LdrFpPre64 {
3374 rt,
3375 rn,
3376 offset: offset as i16,
3377 },
3378 (false, FpMemWidth::D64) => Inst::StrFpPre64 {
3379 rt,
3380 rn,
3381 offset: offset as i16,
3382 },
3383 (true, FpMemWidth::S32) => Inst::LdrFpPre32 {
3384 rt,
3385 rn,
3386 offset: offset as i16,
3387 },
3388 (false, FpMemWidth::S32) => Inst::StrFpPre32 {
3389 rt,
3390 rn,
3391 offset: offset as i16,
3392 },
3393 (true, FpMemWidth::Q128) => Inst::LdrFpPre128 {
3394 rt,
3395 rn,
3396 offset: offset as i16,
3397 },
3398 (false, FpMemWidth::Q128) => Inst::StrFpPre128 {
3399 rt,
3400 rn,
3401 offset: offset as i16,
3402 },
3403 };
3404 return Ok(Stmt::Instruction(inst));
3405 }
3406
3407 let inst = self.fp_mem_offset_inst(is_load, width, rt, rn, offset)?;
3408 Ok(Stmt::Instruction(inst))
3409 }
3410
3411 fn parse_ldstb_h_offset_inst(&self, mnemonic: &str, rt: GpReg, rn: GpReg, offset: i16) -> Inst {
3412 match mnemonic {
3413 "ldrb" => Inst::Ldrb {
3414 rt,
3415 rn,
3416 offset: offset as u16,
3417 },
3418 "ldrh" => Inst::Ldrh {
3419 rt,
3420 rn,
3421 offset: offset as u16,
3422 },
3423 "strb" => Inst::Strb {
3424 rt,
3425 rn,
3426 offset: offset as u16,
3427 },
3428 "strh" => Inst::Strh {
3429 rt,
3430 rn,
3431 offset: offset as u16,
3432 },
3433 _ => unreachable!(),
3434 }
3435 }
3436
3437 fn parse_ldst_signed_b_h_offset_inst(
3438 &self,
3439 mnemonic: &str,
3440 rt: GpReg,
3441 sf: bool,
3442 rn: GpReg,
3443 offset: i16,
3444 ) -> Inst {
3445 match (mnemonic, sf) {
3446 ("ldrsb", false) => Inst::Ldrsb32 {
3447 rt,
3448 rn,
3449 offset: offset as u16,
3450 },
3451 ("ldrsb", true) => Inst::Ldrsb64 {
3452 rt,
3453 rn,
3454 offset: offset as u16,
3455 },
3456 ("ldrsh", false) => Inst::Ldrsh32 {
3457 rt,
3458 rn,
3459 offset: offset as u16,
3460 },
3461 ("ldrsh", true) => Inst::Ldrsh64 {
3462 rt,
3463 rn,
3464 offset: offset as u16,
3465 },
3466 _ => unreachable!(),
3467 }
3468 }
3469
3470 fn parse_ldstb_h_reg_inst(
3471 &self,
3472 mnemonic: &str,
3473 rt: GpReg,
3474 rn: GpReg,
3475 rm: GpReg,
3476 extend: AddrExtend,
3477 shift: bool,
3478 ) -> Inst {
3479 match mnemonic {
3480 "ldrb" => Inst::LdrbReg {
3481 rt,
3482 rn,
3483 rm,
3484 extend,
3485 shift,
3486 },
3487 "ldrh" => Inst::LdrhReg {
3488 rt,
3489 rn,
3490 rm,
3491 extend,
3492 shift,
3493 },
3494 "strb" => Inst::StrbReg {
3495 rt,
3496 rn,
3497 rm,
3498 extend,
3499 shift,
3500 },
3501 "strh" => Inst::StrhReg {
3502 rt,
3503 rn,
3504 rm,
3505 extend,
3506 shift,
3507 },
3508 _ => unreachable!(),
3509 }
3510 }
3511
3512 fn parse_ldst_signed_b_h_reg_inst(
3513 &self,
3514 mnemonic: &str,
3515 rt_and_sf: (GpReg, bool),
3516 rn: GpReg,
3517 rm: GpReg,
3518 extend: AddrExtend,
3519 shift: bool,
3520 ) -> Inst {
3521 let (rt, sf) = rt_and_sf;
3522 match (mnemonic, sf) {
3523 ("ldrsb", false) => Inst::LdrsbReg32 {
3524 rt,
3525 rn,
3526 rm,
3527 extend,
3528 shift,
3529 },
3530 ("ldrsb", true) => Inst::LdrsbReg64 {
3531 rt,
3532 rn,
3533 rm,
3534 extend,
3535 shift,
3536 },
3537 ("ldrsh", false) => Inst::LdrshReg32 {
3538 rt,
3539 rn,
3540 rm,
3541 extend,
3542 shift,
3543 },
3544 ("ldrsh", true) => Inst::LdrshReg64 {
3545 rt,
3546 rn,
3547 rm,
3548 extend,
3549 shift,
3550 },
3551 _ => unreachable!(),
3552 }
3553 }
3554
3555 fn parse_ldstb_h_pre_inst(&self, mnemonic: &str, rt: GpReg, rn: GpReg, offset: i16) -> Inst {
3556 match mnemonic {
3557 "ldrb" => Inst::LdrbPre { rt, rn, offset },
3558 "ldrh" => Inst::LdrhPre { rt, rn, offset },
3559 "strb" => Inst::StrbPre { rt, rn, offset },
3560 "strh" => Inst::StrhPre { rt, rn, offset },
3561 _ => unreachable!(),
3562 }
3563 }
3564
3565 fn parse_ldst_signed_b_h_pre_inst(
3566 &self,
3567 mnemonic: &str,
3568 rt: GpReg,
3569 sf: bool,
3570 rn: GpReg,
3571 offset: i16,
3572 ) -> Inst {
3573 match (mnemonic, sf) {
3574 ("ldrsb", false) => Inst::LdrsbPre32 { rt, rn, offset },
3575 ("ldrsb", true) => Inst::LdrsbPre64 { rt, rn, offset },
3576 ("ldrsh", false) => Inst::LdrshPre32 { rt, rn, offset },
3577 ("ldrsh", true) => Inst::LdrshPre64 { rt, rn, offset },
3578 _ => unreachable!(),
3579 }
3580 }
3581
3582 fn parse_ldstb_h_post_inst(&self, mnemonic: &str, rt: GpReg, rn: GpReg, offset: i16) -> Inst {
3583 match mnemonic {
3584 "ldrb" => Inst::LdrbPost { rt, rn, offset },
3585 "ldrh" => Inst::LdrhPost { rt, rn, offset },
3586 "strb" => Inst::StrbPost { rt, rn, offset },
3587 "strh" => Inst::StrhPost { rt, rn, offset },
3588 _ => unreachable!(),
3589 }
3590 }
3591
3592 fn parse_ldst_signed_b_h_post_inst(
3593 &self,
3594 mnemonic: &str,
3595 rt: GpReg,
3596 sf: bool,
3597 rn: GpReg,
3598 offset: i16,
3599 ) -> Inst {
3600 match (mnemonic, sf) {
3601 ("ldrsb", false) => Inst::LdrsbPost32 { rt, rn, offset },
3602 ("ldrsb", true) => Inst::LdrsbPost64 { rt, rn, offset },
3603 ("ldrsh", false) => Inst::LdrshPost32 { rt, rn, offset },
3604 ("ldrsh", true) => Inst::LdrshPost64 { rt, rn, offset },
3605 _ => unreachable!(),
3606 }
3607 }
3608
3609 fn parse_ldstb_h(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
3610 let rt = self.parse_gp_reg()?;
3611 self.expect(&Tok::Comma)?;
3612 self.expect(&Tok::LBracket)?;
3613 let rn = self.parse_gp_reg()?;
3614 if self.eat(&Tok::RBracket) {
3615 if self.eat(&Tok::Comma) {
3616 let offset = self.parse_immediate_const_expr("post-index offset")? as i16;
3617 return Ok(self.parse_ldstb_h_post_inst(mnemonic, rt, rn, offset));
3618 }
3619 return Ok(self.parse_ldstb_h_offset_inst(mnemonic, rt, rn, 0));
3620 }
3621
3622 self.expect(&Tok::Comma)?;
3623 if self.starts_register_like_operand() {
3624 let scale = match mnemonic {
3625 "ldrb" | "strb" => 0,
3626 "ldrh" | "strh" => 1,
3627 _ => unreachable!(),
3628 };
3629 let (rm, extend, shift) = self.parse_reg_offset_operand(scale)?;
3630 self.expect(&Tok::RBracket)?;
3631 return Ok(self.parse_ldstb_h_reg_inst(mnemonic, rt, rn, rm, extend, shift));
3632 }
3633
3634 let offset = self.parse_immediate_const_expr("memory offset")? as i16;
3635 self.expect(&Tok::RBracket)?;
3636 if self.eat(&Tok::Bang) {
3637 return Ok(self.parse_ldstb_h_pre_inst(mnemonic, rt, rn, offset));
3638 }
3639 Ok(self.parse_ldstb_h_offset_inst(mnemonic, rt, rn, offset))
3640 }
3641
3642 fn parse_ldst_signed_b_h(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
3643 let (rt, sf) = self.parse_gp_reg_with_size()?;
3644 self.expect(&Tok::Comma)?;
3645 self.expect(&Tok::LBracket)?;
3646 let rn = self.parse_gp_reg()?;
3647 if self.eat(&Tok::RBracket) {
3648 if self.eat(&Tok::Comma) {
3649 let offset = self.parse_immediate_const_expr("post-index offset")? as i16;
3650 return Ok(self.parse_ldst_signed_b_h_post_inst(mnemonic, rt, sf, rn, offset));
3651 }
3652 return Ok(self.parse_ldst_signed_b_h_offset_inst(mnemonic, rt, sf, rn, 0));
3653 }
3654
3655 self.expect(&Tok::Comma)?;
3656 if self.starts_register_like_operand() {
3657 let scale = match mnemonic {
3658 "ldrsb" => 0,
3659 "ldrsh" => 1,
3660 _ => unreachable!(),
3661 };
3662 let (rm, extend, shift) = self.parse_reg_offset_operand(scale)?;
3663 self.expect(&Tok::RBracket)?;
3664 return Ok(self.parse_ldst_signed_b_h_reg_inst(
3665 mnemonic,
3666 (rt, sf),
3667 rn,
3668 rm,
3669 extend,
3670 shift,
3671 ));
3672 }
3673
3674 let offset = self.parse_immediate_const_expr("memory offset")? as i16;
3675 self.expect(&Tok::RBracket)?;
3676 if self.eat(&Tok::Bang) {
3677 return Ok(self.parse_ldst_signed_b_h_pre_inst(mnemonic, rt, sf, rn, offset));
3678 }
3679 Ok(self.parse_ldst_signed_b_h_offset_inst(mnemonic, rt, sf, rn, offset))
3680 }
3681
3682 fn parse_ldrsw(&mut self) -> Result<Stmt, ParseError> {
3683 let (rt, sf) = self.parse_gp_reg_with_size()?;
3684 if !sf {
3685 return Err(self.err("ldrsw destination must be an x-register".into()));
3686 }
3687 self.expect(&Tok::Comma)?;
3688
3689 if self.starts_immediate_expr() {
3690 let offset = self.parse_immediate_const_expr("ldrsw literal offset")? as i32;
3691 return Ok(Stmt::Instruction(Inst::LdrswLit { rt, offset }));
3692 }
3693
3694 if self.starts_non_register_literal_reference() {
3695 let label = self.parse_label_reference()?;
3696 let addend = self.parse_optional_symbol_addend()?;
3697 return Ok(Stmt::InstructionWithReloc(
3698 Inst::LdrswLit { rt, offset: 0 },
3699 LabelRef {
3700 symbol: label,
3701 kind: RelocKind::Literal19,
3702 addend,
3703 },
3704 ));
3705 }
3706
3707 self.expect(&Tok::LBracket)?;
3708 let rn = self.parse_gp_reg()?;
3709 let offset = if self.eat(&Tok::Comma) {
3710 if self.starts_register_like_operand() {
3711 let (rm, extend, shift) = self.parse_reg_offset_operand(2)?;
3712 self.expect(&Tok::RBracket)?;
3713 return Ok(Stmt::Instruction(Inst::LdrswReg {
3714 rt,
3715 rn,
3716 rm,
3717 extend,
3718 shift,
3719 }));
3720 }
3721 self.parse_immediate_const_expr("memory offset")?
3722 } else {
3723 0
3724 };
3725 self.expect(&Tok::RBracket)?;
3726 Ok(Stmt::Instruction(Inst::Ldrsw {
3727 rt,
3728 rn,
3729 offset: offset as u16,
3730 }))
3731 }
3732
3733 fn parse_ldp_stp(&mut self, is_load: bool) -> Result<Inst, ParseError> {
3734 if self.starts_fp_register_like_operand() {
3735 return self.parse_ldp_stp_fp(is_load);
3736 }
3737 self.parse_ldp_stp_gp(is_load)
3738 }
3739
3740 fn parse_ldp_stp_gp(&mut self, is_load: bool) -> Result<Inst, ParseError> {
3741 let (rt1, sf) = self.parse_gp_reg_with_size()?;
3742 self.expect(&Tok::Comma)?;
3743 let (rt2, second_sf) = self.parse_gp_reg_with_size()?;
3744 if sf != second_sf {
3745 return Err(self.err("ldp/stp register pair must use matching register widths".into()));
3746 }
3747 self.expect(&Tok::Comma)?;
3748 self.expect(&Tok::LBracket)?;
3749 let (rn, _) = self.parse_gp_reg_with_size()?;
3750
3751 if self.eat(&Tok::RBracket) {
3752 if self.eat(&Tok::Comma) {
3753 let offset = self.parse_immediate_const_expr("pair post-index offset")? as i16;
3754 return Ok(match (is_load, sf) {
3755 (true, true) => Inst::LdpPost64 {
3756 rt1,
3757 rt2,
3758 rn,
3759 offset,
3760 },
3761 (false, true) => Inst::StpPost64 {
3762 rt1,
3763 rt2,
3764 rn,
3765 offset,
3766 },
3767 (true, false) => Inst::LdpPost32 {
3768 rt1,
3769 rt2,
3770 rn,
3771 offset,
3772 },
3773 (false, false) => Inst::StpPost32 {
3774 rt1,
3775 rt2,
3776 rn,
3777 offset,
3778 },
3779 });
3780 }
3781
3782 return Ok(match (is_load, sf) {
3783 (true, true) => Inst::LdpOff64 {
3784 rt1,
3785 rt2,
3786 rn,
3787 offset: 0,
3788 },
3789 (false, true) => Inst::StpOff64 {
3790 rt1,
3791 rt2,
3792 rn,
3793 offset: 0,
3794 },
3795 (true, false) => Inst::LdpOff32 {
3796 rt1,
3797 rt2,
3798 rn,
3799 offset: 0,
3800 },
3801 (false, false) => Inst::StpOff32 {
3802 rt1,
3803 rt2,
3804 rn,
3805 offset: 0,
3806 },
3807 });
3808 }
3809
3810 self.expect(&Tok::Comma)?;
3811 let offset = self.parse_immediate_const_expr("pair offset")? as i16;
3812 self.expect(&Tok::RBracket)?;
3813
3814 if self.eat(&Tok::Bang) {
3815 // Pre-index
3816 return Ok(match (is_load, sf) {
3817 (true, true) => Inst::LdpPre64 {
3818 rt1,
3819 rt2,
3820 rn,
3821 offset,
3822 },
3823 (false, true) => Inst::StpPre64 {
3824 rt1,
3825 rt2,
3826 rn,
3827 offset,
3828 },
3829 (true, false) => Inst::LdpPre32 {
3830 rt1,
3831 rt2,
3832 rn,
3833 offset,
3834 },
3835 (false, false) => Inst::StpPre32 {
3836 rt1,
3837 rt2,
3838 rn,
3839 offset,
3840 },
3841 });
3842 }
3843
3844 // Signed offset
3845 Ok(match (is_load, sf) {
3846 (true, true) => Inst::LdpOff64 {
3847 rt1,
3848 rt2,
3849 rn,
3850 offset,
3851 },
3852 (false, true) => Inst::StpOff64 {
3853 rt1,
3854 rt2,
3855 rn,
3856 offset,
3857 },
3858 (true, false) => Inst::LdpOff32 {
3859 rt1,
3860 rt2,
3861 rn,
3862 offset,
3863 },
3864 (false, false) => Inst::StpOff32 {
3865 rt1,
3866 rt2,
3867 rn,
3868 offset,
3869 },
3870 })
3871 }
3872
3873 fn parse_ldp_stp_fp(&mut self, is_load: bool) -> Result<Inst, ParseError> {
3874 let (rt1, width) = self.parse_fp_mem_reg_with_width()?;
3875 self.expect(&Tok::Comma)?;
3876 let (rt2, second_width) = self.parse_fp_mem_reg_with_width()?;
3877 if width != second_width {
3878 return Err(
3879 self.err("ldp/stp FP register pair must use matching register widths".into())
3880 );
3881 }
3882 if matches!(width, FpMemWidth::B8 | FpMemWidth::H16) {
3883 return Err(self.err("ldp/stp does not support b/h FP register pairs".into()));
3884 }
3885 self.expect(&Tok::Comma)?;
3886 self.expect(&Tok::LBracket)?;
3887 let (rn, _) = self.parse_gp_reg_with_size()?;
3888
3889 if self.eat(&Tok::RBracket) {
3890 if self.eat(&Tok::Comma) {
3891 let offset = self.parse_immediate_const_expr("pair post-index offset")? as i16;
3892 return Ok(match (is_load, width) {
3893 (_, FpMemWidth::B8 | FpMemWidth::H16) => unreachable!(),
3894 (true, FpMemWidth::D64) => Inst::LdpFpPost64 {
3895 rt1,
3896 rt2,
3897 rn,
3898 offset,
3899 },
3900 (false, FpMemWidth::D64) => Inst::StpFpPost64 {
3901 rt1,
3902 rt2,
3903 rn,
3904 offset,
3905 },
3906 (true, FpMemWidth::S32) => Inst::LdpFpPost32 {
3907 rt1,
3908 rt2,
3909 rn,
3910 offset,
3911 },
3912 (false, FpMemWidth::S32) => Inst::StpFpPost32 {
3913 rt1,
3914 rt2,
3915 rn,
3916 offset,
3917 },
3918 (true, FpMemWidth::Q128) => Inst::LdpFpPost128 {
3919 rt1,
3920 rt2,
3921 rn,
3922 offset,
3923 },
3924 (false, FpMemWidth::Q128) => Inst::StpFpPost128 {
3925 rt1,
3926 rt2,
3927 rn,
3928 offset,
3929 },
3930 });
3931 }
3932
3933 return Ok(match (is_load, width) {
3934 (_, FpMemWidth::B8 | FpMemWidth::H16) => unreachable!(),
3935 (true, FpMemWidth::D64) => Inst::LdpFpOff64 {
3936 rt1,
3937 rt2,
3938 rn,
3939 offset: 0,
3940 },
3941 (false, FpMemWidth::D64) => Inst::StpFpOff64 {
3942 rt1,
3943 rt2,
3944 rn,
3945 offset: 0,
3946 },
3947 (true, FpMemWidth::S32) => Inst::LdpFpOff32 {
3948 rt1,
3949 rt2,
3950 rn,
3951 offset: 0,
3952 },
3953 (false, FpMemWidth::S32) => Inst::StpFpOff32 {
3954 rt1,
3955 rt2,
3956 rn,
3957 offset: 0,
3958 },
3959 (true, FpMemWidth::Q128) => Inst::LdpFpOff128 {
3960 rt1,
3961 rt2,
3962 rn,
3963 offset: 0,
3964 },
3965 (false, FpMemWidth::Q128) => Inst::StpFpOff128 {
3966 rt1,
3967 rt2,
3968 rn,
3969 offset: 0,
3970 },
3971 });
3972 }
3973
3974 self.expect(&Tok::Comma)?;
3975 let offset = self.parse_immediate_const_expr("pair offset")? as i16;
3976 self.expect(&Tok::RBracket)?;
3977
3978 if self.eat(&Tok::Bang) {
3979 return Ok(match (is_load, width) {
3980 (_, FpMemWidth::B8 | FpMemWidth::H16) => unreachable!(),
3981 (true, FpMemWidth::D64) => Inst::LdpFpPre64 {
3982 rt1,
3983 rt2,
3984 rn,
3985 offset,
3986 },
3987 (false, FpMemWidth::D64) => Inst::StpFpPre64 {
3988 rt1,
3989 rt2,
3990 rn,
3991 offset,
3992 },
3993 (true, FpMemWidth::S32) => Inst::LdpFpPre32 {
3994 rt1,
3995 rt2,
3996 rn,
3997 offset,
3998 },
3999 (false, FpMemWidth::S32) => Inst::StpFpPre32 {
4000 rt1,
4001 rt2,
4002 rn,
4003 offset,
4004 },
4005 (true, FpMemWidth::Q128) => Inst::LdpFpPre128 {
4006 rt1,
4007 rt2,
4008 rn,
4009 offset,
4010 },
4011 (false, FpMemWidth::Q128) => Inst::StpFpPre128 {
4012 rt1,
4013 rt2,
4014 rn,
4015 offset,
4016 },
4017 });
4018 }
4019
4020 Ok(match (is_load, width) {
4021 (_, FpMemWidth::B8 | FpMemWidth::H16) => unreachable!(),
4022 (true, FpMemWidth::D64) => Inst::LdpFpOff64 {
4023 rt1,
4024 rt2,
4025 rn,
4026 offset,
4027 },
4028 (false, FpMemWidth::D64) => Inst::StpFpOff64 {
4029 rt1,
4030 rt2,
4031 rn,
4032 offset,
4033 },
4034 (true, FpMemWidth::S32) => Inst::LdpFpOff32 {
4035 rt1,
4036 rt2,
4037 rn,
4038 offset,
4039 },
4040 (false, FpMemWidth::S32) => Inst::StpFpOff32 {
4041 rt1,
4042 rt2,
4043 rn,
4044 offset,
4045 },
4046 (true, FpMemWidth::Q128) => Inst::LdpFpOff128 {
4047 rt1,
4048 rt2,
4049 rn,
4050 offset,
4051 },
4052 (false, FpMemWidth::Q128) => Inst::StpFpOff128 {
4053 rt1,
4054 rt2,
4055 rn,
4056 offset,
4057 },
4058 })
4059 }
4060
4061 // ---- FP instruction parsers ----
4062
4063 fn parse_fp_arith(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
4064 let (rd, is_double) = self.parse_fp_reg_with_size()?;
4065 self.expect(&Tok::Comma)?;
4066 let (rn, _) = self.parse_fp_reg_with_size()?;
4067 self.expect(&Tok::Comma)?;
4068 let (rm, _) = self.parse_fp_reg_with_size()?;
4069 Ok(match (mnemonic, is_double) {
4070 ("fadd", true) => Inst::FaddD { rd, rn, rm },
4071 ("fadd", false) => Inst::FaddS { rd, rn, rm },
4072 ("fsub", true) => Inst::FsubD { rd, rn, rm },
4073 ("fsub", false) => Inst::FsubS { rd, rn, rm },
4074 ("fmul", true) => Inst::FmulD { rd, rn, rm },
4075 ("fmul", false) => Inst::FmulS { rd, rn, rm },
4076 ("fdiv", true) => Inst::FdivD { rd, rn, rm },
4077 ("fdiv", false) => Inst::FdivS { rd, rn, rm },
4078 _ => unreachable!(),
4079 })
4080 }
4081
4082 fn parse_simd_fp_arith_4s(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
4083 let rd = self.parse_simd_reg()?;
4084 self.expect(&Tok::Comma)?;
4085 let rn = self.parse_simd_reg()?;
4086 self.expect(&Tok::Comma)?;
4087 let rm = self.parse_simd_reg()?;
4088 Ok(match mnemonic {
4089 "fadd.4s" => Inst::FaddV4S { rd, rn, rm },
4090 "faddp.4s" => Inst::FaddpV4S { rd, rn, rm },
4091 "fmaxp.4s" => Inst::FmaxpV4S { rd, rn, rm },
4092 "fminp.4s" => Inst::FminpV4S { rd, rn, rm },
4093 "fmaxnmp.4s" => Inst::FmaxnmpV4S { rd, rn, rm },
4094 "fminnmp.4s" => Inst::FminnmpV4S { rd, rn, rm },
4095 "fmla.4s" => Inst::FmlaV4S { rd, rn, rm },
4096 "fmls.4s" => Inst::FmlsV4S { rd, rn, rm },
4097 "fmax.4s" => Inst::FmaxV4S { rd, rn, rm },
4098 "fmin.4s" => Inst::FminV4S { rd, rn, rm },
4099 "fmaxnm.4s" => Inst::FmaxnmV4S { rd, rn, rm },
4100 "fminnm.4s" => Inst::FminnmV4S { rd, rn, rm },
4101 "fsub.4s" => Inst::FsubV4S { rd, rn, rm },
4102 "fmul.4s" => Inst::FmulV4S { rd, rn, rm },
4103 "fdiv.4s" => Inst::FdivV4S { rd, rn, rm },
4104 "frecps.4s" => Inst::FrecpsV4S { rd, rn, rm },
4105 "frsqrts.4s" => Inst::FrsqrtsV4S { rd, rn, rm },
4106 _ => unreachable!(),
4107 })
4108 }
4109
4110 fn parse_simd_fp_arith_2d(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
4111 let rd = self.parse_simd_reg()?;
4112 self.expect(&Tok::Comma)?;
4113 let rn = self.parse_simd_reg()?;
4114 self.expect(&Tok::Comma)?;
4115 let rm = self.parse_simd_reg()?;
4116 Ok(match mnemonic {
4117 "fadd.2d" => Inst::FaddV2D { rd, rn, rm },
4118 "fsub.2d" => Inst::FsubV2D { rd, rn, rm },
4119 "fmul.2d" => Inst::FmulV2D { rd, rn, rm },
4120 "fdiv.2d" => Inst::FdivV2D { rd, rn, rm },
4121 "fabd.2d" => Inst::FabdV2D { rd, rn, rm },
4122 "fmla.2d" => Inst::FmlaV2D { rd, rn, rm },
4123 "fmls.2d" => Inst::FmlsV2D { rd, rn, rm },
4124 "fmax.2d" => Inst::FmaxV2D { rd, rn, rm },
4125 "fmin.2d" => Inst::FminV2D { rd, rn, rm },
4126 "fmaxnm.2d" => Inst::FmaxnmV2D { rd, rn, rm },
4127 "fminnm.2d" => Inst::FminnmV2D { rd, rn, rm },
4128 "faddp.2d" => Inst::FaddpV2D { rd, rn, rm },
4129 "fmaxp.2d" => Inst::FmaxpV2D { rd, rn, rm },
4130 "fminp.2d" => Inst::FminpV2D { rd, rn, rm },
4131 "fmaxnmp.2d" => Inst::FmaxnmpV2D { rd, rn, rm },
4132 "fminnmp.2d" => Inst::FminnmpV2D { rd, rn, rm },
4133 "frecps.2d" => Inst::FrecpsV2D { rd, rn, rm },
4134 "frsqrts.2d" => Inst::FrsqrtsV2D { rd, rn, rm },
4135 _ => unreachable!(),
4136 })
4137 }
4138
4139 fn parse_simd_fp_unary_4s(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
4140 let rd = self.parse_simd_reg()?;
4141 self.expect(&Tok::Comma)?;
4142 let rn = self.parse_simd_reg()?;
4143 Ok(match mnemonic {
4144 "fabs.4s" => Inst::FabsV4S { rd, rn },
4145 "fneg.4s" => Inst::FnegV4S { rd, rn },
4146 "fsqrt.4s" => Inst::FsqrtV4S { rd, rn },
4147 "scvtf.4s" => Inst::ScvtfV4S { rd, rn },
4148 "ucvtf.4s" => Inst::UcvtfV4S { rd, rn },
4149 "fcvtzs.4s" => Inst::FcvtzsV4S { rd, rn },
4150 "fcvtzu.4s" => Inst::FcvtzuV4S { rd, rn },
4151 "frecpe.4s" => Inst::FrecpeV4S { rd, rn },
4152 "frsqrte.4s" => Inst::FrsqrteV4S { rd, rn },
4153 "frintn.4s" => Inst::FrintnV4S { rd, rn },
4154 "frintm.4s" => Inst::FrintmV4S { rd, rn },
4155 "frintp.4s" => Inst::FrintpV4S { rd, rn },
4156 "frintz.4s" => Inst::FrintzV4S { rd, rn },
4157 "frinta.4s" => Inst::FrintaV4S { rd, rn },
4158 "frinti.4s" => Inst::FrintiV4S { rd, rn },
4159 _ => unreachable!(),
4160 })
4161 }
4162
4163 fn parse_simd_fp_unary_2d(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
4164 let rd = self.parse_simd_reg()?;
4165 self.expect(&Tok::Comma)?;
4166 let rn = self.parse_simd_reg()?;
4167 Ok(match mnemonic {
4168 "fabs.2d" => Inst::FabsV2D { rd, rn },
4169 "fneg.2d" => Inst::FnegV2D { rd, rn },
4170 "fsqrt.2d" => Inst::FsqrtV2D { rd, rn },
4171 "scvtf.2d" => Inst::ScvtfV2D { rd, rn },
4172 "ucvtf.2d" => Inst::UcvtfV2D { rd, rn },
4173 "fcvtzs.2d" => Inst::FcvtzsV2D { rd, rn },
4174 "fcvtzu.2d" => Inst::FcvtzuV2D { rd, rn },
4175 "frecpe.2d" => Inst::FrecpeV2D { rd, rn },
4176 "frsqrte.2d" => Inst::FrsqrteV2D { rd, rn },
4177 "frintn.2d" => Inst::FrintnV2D { rd, rn },
4178 "frintm.2d" => Inst::FrintmV2D { rd, rn },
4179 "frintp.2d" => Inst::FrintpV2D { rd, rn },
4180 "frintz.2d" => Inst::FrintzV2D { rd, rn },
4181 "frinta.2d" => Inst::FrintaV2D { rd, rn },
4182 "frinti.2d" => Inst::FrintiV2D { rd, rn },
4183 _ => unreachable!(),
4184 })
4185 }
4186
4187 fn parse_simd_int_arith_4s(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
4188 let rd = self.parse_simd_reg()?;
4189 self.expect(&Tok::Comma)?;
4190 let rn = self.parse_simd_reg()?;
4191 self.expect(&Tok::Comma)?;
4192 let rm = self.parse_simd_reg()?;
4193 Ok(match mnemonic {
4194 "add.4s" => Inst::AddV4S { rd, rn, rm },
4195 "addp.4s" => Inst::AddpV4S { rd, rn, rm },
4196 "smax.4s" => Inst::SmaxV4S { rd, rn, rm },
4197 "smaxp.4s" => Inst::SmaxpV4S { rd, rn, rm },
4198 "smin.4s" => Inst::SminV4S { rd, rn, rm },
4199 "sminp.4s" => Inst::SminpV4S { rd, rn, rm },
4200 "sub.4s" => Inst::SubV4S { rd, rn, rm },
4201 "umax.4s" => Inst::UmaxV4S { rd, rn, rm },
4202 "umaxp.4s" => Inst::UmaxpV4S { rd, rn, rm },
4203 "umin.4s" => Inst::UminV4S { rd, rn, rm },
4204 "uminp.4s" => Inst::UminpV4S { rd, rn, rm },
4205 _ => unreachable!(),
4206 })
4207 }
4208
4209 fn parse_simd_int_arith_8h(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
4210 let rd = self.parse_simd_reg()?;
4211 self.expect(&Tok::Comma)?;
4212 let rn = self.parse_simd_reg()?;
4213 self.expect(&Tok::Comma)?;
4214 let rm = self.parse_simd_reg()?;
4215 Ok(match mnemonic {
4216 "addp.8h" => Inst::AddpV8H { rd, rn, rm },
4217 "smaxp.8h" => Inst::SmaxpV8H { rd, rn, rm },
4218 "sminp.8h" => Inst::SminpV8H { rd, rn, rm },
4219 "umaxp.8h" => Inst::UmaxpV8H { rd, rn, rm },
4220 "uminp.8h" => Inst::UminpV8H { rd, rn, rm },
4221 _ => unreachable!(),
4222 })
4223 }
4224
4225 fn parse_simd_int_arith_16b(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
4226 let rd = self.parse_simd_reg()?;
4227 self.expect(&Tok::Comma)?;
4228 let rn = self.parse_simd_reg()?;
4229 self.expect(&Tok::Comma)?;
4230 let rm = self.parse_simd_reg()?;
4231 Ok(match mnemonic {
4232 "addp.16b" => Inst::AddpV16B { rd, rn, rm },
4233 "smaxp.16b" => Inst::SmaxpV16B { rd, rn, rm },
4234 "sminp.16b" => Inst::SminpV16B { rd, rn, rm },
4235 "umaxp.16b" => Inst::UmaxpV16B { rd, rn, rm },
4236 "uminp.16b" => Inst::UminpV16B { rd, rn, rm },
4237 _ => unreachable!(),
4238 })
4239 }
4240
4241 fn parse_simd_int_arith_2d(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
4242 let rd = self.parse_simd_reg()?;
4243 self.expect(&Tok::Comma)?;
4244 let rn = self.parse_simd_reg()?;
4245 self.expect(&Tok::Comma)?;
4246 let rm = self.parse_simd_reg()?;
4247 Ok(match mnemonic {
4248 "addp.2d" => Inst::AddpV2D { rd, rn, rm },
4249 _ => unreachable!(),
4250 })
4251 }
4252
4253 fn parse_fpairwise_or_reduce_2d(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
4254 if self.peek_is_scalar_fp_reg() {
4255 self.parse_simd_reduce_2d(mnemonic)
4256 } else {
4257 self.parse_simd_fp_arith_2d(mnemonic)
4258 }
4259 }
4260
4261 fn parse_simd_reduce_4s(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
4262 let (rd, is_double) = self.parse_fp_reg_with_size()?;
4263 if is_double {
4264 return Err(self.err(format!("{} requires an s-register destination", mnemonic)));
4265 }
4266 self.expect(&Tok::Comma)?;
4267 let rn = self.parse_simd_reg()?;
4268 Ok(match mnemonic {
4269 "addv.4s" => Inst::AddvV4S { rd, rn },
4270 "faddp.2s" => Inst::FaddpV2S { rd, rn },
4271 "fmaxv.4s" => Inst::FmaxvV4S { rd, rn },
4272 "fminv.4s" => Inst::FminvV4S { rd, rn },
4273 "fmaxnmv.4s" => Inst::FmaxnmvV4S { rd, rn },
4274 "fminnmv.4s" => Inst::FminnmvV4S { rd, rn },
4275 "umaxv.4s" => Inst::UmaxvV4S { rd, rn },
4276 "smaxv.4s" => Inst::SmaxvV4S { rd, rn },
4277 "uminv.4s" => Inst::UminvV4S { rd, rn },
4278 "sminv.4s" => Inst::SminvV4S { rd, rn },
4279 _ => unreachable!(),
4280 })
4281 }
4282
4283 fn parse_simd_reduce_2d(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
4284 let (rd, is_double) = self.parse_fp_reg_with_size()?;
4285 if !is_double {
4286 return Err(self.err(format!("{} requires a d-register destination", mnemonic)));
4287 }
4288 self.expect(&Tok::Comma)?;
4289 let rn = self.parse_simd_reg()?;
4290 Ok(match mnemonic {
4291 "faddp.2d" => Inst::FaddpV2DScalar { rd, rn },
4292 "fmaxp.2d" => Inst::FmaxpV2DScalar { rd, rn },
4293 "fminp.2d" => Inst::FminpV2DScalar { rd, rn },
4294 "fmaxnmp.2d" => Inst::FmaxnmpV2DScalar { rd, rn },
4295 "fminnmp.2d" => Inst::FminnmpV2DScalar { rd, rn },
4296 _ => unreachable!(),
4297 })
4298 }
4299
4300 fn parse_simd_reduce_16b(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
4301 let (rd, width) = self.parse_fp_mem_reg_with_width()?;
4302 if width != FpMemWidth::B8 {
4303 return Err(self.err(format!("{} requires a b-register destination", mnemonic)));
4304 }
4305 self.expect(&Tok::Comma)?;
4306 let rn = self.parse_simd_reg()?;
4307 Ok(match mnemonic {
4308 "addv.16b" => Inst::AddvV16B { rd, rn },
4309 "umaxv.16b" => Inst::UmaxvV16B { rd, rn },
4310 "smaxv.16b" => Inst::SmaxvV16B { rd, rn },
4311 "uminv.16b" => Inst::UminvV16B { rd, rn },
4312 "sminv.16b" => Inst::SminvV16B { rd, rn },
4313 _ => unreachable!(),
4314 })
4315 }
4316
4317 fn parse_simd_reduce_8h(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
4318 let (rd, width) = self.parse_fp_mem_reg_with_width()?;
4319 if width != FpMemWidth::H16 {
4320 return Err(self.err(format!("{} requires an h-register destination", mnemonic)));
4321 }
4322 self.expect(&Tok::Comma)?;
4323 let rn = self.parse_simd_reg()?;
4324 Ok(match mnemonic {
4325 "addv.8h" => Inst::AddvV8H { rd, rn },
4326 "umaxv.8h" => Inst::UmaxvV8H { rd, rn },
4327 "smaxv.8h" => Inst::SmaxvV8H { rd, rn },
4328 "uminv.8h" => Inst::UminvV8H { rd, rn },
4329 "sminv.8h" => Inst::SminvV8H { rd, rn },
4330 _ => unreachable!(),
4331 })
4332 }
4333
4334 fn parse_simd_logic_16b(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
4335 let rd = self.parse_simd_reg()?;
4336 self.expect(&Tok::Comma)?;
4337 let rn = self.parse_simd_reg()?;
4338 self.expect(&Tok::Comma)?;
4339 let rm = self.parse_simd_reg()?;
4340 Ok(match mnemonic {
4341 "and.16b" => Inst::AndV16B { rd, rn, rm },
4342 "bic.16b" => Inst::BicV16B { rd, rn, rm },
4343 "bif.16b" => Inst::BifV16B { rd, rn, rm },
4344 "bit.16b" => Inst::BitV16B { rd, rn, rm },
4345 "bsl.16b" => Inst::BslV16B { rd, rn, rm },
4346 "orr.16b" => Inst::OrrV16B { rd, rn, rm },
4347 "eor.16b" => Inst::EorV16B { rd, rn, rm },
4348 _ => unreachable!(),
4349 })
4350 }
4351
4352 fn parse_simd_compare_4s(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
4353 let rd = self.parse_simd_reg()?;
4354 self.expect(&Tok::Comma)?;
4355 let rn = self.parse_simd_reg()?;
4356 self.expect(&Tok::Comma)?;
4357 let rm = self.parse_simd_reg()?;
4358 Ok(match mnemonic {
4359 "cmeq.4s" => Inst::CmeqV4S { rd, rn, rm },
4360 "fcmeq.4s" => Inst::FcmeqV4S { rd, rn, rm },
4361 "cmhs.4s" => Inst::CmhsV4S { rd, rn, rm },
4362 "cmhi.4s" => Inst::CmhiV4S { rd, rn, rm },
4363 "cmge.4s" => Inst::CmgeV4S { rd, rn, rm },
4364 "fcmge.4s" => Inst::FcmgeV4S { rd, rn, rm },
4365 "cmgt.4s" => Inst::CmgtV4S { rd, rn, rm },
4366 "fcmgt.4s" => Inst::FcmgtV4S { rd, rn, rm },
4367 _ => unreachable!(),
4368 })
4369 }
4370
4371 fn parse_simd_compare_2d(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
4372 let rd = self.parse_simd_reg()?;
4373 self.expect(&Tok::Comma)?;
4374 let rn = self.parse_simd_reg()?;
4375 self.expect(&Tok::Comma)?;
4376 if matches!(self.peek(), Tok::Hash | Tok::Integer(_) | Tok::Float(_)) {
4377 self.parse_fp_zero_immediate("vector FP compare immediate")?;
4378 return Ok(match mnemonic {
4379 "fcmge.2d" => Inst::FcmgeZeroV2D { rd, rn },
4380 "fcmgt.2d" => Inst::FcmgtZeroV2D { rd, rn },
4381 "fcmle.2d" => Inst::FcmleZeroV2D { rd, rn },
4382 "fcmlt.2d" => Inst::FcmltZeroV2D { rd, rn },
4383 _ => {
4384 return Err(self.err(format!(
4385 "{} does not support a zero immediate compare form",
4386 mnemonic
4387 )))
4388 }
4389 });
4390 }
4391 if matches!(mnemonic, "fcmle.2d" | "fcmlt.2d") {
4392 return Err(self.err(format!("{} requires a #0.0 immediate", mnemonic)));
4393 }
4394 let rm = self.parse_simd_reg()?;
4395 Ok(match mnemonic {
4396 "fcmeq.2d" => Inst::FcmeqV2D { rd, rn, rm },
4397 "fcmge.2d" => Inst::FcmgeV2D { rd, rn, rm },
4398 "fcmgt.2d" => Inst::FcmgtV2D { rd, rn, rm },
4399 _ => unreachable!(),
4400 })
4401 }
4402
4403 fn parse_simd_ext_16b(&mut self) -> Result<Inst, ParseError> {
4404 let rd = self.parse_simd_reg()?;
4405 self.expect(&Tok::Comma)?;
4406 let rn = self.parse_simd_reg()?;
4407 self.expect(&Tok::Comma)?;
4408 let rm = self.parse_simd_reg()?;
4409 self.expect(&Tok::Comma)?;
4410 let index_value = self.parse_immediate_const_expr("vector extract offset")?;
4411 let index = u8::try_from(index_value).map_err(|_| {
4412 self.err(format!(
4413 "vector extract offset {} out of range",
4414 index_value
4415 ))
4416 })?;
4417 if index > 15 {
4418 return Err(self.err(format!(
4419 "vector extract offset {} out of range",
4420 index_value
4421 )));
4422 }
4423 Ok(Inst::ExtV16B { rd, rn, rm, index })
4424 }
4425
4426 fn parse_simd_table_lookup(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
4427 let rd = self.parse_simd_reg()?;
4428 self.expect(&Tok::Comma)?;
4429 let (table, table_len) = self.parse_simd_table_list()?;
4430 self.expect(&Tok::Comma)?;
4431 let index = self.parse_simd_reg()?;
4432 Ok(match mnemonic {
4433 "tbl.16b" => Inst::TblV16B {
4434 rd,
4435 table,
4436 table_len,
4437 index,
4438 },
4439 "tbx.16b" => Inst::TbxV16B {
4440 rd,
4441 table,
4442 table_len,
4443 index,
4444 },
4445 _ => unreachable!(),
4446 })
4447 }
4448
4449 fn parse_simd_table_list(&mut self) -> Result<(FpReg, u8), ParseError> {
4450 self.expect(&Tok::LBrace)?;
4451 let first = self.parse_simd_reg()?;
4452 let mut prev = first;
4453 let mut count = 1u8;
4454 while self.eat(&Tok::Comma) {
4455 let next = self.parse_simd_reg()?;
4456 if next.num() != prev.num() + 1 {
4457 return Err(self.err("SIMD table register list must be consecutive".to_string()));
4458 }
4459 count += 1;
4460 if count > 4 {
4461 return Err(
4462 self.err("SIMD table register list supports at most 4 registers".to_string())
4463 );
4464 }
4465 prev = next;
4466 }
4467 self.expect(&Tok::RBrace)?;
4468 Ok((first, count))
4469 }
4470
4471 fn parse_simd_rev64_4s(&mut self) -> Result<Inst, ParseError> {
4472 let rd = self.parse_simd_reg()?;
4473 self.expect(&Tok::Comma)?;
4474 let rn = self.parse_simd_reg()?;
4475 Ok(Inst::Rev64V4S { rd, rn })
4476 }
4477
4478 fn parse_simd_shuffle_4s(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
4479 let rd = self.parse_simd_reg()?;
4480 self.expect(&Tok::Comma)?;
4481 let rn = self.parse_simd_reg()?;
4482 self.expect(&Tok::Comma)?;
4483 let rm = self.parse_simd_reg()?;
4484 Ok(match mnemonic {
4485 "zip1.4s" => Inst::Zip1V4S { rd, rn, rm },
4486 "zip2.4s" => Inst::Zip2V4S { rd, rn, rm },
4487 "uzp1.4s" => Inst::Uzp1V4S { rd, rn, rm },
4488 "uzp2.4s" => Inst::Uzp2V4S { rd, rn, rm },
4489 "trn1.4s" => Inst::Trn1V4S { rd, rn, rm },
4490 "trn2.4s" => Inst::Trn2V4S { rd, rn, rm },
4491 _ => unreachable!(),
4492 })
4493 }
4494
4495 fn parse_simd_shuffle_2d(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
4496 let rd = self.parse_simd_reg()?;
4497 self.expect(&Tok::Comma)?;
4498 let rn = self.parse_simd_reg()?;
4499 self.expect(&Tok::Comma)?;
4500 let rm = self.parse_simd_reg()?;
4501 Ok(match mnemonic {
4502 "zip1.2d" => Inst::Zip1V2D { rd, rn, rm },
4503 "zip2.2d" => Inst::Zip2V2D { rd, rn, rm },
4504 "uzp1.2d" => Inst::Uzp1V2D { rd, rn, rm },
4505 "uzp2.2d" => Inst::Uzp2V2D { rd, rn, rm },
4506 "trn1.2d" => Inst::Trn1V2D { rd, rn, rm },
4507 "trn2.2d" => Inst::Trn2V2D { rd, rn, rm },
4508 _ => unreachable!(),
4509 })
4510 }
4511
4512 fn parse_simd_mov(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
4513 let rd = self.parse_simd_reg()?;
4514 self.expect(&Tok::Comma)?;
4515 let rn = self.parse_simd_reg()?;
4516 Ok(match mnemonic {
4517 "mov.8b" => Inst::MovV8B { rd, rn },
4518 "mov.16b" => Inst::MovV16B { rd, rn },
4519 "mov.4s" => Inst::MovV4S { rd, rn },
4520 "mov.2d" => Inst::MovV2D { rd, rn },
4521 _ => unreachable!(),
4522 })
4523 }
4524
4525 fn parse_simd_dup(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
4526 let (width, make_inst): (SimdLaneWidth, fn(FpReg, FpReg, u8) -> Inst) = match mnemonic {
4527 "dup.16b" => (SimdLaneWidth::B8, |rd, rn, index| Inst::DupV16B {
4528 rd,
4529 rn,
4530 index,
4531 }),
4532 "dup.8h" => (SimdLaneWidth::H16, |rd, rn, index| Inst::DupV8H {
4533 rd,
4534 rn,
4535 index,
4536 }),
4537 "dup.4s" => (SimdLaneWidth::S32, |rd, rn, index| Inst::DupV4S {
4538 rd,
4539 rn,
4540 index,
4541 }),
4542 "dup.2d" => (SimdLaneWidth::D64, |rd, rn, index| Inst::DupV2D {
4543 rd,
4544 rn,
4545 index,
4546 }),
4547 _ => unreachable!(),
4548 };
4549 let rd = self.parse_simd_reg()?;
4550 self.expect(&Tok::Comma)?;
4551 let (rn, index) = self.parse_simd_lane_ref(width)?;
4552 Ok(make_inst(rd, rn, index))
4553 }
4554
4555 fn parse_simd_lane_ref(&mut self, width: SimdLaneWidth) -> Result<(FpReg, u8), ParseError> {
4556 let reg = self.parse_simd_reg()?;
4557 self.expect(&Tok::LBracket)?;
4558 let index = self.parse_lane_index(width.max_index())?;
4559 self.expect(&Tok::RBracket)?;
4560 Ok((reg, index))
4561 }
4562
4563 fn parse_lane_index(&mut self, max_index: u8) -> Result<u8, ParseError> {
4564 let token = self.peek().clone();
4565 let value = match token {
4566 Tok::Integer(value) => {
4567 self.advance();
4568 value
4569 }
4570 other => return Err(self.err(format!("expected lane index, got {}", other))),
4571 };
4572 let index = u8::try_from(value)
4573 .map_err(|_| self.err(format!("lane index {} out of range", value)))?;
4574 if index > max_index {
4575 return Err(self.err(format!("lane index {} out of range for lane width", value)));
4576 }
4577 Ok(index)
4578 }
4579
4580 fn parse_fp_unary(&mut self, mnemonic: &str) -> Result<Inst, ParseError> {
4581 let (rd, is_double) = self.parse_fp_reg_with_size()?;
4582 self.expect(&Tok::Comma)?;
4583 let (rn, _) = self.parse_fp_reg_with_size()?;
4584 Ok(match (mnemonic, is_double) {
4585 ("fneg", true) => Inst::FnegD { rd, rn },
4586 ("fneg", false) => Inst::FnegS { rd, rn },
4587 ("fabs", true) => Inst::FabsD { rd, rn },
4588 ("fabs", false) => Inst::FabsS { rd, rn },
4589 ("fsqrt", true) => Inst::FsqrtD { rd, rn },
4590 ("fsqrt", false) => Inst::FsqrtS { rd, rn },
4591 _ => unreachable!(),
4592 })
4593 }
4594
4595 fn parse_fcmp(&mut self) -> Result<Inst, ParseError> {
4596 let (rn, is_double) = self.parse_fp_reg_with_size()?;
4597 self.expect(&Tok::Comma)?;
4598 let (rm, _) = self.parse_fp_reg_with_size()?;
4599 if is_double {
4600 Ok(Inst::FcmpD { rn, rm })
4601 } else {
4602 Ok(Inst::FcmpS { rn, rm })
4603 }
4604 }
4605
4606 fn parse_fcsel(&mut self) -> Result<Inst, ParseError> {
4607 let (rd, is_double) = self.parse_fp_reg_with_size()?;
4608 self.expect(&Tok::Comma)?;
4609 let (rn, _) = self.parse_fp_reg_with_size()?;
4610 self.expect(&Tok::Comma)?;
4611 let (rm, _) = self.parse_fp_reg_with_size()?;
4612 self.expect(&Tok::Comma)?;
4613 let cond_name = self.expect_ident()?;
4614 let cond = parse_condition(&cond_name)
4615 .ok_or_else(|| self.err(format!("unknown condition: {}", cond_name)))?;
4616 if is_double {
4617 Ok(Inst::FcselD { rd, rn, rm, cond })
4618 } else {
4619 Ok(Inst::FcselS { rd, rn, rm, cond })
4620 }
4621 }
4622
4623 fn parse_fmadd(&mut self) -> Result<Inst, ParseError> {
4624 let (rd, is_double) = self.parse_fp_reg_with_size()?;
4625 self.expect(&Tok::Comma)?;
4626 let (rn, _) = self.parse_fp_reg_with_size()?;
4627 self.expect(&Tok::Comma)?;
4628 let (rm, _) = self.parse_fp_reg_with_size()?;
4629 self.expect(&Tok::Comma)?;
4630 let (ra, _) = self.parse_fp_reg_with_size()?;
4631 if is_double {
4632 Ok(Inst::FmaddD { rd, rn, rm, ra })
4633 } else {
4634 Ok(Inst::FmaddS { rd, rn, rm, ra })
4635 }
4636 }
4637
4638 fn parse_fcvtzs(&mut self) -> Result<Inst, ParseError> {
4639 let rd = self.parse_gp_reg()?;
4640 self.expect(&Tok::Comma)?;
4641 let (rn, _) = self.parse_fp_reg_with_size()?;
4642 Ok(Inst::FcvtzsD { rd, rn })
4643 }
4644
4645 fn parse_scvtf(&mut self) -> Result<Inst, ParseError> {
4646 let (rd, _) = self.parse_fp_reg_with_size()?;
4647 self.expect(&Tok::Comma)?;
4648 let rn = self.parse_gp_reg()?;
4649 Ok(Inst::ScvtfD { rd, rn })
4650 }
4651
4652 fn parse_fmov(&mut self) -> Result<Inst, ParseError> {
4653 let name = self.expect_ident()?;
4654 let lower = name.to_lowercase();
4655 self.expect(&Tok::Comma)?;
4656
4657 if lower.starts_with('d') || lower.starts_with('s') {
4658 let rd = parse_fp_reg_name(&lower)
4659 .ok_or_else(|| self.err(format!("bad FP reg '{}'", name)))?;
4660 let is_double = lower.starts_with('d');
4661 match self.peek() {
4662 Tok::Integer(_) | Tok::Float(_) => {
4663 let imm8 = self.parse_fp_modified_immediate(is_double)?;
4664 if is_double {
4665 Ok(Inst::FmovImmD { rd, imm8 })
4666 } else {
4667 Ok(Inst::FmovImmS { rd, imm8 })
4668 }
4669 }
4670 Tok::Ident(src) if looks_like_fp_register_name(src) => {
4671 let (rn, src_is_double) = self.parse_fp_reg_with_size()?;
4672 if src_is_double != is_double {
4673 Err(self.err("fmov register copy requires matching FP widths".into()))
4674 } else if is_double {
4675 Ok(Inst::FmovRegD { rd, rn })
4676 } else {
4677 Ok(Inst::FmovRegS { rd, rn })
4678 }
4679 }
4680 _ => {
4681 // FMOV Dd, Xn or FMOV Sd, Wn (GP → FP)
4682 let (rn, sf, kind) = self.parse_gp_reg_with_size_kind()?;
4683 if kind == GpRegKind::Sp {
4684 return Err(self.err("fmov does not allow SP".into()));
4685 }
4686 if is_double {
4687 if !sf {
4688 return Err(
4689 self.err("fmov dN, ... requires an x-register source".into())
4690 );
4691 }
4692 Ok(Inst::FmovToD { rd, rn })
4693 } else {
4694 if sf {
4695 return Err(
4696 self.err("fmov sN, ... requires a w-register source".into())
4697 );
4698 }
4699 Ok(Inst::FmovToS { rd, rn })
4700 }
4701 }
4702 }
4703 } else {
4704 // FMOV Xd, Dn or FMOV Wd, Sn (FP → GP)
4705 let (rd, sf, kind) = self.gp_reg_with_size_kind_from_name(&name, &lower)?;
4706 if kind == GpRegKind::Sp {
4707 return Err(self.err("fmov does not allow SP".into()));
4708 }
4709 let (rn, is_double) = self.parse_fp_reg_with_size()?;
4710 match (sf, is_double) {
4711 (true, true) => Ok(Inst::FmovFromD { rd, rn }),
4712 (false, false) => Ok(Inst::FmovFromS { rd, rn }),
4713 (true, false) => Err(self.err("fmov xN, ... requires a d-register source".into())),
4714 (false, true) => Err(self.err("fmov wN, ... requires an s-register source".into())),
4715 }
4716 }
4717 }
4718
4719 fn parse_simd_reg(&mut self) -> Result<FpReg, ParseError> {
4720 let name = self.expect_ident()?;
4721 let lower = name.to_lowercase();
4722 parse_simd_reg_name(&lower)
4723 .ok_or_else(|| self.err(format!("expected vector register, got '{}'", name)))
4724 }
4725
4726 fn peek_is_gp_reg(&self) -> bool {
4727 match self.peek() {
4728 Tok::Ident(name) => parse_gp_reg_name(&name.to_lowercase()).is_some(),
4729 _ => false,
4730 }
4731 }
4732
4733 fn peek_is_simd_reg(&self) -> bool {
4734 match self.peek() {
4735 Tok::Ident(name) => parse_simd_reg_name(&name.to_lowercase()).is_some(),
4736 _ => false,
4737 }
4738 }
4739
4740 fn peek_is_scalar_fp_reg(&self) -> bool {
4741 match self.peek() {
4742 Tok::Ident(name) => {
4743 let lower = name.to_lowercase();
4744 (lower.starts_with('s') || lower.starts_with('d'))
4745 && parse_fp_reg_name(&lower).is_some()
4746 }
4747 _ => false,
4748 }
4749 }
4750
4751 fn parse_fp_modified_immediate(&mut self, is_double: bool) -> Result<u8, ParseError> {
4752 let literal = match self.peek().clone() {
4753 Tok::Integer(value) => {
4754 self.advance();
4755 value.to_string()
4756 }
4757 Tok::Float(value) => {
4758 self.advance();
4759 value
4760 }
4761 other => {
4762 return Err(self.err(format!("expected floating-point immediate, got {}", other)))
4763 }
4764 };
4765
4766 if is_double {
4767 encode_fp_modified_immediate64(&literal).ok_or_else(|| {
4768 self.err(format!(
4769 "unsupported floating-point immediate '{}'",
4770 literal
4771 ))
4772 })
4773 } else {
4774 encode_fp_modified_immediate32(&literal).ok_or_else(|| {
4775 self.err(format!(
4776 "unsupported floating-point immediate '{}'",
4777 literal
4778 ))
4779 })
4780 }
4781 }
4782
4783 fn parse_fp_zero_immediate(&mut self, context: &str) -> Result<(), ParseError> {
4784 self.eat(&Tok::Hash);
4785 match self.peek().clone() {
4786 Tok::Integer(0) => {
4787 self.advance();
4788 Ok(())
4789 }
4790 Tok::Float(value) => match value.parse::<f64>() {
4791 Ok(0.0) => {
4792 self.advance();
4793 Ok(())
4794 }
4795 _ => Err(self.err(format!("{} must be #0.0", context))),
4796 },
4797 other => Err(self.err(format!("{} must be #0.0, got {}", context, other))),
4798 }
4799 }
4800
4801 fn parse_barrier_option(&mut self, context: &str) -> Result<BarrierOpt, ParseError> {
4802 let name = self.expect_ident()?.to_lowercase();
4803 match name.as_str() {
4804 "oshld" => Ok(BarrierOpt::Oshld),
4805 "oshst" => Ok(BarrierOpt::Oshst),
4806 "osh" => Ok(BarrierOpt::Osh),
4807 "nshld" => Ok(BarrierOpt::Nshld),
4808 "nshst" => Ok(BarrierOpt::Nshst),
4809 "nsh" => Ok(BarrierOpt::Nsh),
4810 "ishld" => Ok(BarrierOpt::Ishld),
4811 "ishst" => Ok(BarrierOpt::Ishst),
4812 "ish" => Ok(BarrierOpt::Ish),
4813 "ld" => Ok(BarrierOpt::Ld),
4814 "st" => Ok(BarrierOpt::St),
4815 "sy" => Ok(BarrierOpt::Sy),
4816 _ => Err(self.err(format!("unknown {} '{}'", context, name))),
4817 }
4818 }
4819
4820 // ---- Helpers ----
4821
4822 fn parse_optional_lsl12(&mut self) -> Result<bool, ParseError> {
4823 // Check for ", lsl #12" suffix
4824 if self.peek() == &Tok::Comma {
4825 // Peek ahead to see if it's "lsl"
4826 if self.pos + 1 < self.tokens.len() {
4827 if let Tok::Ident(ref s) = self.tokens[self.pos + 1].kind {
4828 if s.to_lowercase() == "lsl" {
4829 self.advance(); // comma
4830 self.advance(); // lsl
4831 let amount = self.parse_immediate_const_expr("lsl amount")?;
4832 if amount == 12 {
4833 return Ok(true);
4834 }
4835 return Err(self.err(format!("expected lsl #12, got lsl #{}", amount)));
4836 }
4837 }
4838 }
4839 }
4840 Ok(false)
4841 }
4842
4843 fn parse_optional_lsl_amount(&mut self) -> Result<u8, ParseError> {
4844 if self.eat(&Tok::Comma) {
4845 let s = self.expect_ident()?;
4846 if s.to_lowercase() != "lsl" {
4847 return Err(self.err(format!("expected 'lsl', got '{}'", s)));
4848 }
4849 let amount = self.parse_immediate_const_expr("lsl amount")? as u8;
4850 Ok(amount)
4851 } else {
4852 Ok(0)
4853 }
4854 }
4855
4856 fn parse_bit_index(&mut self, sf: bool, context: &str) -> Result<u8, ParseError> {
4857 let bit = self.parse_immediate_const_expr(context)?;
4858 let max = if sf { 63 } else { 31 };
4859 if !(0..=max).contains(&bit) {
4860 return Err(self.err(format!(
4861 "{} must be in the range 0..={} for this register width",
4862 context, max
4863 )));
4864 }
4865 Ok(bit as u8)
4866 }
4867
4868 fn parse_optional_add_sub_modifier(
4869 &mut self,
4870 sf: bool,
4871 rm_is_64bit: bool,
4872 ) -> Result<Option<AddSubModifier>, ParseError> {
4873 if !self.eat(&Tok::Comma) {
4874 return Ok(None);
4875 }
4876
4877 let name = self.expect_ident()?.to_lowercase();
4878 match name.as_str() {
4879 "lsl" | "lsr" | "asr" => {
4880 let shift = match name.as_str() {
4881 "lsl" => RegShift::Lsl,
4882 "lsr" => RegShift::Lsr,
4883 "asr" => RegShift::Asr,
4884 _ => unreachable!(),
4885 };
4886 let amount = self.parse_immediate_const_expr("shift amount")?;
4887 let max = if sf { 63 } else { 31 };
4888 if !(0..=max).contains(&amount) {
4889 return Err(self.err(format!(
4890 "shift amount must be in the range 0..={} for this register width",
4891 max
4892 )));
4893 }
4894 Ok(Some(AddSubModifier::Shift(shift, amount as u8)))
4895 }
4896 "uxtb" | "uxth" | "uxtw" | "uxtx" | "sxtb" | "sxth" | "sxtw" | "sxtx" => {
4897 let extend = match name.as_str() {
4898 "uxtb" => {
4899 if rm_is_64bit {
4900 return Err(self.err(
4901 "uxtb add/sub extensions require a w-register operand".into(),
4902 ));
4903 }
4904 RegExtend::Uxtb
4905 }
4906 "uxth" => {
4907 if rm_is_64bit {
4908 return Err(self.err(
4909 "uxth add/sub extensions require a w-register operand".into(),
4910 ));
4911 }
4912 RegExtend::Uxth
4913 }
4914 "uxtw" => {
4915 if rm_is_64bit {
4916 return Err(self.err(
4917 "uxtw add/sub extensions require a w-register operand".into(),
4918 ));
4919 }
4920 RegExtend::Uxtw
4921 }
4922 "uxtx" => {
4923 if !rm_is_64bit {
4924 return Err(self.err(
4925 "uxtx add/sub extensions require an x-register operand".into(),
4926 ));
4927 }
4928 RegExtend::Uxtx
4929 }
4930 "sxtb" => {
4931 if rm_is_64bit {
4932 return Err(self.err(
4933 "sxtb add/sub extensions require a w-register operand".into(),
4934 ));
4935 }
4936 RegExtend::Sxtb
4937 }
4938 "sxth" => {
4939 if rm_is_64bit {
4940 return Err(self.err(
4941 "sxth add/sub extensions require a w-register operand".into(),
4942 ));
4943 }
4944 RegExtend::Sxth
4945 }
4946 "sxtw" => {
4947 if rm_is_64bit {
4948 return Err(self.err(
4949 "sxtw add/sub extensions require a w-register operand".into(),
4950 ));
4951 }
4952 RegExtend::Sxtw
4953 }
4954 "sxtx" => {
4955 if !rm_is_64bit {
4956 return Err(self.err(
4957 "sxtx add/sub extensions require an x-register operand".into(),
4958 ));
4959 }
4960 RegExtend::Sxtx
4961 }
4962 _ => unreachable!(),
4963 };
4964 let amount = if self.starts_immediate_expr() {
4965 self.parse_immediate_const_expr("extend shift amount")?
4966 } else {
4967 0
4968 };
4969 if !(0..=4).contains(&amount) {
4970 return Err(self.err("extend shift amount must be in the range 0..=4".into()));
4971 }
4972 Ok(Some(AddSubModifier::Extend(extend, amount as u8)))
4973 }
4974 _ => Err(self.err(format!(
4975 "expected add/sub modifier (lsl/lsr/asr/uxtb/uxth/uxtw/uxtx/sxtb/sxth/sxtw/sxtx), got '{}'",
4976 name
4977 ))),
4978 }
4979 }
4980
4981 fn validate_add_sub_extended_base_reg(
4982 &self,
4983 rn_kind: GpRegKind,
4984 modifier: Option<AddSubModifier>,
4985 ) -> Result<(), ParseError> {
4986 if matches!(modifier, Some(AddSubModifier::Extend(..))) && rn_kind == GpRegKind::Zr {
4987 return Err(self.err(
4988 "extended add/sub forms require an x-register or sp base operand, not xzr/wzr"
4989 .into(),
4990 ));
4991 }
4992 Ok(())
4993 }
4994
4995 fn validate_bitfield_alias_args(
4996 &self,
4997 mnemonic: &str,
4998 sf: bool,
4999 lsb: u8,
5000 width: u8,
5001 ) -> Result<(), ParseError> {
5002 let bits = if sf { 64u8 } else { 32u8 };
5003 if width == 0 {
5004 return Err(self.err(format!("{} width must be at least 1", mnemonic)));
5005 }
5006 if lsb >= bits {
5007 return Err(self.err(format!(
5008 "{} lsb {} is out of range for {}-bit register",
5009 mnemonic, lsb, bits
5010 )));
5011 }
5012 if width > bits - lsb {
5013 return Err(self.err(format!(
5014 "{} width {} with lsb {} exceeds {}-bit register width",
5015 mnemonic, width, lsb, bits
5016 )));
5017 }
5018 Ok(())
5019 }
5020
5021 fn starts_register_like_operand(&self) -> bool {
5022 matches!(self.peek(), Tok::Ident(name) if looks_like_gp_register_name(name))
5023 }
5024
5025 fn starts_fp_register_like_operand(&self) -> bool {
5026 matches!(self.peek(), Tok::Ident(name) if looks_like_fp_register_name(name))
5027 }
5028
5029 fn parse_reg_offset_operand(
5030 &mut self,
5031 scale: u8,
5032 ) -> Result<(GpReg, AddrExtend, bool), ParseError> {
5033 let (rm, is_64bit) = self.parse_gp_reg_with_size()?;
5034 let mut extend = AddrExtend::Lsl;
5035 let mut shift = false;
5036
5037 if self.eat(&Tok::Comma) {
5038 let modifier = self.expect_ident()?.to_lowercase();
5039 extend = match modifier.as_str() {
5040 "lsl" if is_64bit => AddrExtend::Lsl,
5041 "uxtw" if !is_64bit => AddrExtend::Uxtw,
5042 "sxtw" if !is_64bit => AddrExtend::Sxtw,
5043 "sxtx" if is_64bit => AddrExtend::Sxtx,
5044 "lsl" => {
5045 return Err(self.err("lsl register offsets require an x-register index".into()));
5046 }
5047 "uxtw" | "sxtw" => {
5048 return Err(self.err(format!(
5049 "{} register offsets require a w-register index",
5050 modifier
5051 )));
5052 }
5053 "sxtx" => {
5054 return Err(
5055 self.err("sxtx register offsets require an x-register index".into())
5056 );
5057 }
5058 _ => {
5059 return Err(self.err(format!(
5060 "unsupported register offset modifier '{}'",
5061 modifier
5062 )));
5063 }
5064 };
5065
5066 let amount = if self.starts_immediate_expr() {
5067 self.parse_immediate_const_expr("register offset shift")? as u8
5068 } else {
5069 0
5070 };
5071 shift = parse_index_shift(amount, scale)
5072 .map_err(|msg| self.err(format!("{} for {}", msg, modifier)))?;
5073 } else if !is_64bit {
5074 return Err(self
5075 .err("32-bit register offsets require an explicit uxtw or sxtw modifier".into()));
5076 }
5077
5078 Ok((rm, extend, shift))
5079 }
5080 }
5081
5082 // ---- Name resolution helpers ----
5083
5084 fn numeric_label_symbol(number: u32, ordinal: u32) -> String {
5085 format!(".Ltmp${number}${ordinal}")
5086 }
5087
5088 fn decimal_width(mut value: u32) -> u32 {
5089 let mut width = 1;
5090 while value >= 10 {
5091 value /= 10;
5092 width += 1;
5093 }
5094 width
5095 }
5096
5097 fn parse_gp_reg_name(name: &str) -> Option<GpReg> {
5098 let lower = name.to_lowercase();
5099 match lower.as_str() {
5100 "sp" => Some(SP),
5101 "xzr" | "wzr" => Some(XZR),
5102 _ => {
5103 let (prefix, num_str) = if lower.starts_with('x') || lower.starts_with('w') {
5104 (&lower[..1], &lower[1..])
5105 } else {
5106 return None;
5107 };
5108 let num: u8 = num_str.parse().ok()?;
5109 if num > 30 {
5110 return None;
5111 }
5112 let _ = prefix; // both x and w map to the same encoding
5113 Some(GpReg::new(num))
5114 }
5115 }
5116 }
5117
5118 fn looks_like_gp_register_name(name: &str) -> bool {
5119 let lower = name.to_lowercase();
5120 lower == "sp"
5121 || lower == "xzr"
5122 || lower == "wzr"
5123 || lower.starts_with('x')
5124 || lower.starts_with('w')
5125 }
5126
5127 fn parse_fp_reg_name(name: &str) -> Option<FpReg> {
5128 let lower = name.to_lowercase();
5129 let (prefix, num_str) = if lower.starts_with('b')
5130 || lower.starts_with('h')
5131 || lower.starts_with('d')
5132 || lower.starts_with('s')
5133 || lower.starts_with('q')
5134 {
5135 (&lower[..1], &lower[1..])
5136 } else {
5137 return None;
5138 };
5139 let num: u8 = num_str.parse().ok()?;
5140 if num > 31 {
5141 return None;
5142 }
5143 let _ = prefix;
5144 Some(FpReg::new(num))
5145 }
5146
5147 fn parse_simd_reg_name(name: &str) -> Option<FpReg> {
5148 let lower = name.to_lowercase();
5149 let num_str = lower.strip_prefix('v')?;
5150 let num: u8 = num_str.parse().ok()?;
5151 if num > 31 {
5152 return None;
5153 }
5154 Some(FpReg::new(num))
5155 }
5156
5157 fn looks_like_fp_register_name(name: &str) -> bool {
5158 parse_fp_reg_name(name).is_some()
5159 }
5160
5161 fn parse_condition(s: &str) -> Option<Cond> {
5162 match s.to_lowercase().as_str() {
5163 "eq" => Some(Cond::EQ),
5164 "ne" => Some(Cond::NE),
5165 "cs" | "hs" => Some(Cond::CS),
5166 "cc" | "lo" => Some(Cond::CC),
5167 "mi" => Some(Cond::MI),
5168 "pl" => Some(Cond::PL),
5169 "vs" => Some(Cond::VS),
5170 "vc" => Some(Cond::VC),
5171 "hi" => Some(Cond::HI),
5172 "ls" => Some(Cond::LS),
5173 "ge" => Some(Cond::GE),
5174 "lt" => Some(Cond::LT),
5175 "gt" => Some(Cond::GT),
5176 "le" => Some(Cond::LE),
5177 "al" => Some(Cond::AL),
5178 _ => None,
5179 }
5180 }
5181
5182 fn invert_condition(cond: Cond) -> Cond {
5183 match cond {
5184 Cond::EQ => Cond::NE,
5185 Cond::NE => Cond::EQ,
5186 Cond::CS => Cond::CC,
5187 Cond::CC => Cond::CS,
5188 Cond::MI => Cond::PL,
5189 Cond::PL => Cond::MI,
5190 Cond::VS => Cond::VC,
5191 Cond::VC => Cond::VS,
5192 Cond::HI => Cond::LS,
5193 Cond::LS => Cond::HI,
5194 Cond::GE => Cond::LT,
5195 Cond::LT => Cond::GE,
5196 Cond::GT => Cond::LE,
5197 Cond::LE => Cond::GT,
5198 Cond::AL => Cond::NV,
5199 Cond::NV => Cond::AL,
5200 }
5201 }
5202
5203 fn encode_fp_modified_immediate32(literal: &str) -> Option<u8> {
5204 let value: f32 = literal.parse().ok()?;
5205 let bits = value.to_bits();
5206 (0u8..=u8::MAX).find(|imm8| expand_fp_modified_immediate(*imm8, false) as u32 == bits)
5207 }
5208
5209 fn encode_fp_modified_immediate64(literal: &str) -> Option<u8> {
5210 let value: f64 = literal.parse().ok()?;
5211 let bits = value.to_bits();
5212 (0u8..=u8::MAX).find(|imm8| expand_fp_modified_immediate(*imm8, true) == bits)
5213 }
5214
5215 fn expand_fp_modified_immediate(imm8: u8, is_double: bool) -> u64 {
5216 let exponent_bits = if is_double { 11 } else { 8 };
5217 let fraction_bits = if is_double { 52 } else { 23 };
5218 let sign = ((imm8 >> 7) & 1) as u64;
5219 let bit6 = ((imm8 >> 6) & 1) as u64;
5220 let low_exponent = ((imm8 >> 4) & 0b11) as u64;
5221 let repeated_len = exponent_bits - 3;
5222 let repeated = if bit6 == 0 {
5223 0
5224 } else {
5225 ((1u64 << repeated_len) - 1) << 2
5226 };
5227 let exponent = (((bit6 ^ 1) & 1) << (exponent_bits - 1)) | repeated | low_exponent;
5228 let fraction = ((imm8 & 0xF) as u64) << (fraction_bits - 4);
5229 (sign << (exponent_bits + fraction_bits)) | (exponent << fraction_bits) | fraction
5230 }
5231
5232 fn parse_index_shift(amount: u8, scale: u8) -> Result<bool, &'static str> {
5233 match amount {
5234 0 => Ok(false),
5235 value if value == scale => Ok(true),
5236 _ => Err("register offset shift must be omitted, #0, or the element scale"),
5237 }
5238 }
5239
5240 fn logical_immediate_encodable(imm: u64, width: u8) -> bool {
5241 let mask = if width == 64 {
5242 u64::MAX
5243 } else {
5244 (1u64 << width) - 1
5245 };
5246 let imm = imm & mask;
5247 if imm == 0 || imm == mask {
5248 return false;
5249 }
5250
5251 for esize in [2u8, 4, 8, 16, 32, 64] {
5252 if esize > width {
5253 continue;
5254 }
5255 for ones in 1..esize {
5256 let base = if ones == 64 {
5257 u64::MAX
5258 } else {
5259 (1u64 << ones) - 1
5260 };
5261 for rot in 0..esize {
5262 let pattern = rotate_right_for_logical_immediate(base, rot, esize);
5263 let candidate = replicate_logical_immediate_pattern(pattern, esize, width);
5264 if candidate == imm {
5265 return true;
5266 }
5267 }
5268 }
5269 }
5270
5271 false
5272 }
5273
5274 fn rotate_right_for_logical_immediate(value: u64, rot: u8, width: u8) -> u64 {
5275 let mask = if width == 64 {
5276 u64::MAX
5277 } else {
5278 (1u64 << width) - 1
5279 };
5280 let value = value & mask;
5281 let rot = rot % width;
5282 if rot == 0 {
5283 value
5284 } else {
5285 ((value >> rot) | (value << (width - rot))) & mask
5286 }
5287 }
5288
5289 fn replicate_logical_immediate_pattern(pattern: u64, esize: u8, width: u8) -> u64 {
5290 let mut out = 0u64;
5291 let mut shift = 0u8;
5292 while shift < width {
5293 out |= pattern << shift;
5294 shift += esize;
5295 }
5296 if width == 64 {
5297 out
5298 } else {
5299 out & ((1u64 << width) - 1)
5300 }
5301 }
5302
5303 fn mov_alias_imm(rd: GpReg, imm: i64, sf: bool) -> Option<Inst> {
5304 let mask = if sf { u64::MAX } else { u32::MAX as u64 };
5305 let shifts: &[u8] = if sf { &[0, 16, 32, 48] } else { &[0, 16] };
5306 let value = (imm as u64) & mask;
5307 let inverted = (!value) & mask;
5308
5309 for &shift in shifts {
5310 let shift_bits = shift as u32;
5311 let movz_imm = ((value >> shift_bits) & 0xFFFF) as u16;
5312 if value == ((movz_imm as u64) << shift_bits) {
5313 return Some(Inst::Movz {
5314 rd,
5315 imm16: movz_imm,
5316 shift,
5317 sf,
5318 });
5319 }
5320
5321 let movn_imm = ((inverted >> shift_bits) & 0xFFFF) as u16;
5322 if inverted == ((movn_imm as u64) << shift_bits) {
5323 return Some(Inst::Movn {
5324 rd,
5325 imm16: movn_imm,
5326 shift,
5327 sf,
5328 });
5329 }
5330 }
5331
5332 None
5333 }
5334
5335 #[cfg(test)]
5336 mod tests {
5337 use super::*;
5338
5339 fn parse_inst(src: &str) -> Inst {
5340 let stmts = parse(src).unwrap();
5341 stmts
5342 .into_iter()
5343 .find_map(|s| {
5344 if let Stmt::Instruction(i) = s {
5345 Some(i)
5346 } else {
5347 None
5348 }
5349 })
5350 .unwrap()
5351 }
5352
5353 fn parse_stmts(src: &str) -> Vec<Stmt> {
5354 parse(src).unwrap()
5355 }
5356
5357 fn parse_err(src: &str) -> String {
5358 parse(src).unwrap_err().to_string()
5359 }
5360
5361 // ---- Data processing ----
5362
5363 #[test]
5364 fn parse_add_reg() {
5365 assert_eq!(
5366 parse_inst("add x0, x1, x2"),
5367 Inst::AddReg {
5368 rd: X0,
5369 rn: X1,
5370 rm: X2,
5371 sf: true
5372 }
5373 );
5374 }
5375
5376 #[test]
5377 fn parse_add_w_reg() {
5378 assert_eq!(
5379 parse_inst("add w3, w4, w5"),
5380 Inst::AddReg {
5381 rd: W3,
5382 rn: W4,
5383 rm: W5,
5384 sf: false
5385 }
5386 );
5387 }
5388
5389 #[test]
5390 fn parse_sub_imm() {
5391 assert_eq!(
5392 parse_inst("sub x0, x1, #42"),
5393 Inst::SubImm {
5394 rd: X0,
5395 rn: X1,
5396 imm12: 42,
5397 shift: false,
5398 sf: true
5399 }
5400 );
5401 }
5402
5403 #[test]
5404 fn parse_add_imm_lsl12() {
5405 assert_eq!(
5406 parse_inst("add x0, x1, #42, lsl #12"),
5407 Inst::AddImm {
5408 rd: X0,
5409 rn: X1,
5410 imm12: 42,
5411 shift: true,
5412 sf: true
5413 }
5414 );
5415 }
5416
5417 #[test]
5418 fn parse_add_immediate_expression() {
5419 assert_eq!(
5420 parse_inst("add x0, x1, #1 + 2"),
5421 Inst::AddImm {
5422 rd: X0,
5423 rn: X1,
5424 imm12: 3,
5425 shift: false,
5426 sf: true
5427 }
5428 );
5429 }
5430
5431 #[test]
5432 fn parse_add_shifted_reg() {
5433 assert_eq!(
5434 parse_inst("add x0, x1, x2, lsl #3"),
5435 Inst::AddShiftReg {
5436 rd: X0,
5437 rn: X1,
5438 rm: X2,
5439 shift: RegShift::Lsl,
5440 amount: 3,
5441 sf: true
5442 }
5443 );
5444 }
5445
5446 #[test]
5447 fn parse_sub_shifted_reg() {
5448 assert_eq!(
5449 parse_inst("sub w3, w4, w5, asr #7"),
5450 Inst::SubShiftReg {
5451 rd: W3,
5452 rn: W4,
5453 rm: W5,
5454 shift: RegShift::Asr,
5455 amount: 7,
5456 sf: false
5457 }
5458 );
5459 }
5460
5461 #[test]
5462 fn parse_add_extended_reg() {
5463 assert_eq!(
5464 parse_inst("add x0, x0, w1, sxtw #3"),
5465 Inst::AddExtReg {
5466 rd: X0,
5467 rn: X0,
5468 rm: W1,
5469 extend: RegExtend::Sxtw,
5470 amount: 3,
5471 sf: true
5472 }
5473 );
5474 }
5475
5476 #[test]
5477 fn parse_add_extended_reg_with_sp_base() {
5478 assert_eq!(
5479 parse_inst("add x11, sp, w12, sxtw #2"),
5480 Inst::AddExtReg {
5481 rd: X11,
5482 rn: SP,
5483 rm: W12,
5484 extend: RegExtend::Sxtw,
5485 amount: 2,
5486 sf: true
5487 }
5488 );
5489 }
5490
5491 #[test]
5492 fn parse_sub_extended_reg() {
5493 assert_eq!(
5494 parse_inst("sub x2, x3, w4, uxtw #2"),
5495 Inst::SubExtReg {
5496 rd: X2,
5497 rn: X3,
5498 rm: W4,
5499 extend: RegExtend::Uxtw,
5500 amount: 2,
5501 sf: true
5502 }
5503 );
5504 }
5505
5506 #[test]
5507 fn parse_subs_extended_reg_uxtb() {
5508 assert_eq!(
5509 parse_inst("subs w10, w8, w9, uxtb"),
5510 Inst::SubsExtReg {
5511 rd: W10,
5512 rn: W8,
5513 rm: W9,
5514 extend: RegExtend::Uxtb,
5515 amount: 0,
5516 sf: false
5517 }
5518 );
5519 }
5520
5521 #[test]
5522 fn parse_subs_extended_reg_uxth() {
5523 assert_eq!(
5524 parse_inst("subs w11, w12, w13, uxth"),
5525 Inst::SubsExtReg {
5526 rd: W11,
5527 rn: W12,
5528 rm: W13,
5529 extend: RegExtend::Uxth,
5530 amount: 0,
5531 sf: false
5532 }
5533 );
5534 }
5535
5536 #[test]
5537 fn parse_subs_extended_reg_sxtb() {
5538 assert_eq!(
5539 parse_inst("subs x14, x15, w16, sxtb"),
5540 Inst::SubsExtReg {
5541 rd: X14,
5542 rn: X15,
5543 rm: W16,
5544 extend: RegExtend::Sxtb,
5545 amount: 0,
5546 sf: true
5547 }
5548 );
5549 }
5550
5551 #[test]
5552 fn parse_subs_extended_reg_sxth() {
5553 assert_eq!(
5554 parse_inst("subs x17, x18, w19, sxth #1"),
5555 Inst::SubsExtReg {
5556 rd: X17,
5557 rn: X18,
5558 rm: W19,
5559 extend: RegExtend::Sxth,
5560 amount: 1,
5561 sf: true
5562 }
5563 );
5564 }
5565
5566 #[test]
5567 fn parse_cmp_reg() {
5568 assert_eq!(
5569 parse_inst("cmp x0, x1"),
5570 Inst::SubsReg {
5571 rd: XZR,
5572 rn: X0,
5573 rm: X1,
5574 sf: true
5575 }
5576 );
5577 }
5578
5579 #[test]
5580 fn parse_cmp_shifted_reg() {
5581 assert_eq!(
5582 parse_inst("cmp x6, x7, lsr #4"),
5583 Inst::SubsShiftReg {
5584 rd: XZR,
5585 rn: X6,
5586 rm: X7,
5587 shift: RegShift::Lsr,
5588 amount: 4,
5589 sf: true
5590 }
5591 );
5592 }
5593
5594 #[test]
5595 fn parse_cmp_extended_reg() {
5596 assert_eq!(
5597 parse_inst("cmp x0, w1, sxtw"),
5598 Inst::SubsExtReg {
5599 rd: XZR,
5600 rn: X0,
5601 rm: W1,
5602 extend: RegExtend::Sxtw,
5603 amount: 0,
5604 sf: true
5605 }
5606 );
5607 }
5608
5609 #[test]
5610 fn parse_cmp_imm() {
5611 assert_eq!(
5612 parse_inst("cmp x5, #255"),
5613 Inst::SubsImm {
5614 rd: XZR,
5615 rn: X5,
5616 imm12: 255,
5617 shift: false,
5618 sf: true
5619 }
5620 );
5621 }
5622
5623 #[test]
5624 fn parse_cmn_shifted_reg() {
5625 assert_eq!(
5626 parse_inst("cmn x8, x9, lsl #1"),
5627 Inst::AddsShiftReg {
5628 rd: XZR,
5629 rn: X8,
5630 rm: X9,
5631 shift: RegShift::Lsl,
5632 amount: 1,
5633 sf: true
5634 }
5635 );
5636 }
5637
5638 #[test]
5639 fn parse_cmn_extended_reg() {
5640 assert_eq!(
5641 parse_inst("cmn x6, w7, sxtw #3"),
5642 Inst::AddsExtReg {
5643 rd: XZR,
5644 rn: X6,
5645 rm: W7,
5646 extend: RegExtend::Sxtw,
5647 amount: 3,
5648 sf: true
5649 }
5650 );
5651 }
5652
5653 #[test]
5654 fn parse_tst_() {
5655 assert_eq!(
5656 parse_inst("tst x0, x1"),
5657 Inst::AndsReg {
5658 rd: XZR,
5659 rn: X0,
5660 rm: X1,
5661 sf: true
5662 }
5663 );
5664 }
5665
5666 #[test]
5667 fn parse_tst_imm() {
5668 assert_eq!(
5669 parse_inst("tst w8, #0x7"),
5670 Inst::AndsImm {
5671 rd: XZR,
5672 rn: W8,
5673 imm: 0x7,
5674 sf: false
5675 }
5676 );
5677 }
5678
5679 #[test]
5680 fn parse_mul_() {
5681 assert_eq!(
5682 parse_inst("mul x6, x7, x8"),
5683 Inst::Mul {
5684 rd: X6,
5685 rn: X7,
5686 rm: X8,
5687 sf: true
5688 }
5689 );
5690 }
5691
5692 #[test]
5693 fn parse_madd_() {
5694 assert_eq!(
5695 parse_inst("madd w0, w0, w0, w8"),
5696 Inst::Madd {
5697 rd: W0,
5698 rn: W0,
5699 rm: W0,
5700 ra: W8,
5701 sf: false
5702 }
5703 );
5704 }
5705
5706 #[test]
5707 fn parse_msub_() {
5708 assert_eq!(
5709 parse_inst("msub w9, w8, w1, w0"),
5710 Inst::Msub {
5711 rd: W9,
5712 rn: W8,
5713 rm: W1,
5714 ra: W0,
5715 sf: false
5716 }
5717 );
5718 }
5719
5720 #[test]
5721 fn parse_umull_() {
5722 assert_eq!(
5723 parse_inst("umull x9, w8, w9"),
5724 Inst::Umull {
5725 rd: X9,
5726 rn: W8,
5727 rm: W9
5728 }
5729 );
5730 }
5731
5732 #[test]
5733 fn parse_and_() {
5734 assert_eq!(
5735 parse_inst("and x3, x4, x5"),
5736 Inst::AndReg {
5737 rd: X3,
5738 rn: X4,
5739 rm: X5,
5740 sf: true
5741 }
5742 );
5743 }
5744
5745 #[test]
5746 fn parse_and_imm() {
5747 assert_eq!(
5748 parse_inst("and w8, w8, #0x7"),
5749 Inst::AndImm {
5750 rd: W8,
5751 rn: W8,
5752 imm: 0x7,
5753 sf: false
5754 }
5755 );
5756 }
5757
5758 // ---- Move ----
5759
5760 #[test]
5761 fn parse_mov_imm() {
5762 assert_eq!(
5763 parse_inst("mov x0, #42"),
5764 Inst::Movz {
5765 rd: X0,
5766 imm16: 42,
5767 shift: 0,
5768 sf: true
5769 }
5770 );
5771 }
5772
5773 #[test]
5774 fn parse_mov_reg() {
5775 assert_eq!(
5776 parse_inst("mov x0, x1"),
5777 Inst::OrrReg {
5778 rd: X0,
5779 rn: XZR,
5780 rm: X1,
5781 sf: true
5782 }
5783 );
5784 }
5785
5786 #[test]
5787 fn parse_mov_wzr_keeps_zero_register() {
5788 assert_eq!(
5789 parse_inst("mov w26, wzr"),
5790 Inst::OrrReg {
5791 rd: W26,
5792 rn: WZR,
5793 rm: WZR,
5794 sf: false
5795 }
5796 );
5797 }
5798
5799 #[test]
5800 fn parse_ubfiz_() {
5801 assert_eq!(
5802 parse_inst("ubfiz w8, w0, #5, #3"),
5803 Inst::Ubfiz {
5804 rd: W8,
5805 rn: W0,
5806 lsb: 5,
5807 width: 3,
5808 sf: false
5809 }
5810 );
5811 }
5812
5813 #[test]
5814 fn parse_bfi_() {
5815 assert_eq!(
5816 parse_inst("bfi w0, w8, #5, #27"),
5817 Inst::Bfi {
5818 rd: W0,
5819 rn: W8,
5820 lsb: 5,
5821 width: 27,
5822 sf: false
5823 }
5824 );
5825 }
5826
5827 #[test]
5828 fn parse_bfxil_() {
5829 assert_eq!(
5830 parse_inst("bfxil w8, w0, #3, #5"),
5831 Inst::Bfxil {
5832 rd: W8,
5833 rn: W0,
5834 lsb: 3,
5835 width: 5,
5836 sf: false
5837 }
5838 );
5839 }
5840
5841 #[test]
5842 fn parse_neg_alias() {
5843 assert_eq!(
5844 parse_inst("neg x0, x1"),
5845 Inst::SubReg {
5846 rd: X0,
5847 rn: XZR,
5848 rm: X1,
5849 sf: true
5850 }
5851 );
5852 }
5853
5854 #[test]
5855 fn parse_neg_shift_alias() {
5856 assert_eq!(
5857 parse_inst("neg x0, x1, lsl #2"),
5858 Inst::SubShiftReg {
5859 rd: X0,
5860 rn: XZR,
5861 rm: X1,
5862 shift: RegShift::Lsl,
5863 amount: 2,
5864 sf: true
5865 }
5866 );
5867 }
5868
5869 #[test]
5870 fn error_neg_extend_alias_rejects_zero_base_register() {
5871 let err = parse_err("neg x8, w9, sxtw #2");
5872 assert!(
5873 err.contains("x-register or sp base operand"),
5874 "got: {}",
5875 err
5876 );
5877 }
5878
5879 #[test]
5880 fn parse_mvn_alias() {
5881 assert_eq!(
5882 parse_inst("mvn x0, x1"),
5883 Inst::OrnReg {
5884 rd: X0,
5885 rn: XZR,
5886 rm: X1,
5887 sf: true
5888 }
5889 );
5890 }
5891
5892 #[test]
5893 fn parse_cset_alias() {
5894 assert_eq!(
5895 parse_inst("cset x0, eq"),
5896 Inst::Csinc {
5897 rd: X0,
5898 rn: XZR,
5899 rm: XZR,
5900 cond: Cond::NE,
5901 sf: true
5902 }
5903 );
5904 }
5905
5906 #[test]
5907 fn parse_csel() {
5908 assert_eq!(
5909 parse_inst("csel w0, w0, w1, gt"),
5910 Inst::Csel {
5911 rd: W0,
5912 rn: W0,
5913 rm: W1,
5914 cond: Cond::GT,
5915 sf: false
5916 }
5917 );
5918 }
5919
5920 #[test]
5921 fn parse_csinc() {
5922 assert_eq!(
5923 parse_inst("csinc x2, x3, x4, ne"),
5924 Inst::Csinc {
5925 rd: X2,
5926 rn: X3,
5927 rm: X4,
5928 cond: Cond::NE,
5929 sf: true
5930 }
5931 );
5932 }
5933
5934 #[test]
5935 fn parse_csinv() {
5936 assert_eq!(
5937 parse_inst("csinv x2, x3, x4, ne"),
5938 Inst::Csinv {
5939 rd: X2,
5940 rn: X3,
5941 rm: X4,
5942 cond: Cond::NE,
5943 sf: true
5944 }
5945 );
5946 }
5947
5948 #[test]
5949 fn parse_ccmp() {
5950 assert_eq!(
5951 parse_inst("ccmp w0, #3, #4, ne"),
5952 Inst::CcmpImm {
5953 rn: W0,
5954 imm5: 3,
5955 nzcv: 4,
5956 cond: Cond::NE,
5957 sf: false
5958 }
5959 );
5960 }
5961
5962 #[test]
5963 fn parse_ccmn() {
5964 assert_eq!(
5965 parse_inst("ccmn x3, #9, #1, ge"),
5966 Inst::CcmnImm {
5967 rn: X3,
5968 imm5: 9,
5969 nzcv: 1,
5970 cond: Cond::GE,
5971 sf: true
5972 }
5973 );
5974 }
5975
5976 #[test]
5977 fn parse_csneg() {
5978 assert_eq!(
5979 parse_inst("csneg x5, x6, x7, gt"),
5980 Inst::Csneg {
5981 rd: X5,
5982 rn: X6,
5983 rm: X7,
5984 cond: Cond::GT,
5985 sf: true
5986 }
5987 );
5988 }
5989
5990 #[test]
5991 fn parse_cinc_alias() {
5992 assert_eq!(
5993 parse_inst("cinc w2, w3, ne"),
5994 Inst::Csinc {
5995 rd: W2,
5996 rn: W3,
5997 rm: W3,
5998 cond: Cond::EQ,
5999 sf: false
6000 }
6001 );
6002 }
6003
6004 #[test]
6005 fn parse_csetm_alias() {
6006 assert_eq!(
6007 parse_inst("csetm w8, eq"),
6008 Inst::Csinv {
6009 rd: W8,
6010 rn: XZR,
6011 rm: XZR,
6012 cond: Cond::NE,
6013 sf: false
6014 }
6015 );
6016 }
6017
6018 #[test]
6019 fn parse_cinv_alias() {
6020 assert_eq!(
6021 parse_inst("cinv w9, w10, mi"),
6022 Inst::Csinv {
6023 rd: W9,
6024 rn: W10,
6025 rm: W10,
6026 cond: Cond::PL,
6027 sf: false
6028 }
6029 );
6030 }
6031
6032 #[test]
6033 fn parse_cneg_alias() {
6034 assert_eq!(
6035 parse_inst("cneg x11, x12, lt"),
6036 Inst::Csneg {
6037 rd: X11,
6038 rn: X12,
6039 rm: X12,
6040 cond: Cond::GE,
6041 sf: true
6042 }
6043 );
6044 }
6045
6046 #[test]
6047 fn parse_movz_shift() {
6048 assert_eq!(
6049 parse_inst("movz x0, #0x1234, lsl #16"),
6050 Inst::Movz {
6051 rd: X0,
6052 imm16: 0x1234,
6053 shift: 16,
6054 sf: true
6055 }
6056 );
6057 }
6058
6059 // ---- Shifts ----
6060
6061 #[test]
6062 fn parse_lsl_() {
6063 assert_eq!(
6064 parse_inst("lsl x0, x1, #3"),
6065 Inst::LslImm {
6066 rd: X0,
6067 rn: X1,
6068 amount: 3,
6069 sf: true
6070 }
6071 );
6072 }
6073
6074 // ---- Branches ----
6075
6076 #[test]
6077 fn parse_b_() {
6078 assert_eq!(parse_inst("b #20"), Inst::B { offset: 20 });
6079 }
6080
6081 #[test]
6082 fn parse_bl_() {
6083 assert_eq!(parse_inst("bl #40"), Inst::Bl { offset: 40 });
6084 }
6085
6086 #[test]
6087 fn parse_b_eq() {
6088 assert_eq!(
6089 parse_inst("b.eq #8"),
6090 Inst::BCond {
6091 cond: Cond::EQ,
6092 offset: 8
6093 }
6094 );
6095 }
6096
6097 #[test]
6098 fn parse_b_ne() {
6099 assert_eq!(
6100 parse_inst("b.ne #12"),
6101 Inst::BCond {
6102 cond: Cond::NE,
6103 offset: 12
6104 }
6105 );
6106 }
6107
6108 #[test]
6109 fn parse_b_ge() {
6110 assert_eq!(
6111 parse_inst("b.ge #16"),
6112 Inst::BCond {
6113 cond: Cond::GE,
6114 offset: 16
6115 }
6116 );
6117 }
6118
6119 #[test]
6120 fn parse_tbz_() {
6121 assert_eq!(
6122 parse_inst("tbz x0, #5, #8"),
6123 Inst::Tbz {
6124 rt: X0,
6125 bit: 5,
6126 offset: 8,
6127 sf: true
6128 }
6129 );
6130 }
6131
6132 #[test]
6133 fn parse_tbnz_() {
6134 assert_eq!(
6135 parse_inst("tbnz w1, #31, #12"),
6136 Inst::Tbnz {
6137 rt: W1,
6138 bit: 31,
6139 offset: 12,
6140 sf: false
6141 }
6142 );
6143 }
6144
6145 #[test]
6146 fn parse_tbz_label() {
6147 assert_eq!(
6148 parse_stmts("tbz x0, #5, target"),
6149 vec![Stmt::InstructionWithReloc(
6150 Inst::Tbz {
6151 rt: X0,
6152 bit: 5,
6153 offset: 0,
6154 sf: true
6155 },
6156 LabelRef {
6157 symbol: "target".into(),
6158 kind: RelocKind::Branch14,
6159 addend: 0
6160 },
6161 )]
6162 );
6163 }
6164
6165 #[test]
6166 fn parse_tbnz_numeric_local_label() {
6167 assert_eq!(
6168 parse_stmts("tbnz x0, #33, 1f\n1:\n"),
6169 vec![
6170 Stmt::InstructionWithReloc(
6171 Inst::Tbnz {
6172 rt: X0,
6173 bit: 33,
6174 offset: 0,
6175 sf: true
6176 },
6177 LabelRef {
6178 symbol: ".Ltmp$1$1".into(),
6179 kind: RelocKind::Branch14,
6180 addend: 0
6181 },
6182 ),
6183 Stmt::Label(".Ltmp$1$1".into()),
6184 ]
6185 );
6186 }
6187
6188 #[test]
6189 fn parse_adr_label() {
6190 assert_eq!(
6191 parse_stmts("adr x0, target"),
6192 vec![Stmt::InstructionWithReloc(
6193 Inst::Adr { rd: X0, imm: 0 },
6194 LabelRef {
6195 symbol: "target".into(),
6196 kind: RelocKind::Adr21,
6197 addend: 0
6198 },
6199 )]
6200 );
6201 }
6202
6203 #[test]
6204 fn parse_adr_offset() {
6205 assert_eq!(parse_inst("adr x0, #8"), Inst::Adr { rd: X0, imm: 8 });
6206 }
6207
6208 #[test]
6209 fn parse_adr_numeric_local_label() {
6210 assert_eq!(
6211 parse_stmts("adr x0, 1f\n1:\n"),
6212 vec![
6213 Stmt::InstructionWithReloc(
6214 Inst::Adr { rd: X0, imm: 0 },
6215 LabelRef {
6216 symbol: ".Ltmp$1$1".into(),
6217 kind: RelocKind::Adr21,
6218 addend: 0
6219 },
6220 ),
6221 Stmt::Label(".Ltmp$1$1".into()),
6222 ]
6223 );
6224 }
6225
6226 #[test]
6227 fn parse_cbz_() {
6228 assert_eq!(
6229 parse_inst("cbz x0, #8"),
6230 Inst::Cbz {
6231 rt: X0,
6232 offset: 8,
6233 sf: true
6234 }
6235 );
6236 }
6237
6238 #[test]
6239 fn error_tbz_bit_index_out_of_range() {
6240 let err = parse_err("tbz w0, #32, #8");
6241 assert!(err.contains("range 0..=31"), "got: {}", err);
6242 }
6243
6244 #[test]
6245 fn error_shift_amount_out_of_range_for_w_reg() {
6246 let err = parse_err("add w0, w1, w2, lsl #32");
6247 assert!(err.contains("range 0..=31"), "got: {}", err);
6248 }
6249
6250 #[test]
6251 fn error_extend_shift_amount_out_of_range() {
6252 let err = parse_err("add x0, x1, w2, sxtw #5");
6253 assert!(err.contains("range 0..=4"), "got: {}", err);
6254 }
6255
6256 #[test]
6257 fn error_uxtw_requires_w_register_operand() {
6258 let err = parse_err("add x0, x1, x2, uxtw");
6259 assert!(err.contains("w-register operand"), "got: {}", err);
6260 }
6261
6262 #[test]
6263 fn error_extended_add_sub_rejects_zero_base_register() {
6264 let err = parse_err("sub x11, xzr, w12, sxtw #2");
6265 assert!(
6266 err.contains("x-register or sp base operand"),
6267 "got: {}",
6268 err
6269 );
6270 }
6271
6272 #[test]
6273 fn parse_ret_default() {
6274 assert_eq!(parse_inst("ret"), Inst::Ret { rn: X30 });
6275 }
6276
6277 #[test]
6278 fn parse_ret_reg() {
6279 assert_eq!(parse_inst("ret x16"), Inst::Ret { rn: X16 });
6280 }
6281
6282 // ---- Load/store ----
6283
6284 #[test]
6285 fn parse_ldr_base() {
6286 assert_eq!(
6287 parse_inst("ldr x0, [x1]"),
6288 Inst::LdrImm64 {
6289 rt: X0,
6290 rn: X1,
6291 offset: 0
6292 }
6293 );
6294 }
6295
6296 #[test]
6297 fn parse_ldr_offset() {
6298 assert_eq!(
6299 parse_inst("ldr x0, [x1, #8]"),
6300 Inst::LdrImm64 {
6301 rt: X0,
6302 rn: X1,
6303 offset: 8
6304 }
6305 );
6306 }
6307
6308 #[test]
6309 fn parse_ldr_register_offset() {
6310 assert_eq!(
6311 parse_inst("ldr x0, [x1, x2]"),
6312 Inst::LdrReg64 {
6313 rt: X0,
6314 rn: X1,
6315 rm: X2,
6316 extend: AddrExtend::Lsl,
6317 shift: false
6318 }
6319 );
6320 }
6321
6322 #[test]
6323 fn parse_ldr_register_offset_with_extend() {
6324 assert_eq!(
6325 parse_inst("ldr x6, [x7, w8, uxtw #3]"),
6326 Inst::LdrReg64 {
6327 rt: X6,
6328 rn: X7,
6329 rm: W8,
6330 extend: AddrExtend::Uxtw,
6331 shift: true
6332 }
6333 );
6334 }
6335
6336 #[test]
6337 fn parse_str_offset() {
6338 assert_eq!(
6339 parse_inst("str x2, [x3, #16]"),
6340 Inst::StrImm64 {
6341 rt: X2,
6342 rn: X3,
6343 offset: 16
6344 }
6345 );
6346 }
6347
6348 #[test]
6349 fn parse_str_register_offset() {
6350 assert_eq!(
6351 parse_inst("str w9, [x10, x11]"),
6352 Inst::StrReg32 {
6353 rt: W9,
6354 rn: X10,
6355 rm: X11,
6356 extend: AddrExtend::Lsl,
6357 shift: false
6358 }
6359 );
6360 }
6361
6362 #[test]
6363 fn parse_ldr_w() {
6364 assert_eq!(
6365 parse_inst("ldr w4, [x5, #4]"),
6366 Inst::LdrImm32 {
6367 rt: W4,
6368 rn: X5,
6369 offset: 4
6370 }
6371 );
6372 }
6373
6374 #[test]
6375 fn parse_ldr_negative_offset_aliases_to_ldur() {
6376 assert_eq!(
6377 parse_inst("ldr x9, [x29, #-8]"),
6378 Inst::Ldur64 {
6379 rt: X9,
6380 rn: X29,
6381 offset: -8
6382 }
6383 );
6384 }
6385
6386 #[test]
6387 fn parse_str_negative_offset_aliases_to_stur() {
6388 assert_eq!(
6389 parse_inst("str w6, [x7, #-4]"),
6390 Inst::Stur32 {
6391 rt: W6,
6392 rn: X7,
6393 offset: -4
6394 }
6395 );
6396 }
6397
6398 #[test]
6399 fn parse_ldur_() {
6400 assert_eq!(
6401 parse_inst("ldur x9, [x29, #-8]"),
6402 Inst::Ldur64 {
6403 rt: X9,
6404 rn: X29,
6405 offset: -8
6406 }
6407 );
6408 }
6409
6410 #[test]
6411 fn parse_stur_() {
6412 assert_eq!(
6413 parse_inst("stur w6, [x7, #-4]"),
6414 Inst::Stur32 {
6415 rt: W6,
6416 rn: X7,
6417 offset: -4
6418 }
6419 );
6420 }
6421
6422 #[test]
6423 fn parse_ldr_w_post_index() {
6424 assert_eq!(
6425 parse_inst("ldr w0, [x1], #4"),
6426 Inst::LdrPost32 {
6427 rt: W0,
6428 rn: X1,
6429 offset: 4
6430 }
6431 );
6432 }
6433
6434 #[test]
6435 fn parse_str_w_pre_index() {
6436 assert_eq!(
6437 parse_inst("str w2, [x3, #-4]!"),
6438 Inst::StrPre32 {
6439 rt: W2,
6440 rn: X3,
6441 offset: -4
6442 }
6443 );
6444 }
6445
6446 #[test]
6447 fn parse_ldr_d_base() {
6448 assert_eq!(
6449 parse_inst("ldr d0, [x1]"),
6450 Inst::LdrFpImm64 {
6451 rt: D0,
6452 rn: X1,
6453 offset: 0
6454 }
6455 );
6456 }
6457
6458 #[test]
6459 fn parse_ldr_q_offset() {
6460 assert_eq!(
6461 parse_inst("ldr q0, [sp, #16]"),
6462 Inst::LdrFpImm128 {
6463 rt: FpReg::new(0),
6464 rn: SP,
6465 offset: 16
6466 }
6467 );
6468 }
6469
6470 #[test]
6471 fn parse_ldr_h_offset() {
6472 assert_eq!(
6473 parse_inst("ldr h2, [sp, #14]"),
6474 Inst::LdrFpImm16 {
6475 rt: FpReg::new(2),
6476 rn: SP,
6477 offset: 14
6478 }
6479 );
6480 }
6481
6482 #[test]
6483 fn parse_ldr_b_offset() {
6484 assert_eq!(
6485 parse_inst("ldr b2, [sp, #15]"),
6486 Inst::LdrFpImm8 {
6487 rt: FpReg::new(2),
6488 rn: SP,
6489 offset: 15
6490 }
6491 );
6492 }
6493
6494 #[test]
6495 fn parse_str_q_base() {
6496 assert_eq!(
6497 parse_inst("str q1, [x0]"),
6498 Inst::StrFpImm128 {
6499 rt: FpReg::new(1),
6500 rn: X0,
6501 offset: 0
6502 }
6503 );
6504 }
6505
6506 #[test]
6507 fn parse_str_h_offset() {
6508 assert_eq!(
6509 parse_inst("str h2, [sp, #14]"),
6510 Inst::StrFpImm16 {
6511 rt: FpReg::new(2),
6512 rn: SP,
6513 offset: 14
6514 }
6515 );
6516 }
6517
6518 #[test]
6519 fn parse_str_b_offset() {
6520 assert_eq!(
6521 parse_inst("str b2, [sp, #15]"),
6522 Inst::StrFpImm8 {
6523 rt: FpReg::new(2),
6524 rn: SP,
6525 offset: 15
6526 }
6527 );
6528 }
6529
6530 #[test]
6531 fn parse_ldr_q_literal_offset() {
6532 assert_eq!(
6533 parse_inst("ldr q0, #16"),
6534 Inst::LdrFpLit128 {
6535 rt: FpReg::new(0),
6536 offset: 16
6537 }
6538 );
6539 }
6540
6541 #[test]
6542 fn parse_ldr_q_register_offset() {
6543 assert_eq!(
6544 parse_inst("ldr q0, [x1, x2]"),
6545 Inst::LdrFpReg128 {
6546 rt: FpReg::new(0),
6547 rn: X1,
6548 rm: X2,
6549 extend: AddrExtend::Lsl,
6550 shift: false
6551 }
6552 );
6553 }
6554
6555 #[test]
6556 fn parse_ldr_q_pageoff_memory_operand() {
6557 assert_eq!(
6558 parse_stmts("ldr q2, [x8, lCPI0_0@PAGEOFF]"),
6559 vec![Stmt::InstructionWithReloc(
6560 Inst::LdrFpImm128 {
6561 rt: FpReg::new(2),
6562 rn: X8,
6563 offset: 0
6564 },
6565 LabelRef {
6566 symbol: "lCPI0_0".into(),
6567 kind: RelocKind::PageOff12,
6568 addend: 0
6569 },
6570 )]
6571 );
6572 }
6573
6574 #[test]
6575 fn parse_str_q_register_offset_with_extend() {
6576 assert_eq!(
6577 parse_inst("str q1, [x3, w4, uxtw #4]"),
6578 Inst::StrFpReg128 {
6579 rt: FpReg::new(1),
6580 rn: X3,
6581 rm: W4,
6582 extend: AddrExtend::Uxtw,
6583 shift: true
6584 }
6585 );
6586 }
6587
6588 #[test]
6589 fn parse_ldr_q_post_index() {
6590 assert_eq!(
6591 parse_inst("ldr q0, [sp], #16"),
6592 Inst::LdrFpPost128 {
6593 rt: FpReg::new(0),
6594 rn: SP,
6595 offset: 16
6596 }
6597 );
6598 }
6599
6600 #[test]
6601 fn parse_str_q_pre_index() {
6602 assert_eq!(
6603 parse_inst("str q1, [sp, #-16]!"),
6604 Inst::StrFpPre128 {
6605 rt: FpReg::new(1),
6606 rn: SP,
6607 offset: -16
6608 }
6609 );
6610 }
6611
6612 #[test]
6613 fn parse_str_d_offset() {
6614 assert_eq!(
6615 parse_inst("str d2, [x3, #16]"),
6616 Inst::StrFpImm64 {
6617 rt: D2,
6618 rn: X3,
6619 offset: 16
6620 }
6621 );
6622 }
6623
6624 #[test]
6625 fn parse_str_s_negative_offset_uses_unscaled() {
6626 assert_eq!(
6627 parse_inst("str s8, [x29, #-4]"),
6628 Inst::SturFp32 {
6629 rt: S8,
6630 rn: X29,
6631 offset: -4
6632 }
6633 );
6634 }
6635
6636 #[test]
6637 fn parse_ldr_s_negative_offset_uses_unscaled() {
6638 assert_eq!(
6639 parse_inst("ldr s9, [x29, #-4]"),
6640 Inst::LdurFp32 {
6641 rt: S9,
6642 rn: X29,
6643 offset: -4
6644 }
6645 );
6646 }
6647
6648 #[test]
6649 fn parse_ldr_s_register_offset() {
6650 assert_eq!(
6651 parse_inst("ldr s4, [x5, x6]"),
6652 Inst::LdrFpReg32 {
6653 rt: S4,
6654 rn: X5,
6655 rm: X6,
6656 extend: AddrExtend::Lsl,
6657 shift: false
6658 }
6659 );
6660 }
6661
6662 #[test]
6663 fn parse_str_s_register_offset_with_extend() {
6664 assert_eq!(
6665 parse_inst("str s7, [x8, w9, uxtw #2]"),
6666 Inst::StrFpReg32 {
6667 rt: S7,
6668 rn: X8,
6669 rm: W9,
6670 extend: AddrExtend::Uxtw,
6671 shift: true
6672 }
6673 );
6674 }
6675
6676 #[test]
6677 fn parse_ldr_d_post_index() {
6678 assert_eq!(
6679 parse_inst("ldr d0, [sp], #8"),
6680 Inst::LdrFpPost64 {
6681 rt: D0,
6682 rn: SP,
6683 offset: 8
6684 }
6685 );
6686 }
6687
6688 #[test]
6689 fn parse_str_s_pre_index() {
6690 assert_eq!(
6691 parse_inst("str s3, [sp, #-8]!"),
6692 Inst::StrFpPre32 {
6693 rt: S3,
6694 rn: SP,
6695 offset: -8
6696 }
6697 );
6698 }
6699
6700 #[test]
6701 fn parse_ldrb_() {
6702 assert_eq!(
6703 parse_inst("ldrb w0, [x1, #3]"),
6704 Inst::Ldrb {
6705 rt: W0,
6706 rn: X1,
6707 offset: 3
6708 }
6709 );
6710 }
6711
6712 #[test]
6713 fn parse_ldrsb_32() {
6714 assert_eq!(
6715 parse_inst("ldrsb w0, [x1, #3]"),
6716 Inst::Ldrsb32 {
6717 rt: W0,
6718 rn: X1,
6719 offset: 3
6720 }
6721 );
6722 }
6723
6724 #[test]
6725 fn parse_ldrsb_post_index_64() {
6726 assert_eq!(
6727 parse_inst("ldrsb x9, [x1], #1"),
6728 Inst::LdrsbPost64 {
6729 rt: X9,
6730 rn: X1,
6731 offset: 1
6732 }
6733 );
6734 }
6735
6736 #[test]
6737 fn parse_ldrb_post_index() {
6738 assert_eq!(
6739 parse_inst("ldrb w9, [x1], #1"),
6740 Inst::LdrbPost {
6741 rt: W9,
6742 rn: X1,
6743 offset: 1
6744 }
6745 );
6746 }
6747
6748 #[test]
6749 fn parse_ldrb_register_offset() {
6750 assert_eq!(
6751 parse_inst("ldrb w0, [x1, x2]"),
6752 Inst::LdrbReg {
6753 rt: W0,
6754 rn: X1,
6755 rm: X2,
6756 extend: AddrExtend::Lsl,
6757 shift: false
6758 }
6759 );
6760 }
6761
6762 #[test]
6763 fn parse_strb_() {
6764 assert_eq!(
6765 parse_inst("strb w8, [x9]"),
6766 Inst::Strb {
6767 rt: W8,
6768 rn: X9,
6769 offset: 0
6770 }
6771 );
6772 }
6773
6774 #[test]
6775 fn parse_strb_post_index() {
6776 assert_eq!(
6777 parse_inst("strb w9, [x8], #1"),
6778 Inst::StrbPost {
6779 rt: W9,
6780 rn: X8,
6781 offset: 1
6782 }
6783 );
6784 }
6785
6786 #[test]
6787 fn parse_ldrh_register_offset() {
6788 assert_eq!(
6789 parse_inst("ldrh w3, [x4, w5, uxtw #1]"),
6790 Inst::LdrhReg {
6791 rt: W3,
6792 rn: X4,
6793 rm: W5,
6794 extend: AddrExtend::Uxtw,
6795 shift: true
6796 }
6797 );
6798 }
6799
6800 #[test]
6801 fn parse_ldrsh_register_offset_32() {
6802 assert_eq!(
6803 parse_inst("ldrsh w3, [x4, w5, uxtw #1]"),
6804 Inst::LdrshReg32 {
6805 rt: W3,
6806 rn: X4,
6807 rm: W5,
6808 extend: AddrExtend::Uxtw,
6809 shift: true
6810 }
6811 );
6812 }
6813
6814 #[test]
6815 fn parse_strh_pre_index() {
6816 assert_eq!(
6817 parse_inst("strh w5, [x6, #2]!"),
6818 Inst::StrhPre {
6819 rt: W5,
6820 rn: X6,
6821 offset: 2
6822 }
6823 );
6824 }
6825
6826 #[test]
6827 fn parse_ldrsw_register_offset() {
6828 assert_eq!(
6829 parse_inst("ldrsw x6, [x7, w8, sxtw #2]"),
6830 Inst::LdrswReg {
6831 rt: X6,
6832 rn: X7,
6833 rm: W8,
6834 extend: AddrExtend::Sxtw,
6835 shift: true
6836 }
6837 );
6838 }
6839
6840 #[test]
6841 fn parse_ldrsh_pre_index_64() {
6842 assert_eq!(
6843 parse_inst("ldrsh x5, [x6, #2]!"),
6844 Inst::LdrshPre64 {
6845 rt: X5,
6846 rn: X6,
6847 offset: 2
6848 }
6849 );
6850 }
6851
6852 #[test]
6853 fn parse_ldapr_w() {
6854 assert_eq!(
6855 parse_inst("ldapr w8, [x9]"),
6856 Inst::Ldapr32 { rt: W8, rn: X9 }
6857 );
6858 }
6859
6860 #[test]
6861 fn parse_ldaprb_w() {
6862 assert_eq!(
6863 parse_inst("ldaprb w0, [x1]"),
6864 Inst::Ldaprb { rt: W0, rn: X1 }
6865 );
6866 }
6867
6868 #[test]
6869 fn parse_ldaprh_w() {
6870 assert_eq!(
6871 parse_inst("ldaprh w2, [x3]"),
6872 Inst::Ldaprh { rt: W2, rn: X3 }
6873 );
6874 }
6875
6876 #[test]
6877 fn parse_stlr_x() {
6878 assert_eq!(
6879 parse_inst("stlr x10, [x11]"),
6880 Inst::Stlr64 { rt: X10, rn: X11 }
6881 );
6882 }
6883
6884 #[test]
6885 fn parse_stlrb_w() {
6886 assert_eq!(parse_inst("stlrb w4, [x5]"), Inst::Stlrb { rt: W4, rn: X5 });
6887 }
6888
6889 #[test]
6890 fn parse_stlrh_w() {
6891 assert_eq!(parse_inst("stlrh w6, [x7]"), Inst::Stlrh { rt: W6, rn: X7 });
6892 }
6893
6894 #[test]
6895 fn parse_ldaddal_w() {
6896 assert_eq!(
6897 parse_inst("ldaddal w0, w8, [x8]"),
6898 Inst::Ldaddal32 {
6899 rs: W0,
6900 rt: W8,
6901 rn: X8
6902 }
6903 );
6904 }
6905
6906 #[test]
6907 fn parse_ldaddalb_w() {
6908 assert_eq!(
6909 parse_inst("ldaddalb w0, w1, [x2]"),
6910 Inst::Ldaddalb {
6911 rs: W0,
6912 rt: W1,
6913 rn: X2
6914 }
6915 );
6916 }
6917
6918 #[test]
6919 fn parse_ldaddalh_w() {
6920 assert_eq!(
6921 parse_inst("ldaddalh w3, w4, [x5]"),
6922 Inst::Ldaddalh {
6923 rs: W3,
6924 rt: W4,
6925 rn: X5
6926 }
6927 );
6928 }
6929
6930 #[test]
6931 fn parse_ldumaxalb_w() {
6932 assert_eq!(
6933 parse_inst("ldumaxalb w0, w1, [x2]"),
6934 Inst::Ldumaxalb {
6935 rs: W0,
6936 rt: W1,
6937 rn: X2
6938 }
6939 );
6940 }
6941
6942 #[test]
6943 fn parse_ldumaxalh_w() {
6944 assert_eq!(
6945 parse_inst("ldumaxalh w3, w4, [x5]"),
6946 Inst::Ldumaxalh {
6947 rs: W3,
6948 rt: W4,
6949 rn: X5
6950 }
6951 );
6952 }
6953
6954 #[test]
6955 fn parse_ldsmaxalb_w() {
6956 assert_eq!(
6957 parse_inst("ldsmaxalb w18, w19, [x20]"),
6958 Inst::Ldsmaxalb {
6959 rs: W18,
6960 rt: W19,
6961 rn: X20
6962 }
6963 );
6964 }
6965
6966 #[test]
6967 fn parse_ldsmaxalh_w() {
6968 assert_eq!(
6969 parse_inst("ldsmaxalh w24, w25, [x26]"),
6970 Inst::Ldsmaxalh {
6971 rs: W24,
6972 rt: W25,
6973 rn: X26
6974 }
6975 );
6976 }
6977
6978 #[test]
6979 fn parse_ldumaxal_w() {
6980 assert_eq!(
6981 parse_inst("ldumaxal w6, w7, [x8]"),
6982 Inst::Ldumaxal32 {
6983 rs: W6,
6984 rt: W7,
6985 rn: X8
6986 }
6987 );
6988 }
6989
6990 #[test]
6991 fn parse_ldsmaxal_w() {
6992 assert_eq!(
6993 parse_inst("ldsmaxal w0, w1, [x2]"),
6994 Inst::Ldsmaxal32 {
6995 rs: W0,
6996 rt: W1,
6997 rn: X2
6998 }
6999 );
7000 }
7001
7002 #[test]
7003 fn parse_ldsminal_x() {
7004 assert_eq!(
7005 parse_inst("ldsminal x9, x10, [x11]"),
7006 Inst::Ldsminal64 {
7007 rs: X9,
7008 rt: X10,
7009 rn: X11
7010 }
7011 );
7012 }
7013
7014 #[test]
7015 fn parse_lduminalb_w() {
7016 assert_eq!(
7017 parse_inst("lduminalb w12, w13, [x14]"),
7018 Inst::Lduminalb {
7019 rs: W12,
7020 rt: W13,
7021 rn: X14
7022 }
7023 );
7024 }
7025
7026 #[test]
7027 fn parse_lduminalh_w() {
7028 assert_eq!(
7029 parse_inst("lduminalh w15, w16, [x17]"),
7030 Inst::Lduminalh {
7031 rs: W15,
7032 rt: W16,
7033 rn: X17
7034 }
7035 );
7036 }
7037
7038 #[test]
7039 fn parse_ldsminalb_w() {
7040 assert_eq!(
7041 parse_inst("ldsminalb w21, w22, [x23]"),
7042 Inst::Ldsminalb {
7043 rs: W21,
7044 rt: W22,
7045 rn: X23
7046 }
7047 );
7048 }
7049
7050 #[test]
7051 fn parse_ldsminalh_w() {
7052 assert_eq!(
7053 parse_inst("ldsminalh w27, w28, [x29]"),
7054 Inst::Ldsminalh {
7055 rs: W27,
7056 rt: W28,
7057 rn: X29
7058 }
7059 );
7060 }
7061
7062 #[test]
7063 fn parse_lduminal_w() {
7064 assert_eq!(
7065 parse_inst("lduminal w18, w19, [x20]"),
7066 Inst::Lduminal32 {
7067 rs: W18,
7068 rt: W19,
7069 rn: X20
7070 }
7071 );
7072 }
7073
7074 #[test]
7075 fn parse_ldclral_w() {
7076 assert_eq!(
7077 parse_inst("ldclral w12, w13, [x14]"),
7078 Inst::Ldclral32 {
7079 rs: W12,
7080 rt: W13,
7081 rn: X14
7082 }
7083 );
7084 }
7085
7086 #[test]
7087 fn parse_ldclralb_w() {
7088 assert_eq!(
7089 parse_inst("ldclralb w6, w7, [x8]"),
7090 Inst::Ldclralb {
7091 rs: W6,
7092 rt: W7,
7093 rn: X8
7094 }
7095 );
7096 }
7097
7098 #[test]
7099 fn parse_ldclralh_w() {
7100 assert_eq!(
7101 parse_inst("ldclralh w15, w16, [x17]"),
7102 Inst::Ldclralh {
7103 rs: W15,
7104 rt: W16,
7105 rn: X17
7106 }
7107 );
7108 }
7109
7110 #[test]
7111 fn parse_ldeoral_x() {
7112 assert_eq!(
7113 parse_inst("ldeoral x9, x10, [x11]"),
7114 Inst::Ldeoral64 {
7115 rs: X9,
7116 rt: X10,
7117 rn: X11
7118 }
7119 );
7120 }
7121
7122 #[test]
7123 fn parse_ldeoralb_w() {
7124 assert_eq!(
7125 parse_inst("ldeoralb w3, w4, [x5]"),
7126 Inst::Ldeoralb {
7127 rs: W3,
7128 rt: W4,
7129 rn: X5
7130 }
7131 );
7132 }
7133
7134 #[test]
7135 fn parse_ldeoralh_w() {
7136 assert_eq!(
7137 parse_inst("ldeoralh w12, w13, [x14]"),
7138 Inst::Ldeoralh {
7139 rs: W12,
7140 rt: W13,
7141 rn: X14
7142 }
7143 );
7144 }
7145
7146 #[test]
7147 fn parse_ldsetal_w() {
7148 assert_eq!(
7149 parse_inst("ldsetal w0, w1, [x2]"),
7150 Inst::Ldsetal32 {
7151 rs: W0,
7152 rt: W1,
7153 rn: X2
7154 }
7155 );
7156 }
7157
7158 #[test]
7159 fn parse_ldsetalb_w() {
7160 assert_eq!(
7161 parse_inst("ldsetalb w0, w1, [x2]"),
7162 Inst::Ldsetalb {
7163 rs: W0,
7164 rt: W1,
7165 rn: X2
7166 }
7167 );
7168 }
7169
7170 #[test]
7171 fn parse_ldsetalh_w() {
7172 assert_eq!(
7173 parse_inst("ldsetalh w9, w10, [x11]"),
7174 Inst::Ldsetalh {
7175 rs: W9,
7176 rt: W10,
7177 rn: X11
7178 }
7179 );
7180 }
7181
7182 #[test]
7183 fn parse_swpal_x() {
7184 assert_eq!(
7185 parse_inst("swpal x1, x2, [x3]"),
7186 Inst::Swpal64 {
7187 rs: X1,
7188 rt: X2,
7189 rn: X3
7190 }
7191 );
7192 }
7193
7194 #[test]
7195 fn parse_swpalb_w() {
7196 assert_eq!(
7197 parse_inst("swpalb w8, w9, [x10]"),
7198 Inst::Swpalb {
7199 rs: W8,
7200 rt: W9,
7201 rn: X10
7202 }
7203 );
7204 }
7205
7206 #[test]
7207 fn parse_swpalh_w() {
7208 assert_eq!(
7209 parse_inst("swpalh w11, w12, [x13]"),
7210 Inst::Swpalh {
7211 rs: W11,
7212 rt: W12,
7213 rn: X13
7214 }
7215 );
7216 }
7217
7218 #[test]
7219 fn parse_casal_w() {
7220 assert_eq!(
7221 parse_inst("casal w4, w5, [x6]"),
7222 Inst::Casal32 {
7223 rs: W4,
7224 rt: W5,
7225 rn: X6
7226 }
7227 );
7228 }
7229
7230 #[test]
7231 fn parse_casalb_w() {
7232 assert_eq!(
7233 parse_inst("casalb w6, w7, [x8]"),
7234 Inst::Casalb {
7235 rs: W6,
7236 rt: W7,
7237 rn: X8
7238 }
7239 );
7240 }
7241
7242 #[test]
7243 fn parse_casalh_w() {
7244 assert_eq!(
7245 parse_inst("casalh w9, w10, [x11]"),
7246 Inst::Casalh {
7247 rs: W9,
7248 rt: W10,
7249 rn: X11
7250 }
7251 );
7252 }
7253
7254 #[test]
7255 fn parse_ldr_literal_label() {
7256 assert_eq!(
7257 parse_stmts("ldr x0, target"),
7258 vec![Stmt::InstructionWithReloc(
7259 Inst::LdrLit64 { rt: X0, offset: 0 },
7260 LabelRef {
7261 symbol: "target".into(),
7262 kind: RelocKind::Literal19,
7263 addend: 0
7264 },
7265 )]
7266 );
7267 }
7268
7269 #[test]
7270 fn parse_ldr_literal_offset() {
7271 assert_eq!(
7272 parse_inst("ldr x0, #8"),
7273 Inst::LdrLit64 { rt: X0, offset: 8 }
7274 );
7275 }
7276
7277 #[test]
7278 fn parse_ldr_d_literal_label() {
7279 assert_eq!(
7280 parse_stmts("ldr d10, target"),
7281 vec![Stmt::InstructionWithReloc(
7282 Inst::LdrFpLit64 { rt: D10, offset: 0 },
7283 LabelRef {
7284 symbol: "target".into(),
7285 kind: RelocKind::Literal19,
7286 addend: 0
7287 },
7288 )]
7289 );
7290 }
7291
7292 #[test]
7293 fn parse_ldr_s_literal_offset() {
7294 assert_eq!(
7295 parse_inst("ldr s11, #20"),
7296 Inst::LdrFpLit32 {
7297 rt: S11,
7298 offset: 20
7299 }
7300 );
7301 }
7302
7303 #[test]
7304 fn parse_ldr_literal_numeric_local_label() {
7305 assert_eq!(
7306 parse_stmts("ldr x0, 1f\n1:\n"),
7307 vec![
7308 Stmt::InstructionWithReloc(
7309 Inst::LdrLit64 { rt: X0, offset: 0 },
7310 LabelRef {
7311 symbol: ".Ltmp$1$1".into(),
7312 kind: RelocKind::Literal19,
7313 addend: 0
7314 },
7315 ),
7316 Stmt::Label(".Ltmp$1$1".into()),
7317 ]
7318 );
7319 }
7320
7321 #[test]
7322 fn parse_ldr_s_literal_numeric_local_label() {
7323 assert_eq!(
7324 parse_stmts("ldr s0, 1f\n1:\n"),
7325 vec![
7326 Stmt::InstructionWithReloc(
7327 Inst::LdrFpLit32 { rt: S0, offset: 0 },
7328 LabelRef {
7329 symbol: ".Ltmp$1$1".into(),
7330 kind: RelocKind::Literal19,
7331 addend: 0
7332 },
7333 ),
7334 Stmt::Label(".Ltmp$1$1".into()),
7335 ]
7336 );
7337 }
7338
7339 #[test]
7340 fn parse_ldrsw_literal_label() {
7341 assert_eq!(
7342 parse_stmts("ldrsw x0, target"),
7343 vec![Stmt::InstructionWithReloc(
7344 Inst::LdrswLit { rt: X0, offset: 0 },
7345 LabelRef {
7346 symbol: "target".into(),
7347 kind: RelocKind::Literal19,
7348 addend: 0
7349 },
7350 )]
7351 );
7352 }
7353
7354 #[test]
7355 fn parse_ldrsw_literal_offset() {
7356 assert_eq!(
7357 parse_inst("ldrsw x1, #12"),
7358 Inst::LdrswLit { rt: X1, offset: 12 }
7359 );
7360 }
7361
7362 #[test]
7363 fn parse_ldrsw_literal_numeric_local_label() {
7364 assert_eq!(
7365 parse_stmts("ldrsw x0, 1f\n1:\n"),
7366 vec![
7367 Stmt::InstructionWithReloc(
7368 Inst::LdrswLit { rt: X0, offset: 0 },
7369 LabelRef {
7370 symbol: ".Ltmp$1$1".into(),
7371 kind: RelocKind::Literal19,
7372 addend: 0
7373 },
7374 ),
7375 Stmt::Label(".Ltmp$1$1".into()),
7376 ]
7377 );
7378 }
7379
7380 #[test]
7381 fn parse_stp_pre() {
7382 assert_eq!(
7383 parse_inst("stp x29, x30, [sp, #-16]!"),
7384 Inst::StpPre64 {
7385 rt1: X29,
7386 rt2: X30,
7387 rn: SP,
7388 offset: -16
7389 }
7390 );
7391 }
7392
7393 #[test]
7394 fn parse_ldp_d_pre() {
7395 assert_eq!(
7396 parse_inst("ldp d8, d9, [sp, #-16]!"),
7397 Inst::LdpFpPre64 {
7398 rt1: D8,
7399 rt2: D9,
7400 rn: SP,
7401 offset: -16
7402 }
7403 );
7404 }
7405
7406 #[test]
7407 fn parse_stp_d_post() {
7408 assert_eq!(
7409 parse_inst("stp d10, d11, [sp], #16"),
7410 Inst::StpFpPost64 {
7411 rt1: D10,
7412 rt2: D11,
7413 rn: SP,
7414 offset: 16
7415 }
7416 );
7417 }
7418
7419 #[test]
7420 fn parse_ldp_d_offset() {
7421 assert_eq!(
7422 parse_inst("ldp d12, d13, [sp, #32]"),
7423 Inst::LdpFpOff64 {
7424 rt1: D12,
7425 rt2: D13,
7426 rn: SP,
7427 offset: 32
7428 }
7429 );
7430 }
7431
7432 #[test]
7433 fn parse_stp_s_post() {
7434 assert_eq!(
7435 parse_inst("stp s0, s1, [sp], #8"),
7436 Inst::StpFpPost32 {
7437 rt1: S0,
7438 rt2: S1,
7439 rn: SP,
7440 offset: 8
7441 }
7442 );
7443 }
7444
7445 #[test]
7446 fn parse_ldp_s_pre() {
7447 assert_eq!(
7448 parse_inst("ldp s2, s3, [sp, #-8]!"),
7449 Inst::LdpFpPre32 {
7450 rt1: S2,
7451 rt2: S3,
7452 rn: SP,
7453 offset: -8
7454 }
7455 );
7456 }
7457
7458 #[test]
7459 fn parse_stp_q_pre() {
7460 assert_eq!(
7461 parse_inst("stp q0, q1, [sp, #-32]!"),
7462 Inst::StpFpPre128 {
7463 rt1: FpReg::new(0),
7464 rt2: FpReg::new(1),
7465 rn: SP,
7466 offset: -32
7467 }
7468 );
7469 }
7470
7471 #[test]
7472 fn parse_ldp_q_post() {
7473 assert_eq!(
7474 parse_inst("ldp q2, q3, [sp], #32"),
7475 Inst::LdpFpPost128 {
7476 rt1: FpReg::new(2),
7477 rt2: FpReg::new(3),
7478 rn: SP,
7479 offset: 32
7480 }
7481 );
7482 }
7483
7484 #[test]
7485 fn parse_ldp_q_offset() {
7486 assert_eq!(
7487 parse_inst("ldp q4, q5, [sp, #64]"),
7488 Inst::LdpFpOff128 {
7489 rt1: FpReg::new(4),
7490 rt2: FpReg::new(5),
7491 rn: SP,
7492 offset: 64
7493 }
7494 );
7495 }
7496
7497 #[test]
7498 fn error_ldp_fp_pair_requires_matching_widths() {
7499 let err = parse_err("ldp d0, s1, [sp]");
7500 assert!(err.contains("matching register widths"), "got: {}", err);
7501 }
7502
7503 #[test]
7504 fn parse_ldp_post() {
7505 assert_eq!(
7506 parse_inst("ldp x29, x30, [sp], #16"),
7507 Inst::LdpPost64 {
7508 rt1: X29,
7509 rt2: X30,
7510 rn: SP,
7511 offset: 16
7512 }
7513 );
7514 }
7515
7516 #[test]
7517 fn parse_stp_post() {
7518 assert_eq!(
7519 parse_inst("stp x29, x30, [sp], #16"),
7520 Inst::StpPost64 {
7521 rt1: X29,
7522 rt2: X30,
7523 rn: SP,
7524 offset: 16
7525 }
7526 );
7527 }
7528
7529 #[test]
7530 fn parse_ldp_pre() {
7531 assert_eq!(
7532 parse_inst("ldp x29, x30, [sp, #-16]!"),
7533 Inst::LdpPre64 {
7534 rt1: X29,
7535 rt2: X30,
7536 rn: SP,
7537 offset: -16
7538 }
7539 );
7540 }
7541
7542 #[test]
7543 fn parse_ldp_off() {
7544 assert_eq!(
7545 parse_inst("ldp x19, x20, [sp, #16]"),
7546 Inst::LdpOff64 {
7547 rt1: X19,
7548 rt2: X20,
7549 rn: SP,
7550 offset: 16
7551 }
7552 );
7553 }
7554
7555 #[test]
7556 fn parse_ldp_w_off() {
7557 assert_eq!(
7558 parse_inst("ldp w9, w8, [x8]"),
7559 Inst::LdpOff32 {
7560 rt1: W9,
7561 rt2: W8,
7562 rn: X8,
7563 offset: 0
7564 }
7565 );
7566 }
7567
7568 #[test]
7569 fn parse_stp_w_off() {
7570 assert_eq!(
7571 parse_inst("stp w1, w2, [sp, #16]"),
7572 Inst::StpOff32 {
7573 rt1: W1,
7574 rt2: W2,
7575 rn: SP,
7576 offset: 16
7577 }
7578 );
7579 }
7580
7581 #[test]
7582 fn parse_ldp_w_post() {
7583 assert_eq!(
7584 parse_inst("ldp w9, w8, [sp], #8"),
7585 Inst::LdpPost32 {
7586 rt1: W9,
7587 rt2: W8,
7588 rn: SP,
7589 offset: 8
7590 }
7591 );
7592 }
7593
7594 #[test]
7595 fn parse_ldp_w_pre() {
7596 assert_eq!(
7597 parse_inst("ldp w9, w8, [sp, #-8]!"),
7598 Inst::LdpPre32 {
7599 rt1: W9,
7600 rt2: W8,
7601 rn: SP,
7602 offset: -8
7603 }
7604 );
7605 }
7606
7607 #[test]
7608 fn error_ldp_gp_pair_requires_matching_widths() {
7609 let err = parse_err("ldp x0, w1, [sp]");
7610 assert!(err.contains("matching register widths"), "got: {}", err);
7611 }
7612
7613 // ---- FP ----
7614
7615 #[test]
7616 fn parse_fadd_d() {
7617 assert_eq!(
7618 parse_inst("fadd d0, d1, d2"),
7619 Inst::FaddD {
7620 rd: D0,
7621 rn: D1,
7622 rm: D2
7623 }
7624 );
7625 }
7626
7627 #[test]
7628 fn parse_fadd_s() {
7629 assert_eq!(
7630 parse_inst("fadd s0, s1, s2"),
7631 Inst::FaddS {
7632 rd: S0,
7633 rn: S1,
7634 rm: S2
7635 }
7636 );
7637 }
7638
7639 #[test]
7640 fn parse_fadd_4s() {
7641 assert_eq!(
7642 parse_inst("fadd.4s v0, v1, v2"),
7643 Inst::FaddV4S {
7644 rd: FpReg::new(0),
7645 rn: FpReg::new(1),
7646 rm: FpReg::new(2)
7647 }
7648 );
7649 }
7650
7651 #[test]
7652 fn parse_fadd_2d() {
7653 assert_eq!(
7654 parse_inst("fadd.2d v0, v1, v2"),
7655 Inst::FaddV2D {
7656 rd: FpReg::new(0),
7657 rn: FpReg::new(1),
7658 rm: FpReg::new(2)
7659 }
7660 );
7661 }
7662
7663 #[test]
7664 fn parse_fmla_2d() {
7665 assert_eq!(
7666 parse_inst("fmla.2d v0, v1, v2"),
7667 Inst::FmlaV2D {
7668 rd: FpReg::new(0),
7669 rn: FpReg::new(1),
7670 rm: FpReg::new(2)
7671 }
7672 );
7673 }
7674
7675 #[test]
7676 fn parse_fmls_2d() {
7677 assert_eq!(
7678 parse_inst("fmls.2d v3, v4, v5"),
7679 Inst::FmlsV2D {
7680 rd: FpReg::new(3),
7681 rn: FpReg::new(4),
7682 rm: FpReg::new(5)
7683 }
7684 );
7685 }
7686
7687 #[test]
7688 fn parse_faddp_4s() {
7689 assert_eq!(
7690 parse_inst("faddp.4s v0, v1, v2"),
7691 Inst::FaddpV4S {
7692 rd: FpReg::new(0),
7693 rn: FpReg::new(1),
7694 rm: FpReg::new(2)
7695 }
7696 );
7697 }
7698
7699 #[test]
7700 fn parse_faddp_2d() {
7701 assert_eq!(
7702 parse_inst("faddp.2d v0, v1, v2"),
7703 Inst::FaddpV2D {
7704 rd: FpReg::new(0),
7705 rn: FpReg::new(1),
7706 rm: FpReg::new(2)
7707 }
7708 );
7709 }
7710
7711 #[test]
7712 fn parse_fmaxp_4s() {
7713 assert_eq!(
7714 parse_inst("fmaxp.4s v0, v1, v2"),
7715 Inst::FmaxpV4S {
7716 rd: FpReg::new(0),
7717 rn: FpReg::new(1),
7718 rm: FpReg::new(2)
7719 }
7720 );
7721 }
7722
7723 #[test]
7724 fn parse_fmaxp_2d() {
7725 assert_eq!(
7726 parse_inst("fmaxp.2d v3, v4, v5"),
7727 Inst::FmaxpV2D {
7728 rd: FpReg::new(3),
7729 rn: FpReg::new(4),
7730 rm: FpReg::new(5)
7731 }
7732 );
7733 }
7734
7735 #[test]
7736 fn parse_fmaxp_2d_scalar() {
7737 assert_eq!(
7738 parse_inst("fmaxp.2d d1, v2"),
7739 Inst::FmaxpV2DScalar {
7740 rd: FpReg::new(1),
7741 rn: FpReg::new(2)
7742 }
7743 );
7744 }
7745
7746 #[test]
7747 fn parse_fminp_4s() {
7748 assert_eq!(
7749 parse_inst("fminp.4s v3, v4, v5"),
7750 Inst::FminpV4S {
7751 rd: FpReg::new(3),
7752 rn: FpReg::new(4),
7753 rm: FpReg::new(5)
7754 }
7755 );
7756 }
7757
7758 #[test]
7759 fn parse_fmaxnmp_4s() {
7760 assert_eq!(
7761 parse_inst("fmaxnmp.4s v0, v1, v2"),
7762 Inst::FmaxnmpV4S {
7763 rd: FpReg::new(0),
7764 rn: FpReg::new(1),
7765 rm: FpReg::new(2)
7766 }
7767 );
7768 }
7769
7770 #[test]
7771 fn parse_fmaxnmp_2d() {
7772 assert_eq!(
7773 parse_inst("fmaxnmp.2d v0, v1, v2"),
7774 Inst::FmaxnmpV2D {
7775 rd: FpReg::new(0),
7776 rn: FpReg::new(1),
7777 rm: FpReg::new(2)
7778 }
7779 );
7780 }
7781
7782 #[test]
7783 fn parse_fmaxnmp_2d_scalar() {
7784 assert_eq!(
7785 parse_inst("fmaxnmp.2d d0, v0"),
7786 Inst::FmaxnmpV2DScalar {
7787 rd: FpReg::new(0),
7788 rn: FpReg::new(0)
7789 }
7790 );
7791 }
7792
7793 #[test]
7794 fn parse_fminnmp_4s() {
7795 assert_eq!(
7796 parse_inst("fminnmp.4s v3, v4, v5"),
7797 Inst::FminnmpV4S {
7798 rd: FpReg::new(3),
7799 rn: FpReg::new(4),
7800 rm: FpReg::new(5)
7801 }
7802 );
7803 }
7804
7805 #[test]
7806 fn parse_fminnmp_2d() {
7807 assert_eq!(
7808 parse_inst("fminnmp.2d v3, v4, v5"),
7809 Inst::FminnmpV2D {
7810 rd: FpReg::new(3),
7811 rn: FpReg::new(4),
7812 rm: FpReg::new(5)
7813 }
7814 );
7815 }
7816
7817 #[test]
7818 fn parse_fminnmp_2d_scalar() {
7819 assert_eq!(
7820 parse_inst("fminnmp.2d d1, v2"),
7821 Inst::FminnmpV2DScalar {
7822 rd: FpReg::new(1),
7823 rn: FpReg::new(2)
7824 }
7825 );
7826 }
7827
7828 #[test]
7829 fn parse_fabs_2d() {
7830 assert_eq!(
7831 parse_inst("fabs.2d v0, v0"),
7832 Inst::FabsV2D {
7833 rd: FpReg::new(0),
7834 rn: FpReg::new(0)
7835 }
7836 );
7837 }
7838
7839 #[test]
7840 fn parse_fneg_2d() {
7841 assert_eq!(
7842 parse_inst("fneg.2d v3, v4"),
7843 Inst::FnegV2D {
7844 rd: FpReg::new(3),
7845 rn: FpReg::new(4)
7846 }
7847 );
7848 }
7849
7850 #[test]
7851 fn parse_fsqrt_2d() {
7852 assert_eq!(
7853 parse_inst("fsqrt.2d v1, v2"),
7854 Inst::FsqrtV2D {
7855 rd: FpReg::new(1),
7856 rn: FpReg::new(2)
7857 }
7858 );
7859 }
7860
7861 #[test]
7862 fn parse_scvtf_2d() {
7863 assert_eq!(
7864 parse_inst("scvtf.2d v0, v0"),
7865 Inst::ScvtfV2D {
7866 rd: FpReg::new(0),
7867 rn: FpReg::new(0)
7868 }
7869 );
7870 }
7871
7872 #[test]
7873 fn parse_ucvtf_2d() {
7874 assert_eq!(
7875 parse_inst("ucvtf.2d v1, v2"),
7876 Inst::UcvtfV2D {
7877 rd: FpReg::new(1),
7878 rn: FpReg::new(2)
7879 }
7880 );
7881 }
7882
7883 #[test]
7884 fn parse_fcvtzs_2d() {
7885 assert_eq!(
7886 parse_inst("fcvtzs.2d v3, v4"),
7887 Inst::FcvtzsV2D {
7888 rd: FpReg::new(3),
7889 rn: FpReg::new(4)
7890 }
7891 );
7892 }
7893
7894 #[test]
7895 fn parse_fcvtzu_2d() {
7896 assert_eq!(
7897 parse_inst("fcvtzu.2d v5, v6"),
7898 Inst::FcvtzuV2D {
7899 rd: FpReg::new(5),
7900 rn: FpReg::new(6)
7901 }
7902 );
7903 }
7904
7905 #[test]
7906 fn parse_frecpe_2d() {
7907 assert_eq!(
7908 parse_inst("frecpe.2d v0, v0"),
7909 Inst::FrecpeV2D {
7910 rd: FpReg::new(0),
7911 rn: FpReg::new(0)
7912 }
7913 );
7914 }
7915
7916 #[test]
7917 fn parse_frecps_2d() {
7918 assert_eq!(
7919 parse_inst("frecps.2d v1, v2, v3"),
7920 Inst::FrecpsV2D {
7921 rd: FpReg::new(1),
7922 rn: FpReg::new(2),
7923 rm: FpReg::new(3)
7924 }
7925 );
7926 }
7927
7928 #[test]
7929 fn parse_frsqrte_2d() {
7930 assert_eq!(
7931 parse_inst("frsqrte.2d v4, v5"),
7932 Inst::FrsqrteV2D {
7933 rd: FpReg::new(4),
7934 rn: FpReg::new(5)
7935 }
7936 );
7937 }
7938
7939 #[test]
7940 fn parse_frsqrts_2d() {
7941 assert_eq!(
7942 parse_inst("frsqrts.2d v6, v7, v8"),
7943 Inst::FrsqrtsV2D {
7944 rd: FpReg::new(6),
7945 rn: FpReg::new(7),
7946 rm: FpReg::new(8)
7947 }
7948 );
7949 }
7950
7951 #[test]
7952 fn parse_frintn_2d() {
7953 assert_eq!(
7954 parse_inst("frintn.2d v0, v0"),
7955 Inst::FrintnV2D {
7956 rd: FpReg::new(0),
7957 rn: FpReg::new(0)
7958 }
7959 );
7960 }
7961
7962 #[test]
7963 fn parse_frintm_2d() {
7964 assert_eq!(
7965 parse_inst("frintm.2d v1, v2"),
7966 Inst::FrintmV2D {
7967 rd: FpReg::new(1),
7968 rn: FpReg::new(2)
7969 }
7970 );
7971 }
7972
7973 #[test]
7974 fn parse_frintp_2d() {
7975 assert_eq!(
7976 parse_inst("frintp.2d v3, v4"),
7977 Inst::FrintpV2D {
7978 rd: FpReg::new(3),
7979 rn: FpReg::new(4)
7980 }
7981 );
7982 }
7983
7984 #[test]
7985 fn parse_frintz_2d() {
7986 assert_eq!(
7987 parse_inst("frintz.2d v5, v6"),
7988 Inst::FrintzV2D {
7989 rd: FpReg::new(5),
7990 rn: FpReg::new(6)
7991 }
7992 );
7993 }
7994
7995 #[test]
7996 fn parse_frinta_2d() {
7997 assert_eq!(
7998 parse_inst("frinta.2d v7, v8"),
7999 Inst::FrintaV2D {
8000 rd: FpReg::new(7),
8001 rn: FpReg::new(8)
8002 }
8003 );
8004 }
8005
8006 #[test]
8007 fn parse_frinti_2d() {
8008 assert_eq!(
8009 parse_inst("frinti.2d v9, v10"),
8010 Inst::FrintiV2D {
8011 rd: FpReg::new(9),
8012 rn: FpReg::new(10)
8013 }
8014 );
8015 }
8016
8017 #[test]
8018 fn parse_add_4s() {
8019 assert_eq!(
8020 parse_inst("add.4s v0, v1, v2"),
8021 Inst::AddV4S {
8022 rd: FpReg::new(0),
8023 rn: FpReg::new(1),
8024 rm: FpReg::new(2)
8025 }
8026 );
8027 }
8028
8029 #[test]
8030 fn parse_fminp_2d() {
8031 assert_eq!(
8032 parse_inst("fminp.2d v6, v7, v8"),
8033 Inst::FminpV2D {
8034 rd: FpReg::new(6),
8035 rn: FpReg::new(7),
8036 rm: FpReg::new(8)
8037 }
8038 );
8039 }
8040
8041 #[test]
8042 fn parse_fminp_2d_scalar() {
8043 assert_eq!(
8044 parse_inst("fminp.2d d3, v4"),
8045 Inst::FminpV2DScalar {
8046 rd: FpReg::new(3),
8047 rn: FpReg::new(4)
8048 }
8049 );
8050 }
8051
8052 #[test]
8053 fn parse_addp_4s() {
8054 assert_eq!(
8055 parse_inst("addp.4s v0, v1, v2"),
8056 Inst::AddpV4S {
8057 rd: FpReg::new(0),
8058 rn: FpReg::new(1),
8059 rm: FpReg::new(2)
8060 }
8061 );
8062 }
8063
8064 #[test]
8065 fn parse_addp_2d() {
8066 assert_eq!(
8067 parse_inst("addp.2d v0, v1, v2"),
8068 Inst::AddpV2D {
8069 rd: FpReg::new(0),
8070 rn: FpReg::new(1),
8071 rm: FpReg::new(2)
8072 }
8073 );
8074 }
8075
8076 #[test]
8077 fn parse_addp_8h() {
8078 assert_eq!(
8079 parse_inst("addp.8h v0, v1, v2"),
8080 Inst::AddpV8H {
8081 rd: FpReg::new(0),
8082 rn: FpReg::new(1),
8083 rm: FpReg::new(2)
8084 }
8085 );
8086 }
8087
8088 #[test]
8089 fn parse_addp_16b() {
8090 assert_eq!(
8091 parse_inst("addp.16b v6, v7, v8"),
8092 Inst::AddpV16B {
8093 rd: FpReg::new(6),
8094 rn: FpReg::new(7),
8095 rm: FpReg::new(8)
8096 }
8097 );
8098 }
8099
8100 #[test]
8101 fn parse_fmax_2d() {
8102 assert_eq!(
8103 parse_inst("fmax.2d v0, v0, v1"),
8104 Inst::FmaxV2D {
8105 rd: FpReg::new(0),
8106 rn: FpReg::new(0),
8107 rm: FpReg::new(1)
8108 }
8109 );
8110 }
8111
8112 #[test]
8113 fn parse_fmax_4s() {
8114 assert_eq!(
8115 parse_inst("fmax.4s v0, v0, v1"),
8116 Inst::FmaxV4S {
8117 rd: FpReg::new(0),
8118 rn: FpReg::new(0),
8119 rm: FpReg::new(1)
8120 }
8121 );
8122 }
8123
8124 #[test]
8125 fn parse_fmaxnm_4s() {
8126 assert_eq!(
8127 parse_inst("fmaxnm.4s v0, v1, v2"),
8128 Inst::FmaxnmV4S {
8129 rd: FpReg::new(0),
8130 rn: FpReg::new(1),
8131 rm: FpReg::new(2)
8132 }
8133 );
8134 }
8135
8136 #[test]
8137 fn parse_fmin_2d() {
8138 assert_eq!(
8139 parse_inst("fmin.2d v2, v3, v4"),
8140 Inst::FminV2D {
8141 rd: FpReg::new(2),
8142 rn: FpReg::new(3),
8143 rm: FpReg::new(4)
8144 }
8145 );
8146 }
8147
8148 #[test]
8149 fn parse_fmaxnm_2d() {
8150 assert_eq!(
8151 parse_inst("fmaxnm.2d v0, v0, v1"),
8152 Inst::FmaxnmV2D {
8153 rd: FpReg::new(0),
8154 rn: FpReg::new(0),
8155 rm: FpReg::new(1)
8156 }
8157 );
8158 }
8159
8160 #[test]
8161 fn parse_fmin_4s() {
8162 assert_eq!(
8163 parse_inst("fmin.4s v2, v3, v4"),
8164 Inst::FminV4S {
8165 rd: FpReg::new(2),
8166 rn: FpReg::new(3),
8167 rm: FpReg::new(4)
8168 }
8169 );
8170 }
8171
8172 #[test]
8173 fn parse_fminnm_4s() {
8174 assert_eq!(
8175 parse_inst("fminnm.4s v3, v4, v5"),
8176 Inst::FminnmV4S {
8177 rd: FpReg::new(3),
8178 rn: FpReg::new(4),
8179 rm: FpReg::new(5)
8180 }
8181 );
8182 }
8183
8184 #[test]
8185 fn parse_fminnm_2d() {
8186 assert_eq!(
8187 parse_inst("fminnm.2d v2, v3, v4"),
8188 Inst::FminnmV2D {
8189 rd: FpReg::new(2),
8190 rn: FpReg::new(3),
8191 rm: FpReg::new(4)
8192 }
8193 );
8194 }
8195
8196 #[test]
8197 fn parse_smax_4s() {
8198 assert_eq!(
8199 parse_inst("smax.4s v5, v6, v7"),
8200 Inst::SmaxV4S {
8201 rd: FpReg::new(5),
8202 rn: FpReg::new(6),
8203 rm: FpReg::new(7)
8204 }
8205 );
8206 }
8207
8208 #[test]
8209 fn parse_smaxp_4s() {
8210 assert_eq!(
8211 parse_inst("smaxp.4s v6, v7, v8"),
8212 Inst::SmaxpV4S {
8213 rd: FpReg::new(6),
8214 rn: FpReg::new(7),
8215 rm: FpReg::new(8)
8216 }
8217 );
8218 }
8219
8220 #[test]
8221 fn parse_smaxp_8h() {
8222 assert_eq!(
8223 parse_inst("smaxp.8h v6, v7, v8"),
8224 Inst::SmaxpV8H {
8225 rd: FpReg::new(6),
8226 rn: FpReg::new(7),
8227 rm: FpReg::new(8)
8228 }
8229 );
8230 }
8231
8232 #[test]
8233 fn parse_smaxp_16b() {
8234 assert_eq!(
8235 parse_inst("smaxp.16b v6, v7, v8"),
8236 Inst::SmaxpV16B {
8237 rd: FpReg::new(6),
8238 rn: FpReg::new(7),
8239 rm: FpReg::new(8)
8240 }
8241 );
8242 }
8243
8244 #[test]
8245 fn parse_smin_4s() {
8246 assert_eq!(
8247 parse_inst("smin.4s v8, v9, v10"),
8248 Inst::SminV4S {
8249 rd: FpReg::new(8),
8250 rn: FpReg::new(9),
8251 rm: FpReg::new(10)
8252 }
8253 );
8254 }
8255
8256 #[test]
8257 fn parse_sminp_4s() {
8258 assert_eq!(
8259 parse_inst("sminp.4s v9, v10, v11"),
8260 Inst::SminpV4S {
8261 rd: FpReg::new(9),
8262 rn: FpReg::new(10),
8263 rm: FpReg::new(11)
8264 }
8265 );
8266 }
8267
8268 #[test]
8269 fn parse_sminp_8h() {
8270 assert_eq!(
8271 parse_inst("sminp.8h v9, v10, v11"),
8272 Inst::SminpV8H {
8273 rd: FpReg::new(9),
8274 rn: FpReg::new(10),
8275 rm: FpReg::new(11)
8276 }
8277 );
8278 }
8279
8280 #[test]
8281 fn parse_sminp_16b() {
8282 assert_eq!(
8283 parse_inst("sminp.16b v9, v10, v11"),
8284 Inst::SminpV16B {
8285 rd: FpReg::new(9),
8286 rn: FpReg::new(10),
8287 rm: FpReg::new(11)
8288 }
8289 );
8290 }
8291
8292 #[test]
8293 fn parse_umax_4s() {
8294 assert_eq!(
8295 parse_inst("umax.4s v0, v0, v1"),
8296 Inst::UmaxV4S {
8297 rd: FpReg::new(0),
8298 rn: FpReg::new(0),
8299 rm: FpReg::new(1)
8300 }
8301 );
8302 }
8303
8304 #[test]
8305 fn parse_umaxp_4s() {
8306 assert_eq!(
8307 parse_inst("umaxp.4s v0, v1, v2"),
8308 Inst::UmaxpV4S {
8309 rd: FpReg::new(0),
8310 rn: FpReg::new(1),
8311 rm: FpReg::new(2)
8312 }
8313 );
8314 }
8315
8316 #[test]
8317 fn parse_umaxp_8h() {
8318 assert_eq!(
8319 parse_inst("umaxp.8h v0, v1, v2"),
8320 Inst::UmaxpV8H {
8321 rd: FpReg::new(0),
8322 rn: FpReg::new(1),
8323 rm: FpReg::new(2)
8324 }
8325 );
8326 }
8327
8328 #[test]
8329 fn parse_umaxp_16b() {
8330 assert_eq!(
8331 parse_inst("umaxp.16b v0, v1, v2"),
8332 Inst::UmaxpV16B {
8333 rd: FpReg::new(0),
8334 rn: FpReg::new(1),
8335 rm: FpReg::new(2)
8336 }
8337 );
8338 }
8339
8340 #[test]
8341 fn parse_umin_4s() {
8342 assert_eq!(
8343 parse_inst("umin.4s v2, v3, v4"),
8344 Inst::UminV4S {
8345 rd: FpReg::new(2),
8346 rn: FpReg::new(3),
8347 rm: FpReg::new(4)
8348 }
8349 );
8350 }
8351
8352 #[test]
8353 fn parse_uminp_4s() {
8354 assert_eq!(
8355 parse_inst("uminp.4s v3, v4, v5"),
8356 Inst::UminpV4S {
8357 rd: FpReg::new(3),
8358 rn: FpReg::new(4),
8359 rm: FpReg::new(5)
8360 }
8361 );
8362 }
8363
8364 #[test]
8365 fn parse_uminp_8h() {
8366 assert_eq!(
8367 parse_inst("uminp.8h v3, v4, v5"),
8368 Inst::UminpV8H {
8369 rd: FpReg::new(3),
8370 rn: FpReg::new(4),
8371 rm: FpReg::new(5)
8372 }
8373 );
8374 }
8375
8376 #[test]
8377 fn parse_uminp_16b() {
8378 assert_eq!(
8379 parse_inst("uminp.16b v3, v4, v5"),
8380 Inst::UminpV16B {
8381 rd: FpReg::new(3),
8382 rn: FpReg::new(4),
8383 rm: FpReg::new(5)
8384 }
8385 );
8386 }
8387
8388 #[test]
8389 fn parse_addv_4s() {
8390 assert_eq!(
8391 parse_inst("addv.4s s0, v0"),
8392 Inst::AddvV4S {
8393 rd: FpReg::new(0),
8394 rn: FpReg::new(0)
8395 }
8396 );
8397 }
8398
8399 #[test]
8400 fn parse_addv_16b() {
8401 assert_eq!(
8402 parse_inst("addv.16b b0, v0"),
8403 Inst::AddvV16B {
8404 rd: FpReg::new(0),
8405 rn: FpReg::new(0)
8406 }
8407 );
8408 }
8409
8410 #[test]
8411 fn parse_addv_8h() {
8412 assert_eq!(
8413 parse_inst("addv.8h h0, v0"),
8414 Inst::AddvV8H {
8415 rd: FpReg::new(0),
8416 rn: FpReg::new(0)
8417 }
8418 );
8419 }
8420
8421 #[test]
8422 fn parse_faddp_2s() {
8423 assert_eq!(
8424 parse_inst("faddp.2s s3, v4"),
8425 Inst::FaddpV2S {
8426 rd: FpReg::new(3),
8427 rn: FpReg::new(4)
8428 }
8429 );
8430 }
8431
8432 #[test]
8433 fn parse_faddp_2d_scalar() {
8434 assert_eq!(
8435 parse_inst("faddp.2d d0, v0"),
8436 Inst::FaddpV2DScalar {
8437 rd: FpReg::new(0),
8438 rn: FpReg::new(0)
8439 }
8440 );
8441 }
8442
8443 #[test]
8444 fn parse_fmaxv_4s() {
8445 assert_eq!(
8446 parse_inst("fmaxv.4s s1, v2"),
8447 Inst::FmaxvV4S {
8448 rd: FpReg::new(1),
8449 rn: FpReg::new(2)
8450 }
8451 );
8452 }
8453
8454 #[test]
8455 fn parse_fmaxnmv_4s() {
8456 assert_eq!(
8457 parse_inst("fmaxnmv.4s s1, v2"),
8458 Inst::FmaxnmvV4S {
8459 rd: FpReg::new(1),
8460 rn: FpReg::new(2)
8461 }
8462 );
8463 }
8464
8465 #[test]
8466 fn parse_fminv_4s() {
8467 assert_eq!(
8468 parse_inst("fminv.4s s3, v4"),
8469 Inst::FminvV4S {
8470 rd: FpReg::new(3),
8471 rn: FpReg::new(4)
8472 }
8473 );
8474 }
8475
8476 #[test]
8477 fn parse_fminnmv_4s() {
8478 assert_eq!(
8479 parse_inst("fminnmv.4s s3, v4"),
8480 Inst::FminnmvV4S {
8481 rd: FpReg::new(3),
8482 rn: FpReg::new(4)
8483 }
8484 );
8485 }
8486
8487 #[test]
8488 fn parse_umaxv_4s() {
8489 assert_eq!(
8490 parse_inst("umaxv.4s s1, v2"),
8491 Inst::UmaxvV4S {
8492 rd: FpReg::new(1),
8493 rn: FpReg::new(2)
8494 }
8495 );
8496 }
8497
8498 #[test]
8499 fn parse_umaxv_16b() {
8500 assert_eq!(
8501 parse_inst("umaxv.16b b0, v0"),
8502 Inst::UmaxvV16B {
8503 rd: FpReg::new(0),
8504 rn: FpReg::new(0)
8505 }
8506 );
8507 }
8508
8509 #[test]
8510 fn parse_umaxv_8h() {
8511 assert_eq!(
8512 parse_inst("umaxv.8h h0, v0"),
8513 Inst::UmaxvV8H {
8514 rd: FpReg::new(0),
8515 rn: FpReg::new(0)
8516 }
8517 );
8518 }
8519
8520 #[test]
8521 fn parse_smaxv_4s() {
8522 assert_eq!(
8523 parse_inst("smaxv.4s s3, v4"),
8524 Inst::SmaxvV4S {
8525 rd: FpReg::new(3),
8526 rn: FpReg::new(4)
8527 }
8528 );
8529 }
8530
8531 #[test]
8532 fn parse_smaxv_16b() {
8533 assert_eq!(
8534 parse_inst("smaxv.16b b0, v0"),
8535 Inst::SmaxvV16B {
8536 rd: FpReg::new(0),
8537 rn: FpReg::new(0)
8538 }
8539 );
8540 }
8541
8542 #[test]
8543 fn parse_smaxv_8h() {
8544 assert_eq!(
8545 parse_inst("smaxv.8h h0, v0"),
8546 Inst::SmaxvV8H {
8547 rd: FpReg::new(0),
8548 rn: FpReg::new(0)
8549 }
8550 );
8551 }
8552
8553 #[test]
8554 fn parse_uminv_4s() {
8555 assert_eq!(
8556 parse_inst("uminv.4s s1, v2"),
8557 Inst::UminvV4S {
8558 rd: FpReg::new(1),
8559 rn: FpReg::new(2)
8560 }
8561 );
8562 }
8563
8564 #[test]
8565 fn parse_uminv_16b() {
8566 assert_eq!(
8567 parse_inst("uminv.16b b0, v0"),
8568 Inst::UminvV16B {
8569 rd: FpReg::new(0),
8570 rn: FpReg::new(0)
8571 }
8572 );
8573 }
8574
8575 #[test]
8576 fn parse_uminv_8h() {
8577 assert_eq!(
8578 parse_inst("uminv.8h h0, v0"),
8579 Inst::UminvV8H {
8580 rd: FpReg::new(0),
8581 rn: FpReg::new(0)
8582 }
8583 );
8584 }
8585
8586 #[test]
8587 fn parse_sminv_4s() {
8588 assert_eq!(
8589 parse_inst("sminv.4s s3, v4"),
8590 Inst::SminvV4S {
8591 rd: FpReg::new(3),
8592 rn: FpReg::new(4)
8593 }
8594 );
8595 }
8596
8597 #[test]
8598 fn parse_sminv_16b() {
8599 assert_eq!(
8600 parse_inst("sminv.16b b0, v0"),
8601 Inst::SminvV16B {
8602 rd: FpReg::new(0),
8603 rn: FpReg::new(0)
8604 }
8605 );
8606 }
8607
8608 #[test]
8609 fn parse_sminv_8h() {
8610 assert_eq!(
8611 parse_inst("sminv.8h h0, v0"),
8612 Inst::SminvV8H {
8613 rd: FpReg::new(0),
8614 rn: FpReg::new(0)
8615 }
8616 );
8617 }
8618
8619 #[test]
8620 fn parse_fsub_4s() {
8621 assert_eq!(
8622 parse_inst("fsub.4s v3, v4, v5"),
8623 Inst::FsubV4S {
8624 rd: FpReg::new(3),
8625 rn: FpReg::new(4),
8626 rm: FpReg::new(5)
8627 }
8628 );
8629 }
8630
8631 #[test]
8632 fn parse_fsub_2d() {
8633 assert_eq!(
8634 parse_inst("fsub.2d v3, v4, v5"),
8635 Inst::FsubV2D {
8636 rd: FpReg::new(3),
8637 rn: FpReg::new(4),
8638 rm: FpReg::new(5)
8639 }
8640 );
8641 }
8642
8643 #[test]
8644 fn parse_sub_4s() {
8645 assert_eq!(
8646 parse_inst("sub.4s v3, v4, v5"),
8647 Inst::SubV4S {
8648 rd: FpReg::new(3),
8649 rn: FpReg::new(4),
8650 rm: FpReg::new(5)
8651 }
8652 );
8653 }
8654
8655 #[test]
8656 fn parse_fmul_4s() {
8657 assert_eq!(
8658 parse_inst("fmul.4s v6, v7, v8"),
8659 Inst::FmulV4S {
8660 rd: FpReg::new(6),
8661 rn: FpReg::new(7),
8662 rm: FpReg::new(8)
8663 }
8664 );
8665 }
8666
8667 #[test]
8668 fn parse_fmul_2d() {
8669 assert_eq!(
8670 parse_inst("fmul.2d v6, v7, v8"),
8671 Inst::FmulV2D {
8672 rd: FpReg::new(6),
8673 rn: FpReg::new(7),
8674 rm: FpReg::new(8)
8675 }
8676 );
8677 }
8678
8679 #[test]
8680 fn parse_fdiv_4s() {
8681 assert_eq!(
8682 parse_inst("fdiv.4s v9, v10, v11"),
8683 Inst::FdivV4S {
8684 rd: FpReg::new(9),
8685 rn: FpReg::new(10),
8686 rm: FpReg::new(11)
8687 }
8688 );
8689 }
8690
8691 #[test]
8692 fn parse_fdiv_2d() {
8693 assert_eq!(
8694 parse_inst("fdiv.2d v9, v10, v11"),
8695 Inst::FdivV2D {
8696 rd: FpReg::new(9),
8697 rn: FpReg::new(10),
8698 rm: FpReg::new(11)
8699 }
8700 );
8701 }
8702
8703 #[test]
8704 fn parse_fabd_2d() {
8705 assert_eq!(
8706 parse_inst("fabd.2d v0, v1, v2"),
8707 Inst::FabdV2D {
8708 rd: FpReg::new(0),
8709 rn: FpReg::new(1),
8710 rm: FpReg::new(2)
8711 }
8712 );
8713 }
8714
8715 #[test]
8716 fn parse_and_16b() {
8717 assert_eq!(
8718 parse_inst("and.16b v6, v7, v8"),
8719 Inst::AndV16B {
8720 rd: FpReg::new(6),
8721 rn: FpReg::new(7),
8722 rm: FpReg::new(8)
8723 }
8724 );
8725 }
8726
8727 #[test]
8728 fn parse_bic_16b() {
8729 assert_eq!(
8730 parse_inst("bic.16b v5, v6, v7"),
8731 Inst::BicV16B {
8732 rd: FpReg::new(5),
8733 rn: FpReg::new(6),
8734 rm: FpReg::new(7)
8735 }
8736 );
8737 }
8738
8739 #[test]
8740 fn parse_bif_16b() {
8741 assert_eq!(
8742 parse_inst("bif.16b v0, v1, v2"),
8743 Inst::BifV16B {
8744 rd: FpReg::new(0),
8745 rn: FpReg::new(1),
8746 rm: FpReg::new(2)
8747 }
8748 );
8749 }
8750
8751 #[test]
8752 fn parse_bit_16b() {
8753 assert_eq!(
8754 parse_inst("bit.16b v3, v4, v5"),
8755 Inst::BitV16B {
8756 rd: FpReg::new(3),
8757 rn: FpReg::new(4),
8758 rm: FpReg::new(5)
8759 }
8760 );
8761 }
8762
8763 #[test]
8764 fn parse_bsl_16b() {
8765 assert_eq!(
8766 parse_inst("bsl.16b v6, v7, v8"),
8767 Inst::BslV16B {
8768 rd: FpReg::new(6),
8769 rn: FpReg::new(7),
8770 rm: FpReg::new(8)
8771 }
8772 );
8773 }
8774
8775 #[test]
8776 fn parse_cmeq_4s() {
8777 assert_eq!(
8778 parse_inst("cmeq.4s v0, v0, v1"),
8779 Inst::CmeqV4S {
8780 rd: FpReg::new(0),
8781 rn: FpReg::new(0),
8782 rm: FpReg::new(1)
8783 }
8784 );
8785 }
8786
8787 #[test]
8788 fn parse_fcmeq_4s() {
8789 assert_eq!(
8790 parse_inst("fcmeq.4s v0, v1, v2"),
8791 Inst::FcmeqV4S {
8792 rd: FpReg::new(0),
8793 rn: FpReg::new(1),
8794 rm: FpReg::new(2)
8795 }
8796 );
8797 }
8798
8799 #[test]
8800 fn parse_fcmeq_2d() {
8801 assert_eq!(
8802 parse_inst("fcmeq.2d v0, v1, v2"),
8803 Inst::FcmeqV2D {
8804 rd: FpReg::new(0),
8805 rn: FpReg::new(1),
8806 rm: FpReg::new(2)
8807 }
8808 );
8809 }
8810
8811 #[test]
8812 fn parse_cmhs_4s() {
8813 assert_eq!(
8814 parse_inst("cmhs.4s v0, v0, v1"),
8815 Inst::CmhsV4S {
8816 rd: FpReg::new(0),
8817 rn: FpReg::new(0),
8818 rm: FpReg::new(1)
8819 }
8820 );
8821 }
8822
8823 #[test]
8824 fn parse_cmhi_4s() {
8825 assert_eq!(
8826 parse_inst("cmhi.4s v2, v3, v4"),
8827 Inst::CmhiV4S {
8828 rd: FpReg::new(2),
8829 rn: FpReg::new(3),
8830 rm: FpReg::new(4)
8831 }
8832 );
8833 }
8834
8835 #[test]
8836 fn parse_cmge_4s() {
8837 assert_eq!(
8838 parse_inst("cmge.4s v5, v6, v7"),
8839 Inst::CmgeV4S {
8840 rd: FpReg::new(5),
8841 rn: FpReg::new(6),
8842 rm: FpReg::new(7)
8843 }
8844 );
8845 }
8846
8847 #[test]
8848 fn parse_fcmge_4s() {
8849 assert_eq!(
8850 parse_inst("fcmge.4s v3, v4, v5"),
8851 Inst::FcmgeV4S {
8852 rd: FpReg::new(3),
8853 rn: FpReg::new(4),
8854 rm: FpReg::new(5)
8855 }
8856 );
8857 }
8858
8859 #[test]
8860 fn parse_fcmge_2d() {
8861 assert_eq!(
8862 parse_inst("fcmge.2d v3, v4, v5"),
8863 Inst::FcmgeV2D {
8864 rd: FpReg::new(3),
8865 rn: FpReg::new(4),
8866 rm: FpReg::new(5)
8867 }
8868 );
8869 }
8870
8871 #[test]
8872 fn parse_fcmge_2d_zero() {
8873 assert_eq!(
8874 parse_inst("fcmge.2d v0, v0, #0.0"),
8875 Inst::FcmgeZeroV2D {
8876 rd: FpReg::new(0),
8877 rn: FpReg::new(0)
8878 }
8879 );
8880 }
8881
8882 #[test]
8883 fn parse_cmgt_4s() {
8884 assert_eq!(
8885 parse_inst("cmgt.4s v2, v3, v4"),
8886 Inst::CmgtV4S {
8887 rd: FpReg::new(2),
8888 rn: FpReg::new(3),
8889 rm: FpReg::new(4)
8890 }
8891 );
8892 }
8893
8894 #[test]
8895 fn parse_fcmgt_4s() {
8896 assert_eq!(
8897 parse_inst("fcmgt.4s v6, v7, v8"),
8898 Inst::FcmgtV4S {
8899 rd: FpReg::new(6),
8900 rn: FpReg::new(7),
8901 rm: FpReg::new(8)
8902 }
8903 );
8904 }
8905
8906 #[test]
8907 fn parse_fcmgt_2d() {
8908 assert_eq!(
8909 parse_inst("fcmgt.2d v6, v7, v8"),
8910 Inst::FcmgtV2D {
8911 rd: FpReg::new(6),
8912 rn: FpReg::new(7),
8913 rm: FpReg::new(8)
8914 }
8915 );
8916 }
8917
8918 #[test]
8919 fn parse_fcmgt_2d_zero() {
8920 assert_eq!(
8921 parse_inst("fcmgt.2d v1, v1, #0.0"),
8922 Inst::FcmgtZeroV2D {
8923 rd: FpReg::new(1),
8924 rn: FpReg::new(1)
8925 }
8926 );
8927 }
8928
8929 #[test]
8930 fn parse_fcmle_2d_zero() {
8931 assert_eq!(
8932 parse_inst("fcmle.2d v2, v2, #0.0"),
8933 Inst::FcmleZeroV2D {
8934 rd: FpReg::new(2),
8935 rn: FpReg::new(2)
8936 }
8937 );
8938 }
8939
8940 #[test]
8941 fn parse_fcmlt_2d_zero() {
8942 assert_eq!(
8943 parse_inst("fcmlt.2d v3, v3, #0.0"),
8944 Inst::FcmltZeroV2D {
8945 rd: FpReg::new(3),
8946 rn: FpReg::new(3)
8947 }
8948 );
8949 }
8950
8951 #[test]
8952 fn parse_orr_16b() {
8953 assert_eq!(
8954 parse_inst("orr.16b v9, v10, v11"),
8955 Inst::OrrV16B {
8956 rd: FpReg::new(9),
8957 rn: FpReg::new(10),
8958 rm: FpReg::new(11)
8959 }
8960 );
8961 }
8962
8963 #[test]
8964 fn parse_eor_16b() {
8965 assert_eq!(
8966 parse_inst("eor.16b v12, v13, v14"),
8967 Inst::EorV16B {
8968 rd: FpReg::new(12),
8969 rn: FpReg::new(13),
8970 rm: FpReg::new(14)
8971 }
8972 );
8973 }
8974
8975 #[test]
8976 fn parse_ext_16b() {
8977 assert_eq!(
8978 parse_inst("ext.16b v0, v0, v0, #8"),
8979 Inst::ExtV16B {
8980 rd: FpReg::new(0),
8981 rn: FpReg::new(0),
8982 rm: FpReg::new(0),
8983 index: 8
8984 }
8985 );
8986 }
8987
8988 #[test]
8989 fn parse_rev64_4s() {
8990 assert_eq!(
8991 parse_inst("rev64.4s v1, v2"),
8992 Inst::Rev64V4S {
8993 rd: FpReg::new(1),
8994 rn: FpReg::new(2)
8995 }
8996 );
8997 }
8998
8999 #[test]
9000 fn parse_zip1_4s() {
9001 assert_eq!(
9002 parse_inst("zip1.4s v0, v0, v1"),
9003 Inst::Zip1V4S {
9004 rd: FpReg::new(0),
9005 rn: FpReg::new(0),
9006 rm: FpReg::new(1)
9007 }
9008 );
9009 }
9010
9011 #[test]
9012 fn parse_zip2_4s() {
9013 assert_eq!(
9014 parse_inst("zip2.4s v2, v3, v4"),
9015 Inst::Zip2V4S {
9016 rd: FpReg::new(2),
9017 rn: FpReg::new(3),
9018 rm: FpReg::new(4)
9019 }
9020 );
9021 }
9022
9023 #[test]
9024 fn parse_uzp1_4s() {
9025 assert_eq!(
9026 parse_inst("uzp1.4s v5, v6, v7"),
9027 Inst::Uzp1V4S {
9028 rd: FpReg::new(5),
9029 rn: FpReg::new(6),
9030 rm: FpReg::new(7)
9031 }
9032 );
9033 }
9034
9035 #[test]
9036 fn parse_uzp2_4s() {
9037 assert_eq!(
9038 parse_inst("uzp2.4s v8, v9, v10"),
9039 Inst::Uzp2V4S {
9040 rd: FpReg::new(8),
9041 rn: FpReg::new(9),
9042 rm: FpReg::new(10)
9043 }
9044 );
9045 }
9046
9047 #[test]
9048 fn parse_trn1_4s() {
9049 assert_eq!(
9050 parse_inst("trn1.4s v11, v12, v13"),
9051 Inst::Trn1V4S {
9052 rd: FpReg::new(11),
9053 rn: FpReg::new(12),
9054 rm: FpReg::new(13)
9055 }
9056 );
9057 }
9058
9059 #[test]
9060 fn parse_trn2_4s() {
9061 assert_eq!(
9062 parse_inst("trn2.4s v3, v4, v5"),
9063 Inst::Trn2V4S {
9064 rd: FpReg::new(3),
9065 rn: FpReg::new(4),
9066 rm: FpReg::new(5)
9067 }
9068 );
9069 }
9070
9071 #[test]
9072 fn parse_zip1_2d() {
9073 assert_eq!(
9074 parse_inst("zip1.2d v0, v1, v2"),
9075 Inst::Zip1V2D {
9076 rd: FpReg::new(0),
9077 rn: FpReg::new(1),
9078 rm: FpReg::new(2)
9079 }
9080 );
9081 }
9082
9083 #[test]
9084 fn parse_zip2_2d() {
9085 assert_eq!(
9086 parse_inst("zip2.2d v3, v4, v5"),
9087 Inst::Zip2V2D {
9088 rd: FpReg::new(3),
9089 rn: FpReg::new(4),
9090 rm: FpReg::new(5)
9091 }
9092 );
9093 }
9094
9095 #[test]
9096 fn parse_uzp1_2d() {
9097 assert_eq!(
9098 parse_inst("uzp1.2d v6, v7, v8"),
9099 Inst::Uzp1V2D {
9100 rd: FpReg::new(6),
9101 rn: FpReg::new(7),
9102 rm: FpReg::new(8)
9103 }
9104 );
9105 }
9106
9107 #[test]
9108 fn parse_uzp2_2d() {
9109 assert_eq!(
9110 parse_inst("uzp2.2d v9, v10, v11"),
9111 Inst::Uzp2V2D {
9112 rd: FpReg::new(9),
9113 rn: FpReg::new(10),
9114 rm: FpReg::new(11)
9115 }
9116 );
9117 }
9118
9119 #[test]
9120 fn parse_trn1_2d() {
9121 assert_eq!(
9122 parse_inst("trn1.2d v12, v13, v14"),
9123 Inst::Trn1V2D {
9124 rd: FpReg::new(12),
9125 rn: FpReg::new(13),
9126 rm: FpReg::new(14)
9127 }
9128 );
9129 }
9130
9131 #[test]
9132 fn parse_trn2_2d() {
9133 assert_eq!(
9134 parse_inst("trn2.2d v15, v16, v17"),
9135 Inst::Trn2V2D {
9136 rd: FpReg::new(15),
9137 rn: FpReg::new(16),
9138 rm: FpReg::new(17)
9139 }
9140 );
9141 }
9142
9143 #[test]
9144 fn parse_tbl_16b_single() {
9145 assert_eq!(
9146 parse_inst("tbl.16b v0, { v1 }, v2"),
9147 Inst::TblV16B {
9148 rd: FpReg::new(0),
9149 table: FpReg::new(1),
9150 table_len: 1,
9151 index: FpReg::new(2)
9152 }
9153 );
9154 }
9155
9156 #[test]
9157 fn parse_tbl_16b_pair() {
9158 assert_eq!(
9159 parse_inst("tbl.16b v3, { v4, v5 }, v6"),
9160 Inst::TblV16B {
9161 rd: FpReg::new(3),
9162 table: FpReg::new(4),
9163 table_len: 2,
9164 index: FpReg::new(6)
9165 }
9166 );
9167 }
9168
9169 #[test]
9170 fn parse_tbx_16b_pair() {
9171 assert_eq!(
9172 parse_inst("tbx.16b v21, { v22, v23 }, v24"),
9173 Inst::TbxV16B {
9174 rd: FpReg::new(21),
9175 table: FpReg::new(22),
9176 table_len: 2,
9177 index: FpReg::new(24)
9178 }
9179 );
9180 }
9181
9182 #[test]
9183 fn parse_tbl_16b_requires_consecutive_table_regs() {
9184 let err = parse("tbl.16b v0, { v1, v3 }, v2").unwrap_err();
9185 assert!(err.msg.contains("consecutive"), "got: {}", err);
9186 }
9187
9188 #[test]
9189 fn parse_mov_16b() {
9190 assert_eq!(
9191 parse_inst("mov.16b v0, v2"),
9192 Inst::MovV16B {
9193 rd: FpReg::new(0),
9194 rn: FpReg::new(2)
9195 }
9196 );
9197 }
9198
9199 #[test]
9200 fn parse_mov_8b() {
9201 assert_eq!(
9202 parse_inst("mov.8b v1, v3"),
9203 Inst::MovV8B {
9204 rd: FpReg::new(1),
9205 rn: FpReg::new(3)
9206 }
9207 );
9208 }
9209
9210 #[test]
9211 fn parse_mov_4s() {
9212 assert_eq!(
9213 parse_inst("mov.4s v4, v5"),
9214 Inst::MovV4S {
9215 rd: FpReg::new(4),
9216 rn: FpReg::new(5)
9217 }
9218 );
9219 }
9220
9221 #[test]
9222 fn parse_mov_2d() {
9223 assert_eq!(
9224 parse_inst("mov.2d v6, v7"),
9225 Inst::MovV2D {
9226 rd: FpReg::new(6),
9227 rn: FpReg::new(7)
9228 }
9229 );
9230 }
9231
9232 #[test]
9233 fn parse_mov_from_lane_s() {
9234 assert_eq!(
9235 parse_inst("mov s0, v1[2]"),
9236 Inst::MovFromLaneS {
9237 rd: FpReg::new(0),
9238 rn: FpReg::new(1),
9239 index: 2
9240 }
9241 );
9242 }
9243
9244 #[test]
9245 fn parse_fmov_reg_s() {
9246 assert_eq!(parse_inst("fmov s1, s2"), Inst::FmovRegS { rd: S1, rn: S2 });
9247 }
9248
9249 #[test]
9250 fn parse_fmov_reg_d() {
9251 assert_eq!(parse_inst("fmov d1, d2"), Inst::FmovRegD { rd: D1, rn: D2 });
9252 }
9253
9254 #[test]
9255 fn parse_fmov_to_s() {
9256 assert_eq!(parse_inst("fmov s0, w1"), Inst::FmovToS { rd: S0, rn: W1 });
9257 }
9258
9259 #[test]
9260 fn parse_fmov_from_s() {
9261 assert_eq!(
9262 parse_inst("fmov w0, s1"),
9263 Inst::FmovFromS { rd: W0, rn: S1 }
9264 );
9265 }
9266
9267 #[test]
9268 fn parse_mov_from_lane_d() {
9269 assert_eq!(
9270 parse_inst("mov d3, v4[1]"),
9271 Inst::MovFromLaneD {
9272 rd: FpReg::new(3),
9273 rn: FpReg::new(4),
9274 index: 1
9275 }
9276 );
9277 }
9278
9279 #[test]
9280 fn parse_mov_lane_s() {
9281 assert_eq!(
9282 parse_inst("mov.s v5[0], v6[0]"),
9283 Inst::MovLaneS {
9284 rd: FpReg::new(5),
9285 rd_index: 0,
9286 rn: FpReg::new(6),
9287 rn_index: 0
9288 }
9289 );
9290 }
9291
9292 #[test]
9293 fn parse_mov_lane_d() {
9294 assert_eq!(
9295 parse_inst("mov.d v7[1], v8[1]"),
9296 Inst::MovLaneD {
9297 rd: FpReg::new(7),
9298 rd_index: 1,
9299 rn: FpReg::new(8),
9300 rn_index: 1
9301 }
9302 );
9303 }
9304
9305 #[test]
9306 fn parse_mov_lane_h() {
9307 assert_eq!(
9308 parse_inst("mov.h v0[5], v1[0]"),
9309 Inst::MovLaneH {
9310 rd: FpReg::new(0),
9311 rd_index: 5,
9312 rn: FpReg::new(1),
9313 rn_index: 0
9314 }
9315 );
9316 }
9317
9318 #[test]
9319 fn parse_mov_lane_b() {
9320 assert_eq!(
9321 parse_inst("mov.b v0[7], v1[0]"),
9322 Inst::MovLaneB {
9323 rd: FpReg::new(0),
9324 rd_index: 7,
9325 rn: FpReg::new(1),
9326 rn_index: 0
9327 }
9328 );
9329 }
9330
9331 #[test]
9332 fn parse_mov_from_lane_gp_s() {
9333 assert_eq!(
9334 parse_inst("mov.s w0, v1[2]"),
9335 Inst::MovFromLaneGpS {
9336 rd: W0,
9337 rn: FpReg::new(1),
9338 index: 2
9339 }
9340 );
9341 }
9342
9343 #[test]
9344 fn parse_mov_from_lane_gp_d() {
9345 assert_eq!(
9346 parse_inst("mov.d x0, v1[1]"),
9347 Inst::MovFromLaneGpD {
9348 rd: X0,
9349 rn: FpReg::new(1),
9350 index: 1
9351 }
9352 );
9353 }
9354
9355 #[test]
9356 fn parse_umov_h() {
9357 assert_eq!(
9358 parse_inst("umov.h w1, v2[5]"),
9359 Inst::UmovFromLaneH {
9360 rd: W1,
9361 rn: FpReg::new(2),
9362 index: 5
9363 }
9364 );
9365 }
9366
9367 #[test]
9368 fn parse_umov_b() {
9369 assert_eq!(
9370 parse_inst("umov.b w3, v4[7]"),
9371 Inst::UmovFromLaneB {
9372 rd: W3,
9373 rn: FpReg::new(4),
9374 index: 7
9375 }
9376 );
9377 }
9378
9379 #[test]
9380 fn parse_smov_h() {
9381 assert_eq!(
9382 parse_inst("smov.h w1, v2[3]"),
9383 Inst::SmovFromLaneH {
9384 rd: W1,
9385 rn: FpReg::new(2),
9386 index: 3
9387 }
9388 );
9389 }
9390
9391 #[test]
9392 fn parse_smov_b() {
9393 assert_eq!(
9394 parse_inst("smov.b w0, v0[0]"),
9395 Inst::SmovFromLaneB {
9396 rd: W0,
9397 rn: FpReg::new(0),
9398 index: 0
9399 }
9400 );
9401 }
9402
9403 #[test]
9404 fn parse_mov_lane_from_gp_s() {
9405 assert_eq!(
9406 parse_inst("mov.s v5[1], w6"),
9407 Inst::MovLaneFromGpS {
9408 rd: FpReg::new(5),
9409 rd_index: 1,
9410 rn: W6
9411 }
9412 );
9413 }
9414
9415 #[test]
9416 fn parse_mov_lane_from_gp_d() {
9417 assert_eq!(
9418 parse_inst("mov.d v0[1], x1"),
9419 Inst::MovLaneFromGpD {
9420 rd: FpReg::new(0),
9421 rd_index: 1,
9422 rn: X1
9423 }
9424 );
9425 }
9426
9427 #[test]
9428 fn parse_mov_lane_from_gp_h() {
9429 assert_eq!(
9430 parse_inst("mov.h v7[5], w8"),
9431 Inst::MovLaneFromGpH {
9432 rd: FpReg::new(7),
9433 rd_index: 5,
9434 rn: W8
9435 }
9436 );
9437 }
9438
9439 #[test]
9440 fn parse_mov_lane_from_gp_b() {
9441 assert_eq!(
9442 parse_inst("mov.b v9[7], w10"),
9443 Inst::MovLaneFromGpB {
9444 rd: FpReg::new(9),
9445 rd_index: 7,
9446 rn: W10
9447 }
9448 );
9449 }
9450
9451 #[test]
9452 fn parse_ld1_lane_s() {
9453 assert_eq!(
9454 parse_inst("ld1.s { v0 }[1], [x8]"),
9455 Inst::Ld1LaneS {
9456 rt: FpReg::new(0),
9457 index: 1,
9458 rn: X8
9459 }
9460 );
9461 }
9462
9463 #[test]
9464 fn parse_ld1_lane_d() {
9465 assert_eq!(
9466 parse_inst("ld1.d { v2 }[1], [x10]"),
9467 Inst::Ld1LaneD {
9468 rt: FpReg::new(2),
9469 index: 1,
9470 rn: X10
9471 }
9472 );
9473 }
9474
9475 #[test]
9476 fn parse_ld1_lane_h() {
9477 assert_eq!(
9478 parse_inst("ld1.h { v3 }[5], [x11]"),
9479 Inst::Ld1LaneH {
9480 rt: FpReg::new(3),
9481 index: 5,
9482 rn: X11
9483 }
9484 );
9485 }
9486
9487 #[test]
9488 fn parse_ld1_lane_b() {
9489 assert_eq!(
9490 parse_inst("ld1.b { v4 }[7], [x12]"),
9491 Inst::Ld1LaneB {
9492 rt: FpReg::new(4),
9493 index: 7,
9494 rn: X12
9495 }
9496 );
9497 }
9498
9499 #[test]
9500 fn parse_dup_16b() {
9501 assert_eq!(
9502 parse_inst("dup.16b v0, v1[15]"),
9503 Inst::DupV16B {
9504 rd: FpReg::new(0),
9505 rn: FpReg::new(1),
9506 index: 15
9507 }
9508 );
9509 }
9510
9511 #[test]
9512 fn parse_dup_8h() {
9513 assert_eq!(
9514 parse_inst("dup.8h v1, v2[5]"),
9515 Inst::DupV8H {
9516 rd: FpReg::new(1),
9517 rn: FpReg::new(2),
9518 index: 5
9519 }
9520 );
9521 }
9522
9523 #[test]
9524 fn parse_dup_4s() {
9525 assert_eq!(
9526 parse_inst("dup.4s v3, v4[2]"),
9527 Inst::DupV4S {
9528 rd: FpReg::new(3),
9529 rn: FpReg::new(4),
9530 index: 2
9531 }
9532 );
9533 }
9534
9535 #[test]
9536 fn parse_dup_2d() {
9537 assert_eq!(
9538 parse_inst("dup.2d v5, v6[1]"),
9539 Inst::DupV2D {
9540 rd: FpReg::new(5),
9541 rn: FpReg::new(6),
9542 index: 1
9543 }
9544 );
9545 }
9546
9547 #[test]
9548 fn parse_fcmp_() {
9549 assert_eq!(parse_inst("fcmp d0, d1"), Inst::FcmpD { rn: D0, rm: D1 });
9550 }
9551
9552 #[test]
9553 fn parse_fmadd_() {
9554 assert_eq!(
9555 parse_inst("fmadd d0, d1, d2, d3"),
9556 Inst::FmaddD {
9557 rd: D0,
9558 rn: D1,
9559 rm: D2,
9560 ra: D3
9561 }
9562 );
9563 }
9564
9565 #[test]
9566 fn parse_fcvtzs_() {
9567 assert_eq!(
9568 parse_inst("fcvtzs x0, d1"),
9569 Inst::FcvtzsD { rd: X0, rn: D1 }
9570 );
9571 }
9572
9573 #[test]
9574 fn parse_scvtf_() {
9575 assert_eq!(parse_inst("scvtf d0, x1"), Inst::ScvtfD { rd: D0, rn: X1 });
9576 }
9577
9578 #[test]
9579 fn parse_fmov_to_fp() {
9580 assert_eq!(parse_inst("fmov d0, x1"), Inst::FmovToD { rd: D0, rn: X1 });
9581 }
9582
9583 #[test]
9584 fn parse_fmov_imm_d() {
9585 assert_eq!(
9586 parse_inst("fmov d2, #3.50000000"),
9587 Inst::FmovImmD { rd: D2, imm8: 12 }
9588 );
9589 }
9590
9591 #[test]
9592 fn parse_fmov_imm_s() {
9593 assert_eq!(
9594 parse_inst("fmov s2, #3.50000000"),
9595 Inst::FmovImmS { rd: S2, imm8: 12 }
9596 );
9597 }
9598
9599 #[test]
9600 fn parse_fmov_from_fp() {
9601 assert_eq!(
9602 parse_inst("fmov x0, d1"),
9603 Inst::FmovFromD { rd: X0, rn: D1 }
9604 );
9605 }
9606
9607 #[test]
9608 fn parse_fcsel_d() {
9609 assert_eq!(
9610 parse_inst("fcsel d0, d0, d1, mi"),
9611 Inst::FcselD {
9612 rd: D0,
9613 rn: D0,
9614 rm: D1,
9615 cond: Cond::MI
9616 }
9617 );
9618 }
9619
9620 #[test]
9621 fn parse_fcsel_s() {
9622 assert_eq!(
9623 parse_inst("fcsel s0, s0, s1, mi"),
9624 Inst::FcselS {
9625 rd: S0,
9626 rn: S0,
9627 rm: S1,
9628 cond: Cond::MI
9629 }
9630 );
9631 }
9632
9633 // ---- FP single-precision ----
9634
9635 #[test]
9636 fn parse_fneg_s() {
9637 assert_eq!(parse_inst("fneg s0, s1"), Inst::FnegS { rd: S0, rn: S1 });
9638 }
9639
9640 #[test]
9641 fn parse_fabs_s() {
9642 assert_eq!(parse_inst("fabs s0, s1"), Inst::FabsS { rd: S0, rn: S1 });
9643 }
9644
9645 #[test]
9646 fn parse_fsqrt_s() {
9647 assert_eq!(parse_inst("fsqrt s0, s1"), Inst::FsqrtS { rd: S0, rn: S1 });
9648 }
9649
9650 #[test]
9651 fn parse_fcmp_s() {
9652 assert_eq!(parse_inst("fcmp s0, s1"), Inst::FcmpS { rn: S0, rm: S1 });
9653 }
9654
9655 #[test]
9656 fn parse_fmadd_s() {
9657 assert_eq!(
9658 parse_inst("fmadd s0, s1, s2, s3"),
9659 Inst::FmaddS {
9660 rd: S0,
9661 rn: S1,
9662 rm: S2,
9663 ra: S3
9664 }
9665 );
9666 }
9667
9668 // ---- System ----
9669
9670 #[test]
9671 fn parse_svc_() {
9672 assert_eq!(parse_inst("svc #0x80"), Inst::Svc { imm16: 0x80 });
9673 }
9674
9675 #[test]
9676 fn parse_svc_expression() {
9677 assert_eq!(parse_inst("svc #0x40 + 0x40"), Inst::Svc { imm16: 0x80 });
9678 }
9679
9680 #[test]
9681 fn parse_nop_() {
9682 assert_eq!(parse_inst("nop"), Inst::Nop);
9683 }
9684
9685 #[test]
9686 fn parse_yield_() {
9687 assert_eq!(parse_inst("yield"), Inst::Yield);
9688 }
9689
9690 #[test]
9691 fn parse_wfe_() {
9692 assert_eq!(parse_inst("wfe"), Inst::Wfe);
9693 }
9694
9695 #[test]
9696 fn parse_sevl_() {
9697 assert_eq!(parse_inst("sevl"), Inst::Sevl);
9698 }
9699
9700 #[test]
9701 fn parse_dmb_ish() {
9702 assert_eq!(
9703 parse_inst("dmb ish"),
9704 Inst::Dmb {
9705 option: BarrierOpt::Ish
9706 }
9707 );
9708 }
9709
9710 #[test]
9711 fn parse_dsb_ishst() {
9712 assert_eq!(
9713 parse_inst("dsb ishst"),
9714 Inst::Dsb {
9715 option: BarrierOpt::Ishst
9716 }
9717 );
9718 }
9719
9720 #[test]
9721 fn parse_isb_default_sy() {
9722 assert_eq!(
9723 parse_inst("isb"),
9724 Inst::Isb {
9725 option: BarrierOpt::Sy
9726 }
9727 );
9728 }
9729
9730 #[test]
9731 fn parse_isb_sy() {
9732 assert_eq!(
9733 parse_inst("isb sy"),
9734 Inst::Isb {
9735 option: BarrierOpt::Sy
9736 }
9737 );
9738 }
9739
9740 #[test]
9741 fn error_unknown_barrier_option() {
9742 let err = parse_err("dmb bogus");
9743 assert!(err.contains("unknown dmb option"), "got: {}", err);
9744 }
9745
9746 #[test]
9747 fn parse_brk_() {
9748 assert_eq!(parse_inst("brk #1"), Inst::Brk { imm16: 1 });
9749 }
9750
9751 // ---- Labels and directives ----
9752
9753 #[test]
9754 fn parse_label() {
9755 let stmts = parse_stmts("_main:");
9756 assert_eq!(stmts, vec![Stmt::Label("_main".into())]);
9757 }
9758
9759 #[test]
9760 fn parse_local_label() {
9761 let stmts = parse_stmts(".Lloop:");
9762 assert_eq!(stmts, vec![Stmt::Label(".Lloop".into())]);
9763 }
9764
9765 #[test]
9766 fn parse_global_directive() {
9767 let stmts = parse_stmts(".global _main");
9768 assert_eq!(
9769 stmts,
9770 vec![Stmt::Directive(Directive::Global("_main".into()))]
9771 );
9772 }
9773
9774 #[test]
9775 fn parse_extern_directive() {
9776 let stmts = parse_stmts(".extern _puts");
9777 assert_eq!(
9778 stmts,
9779 vec![Stmt::Directive(Directive::Extern("_puts".into()))]
9780 );
9781 }
9782
9783 #[test]
9784 fn parse_comm_directive() {
9785 let stmts = parse_stmts(".comm _common, 24, 3");
9786 assert_eq!(
9787 stmts,
9788 vec![Stmt::Directive(Directive::Comm {
9789 name: "_common".into(),
9790 size: 24,
9791 align_pow2: 3,
9792 })]
9793 );
9794 }
9795
9796 #[test]
9797 fn parse_private_extern_directive() {
9798 let stmts = parse_stmts(".private_extern _hidden");
9799 assert_eq!(
9800 stmts,
9801 vec![Stmt::Directive(Directive::PrivateExtern("_hidden".into()))]
9802 );
9803 }
9804
9805 #[test]
9806 fn parse_weak_reference_directive() {
9807 let stmts = parse_stmts(".weak_reference _puts");
9808 assert_eq!(
9809 stmts,
9810 vec![Stmt::Directive(Directive::WeakReference("_puts".into()))]
9811 );
9812 }
9813
9814 #[test]
9815 fn parse_weak_definition_directive() {
9816 let stmts = parse_stmts(".weak_definition _entry");
9817 assert_eq!(
9818 stmts,
9819 vec![Stmt::Directive(Directive::WeakDefinition("_entry".into()))]
9820 );
9821 }
9822
9823 #[test]
9824 fn parse_set_directive() {
9825 let stmts = parse_stmts(".set ABS1, 7");
9826 assert_eq!(
9827 stmts,
9828 vec![Stmt::Directive(Directive::Set("ABS1".into(), Expr::Int(7)))]
9829 );
9830 }
9831
9832 #[test]
9833 fn parse_equ_directive() {
9834 let stmts = parse_stmts(".equ ABS2, ABS1 + 5");
9835 assert_eq!(
9836 stmts,
9837 vec![Stmt::Directive(Directive::Set(
9838 "ABS2".into(),
9839 Expr::Add(
9840 Box::new(Expr::Symbol("ABS1".into())),
9841 Box::new(Expr::Int(5))
9842 ),
9843 ))]
9844 );
9845 }
9846
9847 #[test]
9848 fn parse_asciz_directive() {
9849 let stmts = parse_stmts(".asciz \"Hello\\n\"");
9850 assert_eq!(
9851 stmts,
9852 vec![Stmt::Directive(Directive::Asciz(b"Hello\n\0".to_vec()))]
9853 );
9854 }
9855
9856 #[test]
9857 fn parse_align_directive() {
9858 let stmts = parse_stmts(".p2align 4");
9859 assert_eq!(
9860 stmts,
9861 vec![Stmt::Directive(Directive::P2Align {
9862 power: 4,
9863 fill: None,
9864 max_skip: None,
9865 })]
9866 );
9867 }
9868
9869 #[test]
9870 fn parse_align_directive_expression() {
9871 let stmts = parse_stmts(".align 1 + 1");
9872 assert_eq!(
9873 stmts,
9874 vec![Stmt::Directive(Directive::Align {
9875 power: 2,
9876 fill: None,
9877 max_skip: None,
9878 })]
9879 );
9880 }
9881
9882 #[test]
9883 fn parse_align_directive_with_fill_and_max_skip() {
9884 let stmts = parse_stmts(".p2align 4, 0xAA, 2");
9885 assert_eq!(
9886 stmts,
9887 vec![Stmt::Directive(Directive::P2Align {
9888 power: 4,
9889 fill: Some(0xAA),
9890 max_skip: Some(2),
9891 })]
9892 );
9893 }
9894
9895 #[test]
9896 fn parse_align_directive_with_omitted_fill() {
9897 let stmts = parse_stmts(".align 4,,2");
9898 assert_eq!(
9899 stmts,
9900 vec![Stmt::Directive(Directive::Align {
9901 power: 4,
9902 fill: None,
9903 max_skip: Some(2),
9904 })]
9905 );
9906 }
9907
9908 #[test]
9909 fn parse_byte_directive() {
9910 let stmts = parse_stmts(".byte 0x41, 0x42, 0x43");
9911 assert_eq!(
9912 stmts,
9913 vec![Stmt::Directive(Directive::Byte(vec![
9914 Expr::Int(0x41),
9915 Expr::Int(0x42),
9916 Expr::Int(0x43),
9917 ]))]
9918 );
9919 }
9920
9921 #[test]
9922 fn parse_short_directive() {
9923 let stmts = parse_stmts(".short 0x1234, 2");
9924 assert_eq!(
9925 stmts,
9926 vec![Stmt::Directive(Directive::Short(vec![
9927 Expr::Int(0x1234),
9928 Expr::Int(2)
9929 ]))]
9930 );
9931 }
9932
9933 #[test]
9934 fn parse_word_directive_expression() {
9935 let stmts = parse_stmts(".word 1 + 2 - 3");
9936 assert_eq!(
9937 stmts,
9938 vec![Stmt::Directive(Directive::Word(vec![Expr::Sub(
9939 Box::new(Expr::Add(Box::new(Expr::Int(1)), Box::new(Expr::Int(2)))),
9940 Box::new(Expr::Int(3)),
9941 )]))]
9942 );
9943 }
9944
9945 #[test]
9946 fn parse_quad_directive_parenthesized_expression() {
9947 let stmts = parse_stmts(".quad -(1 + 2)");
9948 assert_eq!(
9949 stmts,
9950 vec![Stmt::Directive(Directive::Quad(vec![Expr::UnaryMinus(
9951 Box::new(Expr::Add(Box::new(Expr::Int(1)), Box::new(Expr::Int(2)),))
9952 )]))]
9953 );
9954 }
9955
9956 #[test]
9957 fn parse_space_directive_expression() {
9958 let stmts = parse_stmts(".space (2 + 3)");
9959 assert_eq!(stmts, vec![Stmt::Directive(Directive::Space(5))]);
9960 }
9961
9962 #[test]
9963 fn parse_zero_directive() {
9964 let stmts = parse_stmts(".zero 3");
9965 assert_eq!(stmts, vec![Stmt::Directive(Directive::Space(3))]);
9966 }
9967
9968 #[test]
9969 fn parse_fill_directive() {
9970 let stmts = parse_stmts(".fill 2, 2, 0x3344");
9971 assert_eq!(
9972 stmts,
9973 vec![Stmt::Directive(Directive::Fill {
9974 repeat: 2,
9975 size: 2,
9976 value: 0x3344,
9977 })]
9978 );
9979 }
9980
9981 #[test]
9982 fn parse_cstring_directive() {
9983 let stmts = parse_stmts(".cstring");
9984 assert_eq!(
9985 stmts,
9986 vec![Stmt::Directive(Directive::Section(
9987 "__TEXT".into(),
9988 "__cstring".into()
9989 ))]
9990 );
9991 }
9992
9993 #[test]
9994 fn parse_zerofill_directive() {
9995 let stmts = parse_stmts(".zerofill __DATA, __bss, _scratch, 16, 4");
9996 assert_eq!(
9997 stmts,
9998 vec![Stmt::Directive(Directive::Zerofill {
9999 segment: "__DATA".into(),
10000 section: "__bss".into(),
10001 symbol: Some("_scratch".into()),
10002 size: 16,
10003 align_pow2: 4,
10004 })]
10005 );
10006 }
10007
10008 #[test]
10009 fn parse_zerofill_without_symbol() {
10010 let stmts = parse_stmts(".zerofill __DATA, __bss, , 8, 2");
10011 assert_eq!(
10012 stmts,
10013 vec![Stmt::Directive(Directive::Zerofill {
10014 segment: "__DATA".into(),
10015 section: "__bss".into(),
10016 symbol: None,
10017 size: 8,
10018 align_pow2: 2,
10019 })]
10020 );
10021 }
10022
10023 #[test]
10024 fn parse_tbss_directive() {
10025 let stmts = parse_stmts(".tbss _tls_counter$tlv$init, 4, 2");
10026 assert_eq!(
10027 stmts,
10028 vec![Stmt::Directive(Directive::Zerofill {
10029 segment: "__DATA".into(),
10030 section: "__thread_bss".into(),
10031 symbol: Some("_tls_counter$tlv$init".into()),
10032 size: 4,
10033 align_pow2: 2,
10034 })]
10035 );
10036 }
10037
10038 #[test]
10039 fn parse_text_section_with_attrs() {
10040 let stmts = parse_stmts(".section __TEXT,__text,regular,pure_instructions");
10041 assert_eq!(
10042 stmts,
10043 vec![Stmt::Directive(Directive::Section(
10044 "__TEXT".into(),
10045 "__text".into()
10046 ))]
10047 );
10048 }
10049
10050 #[test]
10051 fn parse_cstring_section_with_attrs() {
10052 let stmts = parse_stmts(".section __TEXT,__cstring,cstring_literals");
10053 assert_eq!(
10054 stmts,
10055 vec![Stmt::Directive(Directive::Section(
10056 "__TEXT".into(),
10057 "__cstring".into()
10058 ))]
10059 );
10060 }
10061
10062 #[test]
10063 fn parse_thread_data_section_with_attrs() {
10064 let stmts = parse_stmts(".section __DATA,__thread_data,thread_local_regular");
10065 assert_eq!(
10066 stmts,
10067 vec![Stmt::Directive(Directive::Section(
10068 "__DATA".into(),
10069 "__thread_data".into()
10070 ))]
10071 );
10072 }
10073
10074 #[test]
10075 fn parse_literal16_section_with_attrs() {
10076 let stmts = parse_stmts(".section __TEXT,__literal16,16byte_literals");
10077 assert_eq!(
10078 stmts,
10079 vec![Stmt::Directive(Directive::Section(
10080 "__TEXT".into(),
10081 "__literal16".into()
10082 ))]
10083 );
10084 }
10085
10086 #[test]
10087 fn parse_thread_vars_section_with_attrs() {
10088 let stmts = parse_stmts(".section __DATA,__thread_vars,thread_local_variables");
10089 assert_eq!(
10090 stmts,
10091 vec![Stmt::Directive(Directive::Section(
10092 "__DATA".into(),
10093 "__thread_vars".into()
10094 ))]
10095 );
10096 }
10097
10098 #[test]
10099 fn parse_section_with_unsupported_attrs_errors() {
10100 let err = parse_err(".section __TEXT,__text,regular,garbage");
10101 assert!(
10102 err.contains(
10103 "unsupported section attributes for __TEXT,__text: garbage (supported attrs: regular, pure_instructions)"
10104 ),
10105 "got: {}",
10106 err
10107 );
10108 }
10109
10110 #[test]
10111 fn parse_const_section_with_regular_attr() {
10112 let stmts = parse_stmts(".section __TEXT,__const,regular");
10113 assert_eq!(
10114 stmts,
10115 vec![Stmt::Directive(Directive::Section(
10116 "__TEXT".into(),
10117 "__const".into()
10118 ))]
10119 );
10120 }
10121
10122 #[test]
10123 fn parse_data_const_section_with_regular_attr() {
10124 let stmts = parse_stmts(".section __DATA,__const,regular");
10125 assert_eq!(
10126 stmts,
10127 vec![Stmt::Directive(Directive::Section(
10128 "__DATA".into(),
10129 "__const".into()
10130 ))]
10131 );
10132 }
10133
10134 #[test]
10135 fn parse_text_section_pure_without_regular_errors() {
10136 let err = parse_err(".section __TEXT,__text,pure_instructions");
10137 assert!(
10138 err.contains("section __TEXT,__text requires 'regular' when using 'pure_instructions'"),
10139 "got: {}",
10140 err
10141 );
10142 }
10143
10144 #[test]
10145 fn parse_unknown_directive_errors() {
10146 let err = parse_err(".unknown_directive");
10147 assert!(
10148 err.contains("unsupported directive '.unknown_directive'"),
10149 "got: {}",
10150 err
10151 );
10152 }
10153
10154 #[test]
10155 fn parse_build_version_with_sdk_version() {
10156 let stmts = parse_stmts(".build_version macos, 11, 0 sdk_version 15, 5");
10157 assert_eq!(
10158 stmts,
10159 vec![Stmt::Directive(Directive::BuildVersion(
10160 BuildVersionDirective {
10161 platform: "macos".into(),
10162 minos: VersionTriple {
10163 major: 11,
10164 minor: 0,
10165 patch: 0
10166 },
10167 sdk: Some(VersionTriple {
10168 major: 15,
10169 minor: 5,
10170 patch: 0
10171 }),
10172 }
10173 ))]
10174 );
10175 }
10176
10177 #[test]
10178 fn parse_cfi_startproc() {
10179 let stmts = parse_stmts(".cfi_startproc");
10180 assert_eq!(stmts, vec![Stmt::Directive(Directive::CfiStartProc)]);
10181 }
10182
10183 #[test]
10184 fn parse_cfi_def_cfa() {
10185 let stmts = parse_stmts(".cfi_def_cfa w29, 16");
10186 assert_eq!(
10187 stmts,
10188 vec![Stmt::Directive(Directive::CfiDefCfa {
10189 register: W29,
10190 offset: 16,
10191 })]
10192 );
10193 }
10194
10195 #[test]
10196 fn parse_cfi_offset() {
10197 let stmts = parse_stmts(".cfi_offset w30, -8");
10198 assert_eq!(
10199 stmts,
10200 vec![Stmt::Directive(Directive::CfiOffset {
10201 register: W30,
10202 offset: -8,
10203 })]
10204 );
10205 }
10206
10207 #[test]
10208 fn parse_unsupported_cfi_directive_errors() {
10209 let err = parse(".cfi_escape 0x1").unwrap_err();
10210 assert!(
10211 err.msg.contains("unsupported CFI directive"),
10212 "got: {}",
10213 err.msg
10214 );
10215 }
10216
10217 #[test]
10218 fn parse_build_version_without_sdk_version() {
10219 let stmts = parse_stmts(".build_version macos, 14, 1");
10220 assert_eq!(
10221 stmts,
10222 vec![Stmt::Directive(Directive::BuildVersion(
10223 BuildVersionDirective {
10224 platform: "macos".into(),
10225 minos: VersionTriple {
10226 major: 14,
10227 minor: 1,
10228 patch: 0
10229 },
10230 sdk: None,
10231 }
10232 ))]
10233 );
10234 }
10235
10236 #[test]
10237 fn parse_linker_optimization_hint() {
10238 let stmts = parse_stmts(".loh AdrpAdd Lloh0, Lloh1");
10239 assert_eq!(
10240 stmts,
10241 vec![Stmt::Directive(Directive::LinkerOptimizationHint(
10242 LinkerOptimizationHintDirective {
10243 kind: "AdrpAdd".into(),
10244 labels: vec!["Lloh0".into(), "Lloh1".into()],
10245 }
10246 ))]
10247 );
10248 }
10249
10250 #[test]
10251 fn parse_linker_optimization_hint_requires_expected_label_count() {
10252 let err = parse(".loh AdrpAdd Lloh0").unwrap_err();
10253 assert_eq!(err.line, 1);
10254 assert_eq!(err.col, 19);
10255 assert_eq!(err.msg, ".loh AdrpAdd expects 2 labels, got 1");
10256 }
10257
10258 #[test]
10259 fn parse_linker_optimization_hint_requires_commas() {
10260 let err = parse(".loh AdrpAdd Lloh0 Lloh1").unwrap_err();
10261 assert_eq!(err.line, 1);
10262 assert_eq!(err.col, 20);
10263 assert_eq!(err.msg, "expected ,, got Lloh1");
10264 }
10265
10266 #[test]
10267 fn parse_linker_optimization_hint_unknown_kind_errors() {
10268 let err = parse(".loh UnknownKind Lloh0").unwrap_err();
10269 assert_eq!(err.line, 1);
10270 assert_eq!(err.col, 18);
10271 assert_eq!(
10272 err.msg,
10273 "unsupported .loh kind 'UnknownKind' (supported: AdrpAdd, AdrpLdr, AdrpLdrGot, AdrpLdrGotLdr)"
10274 );
10275 }
10276
10277 // ---- Multi-line programs ----
10278
10279 #[test]
10280 fn parse_hello_world() {
10281 let src = "\
10282 .global _main
10283 .align 4
10284
10285 _main:
10286 mov x0, #1
10287 mov x16, #4
10288 svc #0x80
10289 mov x0, #0
10290 mov x16, #1
10291 svc #0x80
10292 ";
10293 let stmts = parse_stmts(src);
10294 // Should have: global, align, label, 6 instructions
10295 let labels = stmts.iter().filter(|s| matches!(s, Stmt::Label(_))).count();
10296 let insts = stmts
10297 .iter()
10298 .filter(|s| matches!(s, Stmt::Instruction(_)))
10299 .count();
10300 assert_eq!(labels, 1);
10301 assert_eq!(insts, 6);
10302 }
10303
10304 // ---- Error cases ----
10305
10306 #[test]
10307 fn error_unknown_mnemonic() {
10308 let result = parse("blorp x0, x1, x2");
10309 assert!(result.is_err());
10310 let err = result.unwrap_err();
10311 assert!(err.msg.contains("unknown mnemonic"), "got: {}", err.msg);
10312 }
10313
10314 #[test]
10315 fn error_bad_register() {
10316 let result = parse("add x0, x1, x99");
10317 assert!(result.is_err());
10318 }
10319
10320 #[test]
10321 fn error_missing_comma() {
10322 let result = parse("add x0 x1 x2");
10323 assert!(result.is_err());
10324 }
10325
10326 #[test]
10327 fn error_str_requires_bracketed_operand() {
10328 let result = parse("str x0, some_label");
10329 assert!(result.is_err());
10330 let err = result.unwrap_err();
10331 assert!(
10332 err.msg.contains("bracketed memory operand"),
10333 "got: {}",
10334 err.msg
10335 );
10336 }
10337
10338 #[test]
10339 fn parse_symbolic_quad_expression() {
10340 let stmts = parse_stmts(".quad foo - 1");
10341 assert_eq!(
10342 stmts,
10343 vec![Stmt::Directive(Directive::Quad(vec![Expr::Sub(
10344 Box::new(Expr::Symbol("foo".into())),
10345 Box::new(Expr::Int(1)),
10346 )]))]
10347 );
10348 }
10349
10350 #[test]
10351 fn parse_quad_got_expression() {
10352 assert_eq!(
10353 parse_stmts(".quad _puts@GOT"),
10354 vec![Stmt::Directive(Directive::Quad(vec![
10355 Expr::ModifiedSymbol {
10356 symbol: "_puts".into(),
10357 modifier: SymbolModifier::Got,
10358 }
10359 ]))]
10360 );
10361 }
10362
10363 #[test]
10364 fn parse_word_got_pcrel_expression() {
10365 assert_eq!(
10366 parse_stmts(".long _puts@GOT - ."),
10367 vec![Stmt::Directive(Directive::Word(vec![Expr::Sub(
10368 Box::new(Expr::ModifiedSymbol {
10369 symbol: "_puts".into(),
10370 modifier: SymbolModifier::Got,
10371 }),
10372 Box::new(Expr::CurrentLocation),
10373 )]))]
10374 );
10375 }
10376
10377 // ---- Case insensitivity ----
10378
10379 #[test]
10380 fn parse_uppercase_add() {
10381 assert_eq!(
10382 parse_inst("ADD X0, X1, X2"),
10383 Inst::AddReg {
10384 rd: X0,
10385 rn: X1,
10386 rm: X2,
10387 sf: true
10388 }
10389 );
10390 }
10391
10392 #[test]
10393 fn parse_uppercase_b_eq() {
10394 assert_eq!(
10395 parse_inst("B.EQ #8"),
10396 Inst::BCond {
10397 cond: Cond::EQ,
10398 offset: 8
10399 }
10400 );
10401 }
10402
10403 #[test]
10404 fn parse_mixed_case_ldr() {
10405 assert_eq!(
10406 parse_inst("Ldr X0, [X1, #8]"),
10407 Inst::LdrImm64 {
10408 rt: X0,
10409 rn: X1,
10410 offset: 8
10411 }
10412 );
10413 }
10414
10415 #[test]
10416 fn parse_uppercase_add_does_not_treat_x2_as_label() {
10417 // BUG 3 regression: uppercase X2 must be parsed as register, not label
10418 assert_eq!(
10419 parse_inst("ADD X0, X1, X2"),
10420 Inst::AddReg {
10421 rd: X0,
10422 rn: X1,
10423 rm: X2,
10424 sf: true
10425 }
10426 );
10427 }
10428
10429 #[test]
10430 fn parse_uppercase_mov_sp() {
10431 assert_eq!(
10432 parse_inst("MOV X29, SP"),
10433 Inst::AddImm {
10434 rd: X29,
10435 rn: SP,
10436 imm12: 0,
10437 shift: false,
10438 sf: true
10439 }
10440 );
10441 }
10442
10443 // ---- Test gap coverage (from audit) ----
10444
10445 #[test]
10446 fn parse_negative_branch() {
10447 assert_eq!(parse_inst("b #-8"), Inst::B { offset: -8 });
10448 }
10449
10450 #[test]
10451 fn parse_branch_expression() {
10452 assert_eq!(parse_inst("b #4 + 4"), Inst::B { offset: 8 });
10453 }
10454
10455 #[test]
10456 fn parse_negative_bl() {
10457 assert_eq!(parse_inst("bl #-16"), Inst::Bl { offset: -16 });
10458 }
10459
10460 #[test]
10461 fn parse_w_register_shift() {
10462 assert_eq!(
10463 parse_inst("lsl w0, w1, #3"),
10464 Inst::LslImm {
10465 rd: W0,
10466 rn: W1,
10467 amount: 3,
10468 sf: false
10469 }
10470 );
10471 }
10472
10473 #[test]
10474 fn parse_w_register_lsr() {
10475 assert_eq!(
10476 parse_inst("lsr w5, w6, #8"),
10477 Inst::LsrImm {
10478 rd: W5,
10479 rn: W6,
10480 amount: 8,
10481 sf: false
10482 }
10483 );
10484 }
10485
10486 #[test]
10487 fn parse_w_register_asr() {
10488 assert_eq!(
10489 parse_inst("asr w5, w6, #15"),
10490 Inst::AsrImm {
10491 rd: W5,
10492 rn: W6,
10493 amount: 15,
10494 sf: false
10495 }
10496 );
10497 }
10498
10499 #[test]
10500 fn parse_mov_negative_imm() {
10501 // mov x0, #-1 → movn x0, #0
10502 assert_eq!(
10503 parse_inst("mov x0, #-1"),
10504 Inst::Movn {
10505 rd: X0,
10506 imm16: 0,
10507 shift: 0,
10508 sf: true
10509 }
10510 );
10511 }
10512
10513 #[test]
10514 fn parse_mov_negative_42() {
10515 // mov x0, #-42 → movn x0, #41
10516 assert_eq!(
10517 parse_inst("mov x0, #-42"),
10518 Inst::Movn {
10519 rd: X0,
10520 imm16: 41,
10521 shift: 0,
10522 sf: true
10523 }
10524 );
10525 }
10526
10527 #[test]
10528 fn parse_mov_negative_65537() {
10529 assert_eq!(
10530 parse_inst("mov x0, #-65537"),
10531 Inst::Movn {
10532 rd: X0,
10533 imm16: 1,
10534 shift: 16,
10535 sf: true
10536 }
10537 );
10538 }
10539
10540 #[test]
10541 fn parse_mov_large_positive_shifted() {
10542 assert_eq!(
10543 parse_inst("mov x0, #0x12340000"),
10544 Inst::Movz {
10545 rd: X0,
10546 imm16: 0x1234,
10547 shift: 16,
10548 sf: true
10549 }
10550 );
10551 }
10552
10553 #[test]
10554 fn parse_mov_wide_expression() {
10555 assert_eq!(
10556 parse_inst("movz x0, #1 + 1, lsl #4 + 12"),
10557 Inst::Movz {
10558 rd: X0,
10559 imm16: 2,
10560 shift: 16,
10561 sf: true
10562 }
10563 );
10564 }
10565
10566 #[test]
10567 fn parse_immediate_absolute_symbol_after_set() {
10568 let stmts = parse_stmts(".set ABS1, 7\nmovz x0, #ABS1\n");
10569 assert_eq!(
10570 stmts[1],
10571 Stmt::Instruction(Inst::Movz {
10572 rd: X0,
10573 imm16: 7,
10574 shift: 0,
10575 sf: true
10576 })
10577 );
10578 }
10579
10580 #[test]
10581 fn parse_parenthesized_immediate_after_hash() {
10582 assert_eq!(
10583 parse_inst("movz x0, #(1 + 2)"),
10584 Inst::Movz {
10585 rd: X0,
10586 imm16: 3,
10587 shift: 0,
10588 sf: true
10589 }
10590 );
10591 }
10592
10593 #[test]
10594 fn parse_cbnz_w() {
10595 assert_eq!(
10596 parse_inst("cbnz w5, #8"),
10597 Inst::Cbnz {
10598 rt: W5,
10599 offset: 8,
10600 sf: false
10601 }
10602 );
10603 }
10604
10605 #[test]
10606 fn parse_cbz_w() {
10607 assert_eq!(
10608 parse_inst("cbz w0, #12"),
10609 Inst::Cbz {
10610 rt: W0,
10611 offset: 12,
10612 sf: false
10613 }
10614 );
10615 }
10616
10617 #[test]
10618 fn parse_memory_offset_expression() {
10619 assert_eq!(
10620 parse_inst("ldr x0, [x1, #4 + 4]"),
10621 Inst::LdrImm64 {
10622 rt: X0,
10623 rn: X1,
10624 offset: 8
10625 }
10626 );
10627 }
10628
10629 #[test]
10630 fn parse_all_condition_codes() {
10631 // Exercise all 14 named condition codes
10632 for (name, cond) in [
10633 ("eq", Cond::EQ),
10634 ("ne", Cond::NE),
10635 ("cs", Cond::CS),
10636 ("cc", Cond::CC),
10637 ("mi", Cond::MI),
10638 ("pl", Cond::PL),
10639 ("vs", Cond::VS),
10640 ("vc", Cond::VC),
10641 ("hi", Cond::HI),
10642 ("ls", Cond::LS),
10643 ("ge", Cond::GE),
10644 ("lt", Cond::LT),
10645 ("gt", Cond::GT),
10646 ("le", Cond::LE),
10647 ] {
10648 let src = format!("b.{} #4", name);
10649 assert_eq!(
10650 parse_inst(&src),
10651 Inst::BCond { cond, offset: 4 },
10652 "failed for b.{}",
10653 name
10654 );
10655 }
10656 }
10657
10658 #[test]
10659 fn parse_hs_lo_aliases() {
10660 assert_eq!(
10661 parse_inst("b.hs #4"),
10662 Inst::BCond {
10663 cond: Cond::CS,
10664 offset: 4
10665 }
10666 );
10667 assert_eq!(
10668 parse_inst("b.lo #4"),
10669 Inst::BCond {
10670 cond: Cond::CC,
10671 offset: 4
10672 }
10673 );
10674 }
10675
10676 #[test]
10677 fn parse_b_label() {
10678 assert_eq!(
10679 parse_stmts("b done"),
10680 vec![Stmt::InstructionWithReloc(
10681 Inst::B { offset: 0 },
10682 LabelRef {
10683 symbol: "done".into(),
10684 kind: RelocKind::Branch26,
10685 addend: 0
10686 },
10687 )]
10688 );
10689 }
10690
10691 #[test]
10692 fn parse_b_eq_label() {
10693 assert_eq!(
10694 parse_stmts("b.eq done"),
10695 vec![Stmt::InstructionWithReloc(
10696 Inst::BCond {
10697 cond: Cond::EQ,
10698 offset: 0
10699 },
10700 LabelRef {
10701 symbol: "done".into(),
10702 kind: RelocKind::Branch19,
10703 addend: 0
10704 },
10705 )]
10706 );
10707 }
10708
10709 #[test]
10710 fn parse_dotted_symbol_label_and_page_refs() {
10711 assert_eq!(
10712 parse_stmts("l_.str:\nadrp x0, l_.str@PAGE\nadd x0, x0, l_.str@PAGEOFF\n"),
10713 vec![
10714 Stmt::Label("l_.str".into()),
10715 Stmt::InstructionWithReloc(
10716 Inst::Adrp { rd: X0, imm: 0 },
10717 LabelRef {
10718 symbol: "l_.str".into(),
10719 kind: RelocKind::Page21,
10720 addend: 0
10721 },
10722 ),
10723 Stmt::InstructionWithReloc(
10724 Inst::AddImm {
10725 rd: X0,
10726 rn: X0,
10727 imm12: 0,
10728 shift: false,
10729 sf: true
10730 },
10731 LabelRef {
10732 symbol: "l_.str".into(),
10733 kind: RelocKind::PageOff12,
10734 addend: 0
10735 },
10736 ),
10737 ]
10738 );
10739 }
10740
10741 #[test]
10742 fn parse_adrp_gotpage() {
10743 assert_eq!(
10744 parse_stmts("adrp x8, _ext_global@GOTPAGE"),
10745 vec![Stmt::InstructionWithReloc(
10746 Inst::Adrp { rd: X8, imm: 0 },
10747 LabelRef {
10748 symbol: "_ext_global".into(),
10749 kind: RelocKind::GotLoadPage21,
10750 addend: 0
10751 },
10752 )]
10753 );
10754 }
10755
10756 #[test]
10757 fn parse_ldr_gotpageoff_memory_operand() {
10758 assert_eq!(
10759 parse_stmts("ldr x8, [x8, _ext_global@GOTPAGEOFF]"),
10760 vec![Stmt::InstructionWithReloc(
10761 Inst::LdrImm64 {
10762 rt: X8,
10763 rn: X8,
10764 offset: 0
10765 },
10766 LabelRef {
10767 symbol: "_ext_global".into(),
10768 kind: RelocKind::GotLoadPageOff12,
10769 addend: 0,
10770 },
10771 )]
10772 );
10773 }
10774
10775 #[test]
10776 fn parse_adrp_tlvppage() {
10777 assert_eq!(
10778 parse_stmts("adrp x0, _tls_counter@TLVPPAGE"),
10779 vec![Stmt::InstructionWithReloc(
10780 Inst::Adrp { rd: X0, imm: 0 },
10781 LabelRef {
10782 symbol: "_tls_counter".into(),
10783 kind: RelocKind::TlvpLoadPage21,
10784 addend: 0
10785 },
10786 )]
10787 );
10788 }
10789
10790 #[test]
10791 fn parse_bl_symbol_addend() {
10792 assert_eq!(
10793 parse_stmts("bl _puts + 4"),
10794 vec![Stmt::InstructionWithReloc(
10795 Inst::Bl { offset: 0 },
10796 LabelRef {
10797 symbol: "_puts".into(),
10798 kind: RelocKind::Branch26,
10799 addend: 4
10800 },
10801 )]
10802 );
10803 }
10804
10805 #[test]
10806 fn parse_adrp_page_addend() {
10807 assert_eq!(
10808 parse_stmts("adrp x0, _data@PAGE + 0x24"),
10809 vec![Stmt::InstructionWithReloc(
10810 Inst::Adrp { rd: X0, imm: 0 },
10811 LabelRef {
10812 symbol: "_data".into(),
10813 kind: RelocKind::Page21,
10814 addend: 0x24
10815 },
10816 )]
10817 );
10818 }
10819
10820 #[test]
10821 fn parse_ldr_tlvppageoff_memory_operand() {
10822 assert_eq!(
10823 parse_stmts("ldr x0, [x0, _tls_counter@TLVPPAGEOFF]"),
10824 vec![Stmt::InstructionWithReloc(
10825 Inst::LdrImm64 {
10826 rt: X0,
10827 rn: X0,
10828 offset: 0
10829 },
10830 LabelRef {
10831 symbol: "_tls_counter".into(),
10832 kind: RelocKind::TlvpLoadPageOff12,
10833 addend: 0,
10834 },
10835 )]
10836 );
10837 }
10838
10839 #[test]
10840 fn parse_ldr_pageoff_addend_memory_operand() {
10841 assert_eq!(
10842 parse_stmts("ldr x0, [x0, _data@PAGEOFF + 0x24]"),
10843 vec![Stmt::InstructionWithReloc(
10844 Inst::LdrImm64 {
10845 rt: X0,
10846 rn: X0,
10847 offset: 0
10848 },
10849 LabelRef {
10850 symbol: "_data".into(),
10851 kind: RelocKind::PageOff12,
10852 addend: 0x24
10853 },
10854 )]
10855 );
10856 }
10857
10858 #[test]
10859 fn parse_ldr_pageoff_memory_operand() {
10860 assert_eq!(
10861 parse_stmts("ldr x0, [x1, value@PAGEOFF]"),
10862 vec![Stmt::InstructionWithReloc(
10863 Inst::LdrImm64 {
10864 rt: X0,
10865 rn: X1,
10866 offset: 0
10867 },
10868 LabelRef {
10869 symbol: "value".into(),
10870 kind: RelocKind::PageOff12,
10871 addend: 0
10872 },
10873 )]
10874 );
10875 }
10876
10877 #[test]
10878 fn parse_adrp_rejects_unsupported_modifier() {
10879 let err = parse_err("adrp x0, _foo@GOTPAGEOFF");
10880 assert!(
10881 err.contains("unsupported relocation modifier"),
10882 "got: {}",
10883 err
10884 );
10885 }
10886
10887 #[test]
10888 fn parse_b_named_local_label() {
10889 assert_eq!(
10890 parse_stmts("b .Ldone"),
10891 vec![Stmt::InstructionWithReloc(
10892 Inst::B { offset: 0 },
10893 LabelRef {
10894 symbol: ".Ldone".into(),
10895 kind: RelocKind::Branch26,
10896 addend: 0
10897 },
10898 )]
10899 );
10900 }
10901
10902 #[test]
10903 fn parse_cbz_label() {
10904 assert_eq!(
10905 parse_stmts("cbz x0, done"),
10906 vec![Stmt::InstructionWithReloc(
10907 Inst::Cbz {
10908 rt: X0,
10909 offset: 0,
10910 sf: true
10911 },
10912 LabelRef {
10913 symbol: "done".into(),
10914 kind: RelocKind::Branch19,
10915 addend: 0
10916 },
10917 )]
10918 );
10919 }
10920
10921 #[test]
10922 fn parse_numeric_local_label_definition_and_backward_branch() {
10923 assert_eq!(
10924 parse_stmts("1:\nb 1b\n"),
10925 vec![
10926 Stmt::Label(".Ltmp$1$1".into()),
10927 Stmt::InstructionWithReloc(
10928 Inst::B { offset: 0 },
10929 LabelRef {
10930 symbol: ".Ltmp$1$1".into(),
10931 kind: RelocKind::Branch26,
10932 addend: 0
10933 },
10934 ),
10935 ]
10936 );
10937 }
10938
10939 #[test]
10940 fn parse_numeric_local_forward_reference() {
10941 assert_eq!(
10942 parse_stmts("b 2f\n2:\n"),
10943 vec![
10944 Stmt::InstructionWithReloc(
10945 Inst::B { offset: 0 },
10946 LabelRef {
10947 symbol: ".Ltmp$2$1".into(),
10948 kind: RelocKind::Branch26,
10949 addend: 0
10950 },
10951 ),
10952 Stmt::Label(".Ltmp$2$1".into()),
10953 ]
10954 );
10955 }
10956
10957 #[test]
10958 fn parse_numeric_local_label_on_same_line() {
10959 assert_eq!(
10960 parse_stmts("1: cbz x0, 1b"),
10961 vec![
10962 Stmt::Label(".Ltmp$1$1".into()),
10963 Stmt::InstructionWithReloc(
10964 Inst::Cbz {
10965 rt: X0,
10966 offset: 0,
10967 sf: true
10968 },
10969 LabelRef {
10970 symbol: ".Ltmp$1$1".into(),
10971 kind: RelocKind::Branch19,
10972 addend: 0
10973 },
10974 ),
10975 ]
10976 );
10977 }
10978
10979 #[test]
10980 fn parse_numeric_local_in_expression() {
10981 assert_eq!(
10982 parse_stmts("1:\n.quad 1b + 4\n"),
10983 vec![
10984 Stmt::Label(".Ltmp$1$1".into()),
10985 Stmt::Directive(Directive::Quad(vec![Expr::Add(
10986 Box::new(Expr::Symbol(".Ltmp$1$1".into())),
10987 Box::new(Expr::Int(4)),
10988 )])),
10989 ]
10990 );
10991 }
10992
10993 #[test]
10994 fn parse_numeric_local_adrp_and_add_operands() {
10995 assert_eq!(
10996 parse_stmts("adrp x0, 1f@PAGE\nadd x0, x0, 1f@PAGEOFF\n1:\n"),
10997 vec![
10998 Stmt::InstructionWithReloc(
10999 Inst::Adrp { rd: X0, imm: 0 },
11000 LabelRef {
11001 symbol: ".Ltmp$1$1".into(),
11002 kind: RelocKind::Page21,
11003 addend: 0
11004 },
11005 ),
11006 Stmt::InstructionWithReloc(
11007 Inst::AddImm {
11008 rd: X0,
11009 rn: X0,
11010 imm12: 0,
11011 shift: false,
11012 sf: true
11013 },
11014 LabelRef {
11015 symbol: ".Ltmp$1$1".into(),
11016 kind: RelocKind::PageOff12,
11017 addend: 0
11018 },
11019 ),
11020 Stmt::Label(".Ltmp$1$1".into()),
11021 ]
11022 );
11023 }
11024
11025 #[test]
11026 fn parse_numeric_local_requires_previous_definition_for_backward_ref() {
11027 let err = parse_err("b 1b\n");
11028 assert!(
11029 err.contains("numeric label '1' has no previous definition"),
11030 "got: {}",
11031 err
11032 );
11033 }
11034 }
11035