Skip to main content

rucc_mir/
parse.rs

1//! The parser: text back into machine functions.
2//!
3//! Design: `spec/10-backend.md` section 10.1.
4//!
5//! The other half of the round trip. What the printer wrote, this reads, and printing the
6//! result gives the same bytes back. That is what makes a `--emit=mir` dump worth trusting,
7//! what lets a test state a machine function directly instead of running a front end to get
8//! one, and what will let the allocator be tested on inputs written by hand.
9//!
10//! # Forward references
11//!
12//! A terminator names a block further down the text, and an instruction can read a virtual
13//! register that a later block writes, which is what a loop looks like once its header has
14//! parameters. So a function is read in three passes over what the text said. The blocks are
15//! created first, so that every label exists. Then the virtual registers are handed out in the
16//! order the text writes them, which is the order the printer numbered them in, and the parser
17//! checks that the numbering adds up rather than assuming it. Only then are the instructions
18//! built, by which point everything either of them names exists.
19//!
20//! # What the reader is given
21//!
22//! The same register file the printer was given. Physical registers are written by name, and a
23//! name is what a target's register file says it is, so reading a dump of one target's MIR
24//! against another target's file is not a thing that can be made to work and is refused at the
25//! first register rather than half way through.
26//!
27//! A virtual register says its class only where the text writes it, so reading one is what the
28//! second pass is for: by the time an instruction is built, every register the function has
29//! exists and knows its class, and a register that is read and never written is the error that
30//! falls out of that rather than a case anybody had to look for.
31
32use std::fmt;
33
34use rucc_base::{Interner, Symbol};
35use rucc_target::{PhysReg, RegClass, RegFile};
36
37use crate::func::Func;
38use crate::inst::{BlockCall, Constraint, Mem, Opcode, Operand, Param, Reg, Role};
39
40/// Why a text could not be read.
41///
42/// One error and then nothing, rather than a list. A malformed dump is a bug in whatever wrote
43/// it or a file somebody edited by hand, and in both cases the first thing that does not add up
44/// is the thing worth reporting.
45#[derive(Clone, Debug, PartialEq, Eq)]
46pub struct ParseError {
47    /// Which line of the text, counting from one.
48    pub line: u32,
49    /// What was wrong with it.
50    pub message: String,
51}
52
53impl fmt::Display for ParseError {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        write!(f, "line {}: {}", self.line, self.message)
56    }
57}
58
59impl std::error::Error for ParseError {}
60
61/// Reads every function in the text the printer writes.
62///
63/// Names are interned into `names`, and physical registers are the ones `regs` describes, which
64/// are the two things the printer was given.
65///
66/// # Errors
67///
68/// Gives back the first thing in the text that does not add up, with the line it is on.
69pub fn parse(text: &str, names: &mut Interner, regs: &RegFile) -> Result<Vec<Func>, ParseError> {
70    Parser { text, pos: 0, line: 1, names, regs }.funcs()
71}
72
73/// A register as the text writes one.
74///
75/// A virtual register carries a class only where the text is writing the register, which is a
76/// block parameter or an operand to the left of an `=`. Everywhere else the class is not in the
77/// text and is the one the function already gave the register.
78#[derive(Clone, Copy, PartialEq, Eq)]
79enum Written {
80    /// A virtual register, by the number it was printed as.
81    Virtual { number: u32, class: Option<RegClass> },
82    /// A physical register, by its name, which says its class as well.
83    Physical { reg: PhysReg, class: RegClass },
84}
85
86/// An operand, read but not yet resolved.
87#[derive(Clone, Copy)]
88struct PendingOperand {
89    reg: Written,
90    role: Role,
91    constraint: Constraint,
92}
93
94/// A memory operand, read but not yet resolved.
95#[derive(Clone, Copy, Default)]
96struct PendingMem {
97    base: Option<PendingOperand>,
98    index: Option<PendingOperand>,
99    scale: u8,
100    disp: i32,
101    symbol: Option<Symbol>,
102}
103
104/// One arm of a terminator, read but not yet resolved.
105struct PendingCall {
106    block: u32,
107    args: Vec<Written>,
108}
109
110/// An instruction, read but not yet built.
111struct PendingInst {
112    opcode: Symbol,
113    operands: Vec<PendingOperand>,
114    mem: Option<PendingMem>,
115    imm: Option<i64>,
116    symbol: Option<Symbol>,
117    succs: Vec<PendingCall>,
118    line: u32,
119}
120
121/// A block, read but not yet built.
122struct PendingBlock {
123    number: u32,
124    params: Vec<Written>,
125    insts: Vec<PendingInst>,
126    line: u32,
127}
128
129/// Reading one text.
130struct Parser<'a, 'n> {
131    text: &'a str,
132    pos: usize,
133    line: u32,
134    names: &'n mut Interner,
135    regs: &'n RegFile,
136}
137
138impl<'a> Parser<'a, '_> {
139    // The text.
140
141    fn funcs(mut self) -> Result<Vec<Func>, ParseError> {
142        let mut funcs = Vec::new();
143        loop {
144            self.skip_blank_lines();
145            if self.at_end() {
146                return Ok(funcs);
147            }
148            funcs.push(self.func()?);
149        }
150    }
151
152    fn func(&mut self) -> Result<Func, ParseError> {
153        self.expect("mfunc")?;
154        let name = self.symbol()?;
155        self.expect("{")?;
156        self.end_of_line()?;
157
158        let mut blocks: Vec<PendingBlock> = Vec::new();
159        loop {
160            self.skip_blank_lines();
161            if self.eat("}") {
162                self.end_of_line()?;
163                break;
164            }
165            if self.at_end() {
166                return self.fail("the function is not closed");
167            }
168            blocks.push(self.block()?);
169        }
170        self.build(name, blocks)
171    }
172
173    fn block(&mut self) -> Result<PendingBlock, ParseError> {
174        let line = self.line;
175        let number = self.label()?;
176        let mut params = Vec::new();
177        if self.eat("(") {
178            loop {
179                params.push(self.written(true)?);
180                if !self.eat(",") {
181                    break;
182                }
183            }
184            self.expect(")")?;
185        }
186        self.expect(":")?;
187        self.end_of_line()?;
188
189        let mut insts = Vec::new();
190        loop {
191            self.spaces();
192            if self.at_end() || self.at("\n") || self.at("}") || self.at_label() {
193                break;
194            }
195            insts.push(self.inst()?);
196        }
197        Ok(PendingBlock { number, params, insts, line })
198    }
199
200    fn inst(&mut self) -> Result<PendingInst, ParseError> {
201        let line = self.line;
202        let mut operands = Vec::new();
203        if self.at("%") || self.at("$") || self.peek_word() == "early" {
204            loop {
205                operands.push(self.operand(true)?);
206                if !self.eat(",") {
207                    break;
208                }
209            }
210            self.expect("=")?;
211        }
212        let opcode = self.opcode()?;
213
214        let mut inst = PendingInst {
215            opcode,
216            operands,
217            mem: None,
218            imm: None,
219            symbol: None,
220            succs: Vec::new(),
221            line,
222        };
223        self.spaces();
224        if !self.at("\n") && !self.at_end() {
225            loop {
226                self.item(&mut inst)?;
227                if !self.eat(",") {
228                    break;
229                }
230            }
231        }
232        self.end_of_line()?;
233        Ok(inst)
234    }
235
236    /// One of the things that can appear to the right of an opcode.
237    fn item(&mut self, inst: &mut PendingInst) -> Result<(), ParseError> {
238        self.spaces();
239        if self.at("%") || self.at("$") || self.peek_word() == "early" {
240            inst.operands.push(self.operand(false)?);
241            return Ok(());
242        }
243        if self.at("@") {
244            let symbol = self.symbol()?;
245            if inst.symbol.is_some() {
246                return self.fail("the instruction already names a symbol");
247            }
248            inst.symbol = Some(symbol);
249            return Ok(());
250        }
251        if self.at("[") {
252            let mem = self.mem()?;
253            if inst.mem.is_some() {
254                return self.fail("the instruction already has a memory operand");
255            }
256            inst.mem = Some(mem);
257            return Ok(());
258        }
259        if self.at_label() {
260            let call = self.block_call()?;
261            inst.succs.push(call);
262            return Ok(());
263        }
264        let value = self.i64()?;
265        if inst.imm.is_some() {
266            return self.fail("the instruction already has an immediate");
267        }
268        inst.imm = Some(value);
269        Ok(())
270    }
271
272    /// One operand: a register, and whatever is true of it besides.
273    fn operand(&mut self, written: bool) -> Result<PendingOperand, ParseError> {
274        let early = self.eat_word("early");
275        if early && !written {
276            return self.fail("only an operand an instruction writes can be early");
277        }
278        let reg = self.written(written)?;
279        let role = match (written, early) {
280            (false, _) => Role::Use,
281            (true, false) => Role::Def,
282            (true, true) => Role::EarlyDef,
283        };
284        let mut constraint = Constraint::Reg;
285        if self.eat("(") {
286            constraint = self.constraint()?;
287            self.expect(")")?;
288        }
289        Ok(PendingOperand { reg, role, constraint })
290    }
291
292    fn constraint(&mut self) -> Result<Constraint, ParseError> {
293        if self.at("$") {
294            let Written::Physical { reg, .. } = self.written(false)? else {
295                return self.fail("a register is fixed to a physical register");
296            };
297            return Ok(Constraint::Fixed(reg));
298        }
299        match self.word() {
300            "any" => Ok(Constraint::Any),
301            "stack" => Ok(Constraint::Stack),
302            "reuse" => {
303                let at = self.u32()?;
304                match u8::try_from(at) {
305                    Ok(at) => Ok(Constraint::Reuse(at)),
306                    Err(_) => self.fail(format!("no instruction has {at} operands")),
307                }
308            }
309            other => self.fail(format!("`{other}` is not something an operand can be")),
310        }
311    }
312
313    /// One register, as the text writes one.
314    ///
315    /// `declared` says whether this is the place the register is written, which is where a
316    /// virtual one says its class and the only place it is allowed to.
317    fn written(&mut self, declared: bool) -> Result<Written, ParseError> {
318        self.spaces();
319        if self.eat("$") {
320            let name = self.glued_word();
321            return match self.regs.reg_named(name) {
322                Some((class, reg)) => Ok(Written::Physical { reg, class }),
323                None => self.fail(format!("this target has no register called `{name}`")),
324            };
325        }
326        self.expect("%")?;
327        let number = self.u32()?;
328        if !declared {
329            return Ok(Written::Virtual { number, class: None });
330        }
331        self.expect(":")?;
332        let name = self.word();
333        match self.regs.class_named(name) {
334            Some(class) => Ok(Written::Virtual { number, class: Some(class) }),
335            None => self.fail(format!("this target has no register class called `{name}`")),
336        }
337    }
338
339    /// A memory operand, in brackets.
340    ///
341    /// The pieces are read as a sum rather than against a fixed shape, so the first register is
342    /// the base, the second is the index, and a number is the displacement. A target whose
343    /// addressing modes are narrower than that is the encoder's business, and one whose modes
344    /// are wider is a reason to widen this, not a reason for the reader to be strict about a
345    /// shape it cannot check anyway.
346    fn mem(&mut self) -> Result<PendingMem, ParseError> {
347        self.expect("[")?;
348        let mut mem = PendingMem { scale: 1, ..PendingMem::default() };
349        let mut negative = false;
350        loop {
351            self.spaces();
352            if self.at("@") {
353                if mem.symbol.is_some() {
354                    return self.fail("an address names one symbol");
355                }
356                mem.symbol = Some(self.symbol()?);
357            } else if self.at("%") || self.at("$") {
358                let operand = PendingOperand {
359                    reg: self.written(false)?,
360                    role: Role::Use,
361                    constraint: Constraint::Reg,
362                };
363                if mem.base.is_none() {
364                    mem.base = Some(operand);
365                } else if mem.index.is_none() {
366                    mem.index = Some(operand);
367                    if self.eat("*") {
368                        let scale = self.u32()?;
369                        match u8::try_from(scale) {
370                            Ok(scale) => mem.scale = scale,
371                            Err(_) => return self.fail(format!("{scale} is not a scale")),
372                        }
373                    }
374                } else {
375                    return self.fail("an address names at most two registers");
376                }
377            } else {
378                let disp = self.i64()?;
379                let disp = if negative { -disp } else { disp };
380                match i32::try_from(disp) {
381                    Ok(disp) => mem.disp = disp,
382                    Err(_) => return self.fail(format!("{disp} is too far for a displacement")),
383                }
384            }
385            if self.eat("+") {
386                negative = false;
387            } else if self.eat("-") {
388                negative = true;
389            } else {
390                break;
391            }
392        }
393        self.expect("]")?;
394        Ok(mem)
395    }
396
397    /// One arm of a terminator.
398    fn block_call(&mut self) -> Result<PendingCall, ParseError> {
399        let block = self.label()?;
400        let mut args = Vec::new();
401        if self.eat("(") {
402            loop {
403                // An argument is a register being read, so it carries no class: the parameter it
404                // arrives as is what declares one, and that is what it is checked against.
405                args.push(self.written(false)?);
406                if !self.eat(",") {
407                    break;
408                }
409            }
410            self.expect(")")?;
411        }
412        Ok(PendingCall { block, args })
413    }
414
415    // Building what was read.
416
417    fn build(&mut self, name: Symbol, pending: Vec<PendingBlock>) -> Result<Func, ParseError> {
418        let mut func = Func::new(name);
419        for (index, block) in pending.iter().enumerate() {
420            if block.number as usize != index {
421                self.line = block.line;
422                return self.fail(format!(
423                    "this is block {index} of the function and the text calls it block{}",
424                    block.number
425                ));
426            }
427            func.create_block();
428        }
429        let blocks: Vec<_> = func.blocks().collect();
430
431        // The virtual registers, in the order the text writes them, which is the order the
432        // printer numbered them in.
433        let mut next = 0;
434        for (block, read) in blocks.iter().zip(&pending) {
435            for param in &read.params {
436                match *param {
437                    Written::Virtual { number, class } => {
438                        self.line = read.line;
439                        self.expect_number(number, next)?;
440                        let Some(class) = class else {
441                            return self
442                                .fail(format!("%{number} arrives without saying its class"));
443                        };
444                        func.append_param(*block, class);
445                        next += 1;
446                    }
447                    Written::Physical { reg, class } => {
448                        func.append_given_param(*block, Param { reg: Reg::physical(reg), class });
449                    }
450                }
451            }
452            for inst in &read.insts {
453                for operand in &inst.operands {
454                    if operand.role == Role::Use {
455                        continue;
456                    }
457                    if let Written::Virtual { number, class } = operand.reg {
458                        self.line = inst.line;
459                        self.expect_number(number, next)?;
460                        let Some(class) = class else {
461                            return self.fail(format!("%{number} is written without a class"));
462                        };
463                        func.new_vreg(class);
464                        next += 1;
465                    }
466                }
467            }
468        }
469
470        for (block, read) in blocks.iter().zip(&pending) {
471            let last = read.insts.len().saturating_sub(1);
472            for (at, inst) in read.insts.iter().enumerate() {
473                self.line = inst.line;
474                if !inst.succs.is_empty() && at != last {
475                    return self.fail("only the last instruction of a block says where it goes");
476                }
477                // Every register is resolved before the builder exists, because resolving one
478                // reads the function and the builder is holding it.
479                let mut operands = Vec::new();
480                for operand in &inst.operands {
481                    operands.push(self.resolve(&func, operand)?);
482                }
483                let mem = match inst.mem {
484                    Some(mem) => Some(Mem {
485                        base: match mem.base {
486                            Some(operand) => Some(self.resolve(&func, &operand)?),
487                            None => None,
488                        },
489                        index: match mem.index {
490                            Some(operand) => Some(self.resolve(&func, &operand)?),
491                            None => None,
492                        },
493                        scale: mem.scale,
494                        disp: mem.disp,
495                        symbol: mem.symbol,
496                    }),
497                    None => None,
498                };
499                let mut builder = func.build(*block, Opcode::new(inst.opcode));
500                for operand in operands {
501                    builder = builder.operand(operand);
502                }
503                if let Some(mem) = mem {
504                    builder = builder.mem(mem);
505                }
506                if let Some(symbol) = inst.symbol {
507                    builder = builder.symbol(symbol);
508                }
509                if let Some(value) = inst.imm {
510                    builder = builder.imm(value);
511                }
512                builder.finish();
513            }
514            let Some(terminator) = read.insts.last() else { continue };
515            self.line = terminator.line;
516            let mut succs = Vec::new();
517            for call in &terminator.succs {
518                let Some(&target) = blocks.get(call.block as usize) else {
519                    return self
520                        .fail(format!("block{} is branched to and never begins", call.block));
521                };
522                let mut args = Vec::new();
523                for (at, arg) in call.args.iter().enumerate() {
524                    args.push(match *arg {
525                        Written::Physical { reg, .. } => Reg::physical(reg),
526                        Written::Virtual { number, .. } => {
527                            let class = func[target].params.get(at).map(|param| param.class);
528                            let Some(class) = class else {
529                                return self.fail(format!(
530                                    "block{} takes {} arguments and is given more",
531                                    call.block,
532                                    func[target].params.len()
533                                ));
534                            };
535                            self.virtual_reg(&func, number, class)?
536                        }
537                    });
538                }
539                if args.len() != func[target].params.len() {
540                    return self.fail(format!(
541                        "block{} takes {} arguments and is given {}",
542                        call.block,
543                        func[target].params.len(),
544                        args.len()
545                    ));
546                }
547                succs.push(BlockCall::with(target, args));
548            }
549            *func.succs_mut(*block) = succs;
550        }
551        Ok(func)
552    }
553
554    /// One operand, once every register the function has exists.
555    ///
556    /// A register the instruction reads has to be one something writes, and the class it is in
557    /// is the one it was written as. That is checked here rather than left to a verifier,
558    /// because the alternative is a function that was read successfully and means something
559    /// else.
560    fn resolve(&self, func: &Func, operand: &PendingOperand) -> Result<Operand, ParseError> {
561        let (reg, class) = match operand.reg {
562            Written::Physical { reg, class } => (Reg::physical(reg), class),
563            Written::Virtual { number, class } => {
564                let reg = Reg::virtual_reg(number);
565                match (class, func.class_of(reg)) {
566                    (Some(class), _) => (reg, class),
567                    (None, Some(class)) => (reg, class),
568                    (None, None) => {
569                        return self.fail(format!("%{number} is read and never written"));
570                    }
571                }
572            }
573        };
574        Ok(Operand { reg, class, role: operand.role, constraint: operand.constraint })
575    }
576
577    /// The register a branch argument names, checked against the class it arrives as.
578    fn virtual_reg(&self, func: &Func, number: u32, class: RegClass) -> Result<Reg, ParseError> {
579        let reg = Reg::virtual_reg(number);
580        match func.class_of(reg) {
581            None => self.fail(format!("%{number} is read and never written")),
582            Some(found) if found != class => {
583                self.fail(format!("%{number} is passed to a parameter of another class"))
584            }
585            Some(_) => Ok(reg),
586        }
587    }
588
589    /// Checks that a register is numbered the way the printer numbers them.
590    fn expect_number(&self, number: u32, next: u32) -> Result<(), ParseError> {
591        if number == next {
592            return Ok(());
593        }
594        self.fail(format!("this is %{next} of the function and the text calls it %{number}"))
595    }
596
597    // Words, numbers and the rest of the text.
598
599    fn fail<T>(&self, message: impl Into<String>) -> Result<T, ParseError> {
600        Err(ParseError { line: self.line, message: message.into() })
601    }
602
603    fn at(&self, text: &str) -> bool {
604        self.text[self.pos..].starts_with(text)
605    }
606
607    fn at_end(&self) -> bool {
608        self.pos >= self.text.len()
609    }
610
611    /// Whether a block label begins here, which is what says an instruction does not.
612    fn at_label(&self) -> bool {
613        let rest = self.text[self.pos..].trim_start_matches([' ', '\t']);
614        let Some(rest) = rest.strip_prefix("block") else { return false };
615        rest.starts_with(|c: char| c.is_ascii_digit())
616    }
617
618    fn peek(&self) -> Option<u8> {
619        self.text.as_bytes().get(self.pos).copied()
620    }
621
622    fn spaces(&mut self) {
623        while matches!(self.peek(), Some(b' ' | b'\t')) {
624            self.pos += 1;
625        }
626    }
627
628    fn eat(&mut self, text: &str) -> bool {
629        self.spaces();
630        if self.at(text) {
631            self.pos += text.len();
632            return true;
633        }
634        false
635    }
636
637    fn expect(&mut self, text: &str) -> Result<(), ParseError> {
638        if self.eat(text) {
639            return Ok(());
640        }
641        self.fail(format!("expected `{text}`"))
642    }
643
644    fn word(&mut self) -> &'a str {
645        self.spaces();
646        self.glued_word()
647    }
648
649    /// The word starting exactly here, with no space skipped first.
650    fn glued_word(&mut self) -> &'a str {
651        let start = self.pos;
652        while self.peek().is_some_and(is_name_byte) {
653            self.pos += 1;
654        }
655        &self.text[start..self.pos]
656    }
657
658    fn peek_word(&self) -> &'a str {
659        let rest = self.text[self.pos..].trim_start_matches([' ', '\t']);
660        let end = rest
661            .find(|c: char| !(c.is_ascii_alphanumeric() || c == '_' || c == '.'))
662            .unwrap_or(rest.len());
663        &rest[..end]
664    }
665
666    fn eat_word(&mut self, word: &str) -> bool {
667        if self.peek_word() != word {
668            return false;
669        }
670        self.word();
671        true
672    }
673
674    /// A `blockN` label, giving back the number.
675    fn label(&mut self) -> Result<u32, ParseError> {
676        self.expect("block")?;
677        self.u32()
678    }
679
680    /// An `@name`.
681    fn symbol(&mut self) -> Result<Symbol, ParseError> {
682        self.expect("@")?;
683        let name = self.glued_word();
684        if name.is_empty() {
685            return self.fail("a symbol has a name");
686        }
687        Ok(self.names.intern(name))
688    }
689
690    fn u32(&mut self) -> Result<u32, ParseError> {
691        let word = self.word();
692        match word.parse::<u32>() {
693            Ok(number) => Ok(number),
694            Err(_) => self.fail(format!("`{word}` is not a number")),
695        }
696    }
697
698    fn i64(&mut self) -> Result<i64, ParseError> {
699        self.spaces();
700        let start = self.pos;
701        if self.at("-") {
702            self.pos += 1;
703        }
704        self.glued_word();
705        let word = &self.text[start..self.pos];
706        match word.parse::<i64>() {
707            Ok(number) => Ok(number),
708            Err(_) => self.fail(format!("`{word}` is not a number")),
709        }
710    }
711
712    /// The name of an instruction, which is a word with the dots targets put in theirs.
713    fn opcode(&mut self) -> Result<Symbol, ParseError> {
714        let word = self.word();
715        if word.is_empty() {
716            return self.fail("expected an instruction");
717        }
718        Ok(self.names.intern(word))
719    }
720
721    fn end_of_line(&mut self) -> Result<(), ParseError> {
722        self.spaces();
723        if self.at_end() {
724            return Ok(());
725        }
726        if !self.at("\n") {
727            let rest = self.text[self.pos..].lines().next().unwrap_or_default();
728            return self.fail(format!("`{rest}` is left over at the end of the line"));
729        }
730        self.pos += 1;
731        self.line += 1;
732        Ok(())
733    }
734
735    fn skip_blank_lines(&mut self) {
736        loop {
737            let held = self.pos;
738            self.spaces();
739            if self.at("\n") {
740                self.pos += 1;
741                self.line += 1;
742                continue;
743            }
744            self.pos = held;
745            return;
746        }
747    }
748}
749
750/// Whether the byte can be part of a name, a class, an opcode or a number.
751fn is_name_byte(byte: u8) -> bool {
752    byte.is_ascii_alphanumeric() || byte == b'_' || byte == b'.'
753}
754
755#[cfg(test)]
756mod tests {
757    use super::*;
758    use crate::fixtures::{AFTER, BEFORE, REGS};
759    use crate::print::{print, print_func};
760
761    /// Reads the text and writes it back, which has to give the same bytes.
762    fn round_trip(text: &str) {
763        let mut names = Interner::new();
764        let funcs = parse(text, &mut names, &REGS).expect("the fixture is what the printer writes");
765        let [func] = &funcs[..] else { panic!("the text is one function") };
766        assert_eq!(print_func(func, &names, &REGS), text);
767    }
768
769    /// The first thing the text says that does not add up.
770    fn error(text: &str) -> String {
771        let mut names = Interner::new();
772        parse(text, &mut names, &REGS).expect_err("the text does not add up").to_string()
773    }
774
775    #[test]
776    fn a_function_before_allocation_round_trips() {
777        round_trip(BEFORE);
778    }
779
780    #[test]
781    fn a_function_after_allocation_round_trips() {
782        round_trip(AFTER);
783    }
784
785    #[test]
786    fn two_functions_round_trip() {
787        let text = format!("{BEFORE}\n{AFTER}");
788        let mut names = Interner::new();
789        let funcs = parse(&text, &mut names, &REGS).expect("both fixtures are readable");
790        assert_eq!(funcs.len(), 2);
791        assert_eq!(print(&funcs, &names, &REGS), text);
792    }
793
794    #[test]
795    fn an_empty_text_is_no_functions() {
796        let mut names = Interner::new();
797        assert!(parse("\n\n", &mut names, &REGS).expect("nothing is readable").is_empty());
798    }
799
800    #[test]
801    fn a_register_nothing_writes_is_refused() {
802        let text = "mfunc @f {\nblock0:\n    x64.ret %3\n}\n";
803        assert_eq!(error(text), "line 3: %3 is read and never written");
804    }
805
806    #[test]
807    fn a_register_numbered_out_of_order_is_refused() {
808        let text = "mfunc @f {\nblock0:\n    %1:gpr = x64.mov_ri 4\n    x64.ret %1\n}\n";
809        assert_eq!(error(text), "line 3: this is %0 of the function and the text calls it %1");
810    }
811
812    #[test]
813    fn a_block_numbered_out_of_order_is_refused() {
814        let text = "mfunc @f {\nblock1:\n    x64.ret $rax\n}\n";
815        assert_eq!(
816            error(text),
817            "line 2: this is block 0 of the function and the text calls it block1"
818        );
819    }
820
821    #[test]
822    fn a_register_the_target_does_not_have_is_refused() {
823        let text = "mfunc @f {\nblock0:\n    x64.ret $r13\n}\n";
824        assert_eq!(error(text), "line 3: this target has no register called `r13`");
825    }
826
827    #[test]
828    fn a_class_the_target_does_not_have_is_refused() {
829        let text = "mfunc @f {\nblock0:\n    %0:vec = x64.mov_ri 4\n    x64.ret $rax\n}\n";
830        assert_eq!(error(text), "line 3: this target has no register class called `vec`");
831    }
832
833    #[test]
834    fn an_argument_of_another_class_is_refused() {
835        let text = "\
836mfunc @f {
837block0:
838    %0:xmm = x64.movd_xr $rax
839    x64.jmp block1(%0)
840
841block1(%1:gpr):
842    x64.ret $rax
843}
844";
845        assert_eq!(error(text), "line 4: %0 is passed to a parameter of another class");
846    }
847
848    #[test]
849    fn a_block_given_the_wrong_number_of_arguments_is_refused() {
850        let text = "\
851mfunc @f {
852block0(%0:gpr):
853    x64.jmp block1(%0)
854
855block1(%1:gpr, %2:gpr):
856    x64.ret $rax
857}
858";
859        assert_eq!(error(text), "line 3: block1 takes 2 arguments and is given 1");
860    }
861
862    #[test]
863    fn a_branch_to_a_block_that_never_begins_is_refused() {
864        let text = "mfunc @f {\nblock0:\n    x64.jmp block4\n}\n";
865        assert_eq!(error(text), "line 3: block4 is branched to and never begins");
866    }
867
868    #[test]
869    fn a_branch_before_the_end_of_a_block_is_refused() {
870        let text = "mfunc @f {\nblock0:\n    x64.jmp block0\n    x64.ret $rax\n}\n";
871        assert_eq!(error(text), "line 3: only the last instruction of a block says where it goes");
872    }
873
874    #[test]
875    fn a_read_operand_cannot_be_early() {
876        let text = "mfunc @f {\nblock0:\n    x64.ret early $rax\n}\n";
877        assert_eq!(error(text), "line 3: only an operand an instruction writes can be early");
878    }
879
880    #[test]
881    fn a_second_immediate_is_refused() {
882        let text = "mfunc @f {\nblock0:\n    x64.ret 1, 2\n}\n";
883        assert_eq!(error(text), "line 3: the instruction already has an immediate");
884    }
885
886    #[test]
887    fn a_function_that_does_not_close_is_refused() {
888        let text = "mfunc @f {\nblock0:\n    x64.ret $rax\n";
889        assert_eq!(error(text), "line 4: the function is not closed");
890    }
891
892    #[test]
893    fn what_is_left_over_on_a_line_is_reported() {
894        let text = "mfunc @f {\nblock0:\n    x64.ret $rax ]\n}\n";
895        assert_eq!(error(text), "line 3: `]` is left over at the end of the line");
896    }
897}