1use super::{
33 BasicBlock, BlockId, EffectKind, Function, FunctionId, InstKind, Instruction,
34 InstructionMetadata, MemoryRegion, Module, StorageAlias, Terminator, Value, ValueId,
35};
36use crate::mir::{Immediate, MirType};
37use alloy_primitives::U256;
38use solar_data_structures::map::FxHashMap;
39use solar_interface::{BytePos, Ident, Span, Symbol};
40use solar_sema::hir;
41use std::fmt;
42
43pub fn parse_module(input: &str) -> Result<Module, ParseError> {
59 let mut p = Parser::new(input);
60 p.parse_module()
61}
62
63pub fn parse_function(input: &str) -> Result<Function, ParseError> {
73 let mut p = Parser::new(input);
74 p.skip_blank_and_comments();
75 let func = p.parse_function()?;
76 p.skip_blank_and_comments();
77 if !p.is_eof() {
78 return Err(p.error("trailing input after function"));
79 }
80 Ok(func)
81}
82
83#[derive(Clone, Debug)]
85pub struct ParseError {
86 pub line: usize,
88 pub col: usize,
90 pub msg: String,
92 pub line_text: String,
96}
97
98impl fmt::Display for ParseError {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 writeln!(f, "MIR parse error at line {}, col {}: {}", self.line, self.col, self.msg)?;
101 if !self.line_text.is_empty() {
102 writeln!(f, " |")?;
103 writeln!(f, "{:>3} | {}", self.line, self.line_text)?;
104 let caret_pad = " ".repeat(self.col.saturating_sub(1));
107 write!(f, " | {caret_pad}^")?;
108 }
109 Ok(())
110 }
111}
112
113impl std::error::Error for ParseError {}
114
115struct Parser<'a> {
121 input: &'a str,
122 pos: usize,
123 line: usize,
124 col: usize,
125}
126
127impl<'a> Parser<'a> {
128 fn new(input: &'a str) -> Self {
129 Self { input, pos: 0, line: 1, col: 1 }
130 }
131
132 fn is_eof(&self) -> bool {
135 self.pos >= self.input.len()
136 }
137
138 fn peek_char(&self) -> Option<char> {
139 self.input[self.pos..].chars().next()
140 }
141
142 fn advance(&mut self) -> Option<char> {
143 let c = self.peek_char()?;
144 self.pos += c.len_utf8();
145 if c == '\n' {
146 self.line += 1;
147 self.col = 1;
148 } else {
149 self.col += 1;
150 }
151 Some(c)
152 }
153
154 fn skip_inline_whitespace(&mut self) {
155 while let Some(c) = self.peek_char() {
156 if c == ' ' || c == '\t' {
157 self.advance();
158 } else {
159 break;
160 }
161 }
162 }
163
164 fn skip_blank_and_comments(&mut self) {
170 loop {
171 self.skip_inline_whitespace();
172 match self.peek_char() {
173 Some('\n') | Some('\r') => {
174 self.advance();
175 }
176 Some('/') if self.input[self.pos..].starts_with("//") => {
177 self.skip_to_eol();
178 }
179 Some(';') => {
180 if self.input[self.pos..]
182 .trim_start_matches(';')
183 .trim_start()
184 .starts_with("module")
185 {
186 break;
187 }
188 self.skip_to_eol();
189 }
190 _ => break,
191 }
192 }
193 }
194
195 fn skip_to_eol(&mut self) {
196 while let Some(c) = self.peek_char() {
197 if c == '\n' {
198 break;
199 }
200 self.advance();
201 }
202 }
203
204 fn error(&self, msg: impl Into<String>) -> ParseError {
205 ParseError {
206 line: self.line,
207 col: self.col,
208 msg: msg.into(),
209 line_text: self.current_line_text(),
210 }
211 }
212
213 fn error_at(&self, line: usize, col: usize, pos: usize, msg: impl Into<String>) -> ParseError {
218 let bytes = self.input.as_bytes();
220 let p = pos.min(bytes.len());
221 let mut start = p;
222 while start > 0 && bytes[start - 1] != b'\n' {
223 start -= 1;
224 }
225 let mut end = start;
226 while end < bytes.len() && bytes[end] != b'\n' {
227 end += 1;
228 }
229 let line_text = self.input[start..end].trim_end_matches('\r').to_string();
230 ParseError { line, col, msg: msg.into(), line_text }
231 }
232
233 fn current_line_text(&self) -> String {
237 let bytes = self.input.as_bytes();
238 let pos = self.pos.min(bytes.len());
239 let mut start = pos;
241 while start > 0 && bytes[start - 1] != b'\n' {
242 start -= 1;
243 }
244 let mut end = start;
246 while end < bytes.len() && bytes[end] != b'\n' {
247 end += 1;
248 }
249 self.input[start..end].trim_end_matches('\r').to_string()
250 }
251
252 fn skip_inline(&mut self) {
259 self.skip_inline_whitespace();
260 }
261
262 fn expect_keyword(&mut self, kw: &str) -> Result<(), ParseError> {
264 self.skip_inline();
265 if self.input[self.pos..].starts_with(kw) {
266 for _ in 0..kw.chars().count() {
267 self.advance();
268 }
269 Ok(())
270 } else {
271 Err(self.error(format!("expected `{kw}`")))
272 }
273 }
274
275 fn expect_punct(&mut self, expected: char) -> Result<(), ParseError> {
277 self.skip_inline();
278 match self.peek_char() {
279 Some(c) if c == expected => {
280 self.advance();
281 Ok(())
282 }
283 Some(c) => Err(self.error(format!("expected `{expected}`, found `{c}`"))),
284 None => Err(self.error(format!("expected `{expected}`, found EOF"))),
285 }
286 }
287
288 fn try_punct(&mut self, expected: char) -> bool {
290 self.skip_inline();
291 if self.peek_char() == Some(expected) {
292 self.advance();
293 true
294 } else {
295 false
296 }
297 }
298
299 fn parse_ident(&mut self) -> Result<&'a str, ParseError> {
301 self.skip_inline();
302 let start = self.pos;
303 match self.peek_char() {
304 Some(c) if c.is_ascii_alphabetic() || c == '_' => {
305 self.advance();
306 }
307 _ => return Err(self.error("expected identifier")),
308 }
309 while let Some(c) = self.peek_char() {
310 if c.is_ascii_alphanumeric() || c == '_' {
311 self.advance();
312 } else {
313 break;
314 }
315 }
316 Ok(&self.input[start..self.pos])
317 }
318
319 fn parse_uint_literal(&mut self) -> Result<U256, ParseError> {
321 self.skip_inline();
322 let start = self.pos;
323 if self.input[self.pos..].starts_with("0x") || self.input[self.pos..].starts_with("0X") {
324 self.advance();
325 self.advance();
326 while let Some(c) = self.peek_char() {
327 if c.is_ascii_hexdigit() {
328 self.advance();
329 } else {
330 break;
331 }
332 }
333 let s = &self.input[start..self.pos];
334 U256::from_str_radix(&s[2..], 16).map_err(|e| self.error(format!("invalid hex: {e}")))
335 } else if matches!(self.peek_char(), Some(c) if c.is_ascii_digit()) {
336 while let Some(c) = self.peek_char() {
337 if c.is_ascii_digit() {
338 self.advance();
339 } else {
340 break;
341 }
342 }
343 let s = &self.input[start..self.pos];
344 s.parse::<U256>().map_err(|e| self.error(format!("invalid integer: {e}")))
345 } else {
346 Err(self.error("expected integer literal"))
347 }
348 }
349
350 fn parse_module(&mut self) -> Result<Module, ParseError> {
353 self.skip_blank_and_comments();
354
355 let module_name = if self.try_punct(';') {
358 self.expect_keyword("module")?;
359 self.skip_inline();
360 self.expect_punct('@')?;
361 let name = self.parse_ident()?.to_string();
362 self.skip_to_eol();
363 self.skip_blank_and_comments();
364 name
365 } else {
366 "module".to_string()
367 };
368
369 let module_ident = Ident::with_dummy_span(Symbol::intern(&module_name));
370 let mut module = Module::new(module_ident);
371
372 while !self.is_eof() {
373 self.skip_blank_and_comments();
374 if self.is_eof() {
375 break;
376 }
377 if self.input[self.pos..].starts_with("//") {
379 self.skip_to_eol();
380 continue;
381 }
382 let func = self.parse_function()?;
383 module.add_function(func);
384 }
385
386 Ok(module)
387 }
388
389 fn parse_function(&mut self) -> Result<Function, ParseError> {
390 self.skip_blank_and_comments();
391 self.expect_keyword("fn")?;
392 self.skip_inline();
393 self.expect_punct('@')?;
394 let name = self.parse_ident()?.to_string();
395 let func_ident = Ident::with_dummy_span(Symbol::intern(&name));
396 let mut func = Function::new(func_ident);
397
398 self.expect_punct('(')?;
400 let mut arg_values: Vec<ValueId> = Vec::new();
401 if !self.try_punct(')') {
402 loop {
403 let arg_name = self.parse_ident()?;
404 if !arg_name.starts_with("arg") {
405 return Err(self.error(format!("expected `argN`, got `{arg_name}`")));
406 }
407 let _idx: u32 = arg_name[3..]
408 .parse()
409 .map_err(|_| self.error(format!("invalid arg index in `{arg_name}`")))?;
410 self.expect_punct(':')?;
411 let ty = self.parse_type()?;
412 let index = func.params.len() as u32;
413 func.params.push(ty);
414 let val = func.alloc_value(Value::Arg { index, ty });
415 arg_values.push(val);
416 if self.try_punct(',') {
417 continue;
418 }
419 self.expect_punct(')')?;
420 break;
421 }
422 }
423
424 self.skip_inline();
426 if self.input[self.pos..].starts_with("->") {
427 self.advance();
428 self.advance();
429 self.skip_inline();
430 if self.try_punct('(') {
431 if !self.try_punct(')') {
432 loop {
433 let ty = self.parse_type()?;
434 func.returns.push(ty);
435 if self.try_punct(',') {
436 continue;
437 }
438 self.expect_punct(')')?;
439 break;
440 }
441 }
442 } else {
443 let ty = self.parse_type()?;
444 func.returns.push(ty);
445 }
446 }
447
448 self.parse_function_attributes(&mut func)?;
449 self.expect_punct('{')?;
450 self.skip_blank_and_comments();
451
452 let mut block_labels: FxHashMap<u32, BlockId> = FxHashMap::default();
455
456 let scan_start = self.pos;
458 let scan_line = self.line;
459 let scan_col = self.col;
460
461 let mut first_block: Option<u32> = None;
463 loop {
464 self.skip_blank_and_comments();
465 if self.is_eof() {
466 return Err(self.error("unterminated function body"));
467 }
468 if self.peek_char() == Some('}') {
469 break;
470 }
471 let line_start_pos = self.pos;
473 let line_start_line = self.line;
474 let line_start_col = self.col;
475 self.skip_inline_whitespace();
476 if let Some('b') = self.peek_char()
477 && self.input[self.pos..].starts_with("bb")
478 {
479 let save = self.pos;
480 let save_line = self.line;
481 let save_col = self.col;
482 self.advance();
483 self.advance();
484 let mut idx_str = String::new();
485 while let Some(c) = self.peek_char() {
486 if c.is_ascii_digit() {
487 idx_str.push(c);
488 self.advance();
489 } else {
490 break;
491 }
492 }
493 if idx_str.is_empty() {
494 self.pos = save;
495 self.line = save_line;
496 self.col = save_col;
497 } else {
498 self.skip_inline_whitespace();
500 if self.peek_char() == Some('(') {
501 while let Some(c) = self.peek_char() {
502 self.advance();
503 if c == ')' {
504 break;
505 }
506 }
507 }
508 if self.peek_char() == Some(':') {
509 let idx: u32 =
510 idx_str.parse().map_err(|_| self.error("invalid block index"))?;
511 if first_block.is_none() {
512 first_block = Some(idx);
513 }
514 if block_labels.is_empty() {
518 block_labels.insert(idx, func.entry_block);
519 } else {
520 let id = func.alloc_block();
521 block_labels.insert(idx, id);
522 }
523 self.advance();
525 self.skip_to_eol();
526 continue;
527 } else {
528 self.pos = save;
529 self.line = save_line;
530 self.col = save_col;
531 }
532 }
533 }
534 self.pos = line_start_pos;
536 self.line = line_start_line;
537 self.col = line_start_col;
538 self.skip_to_eol();
539 }
540
541 self.pos = scan_start;
543 self.line = scan_line;
544 self.col = scan_col;
545
546 let mut value_labels: FxHashMap<u32, ValueId> = FxHashMap::default();
548 let mut current_block: Option<BlockId> = None;
549
550 loop {
551 self.skip_blank_and_comments();
552 if self.is_eof() {
553 return Err(self.error("unterminated function body"));
554 }
555 if self.try_punct('}') {
556 break;
557 }
558
559 self.skip_inline_whitespace();
561 if self.input[self.pos..].starts_with("bb") {
562 let save = self.pos;
563 let save_line = self.line;
564 let save_col = self.col;
565 self.advance();
566 self.advance();
567 let mut idx_str = String::new();
568 while let Some(c) = self.peek_char() {
569 if c.is_ascii_digit() {
570 idx_str.push(c);
571 self.advance();
572 } else {
573 break;
574 }
575 }
576 if !idx_str.is_empty() {
577 self.skip_inline_whitespace();
578 if self.peek_char() == Some('(') {
580 while let Some(c) = self.peek_char() {
581 self.advance();
582 if c == ')' {
583 break;
584 }
585 }
586 self.skip_inline_whitespace();
587 }
588 if self.peek_char() == Some(':') {
589 self.advance();
590 let idx: u32 = idx_str.parse().unwrap();
591 let bid = *block_labels
592 .get(&idx)
593 .ok_or_else(|| self.error(format!("unknown block bb{idx}")))?;
594 current_block = Some(bid);
595 self.skip_to_eol();
596 continue;
597 }
598 self.pos = save;
599 self.line = save_line;
600 self.col = save_col;
601 } else {
602 self.pos = save;
603 self.line = save_line;
604 self.col = save_col;
605 }
606 }
607
608 let block =
610 current_block.ok_or_else(|| self.error("instruction outside of any block"))?;
611 self.parse_instruction_or_terminator(
612 &mut func,
613 block,
614 &arg_values,
615 &block_labels,
616 &mut value_labels,
617 )?;
618 }
619
620 self.reject_unresolved_value_labels(&func, &value_labels)?;
621
622 Ok(func)
623 }
624
625 fn parse_function_attributes(&mut self, func: &mut Function) -> Result<(), ParseError> {
626 self.skip_inline();
627 if !self.try_punct('[') {
628 return Ok(());
629 }
630
631 loop {
632 let key = self.parse_ident()?.to_string();
633 match key.as_str() {
634 "selector" => {
635 self.expect_punct('=')?;
636 let selector = self.parse_uint_literal()?;
637 let selector = self.u256_to_u32(selector)?;
638 func.selector = Some(selector.to_be_bytes());
639 }
640 _ => return Err(self.error(format!("unknown function attribute `{key}`"))),
641 }
642
643 if self.try_punct(',') {
644 continue;
645 }
646 self.expect_punct(']')?;
647 break;
648 }
649
650 Ok(())
651 }
652
653 fn parse_type(&mut self) -> Result<MirType, ParseError> {
654 self.skip_inline();
655 let id = self.parse_ident()?;
656 let ty = if let Some(rest) = id.strip_prefix('u') {
658 let bits: u16 =
659 rest.parse().map_err(|_| self.error(format!("invalid u-type `{id}`")))?;
660 MirType::UInt(bits)
661 } else if let Some(rest) = id.strip_prefix('i') {
662 let bits: u16 =
663 rest.parse().map_err(|_| self.error(format!("invalid i-type `{id}`")))?;
664 MirType::Int(bits)
665 } else if let Some(rest) = id.strip_prefix("bytes") {
666 let n: u8 =
667 rest.parse().map_err(|_| self.error(format!("invalid bytes type `{id}`")))?;
668 MirType::FixedBytes(n)
669 } else {
670 match id {
671 "bool" => MirType::Bool,
672 "address" => MirType::Address,
673 "memptr" => MirType::MemPtr,
674 "storageptr" => MirType::StoragePtr,
675 "calldataptr" => MirType::CalldataPtr,
676 "function" => MirType::Function,
677 "void" => MirType::Void,
678 _ => return Err(self.error(format!("unknown type `{id}`"))),
679 }
680 };
681 Ok(ty)
682 }
683
684 fn parse_value(
687 &mut self,
688 func: &mut Function,
689 arg_values: &[ValueId],
690 value_labels: &mut FxHashMap<u32, ValueId>,
691 ) -> Result<ValueId, ParseError> {
692 self.skip_inline();
693 if matches!(self.peek_char(), Some(c) if c.is_ascii_digit()) {
695 let v = self.parse_uint_literal()?;
696 return Ok(func.alloc_value(Value::Immediate(Immediate::uint256(v))));
697 }
698 let ident = self.parse_ident()?;
700 if ident == "true" {
701 return Ok(func.alloc_value(Value::Immediate(Immediate::bool(true))));
702 }
703 if ident == "false" {
704 return Ok(func.alloc_value(Value::Immediate(Immediate::bool(false))));
705 }
706 if ident == "err" {
707 let guar = solar_interface::diagnostics::ErrorGuaranteed::new_unchecked();
710 return Ok(func.alloc_value(Value::Error(guar)));
711 }
712 if let Some(rest) = ident.strip_prefix("arg") {
713 let idx: usize =
714 rest.parse().map_err(|_| self.error(format!("invalid arg `{ident}`")))?;
715 return arg_values
716 .get(idx)
717 .copied()
718 .ok_or_else(|| self.error(format!("arg{idx} out of range")));
719 }
720 if let Some(rest) = ident.strip_prefix('v') {
721 let idx: u32 = rest
722 .parse()
723 .map_err(|_| self.error(format!("invalid value reference `{ident}`")))?;
724 if let Some(value) = value_labels.get(&idx).copied() {
725 return Ok(value);
726 }
727 let value = func.alloc_value(Value::Undef(MirType::uint256()));
728 value_labels.insert(idx, value);
729 return Ok(value);
730 }
731 Err(self.error(format!("expected value reference, got `{ident}`")))
732 }
733
734 fn resolve_result_label(
735 &self,
736 func: &mut Function,
737 value_labels: &mut FxHashMap<u32, ValueId>,
738 label: u32,
739 inst_id: super::InstId,
740 ) -> Result<(), ParseError> {
741 if let Some(value) = value_labels.get(&label).copied() {
742 if matches!(func.values[value], Value::Undef(_)) {
743 func.values[value] = Value::Inst(inst_id);
744 return Ok(());
745 }
746 return Err(self.error(format!("duplicate value `v{label}`")));
747 }
748
749 let value = func.alloc_value(Value::Inst(inst_id));
750 value_labels.insert(label, value);
751 Ok(())
752 }
753
754 fn reject_unresolved_value_labels(
755 &self,
756 func: &Function,
757 value_labels: &FxHashMap<u32, ValueId>,
758 ) -> Result<(), ParseError> {
759 let mut unresolved: Vec<_> = value_labels
760 .iter()
761 .filter_map(|(&label, &value)| {
762 matches!(func.values[value], Value::Undef(_)).then_some(label)
763 })
764 .collect();
765 unresolved.sort_unstable();
766 if let Some(label) = unresolved.first() {
767 return Err(self.error(format!("undefined value `v{label}`")));
768 }
769 Ok(())
770 }
771
772 fn parse_block_id(
773 &mut self,
774 block_labels: &FxHashMap<u32, BlockId>,
775 ) -> Result<BlockId, ParseError> {
776 self.skip_inline();
777 let id = self.parse_ident()?;
778 let rest = id
779 .strip_prefix("bb")
780 .ok_or_else(|| self.error(format!("expected `bbN`, got `{id}`")))?;
781 let idx: u32 =
782 rest.parse().map_err(|_| self.error(format!("invalid block index `{id}`")))?;
783 block_labels.get(&idx).copied().ok_or_else(|| self.error(format!("unknown block `{id}`")))
784 }
785
786 fn parse_function_id(&mut self) -> Result<FunctionId, ParseError> {
787 self.skip_inline();
788 let id = self.parse_ident()?;
789 let rest = id
790 .strip_prefix("fn")
791 .ok_or_else(|| self.error(format!("expected `fnN`, got `{id}`")))?;
792 let idx: usize =
793 rest.parse().map_err(|_| self.error(format!("invalid function index `{id}`")))?;
794 Ok(FunctionId::from_usize(idx))
795 }
796
797 fn parse_instruction_or_terminator(
799 &mut self,
800 func: &mut Function,
801 block: BlockId,
802 arg_values: &[ValueId],
803 block_labels: &FxHashMap<u32, BlockId>,
804 value_labels: &mut FxHashMap<u32, ValueId>,
805 ) -> Result<(), ParseError> {
806 self.skip_inline_whitespace();
807
808 let result_label: Option<u32> = if self.input[self.pos..].starts_with('v')
810 && self.input[self.pos..].chars().nth(1).is_some_and(|c| c.is_ascii_digit())
811 {
812 let save_pos = self.pos;
814 let save_line = self.line;
815 let save_col = self.col;
816 self.advance();
817 let mut idx_str = String::new();
818 while let Some(c) = self.peek_char() {
819 if c.is_ascii_digit() {
820 idx_str.push(c);
821 self.advance();
822 } else {
823 break;
824 }
825 }
826 self.skip_inline_whitespace();
827 if self.peek_char() == Some('=') {
828 self.advance();
829 Some(idx_str.parse().unwrap())
830 } else {
831 self.pos = save_pos;
832 self.line = save_line;
833 self.col = save_col;
834 None
835 }
836 } else {
837 None
838 };
839
840 self.skip_inline_whitespace();
841 let mnemonic_line = self.line;
845 let mnemonic_col = self.col;
846 let mnemonic_pos = self.pos;
847 let mnemonic = self.parse_ident()?.to_string();
848
849 match mnemonic.as_str() {
851 "jump" => {
852 let target = self.parse_block_id(block_labels)?;
853 self.set_terminator(func, block, Terminator::Jump(target));
854 self.skip_to_eol();
855 return Ok(());
856 }
857 "br" => {
858 let condition = self.parse_value(func, arg_values, value_labels)?;
859 self.expect_punct(',')?;
860 let then_block = self.parse_block_id(block_labels)?;
861 self.expect_punct(',')?;
862 let else_block = self.parse_block_id(block_labels)?;
863 self.set_terminator(
864 func,
865 block,
866 Terminator::Branch { condition, then_block, else_block },
867 );
868 self.skip_to_eol();
869 return Ok(());
870 }
871 "switch" => {
872 let value = self.parse_value(func, arg_values, value_labels)?;
873 self.expect_punct(',')?;
874 self.expect_keyword("default")?;
875 let default = self.parse_block_id(block_labels)?;
876 self.expect_punct(',')?;
877 self.expect_punct('[')?;
878 let mut cases = Vec::new();
879 if !self.try_punct(']') {
880 loop {
881 let val = self.parse_value(func, arg_values, value_labels)?;
882 self.expect_keyword("=>")?;
883 let bid = self.parse_block_id(block_labels)?;
884 cases.push((val, bid));
885 if self.try_punct(',') {
886 continue;
887 }
888 self.expect_punct(']')?;
889 break;
890 }
891 }
892 self.set_terminator(func, block, Terminator::Switch { value, default, cases });
893 self.skip_to_eol();
894 return Ok(());
895 }
896 "ret" => {
897 use smallvec::SmallVec;
898 let mut values: SmallVec<[ValueId; 2]> = SmallVec::new();
899 self.skip_inline_whitespace();
900 if self.peek_char() != Some('\n') && !self.is_eof() {
902 loop {
903 values.push(self.parse_value(func, arg_values, value_labels)?);
904 if !self.try_punct(',') {
905 break;
906 }
907 }
908 }
909 self.set_terminator(func, block, Terminator::Return { values });
910 self.skip_to_eol();
911 return Ok(());
912 }
913 "revert" => {
914 let offset = self.parse_value(func, arg_values, value_labels)?;
915 self.expect_punct(',')?;
916 let size = self.parse_value(func, arg_values, value_labels)?;
917 self.set_terminator(func, block, Terminator::Revert { offset, size });
918 self.skip_to_eol();
919 return Ok(());
920 }
921 "returndata" => {
922 let offset = self.parse_value(func, arg_values, value_labels)?;
923 self.expect_punct(',')?;
924 let size = self.parse_value(func, arg_values, value_labels)?;
925 self.set_terminator(func, block, Terminator::ReturnData { offset, size });
926 self.skip_to_eol();
927 return Ok(());
928 }
929 "stop" => {
930 self.set_terminator(func, block, Terminator::Stop);
931 self.skip_to_eol();
932 return Ok(());
933 }
934 "selfdestruct" => {
935 let recipient = self.parse_value(func, arg_values, value_labels)?;
936 self.set_terminator(func, block, Terminator::SelfDestruct { recipient });
937 self.skip_to_eol();
938 return Ok(());
939 }
940 "invalid" => {
941 self.set_terminator(func, block, Terminator::Invalid);
942 self.skip_to_eol();
943 return Ok(());
944 }
945 _ => {}
946 }
947
948 let (kind, result_ty) = self
950 .parse_inst_kind(&mnemonic, func, arg_values, block_labels, value_labels)
951 .map_err(|e| {
952 if e.msg.starts_with("unknown instruction") {
955 self.error_at(mnemonic_line, mnemonic_col, mnemonic_pos, e.msg)
956 } else {
957 e
958 }
959 })?;
960
961 let metadata = self.parse_metadata(func, arg_values, value_labels)?;
962 let mut inst = Instruction::new(kind, result_ty);
963 inst.metadata = metadata;
964 let inst_id = func.alloc_inst(inst);
965 func.blocks[block].instructions.push(inst_id);
966 if let Some(label) = result_label {
967 self.resolve_result_label(func, value_labels, label, inst_id)?;
968 }
969 self.skip_to_eol();
970 Ok(())
971 }
972
973 fn parse_metadata(
974 &mut self,
975 func: &mut Function,
976 arg_values: &[ValueId],
977 value_labels: &mut FxHashMap<u32, ValueId>,
978 ) -> Result<InstructionMetadata, ParseError> {
979 let mut metadata = InstructionMetadata::EMPTY;
980 self.skip_inline();
981 if !self.try_punct('!') {
982 return Ok(metadata);
983 }
984 self.expect_keyword("metadata")?;
985 self.expect_punct('(')?;
986 if self.try_punct(')') {
987 return Ok(metadata);
988 }
989
990 loop {
991 let key = self.parse_ident()?.to_string();
992 match key.as_str() {
993 "unchecked" => {
994 metadata.set_unchecked(true);
995 }
996 "storage" => {
997 self.expect_punct('=')?;
998 metadata.set_storage_alias(Some(self.parse_storage_alias(
999 func,
1000 arg_values,
1001 value_labels,
1002 )?));
1003 }
1004 "memory" => {
1005 self.expect_punct('=')?;
1006 let value = self.parse_ident()?;
1007 metadata.set_memory_region(Some(self.parse_memory_region(value)?));
1008 }
1009 "effect" => {
1010 self.expect_punct('=')?;
1011 let value = self.parse_ident()?;
1012 metadata.set_effect(Some(self.parse_effect_kind(value)?));
1013 }
1014 "loop_depth" => {
1015 self.expect_punct('=')?;
1016 let value = self.parse_uint_literal()?;
1017 metadata.loop_depth = self.u256_to_u16(value)?;
1018 }
1019 "hir" => {
1020 self.expect_punct('=')?;
1021 let value = self.parse_uint_literal()?;
1022 metadata.set_hir_expr(Some(hir::ExprId::from_usize(
1023 self.u256_to_u32(value)? as usize
1024 )));
1025 }
1026 "span" => {
1027 self.expect_punct('=')?;
1028 let lo = self.parse_uint_literal()?;
1029 let lo = self.u256_to_u32(lo)?;
1030 self.expect_punct('.')?;
1031 self.expect_punct('.')?;
1032 let hi = self.parse_uint_literal()?;
1033 let hi = self.u256_to_u32(hi)?;
1034 metadata.set_source_span(Some(Span::new(BytePos(lo), BytePos(hi))));
1035 }
1036 _ => return Err(self.error(format!("unknown metadata key `{key}`"))),
1037 }
1038
1039 if self.try_punct(',') {
1040 continue;
1041 }
1042 self.expect_punct(')')?;
1043 break;
1044 }
1045
1046 Ok(metadata)
1047 }
1048
1049 fn parse_storage_alias(
1050 &mut self,
1051 func: &mut Function,
1052 arg_values: &[ValueId],
1053 value_labels: &mut FxHashMap<u32, ValueId>,
1054 ) -> Result<StorageAlias, ParseError> {
1055 let kind = self.parse_ident()?.to_string();
1056 self.expect_punct('(')?;
1057 let alias = match kind.as_str() {
1058 "slot" => StorageAlias::Slot(self.parse_uint_literal()?),
1059 "symbolic" => {
1060 StorageAlias::Symbolic(self.parse_value(func, arg_values, value_labels)?)
1061 }
1062 "offset" => {
1063 let base = self.parse_value(func, arg_values, value_labels)?;
1064 self.expect_punct(',')?;
1065 let offset = self.parse_uint_literal()?;
1066 StorageAlias::Offset { base, offset }
1067 }
1068 _ => return Err(self.error(format!("unknown storage metadata value `{kind}`"))),
1069 };
1070 self.expect_punct(')')?;
1071 Ok(alias)
1072 }
1073
1074 fn parse_memory_region(&self, value: &str) -> Result<MemoryRegion, ParseError> {
1075 Ok(match value {
1076 "scratch" => MemoryRegion::Scratch,
1077 "abi_return" => MemoryRegion::AbiReturn,
1078 "heap" => MemoryRegion::Heap,
1079 "internal_frame" => MemoryRegion::InternalFrame,
1080 "unknown" => MemoryRegion::Unknown,
1081 _ => return Err(self.error(format!("unknown memory metadata value `{value}`"))),
1082 })
1083 }
1084
1085 fn parse_effect_kind(&self, value: &str) -> Result<EffectKind, ParseError> {
1086 Ok(match value {
1087 "pure" => EffectKind::Pure,
1088 "memory_read" => EffectKind::MemoryRead,
1089 "memory_write" => EffectKind::MemoryWrite,
1090 "storage_read" => EffectKind::StorageRead,
1091 "storage_write" => EffectKind::StorageWrite,
1092 "transient_read" => EffectKind::TransientRead,
1093 "transient_write" => EffectKind::TransientWrite,
1094 "environment_read" => EffectKind::EnvironmentRead,
1095 "external_call" => EffectKind::ExternalCall,
1096 "internal_call" => EffectKind::InternalCall,
1097 "create" => EffectKind::Create,
1098 "log" => EffectKind::Log,
1099 _ => return Err(self.error(format!("unknown effect metadata value `{value}`"))),
1100 })
1101 }
1102
1103 fn u256_to_u32(&self, value: U256) -> Result<u32, ParseError> {
1104 value.try_into().map_err(|_| self.error(format!("integer `{value}` does not fit in u32")))
1105 }
1106
1107 fn u256_to_u16(&self, value: U256) -> Result<u16, ParseError> {
1108 value.try_into().map_err(|_| self.error(format!("integer `{value}` does not fit in u16")))
1109 }
1110
1111 fn set_terminator(&self, func: &mut Function, block: BlockId, term: Terminator) {
1112 let succs = term.successors();
1114 for s in succs {
1115 func.blocks[s].predecessors.push(block);
1116 }
1117 func.blocks[block].terminator = Some(term);
1118 }
1119
1120 #[allow(clippy::too_many_lines)]
1123 fn parse_inst_kind(
1124 &mut self,
1125 mnemonic: &str,
1126 func: &mut Function,
1127 arg_values: &[ValueId],
1128 block_labels: &FxHashMap<u32, BlockId>,
1129 value_labels: &mut FxHashMap<u32, ValueId>,
1130 ) -> Result<(InstKind, Option<MirType>), ParseError> {
1131 macro_rules! v {
1133 () => {
1134 self.parse_value(func, arg_values, value_labels)?
1135 };
1136 }
1137 macro_rules! comma {
1138 () => {
1139 self.expect_punct(',')?
1140 };
1141 }
1142
1143 Ok(match mnemonic {
1144 "add" => {
1146 let a = v!();
1147 comma!();
1148 let b = v!();
1149 (InstKind::Add(a, b), Some(MirType::uint256()))
1150 }
1151 "sub" => {
1152 let a = v!();
1153 comma!();
1154 let b = v!();
1155 (InstKind::Sub(a, b), Some(MirType::uint256()))
1156 }
1157 "mul" => {
1158 let a = v!();
1159 comma!();
1160 let b = v!();
1161 (InstKind::Mul(a, b), Some(MirType::uint256()))
1162 }
1163 "div" => {
1164 let a = v!();
1165 comma!();
1166 let b = v!();
1167 (InstKind::Div(a, b), Some(MirType::uint256()))
1168 }
1169 "sdiv" => {
1170 let a = v!();
1171 comma!();
1172 let b = v!();
1173 (InstKind::SDiv(a, b), Some(MirType::int256()))
1174 }
1175 "mod" => {
1176 let a = v!();
1177 comma!();
1178 let b = v!();
1179 (InstKind::Mod(a, b), Some(MirType::uint256()))
1180 }
1181 "smod" => {
1182 let a = v!();
1183 comma!();
1184 let b = v!();
1185 (InstKind::SMod(a, b), Some(MirType::int256()))
1186 }
1187 "exp" => {
1188 let a = v!();
1189 comma!();
1190 let b = v!();
1191 (InstKind::Exp(a, b), Some(MirType::uint256()))
1192 }
1193 "addmod" => {
1194 let a = v!();
1195 comma!();
1196 let b = v!();
1197 comma!();
1198 let c = v!();
1199 (InstKind::AddMod(a, b, c), Some(MirType::uint256()))
1200 }
1201 "mulmod" => {
1202 let a = v!();
1203 comma!();
1204 let b = v!();
1205 comma!();
1206 let c = v!();
1207 (InstKind::MulMod(a, b, c), Some(MirType::uint256()))
1208 }
1209
1210 "and" => {
1212 let a = v!();
1213 comma!();
1214 let b = v!();
1215 (InstKind::And(a, b), Some(MirType::uint256()))
1216 }
1217 "or" => {
1218 let a = v!();
1219 comma!();
1220 let b = v!();
1221 (InstKind::Or(a, b), Some(MirType::uint256()))
1222 }
1223 "xor" => {
1224 let a = v!();
1225 comma!();
1226 let b = v!();
1227 (InstKind::Xor(a, b), Some(MirType::uint256()))
1228 }
1229 "not" => {
1230 let a = v!();
1231 (InstKind::Not(a), Some(MirType::uint256()))
1232 }
1233 "shl" => {
1234 let a = v!();
1235 comma!();
1236 let b = v!();
1237 (InstKind::Shl(a, b), Some(MirType::uint256()))
1238 }
1239 "shr" => {
1240 let a = v!();
1241 comma!();
1242 let b = v!();
1243 (InstKind::Shr(a, b), Some(MirType::uint256()))
1244 }
1245 "sar" => {
1246 let a = v!();
1247 comma!();
1248 let b = v!();
1249 (InstKind::Sar(a, b), Some(MirType::int256()))
1250 }
1251 "byte" => {
1252 let a = v!();
1253 comma!();
1254 let b = v!();
1255 (InstKind::Byte(a, b), Some(MirType::uint256()))
1256 }
1257 "signextend" => {
1258 let a = v!();
1259 comma!();
1260 let b = v!();
1261 (InstKind::SignExtend(a, b), Some(MirType::int256()))
1262 }
1263
1264 "lt" => {
1266 let a = v!();
1267 comma!();
1268 let b = v!();
1269 (InstKind::Lt(a, b), Some(MirType::Bool))
1270 }
1271 "gt" => {
1272 let a = v!();
1273 comma!();
1274 let b = v!();
1275 (InstKind::Gt(a, b), Some(MirType::Bool))
1276 }
1277 "slt" => {
1278 let a = v!();
1279 comma!();
1280 let b = v!();
1281 (InstKind::SLt(a, b), Some(MirType::Bool))
1282 }
1283 "sgt" => {
1284 let a = v!();
1285 comma!();
1286 let b = v!();
1287 (InstKind::SGt(a, b), Some(MirType::Bool))
1288 }
1289 "eq" => {
1290 let a = v!();
1291 comma!();
1292 let b = v!();
1293 (InstKind::Eq(a, b), Some(MirType::Bool))
1294 }
1295 "iszero" => {
1296 let a = v!();
1297 (InstKind::IsZero(a), Some(MirType::Bool))
1298 }
1299
1300 "mload" => {
1302 let a = v!();
1303 (InstKind::MLoad(a), Some(MirType::uint256()))
1304 }
1305 "mstore" => {
1306 let a = v!();
1307 comma!();
1308 let b = v!();
1309 (InstKind::MStore(a, b), None)
1310 }
1311 "mstore8" => {
1312 let a = v!();
1313 comma!();
1314 let b = v!();
1315 (InstKind::MStore8(a, b), None)
1316 }
1317 "msize" => (InstKind::MSize, Some(MirType::uint256())),
1318 "mcopy" => {
1319 let a = v!();
1320 comma!();
1321 let b = v!();
1322 comma!();
1323 let c = v!();
1324 (InstKind::MCopy(a, b, c), None)
1325 }
1326
1327 "sload" => {
1329 let a = v!();
1330 (InstKind::SLoad(a), Some(MirType::uint256()))
1331 }
1332 "sstore" => {
1333 let a = v!();
1334 comma!();
1335 let b = v!();
1336 (InstKind::SStore(a, b), None)
1337 }
1338 "tload" => {
1339 let a = v!();
1340 (InstKind::TLoad(a), Some(MirType::uint256()))
1341 }
1342 "tstore" => {
1343 let a = v!();
1344 comma!();
1345 let b = v!();
1346 (InstKind::TStore(a, b), None)
1347 }
1348
1349 "calldataload" => {
1351 let a = v!();
1352 (InstKind::CalldataLoad(a), Some(MirType::uint256()))
1353 }
1354 "calldatasize" => (InstKind::CalldataSize, Some(MirType::uint256())),
1355 "calldatacopy" => {
1356 let a = v!();
1357 comma!();
1358 let b = v!();
1359 comma!();
1360 let c = v!();
1361 (InstKind::CalldataCopy(a, b, c), None)
1362 }
1363
1364 "codesize" => (InstKind::CodeSize, Some(MirType::uint256())),
1366 "codecopy" => {
1367 let a = v!();
1368 comma!();
1369 let b = v!();
1370 comma!();
1371 let c = v!();
1372 (InstKind::CodeCopy(a, b, c), None)
1373 }
1374 "loadimmutable" => {
1375 let offset = self.parse_uint_literal()?;
1376 let offset = self.u256_to_u32(offset)?;
1377 (InstKind::LoadImmutable(offset), Some(MirType::uint256()))
1378 }
1379 "extcodesize" => {
1380 let a = v!();
1381 (InstKind::ExtCodeSize(a), Some(MirType::uint256()))
1382 }
1383 "extcodecopy" => {
1384 let a = v!();
1385 comma!();
1386 let b = v!();
1387 comma!();
1388 let c = v!();
1389 comma!();
1390 let d = v!();
1391 (InstKind::ExtCodeCopy(a, b, c, d), None)
1392 }
1393 "extcodehash" => {
1394 let a = v!();
1395 (InstKind::ExtCodeHash(a), Some(MirType::uint256()))
1396 }
1397
1398 "returndatasize" => (InstKind::ReturnDataSize, Some(MirType::uint256())),
1400 "returndatacopy" => {
1401 let a = v!();
1402 comma!();
1403 let b = v!();
1404 comma!();
1405 let c = v!();
1406 (InstKind::ReturnDataCopy(a, b, c), None)
1407 }
1408
1409 "caller" => (InstKind::Caller, Some(MirType::Address)),
1411 "callvalue" => (InstKind::CallValue, Some(MirType::uint256())),
1412 "origin" => (InstKind::Origin, Some(MirType::Address)),
1413 "gasprice" => (InstKind::GasPrice, Some(MirType::uint256())),
1414 "coinbase" => (InstKind::Coinbase, Some(MirType::Address)),
1415 "timestamp" => (InstKind::Timestamp, Some(MirType::uint256())),
1416 "number" => (InstKind::BlockNumber, Some(MirType::uint256())),
1417 "prevrandao" => (InstKind::PrevRandao, Some(MirType::uint256())),
1418 "gaslimit" => (InstKind::GasLimit, Some(MirType::uint256())),
1419 "chainid" => (InstKind::ChainId, Some(MirType::uint256())),
1420 "address" => (InstKind::Address, Some(MirType::Address)),
1421 "selfbalance" => (InstKind::SelfBalance, Some(MirType::uint256())),
1422 "gas" => (InstKind::Gas, Some(MirType::uint256())),
1423 "basefee" => (InstKind::BaseFee, Some(MirType::uint256())),
1424 "blobbasefee" => (InstKind::BlobBaseFee, Some(MirType::uint256())),
1425
1426 "blockhash" => {
1428 let a = v!();
1429 (InstKind::BlockHash(a), Some(MirType::FixedBytes(32)))
1430 }
1431 "balance" => {
1432 let a = v!();
1433 (InstKind::Balance(a), Some(MirType::uint256()))
1434 }
1435 "blobhash" => {
1436 let a = v!();
1437 (InstKind::BlobHash(a), Some(MirType::FixedBytes(32)))
1438 }
1439
1440 "keccak256" => {
1442 let a = v!();
1443 comma!();
1444 let b = v!();
1445 (InstKind::Keccak256(a, b), Some(MirType::bytes32()))
1446 }
1447
1448 "call" => {
1450 let gas = v!();
1451 comma!();
1452 let addr = v!();
1453 comma!();
1454 let value = v!();
1455 comma!();
1456 let args_offset = v!();
1457 comma!();
1458 let args_size = v!();
1459 comma!();
1460 let ret_offset = v!();
1461 comma!();
1462 let ret_size = v!();
1463 (
1464 InstKind::Call {
1465 gas,
1466 addr,
1467 value,
1468 args_offset,
1469 args_size,
1470 ret_offset,
1471 ret_size,
1472 },
1473 Some(MirType::uint256()),
1474 )
1475 }
1476 "staticcall" => {
1477 let gas = v!();
1478 comma!();
1479 let addr = v!();
1480 comma!();
1481 let args_offset = v!();
1482 comma!();
1483 let args_size = v!();
1484 comma!();
1485 let ret_offset = v!();
1486 comma!();
1487 let ret_size = v!();
1488 (
1489 InstKind::StaticCall {
1490 gas,
1491 addr,
1492 args_offset,
1493 args_size,
1494 ret_offset,
1495 ret_size,
1496 },
1497 Some(MirType::uint256()),
1498 )
1499 }
1500 "delegatecall" => {
1501 let gas = v!();
1502 comma!();
1503 let addr = v!();
1504 comma!();
1505 let args_offset = v!();
1506 comma!();
1507 let args_size = v!();
1508 comma!();
1509 let ret_offset = v!();
1510 comma!();
1511 let ret_size = v!();
1512 (
1513 InstKind::DelegateCall {
1514 gas,
1515 addr,
1516 args_offset,
1517 args_size,
1518 ret_offset,
1519 ret_size,
1520 },
1521 Some(MirType::uint256()),
1522 )
1523 }
1524 "internal_call" => {
1525 let function = self.parse_function_id()?;
1526 comma!();
1527 let returns = self.parse_uint_literal()?.to::<u32>();
1528 let mut args = Vec::new();
1529 while self.try_punct(',') {
1530 args.push(v!());
1531 }
1532 let result_ty = (returns > 0).then(MirType::uint256);
1533 (InstKind::InternalCall { function, args: args.into(), returns }, result_ty)
1534 }
1535 "internal_frame_addr" => {
1536 let offset = self.parse_uint_literal()?.to::<u64>();
1537 (InstKind::InternalFrameAddr(offset), Some(MirType::MemPtr))
1538 }
1539
1540 "create" => {
1542 let a = v!();
1543 comma!();
1544 let b = v!();
1545 comma!();
1546 let c = v!();
1547 (InstKind::Create(a, b, c), Some(MirType::Address))
1548 }
1549 "create2" => {
1550 let a = v!();
1551 comma!();
1552 let b = v!();
1553 comma!();
1554 let c = v!();
1555 comma!();
1556 let d = v!();
1557 (InstKind::Create2(a, b, c, d), Some(MirType::Address))
1558 }
1559
1560 "log0" => {
1562 let a = v!();
1563 comma!();
1564 let b = v!();
1565 (InstKind::Log0(a, b), None)
1566 }
1567 "log1" => {
1568 let a = v!();
1569 comma!();
1570 let b = v!();
1571 comma!();
1572 let c = v!();
1573 (InstKind::Log1(a, b, c), None)
1574 }
1575 "log2" => {
1576 let a = v!();
1577 comma!();
1578 let b = v!();
1579 comma!();
1580 let c = v!();
1581 comma!();
1582 let d = v!();
1583 (InstKind::Log2(a, b, c, d), None)
1584 }
1585 "log3" => {
1586 let a = v!();
1587 comma!();
1588 let b = v!();
1589 comma!();
1590 let c = v!();
1591 comma!();
1592 let d = v!();
1593 comma!();
1594 let e = v!();
1595 (InstKind::Log3(a, b, c, d, e), None)
1596 }
1597 "log4" => {
1598 let a = v!();
1599 comma!();
1600 let b = v!();
1601 comma!();
1602 let c = v!();
1603 comma!();
1604 let d = v!();
1605 comma!();
1606 let e = v!();
1607 comma!();
1608 let f = v!();
1609 (InstKind::Log4(a, b, c, d, e, f), None)
1610 }
1611
1612 "select" => {
1614 let cond = v!();
1615 comma!();
1616 let then_v = v!();
1617 comma!();
1618 let else_v = v!();
1619 (InstKind::Select(cond, then_v, else_v), Some(MirType::uint256()))
1620 }
1621 "phi" => {
1622 let mut incoming: Vec<(BlockId, ValueId)> = Vec::new();
1625 loop {
1626 self.expect_punct('[')?;
1627 let bid = self.parse_block_id(block_labels)?;
1628 self.expect_punct(':')?;
1629 let val = v!();
1630 self.expect_punct(']')?;
1631 incoming.push((bid, val));
1632 if !self.try_punct(',') {
1633 break;
1634 }
1635 }
1636 (InstKind::Phi(incoming), Some(MirType::uint256()))
1637 }
1638
1639 other => return Err(self.error(format!("unknown instruction `{other}`"))),
1640 })
1641 }
1642}
1643
1644#[allow(dead_code)]
1651const _BLOCK_TYPE_REFERENCE: Option<BasicBlock> = None;
1652
1653#[cfg(test)]
1658mod tests {
1659 use super::*;
1660 use solar_interface::{ColorChoice, Session};
1661
1662 fn with_session<F: FnOnce() + Send>(f: F) {
1663 let sess = Session::builder().with_buffer_emitter(ColorChoice::Never).build();
1664 sess.enter(f);
1665 }
1666
1667 #[test]
1668 fn parse_linear_function() {
1669 with_session(|| {
1670 let src = "\
1671fn @add(arg0: u256, arg1: u256) -> u256 {
1672 bb0 (entry):
1673 v2 = add arg0, arg1
1674 ret v2
1675}
1676";
1677 let func = parse_function(src).unwrap();
1678 assert_eq!(func.blocks.len(), 1);
1679 assert_eq!(func.params.len(), 2);
1680 assert_eq!(func.returns.len(), 1);
1681 let printed = func.to_text().to_string();
1683 let _func2 = parse_function(&printed).unwrap();
1684 });
1685 }
1686
1687 #[test]
1688 fn parse_storage_ops() {
1689 with_session(|| {
1690 let src = "\
1691fn @increment() {
1692 bb0 (entry):
1693 v0 = sload 0
1694 v1 = add v0, 1
1695 sstore 0, v1
1696 stop
1697}
1698";
1699 let func = parse_function(src).unwrap();
1700 assert_eq!(func.blocks.len(), 1);
1701 assert_eq!(func.params.len(), 0);
1702 assert_eq!(func.instructions.len(), 3);
1705 assert!(func.values.len() >= 4);
1707 });
1708 }
1709
1710 #[test]
1711 fn parse_branch() {
1712 with_session(|| {
1713 let src = "\
1714fn @max(arg0: u256, arg1: u256) -> u256 {
1715 bb0 (entry):
1716 v2 = gt arg0, arg1
1717 br v2, bb1, bb2
1718 bb1:
1719 ret arg0
1720 bb2:
1721 ret arg1
1722}
1723";
1724 let func = parse_function(src).unwrap();
1725 assert_eq!(func.blocks.len(), 3);
1726 assert_eq!(func.blocks[func.entry_block].terminator().unwrap().successors().len(), 2);
1728 });
1729 }
1730
1731 #[test]
1732 fn parse_loop_with_jump() {
1733 with_session(|| {
1734 let src = "\
1735fn @count_down(arg0: u256) -> u256 {
1736 bb0 (entry):
1737 jump bb1
1738 bb1:
1739 v1 = lt 0, arg0
1740 br v1, bb2, bb3
1741 bb2:
1742 jump bb1
1743 bb3:
1744 ret arg0
1745}
1746";
1747 let func = parse_function(src).unwrap();
1748 assert_eq!(func.blocks.len(), 4);
1749 });
1750 }
1751
1752 #[test]
1753 fn parse_call_instruction() {
1754 with_session(|| {
1755 let src = "\
1756fn @do_call(arg0: address, arg1: u256) -> u256 {
1757 bb0 (entry):
1758 v2 = call 100, arg0, arg1, 0, 0, 0, 0
1759 ret v2
1760}
1761";
1762 let func = parse_function(src).unwrap();
1763 assert_eq!(func.instructions.len(), 1);
1764 });
1765 }
1766
1767 #[test]
1768 fn parse_keccak_and_mload_mstore() {
1769 with_session(|| {
1770 let src = "\
1771fn @hash() -> u256 {
1772 bb0 (entry):
1773 mstore 0, 1
1774 mstore 32, 2
1775 v1 = keccak256 0, 64
1776 ret v1
1777}
1778";
1779 let func = parse_function(src).unwrap();
1780 assert_eq!(func.instructions.len(), 3);
1781 });
1782 }
1783
1784 #[test]
1785 fn parse_round_trip_module() {
1786 with_session(|| {
1787 let src = "\
1788; module @Counter
1789fn @count() -> u256 {
1790 bb0 (entry):
1791 v1 = sload 0
1792 ret v1
1793}
1794
1795fn @set(arg0: u256) {
1796 bb0 (entry):
1797 sstore 0, arg0
1798 stop
1799}
1800";
1801 let module = parse_module(src).unwrap();
1802 assert_eq!(module.functions.len(), 2);
1803 let printed = module.to_text().to_string();
1805 let module2 = parse_module(&printed).unwrap();
1806 assert_eq!(module2.functions.len(), 2);
1807 });
1808 }
1809
1810 #[test]
1811 fn parse_unknown_instruction_errors() {
1812 with_session(|| {
1813 let src = "\
1814fn @bad() {
1815 bb0 (entry):
1816 v1 = bogus arg0
1817 stop
1818}
1819";
1820 let err = parse_function(src).unwrap_err();
1821 assert!(err.msg.contains("bogus") || err.msg.contains("unknown"));
1822 });
1823 }
1824
1825 #[test]
1826 fn error_includes_source_snippet() {
1827 with_session(|| {
1828 let src = "\
1829fn @bad() {
1830 bb0 (entry):
1831 v1 = bogus arg0
1832 stop
1833}
1834";
1835 let err = parse_function(src).unwrap_err();
1836 assert!(err.line_text.contains("bogus arg0"), "got line_text: {:?}", err.line_text);
1838 let formatted = err.to_string();
1840 assert!(formatted.contains("bogus"), "missing line in:\n{formatted}");
1841 assert!(formatted.contains("|"), "missing snippet bar in:\n{formatted}");
1842 assert!(formatted.contains("^"), "missing caret in:\n{formatted}");
1843 assert!(formatted.contains(&format!("{} | ", err.line)), "missing line number");
1845 });
1846 }
1847
1848 #[test]
1849 fn error_snippet_format_is_clang_like() {
1850 with_session(|| {
1852 let src = "fn @x() -> notatype {\n bb0 (entry):\n stop\n}\n";
1853 let err = parse_function(src).unwrap_err();
1854 let formatted = err.to_string();
1855 assert!(formatted.starts_with("MIR parse error at line "));
1861 assert!(formatted.contains("\n |\n"));
1862 assert!(formatted.contains("notatype"));
1863 });
1864 }
1865
1866 #[test]
1867 fn parse_revert_terminator() {
1868 with_session(|| {
1869 let src = "\
1870fn @oops() {
1871 bb0 (entry):
1872 revert 0, 0
1873}
1874";
1875 let func = parse_function(src).unwrap();
1876 assert!(matches!(
1877 func.blocks[func.entry_block].terminator,
1878 Some(Terminator::Revert { .. })
1879 ));
1880 });
1881 }
1882
1883 #[test]
1884 fn parse_environment_nullary() {
1885 with_session(|| {
1886 let src = "\
1887fn @env() -> u256 {
1888 bb0 (entry):
1889 v0 = caller
1890 v1 = callvalue
1891 v2 = gas
1892 v3 = chainid
1893 ret v3
1894}
1895";
1896 let func = parse_function(src).unwrap();
1897 assert_eq!(func.instructions.len(), 4);
1898 });
1899 }
1900
1901 #[test]
1902 fn parse_select_and_logs() {
1903 with_session(|| {
1904 let src = "\
1905fn @sel(arg0: bool, arg1: u256, arg2: u256) -> u256 {
1906 bb0 (entry):
1907 v3 = select arg0, arg1, arg2
1908 log1 0, 32, v3
1909 ret v3
1910}
1911";
1912 let func = parse_function(src).unwrap();
1913 assert_eq!(func.instructions.len(), 2);
1914 });
1915 }
1916
1917 #[test]
1918 fn parse_phi_node() {
1919 with_session(|| {
1920 let src = "\
1921fn @diamond(arg0: bool) -> u256 {
1922 bb0 (entry):
1923 br arg0, bb1, bb2
1924 bb1:
1925 jump bb3
1926 bb2:
1927 jump bb3
1928 bb3:
1929 v1 = phi [bb1: 10], [bb2: 20]
1930 ret v1
1931}
1932";
1933 let func = parse_function(src).unwrap();
1934 assert_eq!(func.blocks.len(), 4);
1935 let phi_inst =
1937 func.instructions.iter().find(|i| matches!(i.kind, InstKind::Phi(_))).unwrap();
1938 if let InstKind::Phi(args) = &phi_inst.kind {
1939 assert_eq!(args.len(), 2);
1940 } else {
1941 panic!("expected phi");
1942 }
1943 });
1944 }
1945
1946 #[test]
1947 fn parse_switch_terminator() {
1948 with_session(|| {
1949 let src = "\
1950fn @dispatch(arg0: u256) -> u256 {
1951 bb0 (entry):
1952 switch arg0, default bb4, [1 => bb1, 2 => bb2, 3 => bb3]
1953 bb1:
1954 ret arg0
1955 bb2:
1956 ret 0
1957 bb3:
1958 ret 1
1959 bb4:
1960 ret 2
1961}
1962";
1963 let func = parse_function(src).unwrap();
1964 assert_eq!(func.blocks.len(), 5);
1965 let term = func.blocks[func.entry_block].terminator.as_ref().unwrap();
1966 if let Terminator::Switch { cases, .. } = term {
1967 assert_eq!(cases.len(), 3);
1968 } else {
1969 panic!("expected switch terminator");
1970 }
1971 });
1972 }
1973
1974 #[test]
1975 fn parse_phi_round_trip_with_printer() {
1976 with_session(|| {
1979 let src = "\
1980fn @diamond(arg0: bool) -> u256 {
1981 bb0 (entry):
1982 br arg0, bb1, bb2
1983 bb1:
1984 jump bb3
1985 bb2:
1986 jump bb3
1987 bb3:
1988 v1 = phi [bb1: 10], [bb2: 20]
1989 ret v1
1990}
1991";
1992 let func = parse_function(src).unwrap();
1993 let printed = func.to_text().to_string();
1994 assert!(
1996 printed.contains("phi [bb1:") && printed.contains("], [bb2:"),
1997 "expected `phi [bb1: ..], [bb2: ..]`, got:\n{printed}"
1998 );
1999 let _func2 = parse_function(&printed).unwrap();
2001 });
2002 }
2003}