1use alloc::boxed::Box;
9use alloc::collections::BTreeMap;
10use alloc::string::String;
11use alloc::string::ToString;
12use alloc::vec;
13use alloc::vec::Vec;
14
15use crate::error::{AsmError, Span};
16use crate::ir::*;
17use crate::lexer::{Token, TokenKind};
18
19#[inline]
22fn to_lower_buf<'b>(s: &str, buf: &'b mut [u8]) -> &'b str {
23 let len = s.len().min(buf.len());
24 buf[..len].copy_from_slice(&s.as_bytes()[..len]);
25 buf[..len].make_ascii_lowercase();
26 core::str::from_utf8(&buf[..len]).unwrap_or("")
29}
30
31pub fn parse(tokens: &[Token<'_>]) -> Result<Vec<Statement>, AsmError> {
38 parse_with_arch(tokens, Arch::X86_64)
39}
40
41pub fn parse_with_arch(tokens: &[Token<'_>], arch: Arch) -> Result<Vec<Statement>, AsmError> {
43 parse_with_syntax(tokens, arch, Syntax::Intel)
44}
45
46pub fn parse_with_syntax(
48 tokens: &[Token<'_>],
49 arch: Arch,
50 syntax: Syntax,
51) -> Result<Vec<Statement>, AsmError> {
52 let mut parser = Parser::new(tokens, arch, syntax);
53 parser.parse_program()
54}
55
56pub fn parse_streaming<F>(
71 tokens: &[Token<'_>],
72 arch: Arch,
73 syntax: Syntax,
74 mut sink: F,
75) -> Result<(), AsmError>
76where
77 F: FnMut(Statement) -> Result<(), AsmError>,
78{
79 let mut parser = Parser::new(tokens, arch, syntax);
80 parser.skip_newlines();
81 while !parser.at_end() {
82 if let Some(stmt) = parser.parse_statement()? {
83 sink(stmt)?;
84 }
85 parser.skip_newlines();
86 }
87 Ok(())
88}
89
90struct Parser<'a> {
91 tokens: &'a [Token<'a>],
92 pos: usize,
93 arch: Arch,
95 syntax: Syntax,
97 constants: BTreeMap<String, i128>,
101}
102
103impl<'a> Parser<'a> {
104 fn new(tokens: &'a [Token<'a>], arch: Arch, syntax: Syntax) -> Self {
105 Self {
106 tokens,
107 pos: 0,
108 arch,
109 syntax,
110 constants: BTreeMap::new(),
111 }
112 }
113
114 #[inline]
115 fn peek(&self) -> &Token<'a> {
116 &self.tokens[self.pos.min(self.tokens.len() - 1)]
117 }
118
119 #[inline]
120 fn advance(&mut self) -> &Token<'a> {
121 let tok = &self.tokens[self.pos.min(self.tokens.len() - 1)];
122 if self.pos < self.tokens.len() {
123 self.pos += 1;
124 }
125 tok
126 }
127
128 #[inline]
129 fn at_end(&self) -> bool {
130 self.pos >= self.tokens.len() || self.peek().kind == TokenKind::Eof
131 }
132
133 fn expect_ident(&mut self) -> Result<(String, Span), AsmError> {
134 let tok = self.advance();
135 if tok.kind == TokenKind::Ident {
136 Ok((tok.text.to_string(), tok.span))
137 } else {
138 Err(AsmError::Syntax {
139 msg: alloc::format!("expected identifier, found '{}'", tok.text),
140 span: tok.span,
141 })
142 }
143 }
144
145 #[inline]
146 fn skip_newlines(&mut self) {
147 while !self.at_end() && self.peek().kind == TokenKind::Newline {
148 self.advance();
149 }
150 }
151
152 fn parse_program(&mut self) -> Result<Vec<Statement>, AsmError> {
153 let mut stmts = Vec::with_capacity(self.tokens.len() / 3 + 1);
155 self.skip_newlines();
156 while !self.at_end() {
157 if let Some(stmt) = self.parse_statement()? {
158 stmts.push(stmt);
159 }
160 self.skip_newlines();
161 }
162 Ok(stmts)
163 }
164
165 fn parse_statement(&mut self) -> Result<Option<Statement>, AsmError> {
166 let tok = self.peek().clone();
167
168 match &tok.kind {
169 TokenKind::Eof => Ok(None),
170 TokenKind::Newline => {
171 self.advance();
172 Ok(None)
173 }
174
175 TokenKind::LabelDef => {
177 self.advance();
178 Ok(Some(Statement::Label(tok.text.to_string(), tok.span)))
179 }
180
181 TokenKind::NumericLabelDef(n) => {
183 self.advance();
184 let name = alloc::format!("{}", n);
185 Ok(Some(Statement::Label(name, tok.span)))
186 }
187
188 TokenKind::Directive => self.parse_directive(),
190
191 TokenKind::Ident => self.parse_instruction_or_prefix(),
193
194 _ => Err(AsmError::Syntax {
195 msg: alloc::format!("unexpected token '{}'", tok.text),
196 span: tok.span,
197 }),
198 }
199 }
200
201 fn parse_directive(&mut self) -> Result<Option<Statement>, AsmError> {
202 let tok = self.advance().clone();
203 let mut dir_buf = [0u8; 32];
204 let dir = to_lower_buf(&tok.text, &mut dir_buf);
205 let span = tok.span;
206
207 match dir {
208 ".byte" | ".db" => self.parse_data_directive(DataSize::Byte, span),
210 ".word" | ".dw" | ".short" => self.parse_data_directive(DataSize::Word, span),
211 ".long" | ".dd" | ".int" => self.parse_data_directive(DataSize::Long, span),
212 ".quad" | ".dq" => self.parse_data_directive(DataSize::Quad, span),
213
214 ".ascii" => self.parse_string_directive(false, span),
216 ".asciz" | ".string" => self.parse_string_directive(true, span),
217
218 ".equ" | ".set" => self.parse_equ_directive(span),
220
221 ".align" | ".balign" | ".p2align" => {
223 let is_p2 = dir == ".p2align";
224 self.parse_align_directive(is_p2, span)
225 }
226
227 ".fill" => self.parse_fill_directive(span),
229
230 ".space" | ".skip" => self.parse_space_directive(span),
232
233 ".org" => self.parse_org_directive(span),
235
236 ".global" | ".globl" | ".extern" => {
238 if !self.at_end() && self.peek().kind == TokenKind::Ident {
240 self.advance();
241 }
242 Ok(None)
243 }
244
245 ".text" | ".data" | ".bss" | ".rodata" | ".section" => {
247 while !self.at_end()
249 && self.peek().kind != TokenKind::Newline
250 && self.peek().kind != TokenKind::Eof
251 {
252 self.advance();
253 }
254 Ok(None)
255 }
256
257 ".code16" => Ok(Some(Statement::CodeMode(crate::ir::X86Mode::Mode16, span))),
259 ".code32" => Ok(Some(Statement::CodeMode(crate::ir::X86Mode::Mode32, span))),
260 ".code64" => Ok(Some(Statement::CodeMode(crate::ir::X86Mode::Mode64, span))),
261
262 ".ltorg" | ".pool" => Ok(Some(Statement::Ltorg(span))),
264
265 ".thumb" => Ok(Some(Statement::ThumbMode(true, span))),
267 ".arm" => Ok(Some(Statement::ThumbMode(false, span))),
268 ".thumb_func" => Ok(Some(Statement::ThumbFunc(span))),
269
270 ".syntax" => {
272 let next = self.peek().clone();
273 if next.kind == TokenKind::Ident {
274 self.advance();
275 if next.text.eq_ignore_ascii_case("att") {
276 self.syntax = Syntax::Att;
277 Ok(None)
278 } else if next.text.eq_ignore_ascii_case("intel") {
279 self.syntax = Syntax::Intel;
280 Ok(None)
281 } else {
282 Err(AsmError::Syntax {
283 msg: alloc::format!(
284 "unknown syntax '{}' (expected 'att' or 'intel')",
285 next.text
286 ),
287 span: next.span,
288 })
289 }
290 } else {
291 Err(AsmError::Syntax {
292 msg: String::from("expected 'att' or 'intel' after .syntax"),
293 span: next.span,
294 })
295 }
296 }
297
298 ".option" => {
300 let next = self.peek().clone();
301 if next.kind == TokenKind::Ident {
302 self.advance();
303 if next.text.eq_ignore_ascii_case("rvc") {
304 Ok(Some(Statement::OptionRvc(true, span)))
305 } else if next.text.eq_ignore_ascii_case("norvc") {
306 Ok(Some(Statement::OptionRvc(false, span)))
307 } else {
308 Err(AsmError::Syntax {
309 msg: alloc::format!(
310 "unknown option '{}' (expected 'rvc' or 'norvc')",
311 next.text
312 ),
313 span: next.span,
314 })
315 }
316 } else {
317 Err(AsmError::Syntax {
318 msg: String::from("expected 'rvc' or 'norvc' after .option"),
319 span: next.span,
320 })
321 }
322 }
323
324 _ => Err(AsmError::Syntax {
325 msg: alloc::format!("unknown directive '{}'", dir),
326 span,
327 }),
328 }
329 }
330
331 fn parse_data_directive(
332 &mut self,
333 size: DataSize,
334 span: Span,
335 ) -> Result<Option<Statement>, AsmError> {
336 let mut values = Vec::new();
337 loop {
338 let val = self.parse_data_value()?;
339 values.push(val);
340 if self.peek().kind == TokenKind::Comma {
341 self.advance();
342 } else {
343 break;
344 }
345 }
346 Ok(Some(Statement::Data(DataDecl { size, values, span })))
347 }
348
349 fn parse_data_value(&mut self) -> Result<DataValue, AsmError> {
350 let tok = self.peek().clone();
351 match &tok.kind {
352 TokenKind::Number(n) => {
353 self.advance();
354 Ok(DataValue::Integer(*n))
355 }
356 TokenKind::CharLit(ch) => {
357 self.advance();
358 Ok(DataValue::Integer(*ch as i128))
359 }
360 TokenKind::Ident => {
361 self.advance();
362 let label = tok.text.to_string();
363 let addend = if self.peek().kind == TokenKind::Plus {
365 self.advance();
366 let n = self.parse_const_expr()?;
367 n as i64
368 } else if self.peek().kind == TokenKind::Minus {
369 self.advance();
370 let n = self.parse_const_expr()?;
371 -(n as i64)
372 } else {
373 0
374 };
375 Ok(DataValue::Label(label, addend))
376 }
377 TokenKind::Minus => {
378 self.advance();
379 let next = self.peek().clone();
380 if let TokenKind::Number(n) = next.kind {
381 self.advance();
382 Ok(DataValue::Integer(-n))
383 } else {
384 Err(AsmError::Syntax {
385 msg: String::from("expected number after '-'"),
386 span: tok.span,
387 })
388 }
389 }
390 _ => Err(AsmError::Syntax {
391 msg: alloc::format!("expected data value, found '{}'", tok.text),
392 span: tok.span,
393 }),
394 }
395 }
396
397 fn parse_string_directive(
398 &mut self,
399 null_terminate: bool,
400 span: Span,
401 ) -> Result<Option<Statement>, AsmError> {
402 let tok = self.advance().clone();
403 if tok.kind != TokenKind::StringLit {
404 return Err(AsmError::Syntax {
405 msg: String::from("expected string literal"),
406 span: tok.span,
407 });
408 }
409 let mut bytes: Vec<u8> = tok.text.as_bytes().to_vec();
410 if null_terminate {
411 bytes.push(0);
412 }
413 Ok(Some(Statement::Data(DataDecl {
414 size: DataSize::Byte,
415 values: vec![DataValue::Bytes(bytes)],
416 span,
417 })))
418 }
419
420 fn parse_equ_directive(&mut self, span: Span) -> Result<Option<Statement>, AsmError> {
421 let (name, _) = self.expect_ident()?;
422 if self.peek().kind == TokenKind::Comma {
424 self.advance();
425 }
426 let value = self.parse_const_expr()?;
427 self.constants.insert(name.clone(), value);
428 Ok(Some(Statement::Const(ConstDef { name, value, span })))
429 }
430
431 fn parse_align_directive(
432 &mut self,
433 is_p2: bool,
434 span: Span,
435 ) -> Result<Option<Statement>, AsmError> {
436 let raw = self.parse_const_expr()? as u32;
437 let alignment = if is_p2 { 1u32 << raw } else { raw };
438
439 if alignment > 1 && !alignment.is_power_of_two() {
441 return Err(AsmError::Syntax {
442 msg: alloc::format!("alignment must be a power of 2, got {alignment}"),
443 span,
444 });
445 }
446
447 let fill = if self.peek().kind == TokenKind::Comma {
448 self.advance();
449 if matches!(
451 self.peek().kind,
452 TokenKind::Number(_) | TokenKind::Minus | TokenKind::Ident
453 ) {
454 Some(self.parse_const_expr()? as u8)
455 } else {
456 None
457 }
458 } else {
459 None
460 };
461
462 let max_skip = if self.peek().kind == TokenKind::Comma {
463 self.advance();
464 if matches!(
465 self.peek().kind,
466 TokenKind::Number(_) | TokenKind::Minus | TokenKind::Ident
467 ) {
468 Some(self.parse_const_expr()? as u32)
469 } else {
470 None
471 }
472 } else {
473 None
474 };
475
476 Ok(Some(Statement::Align(AlignDirective {
477 alignment,
478 fill,
479 max_skip,
480 span,
481 })))
482 }
483
484 fn parse_fill_directive(&mut self, span: Span) -> Result<Option<Statement>, AsmError> {
485 let count = self.parse_const_expr()? as u32;
486 let mut size = 1u8;
487 let mut value = 0i64;
488 if self.peek().kind == TokenKind::Comma {
489 self.advance();
490 size = self.parse_const_expr()? as u8;
491 if self.peek().kind == TokenKind::Comma {
492 self.advance();
493 value = self.parse_const_expr()? as i64;
494 }
495 }
496 Ok(Some(Statement::Fill(FillDirective {
497 count,
498 size,
499 value,
500 span,
501 })))
502 }
503
504 fn parse_space_directive(&mut self, span: Span) -> Result<Option<Statement>, AsmError> {
505 let size = self.parse_const_expr()? as u32;
506 let fill = if self.peek().kind == TokenKind::Comma {
507 self.advance();
508 self.parse_const_expr()? as u8
509 } else {
510 0
511 };
512 Ok(Some(Statement::Space(SpaceDirective { size, fill, span })))
513 }
514
515 fn parse_org_directive(&mut self, span: Span) -> Result<Option<Statement>, AsmError> {
516 let offset = self.parse_const_expr()? as u64;
517 let fill = if self.peek().kind == TokenKind::Comma {
519 self.advance(); self.parse_const_expr()? as u8
521 } else {
522 0x00
523 };
524 Ok(Some(Statement::Org(OrgDirective { offset, fill, span })))
525 }
526
527 fn parse_const_expr(&mut self) -> Result<i128, AsmError> {
539 self.const_expr_or()
540 }
541
542 fn const_expr_or(&mut self) -> Result<i128, AsmError> {
546 let mut val = self.const_expr_xor()?;
547 while self.peek().kind == TokenKind::Pipe {
548 self.advance();
549 val |= self.const_expr_xor()?;
550 }
551 Ok(val)
552 }
553
554 fn const_expr_xor(&mut self) -> Result<i128, AsmError> {
556 let mut val = self.const_expr_and()?;
557 while self.peek().kind == TokenKind::Caret {
558 self.advance();
559 val ^= self.const_expr_and()?;
560 }
561 Ok(val)
562 }
563
564 fn const_expr_and(&mut self) -> Result<i128, AsmError> {
566 let mut val = self.const_expr_shift()?;
567 while self.peek().kind == TokenKind::Ampersand {
568 self.advance();
569 val &= self.const_expr_shift()?;
570 }
571 Ok(val)
572 }
573
574 fn const_expr_shift(&mut self) -> Result<i128, AsmError> {
576 let mut val = self.const_expr_add()?;
577 loop {
578 match self.peek().kind {
579 TokenKind::LShift => {
580 self.advance();
581 let rhs = self.const_expr_add()?;
582 val = val.wrapping_shl(rhs as u32);
583 }
584 TokenKind::RShift => {
585 self.advance();
586 let rhs = self.const_expr_add()?;
587 val = val.wrapping_shr(rhs as u32);
588 }
589 _ => break,
590 }
591 }
592 Ok(val)
593 }
594
595 fn const_expr_add(&mut self) -> Result<i128, AsmError> {
597 let mut val = self.const_expr_mul()?;
598 loop {
599 match self.peek().kind {
600 TokenKind::Plus => {
601 self.advance();
602 val = val.wrapping_add(self.const_expr_mul()?);
603 }
604 TokenKind::Minus => {
605 self.advance();
606 val = val.wrapping_sub(self.const_expr_mul()?);
607 }
608 _ => break,
609 }
610 }
611 Ok(val)
612 }
613
614 fn const_expr_mul(&mut self) -> Result<i128, AsmError> {
616 let mut val = self.const_expr_unary()?;
617 loop {
618 match self.peek().kind {
619 TokenKind::Star => {
620 self.advance();
621 val = val.wrapping_mul(self.const_expr_unary()?);
622 }
623 TokenKind::Slash => {
624 let span = self.peek().span;
625 self.advance();
626 let rhs = self.const_expr_unary()?;
627 if rhs == 0 {
628 return Err(AsmError::Syntax {
629 msg: String::from("division by zero in constant expression"),
630 span,
631 });
632 }
633 val /= rhs;
634 }
635 TokenKind::Percent => {
636 let span = self.peek().span;
637 self.advance();
638 let rhs = self.const_expr_unary()?;
639 if rhs == 0 {
640 return Err(AsmError::Syntax {
641 msg: String::from("modulo by zero in constant expression"),
642 span,
643 });
644 }
645 val %= rhs;
646 }
647 _ => break,
648 }
649 }
650 Ok(val)
651 }
652
653 fn const_expr_unary(&mut self) -> Result<i128, AsmError> {
655 match self.peek().kind {
656 TokenKind::Minus => {
657 self.advance();
658 Ok(-self.const_expr_unary()?)
659 }
660 TokenKind::Tilde => {
661 self.advance();
662 Ok(!self.const_expr_unary()?)
663 }
664 _ => self.const_expr_atom(),
665 }
666 }
667
668 fn const_expr_atom(&mut self) -> Result<i128, AsmError> {
670 let tok = self.peek().clone();
671 match &tok.kind {
672 TokenKind::Number(n) => {
673 self.advance();
674 Ok(*n)
675 }
676 TokenKind::Ident => {
677 if let Some(&val) = self.constants.get(&*tok.text) {
678 self.advance();
679 Ok(val)
680 } else {
681 Err(AsmError::Syntax {
682 msg: alloc::format!(
683 "expected constant expression, found undefined identifier '{}'",
684 tok.text
685 ),
686 span: tok.span,
687 })
688 }
689 }
690 TokenKind::OpenParen => {
691 self.advance(); let val = self.parse_const_expr()?;
693 if self.peek().kind != TokenKind::CloseParen {
694 return Err(AsmError::Syntax {
695 msg: String::from("expected ')' in constant expression"),
696 span: self.peek().span,
697 });
698 }
699 self.advance(); Ok(val)
701 }
702 _ => Err(AsmError::Syntax {
703 msg: alloc::format!("expected constant expression, found '{}'", tok.text),
704 span: tok.span,
705 }),
706 }
707 }
708
709 fn parse_instruction_or_prefix(&mut self) -> Result<Option<Statement>, AsmError> {
710 let tok = self.peek().clone();
711
712 if self.pos + 1 < self.tokens.len() && self.tokens[self.pos + 1].kind == TokenKind::Equals {
714 let name = tok.text.to_string();
715 let span = tok.span;
716 self.advance(); self.advance(); let value = self.parse_const_expr()?;
719 self.constants.insert(name.clone(), value);
720 return Ok(Some(Statement::Const(ConstDef { name, value, span })));
721 }
722
723 let mut prefixes = PrefixList::new();
727 let mut mnemonic_pos = self.pos;
728 let mut current_span = tok.span;
729
730 loop {
731 let prefix = {
732 let text = &*self.tokens[mnemonic_pos].text;
733 if text.eq_ignore_ascii_case("lock") {
734 Some(Prefix::Lock)
735 } else if text.eq_ignore_ascii_case("rep")
736 || text.eq_ignore_ascii_case("repe")
737 || text.eq_ignore_ascii_case("repz")
738 {
739 Some(Prefix::Rep)
740 } else if text.eq_ignore_ascii_case("repne") || text.eq_ignore_ascii_case("repnz") {
741 Some(Prefix::Repne)
742 } else {
743 None
744 }
745 };
746 match prefix {
747 Some(p) => {
748 prefixes.push(p);
749 self.advance();
750 }
751 None => break,
752 }
753 if self.at_end() || self.peek().kind != TokenKind::Ident {
754 return Ok(Some(Statement::Instruction(Instruction {
756 mnemonic: {
757 let mut lbuf = [0u8; 32];
758 Mnemonic::from(to_lower_buf(&self.tokens[mnemonic_pos].text, &mut lbuf))
759 },
760 operands: OperandList::new(),
761 size_hint: None,
762 prefixes,
763 opmask: None,
764 zeroing: false,
765 broadcast: None,
766 span: current_span,
767 })));
768 }
769 mnemonic_pos = self.pos;
770 current_span = self.tokens[mnemonic_pos].span;
771 }
772
773 let mut mnemonic = {
775 let mut lbuf = [0u8; 32];
776 Mnemonic::from(to_lower_buf(&self.tokens[mnemonic_pos].text, &mut lbuf))
777 };
778 let mnemonic_span = current_span;
779 self.advance(); let mut operands = OperandList::new();
783 let mut size_hint = None;
784 let mut opmask: Option<Register> = None;
785 let mut zeroing = false;
786 let mut broadcast: Option<BroadcastMode> = None;
787
788 if self.syntax == Syntax::Att {
790 if let Some((base, sz)) = strip_att_suffix(&mnemonic) {
791 mnemonic = base;
792 size_hint = Some(sz);
793 }
794 }
795
796 if !self.at_end() && !self.is_statement_end() {
797 let (op, hint) = self.parse_operand()?;
798 let op = if self.peek().kind == TokenKind::Bang {
800 if let Operand::Register(r) = &op {
801 if r.is_arm() || r.is_aarch64() {
802 self.advance(); Operand::Memory(Box::new(MemoryOperand {
804 base: Some(*r),
805 index: None,
806 scale: 1,
807 disp: 0,
808 disp_label: None,
809 segment: None,
810 size: None,
811 addr_mode: AddrMode::PreIndex,
812 index_subtract: false,
813 }))
814 } else {
815 op
816 }
817 } else {
818 op
819 }
820 } else {
821 op
822 };
823 push_operand(&mut operands, op, &mnemonic, mnemonic_span)?;
824 if hint.is_some() && size_hint.is_none() {
825 size_hint = hint;
826 }
827
828 if self.arch == Arch::X86_64 || self.arch == Arch::X86 {
830 self.parse_evex_decorators(&mut opmask, &mut zeroing, &mut broadcast)?;
831 }
832
833 while self.peek().kind == TokenKind::Comma {
834 self.advance(); let (op, hint) = self.parse_operand()?;
836 push_operand(&mut operands, op, &mnemonic, mnemonic_span)?;
837 if hint.is_some() && size_hint.is_none() {
838 size_hint = hint;
839 }
840 if self.arch == Arch::X86_64 || self.arch == Arch::X86 {
842 self.parse_evex_decorators(&mut opmask, &mut zeroing, &mut broadcast)?;
843 }
844 }
845 }
846
847 if self.syntax == Syntax::Att && operands.len() >= 2 {
851 operands.reverse();
852 }
853
854 Ok(Some(Statement::Instruction(Instruction {
855 mnemonic,
856 operands,
857 size_hint,
858 prefixes,
859 opmask,
860 zeroing,
861 broadcast,
862 span: mnemonic_span,
863 })))
864 }
865
866 fn is_statement_end(&self) -> bool {
867 matches!(self.peek().kind, TokenKind::Newline | TokenKind::Eof)
868 }
869
870 fn parse_expr_atom(&mut self, ctx_tok: &Token<'a>) -> Result<Expr, AsmError> {
874 let next = self.peek().clone();
875 match &next.kind {
876 TokenKind::Number(n) => {
877 self.advance();
878 Ok(Expr::Num(*n))
879 }
880 TokenKind::Ident => {
881 self.advance();
882 if let Some(&val) = self.constants.get(&*next.text) {
884 Ok(Expr::Num(val))
885 } else {
886 Ok(Expr::Label(next.text.to_string()))
887 }
888 }
889 _ => Err(AsmError::Syntax {
890 msg: alloc::format!(
891 "expected number or identifier after '+'/'-' near '{}'",
892 ctx_tok.text
893 ),
894 span: next.span,
895 }),
896 }
897 }
898
899 fn parse_evex_decorators(
904 &mut self,
905 opmask: &mut Option<Register>,
906 zeroing: &mut bool,
907 broadcast: &mut Option<BroadcastMode>,
908 ) -> Result<(), AsmError> {
909 while self.peek().kind == TokenKind::OpenBrace {
910 let brace_span = self.peek().span;
911 self.advance(); let tok = self.peek().clone();
913 match tok.kind {
914 TokenKind::Ident => {
915 let mut lbuf = [0u8; 32];
916 let lower = to_lower_buf(&tok.text, &mut lbuf);
917 if lower == "z" {
918 *zeroing = true;
919 self.advance();
920 } else if let Some(kreg) = parse_register_lower(lower, Arch::X86_64) {
921 if kreg.is_opmask() {
922 *opmask = Some(kreg);
923 self.advance();
924 } else {
925 return Err(AsmError::Syntax {
926 msg: String::from("expected opmask register k0-k7"),
927 span: tok.span,
928 });
929 }
930 } else {
931 return Err(AsmError::Syntax {
932 msg: String::from("unexpected identifier in AVX-512 decorator"),
933 span: tok.span,
934 });
935 }
936 }
937 TokenKind::Number(_) => {
938 if tok.text == "1" {
940 self.advance(); let next = self.peek().clone();
943 if next.kind == TokenKind::Ident {
944 let mut lbuf = [0u8; 32];
945 let nlower = to_lower_buf(&next.text, &mut lbuf);
946 let mode = match nlower {
947 "to2" => Some(BroadcastMode::OneToTwo),
948 "to4" => Some(BroadcastMode::OneToFour),
949 "to8" => Some(BroadcastMode::OneToEight),
950 "to16" => Some(BroadcastMode::OneToSixteen),
951 _ => None,
952 };
953 if let Some(m) = mode {
954 *broadcast = Some(m);
955 self.advance();
956 } else {
957 return Err(AsmError::Syntax {
958 msg: String::from("expected 1to2, 1to4, 1to8, or 1to16"),
959 span: next.span,
960 });
961 }
962 } else {
963 return Err(AsmError::Syntax {
964 msg: String::from("expected broadcast specifier (1to2/4/8/16)"),
965 span: next.span,
966 });
967 }
968 } else {
969 return Err(AsmError::Syntax {
970 msg: String::from("unexpected number in AVX-512 decorator"),
971 span: tok.span,
972 });
973 }
974 }
975 _ => {
976 return Err(AsmError::Syntax {
980 msg: String::from("unexpected token in AVX-512 decorator"),
981 span: brace_span,
982 });
983 }
984 }
985 if self.peek().kind == TokenKind::CloseBrace {
987 self.advance();
988 } else {
989 return Err(AsmError::Syntax {
990 msg: String::from("expected '}' after AVX-512 decorator"),
991 span: self.peek().span,
992 });
993 }
994 }
995 Ok(())
996 }
997
998 fn parse_operand(&mut self) -> Result<(Operand, Option<OperandSize>), AsmError> {
999 let tok = self.peek().clone();
1000
1001 if tok.kind == TokenKind::Ident {
1003 let mut lbuf = [0u8; 32];
1004 let lower = to_lower_buf(&tok.text, &mut lbuf);
1005 if let Some(sz) = self.try_parse_size_hint(lower) {
1006 if self.peek().kind == TokenKind::Ident
1008 && self.peek().text.eq_ignore_ascii_case("ptr")
1009 {
1010 self.advance(); }
1012 let (op, _) = self.parse_operand_inner()?;
1013 return Ok((op, Some(sz)));
1014 }
1015 }
1016
1017 self.parse_operand_inner()
1018 }
1019
1020 fn parse_shift_amount(&mut self) -> Result<ShiftAmount, AsmError> {
1025 let tok = self.peek().clone();
1026 match &tok.kind {
1027 TokenKind::Number(n) => {
1028 self.advance();
1029 Ok(ShiftAmount::Immediate(clamp_shift(*n)))
1030 }
1031 TokenKind::Minus => {
1032 self.advance();
1033 match self.peek().kind {
1034 TokenKind::Number(n) => {
1035 self.advance();
1036 Ok(ShiftAmount::Immediate(clamp_shift(-n)))
1037 }
1038 _ => Err(AsmError::Syntax {
1039 msg: String::from("expected a number after '-' in shift amount"),
1040 span: self.peek().span,
1041 }),
1042 }
1043 }
1044 TokenKind::Ident => {
1045 let mut lbuf = [0u8; 32];
1046 let lower = to_lower_buf(&tok.text, &mut lbuf);
1047 match parse_register_lower(lower, self.arch) {
1048 Some(reg) => {
1049 self.advance();
1050 Ok(ShiftAmount::Register(reg))
1051 }
1052 None => Ok(ShiftAmount::None),
1055 }
1056 }
1057 _ => Ok(ShiftAmount::None),
1058 }
1059 }
1060
1061 fn try_parse_size_hint(&mut self, ident: &str) -> Option<OperandSize> {
1063 if ident.eq_ignore_ascii_case("byte") {
1064 self.advance();
1065 Some(OperandSize::Byte)
1066 } else if ident.eq_ignore_ascii_case("word") {
1067 self.advance();
1068 Some(OperandSize::Word)
1069 } else if ident.eq_ignore_ascii_case("dword") {
1070 self.advance();
1071 Some(OperandSize::Dword)
1072 } else if ident.eq_ignore_ascii_case("qword") {
1073 self.advance();
1074 Some(OperandSize::Qword)
1075 } else if ident.eq_ignore_ascii_case("xmmword") || ident.eq_ignore_ascii_case("oword") {
1076 self.advance();
1077 Some(OperandSize::Xmmword)
1078 } else if ident.eq_ignore_ascii_case("ymmword") {
1079 self.advance();
1080 Some(OperandSize::Ymmword)
1081 } else if ident.eq_ignore_ascii_case("zmmword") {
1082 self.advance();
1083 Some(OperandSize::Zmmword)
1084 } else {
1085 None
1086 }
1087 }
1088
1089 fn parse_operand_inner(&mut self) -> Result<(Operand, Option<OperandSize>), AsmError> {
1090 if self.syntax == Syntax::Att {
1092 return self.parse_att_operand();
1093 }
1094
1095 let tok = self.peek().clone();
1096
1097 match &tok.kind {
1098 TokenKind::OpenBrace => {
1102 self.advance(); let first = self.peek().clone();
1105 if let TokenKind::Ident = &first.kind {
1106 let mut lbuf = [0u8; 32];
1107 let lower = to_lower_buf(&first.text, &mut lbuf);
1108 if let Some(dot_pos) = lower.find('.') {
1109 let reg_part = &lower[..dot_pos];
1110 let arr_part = &lower[dot_pos + 1..];
1111 if let Some(reg) = parse_register_lower(reg_part, self.arch) {
1112 if reg.is_a64_sve_z() || reg.is_a64_vector() {
1113 if let Some(arr) = VectorArrangement::parse(arr_part) {
1114 self.advance(); if self.peek().kind != TokenKind::CloseBrace {
1116 return Err(AsmError::Syntax {
1117 msg: String::from("expected '}' after vector register"),
1118 span: self.peek().span,
1119 });
1120 }
1121 self.advance(); return Ok((Operand::VectorRegister(reg, arr), None));
1123 }
1124 }
1125 }
1126 }
1127 }
1128 let mut regs = Vec::new();
1130 loop {
1131 let rtok = self.peek().clone();
1132 if rtok.kind == TokenKind::CloseBrace {
1133 self.advance();
1134 break;
1135 }
1136 if rtok.kind == TokenKind::Comma {
1137 self.advance();
1138 continue;
1139 }
1140 if let TokenKind::Ident = &rtok.kind {
1141 let mut lbuf = [0u8; 32];
1142 let lower = to_lower_buf(&rtok.text, &mut lbuf);
1143 if let Some(reg) = parse_register_lower(lower, self.arch) {
1144 self.advance();
1145 if self.peek().kind == TokenKind::Minus {
1148 self.advance(); let etok = self.peek().clone();
1150 let mut ebuf = [0u8; 32];
1151 let elower = to_lower_buf(&etok.text, &mut ebuf);
1152 let end = match (&etok.kind, parse_register_lower(elower, self.arch))
1153 {
1154 (TokenKind::Ident, Some(r)) => r,
1155 _ => {
1156 return Err(AsmError::Syntax {
1157 msg: alloc::format!(
1158 "expected register after '-' in register range, found '{}'",
1159 etok.text
1160 ),
1161 span: etok.span,
1162 })
1163 }
1164 };
1165 self.advance();
1166 expand_register_range(reg, end, &mut regs, etok.span)?;
1167 continue;
1168 }
1169 regs.push(reg);
1170 continue;
1171 }
1172 }
1173 return Err(AsmError::Syntax {
1174 msg: alloc::format!(
1175 "expected register in register list, found '{}'",
1176 rtok.text
1177 ),
1178 span: rtok.span,
1179 });
1180 }
1181 Ok((Operand::RegisterList(regs), None))
1182 }
1183
1184 TokenKind::OpenBracket => {
1186 let mem = self.parse_memory_operand()?;
1187 Ok((Operand::Memory(Box::new(mem)), None))
1188 }
1189
1190 TokenKind::Equals => {
1192 self.advance(); let next = self.peek().clone();
1194 match next.kind {
1195 TokenKind::Number(n) => {
1196 self.advance();
1197 Ok((Operand::LiteralPoolValue(n), None))
1198 }
1199 TokenKind::Minus => {
1200 self.advance();
1201 if let TokenKind::Number(n) = self.peek().kind {
1202 self.advance();
1203 Ok((Operand::LiteralPoolValue(-n), None))
1204 } else {
1205 Err(AsmError::Syntax {
1206 msg: String::from("expected number after '=-'"),
1207 span: next.span,
1208 })
1209 }
1210 }
1211 _ => Err(AsmError::Syntax {
1212 msg: alloc::format!("expected number after '=', found '{}'", next.text),
1213 span: next.span,
1214 }),
1215 }
1216 }
1217
1218 TokenKind::OpenParen if matches!(self.arch, Arch::Rv32 | Arch::Rv64) => {
1220 self.parse_riscv_mem_operand(0)
1221 }
1222
1223 TokenKind::Number(n) => {
1225 let val = *n;
1226 self.advance();
1227 if matches!(self.arch, Arch::Rv32 | Arch::Rv64)
1229 && self.peek().kind == TokenKind::OpenParen
1230 {
1231 return self.parse_riscv_mem_operand(val);
1232 }
1233 Ok((Operand::Immediate(val), None))
1234 }
1235
1236 TokenKind::Minus => {
1238 self.advance();
1239 let next = self.peek().clone();
1240 if let TokenKind::Number(n) = next.kind {
1241 self.advance();
1242 let val = -n;
1243 if matches!(self.arch, Arch::Rv32 | Arch::Rv64)
1245 && self.peek().kind == TokenKind::OpenParen
1246 {
1247 return self.parse_riscv_mem_operand(val);
1248 }
1249 Ok((Operand::Immediate(val), None))
1250 } else {
1251 Err(AsmError::Syntax {
1252 msg: String::from("expected number after '-'"),
1253 span: tok.span,
1254 })
1255 }
1256 }
1257
1258 TokenKind::CharLit(ch) => {
1260 self.advance();
1261 Ok((Operand::Immediate(*ch as i128), None))
1262 }
1263
1264 TokenKind::Ident => {
1266 let mut lbuf = [0u8; 32];
1267 let lower = to_lower_buf(&tok.text, &mut lbuf);
1268
1269 if is_segment_name(lower) {
1271 let seg = match parse_segment(lower) {
1272 Some(s) => s,
1273 None => {
1274 return Err(AsmError::Syntax {
1275 msg: alloc::format!("unknown segment register: {}", lower),
1276 span: tok.span,
1277 });
1278 }
1279 };
1280 if self.pos + 1 < self.tokens.len()
1282 && self.tokens[self.pos + 1].kind == TokenKind::Colon
1283 {
1284 self.advance(); self.advance(); if self.peek().kind == TokenKind::OpenBracket {
1288 let mut mem = self.parse_memory_operand()?;
1289 mem.segment = Some(seg);
1290 return Ok((Operand::Memory(Box::new(mem)), None));
1291 }
1292 }
1293 }
1294
1295 if matches!(self.arch, Arch::Arm | Arch::Thumb) {
1299 if let Some(psr) = PsrField::from_lower(lower) {
1300 self.advance();
1301 return Ok((Operand::PsrField(psr), None));
1302 }
1303 }
1304
1305 if matches!(self.arch, Arch::Arm | Arch::Thumb | Arch::Aarch64) {
1310 if let Some(shift_op) = ShiftOp::from_lower(lower) {
1311 self.advance();
1312 let amount = self.parse_shift_amount()?;
1313 return Ok((Operand::Shift(shift_op, amount), None));
1314 }
1315 }
1316
1317 if let Some(dot_pos) = lower.find('.') {
1319 let reg_part = &lower[..dot_pos];
1321 let arr_part = &lower[dot_pos + 1..];
1322 if let Some(reg) = parse_register_lower(reg_part, self.arch) {
1323 if reg.is_a64_vector() {
1327 if let Some(size) = ElementSize::from_lower(arr_part) {
1328 if self.tokens.get(self.pos + 1).map(|t| &t.kind)
1329 == Some(&TokenKind::OpenBracket)
1330 {
1331 self.advance(); self.advance(); let idx_tok = self.peek().clone();
1334 let index = match idx_tok.kind {
1335 TokenKind::Number(n)
1336 if (0..i128::from(size.lane_count())).contains(&n) =>
1337 {
1338 n as u8
1339 }
1340 TokenKind::Number(n) => {
1341 return Err(AsmError::Syntax {
1342 msg: alloc::format!(
1343 "lane index {} out of range for .{} (0-{})",
1344 n,
1345 size,
1346 size.lane_count() - 1
1347 ),
1348 span: idx_tok.span,
1349 })
1350 }
1351 _ => {
1352 return Err(AsmError::Syntax {
1353 msg: String::from("expected a lane index"),
1354 span: idx_tok.span,
1355 })
1356 }
1357 };
1358 self.advance(); if self.peek().kind != TokenKind::CloseBracket {
1360 return Err(AsmError::Syntax {
1361 msg: String::from("expected ']' after lane index"),
1362 span: self.peek().span,
1363 });
1364 }
1365 self.advance(); return Ok((Operand::VectorElement(reg, size, index), None));
1367 }
1368 }
1369 }
1370 if reg.is_a64_vector() || reg.is_a64_sve_z() || reg.is_a64_sve_p() {
1371 if let Some(arr) = VectorArrangement::parse(arr_part) {
1372 self.advance();
1373 return Ok((Operand::VectorRegister(reg, arr), None));
1374 }
1375 }
1376 if reg.is_riscv_vec() && arr_part == "t" {
1378 self.advance();
1379 return Ok((Operand::Register(reg), None));
1380 }
1381 }
1382 }
1383
1384 if let Some(reg) = parse_register_lower(lower, self.arch) {
1385 self.advance();
1386 if reg.is_a64_sve_p() && self.peek().kind == TokenKind::Slash {
1388 let next_pos = self.pos + 1;
1389 if next_pos < self.tokens.len() {
1390 let qual_text = &self.tokens[next_pos].text;
1391 let qual = if qual_text.eq_ignore_ascii_case("m") {
1392 Some(SvePredQual::Merging)
1393 } else if qual_text.eq_ignore_ascii_case("z") {
1394 Some(SvePredQual::Zeroing)
1395 } else {
1396 None
1397 };
1398 if let Some(q) = qual {
1399 self.advance(); self.advance(); return Ok((Operand::SvePredicate(reg, q), None));
1402 }
1403 }
1404 }
1405 return Ok((Operand::Register(reg), None));
1406 }
1407
1408 if let Some(&val) = self.constants.get(&*tok.text) {
1410 self.advance();
1411 let mut result = val;
1413 loop {
1414 if self.peek().kind == TokenKind::Plus {
1415 self.advance();
1416 let next = self.peek().clone();
1417 match &next.kind {
1418 TokenKind::Number(n) => {
1419 self.advance();
1420 result += n;
1421 }
1422 TokenKind::Ident => {
1423 if let Some(&v) = self.constants.get(&*next.text) {
1424 self.advance();
1425 result += v;
1426 } else {
1427 break;
1428 }
1429 }
1430 _ => break,
1431 }
1432 } else if self.peek().kind == TokenKind::Minus {
1433 self.advance();
1434 let next = self.peek().clone();
1435 match &next.kind {
1436 TokenKind::Number(n) => {
1437 self.advance();
1438 result -= n;
1439 }
1440 TokenKind::Ident => {
1441 if let Some(&v) = self.constants.get(&*next.text) {
1442 self.advance();
1443 result -= v;
1444 } else {
1445 break;
1446 }
1447 }
1448 _ => break,
1449 }
1450 } else {
1451 break;
1452 }
1453 }
1454 return Ok((Operand::Immediate(result), None));
1455 }
1456
1457 self.advance();
1459 let mut expr: Expr = Expr::Label(tok.text.to_string());
1462 let mut is_expression = false;
1463 loop {
1464 if self.peek().kind == TokenKind::Plus {
1465 self.advance();
1466 let rhs = self.parse_expr_atom(&tok)?;
1467 expr = Expr::Add(Box::new(expr), Box::new(rhs));
1468 is_expression = true;
1469 } else if self.peek().kind == TokenKind::Minus {
1470 self.advance();
1471 let rhs = self.parse_expr_atom(&tok)?;
1472 expr = Expr::Sub(Box::new(expr), Box::new(rhs));
1473 is_expression = true;
1474 } else {
1475 break;
1476 }
1477 }
1478
1479 if is_expression {
1480 expr.resolve_constants(|name| self.constants.get(name).copied());
1482 if let Some(val) = expr.eval() {
1483 return Ok((Operand::Immediate(val), None));
1484 }
1485 return Ok((Operand::Expression(expr), None));
1486 }
1487
1488 Ok((Operand::Label(tok.text.to_string()), None))
1489 }
1490
1491 TokenKind::NumericLabelFwd(n) => {
1493 self.advance();
1494 Ok((Operand::Label(alloc::format!("{}f", n)), None))
1495 }
1496 TokenKind::NumericLabelBwd(n) => {
1497 self.advance();
1498 Ok((Operand::Label(alloc::format!("{}b", n)), None))
1499 }
1500
1501 _ => Err(AsmError::Syntax {
1502 msg: alloc::format!("expected operand, found '{}'", tok.text),
1503 span: tok.span,
1504 }),
1505 }
1506 }
1507
1508 fn parse_att_operand(&mut self) -> Result<(Operand, Option<OperandSize>), AsmError> {
1520 let tok = self.peek().clone();
1521
1522 match &tok.kind {
1523 TokenKind::Dollar => {
1525 self.advance(); let next = self.peek().clone();
1527 match &next.kind {
1528 TokenKind::Number(n) => {
1529 let val = *n;
1530 self.advance();
1531 Ok((Operand::Immediate(val), None))
1532 }
1533 TokenKind::Minus => {
1534 self.advance(); let num_tok = self.peek().clone();
1536 if let TokenKind::Number(n) = num_tok.kind {
1537 self.advance();
1538 Ok((Operand::Immediate(-n), None))
1539 } else {
1540 Err(AsmError::Syntax {
1541 msg: String::from("expected number after '$-'"),
1542 span: num_tok.span,
1543 })
1544 }
1545 }
1546 TokenKind::Ident => {
1547 let name = next.text.to_string();
1548 self.advance();
1549 if let Some(&val) = self.constants.get(&name) {
1551 Ok((Operand::Immediate(val), None))
1552 } else {
1553 Ok((Operand::Label(name), None))
1555 }
1556 }
1557 _ => Err(AsmError::Syntax {
1558 msg: alloc::format!(
1559 "expected number or identifier after '$', found '{}'",
1560 next.text
1561 ),
1562 span: next.span,
1563 }),
1564 }
1565 }
1566
1567 TokenKind::Percent => {
1569 self.advance(); let reg_tok = self.peek().clone();
1571 if reg_tok.kind != TokenKind::Ident {
1572 return Err(AsmError::Syntax {
1573 msg: alloc::format!(
1574 "expected register name after '%', found '{}'",
1575 reg_tok.text
1576 ),
1577 span: reg_tok.span,
1578 });
1579 }
1580 let mut lbuf = [0u8; 32];
1581 let lower = to_lower_buf(®_tok.text, &mut lbuf);
1582
1583 if is_segment_name(lower) {
1585 let seg = parse_segment(lower).unwrap();
1588 if self.pos + 1 < self.tokens.len()
1590 && self.tokens[self.pos + 1].kind == TokenKind::Colon
1591 {
1592 self.advance(); self.advance(); let (seg_disp, seg_disp_label) = match self.peek().kind {
1596 TokenKind::Number(n) => {
1597 let val = n;
1598 self.advance();
1599 (val as i64, None)
1600 }
1601 TokenKind::Minus => {
1602 self.advance();
1603 if let TokenKind::Number(n) = self.peek().kind {
1604 let val = n;
1605 self.advance();
1606 (-(val as i64), None)
1607 } else {
1608 (0, None)
1609 }
1610 }
1611 _ => (0, None),
1612 };
1613 let mut mem = self.parse_att_memory_operand(seg_disp, seg_disp_label)?;
1614 mem.segment = Some(seg);
1615 return Ok((Operand::Memory(Box::new(mem)), None));
1616 }
1617 }
1618
1619 if let Some(reg) = parse_register_lower(lower, self.arch) {
1621 self.advance();
1622 Ok((Operand::Register(reg), None))
1623 } else {
1624 Err(AsmError::Syntax {
1625 msg: alloc::format!("unknown register: %{}", lower),
1626 span: reg_tok.span,
1627 })
1628 }
1629 }
1630
1631 TokenKind::OpenParen => {
1633 let mem = self.parse_att_memory_operand(0, None)?;
1634 Ok((Operand::Memory(Box::new(mem)), None))
1635 }
1636
1637 TokenKind::Number(n) => {
1639 let val = *n;
1640 self.advance();
1641 if self.peek().kind == TokenKind::OpenParen {
1643 let mem = self.parse_att_memory_operand(val as i64, None)?;
1644 Ok((Operand::Memory(Box::new(mem)), None))
1645 } else {
1646 Ok((Operand::Immediate(val), None))
1648 }
1649 }
1650
1651 TokenKind::Minus => {
1653 self.advance(); let next = self.peek().clone();
1655 if let TokenKind::Number(n) = next.kind {
1656 self.advance();
1657 let val = -n;
1658 if self.peek().kind == TokenKind::OpenParen {
1659 let mem = self.parse_att_memory_operand(val as i64, None)?;
1660 Ok((Operand::Memory(Box::new(mem)), None))
1661 } else {
1662 Ok((Operand::Immediate(val), None))
1663 }
1664 } else {
1665 Err(AsmError::Syntax {
1666 msg: String::from("expected number after '-' in AT&T operand"),
1667 span: tok.span,
1668 })
1669 }
1670 }
1671
1672 TokenKind::Ident => {
1674 let name = tok.text.to_string();
1675 self.advance();
1676 if let Some(&val) = self.constants.get(&name) {
1678 if self.peek().kind == TokenKind::OpenParen {
1680 let mem = self.parse_att_memory_operand(val as i64, None)?;
1681 return Ok((Operand::Memory(Box::new(mem)), None));
1682 }
1683 return Ok((Operand::Immediate(val), None));
1684 }
1685 let mut expr = Expr::Label(name.clone());
1687 let mut has_offset = false;
1688 loop {
1689 if self.peek().kind == TokenKind::Plus {
1690 self.advance();
1691 let atom = self.parse_expr_atom(&tok)?;
1692 expr = Expr::Add(Box::new(expr), Box::new(atom));
1693 has_offset = true;
1694 } else if self.peek().kind == TokenKind::Minus {
1695 self.advance();
1696 let atom = self.parse_expr_atom(&tok)?;
1697 expr = Expr::Sub(Box::new(expr), Box::new(atom));
1698 has_offset = true;
1699 } else {
1700 break;
1701 }
1702 }
1703 if has_offset {
1704 Ok((Operand::Expression(expr), None))
1705 } else {
1706 Ok((Operand::Label(name), None))
1707 }
1708 }
1709
1710 TokenKind::NumericLabelFwd(n) => {
1712 let n = *n;
1713 self.advance();
1714 Ok((Operand::Label(alloc::format!("{}f", n)), None))
1715 }
1716 TokenKind::NumericLabelBwd(n) => {
1717 let n = *n;
1718 self.advance();
1719 Ok((Operand::Label(alloc::format!("{}b", n)), None))
1720 }
1721
1722 TokenKind::Star => {
1724 self.advance(); self.parse_att_operand()
1727 }
1728
1729 _ => Err(AsmError::Syntax {
1730 msg: alloc::format!("unexpected token in AT&T operand: '{}'", tok.text),
1731 span: tok.span,
1732 }),
1733 }
1734 }
1735
1736 fn parse_att_memory_operand(
1740 &mut self,
1741 disp: i64,
1742 disp_label: Option<String>,
1743 ) -> Result<MemoryOperand, AsmError> {
1744 let open = self.peek().clone();
1745 if open.kind != TokenKind::OpenParen {
1746 return Err(AsmError::Syntax {
1747 msg: alloc::format!("expected '(' in AT&T memory operand, found '{}'", open.text),
1748 span: open.span,
1749 });
1750 }
1751 self.advance(); let mut base = None;
1754 let mut index = None;
1755 let mut scale: u8 = 1;
1756
1757 if self.peek().kind == TokenKind::Percent {
1759 self.advance(); let reg_tok = self.peek().clone();
1761 let mut lbuf = [0u8; 32];
1762 let lower = to_lower_buf(®_tok.text, &mut lbuf);
1763 base =
1764 Some(
1765 parse_register_lower(lower, self.arch).ok_or_else(|| AsmError::Syntax {
1766 msg: alloc::format!("unknown register: %{}", lower),
1767 span: reg_tok.span,
1768 })?,
1769 );
1770 self.advance();
1771 }
1772
1773 if self.peek().kind == TokenKind::Comma {
1775 self.advance(); if self.peek().kind == TokenKind::Percent {
1777 self.advance(); let reg_tok = self.peek().clone();
1779 let mut lbuf = [0u8; 32];
1780 let lower = to_lower_buf(®_tok.text, &mut lbuf);
1781 index = Some(parse_register_lower(lower, self.arch).ok_or_else(|| {
1782 AsmError::Syntax {
1783 msg: alloc::format!("unknown register: %{}", lower),
1784 span: reg_tok.span,
1785 }
1786 })?);
1787 self.advance();
1788 }
1789
1790 if self.peek().kind == TokenKind::Comma {
1792 self.advance(); let scale_tok = self.peek().clone();
1794 if let TokenKind::Number(n) = scale_tok.kind {
1795 scale = n as u8;
1796 self.advance();
1797 } else {
1798 return Err(AsmError::Syntax {
1799 msg: alloc::format!(
1800 "expected scale factor (1,2,4,8), found '{}'",
1801 scale_tok.text
1802 ),
1803 span: scale_tok.span,
1804 });
1805 }
1806 }
1807 }
1808
1809 let close = self.peek().clone();
1811 if close.kind != TokenKind::CloseParen {
1812 return Err(AsmError::Syntax {
1813 msg: alloc::format!(
1814 "expected ')' in AT&T memory operand, found '{}'",
1815 close.text
1816 ),
1817 span: close.span,
1818 });
1819 }
1820 self.advance();
1821
1822 Ok(MemoryOperand {
1823 base,
1824 index,
1825 scale,
1826 disp,
1827 disp_label,
1828 segment: None,
1829 size: None,
1830 addr_mode: AddrMode::Offset,
1831 index_subtract: false,
1832 })
1833 }
1834
1835 fn parse_riscv_mem_operand(
1838 &mut self,
1839 offset: i128,
1840 ) -> Result<(Operand, Option<OperandSize>), AsmError> {
1841 let open_tok = self.advance().clone(); debug_assert_eq!(open_tok.kind, TokenKind::OpenParen);
1843
1844 let reg_tok = self.peek().clone();
1845 let mut lbuf = [0u8; 32];
1846 let lower = to_lower_buf(®_tok.text, &mut lbuf);
1847 let reg = if let Some(r) = parse_register_lower(lower, self.arch) {
1848 self.advance();
1849 r
1850 } else {
1851 return Err(AsmError::Syntax {
1852 msg: alloc::format!(
1853 "expected register in memory operand, found '{}'",
1854 reg_tok.text
1855 ),
1856 span: reg_tok.span,
1857 });
1858 };
1859
1860 let close = self.peek().clone();
1862 if close.kind != TokenKind::CloseParen {
1863 return Err(AsmError::Syntax {
1864 msg: alloc::format!("expected ')' after register, found '{}'", close.text),
1865 span: close.span,
1866 });
1867 }
1868 self.advance();
1869
1870 let mem = MemoryOperand {
1871 base: Some(reg),
1872 disp: offset as i64,
1873 ..Default::default()
1874 };
1875 Ok((Operand::Memory(Box::new(mem)), None))
1876 }
1877
1878 fn parse_memory_operand(&mut self) -> Result<MemoryOperand, AsmError> {
1880 let open = self.advance().clone(); debug_assert_eq!(open.kind, TokenKind::OpenBracket);
1882
1883 let mut mem = MemoryOperand::default();
1884 let mut _expect_term = true;
1885 let mut sign: i64 = 1;
1886
1887 while self.peek().kind != TokenKind::CloseBracket {
1888 if self.at_end() {
1889 return Err(AsmError::Syntax {
1890 msg: String::from("unterminated memory operand, expected ']'"),
1891 span: open.span,
1892 });
1893 }
1894
1895 let tok = self.peek().clone();
1896
1897 match &tok.kind {
1898 TokenKind::Plus | TokenKind::Comma => {
1899 self.advance();
1901 sign = 1;
1902 _expect_term = true;
1903 continue;
1904 }
1905 TokenKind::Minus => {
1906 self.advance();
1907 sign = -1;
1908 _expect_term = true;
1909 continue;
1910 }
1911 TokenKind::Ident => {
1912 let mut lbuf = [0u8; 32];
1913 let lower = to_lower_buf(&tok.text, &mut lbuf);
1914 if let Some(reg) = parse_register_lower(lower, self.arch) {
1915 self.advance();
1916 if self.peek().kind == TokenKind::Star {
1918 self.advance(); let scale_tok = self.peek().clone();
1920 if let TokenKind::Number(s) = scale_tok.kind {
1921 if !matches!(s, 1 | 2 | 4 | 8) {
1922 return Err(AsmError::Syntax {
1923 msg: String::from("scale factor must be 1, 2, 4, or 8"),
1924 span: scale_tok.span,
1925 });
1926 }
1927 self.advance();
1928 mem.index = Some(reg);
1929 mem.scale = s as u8;
1930 mem.index_subtract = sign < 0;
1931 } else {
1932 return Err(AsmError::Syntax {
1933 msg: String::from("expected scale factor (1, 2, 4, or 8)"),
1934 span: scale_tok.span,
1935 });
1936 }
1937 } else if mem.base.is_none() {
1938 mem.base = Some(reg);
1939 } else if mem.index.is_none() {
1940 mem.index = Some(reg);
1941 mem.scale = 1;
1942 mem.index_subtract = sign < 0;
1943 } else {
1944 return Err(AsmError::Syntax {
1945 msg: String::from("too many registers in memory operand"),
1946 span: tok.span,
1947 });
1948 }
1949 } else {
1950 self.advance();
1952 mem.disp_label = Some(tok.text.to_string());
1953 }
1954 _expect_term = false;
1955 }
1956 TokenKind::Number(n) => {
1957 self.advance();
1958 if self.peek().kind == TokenKind::Star {
1960 self.advance(); let reg_tok = self.peek().clone();
1962 if reg_tok.kind == TokenKind::Ident {
1963 let mut lbuf = [0u8; 32];
1964 let lower = to_lower_buf(®_tok.text, &mut lbuf);
1965 if let Some(reg) = parse_register_lower(lower, self.arch) {
1966 if !matches!(*n, 1 | 2 | 4 | 8) {
1967 return Err(AsmError::Syntax {
1968 msg: String::from("scale factor must be 1, 2, 4, or 8"),
1969 span: tok.span,
1970 });
1971 }
1972 self.advance();
1973 mem.index = Some(reg);
1974 mem.scale = *n as u8;
1975 mem.index_subtract = sign < 0;
1976 _expect_term = false;
1977 continue;
1978 }
1979 }
1980 return Err(AsmError::Syntax {
1981 msg: String::from("expected register after scale factor"),
1982 span: reg_tok.span,
1983 });
1984 }
1985 mem.disp = mem.disp.wrapping_add(sign * (*n as i64));
1986 _expect_term = false;
1987 }
1988 _ => {
1989 return Err(AsmError::Syntax {
1990 msg: alloc::format!("unexpected token '{}' in memory operand", tok.text),
1991 span: tok.span,
1992 });
1993 }
1994 }
1995 }
1996
1997 self.advance(); if self.peek().kind == TokenKind::Bang {
2001 self.advance(); mem.addr_mode = AddrMode::PreIndex;
2003 }
2004
2005 if let Some(idx) = mem.index {
2009 if idx.base_code() == 4 && !idx.is_extended() {
2010 return Err(AsmError::Syntax {
2011 msg: String::from("RSP/ESP/SP cannot be used as a SIB index register"),
2012 span: open.span,
2013 });
2014 }
2015 }
2016
2017 Ok(mem)
2018 }
2019}
2020
2021pub fn parse_register(name: &str, arch: Arch) -> Option<Register> {
2026 let mut buf = [0u8; 16];
2028 let name = to_lower_buf(name, &mut buf);
2029 parse_register_lower(name, arch)
2030}
2031
2032fn push_operand(
2040 operands: &mut OperandList,
2041 op: Operand,
2042 mnemonic: &str,
2043 span: Span,
2044) -> Result<(), AsmError> {
2045 if operands.try_push(op) {
2046 Ok(())
2047 } else {
2048 Err(AsmError::InvalidOperands {
2049 detail: alloc::format!(
2050 "'{}' has more than {} operands",
2051 mnemonic,
2052 OperandList::MAX_LEN
2053 ),
2054 span,
2055 })
2056 }
2057}
2058
2059fn clamp_shift(n: i128) -> i32 {
2065 n.clamp(i128::from(i32::MIN), i128::from(i32::MAX)) as i32
2066}
2067
2068fn expand_register_range(
2073 start: Register,
2074 end: Register,
2075 regs: &mut Vec<Register>,
2076 span: Span,
2077) -> Result<(), AsmError> {
2078 if !start.is_arm() || !end.is_arm() {
2079 return Err(AsmError::Syntax {
2080 msg: String::from("register ranges are only supported for ARM32/Thumb registers"),
2081 span,
2082 });
2083 }
2084 let (lo, hi) = (start.arm_reg_num(), end.arm_reg_num());
2085 if lo > hi {
2086 return Err(AsmError::Syntax {
2087 msg: alloc::format!("register range r{}-r{} is inverted", lo, hi),
2088 span,
2089 });
2090 }
2091 for n in lo..=hi {
2092 if let Some(r) = Register::from_arm_reg_num(n) {
2093 regs.push(r);
2094 }
2095 }
2096 Ok(())
2097}
2098
2099fn parse_register_lower(name: &str, arch: Arch) -> Option<Register> {
2100 use Register::*;
2101
2102 match arch {
2104 Arch::Arm | Arch::Thumb => return parse_register_arm(name),
2105 Arch::Aarch64 => return parse_register_aarch64(name),
2106 Arch::Rv32 | Arch::Rv64 => return parse_register_riscv(name),
2107 _ => {}
2108 }
2109
2110 match name {
2112 "rax" => Some(Rax),
2114 "rcx" => Some(Rcx),
2115 "rdx" => Some(Rdx),
2116 "rbx" => Some(Rbx),
2117 "rsp" => Some(Rsp),
2118 "rbp" => Some(Rbp),
2119 "rsi" => Some(Rsi),
2120 "rdi" => Some(Rdi),
2121 "r8" => Some(R8),
2122 "r9" => Some(R9),
2123 "r10" => Some(R10),
2124 "r11" => Some(R11),
2125 "r12" => Some(R12),
2126 "r13" => Some(R13),
2127 "r14" => Some(R14),
2128 "r15" => Some(R15),
2129 "eax" => Some(Eax),
2131 "ecx" => Some(Ecx),
2132 "edx" => Some(Edx),
2133 "ebx" => Some(Ebx),
2134 "esp" => Some(Esp),
2135 "ebp" => Some(Ebp),
2136 "esi" => Some(Esi),
2137 "edi" => Some(Edi),
2138 "r8d" => Some(R8d),
2139 "r9d" => Some(R9d),
2140 "r10d" => Some(R10d),
2141 "r11d" => Some(R11d),
2142 "r12d" => Some(R12d),
2143 "r13d" => Some(R13d),
2144 "r14d" => Some(R14d),
2145 "r15d" => Some(R15d),
2146 "ax" => Some(Ax),
2148 "cx" => Some(Cx),
2149 "dx" => Some(Dx),
2150 "bx" => Some(Bx),
2151 "sp" => Some(Sp),
2152 "bp" => Some(Bp),
2153 "si" => Some(Si),
2154 "di" => Some(Di),
2155 "r8w" => Some(R8w),
2156 "r9w" => Some(R9w),
2157 "r10w" => Some(R10w),
2158 "r11w" => Some(R11w),
2159 "r12w" => Some(R12w),
2160 "r13w" => Some(R13w),
2161 "r14w" => Some(R14w),
2162 "r15w" => Some(R15w),
2163 "al" => Some(Al),
2165 "cl" => Some(Cl),
2166 "dl" => Some(Dl),
2167 "bl" => Some(Bl),
2168 "spl" => Some(Spl),
2169 "bpl" => Some(Bpl),
2170 "sil" => Some(Sil),
2171 "dil" => Some(Dil),
2172 "ah" => Some(Ah),
2173 "ch" => Some(Ch),
2174 "dh" => Some(Dh),
2175 "bh" => Some(Bh),
2176 "r8b" => Some(R8b),
2177 "r9b" => Some(R9b),
2178 "r10b" => Some(R10b),
2179 "r11b" => Some(R11b),
2180 "r12b" => Some(R12b),
2181 "r13b" => Some(R13b),
2182 "r14b" => Some(R14b),
2183 "r15b" => Some(R15b),
2184 "rip" => Some(Rip),
2186 "eip" => Some(Eip),
2187 "cs" => Some(Cs),
2189 "ds" => Some(Ds),
2190 "es" => Some(Es),
2191 "fs" => Some(Fs),
2192 "gs" => Some(Gs),
2193 "ss" => Some(Ss),
2194 "xmm0" => Some(Xmm0),
2196 "xmm1" => Some(Xmm1),
2197 "xmm2" => Some(Xmm2),
2198 "xmm3" => Some(Xmm3),
2199 "xmm4" => Some(Xmm4),
2200 "xmm5" => Some(Xmm5),
2201 "xmm6" => Some(Xmm6),
2202 "xmm7" => Some(Xmm7),
2203 "xmm8" => Some(Xmm8),
2204 "xmm9" => Some(Xmm9),
2205 "xmm10" => Some(Xmm10),
2206 "xmm11" => Some(Xmm11),
2207 "xmm12" => Some(Xmm12),
2208 "xmm13" => Some(Xmm13),
2209 "xmm14" => Some(Xmm14),
2210 "xmm15" => Some(Xmm15),
2211 "ymm0" => Some(Ymm0),
2213 "ymm1" => Some(Ymm1),
2214 "ymm2" => Some(Ymm2),
2215 "ymm3" => Some(Ymm3),
2216 "ymm4" => Some(Ymm4),
2217 "ymm5" => Some(Ymm5),
2218 "ymm6" => Some(Ymm6),
2219 "ymm7" => Some(Ymm7),
2220 "ymm8" => Some(Ymm8),
2221 "ymm9" => Some(Ymm9),
2222 "ymm10" => Some(Ymm10),
2223 "ymm11" => Some(Ymm11),
2224 "ymm12" => Some(Ymm12),
2225 "ymm13" => Some(Ymm13),
2226 "ymm14" => Some(Ymm14),
2227 "ymm15" => Some(Ymm15),
2228 "zmm0" => Some(Zmm0),
2230 "zmm1" => Some(Zmm1),
2231 "zmm2" => Some(Zmm2),
2232 "zmm3" => Some(Zmm3),
2233 "zmm4" => Some(Zmm4),
2234 "zmm5" => Some(Zmm5),
2235 "zmm6" => Some(Zmm6),
2236 "zmm7" => Some(Zmm7),
2237 "zmm8" => Some(Zmm8),
2238 "zmm9" => Some(Zmm9),
2239 "zmm10" => Some(Zmm10),
2240 "zmm11" => Some(Zmm11),
2241 "zmm12" => Some(Zmm12),
2242 "zmm13" => Some(Zmm13),
2243 "zmm14" => Some(Zmm14),
2244 "zmm15" => Some(Zmm15),
2245 "zmm16" => Some(Zmm16),
2246 "zmm17" => Some(Zmm17),
2247 "zmm18" => Some(Zmm18),
2248 "zmm19" => Some(Zmm19),
2249 "zmm20" => Some(Zmm20),
2250 "zmm21" => Some(Zmm21),
2251 "zmm22" => Some(Zmm22),
2252 "zmm23" => Some(Zmm23),
2253 "zmm24" => Some(Zmm24),
2254 "zmm25" => Some(Zmm25),
2255 "zmm26" => Some(Zmm26),
2256 "zmm27" => Some(Zmm27),
2257 "zmm28" => Some(Zmm28),
2258 "zmm29" => Some(Zmm29),
2259 "zmm30" => Some(Zmm30),
2260 "zmm31" => Some(Zmm31),
2261 "k0" => Some(K0),
2263 "k1" => Some(K1),
2264 "k2" => Some(K2),
2265 "k3" => Some(K3),
2266 "k4" => Some(K4),
2267 "k5" => Some(K5),
2268 "k6" => Some(K6),
2269 "k7" => Some(K7),
2270 _ => None,
2271 }
2272}
2273
2274fn is_segment_name(name: &str) -> bool {
2275 name.eq_ignore_ascii_case("cs")
2276 || name.eq_ignore_ascii_case("ds")
2277 || name.eq_ignore_ascii_case("es")
2278 || name.eq_ignore_ascii_case("fs")
2279 || name.eq_ignore_ascii_case("gs")
2280 || name.eq_ignore_ascii_case("ss")
2281}
2282
2283fn parse_segment(name: &str) -> Option<Register> {
2284 if name.eq_ignore_ascii_case("cs") {
2285 Some(Register::Cs)
2286 } else if name.eq_ignore_ascii_case("ds") {
2287 Some(Register::Ds)
2288 } else if name.eq_ignore_ascii_case("es") {
2289 Some(Register::Es)
2290 } else if name.eq_ignore_ascii_case("fs") {
2291 Some(Register::Fs)
2292 } else if name.eq_ignore_ascii_case("gs") {
2293 Some(Register::Gs)
2294 } else if name.eq_ignore_ascii_case("ss") {
2295 Some(Register::Ss)
2296 } else {
2297 None
2298 }
2299}
2300
2301fn parse_register_arm(name: &str) -> Option<Register> {
2303 use Register::*;
2304 match name {
2305 "r0" => Some(ArmR0),
2306 "r1" => Some(ArmR1),
2307 "r2" => Some(ArmR2),
2308 "r3" => Some(ArmR3),
2309 "r4" => Some(ArmR4),
2310 "r5" => Some(ArmR5),
2311 "r6" => Some(ArmR6),
2312 "r7" => Some(ArmR7),
2313 "r8" => Some(ArmR8),
2314 "r9" => Some(ArmR9),
2315 "r10" => Some(ArmR10),
2316 "r11" | "fp" => Some(ArmR11),
2317 "r12" | "ip" => Some(ArmR12),
2318 "r13" | "sp" => Some(ArmSp),
2319 "r14" | "lr" => Some(ArmLr),
2320 "r15" | "pc" => Some(ArmPc),
2321 "cpsr" => Some(ArmCpsr),
2322 _ => None,
2323 }
2324}
2325
2326fn parse_register_aarch64(name: &str) -> Option<Register> {
2328 use Register::*;
2329 match name {
2330 "x0" => Some(A64X0),
2331 "x1" => Some(A64X1),
2332 "x2" => Some(A64X2),
2333 "x3" => Some(A64X3),
2334 "x4" => Some(A64X4),
2335 "x5" => Some(A64X5),
2336 "x6" => Some(A64X6),
2337 "x7" => Some(A64X7),
2338 "x8" => Some(A64X8),
2339 "x9" => Some(A64X9),
2340 "x10" => Some(A64X10),
2341 "x11" => Some(A64X11),
2342 "x12" => Some(A64X12),
2343 "x13" => Some(A64X13),
2344 "x14" => Some(A64X14),
2345 "x15" => Some(A64X15),
2346 "x16" => Some(A64X16),
2347 "x17" => Some(A64X17),
2348 "x18" => Some(A64X18),
2349 "x19" => Some(A64X19),
2350 "x20" => Some(A64X20),
2351 "x21" => Some(A64X21),
2352 "x22" => Some(A64X22),
2353 "x23" => Some(A64X23),
2354 "x24" => Some(A64X24),
2355 "x25" => Some(A64X25),
2356 "x26" => Some(A64X26),
2357 "x27" => Some(A64X27),
2358 "x28" => Some(A64X28),
2359 "x29" => Some(A64X29),
2360 "x30" => Some(A64X30),
2361 "fp" => Some(A64X29),
2362 "lr" => Some(A64X30),
2363 "sp" => Some(A64Sp),
2364 "xzr" => Some(A64Xzr),
2365 "w0" => Some(A64W0),
2366 "w1" => Some(A64W1),
2367 "w2" => Some(A64W2),
2368 "w3" => Some(A64W3),
2369 "w4" => Some(A64W4),
2370 "w5" => Some(A64W5),
2371 "w6" => Some(A64W6),
2372 "w7" => Some(A64W7),
2373 "w8" => Some(A64W8),
2374 "w9" => Some(A64W9),
2375 "w10" => Some(A64W10),
2376 "w11" => Some(A64W11),
2377 "w12" => Some(A64W12),
2378 "w13" => Some(A64W13),
2379 "w14" => Some(A64W14),
2380 "w15" => Some(A64W15),
2381 "w16" => Some(A64W16),
2382 "w17" => Some(A64W17),
2383 "w18" => Some(A64W18),
2384 "w19" => Some(A64W19),
2385 "w20" => Some(A64W20),
2386 "w21" => Some(A64W21),
2387 "w22" => Some(A64W22),
2388 "w23" => Some(A64W23),
2389 "w24" => Some(A64W24),
2390 "w25" => Some(A64W25),
2391 "w26" => Some(A64W26),
2392 "w27" => Some(A64W27),
2393 "w28" => Some(A64W28),
2394 "w29" => Some(A64W29),
2395 "w30" => Some(A64W30),
2396 "wzr" => Some(A64Wzr),
2397 "wsp" => Some(A64Wsp),
2398 "v0" => Some(A64V0),
2400 "v1" => Some(A64V1),
2401 "v2" => Some(A64V2),
2402 "v3" => Some(A64V3),
2403 "v4" => Some(A64V4),
2404 "v5" => Some(A64V5),
2405 "v6" => Some(A64V6),
2406 "v7" => Some(A64V7),
2407 "v8" => Some(A64V8),
2408 "v9" => Some(A64V9),
2409 "v10" => Some(A64V10),
2410 "v11" => Some(A64V11),
2411 "v12" => Some(A64V12),
2412 "v13" => Some(A64V13),
2413 "v14" => Some(A64V14),
2414 "v15" => Some(A64V15),
2415 "v16" => Some(A64V16),
2416 "v17" => Some(A64V17),
2417 "v18" => Some(A64V18),
2418 "v19" => Some(A64V19),
2419 "v20" => Some(A64V20),
2420 "v21" => Some(A64V21),
2421 "v22" => Some(A64V22),
2422 "v23" => Some(A64V23),
2423 "v24" => Some(A64V24),
2424 "v25" => Some(A64V25),
2425 "v26" => Some(A64V26),
2426 "v27" => Some(A64V27),
2427 "v28" => Some(A64V28),
2428 "v29" => Some(A64V29),
2429 "v30" => Some(A64V30),
2430 "v31" => Some(A64V31),
2431 "q0" => Some(A64Q0),
2433 "q1" => Some(A64Q1),
2434 "q2" => Some(A64Q2),
2435 "q3" => Some(A64Q3),
2436 "q4" => Some(A64Q4),
2437 "q5" => Some(A64Q5),
2438 "q6" => Some(A64Q6),
2439 "q7" => Some(A64Q7),
2440 "q8" => Some(A64Q8),
2441 "q9" => Some(A64Q9),
2442 "q10" => Some(A64Q10),
2443 "q11" => Some(A64Q11),
2444 "q12" => Some(A64Q12),
2445 "q13" => Some(A64Q13),
2446 "q14" => Some(A64Q14),
2447 "q15" => Some(A64Q15),
2448 "q16" => Some(A64Q16),
2449 "q17" => Some(A64Q17),
2450 "q18" => Some(A64Q18),
2451 "q19" => Some(A64Q19),
2452 "q20" => Some(A64Q20),
2453 "q21" => Some(A64Q21),
2454 "q22" => Some(A64Q22),
2455 "q23" => Some(A64Q23),
2456 "q24" => Some(A64Q24),
2457 "q25" => Some(A64Q25),
2458 "q26" => Some(A64Q26),
2459 "q27" => Some(A64Q27),
2460 "q28" => Some(A64Q28),
2461 "q29" => Some(A64Q29),
2462 "q30" => Some(A64Q30),
2463 "q31" => Some(A64Q31),
2464 "d0" => Some(A64D0),
2466 "d1" => Some(A64D1),
2467 "d2" => Some(A64D2),
2468 "d3" => Some(A64D3),
2469 "d4" => Some(A64D4),
2470 "d5" => Some(A64D5),
2471 "d6" => Some(A64D6),
2472 "d7" => Some(A64D7),
2473 "d8" => Some(A64D8),
2474 "d9" => Some(A64D9),
2475 "d10" => Some(A64D10),
2476 "d11" => Some(A64D11),
2477 "d12" => Some(A64D12),
2478 "d13" => Some(A64D13),
2479 "d14" => Some(A64D14),
2480 "d15" => Some(A64D15),
2481 "d16" => Some(A64D16),
2482 "d17" => Some(A64D17),
2483 "d18" => Some(A64D18),
2484 "d19" => Some(A64D19),
2485 "d20" => Some(A64D20),
2486 "d21" => Some(A64D21),
2487 "d22" => Some(A64D22),
2488 "d23" => Some(A64D23),
2489 "d24" => Some(A64D24),
2490 "d25" => Some(A64D25),
2491 "d26" => Some(A64D26),
2492 "d27" => Some(A64D27),
2493 "d28" => Some(A64D28),
2494 "d29" => Some(A64D29),
2495 "d30" => Some(A64D30),
2496 "d31" => Some(A64D31),
2497 "s0" => Some(A64S0),
2499 "s1" => Some(A64S1),
2500 "s2" => Some(A64S2),
2501 "s3" => Some(A64S3),
2502 "s4" => Some(A64S4),
2503 "s5" => Some(A64S5),
2504 "s6" => Some(A64S6),
2505 "s7" => Some(A64S7),
2506 "s8" => Some(A64S8),
2507 "s9" => Some(A64S9),
2508 "s10" => Some(A64S10),
2509 "s11" => Some(A64S11),
2510 "s12" => Some(A64S12),
2511 "s13" => Some(A64S13),
2512 "s14" => Some(A64S14),
2513 "s15" => Some(A64S15),
2514 "s16" => Some(A64S16),
2515 "s17" => Some(A64S17),
2516 "s18" => Some(A64S18),
2517 "s19" => Some(A64S19),
2518 "s20" => Some(A64S20),
2519 "s21" => Some(A64S21),
2520 "s22" => Some(A64S22),
2521 "s23" => Some(A64S23),
2522 "s24" => Some(A64S24),
2523 "s25" => Some(A64S25),
2524 "s26" => Some(A64S26),
2525 "s27" => Some(A64S27),
2526 "s28" => Some(A64S28),
2527 "s29" => Some(A64S29),
2528 "s30" => Some(A64S30),
2529 "s31" => Some(A64S31),
2530 "h0" => Some(A64H0),
2532 "h1" => Some(A64H1),
2533 "h2" => Some(A64H2),
2534 "h3" => Some(A64H3),
2535 "h4" => Some(A64H4),
2536 "h5" => Some(A64H5),
2537 "h6" => Some(A64H6),
2538 "h7" => Some(A64H7),
2539 "h8" => Some(A64H8),
2540 "h9" => Some(A64H9),
2541 "h10" => Some(A64H10),
2542 "h11" => Some(A64H11),
2543 "h12" => Some(A64H12),
2544 "h13" => Some(A64H13),
2545 "h14" => Some(A64H14),
2546 "h15" => Some(A64H15),
2547 "h16" => Some(A64H16),
2548 "h17" => Some(A64H17),
2549 "h18" => Some(A64H18),
2550 "h19" => Some(A64H19),
2551 "h20" => Some(A64H20),
2552 "h21" => Some(A64H21),
2553 "h22" => Some(A64H22),
2554 "h23" => Some(A64H23),
2555 "h24" => Some(A64H24),
2556 "h25" => Some(A64H25),
2557 "h26" => Some(A64H26),
2558 "h27" => Some(A64H27),
2559 "h28" => Some(A64H28),
2560 "h29" => Some(A64H29),
2561 "h30" => Some(A64H30),
2562 "h31" => Some(A64H31),
2563 "b0" => Some(A64B0),
2565 "b1" => Some(A64B1),
2566 "b2" => Some(A64B2),
2567 "b3" => Some(A64B3),
2568 "b4" => Some(A64B4),
2569 "b5" => Some(A64B5),
2570 "b6" => Some(A64B6),
2571 "b7" => Some(A64B7),
2572 "b8" => Some(A64B8),
2573 "b9" => Some(A64B9),
2574 "b10" => Some(A64B10),
2575 "b11" => Some(A64B11),
2576 "b12" => Some(A64B12),
2577 "b13" => Some(A64B13),
2578 "b14" => Some(A64B14),
2579 "b15" => Some(A64B15),
2580 "b16" => Some(A64B16),
2581 "b17" => Some(A64B17),
2582 "b18" => Some(A64B18),
2583 "b19" => Some(A64B19),
2584 "b20" => Some(A64B20),
2585 "b21" => Some(A64B21),
2586 "b22" => Some(A64B22),
2587 "b23" => Some(A64B23),
2588 "b24" => Some(A64B24),
2589 "b25" => Some(A64B25),
2590 "b26" => Some(A64B26),
2591 "b27" => Some(A64B27),
2592 "b28" => Some(A64B28),
2593 "b29" => Some(A64B29),
2594 "b30" => Some(A64B30),
2595 "b31" => Some(A64B31),
2596 "z0" => Some(A64Z0),
2598 "z1" => Some(A64Z1),
2599 "z2" => Some(A64Z2),
2600 "z3" => Some(A64Z3),
2601 "z4" => Some(A64Z4),
2602 "z5" => Some(A64Z5),
2603 "z6" => Some(A64Z6),
2604 "z7" => Some(A64Z7),
2605 "z8" => Some(A64Z8),
2606 "z9" => Some(A64Z9),
2607 "z10" => Some(A64Z10),
2608 "z11" => Some(A64Z11),
2609 "z12" => Some(A64Z12),
2610 "z13" => Some(A64Z13),
2611 "z14" => Some(A64Z14),
2612 "z15" => Some(A64Z15),
2613 "z16" => Some(A64Z16),
2614 "z17" => Some(A64Z17),
2615 "z18" => Some(A64Z18),
2616 "z19" => Some(A64Z19),
2617 "z20" => Some(A64Z20),
2618 "z21" => Some(A64Z21),
2619 "z22" => Some(A64Z22),
2620 "z23" => Some(A64Z23),
2621 "z24" => Some(A64Z24),
2622 "z25" => Some(A64Z25),
2623 "z26" => Some(A64Z26),
2624 "z27" => Some(A64Z27),
2625 "z28" => Some(A64Z28),
2626 "z29" => Some(A64Z29),
2627 "z30" => Some(A64Z30),
2628 "z31" => Some(A64Z31),
2629 "p0" => Some(A64P0),
2631 "p1" => Some(A64P1),
2632 "p2" => Some(A64P2),
2633 "p3" => Some(A64P3),
2634 "p4" => Some(A64P4),
2635 "p5" => Some(A64P5),
2636 "p6" => Some(A64P6),
2637 "p7" => Some(A64P7),
2638 "p8" => Some(A64P8),
2639 "p9" => Some(A64P9),
2640 "p10" => Some(A64P10),
2641 "p11" => Some(A64P11),
2642 "p12" => Some(A64P12),
2643 "p13" => Some(A64P13),
2644 "p14" => Some(A64P14),
2645 "p15" => Some(A64P15),
2646 _ => None,
2647 }
2648}
2649
2650fn parse_register_riscv(name: &str) -> Option<Register> {
2653 use Register::*;
2654 match name {
2655 "x0" => Some(RvX0),
2657 "x1" => Some(RvX1),
2658 "x2" => Some(RvX2),
2659 "x3" => Some(RvX3),
2660 "x4" => Some(RvX4),
2661 "x5" => Some(RvX5),
2662 "x6" => Some(RvX6),
2663 "x7" => Some(RvX7),
2664 "x8" => Some(RvX8),
2665 "x9" => Some(RvX9),
2666 "x10" => Some(RvX10),
2667 "x11" => Some(RvX11),
2668 "x12" => Some(RvX12),
2669 "x13" => Some(RvX13),
2670 "x14" => Some(RvX14),
2671 "x15" => Some(RvX15),
2672 "x16" => Some(RvX16),
2673 "x17" => Some(RvX17),
2674 "x18" => Some(RvX18),
2675 "x19" => Some(RvX19),
2676 "x20" => Some(RvX20),
2677 "x21" => Some(RvX21),
2678 "x22" => Some(RvX22),
2679 "x23" => Some(RvX23),
2680 "x24" => Some(RvX24),
2681 "x25" => Some(RvX25),
2682 "x26" => Some(RvX26),
2683 "x27" => Some(RvX27),
2684 "x28" => Some(RvX28),
2685 "x29" => Some(RvX29),
2686 "x30" => Some(RvX30),
2687 "x31" => Some(RvX31),
2688 "zero" => Some(RvX0),
2690 "ra" => Some(RvX1),
2691 "sp" => Some(RvX2),
2692 "gp" => Some(RvX3),
2693 "tp" => Some(RvX4),
2694 "t0" => Some(RvX5),
2695 "t1" => Some(RvX6),
2696 "t2" => Some(RvX7),
2697 "s0" => Some(RvX8),
2698 "fp" => Some(RvX8), "s1" => Some(RvX9),
2700 "a0" => Some(RvX10),
2701 "a1" => Some(RvX11),
2702 "a2" => Some(RvX12),
2703 "a3" => Some(RvX13),
2704 "a4" => Some(RvX14),
2705 "a5" => Some(RvX15),
2706 "a6" => Some(RvX16),
2707 "a7" => Some(RvX17),
2708 "s2" => Some(RvX18),
2709 "s3" => Some(RvX19),
2710 "s4" => Some(RvX20),
2711 "s5" => Some(RvX21),
2712 "s6" => Some(RvX22),
2713 "s7" => Some(RvX23),
2714 "s8" => Some(RvX24),
2715 "s9" => Some(RvX25),
2716 "s10" => Some(RvX26),
2717 "s11" => Some(RvX27),
2718 "t3" => Some(RvX28),
2719 "t4" => Some(RvX29),
2720 "t5" => Some(RvX30),
2721 "t6" => Some(RvX31),
2722 "f0" => Some(RvF0),
2724 "f1" => Some(RvF1),
2725 "f2" => Some(RvF2),
2726 "f3" => Some(RvF3),
2727 "f4" => Some(RvF4),
2728 "f5" => Some(RvF5),
2729 "f6" => Some(RvF6),
2730 "f7" => Some(RvF7),
2731 "f8" => Some(RvF8),
2732 "f9" => Some(RvF9),
2733 "f10" => Some(RvF10),
2734 "f11" => Some(RvF11),
2735 "f12" => Some(RvF12),
2736 "f13" => Some(RvF13),
2737 "f14" => Some(RvF14),
2738 "f15" => Some(RvF15),
2739 "f16" => Some(RvF16),
2740 "f17" => Some(RvF17),
2741 "f18" => Some(RvF18),
2742 "f19" => Some(RvF19),
2743 "f20" => Some(RvF20),
2744 "f21" => Some(RvF21),
2745 "f22" => Some(RvF22),
2746 "f23" => Some(RvF23),
2747 "f24" => Some(RvF24),
2748 "f25" => Some(RvF25),
2749 "f26" => Some(RvF26),
2750 "f27" => Some(RvF27),
2751 "f28" => Some(RvF28),
2752 "f29" => Some(RvF29),
2753 "f30" => Some(RvF30),
2754 "f31" => Some(RvF31),
2755 "ft0" => Some(RvF0),
2757 "ft1" => Some(RvF1),
2758 "ft2" => Some(RvF2),
2759 "ft3" => Some(RvF3),
2760 "ft4" => Some(RvF4),
2761 "ft5" => Some(RvF5),
2762 "ft6" => Some(RvF6),
2763 "ft7" => Some(RvF7),
2764 "fs0" => Some(RvF8),
2765 "fs1" => Some(RvF9),
2766 "fa0" => Some(RvF10),
2767 "fa1" => Some(RvF11),
2768 "fa2" => Some(RvF12),
2769 "fa3" => Some(RvF13),
2770 "fa4" => Some(RvF14),
2771 "fa5" => Some(RvF15),
2772 "fa6" => Some(RvF16),
2773 "fa7" => Some(RvF17),
2774 "fs2" => Some(RvF18),
2775 "fs3" => Some(RvF19),
2776 "fs4" => Some(RvF20),
2777 "fs5" => Some(RvF21),
2778 "fs6" => Some(RvF22),
2779 "fs7" => Some(RvF23),
2780 "fs8" => Some(RvF24),
2781 "fs9" => Some(RvF25),
2782 "fs10" => Some(RvF26),
2783 "fs11" => Some(RvF27),
2784 "ft8" => Some(RvF28),
2785 "ft9" => Some(RvF29),
2786 "ft10" => Some(RvF30),
2787 "ft11" => Some(RvF31),
2788 "v0" => Some(RvV0),
2790 "v1" => Some(RvV1),
2791 "v2" => Some(RvV2),
2792 "v3" => Some(RvV3),
2793 "v4" => Some(RvV4),
2794 "v5" => Some(RvV5),
2795 "v6" => Some(RvV6),
2796 "v7" => Some(RvV7),
2797 "v8" => Some(RvV8),
2798 "v9" => Some(RvV9),
2799 "v10" => Some(RvV10),
2800 "v11" => Some(RvV11),
2801 "v12" => Some(RvV12),
2802 "v13" => Some(RvV13),
2803 "v14" => Some(RvV14),
2804 "v15" => Some(RvV15),
2805 "v16" => Some(RvV16),
2806 "v17" => Some(RvV17),
2807 "v18" => Some(RvV18),
2808 "v19" => Some(RvV19),
2809 "v20" => Some(RvV20),
2810 "v21" => Some(RvV21),
2811 "v22" => Some(RvV22),
2812 "v23" => Some(RvV23),
2813 "v24" => Some(RvV24),
2814 "v25" => Some(RvV25),
2815 "v26" => Some(RvV26),
2816 "v27" => Some(RvV27),
2817 "v28" => Some(RvV28),
2818 "v29" => Some(RvV29),
2819 "v30" => Some(RvV30),
2820 "v31" => Some(RvV31),
2821 _ => None,
2822 }
2823}
2824
2825pub fn parse_str(source: &str) -> Result<Vec<Statement>, AsmError> {
2827 let tokens = crate::lexer::tokenize(source)?;
2828 parse(&tokens)
2829}
2830
2831fn strip_att_suffix(mnemonic: &str) -> Option<(Mnemonic, OperandSize)> {
2837 let att_translations: &[(&str, &str, OperandSize)] = &[
2840 ("movzbl", "movzx", OperandSize::Dword),
2842 ("movzbw", "movzx", OperandSize::Word),
2843 ("movzbq", "movzx", OperandSize::Qword),
2844 ("movzwl", "movzx", OperandSize::Dword),
2845 ("movzwq", "movzx", OperandSize::Qword),
2846 ("movsbl", "movsx", OperandSize::Dword),
2848 ("movsbw", "movsx", OperandSize::Word),
2849 ("movsbq", "movsx", OperandSize::Qword),
2850 ("movswl", "movsx", OperandSize::Dword),
2851 ("movswq", "movsx", OperandSize::Qword),
2852 ("movslq", "movsxd", OperandSize::Qword),
2853 ("cbtw", "cbw", OperandSize::Word),
2855 ("cwtl", "cwde", OperandSize::Dword),
2856 ("cwtd", "cwd", OperandSize::Word),
2857 ("cltd", "cdq", OperandSize::Dword),
2858 ("cltq", "cdqe", OperandSize::Qword),
2859 ("cqto", "cqo", OperandSize::Qword),
2860 ];
2861
2862 for &(att, intel, size) in att_translations {
2863 if mnemonic == att {
2864 return Some((Mnemonic::from(intel), size));
2865 }
2866 }
2867
2868 if mnemonic.len() < 2 {
2869 return None;
2870 }
2871
2872 let no_strip = [
2875 "call",
2876 "jmp",
2877 "ret",
2878 "nop",
2879 "hlt",
2880 "int",
2881 "syscall",
2882 "sysenter",
2883 "sysexit",
2884 "cpuid",
2885 "rdtsc",
2886 "rdtscp",
2887 "ud2",
2888 "leave",
2889 "enter",
2890 "pushf",
2891 "popf",
2892 "pushfq",
2893 "popfq",
2894 "lahf",
2895 "sahf",
2896 "clc",
2897 "stc",
2898 "cmc",
2899 "cld",
2900 "std",
2901 "cli",
2902 "sti",
2903 "rep",
2904 "repe",
2905 "repne",
2906 "repz",
2907 "repnz",
2908 "lock",
2909 "pause",
2910 "mfence",
2911 "lfence",
2912 "sfence",
2913 "endbr64",
2914 "endbr32",
2915 "iretq",
2916 "cdq",
2917 "cqo",
2918 "cbw",
2919 "cwde",
2920 "cdqe",
2921 "cwd",
2922 "xlat",
2923 "xlatb",
2924 "swapgs",
2925 "wrmsr",
2926 "rdmsr",
2927 "invd",
2928 "wbinvd",
2929 "clts",
2930 "monitor",
2931 "mwait",
2932 "rdrand",
2933 "rdseed",
2934 "xtest",
2935 "xend",
2936 "vzeroall",
2937 "vzeroupper",
2938 "int3",
2939 "setal",
2941 "setbl",
2942 "setcl",
2943 "setgl",
2944 "setol",
2945 "setnl",
2946 "setpl",
2947 "jal",
2949 "jbl",
2950 "jcl",
2951 "jgl",
2952 "jol",
2953 "jnl",
2954 "jpl",
2955 "movsb",
2957 "movsw",
2958 "movsd",
2959 "movsq",
2960 "stosb",
2961 "stosw",
2962 "stosd",
2963 "stosq",
2964 "lodsb",
2965 "lodsw",
2966 "lodsd",
2967 "lodsq",
2968 "scasb",
2969 "scasw",
2970 "scasd",
2971 "scasq",
2972 "cmpsb",
2973 "cmpsw",
2974 "cmpsd",
2975 "cmpsq",
2976 "insb",
2977 "insw",
2978 "insd",
2979 "outsb",
2980 "outsw",
2981 "outsd",
2982 "inb",
2984 "inw",
2985 "inl",
2986 "outb",
2987 "outw",
2988 "outl",
2989 "loop",
2991 "loope",
2992 "loopne",
2993 "loopz",
2994 "loopnz",
2995 "jecxz",
2996 "jrcxz",
2997 "cmpxchg8b",
2999 "cmpxchg16b",
3000 "bswap",
3002 ];
3003
3004 let suffix = mnemonic.as_bytes()[mnemonic.len() - 1];
3005 let size = match suffix {
3006 b'b' => OperandSize::Byte,
3007 b'w' => OperandSize::Word,
3008 b'l' => OperandSize::Dword,
3009 b'q' => OperandSize::Qword,
3010 _ => return None,
3011 };
3012
3013 if no_strip.contains(&mnemonic) {
3014 return None;
3015 }
3016
3017 let base = &mnemonic[..mnemonic.len() - 1];
3018
3019 if base.is_empty() {
3021 return None;
3022 }
3023
3024 let known_bases = [
3026 "mov", "add", "sub", "adc", "sbb", "and", "or", "xor", "cmp", "test", "push", "pop", "inc",
3027 "dec", "neg", "not", "mul", "imul", "div", "idiv", "lea", "xchg", "cmpxchg", "xadd",
3028 "movzx", "movsx", "movsxd", "shl", "shr", "sar", "rol", "ror", "rcl", "rcr", "bt", "bts",
3029 "btr", "btc", "bsf", "bsr", "set", "cmov", "in", "out", "movabs",
3031 ];
3032
3033 if known_bases.contains(&base) {
3035 return Some((Mnemonic::from(base), size));
3036 }
3037
3038 if base.starts_with("cmov") || base.starts_with("set") || base.starts_with('j') {
3042 return Some((Mnemonic::from(base), size));
3043 }
3044
3045 Some((Mnemonic::from(base), size))
3047}
3048
3049#[cfg(test)]
3050mod tests {
3051 use super::*;
3052
3053 fn parse_one(src: &str) -> Statement {
3054 let stmts = parse_str(src).unwrap();
3055 assert_eq!(
3056 stmts.len(),
3057 1,
3058 "expected 1 statement, got {}: {:?}",
3059 stmts.len(),
3060 stmts
3061 );
3062 stmts.into_iter().next().unwrap()
3063 }
3064
3065 fn parse_instr(src: &str) -> Instruction {
3066 match parse_one(src) {
3067 Statement::Instruction(i) => i,
3068 s => panic!("expected instruction, got {:?}", s),
3069 }
3070 }
3071
3072 #[test]
3075 fn parse_nop() {
3076 let i = parse_instr("nop");
3077 assert_eq!(i.mnemonic, "nop");
3078 assert!(i.operands.is_empty());
3079 }
3080
3081 #[test]
3082 fn parse_ret() {
3083 let i = parse_instr("ret");
3084 assert_eq!(i.mnemonic, "ret");
3085 }
3086
3087 #[test]
3088 fn parse_syscall() {
3089 let i = parse_instr("syscall");
3090 assert_eq!(i.mnemonic, "syscall");
3091 }
3092
3093 #[test]
3096 fn parse_mov_reg_reg() {
3097 let i = parse_instr("mov rax, rbx");
3098 assert_eq!(i.mnemonic, "mov");
3099 assert_eq!(i.operands.len(), 2);
3100 assert_eq!(i.operands[0], Operand::Register(Register::Rax));
3101 assert_eq!(i.operands[1], Operand::Register(Register::Rbx));
3102 }
3103
3104 #[test]
3105 fn parse_add_r32() {
3106 let i = parse_instr("add eax, ecx");
3107 assert_eq!(i.mnemonic, "add");
3108 assert_eq!(i.operands[0], Operand::Register(Register::Eax));
3109 assert_eq!(i.operands[1], Operand::Register(Register::Ecx));
3110 }
3111
3112 #[test]
3113 fn parse_xor_r8() {
3114 let i = parse_instr("xor al, bl");
3115 assert_eq!(i.operands[0], Operand::Register(Register::Al));
3116 assert_eq!(i.operands[1], Operand::Register(Register::Bl));
3117 }
3118
3119 #[test]
3122 fn parse_mov_reg_imm() {
3123 let i = parse_instr("mov rax, 42");
3124 assert_eq!(i.operands[0], Operand::Register(Register::Rax));
3125 assert_eq!(i.operands[1], Operand::Immediate(42));
3126 }
3127
3128 #[test]
3129 fn parse_mov_reg_hex() {
3130 let i = parse_instr("mov rdi, 0xDEAD");
3131 assert_eq!(i.operands[1], Operand::Immediate(0xDEAD));
3132 }
3133
3134 #[test]
3135 fn parse_add_imm_negative() {
3136 let i = parse_instr("add rsp, -8");
3137 assert_eq!(i.operands[1], Operand::Immediate(-8));
3138 }
3139
3140 #[test]
3141 fn parse_char_immediate() {
3142 let i = parse_instr("mov al, 'A'");
3143 assert_eq!(i.operands[1], Operand::Immediate(65));
3144 }
3145
3146 #[test]
3149 fn parse_mem_base() {
3150 let i = parse_instr("mov rax, [rbx]");
3151 assert_eq!(i.operands[0], Operand::Register(Register::Rax));
3152 match &i.operands[1] {
3153 Operand::Memory(m) => {
3154 assert_eq!(m.base, Some(Register::Rbx));
3155 assert_eq!(m.index, None);
3156 assert_eq!(m.disp, 0);
3157 }
3158 _ => panic!("expected memory operand"),
3159 }
3160 }
3161
3162 #[test]
3163 fn parse_mem_base_disp() {
3164 let i = parse_instr("mov rax, [rbp + 8]");
3165 match &i.operands[1] {
3166 Operand::Memory(m) => {
3167 assert_eq!(m.base, Some(Register::Rbp));
3168 assert_eq!(m.disp, 8);
3169 }
3170 _ => panic!("expected memory operand"),
3171 }
3172 }
3173
3174 #[test]
3175 fn parse_mem_base_neg_disp() {
3176 let i = parse_instr("mov rax, [rbp - 0x10]");
3177 match &i.operands[1] {
3178 Operand::Memory(m) => {
3179 assert_eq!(m.base, Some(Register::Rbp));
3180 assert_eq!(m.disp, -16);
3181 }
3182 _ => panic!("expected memory operand"),
3183 }
3184 }
3185
3186 #[test]
3187 fn parse_mem_base_index() {
3188 let i = parse_instr("mov rax, [rbx + rcx]");
3189 match &i.operands[1] {
3190 Operand::Memory(m) => {
3191 assert_eq!(m.base, Some(Register::Rbx));
3192 assert_eq!(m.index, Some(Register::Rcx));
3193 assert_eq!(m.scale, 1);
3194 }
3195 _ => panic!("expected memory operand"),
3196 }
3197 }
3198
3199 #[test]
3200 fn parse_mem_base_index_scale() {
3201 let i = parse_instr("lea rax, [rbx + rcx*8]");
3202 match &i.operands[1] {
3203 Operand::Memory(m) => {
3204 assert_eq!(m.base, Some(Register::Rbx));
3205 assert_eq!(m.index, Some(Register::Rcx));
3206 assert_eq!(m.scale, 8);
3207 }
3208 _ => panic!("expected memory operand"),
3209 }
3210 }
3211
3212 #[test]
3213 fn parse_mem_full() {
3214 let i = parse_instr("mov rax, [rbx + rcx*4 + 16]");
3215 match &i.operands[1] {
3216 Operand::Memory(m) => {
3217 assert_eq!(m.base, Some(Register::Rbx));
3218 assert_eq!(m.index, Some(Register::Rcx));
3219 assert_eq!(m.scale, 4);
3220 assert_eq!(m.disp, 16);
3221 }
3222 _ => panic!("expected memory operand"),
3223 }
3224 }
3225
3226 #[test]
3227 fn parse_mem_disp_only() {
3228 let i = parse_instr("mov rax, [0x1000]");
3229 match &i.operands[1] {
3230 Operand::Memory(m) => {
3231 assert_eq!(m.base, None);
3232 assert_eq!(m.disp, 0x1000);
3233 }
3234 _ => panic!("expected memory operand"),
3235 }
3236 }
3237
3238 #[test]
3241 fn parse_byte_ptr() {
3242 let i = parse_instr("mov byte ptr [rax], 0");
3243 assert_eq!(i.size_hint, Some(OperandSize::Byte));
3244 match &i.operands[0] {
3245 Operand::Memory(m) => assert_eq!(m.base, Some(Register::Rax)),
3246 _ => panic!("expected memory operand"),
3247 }
3248 }
3249
3250 #[test]
3251 fn parse_qword_no_ptr() {
3252 let i = parse_instr("mov qword [rax], 0");
3253 assert_eq!(i.size_hint, Some(OperandSize::Qword));
3254 }
3255
3256 #[test]
3257 fn parse_dword_ptr() {
3258 let i = parse_instr("add dword ptr [rbp - 4], 1");
3259 assert_eq!(i.size_hint, Some(OperandSize::Dword));
3260 }
3261
3262 #[test]
3265 fn parse_label_def() {
3266 let stmt = parse_one("start:");
3267 match stmt {
3268 Statement::Label(name, _) => assert_eq!(name, "start"),
3269 _ => panic!("expected label"),
3270 }
3271 }
3272
3273 #[test]
3274 fn parse_label_ref() {
3275 let i = parse_instr("jmp loop");
3276 assert_eq!(i.operands[0], Operand::Label(String::from("loop")));
3277 }
3278
3279 #[test]
3280 fn parse_call_label() {
3281 let i = parse_instr("call printf");
3282 assert_eq!(i.operands[0], Operand::Label(String::from("printf")));
3283 }
3284
3285 #[test]
3286 fn parse_label_with_offset() {
3287 let i = parse_instr("lea rax, data + 4");
3288 match &i.operands[1] {
3289 Operand::Expression(Expr::Add(l, r)) => {
3290 assert_eq!(**l, Expr::Label(String::from("data")));
3291 assert_eq!(**r, Expr::Num(4));
3292 }
3293 _ => panic!("expected expression operand"),
3294 }
3295 }
3296
3297 #[test]
3300 fn parse_lock_prefix() {
3301 let i = parse_instr("lock add [rax], 1");
3302 assert_eq!(i.prefixes, vec![Prefix::Lock]);
3303 assert_eq!(i.mnemonic, "add");
3304 }
3305
3306 #[test]
3307 fn parse_rep_prefix() {
3308 let i = parse_instr("rep movsb");
3309 assert_eq!(i.prefixes, vec![Prefix::Rep]);
3310 assert_eq!(i.mnemonic, "movsb");
3311 }
3312
3313 #[test]
3316 fn parse_byte_directive() {
3317 let stmt = parse_one(".byte 0x90, 0xCC");
3318 match stmt {
3319 Statement::Data(d) => {
3320 assert_eq!(d.size, DataSize::Byte);
3321 assert_eq!(
3322 d.values,
3323 vec![DataValue::Integer(0x90), DataValue::Integer(0xCC)]
3324 );
3325 }
3326 _ => panic!("expected data"),
3327 }
3328 }
3329
3330 #[test]
3331 fn parse_word_directive() {
3332 let stmt = parse_one(".word 0x1234");
3333 match stmt {
3334 Statement::Data(d) => {
3335 assert_eq!(d.size, DataSize::Word);
3336 assert_eq!(d.values, vec![DataValue::Integer(0x1234)]);
3337 }
3338 _ => panic!("expected data"),
3339 }
3340 }
3341
3342 #[test]
3343 fn parse_ascii_directive() {
3344 let stmt = parse_one(".ascii \"hello\"");
3345 match stmt {
3346 Statement::Data(d) => {
3347 assert_eq!(d.size, DataSize::Byte);
3348 assert_eq!(d.values, vec![DataValue::Bytes(b"hello".to_vec())]);
3349 }
3350 _ => panic!("expected data"),
3351 }
3352 }
3353
3354 #[test]
3355 fn parse_asciz_null_terminates() {
3356 let stmt = parse_one(".asciz \"ok\"");
3357 match stmt {
3358 Statement::Data(d) => {
3359 assert_eq!(d.values, vec![DataValue::Bytes(b"ok\0".to_vec())]);
3360 }
3361 _ => panic!("expected data"),
3362 }
3363 }
3364
3365 #[test]
3366 fn parse_equ_directive() {
3367 let stmt = parse_one(".equ SYS_WRITE, 1");
3368 match stmt {
3369 Statement::Const(c) => {
3370 assert_eq!(c.name, "SYS_WRITE");
3371 assert_eq!(c.value, 1);
3372 }
3373 _ => panic!("expected const"),
3374 }
3375 }
3376
3377 #[test]
3378 fn parse_align_directive() {
3379 let stmt = parse_one(".align 16");
3380 match stmt {
3381 Statement::Align(a) => {
3382 assert_eq!(a.alignment, 16);
3383 assert_eq!(a.fill, None);
3384 }
3385 _ => panic!("expected align"),
3386 }
3387 }
3388
3389 #[test]
3390 fn parse_p2align_directive() {
3391 let stmt = parse_one(".p2align 4");
3392 match stmt {
3393 Statement::Align(a) => {
3394 assert_eq!(a.alignment, 16); }
3396 _ => panic!("expected align"),
3397 }
3398 }
3399
3400 #[test]
3401 fn parse_fill_directive() {
3402 let stmt = parse_one(".fill 10, 1, 0x90");
3403 match stmt {
3404 Statement::Fill(f) => {
3405 assert_eq!(f.count, 10);
3406 assert_eq!(f.size, 1);
3407 assert_eq!(f.value, 0x90);
3408 }
3409 _ => panic!("expected fill"),
3410 }
3411 }
3412
3413 #[test]
3414 fn parse_space_directive() {
3415 let stmt = parse_one(".space 64");
3416 match stmt {
3417 Statement::Space(s) => {
3418 assert_eq!(s.size, 64);
3419 assert_eq!(s.fill, 0);
3420 }
3421 _ => panic!("expected space"),
3422 }
3423 }
3424
3425 #[test]
3426 fn parse_org_directive() {
3427 let stmt = parse_one(".org 0x1000");
3428 match stmt {
3429 Statement::Org(o) => {
3430 assert_eq!(o.offset, 0x1000);
3431 assert_eq!(o.fill, 0x00);
3432 }
3433 _ => panic!("expected org"),
3434 }
3435 }
3436
3437 #[test]
3438 fn parse_org_with_fill() {
3439 let stmt = parse_one(".org 0x100, 0xFF");
3440 match stmt {
3441 Statement::Org(o) => {
3442 assert_eq!(o.offset, 0x100);
3443 assert_eq!(o.fill, 0xFF);
3444 }
3445 _ => panic!("expected org"),
3446 }
3447 }
3448
3449 #[test]
3452 fn parse_multi_line() {
3453 let stmts = parse_str("nop\nret").unwrap();
3454 assert_eq!(stmts.len(), 2);
3455 match (&stmts[0], &stmts[1]) {
3456 (Statement::Instruction(i1), Statement::Instruction(i2)) => {
3457 assert_eq!(i1.mnemonic, "nop");
3458 assert_eq!(i2.mnemonic, "ret");
3459 }
3460 _ => panic!("expected two instructions"),
3461 }
3462 }
3463
3464 #[test]
3465 fn parse_label_and_instruction() {
3466 let stmts = parse_str("start:\n mov rax, 1").unwrap();
3467 assert_eq!(stmts.len(), 2);
3468 assert!(matches!(&stmts[0], Statement::Label(name, _) if name == "start"));
3469 assert!(matches!(&stmts[1], Statement::Instruction(_)));
3470 }
3471
3472 #[test]
3473 fn parse_semicolon_separated() {
3474 let stmts = parse_str("nop; ret").unwrap();
3475 assert_eq!(stmts.len(), 2);
3476 }
3477
3478 #[test]
3481 fn case_insensitive_mnemonic() {
3482 let i = parse_instr("MOV RAX, RBX");
3483 assert_eq!(i.mnemonic, "mov");
3484 assert_eq!(i.operands[0], Operand::Register(Register::Rax));
3485 }
3486
3487 #[test]
3488 fn case_insensitive_register() {
3489 let i = parse_instr("xor EAX, eax");
3490 assert_eq!(i.operands[0], Operand::Register(Register::Eax));
3491 assert_eq!(i.operands[1], Operand::Register(Register::Eax));
3492 }
3493
3494 #[test]
3497 fn parse_extended_reg() {
3498 let i = parse_instr("mov r8, r15");
3499 assert_eq!(i.operands[0], Operand::Register(Register::R8));
3500 assert_eq!(i.operands[1], Operand::Register(Register::R15));
3501 }
3502
3503 #[test]
3504 fn parse_extended_reg_dword() {
3505 let i = parse_instr("mov r8d, r15d");
3506 assert_eq!(i.operands[0], Operand::Register(Register::R8d));
3507 assert_eq!(i.operands[1], Operand::Register(Register::R15d));
3508 }
3509
3510 #[test]
3513 fn parse_push_pop() {
3514 let i = parse_instr("push rbp");
3515 assert_eq!(i.mnemonic, "push");
3516 assert_eq!(i.operands[0], Operand::Register(Register::Rbp));
3517 }
3518
3519 #[test]
3520 fn parse_lea() {
3521 let i = parse_instr("lea rdi, [rip + 0x10]");
3522 assert_eq!(i.mnemonic, "lea");
3523 match &i.operands[1] {
3524 Operand::Memory(m) => {
3525 assert_eq!(m.base, Some(Register::Rip));
3526 assert_eq!(m.disp, 0x10);
3527 }
3528 _ => panic!("expected memory"),
3529 }
3530 }
3531
3532 #[test]
3533 fn parse_three_operand_imul() {
3534 let i = parse_instr("imul rax, rbx, 10");
3535 assert_eq!(i.operands.len(), 3);
3536 assert_eq!(i.operands[0], Operand::Register(Register::Rax));
3537 assert_eq!(i.operands[1], Operand::Register(Register::Rbx));
3538 assert_eq!(i.operands[2], Operand::Immediate(10));
3539 }
3540
3541 #[test]
3542 fn global_directive_ignored() {
3543 let stmts = parse_str(".global main\nmov rax, 1").unwrap();
3544 assert_eq!(stmts.len(), 1);
3546 }
3547
3548 #[test]
3549 fn section_directive_ignored() {
3550 let stmts = parse_str(".section .text\nnop").unwrap();
3551 assert_eq!(stmts.len(), 1);
3552 }
3553
3554 #[test]
3555 fn empty_input() {
3556 let stmts = parse_str("").unwrap();
3557 assert!(stmts.is_empty());
3558 }
3559
3560 #[test]
3561 fn only_labels() {
3562 let stmts = parse_str("start:\nend:").unwrap();
3563 assert_eq!(stmts.len(), 2);
3564 assert!(matches!(&stmts[0], Statement::Label(n, _) if n == "start"));
3565 assert!(matches!(&stmts[1], Statement::Label(n, _) if n == "end"));
3566 }
3567
3568 #[test]
3569 fn mem_with_label() {
3570 let i = parse_instr("mov rax, [msg]");
3571 match &i.operands[1] {
3572 Operand::Memory(m) => {
3573 assert_eq!(m.base, None);
3574 assert_eq!(m.disp_label, Some(String::from("msg")));
3575 }
3576 _ => panic!("expected memory operand with label"),
3577 }
3578 }
3579
3580 #[test]
3581 fn xmm_registers() {
3582 let i = parse_instr("movaps xmm0, xmm1");
3583 assert_eq!(i.operands[0], Operand::Register(Register::Xmm0));
3584 assert_eq!(i.operands[1], Operand::Register(Register::Xmm1));
3585 }
3586
3587 #[test]
3588 fn segment_override_mem() {
3589 let i = parse_instr("mov rax, fs:[0x28]");
3590 match &i.operands[1] {
3591 Operand::Memory(m) => {
3592 assert_eq!(m.segment, Some(Register::Fs));
3593 assert_eq!(m.disp, 0x28);
3594 }
3595 _ => panic!("expected segment memory operand"),
3596 }
3597 }
3598
3599 #[test]
3602 fn parse_name_equals_constant() {
3603 let stmt = parse_one("EXIT = 60");
3604 match stmt {
3605 Statement::Const(c) => {
3606 assert_eq!(c.name, "EXIT");
3607 assert_eq!(c.value, 60);
3608 }
3609 _ => panic!("expected const, got {:?}", stmt),
3610 }
3611 }
3612
3613 #[test]
3614 fn parse_name_equals_hex() {
3615 let stmt = parse_one("MAGIC = 0xDEAD");
3616 match stmt {
3617 Statement::Const(c) => {
3618 assert_eq!(c.name, "MAGIC");
3619 assert_eq!(c.value, 0xDEAD);
3620 }
3621 _ => panic!("expected const"),
3622 }
3623 }
3624
3625 #[test]
3626 fn parse_name_equals_negative() {
3627 let stmt = parse_one("NEG = -1");
3628 match stmt {
3629 Statement::Const(c) => {
3630 assert_eq!(c.name, "NEG");
3631 assert_eq!(c.value, -1);
3632 }
3633 _ => panic!("expected const"),
3634 }
3635 }
3636
3637 #[test]
3638 fn parse_set_directive() {
3639 let stmt = parse_one(".set COUNT, 42");
3640 match stmt {
3641 Statement::Const(c) => {
3642 assert_eq!(c.name, "COUNT");
3643 assert_eq!(c.value, 42);
3644 }
3645 _ => panic!("expected const"),
3646 }
3647 }
3648
3649 #[test]
3650 fn name_equals_used_in_program() {
3651 let stmts = parse_str("EXIT = 60\nmov eax, EXIT").unwrap();
3652 assert_eq!(stmts.len(), 2);
3653 assert!(matches!(&stmts[0], Statement::Const(_)));
3654 assert!(matches!(&stmts[1], Statement::Instruction(_)));
3655 }
3656
3657 #[test]
3658 fn parse_const_expr_with_identifier() {
3659 let stmts = parse_str("SIZE = 10\n.fill SIZE, 1, 0").unwrap();
3661 assert_eq!(stmts.len(), 2);
3662 match &stmts[1] {
3663 Statement::Fill(f) => assert_eq!(f.count, 10),
3664 _ => panic!("expected Fill"),
3665 }
3666 }
3667
3668 #[test]
3669 fn parse_const_chain() {
3670 let stmts = parse_str("A = 5\nB = A + 3\n.space B, 0").unwrap();
3672 assert_eq!(stmts.len(), 3);
3673 match &stmts[2] {
3674 Statement::Space(s) => assert_eq!(s.size, 8),
3675 _ => panic!("expected Space"),
3676 }
3677 }
3678
3679 #[test]
3680 fn parse_equ_identifier_in_const_expr() {
3681 let stmts = parse_str(".equ BASE, 100\n.equ TOTAL, BASE + 50").unwrap();
3682 match &stmts[1] {
3683 Statement::Const(c) => assert_eq!(c.value, 150),
3684 _ => panic!("expected Const"),
3685 }
3686 }
3687
3688 #[test]
3689 fn parse_label_plus_identifier_expression() {
3690 let stmts = parse_str("OFF = 8\nmov rax, data + OFF").unwrap();
3693 match &stmts[1] {
3694 Statement::Instruction(i) => {
3695 match &i.operands[1] {
3696 Operand::Expression(Expr::Add(l, r)) => {
3698 assert_eq!(**l, Expr::Label(String::from("data")));
3699 assert_eq!(**r, Expr::Num(8));
3700 }
3701 other => panic!("expected Expression, got {:?}", other),
3702 }
3703 }
3704 _ => panic!("expected Instruction"),
3705 }
3706 }
3707
3708 #[test]
3709 fn parse_all_constants_resolve_to_immediate() {
3710 let stmts = parse_str("BASE = 100\nOFF = 8\nmov eax, BASE + OFF").unwrap();
3712 match &stmts[2] {
3713 Statement::Instruction(i) => {
3714 assert_eq!(i.operands[1], Operand::Immediate(108));
3715 }
3716 _ => panic!("expected Instruction"),
3717 }
3718 }
3719
3720 #[test]
3721 fn parse_align_with_constant() {
3722 let stmts = parse_str("ALIGN_VAL = 8\n.align ALIGN_VAL").unwrap();
3723 match &stmts[1] {
3724 Statement::Align(a) => assert_eq!(a.alignment, 8),
3725 _ => panic!("expected Align"),
3726 }
3727 }
3728
3729 #[test]
3730 fn parse_label_minus_offset() {
3731 let i = parse_instr("jmp target - 8");
3732 match &i.operands[0] {
3733 Operand::Expression(Expr::Sub(l, r)) => {
3734 assert_eq!(**l, Expr::Label(String::from("target")));
3735 assert_eq!(**r, Expr::Num(8));
3736 }
3737 _ => panic!("expected Sub expression"),
3738 }
3739 }
3740
3741 #[test]
3742 fn parse_const_negation_precedence() {
3743 let stmts = parse_str("A = 10\nB = 3\nX = -A + B\nmov eax, X").unwrap();
3745 match &stmts[3] {
3746 Statement::Instruction(i) => {
3747 assert_eq!(
3748 i.operands[1],
3749 Operand::Immediate(-7),
3750 "-10 + 3 should be -7, not -13"
3751 );
3752 }
3753 _ => panic!("expected Instruction"),
3754 }
3755 }
3756
3757 #[test]
3758 fn parse_const_negation_only() {
3759 let stmts = parse_str("A = 10\nX = -A\nmov eax, X").unwrap();
3761 match &stmts[2] {
3762 Statement::Instruction(i) => {
3763 assert_eq!(i.operands[1], Operand::Immediate(-10));
3764 }
3765 _ => panic!("expected Instruction"),
3766 }
3767 }
3768
3769 #[test]
3770 fn parse_const_negation_sub_chain() {
3771 let stmts = parse_str("A = 10\nB = 3\nX = -A - B\nmov eax, X").unwrap();
3773 match &stmts[3] {
3774 Statement::Instruction(i) => {
3775 assert_eq!(
3776 i.operands[1],
3777 Operand::Immediate(-13),
3778 "-10 - 3 should be -13"
3779 );
3780 }
3781 _ => panic!("expected Instruction"),
3782 }
3783 }
3784
3785 #[test]
3786 fn parse_align_rejects_non_power_of_2() {
3787 let result = crate::parser::parse_str(".align 3");
3788 assert!(result.is_err(), ".align 3 should be rejected");
3789 let err = result.unwrap_err();
3790 let msg = alloc::format!("{err:?}");
3791 assert!(
3792 msg.contains("power of 2"),
3793 "error should mention power of 2, got: {msg}"
3794 );
3795 }
3796
3797 #[test]
3798 fn parse_align_accepts_power_of_2() {
3799 for val in &["1", "2", "4", "8", "16", "32", "64", "4096"] {
3801 let src = alloc::format!(".align {val}");
3802 let result = crate::parser::parse_str(&src);
3803 assert!(
3804 result.is_ok(),
3805 ".align {val} should be accepted, got: {result:?}"
3806 );
3807 }
3808 }
3809
3810 #[test]
3813 fn parse_rsp_as_index_rejects() {
3814 let result = crate::parser::parse_str("mov rax, [rbx + rsp*2]");
3816 assert!(result.is_err(), "RSP as SIB index should be rejected");
3817 }
3818
3819 #[test]
3820 fn parse_esp_as_index_rejects() {
3821 let result = crate::parser::parse_str("mov eax, [ebx + esp*1]");
3823 assert!(result.is_err(), "ESP as SIB index should be rejected");
3824 }
3825
3826 #[test]
3827 fn parse_r12_as_index_accepts() {
3828 let result = crate::parser::parse_str("mov rax, [rbx + r12*2]");
3830 assert!(
3831 result.is_ok(),
3832 "R12 as SIB index should be accepted, got: {result:?}"
3833 );
3834 }
3835
3836 fn parse_att(src: &str) -> Vec<Statement> {
3839 let tokens = crate::lexer::tokenize(src).unwrap();
3840 parse_with_syntax(&tokens, Arch::X86_64, Syntax::Att).unwrap()
3841 }
3842
3843 fn parse_att_instr(src: &str) -> Instruction {
3844 let stmts = parse_att(src);
3845 assert_eq!(stmts.len(), 1, "expected 1 statement, got {:?}", stmts);
3846 match stmts.into_iter().next().unwrap() {
3847 Statement::Instruction(i) => i,
3848 s => panic!("expected instruction, got {s:?}"),
3849 }
3850 }
3851
3852 #[test]
3853 fn att_register_operand() {
3854 let i = parse_att_instr("nop");
3855 assert_eq!(i.mnemonic, "nop");
3856 assert!(i.operands.is_empty());
3857 }
3858
3859 #[test]
3860 fn att_mov_imm_to_reg() {
3861 let i = parse_att_instr("movq $42, %rax");
3862 assert_eq!(i.mnemonic, "mov");
3863 assert_eq!(i.size_hint, Some(OperandSize::Qword));
3864 assert_eq!(i.operands.len(), 2);
3866 assert_eq!(i.operands[0], Operand::Register(Register::Rax));
3867 assert_eq!(i.operands[1], Operand::Immediate(42));
3868 }
3869
3870 #[test]
3871 fn att_mov_reg_to_reg() {
3872 let i = parse_att_instr("movl %eax, %ecx");
3873 assert_eq!(i.mnemonic, "mov");
3874 assert_eq!(i.size_hint, Some(OperandSize::Dword));
3875 assert_eq!(i.operands[0], Operand::Register(Register::Ecx));
3876 assert_eq!(i.operands[1], Operand::Register(Register::Eax));
3877 }
3878
3879 #[test]
3880 fn att_add_imm_to_reg() {
3881 let i = parse_att_instr("addl $0x10, %eax");
3882 assert_eq!(i.mnemonic, "add");
3883 assert_eq!(i.size_hint, Some(OperandSize::Dword));
3884 assert_eq!(i.operands[0], Operand::Register(Register::Eax));
3885 assert_eq!(i.operands[1], Operand::Immediate(0x10));
3886 }
3887
3888 #[test]
3889 fn att_negative_immediate() {
3890 let i = parse_att_instr("addq $-1, %rax");
3891 assert_eq!(i.mnemonic, "add");
3892 assert_eq!(i.operands[1], Operand::Immediate(-1));
3893 }
3894
3895 #[test]
3896 fn att_byte_suffix() {
3897 let i = parse_att_instr("movb $0x41, %al");
3898 assert_eq!(i.mnemonic, "mov");
3899 assert_eq!(i.size_hint, Some(OperandSize::Byte));
3900 assert_eq!(i.operands[0], Operand::Register(Register::Al));
3901 }
3902
3903 #[test]
3904 fn att_word_suffix() {
3905 let i = parse_att_instr("movw $0x1234, %ax");
3906 assert_eq!(i.mnemonic, "mov");
3907 assert_eq!(i.size_hint, Some(OperandSize::Word));
3908 assert_eq!(i.operands[0], Operand::Register(Register::Ax));
3909 }
3910
3911 #[test]
3912 fn att_memory_base_only() {
3913 let i = parse_att_instr("movq (%rax), %rbx");
3914 assert_eq!(i.mnemonic, "mov");
3915 assert_eq!(i.operands[0], Operand::Register(Register::Rbx));
3917 if let Operand::Memory(m) = &i.operands[1] {
3918 assert_eq!(m.base, Some(Register::Rax));
3919 assert_eq!(m.disp, 0);
3920 assert!(m.index.is_none());
3921 } else {
3922 panic!("expected memory operand");
3923 }
3924 }
3925
3926 #[test]
3927 fn att_memory_disp_base() {
3928 let i = parse_att_instr("movl 8(%rsp), %eax");
3929 assert_eq!(i.mnemonic, "mov");
3930 if let Operand::Memory(m) = &i.operands[1] {
3931 assert_eq!(m.base, Some(Register::Rsp));
3932 assert_eq!(m.disp, 8);
3933 } else {
3934 panic!("expected memory operand");
3935 }
3936 }
3937
3938 #[test]
3939 fn att_memory_negative_disp() {
3940 let i = parse_att_instr("movq -16(%rbp), %rax");
3941 if let Operand::Memory(m) = &i.operands[1] {
3942 assert_eq!(m.base, Some(Register::Rbp));
3943 assert_eq!(m.disp, -16);
3944 } else {
3945 panic!("expected memory operand");
3946 }
3947 }
3948
3949 #[test]
3950 fn att_memory_base_index() {
3951 let i = parse_att_instr("movl (%rax, %rcx), %edx");
3952 if let Operand::Memory(m) = &i.operands[1] {
3953 assert_eq!(m.base, Some(Register::Rax));
3954 assert_eq!(m.index, Some(Register::Rcx));
3955 assert_eq!(m.scale, 1);
3956 } else {
3957 panic!("expected memory operand");
3958 }
3959 }
3960
3961 #[test]
3962 fn att_memory_base_index_scale() {
3963 let i = parse_att_instr("movq (%rax, %rcx, 4), %rdx");
3964 if let Operand::Memory(m) = &i.operands[1] {
3965 assert_eq!(m.base, Some(Register::Rax));
3966 assert_eq!(m.index, Some(Register::Rcx));
3967 assert_eq!(m.scale, 4);
3968 assert_eq!(m.disp, 0);
3969 } else {
3970 panic!("expected memory operand");
3971 }
3972 }
3973
3974 #[test]
3975 fn att_memory_disp_base_index_scale() {
3976 let i = parse_att_instr("movl 16(%rbx, %rsi, 8), %eax");
3977 if let Operand::Memory(m) = &i.operands[1] {
3978 assert_eq!(m.base, Some(Register::Rbx));
3979 assert_eq!(m.index, Some(Register::Rsi));
3980 assert_eq!(m.scale, 8);
3981 assert_eq!(m.disp, 16);
3982 } else {
3983 panic!("expected memory operand");
3984 }
3985 }
3986
3987 #[test]
3988 fn att_segment_override() {
3989 let i = parse_att_instr("movq %fs:0x28(%rax), %rbx");
3990 if let Operand::Memory(m) = &i.operands[1] {
3991 assert_eq!(m.segment, Some(Register::Fs));
3992 assert_eq!(m.base, Some(Register::Rax));
3993 assert_eq!(m.disp, 0x28);
3994 } else {
3995 panic!("expected memory operand");
3996 }
3997 }
3998
3999 #[test]
4000 fn att_push_pop() {
4001 let i = parse_att_instr("pushq %rbp");
4002 assert_eq!(i.mnemonic, "push");
4003 assert_eq!(i.operands[0], Operand::Register(Register::Rbp));
4004
4005 let i2 = parse_att_instr("popq %rbp");
4006 assert_eq!(i2.mnemonic, "pop");
4007 assert_eq!(i2.operands[0], Operand::Register(Register::Rbp));
4008 }
4009
4010 #[test]
4011 fn att_xor_reg_reg() {
4012 let i = parse_att_instr("xorl %eax, %eax");
4013 assert_eq!(i.mnemonic, "xor");
4014 assert_eq!(i.operands[0], Operand::Register(Register::Eax));
4016 assert_eq!(i.operands[1], Operand::Register(Register::Eax));
4017 }
4018
4019 #[test]
4020 fn att_call_label() {
4021 let i = parse_att_instr("call func");
4022 assert_eq!(i.mnemonic, "call");
4023 assert_eq!(i.operands[0], Operand::Label(String::from("func")));
4024 }
4025
4026 #[test]
4027 fn att_jmp_label() {
4028 let i = parse_att_instr("jmp done");
4029 assert_eq!(i.mnemonic, "jmp");
4030 assert_eq!(i.operands[0], Operand::Label(String::from("done")));
4031 }
4032
4033 #[test]
4034 fn att_jcc_label() {
4035 let i = parse_att_instr("jne loop");
4036 assert_eq!(i.mnemonic, "jne");
4037 assert_eq!(i.operands[0], Operand::Label(String::from("loop")));
4038 }
4039
4040 #[test]
4041 fn att_ret() {
4042 let i = parse_att_instr("ret");
4043 assert_eq!(i.mnemonic, "ret");
4044 assert!(i.operands.is_empty());
4045 }
4046
4047 #[test]
4048 fn att_syscall() {
4049 let i = parse_att_instr("syscall");
4050 assert_eq!(i.mnemonic, "syscall");
4051 }
4052
4053 #[test]
4054 fn att_lock_prefix() {
4055 let i = parse_att_instr("lock xchgl %eax, (%rbx)");
4056 assert_eq!(i.mnemonic, "xchg");
4057 assert!(i.prefixes.contains(&Prefix::Lock));
4058 }
4059
4060 #[test]
4061 fn att_lea() {
4062 let i = parse_att_instr("leaq 8(%rsp), %rax");
4063 assert_eq!(i.mnemonic, "lea");
4064 assert_eq!(i.operands[0], Operand::Register(Register::Rax));
4066 if let Operand::Memory(m) = &i.operands[1] {
4067 assert_eq!(m.base, Some(Register::Rsp));
4068 assert_eq!(m.disp, 8);
4069 } else {
4070 panic!("expected memory operand");
4071 }
4072 }
4073
4074 #[test]
4075 fn att_imm_label_ref() {
4076 let i = parse_att_instr("movq $myvar, %rax");
4077 assert_eq!(i.mnemonic, "mov");
4078 assert_eq!(i.operands[1], Operand::Label(String::from("myvar")));
4079 }
4080
4081 #[test]
4082 fn att_no_suffix_no_size_hint() {
4083 let i = parse_att_instr("nop");
4085 assert!(i.size_hint.is_none());
4086 }
4087
4088 #[test]
4089 fn att_int_not_stripped() {
4090 let i = parse_att_instr("int $0x80");
4092 assert_eq!(i.mnemonic, "int");
4093 assert_eq!(i.operands[0], Operand::Immediate(0x80));
4094 }
4095
4096 #[test]
4097 fn att_string_ops_not_stripped() {
4098 let i = parse_att_instr("movsb");
4099 assert_eq!(i.mnemonic, "movsb");
4100 let i = parse_att_instr("stosq");
4101 assert_eq!(i.mnemonic, "stosq");
4102 }
4103
4104 #[test]
4105 fn att_rep_prefix() {
4106 let i = parse_att_instr("rep movsb");
4107 assert_eq!(i.mnemonic, "movsb");
4108 assert!(i.prefixes.contains(&Prefix::Rep));
4109 }
4110
4111 #[test]
4112 fn att_cmp_operand_order() {
4113 let i = parse_att_instr("cmpl $0, %eax");
4115 assert_eq!(i.mnemonic, "cmp");
4116 assert_eq!(i.operands[0], Operand::Register(Register::Eax));
4117 assert_eq!(i.operands[1], Operand::Immediate(0));
4118 }
4119
4120 #[test]
4121 fn att_test_operand_order() {
4122 let i = parse_att_instr("testl %eax, %eax");
4124 assert_eq!(i.mnemonic, "test");
4125 assert_eq!(i.operands[0], Operand::Register(Register::Eax));
4126 assert_eq!(i.operands[1], Operand::Register(Register::Eax));
4127 }
4128
4129 #[test]
4130 fn att_sub_mem_to_reg() {
4131 let i = parse_att_instr("subq 8(%rbp), %rax");
4132 assert_eq!(i.mnemonic, "sub");
4133 assert_eq!(i.operands[0], Operand::Register(Register::Rax));
4134 if let Operand::Memory(m) = &i.operands[1] {
4135 assert_eq!(m.base, Some(Register::Rbp));
4136 assert_eq!(m.disp, 8);
4137 } else {
4138 panic!("expected memory operand");
4139 }
4140 }
4141
4142 #[test]
4143 fn att_push_immediate() {
4144 let i = parse_att_instr("pushq $42");
4145 assert_eq!(i.mnemonic, "push");
4146 assert_eq!(i.operands[0], Operand::Immediate(42));
4147 }
4148
4149 #[test]
4150 fn att_numeric_label_fwd() {
4151 let i = parse_att_instr("jmp 1f");
4152 assert_eq!(i.mnemonic, "jmp");
4153 assert_eq!(i.operands[0], Operand::Label(String::from("1f")));
4154 }
4155
4156 #[test]
4157 fn att_numeric_label_bwd() {
4158 let i = parse_att_instr("jne 1b");
4159 assert_eq!(i.mnemonic, "jne");
4160 assert_eq!(i.operands[0], Operand::Label(String::from("1b")));
4161 }
4162
4163 #[test]
4164 fn att_syntax_directive_switches_mode() {
4165 let src = ".syntax att\nmovq $1, %rax";
4166 let tokens = crate::lexer::tokenize(src).unwrap();
4168 let stmts = parse_with_syntax(&tokens, Arch::X86_64, Syntax::Intel).unwrap();
4169 let instr = stmts
4171 .iter()
4172 .find_map(|s| {
4173 if let Statement::Instruction(i) = s {
4174 Some(i)
4175 } else {
4176 None
4177 }
4178 })
4179 .expect("no instruction found");
4180 assert_eq!(instr.mnemonic, "mov");
4181 assert_eq!(instr.operands[0], Operand::Register(Register::Rax));
4183 assert_eq!(instr.operands[1], Operand::Immediate(1));
4184 }
4185
4186 #[test]
4187 fn att_star_indirect_reg() {
4188 let i = parse_att_instr("jmp *%rax");
4189 assert_eq!(i.mnemonic, "jmp");
4190 assert_eq!(i.operands[0], Operand::Register(Register::Rax));
4191 }
4192
4193 #[test]
4194 fn att_star_indirect_mem() {
4195 let i = parse_att_instr("call *(%rax)");
4196 assert_eq!(i.mnemonic, "call");
4197 if let Operand::Memory(m) = &i.operands[0] {
4198 assert_eq!(m.base, Some(Register::Rax));
4199 } else {
4200 panic!("expected memory operand");
4201 }
4202 }
4203
4204 fn parse_aarch64(src: &str) -> Vec<Statement> {
4207 let tokens = crate::lexer::tokenize(src).unwrap();
4208 parse_with_syntax(&tokens, Arch::Aarch64, Syntax::Ual).unwrap()
4209 }
4210
4211 #[test]
4212 fn parse_ldr_literal_pool_x_reg() {
4213 let stmts = parse_aarch64("ldr x0, =0x12345678");
4214 assert_eq!(stmts.len(), 1);
4215 if let Statement::Instruction(instr) = &stmts[0] {
4216 assert_eq!(instr.mnemonic, "ldr");
4217 assert_eq!(instr.operands.len(), 2);
4218 assert!(matches!(
4219 &instr.operands[0],
4220 Operand::Register(Register::A64X0)
4221 ));
4222 assert!(matches!(
4223 &instr.operands[1],
4224 Operand::LiteralPoolValue(0x12345678)
4225 ));
4226 } else {
4227 panic!("expected instruction");
4228 }
4229 }
4230
4231 #[test]
4232 fn parse_ldr_literal_pool_w_reg() {
4233 let stmts = parse_aarch64("ldr w5, =42");
4234 assert_eq!(stmts.len(), 1);
4235 if let Statement::Instruction(instr) = &stmts[0] {
4236 assert_eq!(instr.mnemonic, "ldr");
4237 assert!(matches!(&instr.operands[1], Operand::LiteralPoolValue(42)));
4238 } else {
4239 panic!("expected instruction");
4240 }
4241 }
4242
4243 #[test]
4244 fn parse_ldr_literal_pool_negative() {
4245 let stmts = parse_aarch64("ldr x1, =-1");
4246 if let Statement::Instruction(instr) = &stmts[0] {
4247 assert!(matches!(&instr.operands[1], Operand::LiteralPoolValue(-1)));
4248 } else {
4249 panic!("expected instruction");
4250 }
4251 }
4252
4253 #[test]
4254 fn parse_ldr_literal_pool_hex_large() {
4255 let stmts = parse_aarch64("ldr x0, =0xDEADBEEFCAFEBABE");
4256 if let Statement::Instruction(instr) = &stmts[0] {
4257 if let Operand::LiteralPoolValue(v) = &instr.operands[1] {
4258 assert_eq!(*v, 0xDEADBEEFCAFEBABEu64 as i128);
4259 } else {
4260 panic!("expected LiteralPoolValue");
4261 }
4262 } else {
4263 panic!("expected instruction");
4264 }
4265 }
4266
4267 #[test]
4268 fn parse_ltorg_directive() {
4269 let stmts = parse_aarch64("ldr x0, =1\n.ltorg");
4270 assert_eq!(stmts.len(), 2);
4271 assert!(matches!(&stmts[1], Statement::Ltorg(_)));
4272 }
4273
4274 #[test]
4275 fn parse_pool_directive() {
4276 let stmts = parse_aarch64("ldr x0, =1\n.pool");
4277 assert_eq!(stmts.len(), 2);
4278 assert!(matches!(&stmts[1], Statement::Ltorg(_)));
4279 }
4280
4281 #[test]
4284 fn parse_simd_v_registers() {
4285 for i in 0..32 {
4287 let src = alloc::format!("fmov v{}, v0", i);
4288 let stmts = parse_aarch64(&src);
4289 assert_eq!(stmts.len(), 1, "parsing 'fmov v{}, v0' failed", i);
4290 if let Statement::Instruction(instr) = &stmts[0] {
4291 if let Operand::Register(r) = &instr.operands[0] {
4292 assert!(r.is_a64_simd_fp(), "v{} should be SIMD/FP", i);
4293 assert_eq!(r.a64_reg_num(), i as u8, "v{} reg num", i);
4294 assert_eq!(r.a64_simd_fp_bits(), 128, "v{} should be 128 bits", i);
4295 } else {
4296 panic!("expected register for v{}", i);
4297 }
4298 }
4299 }
4300 }
4301
4302 #[test]
4303 fn parse_simd_q_registers() {
4304 for i in 0..32 {
4305 let src = alloc::format!("mov q{}, q0", i);
4306 let stmts = parse_aarch64(&src);
4307 if let Statement::Instruction(instr) = &stmts[0] {
4308 if let Operand::Register(r) = &instr.operands[0] {
4309 assert!(r.is_a64_simd_fp(), "q{} should be SIMD/FP", i);
4310 assert_eq!(r.a64_reg_num(), i as u8);
4311 assert_eq!(r.a64_simd_fp_bits(), 128);
4312 }
4313 }
4314 }
4315 }
4316
4317 #[test]
4318 fn parse_simd_d_registers() {
4319 for i in 0..32 {
4320 let src = alloc::format!("fmov d{}, d0", i);
4321 let stmts = parse_aarch64(&src);
4322 if let Statement::Instruction(instr) = &stmts[0] {
4323 if let Operand::Register(r) = &instr.operands[0] {
4324 assert!(r.is_a64_simd_fp(), "d{} should be SIMD/FP", i);
4325 assert_eq!(r.a64_reg_num(), i as u8);
4326 assert_eq!(r.a64_simd_fp_bits(), 64);
4327 }
4328 }
4329 }
4330 }
4331
4332 #[test]
4333 fn parse_simd_s_registers() {
4334 for i in 0..32 {
4335 let src = alloc::format!("fmov s{}, s0", i);
4336 let stmts = parse_aarch64(&src);
4337 if let Statement::Instruction(instr) = &stmts[0] {
4338 if let Operand::Register(r) = &instr.operands[0] {
4339 assert!(r.is_a64_simd_fp(), "s{} should be SIMD/FP", i);
4340 assert_eq!(r.a64_reg_num(), i as u8);
4341 assert_eq!(r.a64_simd_fp_bits(), 32);
4342 }
4343 }
4344 }
4345 }
4346
4347 #[test]
4348 fn parse_simd_h_registers() {
4349 for i in 0..32 {
4350 let src = alloc::format!("fmov h{}, h0", i);
4351 let stmts = parse_aarch64(&src);
4352 if let Statement::Instruction(instr) = &stmts[0] {
4353 if let Operand::Register(r) = &instr.operands[0] {
4354 assert!(r.is_a64_simd_fp(), "h{} should be SIMD/FP", i);
4355 assert_eq!(r.a64_reg_num(), i as u8);
4356 assert_eq!(r.a64_simd_fp_bits(), 16);
4357 }
4358 }
4359 }
4360 }
4361
4362 #[test]
4363 fn parse_simd_b_registers() {
4364 for i in 0..32 {
4365 let src = alloc::format!("fmov b{}, b0", i);
4366 let stmts = parse_aarch64(&src);
4367 if let Statement::Instruction(instr) = &stmts[0] {
4368 if let Operand::Register(r) = &instr.operands[0] {
4369 assert!(r.is_a64_simd_fp(), "b{} should be SIMD/FP", i);
4370 assert_eq!(r.a64_reg_num(), i as u8);
4371 assert_eq!(r.a64_simd_fp_bits(), 8);
4372 }
4373 }
4374 }
4375 }
4376
4377 #[test]
4379 fn parse_vector_arrangement_all_specifiers() {
4380 let cases = [
4381 ("add v0.8b, v1.8b, v2.8b", VectorArrangement::B8),
4382 ("add v0.16b, v1.16b, v2.16b", VectorArrangement::B16),
4383 ("add v0.4h, v1.4h, v2.4h", VectorArrangement::H4),
4384 ("add v0.8h, v1.8h, v2.8h", VectorArrangement::H8),
4385 ("add v0.2s, v1.2s, v2.2s", VectorArrangement::S2),
4386 ("add v0.4s, v1.4s, v2.4s", VectorArrangement::S4),
4387 ("add v0.1d, v1.1d, v2.1d", VectorArrangement::D1),
4388 ("add v0.2d, v1.2d, v2.2d", VectorArrangement::D2),
4389 ];
4390 for (src, expected_arr) in &cases {
4391 let stmts = parse_aarch64(src);
4392 if let Statement::Instruction(instr) = &stmts[0] {
4393 assert_eq!(instr.operands.len(), 3, "source: {}", src);
4394 for (j, op) in instr.operands.iter().enumerate() {
4395 match op {
4396 Operand::VectorRegister(_, arr) => {
4397 assert_eq!(arr, expected_arr, "source: {}, operand {}", src, j);
4398 }
4399 other => panic!(
4400 "expected VectorRegister, got {:?} for source: {}, operand {}",
4401 other, src, j
4402 ),
4403 }
4404 }
4405 } else {
4406 panic!("expected instruction for source: {}", src);
4407 }
4408 }
4409 }
4410
4411 #[test]
4412 fn parse_vector_arrangement_register_numbers() {
4413 for i in [0u32, 1, 7, 15, 16, 31] {
4415 let src = alloc::format!("add v{}.4s, v0.4s, v0.4s", i);
4416 let stmts = parse_aarch64(&src);
4417 if let Statement::Instruction(instr) = &stmts[0] {
4418 match &instr.operands[0] {
4419 Operand::VectorRegister(reg, arr) => {
4420 assert_eq!(reg.a64_reg_num(), i as u8, "v{}.4s reg num", i);
4421 assert_eq!(*arr, VectorArrangement::S4);
4422 assert!(reg.is_a64_vector());
4423 }
4424 other => panic!("expected VectorRegister, got {:?}", other),
4425 }
4426 }
4427 }
4428 }
4429
4430 #[test]
4431 fn parse_vector_arrangement_case_insensitive() {
4432 let cases = ["add v0.4S, v1.4S, v2.4S", "add V0.4s, V1.4s, V2.4s"];
4434 for src in &cases {
4435 let stmts = parse_aarch64(src);
4436 if let Statement::Instruction(instr) = &stmts[0] {
4437 for op in &instr.operands {
4438 match op {
4439 Operand::VectorRegister(_, arr) => {
4440 assert_eq!(*arr, VectorArrangement::S4, "source: {}", src);
4441 }
4442 other => panic!(
4443 "expected VectorRegister, got {:?} for source: {}",
4444 other, src
4445 ),
4446 }
4447 }
4448 }
4449 }
4450 }
4451
4452 #[test]
4453 fn parse_vector_reg_without_arrangement() {
4454 let stmts = parse_aarch64("mov v0, v1");
4456 if let Statement::Instruction(instr) = &stmts[0] {
4457 match &instr.operands[0] {
4458 Operand::Register(reg) => {
4459 assert!(reg.is_a64_vector());
4460 assert_eq!(reg.a64_reg_num(), 0);
4461 }
4462 other => panic!("expected Register, got {:?}", other),
4463 }
4464 }
4465 }
4466
4467 #[test]
4468 fn parse_vector_arrangement_display() {
4469 let stmts = parse_aarch64("add v3.2d, v4.2d, v5.2d");
4470 if let Statement::Instruction(instr) = &stmts[0] {
4471 if let Operand::VectorRegister(reg, arr) = &instr.operands[0] {
4472 assert_eq!(reg.a64_reg_num(), 3);
4473 assert_eq!(*arr, VectorArrangement::D2);
4474 let display = alloc::format!("{}", instr.operands[0]);
4475 assert!(
4477 display.contains("2D") || display.contains("2d"),
4478 "Display should contain arrangement: {}",
4479 display
4480 );
4481 } else {
4482 panic!("expected VectorRegister");
4483 }
4484 }
4485 }
4486
4487 #[test]
4488 fn parse_vector_arrangement_element_properties() {
4489 let cases = [
4491 ("add v0.8b, v0.8b, v0.8b", 8u32, 64u32, 8u32),
4492 ("add v0.16b, v0.16b, v0.16b", 8, 128, 16),
4493 ("add v0.4h, v0.4h, v0.4h", 16, 64, 4),
4494 ("add v0.8h, v0.8h, v0.8h", 16, 128, 8),
4495 ("add v0.2s, v0.2s, v0.2s", 32, 64, 2),
4496 ("add v0.4s, v0.4s, v0.4s", 32, 128, 4),
4497 ("add v0.1d, v0.1d, v0.1d", 64, 64, 1),
4498 ("add v0.2d, v0.2d, v0.2d", 64, 128, 2),
4499 ];
4500 for (src, elem_bits, total_bits, lanes) in &cases {
4501 let stmts = parse_aarch64(src);
4502 if let Statement::Instruction(instr) = &stmts[0] {
4503 if let Operand::VectorRegister(_, arr) = &instr.operands[0] {
4504 assert_eq!(arr.element_bits(), *elem_bits, "{}", src);
4505 assert_eq!(arr.total_bits(), *total_bits, "{}", src);
4506 assert_eq!(arr.lane_count(), *lanes, "{}", src);
4507 }
4508 }
4509 }
4510 }
4511
4512 fn parse_rv64(src: &str) -> Vec<Statement> {
4515 let tokens = crate::lexer::tokenize(src).unwrap();
4516 parse_with_syntax(&tokens, Arch::Rv64, Syntax::RiscV).unwrap()
4517 }
4518
4519 fn parse_rv64_instr(src: &str) -> Instruction {
4520 let stmts = parse_rv64(src);
4521 assert_eq!(stmts.len(), 1, "expected 1 statement, got {}", stmts.len());
4522 match stmts.into_iter().next().unwrap() {
4523 Statement::Instruction(i) => i,
4524 s => panic!("expected instruction, got {:?}", s),
4525 }
4526 }
4527
4528 #[test]
4529 fn riscv_fp_register_hardware_names() {
4530 for i in 0u8..32 {
4532 let src = alloc::format!("fadd.d f{}, f{}, f{}", i, i, i);
4533 let instr = parse_rv64_instr(&src);
4534 assert_eq!(instr.mnemonic, "fadd.d");
4535 assert_eq!(instr.operands.len(), 3);
4536 for op in &instr.operands {
4537 if let Operand::Register(reg) = op {
4538 assert!(reg.is_riscv_fp(), "f{} should be FP register", i);
4539 assert_eq!(reg.rv_fp_reg_num(), i, "f{} should map to reg {}", i, i);
4540 } else {
4541 panic!("expected register operand for f{}", i);
4542 }
4543 }
4544 }
4545 }
4546
4547 #[test]
4548 fn riscv_fp_register_abi_ft() {
4549 let mapping: &[(&str, u8)] = &[
4551 ("ft0", 0),
4552 ("ft1", 1),
4553 ("ft2", 2),
4554 ("ft3", 3),
4555 ("ft4", 4),
4556 ("ft5", 5),
4557 ("ft6", 6),
4558 ("ft7", 7),
4559 ("ft8", 28),
4560 ("ft9", 29),
4561 ("ft10", 30),
4562 ("ft11", 31),
4563 ];
4564 for &(name, expected_num) in mapping {
4565 let src = alloc::format!("fmv.d {}, {}", name, name);
4566 let instr = parse_rv64_instr(&src);
4567 if let Operand::Register(reg) = &instr.operands[0] {
4568 assert!(reg.is_riscv_fp(), "{} should be FP", name);
4569 assert_eq!(
4570 reg.rv_fp_reg_num(),
4571 expected_num,
4572 "{} → f{}",
4573 name,
4574 expected_num
4575 );
4576 } else {
4577 panic!("expected register for {}", name);
4578 }
4579 }
4580 }
4581
4582 #[test]
4583 fn riscv_fp_register_abi_fs() {
4584 let mapping: &[(&str, u8)] = &[
4586 ("fs0", 8),
4587 ("fs1", 9),
4588 ("fs2", 18),
4589 ("fs3", 19),
4590 ("fs4", 20),
4591 ("fs5", 21),
4592 ("fs6", 22),
4593 ("fs7", 23),
4594 ("fs8", 24),
4595 ("fs9", 25),
4596 ("fs10", 26),
4597 ("fs11", 27),
4598 ];
4599 for &(name, expected_num) in mapping {
4600 let src = alloc::format!("fmv.d {}, {}", name, name);
4601 let instr = parse_rv64_instr(&src);
4602 if let Operand::Register(reg) = &instr.operands[0] {
4603 assert!(reg.is_riscv_fp(), "{} should be FP", name);
4604 assert_eq!(
4605 reg.rv_fp_reg_num(),
4606 expected_num,
4607 "{} → f{}",
4608 name,
4609 expected_num
4610 );
4611 } else {
4612 panic!("expected register for {}", name);
4613 }
4614 }
4615 }
4616
4617 #[test]
4618 fn riscv_fp_register_abi_fa() {
4619 let mapping: &[(&str, u8)] = &[
4621 ("fa0", 10),
4622 ("fa1", 11),
4623 ("fa2", 12),
4624 ("fa3", 13),
4625 ("fa4", 14),
4626 ("fa5", 15),
4627 ("fa6", 16),
4628 ("fa7", 17),
4629 ];
4630 for &(name, expected_num) in mapping {
4631 let src = alloc::format!("fmv.d {}, {}", name, name);
4632 let instr = parse_rv64_instr(&src);
4633 if let Operand::Register(reg) = &instr.operands[0] {
4634 assert!(reg.is_riscv_fp(), "{} should be FP", name);
4635 assert_eq!(
4636 reg.rv_fp_reg_num(),
4637 expected_num,
4638 "{} → f{}",
4639 name,
4640 expected_num
4641 );
4642 } else {
4643 panic!("expected register for {}", name);
4644 }
4645 }
4646 }
4647
4648 #[test]
4649 fn riscv_fp_mixed_with_integer_regs() {
4650 let instr = parse_rv64_instr("flw ft0, 0(sp)");
4652 assert_eq!(instr.mnemonic, "flw");
4653 if let Operand::Register(reg) = &instr.operands[0] {
4654 assert!(reg.is_riscv_fp());
4655 assert_eq!(reg.rv_fp_reg_num(), 0);
4656 } else {
4657 panic!("expected FP register");
4658 }
4659 }
4660
4661 #[test]
4662 fn riscv_fp_not_integer() {
4663 let instr = parse_rv64_instr("fadd.d fa0, fa1, fa2");
4665 for op in &instr.operands {
4666 if let Operand::Register(reg) = op {
4667 assert!(reg.is_riscv_fp());
4668 assert!(!reg.is_riscv());
4669 }
4670 }
4671 }
4672
4673 #[test]
4674 fn riscv_integer_not_fp() {
4675 let instr = parse_rv64_instr("add a0, a1, a2");
4677 for op in &instr.operands {
4678 if let Operand::Register(reg) = op {
4679 assert!(reg.is_riscv());
4680 assert!(!reg.is_riscv_fp());
4681 }
4682 }
4683 }
4684}