1use std::fmt;
34
35use rucc_base::{Idx, Interner, Symbol};
36use rucc_diag::Span;
37use rucc_target::{TargetInfo, Triple};
38
39use crate::attrs::{AttrSet, Attrs, FpContract};
40use crate::func::Func;
41use crate::inst::{
42 Abi, AsmInfo, Block, BlockCall, CallInfo, Imm, Inst, InstData, MemInfo, Meta, MetaNode, Param,
43 Signature, SwitchInfo, Value,
44};
45use crate::module::{
46 Alias, AliasKind, DataLayout, Datum, Global, Linkage, Module, Reloc, TlsModel, Visibility,
47};
48use crate::{
49 Extra, ExtraKind, FORMAT_VERSION, Flags, FloatPred, IntPred, MemOrder, Opcode, RmwOp, Type,
50};
51
52#[derive(Clone, Debug, PartialEq, Eq)]
58pub struct ParseError {
59 pub line: u32,
61 pub message: String,
63}
64
65impl fmt::Display for ParseError {
66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 write!(f, "line {}: {}", self.line, self.message)
68 }
69}
70
71impl std::error::Error for ParseError {}
72
73pub fn parse(text: &str, names: &mut Interner) -> Result<Module, ParseError> {
82 Parser::new(text, names).module()
83}
84
85struct Parser<'a, 'n> {
87 text: &'a str,
88 pos: usize,
89 line: u32,
90 names: &'n mut Interner,
91 meta_used: Option<(u32, u32)>,
94}
95
96struct PendingBlock<'a> {
98 params: Vec<(u32, Type)>,
99 insts: Vec<PendingInst<'a>>,
100}
101
102struct PendingInst<'a> {
104 opcode: Opcode,
105 flags: Flags,
106 results: Vec<u32>,
107 written: Vec<Type>,
109 args: Vec<u32>,
110 extra: PendingExtra<'a>,
111 line: u32,
112}
113
114struct PendingCall {
116 block: u32,
117 args: Vec<u32>,
118}
119
120enum PendingExtra<'a> {
122 None,
123 Imm(&'a str),
125 Symbol(Symbol),
126 IntPred(IntPred),
127 FloatPred(FloatPred),
128 Mem(MemInfo),
129 Rmw(RmwOp, MemInfo),
130 Order(MemOrder),
131 Targets(Vec<PendingCall>),
132 Call {
133 callee: Option<Symbol>,
134 signature: Signature,
135 abis: Vec<Abi>,
138 },
139 Switch {
140 targets: Vec<PendingCall>,
141 cases: Vec<&'a str>,
142 },
143 Asm {
144 template: Symbol,
145 constraints: Symbol,
146 clobbers: Symbol,
147 targets: Vec<PendingCall>,
148 },
149}
150
151impl<'a, 'n> Parser<'a, 'n> {
152 fn new(text: &'a str, names: &'n mut Interner) -> Self {
153 Parser { text, pos: 0, line: 1, names, meta_used: None }
154 }
155
156 fn module(mut self) -> Result<Module, ParseError> {
159 let name = self.header_name()?;
160 self.expect("; format ")?;
161 let version = self.u32()?;
162 if version != FORMAT_VERSION {
163 return self.fail(format!(
164 "this build reads format {FORMAT_VERSION} and the text says format {version}"
165 ));
166 }
167 self.end_of_line()?;
168
169 self.expect("target triple = ")?;
170 let triple = self.quoted_str()?;
171 let Ok(triple) = triple.parse::<Triple>() else {
172 return self.fail(format!("`{triple}` is not a target triple"));
173 };
174 self.end_of_line()?;
175
176 self.expect("target datalayout = ")?;
177 let layout = self.quoted_str()?;
178 let Some(datalayout) = DataLayout::parse(&layout) else {
179 return self.fail(format!("`{layout}` is not a data layout"));
180 };
181 self.end_of_line()?;
182
183 let name = self.names.intern(&name);
184 let mut module = Module::new(name, &TargetInfo::new(triple));
185 module.datalayout = datalayout;
186
187 loop {
188 self.skip_blank_lines();
189 if self.at_end() {
190 break;
191 }
192 match self.peek_word() {
193 "global" => self.global(&mut module)?,
194 "alias" | "ifunc" => self.alias(&mut module)?,
195 "func" => self.func(&mut module)?,
196 _ if self.at("!") => self.meta(&mut module)?,
197 other => return self.fail(format!("`{other}` does not start anything")),
198 }
199 }
200
201 if let Some((used, line)) = self.meta_used {
202 let count = module.counts().metadata as u32;
203 if used >= count {
204 self.line = line;
205 return self.fail(format!("!{used} is used and never defined"));
206 }
207 }
208 Ok(module)
209 }
210
211 fn header_name(&mut self) -> Result<String, ParseError> {
213 self.expect("; ModuleID = '")?;
214 let start = self.pos;
215 let Some(end) = self.text[start..].find('\'') else {
216 return self.fail("the module name is not closed");
217 };
218 let name = self.text[start..start + end].to_string();
219 self.pos = start + end + 1;
220 self.end_of_line()?;
221 Ok(name)
222 }
223
224 fn global(&mut self, module: &mut Module) -> Result<(), ParseError> {
227 self.expect("global")?;
228 let name = self.symbol()?;
229 self.expect(":")?;
230
231 let mut global = Global::new(name, 0, 1);
232 if self.eat_word("bytes") {
233 global.size = self.u64()?;
234 if self.eat("=") {
235 self.expect("{")?;
236 let mut data = Vec::new();
237 if !self.eat("}") {
241 loop {
242 data.push(self.datum(module)?);
243 if !self.eat(",") {
244 break;
245 }
246 }
247 self.expect("}")?;
248 }
249 global.init = Some(module.push_data(&data));
250 }
251 } else {
252 let ty = self.ty()?;
253 self.expect("=")?;
254 let text = self.imm_text()?;
255 let value = self.imm(text, ty)?;
256 let datum = Datum::Scalar { ty, value: module.add_imm(value) };
257 global.size = datum.size(module);
258 global.init = Some(module.push_data(&[datum]));
259 }
260
261 while self.eat(",") {
262 match self.word() {
263 "align" => global.align = self.u32()?,
264 "linkage" => {
265 global.linkage = self.parenthesised(Linkage::from_name, "a linkage")?
266 }
267 "visibility" => {
268 global.visibility =
269 self.parenthesised(Visibility::from_name, "a visibility")?;
270 }
271 "tls" => {
272 global.tls =
273 Some(self.parenthesised(TlsModel::from_name, "a thread-local model")?);
274 }
275 "constant" => global.constant = true,
276 "section" => global.section = Some(self.symbol_from_string()?),
277 other => return self.fail(format!("a global has no `{other}`")),
278 }
279 }
280 self.end_of_line()?;
281 module.add_global(global);
282 Ok(())
283 }
284
285 fn datum(&mut self, module: &mut Module) -> Result<Datum, ParseError> {
287 match self.peek_word() {
288 "zero" => {
289 self.expect("zero")?;
290 Ok(Datum::Zero(self.u64()?))
291 }
292 "bytes" => {
293 self.expect("bytes")?;
294 let bytes = self.string()?;
295 Ok(Datum::Bytes(module.push_bytes(&bytes)))
296 }
297 "addr" => {
298 self.expect("addr")?;
299 self.expect(".")?;
300 let size = self.u32()?;
301 let symbol = self.symbol()?;
302 let addend = if self.eat("+") {
303 self.i64()?
304 } else if self.eat("-") {
305 let amount = self.i64()?;
306 match amount.checked_neg() {
307 Some(negated) => negated,
308 None => return self.fail("that offset has no negative"),
309 }
310 } else {
311 0
312 };
313 Ok(Datum::Addr(module.add_reloc(Reloc { symbol, addend, size })))
314 }
315 _ => {
316 let ty = self.ty()?;
317 let text = self.imm_text()?;
318 let value = self.imm(text, ty)?;
319 Ok(Datum::Scalar { ty, value: module.add_imm(value) })
320 }
321 }
322 }
323
324 fn alias(&mut self, module: &mut Module) -> Result<(), ParseError> {
325 let word = self.word();
326 let Some(kind) = AliasKind::from_name(word) else {
327 return self.fail(format!("`{word}` is not a kind of alias"));
328 };
329 let name = self.symbol()?;
330 self.expect("=")?;
331 let target = self.symbol()?;
332 let mut alias = Alias::new(name, target);
333 alias.kind = kind;
334 while self.eat(",") {
335 match self.word() {
336 "linkage" => alias.linkage = self.parenthesised(Linkage::from_name, "a linkage")?,
337 "visibility" => {
338 alias.visibility = self.parenthesised(Visibility::from_name, "a visibility")?;
339 }
340 other => return self.fail(format!("an alias has no `{other}`")),
341 }
342 }
343 self.end_of_line()?;
344 module.add_alias(alias);
345 Ok(())
346 }
347
348 fn meta(&mut self, module: &mut Module) -> Result<(), ParseError> {
349 let index = self.meta_ref()?;
350 let expected = module.counts().metadata as u32;
351 if index.raw() != expected {
352 return self.fail(format!("metadata is numbered in order and !{expected} comes next"));
353 }
354 self.expect("=")?;
355 self.expect("tbaa")?;
356 let name = self.symbol_from_string()?;
357 let mut node = MetaNode { name, parent: None, offset: 0 };
358 while self.eat(",") {
359 match self.word() {
360 "parent" => node.parent = Some(self.meta_ref()?),
361 "offset" => node.offset = self.u64()?,
362 other => return self.fail(format!("a metadata node has no `{other}`")),
363 }
364 }
365 if node.parent.is_some_and(|parent| parent.raw() >= index.raw()) {
366 return self.fail("a metadata node's parent comes before it");
367 }
368 self.end_of_line()?;
369 module.add_meta(node);
370 Ok(())
371 }
372
373 fn func(&mut self, module: &mut Module) -> Result<(), ParseError> {
376 self.expect("func")?;
377 let name = self.symbol()?;
378 let signature = self.signature()?;
379 let mut func = Func::new(name, signature);
380 while self.eat(",") {
381 match self.word() {
382 "linkage" => func.linkage = self.parenthesised(Linkage::from_name, "a linkage")?,
383 "visibility" => {
384 func.visibility = self.parenthesised(Visibility::from_name, "a visibility")?;
385 }
386 "attrs" => func.attrs = self.attrs()?,
387 "section" => func.section = Some(self.symbol_from_string()?),
388 other => return self.fail(format!("a function has no `{other}`")),
389 }
390 }
391 if self.eat(";") {
392 self.end_of_line()?;
393 module.add_func(func);
394 return Ok(());
395 }
396 self.expect("{")?;
397 self.end_of_line()?;
398 let blocks = self.body()?;
399 self.build(&mut func, &blocks)?;
400 module.add_func(func);
401 Ok(())
402 }
403
404 fn signature(&mut self) -> Result<Signature, ParseError> {
406 self.expect("(")?;
407 let mut signature = Signature::new();
408 if !self.eat(")") {
409 loop {
410 if self.eat("...") {
411 signature.variadic = true;
412 break;
413 }
414 signature.params.push(self.param()?);
415 if !self.eat(",") {
416 break;
417 }
418 }
419 self.expect(")")?;
420 }
421 if self.eat("->") {
422 if self.eat("(") {
423 loop {
424 signature.returns.push(self.param()?);
425 if !self.eat(",") {
426 break;
427 }
428 }
429 self.expect(")")?;
430 } else {
431 signature.returns.push(self.param()?);
432 }
433 }
434 Ok(signature)
435 }
436
437 fn param(&mut self) -> Result<Param, ParseError> {
439 let ty = self.ty()?;
440 let abi = self.abi()?;
441 Ok(Param { ty, abi })
442 }
443
444 fn abi(&mut self) -> Result<Abi, ParseError> {
446 let abi = match self.peek_word() {
447 "sext" => {
448 self.word();
449 Abi::Sext
450 }
451 "zext" => {
452 self.word();
453 Abi::Zext
454 }
455 word @ ("byval" | "sret") => {
456 let indirect = word == "byval";
457 self.word();
458 self.expect("(")?;
459 let size = self.u64()?;
460 self.expect(",")?;
461 self.expect("align")?;
462 let align = self.u32()?;
463 self.expect(")")?;
464 if indirect { Abi::ByVal { size, align } } else { Abi::Sret { size, align } }
465 }
466 _ => Abi::Plain,
467 };
468 Ok(abi)
469 }
470
471 fn attrs(&mut self) -> Result<Attrs, ParseError> {
472 self.expect("(")?;
473 let mut attrs = Attrs::NONE;
474 loop {
475 let word = self.word();
476 if self.eat("=") {
477 let value = self.word();
478 match word {
479 "fp_contract" => match FpContract::from_name(value) {
480 Some(contract) => attrs.fp_contract = contract,
481 None => return self.fail(format!("`{value}` is not a contraction")),
482 },
483 other => return self.fail(format!("`{other}` takes no value")),
484 }
485 } else {
486 match AttrSet::from_name(word) {
487 Some(attr) => attrs.set |= attr,
488 None => return self.fail(format!("`{word}` is not an attribute")),
489 }
490 }
491 if !self.eat(",") {
492 break;
493 }
494 }
495 self.expect(")")?;
496 Ok(attrs)
497 }
498
499 fn body(&mut self) -> Result<Vec<PendingBlock<'a>>, ParseError> {
501 let mut blocks: Vec<PendingBlock<'a>> = Vec::new();
502 loop {
503 self.skip_blank_lines();
504 if self.eat("}") {
505 self.end_of_line()?;
506 return Ok(blocks);
507 }
508 if self.at_end() {
509 return self.fail("the function is not closed");
510 }
511 if self.peek_word().starts_with("block") {
512 let number = self.block_ref()?;
513 if number as usize != blocks.len() {
514 return self.fail(format!(
515 "blocks are numbered in order and block{} comes next",
516 blocks.len()
517 ));
518 }
519 let mut params = Vec::new();
520 if self.eat("(") {
521 loop {
522 let value = self.value_ref()?;
523 self.expect(":")?;
524 params.push((value, self.ty()?));
525 if !self.eat(",") {
526 break;
527 }
528 }
529 self.expect(")")?;
530 }
531 self.expect(":")?;
532 self.end_of_line()?;
533 blocks.push(PendingBlock { params, insts: Vec::new() });
534 continue;
535 }
536 let inst = self.inst()?;
537 match blocks.last_mut() {
538 Some(block) => block.insts.push(inst),
539 None => return self.fail("an instruction before any block"),
540 }
541 }
542 }
543
544 fn inst(&mut self) -> Result<PendingInst<'a>, ParseError> {
546 let line = self.line;
547 let mut results = Vec::new();
548 if self.peek_is("%") {
549 results = self.value_list()?;
550 self.expect("=")?;
551 }
552
553 let word = self.word();
554 let Some(opcode) = Opcode::from_name(word) else {
555 return self.fail(format!("`{word}` is not an opcode"));
556 };
557 let (written, flags) = self.suffixes()?;
558
559 let mut args = Vec::new();
560 let extra = match opcode.extra_kind() {
561 ExtraKind::None => {
562 args = self.value_list()?;
563 PendingExtra::None
564 }
565 ExtraKind::Imm => PendingExtra::Imm(self.imm_text()?),
566 ExtraKind::Symbol => {
567 let symbol = self.symbol()?;
568 if self.eat("(") {
569 args = self.value_list()?;
570 self.expect(")")?;
571 }
572 PendingExtra::Symbol(symbol)
573 }
574 ExtraKind::IntPred => {
575 let word = self.word();
576 let Some(pred) = IntPred::from_name(word) else {
577 return self.fail(format!("`{word}` is not an integer comparison"));
578 };
579 args = self.value_list()?;
580 PendingExtra::IntPred(pred)
581 }
582 ExtraKind::FloatPred => {
583 let word = self.word();
584 let Some(pred) = FloatPred::from_name(word) else {
585 return self.fail(format!("`{word}` is not a floating point comparison"));
586 };
587 args = self.value_list()?;
588 PendingExtra::FloatPred(pred)
589 }
590 ExtraKind::Mem => {
591 if matches!(opcode, Opcode::Store | Opcode::AtomicStore) {
592 let value = self.value_ref()?;
594 self.expect("->")?;
595 args = vec![value, self.value_ref()?];
596 } else {
597 args = self.value_list()?;
598 }
599 PendingExtra::Mem(self.mem()?)
600 }
601 ExtraKind::Rmw => {
602 let word = self.word();
603 let Some(op) = RmwOp::from_name(word) else {
604 return self.fail(format!("`{word}` is not a read-modify-write"));
605 };
606 args = self.value_list()?;
607 PendingExtra::Rmw(op, self.mem()?)
608 }
609 ExtraKind::Order => {
610 let word = self.word();
611 let Some(order) = MemOrder::from_name(word) else {
612 return self.fail(format!("`{word}` is not an ordering"));
613 };
614 PendingExtra::Order(order)
615 }
616 ExtraKind::Targets => {
617 args = self.value_list()?;
618 if !args.is_empty() {
619 self.expect(",")?;
620 }
621 let mut targets = Vec::new();
622 loop {
623 targets.push(self.block_call()?);
624 if !self.eat(",") {
625 break;
626 }
627 }
628 PendingExtra::Targets(targets)
629 }
630 ExtraKind::Call => {
631 let callee = if self.peek_is("@") {
632 Some(self.symbol()?)
633 } else {
634 args.push(self.value_ref()?);
635 None
636 };
637 self.expect("(")?;
638 let (values, abis) = self.call_args()?;
639 args.extend(values);
640 self.expect(")")?;
641 self.expect(":")?;
642 PendingExtra::Call { callee, signature: self.signature()?, abis }
643 }
644 ExtraKind::Switch => {
645 args = self.value_list()?;
646 let mut targets = Vec::new();
647 let mut cases = Vec::new();
648 if self.eat(",") {
649 targets.push(self.block_call()?);
650 self.expect(",")?;
651 self.expect("[")?;
652 if !self.eat("]") {
653 loop {
654 cases.push(self.imm_text()?);
655 self.expect("=>")?;
656 targets.push(self.block_call()?);
657 if !self.eat(",") {
658 break;
659 }
660 }
661 self.expect("]")?;
662 }
663 }
664 PendingExtra::Switch { targets, cases }
665 }
666 ExtraKind::Asm => {
667 let template = self.symbol_from_string()?;
668 self.expect(",")?;
669 let constraints = self.symbol_from_string()?;
670 self.expect(",")?;
671 let clobbers = self.symbol_from_string()?;
672 self.expect("(")?;
673 args = self.value_list()?;
674 self.expect(")")?;
675 let mut targets = Vec::new();
676 if self.eat(",") {
677 self.expect("labels")?;
678 self.expect("[")?;
679 loop {
680 targets.push(self.block_call()?);
681 if !self.eat(",") {
682 break;
683 }
684 }
685 self.expect("]")?;
686 }
687 PendingExtra::Asm { template, constraints, clobbers, targets }
688 }
689 };
690 self.end_of_line()?;
691 Ok(PendingInst { opcode, flags, results, written, args, extra, line })
692 }
693
694 fn suffixes(&mut self) -> Result<(Vec<Type>, Flags), ParseError> {
696 let mut written = Vec::new();
697 let mut flags = Flags::NONE;
698 while self.at(".") {
699 self.pos += 1;
700 if self.at("(") {
701 self.pos += 1;
702 loop {
703 written.push(self.ty()?);
704 if !self.eat(",") {
705 break;
706 }
707 }
708 self.expect(")")?;
709 continue;
710 }
711 let word = self.glued_word();
712 if let Some(flag) = Flags::from_name(word) {
713 flags |= flag;
714 } else if let Some(ty) = Type::parse(word) {
715 written.push(ty);
716 } else {
717 return self.fail(format!("`{word}` is neither a type nor a flag"));
718 }
719 }
720 Ok((written, flags))
721 }
722
723 fn mem(&mut self) -> Result<MemInfo, ParseError> {
725 let mut info = MemInfo { size: 0, align: 1, order: MemOrder::NotAtomic, tbaa: None };
726 while self.eat(",") {
727 let word = self.word();
728 match word {
729 "size" => info.size = self.u64()?,
730 "align" => info.align = self.u32()?,
731 "tbaa" => info.tbaa = Some(self.meta_ref()?),
732 _ => match MemOrder::from_name(word) {
733 Some(order) => info.order = order,
734 None => return self.fail(format!("an access has no `{word}`")),
735 },
736 }
737 }
738 Ok(info)
739 }
740
741 fn block_call(&mut self) -> Result<PendingCall, ParseError> {
743 let block = self.block_ref()?;
744 let mut args = Vec::new();
745 if self.eat("(") {
746 args = self.value_list()?;
747 self.expect(")")?;
748 }
749 Ok(PendingCall { block, args })
750 }
751
752 fn build(&mut self, func: &mut Func, blocks: &[PendingBlock<'a>]) -> Result<(), ParseError> {
756 let count = self.check_numbering(blocks)?;
757 let types = self.value_types(blocks, count)?;
758
759 let mut next = 0;
760 for pending in blocks {
761 let block = func.create_block();
762 for &(number, ty) in &pending.params {
763 if number != next {
764 self.line = self.line_of(blocks, number);
765 return self
766 .fail(format!("values are numbered in order and %{next} comes next"));
767 }
768 func.append_param(block, ty);
769 next += 1;
770 }
771 for inst in &pending.insts {
772 if inst.results.first().is_some_and(|&first| first != next) {
773 self.line = inst.line;
774 return self
775 .fail(format!("values are numbered in order and %{next} comes next"));
776 }
777 let built = self.build_inst(func, inst, &types)?;
778 func.append_inst(block, built);
779 next += inst.results.len() as u32;
780 }
781 }
782 Ok(())
783 }
784
785 fn check_numbering(&mut self, blocks: &[PendingBlock<'a>]) -> Result<u32, ParseError> {
789 let mut count = 0;
790 for pending in blocks {
791 count += pending.params.len();
792 for inst in &pending.insts {
793 count += inst.results.len();
794 }
795 }
796 let count = count as u32;
797 let total = blocks.len() as u32;
798 for pending in blocks {
799 for inst in &pending.insts {
800 self.line = inst.line;
801 for &arg in &inst.args {
802 if arg >= count {
803 return self.fail(format!("%{arg} is used and never defined"));
804 }
805 }
806 for call in inst_calls(inst) {
807 if call.block >= total {
808 return self.fail(format!("block{} is used and never defined", call.block));
809 }
810 for &arg in &call.args {
811 if arg >= count {
812 return self.fail(format!("%{arg} is used and never defined"));
813 }
814 }
815 }
816 }
817 }
818 Ok(count)
819 }
820
821 fn value_types(
828 &mut self,
829 blocks: &[PendingBlock<'a>],
830 count: u32,
831 ) -> Result<Vec<Option<Type>>, ParseError> {
832 let mut types = vec![None; count as usize];
833 for pending in blocks {
834 for &(number, ty) in &pending.params {
835 types[number as usize] = Some(ty);
836 }
837 }
838 loop {
839 let mut progress = false;
840 for pending in blocks {
841 for inst in &pending.insts {
842 let Some(&first) = inst.results.first() else { continue };
843 if types[first as usize].is_some() {
844 continue;
845 }
846 let Some(resolved) = result_types(inst, &types) else { continue };
847 if resolved.len() != inst.results.len() {
848 self.line = inst.line;
849 return self.fail(format!(
850 "{} produces {} values and the text names {}",
851 inst.opcode.name(),
852 resolved.len(),
853 inst.results.len()
854 ));
855 }
856 for (&number, ty) in inst.results.iter().zip(resolved) {
857 types[number as usize] = Some(ty);
858 }
859 progress = true;
860 }
861 }
862 if !progress {
863 break;
864 }
865 }
866 for pending in blocks {
867 for inst in &pending.insts {
868 if inst.results.first().is_some_and(|&first| types[first as usize].is_none()) {
869 self.line = inst.line;
870 return self.fail(format!(
871 "nothing in the text says what {} produces",
872 inst.opcode.name()
873 ));
874 }
875 }
876 }
877 Ok(types)
878 }
879
880 fn build_inst(
881 &mut self,
882 func: &mut Func,
883 pending: &PendingInst<'a>,
884 types: &[Option<Type>],
885 ) -> Result<Inst, ParseError> {
886 let results: Vec<Type> = pending
887 .results
888 .iter()
889 .map(|&number| types[number as usize].unwrap_or(Type::VOID))
890 .collect();
891 let args: Vec<Value> =
892 pending.args.iter().map(|&number| Value::from_usize(number as usize)).collect();
893 let arg_list = func.push_values(&args);
894
895 let extra = match &pending.extra {
896 PendingExtra::None => Extra::None,
897 PendingExtra::Imm(text) => {
898 let ty = results.first().copied().unwrap_or(Type::VOID);
899 self.line = pending.line;
900 let imm = self.imm(text, ty)?;
901 Extra::Imm(func.add_imm(imm))
902 }
903 PendingExtra::Symbol(symbol) => Extra::Symbol(*symbol),
904 PendingExtra::IntPred(pred) => Extra::IntPred(*pred),
905 PendingExtra::FloatPred(pred) => Extra::FloatPred(*pred),
906 PendingExtra::Mem(info) => Extra::Mem(func.add_mem(*info)),
907 PendingExtra::Rmw(op, info) => Extra::Rmw(*op, func.add_mem(*info)),
908 PendingExtra::Order(order) => Extra::Order(*order),
909 PendingExtra::Targets(targets) => {
910 let calls = build_calls(func, targets);
911 Extra::Targets(func.push_block_calls(&calls))
912 }
913 PendingExtra::Call { callee, signature, abis } => {
914 let named = signature.params.len();
915 if abis.iter().take(named).any(|&abi| abi != Abi::Plain) {
916 self.line = pending.line;
917 return self.fail(
918 "the signature is what says how an argument it names travels".to_string(),
919 );
920 }
921 let varargs = match abis.get(named.min(abis.len())..) {
922 Some(rest) if rest.iter().any(|&abi| abi != Abi::Plain) => rest,
923 _ => &[],
924 };
925 let varargs = func.push_abis(varargs);
926 let sig = func.add_signature(signature.clone());
927 Extra::Call(func.add_call(CallInfo { callee: *callee, signature: sig, varargs }))
928 }
929 PendingExtra::Switch { targets, cases } => {
930 let ty = pending
931 .args
932 .first()
933 .and_then(|&number| types[number as usize])
934 .unwrap_or(Type::VOID);
935 self.line = pending.line;
936 let mut imms = Vec::with_capacity(cases.len());
937 for case in cases {
938 imms.push(self.imm(case, ty)?);
939 }
940 let calls = build_calls(func, targets);
941 let targets = func.push_block_calls(&calls);
942 let cases = func.push_imms(&imms);
943 Extra::Switch(func.add_switch(SwitchInfo { targets, cases }))
944 }
945 PendingExtra::Asm { template, constraints, clobbers, targets } => {
946 let calls = build_calls(func, targets);
947 let targets = func.push_block_calls(&calls);
948 Extra::Asm(func.add_asm(AsmInfo {
949 template: *template,
950 constraints: *constraints,
951 clobbers: *clobbers,
952 targets,
953 }))
954 }
955 };
956
957 let data = InstData {
958 opcode: pending.opcode,
959 flags: pending.flags,
960 args: arg_list,
961 extra,
962 ..InstData::new(pending.opcode)
963 };
964 Ok(func.create_inst(data, &results, Span::DUMMY))
965 }
966
967 fn line_of(&self, blocks: &[PendingBlock<'a>], number: u32) -> u32 {
969 for pending in blocks {
970 for inst in &pending.insts {
971 if inst.results.contains(&number) {
972 return inst.line;
973 }
974 }
975 }
976 self.line
977 }
978
979 fn imm(&mut self, text: &str, ty: Type) -> Result<Imm, ParseError> {
983 let scalar = if ty.is_vector() { ty.lane() } else { ty };
984 if scalar.is_int() {
985 match parse_i128(text) {
986 Some(value) => Ok(Imm::int(value, scalar)),
987 None => self.fail(format!("`{text}` is not an {scalar}")),
988 }
989 } else {
990 match text.strip_prefix("0x").and_then(|rest| u128::from_str_radix(rest, 16).ok()) {
991 Some(bits) => Ok(Imm::from_bits(bits)),
992 None => self.fail(format!("`{text}` is not the bits of a {scalar}")),
993 }
994 }
995 }
996
997 fn imm_text(&mut self) -> Result<&'a str, ParseError> {
999 self.spaces();
1000 let start = self.pos;
1001 if self.at("-") {
1002 self.pos += 1;
1003 }
1004 while self.peek().is_some_and(|byte| byte.is_ascii_alphanumeric()) {
1005 self.pos += 1;
1006 }
1007 if self.pos == start {
1008 return self.fail("a constant was expected");
1009 }
1010 Ok(&self.text[start..self.pos])
1011 }
1012
1013 fn ty(&mut self) -> Result<Type, ParseError> {
1014 let word = self.word();
1015 match Type::parse(word) {
1016 Some(ty) => Ok(ty),
1017 None => self.fail(format!("`{word}` is not a type")),
1018 }
1019 }
1020
1021 fn value_ref(&mut self) -> Result<u32, ParseError> {
1023 self.expect("%")?;
1024 self.u32()
1025 }
1026
1027 fn value_list(&mut self) -> Result<Vec<u32>, ParseError> {
1029 let mut values = Vec::new();
1030 if !self.peek_is("%") {
1031 return Ok(values);
1032 }
1033 loop {
1034 values.push(self.value_ref()?);
1035 let save = self.pos;
1036 if !self.eat(",") {
1037 return Ok(values);
1038 }
1039 if !self.peek_is("%") {
1040 self.pos = save;
1041 return Ok(values);
1042 }
1043 }
1044 }
1045
1046 fn call_args(&mut self) -> Result<(Vec<u32>, Vec<Abi>), ParseError> {
1049 let mut values = Vec::new();
1050 let mut abis = Vec::new();
1051 if !self.peek_is("%") {
1052 return Ok((values, abis));
1053 }
1054 loop {
1055 values.push(self.value_ref()?);
1056 abis.push(self.abi()?);
1057 let save = self.pos;
1058 if !self.eat(",") {
1059 return Ok((values, abis));
1060 }
1061 if !self.peek_is("%") {
1062 self.pos = save;
1063 return Ok((values, abis));
1064 }
1065 }
1066 }
1067
1068 fn block_ref(&mut self) -> Result<u32, ParseError> {
1070 let word = self.word();
1071 match word.strip_prefix("block").and_then(parse_u32) {
1072 Some(number) => Ok(number),
1073 None => self.fail(format!("`{word}` is not a block")),
1074 }
1075 }
1076
1077 fn meta_ref(&mut self) -> Result<Meta, ParseError> {
1079 self.expect("!")?;
1080 let index = self.u32()?;
1081 let seen = self.meta_used.is_none_or(|(used, _)| index > used);
1082 if seen {
1083 self.meta_used = Some((index, self.line));
1084 }
1085 Ok(Idx::from_usize(index as usize))
1086 }
1087
1088 fn symbol(&mut self) -> Result<Symbol, ParseError> {
1090 self.expect("@")?;
1091 let start = self.pos;
1092 while self.peek().is_some_and(is_name_byte) {
1093 self.pos += 1;
1094 }
1095 if self.pos == start {
1096 return self.fail("a name was expected");
1097 }
1098 let name = &self.text[start..self.pos];
1099 Ok(self.names.intern(name))
1100 }
1101
1102 fn symbol_from_string(&mut self) -> Result<Symbol, ParseError> {
1104 let text = self.quoted_str()?;
1105 Ok(self.names.intern(&text))
1106 }
1107
1108 fn quoted_str(&mut self) -> Result<String, ParseError> {
1110 let bytes = self.string()?;
1111 match String::from_utf8(bytes) {
1112 Ok(text) => Ok(text),
1113 Err(_) => self.fail("that string is not text"),
1114 }
1115 }
1116
1117 fn string(&mut self) -> Result<Vec<u8>, ParseError> {
1119 self.expect("\"")?;
1120 let mut bytes = Vec::new();
1121 loop {
1122 let Some(byte) = self.peek() else {
1123 return self.fail("that string is not closed");
1124 };
1125 self.pos += 1;
1126 match byte {
1127 b'"' => return Ok(bytes),
1128 b'\n' => return self.fail("that string is not closed"),
1129 b'\\' => {
1130 let escape = match self.peek() {
1131 Some(b'"') => b'"',
1132 Some(b'\\') => b'\\',
1133 _ => {
1134 let hex = self.text.get(self.pos..self.pos + 2);
1135 match hex.and_then(|hex| u8::from_str_radix(hex, 16).ok()) {
1136 Some(byte) => {
1137 self.pos += 2;
1138 bytes.push(byte);
1139 continue;
1140 }
1141 None => return self.fail("that is not an escape"),
1142 }
1143 }
1144 };
1145 self.pos += 1;
1146 bytes.push(escape);
1147 }
1148 _ => bytes.push(byte),
1149 }
1150 }
1151 }
1152
1153 fn u32(&mut self) -> Result<u32, ParseError> {
1154 let word = self.word();
1155 match parse_u32(word) {
1156 Some(number) => Ok(number),
1157 None => self.fail(format!("`{word}` is not a number")),
1158 }
1159 }
1160
1161 fn u64(&mut self) -> Result<u64, ParseError> {
1162 let word = self.word();
1163 match parse_u64(word) {
1164 Some(number) => Ok(number),
1165 None => self.fail(format!("`{word}` is not a number")),
1166 }
1167 }
1168
1169 fn i64(&mut self) -> Result<i64, ParseError> {
1170 let word = self.word();
1171 match parse_u64(word).and_then(|number| i64::try_from(number).ok()) {
1172 Some(number) => Ok(number),
1173 None => self.fail(format!("`{word}` is not an offset")),
1174 }
1175 }
1176
1177 fn parenthesised<T>(
1179 &mut self,
1180 from_name: impl Fn(&str) -> Option<T>,
1181 what: &str,
1182 ) -> Result<T, ParseError> {
1183 self.expect("(")?;
1184 let word = self.word();
1185 let Some(value) = from_name(word) else {
1186 return self.fail(format!("`{word}` is not {what}"));
1187 };
1188 self.expect(")")?;
1189 Ok(value)
1190 }
1191
1192 fn fail<T>(&self, message: impl Into<String>) -> Result<T, ParseError> {
1195 Err(ParseError { line: self.line, message: message.into() })
1196 }
1197
1198 fn peek(&self) -> Option<u8> {
1199 self.text.as_bytes().get(self.pos).copied()
1200 }
1201
1202 fn at(&self, text: &str) -> bool {
1203 self.text[self.pos..].starts_with(text)
1204 }
1205
1206 fn at_end(&self) -> bool {
1207 self.pos >= self.text.len()
1208 }
1209
1210 fn peek_is(&self, text: &str) -> bool {
1212 self.text[self.pos..].trim_start_matches([' ', '\t']).starts_with(text)
1213 }
1214
1215 fn spaces(&mut self) {
1216 while matches!(self.peek(), Some(b' ' | b'\t')) {
1217 self.pos += 1;
1218 }
1219 }
1220
1221 fn eat(&mut self, text: &str) -> bool {
1222 self.spaces();
1223 if self.at(text) {
1224 self.pos += text.len();
1225 return true;
1226 }
1227 false
1228 }
1229
1230 fn expect(&mut self, text: &str) -> Result<(), ParseError> {
1231 if self.eat(text) {
1232 return Ok(());
1233 }
1234 self.fail(format!("`{text}` was expected"))
1235 }
1236
1237 fn word(&mut self) -> &'a str {
1239 self.spaces();
1240 self.glued_word()
1241 }
1242
1243 fn glued_word(&mut self) -> &'a str {
1245 let start = self.pos;
1246 while self.peek().is_some_and(|byte| byte.is_ascii_alphanumeric() || byte == b'_') {
1247 self.pos += 1;
1248 }
1249 &self.text[start..self.pos]
1250 }
1251
1252 fn peek_word(&self) -> &'a str {
1254 let rest = self.text[self.pos..].trim_start_matches([' ', '\t']);
1255 let end =
1256 rest.find(|c: char| !(c.is_ascii_alphanumeric() || c == '_')).unwrap_or(rest.len());
1257 &rest[..end]
1258 }
1259
1260 fn eat_word(&mut self, word: &str) -> bool {
1262 if self.peek_word() == word {
1263 self.word();
1264 return true;
1265 }
1266 false
1267 }
1268
1269 fn end_of_line(&mut self) -> Result<(), ParseError> {
1270 self.spaces();
1271 if self.at_end() {
1272 return Ok(());
1273 }
1274 if self.at("\n") {
1275 self.pos += 1;
1276 self.line += 1;
1277 return Ok(());
1278 }
1279 let rest = &self.text[self.pos..];
1280 let end = rest.find('\n').unwrap_or(rest.len());
1281 self.fail(format!("`{}` is left over at the end of the line", &rest[..end]))
1282 }
1283
1284 fn skip_blank_lines(&mut self) {
1285 loop {
1286 let save = self.pos;
1287 self.spaces();
1288 if self.at("\n") {
1289 self.pos += 1;
1290 self.line += 1;
1291 } else {
1292 self.pos = save;
1293 return;
1294 }
1295 }
1296 }
1297}
1298
1299fn inst_calls<'p>(inst: &'p PendingInst<'_>) -> &'p [PendingCall] {
1301 match &inst.extra {
1302 PendingExtra::Targets(targets)
1303 | PendingExtra::Switch { targets, .. }
1304 | PendingExtra::Asm { targets, .. } => targets,
1305 _ => &[],
1306 }
1307}
1308
1309fn build_calls(func: &mut Func, targets: &[PendingCall]) -> Vec<BlockCall> {
1311 targets
1312 .iter()
1313 .map(|call| {
1314 let args: Vec<Value> =
1315 call.args.iter().map(|&number| Value::from_usize(number as usize)).collect();
1316 BlockCall {
1317 block: Block::from_usize(call.block as usize),
1318 args: func.push_values(&args),
1319 }
1320 })
1321 .collect()
1322}
1323
1324fn result_types(inst: &PendingInst<'_>, types: &[Option<Type>]) -> Option<Vec<Type>> {
1329 if inst.results.is_empty() {
1330 return Some(Vec::new());
1331 }
1332 if !inst.written.is_empty() {
1333 return Some(inst.written.clone());
1334 }
1335 match inst.opcode {
1336 Opcode::GlobalAddr | Opcode::BlockAddr | Opcode::Alloca => Some(vec![Type::PTR]),
1337 Opcode::ICmp | Opcode::FCmp => {
1338 let ty = arg_type(inst, types)?;
1339 Some(vec![ty.with_lane(Type::I1)])
1340 }
1341 Opcode::Call | Opcode::CallIndirect | Opcode::TailCall => match &inst.extra {
1342 PendingExtra::Call { signature, .. } => Some(signature.return_types().collect()),
1343 _ => None,
1344 },
1345 _ => Some(vec![arg_type(inst, types)?]),
1346 }
1347}
1348
1349fn arg_type(inst: &PendingInst<'_>, types: &[Option<Type>]) -> Option<Type> {
1351 let &first = inst.args.first()?;
1352 *types.get(first as usize)?
1353}
1354
1355fn parse_u64(text: &str) -> Option<u64> {
1357 if text.is_empty() || (text.starts_with('0') && text.len() > 1) {
1358 return None;
1359 }
1360 if !text.bytes().all(|byte| byte.is_ascii_digit()) {
1361 return None;
1362 }
1363 text.parse().ok()
1364}
1365
1366fn parse_u32(text: &str) -> Option<u32> {
1367 parse_u64(text).and_then(|number| u32::try_from(number).ok())
1368}
1369
1370fn parse_i128(text: &str) -> Option<i128> {
1372 let (negative, digits) = match text.strip_prefix('-') {
1373 Some(rest) => (true, rest),
1374 None => (false, text),
1375 };
1376 if digits.is_empty() || (digits.starts_with('0') && digits.len() > 1) {
1377 return None;
1378 }
1379 if negative && digits == "0" {
1380 return None;
1381 }
1382 if !digits.bytes().all(|byte| byte.is_ascii_digit()) {
1383 return None;
1384 }
1385 let magnitude: u128 = digits.parse().ok()?;
1386 if negative {
1387 (magnitude <= 1 << 127).then(|| (magnitude as i128).wrapping_neg())
1390 } else {
1391 i128::try_from(magnitude).ok()
1392 }
1393}
1394
1395fn is_name_byte(byte: u8) -> bool {
1403 byte.is_ascii_alphanumeric() || matches!(byte, b'_' | b'.' | b'$') || byte >= 0x80
1404}
1405
1406#[cfg(test)]
1407mod tests {
1408 use rucc_base::Interner;
1409
1410 use super::*;
1411 use crate::fixtures::{EXAMPLE, SYMBOLS, ZOO};
1412 use crate::print;
1413
1414 fn round_trip(text: &str) -> String {
1416 let mut names = Interner::new();
1417 let module = match parse(text, &mut names) {
1418 Ok(module) => module,
1419 Err(error) => panic!("{error}"),
1420 };
1421 print(&module, &names)
1422 }
1423
1424 fn error(text: &str) -> String {
1425 let mut names = Interner::new();
1426 match parse(text, &mut names) {
1427 Ok(_) => panic!("that was expected to be turned down"),
1428 Err(error) => error.to_string(),
1429 }
1430 }
1431
1432 const HEADER: &str = "\
1433; ModuleID = 'example.c'
1434; format 0
1435target triple = \"x86_64-unknown-linux-gnu\"
1436target datalayout = \"e-p:64:64-i64:64-f80:128-S128\"
1437";
1438
1439 #[test]
1440 fn the_example_in_the_spec_comes_back_byte_for_byte() {
1441 assert_eq!(round_trip(EXAMPLE), EXAMPLE);
1442 }
1443
1444 #[test]
1445 fn one_of_almost_everything_comes_back_byte_for_byte() {
1446 assert_eq!(round_trip(ZOO), ZOO);
1447 }
1448
1449 #[test]
1450 fn the_shapes_a_symbol_comes_in_come_back_byte_for_byte() {
1451 assert_eq!(round_trip(SYMBOLS), SYMBOLS);
1452 }
1453
1454 #[test]
1455 fn a_name_that_is_not_ascii_comes_back_byte_for_byte() {
1456 let text = format!("{HEADER}\nglobal @été : i32 = 1, align 4, linkage(external)\n");
1460 assert_eq!(round_trip(&text), text);
1461 }
1462
1463 #[test]
1464 fn a_value_defined_after_it_is_used() {
1465 let text = format!(
1469 "{HEADER}
1470func @late() -> i64, linkage(external) {{
1471block0:
1472 jump block2
1473
1474block1(%0: i64):
1475 %1 = add %2, %0
1476 return %1
1477
1478block2:
1479 %2 = iconst.i64 7
1480 jump block1(%2)
1481}}
1482"
1483 );
1484 assert_eq!(round_trip(&text), text);
1485 }
1486
1487 #[test]
1488 fn what_the_abi_asks_of_a_parameter_comes_back_byte_for_byte() {
1489 let text = format!(
1492 "{HEADER}
1493func @f(ptr sret(24, align 8), ptr byval(16, align 8), i8 zext), linkage(external) {{
1494block0(%0: ptr, %1: ptr, %2: i8):
1495 return
1496}}
1497
1498func @g(i8 sext) -> i8 sext, linkage(external);
1499"
1500 );
1501 assert_eq!(round_trip(&text), text);
1502 }
1503
1504 #[test]
1505 fn a_word_that_is_not_an_attribute_is_not_read_as_one() {
1506 let text = format!(
1507 "{HEADER}
1508func @f(ptr inreg), linkage(external);
1509"
1510 );
1511 assert_eq!(error(&text), "line 6: `)` was expected");
1512 }
1513
1514 #[test]
1515 fn an_empty_module_is_its_header() {
1516 assert_eq!(round_trip(HEADER), HEADER);
1517 }
1518
1519 #[test]
1520 fn the_format_version_is_checked_before_anything_else() {
1521 let text = "; ModuleID = 'a.c'\n; format 99\n";
1522 assert_eq!(error(text), "line 2: this build reads format 0 and the text says format 99");
1523 }
1524
1525 #[test]
1526 fn an_opcode_nobody_has_is_reported_with_its_line() {
1527 let text = format!(
1528 "{HEADER}
1529func @f(), linkage(external) {{
1530block0:
1531 getelementptr %0
1532}}
1533"
1534 );
1535 assert_eq!(error(&text), "line 8: `getelementptr` is not an opcode");
1536 }
1537
1538 #[test]
1539 fn an_abi_on_an_argument_the_signature_names_is_turned_down() {
1540 let text = format!(
1543 "{HEADER}
1544func @f(ptr), linkage(external) {{
1545block0(%0: ptr):
1546 call @p(%0 byval(8, align 8)) : (ptr, ...)
1547 return
1548}}
1549"
1550 );
1551 assert_eq!(
1552 error(&text),
1553 "line 8: the signature is what says how an argument it names travels"
1554 );
1555 }
1556
1557 #[test]
1558 fn a_value_nothing_defines_is_reported() {
1559 let text = format!(
1560 "{HEADER}
1561func @f(i32) -> i32, linkage(external) {{
1562block0(%0: i32):
1563 %1 = add %0, %9
1564 return %1
1565}}
1566"
1567 );
1568 assert_eq!(error(&text), "line 8: %9 is used and never defined");
1569 }
1570
1571 #[test]
1572 fn a_block_nothing_defines_is_reported() {
1573 let text = format!(
1574 "{HEADER}
1575func @f(), linkage(external) {{
1576block0:
1577 jump block7
1578}}
1579"
1580 );
1581 assert_eq!(error(&text), "line 8: block7 is used and never defined");
1582 }
1583
1584 #[test]
1585 fn values_have_to_be_numbered_in_print_order() {
1586 let text = format!(
1587 "{HEADER}
1588func @f() -> i32, linkage(external) {{
1589block0:
1590 %1 = iconst.i32 0
1591 %0 = iconst.i32 1
1592 return %1
1593}}
1594"
1595 );
1596 assert_eq!(error(&text), "line 8: values are numbered in order and %0 comes next");
1597 }
1598
1599 #[test]
1600 fn blocks_have_to_be_numbered_in_print_order() {
1601 let text = format!(
1602 "{HEADER}
1603func @f(), linkage(external) {{
1604block1:
1605 return
1606}}
1607"
1608 );
1609 assert_eq!(error(&text), "line 7: blocks are numbered in order and block0 comes next");
1610 }
1611
1612 #[test]
1613 fn metadata_nobody_defines_is_reported() {
1614 let text = format!(
1615 "{HEADER}
1616func @f(ptr), linkage(external) {{
1617block0(%0: ptr):
1618 %1 = load.i32 %0, align 4, tbaa !3
1619 return
1620}}
1621"
1622 );
1623 assert_eq!(error(&text), "line 8: !3 is used and never defined");
1624 }
1625
1626 #[test]
1627 fn a_type_nothing_says_is_reported_rather_than_guessed() {
1628 let text = format!(
1629 "{HEADER}
1630func @f(), linkage(external) {{
1631block0:
1632 %0 = add
1633 return
1634}}
1635"
1636 );
1637 assert_eq!(error(&text), "line 8: nothing in the text says what add produces");
1638 }
1639
1640 #[test]
1641 fn a_line_with_something_left_on_it_is_turned_down() {
1642 let text = format!(
1643 "{HEADER}
1644global @x : i32 = 0, align 4, linkage(internal) and then some
1645"
1646 );
1647 assert_eq!(error(&text), "line 6: `and then some` is left over at the end of the line");
1648 }
1649
1650 #[test]
1651 fn a_constant_that_is_not_one_is_turned_down() {
1652 let text = format!(
1653 "{HEADER}
1654global @x : i32 = 007, align 4, linkage(internal)
1655"
1656 );
1657 assert_eq!(error(&text), "line 6: `007` is not an i32");
1658 }
1659
1660 #[test]
1661 fn a_number_is_read_the_way_the_printer_writes_it() {
1662 assert_eq!(parse_i128("-1"), Some(-1));
1663 assert_eq!(parse_i128("0"), Some(0));
1664 assert_eq!(parse_i128("-0"), None);
1665 assert_eq!(parse_i128("+1"), None);
1666 assert_eq!(parse_i128("01"), None);
1667 assert_eq!(parse_i128(""), None);
1668 assert_eq!(parse_i128("170141183460469231731687303715884105728"), None);
1669 assert_eq!(parse_i128("-170141183460469231731687303715884105728"), Some(i128::MIN));
1670 }
1671
1672 #[test]
1673 fn every_opcode_says_which_payload_it_carries() {
1674 for opcode in Opcode::all() {
1677 let kind = opcode.extra_kind();
1678 let expected = match opcode {
1679 Opcode::IConst | Opcode::FConst | Opcode::Splat => ExtraKind::Imm,
1680 Opcode::GlobalAddr | Opcode::TargetIntrinsic => ExtraKind::Symbol,
1681 Opcode::ICmp => ExtraKind::IntPred,
1682 Opcode::FCmp => ExtraKind::FloatPred,
1683 Opcode::Fence => ExtraKind::Order,
1684 Opcode::AtomicRmw => ExtraKind::Rmw,
1685 Opcode::Switch => ExtraKind::Switch,
1686 Opcode::InlineAsm => ExtraKind::Asm,
1687 Opcode::Jump | Opcode::BrIf | Opcode::BlockAddr | Opcode::IndirectBr => {
1688 ExtraKind::Targets
1689 }
1690 Opcode::Call | Opcode::CallIndirect | Opcode::TailCall => ExtraKind::Call,
1691 Opcode::Alloca
1692 | Opcode::Load
1693 | Opcode::Store
1694 | Opcode::Memcpy
1695 | Opcode::Memmove
1696 | Opcode::Memset
1697 | Opcode::AtomicLoad
1698 | Opcode::AtomicStore
1699 | Opcode::Cmpxchg
1700 | Opcode::VaObject => ExtraKind::Mem,
1701 _ => ExtraKind::None,
1702 };
1703 assert_eq!(kind, expected, "{}", opcode.name());
1704 }
1705 }
1706}