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::{Constraint, PhysReg, RegClass, RegFile, Role, Segment};
36
37use crate::func::Func;
38use crate::inst::{BlockCall, Mem, Opcode, Operand, Param, Reg};
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    got: bool,
103    segment: Option<Segment>,
104}
105
106/// One arm of a terminator, read but not yet resolved.
107struct PendingCall {
108    block: u32,
109    args: Vec<Written>,
110}
111
112/// An instruction, read but not yet built.
113struct PendingInst {
114    opcode: Symbol,
115    operands: Vec<PendingOperand>,
116    mem: Option<PendingMem>,
117    imm: Option<i64>,
118    symbol: Option<Symbol>,
119    succs: Vec<PendingCall>,
120    line: u32,
121}
122
123/// A block, read but not yet built.
124struct PendingBlock {
125    number: u32,
126    params: Vec<Written>,
127    insts: Vec<PendingInst>,
128    line: u32,
129}
130
131/// Reading one text.
132struct Parser<'a, 'n> {
133    text: &'a str,
134    pos: usize,
135    line: u32,
136    names: &'n mut Interner,
137    regs: &'n RegFile,
138}
139
140impl<'a> Parser<'a, '_> {
141    // The text.
142
143    fn funcs(mut self) -> Result<Vec<Func>, ParseError> {
144        let mut funcs = Vec::new();
145        loop {
146            self.skip_blank_lines();
147            if self.at_end() {
148                return Ok(funcs);
149            }
150            funcs.push(self.func()?);
151        }
152    }
153
154    fn func(&mut self) -> Result<Func, ParseError> {
155        self.expect("mfunc")?;
156        let name = self.symbol()?;
157        self.expect("{")?;
158        self.end_of_line()?;
159
160        let mut blocks: Vec<PendingBlock> = Vec::new();
161        loop {
162            self.skip_blank_lines();
163            if self.eat("}") {
164                self.end_of_line()?;
165                break;
166            }
167            if self.at_end() {
168                return self.fail("the function is not closed");
169            }
170            blocks.push(self.block()?);
171        }
172        self.build(name, blocks)
173    }
174
175    fn block(&mut self) -> Result<PendingBlock, ParseError> {
176        let line = self.line;
177        let number = self.label()?;
178        let mut params = Vec::new();
179        if self.eat("(") {
180            loop {
181                params.push(self.written(true)?);
182                if !self.eat(",") {
183                    break;
184                }
185            }
186            self.expect(")")?;
187        }
188        self.expect(":")?;
189        self.end_of_line()?;
190
191        let mut insts = Vec::new();
192        loop {
193            self.spaces();
194            if self.at_end() || self.at("\n") || self.at("}") || self.at_label() {
195                break;
196            }
197            insts.push(self.inst()?);
198        }
199        Ok(PendingBlock { number, params, insts, line })
200    }
201
202    fn inst(&mut self) -> Result<PendingInst, ParseError> {
203        let line = self.line;
204        let mut operands = Vec::new();
205        if self.at("%") || self.at("$") || self.peek_word() == "early" {
206            loop {
207                operands.push(self.operand(true)?);
208                if !self.eat(",") {
209                    break;
210                }
211            }
212            self.expect("=")?;
213        }
214        let opcode = self.opcode()?;
215
216        let mut inst = PendingInst {
217            opcode,
218            operands,
219            mem: None,
220            imm: None,
221            symbol: None,
222            succs: Vec::new(),
223            line,
224        };
225        self.spaces();
226        if !self.at("\n") && !self.at_end() {
227            loop {
228                self.item(&mut inst)?;
229                if !self.eat(",") {
230                    break;
231                }
232            }
233        }
234        self.end_of_line()?;
235        Ok(inst)
236    }
237
238    /// One of the things that can appear to the right of an opcode.
239    fn item(&mut self, inst: &mut PendingInst) -> Result<(), ParseError> {
240        self.spaces();
241        if self.at("%") || self.at("$") || self.peek_word() == "early" {
242            inst.operands.push(self.operand(false)?);
243            return Ok(());
244        }
245        if self.at("@") {
246            let symbol = self.symbol()?;
247            if inst.symbol.is_some() {
248                return self.fail("the instruction already names a symbol");
249            }
250            inst.symbol = Some(symbol);
251            return Ok(());
252        }
253        if self.at("[") {
254            let mem = self.mem()?;
255            if inst.mem.is_some() {
256                return self.fail("the instruction already has a memory operand");
257            }
258            inst.mem = Some(mem);
259            return Ok(());
260        }
261        if self.at_label() {
262            let call = self.block_call()?;
263            inst.succs.push(call);
264            return Ok(());
265        }
266        let value = self.i64()?;
267        if inst.imm.is_some() {
268            return self.fail("the instruction already has an immediate");
269        }
270        inst.imm = Some(value);
271        Ok(())
272    }
273
274    /// One operand: a register, and whatever is true of it besides.
275    fn operand(&mut self, written: bool) -> Result<PendingOperand, ParseError> {
276        let early = self.eat_word("early");
277        if early && !written {
278            return self.fail("only an operand an instruction writes can be early");
279        }
280        let reg = self.written(written)?;
281        let role = match (written, early) {
282            (false, _) => Role::Use,
283            (true, false) => Role::Def,
284            (true, true) => Role::EarlyDef,
285        };
286        let mut constraint = Constraint::Reg;
287        if self.eat("(") {
288            constraint = self.constraint()?;
289            self.expect(")")?;
290        }
291        Ok(PendingOperand { reg, role, constraint })
292    }
293
294    fn constraint(&mut self) -> Result<Constraint, ParseError> {
295        if self.at("$") {
296            let Written::Physical { reg, .. } = self.written(false)? else {
297                return self.fail("a register is fixed to a physical register");
298            };
299            return Ok(Constraint::Fixed(reg));
300        }
301        match self.word() {
302            "any" => Ok(Constraint::Any),
303            "stack" => Ok(Constraint::Stack),
304            "reuse" => {
305                let at = self.u32()?;
306                match u8::try_from(at) {
307                    Ok(at) => Ok(Constraint::Reuse(at)),
308                    Err(_) => self.fail(format!("no instruction has {at} operands")),
309                }
310            }
311            other => self.fail(format!("`{other}` is not something an operand can be")),
312        }
313    }
314
315    /// One register, as the text writes one.
316    ///
317    /// `declared` says whether this is the place the register is written, which is where a
318    /// virtual one says its class and the only place it is allowed to.
319    fn written(&mut self, declared: bool) -> Result<Written, ParseError> {
320        self.spaces();
321        if self.eat("$") {
322            let name = self.glued_word();
323            return match self.regs.reg_named(name) {
324                Some((class, reg)) => Ok(Written::Physical { reg, class }),
325                None => self.fail(format!("this target has no register called `{name}`")),
326            };
327        }
328        self.expect("%")?;
329        let number = self.u32()?;
330        if !declared {
331            return Ok(Written::Virtual { number, class: None });
332        }
333        self.expect(":")?;
334        let name = self.word();
335        match self.regs.class_named(name) {
336            Some(class) => Ok(Written::Virtual { number, class: Some(class) }),
337            None => self.fail(format!("this target has no register class called `{name}`")),
338        }
339    }
340
341    /// A memory operand, in brackets.
342    ///
343    /// The pieces are read as a sum rather than against a fixed shape, so the first register is
344    /// the base, the second is the index, and a number is the displacement. A target whose
345    /// addressing modes are narrower than that is the encoder's business, and one whose modes
346    /// are wider is a reason to widen this, not a reason for the reader to be strict about a
347    /// shape it cannot check anyway.
348    fn mem(&mut self) -> Result<PendingMem, ParseError> {
349        self.expect("[")?;
350        let mut mem = PendingMem { scale: 1, ..PendingMem::default() };
351        // Written in front of everything else, and a bare word where every other part of an
352        // address starts with a sigil or a digit, so one look is enough to know it is there.
353        mem.got = self.eat_word("got");
354        // In front of everything as well, and behind the marker above only because that is the
355        // order they read in. Both are facts about how the address is come by rather than about
356        // what is added to what, which is what the loop below reads.
357        mem.segment = if self.eat("fs:") {
358            Some(Segment::Fs)
359        } else if self.eat("gs:") {
360            Some(Segment::Gs)
361        } else {
362            None
363        };
364        let mut negative = false;
365        loop {
366            self.spaces();
367            if self.at("@") {
368                if mem.symbol.is_some() {
369                    return self.fail("an address names one symbol");
370                }
371                mem.symbol = Some(self.symbol()?);
372            } else if self.at("%") || self.at("$") {
373                let operand = PendingOperand {
374                    reg: self.written(false)?,
375                    role: Role::Use,
376                    constraint: Constraint::Reg,
377                };
378                if mem.base.is_none() {
379                    mem.base = Some(operand);
380                } else if mem.index.is_none() {
381                    mem.index = Some(operand);
382                    if self.eat("*") {
383                        let scale = self.u32()?;
384                        match u8::try_from(scale) {
385                            Ok(scale) => mem.scale = scale,
386                            Err(_) => return self.fail(format!("{scale} is not a scale")),
387                        }
388                    }
389                } else {
390                    return self.fail("an address names at most two registers");
391                }
392            } else {
393                let disp = self.i64()?;
394                let disp = if negative { -disp } else { disp };
395                match i32::try_from(disp) {
396                    Ok(disp) => mem.disp = disp,
397                    Err(_) => return self.fail(format!("{disp} is too far for a displacement")),
398                }
399            }
400            if self.eat("+") {
401                negative = false;
402            } else if self.eat("-") {
403                negative = true;
404            } else {
405                break;
406            }
407        }
408        self.expect("]")?;
409        Ok(mem)
410    }
411
412    /// One arm of a terminator.
413    fn block_call(&mut self) -> Result<PendingCall, ParseError> {
414        let block = self.label()?;
415        let mut args = Vec::new();
416        if self.eat("(") {
417            loop {
418                // An argument is a register being read, so it carries no class: the parameter it
419                // arrives as is what declares one, and that is what it is checked against.
420                args.push(self.written(false)?);
421                if !self.eat(",") {
422                    break;
423                }
424            }
425            self.expect(")")?;
426        }
427        Ok(PendingCall { block, args })
428    }
429
430    // Building what was read.
431
432    fn build(&mut self, name: Symbol, pending: Vec<PendingBlock>) -> Result<Func, ParseError> {
433        let mut func = Func::new(name);
434        for (index, block) in pending.iter().enumerate() {
435            if block.number as usize != index {
436                self.line = block.line;
437                return self.fail(format!(
438                    "this is block {index} of the function and the text calls it block{}",
439                    block.number
440                ));
441            }
442            func.create_block();
443        }
444        let blocks: Vec<_> = func.blocks().collect();
445
446        // The virtual registers, in the order the text writes them, which is the order the
447        // printer numbered them in.
448        let mut next = 0;
449        for (block, read) in blocks.iter().zip(&pending) {
450            for param in &read.params {
451                match *param {
452                    Written::Virtual { number, class } => {
453                        self.line = read.line;
454                        self.expect_number(number, next)?;
455                        let Some(class) = class else {
456                            return self
457                                .fail(format!("%{number} arrives without saying its class"));
458                        };
459                        func.append_param(*block, class);
460                        next += 1;
461                    }
462                    Written::Physical { reg, class } => {
463                        func.append_given_param(*block, Param { reg: Reg::physical(reg), class });
464                    }
465                }
466            }
467            for inst in &read.insts {
468                for operand in &inst.operands {
469                    if operand.role == Role::Use {
470                        continue;
471                    }
472                    if let Written::Virtual { number, class } = operand.reg {
473                        self.line = inst.line;
474                        self.expect_number(number, next)?;
475                        let Some(class) = class else {
476                            return self.fail(format!("%{number} is written without a class"));
477                        };
478                        func.new_vreg(class);
479                        next += 1;
480                    }
481                }
482            }
483        }
484
485        for (block, read) in blocks.iter().zip(&pending) {
486            let last = read.insts.len().saturating_sub(1);
487            for (at, inst) in read.insts.iter().enumerate() {
488                self.line = inst.line;
489                if !inst.succs.is_empty() && at != last {
490                    return self.fail("only the last instruction of a block says where it goes");
491                }
492                // Every register is resolved before the builder exists, because resolving one
493                // reads the function and the builder is holding it.
494                let mut operands = Vec::new();
495                for operand in &inst.operands {
496                    operands.push(self.resolve(&func, operand)?);
497                }
498                let mem = match inst.mem {
499                    Some(mem) => Some(Mem {
500                        base: match mem.base {
501                            Some(operand) => Some(self.resolve(&func, &operand)?),
502                            None => None,
503                        },
504                        index: match mem.index {
505                            Some(operand) => Some(self.resolve(&func, &operand)?),
506                            None => None,
507                        },
508                        scale: mem.scale,
509                        disp: mem.disp,
510                        symbol: mem.symbol,
511                        got: mem.got,
512                        segment: mem.segment,
513                    }),
514                    None => None,
515                };
516                let mut builder = func.build(*block, Opcode::new(inst.opcode));
517                for operand in operands {
518                    builder = builder.operand(operand);
519                }
520                if let Some(mem) = mem {
521                    builder = builder.mem(mem);
522                }
523                if let Some(symbol) = inst.symbol {
524                    builder = builder.symbol(symbol);
525                }
526                if let Some(value) = inst.imm {
527                    builder = builder.imm(value);
528                }
529                builder.finish();
530            }
531            let Some(terminator) = read.insts.last() else { continue };
532            self.line = terminator.line;
533            let mut succs = Vec::new();
534            for call in &terminator.succs {
535                let Some(&target) = blocks.get(call.block as usize) else {
536                    return self
537                        .fail(format!("block{} is branched to and never begins", call.block));
538                };
539                let mut args = Vec::new();
540                for (at, arg) in call.args.iter().enumerate() {
541                    args.push(match *arg {
542                        Written::Physical { reg, .. } => Reg::physical(reg),
543                        Written::Virtual { number, .. } => {
544                            let class = func[target].params.get(at).map(|param| param.class);
545                            let Some(class) = class else {
546                                return self.fail(format!(
547                                    "block{} takes {} arguments and is given more",
548                                    call.block,
549                                    func[target].params.len()
550                                ));
551                            };
552                            self.virtual_reg(&func, number, class)?
553                        }
554                    });
555                }
556                if args.len() != func[target].params.len() {
557                    return self.fail(format!(
558                        "block{} takes {} arguments and is given {}",
559                        call.block,
560                        func[target].params.len(),
561                        args.len()
562                    ));
563                }
564                succs.push(BlockCall::with(target, args));
565            }
566            *func.succs_mut(*block) = succs;
567        }
568        Ok(func)
569    }
570
571    /// One operand, once every register the function has exists.
572    ///
573    /// A register the instruction reads has to be one something writes, and the class it is in
574    /// is the one it was written as. That is checked here rather than left to a verifier,
575    /// because the alternative is a function that was read successfully and means something
576    /// else.
577    fn resolve(&self, func: &Func, operand: &PendingOperand) -> Result<Operand, ParseError> {
578        let (reg, class) = match operand.reg {
579            Written::Physical { reg, class } => (Reg::physical(reg), class),
580            Written::Virtual { number, class } => {
581                let reg = Reg::virtual_reg(number);
582                match (class, func.class_of(reg)) {
583                    (Some(class), _) => (reg, class),
584                    (None, Some(class)) => (reg, class),
585                    (None, None) => {
586                        return self.fail(format!("%{number} is read and never written"));
587                    }
588                }
589            }
590        };
591        Ok(Operand { reg, class, role: operand.role, constraint: operand.constraint })
592    }
593
594    /// The register a branch argument names, checked against the class it arrives as.
595    fn virtual_reg(&self, func: &Func, number: u32, class: RegClass) -> Result<Reg, ParseError> {
596        let reg = Reg::virtual_reg(number);
597        match func.class_of(reg) {
598            None => self.fail(format!("%{number} is read and never written")),
599            Some(found) if found != class => {
600                self.fail(format!("%{number} is passed to a parameter of another class"))
601            }
602            Some(_) => Ok(reg),
603        }
604    }
605
606    /// Checks that a register is numbered the way the printer numbers them.
607    fn expect_number(&self, number: u32, next: u32) -> Result<(), ParseError> {
608        if number == next {
609            return Ok(());
610        }
611        self.fail(format!("this is %{next} of the function and the text calls it %{number}"))
612    }
613
614    // Words, numbers and the rest of the text.
615
616    fn fail<T>(&self, message: impl Into<String>) -> Result<T, ParseError> {
617        Err(ParseError { line: self.line, message: message.into() })
618    }
619
620    fn at(&self, text: &str) -> bool {
621        self.text[self.pos..].starts_with(text)
622    }
623
624    fn at_end(&self) -> bool {
625        self.pos >= self.text.len()
626    }
627
628    /// Whether a block label begins here, which is what says an instruction does not.
629    fn at_label(&self) -> bool {
630        let rest = self.text[self.pos..].trim_start_matches([' ', '\t']);
631        let Some(rest) = rest.strip_prefix("block") else { return false };
632        rest.starts_with(|c: char| c.is_ascii_digit())
633    }
634
635    fn peek(&self) -> Option<u8> {
636        self.text.as_bytes().get(self.pos).copied()
637    }
638
639    fn spaces(&mut self) {
640        while matches!(self.peek(), Some(b' ' | b'\t')) {
641            self.pos += 1;
642        }
643    }
644
645    fn eat(&mut self, text: &str) -> bool {
646        self.spaces();
647        if self.at(text) {
648            self.pos += text.len();
649            return true;
650        }
651        false
652    }
653
654    fn expect(&mut self, text: &str) -> Result<(), ParseError> {
655        if self.eat(text) {
656            return Ok(());
657        }
658        self.fail(format!("expected `{text}`"))
659    }
660
661    fn word(&mut self) -> &'a str {
662        self.spaces();
663        self.glued_word()
664    }
665
666    /// The word starting exactly here, with no space skipped first.
667    fn glued_word(&mut self) -> &'a str {
668        let start = self.pos;
669        while self.peek().is_some_and(is_name_byte) {
670            self.pos += 1;
671        }
672        &self.text[start..self.pos]
673    }
674
675    fn peek_word(&self) -> &'a str {
676        let rest = self.text[self.pos..].trim_start_matches([' ', '\t']);
677        let end = rest
678            .find(|c: char| !(c.is_ascii_alphanumeric() || c == '_' || c == '.'))
679            .unwrap_or(rest.len());
680        &rest[..end]
681    }
682
683    fn eat_word(&mut self, word: &str) -> bool {
684        if self.peek_word() != word {
685            return false;
686        }
687        self.word();
688        true
689    }
690
691    /// A `blockN` label, giving back the number.
692    fn label(&mut self) -> Result<u32, ParseError> {
693        self.expect("block")?;
694        self.u32()
695    }
696
697    /// An `@name`.
698    fn symbol(&mut self) -> Result<Symbol, ParseError> {
699        self.expect("@")?;
700        let name = self.glued_word();
701        if name.is_empty() {
702            return self.fail("a symbol has a name");
703        }
704        Ok(self.names.intern(name))
705    }
706
707    fn u32(&mut self) -> Result<u32, ParseError> {
708        let word = self.word();
709        match word.parse::<u32>() {
710            Ok(number) => Ok(number),
711            Err(_) => self.fail(format!("`{word}` is not a number")),
712        }
713    }
714
715    fn i64(&mut self) -> Result<i64, ParseError> {
716        self.spaces();
717        let start = self.pos;
718        if self.at("-") {
719            self.pos += 1;
720        }
721        self.glued_word();
722        let word = &self.text[start..self.pos];
723        match word.parse::<i64>() {
724            Ok(number) => Ok(number),
725            Err(_) => self.fail(format!("`{word}` is not a number")),
726        }
727    }
728
729    /// The name of an instruction, which is a word with the dots targets put in theirs.
730    fn opcode(&mut self) -> Result<Symbol, ParseError> {
731        let word = self.word();
732        if word.is_empty() {
733            return self.fail("expected an instruction");
734        }
735        Ok(self.names.intern(word))
736    }
737
738    fn end_of_line(&mut self) -> Result<(), ParseError> {
739        self.spaces();
740        if self.at_end() {
741            return Ok(());
742        }
743        if !self.at("\n") {
744            let rest = self.text[self.pos..].lines().next().unwrap_or_default();
745            return self.fail(format!("`{rest}` is left over at the end of the line"));
746        }
747        self.pos += 1;
748        self.line += 1;
749        Ok(())
750    }
751
752    fn skip_blank_lines(&mut self) {
753        loop {
754            let held = self.pos;
755            self.spaces();
756            if self.at("\n") {
757                self.pos += 1;
758                self.line += 1;
759                continue;
760            }
761            self.pos = held;
762            return;
763        }
764    }
765}
766
767/// Whether the byte can be part of a name, a class, an opcode or a number.
768fn is_name_byte(byte: u8) -> bool {
769    byte.is_ascii_alphanumeric() || byte == b'_' || byte == b'.'
770}
771
772#[cfg(test)]
773mod tests {
774    use super::*;
775    use crate::fixtures::{AFTER, BEFORE, REGS};
776    use crate::print::{print, print_func};
777
778    /// Reads the text and writes it back, which has to give the same bytes.
779    fn round_trip(text: &str) {
780        let mut names = Interner::new();
781        let funcs = parse(text, &mut names, &REGS).expect("the fixture is what the printer writes");
782        let [func] = &funcs[..] else { panic!("the text is one function") };
783        assert_eq!(print_func(func, &names, &REGS), text);
784    }
785
786    /// The first thing the text says that does not add up.
787    fn error(text: &str) -> String {
788        let mut names = Interner::new();
789        parse(text, &mut names, &REGS).expect_err("the text does not add up").to_string()
790    }
791
792    #[test]
793    fn a_function_before_allocation_round_trips() {
794        round_trip(BEFORE);
795    }
796
797    #[test]
798    fn a_function_after_allocation_round_trips() {
799        round_trip(AFTER);
800    }
801
802    #[test]
803    fn an_address_read_out_of_the_offset_table_round_trips() {
804        // Not in the shared fixtures, because it is the one part of an address that says how the
805        // address is come by rather than what it is made of, and a fixture read for the shape of
806        // an addressing mode should not have to carry it.
807        round_trip(
808            "\
809mfunc @take {
810block0:
811    %0:gpr = x64.mov_rm [got @away]
812    x64.ret
813}
814",
815        );
816    }
817
818    #[test]
819    fn an_address_in_a_thread_s_own_block_round_trips() {
820        // The other part of an address that says how it is come by rather than what it is made of,
821        // and the one an address can be made of nothing but: no base, no index and no symbol, so
822        // the whole of it is the storage it is counted in and the constant.
823        round_trip(
824            "\
825mfunc @guard {
826block0:
827    %0:gpr = x64.mov_rm [fs:40]
828    x64.ret
829}
830",
831        );
832    }
833
834    #[test]
835    fn an_instruction_of_an_immediate_and_an_address_round_trips() {
836        // The touch a probing prologue puts on each page of a large frame, which is the one thing
837        // this compiler writes that has a number and an address and no register of its own. Worth
838        // a case because the reader works out what an operand is from the character it starts with
839        // and this is the only shape where a constant is followed by a bracket.
840        round_trip(
841            "\
842mfunc @deep {
843block0:
844    x64.or_mi_8 [$rsp], 0
845    x64.ret
846}
847",
848        );
849    }
850
851    #[test]
852    fn two_functions_round_trip() {
853        let text = format!("{BEFORE}\n{AFTER}");
854        let mut names = Interner::new();
855        let funcs = parse(&text, &mut names, &REGS).expect("both fixtures are readable");
856        assert_eq!(funcs.len(), 2);
857        assert_eq!(print(&funcs, &names, &REGS), text);
858    }
859
860    #[test]
861    fn an_empty_text_is_no_functions() {
862        let mut names = Interner::new();
863        assert!(parse("\n\n", &mut names, &REGS).expect("nothing is readable").is_empty());
864    }
865
866    #[test]
867    fn a_register_nothing_writes_is_refused() {
868        let text = "mfunc @f {\nblock0:\n    x64.ret %3\n}\n";
869        assert_eq!(error(text), "line 3: %3 is read and never written");
870    }
871
872    #[test]
873    fn a_register_numbered_out_of_order_is_refused() {
874        let text = "mfunc @f {\nblock0:\n    %1:gpr = x64.mov_ri 4\n    x64.ret %1\n}\n";
875        assert_eq!(error(text), "line 3: this is %0 of the function and the text calls it %1");
876    }
877
878    #[test]
879    fn a_block_numbered_out_of_order_is_refused() {
880        let text = "mfunc @f {\nblock1:\n    x64.ret $rax\n}\n";
881        assert_eq!(
882            error(text),
883            "line 2: this is block 0 of the function and the text calls it block1"
884        );
885    }
886
887    #[test]
888    fn a_register_the_target_does_not_have_is_refused() {
889        let text = "mfunc @f {\nblock0:\n    x64.ret $r13\n}\n";
890        assert_eq!(error(text), "line 3: this target has no register called `r13`");
891    }
892
893    #[test]
894    fn a_class_the_target_does_not_have_is_refused() {
895        let text = "mfunc @f {\nblock0:\n    %0:vec = x64.mov_ri 4\n    x64.ret $rax\n}\n";
896        assert_eq!(error(text), "line 3: this target has no register class called `vec`");
897    }
898
899    #[test]
900    fn an_argument_of_another_class_is_refused() {
901        let text = "\
902mfunc @f {
903block0:
904    %0:xmm = x64.movd_xr $rax
905    x64.jmp block1(%0)
906
907block1(%1:gpr):
908    x64.ret $rax
909}
910";
911        assert_eq!(error(text), "line 4: %0 is passed to a parameter of another class");
912    }
913
914    #[test]
915    fn a_block_given_the_wrong_number_of_arguments_is_refused() {
916        let text = "\
917mfunc @f {
918block0(%0:gpr):
919    x64.jmp block1(%0)
920
921block1(%1:gpr, %2:gpr):
922    x64.ret $rax
923}
924";
925        assert_eq!(error(text), "line 3: block1 takes 2 arguments and is given 1");
926    }
927
928    #[test]
929    fn a_branch_to_a_block_that_never_begins_is_refused() {
930        let text = "mfunc @f {\nblock0:\n    x64.jmp block4\n}\n";
931        assert_eq!(error(text), "line 3: block4 is branched to and never begins");
932    }
933
934    #[test]
935    fn a_branch_before_the_end_of_a_block_is_refused() {
936        let text = "mfunc @f {\nblock0:\n    x64.jmp block0\n    x64.ret $rax\n}\n";
937        assert_eq!(error(text), "line 3: only the last instruction of a block says where it goes");
938    }
939
940    #[test]
941    fn a_read_operand_cannot_be_early() {
942        let text = "mfunc @f {\nblock0:\n    x64.ret early $rax\n}\n";
943        assert_eq!(error(text), "line 3: only an operand an instruction writes can be early");
944    }
945
946    #[test]
947    fn a_second_immediate_is_refused() {
948        let text = "mfunc @f {\nblock0:\n    x64.ret 1, 2\n}\n";
949        assert_eq!(error(text), "line 3: the instruction already has an immediate");
950    }
951
952    #[test]
953    fn a_function_that_does_not_close_is_refused() {
954        let text = "mfunc @f {\nblock0:\n    x64.ret $rax\n";
955        assert_eq!(error(text), "line 4: the function is not closed");
956    }
957
958    #[test]
959    fn what_is_left_over_on_a_line_is_reported() {
960        let text = "mfunc @f {\nblock0:\n    x64.ret $rax ]\n}\n";
961        assert_eq!(error(text), "line 3: `]` is left over at the end of the line");
962    }
963}